1 /*
2  * Joshua Henderson <joshua.henderson@microchip.com>
3  * Copyright (C) 2015 Microchip Technology Inc.  All rights reserved.
4  *
5  *  This program is free software; you can distribute it and/or modify it
6  *  under the terms of the GNU General Public License (Version 2) as
7  *  published by the Free Software Foundation.
8  *
9  *  This program is distributed in the hope it will be useful, but WITHOUT
10  *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  *  for more details.
13  */
14 #include <asm/mach-pic32/pic32.h>
15 
16 #include "pic32mzda.h"
17 
18 /* Oscillators, PLL & clocks */
19 #define ICLK_MASK	0x00000080
20 #define PLLDIV_MASK	0x00000007
21 #define CUROSC_MASK	0x00000007
22 #define PLLMUL_MASK	0x0000007F
23 #define PB_MASK		0x00000007
24 #define FRC1		0
25 #define FRC2		7
26 #define SPLL		1
27 #define POSC		2
28 #define FRC_CLK		8000000
29 
30 #define PIC32_POSC_FREQ	24000000
31 
32 #define OSCCON		0x0000
33 #define SPLLCON		0x0020
34 #define PB1DIV		0x0140
35 
36 u32 pic32_get_sysclk(void)
37 {
38 	u32 osc_freq = 0;
39 	u32 pllclk;
40 	u32 frcdivn;
41 	u32 osccon;
42 	u32 spllcon;
43 	int curr_osc;
44 
45 	u32 plliclk;
46 	u32 pllidiv;
47 	u32 pllodiv;
48 	u32 pllmult;
49 	u32 frcdiv;
50 
51 	void __iomem *osc_base = ioremap(PIC32_BASE_OSC, 0x200);
52 
53 	osccon = __raw_readl(osc_base + OSCCON);
54 	spllcon = __raw_readl(osc_base + SPLLCON);
55 
56 	plliclk = (spllcon & ICLK_MASK);
57 	pllidiv = ((spllcon >> 8) & PLLDIV_MASK) + 1;
58 	pllodiv = ((spllcon >> 24) & PLLDIV_MASK);
59 	pllmult = ((spllcon >> 16) & PLLMUL_MASK) + 1;
60 	frcdiv = ((osccon >> 24) & PLLDIV_MASK);
61 
62 	pllclk = plliclk ? FRC_CLK : PIC32_POSC_FREQ;
63 	frcdivn = ((1 << frcdiv) + 1) + (128 * (frcdiv == 7));
64 
65 	if (pllodiv < 2)
66 		pllodiv = 2;
67 	else if (pllodiv < 5)
68 		pllodiv = (1 << pllodiv);
69 	else
70 		pllodiv = 32;
71 
72 	curr_osc = (int)((osccon >> 12) & CUROSC_MASK);
73 
74 	switch (curr_osc) {
75 	case FRC1:
76 	case FRC2:
77 		osc_freq = FRC_CLK / frcdivn;
78 		break;
79 	case SPLL:
80 		osc_freq = ((pllclk / pllidiv) * pllmult) / pllodiv;
81 		break;
82 	case POSC:
83 		osc_freq = PIC32_POSC_FREQ;
84 		break;
85 	default:
86 		break;
87 	}
88 
89 	iounmap(osc_base);
90 
91 	return osc_freq;
92 }
93 
94 u32 pic32_get_pbclk(int bus)
95 {
96 	u32 clk_freq;
97 	void __iomem *osc_base = ioremap(PIC32_BASE_OSC, 0x200);
98 	u32 pbxdiv = PB1DIV + ((bus - 1) * 0x10);
99 	u32 pbdiv = (__raw_readl(osc_base + pbxdiv) & PB_MASK) + 1;
100 
101 	iounmap(osc_base);
102 
103 	clk_freq = pic32_get_sysclk();
104 
105 	return clk_freq / pbdiv;
106 }
107