1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * Joshua Henderson <joshua.henderson@microchip.com>
4 * Copyright (C) 2015 Microchip Technology Inc.  All rights reserved.
5 */
6#include <asm/mach-pic32/pic32.h>
7
8#include "pic32mzda.h"
9
10/* Oscillators, PLL & clocks */
11#define ICLK_MASK	0x00000080
12#define PLLDIV_MASK	0x00000007
13#define CUROSC_MASK	0x00000007
14#define PLLMUL_MASK	0x0000007F
15#define PB_MASK		0x00000007
16#define FRC1		0
17#define FRC2		7
18#define SPLL		1
19#define POSC		2
20#define FRC_CLK		8000000
21
22#define PIC32_POSC_FREQ	24000000
23
24#define OSCCON		0x0000
25#define SPLLCON		0x0020
26#define PB1DIV		0x0140
27
28u32 pic32_get_sysclk(void)
29{
30	u32 osc_freq = 0;
31	u32 pllclk;
32	u32 frcdivn;
33	u32 osccon;
34	u32 spllcon;
35	int curr_osc;
36
37	u32 plliclk;
38	u32 pllidiv;
39	u32 pllodiv;
40	u32 pllmult;
41	u32 frcdiv;
42
43	void __iomem *osc_base = ioremap(PIC32_BASE_OSC, 0x200);
44
45	osccon = __raw_readl(osc_base + OSCCON);
46	spllcon = __raw_readl(osc_base + SPLLCON);
47
48	plliclk = (spllcon & ICLK_MASK);
49	pllidiv = ((spllcon >> 8) & PLLDIV_MASK) + 1;
50	pllodiv = ((spllcon >> 24) & PLLDIV_MASK);
51	pllmult = ((spllcon >> 16) & PLLMUL_MASK) + 1;
52	frcdiv = ((osccon >> 24) & PLLDIV_MASK);
53
54	pllclk = plliclk ? FRC_CLK : PIC32_POSC_FREQ;
55	frcdivn = ((1 << frcdiv) + 1) + (128 * (frcdiv == 7));
56
57	if (pllodiv < 2)
58		pllodiv = 2;
59	else if (pllodiv < 5)
60		pllodiv = (1 << pllodiv);
61	else
62		pllodiv = 32;
63
64	curr_osc = (int)((osccon >> 12) & CUROSC_MASK);
65
66	switch (curr_osc) {
67	case FRC1:
68	case FRC2:
69		osc_freq = FRC_CLK / frcdivn;
70		break;
71	case SPLL:
72		osc_freq = ((pllclk / pllidiv) * pllmult) / pllodiv;
73		break;
74	case POSC:
75		osc_freq = PIC32_POSC_FREQ;
76		break;
77	default:
78		break;
79	}
80
81	iounmap(osc_base);
82
83	return osc_freq;
84}
85
86u32 pic32_get_pbclk(int bus)
87{
88	u32 clk_freq;
89	void __iomem *osc_base = ioremap(PIC32_BASE_OSC, 0x200);
90	u32 pbxdiv = PB1DIV + ((bus - 1) * 0x10);
91	u32 pbdiv = (__raw_readl(osc_base + pbxdiv) & PB_MASK) + 1;
92
93	iounmap(osc_base);
94
95	clk_freq = pic32_get_sysclk();
96
97	return clk_freq / pbdiv;
98}
99