Linux preempt-rt

Check our new training course

Real-Time Linux with PREEMPT_RT

Check our new training course
with Creative Commons CC-BY-SA
lecture and lab materials

Bootlin logo

Elixir Cross Referencer

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
/*
 * Copyright (c) 2021 Microchip Technology Inc.
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#define DT_DRV_COMPAT microchip_xec_pcr

#include <soc.h>
#include <arch/cpu.h>
#include <arch/arm/aarch32/cortex_m/cmsis.h>
#include <drivers/clock_control.h>
#include <drivers/clock_control/mchp_xec_clock_control.h>
#include <dt-bindings/clock/mchp_xec_pcr.h>

#include <logging/log.h>
LOG_MODULE_REGISTER(clock_control_xec, LOG_LEVEL_ERR);

#define CLK32K_SIL_OSC_DELAY		256
#define CLK32K_PLL_LOCK_WAIT		(16 * 1024)
#define CLK32K_PIN_WAIT			4096
#define CLK32K_XTAL_WAIT		(16 * 1024)
#define CLK32K_XTAL_MON_WAIT		(64 * 1024)

/*
 * Counter checks:
 * 32KHz period counter minimum for pass/fail: 16-bit
 * 32KHz period counter maximum for pass/fail: 16-bit
 * 32KHz duty cycle variation max for pass/fail: 16-bit
 * 32KHz valid count minimum: 8-bit
 *
 * 32768 Hz period is 30.518 us
 * HW count resolution is 48 MHz.
 * One 32KHz clock pulse = 1464.84 48 MHz counts.
 */
#define CNT32K_TMIN			1435
#define CNT32K_TMAX			1495
#define CNT32K_DUTY_MAX			74
#define CNT32K_VAL_MIN			4

#define DEST_PLL			0
#define DEST_PERIPH			1

#define CLK32K_FLAG_CRYSTAL_SE		BIT(0)
#define CLK32K_FLAG_PIN_FB_CRYSTAL	BIT(1)

#define PCR_PERIPH_RESET_SPIN		8u

#define HIBTIMER_10_MS			328u
#define HIBTIMER_300_MS			9830u

#define PCR_XEC_REG_BASE						\
	((struct pcr_regs *)(DT_REG_ADDR(DT_NODELABEL(pcr))))

#define HIBTIMER_0_XEC_REG_BASE						\
	((struct htmr_regs *)(DT_REG_ADDR(DT_NODELABEL(hibtimer0))))

#define GIRQ23_XEC_REG_BASE						\
	((struct girq_regs *)(DT_REG_ADDR(DT_NODELABEL(girq23))))

enum clk32k_src {
	CLK32K_SRC_SIL_OSC = 0,
	CLK32K_SRC_CRYSTAL,
	CLK32K_SRC_MAX
};

enum clk32k_dest { CLK32K_DEST_PLL = 0, CLK32K_DEST_PERIPH, CLK32K_DEST_MAX };


/* Driver config */
struct xec_pcr_config {
	uintptr_t pcr_base; /* pcr base address */
	uintptr_t vbr_base; /* vbat registers base address */
};

/* Driver convenience defines */

#define PCR_NODE_LBL	DT_NODELABEL(pcr)

#define XEC_CORE_CLK_DIV \
	DT_PROP_OR(PCR_NODE_LBL, core_clk_div, CONFIG_SOC_MEC172X_PROC_CLK_DIV)

#define DRV_CONFIG(dev) \
	((const struct xec_pcr_config *)(dev)->config)

#define XEC_PCR_REGS_BASE(dev) \
	(struct pcr_regs *)(DRV_CONFIG(dev)->pcr_base)

#define XEC_VBATR_REGS_BASE(dev) \
	(struct vbatr_regs *)(DRV_CONFIG(dev)->vbr_base)

/*
 * In early Zephyr initialization we don't have timer services. Also, the SoC
 * may be running on its ring oscillator (+/- 50% accuracy). Configuring the
 * SoC's clock subsystem requires wait/delays. We implement a simple delay
 * by writing to a read-only hardware register in the PCR block.
 */
static uint32_t spin_delay(struct pcr_regs *pcr, uint32_t cnt)
{
	uint32_t n;

	for (n = 0U; n < cnt; n++) {
		pcr->OSC_ID = n;
	}

	return n;
}

/*
 * Make sure PCR sleep enables are clear except for crypto
 * which do not have internal clock gating.
 */
