162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* Marvell PTP driver
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2020 Marvell.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/bitfield.h>
962306a36Sopenharmony_ci#include <linux/device.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/pci.h>
1262306a36Sopenharmony_ci#include <linux/hrtimer.h>
1362306a36Sopenharmony_ci#include <linux/ktime.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include "mbox.h"
1662306a36Sopenharmony_ci#include "ptp.h"
1762306a36Sopenharmony_ci#include "rvu.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#define DRV_NAME				"Marvell PTP Driver"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define PCI_DEVID_OCTEONTX2_PTP			0xA00C
2262306a36Sopenharmony_ci#define PCI_SUBSYS_DEVID_OCTX2_98xx_PTP		0xB100
2362306a36Sopenharmony_ci#define PCI_SUBSYS_DEVID_OCTX2_96XX_PTP		0xB200
2462306a36Sopenharmony_ci#define PCI_SUBSYS_DEVID_OCTX2_95XX_PTP		0xB300
2562306a36Sopenharmony_ci#define PCI_SUBSYS_DEVID_OCTX2_95XXN_PTP	0xB400
2662306a36Sopenharmony_ci#define PCI_SUBSYS_DEVID_OCTX2_95MM_PTP		0xB500
2762306a36Sopenharmony_ci#define PCI_SUBSYS_DEVID_OCTX2_95XXO_PTP	0xB600
2862306a36Sopenharmony_ci#define PCI_DEVID_OCTEONTX2_RST			0xA085
2962306a36Sopenharmony_ci#define PCI_DEVID_CN10K_PTP			0xA09E
3062306a36Sopenharmony_ci#define PCI_SUBSYS_DEVID_CN10K_A_PTP		0xB900
3162306a36Sopenharmony_ci#define PCI_SUBSYS_DEVID_CNF10K_A_PTP		0xBA00
3262306a36Sopenharmony_ci#define PCI_SUBSYS_DEVID_CNF10K_B_PTP		0xBC00
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#define PCI_PTP_BAR_NO				0
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#define PTP_CLOCK_CFG				0xF00ULL
3762306a36Sopenharmony_ci#define PTP_CLOCK_CFG_PTP_EN			BIT_ULL(0)
3862306a36Sopenharmony_ci#define PTP_CLOCK_CFG_EXT_CLK_EN		BIT_ULL(1)
3962306a36Sopenharmony_ci#define PTP_CLOCK_CFG_EXT_CLK_IN_MASK		GENMASK_ULL(7, 2)
4062306a36Sopenharmony_ci#define PTP_CLOCK_CFG_TSTMP_EDGE		BIT_ULL(9)
4162306a36Sopenharmony_ci#define PTP_CLOCK_CFG_TSTMP_EN			BIT_ULL(8)
4262306a36Sopenharmony_ci#define PTP_CLOCK_CFG_TSTMP_IN_MASK		GENMASK_ULL(15, 10)
4362306a36Sopenharmony_ci#define PTP_CLOCK_CFG_ATOMIC_OP_MASK		GENMASK_ULL(28, 26)
4462306a36Sopenharmony_ci#define PTP_CLOCK_CFG_PPS_EN			BIT_ULL(30)
4562306a36Sopenharmony_ci#define PTP_CLOCK_CFG_PPS_INV			BIT_ULL(31)
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci#define PTP_PPS_HI_INCR				0xF60ULL
4862306a36Sopenharmony_ci#define PTP_PPS_LO_INCR				0xF68ULL
4962306a36Sopenharmony_ci#define PTP_PPS_THRESH_HI			0xF58ULL
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci#define PTP_CLOCK_LO				0xF08ULL
5262306a36Sopenharmony_ci#define PTP_CLOCK_HI				0xF10ULL
5362306a36Sopenharmony_ci#define PTP_CLOCK_COMP				0xF18ULL
5462306a36Sopenharmony_ci#define PTP_TIMESTAMP				0xF20ULL
5562306a36Sopenharmony_ci#define PTP_CLOCK_SEC				0xFD0ULL
5662306a36Sopenharmony_ci#define PTP_SEC_ROLLOVER			0xFD8ULL
5762306a36Sopenharmony_ci/* Atomic update related CSRs */
5862306a36Sopenharmony_ci#define PTP_FRNS_TIMESTAMP			0xFE0ULL
5962306a36Sopenharmony_ci#define PTP_NXT_ROLLOVER_SET			0xFE8ULL
6062306a36Sopenharmony_ci#define PTP_CURR_ROLLOVER_SET			0xFF0ULL
6162306a36Sopenharmony_ci#define PTP_NANO_TIMESTAMP			0xFF8ULL
6262306a36Sopenharmony_ci#define PTP_SEC_TIMESTAMP			0x1000ULL
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci#define CYCLE_MULT				1000
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci#define is_rev_A0(ptp) (((ptp)->pdev->revision & 0x0F) == 0x0)
6762306a36Sopenharmony_ci#define is_rev_A1(ptp) (((ptp)->pdev->revision & 0x0F) == 0x1)
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci/* PTP atomic update operation type */
7062306a36Sopenharmony_cienum atomic_opcode {
7162306a36Sopenharmony_ci	ATOMIC_SET = 1,
7262306a36Sopenharmony_ci	ATOMIC_INC = 3,
7362306a36Sopenharmony_ci	ATOMIC_DEC = 4
7462306a36Sopenharmony_ci};
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic struct ptp *first_ptp_block;
7762306a36Sopenharmony_cistatic const struct pci_device_id ptp_id_table[];
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic bool is_ptp_dev_cnf10ka(struct ptp *ptp)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	return ptp->pdev->subsystem_device == PCI_SUBSYS_DEVID_CNF10K_A_PTP;
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic bool is_ptp_dev_cn10ka(struct ptp *ptp)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	return ptp->pdev->subsystem_device == PCI_SUBSYS_DEVID_CN10K_A_PTP;
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic bool cn10k_ptp_errata(struct ptp *ptp)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	if ((is_ptp_dev_cn10ka(ptp) || is_ptp_dev_cnf10ka(ptp)) &&
9262306a36Sopenharmony_ci	    (is_rev_A0(ptp) || is_rev_A1(ptp)))
9362306a36Sopenharmony_ci		return true;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	return false;
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic bool is_tstmp_atomic_update_supported(struct rvu *rvu)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	struct ptp *ptp = rvu->ptp;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	if (is_rvu_otx2(rvu))
10362306a36Sopenharmony_ci		return false;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	/* On older silicon variants of CN10K, atomic update feature
10662306a36Sopenharmony_ci	 * is not available.
10762306a36Sopenharmony_ci	 */
10862306a36Sopenharmony_ci	if ((is_ptp_dev_cn10ka(ptp) || is_ptp_dev_cnf10ka(ptp)) &&
10962306a36Sopenharmony_ci	    (is_rev_A0(ptp) || is_rev_A1(ptp)))
11062306a36Sopenharmony_ci		return false;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	return true;
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_cistatic enum hrtimer_restart ptp_reset_thresh(struct hrtimer *hrtimer)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	struct ptp *ptp = container_of(hrtimer, struct ptp, hrtimer);
11862306a36Sopenharmony_ci	ktime_t curr_ts = ktime_get();
11962306a36Sopenharmony_ci	ktime_t delta_ns, period_ns;
12062306a36Sopenharmony_ci	u64 ptp_clock_hi;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	/* calculate the elapsed time since last restart */
12362306a36Sopenharmony_ci	delta_ns = ktime_to_ns(ktime_sub(curr_ts, ptp->last_ts));
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	/* if the ptp clock value has crossed 0.5 seconds,
12662306a36Sopenharmony_ci	 * its too late to update pps threshold value, so
12762306a36Sopenharmony_ci	 * update threshold after 1 second.
12862306a36Sopenharmony_ci	 */
12962306a36Sopenharmony_ci	ptp_clock_hi = readq(ptp->reg_base + PTP_CLOCK_HI);
13062306a36Sopenharmony_ci	if (ptp_clock_hi > 500000000) {
13162306a36Sopenharmony_ci		period_ns = ktime_set(0, (NSEC_PER_SEC + 100 - ptp_clock_hi));
13262306a36Sopenharmony_ci	} else {
13362306a36Sopenharmony_ci		writeq(500000000, ptp->reg_base + PTP_PPS_THRESH_HI);
13462306a36Sopenharmony_ci		period_ns = ktime_set(0, (NSEC_PER_SEC + 100 - delta_ns));
13562306a36Sopenharmony_ci	}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	hrtimer_forward_now(hrtimer, period_ns);
13862306a36Sopenharmony_ci	ptp->last_ts = curr_ts;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	return HRTIMER_RESTART;
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cistatic void ptp_hrtimer_start(struct ptp *ptp, ktime_t start_ns)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	ktime_t period_ns;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	period_ns = ktime_set(0, (NSEC_PER_SEC + 100 - start_ns));
14862306a36Sopenharmony_ci	hrtimer_start(&ptp->hrtimer, period_ns, HRTIMER_MODE_REL);
14962306a36Sopenharmony_ci	ptp->last_ts = ktime_get();
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic u64 read_ptp_tstmp_sec_nsec(struct ptp *ptp)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	u64 sec, sec1, nsec;
15562306a36Sopenharmony_ci	unsigned long flags;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	spin_lock_irqsave(&ptp->ptp_lock, flags);
15862306a36Sopenharmony_ci	sec = readq(ptp->reg_base + PTP_CLOCK_SEC) & 0xFFFFFFFFUL;
15962306a36Sopenharmony_ci	nsec = readq(ptp->reg_base + PTP_CLOCK_HI);
16062306a36Sopenharmony_ci	sec1 = readq(ptp->reg_base + PTP_CLOCK_SEC) & 0xFFFFFFFFUL;
16162306a36Sopenharmony_ci	/* check nsec rollover */
16262306a36Sopenharmony_ci	if (sec1 > sec) {
16362306a36Sopenharmony_ci		nsec = readq(ptp->reg_base + PTP_CLOCK_HI);
16462306a36Sopenharmony_ci		sec = sec1;
16562306a36Sopenharmony_ci	}
16662306a36Sopenharmony_ci	spin_unlock_irqrestore(&ptp->ptp_lock, flags);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	return sec * NSEC_PER_SEC + nsec;
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_cistatic u64 read_ptp_tstmp_nsec(struct ptp *ptp)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	return readq(ptp->reg_base + PTP_CLOCK_HI);
17462306a36Sopenharmony_ci}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistatic u64 ptp_calc_adjusted_comp(u64 ptp_clock_freq)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	u64 comp, adj = 0, cycles_per_sec, ns_drift = 0;
17962306a36Sopenharmony_ci	u32 ptp_clock_nsec, cycle_time;
18062306a36Sopenharmony_ci	int cycle;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	/* Errata:
18362306a36Sopenharmony_ci	 * Issue #1: At the time of 1 sec rollover of the nano-second counter,
18462306a36Sopenharmony_ci	 * the nano-second counter is set to 0. However, it should be set to
18562306a36Sopenharmony_ci	 * (existing counter_value - 10^9).
18662306a36Sopenharmony_ci	 *
18762306a36Sopenharmony_ci	 * Issue #2: The nano-second counter rolls over at 0x3B9A_C9FF.
18862306a36Sopenharmony_ci	 * It should roll over at 0x3B9A_CA00.
18962306a36Sopenharmony_ci	 */
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	/* calculate ptp_clock_comp value */
19262306a36Sopenharmony_ci	comp = ((u64)1000000000ULL << 32) / ptp_clock_freq;
19362306a36Sopenharmony_ci	/* use CYCLE_MULT to avoid accuracy loss due to integer arithmetic */
19462306a36Sopenharmony_ci	cycle_time = NSEC_PER_SEC * CYCLE_MULT / ptp_clock_freq;
19562306a36Sopenharmony_ci	/* cycles per sec */
19662306a36Sopenharmony_ci	cycles_per_sec = ptp_clock_freq;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	/* check whether ptp nanosecond counter rolls over early */
19962306a36Sopenharmony_ci	cycle = cycles_per_sec - 1;
20062306a36Sopenharmony_ci	ptp_clock_nsec = (cycle * comp) >> 32;
20162306a36Sopenharmony_ci	while (ptp_clock_nsec < NSEC_PER_SEC) {
20262306a36Sopenharmony_ci		if (ptp_clock_nsec == 0x3B9AC9FF)
20362306a36Sopenharmony_ci			goto calc_adj_comp;
20462306a36Sopenharmony_ci		cycle++;
20562306a36Sopenharmony_ci		ptp_clock_nsec = (cycle * comp) >> 32;
20662306a36Sopenharmony_ci	}
20762306a36Sopenharmony_ci	/* compute nanoseconds lost per second when nsec counter rolls over */
20862306a36Sopenharmony_ci	ns_drift = ptp_clock_nsec - NSEC_PER_SEC;
20962306a36Sopenharmony_ci	/* calculate ptp_clock_comp adjustment */
21062306a36Sopenharmony_ci	if (ns_drift > 0) {
21162306a36Sopenharmony_ci		adj = comp * ns_drift;
21262306a36Sopenharmony_ci		adj = adj / 1000000000ULL;
21362306a36Sopenharmony_ci	}
21462306a36Sopenharmony_ci	/* speed up the ptp clock to account for nanoseconds lost */
21562306a36Sopenharmony_ci	comp += adj;
21662306a36Sopenharmony_ci	return comp;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cicalc_adj_comp:
21962306a36Sopenharmony_ci	/* slow down the ptp clock to not rollover early */
22062306a36Sopenharmony_ci	adj = comp * cycle_time;
22162306a36Sopenharmony_ci	adj = adj / 1000000000ULL;
22262306a36Sopenharmony_ci	adj = adj / CYCLE_MULT;
22362306a36Sopenharmony_ci	comp -= adj;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	return comp;
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_cistruct ptp *ptp_get(void)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	struct ptp *ptp = first_ptp_block;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	/* Check PTP block is present in hardware */
23362306a36Sopenharmony_ci	if (!pci_dev_present(ptp_id_table))
23462306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
23562306a36Sopenharmony_ci	/* Check driver is bound to PTP block */
23662306a36Sopenharmony_ci	if (!ptp)
23762306a36Sopenharmony_ci		ptp = ERR_PTR(-EPROBE_DEFER);
23862306a36Sopenharmony_ci	else if (!IS_ERR(ptp))
23962306a36Sopenharmony_ci		pci_dev_get(ptp->pdev);
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	return ptp;
24262306a36Sopenharmony_ci}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_civoid ptp_put(struct ptp *ptp)
24562306a36Sopenharmony_ci{
24662306a36Sopenharmony_ci	if (!ptp)
24762306a36Sopenharmony_ci		return;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	pci_dev_put(ptp->pdev);
25062306a36Sopenharmony_ci}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cistatic void ptp_atomic_update(struct ptp *ptp, u64 timestamp)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	u64 regval, curr_rollover_set, nxt_rollover_set;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	/* First setup NSECs and SECs */
25762306a36Sopenharmony_ci	writeq(timestamp, ptp->reg_base + PTP_NANO_TIMESTAMP);
25862306a36Sopenharmony_ci	writeq(0, ptp->reg_base + PTP_FRNS_TIMESTAMP);
25962306a36Sopenharmony_ci	writeq(timestamp / NSEC_PER_SEC,
26062306a36Sopenharmony_ci	       ptp->reg_base + PTP_SEC_TIMESTAMP);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	nxt_rollover_set = roundup(timestamp, NSEC_PER_SEC);
26362306a36Sopenharmony_ci	curr_rollover_set = nxt_rollover_set - NSEC_PER_SEC;
26462306a36Sopenharmony_ci	writeq(nxt_rollover_set, ptp->reg_base + PTP_NXT_ROLLOVER_SET);
26562306a36Sopenharmony_ci	writeq(curr_rollover_set, ptp->reg_base + PTP_CURR_ROLLOVER_SET);
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	/* Now, initiate atomic update */
26862306a36Sopenharmony_ci	regval = readq(ptp->reg_base + PTP_CLOCK_CFG);
26962306a36Sopenharmony_ci	regval &= ~PTP_CLOCK_CFG_ATOMIC_OP_MASK;
27062306a36Sopenharmony_ci	regval |= (ATOMIC_SET << 26);
27162306a36Sopenharmony_ci	writeq(regval, ptp->reg_base + PTP_CLOCK_CFG);
27262306a36Sopenharmony_ci}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_cistatic void ptp_atomic_adjtime(struct ptp *ptp, s64 delta)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	bool neg_adj = false, atomic_inc_dec = false;
27762306a36Sopenharmony_ci	u64 regval, ptp_clock_hi;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	if (delta < 0) {
28062306a36Sopenharmony_ci		delta = -delta;
28162306a36Sopenharmony_ci		neg_adj = true;
28262306a36Sopenharmony_ci	}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	/* use atomic inc/dec when delta < 1 second */
28562306a36Sopenharmony_ci	if (delta < NSEC_PER_SEC)
28662306a36Sopenharmony_ci		atomic_inc_dec = true;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	if (!atomic_inc_dec) {
28962306a36Sopenharmony_ci		ptp_clock_hi = readq(ptp->reg_base + PTP_CLOCK_HI);
29062306a36Sopenharmony_ci		if (neg_adj) {
29162306a36Sopenharmony_ci			if (ptp_clock_hi > delta)
29262306a36Sopenharmony_ci				ptp_clock_hi -= delta;
29362306a36Sopenharmony_ci			else
29462306a36Sopenharmony_ci				ptp_clock_hi = delta - ptp_clock_hi;
29562306a36Sopenharmony_ci		} else {
29662306a36Sopenharmony_ci			ptp_clock_hi += delta;
29762306a36Sopenharmony_ci		}
29862306a36Sopenharmony_ci		ptp_atomic_update(ptp, ptp_clock_hi);
29962306a36Sopenharmony_ci	} else {
30062306a36Sopenharmony_ci		writeq(delta, ptp->reg_base + PTP_NANO_TIMESTAMP);
30162306a36Sopenharmony_ci		writeq(0, ptp->reg_base + PTP_FRNS_TIMESTAMP);
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci		/* initiate atomic inc/dec */
30462306a36Sopenharmony_ci		regval = readq(ptp->reg_base + PTP_CLOCK_CFG);
30562306a36Sopenharmony_ci		regval &= ~PTP_CLOCK_CFG_ATOMIC_OP_MASK;
30662306a36Sopenharmony_ci		regval |= neg_adj ? (ATOMIC_DEC << 26) : (ATOMIC_INC << 26);
30762306a36Sopenharmony_ci		writeq(regval, ptp->reg_base + PTP_CLOCK_CFG);
30862306a36Sopenharmony_ci	}
30962306a36Sopenharmony_ci}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_cistatic int ptp_adjfine(struct ptp *ptp, long scaled_ppm)
31262306a36Sopenharmony_ci{
31362306a36Sopenharmony_ci	bool neg_adj = false;
31462306a36Sopenharmony_ci	u32 freq, freq_adj;
31562306a36Sopenharmony_ci	u64 comp, adj;
31662306a36Sopenharmony_ci	s64 ppb;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	if (scaled_ppm < 0) {
31962306a36Sopenharmony_ci		neg_adj = true;
32062306a36Sopenharmony_ci		scaled_ppm = -scaled_ppm;
32162306a36Sopenharmony_ci	}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	/* The hardware adds the clock compensation value to the PTP clock
32462306a36Sopenharmony_ci	 * on every coprocessor clock cycle. Typical convention is that it
32562306a36Sopenharmony_ci	 * represent number of nanosecond betwen each cycle. In this
32662306a36Sopenharmony_ci	 * convention compensation value is in 64 bit fixed-point
32762306a36Sopenharmony_ci	 * representation where upper 32 bits are number of nanoseconds
32862306a36Sopenharmony_ci	 * and lower is fractions of nanosecond.
32962306a36Sopenharmony_ci	 * The scaled_ppm represent the ratio in "parts per million" by which
33062306a36Sopenharmony_ci	 * the compensation value should be corrected.
33162306a36Sopenharmony_ci	 * To calculate new compenstation value we use 64bit fixed point
33262306a36Sopenharmony_ci	 * arithmetic on following formula
33362306a36Sopenharmony_ci	 * comp = tbase + tbase * scaled_ppm / (1M * 2^16)
33462306a36Sopenharmony_ci	 * where tbase is the basic compensation value calculated
33562306a36Sopenharmony_ci	 * initialy in the probe function.
33662306a36Sopenharmony_ci	 */
33762306a36Sopenharmony_ci	/* convert scaled_ppm to ppb */
33862306a36Sopenharmony_ci	ppb = 1 + scaled_ppm;
33962306a36Sopenharmony_ci	ppb *= 125;
34062306a36Sopenharmony_ci	ppb >>= 13;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	if (cn10k_ptp_errata(ptp)) {
34362306a36Sopenharmony_ci		/* calculate the new frequency based on ppb */
34462306a36Sopenharmony_ci		freq_adj = (ptp->clock_rate * ppb) / 1000000000ULL;
34562306a36Sopenharmony_ci		freq = neg_adj ? ptp->clock_rate + freq_adj : ptp->clock_rate - freq_adj;
34662306a36Sopenharmony_ci		comp = ptp_calc_adjusted_comp(freq);
34762306a36Sopenharmony_ci	} else {
34862306a36Sopenharmony_ci		comp = ((u64)1000000000ull << 32) / ptp->clock_rate;
34962306a36Sopenharmony_ci		adj = comp * ppb;
35062306a36Sopenharmony_ci		adj = div_u64(adj, 1000000000ull);
35162306a36Sopenharmony_ci		comp = neg_adj ? comp - adj : comp + adj;
35262306a36Sopenharmony_ci	}
35362306a36Sopenharmony_ci	writeq(comp, ptp->reg_base + PTP_CLOCK_COMP);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	return 0;
35662306a36Sopenharmony_ci}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_cistatic int ptp_get_clock(struct ptp *ptp, u64 *clk)
35962306a36Sopenharmony_ci{
36062306a36Sopenharmony_ci	/* Return the current PTP clock */
36162306a36Sopenharmony_ci	*clk = ptp->read_ptp_tstmp(ptp);
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	return 0;
36462306a36Sopenharmony_ci}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_civoid ptp_start(struct rvu *rvu, u64 sclk, u32 ext_clk_freq, u32 extts)
36762306a36Sopenharmony_ci{
36862306a36Sopenharmony_ci	struct ptp *ptp = rvu->ptp;
36962306a36Sopenharmony_ci	struct pci_dev *pdev;
37062306a36Sopenharmony_ci	u64 clock_comp;
37162306a36Sopenharmony_ci	u64 clock_cfg;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	if (!ptp)
37462306a36Sopenharmony_ci		return;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	pdev = ptp->pdev;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	if (!sclk) {
37962306a36Sopenharmony_ci		dev_err(&pdev->dev, "PTP input clock cannot be zero\n");
38062306a36Sopenharmony_ci		return;
38162306a36Sopenharmony_ci	}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	/* sclk is in MHz */
38462306a36Sopenharmony_ci	ptp->clock_rate = sclk * 1000000;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	/* Program the seconds rollover value to 1 second */
38762306a36Sopenharmony_ci	if (is_tstmp_atomic_update_supported(rvu)) {
38862306a36Sopenharmony_ci		writeq(0, ptp->reg_base + PTP_NANO_TIMESTAMP);
38962306a36Sopenharmony_ci		writeq(0, ptp->reg_base + PTP_FRNS_TIMESTAMP);
39062306a36Sopenharmony_ci		writeq(0, ptp->reg_base + PTP_SEC_TIMESTAMP);
39162306a36Sopenharmony_ci		writeq(0, ptp->reg_base + PTP_CURR_ROLLOVER_SET);
39262306a36Sopenharmony_ci		writeq(0x3b9aca00, ptp->reg_base + PTP_NXT_ROLLOVER_SET);
39362306a36Sopenharmony_ci		writeq(0x3b9aca00, ptp->reg_base + PTP_SEC_ROLLOVER);
39462306a36Sopenharmony_ci	}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	/* Enable PTP clock */
39762306a36Sopenharmony_ci	clock_cfg = readq(ptp->reg_base + PTP_CLOCK_CFG);
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	if (ext_clk_freq) {
40062306a36Sopenharmony_ci		ptp->clock_rate = ext_clk_freq;
40162306a36Sopenharmony_ci		/* Set GPIO as PTP clock source */
40262306a36Sopenharmony_ci		clock_cfg &= ~PTP_CLOCK_CFG_EXT_CLK_IN_MASK;
40362306a36Sopenharmony_ci		clock_cfg |= PTP_CLOCK_CFG_EXT_CLK_EN;
40462306a36Sopenharmony_ci	}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	if (extts) {
40762306a36Sopenharmony_ci		clock_cfg |= PTP_CLOCK_CFG_TSTMP_EDGE;
40862306a36Sopenharmony_ci		/* Set GPIO as timestamping source */
40962306a36Sopenharmony_ci		clock_cfg &= ~PTP_CLOCK_CFG_TSTMP_IN_MASK;
41062306a36Sopenharmony_ci		clock_cfg |= PTP_CLOCK_CFG_TSTMP_EN;
41162306a36Sopenharmony_ci	}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	clock_cfg |= PTP_CLOCK_CFG_PTP_EN;
41462306a36Sopenharmony_ci	clock_cfg |= PTP_CLOCK_CFG_PPS_EN | PTP_CLOCK_CFG_PPS_INV;
41562306a36Sopenharmony_ci	writeq(clock_cfg, ptp->reg_base + PTP_CLOCK_CFG);
41662306a36Sopenharmony_ci	clock_cfg = readq(ptp->reg_base + PTP_CLOCK_CFG);
41762306a36Sopenharmony_ci	clock_cfg &= ~PTP_CLOCK_CFG_ATOMIC_OP_MASK;
41862306a36Sopenharmony_ci	clock_cfg |= (ATOMIC_SET << 26);
41962306a36Sopenharmony_ci	writeq(clock_cfg, ptp->reg_base + PTP_CLOCK_CFG);
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	/* Set 50% duty cycle for 1Hz output */
42262306a36Sopenharmony_ci	writeq(0x1dcd650000000000, ptp->reg_base + PTP_PPS_HI_INCR);
42362306a36Sopenharmony_ci	writeq(0x1dcd650000000000, ptp->reg_base + PTP_PPS_LO_INCR);
42462306a36Sopenharmony_ci	if (cn10k_ptp_errata(ptp)) {
42562306a36Sopenharmony_ci		/* The ptp_clock_hi rollsover to zero once clock cycle before it
42662306a36Sopenharmony_ci		 * reaches one second boundary. so, program the pps_lo_incr in
42762306a36Sopenharmony_ci		 * such a way that the pps threshold value comparison at one
42862306a36Sopenharmony_ci		 * second boundary will succeed and pps edge changes. After each
42962306a36Sopenharmony_ci		 * one second boundary, the hrtimer handler will be invoked and
43062306a36Sopenharmony_ci		 * reprograms the pps threshold value.
43162306a36Sopenharmony_ci		 */
43262306a36Sopenharmony_ci		ptp->clock_period = NSEC_PER_SEC / ptp->clock_rate;
43362306a36Sopenharmony_ci		writeq((0x1dcd6500ULL - ptp->clock_period) << 32,
43462306a36Sopenharmony_ci		       ptp->reg_base + PTP_PPS_LO_INCR);
43562306a36Sopenharmony_ci	}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	if (cn10k_ptp_errata(ptp))
43862306a36Sopenharmony_ci		clock_comp = ptp_calc_adjusted_comp(ptp->clock_rate);
43962306a36Sopenharmony_ci	else
44062306a36Sopenharmony_ci		clock_comp = ((u64)1000000000ull << 32) / ptp->clock_rate;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	/* Initial compensation value to start the nanosecs counter */
44362306a36Sopenharmony_ci	writeq(clock_comp, ptp->reg_base + PTP_CLOCK_COMP);
44462306a36Sopenharmony_ci}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_cistatic int ptp_get_tstmp(struct ptp *ptp, u64 *clk)
44762306a36Sopenharmony_ci{
44862306a36Sopenharmony_ci	u64 timestamp;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	if (is_ptp_dev_cn10ka(ptp) || is_ptp_dev_cnf10ka(ptp)) {
45162306a36Sopenharmony_ci		timestamp = readq(ptp->reg_base + PTP_TIMESTAMP);
45262306a36Sopenharmony_ci		*clk = (timestamp >> 32) * NSEC_PER_SEC + (timestamp & 0xFFFFFFFF);
45362306a36Sopenharmony_ci	} else {
45462306a36Sopenharmony_ci		*clk = readq(ptp->reg_base + PTP_TIMESTAMP);
45562306a36Sopenharmony_ci	}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	return 0;
45862306a36Sopenharmony_ci}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_cistatic int ptp_set_thresh(struct ptp *ptp, u64 thresh)
46162306a36Sopenharmony_ci{
46262306a36Sopenharmony_ci	if (!cn10k_ptp_errata(ptp))
46362306a36Sopenharmony_ci		writeq(thresh, ptp->reg_base + PTP_PPS_THRESH_HI);
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	return 0;
46662306a36Sopenharmony_ci}
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_cistatic int ptp_extts_on(struct ptp *ptp, int on)
46962306a36Sopenharmony_ci{
47062306a36Sopenharmony_ci	u64 ptp_clock_hi;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	if (cn10k_ptp_errata(ptp)) {
47362306a36Sopenharmony_ci		if (on) {
47462306a36Sopenharmony_ci			ptp_clock_hi = readq(ptp->reg_base + PTP_CLOCK_HI);
47562306a36Sopenharmony_ci			ptp_hrtimer_start(ptp, (ktime_t)ptp_clock_hi);
47662306a36Sopenharmony_ci		} else {
47762306a36Sopenharmony_ci			if (hrtimer_active(&ptp->hrtimer))
47862306a36Sopenharmony_ci				hrtimer_cancel(&ptp->hrtimer);
47962306a36Sopenharmony_ci		}
48062306a36Sopenharmony_ci	}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	return 0;
48362306a36Sopenharmony_ci}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_cistatic int ptp_probe(struct pci_dev *pdev,
48662306a36Sopenharmony_ci		     const struct pci_device_id *ent)
48762306a36Sopenharmony_ci{
48862306a36Sopenharmony_ci	struct ptp *ptp;
48962306a36Sopenharmony_ci	int err;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	ptp = kzalloc(sizeof(*ptp), GFP_KERNEL);
49262306a36Sopenharmony_ci	if (!ptp) {
49362306a36Sopenharmony_ci		err = -ENOMEM;
49462306a36Sopenharmony_ci		goto error;
49562306a36Sopenharmony_ci	}
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	ptp->pdev = pdev;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	err = pcim_enable_device(pdev);
50062306a36Sopenharmony_ci	if (err)
50162306a36Sopenharmony_ci		goto error_free;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	err = pcim_iomap_regions(pdev, 1 << PCI_PTP_BAR_NO, pci_name(pdev));
50462306a36Sopenharmony_ci	if (err)
50562306a36Sopenharmony_ci		goto error_free;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	ptp->reg_base = pcim_iomap_table(pdev)[PCI_PTP_BAR_NO];
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	pci_set_drvdata(pdev, ptp);
51062306a36Sopenharmony_ci	if (!first_ptp_block)
51162306a36Sopenharmony_ci		first_ptp_block = ptp;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	spin_lock_init(&ptp->ptp_lock);
51462306a36Sopenharmony_ci	if (cn10k_ptp_errata(ptp)) {
51562306a36Sopenharmony_ci		ptp->read_ptp_tstmp = &read_ptp_tstmp_sec_nsec;
51662306a36Sopenharmony_ci		hrtimer_init(&ptp->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
51762306a36Sopenharmony_ci		ptp->hrtimer.function = ptp_reset_thresh;
51862306a36Sopenharmony_ci	} else {
51962306a36Sopenharmony_ci		ptp->read_ptp_tstmp = &read_ptp_tstmp_nsec;
52062306a36Sopenharmony_ci	}
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	return 0;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_cierror_free:
52562306a36Sopenharmony_ci	kfree(ptp);
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_cierror:
52862306a36Sopenharmony_ci	/* For `ptp_get()` we need to differentiate between the case
52962306a36Sopenharmony_ci	 * when the core has not tried to probe this device and the case when
53062306a36Sopenharmony_ci	 * the probe failed.  In the later case we keep the error in
53162306a36Sopenharmony_ci	 * `dev->driver_data`.
53262306a36Sopenharmony_ci	 */
53362306a36Sopenharmony_ci	pci_set_drvdata(pdev, ERR_PTR(err));
53462306a36Sopenharmony_ci	if (!first_ptp_block)
53562306a36Sopenharmony_ci		first_ptp_block = ERR_PTR(err);
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	return err;
53862306a36Sopenharmony_ci}
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_cistatic void ptp_remove(struct pci_dev *pdev)
54162306a36Sopenharmony_ci{
54262306a36Sopenharmony_ci	struct ptp *ptp = pci_get_drvdata(pdev);
54362306a36Sopenharmony_ci	u64 clock_cfg;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	if (IS_ERR_OR_NULL(ptp))
54662306a36Sopenharmony_ci		return;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	if (cn10k_ptp_errata(ptp) && hrtimer_active(&ptp->hrtimer))
54962306a36Sopenharmony_ci		hrtimer_cancel(&ptp->hrtimer);
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	/* Disable PTP clock */
55262306a36Sopenharmony_ci	clock_cfg = readq(ptp->reg_base + PTP_CLOCK_CFG);
55362306a36Sopenharmony_ci	clock_cfg &= ~PTP_CLOCK_CFG_PTP_EN;
55462306a36Sopenharmony_ci	writeq(clock_cfg, ptp->reg_base + PTP_CLOCK_CFG);
55562306a36Sopenharmony_ci	kfree(ptp);
55662306a36Sopenharmony_ci}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_cistatic const struct pci_device_id ptp_id_table[] = {
55962306a36Sopenharmony_ci	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_PTP,
56062306a36Sopenharmony_ci			 PCI_VENDOR_ID_CAVIUM,
56162306a36Sopenharmony_ci			 PCI_SUBSYS_DEVID_OCTX2_98xx_PTP) },
56262306a36Sopenharmony_ci	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_PTP,
56362306a36Sopenharmony_ci			 PCI_VENDOR_ID_CAVIUM,
56462306a36Sopenharmony_ci			 PCI_SUBSYS_DEVID_OCTX2_96XX_PTP) },
56562306a36Sopenharmony_ci	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_PTP,
56662306a36Sopenharmony_ci			 PCI_VENDOR_ID_CAVIUM,
56762306a36Sopenharmony_ci			 PCI_SUBSYS_DEVID_OCTX2_95XX_PTP) },
56862306a36Sopenharmony_ci	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_PTP,
56962306a36Sopenharmony_ci			 PCI_VENDOR_ID_CAVIUM,
57062306a36Sopenharmony_ci			 PCI_SUBSYS_DEVID_OCTX2_95XXN_PTP) },
57162306a36Sopenharmony_ci	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_PTP,
57262306a36Sopenharmony_ci			 PCI_VENDOR_ID_CAVIUM,
57362306a36Sopenharmony_ci			 PCI_SUBSYS_DEVID_OCTX2_95MM_PTP) },
57462306a36Sopenharmony_ci	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_PTP,
57562306a36Sopenharmony_ci			 PCI_VENDOR_ID_CAVIUM,
57662306a36Sopenharmony_ci			 PCI_SUBSYS_DEVID_OCTX2_95XXO_PTP) },
57762306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_CN10K_PTP) },
57862306a36Sopenharmony_ci	{ 0, }
57962306a36Sopenharmony_ci};
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_cistruct pci_driver ptp_driver = {
58262306a36Sopenharmony_ci	.name = DRV_NAME,
58362306a36Sopenharmony_ci	.id_table = ptp_id_table,
58462306a36Sopenharmony_ci	.probe = ptp_probe,
58562306a36Sopenharmony_ci	.remove = ptp_remove,
58662306a36Sopenharmony_ci};
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ciint rvu_mbox_handler_ptp_op(struct rvu *rvu, struct ptp_req *req,
58962306a36Sopenharmony_ci			    struct ptp_rsp *rsp)
59062306a36Sopenharmony_ci{
59162306a36Sopenharmony_ci	int err = 0;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	/* This function is the PTP mailbox handler invoked when
59462306a36Sopenharmony_ci	 * called by AF consumers/netdev drivers via mailbox mechanism.
59562306a36Sopenharmony_ci	 * It is used by netdev driver to get the PTP clock and to set
59662306a36Sopenharmony_ci	 * frequency adjustments. Since mailbox can be called without
59762306a36Sopenharmony_ci	 * notion of whether the driver is bound to ptp device below
59862306a36Sopenharmony_ci	 * validation is needed as first step.
59962306a36Sopenharmony_ci	 */
60062306a36Sopenharmony_ci	if (!rvu->ptp)
60162306a36Sopenharmony_ci		return -ENODEV;
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	switch (req->op) {
60462306a36Sopenharmony_ci	case PTP_OP_ADJFINE:
60562306a36Sopenharmony_ci		err = ptp_adjfine(rvu->ptp, req->scaled_ppm);
60662306a36Sopenharmony_ci		break;
60762306a36Sopenharmony_ci	case PTP_OP_GET_CLOCK:
60862306a36Sopenharmony_ci		err = ptp_get_clock(rvu->ptp, &rsp->clk);
60962306a36Sopenharmony_ci		break;
61062306a36Sopenharmony_ci	case PTP_OP_GET_TSTMP:
61162306a36Sopenharmony_ci		err = ptp_get_tstmp(rvu->ptp, &rsp->clk);
61262306a36Sopenharmony_ci		break;
61362306a36Sopenharmony_ci	case PTP_OP_SET_THRESH:
61462306a36Sopenharmony_ci		err = ptp_set_thresh(rvu->ptp, req->thresh);
61562306a36Sopenharmony_ci		break;
61662306a36Sopenharmony_ci	case PTP_OP_EXTTS_ON:
61762306a36Sopenharmony_ci		err = ptp_extts_on(rvu->ptp, req->extts_on);
61862306a36Sopenharmony_ci		break;
61962306a36Sopenharmony_ci	case PTP_OP_ADJTIME:
62062306a36Sopenharmony_ci		ptp_atomic_adjtime(rvu->ptp, req->delta);
62162306a36Sopenharmony_ci		break;
62262306a36Sopenharmony_ci	case PTP_OP_SET_CLOCK:
62362306a36Sopenharmony_ci		ptp_atomic_update(rvu->ptp, (u64)req->clk);
62462306a36Sopenharmony_ci		break;
62562306a36Sopenharmony_ci	default:
62662306a36Sopenharmony_ci		err = -EINVAL;
62762306a36Sopenharmony_ci		break;
62862306a36Sopenharmony_ci	}
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	return err;
63162306a36Sopenharmony_ci}
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ciint rvu_mbox_handler_ptp_get_cap(struct rvu *rvu, struct msg_req *req,
63462306a36Sopenharmony_ci				 struct ptp_get_cap_rsp *rsp)
63562306a36Sopenharmony_ci{
63662306a36Sopenharmony_ci	if (!rvu->ptp)
63762306a36Sopenharmony_ci		return -ENODEV;
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	if (is_tstmp_atomic_update_supported(rvu))
64062306a36Sopenharmony_ci		rsp->cap |= PTP_CAP_HW_ATOMIC_UPDATE;
64162306a36Sopenharmony_ci	else
64262306a36Sopenharmony_ci		rsp->cap &= ~BIT_ULL_MASK(0);
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	return 0;
64562306a36Sopenharmony_ci}
646