brintos

brintos / linux-shallow public Read only

0
0
Text · 25.1 KiB · 5d6d1cf Raw
954 lines · c
1// SPDX-License-Identifier: GPL-2.02 3/* Texas Instruments ICSSG Industrial Ethernet Peripheral (IEP) Driver4 *5 * Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com6 *7 */8 9#include <linux/bitops.h>10#include <linux/clk.h>11#include <linux/err.h>12#include <linux/io.h>13#include <linux/module.h>14#include <linux/of.h>15#include <linux/of_platform.h>16#include <linux/platform_device.h>17#include <linux/timekeeping.h>18#include <linux/interrupt.h>19#include <linux/of_irq.h>20#include <linux/workqueue.h>21 22#include "icss_iep.h"23 24#define IEP_MAX_DEF_INC		0xf25#define IEP_MAX_COMPEN_INC		0xfff26#define IEP_MAX_COMPEN_COUNT	0xffffff27 28#define IEP_GLOBAL_CFG_CNT_ENABLE	BIT(0)29#define IEP_GLOBAL_CFG_DEFAULT_INC_MASK		GENMASK(7, 4)30#define IEP_GLOBAL_CFG_DEFAULT_INC_SHIFT	431#define IEP_GLOBAL_CFG_COMPEN_INC_MASK		GENMASK(19, 8)32#define IEP_GLOBAL_CFG_COMPEN_INC_SHIFT		833 34#define IEP_GLOBAL_STATUS_CNT_OVF	BIT(0)35 36#define IEP_CMP_CFG_SHADOW_EN		BIT(17)37#define IEP_CMP_CFG_CMP0_RST_CNT_EN	BIT(0)38#define IEP_CMP_CFG_CMP_EN(cmp)		(GENMASK(16, 1) & (1 << ((cmp) + 1)))39 40#define IEP_CMP_STATUS(cmp)		(1 << (cmp))41 42#define IEP_SYNC_CTRL_SYNC_EN		BIT(0)43#define IEP_SYNC_CTRL_SYNC_N_EN(n)	(GENMASK(2, 1) & (BIT(1) << (n)))44 45#define IEP_MIN_CMP	046#define IEP_MAX_CMP	1547 48#define ICSS_IEP_64BIT_COUNTER_SUPPORT		BIT(0)49#define ICSS_IEP_SLOW_COMPEN_REG_SUPPORT	BIT(1)50#define ICSS_IEP_SHADOW_MODE_SUPPORT		BIT(2)51 52#define LATCH_INDEX(ts_index)			((ts_index) + 6)53#define IEP_CAP_CFG_CAPNR_1ST_EVENT_EN(n)	BIT(LATCH_INDEX(n))54#define IEP_CAP_CFG_CAP_ASYNC_EN(n)		BIT(LATCH_INDEX(n) + 10)55 56/**57 * icss_iep_get_count_hi() - Get the upper 32 bit IEP counter58 * @iep: Pointer to structure representing IEP.59 *60 * Return: upper 32 bit IEP counter61 */62int icss_iep_get_count_hi(struct icss_iep *iep)63{64	u32 val = 0;65 66	if (iep && (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT))67		val = readl(iep->base + iep->plat_data->reg_offs[ICSS_IEP_COUNT_REG1]);68 69	return val;70}71EXPORT_SYMBOL_GPL(icss_iep_get_count_hi);72 73/**74 * icss_iep_get_count_low() - Get the lower 32 bit IEP counter75 * @iep: Pointer to structure representing IEP.76 *77 * Return: lower 32 bit IEP counter78 */79int icss_iep_get_count_low(struct icss_iep *iep)80{81	u32 val = 0;82 83	if (iep)84		val = readl(iep->base + iep->plat_data->reg_offs[ICSS_IEP_COUNT_REG0]);85 86	return val;87}88EXPORT_SYMBOL_GPL(icss_iep_get_count_low);89 90/**91 * icss_iep_get_ptp_clock_idx() - Get PTP clock index using IEP driver92 * @iep: Pointer to structure representing IEP.93 *94 * Return: PTP clock index, -1 if not registered95 */96int icss_iep_get_ptp_clock_idx(struct icss_iep *iep)97{98	if (!iep || !iep->ptp_clock)99		return -1;100	return ptp_clock_index(iep->ptp_clock);101}102EXPORT_SYMBOL_GPL(icss_iep_get_ptp_clock_idx);103 104static void icss_iep_set_counter(struct icss_iep *iep, u64 ns)105{106	if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT)107		writel(upper_32_bits(ns), iep->base +108		       iep->plat_data->reg_offs[ICSS_IEP_COUNT_REG1]);109	writel(lower_32_bits(ns), iep->base + iep->plat_data->reg_offs[ICSS_IEP_COUNT_REG0]);110}111 112static void icss_iep_update_to_next_boundary(struct icss_iep *iep, u64 start_ns);113 114/**115 * icss_iep_settime() - Set time of the PTP clock using IEP driver116 * @iep: Pointer to structure representing IEP.117 * @ns: Time to be set in nanoseconds118 *119 * This API uses writel() instead of regmap_write() for write operations as120 * regmap_write() is too slow and this API is time sensitive.121 */122static void icss_iep_settime(struct icss_iep *iep, u64 ns)123{124	if (iep->ops && iep->ops->settime) {125		iep->ops->settime(iep->clockops_data, ns);126		return;127	}128 129	if (iep->pps_enabled || iep->perout_enabled)130		writel(0, iep->base + iep->plat_data->reg_offs[ICSS_IEP_SYNC_CTRL_REG]);131 132	icss_iep_set_counter(iep, ns);133 134	if (iep->pps_enabled || iep->perout_enabled) {135		icss_iep_update_to_next_boundary(iep, ns);136		writel(IEP_SYNC_CTRL_SYNC_N_EN(0) | IEP_SYNC_CTRL_SYNC_EN,137		       iep->base + iep->plat_data->reg_offs[ICSS_IEP_SYNC_CTRL_REG]);138	}139}140 141/**142 * icss_iep_gettime() - Get time of the PTP clock using IEP driver143 * @iep: Pointer to structure representing IEP.144 * @sts: Pointer to structure representing PTP system timestamp.145 *146 * This API uses readl() instead of regmap_read() for read operations as147 * regmap_read() is too slow and this API is time sensitive.148 *149 * Return: The current timestamp of the PTP clock using IEP driver150 */151static u64 icss_iep_gettime(struct icss_iep *iep,152			    struct ptp_system_timestamp *sts)153{154	u32 ts_hi = 0, ts_lo;155	unsigned long flags;156 157	if (iep->ops && iep->ops->gettime)158		return iep->ops->gettime(iep->clockops_data, sts);159 160	/* use local_irq_x() to make it work for both RT/non-RT */161	local_irq_save(flags);162 163	/* no need to play with hi-lo, hi is latched when lo is read */164	ptp_read_system_prets(sts);165	ts_lo = readl(iep->base + iep->plat_data->reg_offs[ICSS_IEP_COUNT_REG0]);166	ptp_read_system_postts(sts);167	if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT)168		ts_hi = readl(iep->base + iep->plat_data->reg_offs[ICSS_IEP_COUNT_REG1]);169 170	local_irq_restore(flags);171 172	return (u64)ts_lo | (u64)ts_hi << 32;173}174 175static void icss_iep_enable(struct icss_iep *iep)176{177	regmap_update_bits(iep->map, ICSS_IEP_GLOBAL_CFG_REG,178			   IEP_GLOBAL_CFG_CNT_ENABLE,179			   IEP_GLOBAL_CFG_CNT_ENABLE);180}181 182static void icss_iep_disable(struct icss_iep *iep)183{184	regmap_update_bits(iep->map, ICSS_IEP_GLOBAL_CFG_REG,185			   IEP_GLOBAL_CFG_CNT_ENABLE,186			   0);187}188 189static void icss_iep_enable_shadow_mode(struct icss_iep *iep)190{191	u32 cycle_time;192	int cmp;193 194	cycle_time = iep->cycle_time_ns - iep->def_inc;195 196	icss_iep_disable(iep);197 198	/* disable shadow mode */199	regmap_update_bits(iep->map, ICSS_IEP_CMP_CFG_REG,200			   IEP_CMP_CFG_SHADOW_EN, 0);201 202	/* enable shadow mode */203	regmap_update_bits(iep->map, ICSS_IEP_CMP_CFG_REG,204			   IEP_CMP_CFG_SHADOW_EN, IEP_CMP_CFG_SHADOW_EN);205 206	/* clear counters */207	icss_iep_set_counter(iep, 0);208 209	/* clear overflow status */210	regmap_update_bits(iep->map, ICSS_IEP_GLOBAL_STATUS_REG,211			   IEP_GLOBAL_STATUS_CNT_OVF,212			   IEP_GLOBAL_STATUS_CNT_OVF);213 214	/* clear compare status */215	for (cmp = IEP_MIN_CMP; cmp < IEP_MAX_CMP; cmp++) {216		regmap_update_bits(iep->map, ICSS_IEP_CMP_STAT_REG,217				   IEP_CMP_STATUS(cmp), IEP_CMP_STATUS(cmp));218	}219 220	/* enable reset counter on CMP0 event */221	regmap_update_bits(iep->map, ICSS_IEP_CMP_CFG_REG,222			   IEP_CMP_CFG_CMP0_RST_CNT_EN,223			   IEP_CMP_CFG_CMP0_RST_CNT_EN);224	/* enable compare */225	regmap_update_bits(iep->map, ICSS_IEP_CMP_CFG_REG,226			   IEP_CMP_CFG_CMP_EN(0),227			   IEP_CMP_CFG_CMP_EN(0));228 229	/* set CMP0 value to cycle time */230	regmap_write(iep->map, ICSS_IEP_CMP0_REG0, cycle_time);231	if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT)232		regmap_write(iep->map, ICSS_IEP_CMP0_REG1, cycle_time);233 234	icss_iep_set_counter(iep, 0);235	icss_iep_enable(iep);236}237 238static void icss_iep_set_default_inc(struct icss_iep *iep, u8 def_inc)239{240	regmap_update_bits(iep->map, ICSS_IEP_GLOBAL_CFG_REG,241			   IEP_GLOBAL_CFG_DEFAULT_INC_MASK,242			   def_inc << IEP_GLOBAL_CFG_DEFAULT_INC_SHIFT);243}244 245static void icss_iep_set_compensation_inc(struct icss_iep *iep, u16 compen_inc)246{247	struct device *dev = regmap_get_device(iep->map);248 249	if (compen_inc > IEP_MAX_COMPEN_INC) {250		dev_err(dev, "%s: too high compensation inc %d\n",251			__func__, compen_inc);252		compen_inc = IEP_MAX_COMPEN_INC;253	}254 255	regmap_update_bits(iep->map, ICSS_IEP_GLOBAL_CFG_REG,256			   IEP_GLOBAL_CFG_COMPEN_INC_MASK,257			   compen_inc << IEP_GLOBAL_CFG_COMPEN_INC_SHIFT);258}259 260static void icss_iep_set_compensation_count(struct icss_iep *iep,261					    u32 compen_count)262{263	struct device *dev = regmap_get_device(iep->map);264 265	if (compen_count > IEP_MAX_COMPEN_COUNT) {266		dev_err(dev, "%s: too high compensation count %d\n",267			__func__, compen_count);268		compen_count = IEP_MAX_COMPEN_COUNT;269	}270 271	regmap_write(iep->map, ICSS_IEP_COMPEN_REG, compen_count);272}273 274static void icss_iep_set_slow_compensation_count(struct icss_iep *iep,275						 u32 compen_count)276{277	regmap_write(iep->map, ICSS_IEP_SLOW_COMPEN_REG, compen_count);278}279 280/* PTP PHC operations */281static int icss_iep_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)282{283	struct icss_iep *iep = container_of(ptp, struct icss_iep, ptp_info);284	s32 ppb = scaled_ppm_to_ppb(scaled_ppm);285	u32 cyc_count;286	u16 cmp_inc;287 288	mutex_lock(&iep->ptp_clk_mutex);289 290	/* ppb is amount of frequency we want to adjust in 1GHz (billion)291	 * e.g. 100ppb means we need to speed up clock by 100Hz292	 * i.e. at end of 1 second (1 billion ns) clock time, we should be293	 * counting 100 more ns.294	 * We use IEP slow compensation to achieve continuous freq. adjustment.295	 * There are 2 parts. Cycle time and adjustment per cycle.296	 * Simplest case would be 1 sec Cycle time. Then adjustment297	 * pre cycle would be (def_inc + ppb) value.298	 * Cycle time will have to be chosen based on how worse the ppb is.299	 * e.g. smaller the ppb, cycle time has to be large.300	 * The minimum adjustment we can do is +-1ns per cycle so let's301	 * reduce the cycle time to get 1ns per cycle adjustment.302	 *	1ppb = 1sec cycle time & 1ns adjust303	 *	1000ppb = 1/1000 cycle time & 1ns adjust per cycle304	 */305 306	if (iep->cycle_time_ns)307		iep->slow_cmp_inc = iep->clk_tick_time;	/* 4ns adj per cycle */308	else309		iep->slow_cmp_inc = 1;	/* 1ns adjust per cycle */310 311	if (ppb < 0) {312		iep->slow_cmp_inc = -iep->slow_cmp_inc;313		ppb = -ppb;314	}315 316	cyc_count = NSEC_PER_SEC;		/* 1s cycle time @1GHz */317	cyc_count /= ppb;		/* cycle time per ppb */318 319	/* slow_cmp_count is decremented every clock cycle, e.g. @250MHz */320	if (!iep->cycle_time_ns)321		cyc_count /= iep->clk_tick_time;322	iep->slow_cmp_count = cyc_count;323 324	/* iep->clk_tick_time is def_inc */325	cmp_inc = iep->clk_tick_time + iep->slow_cmp_inc;326	icss_iep_set_compensation_inc(iep, cmp_inc);327	icss_iep_set_slow_compensation_count(iep, iep->slow_cmp_count);328 329	mutex_unlock(&iep->ptp_clk_mutex);330 331	return 0;332}333 334static int icss_iep_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)335{336	struct icss_iep *iep = container_of(ptp, struct icss_iep, ptp_info);337	s64 ns;338 339	mutex_lock(&iep->ptp_clk_mutex);340	if (iep->ops && iep->ops->adjtime) {341		iep->ops->adjtime(iep->clockops_data, delta);342	} else {343		ns = icss_iep_gettime(iep, NULL);344		ns += delta;345		icss_iep_settime(iep, ns);346	}347	mutex_unlock(&iep->ptp_clk_mutex);348 349	return 0;350}351 352static int icss_iep_ptp_gettimeex(struct ptp_clock_info *ptp,353				  struct timespec64 *ts,354				  struct ptp_system_timestamp *sts)355{356	struct icss_iep *iep = container_of(ptp, struct icss_iep, ptp_info);357	u64 ns;358 359	mutex_lock(&iep->ptp_clk_mutex);360	ns = icss_iep_gettime(iep, sts);361	*ts = ns_to_timespec64(ns);362	mutex_unlock(&iep->ptp_clk_mutex);363 364	return 0;365}366 367static int icss_iep_ptp_settime(struct ptp_clock_info *ptp,368				const struct timespec64 *ts)369{370	struct icss_iep *iep = container_of(ptp, struct icss_iep, ptp_info);371	u64 ns;372 373	mutex_lock(&iep->ptp_clk_mutex);374	ns = timespec64_to_ns(ts);375	icss_iep_settime(iep, ns);376	mutex_unlock(&iep->ptp_clk_mutex);377 378	return 0;379}380 381static void icss_iep_update_to_next_boundary(struct icss_iep *iep, u64 start_ns)382{383	u64 ns, p_ns;384	u32 offset;385 386	ns = icss_iep_gettime(iep, NULL);387	if (start_ns < ns)388		start_ns = ns;389	p_ns = iep->period;390	/* Round up to next period boundary */391	start_ns += p_ns - 1;392	offset = do_div(start_ns, p_ns);393	start_ns = start_ns * p_ns;394	/* If it is too close to update, shift to next boundary */395	if (p_ns - offset < 10)396		start_ns += p_ns;397 398	regmap_write(iep->map, ICSS_IEP_CMP1_REG0, lower_32_bits(start_ns));399	if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT)400		regmap_write(iep->map, ICSS_IEP_CMP1_REG1, upper_32_bits(start_ns));401}402 403static int icss_iep_perout_enable_hw(struct icss_iep *iep,404				     struct ptp_perout_request *req, int on)405{406	int ret;407	u64 cmp;408 409	if (iep->ops && iep->ops->perout_enable) {410		ret = iep->ops->perout_enable(iep->clockops_data, req, on, &cmp);411		if (ret)412			return ret;413 414		if (on) {415			/* Configure CMP */416			regmap_write(iep->map, ICSS_IEP_CMP1_REG0, lower_32_bits(cmp));417			if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT)418				regmap_write(iep->map, ICSS_IEP_CMP1_REG1, upper_32_bits(cmp));419			/* Configure SYNC, 1ms pulse width */420			regmap_write(iep->map, ICSS_IEP_SYNC_PWIDTH_REG, 1000000);421			regmap_write(iep->map, ICSS_IEP_SYNC0_PERIOD_REG, 0);422			regmap_write(iep->map, ICSS_IEP_SYNC_START_REG, 0);423			regmap_write(iep->map, ICSS_IEP_SYNC_CTRL_REG, 0); /* one-shot mode */424			/* Enable CMP 1 */425			regmap_update_bits(iep->map, ICSS_IEP_CMP_CFG_REG,426					   IEP_CMP_CFG_CMP_EN(1), IEP_CMP_CFG_CMP_EN(1));427		} else {428			/* Disable CMP 1 */429			regmap_update_bits(iep->map, ICSS_IEP_CMP_CFG_REG,430					   IEP_CMP_CFG_CMP_EN(1), 0);431 432			/* clear regs */433			regmap_write(iep->map, ICSS_IEP_CMP1_REG0, 0);434			if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT)435				regmap_write(iep->map, ICSS_IEP_CMP1_REG1, 0);436		}437	} else {438		if (on) {439			u64 start_ns;440 441			iep->period = ((u64)req->period.sec * NSEC_PER_SEC) +442				      req->period.nsec;443			start_ns = ((u64)req->period.sec * NSEC_PER_SEC)444				   + req->period.nsec;445			icss_iep_update_to_next_boundary(iep, start_ns);446 447			/* Enable Sync in single shot mode  */448			regmap_write(iep->map, ICSS_IEP_SYNC_CTRL_REG,449				     IEP_SYNC_CTRL_SYNC_N_EN(0) | IEP_SYNC_CTRL_SYNC_EN);450			/* Enable CMP 1 */451			regmap_update_bits(iep->map, ICSS_IEP_CMP_CFG_REG,452					   IEP_CMP_CFG_CMP_EN(1), IEP_CMP_CFG_CMP_EN(1));453		} else {454			/* Disable CMP 1 */455			regmap_update_bits(iep->map, ICSS_IEP_CMP_CFG_REG,456					   IEP_CMP_CFG_CMP_EN(1), 0);457 458			/* clear CMP regs */459			regmap_write(iep->map, ICSS_IEP_CMP1_REG0, 0);460			if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT)461				regmap_write(iep->map, ICSS_IEP_CMP1_REG1, 0);462 463			/* Disable sync */464			regmap_write(iep->map, ICSS_IEP_SYNC_CTRL_REG, 0);465		}466	}467 468	return 0;469}470 471static int icss_iep_perout_enable(struct icss_iep *iep,472				  struct ptp_perout_request *req, int on)473{474	int ret = 0;475 476	mutex_lock(&iep->ptp_clk_mutex);477 478	if (iep->pps_enabled) {479		ret = -EBUSY;480		goto exit;481	}482 483	if (iep->perout_enabled == !!on)484		goto exit;485 486	ret = icss_iep_perout_enable_hw(iep, req, on);487	if (!ret)488		iep->perout_enabled = !!on;489 490exit:491	mutex_unlock(&iep->ptp_clk_mutex);492 493	return ret;494}495 496static void icss_iep_cap_cmp_work(struct work_struct *work)497{498	struct icss_iep *iep = container_of(work, struct icss_iep, work);499	const u32 *reg_offs = iep->plat_data->reg_offs;500	struct ptp_clock_event pevent;501	unsigned int val;502	u64 ns, ns_next;503 504	mutex_lock(&iep->ptp_clk_mutex);505 506	ns = readl(iep->base + reg_offs[ICSS_IEP_CMP1_REG0]);507	if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT) {508		val = readl(iep->base + reg_offs[ICSS_IEP_CMP1_REG1]);509		ns |= (u64)val << 32;510	}511	/* set next event */512	ns_next = ns + iep->period;513	writel(lower_32_bits(ns_next),514	       iep->base + reg_offs[ICSS_IEP_CMP1_REG0]);515	if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT)516		writel(upper_32_bits(ns_next),517		       iep->base + reg_offs[ICSS_IEP_CMP1_REG1]);518 519	pevent.pps_times.ts_real = ns_to_timespec64(ns);520	pevent.type = PTP_CLOCK_PPSUSR;521	pevent.index = 0;522	ptp_clock_event(iep->ptp_clock, &pevent);523	dev_dbg(iep->dev, "IEP:pps ts: %llu next:%llu:\n", ns, ns_next);524 525	mutex_unlock(&iep->ptp_clk_mutex);526}527 528static irqreturn_t icss_iep_cap_cmp_irq(int irq, void *dev_id)529{530	struct icss_iep *iep = (struct icss_iep *)dev_id;531	const u32 *reg_offs = iep->plat_data->reg_offs;532	unsigned int val;533 534	val = readl(iep->base + reg_offs[ICSS_IEP_CMP_STAT_REG]);535	/* The driver only enables CMP1 */536	if (val & BIT(1)) {537		/* Clear the event */538		writel(BIT(1), iep->base + reg_offs[ICSS_IEP_CMP_STAT_REG]);539		if (iep->pps_enabled || iep->perout_enabled)540			schedule_work(&iep->work);541		return IRQ_HANDLED;542	}543 544	return IRQ_NONE;545}546 547static int icss_iep_pps_enable(struct icss_iep *iep, int on)548{549	struct ptp_clock_request rq;550	struct timespec64 ts;551	int ret = 0;552	u64 ns;553 554	mutex_lock(&iep->ptp_clk_mutex);555 556	if (iep->perout_enabled) {557		ret = -EBUSY;558		goto exit;559	}560 561	if (iep->pps_enabled == !!on)562		goto exit;563 564	rq.perout.index = 0;565	if (on) {566		ns = icss_iep_gettime(iep, NULL);567		ts = ns_to_timespec64(ns);568		rq.perout.period.sec = 1;569		rq.perout.period.nsec = 0;570		rq.perout.start.sec = ts.tv_sec + 2;571		rq.perout.start.nsec = 0;572		ret = icss_iep_perout_enable_hw(iep, &rq.perout, on);573	} else {574		ret = icss_iep_perout_enable_hw(iep, &rq.perout, on);575		if (iep->cap_cmp_irq)576			cancel_work_sync(&iep->work);577	}578 579	if (!ret)580		iep->pps_enabled = !!on;581 582exit:583	mutex_unlock(&iep->ptp_clk_mutex);584 585	return ret;586}587 588static int icss_iep_extts_enable(struct icss_iep *iep, u32 index, int on)589{590	u32 val, cap, ret = 0;591 592	mutex_lock(&iep->ptp_clk_mutex);593 594	if (iep->ops && iep->ops->extts_enable) {595		ret = iep->ops->extts_enable(iep->clockops_data, index, on);596		goto exit;597	}598 599	if (((iep->latch_enable & BIT(index)) >> index) == on)600		goto exit;601 602	regmap_read(iep->map, ICSS_IEP_CAPTURE_CFG_REG, &val);603	cap = IEP_CAP_CFG_CAP_ASYNC_EN(index) | IEP_CAP_CFG_CAPNR_1ST_EVENT_EN(index);604	if (on) {605		val |= cap;606		iep->latch_enable |= BIT(index);607	} else {608		val &= ~cap;609		iep->latch_enable &= ~BIT(index);610	}611	regmap_write(iep->map, ICSS_IEP_CAPTURE_CFG_REG, val);612 613exit:614	mutex_unlock(&iep->ptp_clk_mutex);615 616	return ret;617}618 619static int icss_iep_ptp_enable(struct ptp_clock_info *ptp,620			       struct ptp_clock_request *rq, int on)621{622	struct icss_iep *iep = container_of(ptp, struct icss_iep, ptp_info);623 624	switch (rq->type) {625	case PTP_CLK_REQ_PEROUT:626		return icss_iep_perout_enable(iep, &rq->perout, on);627	case PTP_CLK_REQ_PPS:628		return icss_iep_pps_enable(iep, on);629	case PTP_CLK_REQ_EXTTS:630		return icss_iep_extts_enable(iep, rq->extts.index, on);631	default:632		break;633	}634 635	return -EOPNOTSUPP;636}637 638static struct ptp_clock_info icss_iep_ptp_info = {639	.owner		= THIS_MODULE,640	.name		= "ICSS IEP timer",641	.max_adj	= 10000000,642	.adjfine	= icss_iep_ptp_adjfine,643	.adjtime	= icss_iep_ptp_adjtime,644	.gettimex64	= icss_iep_ptp_gettimeex,645	.settime64	= icss_iep_ptp_settime,646	.enable		= icss_iep_ptp_enable,647};648 649struct icss_iep *icss_iep_get_idx(struct device_node *np, int idx)650{651	struct platform_device *pdev;652	struct device_node *iep_np;653	struct icss_iep *iep;654 655	iep_np = of_parse_phandle(np, "ti,iep", idx);656	if (!iep_np || !of_device_is_available(iep_np))657		return ERR_PTR(-ENODEV);658 659	pdev = of_find_device_by_node(iep_np);660	of_node_put(iep_np);661 662	if (!pdev)663		/* probably IEP not yet probed */664		return ERR_PTR(-EPROBE_DEFER);665 666	iep = platform_get_drvdata(pdev);667	if (!iep)668		return ERR_PTR(-EPROBE_DEFER);669 670	device_lock(iep->dev);671	if (iep->client_np) {672		device_unlock(iep->dev);673		dev_err(iep->dev, "IEP is already acquired by %s",674			iep->client_np->name);675		return ERR_PTR(-EBUSY);676	}677	iep->client_np = np;678	device_unlock(iep->dev);679	get_device(iep->dev);680 681	return iep;682}683EXPORT_SYMBOL_GPL(icss_iep_get_idx);684 685struct icss_iep *icss_iep_get(struct device_node *np)686{687	return icss_iep_get_idx(np, 0);688}689EXPORT_SYMBOL_GPL(icss_iep_get);690 691void icss_iep_put(struct icss_iep *iep)692{693	device_lock(iep->dev);694	iep->client_np = NULL;695	device_unlock(iep->dev);696	put_device(iep->dev);697}698EXPORT_SYMBOL_GPL(icss_iep_put);699 700void icss_iep_init_fw(struct icss_iep *iep)701{702	/* start IEP for FW use in raw 64bit mode, no PTP support */703	iep->clk_tick_time = iep->def_inc;704	iep->cycle_time_ns = 0;705	iep->ops = NULL;706	iep->clockops_data = NULL;707	icss_iep_set_default_inc(iep, iep->def_inc);708	icss_iep_set_compensation_inc(iep, iep->def_inc);709	icss_iep_set_compensation_count(iep, 0);710	regmap_write(iep->map, ICSS_IEP_SYNC_PWIDTH_REG, iep->refclk_freq / 10); /* 100 ms pulse */711	regmap_write(iep->map, ICSS_IEP_SYNC0_PERIOD_REG, 0);712	if (iep->plat_data->flags & ICSS_IEP_SLOW_COMPEN_REG_SUPPORT)713		icss_iep_set_slow_compensation_count(iep, 0);714 715	icss_iep_enable(iep);716	icss_iep_settime(iep, 0);717}718EXPORT_SYMBOL_GPL(icss_iep_init_fw);719 720void icss_iep_exit_fw(struct icss_iep *iep)721{722	icss_iep_disable(iep);723}724EXPORT_SYMBOL_GPL(icss_iep_exit_fw);725 726int icss_iep_init(struct icss_iep *iep, const struct icss_iep_clockops *clkops,727		  void *clockops_data, u32 cycle_time_ns)728{729	int ret = 0;730 731	iep->cycle_time_ns = cycle_time_ns;732	iep->clk_tick_time = iep->def_inc;733	iep->ops = clkops;734	iep->clockops_data = clockops_data;735	icss_iep_set_default_inc(iep, iep->def_inc);736	icss_iep_set_compensation_inc(iep, iep->def_inc);737	icss_iep_set_compensation_count(iep, 0);738	regmap_write(iep->map, ICSS_IEP_SYNC_PWIDTH_REG, iep->refclk_freq / 10); /* 100 ms pulse */739	regmap_write(iep->map, ICSS_IEP_SYNC0_PERIOD_REG, 0);740	if (iep->plat_data->flags & ICSS_IEP_SLOW_COMPEN_REG_SUPPORT)741		icss_iep_set_slow_compensation_count(iep, 0);742 743	if (!(iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT) ||744	    !(iep->plat_data->flags & ICSS_IEP_SLOW_COMPEN_REG_SUPPORT))745		goto skip_perout;746 747	if (iep->ops && iep->ops->perout_enable) {748		iep->ptp_info.n_per_out = 1;749		iep->ptp_info.pps = 1;750	} else if (iep->cap_cmp_irq) {751		iep->ptp_info.pps = 1;752	}753 754	if (iep->ops && iep->ops->extts_enable)755		iep->ptp_info.n_ext_ts = 2;756 757skip_perout:758	if (cycle_time_ns)759		icss_iep_enable_shadow_mode(iep);760	else761		icss_iep_enable(iep);762	icss_iep_settime(iep, ktime_get_real_ns());763 764	iep->ptp_clock = ptp_clock_register(&iep->ptp_info, iep->dev);765	if (IS_ERR(iep->ptp_clock)) {766		ret = PTR_ERR(iep->ptp_clock);767		iep->ptp_clock = NULL;768		dev_err(iep->dev, "Failed to register ptp clk %d\n", ret);769	}770 771	return ret;772}773EXPORT_SYMBOL_GPL(icss_iep_init);774 775int icss_iep_exit(struct icss_iep *iep)776{777	if (iep->ptp_clock) {778		ptp_clock_unregister(iep->ptp_clock);779		iep->ptp_clock = NULL;780	}781	icss_iep_disable(iep);782 783	return 0;784}785EXPORT_SYMBOL_GPL(icss_iep_exit);786 787static int icss_iep_probe(struct platform_device *pdev)788{789	struct device *dev = &pdev->dev;790	struct icss_iep *iep;791	struct clk *iep_clk;792	int ret, irq;793 794	iep = devm_kzalloc(dev, sizeof(*iep), GFP_KERNEL);795	if (!iep)796		return -ENOMEM;797 798	iep->dev = dev;799	iep->base = devm_platform_ioremap_resource(pdev, 0);800	if (IS_ERR(iep->base))801		return -ENODEV;802 803	irq = platform_get_irq_byname_optional(pdev, "iep_cap_cmp");804	if (irq == -EPROBE_DEFER)805		return irq;806 807	if (irq > 0) {808		ret = devm_request_irq(dev, irq, icss_iep_cap_cmp_irq,809				       IRQF_TRIGGER_HIGH, "iep_cap_cmp", iep);810		if (ret) {811			dev_info(iep->dev, "cap_cmp irq request failed: %x\n",812				 ret);813		} else {814			iep->cap_cmp_irq = irq;815			INIT_WORK(&iep->work, icss_iep_cap_cmp_work);816		}817	}818 819	iep_clk = devm_clk_get(dev, NULL);820	if (IS_ERR(iep_clk))821		return PTR_ERR(iep_clk);822 823	iep->refclk_freq = clk_get_rate(iep_clk);824 825	iep->def_inc = NSEC_PER_SEC / iep->refclk_freq;	/* ns per clock tick */826	if (iep->def_inc > IEP_MAX_DEF_INC) {827		dev_err(dev, "Failed to set def_inc %d.  IEP_clock is too slow to be supported\n",828			iep->def_inc);829		return -EINVAL;830	}831 832	iep->plat_data = device_get_match_data(dev);833	if (!iep->plat_data)834		return -EINVAL;835 836	iep->map = devm_regmap_init(dev, NULL, iep, iep->plat_data->config);837	if (IS_ERR(iep->map)) {838		dev_err(dev, "Failed to create regmap for IEP %ld\n",839			PTR_ERR(iep->map));840		return PTR_ERR(iep->map);841	}842 843	iep->ptp_info = icss_iep_ptp_info;844	mutex_init(&iep->ptp_clk_mutex);845	dev_set_drvdata(dev, iep);846	icss_iep_disable(iep);847 848	return 0;849}850 851static bool am654_icss_iep_valid_reg(struct device *dev, unsigned int reg)852{853	switch (reg) {854	case ICSS_IEP_GLOBAL_CFG_REG ... ICSS_IEP_SYNC_START_REG:855		return true;856	default:857		return false;858	}859 860	return false;861}862 863static int icss_iep_regmap_write(void *context, unsigned int reg,864				 unsigned int val)865{866	struct icss_iep *iep = context;867 868	writel(val, iep->base + iep->plat_data->reg_offs[reg]);869 870	return 0;871}872 873static int icss_iep_regmap_read(void *context, unsigned int reg,874				unsigned int *val)875{876	struct icss_iep *iep = context;877 878	*val = readl(iep->base + iep->plat_data->reg_offs[reg]);879 880	return 0;881}882 883static const struct regmap_config am654_icss_iep_regmap_config = {884	.name = "icss iep",885	.reg_stride = 1,886	.reg_write = icss_iep_regmap_write,887	.reg_read = icss_iep_regmap_read,888	.writeable_reg = am654_icss_iep_valid_reg,889	.readable_reg = am654_icss_iep_valid_reg,890	.fast_io = 1,891};892 893static const struct icss_iep_plat_data am654_icss_iep_plat_data = {894	.flags = ICSS_IEP_64BIT_COUNTER_SUPPORT |895		 ICSS_IEP_SLOW_COMPEN_REG_SUPPORT |896		 ICSS_IEP_SHADOW_MODE_SUPPORT,897	.reg_offs = {898		[ICSS_IEP_GLOBAL_CFG_REG] = 0x00,899		[ICSS_IEP_COMPEN_REG] = 0x08,900		[ICSS_IEP_SLOW_COMPEN_REG] = 0x0C,901		[ICSS_IEP_COUNT_REG0] = 0x10,902		[ICSS_IEP_COUNT_REG1] = 0x14,903		[ICSS_IEP_CAPTURE_CFG_REG] = 0x18,904		[ICSS_IEP_CAPTURE_STAT_REG] = 0x1c,905 906		[ICSS_IEP_CAP6_RISE_REG0] = 0x50,907		[ICSS_IEP_CAP6_RISE_REG1] = 0x54,908 909		[ICSS_IEP_CAP7_RISE_REG0] = 0x60,910		[ICSS_IEP_CAP7_RISE_REG1] = 0x64,911 912		[ICSS_IEP_CMP_CFG_REG] = 0x70,913		[ICSS_IEP_CMP_STAT_REG] = 0x74,914		[ICSS_IEP_CMP0_REG0] = 0x78,915		[ICSS_IEP_CMP0_REG1] = 0x7c,916		[ICSS_IEP_CMP1_REG0] = 0x80,917		[ICSS_IEP_CMP1_REG1] = 0x84,918 919		[ICSS_IEP_CMP8_REG0] = 0xc0,920		[ICSS_IEP_CMP8_REG1] = 0xc4,921		[ICSS_IEP_SYNC_CTRL_REG] = 0x180,922		[ICSS_IEP_SYNC0_STAT_REG] = 0x188,923		[ICSS_IEP_SYNC1_STAT_REG] = 0x18c,924		[ICSS_IEP_SYNC_PWIDTH_REG] = 0x190,925		[ICSS_IEP_SYNC0_PERIOD_REG] = 0x194,926		[ICSS_IEP_SYNC1_DELAY_REG] = 0x198,927		[ICSS_IEP_SYNC_START_REG] = 0x19c,928	},929	.config = &am654_icss_iep_regmap_config,930};931 932static const struct of_device_id icss_iep_of_match[] = {933	{934		.compatible = "ti,am654-icss-iep",935		.data = &am654_icss_iep_plat_data,936	},937	{},938};939MODULE_DEVICE_TABLE(of, icss_iep_of_match);940 941static struct platform_driver icss_iep_driver = {942	.driver = {943		.name = "icss-iep",944		.of_match_table = icss_iep_of_match,945	},946	.probe = icss_iep_probe,947};948module_platform_driver(icss_iep_driver);949 950MODULE_LICENSE("GPL");951MODULE_DESCRIPTION("TI ICSS IEP driver");952MODULE_AUTHOR("Roger Quadros <rogerq@ti.com>");953MODULE_AUTHOR("Md Danish Anwar <danishanwar@ti.com>");954