static void pcr_slp_init(struct pcr_regs *pcr)
{
	pcr->SYS_SLP_CTRL = 0U;
	SCB->SCR &= ~BIT(2);

	for (int i = 0; i < MCHP_MAX_PCR_SCR_REGS; i++) {
		pcr->SLP_EN[i] = 0U;
	}

	pcr->SLP_EN[3] = MCHP_PCR3_CRYPTO_MASK;
}

static bool is_sil_osc_enabled(struct vbatr_regs *vbr)
{
	if (vbr->CLK32_SRC & MCHP_VBATR_CS_SO_EN) {
		return true;
	}

	return false;
}

static void enable_sil_osc(struct vbatr_regs *vbr)
{
	vbr->CLK32_SRC |= MCHP_VBATR_CS_SO_EN;
}

/* caller has enabled internal silicon 32 KHz oscillator */
static void hib_timer_delay(uint16_t hib_timer_count)
{
	struct htmr_regs *htmr0 = HIBTIMER_0_XEC_REG_BASE;
	struct girq_regs *girq23 = GIRQ23_XEC_REG_BASE;

	htmr0->PRLD = 0; /* disable */
	htmr0->CTRL = 0; /* 32k time base */
	girq23->SRC = BIT(16); /* clear hibernation timer 0 status */
	htmr0->PRLD = hib_timer_count;
	if (hib_timer_count == 0) {
		return;
	}

	while ((girq23->SRC & BIT(16)) == 0) {
		;
	}

	girq23->SRC = BIT(16);
	htmr0->PRLD = 0; /* disable */
}

/*
 * Start external 32 KHz crystal.
 * Assumes peripheral clocks source is Silicon OSC.
 * If current configuration matches desired crystal configuration do nothing.
 * NOTE: Crystal requires ~300 ms to stabilize.
 */
static int enable_32k_crystal(const struct device *dev, uint32_t flags)
{
	struct vbatr_regs *const vbr = XEC_VBATR_REGS_BASE(dev);
	uint32_t vbcs = vbr->CLK32_SRC;
	uint32_t cfg = MCHP_VBATR_CS_XTAL_EN;

	if (flags & CLK32K_FLAG_CRYSTAL_SE) {
		cfg |= MCHP_VBATR_CS_XTAL_SE;
	}

	if ((vbcs & cfg) == cfg) {
		return 0;
	}

	/* Configure crystal connection before enabling the crystal. */
	vbr->CLK32_SRC &= ~(MCHP_VBATR_CS_XTAL_SE | MCHP_VBATR_CS_XTAL_DHC |
			    MCHP_VBATR_CS_XTAL_CNTR_MSK);
	if (flags & CLK32K_FLAG_CRYSTAL_SE) {
		vbr->CLK32_SRC |= MCHP_VBATR_CS_XTAL_SE;
	}

	/* Set crystal gain */
	vbr->CLK32_SRC |= MCHP_VBATR_CS_XTAL_CNTR_DG;

	/* enable crystal */
	vbr->CLK32_SRC |= MCHP_VBATR_CS_XTAL_EN;
	/* wait for crystal stabilization */
	hib_timer_delay(HIBTIMER_300_MS);
	/* turn off crystal high startup current */
	vbr->CLK32_SRC |= MCHP_VBATR_CS_XTAL_DHC;

	return 0;
}

/*
 * Use PCR clock monitor hardware to test crystal output.
 * Requires crystal to have stabilized after enable.
 * When enabled the clock monitor hardware measures high/low, edges, and
 * duty cycle and compares to programmed limits.
 */
