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 
pic32_get_sysclk(void)28 u32 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 
pic32_get_pbclk(int bus)86 u32 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