brintos

brintos / linux-shallow public Read only

0
0
Text · 36.1 KiB · 59d6ab9 Raw
1333 lines · c
1// SPDX-License-Identifier: GPL-2.02/* TI K3 AM65x Common Platform Time Sync3 *4 * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com5 *6 */7 8#include <linux/clk.h>9#include <linux/clk-provider.h>10#include <linux/err.h>11#include <linux/if_vlan.h>12#include <linux/interrupt.h>13#include <linux/module.h>14#include <linux/netdevice.h>15#include <linux/net_tstamp.h>16#include <linux/of.h>17#include <linux/of_irq.h>18#include <linux/platform_device.h>19#include <linux/pm_runtime.h>20#include <linux/ptp_classify.h>21#include <linux/ptp_clock_kernel.h>22 23#include "am65-cpts.h"24 25struct am65_genf_regs {26	u32 comp_lo;	/* Comparison Low Value 0:31 */27	u32 comp_hi;	/* Comparison High Value 32:63 */28	u32 control;	/* control */29	u32 length;	/* Length */30	u32 ppm_low;	/* PPM Load Low Value 0:31 */31	u32 ppm_hi;	/* PPM Load High Value 32:63 */32	u32 ts_nudge;	/* Nudge value */33} __aligned(32) __packed;34 35#define AM65_CPTS_GENF_MAX_NUM 936#define AM65_CPTS_ESTF_MAX_NUM 837 38struct am65_cpts_regs {39	u32 idver;		/* Identification and version */40	u32 control;		/* Time sync control */41	u32 rftclk_sel;		/* Reference Clock Select Register */42	u32 ts_push;		/* Time stamp event push */43	u32 ts_load_val_lo;	/* Time Stamp Load Low Value 0:31 */44	u32 ts_load_en;		/* Time stamp load enable */45	u32 ts_comp_lo;		/* Time Stamp Comparison Low Value 0:31 */46	u32 ts_comp_length;	/* Time Stamp Comparison Length */47	u32 intstat_raw;	/* Time sync interrupt status raw */48	u32 intstat_masked;	/* Time sync interrupt status masked */49	u32 int_enable;		/* Time sync interrupt enable */50	u32 ts_comp_nudge;	/* Time Stamp Comparison Nudge Value */51	u32 event_pop;		/* Event interrupt pop */52	u32 event_0;		/* Event Time Stamp lo 0:31 */53	u32 event_1;		/* Event Type Fields */54	u32 event_2;		/* Event Type Fields domain */55	u32 event_3;		/* Event Time Stamp hi 32:63 */56	u32 ts_load_val_hi;	/* Time Stamp Load High Value 32:63 */57	u32 ts_comp_hi;		/* Time Stamp Comparison High Value 32:63 */58	u32 ts_add_val;		/* Time Stamp Add value */59	u32 ts_ppm_low;		/* Time Stamp PPM Load Low Value 0:31 */60	u32 ts_ppm_hi;		/* Time Stamp PPM Load High Value 32:63 */61	u32 ts_nudge;		/* Time Stamp Nudge value */62	u32 reserv[33];63	struct am65_genf_regs genf[AM65_CPTS_GENF_MAX_NUM];64	struct am65_genf_regs estf[AM65_CPTS_ESTF_MAX_NUM];65};66 67/* CONTROL_REG */68#define AM65_CPTS_CONTROL_EN			BIT(0)69#define AM65_CPTS_CONTROL_INT_TEST		BIT(1)70#define AM65_CPTS_CONTROL_TS_COMP_POLARITY	BIT(2)71#define AM65_CPTS_CONTROL_TSTAMP_EN		BIT(3)72#define AM65_CPTS_CONTROL_SEQUENCE_EN		BIT(4)73#define AM65_CPTS_CONTROL_64MODE		BIT(5)74#define AM65_CPTS_CONTROL_TS_COMP_TOG		BIT(6)75#define AM65_CPTS_CONTROL_TS_PPM_DIR		BIT(7)76#define AM65_CPTS_CONTROL_HW1_TS_PUSH_EN	BIT(8)77#define AM65_CPTS_CONTROL_HW2_TS_PUSH_EN	BIT(9)78#define AM65_CPTS_CONTROL_HW3_TS_PUSH_EN	BIT(10)79#define AM65_CPTS_CONTROL_HW4_TS_PUSH_EN	BIT(11)80#define AM65_CPTS_CONTROL_HW5_TS_PUSH_EN	BIT(12)81#define AM65_CPTS_CONTROL_HW6_TS_PUSH_EN	BIT(13)82#define AM65_CPTS_CONTROL_HW7_TS_PUSH_EN	BIT(14)83#define AM65_CPTS_CONTROL_HW8_TS_PUSH_EN	BIT(15)84#define AM65_CPTS_CONTROL_HW1_TS_PUSH_OFFSET	(8)85 86#define AM65_CPTS_CONTROL_TX_GENF_CLR_EN	BIT(17)87 88#define AM65_CPTS_CONTROL_TS_SYNC_SEL_MASK	(0xF)89#define AM65_CPTS_CONTROL_TS_SYNC_SEL_SHIFT	(28)90 91/* RFTCLK_SEL_REG */92#define AM65_CPTS_RFTCLK_SEL_MASK		(0x1F)93 94/* TS_PUSH_REG */95#define AM65_CPTS_TS_PUSH			BIT(0)96 97/* TS_LOAD_EN_REG */98#define AM65_CPTS_TS_LOAD_EN			BIT(0)99 100/* INTSTAT_RAW_REG */101#define AM65_CPTS_INTSTAT_RAW_TS_PEND		BIT(0)102 103/* INTSTAT_MASKED_REG */104#define AM65_CPTS_INTSTAT_MASKED_TS_PEND	BIT(0)105 106/* INT_ENABLE_REG */107#define AM65_CPTS_INT_ENABLE_TS_PEND_EN		BIT(0)108 109/* TS_COMP_NUDGE_REG */110#define AM65_CPTS_TS_COMP_NUDGE_MASK		(0xFF)111 112/* EVENT_POP_REG */113#define AM65_CPTS_EVENT_POP			BIT(0)114 115/* EVENT_1_REG */116#define AM65_CPTS_EVENT_1_SEQUENCE_ID_MASK	GENMASK(15, 0)117 118#define AM65_CPTS_EVENT_1_MESSAGE_TYPE_MASK	GENMASK(19, 16)119#define AM65_CPTS_EVENT_1_MESSAGE_TYPE_SHIFT	(16)120 121#define AM65_CPTS_EVENT_1_EVENT_TYPE_MASK	GENMASK(23, 20)122#define AM65_CPTS_EVENT_1_EVENT_TYPE_SHIFT	(20)123 124#define AM65_CPTS_EVENT_1_PORT_NUMBER_MASK	GENMASK(28, 24)125#define AM65_CPTS_EVENT_1_PORT_NUMBER_SHIFT	(24)126 127/* EVENT_2_REG */128#define AM65_CPTS_EVENT_2_REG_DOMAIN_MASK	(0xFF)129#define AM65_CPTS_EVENT_2_REG_DOMAIN_SHIFT	(0)130 131enum {132	AM65_CPTS_EV_PUSH,	/* Time Stamp Push Event */133	AM65_CPTS_EV_ROLL,	/* Time Stamp Rollover Event */134	AM65_CPTS_EV_HALF,	/* Time Stamp Half Rollover Event */135	AM65_CPTS_EV_HW,		/* Hardware Time Stamp Push Event */136	AM65_CPTS_EV_RX,		/* Ethernet Receive Event */137	AM65_CPTS_EV_TX,		/* Ethernet Transmit Event */138	AM65_CPTS_EV_TS_COMP,	/* Time Stamp Compare Event */139	AM65_CPTS_EV_HOST,	/* Host Transmit Event */140};141 142struct am65_cpts_event {143	struct list_head list;144	unsigned long tmo;145	u32 event1;146	u32 event2;147	u64 timestamp;148};149 150#define AM65_CPTS_FIFO_DEPTH		(16)151#define AM65_CPTS_MAX_EVENTS		(32)152#define AM65_CPTS_EVENT_RX_TX_TIMEOUT	(20) /* ms */153#define AM65_CPTS_SKB_TX_WORK_TIMEOUT	1 /* jiffies */154#define AM65_CPTS_MIN_PPM		0x400155 156struct am65_cpts {157	struct device *dev;158	struct am65_cpts_regs __iomem *reg;159	struct ptp_clock_info ptp_info;160	struct ptp_clock *ptp_clock;161	int phc_index;162	struct clk_hw *clk_mux_hw;163	struct device_node *clk_mux_np;164	struct clk *refclk;165	u32 refclk_freq;166	struct list_head events;167	struct list_head pool;168	struct am65_cpts_event pool_data[AM65_CPTS_MAX_EVENTS];169	spinlock_t lock; /* protects events lists*/170	u32 ext_ts_inputs;171	u32 genf_num;172	u32 ts_add_val;173	int irq;174	struct mutex ptp_clk_lock; /* PHC access sync */175	u64 timestamp;176	u32 genf_enable;177	u32 hw_ts_enable;178	u32 estf_enable;179	struct sk_buff_head txq;180	bool pps_enabled;181	bool pps_present;182	u32 pps_hw_ts_idx;183	u32 pps_genf_idx;184	/* context save/restore */185	u64 sr_cpts_ns;186	u64 sr_ktime_ns;187	u32 sr_control;188	u32 sr_int_enable;189	u32 sr_rftclk_sel;190	u32 sr_ts_ppm_hi;191	u32 sr_ts_ppm_low;192	struct am65_genf_regs sr_genf[AM65_CPTS_GENF_MAX_NUM];193	struct am65_genf_regs sr_estf[AM65_CPTS_ESTF_MAX_NUM];194};195 196struct am65_cpts_skb_cb_data {197	unsigned long tmo;198	u32 skb_mtype_seqid;199};200 201#define am65_cpts_write32(c, v, r) writel(v, &(c)->reg->r)202#define am65_cpts_read32(c, r) readl(&(c)->reg->r)203 204static void am65_cpts_settime(struct am65_cpts *cpts, u64 start_tstamp)205{206	u32 val;207 208	val = upper_32_bits(start_tstamp);209	am65_cpts_write32(cpts, val, ts_load_val_hi);210	val = lower_32_bits(start_tstamp);211	am65_cpts_write32(cpts, val, ts_load_val_lo);212 213	am65_cpts_write32(cpts, AM65_CPTS_TS_LOAD_EN, ts_load_en);214}215 216static void am65_cpts_set_add_val(struct am65_cpts *cpts)217{218	/* select coefficient according to the rate */219	cpts->ts_add_val = (NSEC_PER_SEC / cpts->refclk_freq - 1) & 0x7;220 221	am65_cpts_write32(cpts, cpts->ts_add_val, ts_add_val);222}223 224static void am65_cpts_disable(struct am65_cpts *cpts)225{226	am65_cpts_write32(cpts, 0, control);227	am65_cpts_write32(cpts, 0, int_enable);228}229 230static int am65_cpts_event_get_port(struct am65_cpts_event *event)231{232	return (event->event1 & AM65_CPTS_EVENT_1_PORT_NUMBER_MASK) >>233		AM65_CPTS_EVENT_1_PORT_NUMBER_SHIFT;234}235 236static int am65_cpts_event_get_type(struct am65_cpts_event *event)237{238	return (event->event1 & AM65_CPTS_EVENT_1_EVENT_TYPE_MASK) >>239		AM65_CPTS_EVENT_1_EVENT_TYPE_SHIFT;240}241 242static int am65_cpts_cpts_purge_events(struct am65_cpts *cpts)243{244	struct list_head *this, *next;245	struct am65_cpts_event *event;246	int removed = 0;247 248	list_for_each_safe(this, next, &cpts->events) {249		event = list_entry(this, struct am65_cpts_event, list);250		if (time_after(jiffies, event->tmo)) {251			list_del_init(&event->list);252			list_add(&event->list, &cpts->pool);253			++removed;254		}255	}256 257	if (removed)258		dev_dbg(cpts->dev, "event pool cleaned up %d\n", removed);259	return removed ? 0 : -1;260}261 262static bool am65_cpts_fifo_pop_event(struct am65_cpts *cpts,263				     struct am65_cpts_event *event)264{265	u32 r = am65_cpts_read32(cpts, intstat_raw);266 267	if (r & AM65_CPTS_INTSTAT_RAW_TS_PEND) {268		event->timestamp = am65_cpts_read32(cpts, event_0);269		event->event1 = am65_cpts_read32(cpts, event_1);270		event->event2 = am65_cpts_read32(cpts, event_2);271		event->timestamp |= (u64)am65_cpts_read32(cpts, event_3) << 32;272		am65_cpts_write32(cpts, AM65_CPTS_EVENT_POP, event_pop);273		return false;274	}275	return true;276}277 278static int __am65_cpts_fifo_read(struct am65_cpts *cpts)279{280	struct ptp_clock_event pevent;281	struct am65_cpts_event *event;282	bool schedule = false;283	int i, type, ret = 0;284 285	for (i = 0; i < AM65_CPTS_FIFO_DEPTH; i++) {286		event = list_first_entry_or_null(&cpts->pool,287						 struct am65_cpts_event, list);288 289		if (!event) {290			if (am65_cpts_cpts_purge_events(cpts)) {291				dev_err(cpts->dev, "cpts: event pool empty\n");292				ret = -1;293				goto out;294			}295			continue;296		}297 298		if (am65_cpts_fifo_pop_event(cpts, event))299			break;300 301		type = am65_cpts_event_get_type(event);302		switch (type) {303		case AM65_CPTS_EV_PUSH:304			cpts->timestamp = event->timestamp;305			dev_dbg(cpts->dev, "AM65_CPTS_EV_PUSH t:%llu\n",306				cpts->timestamp);307			break;308		case AM65_CPTS_EV_RX:309		case AM65_CPTS_EV_TX:310			event->tmo = jiffies +311				msecs_to_jiffies(AM65_CPTS_EVENT_RX_TX_TIMEOUT);312 313			list_move_tail(&event->list, &cpts->events);314 315			dev_dbg(cpts->dev,316				"AM65_CPTS_EV_TX e1:%08x e2:%08x t:%lld\n",317				event->event1, event->event2,318				event->timestamp);319			schedule = true;320			break;321		case AM65_CPTS_EV_HW:322			pevent.index = am65_cpts_event_get_port(event) - 1;323			pevent.timestamp = event->timestamp;324			if (cpts->pps_enabled && pevent.index == cpts->pps_hw_ts_idx) {325				pevent.type = PTP_CLOCK_PPSUSR;326				pevent.pps_times.ts_real = ns_to_timespec64(pevent.timestamp);327			} else {328				pevent.type = PTP_CLOCK_EXTTS;329			}330			dev_dbg(cpts->dev, "AM65_CPTS_EV_HW:%s p:%d t:%llu\n",331				pevent.type == PTP_CLOCK_EXTTS ?332				"extts" : "pps",333				pevent.index, event->timestamp);334 335			ptp_clock_event(cpts->ptp_clock, &pevent);336			break;337		case AM65_CPTS_EV_HOST:338			break;339		case AM65_CPTS_EV_ROLL:340		case AM65_CPTS_EV_HALF:341		case AM65_CPTS_EV_TS_COMP:342			dev_dbg(cpts->dev,343				"AM65_CPTS_EVT: %d e1:%08x e2:%08x t:%lld\n",344				type,345				event->event1, event->event2,346				event->timestamp);347			break;348		default:349			dev_err(cpts->dev, "cpts: unknown event type\n");350			ret = -1;351			goto out;352		}353	}354 355out:356	if (schedule)357		ptp_schedule_worker(cpts->ptp_clock, 0);358 359	return ret;360}361 362static int am65_cpts_fifo_read(struct am65_cpts *cpts)363{364	unsigned long flags;365	int ret = 0;366 367	spin_lock_irqsave(&cpts->lock, flags);368	ret = __am65_cpts_fifo_read(cpts);369	spin_unlock_irqrestore(&cpts->lock, flags);370 371	return ret;372}373 374static u64 am65_cpts_gettime(struct am65_cpts *cpts,375			     struct ptp_system_timestamp *sts)376{377	unsigned long flags;378	u64 val = 0;379 380	/* temporarily disable cpts interrupt to avoid intentional381	 * doubled read. Interrupt can be in-flight - it's Ok.382	 */383	am65_cpts_write32(cpts, 0, int_enable);384 385	/* use spin_lock_irqsave() here as it has to run very fast */386	spin_lock_irqsave(&cpts->lock, flags);387	ptp_read_system_prets(sts);388	am65_cpts_write32(cpts, AM65_CPTS_TS_PUSH, ts_push);389	am65_cpts_read32(cpts, ts_push);390	ptp_read_system_postts(sts);391	spin_unlock_irqrestore(&cpts->lock, flags);392 393	am65_cpts_fifo_read(cpts);394 395	am65_cpts_write32(cpts, AM65_CPTS_INT_ENABLE_TS_PEND_EN, int_enable);396 397	val = cpts->timestamp;398 399	return val;400}401 402static irqreturn_t am65_cpts_interrupt(int irq, void *dev_id)403{404	struct am65_cpts *cpts = dev_id;405 406	if (am65_cpts_fifo_read(cpts))407		dev_dbg(cpts->dev, "cpts: unable to obtain a time stamp\n");408 409	return IRQ_HANDLED;410}411 412/* PTP clock operations */413static int am65_cpts_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)414{415	struct am65_cpts *cpts = container_of(ptp, struct am65_cpts, ptp_info);416	u32 estf_ctrl_val = 0, estf_ppm_hi = 0, estf_ppm_low = 0;417	s32 ppb = scaled_ppm_to_ppb(scaled_ppm);418	int pps_index = cpts->pps_genf_idx;419	u64 adj_period, pps_adj_period;420	u32 ctrl_val, ppm_hi, ppm_low;421	unsigned long flags;422	int neg_adj = 0, i;423 424	if (ppb < 0) {425		neg_adj = 1;426		ppb = -ppb;427	}428 429	/* base freq = 1GHz = 1 000 000 000430	 * ppb_norm = ppb * base_freq / clock_freq;431	 * ppm_norm = ppb_norm / 1000432	 * adj_period = 1 000 000 / ppm_norm433	 * adj_period = 1 000 000 000 / ppb_norm434	 * adj_period = 1 000 000 000 / (ppb * base_freq / clock_freq)435	 * adj_period = (1 000 000 000 * clock_freq) / (ppb * base_freq)436	 * adj_period = clock_freq / ppb437	 */438	adj_period = div_u64(cpts->refclk_freq, ppb);439 440	mutex_lock(&cpts->ptp_clk_lock);441 442	ctrl_val = am65_cpts_read32(cpts, control);443	if (neg_adj)444		ctrl_val |= AM65_CPTS_CONTROL_TS_PPM_DIR;445	else446		ctrl_val &= ~AM65_CPTS_CONTROL_TS_PPM_DIR;447 448	ppm_hi = upper_32_bits(adj_period) & 0x3FF;449	ppm_low = lower_32_bits(adj_period);450 451	if (cpts->pps_enabled) {452		estf_ctrl_val = am65_cpts_read32(cpts, genf[pps_index].control);453		if (neg_adj)454			estf_ctrl_val &= ~BIT(1);455		else456			estf_ctrl_val |= BIT(1);457 458		/* GenF PPM will do correction using cpts refclk tick which is459		 * (cpts->ts_add_val + 1) ns, so GenF length PPM adj period460		 * need to be corrected.461		 */462		pps_adj_period = adj_period * (cpts->ts_add_val + 1);463		estf_ppm_hi = upper_32_bits(pps_adj_period) & 0x3FF;464		estf_ppm_low = lower_32_bits(pps_adj_period);465	}466 467	spin_lock_irqsave(&cpts->lock, flags);468 469	/* All below writes must be done extremely fast:470	 *  - delay between PPM dir and PPM value changes can cause err due old471	 *    PPM correction applied in wrong direction472	 *  - delay between CPTS-clock PPM cfg and GenF PPM cfg can cause err473	 *    due CPTS-clock PPM working with new cfg while GenF PPM cfg still474	 *    with old for short period of time475	 */476 477	am65_cpts_write32(cpts, ctrl_val, control);478	am65_cpts_write32(cpts, ppm_hi, ts_ppm_hi);479	am65_cpts_write32(cpts, ppm_low, ts_ppm_low);480 481	if (cpts->pps_enabled) {482		am65_cpts_write32(cpts, estf_ctrl_val, genf[pps_index].control);483		am65_cpts_write32(cpts, estf_ppm_hi, genf[pps_index].ppm_hi);484		am65_cpts_write32(cpts, estf_ppm_low, genf[pps_index].ppm_low);485	}486 487	for (i = 0; i < AM65_CPTS_ESTF_MAX_NUM; i++) {488		if (cpts->estf_enable & BIT(i)) {489			am65_cpts_write32(cpts, estf_ctrl_val, estf[i].control);490			am65_cpts_write32(cpts, estf_ppm_hi, estf[i].ppm_hi);491			am65_cpts_write32(cpts, estf_ppm_low, estf[i].ppm_low);492		}493	}494	/* All GenF/EstF can be updated here the same way */495	spin_unlock_irqrestore(&cpts->lock, flags);496 497	mutex_unlock(&cpts->ptp_clk_lock);498 499	return 0;500}501 502static int am65_cpts_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)503{504	struct am65_cpts *cpts = container_of(ptp, struct am65_cpts, ptp_info);505	s64 ns;506 507	mutex_lock(&cpts->ptp_clk_lock);508	ns = am65_cpts_gettime(cpts, NULL);509	ns += delta;510	am65_cpts_settime(cpts, ns);511	mutex_unlock(&cpts->ptp_clk_lock);512 513	return 0;514}515 516static int am65_cpts_ptp_gettimex(struct ptp_clock_info *ptp,517				  struct timespec64 *ts,518				  struct ptp_system_timestamp *sts)519{520	struct am65_cpts *cpts = container_of(ptp, struct am65_cpts, ptp_info);521	u64 ns;522 523	mutex_lock(&cpts->ptp_clk_lock);524	ns = am65_cpts_gettime(cpts, sts);525	mutex_unlock(&cpts->ptp_clk_lock);526	*ts = ns_to_timespec64(ns);527 528	return 0;529}530 531u64 am65_cpts_ns_gettime(struct am65_cpts *cpts)532{533	u64 ns;534 535	/* reuse ptp_clk_lock as it serialize ts push */536	mutex_lock(&cpts->ptp_clk_lock);537	ns = am65_cpts_gettime(cpts, NULL);538	mutex_unlock(&cpts->ptp_clk_lock);539 540	return ns;541}542EXPORT_SYMBOL_GPL(am65_cpts_ns_gettime);543 544static int am65_cpts_ptp_settime(struct ptp_clock_info *ptp,545				 const struct timespec64 *ts)546{547	struct am65_cpts *cpts = container_of(ptp, struct am65_cpts, ptp_info);548	u64 ns;549 550	ns = timespec64_to_ns(ts);551	mutex_lock(&cpts->ptp_clk_lock);552	am65_cpts_settime(cpts, ns);553	mutex_unlock(&cpts->ptp_clk_lock);554 555	return 0;556}557 558static void am65_cpts_extts_enable_hw(struct am65_cpts *cpts, u32 index, int on)559{560	u32 v;561 562	v = am65_cpts_read32(cpts, control);563	if (on) {564		v |= BIT(AM65_CPTS_CONTROL_HW1_TS_PUSH_OFFSET + index);565		cpts->hw_ts_enable |= BIT(index);566	} else {567		v &= ~BIT(AM65_CPTS_CONTROL_HW1_TS_PUSH_OFFSET + index);568		cpts->hw_ts_enable &= ~BIT(index);569	}570	am65_cpts_write32(cpts, v, control);571}572 573static int am65_cpts_extts_enable(struct am65_cpts *cpts, u32 index, int on)574{575	if (index >= cpts->ptp_info.n_ext_ts)576		return -ENXIO;577 578	if (cpts->pps_present && index == cpts->pps_hw_ts_idx)579		return -EINVAL;580 581	if (((cpts->hw_ts_enable & BIT(index)) >> index) == on)582		return 0;583 584	mutex_lock(&cpts->ptp_clk_lock);585	am65_cpts_extts_enable_hw(cpts, index, on);586	mutex_unlock(&cpts->ptp_clk_lock);587 588	dev_dbg(cpts->dev, "%s: ExtTS:%u %s\n",589		__func__, index, on ? "enabled" : "disabled");590 591	return 0;592}593 594int am65_cpts_estf_enable(struct am65_cpts *cpts, int idx,595			  struct am65_cpts_estf_cfg *cfg)596{597	u64 cycles;598	u32 val;599 600	cycles = cfg->ns_period * cpts->refclk_freq;601	cycles = DIV_ROUND_UP(cycles, NSEC_PER_SEC);602	if (cycles > U32_MAX)603		return -EINVAL;604 605	/* according to TRM should be zeroed */606	am65_cpts_write32(cpts, 0, estf[idx].length);607 608	val = upper_32_bits(cfg->ns_start);609	am65_cpts_write32(cpts, val, estf[idx].comp_hi);610	val = lower_32_bits(cfg->ns_start);611	am65_cpts_write32(cpts, val, estf[idx].comp_lo);612	val = lower_32_bits(cycles);613	am65_cpts_write32(cpts, val, estf[idx].length);614	am65_cpts_write32(cpts, 0, estf[idx].control);615	am65_cpts_write32(cpts, 0, estf[idx].ppm_hi);616	am65_cpts_write32(cpts, 0, estf[idx].ppm_low);617 618	cpts->estf_enable |= BIT(idx);619 620	dev_dbg(cpts->dev, "%s: ESTF:%u enabled\n", __func__, idx);621 622	return 0;623}624EXPORT_SYMBOL_GPL(am65_cpts_estf_enable);625 626void am65_cpts_estf_disable(struct am65_cpts *cpts, int idx)627{628	am65_cpts_write32(cpts, 0, estf[idx].length);629	cpts->estf_enable &= ~BIT(idx);630 631	dev_dbg(cpts->dev, "%s: ESTF:%u disabled\n", __func__, idx);632}633EXPORT_SYMBOL_GPL(am65_cpts_estf_disable);634 635static void am65_cpts_perout_enable_hw(struct am65_cpts *cpts,636				       struct ptp_perout_request *req, int on)637{638	u64 ns_period, ns_start, cycles;639	struct timespec64 ts;640	u32 val;641 642	if (on) {643		ts.tv_sec = req->period.sec;644		ts.tv_nsec = req->period.nsec;645		ns_period = timespec64_to_ns(&ts);646 647		cycles = (ns_period * cpts->refclk_freq) / NSEC_PER_SEC;648 649		ts.tv_sec = req->start.sec;650		ts.tv_nsec = req->start.nsec;651		ns_start = timespec64_to_ns(&ts);652 653		val = upper_32_bits(ns_start);654		am65_cpts_write32(cpts, val, genf[req->index].comp_hi);655		val = lower_32_bits(ns_start);656		am65_cpts_write32(cpts, val, genf[req->index].comp_lo);657		val = lower_32_bits(cycles);658		am65_cpts_write32(cpts, val, genf[req->index].length);659 660		am65_cpts_write32(cpts, 0, genf[req->index].control);661		am65_cpts_write32(cpts, 0, genf[req->index].ppm_hi);662		am65_cpts_write32(cpts, 0, genf[req->index].ppm_low);663 664		cpts->genf_enable |= BIT(req->index);665	} else {666		am65_cpts_write32(cpts, 0, genf[req->index].length);667 668		cpts->genf_enable &= ~BIT(req->index);669	}670}671 672static int am65_cpts_perout_enable(struct am65_cpts *cpts,673				   struct ptp_perout_request *req, int on)674{675	if (req->index >= cpts->ptp_info.n_per_out)676		return -ENXIO;677 678	if (cpts->pps_present && req->index == cpts->pps_genf_idx)679		return -EINVAL;680 681	if (!!(cpts->genf_enable & BIT(req->index)) == !!on)682		return 0;683 684	mutex_lock(&cpts->ptp_clk_lock);685	am65_cpts_perout_enable_hw(cpts, req, on);686	mutex_unlock(&cpts->ptp_clk_lock);687 688	dev_dbg(cpts->dev, "%s: GenF:%u %s\n",689		__func__, req->index, on ? "enabled" : "disabled");690 691	return 0;692}693 694static int am65_cpts_pps_enable(struct am65_cpts *cpts, int on)695{696	int ret = 0;697	struct timespec64 ts;698	struct ptp_clock_request rq;699	u64 ns;700 701	if (!cpts->pps_present)702		return -EINVAL;703 704	if (cpts->pps_enabled == !!on)705		return 0;706 707	mutex_lock(&cpts->ptp_clk_lock);708 709	if (on) {710		am65_cpts_extts_enable_hw(cpts, cpts->pps_hw_ts_idx, on);711 712		ns = am65_cpts_gettime(cpts, NULL);713		ts = ns_to_timespec64(ns);714		rq.perout.period.sec = 1;715		rq.perout.period.nsec = 0;716		rq.perout.start.sec = ts.tv_sec + 2;717		rq.perout.start.nsec = 0;718		rq.perout.index = cpts->pps_genf_idx;719 720		am65_cpts_perout_enable_hw(cpts, &rq.perout, on);721		cpts->pps_enabled = true;722	} else {723		rq.perout.index = cpts->pps_genf_idx;724		am65_cpts_perout_enable_hw(cpts, &rq.perout, on);725		am65_cpts_extts_enable_hw(cpts, cpts->pps_hw_ts_idx, on);726		cpts->pps_enabled = false;727	}728 729	mutex_unlock(&cpts->ptp_clk_lock);730 731	dev_dbg(cpts->dev, "%s: pps: %s\n",732		__func__, on ? "enabled" : "disabled");733	return ret;734}735 736static int am65_cpts_ptp_enable(struct ptp_clock_info *ptp,737				struct ptp_clock_request *rq, int on)738{739	struct am65_cpts *cpts = container_of(ptp, struct am65_cpts, ptp_info);740 741	switch (rq->type) {742	case PTP_CLK_REQ_EXTTS:743		return am65_cpts_extts_enable(cpts, rq->extts.index, on);744	case PTP_CLK_REQ_PEROUT:745		return am65_cpts_perout_enable(cpts, &rq->perout, on);746	case PTP_CLK_REQ_PPS:747		return am65_cpts_pps_enable(cpts, on);748	default:749		break;750	}751 752	return -EOPNOTSUPP;753}754 755static long am65_cpts_ts_work(struct ptp_clock_info *ptp);756 757static struct ptp_clock_info am65_ptp_info = {758	.owner		= THIS_MODULE,759	.name		= "CTPS timer",760	.adjfine	= am65_cpts_ptp_adjfine,761	.adjtime	= am65_cpts_ptp_adjtime,762	.gettimex64	= am65_cpts_ptp_gettimex,763	.settime64	= am65_cpts_ptp_settime,764	.enable		= am65_cpts_ptp_enable,765	.do_aux_work	= am65_cpts_ts_work,766};767 768static bool am65_cpts_match_tx_ts(struct am65_cpts *cpts,769				  struct am65_cpts_event *event)770{771	struct sk_buff_head txq_list;772	struct sk_buff *skb, *tmp;773	unsigned long flags;774	bool found = false;775	u32 mtype_seqid;776 777	mtype_seqid = event->event1 &778		      (AM65_CPTS_EVENT_1_MESSAGE_TYPE_MASK |779		       AM65_CPTS_EVENT_1_EVENT_TYPE_MASK |780		       AM65_CPTS_EVENT_1_SEQUENCE_ID_MASK);781 782	__skb_queue_head_init(&txq_list);783 784	spin_lock_irqsave(&cpts->txq.lock, flags);785	skb_queue_splice_init(&cpts->txq, &txq_list);786	spin_unlock_irqrestore(&cpts->txq.lock, flags);787 788	/* no need to grab txq.lock as access is always done under cpts->lock */789	skb_queue_walk_safe(&txq_list, skb, tmp) {790		struct skb_shared_hwtstamps ssh;791		struct am65_cpts_skb_cb_data *skb_cb =792					(struct am65_cpts_skb_cb_data *)skb->cb;793 794		if ((ptp_classify_raw(skb) & PTP_CLASS_V1) &&795		    ((mtype_seqid & AM65_CPTS_EVENT_1_SEQUENCE_ID_MASK) ==796		     (skb_cb->skb_mtype_seqid & AM65_CPTS_EVENT_1_SEQUENCE_ID_MASK)))797			mtype_seqid = skb_cb->skb_mtype_seqid;798 799		if (mtype_seqid == skb_cb->skb_mtype_seqid) {800			u64 ns = event->timestamp;801 802			memset(&ssh, 0, sizeof(ssh));803			ssh.hwtstamp = ns_to_ktime(ns);804			skb_tstamp_tx(skb, &ssh);805			found = true;806			__skb_unlink(skb, &txq_list);807			dev_consume_skb_any(skb);808			dev_dbg(cpts->dev,809				"match tx timestamp mtype_seqid %08x\n",810				mtype_seqid);811			break;812		}813 814		if (time_after(jiffies, skb_cb->tmo)) {815			/* timeout any expired skbs over 100 ms */816			dev_dbg(cpts->dev,817				"expiring tx timestamp mtype_seqid %08x\n",818				mtype_seqid);819			__skb_unlink(skb, &txq_list);820			dev_consume_skb_any(skb);821		}822	}823 824	spin_lock_irqsave(&cpts->txq.lock, flags);825	skb_queue_splice(&txq_list, &cpts->txq);826	spin_unlock_irqrestore(&cpts->txq.lock, flags);827 828	return found;829}830 831static void am65_cpts_find_ts(struct am65_cpts *cpts)832{833	struct am65_cpts_event *event;834	struct list_head *this, *next;835	LIST_HEAD(events_free);836	unsigned long flags;837	LIST_HEAD(events);838 839	spin_lock_irqsave(&cpts->lock, flags);840	list_splice_init(&cpts->events, &events);841	spin_unlock_irqrestore(&cpts->lock, flags);842 843	list_for_each_safe(this, next, &events) {844		event = list_entry(this, struct am65_cpts_event, list);845		if (am65_cpts_match_tx_ts(cpts, event) ||846		    time_after(jiffies, event->tmo)) {847			list_del_init(&event->list);848			list_add(&event->list, &events_free);849		}850	}851 852	spin_lock_irqsave(&cpts->lock, flags);853	list_splice_tail(&events, &cpts->events);854	list_splice_tail(&events_free, &cpts->pool);855	spin_unlock_irqrestore(&cpts->lock, flags);856}857 858static long am65_cpts_ts_work(struct ptp_clock_info *ptp)859{860	struct am65_cpts *cpts = container_of(ptp, struct am65_cpts, ptp_info);861	unsigned long flags;862	long delay = -1;863 864	am65_cpts_find_ts(cpts);865 866	spin_lock_irqsave(&cpts->txq.lock, flags);867	if (!skb_queue_empty(&cpts->txq))868		delay = AM65_CPTS_SKB_TX_WORK_TIMEOUT;869	spin_unlock_irqrestore(&cpts->txq.lock, flags);870 871	return delay;872}873 874static int am65_skb_get_mtype_seqid(struct sk_buff *skb, u32 *mtype_seqid)875{876	unsigned int ptp_class = ptp_classify_raw(skb);877	struct ptp_header *hdr;878	u8 msgtype;879	u16 seqid;880 881	if (ptp_class == PTP_CLASS_NONE)882		return 0;883 884	hdr = ptp_parse_header(skb, ptp_class);885	if (!hdr)886		return 0;887 888	msgtype = ptp_get_msgtype(hdr, ptp_class);889	seqid	= ntohs(hdr->sequence_id);890 891	*mtype_seqid  = (msgtype << AM65_CPTS_EVENT_1_MESSAGE_TYPE_SHIFT) &892			AM65_CPTS_EVENT_1_MESSAGE_TYPE_MASK;893	*mtype_seqid |= (seqid & AM65_CPTS_EVENT_1_SEQUENCE_ID_MASK);894 895	return 1;896}897 898static u64 am65_cpts_find_rx_ts(struct am65_cpts *cpts, u32 skb_mtype_seqid)899{900	struct list_head *this, *next;901	struct am65_cpts_event *event;902	unsigned long flags;903	u32 mtype_seqid;904	u64 ns = 0;905 906	spin_lock_irqsave(&cpts->lock, flags);907	__am65_cpts_fifo_read(cpts);908	list_for_each_safe(this, next, &cpts->events) {909		event = list_entry(this, struct am65_cpts_event, list);910		if (time_after(jiffies, event->tmo)) {911			list_move(&event->list, &cpts->pool);912			continue;913		}914 915		mtype_seqid = event->event1 &916			      (AM65_CPTS_EVENT_1_MESSAGE_TYPE_MASK |917			       AM65_CPTS_EVENT_1_SEQUENCE_ID_MASK |918			       AM65_CPTS_EVENT_1_EVENT_TYPE_MASK);919 920		if (mtype_seqid == skb_mtype_seqid) {921			ns = event->timestamp;922			list_move(&event->list, &cpts->pool);923			break;924		}925	}926	spin_unlock_irqrestore(&cpts->lock, flags);927 928	return ns;929}930 931void am65_cpts_rx_timestamp(struct am65_cpts *cpts, struct sk_buff *skb)932{933	struct am65_cpts_skb_cb_data *skb_cb = (struct am65_cpts_skb_cb_data *)skb->cb;934	struct skb_shared_hwtstamps *ssh;935	int ret;936	u64 ns;937 938	/* am65_cpts_rx_timestamp() is called before eth_type_trans(), so939	 * skb MAC Hdr properties are not configured yet. Hence need to940	 * reset skb MAC header here941	 */942	skb_reset_mac_header(skb);943	ret = am65_skb_get_mtype_seqid(skb, &skb_cb->skb_mtype_seqid);944	if (!ret)945		return; /* if not PTP class packet */946 947	skb_cb->skb_mtype_seqid |= (AM65_CPTS_EV_RX << AM65_CPTS_EVENT_1_EVENT_TYPE_SHIFT);948 949	dev_dbg(cpts->dev, "%s mtype seqid %08x\n", __func__, skb_cb->skb_mtype_seqid);950 951	ns = am65_cpts_find_rx_ts(cpts, skb_cb->skb_mtype_seqid);952	if (!ns)953		return;954 955	ssh = skb_hwtstamps(skb);956	memset(ssh, 0, sizeof(*ssh));957	ssh->hwtstamp = ns_to_ktime(ns);958}959EXPORT_SYMBOL_GPL(am65_cpts_rx_timestamp);960 961/**962 * am65_cpts_tx_timestamp - save tx packet for timestamping963 * @cpts: cpts handle964 * @skb: packet965 *966 * This functions saves tx packet for timestamping if packet can be timestamped.967 * The future processing is done in from PTP auxiliary worker.968 */969void am65_cpts_tx_timestamp(struct am65_cpts *cpts, struct sk_buff *skb)970{971	struct am65_cpts_skb_cb_data *skb_cb = (void *)skb->cb;972 973	if (!(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS))974		return;975 976	/* add frame to queue for processing later.977	 * The periodic FIFO check will handle this.978	 */979	skb_get(skb);980	/* get the timestamp for timeouts */981	skb_cb->tmo = jiffies + msecs_to_jiffies(100);982	skb_queue_tail(&cpts->txq, skb);983	ptp_schedule_worker(cpts->ptp_clock, 0);984}985EXPORT_SYMBOL_GPL(am65_cpts_tx_timestamp);986 987/**988 * am65_cpts_prep_tx_timestamp - check and prepare tx packet for timestamping989 * @cpts: cpts handle990 * @skb: packet991 *992 * This functions should be called from .xmit().993 * It checks if packet can be timestamped, fills internal cpts data994 * in skb-cb and marks packet as SKBTX_IN_PROGRESS.995 */996void am65_cpts_prep_tx_timestamp(struct am65_cpts *cpts, struct sk_buff *skb)997{998	struct am65_cpts_skb_cb_data *skb_cb = (void *)skb->cb;999	int ret;1000 1001	if (!(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP))1002		return;1003 1004	ret = am65_skb_get_mtype_seqid(skb, &skb_cb->skb_mtype_seqid);1005	if (!ret)1006		return;1007	skb_cb->skb_mtype_seqid |= (AM65_CPTS_EV_TX <<1008				   AM65_CPTS_EVENT_1_EVENT_TYPE_SHIFT);1009 1010	skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;1011}1012EXPORT_SYMBOL_GPL(am65_cpts_prep_tx_timestamp);1013 1014int am65_cpts_phc_index(struct am65_cpts *cpts)1015{1016	return cpts->phc_index;1017}1018EXPORT_SYMBOL_GPL(am65_cpts_phc_index);1019 1020static void cpts_free_clk_mux(void *data)1021{1022	struct am65_cpts *cpts = data;1023 1024	of_clk_del_provider(cpts->clk_mux_np);1025	clk_hw_unregister_mux(cpts->clk_mux_hw);1026	of_node_put(cpts->clk_mux_np);1027}1028 1029static int cpts_of_mux_clk_setup(struct am65_cpts *cpts,1030				 struct device_node *node)1031{1032	unsigned int num_parents;1033	const char **parent_names;1034	char *clk_mux_name;1035	void __iomem *reg;1036	int ret = -EINVAL;1037 1038	cpts->clk_mux_np = of_get_child_by_name(node, "refclk-mux");1039	if (!cpts->clk_mux_np)1040		return 0;1041 1042	num_parents = of_clk_get_parent_count(cpts->clk_mux_np);1043	if (num_parents < 1) {1044		dev_err(cpts->dev, "mux-clock %pOF must have parents\n",1045			cpts->clk_mux_np);1046		goto mux_fail;1047	}1048 1049	parent_names = devm_kcalloc(cpts->dev, sizeof(char *), num_parents,1050				    GFP_KERNEL);1051	if (!parent_names) {1052		ret = -ENOMEM;1053		goto mux_fail;1054	}1055 1056	of_clk_parent_fill(cpts->clk_mux_np, parent_names, num_parents);1057 1058	clk_mux_name = devm_kasprintf(cpts->dev, GFP_KERNEL, "%s.%pOFn",1059				      dev_name(cpts->dev), cpts->clk_mux_np);1060	if (!clk_mux_name) {1061		ret = -ENOMEM;1062		goto mux_fail;1063	}1064 1065	reg = &cpts->reg->rftclk_sel;1066	/* dev must be NULL to avoid recursive incrementing1067	 * of module refcnt1068	 */1069	cpts->clk_mux_hw = clk_hw_register_mux(NULL, clk_mux_name,1070					       parent_names, num_parents,1071					       0, reg, 0, 5, 0, NULL);1072	if (IS_ERR(cpts->clk_mux_hw)) {1073		ret = PTR_ERR(cpts->clk_mux_hw);1074		goto mux_fail;1075	}1076 1077	ret = of_clk_add_hw_provider(cpts->clk_mux_np, of_clk_hw_simple_get,1078				     cpts->clk_mux_hw);1079	if (ret)1080		goto clk_hw_register;1081 1082	ret = devm_add_action_or_reset(cpts->dev, cpts_free_clk_mux, cpts);1083	if (ret)1084		dev_err(cpts->dev, "failed to add clkmux reset action %d", ret);1085 1086	return ret;1087 1088clk_hw_register:1089	clk_hw_unregister_mux(cpts->clk_mux_hw);1090mux_fail:1091	of_node_put(cpts->clk_mux_np);1092	return ret;1093}1094 1095static int am65_cpts_of_parse(struct am65_cpts *cpts, struct device_node *node)1096{1097	u32 prop[2];1098 1099	if (!of_property_read_u32(node, "ti,cpts-ext-ts-inputs", &prop[0]))1100		cpts->ext_ts_inputs = prop[0];1101 1102	if (!of_property_read_u32(node, "ti,cpts-periodic-outputs", &prop[0]))1103		cpts->genf_num = prop[0];1104 1105	if (!of_property_read_u32_array(node, "ti,pps", prop, 2)) {1106		cpts->pps_present = true;1107 1108		if (prop[0] > 7) {1109			dev_err(cpts->dev, "invalid HWx_TS_PUSH index: %u provided\n", prop[0]);1110			cpts->pps_present = false;1111		}1112		if (prop[1] > 1) {1113			dev_err(cpts->dev, "invalid GENFy index: %u provided\n", prop[1]);1114			cpts->pps_present = false;1115		}1116		if (cpts->pps_present) {1117			cpts->pps_hw_ts_idx = prop[0];1118			cpts->pps_genf_idx = prop[1];1119		}1120	}1121 1122	return cpts_of_mux_clk_setup(cpts, node);1123}1124 1125void am65_cpts_release(struct am65_cpts *cpts)1126{1127	ptp_clock_unregister(cpts->ptp_clock);1128	am65_cpts_disable(cpts);1129	clk_disable_unprepare(cpts->refclk);1130}1131EXPORT_SYMBOL_GPL(am65_cpts_release);1132 1133struct am65_cpts *am65_cpts_create(struct device *dev, void __iomem *regs,1134				   struct device_node *node)1135{1136	struct am65_cpts *cpts;1137	int ret, i;1138 1139	cpts = devm_kzalloc(dev, sizeof(*cpts), GFP_KERNEL);1140	if (!cpts)1141		return ERR_PTR(-ENOMEM);1142 1143	cpts->dev = dev;1144	cpts->reg = (struct am65_cpts_regs __iomem *)regs;1145 1146	cpts->irq = of_irq_get_byname(node, "cpts");1147	if (cpts->irq <= 0) {1148		ret = cpts->irq ?: -ENXIO;1149		dev_err_probe(dev, ret, "Failed to get IRQ number\n");1150		return ERR_PTR(ret);1151	}1152 1153	ret = am65_cpts_of_parse(cpts, node);1154	if (ret)1155		return ERR_PTR(ret);1156 1157	mutex_init(&cpts->ptp_clk_lock);1158	INIT_LIST_HEAD(&cpts->events);1159	INIT_LIST_HEAD(&cpts->pool);1160	spin_lock_init(&cpts->lock);1161	skb_queue_head_init(&cpts->txq);1162 1163	for (i = 0; i < AM65_CPTS_MAX_EVENTS; i++)1164		list_add(&cpts->pool_data[i].list, &cpts->pool);1165 1166	cpts->refclk = devm_get_clk_from_child(dev, node, "cpts");1167	if (IS_ERR(cpts->refclk)) {1168		ret = PTR_ERR(cpts->refclk);1169		dev_err_probe(dev, ret, "Failed to get refclk\n");1170		return ERR_PTR(ret);1171	}1172 1173	ret = clk_prepare_enable(cpts->refclk);1174	if (ret) {1175		dev_err(dev, "Failed to enable refclk %d\n", ret);1176		return ERR_PTR(ret);1177	}1178 1179	cpts->refclk_freq = clk_get_rate(cpts->refclk);1180 1181	am65_ptp_info.max_adj = cpts->refclk_freq / AM65_CPTS_MIN_PPM;1182	cpts->ptp_info = am65_ptp_info;1183 1184	if (cpts->ext_ts_inputs)1185		cpts->ptp_info.n_ext_ts = cpts->ext_ts_inputs;1186	if (cpts->genf_num)1187		cpts->ptp_info.n_per_out = cpts->genf_num;1188	if (cpts->pps_present)1189		cpts->ptp_info.pps = 1;1190 1191	am65_cpts_set_add_val(cpts);1192 1193	am65_cpts_write32(cpts, AM65_CPTS_CONTROL_EN |1194			  AM65_CPTS_CONTROL_64MODE |1195			  AM65_CPTS_CONTROL_TX_GENF_CLR_EN,1196			  control);1197	am65_cpts_write32(cpts, AM65_CPTS_INT_ENABLE_TS_PEND_EN, int_enable);1198 1199	/* set time to the current system time */1200	am65_cpts_settime(cpts, ktime_to_ns(ktime_get_real()));1201 1202	cpts->ptp_clock = ptp_clock_register(&cpts->ptp_info, cpts->dev);1203	if (IS_ERR_OR_NULL(cpts->ptp_clock)) {1204		dev_err(dev, "Failed to register ptp clk %ld\n",1205			PTR_ERR(cpts->ptp_clock));1206		ret = cpts->ptp_clock ? PTR_ERR(cpts->ptp_clock) : -ENODEV;1207		goto refclk_disable;1208	}1209	cpts->phc_index = ptp_clock_index(cpts->ptp_clock);1210 1211	ret = devm_request_threaded_irq(dev, cpts->irq, NULL,1212					am65_cpts_interrupt,1213					IRQF_ONESHOT, dev_name(dev), cpts);1214	if (ret < 0) {1215		dev_err(cpts->dev, "error attaching irq %d\n", ret);1216		goto reset_ptpclk;1217	}1218 1219	dev_info(dev, "CPTS ver 0x%08x, freq:%u, add_val:%u pps:%d\n",1220		 am65_cpts_read32(cpts, idver),1221		 cpts->refclk_freq, cpts->ts_add_val, cpts->pps_present);1222 1223	return cpts;1224 1225reset_ptpclk:1226	am65_cpts_release(cpts);1227refclk_disable:1228	clk_disable_unprepare(cpts->refclk);1229	return ERR_PTR(ret);1230}1231EXPORT_SYMBOL_GPL(am65_cpts_create);1232 1233void am65_cpts_suspend(struct am65_cpts *cpts)1234{1235	/* save state and disable CPTS */1236	cpts->sr_control = am65_cpts_read32(cpts, control);1237	cpts->sr_int_enable = am65_cpts_read32(cpts, int_enable);1238	cpts->sr_rftclk_sel = am65_cpts_read32(cpts, rftclk_sel);1239	cpts->sr_ts_ppm_hi = am65_cpts_read32(cpts, ts_ppm_hi);1240	cpts->sr_ts_ppm_low = am65_cpts_read32(cpts, ts_ppm_low);1241	cpts->sr_cpts_ns = am65_cpts_gettime(cpts, NULL);1242	cpts->sr_ktime_ns = ktime_to_ns(ktime_get_real());1243	am65_cpts_disable(cpts);1244	clk_disable(cpts->refclk);1245 1246	/* Save GENF state */1247	memcpy_fromio(&cpts->sr_genf, &cpts->reg->genf, sizeof(cpts->sr_genf));1248 1249	/* Save ESTF state */1250	memcpy_fromio(&cpts->sr_estf, &cpts->reg->estf, sizeof(cpts->sr_estf));1251}1252EXPORT_SYMBOL_GPL(am65_cpts_suspend);1253 1254void am65_cpts_resume(struct am65_cpts *cpts)1255{1256	int i;1257	s64 ktime_ns;1258 1259	/* restore state and enable CPTS */1260	clk_enable(cpts->refclk);1261	am65_cpts_write32(cpts, cpts->sr_rftclk_sel, rftclk_sel);1262	am65_cpts_set_add_val(cpts);1263	am65_cpts_write32(cpts, cpts->sr_control, control);1264	am65_cpts_write32(cpts, cpts->sr_int_enable, int_enable);1265 1266	/* Restore time to saved CPTS time + time in suspend/resume */1267	ktime_ns = ktime_to_ns(ktime_get_real());1268	ktime_ns -= cpts->sr_ktime_ns;1269	am65_cpts_settime(cpts, cpts->sr_cpts_ns + ktime_ns);1270 1271	/* Restore compensation (PPM) */1272	am65_cpts_write32(cpts, cpts->sr_ts_ppm_hi, ts_ppm_hi);1273	am65_cpts_write32(cpts, cpts->sr_ts_ppm_low, ts_ppm_low);1274 1275	/* Restore GENF state */1276	for (i = 0; i < AM65_CPTS_GENF_MAX_NUM; i++) {1277		am65_cpts_write32(cpts, 0, genf[i].length);	/* TRM sequence */1278		am65_cpts_write32(cpts, cpts->sr_genf[i].comp_hi, genf[i].comp_hi);1279		am65_cpts_write32(cpts, cpts->sr_genf[i].comp_lo, genf[i].comp_lo);1280		am65_cpts_write32(cpts, cpts->sr_genf[i].length, genf[i].length);1281		am65_cpts_write32(cpts, cpts->sr_genf[i].control, genf[i].control);1282		am65_cpts_write32(cpts, cpts->sr_genf[i].ppm_hi, genf[i].ppm_hi);1283		am65_cpts_write32(cpts, cpts->sr_genf[i].ppm_low, genf[i].ppm_low);1284	}1285 1286	/* Restore ESTTF state */1287	for (i = 0; i < AM65_CPTS_ESTF_MAX_NUM; i++) {1288		am65_cpts_write32(cpts, 0, estf[i].length);	/* TRM sequence */1289		am65_cpts_write32(cpts, cpts->sr_estf[i].comp_hi, estf[i].comp_hi);1290		am65_cpts_write32(cpts, cpts->sr_estf[i].comp_lo, estf[i].comp_lo);1291		am65_cpts_write32(cpts, cpts->sr_estf[i].length, estf[i].length);1292		am65_cpts_write32(cpts, cpts->sr_estf[i].control, estf[i].control);1293		am65_cpts_write32(cpts, cpts->sr_estf[i].ppm_hi, estf[i].ppm_hi);1294		am65_cpts_write32(cpts, cpts->sr_estf[i].ppm_low, estf[i].ppm_low);1295	}1296}1297EXPORT_SYMBOL_GPL(am65_cpts_resume);1298 1299static int am65_cpts_probe(struct platform_device *pdev)1300{1301	struct device_node *node = pdev->dev.of_node;1302	struct device *dev = &pdev->dev;1303	struct am65_cpts *cpts;1304	void __iomem *base;1305 1306	base = devm_platform_ioremap_resource_byname(pdev, "cpts");1307	if (IS_ERR(base))1308		return PTR_ERR(base);1309 1310	cpts = am65_cpts_create(dev, base, node);1311	return PTR_ERR_OR_ZERO(cpts);1312}1313 1314static const struct of_device_id am65_cpts_of_match[] = {1315	{ .compatible = "ti,am65-cpts", },1316	{ .compatible = "ti,j721e-cpts", },1317	{},1318};1319MODULE_DEVICE_TABLE(of, am65_cpts_of_match);1320 1321static struct platform_driver am65_cpts_driver = {1322	.probe		= am65_cpts_probe,1323	.driver		= {1324		.name	= "am65-cpts",1325		.of_match_table = am65_cpts_of_match,1326	},1327};1328module_platform_driver(am65_cpts_driver);1329 1330MODULE_LICENSE("GPL v2");1331MODULE_AUTHOR("Grygorii Strashko <grygorii.strashko@ti.com>");1332MODULE_DESCRIPTION("TI K3 AM65 CPTS driver");1333