static int check_32k_crystal(const struct device *dev)
{
	struct pcr_regs *const pcr = XEC_PCR_REGS_BASE(dev);
	struct htmr_regs *htmr0 = HIBTIMER_0_XEC_REG_BASE;
	struct girq_regs *girq23 = GIRQ23_XEC_REG_BASE;
	uint32_t status = 0;
	int rc = 0;

	htmr0->PRLD = 0;
	htmr0->CTRL = 0;
	girq23->SRC = BIT(16);

	pcr->CNT32K_CTRL = 0U;
	pcr->CLK32K_MON_IEN = 0U;
	pcr->CLK32K_MON_ISTS = MCHP_PCR_CLK32M_ISTS_MASK;

	pcr->CNT32K_PER_MIN = CNT32K_TMIN;
	pcr->CNT32K_PER_MAX = CNT32K_TMAX;
	pcr->CNT32K_DV_MAX = CNT32K_DUTY_MAX;
	pcr->CNT32K_VALID_MIN = CNT32K_VAL_MIN;

	pcr->CNT32K_CTRL =
		MCHP_PCR_CLK32M_CTRL_PER_EN | MCHP_PCR_CLK32M_CTRL_DC_EN |
		MCHP_PCR_CLK32M_CTRL_VAL_EN | MCHP_PCR_CLK32M_CTRL_CLR_CNT;

	rc = -ETIMEDOUT;
	htmr0->PRLD = HIBTIMER_10_MS;
	status = pcr->CLK32K_MON_ISTS;

	while ((girq23->SRC & BIT(16)) == 0) {
		if (status == (MCHP_PCR_CLK32M_ISTS_PULSE_RDY |
			       MCHP_PCR_CLK32M_ISTS_PASS_PER |
			       MCHP_PCR_CLK32M_ISTS_PASS_DC |
			       MCHP_PCR_CLK32M_ISTS_VALID)) {
			rc = 0;
			break;
		}

		if (status & (MCHP_PCR_CLK32M_ISTS_FAIL |
			      MCHP_PCR_CLK32M_ISTS_STALL)) {
			rc = -EBUSY;
			break;
		}

		status = pcr->CLK32K_MON_ISTS;
	}

	pcr->CNT32K_CTRL = 0u;
	htmr0->PRLD = 0;
	girq23->SRC = BIT(16);

	return rc;
}

/*
 * Set the clock source for either PLL or Peripheral-32K clock domain.
 * The source must be a stable 32 KHz input: internal silicon oscillator,
 * external crystal (parallel or single ended connection), or a 50% duty cycle
 * waveform on the 32KHZ_PIN. The driver does not implement 32KHZ_PIN support
 * at this time.
 */
static void connect_32k_source(const struct device *dev, enum clk32k_src src,
			       enum clk32k_dest dest, uint32_t flags)
{
	struct pcr_regs *const pcr = XEC_PCR_REGS_BASE(dev);
	struct vbatr_regs *const vbr = XEC_VBATR_REGS_BASE(dev);

	if (dest == CLK32K_DEST_PLL) {
		switch (src) {
		case CLK32K_SRC_SIL_OSC:
			pcr->CLK32K_SRC_VTR = MCHP_PCR_VTR_32K_SRC_SILOSC;
			break;
		case CLK32K_SRC_CRYSTAL:
			pcr->CLK32K_SRC_VTR = MCHP_PCR_VTR_32K_SRC_XTAL;
			break;
		default: /* do not touch HW */
			break;
		}
	} else if (dest == CLK32K_DEST_PERIPH) {
		uint32_t vbcs = vbr->CLK32_SRC & ~(MCHP_VBATR_CS_PCS_MSK);

		switch (src) {
		case CLK32K_SRC_SIL_OSC:
			vbr->CLK32_SRC = vbcs | MCHP_VBATR_CS_PCS_VTR_VBAT_SO;
			break;
		case CLK32K_SRC_CRYSTAL:
			vbr->CLK32_SRC = vbcs | MCHP_VBATR_CS_PCS_VTR_VBAT_XTAL;
			break;
		default: /* do not touch HW */
			break;
		}
	}
}

/*
 * This routine checks if the PLL is locked to its input source. Minimum lock
 * time is 3.3 ms. Lock time can be larger when the source is an external
 * crystal. Crystal cold start times may vary greatly based on many factors.
 * Crystals do not like being power cycled.
 */
static int pll_wait_lock(struct pcr_regs *const pcr, uint32_t wait_cnt)
{
	while (!(pcr->OSC_ID & MCHP_PCR_OSC_ID_PLL_LOCK)) {
		if (wait_cnt == 0) {
			return -ETIMEDOUT;
		}
		--wait_cnt;
	}

	return 0;
}

/*
 * MEC172x has two 32 KHz clock domains
 *   PLL domain: 32 KHz clock input for PLL to produce 96 MHz and 48 MHz clocks
 *   Peripheral domain: 32 KHz clock for subset of peripherals.
 * Each domain 32 KHz clock input can be from one of the following sources:
 *   Internal Silicon oscillator: +/- 2%
 *   External Crystal connected as parallel or single ended
 *   External 32KHZ_PIN 50% duty cycle waveform with fall back to either
 *     Silicon OSC or crystal when 32KHZ_PIN signal goes away or VTR power rail
 *     goes off.
 * At chip reset the PLL is held in reset and the +/- 50% ring oscillator is
 * the main clock.
 * If no VBAT reset occurs the VBAT 32 KHz soure register maintains its state.
 */
