162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Joshua Henderson <joshua.henderson@microchip.com>
462306a36Sopenharmony_ci * Copyright (C) 2015 Microchip Technology Inc.  All rights reserved.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci#include <asm/mach-pic32/pic32.h>
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include "pic32mzda.h"
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci/* Oscillators, PLL & clocks */
1162306a36Sopenharmony_ci#define ICLK_MASK	0x00000080
1262306a36Sopenharmony_ci#define PLLDIV_MASK	0x00000007
1362306a36Sopenharmony_ci#define CUROSC_MASK	0x00000007
1462306a36Sopenharmony_ci#define PLLMUL_MASK	0x0000007F
1562306a36Sopenharmony_ci#define PB_MASK		0x00000007
1662306a36Sopenharmony_ci#define FRC1		0
1762306a36Sopenharmony_ci#define FRC2		7
1862306a36Sopenharmony_ci#define SPLL		1
1962306a36Sopenharmony_ci#define POSC		2
2062306a36Sopenharmony_ci#define FRC_CLK		8000000
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define PIC32_POSC_FREQ	24000000
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define OSCCON		0x0000
2562306a36Sopenharmony_ci#define SPLLCON		0x0020
2662306a36Sopenharmony_ci#define PB1DIV		0x0140
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ciu32 pic32_get_sysclk(void)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	u32 osc_freq = 0;
3162306a36Sopenharmony_ci	u32 pllclk;
3262306a36Sopenharmony_ci	u32 frcdivn;
3362306a36Sopenharmony_ci	u32 osccon;
3462306a36Sopenharmony_ci	u32 spllcon;
3562306a36Sopenharmony_ci	int curr_osc;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	u32 plliclk;
3862306a36Sopenharmony_ci	u32 pllidiv;
3962306a36Sopenharmony_ci	u32 pllodiv;
4062306a36Sopenharmony_ci	u32 pllmult;
4162306a36Sopenharmony_ci	u32 frcdiv;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	void __iomem *osc_base = ioremap(PIC32_BASE_OSC, 0x200);
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	osccon = __raw_readl(osc_base + OSCCON);
4662306a36Sopenharmony_ci	spllcon = __raw_readl(osc_base + SPLLCON);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	plliclk = (spllcon & ICLK_MASK);
4962306a36Sopenharmony_ci	pllidiv = ((spllcon >> 8) & PLLDIV_MASK) + 1;
5062306a36Sopenharmony_ci	pllodiv = ((spllcon >> 24) & PLLDIV_MASK);
5162306a36Sopenharmony_ci	pllmult = ((spllcon >> 16) & PLLMUL_MASK) + 1;
5262306a36Sopenharmony_ci	frcdiv = ((osccon >> 24) & PLLDIV_MASK);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	pllclk = plliclk ? FRC_CLK : PIC32_POSC_FREQ;
5562306a36Sopenharmony_ci	frcdivn = ((1 << frcdiv) + 1) + (128 * (frcdiv == 7));
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	if (pllodiv < 2)
5862306a36Sopenharmony_ci		pllodiv = 2;
5962306a36Sopenharmony_ci	else if (pllodiv < 5)
6062306a36Sopenharmony_ci		pllodiv = (1 << pllodiv);
6162306a36Sopenharmony_ci	else
6262306a36Sopenharmony_ci		pllodiv = 32;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	curr_osc = (int)((osccon >> 12) & CUROSC_MASK);
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	switch (curr_osc) {
6762306a36Sopenharmony_ci	case FRC1:
6862306a36Sopenharmony_ci	case FRC2:
6962306a36Sopenharmony_ci		osc_freq = FRC_CLK / frcdivn;
7062306a36Sopenharmony_ci		break;
7162306a36Sopenharmony_ci	case SPLL:
7262306a36Sopenharmony_ci		osc_freq = ((pllclk / pllidiv) * pllmult) / pllodiv;
7362306a36Sopenharmony_ci		break;
7462306a36Sopenharmony_ci	case POSC:
7562306a36Sopenharmony_ci		osc_freq = PIC32_POSC_FREQ;
7662306a36Sopenharmony_ci		break;
7762306a36Sopenharmony_ci	default:
7862306a36Sopenharmony_ci		break;
7962306a36Sopenharmony_ci	}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	iounmap(osc_base);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	return osc_freq;
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ciu32 pic32_get_pbclk(int bus)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	u32 clk_freq;
8962306a36Sopenharmony_ci	void __iomem *osc_base = ioremap(PIC32_BASE_OSC, 0x200);
9062306a36Sopenharmony_ci	u32 pbxdiv = PB1DIV + ((bus - 1) * 0x10);
9162306a36Sopenharmony_ci	u32 pbdiv = (__raw_readl(osc_base + pbxdiv) & PB_MASK) + 1;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	iounmap(osc_base);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	clk_freq = pic32_get_sysclk();
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	return clk_freq / pbdiv;
9862306a36Sopenharmony_ci}
99