xref: /openbsd-src/sys/arch/armv7/exynos/exclock.c (revision 9fdf0c627b1fec102f212f847a6f7676c1829e65)
1*9fdf0c62Smpi /* $OpenBSD: exclock.c,v 1.10 2021/10/24 17:52:27 mpi Exp $ */
207829fe8Sbmercer /*
307829fe8Sbmercer  * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se>
407829fe8Sbmercer  *
507829fe8Sbmercer  * Permission to use, copy, modify, and distribute this software for any
607829fe8Sbmercer  * purpose with or without fee is hereby granted, provided that the above
707829fe8Sbmercer  * copyright notice and this permission notice appear in all copies.
807829fe8Sbmercer  *
907829fe8Sbmercer  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1007829fe8Sbmercer  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1107829fe8Sbmercer  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1207829fe8Sbmercer  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1307829fe8Sbmercer  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1407829fe8Sbmercer  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1507829fe8Sbmercer  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1607829fe8Sbmercer  */
1707829fe8Sbmercer 
1807829fe8Sbmercer #include <sys/param.h>
1907829fe8Sbmercer #include <sys/systm.h>
2007829fe8Sbmercer #include <sys/device.h>
21fe1242bbSkettenis 
2207829fe8Sbmercer #include <machine/bus.h>
2307829fe8Sbmercer #include <machine/fdt.h>
24fe1242bbSkettenis 
25fe1242bbSkettenis #include <dev/ofw/openfirm.h>
26ebc46d47Skettenis #include <dev/ofw/ofw_clock.h>
27fe1242bbSkettenis #include <dev/ofw/fdt.h>
2807829fe8Sbmercer 
2907829fe8Sbmercer /* registers */
3007829fe8Sbmercer #define CLOCK_APLL_CON0				0x0100
3107829fe8Sbmercer #define CLOCK_APLL_CON1				0x0104
3207829fe8Sbmercer #define CLOCK_BPLL_CON0				0x0110
3307829fe8Sbmercer #define CLOCK_BPLL_CON1				0x0114
3407829fe8Sbmercer #define CLOCK_EPLL_CON0				0x0130
3507829fe8Sbmercer #define CLOCK_EPLL_CON1				0x0134
3607829fe8Sbmercer #define CLOCK_EPLL_CON2				0x0138
3707829fe8Sbmercer #define CLOCK_VPLL_CON0				0x0140
3807829fe8Sbmercer #define CLOCK_VPLL_CON1				0x0144
3907829fe8Sbmercer #define CLOCK_VPLL_CON2				0x0148
4007829fe8Sbmercer #define CLOCK_CLK_DIV_CPU0			0x0500
4107829fe8Sbmercer #define CLOCK_CLK_DIV_CPU1			0x0504
4207829fe8Sbmercer #define CLOCK_CLK_DIV_TOP0			0x0510
4307829fe8Sbmercer #define CLOCK_CLK_DIV_TOP1			0x0514
4407829fe8Sbmercer #define CLOCK_PLL_DIV2_SEL			0x0A24
4507829fe8Sbmercer #define CLOCK_MPLL_CON0				0x4100
4607829fe8Sbmercer #define CLOCK_MPLL_CON1				0x4104
4707829fe8Sbmercer 
4807829fe8Sbmercer /* bits and bytes */
4907829fe8Sbmercer #define MPLL_FOUT_SEL_SHIFT			0x4
5007829fe8Sbmercer #define MPLL_FOUT_SEL_MASK			0x1
5107829fe8Sbmercer #define BPLL_FOUT_SEL_SHIFT			0x0
5207829fe8Sbmercer #define BPLL_FOUT_SEL_MASK			0x1
5307829fe8Sbmercer 
5407998b61Skettenis #define HCLK_FREQ				24000000
5507829fe8Sbmercer 
5607829fe8Sbmercer #define HREAD4(sc, reg)							\
5707829fe8Sbmercer 	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
5807829fe8Sbmercer #define HWRITE4(sc, reg, val)						\
5907829fe8Sbmercer 	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
6007829fe8Sbmercer #define HSET4(sc, reg, bits)						\
6107829fe8Sbmercer 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
6207829fe8Sbmercer #define HCLR4(sc, reg, bits)						\
6307829fe8Sbmercer 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
6407829fe8Sbmercer 
6507829fe8Sbmercer struct exclock_softc {
6607829fe8Sbmercer 	struct device		sc_dev;
6707829fe8Sbmercer 	bus_space_tag_t		sc_iot;
6807829fe8Sbmercer 	bus_space_handle_t	sc_ioh;
69ebc46d47Skettenis 
70ebc46d47Skettenis 	struct clock_device	sc_cd;
7107829fe8Sbmercer };
7207829fe8Sbmercer 
7307829fe8Sbmercer enum clocks {
7407829fe8Sbmercer 	/* OSC */
7507829fe8Sbmercer 	OSC,		/* 24 MHz OSC */
7607829fe8Sbmercer 
7707829fe8Sbmercer 	/* PLLs */
7807829fe8Sbmercer 	APLL,		/* ARM core clock */
7907829fe8Sbmercer 	MPLL,		/* System bus clock for memory controller */
8007829fe8Sbmercer 	BPLL,		/* Graphic 3D processor clock and 1066 MHz clock for memory controller if necessary */
8107829fe8Sbmercer 	CPLL,		/* Multi Format Video Hardware Codec clock */
8207829fe8Sbmercer 	GPLL,		/* Graphic 3D processor clock or other clocks for DVFS flexibility */
8307829fe8Sbmercer 	EPLL,		/* Audio interface clocks and clocks for other external device interfaces */
8407829fe8Sbmercer 	VPLL,		/* dithered PLL, helps to reduce the EMI of display and camera */
8507998b61Skettenis 	KPLL,
8607829fe8Sbmercer };
8707829fe8Sbmercer 
8807829fe8Sbmercer struct exclock_softc *exclock_sc;
8907829fe8Sbmercer 
90fe1242bbSkettenis int	exclock_match(struct device *, void *, void *);
91fe1242bbSkettenis void	exclock_attach(struct device *, struct device *, void *);
9207998b61Skettenis uint32_t exclock_decode_pll_clk(enum clocks, unsigned int, unsigned int);
9307998b61Skettenis uint32_t exclock_get_pll_clk(struct exclock_softc *, enum clocks);
9407998b61Skettenis uint32_t exclock_get_armclk(struct exclock_softc *);
9507998b61Skettenis uint32_t exclock_get_kfcclk(struct exclock_softc *);
9607829fe8Sbmercer unsigned int exclock_get_i2cclk(void);
9707829fe8Sbmercer 
98*9fdf0c62Smpi const struct cfattach	exclock_ca = {
9907829fe8Sbmercer 	sizeof (struct exclock_softc), exclock_match, exclock_attach
10007829fe8Sbmercer };
10107829fe8Sbmercer 
10207829fe8Sbmercer struct cfdriver exclock_cd = {
10307829fe8Sbmercer 	NULL, "exclock", DV_DULL
10407829fe8Sbmercer };
10507829fe8Sbmercer 
10616042c3cSkettenis uint32_t exynos5250_get_frequency(void *, uint32_t *);
10716042c3cSkettenis int	exynos5250_set_frequency(void *, uint32_t *, uint32_t);
10816042c3cSkettenis void	exynos5250_enable(void *, uint32_t *, int);
10916042c3cSkettenis uint32_t exynos5420_get_frequency(void *, uint32_t *);
11016042c3cSkettenis int	exynos5420_set_frequency(void *, uint32_t *, uint32_t);
11116042c3cSkettenis void	exynos5420_enable(void *, uint32_t *, int);
112ebc46d47Skettenis 
11307829fe8Sbmercer int
exclock_match(struct device * parent,void * match,void * aux)114fe1242bbSkettenis exclock_match(struct device *parent, void *match, void *aux)
11507829fe8Sbmercer {
116fe1242bbSkettenis 	struct fdt_attach_args *faa = aux;
11707829fe8Sbmercer 
11807998b61Skettenis 	if (OF_is_compatible(faa->fa_node, "samsung,exynos5250-clock") ||
11907998b61Skettenis 	    OF_is_compatible(faa->fa_node, "samsung,exynos5800-clock"))
12007998b61Skettenis 		return 10;	/* Must beat syscon(4). */
12107998b61Skettenis 
12207998b61Skettenis 	return 0;
12307829fe8Sbmercer }
12407829fe8Sbmercer 
12507829fe8Sbmercer void
exclock_attach(struct device * parent,struct device * self,void * aux)126fe1242bbSkettenis exclock_attach(struct device *parent, struct device *self, void *aux)
12707829fe8Sbmercer {
12807829fe8Sbmercer 	struct exclock_softc *sc = (struct exclock_softc *)self;
129fe1242bbSkettenis 	struct fdt_attach_args *faa = aux;
13007829fe8Sbmercer 
131fe1242bbSkettenis 	sc->sc_iot = faa->fa_iot;
132fe1242bbSkettenis 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
133fe1242bbSkettenis 	    faa->fa_reg[0].size, 0, &sc->sc_ioh))
13407829fe8Sbmercer 		panic("%s: bus_space_map failed!", __func__);
13507829fe8Sbmercer 
136fe1242bbSkettenis 	exclock_sc = sc;
13707829fe8Sbmercer 
13807998b61Skettenis 	printf("\n");
13907829fe8Sbmercer 
14016042c3cSkettenis 	if (OF_is_compatible(faa->fa_node, "samsung,exynos5250-clock")) {
14116042c3cSkettenis 		/* Exynos 5250 */
14216042c3cSkettenis 		sc->sc_cd.cd_enable = exynos5250_enable;
14316042c3cSkettenis 		sc->sc_cd.cd_get_frequency = exynos5250_get_frequency;
14416042c3cSkettenis 		sc->sc_cd.cd_set_frequency = exynos5250_set_frequency;
14516042c3cSkettenis 	} else {
14616042c3cSkettenis 		/* Exynos 5420/5800 */
14716042c3cSkettenis 		sc->sc_cd.cd_enable = exynos5420_enable;
14816042c3cSkettenis 		sc->sc_cd.cd_get_frequency = exynos5420_get_frequency;
14916042c3cSkettenis 		sc->sc_cd.cd_set_frequency = exynos5420_set_frequency;
15016042c3cSkettenis 	}
15116042c3cSkettenis 
152ebc46d47Skettenis 	sc->sc_cd.cd_node = faa->fa_node;
153ebc46d47Skettenis 	sc->sc_cd.cd_cookie = sc;
154ebc46d47Skettenis 	clock_register(&sc->sc_cd);
15507829fe8Sbmercer }
15607829fe8Sbmercer 
15716042c3cSkettenis /*
15816042c3cSkettenis  * Exynos 5250
15916042c3cSkettenis  */
16016042c3cSkettenis 
16107998b61Skettenis /* Clocks */
16207998b61Skettenis #define EXYNOS5250_CLK_ARM_CLK		9
16307998b61Skettenis 
164ebc46d47Skettenis uint32_t
exynos5250_get_frequency(void * cookie,uint32_t * cells)16516042c3cSkettenis exynos5250_get_frequency(void *cookie, uint32_t *cells)
166ebc46d47Skettenis {
16707998b61Skettenis 	struct exclock_softc *sc = cookie;
168ebc46d47Skettenis 	uint32_t idx = cells[0];
169ebc46d47Skettenis 
17007998b61Skettenis 	switch (idx) {
17107998b61Skettenis 	case EXYNOS5250_CLK_ARM_CLK:
17207998b61Skettenis 		return exclock_get_armclk(sc);
17307998b61Skettenis 	}
17407998b61Skettenis 
175ebc46d47Skettenis 	printf("%s: 0x%08x\n", __func__, idx);
176ebc46d47Skettenis 	return 0;
177ebc46d47Skettenis }
178ebc46d47Skettenis 
179ebc46d47Skettenis int
exynos5250_set_frequency(void * cookie,uint32_t * cells,uint32_t freq)18016042c3cSkettenis exynos5250_set_frequency(void *cookie, uint32_t *cells, uint32_t freq)
181ebc46d47Skettenis {
182ebc46d47Skettenis 	uint32_t idx = cells[0];
183ebc46d47Skettenis 
18407998b61Skettenis 	switch (idx) {
18507998b61Skettenis 	case EXYNOS5250_CLK_ARM_CLK:
18607998b61Skettenis 		return -1;
18707998b61Skettenis 	}
18807998b61Skettenis 
189ebc46d47Skettenis 	printf("%s: 0x%08x\n", __func__, idx);
190ebc46d47Skettenis 	return -1;
191ebc46d47Skettenis }
192ebc46d47Skettenis 
193ebc46d47Skettenis void
exynos5250_enable(void * cookie,uint32_t * cells,int on)19416042c3cSkettenis exynos5250_enable(void *cookie, uint32_t *cells, int on)
195ebc46d47Skettenis {
196ebc46d47Skettenis 	uint32_t idx = cells[0];
197ebc46d47Skettenis 
198ebc46d47Skettenis 	printf("%s: 0x%08x\n", __func__, idx);
199ebc46d47Skettenis }
200ebc46d47Skettenis 
20116042c3cSkettenis /*
20216042c3cSkettenis  * Exynos 5420/5800
20316042c3cSkettenis  */
20416042c3cSkettenis 
20516042c3cSkettenis /* Clocks */
20616042c3cSkettenis #define EXYNOS5420_CLK_FIN_PLL		1
20716042c3cSkettenis #define EXYNOS5420_CLK_FOUT_RPLL	6
20816042c3cSkettenis #define EXYNOS5420_CLK_FOUT_SPLL	8
20907998b61Skettenis #define EXYNOS5420_CLK_ARM_CLK		13
21007998b61Skettenis #define EXYNOS5420_CLK_KFC_CLK		14
21116042c3cSkettenis #define EXYNOS5420_CLK_SCLK_MMC2	134
21216042c3cSkettenis #define EXYNOS5420_CLK_MMC2		353
21307998b61Skettenis #define EXYNOS5420_CLK_USBH20		365
21407998b61Skettenis #define EXYNOS5420_CLK_USBD300		366
21507998b61Skettenis #define EXYNOS5420_CLK_USBD301		367
21616042c3cSkettenis #define EXYNOS5420_CLK_SCLK_SPLL	-1
21716042c3cSkettenis 
21816042c3cSkettenis /* Registers */
21916042c3cSkettenis #define EXYNOS5420_RPLL_CON0		0x10140
22016042c3cSkettenis #define EXYNOS5420_RPLL_CON1		0x10144
22116042c3cSkettenis #define EXYNOS5420_SPLL_CON0		0x10160
22216042c3cSkettenis #define EXYNOS5420_SRC_TOP6		0x10218
22316042c3cSkettenis #define EXYNOS5420_DIV_FSYS1		0x1054c
22416042c3cSkettenis #define EXYNOS5420_SRC_FSYS		0x10244
22516042c3cSkettenis #define EXYNOS5420_GATE_TOP_SCLK_FSYS	0x10840
22616042c3cSkettenis #define EXYNOS5420_GATE_IP_FSYS		0x10944
22707998b61Skettenis #define EXYNOS5420_KPLL_CON0		0x28100
22807998b61Skettenis #define EXYNOS5420_SRC_KFC		0x28200
22907998b61Skettenis #define EXYNOS5420_DIV_KFC0		0x28500
23016042c3cSkettenis 
23116042c3cSkettenis uint32_t
exynos5420_get_frequency(void * cookie,uint32_t * cells)23216042c3cSkettenis exynos5420_get_frequency(void *cookie, uint32_t *cells)
23316042c3cSkettenis {
23416042c3cSkettenis 	struct exclock_softc *sc = cookie;
23516042c3cSkettenis 	uint32_t idx = cells[0];
23616042c3cSkettenis 	uint32_t reg, div, mux;
23716042c3cSkettenis 	uint32_t kdiv, mdiv, pdiv, sdiv;
23816042c3cSkettenis 	uint64_t freq;
23916042c3cSkettenis 
24016042c3cSkettenis 	switch (idx) {
24116042c3cSkettenis 	case EXYNOS5420_CLK_FIN_PLL:
24216042c3cSkettenis 		return 24000000;
24307998b61Skettenis 	case EXYNOS5420_CLK_ARM_CLK:
24407998b61Skettenis 		return exclock_get_armclk(sc);
24507998b61Skettenis 	case EXYNOS5420_CLK_KFC_CLK:
24607998b61Skettenis 		return exclock_get_kfcclk(sc);
24716042c3cSkettenis 	case EXYNOS5420_CLK_SCLK_MMC2:
24816042c3cSkettenis 		reg = HREAD4(sc, EXYNOS5420_DIV_FSYS1);
24916042c3cSkettenis 		div = ((reg >> 20) & ((1 << 10) - 1)) + 1;
25016042c3cSkettenis 		reg = HREAD4(sc, EXYNOS5420_SRC_FSYS);
25116042c3cSkettenis 		mux = ((reg >> 16) & ((1 << 3) - 1));
25216042c3cSkettenis 		switch (mux) {
25316042c3cSkettenis 		case 0:
25416042c3cSkettenis 			idx = EXYNOS5420_CLK_FIN_PLL;
25516042c3cSkettenis 			break;
25616042c3cSkettenis 		case 4:
25716042c3cSkettenis 			idx = EXYNOS5420_CLK_SCLK_SPLL;
25816042c3cSkettenis 			break;
25916042c3cSkettenis 		default:
26016042c3cSkettenis 			idx = 0;
26116042c3cSkettenis 			break;
26216042c3cSkettenis 		}
26316042c3cSkettenis 		return exynos5420_get_frequency(sc, &idx) / div;
26416042c3cSkettenis 	case EXYNOS5420_CLK_SCLK_SPLL:
26516042c3cSkettenis 		reg = HREAD4(sc, EXYNOS5420_SRC_TOP6);
26616042c3cSkettenis 		mux = ((reg >> 8) & ((1 << 1) - 1));
26716042c3cSkettenis 		switch (mux) {
26816042c3cSkettenis 		case 0:
26916042c3cSkettenis 			idx = EXYNOS5420_CLK_FIN_PLL;
27016042c3cSkettenis 			break;
27116042c3cSkettenis 		case 1:
27216042c3cSkettenis 			idx = EXYNOS5420_CLK_FOUT_SPLL;
27316042c3cSkettenis 			break;
27416042c3cSkettenis 		}
27516042c3cSkettenis 		return exynos5420_get_frequency(sc, &idx);
27616042c3cSkettenis 	case EXYNOS5420_CLK_FOUT_RPLL:
27716042c3cSkettenis 		reg = HREAD4(sc, EXYNOS5420_RPLL_CON0);
27816042c3cSkettenis 		mdiv = (reg >> 16) & 0x1ff;
27916042c3cSkettenis 		pdiv = (reg >> 8) & 0x3f;
28016042c3cSkettenis 		sdiv = (reg >> 0) & 0x7;
28116042c3cSkettenis 		reg = HREAD4(sc, EXYNOS5420_RPLL_CON1);
28216042c3cSkettenis 		kdiv = (reg >> 0) & 0xffff;
28316042c3cSkettenis 		idx = EXYNOS5420_CLK_FIN_PLL;
28416042c3cSkettenis 		freq = exynos5420_get_frequency(sc, &idx);
28516042c3cSkettenis 		freq = ((mdiv << 16) + kdiv) * freq / (pdiv * (1 << sdiv));
28616042c3cSkettenis 		return (freq >> 16);
28716042c3cSkettenis 	case EXYNOS5420_CLK_FOUT_SPLL:
28816042c3cSkettenis 		reg = HREAD4(sc, EXYNOS5420_SPLL_CON0);
28916042c3cSkettenis 		mdiv = (reg >> 16) & 0x3ff;
29016042c3cSkettenis 		pdiv = (reg >> 8) & 0x3f;
29116042c3cSkettenis 		sdiv = (reg >> 0) & 0x7;
29216042c3cSkettenis 		idx = EXYNOS5420_CLK_FIN_PLL;
29316042c3cSkettenis 		freq = exynos5420_get_frequency(sc, &idx);
29416042c3cSkettenis 		return mdiv * freq / (pdiv * (1 << sdiv));
29516042c3cSkettenis 	}
29616042c3cSkettenis 
29716042c3cSkettenis 	printf("%s: 0x%08x\n", __func__, idx);
29816042c3cSkettenis 	return 0;
29916042c3cSkettenis }
30016042c3cSkettenis 
30116042c3cSkettenis int
exynos5420_set_frequency(void * cookie,uint32_t * cells,uint32_t freq)30216042c3cSkettenis exynos5420_set_frequency(void *cookie, uint32_t *cells, uint32_t freq)
30316042c3cSkettenis {
30416042c3cSkettenis 	uint32_t idx = cells[0];
30516042c3cSkettenis 
30607998b61Skettenis 	switch (idx) {
30707998b61Skettenis 	case EXYNOS5420_CLK_ARM_CLK:
30807998b61Skettenis 	case EXYNOS5420_CLK_KFC_CLK:
30907998b61Skettenis 		return -1;
31007998b61Skettenis 	}
31107998b61Skettenis 
31216042c3cSkettenis 	printf("%s: 0x%08x\n", __func__, idx);
31316042c3cSkettenis 	return -1;
31416042c3cSkettenis }
31516042c3cSkettenis 
31616042c3cSkettenis void
exynos5420_enable(void * cookie,uint32_t * cells,int on)31716042c3cSkettenis exynos5420_enable(void *cookie, uint32_t *cells, int on)
31816042c3cSkettenis {
31916042c3cSkettenis 	uint32_t idx = cells[0];
32016042c3cSkettenis 
32116042c3cSkettenis 	switch (idx) {
32216042c3cSkettenis 	case EXYNOS5420_CLK_SCLK_MMC2:	/* CLK_GATE_TOP_SCLK_FSYS */
32316042c3cSkettenis 	case EXYNOS5420_CLK_MMC2:	/* CLK_GATE_IP_FSYS */
32407998b61Skettenis 	case EXYNOS5420_CLK_USBH20:	/* CLK_GATE_IP_FSYS */
32507998b61Skettenis 	case EXYNOS5420_CLK_USBD300:	/* CLK_GATE_IP_FSYS */
32607998b61Skettenis 	case EXYNOS5420_CLK_USBD301:	/* CLK_GATE_IP_FSYS */
32716042c3cSkettenis 		/* Enabled by default. */
32816042c3cSkettenis 		return;
32916042c3cSkettenis 	}
33016042c3cSkettenis 
33116042c3cSkettenis 	printf("%s: 0x%08x\n", __func__, idx);
33216042c3cSkettenis }
33316042c3cSkettenis 
33407998b61Skettenis uint32_t
exclock_decode_pll_clk(enum clocks pll,unsigned int r,unsigned int k)33507829fe8Sbmercer exclock_decode_pll_clk(enum clocks pll, unsigned int r, unsigned int k)
33607829fe8Sbmercer {
33707998b61Skettenis 	uint64_t freq;
33807998b61Skettenis 	uint32_t m, p, s = 0, mask, fout;
33907829fe8Sbmercer 	/*
34007829fe8Sbmercer 	 * APLL_CON: MIDV [25:16]
34107829fe8Sbmercer 	 * MPLL_CON: MIDV [25:16]
34207829fe8Sbmercer 	 * EPLL_CON: MIDV [24:16]
34307829fe8Sbmercer 	 * VPLL_CON: MIDV [24:16]
34407829fe8Sbmercer 	 * BPLL_CON: MIDV [25:16]: Exynos5
34507829fe8Sbmercer 	 */
34607829fe8Sbmercer 
34707829fe8Sbmercer 	switch (pll)
34807829fe8Sbmercer 	{
34907829fe8Sbmercer 	case APLL:
35007829fe8Sbmercer 	case MPLL:
35107829fe8Sbmercer 	case BPLL:
35207998b61Skettenis 	case KPLL:
35307829fe8Sbmercer 		mask = 0x3ff;
35407829fe8Sbmercer 		break;
35507829fe8Sbmercer 	default:
35607829fe8Sbmercer 		mask = 0x1ff;
35707829fe8Sbmercer 	}
35807829fe8Sbmercer 
35907829fe8Sbmercer 	m = (r >> 16) & mask;
36007829fe8Sbmercer 
36107829fe8Sbmercer 	/* PDIV [13:8] */
36207829fe8Sbmercer 	p = (r >> 8) & 0x3f;
36307829fe8Sbmercer 	/* SDIV [2:0] */
36407829fe8Sbmercer 	s = r & 0x7;
36507829fe8Sbmercer 
36607829fe8Sbmercer 	freq = HCLK_FREQ;
36707829fe8Sbmercer 
36807829fe8Sbmercer 	if (pll == EPLL) {
36907829fe8Sbmercer 		k = k & 0xffff;
37007829fe8Sbmercer 		/* FOUT = (MDIV + K / 65536) * FIN / (PDIV * 2^SDIV) */
37107829fe8Sbmercer 		fout = (m + k / 65536) * (freq / (p * (1 << s)));
37207829fe8Sbmercer 	} else if (pll == VPLL) {
37307829fe8Sbmercer 		k = k & 0xfff;
37407829fe8Sbmercer 		/* FOUT = (MDIV + K / 1024) * FIN / (PDIV * 2^SDIV) */
37507829fe8Sbmercer 		fout = (m + k / 1024) * (freq / (p * (1 << s)));
37607829fe8Sbmercer 	} else {
37707829fe8Sbmercer 		/* FOUT = MDIV * FIN / (PDIV * 2^(SDIV - 1)) */
37807829fe8Sbmercer 		fout = m * (freq / (p * (1 << s)));
37907829fe8Sbmercer 	}
38007829fe8Sbmercer 
38107829fe8Sbmercer 	return fout;
38207829fe8Sbmercer }
38307829fe8Sbmercer 
38407998b61Skettenis uint32_t
exclock_get_pll_clk(struct exclock_softc * sc,enum clocks pll)38507998b61Skettenis exclock_get_pll_clk(struct exclock_softc *sc, enum clocks pll)
38607829fe8Sbmercer {
38707829fe8Sbmercer 	uint32_t freq;
38807829fe8Sbmercer 
38907829fe8Sbmercer 	switch (pll) {
39007829fe8Sbmercer 	case APLL:
39107829fe8Sbmercer 		freq = exclock_decode_pll_clk(pll,
39207998b61Skettenis 		    HREAD4(sc, CLOCK_APLL_CON0), 0);
39307829fe8Sbmercer 		break;
39407829fe8Sbmercer 	case MPLL:
39507829fe8Sbmercer 		freq = exclock_decode_pll_clk(pll,
39607998b61Skettenis 		    HREAD4(sc, CLOCK_MPLL_CON0), 0);
39707829fe8Sbmercer 		break;
39807829fe8Sbmercer 	case BPLL:
39907829fe8Sbmercer 		freq = exclock_decode_pll_clk(pll,
40007998b61Skettenis 		    HREAD4(sc, CLOCK_BPLL_CON0), 0);
40107829fe8Sbmercer 		break;
40207829fe8Sbmercer 	case EPLL:
40307829fe8Sbmercer 		freq = exclock_decode_pll_clk(pll,
40407829fe8Sbmercer 		    HREAD4(sc, CLOCK_EPLL_CON0),
40507829fe8Sbmercer 		    HREAD4(sc, CLOCK_EPLL_CON1));
40607829fe8Sbmercer 		break;
40707829fe8Sbmercer 	case VPLL:
40807829fe8Sbmercer 		freq = exclock_decode_pll_clk(pll,
40907829fe8Sbmercer 		    HREAD4(sc, CLOCK_VPLL_CON0),
41007829fe8Sbmercer 		    HREAD4(sc, CLOCK_VPLL_CON1));
41107829fe8Sbmercer 		break;
41207998b61Skettenis 	case KPLL:
41307998b61Skettenis 		freq = exclock_decode_pll_clk(pll,
41407998b61Skettenis 		    HREAD4(sc, EXYNOS5420_KPLL_CON0), 0);
41507998b61Skettenis 		break;
41607829fe8Sbmercer 	default:
41707829fe8Sbmercer 		return 0;
41807829fe8Sbmercer 	}
41907829fe8Sbmercer 
42007829fe8Sbmercer 	/*
42107829fe8Sbmercer 	 * According to the user manual, in EVT1 MPLL and BPLL always gives
42207829fe8Sbmercer 	 * 1.6GHz clock, so divide by 2 to get 800MHz MPLL clock.
42307829fe8Sbmercer 	 */
42407829fe8Sbmercer 	if (pll == MPLL || pll == BPLL) {
42507829fe8Sbmercer 		uint32_t freq_sel;
42607829fe8Sbmercer 		uint32_t pll_div2_sel = HREAD4(sc, CLOCK_PLL_DIV2_SEL);
42707829fe8Sbmercer 
42807829fe8Sbmercer 		switch (pll) {
42907829fe8Sbmercer 		case MPLL:
43007829fe8Sbmercer 			freq_sel = (pll_div2_sel >> MPLL_FOUT_SEL_SHIFT)
43107829fe8Sbmercer 					& MPLL_FOUT_SEL_MASK;
43207829fe8Sbmercer 			break;
43307829fe8Sbmercer 		case BPLL:
43407829fe8Sbmercer 			freq_sel = (pll_div2_sel >> BPLL_FOUT_SEL_SHIFT)
43507829fe8Sbmercer 					& BPLL_FOUT_SEL_MASK;
43607829fe8Sbmercer 			break;
43707829fe8Sbmercer 		default:
43807829fe8Sbmercer 			freq_sel = -1;
43907829fe8Sbmercer 			break;
44007829fe8Sbmercer 		}
44107829fe8Sbmercer 
44207829fe8Sbmercer 		if (freq_sel == 0)
44307829fe8Sbmercer 			freq /= 2;
44407829fe8Sbmercer 	}
44507829fe8Sbmercer 
44607829fe8Sbmercer 	return freq;
44707829fe8Sbmercer }
44807829fe8Sbmercer 
44907998b61Skettenis uint32_t
exclock_get_armclk(struct exclock_softc * sc)45007998b61Skettenis exclock_get_armclk(struct exclock_softc *sc)
45107829fe8Sbmercer {
45207829fe8Sbmercer 	uint32_t div, armclk, arm_ratio, arm2_ratio;
45307829fe8Sbmercer 
45407829fe8Sbmercer 	div = HREAD4(sc, CLOCK_CLK_DIV_CPU0);
45507829fe8Sbmercer 
45607829fe8Sbmercer 	/* ARM_RATIO: [2:0], ARM2_RATIO: [30:28] */
45707829fe8Sbmercer 	arm_ratio = (div >> 0) & 0x7;
45807829fe8Sbmercer 	arm2_ratio = (div >> 28) & 0x7;
45907829fe8Sbmercer 
46007998b61Skettenis 	armclk = exclock_get_pll_clk(sc, APLL) / (arm_ratio + 1);
46107829fe8Sbmercer 	armclk /= (arm2_ratio + 1);
46207829fe8Sbmercer 
46307829fe8Sbmercer 	return armclk;
46407829fe8Sbmercer }
46507829fe8Sbmercer 
46607998b61Skettenis uint32_t
exclock_get_kfcclk(struct exclock_softc * sc)46707998b61Skettenis exclock_get_kfcclk(struct exclock_softc *sc)
46807998b61Skettenis {
46907998b61Skettenis 	uint32_t div, kfc_ratio;
47007998b61Skettenis 
47107998b61Skettenis 	div = HREAD4(sc, EXYNOS5420_DIV_KFC0);
47207998b61Skettenis 
47307998b61Skettenis 	/* KFC_RATIO: [2:0] */
47407998b61Skettenis 	kfc_ratio = (div >> 0) & 0x7;
47507998b61Skettenis 
47607998b61Skettenis 	return exclock_get_pll_clk(sc, KPLL) / (kfc_ratio + 1);
47707998b61Skettenis }
47807998b61Skettenis 
47907829fe8Sbmercer unsigned int
exclock_get_i2cclk(void)480fe1242bbSkettenis exclock_get_i2cclk(void)
48107829fe8Sbmercer {
48207829fe8Sbmercer 	struct exclock_softc *sc = exclock_sc;
48307829fe8Sbmercer 	uint32_t aclk_66, aclk_66_pre, div, ratio;
48407829fe8Sbmercer 
48507829fe8Sbmercer 	div = HREAD4(sc, CLOCK_CLK_DIV_TOP1);
48607829fe8Sbmercer 	ratio = (div >> 24) & 0x7;
48707998b61Skettenis 	aclk_66_pre = exclock_get_pll_clk(sc, MPLL) / (ratio + 1);
48807829fe8Sbmercer 	div = HREAD4(sc, CLOCK_CLK_DIV_TOP0);
48907829fe8Sbmercer 	ratio = (div >> 0) & 0x7;
49007829fe8Sbmercer 	aclk_66 = aclk_66_pre / (ratio + 1);
49107829fe8Sbmercer 
49207998b61Skettenis 	return aclk_66 / 1000;
49307829fe8Sbmercer }
494