static int soc_clk32_init(const struct device *dev,
			  enum clk32k_src pll_clk_src,
			  enum clk32k_src periph_clk_src,
			  uint32_t flags)
{
	struct pcr_regs *const pcr = XEC_PCR_REGS_BASE(dev);
	struct vbatr_regs *const vbr = XEC_VBATR_REGS_BASE(dev);
	int rc = 0;

	/* disable PCR 32K monitor and clear counters */
	pcr->CNT32K_CTRL = MCHP_PCR_CLK32M_CTRL_CLR_CNT;
	pcr->CLK32K_MON_ISTS = MCHP_PCR_CLK32M_ISTS_MASK;
	pcr->CLK32K_MON_IEN = 0;

	if (!is_sil_osc_enabled(vbr)) {
		enable_sil_osc(vbr);
		spin_delay(pcr, CLK32K_SIL_OSC_DELAY);
	}

	/* Default to 32KHz Silicon OSC for PLL and peripherals */
	connect_32k_source(dev, CLK32K_SRC_SIL_OSC,  CLK32K_DEST_PLL, 0);
	connect_32k_source(dev, CLK32K_SRC_SIL_OSC, CLK32K_DEST_PERIPH, 0);

	rc = pll_wait_lock(pcr, CLK32K_PLL_LOCK_WAIT);
	if (rc) {
		return rc;
	}

	/* We only allow Silicon OSC or Crystal as a source. */
	if ((pll_clk_src == CLK32K_SRC_CRYSTAL) ||
	    (periph_clk_src == CLK32K_SRC_CRYSTAL)) {
		enable_32k_crystal(dev, flags);
		rc = check_32k_crystal(dev);
		if (rc) {
			/* disable crystal */
			vbr->CLK32_SRC &= ~(MCHP_VBATR_CS_XTAL_EN);
			return rc;
		}
		if (pll_clk_src == CLK32K_SRC_CRYSTAL) {
			connect_32k_source(dev, CLK32K_SRC_CRYSTAL,
					   CLK32K_DEST_PLL, flags);
		}
		if (periph_clk_src == CLK32K_SRC_CRYSTAL) {
			connect_32k_source(dev, CLK32K_SRC_CRYSTAL,
					   CLK32K_DEST_PERIPH, flags);
		}
		rc = pll_wait_lock(pcr, CLK32K_PLL_LOCK_WAIT);
	}

	return rc;
}

/*
 * MEC172x Errata document DS80000913C
 * Programming the PCR clock divider that divides the clock input to the ARM
 * Cortex-M4 may cause a clock glitch. The recommended work-around is to
 * issue four NOP instruction before and after the write to the PCR processor
 * clock control register. The final four NOP instructions are followed by
 * data and instruction barriers to flush the Cortex-M4's pipeline.
 * NOTE: Zephyr provides inline functions for Cortex-Mx NOP but not for
 * data and instruction barrier instructions. Caller's should only invoke this
 * function with interrupts locked.
 */
static void xec_clock_control_core_clock_divider_set(uint8_t clkdiv)
{
	struct pcr_regs *const pcr =
		(struct pcr_regs *)(DT_REG_ADDR(DT_NODELABEL(pcr)));

	arch_nop();
	arch_nop();
	arch_nop();
	arch_nop();
	pcr->PROC_CLK_CTRL = (uint32_t)clkdiv;
	arch_nop();
	arch_nop();
	arch_nop();
	arch_nop();
	__DSB();
	__ISB();
}

/*
 * PCR peripheral sleep enable allows the clocks to a specific peripheral to
 * be gated off if the peripheral is not requesting a clock.
 * slp_idx = zero based index into 32-bit PCR sleep enable registers.
 * slp_pos = bit position in the register
 * slp_en if non-zero set the bit else clear the bit
 */
