cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

ocelot_ptp.c (9640B)


      1// SPDX-License-Identifier: (GPL-2.0 OR MIT)
      2/* Microsemi Ocelot PTP clock driver
      3 *
      4 * Copyright (c) 2017 Microsemi Corporation
      5 * Copyright 2020 NXP
      6 */
      7#include <linux/time64.h>
      8
      9#include <soc/mscc/ocelot_ptp.h>
     10#include <soc/mscc/ocelot_sys.h>
     11#include <soc/mscc/ocelot.h>
     12
     13int ocelot_ptp_gettime64(struct ptp_clock_info *ptp, struct timespec64 *ts)
     14{
     15	struct ocelot *ocelot = container_of(ptp, struct ocelot, ptp_info);
     16	unsigned long flags;
     17	time64_t s;
     18	u32 val;
     19	s64 ns;
     20
     21	spin_lock_irqsave(&ocelot->ptp_clock_lock, flags);
     22
     23	val = ocelot_read_rix(ocelot, PTP_PIN_CFG, TOD_ACC_PIN);
     24	val &= ~(PTP_PIN_CFG_SYNC | PTP_PIN_CFG_ACTION_MASK | PTP_PIN_CFG_DOM);
     25	val |= PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_SAVE);
     26	ocelot_write_rix(ocelot, val, PTP_PIN_CFG, TOD_ACC_PIN);
     27
     28	s = ocelot_read_rix(ocelot, PTP_PIN_TOD_SEC_MSB, TOD_ACC_PIN) & 0xffff;
     29	s <<= 32;
     30	s += ocelot_read_rix(ocelot, PTP_PIN_TOD_SEC_LSB, TOD_ACC_PIN);
     31	ns = ocelot_read_rix(ocelot, PTP_PIN_TOD_NSEC, TOD_ACC_PIN);
     32
     33	spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
     34
     35	/* Deal with negative values */
     36	if (ns >= 0x3ffffff0 && ns <= 0x3fffffff) {
     37		s--;
     38		ns &= 0xf;
     39		ns += 999999984;
     40	}
     41
     42	set_normalized_timespec64(ts, s, ns);
     43	return 0;
     44}
     45EXPORT_SYMBOL(ocelot_ptp_gettime64);
     46
     47int ocelot_ptp_settime64(struct ptp_clock_info *ptp,
     48			 const struct timespec64 *ts)
     49{
     50	struct ocelot *ocelot = container_of(ptp, struct ocelot, ptp_info);
     51	unsigned long flags;
     52	u32 val;
     53
     54	spin_lock_irqsave(&ocelot->ptp_clock_lock, flags);
     55
     56	val = ocelot_read_rix(ocelot, PTP_PIN_CFG, TOD_ACC_PIN);
     57	val &= ~(PTP_PIN_CFG_SYNC | PTP_PIN_CFG_ACTION_MASK | PTP_PIN_CFG_DOM);
     58	val |= PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_IDLE);
     59
     60	ocelot_write_rix(ocelot, val, PTP_PIN_CFG, TOD_ACC_PIN);
     61
     62	ocelot_write_rix(ocelot, lower_32_bits(ts->tv_sec), PTP_PIN_TOD_SEC_LSB,
     63			 TOD_ACC_PIN);
     64	ocelot_write_rix(ocelot, upper_32_bits(ts->tv_sec), PTP_PIN_TOD_SEC_MSB,
     65			 TOD_ACC_PIN);
     66	ocelot_write_rix(ocelot, ts->tv_nsec, PTP_PIN_TOD_NSEC, TOD_ACC_PIN);
     67
     68	val = ocelot_read_rix(ocelot, PTP_PIN_CFG, TOD_ACC_PIN);
     69	val &= ~(PTP_PIN_CFG_SYNC | PTP_PIN_CFG_ACTION_MASK | PTP_PIN_CFG_DOM);
     70	val |= PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_LOAD);
     71
     72	ocelot_write_rix(ocelot, val, PTP_PIN_CFG, TOD_ACC_PIN);
     73
     74	spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
     75	return 0;
     76}
     77EXPORT_SYMBOL(ocelot_ptp_settime64);
     78
     79int ocelot_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
     80{
     81	if (delta > -(NSEC_PER_SEC / 2) && delta < (NSEC_PER_SEC / 2)) {
     82		struct ocelot *ocelot = container_of(ptp, struct ocelot,
     83						     ptp_info);
     84		unsigned long flags;
     85		u32 val;
     86
     87		spin_lock_irqsave(&ocelot->ptp_clock_lock, flags);
     88
     89		val = ocelot_read_rix(ocelot, PTP_PIN_CFG, TOD_ACC_PIN);
     90		val &= ~(PTP_PIN_CFG_SYNC | PTP_PIN_CFG_ACTION_MASK |
     91			 PTP_PIN_CFG_DOM);
     92		val |= PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_IDLE);
     93
     94		ocelot_write_rix(ocelot, val, PTP_PIN_CFG, TOD_ACC_PIN);
     95
     96		ocelot_write_rix(ocelot, 0, PTP_PIN_TOD_SEC_LSB, TOD_ACC_PIN);
     97		ocelot_write_rix(ocelot, 0, PTP_PIN_TOD_SEC_MSB, TOD_ACC_PIN);
     98		ocelot_write_rix(ocelot, delta, PTP_PIN_TOD_NSEC, TOD_ACC_PIN);
     99
    100		val = ocelot_read_rix(ocelot, PTP_PIN_CFG, TOD_ACC_PIN);
    101		val &= ~(PTP_PIN_CFG_SYNC | PTP_PIN_CFG_ACTION_MASK |
    102			 PTP_PIN_CFG_DOM);
    103		val |= PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_DELTA);
    104
    105		ocelot_write_rix(ocelot, val, PTP_PIN_CFG, TOD_ACC_PIN);
    106
    107		spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
    108	} else {
    109		/* Fall back using ocelot_ptp_settime64 which is not exact. */
    110		struct timespec64 ts;
    111		u64 now;
    112
    113		ocelot_ptp_gettime64(ptp, &ts);
    114
    115		now = ktime_to_ns(timespec64_to_ktime(ts));
    116		ts = ns_to_timespec64(now + delta);
    117
    118		ocelot_ptp_settime64(ptp, &ts);
    119	}
    120	return 0;
    121}
    122EXPORT_SYMBOL(ocelot_ptp_adjtime);
    123
    124int ocelot_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
    125{
    126	struct ocelot *ocelot = container_of(ptp, struct ocelot, ptp_info);
    127	u32 unit = 0, direction = 0;
    128	unsigned long flags;
    129	u64 adj = 0;
    130
    131	spin_lock_irqsave(&ocelot->ptp_clock_lock, flags);
    132
    133	if (!scaled_ppm)
    134		goto disable_adj;
    135
    136	if (scaled_ppm < 0) {
    137		direction = PTP_CFG_CLK_ADJ_CFG_DIR;
    138		scaled_ppm = -scaled_ppm;
    139	}
    140
    141	adj = PSEC_PER_SEC << 16;
    142	do_div(adj, scaled_ppm);
    143	do_div(adj, 1000);
    144
    145	/* If the adjustment value is too large, use ns instead */
    146	if (adj >= (1L << 30)) {
    147		unit = PTP_CFG_CLK_ADJ_FREQ_NS;
    148		do_div(adj, 1000);
    149	}
    150
    151	/* Still too big */
    152	if (adj >= (1L << 30))
    153		goto disable_adj;
    154
    155	ocelot_write(ocelot, unit | adj, PTP_CLK_CFG_ADJ_FREQ);
    156	ocelot_write(ocelot, PTP_CFG_CLK_ADJ_CFG_ENA | direction,
    157		     PTP_CLK_CFG_ADJ_CFG);
    158
    159	spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
    160	return 0;
    161
    162disable_adj:
    163	ocelot_write(ocelot, 0, PTP_CLK_CFG_ADJ_CFG);
    164
    165	spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
    166	return 0;
    167}
    168EXPORT_SYMBOL(ocelot_ptp_adjfine);
    169
    170int ocelot_ptp_verify(struct ptp_clock_info *ptp, unsigned int pin,
    171		      enum ptp_pin_function func, unsigned int chan)
    172{
    173	switch (func) {
    174	case PTP_PF_NONE:
    175	case PTP_PF_PEROUT:
    176		break;
    177	case PTP_PF_EXTTS:
    178	case PTP_PF_PHYSYNC:
    179		return -1;
    180	}
    181	return 0;
    182}
    183EXPORT_SYMBOL(ocelot_ptp_verify);
    184
    185int ocelot_ptp_enable(struct ptp_clock_info *ptp,
    186		      struct ptp_clock_request *rq, int on)
    187{
    188	struct ocelot *ocelot = container_of(ptp, struct ocelot, ptp_info);
    189	struct timespec64 ts_phase, ts_period;
    190	enum ocelot_ptp_pins ptp_pin;
    191	unsigned long flags;
    192	bool pps = false;
    193	int pin = -1;
    194	s64 wf_high;
    195	s64 wf_low;
    196	u32 val;
    197
    198	switch (rq->type) {
    199	case PTP_CLK_REQ_PEROUT:
    200		/* Reject requests with unsupported flags */
    201		if (rq->perout.flags & ~(PTP_PEROUT_DUTY_CYCLE |
    202					 PTP_PEROUT_PHASE))
    203			return -EOPNOTSUPP;
    204
    205		pin = ptp_find_pin(ocelot->ptp_clock, PTP_PF_PEROUT,
    206				   rq->perout.index);
    207		if (pin == 0)
    208			ptp_pin = PTP_PIN_0;
    209		else if (pin == 1)
    210			ptp_pin = PTP_PIN_1;
    211		else if (pin == 2)
    212			ptp_pin = PTP_PIN_2;
    213		else if (pin == 3)
    214			ptp_pin = PTP_PIN_3;
    215		else
    216			return -EBUSY;
    217
    218		ts_period.tv_sec = rq->perout.period.sec;
    219		ts_period.tv_nsec = rq->perout.period.nsec;
    220
    221		if (ts_period.tv_sec == 1 && ts_period.tv_nsec == 0)
    222			pps = true;
    223
    224		/* Handle turning off */
    225		if (!on) {
    226			spin_lock_irqsave(&ocelot->ptp_clock_lock, flags);
    227			val = PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_IDLE);
    228			ocelot_write_rix(ocelot, val, PTP_PIN_CFG, ptp_pin);
    229			spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
    230			break;
    231		}
    232
    233		if (rq->perout.flags & PTP_PEROUT_PHASE) {
    234			ts_phase.tv_sec = rq->perout.phase.sec;
    235			ts_phase.tv_nsec = rq->perout.phase.nsec;
    236		} else {
    237			/* Compatibility */
    238			ts_phase.tv_sec = rq->perout.start.sec;
    239			ts_phase.tv_nsec = rq->perout.start.nsec;
    240		}
    241		if (ts_phase.tv_sec || (ts_phase.tv_nsec && !pps)) {
    242			dev_warn(ocelot->dev,
    243				 "Absolute start time not supported!\n");
    244			dev_warn(ocelot->dev,
    245				 "Accept nsec for PPS phase adjustment, otherwise start time should be 0 0.\n");
    246			return -EINVAL;
    247		}
    248
    249		/* Calculate waveform high and low times */
    250		if (rq->perout.flags & PTP_PEROUT_DUTY_CYCLE) {
    251			struct timespec64 ts_on;
    252
    253			ts_on.tv_sec = rq->perout.on.sec;
    254			ts_on.tv_nsec = rq->perout.on.nsec;
    255
    256			wf_high = timespec64_to_ns(&ts_on);
    257		} else {
    258			if (pps) {
    259				wf_high = 1000;
    260			} else {
    261				wf_high = timespec64_to_ns(&ts_period);
    262				wf_high = div_s64(wf_high, 2);
    263			}
    264		}
    265
    266		wf_low = timespec64_to_ns(&ts_period);
    267		wf_low -= wf_high;
    268
    269		/* Handle PPS request */
    270		if (pps) {
    271			spin_lock_irqsave(&ocelot->ptp_clock_lock, flags);
    272			ocelot_write_rix(ocelot, ts_phase.tv_nsec,
    273					 PTP_PIN_WF_LOW_PERIOD, ptp_pin);
    274			ocelot_write_rix(ocelot, wf_high,
    275					 PTP_PIN_WF_HIGH_PERIOD, ptp_pin);
    276			val = PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_CLOCK);
    277			val |= PTP_PIN_CFG_SYNC;
    278			ocelot_write_rix(ocelot, val, PTP_PIN_CFG, ptp_pin);
    279			spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
    280			break;
    281		}
    282
    283		/* Handle periodic clock */
    284		if (wf_high > 0x3fffffff || wf_high <= 0x6)
    285			return -EINVAL;
    286		if (wf_low > 0x3fffffff || wf_low <= 0x6)
    287			return -EINVAL;
    288
    289		spin_lock_irqsave(&ocelot->ptp_clock_lock, flags);
    290		ocelot_write_rix(ocelot, wf_low, PTP_PIN_WF_LOW_PERIOD,
    291				 ptp_pin);
    292		ocelot_write_rix(ocelot, wf_high, PTP_PIN_WF_HIGH_PERIOD,
    293				 ptp_pin);
    294		val = PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_CLOCK);
    295		ocelot_write_rix(ocelot, val, PTP_PIN_CFG, ptp_pin);
    296		spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
    297		break;
    298	default:
    299		return -EOPNOTSUPP;
    300	}
    301	return 0;
    302}
    303EXPORT_SYMBOL(ocelot_ptp_enable);
    304
    305int ocelot_init_timestamp(struct ocelot *ocelot,
    306			  const struct ptp_clock_info *info)
    307{
    308	struct ptp_clock *ptp_clock;
    309	int i;
    310
    311	ocelot->ptp_info = *info;
    312
    313	for (i = 0; i < OCELOT_PTP_PINS_NUM; i++) {
    314		struct ptp_pin_desc *p = &ocelot->ptp_pins[i];
    315
    316		snprintf(p->name, sizeof(p->name), "switch_1588_dat%d", i);
    317		p->index = i;
    318		p->func = PTP_PF_NONE;
    319	}
    320
    321	ocelot->ptp_info.pin_config = &ocelot->ptp_pins[0];
    322
    323	ptp_clock = ptp_clock_register(&ocelot->ptp_info, ocelot->dev);
    324	if (IS_ERR(ptp_clock))
    325		return PTR_ERR(ptp_clock);
    326	/* Check if PHC support is missing at the configuration level */
    327	if (!ptp_clock)
    328		return 0;
    329
    330	ocelot->ptp_clock = ptp_clock;
    331
    332	ocelot_write(ocelot, SYS_PTP_CFG_PTP_STAMP_WID(30), SYS_PTP_CFG);
    333	ocelot_write(ocelot, 0xffffffff, ANA_TABLES_PTP_ID_LOW);
    334	ocelot_write(ocelot, 0xffffffff, ANA_TABLES_PTP_ID_HIGH);
    335
    336	ocelot_write(ocelot, PTP_CFG_MISC_PTP_EN, PTP_CFG_MISC);
    337
    338	/* There is no device reconfiguration, PTP Rx stamping is always
    339	 * enabled.
    340	 */
    341	ocelot->hwtstamp_config.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
    342
    343	return 0;
    344}
    345EXPORT_SYMBOL(ocelot_init_timestamp);
    346
    347int ocelot_deinit_timestamp(struct ocelot *ocelot)
    348{
    349	if (ocelot->ptp_clock)
    350		ptp_clock_unregister(ocelot->ptp_clock);
    351	return 0;
    352}
    353EXPORT_SYMBOL(ocelot_deinit_timestamp);