int z_mchp_xec_pcr_periph_sleep(uint8_t slp_idx, uint8_t slp_pos,
				uint8_t slp_en)
{
	struct pcr_regs *regs = PCR_XEC_REG_BASE;

	if ((slp_idx >= MCHP_MAX_PCR_SCR_REGS) || (slp_pos >= 32)) {
		return -EINVAL;
	}

	if (slp_en) {
		regs->SLP_EN[slp_idx] |= BIT(slp_pos);
	} else {
		regs->SLP_EN[slp_idx] &= ~BIT(slp_pos);
	}

	return 0;
}

/* clock control driver API implementation */

static int xec_cc_on(const struct device *dev,
		     clock_control_subsys_t sub_system,
		     bool turn_on)
{
	struct pcr_regs *const pcr = XEC_PCR_REGS_BASE(dev);
	struct mchp_xec_pcr_clk_ctrl *cc =
		(struct mchp_xec_pcr_clk_ctrl *)sub_system;
	uint16_t pcr_idx = 0;
	uint16_t bitpos = 0;

	if (!cc) {
		return -EINVAL;
	}

	switch (MCHP_XEC_CLK_SRC_GET(cc->pcr_info)) {
	case MCHP_XEC_PCR_CLK_CORE:
	case MCHP_XEC_PCR_CLK_BUS:
		break;
	case MCHP_XEC_PCR_CLK_CPU:
		if (cc->pcr_info & MCHP_XEC_CLK_CPU_MASK) {
			uint32_t lock = irq_lock();

			xec_clock_control_core_clock_divider_set(
				cc->pcr_info & MCHP_XEC_CLK_CPU_MASK);

			irq_unlock(lock);
		} else {
			return -EINVAL;
		}
		break;
	case MCHP_XEC_PCR_CLK_PERIPH:
	case MCHP_XEC_PCR_CLK_PERIPH_FAST:
		pcr_idx = MCHP_XEC_PCR_SCR_GET_IDX(cc->pcr_info);
		bitpos = MCHP_XEC_PCR_SCR_GET_BITPOS(cc->pcr_info);

		if (pcr_idx >= MCHP_MAX_PCR_SCR_REGS) {
			return -EINVAL;
		}

		if (turn_on) {
			pcr->SLP_EN[pcr_idx] &= ~BIT(bitpos);
		} else {
			pcr->SLP_EN[pcr_idx] |= BIT(bitpos);
		}
		break;
	case MCHP_XEC_PCR_CLK_PERIPH_SLOW:
		if (turn_on) {
			pcr->SLOW_CLK_CTRL =
				cc->pcr_info & MCHP_XEC_CLK_SLOW_MASK;
		} else {
			pcr->SLOW_CLK_CTRL = 0;
		}
		break;
	default:
		return -EINVAL;
	}

	return 0;
}

/*
 * Turn on requested clock source.
 * Core, CPU, and Bus clocks are always on except in deep sleep state.
 * Peripheral clocks can be gated off if the peripheral's PCR sleep enable
 * is set and the peripheral indicates it does not need a clock by clearing
 * its PCR CLOCK_REQ read-only status.
 * Peripheral slow clock my be turned on by writing a non-zero divider value
 * to its PCR control register.
 */
static int xec_clock_control_on(const struct device *dev,
				clock_control_subsys_t sub_system)
{
	return xec_cc_on(dev, sub_system, true);
}

/*
 * Turn off clock source.
 * Core, CPU, and Bus clocks are always on except in deep sleep when PLL is
 * turned off. Exception is 32 KHz clock.
 * Peripheral clocks are gated off when the peripheral's sleep enable is set
 * and the peripheral indicates is no longer needs a clock by de-asserting
 * its read-only PCR CLOCK_REQ bit.
 * Peripheral slow clock can be turned off by writing 0 to its control register.
 */
static inline int xec_clock_control_off(const struct device *dev,
					clock_control_subsys_t sub_system)
{
	return xec_cc_on(dev, sub_system, false);
}

/*
 * MEC172x clock subsystem:
 * Two main clock domains: PLL and Peripheral-32K. Each domain's 32 KHz source
 * can be selected from one of three inputs:
 *  internal silicon OSC +/- 2% accuracy
 *  external crystal connected parallel or single ended
 *  external 32 KHz 50% duty cycle waveform on 32KHZ_IN pin.
 * PLL domain supplies 96 MHz, 48 MHz, and other high speed clocks to all
 * peripherals except those in the Peripheral-32K clock domain. The slow clock
 * is derived from the 48 MHz produced by the PLL.
 *   ARM Cortex-M4 core input: 96MHz
 *   AHB clock input: 48 MHz
 *   Fast AHB peripherals: 96 MHz internal and 48 MHz AHB interface.
 *   Slow clock peripherals: PWM,  TACH, PROCHOT
 * Peripheral-32K domain peripherals:
 *   WDT, RTC, RTOS timer, hibernation timers, week timer
 *
 * Peripherals using both PLL and 32K clock domains:
 *   BBLED, RPMFAN
 */
static int xec_clock_control_get_subsys_rate(const struct device *dev,
					     clock_control_subsys_t sub_system,
					     uint32_t *rate)
{
	struct pcr_regs *const pcr = XEC_PCR_REGS_BASE(dev);
	uint32_t bus = (uint32_t)sub_system;
	uint32_t temp = 0;

	switch (bus) {
	case MCHP_XEC_PCR_CLK_CORE:
	case MCHP_XEC_PCR_CLK_PERIPH_FAST:
		*rate = MHZ(96);
		break;
	case MCHP_XEC_PCR_CLK_CPU:
		/* if PCR PROC_CLK_CTRL is 0 the chip is not running */
		*rate = MHZ(96) / pcr->PROC_CLK_CTRL;
		break;
	case MCHP_XEC_PCR_CLK_BUS:
	case MCHP_XEC_PCR_CLK_PERIPH:
		*rate = MHZ(48);
		break;
	case MCHP_XEC_PCR_CLK_PERIPH_SLOW:
		temp = pcr->SLOW_CLK_CTRL;
		if (pcr->SLOW_CLK_CTRL) {
			*rate = MHZ(48) / temp;
		} else {
			*rate = 0; /* slow clock off */
		}
		break;
	default:
		*rate = 0;
		return -EINVAL;
	}

	return 0;
}

#if defined(CONFIG_PM)
void mchp_xec_clk_ctrl_sys_sleep_enable(bool is_deep)
{
	struct pcr_regs *const pcr =
		(struct pcr_regs *)(DT_REG_ADDR(DT_NODELABEL(pcr)));
	uint32_t sys_sleep_mode = MCHP_PCR_SYS_SLP_CTRL_SLP_ALL;

	if (is_deep) {
		sys_sleep_mode |= MCHP_PCR_SYS_SLP_CTRL_SLP_HEAVY;
	}

	SCB->SCR |= BIT(2);
	pcr->SYS_SLP_CTRL = sys_sleep_mode;
}

void mchp_xec_clk_ctrl_sys_sleep_disable(void)
{
	struct pcr_regs *const pcr =
		(struct pcr_regs *)(DT_REG_ADDR(DT_NODELABEL(pcr)));

	pcr->SYS_SLP_CTRL = 0;
	SCB->SCR &= ~BIT(2);
}
#endif

/* Clock controller driver registration */
static struct clock_control_driver_api xec_clock_control_api = {
	.on = xec_clock_control_on,
	.off = xec_clock_control_off,
	.get_rate = xec_clock_control_get_subsys_rate,
};

static int xec_clock_control_init(const struct device *dev)
{
	int rc = 0;
	uint32_t clk32_flags = 0;
	struct pcr_regs *const pcr = XEC_PCR_REGS_BASE(dev);
	enum clk32k_src clk_src_pll =
		DT_PROP_OR(PCR_NODE_LBL, pll_32k_src, CLK32K_SRC_SIL_OSC);
	enum clk32k_src clk_src_periph =
		DT_PROP_OR(PCR_NODE_LBL, periph_32k_src, CLK32K_SRC_SIL_OSC);

	pcr_slp_init(pcr);

	rc = soc_clk32_init(dev, clk_src_pll, clk_src_periph, clk32_flags);
	__ASSERT(rc == 0, "XEC: PLL and 32 KHz clock initialization failed");

	xec_clock_control_core_clock_divider_set(XEC_CORE_CLK_DIV);

	return rc;
}

const struct xec_pcr_config xec_config = {
	.pcr_base = DT_INST_REG_ADDR_BY_IDX(0, 0),
	.vbr_base = DT_INST_REG_ADDR_BY_IDX(0, 1),
};

DEVICE_DT_INST_DEFINE(0,
		    &xec_clock_control_init,
		    NULL,
		    NULL, &xec_config,
		    PRE_KERNEL_1,
		    CONFIG_KERNEL_INIT_PRIORITY_OBJECTS,
		    &xec_clock_control_api);