mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-29 19:21:31 -04:00
The changes to the common clock framework for 4.2 are dominated by new
drivers and updates to existing ones, as usual. There are some fixes to the framework itself and several cleanups for sparse warnings, etc. Please consider pulling. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJVlCubAAoJEKI6nJvDJaTUJ3cQAKdaU+NpWX9Qajt6snIHcqB6 nBg57DltuPh1XFMPHDIdPe+8rK0RDQjao6jwzzqUKGaZJ3sycPCIn8mh+iZCP7Se yxhacQIAIp2qbIlIQ8Epcc6jnma/8cUyfB0BuYMddzb7bk2PXLfLxzJgAo5pXZM1 LQoBxdpBh3Y7vcdBFLuHsnORTZdRI29Nu7p5dRK27vxWFBoCqL3bshHSS8g7lTBc XUWcFhfFe7WvMvBqqF8pSJlCmHzO90S0MmFPT4OQy4NtAysPpjsqI6RPCHBTyvy+ oniMM6zM/RdN1VnLB49HvAL4mArjAdsQGxNywsUpvD/IcZPbpM8N3VA3xHzANMfy iJ9374zgjRn3/YBfjYFCcyVUY7SPeiRsXh97ZMZJGY5BC5FbXuKMn3STPrUilhw/ CMSq9ARdmjlPQDW5EuEGFeap+7oz4q4Kgk0qgmOktIVYhtt9Pn0ddMKINIpokzbi 4w0z1kc/YVHZpFMNYxAQoxYzejU9hxybUvYUEnu9RFzzOW+o7DsmMv7k5r1XY+oO P1Kz0jVWCw46XnvP6z1V4SforZOQXr1Om698O8fd7ke7Q7gFCr2UQjIuXliC/g8u NREqu1kceXTeWnt0LFZB7GMOo2Edo21qYIAILyqTO50QJL2at5WLoibaM03y7I7x GeMP1APDHJI0E5dn0v5P =Iv72 -----END PGP SIGNATURE----- Merge tag 'clk-for-linus-4.2' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux Pull clock framework updates from Michael Turquette: "The changes to the common clock framework for 4.2 are dominated by new drivers and updates to existing ones, as usual. There are some fixes to the framework itself and several cleanups for sparse warnings, etc" * tag 'clk-for-linus-4.2' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux: (135 commits) clk: stm32: Add clock driver for STM32F4[23]xxx devices dt-bindings: Document the STM32F4 clock bindings cpufreq: exynos: remove Exynos4210 specific cpufreq driver support ARM: Exynos: switch to using generic cpufreq driver for Exynos4210 clk: samsung: exynos4: add cpu clock configuration data and instantiate cpu clock clk: samsung: add infrastructure to register cpu clocks clk: add CLK_RECALC_NEW_RATES clock flag for Exynos cpu clock support doc: dt: add documentation for lpc1850-ccu clk driver clk: add lpc18xx ccu clk driver doc: dt: add documentation for lpc1850-cgu clk driver clk: add lpc18xx cgu clk driver clk: keystone: add support for post divider register for main pll clk: mvebu: flag the crypto clk as CLK_IGNORE_UNUSED clk: cygnus: remove Cygnus dummy clock binding clk: cygnus: add clock support for Broadcom Cygnus clk: Change bcm clocks build dependency clk: iproc: add initial common clock support clk: iproc: define Broadcom iProc clock binding MAINTAINERS: update email for Michael Turquette clk: meson: add some error handling in meson_clk_register_cpu() ...
This commit is contained in:
commit
5f1201d515
182 changed files with 14617 additions and 1765 deletions
|
|
@ -78,6 +78,23 @@ config COMMON_CLK_SI570
|
|||
This driver supports Silicon Labs 570/571/598/599 programmable
|
||||
clock generators.
|
||||
|
||||
config COMMON_CLK_CDCE925
|
||||
tristate "Clock driver for TI CDCE925 devices"
|
||||
depends on I2C
|
||||
depends on OF
|
||||
select REGMAP_I2C
|
||||
help
|
||||
---help---
|
||||
This driver supports the TI CDCE925 programmable clock synthesizer.
|
||||
The chip contains two PLLs with spread-spectrum clocking support and
|
||||
five output dividers. The driver only supports the following setup,
|
||||
and uses a fixed setting for the output muxes.
|
||||
Y1 is derived from the input clock
|
||||
Y2 and Y3 derive from PLL1
|
||||
Y4 and Y5 derive from PLL2
|
||||
Given a target output frequency, the driver will set the PLL and
|
||||
divider to best approximate the desired output.
|
||||
|
||||
config COMMON_CLK_S2MPS11
|
||||
tristate "Clock driver for S2MPS1X/S5M8767 MFD"
|
||||
depends on MFD_SEC_CORE
|
||||
|
|
@ -150,11 +167,13 @@ config COMMON_CLK_CDCE706
|
|||
---help---
|
||||
This driver supports TI CDCE706 programmable 3-PLL clock synthesizer.
|
||||
|
||||
source "drivers/clk/bcm/Kconfig"
|
||||
source "drivers/clk/hisilicon/Kconfig"
|
||||
source "drivers/clk/qcom/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
||||
source "drivers/clk/bcm/Kconfig"
|
||||
source "drivers/clk/mvebu/Kconfig"
|
||||
|
||||
source "drivers/clk/samsung/Kconfig"
|
||||
source "drivers/clk/tegra/Kconfig"
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@ obj-$(CONFIG_COMMON_CLK_RK808) += clk-rk808.o
|
|||
obj-$(CONFIG_COMMON_CLK_S2MPS11) += clk-s2mps11.o
|
||||
obj-$(CONFIG_COMMON_CLK_SI5351) += clk-si5351.o
|
||||
obj-$(CONFIG_COMMON_CLK_SI570) += clk-si570.o
|
||||
obj-$(CONFIG_COMMON_CLK_CDCE925) += clk-cdce925.o
|
||||
obj-$(CONFIG_ARCH_STM32) += clk-stm32f4.o
|
||||
obj-$(CONFIG_CLK_TWL6040) += clk-twl6040.o
|
||||
obj-$(CONFIG_ARCH_U300) += clk-u300.o
|
||||
obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o
|
||||
|
|
@ -45,19 +47,20 @@ obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o
|
|||
obj-$(CONFIG_COMMON_CLK_XGENE) += clk-xgene.o
|
||||
obj-$(CONFIG_COMMON_CLK_PWM) += clk-pwm.o
|
||||
obj-$(CONFIG_COMMON_CLK_AT91) += at91/
|
||||
obj-$(CONFIG_ARCH_BCM_MOBILE) += bcm/
|
||||
obj-$(CONFIG_ARCH_BCM) += bcm/
|
||||
obj-$(CONFIG_ARCH_BERLIN) += berlin/
|
||||
obj-$(CONFIG_ARCH_HI3xxx) += hisilicon/
|
||||
obj-$(CONFIG_ARCH_HIP04) += hisilicon/
|
||||
obj-$(CONFIG_ARCH_HIX5HD2) += hisilicon/
|
||||
obj-$(CONFIG_ARCH_HISI) += hisilicon/
|
||||
obj-$(CONFIG_ARCH_MXC) += imx/
|
||||
obj-$(CONFIG_MACH_INGENIC) += ingenic/
|
||||
obj-$(CONFIG_COMMON_CLK_KEYSTONE) += keystone/
|
||||
obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/
|
||||
ifeq ($(CONFIG_COMMON_CLK), y)
|
||||
obj-$(CONFIG_ARCH_MMP) += mmp/
|
||||
endif
|
||||
obj-$(CONFIG_PLAT_ORION) += mvebu/
|
||||
obj-$(CONFIG_ARCH_MESON) += meson/
|
||||
obj-$(CONFIG_ARCH_MXS) += mxs/
|
||||
obj-$(CONFIG_ARCH_LPC18XX) += nxp/
|
||||
obj-$(CONFIG_MACH_PISTACHIO) += pistachio/
|
||||
obj-$(CONFIG_COMMON_CLK_PXA) += pxa/
|
||||
obj-$(CONFIG_COMMON_CLK_QCOM) += qcom/
|
||||
|
|
|
|||
|
|
@ -614,7 +614,7 @@ void __init of_at91sam9x5_clk_main_setup(struct device_node *np,
|
|||
const char *name = np->name;
|
||||
int i;
|
||||
|
||||
num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells");
|
||||
num_parents = of_clk_get_parent_count(np);
|
||||
if (num_parents <= 0 || num_parents > 2)
|
||||
return;
|
||||
|
||||
|
|
|
|||
|
|
@ -224,7 +224,7 @@ of_at91_clk_master_setup(struct device_node *np, struct at91_pmc *pmc,
|
|||
const char *name = np->name;
|
||||
struct clk_master_characteristics *characteristics;
|
||||
|
||||
num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells");
|
||||
num_parents = of_clk_get_parent_count(np);
|
||||
if (num_parents <= 0 || num_parents > MASTER_SOURCE_MAX)
|
||||
return;
|
||||
|
||||
|
|
|
|||
|
|
@ -237,7 +237,7 @@ of_at91_clk_prog_setup(struct device_node *np, struct at91_pmc *pmc,
|
|||
const char *name;
|
||||
struct device_node *progclknp;
|
||||
|
||||
num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells");
|
||||
num_parents = of_clk_get_parent_count(np);
|
||||
if (num_parents <= 0 || num_parents > PROG_SOURCE_MAX)
|
||||
return;
|
||||
|
||||
|
|
|
|||
|
|
@ -373,7 +373,7 @@ void __init of_at91sam9x5_clk_slow_setup(struct device_node *np,
|
|||
const char *name = np->name;
|
||||
int i;
|
||||
|
||||
num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells");
|
||||
num_parents = of_clk_get_parent_count(np);
|
||||
if (num_parents <= 0 || num_parents > 2)
|
||||
return;
|
||||
|
||||
|
|
@ -451,7 +451,7 @@ void __init of_at91sam9260_clk_slow_setup(struct device_node *np,
|
|||
const char *name = np->name;
|
||||
int i;
|
||||
|
||||
num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells");
|
||||
num_parents = of_clk_get_parent_count(np);
|
||||
if (num_parents != 2)
|
||||
return;
|
||||
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@ void __init of_at91sam9x5_clk_smd_setup(struct device_node *np,
|
|||
const char *parent_names[SMD_SOURCE_MAX];
|
||||
const char *name = np->name;
|
||||
|
||||
num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells");
|
||||
num_parents = of_clk_get_parent_count(np);
|
||||
if (num_parents <= 0 || num_parents > SMD_SOURCE_MAX)
|
||||
return;
|
||||
|
||||
|
|
|
|||
|
|
@ -378,7 +378,7 @@ void __init of_at91sam9x5_clk_usb_setup(struct device_node *np,
|
|||
const char *parent_names[USB_SOURCE_MAX];
|
||||
const char *name = np->name;
|
||||
|
||||
num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells");
|
||||
num_parents = of_clk_get_parent_count(np);
|
||||
if (num_parents <= 0 || num_parents > USB_SOURCE_MAX)
|
||||
return;
|
||||
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ static int pmc_irq_domain_xlate(struct irq_domain *d,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct irq_domain_ops pmc_irq_ops = {
|
||||
static const struct irq_domain_ops pmc_irq_ops = {
|
||||
.map = pmc_irq_map,
|
||||
.xlate = pmc_irq_domain_xlate,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -7,3 +7,12 @@ config CLK_BCM_KONA
|
|||
Enable common clock framework support for Broadcom SoCs
|
||||
using "Kona" style clock control units, including those
|
||||
in the BCM281xx and BCM21664 families.
|
||||
|
||||
config COMMON_CLK_IPROC
|
||||
bool "Broadcom iProc clock support"
|
||||
depends on ARCH_BCM_IPROC
|
||||
depends on COMMON_CLK
|
||||
default ARCH_BCM_IPROC
|
||||
help
|
||||
Enable common clock framework support for Broadcom SoCs
|
||||
based on the iProc architecture
|
||||
|
|
|
|||
|
|
@ -2,3 +2,5 @@ obj-$(CONFIG_CLK_BCM_KONA) += clk-kona.o
|
|||
obj-$(CONFIG_CLK_BCM_KONA) += clk-kona-setup.o
|
||||
obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm281xx.o
|
||||
obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm21664.o
|
||||
obj-$(CONFIG_COMMON_CLK_IPROC) += clk-iproc-armpll.o clk-iproc-pll.o clk-iproc-asiu.o
|
||||
obj-$(CONFIG_ARCH_BCM_CYGNUS) += clk-cygnus.o
|
||||
|
|
|
|||
265
drivers/clk/bcm/clk-cygnus.c
Normal file
265
drivers/clk/bcm/clk-cygnus.c
Normal file
|
|
@ -0,0 +1,265 @@
|
|||
/*
|
||||
* Copyright (C) 2014 Broadcom Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <dt-bindings/clock/bcm-cygnus.h>
|
||||
#include "clk-iproc.h"
|
||||
|
||||
#define reg_val(o, s, w) { .offset = o, .shift = s, .width = w, }
|
||||
|
||||
#define aon_val(o, pw, ps, is) { .offset = o, .pwr_width = pw, \
|
||||
.pwr_shift = ps, .iso_shift = is }
|
||||
|
||||
#define sw_ctrl_val(o, s) { .offset = o, .shift = s, }
|
||||
|
||||
#define asiu_div_val(o, es, hs, hw, ls, lw) \
|
||||
{ .offset = o, .en_shift = es, .high_shift = hs, \
|
||||
.high_width = hw, .low_shift = ls, .low_width = lw }
|
||||
|
||||
#define reset_val(o, rs, prs, kis, kiw, kps, kpw, kas, kaw) { .offset = o, \
|
||||
.reset_shift = rs, .p_reset_shift = prs, .ki_shift = kis, \
|
||||
.ki_width = kiw, .kp_shift = kps, .kp_width = kpw, .ka_shift = kas, \
|
||||
.ka_width = kaw }
|
||||
|
||||
#define vco_ctrl_val(uo, lo) { .u_offset = uo, .l_offset = lo }
|
||||
|
||||
#define enable_val(o, es, hs, bs) { .offset = o, .enable_shift = es, \
|
||||
.hold_shift = hs, .bypass_shift = bs }
|
||||
|
||||
#define asiu_gate_val(o, es) { .offset = o, .en_shift = es }
|
||||
|
||||
static void __init cygnus_armpll_init(struct device_node *node)
|
||||
{
|
||||
iproc_armpll_setup(node);
|
||||
}
|
||||
CLK_OF_DECLARE(cygnus_armpll, "brcm,cygnus-armpll", cygnus_armpll_init);
|
||||
|
||||
static const struct iproc_pll_ctrl genpll = {
|
||||
.flags = IPROC_CLK_AON | IPROC_CLK_PLL_HAS_NDIV_FRAC |
|
||||
IPROC_CLK_PLL_NEEDS_SW_CFG,
|
||||
.aon = aon_val(0x0, 2, 1, 0),
|
||||
.reset = reset_val(0x0, 11, 10, 4, 3, 0, 4, 7, 3),
|
||||
.sw_ctrl = sw_ctrl_val(0x10, 31),
|
||||
.ndiv_int = reg_val(0x10, 20, 10),
|
||||
.ndiv_frac = reg_val(0x10, 0, 20),
|
||||
.pdiv = reg_val(0x14, 0, 4),
|
||||
.vco_ctrl = vco_ctrl_val(0x18, 0x1c),
|
||||
.status = reg_val(0x28, 12, 1),
|
||||
};
|
||||
|
||||
static const struct iproc_clk_ctrl genpll_clk[] = {
|
||||
[BCM_CYGNUS_GENPLL_AXI21_CLK] = {
|
||||
.channel = BCM_CYGNUS_GENPLL_AXI21_CLK,
|
||||
.flags = IPROC_CLK_AON,
|
||||
.enable = enable_val(0x4, 6, 0, 12),
|
||||
.mdiv = reg_val(0x20, 0, 8),
|
||||
},
|
||||
[BCM_CYGNUS_GENPLL_250MHZ_CLK] = {
|
||||
.channel = BCM_CYGNUS_GENPLL_250MHZ_CLK,
|
||||
.flags = IPROC_CLK_AON,
|
||||
.enable = enable_val(0x4, 7, 1, 13),
|
||||
.mdiv = reg_val(0x20, 10, 8),
|
||||
},
|
||||
[BCM_CYGNUS_GENPLL_IHOST_SYS_CLK] = {
|
||||
.channel = BCM_CYGNUS_GENPLL_IHOST_SYS_CLK,
|
||||
.flags = IPROC_CLK_AON,
|
||||
.enable = enable_val(0x4, 8, 2, 14),
|
||||
.mdiv = reg_val(0x20, 20, 8),
|
||||
},
|
||||
[BCM_CYGNUS_GENPLL_ENET_SW_CLK] = {
|
||||
.channel = BCM_CYGNUS_GENPLL_ENET_SW_CLK,
|
||||
.flags = IPROC_CLK_AON,
|
||||
.enable = enable_val(0x4, 9, 3, 15),
|
||||
.mdiv = reg_val(0x24, 0, 8),
|
||||
},
|
||||
[BCM_CYGNUS_GENPLL_AUDIO_125_CLK] = {
|
||||
.channel = BCM_CYGNUS_GENPLL_AUDIO_125_CLK,
|
||||
.flags = IPROC_CLK_AON,
|
||||
.enable = enable_val(0x4, 10, 4, 16),
|
||||
.mdiv = reg_val(0x24, 10, 8),
|
||||
},
|
||||
[BCM_CYGNUS_GENPLL_CAN_CLK] = {
|
||||
.channel = BCM_CYGNUS_GENPLL_CAN_CLK,
|
||||
.flags = IPROC_CLK_AON,
|
||||
.enable = enable_val(0x4, 11, 5, 17),
|
||||
.mdiv = reg_val(0x24, 20, 8),
|
||||
},
|
||||
};
|
||||
|
||||
static void __init cygnus_genpll_clk_init(struct device_node *node)
|
||||
{
|
||||
iproc_pll_clk_setup(node, &genpll, NULL, 0, genpll_clk,
|
||||
ARRAY_SIZE(genpll_clk));
|
||||
}
|
||||
CLK_OF_DECLARE(cygnus_genpll, "brcm,cygnus-genpll", cygnus_genpll_clk_init);
|
||||
|
||||
static const struct iproc_pll_ctrl lcpll0 = {
|
||||
.flags = IPROC_CLK_AON | IPROC_CLK_PLL_NEEDS_SW_CFG,
|
||||
.aon = aon_val(0x0, 2, 5, 4),
|
||||
.reset = reset_val(0x0, 31, 30, 27, 3, 23, 4, 19, 4),
|
||||
.sw_ctrl = sw_ctrl_val(0x4, 31),
|
||||
.ndiv_int = reg_val(0x4, 16, 10),
|
||||
.pdiv = reg_val(0x4, 26, 4),
|
||||
.vco_ctrl = vco_ctrl_val(0x10, 0x14),
|
||||
.status = reg_val(0x18, 12, 1),
|
||||
};
|
||||
|
||||
static const struct iproc_clk_ctrl lcpll0_clk[] = {
|
||||
[BCM_CYGNUS_LCPLL0_PCIE_PHY_REF_CLK] = {
|
||||
.channel = BCM_CYGNUS_LCPLL0_PCIE_PHY_REF_CLK,
|
||||
.flags = IPROC_CLK_AON,
|
||||
.enable = enable_val(0x0, 7, 1, 13),
|
||||
.mdiv = reg_val(0x8, 0, 8),
|
||||
},
|
||||
[BCM_CYGNUS_LCPLL0_DDR_PHY_CLK] = {
|
||||
.channel = BCM_CYGNUS_LCPLL0_DDR_PHY_CLK,
|
||||
.flags = IPROC_CLK_AON,
|
||||
.enable = enable_val(0x0, 8, 2, 14),
|
||||
.mdiv = reg_val(0x8, 10, 8),
|
||||
},
|
||||
[BCM_CYGNUS_LCPLL0_SDIO_CLK] = {
|
||||
.channel = BCM_CYGNUS_LCPLL0_SDIO_CLK,
|
||||
.flags = IPROC_CLK_AON,
|
||||
.enable = enable_val(0x0, 9, 3, 15),
|
||||
.mdiv = reg_val(0x8, 20, 8),
|
||||
},
|
||||
[BCM_CYGNUS_LCPLL0_USB_PHY_REF_CLK] = {
|
||||
.channel = BCM_CYGNUS_LCPLL0_USB_PHY_REF_CLK,
|
||||
.flags = IPROC_CLK_AON,
|
||||
.enable = enable_val(0x0, 10, 4, 16),
|
||||
.mdiv = reg_val(0xc, 0, 8),
|
||||
},
|
||||
[BCM_CYGNUS_LCPLL0_SMART_CARD_CLK] = {
|
||||
.channel = BCM_CYGNUS_LCPLL0_SMART_CARD_CLK,
|
||||
.flags = IPROC_CLK_AON,
|
||||
.enable = enable_val(0x0, 11, 5, 17),
|
||||
.mdiv = reg_val(0xc, 10, 8),
|
||||
},
|
||||
[BCM_CYGNUS_LCPLL0_CH5_UNUSED] = {
|
||||
.channel = BCM_CYGNUS_LCPLL0_CH5_UNUSED,
|
||||
.flags = IPROC_CLK_AON,
|
||||
.enable = enable_val(0x0, 12, 6, 18),
|
||||
.mdiv = reg_val(0xc, 20, 8),
|
||||
},
|
||||
};
|
||||
|
||||
static void __init cygnus_lcpll0_clk_init(struct device_node *node)
|
||||
{
|
||||
iproc_pll_clk_setup(node, &lcpll0, NULL, 0, lcpll0_clk,
|
||||
ARRAY_SIZE(lcpll0_clk));
|
||||
}
|
||||
CLK_OF_DECLARE(cygnus_lcpll0, "brcm,cygnus-lcpll0", cygnus_lcpll0_clk_init);
|
||||
|
||||
/*
|
||||
* MIPI PLL VCO frequency parameter table
|
||||
*/
|
||||
static const struct iproc_pll_vco_param mipipll_vco_params[] = {
|
||||
/* rate (Hz) ndiv_int ndiv_frac pdiv */
|
||||
{ 750000000UL, 30, 0, 1 },
|
||||
{ 1000000000UL, 40, 0, 1 },
|
||||
{ 1350000000ul, 54, 0, 1 },
|
||||
{ 2000000000UL, 80, 0, 1 },
|
||||
{ 2100000000UL, 84, 0, 1 },
|
||||
{ 2250000000UL, 90, 0, 1 },
|
||||
{ 2500000000UL, 100, 0, 1 },
|
||||
{ 2700000000UL, 54, 0, 0 },
|
||||
{ 2975000000UL, 119, 0, 1 },
|
||||
{ 3100000000UL, 124, 0, 1 },
|
||||
{ 3150000000UL, 126, 0, 1 },
|
||||
};
|
||||
|
||||
static const struct iproc_pll_ctrl mipipll = {
|
||||
.flags = IPROC_CLK_PLL_ASIU | IPROC_CLK_PLL_HAS_NDIV_FRAC |
|
||||
IPROC_CLK_NEEDS_READ_BACK,
|
||||
.aon = aon_val(0x0, 4, 17, 16),
|
||||
.asiu = asiu_gate_val(0x0, 3),
|
||||
.reset = reset_val(0x0, 11, 10, 4, 3, 0, 4, 7, 4),
|
||||
.ndiv_int = reg_val(0x10, 20, 10),
|
||||
.ndiv_frac = reg_val(0x10, 0, 20),
|
||||
.pdiv = reg_val(0x14, 0, 4),
|
||||
.vco_ctrl = vco_ctrl_val(0x18, 0x1c),
|
||||
.status = reg_val(0x28, 12, 1),
|
||||
};
|
||||
|
||||
static const struct iproc_clk_ctrl mipipll_clk[] = {
|
||||
[BCM_CYGNUS_MIPIPLL_CH0_UNUSED] = {
|
||||
.channel = BCM_CYGNUS_MIPIPLL_CH0_UNUSED,
|
||||
.flags = IPROC_CLK_NEEDS_READ_BACK,
|
||||
.enable = enable_val(0x4, 12, 6, 18),
|
||||
.mdiv = reg_val(0x20, 0, 8),
|
||||
},
|
||||
[BCM_CYGNUS_MIPIPLL_CH1_LCD] = {
|
||||
.channel = BCM_CYGNUS_MIPIPLL_CH1_LCD,
|
||||
.flags = IPROC_CLK_NEEDS_READ_BACK,
|
||||
.enable = enable_val(0x4, 13, 7, 19),
|
||||
.mdiv = reg_val(0x20, 10, 8),
|
||||
},
|
||||
[BCM_CYGNUS_MIPIPLL_CH2_V3D] = {
|
||||
.channel = BCM_CYGNUS_MIPIPLL_CH2_V3D,
|
||||
.flags = IPROC_CLK_NEEDS_READ_BACK,
|
||||
.enable = enable_val(0x4, 14, 8, 20),
|
||||
.mdiv = reg_val(0x20, 20, 8),
|
||||
},
|
||||
[BCM_CYGNUS_MIPIPLL_CH3_UNUSED] = {
|
||||
.channel = BCM_CYGNUS_MIPIPLL_CH3_UNUSED,
|
||||
.flags = IPROC_CLK_NEEDS_READ_BACK,
|
||||
.enable = enable_val(0x4, 15, 9, 21),
|
||||
.mdiv = reg_val(0x24, 0, 8),
|
||||
},
|
||||
[BCM_CYGNUS_MIPIPLL_CH4_UNUSED] = {
|
||||
.channel = BCM_CYGNUS_MIPIPLL_CH4_UNUSED,
|
||||
.flags = IPROC_CLK_NEEDS_READ_BACK,
|
||||
.enable = enable_val(0x4, 16, 10, 22),
|
||||
.mdiv = reg_val(0x24, 10, 8),
|
||||
},
|
||||
[BCM_CYGNUS_MIPIPLL_CH5_UNUSED] = {
|
||||
.channel = BCM_CYGNUS_MIPIPLL_CH5_UNUSED,
|
||||
.flags = IPROC_CLK_NEEDS_READ_BACK,
|
||||
.enable = enable_val(0x4, 17, 11, 23),
|
||||
.mdiv = reg_val(0x24, 20, 8),
|
||||
},
|
||||
};
|
||||
|
||||
static void __init cygnus_mipipll_clk_init(struct device_node *node)
|
||||
{
|
||||
iproc_pll_clk_setup(node, &mipipll, mipipll_vco_params,
|
||||
ARRAY_SIZE(mipipll_vco_params), mipipll_clk,
|
||||
ARRAY_SIZE(mipipll_clk));
|
||||
}
|
||||
CLK_OF_DECLARE(cygnus_mipipll, "brcm,cygnus-mipipll", cygnus_mipipll_clk_init);
|
||||
|
||||
static const struct iproc_asiu_div asiu_div[] = {
|
||||
[BCM_CYGNUS_ASIU_KEYPAD_CLK] = asiu_div_val(0x0, 31, 16, 10, 0, 10),
|
||||
[BCM_CYGNUS_ASIU_ADC_CLK] = asiu_div_val(0x4, 31, 16, 10, 0, 10),
|
||||
[BCM_CYGNUS_ASIU_PWM_CLK] = asiu_div_val(0x8, 31, 16, 10, 0, 10),
|
||||
};
|
||||
|
||||
static const struct iproc_asiu_gate asiu_gate[] = {
|
||||
[BCM_CYGNUS_ASIU_KEYPAD_CLK] = asiu_gate_val(0x0, 7),
|
||||
[BCM_CYGNUS_ASIU_ADC_CLK] = asiu_gate_val(0x0, 9),
|
||||
[BCM_CYGNUS_ASIU_PWM_CLK] = asiu_gate_val(IPROC_CLK_INVALID_OFFSET, 0),
|
||||
};
|
||||
|
||||
static void __init cygnus_asiu_init(struct device_node *node)
|
||||
{
|
||||
iproc_asiu_setup(node, asiu_div, asiu_gate, ARRAY_SIZE(asiu_div));
|
||||
}
|
||||
CLK_OF_DECLARE(cygnus_asiu_clk, "brcm,cygnus-asiu-clk", cygnus_asiu_init);
|
||||
282
drivers/clk/bcm/clk-iproc-armpll.c
Normal file
282
drivers/clk/bcm/clk-iproc-armpll.c
Normal file
|
|
@ -0,0 +1,282 @@
|
|||
/*
|
||||
* Copyright (C) 2014 Broadcom Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/of_address.h>
|
||||
|
||||
#define IPROC_CLK_MAX_FREQ_POLICY 0x3
|
||||
#define IPROC_CLK_POLICY_FREQ_OFFSET 0x008
|
||||
#define IPROC_CLK_POLICY_FREQ_POLICY_FREQ_SHIFT 8
|
||||
#define IPROC_CLK_POLICY_FREQ_POLICY_FREQ_MASK 0x7
|
||||
|
||||
#define IPROC_CLK_PLLARMA_OFFSET 0xc00
|
||||
#define IPROC_CLK_PLLARMA_LOCK_SHIFT 28
|
||||
#define IPROC_CLK_PLLARMA_PDIV_SHIFT 24
|
||||
#define IPROC_CLK_PLLARMA_PDIV_MASK 0xf
|
||||
#define IPROC_CLK_PLLARMA_NDIV_INT_SHIFT 8
|
||||
#define IPROC_CLK_PLLARMA_NDIV_INT_MASK 0x3ff
|
||||
|
||||
#define IPROC_CLK_PLLARMB_OFFSET 0xc04
|
||||
#define IPROC_CLK_PLLARMB_NDIV_FRAC_MASK 0xfffff
|
||||
|
||||
#define IPROC_CLK_PLLARMC_OFFSET 0xc08
|
||||
#define IPROC_CLK_PLLARMC_BYPCLK_EN_SHIFT 8
|
||||
#define IPROC_CLK_PLLARMC_MDIV_MASK 0xff
|
||||
|
||||
#define IPROC_CLK_PLLARMCTL5_OFFSET 0xc20
|
||||
#define IPROC_CLK_PLLARMCTL5_H_MDIV_MASK 0xff
|
||||
|
||||
#define IPROC_CLK_PLLARM_OFFSET_OFFSET 0xc24
|
||||
#define IPROC_CLK_PLLARM_SW_CTL_SHIFT 29
|
||||
#define IPROC_CLK_PLLARM_NDIV_INT_OFFSET_SHIFT 20
|
||||
#define IPROC_CLK_PLLARM_NDIV_INT_OFFSET_MASK 0xff
|
||||
#define IPROC_CLK_PLLARM_NDIV_FRAC_OFFSET_MASK 0xfffff
|
||||
|
||||
#define IPROC_CLK_ARM_DIV_OFFSET 0xe00
|
||||
#define IPROC_CLK_ARM_DIV_PLL_SELECT_OVERRIDE_SHIFT 4
|
||||
#define IPROC_CLK_ARM_DIV_ARM_PLL_SELECT_MASK 0xf
|
||||
|
||||
#define IPROC_CLK_POLICY_DBG_OFFSET 0xec0
|
||||
#define IPROC_CLK_POLICY_DBG_ACT_FREQ_SHIFT 12
|
||||
#define IPROC_CLK_POLICY_DBG_ACT_FREQ_MASK 0x7
|
||||
|
||||
enum iproc_arm_pll_fid {
|
||||
ARM_PLL_FID_CRYSTAL_CLK = 0,
|
||||
ARM_PLL_FID_SYS_CLK = 2,
|
||||
ARM_PLL_FID_CH0_SLOW_CLK = 6,
|
||||
ARM_PLL_FID_CH1_FAST_CLK = 7
|
||||
};
|
||||
|
||||
struct iproc_arm_pll {
|
||||
struct clk_hw hw;
|
||||
void __iomem *base;
|
||||
unsigned long rate;
|
||||
};
|
||||
|
||||
#define to_iproc_arm_pll(hw) container_of(hw, struct iproc_arm_pll, hw)
|
||||
|
||||
static unsigned int __get_fid(struct iproc_arm_pll *pll)
|
||||
{
|
||||
u32 val;
|
||||
unsigned int policy, fid, active_fid;
|
||||
|
||||
val = readl(pll->base + IPROC_CLK_ARM_DIV_OFFSET);
|
||||
if (val & (1 << IPROC_CLK_ARM_DIV_PLL_SELECT_OVERRIDE_SHIFT))
|
||||
policy = val & IPROC_CLK_ARM_DIV_ARM_PLL_SELECT_MASK;
|
||||
else
|
||||
policy = 0;
|
||||
|
||||
/* something is seriously wrong */
|
||||
BUG_ON(policy > IPROC_CLK_MAX_FREQ_POLICY);
|
||||
|
||||
val = readl(pll->base + IPROC_CLK_POLICY_FREQ_OFFSET);
|
||||
fid = (val >> (IPROC_CLK_POLICY_FREQ_POLICY_FREQ_SHIFT * policy)) &
|
||||
IPROC_CLK_POLICY_FREQ_POLICY_FREQ_MASK;
|
||||
|
||||
val = readl(pll->base + IPROC_CLK_POLICY_DBG_OFFSET);
|
||||
active_fid = IPROC_CLK_POLICY_DBG_ACT_FREQ_MASK &
|
||||
(val >> IPROC_CLK_POLICY_DBG_ACT_FREQ_SHIFT);
|
||||
if (fid != active_fid) {
|
||||
pr_debug("%s: fid override %u->%u\n", __func__, fid,
|
||||
active_fid);
|
||||
fid = active_fid;
|
||||
}
|
||||
|
||||
pr_debug("%s: active fid: %u\n", __func__, fid);
|
||||
|
||||
return fid;
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine the mdiv (post divider) based on the frequency ID being used.
|
||||
* There are 4 sources that can be used to derive the output clock rate:
|
||||
* - 25 MHz Crystal
|
||||
* - System clock
|
||||
* - PLL channel 0 (slow clock)
|
||||
* - PLL channel 1 (fast clock)
|
||||
*/
|
||||
static int __get_mdiv(struct iproc_arm_pll *pll)
|
||||
{
|
||||
unsigned int fid;
|
||||
int mdiv;
|
||||
u32 val;
|
||||
|
||||
fid = __get_fid(pll);
|
||||
|
||||
switch (fid) {
|
||||
case ARM_PLL_FID_CRYSTAL_CLK:
|
||||
case ARM_PLL_FID_SYS_CLK:
|
||||
mdiv = 1;
|
||||
break;
|
||||
|
||||
case ARM_PLL_FID_CH0_SLOW_CLK:
|
||||
val = readl(pll->base + IPROC_CLK_PLLARMC_OFFSET);
|
||||
mdiv = val & IPROC_CLK_PLLARMC_MDIV_MASK;
|
||||
if (mdiv == 0)
|
||||
mdiv = 256;
|
||||
break;
|
||||
|
||||
case ARM_PLL_FID_CH1_FAST_CLK:
|
||||
val = readl(pll->base + IPROC_CLK_PLLARMCTL5_OFFSET);
|
||||
mdiv = val & IPROC_CLK_PLLARMCTL5_H_MDIV_MASK;
|
||||
if (mdiv == 0)
|
||||
mdiv = 256;
|
||||
break;
|
||||
|
||||
default:
|
||||
mdiv = -EFAULT;
|
||||
}
|
||||
|
||||
return mdiv;
|
||||
}
|
||||
|
||||
static unsigned int __get_ndiv(struct iproc_arm_pll *pll)
|
||||
{
|
||||
u32 val;
|
||||
unsigned int ndiv_int, ndiv_frac, ndiv;
|
||||
|
||||
val = readl(pll->base + IPROC_CLK_PLLARM_OFFSET_OFFSET);
|
||||
if (val & (1 << IPROC_CLK_PLLARM_SW_CTL_SHIFT)) {
|
||||
/*
|
||||
* offset mode is active. Read the ndiv from the PLLARM OFFSET
|
||||
* register
|
||||
*/
|
||||
ndiv_int = (val >> IPROC_CLK_PLLARM_NDIV_INT_OFFSET_SHIFT) &
|
||||
IPROC_CLK_PLLARM_NDIV_INT_OFFSET_MASK;
|
||||
if (ndiv_int == 0)
|
||||
ndiv_int = 256;
|
||||
|
||||
ndiv_frac = val & IPROC_CLK_PLLARM_NDIV_FRAC_OFFSET_MASK;
|
||||
} else {
|
||||
/* offset mode not active */
|
||||
val = readl(pll->base + IPROC_CLK_PLLARMA_OFFSET);
|
||||
ndiv_int = (val >> IPROC_CLK_PLLARMA_NDIV_INT_SHIFT) &
|
||||
IPROC_CLK_PLLARMA_NDIV_INT_MASK;
|
||||
if (ndiv_int == 0)
|
||||
ndiv_int = 1024;
|
||||
|
||||
val = readl(pll->base + IPROC_CLK_PLLARMB_OFFSET);
|
||||
ndiv_frac = val & IPROC_CLK_PLLARMB_NDIV_FRAC_MASK;
|
||||
}
|
||||
|
||||
ndiv = (ndiv_int << 20) | ndiv_frac;
|
||||
|
||||
return ndiv;
|
||||
}
|
||||
|
||||
/*
|
||||
* The output frequency of the ARM PLL is calculated based on the ARM PLL
|
||||
* divider values:
|
||||
* pdiv = ARM PLL pre-divider
|
||||
* ndiv = ARM PLL multiplier
|
||||
* mdiv = ARM PLL post divider
|
||||
*
|
||||
* The frequency is calculated by:
|
||||
* ((ndiv * parent clock rate) / pdiv) / mdiv
|
||||
*/
|
||||
static unsigned long iproc_arm_pll_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct iproc_arm_pll *pll = to_iproc_arm_pll(hw);
|
||||
u32 val;
|
||||
int mdiv;
|
||||
u64 ndiv;
|
||||
unsigned int pdiv;
|
||||
|
||||
/* in bypass mode, use parent rate */
|
||||
val = readl(pll->base + IPROC_CLK_PLLARMC_OFFSET);
|
||||
if (val & (1 << IPROC_CLK_PLLARMC_BYPCLK_EN_SHIFT)) {
|
||||
pll->rate = parent_rate;
|
||||
return pll->rate;
|
||||
}
|
||||
|
||||
/* PLL needs to be locked */
|
||||
val = readl(pll->base + IPROC_CLK_PLLARMA_OFFSET);
|
||||
if (!(val & (1 << IPROC_CLK_PLLARMA_LOCK_SHIFT))) {
|
||||
pll->rate = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
pdiv = (val >> IPROC_CLK_PLLARMA_PDIV_SHIFT) &
|
||||
IPROC_CLK_PLLARMA_PDIV_MASK;
|
||||
if (pdiv == 0)
|
||||
pdiv = 16;
|
||||
|
||||
ndiv = __get_ndiv(pll);
|
||||
mdiv = __get_mdiv(pll);
|
||||
if (mdiv <= 0) {
|
||||
pll->rate = 0;
|
||||
return 0;
|
||||
}
|
||||
pll->rate = (ndiv * parent_rate) >> 20;
|
||||
pll->rate = (pll->rate / pdiv) / mdiv;
|
||||
|
||||
pr_debug("%s: ARM PLL rate: %lu. parent rate: %lu\n", __func__,
|
||||
pll->rate, parent_rate);
|
||||
pr_debug("%s: ndiv_int: %u, pdiv: %u, mdiv: %d\n", __func__,
|
||||
(unsigned int)(ndiv >> 20), pdiv, mdiv);
|
||||
|
||||
return pll->rate;
|
||||
}
|
||||
|
||||
static const struct clk_ops iproc_arm_pll_ops = {
|
||||
.recalc_rate = iproc_arm_pll_recalc_rate,
|
||||
};
|
||||
|
||||
void __init iproc_armpll_setup(struct device_node *node)
|
||||
{
|
||||
int ret;
|
||||
struct clk *clk;
|
||||
struct iproc_arm_pll *pll;
|
||||
struct clk_init_data init;
|
||||
const char *parent_name;
|
||||
|
||||
pll = kzalloc(sizeof(*pll), GFP_KERNEL);
|
||||
if (WARN_ON(!pll))
|
||||
return;
|
||||
|
||||
pll->base = of_iomap(node, 0);
|
||||
if (WARN_ON(!pll->base))
|
||||
goto err_free_pll;
|
||||
|
||||
init.name = node->name;
|
||||
init.ops = &iproc_arm_pll_ops;
|
||||
init.flags = 0;
|
||||
parent_name = of_clk_get_parent_name(node, 0);
|
||||
init.parent_names = (parent_name ? &parent_name : NULL);
|
||||
init.num_parents = (parent_name ? 1 : 0);
|
||||
pll->hw.init = &init;
|
||||
|
||||
clk = clk_register(NULL, &pll->hw);
|
||||
if (WARN_ON(IS_ERR(clk)))
|
||||
goto err_iounmap;
|
||||
|
||||
ret = of_clk_add_provider(node, of_clk_src_simple_get, clk);
|
||||
if (WARN_ON(ret))
|
||||
goto err_clk_unregister;
|
||||
|
||||
return;
|
||||
|
||||
err_clk_unregister:
|
||||
clk_unregister(clk);
|
||||
err_iounmap:
|
||||
iounmap(pll->base);
|
||||
err_free_pll:
|
||||
kfree(pll);
|
||||
}
|
||||
276
drivers/clk/bcm/clk-iproc-asiu.c
Normal file
276
drivers/clk/bcm/clk-iproc-asiu.c
Normal file
|
|
@ -0,0 +1,276 @@
|
|||
/*
|
||||
* Copyright (C) 2014 Broadcom Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include "clk-iproc.h"
|
||||
|
||||
struct iproc_asiu;
|
||||
|
||||
struct iproc_asiu_clk {
|
||||
struct clk_hw hw;
|
||||
const char *name;
|
||||
struct iproc_asiu *asiu;
|
||||
unsigned long rate;
|
||||
struct iproc_asiu_div div;
|
||||
struct iproc_asiu_gate gate;
|
||||
};
|
||||
|
||||
struct iproc_asiu {
|
||||
void __iomem *div_base;
|
||||
void __iomem *gate_base;
|
||||
|
||||
struct clk_onecell_data clk_data;
|
||||
struct iproc_asiu_clk *clks;
|
||||
};
|
||||
|
||||
#define to_asiu_clk(hw) container_of(hw, struct iproc_asiu_clk, hw)
|
||||
|
||||
static int iproc_asiu_clk_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct iproc_asiu_clk *clk = to_asiu_clk(hw);
|
||||
struct iproc_asiu *asiu = clk->asiu;
|
||||
u32 val;
|
||||
|
||||
/* some clocks at the ASIU level are always enabled */
|
||||
if (clk->gate.offset == IPROC_CLK_INVALID_OFFSET)
|
||||
return 0;
|
||||
|
||||
val = readl(asiu->gate_base + clk->gate.offset);
|
||||
val |= (1 << clk->gate.en_shift);
|
||||
writel(val, asiu->gate_base + clk->gate.offset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void iproc_asiu_clk_disable(struct clk_hw *hw)
|
||||
{
|
||||
struct iproc_asiu_clk *clk = to_asiu_clk(hw);
|
||||
struct iproc_asiu *asiu = clk->asiu;
|
||||
u32 val;
|
||||
|
||||
/* some clocks at the ASIU level are always enabled */
|
||||
if (clk->gate.offset == IPROC_CLK_INVALID_OFFSET)
|
||||
return;
|
||||
|
||||
val = readl(asiu->gate_base + clk->gate.offset);
|
||||
val &= ~(1 << clk->gate.en_shift);
|
||||
writel(val, asiu->gate_base + clk->gate.offset);
|
||||
}
|
||||
|
||||
static unsigned long iproc_asiu_clk_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct iproc_asiu_clk *clk = to_asiu_clk(hw);
|
||||
struct iproc_asiu *asiu = clk->asiu;
|
||||
u32 val;
|
||||
unsigned int div_h, div_l;
|
||||
|
||||
if (parent_rate == 0) {
|
||||
clk->rate = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* if clock divisor is not enabled, simply return parent rate */
|
||||
val = readl(asiu->div_base + clk->div.offset);
|
||||
if ((val & (1 << clk->div.en_shift)) == 0) {
|
||||
clk->rate = parent_rate;
|
||||
return parent_rate;
|
||||
}
|
||||
|
||||
/* clock rate = parent rate / (high_div + 1) + (low_div + 1) */
|
||||
div_h = (val >> clk->div.high_shift) & bit_mask(clk->div.high_width);
|
||||
div_h++;
|
||||
div_l = (val >> clk->div.low_shift) & bit_mask(clk->div.low_width);
|
||||
div_l++;
|
||||
|
||||
clk->rate = parent_rate / (div_h + div_l);
|
||||
pr_debug("%s: rate: %lu. parent rate: %lu div_h: %u div_l: %u\n",
|
||||
__func__, clk->rate, parent_rate, div_h, div_l);
|
||||
|
||||
return clk->rate;
|
||||
}
|
||||
|
||||
static long iproc_asiu_clk_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
unsigned int div;
|
||||
|
||||
if (rate == 0 || *parent_rate == 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (rate == *parent_rate)
|
||||
return *parent_rate;
|
||||
|
||||
div = DIV_ROUND_UP(*parent_rate, rate);
|
||||
if (div < 2)
|
||||
return *parent_rate;
|
||||
|
||||
return *parent_rate / div;
|
||||
}
|
||||
|
||||
static int iproc_asiu_clk_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct iproc_asiu_clk *clk = to_asiu_clk(hw);
|
||||
struct iproc_asiu *asiu = clk->asiu;
|
||||
unsigned int div, div_h, div_l;
|
||||
u32 val;
|
||||
|
||||
if (rate == 0 || parent_rate == 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* simply disable the divisor if one wants the same rate as parent */
|
||||
if (rate == parent_rate) {
|
||||
val = readl(asiu->div_base + clk->div.offset);
|
||||
val &= ~(1 << clk->div.en_shift);
|
||||
writel(val, asiu->div_base + clk->div.offset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
div = DIV_ROUND_UP(parent_rate, rate);
|
||||
if (div < 2)
|
||||
return -EINVAL;
|
||||
|
||||
div_h = div_l = div >> 1;
|
||||
div_h--;
|
||||
div_l--;
|
||||
|
||||
val = readl(asiu->div_base + clk->div.offset);
|
||||
val |= 1 << clk->div.en_shift;
|
||||
if (div_h) {
|
||||
val &= ~(bit_mask(clk->div.high_width)
|
||||
<< clk->div.high_shift);
|
||||
val |= div_h << clk->div.high_shift;
|
||||
} else {
|
||||
val &= ~(bit_mask(clk->div.high_width)
|
||||
<< clk->div.high_shift);
|
||||
}
|
||||
if (div_l) {
|
||||
val &= ~(bit_mask(clk->div.low_width) << clk->div.low_shift);
|
||||
val |= div_l << clk->div.low_shift;
|
||||
} else {
|
||||
val &= ~(bit_mask(clk->div.low_width) << clk->div.low_shift);
|
||||
}
|
||||
writel(val, asiu->div_base + clk->div.offset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct clk_ops iproc_asiu_ops = {
|
||||
.enable = iproc_asiu_clk_enable,
|
||||
.disable = iproc_asiu_clk_disable,
|
||||
.recalc_rate = iproc_asiu_clk_recalc_rate,
|
||||
.round_rate = iproc_asiu_clk_round_rate,
|
||||
.set_rate = iproc_asiu_clk_set_rate,
|
||||
};
|
||||
|
||||
void __init iproc_asiu_setup(struct device_node *node,
|
||||
const struct iproc_asiu_div *div,
|
||||
const struct iproc_asiu_gate *gate,
|
||||
unsigned int num_clks)
|
||||
{
|
||||
int i, ret;
|
||||
struct iproc_asiu *asiu;
|
||||
|
||||
if (WARN_ON(!gate || !div))
|
||||
return;
|
||||
|
||||
asiu = kzalloc(sizeof(*asiu), GFP_KERNEL);
|
||||
if (WARN_ON(!asiu))
|
||||
return;
|
||||
|
||||
asiu->clk_data.clk_num = num_clks;
|
||||
asiu->clk_data.clks = kcalloc(num_clks, sizeof(*asiu->clk_data.clks),
|
||||
GFP_KERNEL);
|
||||
if (WARN_ON(!asiu->clk_data.clks))
|
||||
goto err_clks;
|
||||
|
||||
asiu->clks = kcalloc(num_clks, sizeof(*asiu->clks), GFP_KERNEL);
|
||||
if (WARN_ON(!asiu->clks))
|
||||
goto err_asiu_clks;
|
||||
|
||||
asiu->div_base = of_iomap(node, 0);
|
||||
if (WARN_ON(!asiu->div_base))
|
||||
goto err_iomap_div;
|
||||
|
||||
asiu->gate_base = of_iomap(node, 1);
|
||||
if (WARN_ON(!asiu->gate_base))
|
||||
goto err_iomap_gate;
|
||||
|
||||
for (i = 0; i < num_clks; i++) {
|
||||
struct clk_init_data init;
|
||||
struct clk *clk;
|
||||
const char *parent_name;
|
||||
struct iproc_asiu_clk *asiu_clk;
|
||||
const char *clk_name;
|
||||
|
||||
clk_name = kzalloc(IPROC_CLK_NAME_LEN, GFP_KERNEL);
|
||||
if (WARN_ON(!clk_name))
|
||||
goto err_clk_register;
|
||||
|
||||
ret = of_property_read_string_index(node, "clock-output-names",
|
||||
i, &clk_name);
|
||||
if (WARN_ON(ret))
|
||||
goto err_clk_register;
|
||||
|
||||
asiu_clk = &asiu->clks[i];
|
||||
asiu_clk->name = clk_name;
|
||||
asiu_clk->asiu = asiu;
|
||||
asiu_clk->div = div[i];
|
||||
asiu_clk->gate = gate[i];
|
||||
init.name = clk_name;
|
||||
init.ops = &iproc_asiu_ops;
|
||||
init.flags = 0;
|
||||
parent_name = of_clk_get_parent_name(node, 0);
|
||||
init.parent_names = (parent_name ? &parent_name : NULL);
|
||||
init.num_parents = (parent_name ? 1 : 0);
|
||||
asiu_clk->hw.init = &init;
|
||||
|
||||
clk = clk_register(NULL, &asiu_clk->hw);
|
||||
if (WARN_ON(IS_ERR(clk)))
|
||||
goto err_clk_register;
|
||||
asiu->clk_data.clks[i] = clk;
|
||||
}
|
||||
|
||||
ret = of_clk_add_provider(node, of_clk_src_onecell_get,
|
||||
&asiu->clk_data);
|
||||
if (WARN_ON(ret))
|
||||
goto err_clk_register;
|
||||
|
||||
return;
|
||||
|
||||
err_clk_register:
|
||||
for (i = 0; i < num_clks; i++)
|
||||
kfree(asiu->clks[i].name);
|
||||
iounmap(asiu->gate_base);
|
||||
|
||||
err_iomap_gate:
|
||||
iounmap(asiu->div_base);
|
||||
|
||||
err_iomap_div:
|
||||
kfree(asiu->clks);
|
||||
|
||||
err_asiu_clks:
|
||||
kfree(asiu->clk_data.clks);
|
||||
|
||||
err_clks:
|
||||
kfree(asiu);
|
||||
}
|
||||
716
drivers/clk/bcm/clk-iproc-pll.c
Normal file
716
drivers/clk/bcm/clk-iproc-pll.c
Normal file
|
|
@ -0,0 +1,716 @@
|
|||
/*
|
||||
* Copyright (C) 2014 Broadcom Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include "clk-iproc.h"
|
||||
|
||||
#define PLL_VCO_HIGH_SHIFT 19
|
||||
#define PLL_VCO_LOW_SHIFT 30
|
||||
|
||||
/* number of delay loops waiting for PLL to lock */
|
||||
#define LOCK_DELAY 100
|
||||
|
||||
/* number of VCO frequency bands */
|
||||
#define NUM_FREQ_BANDS 8
|
||||
|
||||
#define NUM_KP_BANDS 3
|
||||
enum kp_band {
|
||||
KP_BAND_MID = 0,
|
||||
KP_BAND_HIGH,
|
||||
KP_BAND_HIGH_HIGH
|
||||
};
|
||||
|
||||
static const unsigned int kp_table[NUM_KP_BANDS][NUM_FREQ_BANDS] = {
|
||||
{ 5, 6, 6, 7, 7, 8, 9, 10 },
|
||||
{ 4, 4, 5, 5, 6, 7, 8, 9 },
|
||||
{ 4, 5, 5, 6, 7, 8, 9, 10 },
|
||||
};
|
||||
|
||||
static const unsigned long ref_freq_table[NUM_FREQ_BANDS][2] = {
|
||||
{ 10000000, 12500000 },
|
||||
{ 12500000, 15000000 },
|
||||
{ 15000000, 20000000 },
|
||||
{ 20000000, 25000000 },
|
||||
{ 25000000, 50000000 },
|
||||
{ 50000000, 75000000 },
|
||||
{ 75000000, 100000000 },
|
||||
{ 100000000, 125000000 },
|
||||
};
|
||||
|
||||
enum vco_freq_range {
|
||||
VCO_LOW = 700000000U,
|
||||
VCO_MID = 1200000000U,
|
||||
VCO_HIGH = 2200000000U,
|
||||
VCO_HIGH_HIGH = 3100000000U,
|
||||
VCO_MAX = 4000000000U,
|
||||
};
|
||||
|
||||
struct iproc_pll;
|
||||
|
||||
struct iproc_clk {
|
||||
struct clk_hw hw;
|
||||
const char *name;
|
||||
struct iproc_pll *pll;
|
||||
unsigned long rate;
|
||||
const struct iproc_clk_ctrl *ctrl;
|
||||
};
|
||||
|
||||
struct iproc_pll {
|
||||
void __iomem *pll_base;
|
||||
void __iomem *pwr_base;
|
||||
void __iomem *asiu_base;
|
||||
|
||||
const struct iproc_pll_ctrl *ctrl;
|
||||
const struct iproc_pll_vco_param *vco_param;
|
||||
unsigned int num_vco_entries;
|
||||
|
||||
struct clk_onecell_data clk_data;
|
||||
struct iproc_clk *clks;
|
||||
};
|
||||
|
||||
#define to_iproc_clk(hw) container_of(hw, struct iproc_clk, hw)
|
||||
|
||||
/*
|
||||
* Based on the target frequency, find a match from the VCO frequency parameter
|
||||
* table and return its index
|
||||
*/
|
||||
static int pll_get_rate_index(struct iproc_pll *pll, unsigned int target_rate)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < pll->num_vco_entries; i++)
|
||||
if (target_rate == pll->vco_param[i].rate)
|
||||
break;
|
||||
|
||||
if (i >= pll->num_vco_entries)
|
||||
return -EINVAL;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static int get_kp(unsigned long ref_freq, enum kp_band kp_index)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (ref_freq < ref_freq_table[0][0])
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < NUM_FREQ_BANDS; i++) {
|
||||
if (ref_freq >= ref_freq_table[i][0] &&
|
||||
ref_freq < ref_freq_table[i][1])
|
||||
return kp_table[kp_index][i];
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int pll_wait_for_lock(struct iproc_pll *pll)
|
||||
{
|
||||
int i;
|
||||
const struct iproc_pll_ctrl *ctrl = pll->ctrl;
|
||||
|
||||
for (i = 0; i < LOCK_DELAY; i++) {
|
||||
u32 val = readl(pll->pll_base + ctrl->status.offset);
|
||||
|
||||
if (val & (1 << ctrl->status.shift))
|
||||
return 0;
|
||||
udelay(10);
|
||||
}
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static void __pll_disable(struct iproc_pll *pll)
|
||||
{
|
||||
const struct iproc_pll_ctrl *ctrl = pll->ctrl;
|
||||
u32 val;
|
||||
|
||||
if (ctrl->flags & IPROC_CLK_PLL_ASIU) {
|
||||
val = readl(pll->asiu_base + ctrl->asiu.offset);
|
||||
val &= ~(1 << ctrl->asiu.en_shift);
|
||||
writel(val, pll->asiu_base + ctrl->asiu.offset);
|
||||
}
|
||||
|
||||
/* latch input value so core power can be shut down */
|
||||
val = readl(pll->pwr_base + ctrl->aon.offset);
|
||||
val |= (1 << ctrl->aon.iso_shift);
|
||||
writel(val, pll->pwr_base + ctrl->aon.offset);
|
||||
|
||||
/* power down the core */
|
||||
val &= ~(bit_mask(ctrl->aon.pwr_width) << ctrl->aon.pwr_shift);
|
||||
writel(val, pll->pwr_base + ctrl->aon.offset);
|
||||
}
|
||||
|
||||
static int __pll_enable(struct iproc_pll *pll)
|
||||
{
|
||||
const struct iproc_pll_ctrl *ctrl = pll->ctrl;
|
||||
u32 val;
|
||||
|
||||
/* power up the PLL and make sure it's not latched */
|
||||
val = readl(pll->pwr_base + ctrl->aon.offset);
|
||||
val |= bit_mask(ctrl->aon.pwr_width) << ctrl->aon.pwr_shift;
|
||||
val &= ~(1 << ctrl->aon.iso_shift);
|
||||
writel(val, pll->pwr_base + ctrl->aon.offset);
|
||||
|
||||
/* certain PLLs also need to be ungated from the ASIU top level */
|
||||
if (ctrl->flags & IPROC_CLK_PLL_ASIU) {
|
||||
val = readl(pll->asiu_base + ctrl->asiu.offset);
|
||||
val |= (1 << ctrl->asiu.en_shift);
|
||||
writel(val, pll->asiu_base + ctrl->asiu.offset);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __pll_put_in_reset(struct iproc_pll *pll)
|
||||
{
|
||||
u32 val;
|
||||
const struct iproc_pll_ctrl *ctrl = pll->ctrl;
|
||||
const struct iproc_pll_reset_ctrl *reset = &ctrl->reset;
|
||||
|
||||
val = readl(pll->pll_base + reset->offset);
|
||||
val &= ~(1 << reset->reset_shift | 1 << reset->p_reset_shift);
|
||||
writel(val, pll->pll_base + reset->offset);
|
||||
if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK))
|
||||
readl(pll->pll_base + reset->offset);
|
||||
}
|
||||
|
||||
static void __pll_bring_out_reset(struct iproc_pll *pll, unsigned int kp,
|
||||
unsigned int ka, unsigned int ki)
|
||||
{
|
||||
u32 val;
|
||||
const struct iproc_pll_ctrl *ctrl = pll->ctrl;
|
||||
const struct iproc_pll_reset_ctrl *reset = &ctrl->reset;
|
||||
|
||||
val = readl(pll->pll_base + reset->offset);
|
||||
val &= ~(bit_mask(reset->ki_width) << reset->ki_shift |
|
||||
bit_mask(reset->kp_width) << reset->kp_shift |
|
||||
bit_mask(reset->ka_width) << reset->ka_shift);
|
||||
val |= ki << reset->ki_shift | kp << reset->kp_shift |
|
||||
ka << reset->ka_shift;
|
||||
val |= 1 << reset->reset_shift | 1 << reset->p_reset_shift;
|
||||
writel(val, pll->pll_base + reset->offset);
|
||||
if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK))
|
||||
readl(pll->pll_base + reset->offset);
|
||||
}
|
||||
|
||||
static int pll_set_rate(struct iproc_clk *clk, unsigned int rate_index,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct iproc_pll *pll = clk->pll;
|
||||
const struct iproc_pll_vco_param *vco = &pll->vco_param[rate_index];
|
||||
const struct iproc_pll_ctrl *ctrl = pll->ctrl;
|
||||
int ka = 0, ki, kp, ret;
|
||||
unsigned long rate = vco->rate;
|
||||
u32 val;
|
||||
enum kp_band kp_index;
|
||||
unsigned long ref_freq;
|
||||
|
||||
/*
|
||||
* reference frequency = parent frequency / PDIV
|
||||
* If PDIV = 0, then it becomes a multiplier (x2)
|
||||
*/
|
||||
if (vco->pdiv == 0)
|
||||
ref_freq = parent_rate * 2;
|
||||
else
|
||||
ref_freq = parent_rate / vco->pdiv;
|
||||
|
||||
/* determine Ki and Kp index based on target VCO frequency */
|
||||
if (rate >= VCO_LOW && rate < VCO_HIGH) {
|
||||
ki = 4;
|
||||
kp_index = KP_BAND_MID;
|
||||
} else if (rate >= VCO_HIGH && rate && rate < VCO_HIGH_HIGH) {
|
||||
ki = 3;
|
||||
kp_index = KP_BAND_HIGH;
|
||||
} else if (rate >= VCO_HIGH_HIGH && rate < VCO_MAX) {
|
||||
ki = 3;
|
||||
kp_index = KP_BAND_HIGH_HIGH;
|
||||
} else {
|
||||
pr_err("%s: pll: %s has invalid rate: %lu\n", __func__,
|
||||
clk->name, rate);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
kp = get_kp(ref_freq, kp_index);
|
||||
if (kp < 0) {
|
||||
pr_err("%s: pll: %s has invalid kp\n", __func__, clk->name);
|
||||
return kp;
|
||||
}
|
||||
|
||||
ret = __pll_enable(pll);
|
||||
if (ret) {
|
||||
pr_err("%s: pll: %s fails to enable\n", __func__, clk->name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* put PLL in reset */
|
||||
__pll_put_in_reset(pll);
|
||||
|
||||
writel(0, pll->pll_base + ctrl->vco_ctrl.u_offset);
|
||||
if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK))
|
||||
readl(pll->pll_base + ctrl->vco_ctrl.u_offset);
|
||||
val = readl(pll->pll_base + ctrl->vco_ctrl.l_offset);
|
||||
|
||||
if (rate >= VCO_LOW && rate < VCO_MID)
|
||||
val |= (1 << PLL_VCO_LOW_SHIFT);
|
||||
|
||||
if (rate < VCO_HIGH)
|
||||
val &= ~(1 << PLL_VCO_HIGH_SHIFT);
|
||||
else
|
||||
val |= (1 << PLL_VCO_HIGH_SHIFT);
|
||||
|
||||
writel(val, pll->pll_base + ctrl->vco_ctrl.l_offset);
|
||||
if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK))
|
||||
readl(pll->pll_base + ctrl->vco_ctrl.l_offset);
|
||||
|
||||
/* program integer part of NDIV */
|
||||
val = readl(pll->pll_base + ctrl->ndiv_int.offset);
|
||||
val &= ~(bit_mask(ctrl->ndiv_int.width) << ctrl->ndiv_int.shift);
|
||||
val |= vco->ndiv_int << ctrl->ndiv_int.shift;
|
||||
writel(val, pll->pll_base + ctrl->ndiv_int.offset);
|
||||
if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK))
|
||||
readl(pll->pll_base + ctrl->ndiv_int.offset);
|
||||
|
||||
/* program fractional part of NDIV */
|
||||
if (ctrl->flags & IPROC_CLK_PLL_HAS_NDIV_FRAC) {
|
||||
val = readl(pll->pll_base + ctrl->ndiv_frac.offset);
|
||||
val &= ~(bit_mask(ctrl->ndiv_frac.width) <<
|
||||
ctrl->ndiv_frac.shift);
|
||||
val |= vco->ndiv_frac << ctrl->ndiv_frac.shift;
|
||||
writel(val, pll->pll_base + ctrl->ndiv_frac.offset);
|
||||
if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK))
|
||||
readl(pll->pll_base + ctrl->ndiv_frac.offset);
|
||||
}
|
||||
|
||||
/* program PDIV */
|
||||
val = readl(pll->pll_base + ctrl->pdiv.offset);
|
||||
val &= ~(bit_mask(ctrl->pdiv.width) << ctrl->pdiv.shift);
|
||||
val |= vco->pdiv << ctrl->pdiv.shift;
|
||||
writel(val, pll->pll_base + ctrl->pdiv.offset);
|
||||
if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK))
|
||||
readl(pll->pll_base + ctrl->pdiv.offset);
|
||||
|
||||
__pll_bring_out_reset(pll, kp, ka, ki);
|
||||
|
||||
ret = pll_wait_for_lock(pll);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: pll: %s failed to lock\n", __func__, clk->name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iproc_pll_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct iproc_clk *clk = to_iproc_clk(hw);
|
||||
struct iproc_pll *pll = clk->pll;
|
||||
|
||||
return __pll_enable(pll);
|
||||
}
|
||||
|
||||
static void iproc_pll_disable(struct clk_hw *hw)
|
||||
{
|
||||
struct iproc_clk *clk = to_iproc_clk(hw);
|
||||
struct iproc_pll *pll = clk->pll;
|
||||
const struct iproc_pll_ctrl *ctrl = pll->ctrl;
|
||||
|
||||
if (ctrl->flags & IPROC_CLK_AON)
|
||||
return;
|
||||
|
||||
__pll_disable(pll);
|
||||
}
|
||||
|
||||
static unsigned long iproc_pll_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct iproc_clk *clk = to_iproc_clk(hw);
|
||||
struct iproc_pll *pll = clk->pll;
|
||||
const struct iproc_pll_ctrl *ctrl = pll->ctrl;
|
||||
u32 val;
|
||||
u64 ndiv;
|
||||
unsigned int ndiv_int, ndiv_frac, pdiv;
|
||||
|
||||
if (parent_rate == 0)
|
||||
return 0;
|
||||
|
||||
/* PLL needs to be locked */
|
||||
val = readl(pll->pll_base + ctrl->status.offset);
|
||||
if ((val & (1 << ctrl->status.shift)) == 0) {
|
||||
clk->rate = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* PLL output frequency =
|
||||
*
|
||||
* ((ndiv_int + ndiv_frac / 2^20) * (parent clock rate / pdiv)
|
||||
*/
|
||||
val = readl(pll->pll_base + ctrl->ndiv_int.offset);
|
||||
ndiv_int = (val >> ctrl->ndiv_int.shift) &
|
||||
bit_mask(ctrl->ndiv_int.width);
|
||||
ndiv = ndiv_int << ctrl->ndiv_int.shift;
|
||||
|
||||
if (ctrl->flags & IPROC_CLK_PLL_HAS_NDIV_FRAC) {
|
||||
val = readl(pll->pll_base + ctrl->ndiv_frac.offset);
|
||||
ndiv_frac = (val >> ctrl->ndiv_frac.shift) &
|
||||
bit_mask(ctrl->ndiv_frac.width);
|
||||
|
||||
if (ndiv_frac != 0)
|
||||
ndiv = (ndiv_int << ctrl->ndiv_int.shift) | ndiv_frac;
|
||||
}
|
||||
|
||||
val = readl(pll->pll_base + ctrl->pdiv.offset);
|
||||
pdiv = (val >> ctrl->pdiv.shift) & bit_mask(ctrl->pdiv.width);
|
||||
|
||||
clk->rate = (ndiv * parent_rate) >> ctrl->ndiv_int.shift;
|
||||
|
||||
if (pdiv == 0)
|
||||
clk->rate *= 2;
|
||||
else
|
||||
clk->rate /= pdiv;
|
||||
|
||||
return clk->rate;
|
||||
}
|
||||
|
||||
static long iproc_pll_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
unsigned i;
|
||||
struct iproc_clk *clk = to_iproc_clk(hw);
|
||||
struct iproc_pll *pll = clk->pll;
|
||||
|
||||
if (rate == 0 || *parent_rate == 0 || !pll->vco_param)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < pll->num_vco_entries; i++) {
|
||||
if (rate <= pll->vco_param[i].rate)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == pll->num_vco_entries)
|
||||
i--;
|
||||
|
||||
return pll->vco_param[i].rate;
|
||||
}
|
||||
|
||||
static int iproc_pll_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct iproc_clk *clk = to_iproc_clk(hw);
|
||||
struct iproc_pll *pll = clk->pll;
|
||||
int rate_index, ret;
|
||||
|
||||
rate_index = pll_get_rate_index(pll, rate);
|
||||
if (rate_index < 0)
|
||||
return rate_index;
|
||||
|
||||
ret = pll_set_rate(clk, rate_index, parent_rate);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct clk_ops iproc_pll_ops = {
|
||||
.enable = iproc_pll_enable,
|
||||
.disable = iproc_pll_disable,
|
||||
.recalc_rate = iproc_pll_recalc_rate,
|
||||
.round_rate = iproc_pll_round_rate,
|
||||
.set_rate = iproc_pll_set_rate,
|
||||
};
|
||||
|
||||
static int iproc_clk_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct iproc_clk *clk = to_iproc_clk(hw);
|
||||
const struct iproc_clk_ctrl *ctrl = clk->ctrl;
|
||||
struct iproc_pll *pll = clk->pll;
|
||||
u32 val;
|
||||
|
||||
/* channel enable is active low */
|
||||
val = readl(pll->pll_base + ctrl->enable.offset);
|
||||
val &= ~(1 << ctrl->enable.enable_shift);
|
||||
writel(val, pll->pll_base + ctrl->enable.offset);
|
||||
|
||||
/* also make sure channel is not held */
|
||||
val = readl(pll->pll_base + ctrl->enable.offset);
|
||||
val &= ~(1 << ctrl->enable.hold_shift);
|
||||
writel(val, pll->pll_base + ctrl->enable.offset);
|
||||
if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK))
|
||||
readl(pll->pll_base + ctrl->enable.offset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void iproc_clk_disable(struct clk_hw *hw)
|
||||
{
|
||||
struct iproc_clk *clk = to_iproc_clk(hw);
|
||||
const struct iproc_clk_ctrl *ctrl = clk->ctrl;
|
||||
struct iproc_pll *pll = clk->pll;
|
||||
u32 val;
|
||||
|
||||
if (ctrl->flags & IPROC_CLK_AON)
|
||||
return;
|
||||
|
||||
val = readl(pll->pll_base + ctrl->enable.offset);
|
||||
val |= 1 << ctrl->enable.enable_shift;
|
||||
writel(val, pll->pll_base + ctrl->enable.offset);
|
||||
if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK))
|
||||
readl(pll->pll_base + ctrl->enable.offset);
|
||||
}
|
||||
|
||||
static unsigned long iproc_clk_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct iproc_clk *clk = to_iproc_clk(hw);
|
||||
const struct iproc_clk_ctrl *ctrl = clk->ctrl;
|
||||
struct iproc_pll *pll = clk->pll;
|
||||
u32 val;
|
||||
unsigned int mdiv;
|
||||
|
||||
if (parent_rate == 0)
|
||||
return 0;
|
||||
|
||||
val = readl(pll->pll_base + ctrl->mdiv.offset);
|
||||
mdiv = (val >> ctrl->mdiv.shift) & bit_mask(ctrl->mdiv.width);
|
||||
if (mdiv == 0)
|
||||
mdiv = 256;
|
||||
|
||||
clk->rate = parent_rate / mdiv;
|
||||
|
||||
return clk->rate;
|
||||
}
|
||||
|
||||
static long iproc_clk_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
unsigned int div;
|
||||
|
||||
if (rate == 0 || *parent_rate == 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (rate == *parent_rate)
|
||||
return *parent_rate;
|
||||
|
||||
div = DIV_ROUND_UP(*parent_rate, rate);
|
||||
if (div < 2)
|
||||
return *parent_rate;
|
||||
|
||||
if (div > 256)
|
||||
div = 256;
|
||||
|
||||
return *parent_rate / div;
|
||||
}
|
||||
|
||||
static int iproc_clk_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct iproc_clk *clk = to_iproc_clk(hw);
|
||||
const struct iproc_clk_ctrl *ctrl = clk->ctrl;
|
||||
struct iproc_pll *pll = clk->pll;
|
||||
u32 val;
|
||||
unsigned int div;
|
||||
|
||||
if (rate == 0 || parent_rate == 0)
|
||||
return -EINVAL;
|
||||
|
||||
div = DIV_ROUND_UP(parent_rate, rate);
|
||||
if (div > 256)
|
||||
return -EINVAL;
|
||||
|
||||
val = readl(pll->pll_base + ctrl->mdiv.offset);
|
||||
if (div == 256) {
|
||||
val &= ~(bit_mask(ctrl->mdiv.width) << ctrl->mdiv.shift);
|
||||
} else {
|
||||
val &= ~(bit_mask(ctrl->mdiv.width) << ctrl->mdiv.shift);
|
||||
val |= div << ctrl->mdiv.shift;
|
||||
}
|
||||
writel(val, pll->pll_base + ctrl->mdiv.offset);
|
||||
if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK))
|
||||
readl(pll->pll_base + ctrl->mdiv.offset);
|
||||
clk->rate = parent_rate / div;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct clk_ops iproc_clk_ops = {
|
||||
.enable = iproc_clk_enable,
|
||||
.disable = iproc_clk_disable,
|
||||
.recalc_rate = iproc_clk_recalc_rate,
|
||||
.round_rate = iproc_clk_round_rate,
|
||||
.set_rate = iproc_clk_set_rate,
|
||||
};
|
||||
|
||||
/**
|
||||
* Some PLLs require the PLL SW override bit to be set before changes can be
|
||||
* applied to the PLL
|
||||
*/
|
||||
static void iproc_pll_sw_cfg(struct iproc_pll *pll)
|
||||
{
|
||||
const struct iproc_pll_ctrl *ctrl = pll->ctrl;
|
||||
|
||||
if (ctrl->flags & IPROC_CLK_PLL_NEEDS_SW_CFG) {
|
||||
u32 val;
|
||||
|
||||
val = readl(pll->pll_base + ctrl->sw_ctrl.offset);
|
||||
val |= BIT(ctrl->sw_ctrl.shift);
|
||||
writel(val, pll->pll_base + ctrl->sw_ctrl.offset);
|
||||
if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK))
|
||||
readl(pll->pll_base + ctrl->sw_ctrl.offset);
|
||||
}
|
||||
}
|
||||
|
||||
void __init iproc_pll_clk_setup(struct device_node *node,
|
||||
const struct iproc_pll_ctrl *pll_ctrl,
|
||||
const struct iproc_pll_vco_param *vco,
|
||||
unsigned int num_vco_entries,
|
||||
const struct iproc_clk_ctrl *clk_ctrl,
|
||||
unsigned int num_clks)
|
||||
{
|
||||
int i, ret;
|
||||
struct clk *clk;
|
||||
struct iproc_pll *pll;
|
||||
struct iproc_clk *iclk;
|
||||
struct clk_init_data init;
|
||||
const char *parent_name;
|
||||
|
||||
if (WARN_ON(!pll_ctrl) || WARN_ON(!clk_ctrl))
|
||||
return;
|
||||
|
||||
pll = kzalloc(sizeof(*pll), GFP_KERNEL);
|
||||
if (WARN_ON(!pll))
|
||||
return;
|
||||
|
||||
pll->clk_data.clk_num = num_clks;
|
||||
pll->clk_data.clks = kcalloc(num_clks, sizeof(*pll->clk_data.clks),
|
||||
GFP_KERNEL);
|
||||
if (WARN_ON(!pll->clk_data.clks))
|
||||
goto err_clk_data;
|
||||
|
||||
pll->clks = kcalloc(num_clks, sizeof(*pll->clks), GFP_KERNEL);
|
||||
if (WARN_ON(!pll->clks))
|
||||
goto err_clks;
|
||||
|
||||
pll->pll_base = of_iomap(node, 0);
|
||||
if (WARN_ON(!pll->pll_base))
|
||||
goto err_pll_iomap;
|
||||
|
||||
pll->pwr_base = of_iomap(node, 1);
|
||||
if (WARN_ON(!pll->pwr_base))
|
||||
goto err_pwr_iomap;
|
||||
|
||||
/* some PLLs require gating control at the top ASIU level */
|
||||
if (pll_ctrl->flags & IPROC_CLK_PLL_ASIU) {
|
||||
pll->asiu_base = of_iomap(node, 2);
|
||||
if (WARN_ON(!pll->asiu_base))
|
||||
goto err_asiu_iomap;
|
||||
}
|
||||
|
||||
/* initialize and register the PLL itself */
|
||||
pll->ctrl = pll_ctrl;
|
||||
|
||||
iclk = &pll->clks[0];
|
||||
iclk->pll = pll;
|
||||
iclk->name = node->name;
|
||||
|
||||
init.name = node->name;
|
||||
init.ops = &iproc_pll_ops;
|
||||
init.flags = 0;
|
||||
parent_name = of_clk_get_parent_name(node, 0);
|
||||
init.parent_names = (parent_name ? &parent_name : NULL);
|
||||
init.num_parents = (parent_name ? 1 : 0);
|
||||
iclk->hw.init = &init;
|
||||
|
||||
if (vco) {
|
||||
pll->num_vco_entries = num_vco_entries;
|
||||
pll->vco_param = vco;
|
||||
}
|
||||
|
||||
iproc_pll_sw_cfg(pll);
|
||||
|
||||
clk = clk_register(NULL, &iclk->hw);
|
||||
if (WARN_ON(IS_ERR(clk)))
|
||||
goto err_pll_register;
|
||||
|
||||
pll->clk_data.clks[0] = clk;
|
||||
|
||||
/* now initialize and register all leaf clocks */
|
||||
for (i = 1; i < num_clks; i++) {
|
||||
const char *clk_name;
|
||||
|
||||
memset(&init, 0, sizeof(init));
|
||||
parent_name = node->name;
|
||||
|
||||
clk_name = kzalloc(IPROC_CLK_NAME_LEN, GFP_KERNEL);
|
||||
if (WARN_ON(!clk_name))
|
||||
goto err_clk_register;
|
||||
|
||||
ret = of_property_read_string_index(node, "clock-output-names",
|
||||
i, &clk_name);
|
||||
if (WARN_ON(ret))
|
||||
goto err_clk_register;
|
||||
|
||||
iclk = &pll->clks[i];
|
||||
iclk->name = clk_name;
|
||||
iclk->pll = pll;
|
||||
iclk->ctrl = &clk_ctrl[i];
|
||||
|
||||
init.name = clk_name;
|
||||
init.ops = &iproc_clk_ops;
|
||||
init.flags = 0;
|
||||
init.parent_names = (parent_name ? &parent_name : NULL);
|
||||
init.num_parents = (parent_name ? 1 : 0);
|
||||
iclk->hw.init = &init;
|
||||
|
||||
clk = clk_register(NULL, &iclk->hw);
|
||||
if (WARN_ON(IS_ERR(clk)))
|
||||
goto err_clk_register;
|
||||
|
||||
pll->clk_data.clks[i] = clk;
|
||||
}
|
||||
|
||||
ret = of_clk_add_provider(node, of_clk_src_onecell_get, &pll->clk_data);
|
||||
if (WARN_ON(ret))
|
||||
goto err_clk_register;
|
||||
|
||||
return;
|
||||
|
||||
err_clk_register:
|
||||
for (i = 0; i < num_clks; i++) {
|
||||
kfree(pll->clks[i].name);
|
||||
clk_unregister(pll->clk_data.clks[i]);
|
||||
}
|
||||
|
||||
err_pll_register:
|
||||
if (pll->asiu_base)
|
||||
iounmap(pll->asiu_base);
|
||||
|
||||
err_asiu_iomap:
|
||||
iounmap(pll->pwr_base);
|
||||
|
||||
err_pwr_iomap:
|
||||
iounmap(pll->pll_base);
|
||||
|
||||
err_pll_iomap:
|
||||
kfree(pll->clks);
|
||||
|
||||
err_clks:
|
||||
kfree(pll->clk_data.clks);
|
||||
|
||||
err_clk_data:
|
||||
kfree(pll);
|
||||
}
|
||||
178
drivers/clk/bcm/clk-iproc.h
Normal file
178
drivers/clk/bcm/clk-iproc.h
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
/*
|
||||
* Copyright (C) 2014 Broadcom Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef _CLK_IPROC_H
|
||||
#define _CLK_IPROC_H
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/clk-provider.h>
|
||||
|
||||
#define IPROC_CLK_NAME_LEN 25
|
||||
#define IPROC_CLK_INVALID_OFFSET 0xffffffff
|
||||
#define bit_mask(width) ((1 << (width)) - 1)
|
||||
|
||||
/* clocks that should not be disabled at runtime */
|
||||
#define IPROC_CLK_AON BIT(0)
|
||||
|
||||
/* PLL that requires gating through ASIU */
|
||||
#define IPROC_CLK_PLL_ASIU BIT(1)
|
||||
|
||||
/* PLL that has fractional part of the NDIV */
|
||||
#define IPROC_CLK_PLL_HAS_NDIV_FRAC BIT(2)
|
||||
|
||||
/*
|
||||
* Some of the iProc PLL/clocks may have an ASIC bug that requires read back
|
||||
* of the same register following the write to flush the write transaction into
|
||||
* the intended register
|
||||
*/
|
||||
#define IPROC_CLK_NEEDS_READ_BACK BIT(3)
|
||||
|
||||
/*
|
||||
* Some PLLs require the PLL SW override bit to be set before changes can be
|
||||
* applied to the PLL
|
||||
*/
|
||||
#define IPROC_CLK_PLL_NEEDS_SW_CFG BIT(4)
|
||||
|
||||
/*
|
||||
* Parameters for VCO frequency configuration
|
||||
*
|
||||
* VCO frequency =
|
||||
* ((ndiv_int + ndiv_frac / 2^20) * (ref freqeuncy / pdiv)
|
||||
*/
|
||||
struct iproc_pll_vco_param {
|
||||
unsigned long rate;
|
||||
unsigned int ndiv_int;
|
||||
unsigned int ndiv_frac;
|
||||
unsigned int pdiv;
|
||||
};
|
||||
|
||||
struct iproc_clk_reg_op {
|
||||
unsigned int offset;
|
||||
unsigned int shift;
|
||||
unsigned int width;
|
||||
};
|
||||
|
||||
/*
|
||||
* Clock gating control at the top ASIU level
|
||||
*/
|
||||
struct iproc_asiu_gate {
|
||||
unsigned int offset;
|
||||
unsigned int en_shift;
|
||||
};
|
||||
|
||||
/*
|
||||
* Control of powering on/off of a PLL
|
||||
*
|
||||
* Before powering off a PLL, input isolation (ISO) needs to be enabled
|
||||
*/
|
||||
struct iproc_pll_aon_pwr_ctrl {
|
||||
unsigned int offset;
|
||||
unsigned int pwr_width;
|
||||
unsigned int pwr_shift;
|
||||
unsigned int iso_shift;
|
||||
};
|
||||
|
||||
/*
|
||||
* Control of the PLL reset, with Ki, Kp, and Ka parameters
|
||||
*/
|
||||
struct iproc_pll_reset_ctrl {
|
||||
unsigned int offset;
|
||||
unsigned int reset_shift;
|
||||
unsigned int p_reset_shift;
|
||||
unsigned int ki_shift;
|
||||
unsigned int ki_width;
|
||||
unsigned int kp_shift;
|
||||
unsigned int kp_width;
|
||||
unsigned int ka_shift;
|
||||
unsigned int ka_width;
|
||||
};
|
||||
|
||||
/*
|
||||
* To enable SW control of the PLL
|
||||
*/
|
||||
struct iproc_pll_sw_ctrl {
|
||||
unsigned int offset;
|
||||
unsigned int shift;
|
||||
};
|
||||
|
||||
struct iproc_pll_vco_ctrl {
|
||||
unsigned int u_offset;
|
||||
unsigned int l_offset;
|
||||
};
|
||||
|
||||
/*
|
||||
* Main PLL control parameters
|
||||
*/
|
||||
struct iproc_pll_ctrl {
|
||||
unsigned long flags;
|
||||
struct iproc_pll_aon_pwr_ctrl aon;
|
||||
struct iproc_asiu_gate asiu;
|
||||
struct iproc_pll_reset_ctrl reset;
|
||||
struct iproc_pll_sw_ctrl sw_ctrl;
|
||||
struct iproc_clk_reg_op ndiv_int;
|
||||
struct iproc_clk_reg_op ndiv_frac;
|
||||
struct iproc_clk_reg_op pdiv;
|
||||
struct iproc_pll_vco_ctrl vco_ctrl;
|
||||
struct iproc_clk_reg_op status;
|
||||
};
|
||||
|
||||
/*
|
||||
* Controls enabling/disabling a PLL derived clock
|
||||
*/
|
||||
struct iproc_clk_enable_ctrl {
|
||||
unsigned int offset;
|
||||
unsigned int enable_shift;
|
||||
unsigned int hold_shift;
|
||||
unsigned int bypass_shift;
|
||||
};
|
||||
|
||||
/*
|
||||
* Main clock control parameters for clocks derived from the PLLs
|
||||
*/
|
||||
struct iproc_clk_ctrl {
|
||||
unsigned int channel;
|
||||
unsigned long flags;
|
||||
struct iproc_clk_enable_ctrl enable;
|
||||
struct iproc_clk_reg_op mdiv;
|
||||
};
|
||||
|
||||
/*
|
||||
* Divisor of the ASIU clocks
|
||||
*/
|
||||
struct iproc_asiu_div {
|
||||
unsigned int offset;
|
||||
unsigned int en_shift;
|
||||
unsigned int high_shift;
|
||||
unsigned int high_width;
|
||||
unsigned int low_shift;
|
||||
unsigned int low_width;
|
||||
};
|
||||
|
||||
void __init iproc_armpll_setup(struct device_node *node);
|
||||
void __init iproc_pll_clk_setup(struct device_node *node,
|
||||
const struct iproc_pll_ctrl *pll_ctrl,
|
||||
const struct iproc_pll_vco_param *vco,
|
||||
unsigned int num_vco_entries,
|
||||
const struct iproc_clk_ctrl *clk_ctrl,
|
||||
unsigned int num_clks);
|
||||
void __init iproc_asiu_setup(struct device_node *node,
|
||||
const struct iproc_asiu_div *div,
|
||||
const struct iproc_asiu_gate *gate,
|
||||
unsigned int num_clks);
|
||||
|
||||
#endif /* _CLK_IPROC_H */
|
||||
|
|
@ -21,8 +21,6 @@
|
|||
#define selector_clear_exists(sel) ((sel)->width = 0)
|
||||
#define trigger_clear_exists(trig) FLAG_CLEAR(trig, TRIG, EXISTS)
|
||||
|
||||
LIST_HEAD(ccu_list); /* The list of set up CCUs */
|
||||
|
||||
/* Validity checking */
|
||||
|
||||
static bool ccu_data_offsets_valid(struct ccu_data *ccu)
|
||||
|
|
@ -773,7 +771,6 @@ static void kona_ccu_teardown(struct ccu_data *ccu)
|
|||
|
||||
of_clk_del_provider(ccu->node); /* safe if never added */
|
||||
ccu_clks_teardown(ccu);
|
||||
list_del(&ccu->links);
|
||||
of_node_put(ccu->node);
|
||||
ccu->node = NULL;
|
||||
iounmap(ccu->base);
|
||||
|
|
@ -847,7 +844,6 @@ void __init kona_dt_ccu_setup(struct ccu_data *ccu,
|
|||
goto out_err;
|
||||
}
|
||||
ccu->node = of_node_get(node);
|
||||
list_add_tail(&ccu->links, &ccu_list);
|
||||
|
||||
/*
|
||||
* Set up each defined kona clock and save the result in
|
||||
|
|
|
|||
|
|
@ -1240,7 +1240,7 @@ static bool __kona_clk_init(struct kona_clk *bcm_clk)
|
|||
default:
|
||||
BUG();
|
||||
}
|
||||
return -EINVAL;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Set a CCU and all its clocks into their desired initial state */
|
||||
|
|
|
|||
|
|
@ -480,7 +480,6 @@ struct ccu_data {
|
|||
spinlock_t lock; /* serialization lock */
|
||||
bool write_enabled; /* write access is currently enabled */
|
||||
struct ccu_policy policy;
|
||||
struct list_head links; /* for ccu_list */
|
||||
struct device_node *node;
|
||||
struct clk_onecell_data clk_data;
|
||||
const char *name;
|
||||
|
|
@ -492,7 +491,6 @@ struct ccu_data {
|
|||
#define KONA_CCU_COMMON(_prefix, _name, _ccuname) \
|
||||
.name = #_name "_ccu", \
|
||||
.lock = __SPIN_LOCK_UNLOCKED(_name ## _ccu_data.lock), \
|
||||
.links = LIST_HEAD_INIT(_name ## _ccu_data.links), \
|
||||
.clk_data = { \
|
||||
.clk_num = _prefix ## _ ## _ccuname ## _CCU_CLOCK_COUNT, \
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,14 +25,7 @@
|
|||
#include <asm/div64.h>
|
||||
|
||||
#include "berlin2-div.h"
|
||||
|
||||
struct berlin2_pll_map {
|
||||
const u8 vcodiv[16];
|
||||
u8 mult;
|
||||
u8 fbdiv_shift;
|
||||
u8 rfdiv_shift;
|
||||
u8 divsel_shift;
|
||||
};
|
||||
#include "berlin2-pll.h"
|
||||
|
||||
struct berlin2_pll {
|
||||
struct clk_hw hw;
|
||||
|
|
|
|||
|
|
@ -274,7 +274,7 @@ static void __init asm9260_acc_init(struct device_node *np)
|
|||
u32 accuracy = 0;
|
||||
|
||||
base = of_io_request_and_map(np, 0, np->name);
|
||||
if (!base)
|
||||
if (IS_ERR(base))
|
||||
panic("%s: unable to map resource", np->name);
|
||||
|
||||
/* register pll */
|
||||
|
|
|
|||
|
|
@ -556,7 +556,7 @@ static int axmclk_probe(struct platform_device *pdev)
|
|||
return PTR_ERR(regmap);
|
||||
|
||||
num_clks = ARRAY_SIZE(axmclk_clocks);
|
||||
pr_info("axmclk: supporting %u clocks\n", num_clks);
|
||||
pr_info("axmclk: supporting %zu clocks\n", num_clks);
|
||||
priv = devm_kzalloc(dev, sizeof(*priv) + sizeof(*priv->clks) * num_clks,
|
||||
GFP_KERNEL);
|
||||
if (!priv)
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ static const char * const cdce706_source_name[] = {
|
|||
"clk_in0", "clk_in1",
|
||||
};
|
||||
|
||||
static const char *cdce706_clkin_name[] = {
|
||||
static const char * const cdce706_clkin_name[] = {
|
||||
"clk_in",
|
||||
};
|
||||
|
||||
|
|
@ -102,7 +102,7 @@ static const char * const cdce706_pll_name[] = {
|
|||
"pll1", "pll2", "pll3",
|
||||
};
|
||||
|
||||
static const char *cdce706_divider_parent_name[] = {
|
||||
static const char * const cdce706_divider_parent_name[] = {
|
||||
"clk_in", "pll1", "pll2", "pll2", "pll3",
|
||||
};
|
||||
|
||||
|
|
@ -666,6 +666,7 @@ static int cdce706_probe(struct i2c_client *client,
|
|||
|
||||
static int cdce706_remove(struct i2c_client *client)
|
||||
{
|
||||
of_clk_del_provider(client->dev.of_node);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
749
drivers/clk/clk-cdce925.c
Normal file
749
drivers/clk/clk-cdce925.c
Normal file
|
|
@ -0,0 +1,749 @@
|
|||
/*
|
||||
* Driver for TI Dual PLL CDCE925 clock synthesizer
|
||||
*
|
||||
* This driver always connects the Y1 to the input clock, Y2/Y3 to PLL1
|
||||
* and Y4/Y5 to PLL2. PLL frequency is set on a first-come-first-serve
|
||||
* basis. Clients can directly request any frequency that the chip can
|
||||
* deliver using the standard clk framework. In addition, the device can
|
||||
* be configured and activated via the devicetree.
|
||||
*
|
||||
* Copyright (C) 2014, Topic Embedded Products
|
||||
* Licenced under GPL
|
||||
*/
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/gcd.h>
|
||||
|
||||
/* The chip has 2 PLLs which can be routed through dividers to 5 outputs.
|
||||
* Model this as 2 PLL clocks which are parents to the outputs.
|
||||
*/
|
||||
#define NUMBER_OF_PLLS 2
|
||||
#define NUMBER_OF_OUTPUTS 5
|
||||
|
||||
#define CDCE925_REG_GLOBAL1 0x01
|
||||
#define CDCE925_REG_Y1SPIPDIVH 0x02
|
||||
#define CDCE925_REG_PDIVL 0x03
|
||||
#define CDCE925_REG_XCSEL 0x05
|
||||
/* PLL parameters start at 0x10, steps of 0x10 */
|
||||
#define CDCE925_OFFSET_PLL 0x10
|
||||
/* Add CDCE925_OFFSET_PLL * (pll) to these registers before sending */
|
||||
#define CDCE925_PLL_MUX_OUTPUTS 0x14
|
||||
#define CDCE925_PLL_MULDIV 0x18
|
||||
|
||||
#define CDCE925_PLL_FREQUENCY_MIN 80000000ul
|
||||
#define CDCE925_PLL_FREQUENCY_MAX 230000000ul
|
||||
struct clk_cdce925_chip;
|
||||
|
||||
struct clk_cdce925_output {
|
||||
struct clk_hw hw;
|
||||
struct clk_cdce925_chip *chip;
|
||||
u8 index;
|
||||
u16 pdiv; /* 1..127 for Y2-Y5; 1..1023 for Y1 */
|
||||
};
|
||||
#define to_clk_cdce925_output(_hw) \
|
||||
container_of(_hw, struct clk_cdce925_output, hw)
|
||||
|
||||
struct clk_cdce925_pll {
|
||||
struct clk_hw hw;
|
||||
struct clk_cdce925_chip *chip;
|
||||
u8 index;
|
||||
u16 m; /* 1..511 */
|
||||
u16 n; /* 1..4095 */
|
||||
};
|
||||
#define to_clk_cdce925_pll(_hw) container_of(_hw, struct clk_cdce925_pll, hw)
|
||||
|
||||
struct clk_cdce925_chip {
|
||||
struct regmap *regmap;
|
||||
struct i2c_client *i2c_client;
|
||||
struct clk_cdce925_pll pll[NUMBER_OF_PLLS];
|
||||
struct clk_cdce925_output clk[NUMBER_OF_OUTPUTS];
|
||||
struct clk *dt_clk[NUMBER_OF_OUTPUTS];
|
||||
struct clk_onecell_data onecell;
|
||||
};
|
||||
|
||||
/* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */
|
||||
|
||||
static unsigned long cdce925_pll_calculate_rate(unsigned long parent_rate,
|
||||
u16 n, u16 m)
|
||||
{
|
||||
if ((!m || !n) || (m == n))
|
||||
return parent_rate; /* In bypass mode runs at same frequency */
|
||||
return mult_frac(parent_rate, (unsigned long)n, (unsigned long)m);
|
||||
}
|
||||
|
||||
static unsigned long cdce925_pll_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
/* Output frequency of PLL is Fout = (Fin/Pdiv)*(N/M) */
|
||||
struct clk_cdce925_pll *data = to_clk_cdce925_pll(hw);
|
||||
|
||||
return cdce925_pll_calculate_rate(parent_rate, data->n, data->m);
|
||||
}
|
||||
|
||||
static void cdce925_pll_find_rate(unsigned long rate,
|
||||
unsigned long parent_rate, u16 *n, u16 *m)
|
||||
{
|
||||
unsigned long un;
|
||||
unsigned long um;
|
||||
unsigned long g;
|
||||
|
||||
if (rate <= parent_rate) {
|
||||
/* Can always deliver parent_rate in bypass mode */
|
||||
rate = parent_rate;
|
||||
*n = 0;
|
||||
*m = 0;
|
||||
} else {
|
||||
/* In PLL mode, need to apply min/max range */
|
||||
if (rate < CDCE925_PLL_FREQUENCY_MIN)
|
||||
rate = CDCE925_PLL_FREQUENCY_MIN;
|
||||
else if (rate > CDCE925_PLL_FREQUENCY_MAX)
|
||||
rate = CDCE925_PLL_FREQUENCY_MAX;
|
||||
|
||||
g = gcd(rate, parent_rate);
|
||||
um = parent_rate / g;
|
||||
un = rate / g;
|
||||
/* When outside hw range, reduce to fit (rounding errors) */
|
||||
while ((un > 4095) || (um > 511)) {
|
||||
un >>= 1;
|
||||
um >>= 1;
|
||||
}
|
||||
if (un == 0)
|
||||
un = 1;
|
||||
if (um == 0)
|
||||
um = 1;
|
||||
|
||||
*n = un;
|
||||
*m = um;
|
||||
}
|
||||
}
|
||||
|
||||
static long cdce925_pll_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
u16 n, m;
|
||||
|
||||
cdce925_pll_find_rate(rate, *parent_rate, &n, &m);
|
||||
return (long)cdce925_pll_calculate_rate(*parent_rate, n, m);
|
||||
}
|
||||
|
||||
static int cdce925_pll_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_cdce925_pll *data = to_clk_cdce925_pll(hw);
|
||||
|
||||
if (!rate || (rate == parent_rate)) {
|
||||
data->m = 0; /* Bypass mode */
|
||||
data->n = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((rate < CDCE925_PLL_FREQUENCY_MIN) ||
|
||||
(rate > CDCE925_PLL_FREQUENCY_MAX)) {
|
||||
pr_debug("%s: rate %lu outside PLL range.\n", __func__, rate);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (rate < parent_rate) {
|
||||
pr_debug("%s: rate %lu less than parent rate %lu.\n", __func__,
|
||||
rate, parent_rate);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cdce925_pll_find_rate(rate, parent_rate, &data->n, &data->m);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* calculate p = max(0, 4 - int(log2 (n/m))) */
|
||||
static u8 cdce925_pll_calc_p(u16 n, u16 m)
|
||||
{
|
||||
u8 p;
|
||||
u16 r = n / m;
|
||||
|
||||
if (r >= 16)
|
||||
return 0;
|
||||
p = 4;
|
||||
while (r > 1) {
|
||||
r >>= 1;
|
||||
--p;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/* Returns VCO range bits for VCO1_0_RANGE */
|
||||
static u8 cdce925_pll_calc_range_bits(struct clk_hw *hw, u16 n, u16 m)
|
||||
{
|
||||
struct clk *parent = clk_get_parent(hw->clk);
|
||||
unsigned long rate = clk_get_rate(parent);
|
||||
|
||||
rate = mult_frac(rate, (unsigned long)n, (unsigned long)m);
|
||||
if (rate >= 175000000)
|
||||
return 0x3;
|
||||
if (rate >= 150000000)
|
||||
return 0x02;
|
||||
if (rate >= 125000000)
|
||||
return 0x01;
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
/* I2C clock, hence everything must happen in (un)prepare because this
|
||||
* may sleep */
|
||||
static int cdce925_pll_prepare(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_cdce925_pll *data = to_clk_cdce925_pll(hw);
|
||||
u16 n = data->n;
|
||||
u16 m = data->m;
|
||||
u16 r;
|
||||
u8 q;
|
||||
u8 p;
|
||||
u16 nn;
|
||||
u8 pll[4]; /* Bits are spread out over 4 byte registers */
|
||||
u8 reg_ofs = data->index * CDCE925_OFFSET_PLL;
|
||||
unsigned i;
|
||||
|
||||
if ((!m || !n) || (m == n)) {
|
||||
/* Set PLL mux to bypass mode, leave the rest as is */
|
||||
regmap_update_bits(data->chip->regmap,
|
||||
reg_ofs + CDCE925_PLL_MUX_OUTPUTS, 0x80, 0x80);
|
||||
} else {
|
||||
/* According to data sheet: */
|
||||
/* p = max(0, 4 - int(log2 (n/m))) */
|
||||
p = cdce925_pll_calc_p(n, m);
|
||||
/* nn = n * 2^p */
|
||||
nn = n * BIT(p);
|
||||
/* q = int(nn/m) */
|
||||
q = nn / m;
|
||||
if ((q < 16) || (1 > 64)) {
|
||||
pr_debug("%s invalid q=%d\n", __func__, q);
|
||||
return -EINVAL;
|
||||
}
|
||||
r = nn - (m*q);
|
||||
if (r > 511) {
|
||||
pr_debug("%s invalid r=%d\n", __func__, r);
|
||||
return -EINVAL;
|
||||
}
|
||||
pr_debug("%s n=%d m=%d p=%d q=%d r=%d\n", __func__,
|
||||
n, m, p, q, r);
|
||||
/* encode into register bits */
|
||||
pll[0] = n >> 4;
|
||||
pll[1] = ((n & 0x0F) << 4) | ((r >> 5) & 0x0F);
|
||||
pll[2] = ((r & 0x1F) << 3) | ((q >> 3) & 0x07);
|
||||
pll[3] = ((q & 0x07) << 5) | (p << 2) |
|
||||
cdce925_pll_calc_range_bits(hw, n, m);
|
||||
/* Write to registers */
|
||||
for (i = 0; i < ARRAY_SIZE(pll); ++i)
|
||||
regmap_write(data->chip->regmap,
|
||||
reg_ofs + CDCE925_PLL_MULDIV + i, pll[i]);
|
||||
/* Enable PLL */
|
||||
regmap_update_bits(data->chip->regmap,
|
||||
reg_ofs + CDCE925_PLL_MUX_OUTPUTS, 0x80, 0x00);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cdce925_pll_unprepare(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_cdce925_pll *data = to_clk_cdce925_pll(hw);
|
||||
u8 reg_ofs = data->index * CDCE925_OFFSET_PLL;
|
||||
|
||||
regmap_update_bits(data->chip->regmap,
|
||||
reg_ofs + CDCE925_PLL_MUX_OUTPUTS, 0x80, 0x80);
|
||||
}
|
||||
|
||||
static const struct clk_ops cdce925_pll_ops = {
|
||||
.prepare = cdce925_pll_prepare,
|
||||
.unprepare = cdce925_pll_unprepare,
|
||||
.recalc_rate = cdce925_pll_recalc_rate,
|
||||
.round_rate = cdce925_pll_round_rate,
|
||||
.set_rate = cdce925_pll_set_rate,
|
||||
};
|
||||
|
||||
|
||||
static void cdce925_clk_set_pdiv(struct clk_cdce925_output *data, u16 pdiv)
|
||||
{
|
||||
switch (data->index) {
|
||||
case 0:
|
||||
regmap_update_bits(data->chip->regmap,
|
||||
CDCE925_REG_Y1SPIPDIVH,
|
||||
0x03, (pdiv >> 8) & 0x03);
|
||||
regmap_write(data->chip->regmap, 0x03, pdiv & 0xFF);
|
||||
break;
|
||||
case 1:
|
||||
regmap_update_bits(data->chip->regmap, 0x16, 0x7F, pdiv);
|
||||
break;
|
||||
case 2:
|
||||
regmap_update_bits(data->chip->regmap, 0x17, 0x7F, pdiv);
|
||||
break;
|
||||
case 3:
|
||||
regmap_update_bits(data->chip->regmap, 0x26, 0x7F, pdiv);
|
||||
break;
|
||||
case 4:
|
||||
regmap_update_bits(data->chip->regmap, 0x27, 0x7F, pdiv);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void cdce925_clk_activate(struct clk_cdce925_output *data)
|
||||
{
|
||||
switch (data->index) {
|
||||
case 0:
|
||||
regmap_update_bits(data->chip->regmap,
|
||||
CDCE925_REG_Y1SPIPDIVH, 0x0c, 0x0c);
|
||||
break;
|
||||
case 1:
|
||||
case 2:
|
||||
regmap_update_bits(data->chip->regmap, 0x14, 0x03, 0x03);
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
regmap_update_bits(data->chip->regmap, 0x24, 0x03, 0x03);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int cdce925_clk_prepare(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_cdce925_output *data = to_clk_cdce925_output(hw);
|
||||
|
||||
cdce925_clk_set_pdiv(data, data->pdiv);
|
||||
cdce925_clk_activate(data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cdce925_clk_unprepare(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_cdce925_output *data = to_clk_cdce925_output(hw);
|
||||
|
||||
/* Disable clock by setting divider to "0" */
|
||||
cdce925_clk_set_pdiv(data, 0);
|
||||
}
|
||||
|
||||
static unsigned long cdce925_clk_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_cdce925_output *data = to_clk_cdce925_output(hw);
|
||||
|
||||
if (data->pdiv)
|
||||
return parent_rate / data->pdiv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u16 cdce925_calc_divider(unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
unsigned long divider;
|
||||
|
||||
if (!rate)
|
||||
return 0;
|
||||
if (rate >= parent_rate)
|
||||
return 1;
|
||||
|
||||
divider = DIV_ROUND_CLOSEST(parent_rate, rate);
|
||||
if (divider > 0x7F)
|
||||
divider = 0x7F;
|
||||
|
||||
return (u16)divider;
|
||||
}
|
||||
|
||||
static unsigned long cdce925_clk_best_parent_rate(
|
||||
struct clk_hw *hw, unsigned long rate)
|
||||
{
|
||||
struct clk *pll = clk_get_parent(hw->clk);
|
||||
struct clk *root = clk_get_parent(pll);
|
||||
unsigned long root_rate = clk_get_rate(root);
|
||||
unsigned long best_rate_error = rate;
|
||||
u16 pdiv_min;
|
||||
u16 pdiv_max;
|
||||
u16 pdiv_best;
|
||||
u16 pdiv_now;
|
||||
|
||||
if (root_rate % rate == 0)
|
||||
return root_rate; /* Don't need the PLL, use bypass */
|
||||
|
||||
pdiv_min = (u16)max(1ul, DIV_ROUND_UP(CDCE925_PLL_FREQUENCY_MIN, rate));
|
||||
pdiv_max = (u16)min(127ul, CDCE925_PLL_FREQUENCY_MAX / rate);
|
||||
|
||||
if (pdiv_min > pdiv_max)
|
||||
return 0; /* No can do? */
|
||||
|
||||
pdiv_best = pdiv_min;
|
||||
for (pdiv_now = pdiv_min; pdiv_now < pdiv_max; ++pdiv_now) {
|
||||
unsigned long target_rate = rate * pdiv_now;
|
||||
long pll_rate = clk_round_rate(pll, target_rate);
|
||||
unsigned long actual_rate;
|
||||
unsigned long rate_error;
|
||||
|
||||
if (pll_rate <= 0)
|
||||
continue;
|
||||
actual_rate = pll_rate / pdiv_now;
|
||||
rate_error = abs((long)actual_rate - (long)rate);
|
||||
if (rate_error < best_rate_error) {
|
||||
pdiv_best = pdiv_now;
|
||||
best_rate_error = rate_error;
|
||||
}
|
||||
/* TODO: Consider PLL frequency based on smaller n/m values
|
||||
* and pick the better one if the error is equal */
|
||||
}
|
||||
|
||||
return rate * pdiv_best;
|
||||
}
|
||||
|
||||
static long cdce925_clk_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
unsigned long l_parent_rate = *parent_rate;
|
||||
u16 divider = cdce925_calc_divider(rate, l_parent_rate);
|
||||
|
||||
if (l_parent_rate / divider != rate) {
|
||||
l_parent_rate = cdce925_clk_best_parent_rate(hw, rate);
|
||||
divider = cdce925_calc_divider(rate, l_parent_rate);
|
||||
*parent_rate = l_parent_rate;
|
||||
}
|
||||
|
||||
if (divider)
|
||||
return (long)(l_parent_rate / divider);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cdce925_clk_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_cdce925_output *data = to_clk_cdce925_output(hw);
|
||||
|
||||
data->pdiv = cdce925_calc_divider(rate, parent_rate);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct clk_ops cdce925_clk_ops = {
|
||||
.prepare = cdce925_clk_prepare,
|
||||
.unprepare = cdce925_clk_unprepare,
|
||||
.recalc_rate = cdce925_clk_recalc_rate,
|
||||
.round_rate = cdce925_clk_round_rate,
|
||||
.set_rate = cdce925_clk_set_rate,
|
||||
};
|
||||
|
||||
|
||||
static u16 cdce925_y1_calc_divider(unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
unsigned long divider;
|
||||
|
||||
if (!rate)
|
||||
return 0;
|
||||
if (rate >= parent_rate)
|
||||
return 1;
|
||||
|
||||
divider = DIV_ROUND_CLOSEST(parent_rate, rate);
|
||||
if (divider > 0x3FF) /* Y1 has 10-bit divider */
|
||||
divider = 0x3FF;
|
||||
|
||||
return (u16)divider;
|
||||
}
|
||||
|
||||
static long cdce925_clk_y1_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
unsigned long l_parent_rate = *parent_rate;
|
||||
u16 divider = cdce925_y1_calc_divider(rate, l_parent_rate);
|
||||
|
||||
if (divider)
|
||||
return (long)(l_parent_rate / divider);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cdce925_clk_y1_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_cdce925_output *data = to_clk_cdce925_output(hw);
|
||||
|
||||
data->pdiv = cdce925_y1_calc_divider(rate, parent_rate);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct clk_ops cdce925_clk_y1_ops = {
|
||||
.prepare = cdce925_clk_prepare,
|
||||
.unprepare = cdce925_clk_unprepare,
|
||||
.recalc_rate = cdce925_clk_recalc_rate,
|
||||
.round_rate = cdce925_clk_y1_round_rate,
|
||||
.set_rate = cdce925_clk_y1_set_rate,
|
||||
};
|
||||
|
||||
|
||||
static struct regmap_config cdce925_regmap_config = {
|
||||
.name = "configuration0",
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.max_register = 0x2F,
|
||||
};
|
||||
|
||||
#define CDCE925_I2C_COMMAND_BLOCK_TRANSFER 0x00
|
||||
#define CDCE925_I2C_COMMAND_BYTE_TRANSFER 0x80
|
||||
|
||||
static int cdce925_regmap_i2c_write(
|
||||
void *context, const void *data, size_t count)
|
||||
{
|
||||
struct device *dev = context;
|
||||
struct i2c_client *i2c = to_i2c_client(dev);
|
||||
int ret;
|
||||
u8 reg_data[2];
|
||||
|
||||
if (count != 2)
|
||||
return -ENOTSUPP;
|
||||
|
||||
/* First byte is command code */
|
||||
reg_data[0] = CDCE925_I2C_COMMAND_BYTE_TRANSFER | ((u8 *)data)[0];
|
||||
reg_data[1] = ((u8 *)data)[1];
|
||||
|
||||
dev_dbg(&i2c->dev, "%s(%zu) %#x %#x\n", __func__, count,
|
||||
reg_data[0], reg_data[1]);
|
||||
|
||||
ret = i2c_master_send(i2c, reg_data, count);
|
||||
if (likely(ret == count))
|
||||
return 0;
|
||||
else if (ret < 0)
|
||||
return ret;
|
||||
else
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int cdce925_regmap_i2c_read(void *context,
|
||||
const void *reg, size_t reg_size, void *val, size_t val_size)
|
||||
{
|
||||
struct device *dev = context;
|
||||
struct i2c_client *i2c = to_i2c_client(dev);
|
||||
struct i2c_msg xfer[2];
|
||||
int ret;
|
||||
u8 reg_data[2];
|
||||
|
||||
if (reg_size != 1)
|
||||
return -ENOTSUPP;
|
||||
|
||||
xfer[0].addr = i2c->addr;
|
||||
xfer[0].flags = 0;
|
||||
xfer[0].buf = reg_data;
|
||||
if (val_size == 1) {
|
||||
reg_data[0] =
|
||||
CDCE925_I2C_COMMAND_BYTE_TRANSFER | ((u8 *)reg)[0];
|
||||
xfer[0].len = 1;
|
||||
} else {
|
||||
reg_data[0] =
|
||||
CDCE925_I2C_COMMAND_BLOCK_TRANSFER | ((u8 *)reg)[0];
|
||||
reg_data[1] = val_size;
|
||||
xfer[0].len = 2;
|
||||
}
|
||||
|
||||
xfer[1].addr = i2c->addr;
|
||||
xfer[1].flags = I2C_M_RD;
|
||||
xfer[1].len = val_size;
|
||||
xfer[1].buf = val;
|
||||
|
||||
ret = i2c_transfer(i2c->adapter, xfer, 2);
|
||||
if (likely(ret == 2)) {
|
||||
dev_dbg(&i2c->dev, "%s(%zu, %zu) %#x %#x\n", __func__,
|
||||
reg_size, val_size, reg_data[0], *((u8 *)val));
|
||||
return 0;
|
||||
} else if (ret < 0)
|
||||
return ret;
|
||||
else
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* The CDCE925 uses a funky way to read/write registers. Bulk mode is
|
||||
* just weird, so just use the single byte mode exclusively. */
|
||||
static struct regmap_bus regmap_cdce925_bus = {
|
||||
.write = cdce925_regmap_i2c_write,
|
||||
.read = cdce925_regmap_i2c_read,
|
||||
};
|
||||
|
||||
static int cdce925_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct clk_cdce925_chip *data;
|
||||
struct device_node *node = client->dev.of_node;
|
||||
const char *parent_name;
|
||||
const char *pll_clk_name[NUMBER_OF_PLLS] = {NULL,};
|
||||
struct clk_init_data init;
|
||||
struct clk *clk;
|
||||
u32 value;
|
||||
int i;
|
||||
int err;
|
||||
struct device_node *np_output;
|
||||
char child_name[6];
|
||||
|
||||
dev_dbg(&client->dev, "%s\n", __func__);
|
||||
data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
data->i2c_client = client;
|
||||
data->regmap = devm_regmap_init(&client->dev, ®map_cdce925_bus,
|
||||
&client->dev, &cdce925_regmap_config);
|
||||
if (IS_ERR(data->regmap)) {
|
||||
dev_err(&client->dev, "failed to allocate register map\n");
|
||||
return PTR_ERR(data->regmap);
|
||||
}
|
||||
i2c_set_clientdata(client, data);
|
||||
|
||||
parent_name = of_clk_get_parent_name(node, 0);
|
||||
if (!parent_name) {
|
||||
dev_err(&client->dev, "missing parent clock\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
dev_dbg(&client->dev, "parent is: %s\n", parent_name);
|
||||
|
||||
if (of_property_read_u32(node, "xtal-load-pf", &value) == 0)
|
||||
regmap_write(data->regmap,
|
||||
CDCE925_REG_XCSEL, (value << 3) & 0xF8);
|
||||
/* PWDN bit */
|
||||
regmap_update_bits(data->regmap, CDCE925_REG_GLOBAL1, BIT(4), 0);
|
||||
|
||||
/* Set input source for Y1 to be the XTAL */
|
||||
regmap_update_bits(data->regmap, 0x02, BIT(7), 0);
|
||||
|
||||
init.ops = &cdce925_pll_ops;
|
||||
init.flags = 0;
|
||||
init.parent_names = &parent_name;
|
||||
init.num_parents = parent_name ? 1 : 0;
|
||||
|
||||
/* Register PLL clocks */
|
||||
for (i = 0; i < NUMBER_OF_PLLS; ++i) {
|
||||
pll_clk_name[i] = kasprintf(GFP_KERNEL, "%s.pll%d",
|
||||
client->dev.of_node->name, i);
|
||||
init.name = pll_clk_name[i];
|
||||
data->pll[i].chip = data;
|
||||
data->pll[i].hw.init = &init;
|
||||
data->pll[i].index = i;
|
||||
clk = devm_clk_register(&client->dev, &data->pll[i].hw);
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(&client->dev, "Failed register PLL %d\n", i);
|
||||
err = PTR_ERR(clk);
|
||||
goto error;
|
||||
}
|
||||
sprintf(child_name, "PLL%d", i+1);
|
||||
np_output = of_get_child_by_name(node, child_name);
|
||||
if (!np_output)
|
||||
continue;
|
||||
if (!of_property_read_u32(np_output,
|
||||
"clock-frequency", &value)) {
|
||||
err = clk_set_rate(clk, value);
|
||||
if (err)
|
||||
dev_err(&client->dev,
|
||||
"unable to set PLL frequency %ud\n",
|
||||
value);
|
||||
}
|
||||
if (!of_property_read_u32(np_output,
|
||||
"spread-spectrum", &value)) {
|
||||
u8 flag = of_property_read_bool(np_output,
|
||||
"spread-spectrum-center") ? 0x80 : 0x00;
|
||||
regmap_update_bits(data->regmap,
|
||||
0x16 + (i*CDCE925_OFFSET_PLL),
|
||||
0x80, flag);
|
||||
regmap_update_bits(data->regmap,
|
||||
0x12 + (i*CDCE925_OFFSET_PLL),
|
||||
0x07, value & 0x07);
|
||||
}
|
||||
}
|
||||
|
||||
/* Register output clock Y1 */
|
||||
init.ops = &cdce925_clk_y1_ops;
|
||||
init.flags = 0;
|
||||
init.num_parents = 1;
|
||||
init.parent_names = &parent_name; /* Mux Y1 to input */
|
||||
init.name = kasprintf(GFP_KERNEL, "%s.Y1", client->dev.of_node->name);
|
||||
data->clk[0].chip = data;
|
||||
data->clk[0].hw.init = &init;
|
||||
data->clk[0].index = 0;
|
||||
data->clk[0].pdiv = 1;
|
||||
clk = devm_clk_register(&client->dev, &data->clk[0].hw);
|
||||
kfree(init.name); /* clock framework made a copy of the name */
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(&client->dev, "clock registration Y1 failed\n");
|
||||
err = PTR_ERR(clk);
|
||||
goto error;
|
||||
}
|
||||
data->dt_clk[0] = clk;
|
||||
|
||||
/* Register output clocks Y2 .. Y5*/
|
||||
init.ops = &cdce925_clk_ops;
|
||||
init.flags = CLK_SET_RATE_PARENT;
|
||||
init.num_parents = 1;
|
||||
for (i = 1; i < NUMBER_OF_OUTPUTS; ++i) {
|
||||
init.name = kasprintf(GFP_KERNEL, "%s.Y%d",
|
||||
client->dev.of_node->name, i+1);
|
||||
data->clk[i].chip = data;
|
||||
data->clk[i].hw.init = &init;
|
||||
data->clk[i].index = i;
|
||||
data->clk[i].pdiv = 1;
|
||||
switch (i) {
|
||||
case 1:
|
||||
case 2:
|
||||
/* Mux Y2/3 to PLL1 */
|
||||
init.parent_names = &pll_clk_name[0];
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
/* Mux Y4/5 to PLL2 */
|
||||
init.parent_names = &pll_clk_name[1];
|
||||
break;
|
||||
}
|
||||
clk = devm_clk_register(&client->dev, &data->clk[i].hw);
|
||||
kfree(init.name); /* clock framework made a copy of the name */
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(&client->dev, "clock registration failed\n");
|
||||
err = PTR_ERR(clk);
|
||||
goto error;
|
||||
}
|
||||
data->dt_clk[i] = clk;
|
||||
}
|
||||
|
||||
/* Register the output clocks */
|
||||
data->onecell.clk_num = NUMBER_OF_OUTPUTS;
|
||||
data->onecell.clks = data->dt_clk;
|
||||
err = of_clk_add_provider(client->dev.of_node, of_clk_src_onecell_get,
|
||||
&data->onecell);
|
||||
if (err)
|
||||
dev_err(&client->dev, "unable to add OF clock provider\n");
|
||||
|
||||
err = 0;
|
||||
|
||||
error:
|
||||
for (i = 0; i < NUMBER_OF_PLLS; ++i)
|
||||
/* clock framework made a copy of the name */
|
||||
kfree(pll_clk_name[i]);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id cdce925_id[] = {
|
||||
{ "cdce925", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, cdce925_id);
|
||||
|
||||
static const struct of_device_id clk_cdce925_of_match[] = {
|
||||
{ .compatible = "ti,cdce925" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, clk_cdce925_of_match);
|
||||
|
||||
static struct i2c_driver cdce925_driver = {
|
||||
.driver = {
|
||||
.name = "cdce925",
|
||||
.of_match_table = of_match_ptr(clk_cdce925_of_match),
|
||||
},
|
||||
.probe = cdce925_probe,
|
||||
.id_table = cdce925_id,
|
||||
};
|
||||
module_i2c_driver(cdce925_driver);
|
||||
|
||||
MODULE_AUTHOR("Mike Looijmans <[email protected]>");
|
||||
MODULE_DESCRIPTION("cdce925 driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -188,7 +188,7 @@ static void clk_composite_disable(struct clk_hw *hw)
|
|||
}
|
||||
|
||||
struct clk *clk_register_composite(struct device *dev, const char *name,
|
||||
const char **parent_names, int num_parents,
|
||||
const char * const *parent_names, int num_parents,
|
||||
struct clk_hw *mux_hw, const struct clk_ops *mux_ops,
|
||||
struct clk_hw *rate_hw, const struct clk_ops *rate_ops,
|
||||
struct clk_hw *gate_hw, const struct clk_ops *gate_ops,
|
||||
|
|
@ -200,10 +200,8 @@ struct clk *clk_register_composite(struct device *dev, const char *name,
|
|||
struct clk_ops *clk_composite_ops;
|
||||
|
||||
composite = kzalloc(sizeof(*composite), GFP_KERNEL);
|
||||
if (!composite) {
|
||||
pr_err("%s: could not allocate composite clk\n", __func__);
|
||||
if (!composite)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
init.name = name;
|
||||
init.flags = flags | CLK_IS_BASIC;
|
||||
|
|
|
|||
|
|
@ -106,8 +106,9 @@ static int __set_clk_rates(struct device_node *node, bool clk_supplier)
|
|||
|
||||
rc = clk_set_rate(clk, rate);
|
||||
if (rc < 0)
|
||||
pr_err("clk: couldn't set %s clock rate: %d\n",
|
||||
__clk_get_name(clk), rc);
|
||||
pr_err("clk: couldn't set %s clk rate to %d (%d), current rate: %ld\n",
|
||||
__clk_get_name(clk), rate, rc,
|
||||
clk_get_rate(clk));
|
||||
clk_put(clk);
|
||||
}
|
||||
index++;
|
||||
|
|
@ -124,7 +125,7 @@ static int __set_clk_rates(struct device_node *node, bool clk_supplier)
|
|||
* and sets any specified clock parents and rates. The @clk_supplier argument
|
||||
* should be set to true if @node may be also a clock supplier of any clock
|
||||
* listed in its 'assigned-clocks' or 'assigned-clock-parents' properties.
|
||||
* If @clk_supplier is false the function exits returnning 0 as soon as it
|
||||
* If @clk_supplier is false the function exits returning 0 as soon as it
|
||||
* determines the @node is also a supplier of any of the clocks.
|
||||
*/
|
||||
int of_clk_set_defaults(struct device_node *node, bool clk_supplier)
|
||||
|
|
|
|||
|
|
@ -430,11 +430,9 @@ static struct clk *_register_divider(struct device *dev, const char *name,
|
|||
}
|
||||
|
||||
/* allocate the divider */
|
||||
div = kzalloc(sizeof(struct clk_divider), GFP_KERNEL);
|
||||
if (!div) {
|
||||
pr_err("%s: could not allocate divider clk\n", __func__);
|
||||
div = kzalloc(sizeof(*div), GFP_KERNEL);
|
||||
if (!div)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
init.name = name;
|
||||
init.ops = &clk_divider_ops;
|
||||
|
|
|
|||
|
|
@ -55,10 +55,16 @@ static long clk_factor_round_rate(struct clk_hw *hw, unsigned long rate,
|
|||
static int clk_factor_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
/*
|
||||
* We must report success but we can do so unconditionally because
|
||||
* clk_factor_round_rate returns values that ensure this call is a
|
||||
* nop.
|
||||
*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct clk_ops clk_fixed_factor_ops = {
|
||||
const struct clk_ops clk_fixed_factor_ops = {
|
||||
.round_rate = clk_factor_round_rate,
|
||||
.set_rate = clk_factor_set_rate,
|
||||
.recalc_rate = clk_factor_recalc_rate,
|
||||
|
|
@ -74,10 +80,8 @@ struct clk *clk_register_fixed_factor(struct device *dev, const char *name,
|
|||
struct clk *clk;
|
||||
|
||||
fix = kmalloc(sizeof(*fix), GFP_KERNEL);
|
||||
if (!fix) {
|
||||
pr_err("%s: could not allocate fixed factor clk\n", __func__);
|
||||
if (!fix)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
/* struct clk_fixed_factor assignments */
|
||||
fix->mult = mult;
|
||||
|
|
|
|||
|
|
@ -65,11 +65,9 @@ struct clk *clk_register_fixed_rate_with_accuracy(struct device *dev,
|
|||
struct clk_init_data init;
|
||||
|
||||
/* allocate fixed-rate clock */
|
||||
fixed = kzalloc(sizeof(struct clk_fixed_rate), GFP_KERNEL);
|
||||
if (!fixed) {
|
||||
pr_err("%s: could not allocate fixed clk\n", __func__);
|
||||
fixed = kzalloc(sizeof(*fixed), GFP_KERNEL);
|
||||
if (!fixed)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
init.name = name;
|
||||
init.ops = &clk_fixed_rate_ops;
|
||||
|
|
|
|||
|
|
@ -109,10 +109,8 @@ struct clk *clk_register_fractional_divider(struct device *dev,
|
|||
struct clk *clk;
|
||||
|
||||
fd = kzalloc(sizeof(*fd), GFP_KERNEL);
|
||||
if (!fd) {
|
||||
dev_err(dev, "could not allocate fractional divider clk\n");
|
||||
if (!fd)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
init.name = name;
|
||||
init.ops = &clk_fractional_divider_ops;
|
||||
|
|
|
|||
|
|
@ -135,11 +135,9 @@ struct clk *clk_register_gate(struct device *dev, const char *name,
|
|||
}
|
||||
|
||||
/* allocate the gate */
|
||||
gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL);
|
||||
if (!gate) {
|
||||
pr_err("%s: could not allocate gated clk\n", __func__);
|
||||
gate = kzalloc(sizeof(*gate), GFP_KERNEL);
|
||||
if (!gate)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
init.name = name;
|
||||
init.ops = &clk_gate_ops;
|
||||
|
|
|
|||
|
|
@ -189,7 +189,7 @@ static struct clk *of_clk_gpio_gate_delayed_register_get(
|
|||
/**
|
||||
* of_gpio_gate_clk_setup() - Setup function for gpio controlled clock
|
||||
*/
|
||||
void __init of_gpio_gate_clk_setup(struct device_node *node)
|
||||
static void __init of_gpio_gate_clk_setup(struct device_node *node)
|
||||
{
|
||||
struct clk_gpio_gate_delayed_register_data *data;
|
||||
|
||||
|
|
@ -203,6 +203,5 @@ void __init of_gpio_gate_clk_setup(struct device_node *node)
|
|||
|
||||
of_clk_add_provider(node, of_clk_gpio_gate_delayed_register_get, data);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_gpio_gate_clk_setup);
|
||||
CLK_OF_DECLARE(gpio_gate_clk, "gpio-gate-clock", of_gpio_gate_clk_setup);
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -80,9 +80,9 @@ static struct clk *__init clk_register_pll(struct device *dev,
|
|||
return clk;
|
||||
}
|
||||
|
||||
static const char const *cpu_parents[] = { "cpu_clk_div", "osc_33m_clk", };
|
||||
static const char const *ahb_parents[] = { "ahb_clk_div", "osc_33m_clk", };
|
||||
static const char const *dc_parents[] = { "dc_clk_div", "osc_33m_clk", };
|
||||
static const char * const cpu_parents[] = { "cpu_clk_div", "osc_33m_clk", };
|
||||
static const char * const ahb_parents[] = { "ahb_clk_div", "osc_33m_clk", };
|
||||
static const char * const dc_parents[] = { "dc_clk_div", "osc_33m_clk", };
|
||||
|
||||
void __init ls1x_clk_init(void)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@
|
|||
#include <linux/of.h>
|
||||
#include <linux/export.h>
|
||||
|
||||
#include "clk-max-gen.h"
|
||||
|
||||
struct max_gen_clk {
|
||||
struct regmap *regmap;
|
||||
u32 mask;
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
#include <linux/of_address.h>
|
||||
#include <linux/clkdev.h>
|
||||
|
||||
void __init moxart_of_pll_clk_init(struct device_node *node)
|
||||
static void __init moxart_of_pll_clk_init(struct device_node *node)
|
||||
{
|
||||
static void __iomem *base;
|
||||
struct clk *clk, *ref_clk;
|
||||
|
|
@ -53,7 +53,7 @@ void __init moxart_of_pll_clk_init(struct device_node *node)
|
|||
CLK_OF_DECLARE(moxart_pll_clock, "moxa,moxart-pll-clock",
|
||||
moxart_of_pll_clk_init);
|
||||
|
||||
void __init moxart_of_apb_clk_init(struct device_node *node)
|
||||
static void __init moxart_of_apb_clk_init(struct device_node *node)
|
||||
{
|
||||
static void __iomem *base;
|
||||
struct clk *clk, *pll_clk;
|
||||
|
|
|
|||
|
|
@ -114,7 +114,8 @@ const struct clk_ops clk_mux_ro_ops = {
|
|||
EXPORT_SYMBOL_GPL(clk_mux_ro_ops);
|
||||
|
||||
struct clk *clk_register_mux_table(struct device *dev, const char *name,
|
||||
const char **parent_names, u8 num_parents, unsigned long flags,
|
||||
const char * const *parent_names, u8 num_parents,
|
||||
unsigned long flags,
|
||||
void __iomem *reg, u8 shift, u32 mask,
|
||||
u8 clk_mux_flags, u32 *table, spinlock_t *lock)
|
||||
{
|
||||
|
|
@ -166,7 +167,8 @@ struct clk *clk_register_mux_table(struct device *dev, const char *name,
|
|||
EXPORT_SYMBOL_GPL(clk_register_mux_table);
|
||||
|
||||
struct clk *clk_register_mux(struct device *dev, const char *name,
|
||||
const char **parent_names, u8 num_parents, unsigned long flags,
|
||||
const char * const *parent_names, u8 num_parents,
|
||||
unsigned long flags,
|
||||
void __iomem *reg, u8 shift, u8 width,
|
||||
u8 clk_mux_flags, spinlock_t *lock)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -552,7 +552,8 @@ static const struct clk_ops si5351_pll_ops = {
|
|||
* MSx_P2[19:0] = 128 * b - c * floor(128 * b/c) = (128*b) mod c
|
||||
* MSx_P3[19:0] = c
|
||||
*
|
||||
* MS[6,7] are integer (P1) divide only, P2 = 0, P3 = 0
|
||||
* MS[6,7] are integer (P1) divide only, P1 = divide value,
|
||||
* P2 and P3 are not applicable
|
||||
*
|
||||
* for 150MHz < fOUT <= 160MHz:
|
||||
*
|
||||
|
|
@ -606,9 +607,6 @@ static unsigned long si5351_msynth_recalc_rate(struct clk_hw *hw,
|
|||
if (!hwdata->params.valid)
|
||||
si5351_read_parameters(hwdata->drvdata, reg, &hwdata->params);
|
||||
|
||||
if (hwdata->params.p3 == 0)
|
||||
return parent_rate;
|
||||
|
||||
/*
|
||||
* multisync0-5: fOUT = (128 * P3 * fIN) / (P1*P3 + P2 + 512*P3)
|
||||
* multisync6-7: fOUT = fIN / P1
|
||||
|
|
@ -616,6 +614,8 @@ static unsigned long si5351_msynth_recalc_rate(struct clk_hw *hw,
|
|||
rate = parent_rate;
|
||||
if (hwdata->num > 5) {
|
||||
m = hwdata->params.p1;
|
||||
} else if (hwdata->params.p3 == 0) {
|
||||
return parent_rate;
|
||||
} else if ((si5351_reg_read(hwdata->drvdata, reg + 2) &
|
||||
SI5351_OUTPUT_CLK_DIVBY4) == SI5351_OUTPUT_CLK_DIVBY4) {
|
||||
m = 4;
|
||||
|
|
@ -679,6 +679,16 @@ static long si5351_msynth_round_rate(struct clk_hw *hw, unsigned long rate,
|
|||
c = 1;
|
||||
|
||||
*parent_rate = a * rate;
|
||||
} else if (hwdata->num >= 6) {
|
||||
/* determine the closest integer divider */
|
||||
a = DIV_ROUND_CLOSEST(*parent_rate, rate);
|
||||
if (a < SI5351_MULTISYNTH_A_MIN)
|
||||
a = SI5351_MULTISYNTH_A_MIN;
|
||||
if (a > SI5351_MULTISYNTH67_A_MAX)
|
||||
a = SI5351_MULTISYNTH67_A_MAX;
|
||||
|
||||
b = 0;
|
||||
c = 1;
|
||||
} else {
|
||||
unsigned long rfrac, denom;
|
||||
|
||||
|
|
@ -692,9 +702,7 @@ static long si5351_msynth_round_rate(struct clk_hw *hw, unsigned long rate,
|
|||
a = *parent_rate / rate;
|
||||
if (a < SI5351_MULTISYNTH_A_MIN)
|
||||
a = SI5351_MULTISYNTH_A_MIN;
|
||||
if (hwdata->num >= 6 && a > SI5351_MULTISYNTH67_A_MAX)
|
||||
a = SI5351_MULTISYNTH67_A_MAX;
|
||||
else if (a > SI5351_MULTISYNTH_A_MAX)
|
||||
if (a > SI5351_MULTISYNTH_A_MAX)
|
||||
a = SI5351_MULTISYNTH_A_MAX;
|
||||
|
||||
/* find best approximation for b/c = fVCO mod fOUT */
|
||||
|
|
@ -723,6 +731,10 @@ static long si5351_msynth_round_rate(struct clk_hw *hw, unsigned long rate,
|
|||
hwdata->params.p3 = 1;
|
||||
hwdata->params.p2 = 0;
|
||||
hwdata->params.p1 = 0;
|
||||
} else if (hwdata->num >= 6) {
|
||||
hwdata->params.p3 = 0;
|
||||
hwdata->params.p2 = 0;
|
||||
hwdata->params.p1 = a;
|
||||
} else {
|
||||
hwdata->params.p3 = c;
|
||||
hwdata->params.p2 = (128 * b) % c;
|
||||
|
|
|
|||
380
drivers/clk/clk-stm32f4.c
Normal file
380
drivers/clk/clk-stm32f4.c
Normal file
|
|
@ -0,0 +1,380 @@
|
|||
/*
|
||||
* Author: Daniel Thompson <daniel.thompson@linaro.org>
|
||||
*
|
||||
* Inspired by clk-asm9260.c .
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
|
||||
#define STM32F4_RCC_PLLCFGR 0x04
|
||||
#define STM32F4_RCC_CFGR 0x08
|
||||
#define STM32F4_RCC_AHB1ENR 0x30
|
||||
#define STM32F4_RCC_AHB2ENR 0x34
|
||||
#define STM32F4_RCC_AHB3ENR 0x38
|
||||
#define STM32F4_RCC_APB1ENR 0x40
|
||||
#define STM32F4_RCC_APB2ENR 0x44
|
||||
|
||||
struct stm32f4_gate_data {
|
||||
u8 offset;
|
||||
u8 bit_idx;
|
||||
const char *name;
|
||||
const char *parent_name;
|
||||
unsigned long flags;
|
||||
};
|
||||
|
||||
static const struct stm32f4_gate_data stm32f4_gates[] __initconst = {
|
||||
{ STM32F4_RCC_AHB1ENR, 0, "gpioa", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB1ENR, 1, "gpiob", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB1ENR, 2, "gpioc", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB1ENR, 3, "gpiod", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB1ENR, 4, "gpioe", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB1ENR, 5, "gpiof", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB1ENR, 6, "gpiog", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB1ENR, 7, "gpioh", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB1ENR, 8, "gpioi", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB1ENR, 9, "gpioj", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB1ENR, 10, "gpiok", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB1ENR, 12, "crc", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB1ENR, 18, "bkpsra", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB1ENR, 20, "ccmdatam", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB1ENR, 21, "dma1", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB1ENR, 22, "dma2", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB1ENR, 23, "dma2d", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB1ENR, 25, "ethmac", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB1ENR, 26, "ethmactx", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB1ENR, 27, "ethmacrx", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB1ENR, 28, "ethmacptp", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB1ENR, 29, "otghs", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB1ENR, 30, "otghsulpi", "ahb_div" },
|
||||
|
||||
{ STM32F4_RCC_AHB2ENR, 0, "dcmi", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB2ENR, 4, "cryp", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB2ENR, 5, "hash", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB2ENR, 6, "rng", "pll48" },
|
||||
{ STM32F4_RCC_AHB2ENR, 7, "otgfs", "pll48" },
|
||||
|
||||
{ STM32F4_RCC_AHB3ENR, 0, "fmc", "ahb_div",
|
||||
CLK_IGNORE_UNUSED },
|
||||
|
||||
{ STM32F4_RCC_APB1ENR, 0, "tim2", "apb1_mul" },
|
||||
{ STM32F4_RCC_APB1ENR, 1, "tim3", "apb1_mul" },
|
||||
{ STM32F4_RCC_APB1ENR, 2, "tim4", "apb1_mul" },
|
||||
{ STM32F4_RCC_APB1ENR, 3, "tim5", "apb1_mul" },
|
||||
{ STM32F4_RCC_APB1ENR, 4, "tim6", "apb1_mul" },
|
||||
{ STM32F4_RCC_APB1ENR, 5, "tim7", "apb1_mul" },
|
||||
{ STM32F4_RCC_APB1ENR, 6, "tim12", "apb1_mul" },
|
||||
{ STM32F4_RCC_APB1ENR, 7, "tim13", "apb1_mul" },
|
||||
{ STM32F4_RCC_APB1ENR, 8, "tim14", "apb1_mul" },
|
||||
{ STM32F4_RCC_APB1ENR, 11, "wwdg", "apb1_div" },
|
||||
{ STM32F4_RCC_APB1ENR, 14, "spi2", "apb1_div" },
|
||||
{ STM32F4_RCC_APB1ENR, 15, "spi3", "apb1_div" },
|
||||
{ STM32F4_RCC_APB1ENR, 17, "uart2", "apb1_div" },
|
||||
{ STM32F4_RCC_APB1ENR, 18, "uart3", "apb1_div" },
|
||||
{ STM32F4_RCC_APB1ENR, 19, "uart4", "apb1_div" },
|
||||
{ STM32F4_RCC_APB1ENR, 20, "uart5", "apb1_div" },
|
||||
{ STM32F4_RCC_APB1ENR, 21, "i2c1", "apb1_div" },
|
||||
{ STM32F4_RCC_APB1ENR, 22, "i2c2", "apb1_div" },
|
||||
{ STM32F4_RCC_APB1ENR, 23, "i2c3", "apb1_div" },
|
||||
{ STM32F4_RCC_APB1ENR, 25, "can1", "apb1_div" },
|
||||
{ STM32F4_RCC_APB1ENR, 26, "can2", "apb1_div" },
|
||||
{ STM32F4_RCC_APB1ENR, 28, "pwr", "apb1_div" },
|
||||
{ STM32F4_RCC_APB1ENR, 29, "dac", "apb1_div" },
|
||||
{ STM32F4_RCC_APB1ENR, 30, "uart7", "apb1_div" },
|
||||
{ STM32F4_RCC_APB1ENR, 31, "uart8", "apb1_div" },
|
||||
|
||||
{ STM32F4_RCC_APB2ENR, 0, "tim1", "apb2_mul" },
|
||||
{ STM32F4_RCC_APB2ENR, 1, "tim8", "apb2_mul" },
|
||||
{ STM32F4_RCC_APB2ENR, 4, "usart1", "apb2_div" },
|
||||
{ STM32F4_RCC_APB2ENR, 5, "usart6", "apb2_div" },
|
||||
{ STM32F4_RCC_APB2ENR, 8, "adc1", "apb2_div" },
|
||||
{ STM32F4_RCC_APB2ENR, 9, "adc2", "apb2_div" },
|
||||
{ STM32F4_RCC_APB2ENR, 10, "adc3", "apb2_div" },
|
||||
{ STM32F4_RCC_APB2ENR, 11, "sdio", "pll48" },
|
||||
{ STM32F4_RCC_APB2ENR, 12, "spi1", "apb2_div" },
|
||||
{ STM32F4_RCC_APB2ENR, 13, "spi4", "apb2_div" },
|
||||
{ STM32F4_RCC_APB2ENR, 14, "syscfg", "apb2_div" },
|
||||
{ STM32F4_RCC_APB2ENR, 16, "tim9", "apb2_mul" },
|
||||
{ STM32F4_RCC_APB2ENR, 17, "tim10", "apb2_mul" },
|
||||
{ STM32F4_RCC_APB2ENR, 18, "tim11", "apb2_mul" },
|
||||
{ STM32F4_RCC_APB2ENR, 20, "spi5", "apb2_div" },
|
||||
{ STM32F4_RCC_APB2ENR, 21, "spi6", "apb2_div" },
|
||||
{ STM32F4_RCC_APB2ENR, 22, "sai1", "apb2_div" },
|
||||
{ STM32F4_RCC_APB2ENR, 26, "ltdc", "apb2_div" },
|
||||
};
|
||||
|
||||
/*
|
||||
* MAX_CLKS is the maximum value in the enumeration below plus the combined
|
||||
* hweight of stm32f42xx_gate_map (plus one).
|
||||
*/
|
||||
#define MAX_CLKS 74
|
||||
|
||||
enum { SYSTICK, FCLK };
|
||||
|
||||
/*
|
||||
* This bitmask tells us which bit offsets (0..192) on STM32F4[23]xxx
|
||||
* have gate bits associated with them. Its combined hweight is 71.
|
||||
*/
|
||||
static const u64 stm32f42xx_gate_map[] = { 0x000000f17ef417ffull,
|
||||
0x0000000000000001ull,
|
||||
0x04777f33f6fec9ffull };
|
||||
|
||||
static struct clk *clks[MAX_CLKS];
|
||||
static DEFINE_SPINLOCK(stm32f4_clk_lock);
|
||||
static void __iomem *base;
|
||||
|
||||
/*
|
||||
* "Multiplier" device for APBx clocks.
|
||||
*
|
||||
* The APBx dividers are power-of-two dividers and, if *not* running in 1:1
|
||||
* mode, they also tap out the one of the low order state bits to run the
|
||||
* timers. ST datasheets represent this feature as a (conditional) clock
|
||||
* multiplier.
|
||||
*/
|
||||
struct clk_apb_mul {
|
||||
struct clk_hw hw;
|
||||
u8 bit_idx;
|
||||
};
|
||||
|
||||
#define to_clk_apb_mul(_hw) container_of(_hw, struct clk_apb_mul, hw)
|
||||
|
||||
static unsigned long clk_apb_mul_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_apb_mul *am = to_clk_apb_mul(hw);
|
||||
|
||||
if (readl(base + STM32F4_RCC_CFGR) & BIT(am->bit_idx))
|
||||
return parent_rate * 2;
|
||||
|
||||
return parent_rate;
|
||||
}
|
||||
|
||||
static long clk_apb_mul_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *prate)
|
||||
{
|
||||
struct clk_apb_mul *am = to_clk_apb_mul(hw);
|
||||
unsigned long mult = 1;
|
||||
|
||||
if (readl(base + STM32F4_RCC_CFGR) & BIT(am->bit_idx))
|
||||
mult = 2;
|
||||
|
||||
if (__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT) {
|
||||
unsigned long best_parent = rate / mult;
|
||||
|
||||
*prate =
|
||||
__clk_round_rate(__clk_get_parent(hw->clk), best_parent);
|
||||
}
|
||||
|
||||
return *prate * mult;
|
||||
}
|
||||
|
||||
static int clk_apb_mul_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
/*
|
||||
* We must report success but we can do so unconditionally because
|
||||
* clk_apb_mul_round_rate returns values that ensure this call is a
|
||||
* nop.
|
||||
*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct clk_ops clk_apb_mul_factor_ops = {
|
||||
.round_rate = clk_apb_mul_round_rate,
|
||||
.set_rate = clk_apb_mul_set_rate,
|
||||
.recalc_rate = clk_apb_mul_recalc_rate,
|
||||
};
|
||||
|
||||
static struct clk *clk_register_apb_mul(struct device *dev, const char *name,
|
||||
const char *parent_name,
|
||||
unsigned long flags, u8 bit_idx)
|
||||
{
|
||||
struct clk_apb_mul *am;
|
||||
struct clk_init_data init;
|
||||
struct clk *clk;
|
||||
|
||||
am = kzalloc(sizeof(*am), GFP_KERNEL);
|
||||
if (!am)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
am->bit_idx = bit_idx;
|
||||
am->hw.init = &init;
|
||||
|
||||
init.name = name;
|
||||
init.ops = &clk_apb_mul_factor_ops;
|
||||
init.flags = flags;
|
||||
init.parent_names = &parent_name;
|
||||
init.num_parents = 1;
|
||||
|
||||
clk = clk_register(dev, &am->hw);
|
||||
|
||||
if (IS_ERR(clk))
|
||||
kfree(am);
|
||||
|
||||
return clk;
|
||||
}
|
||||
|
||||
/*
|
||||
* Decode current PLL state and (statically) model the state we inherit from
|
||||
* the bootloader.
|
||||
*/
|
||||
static void stm32f4_rcc_register_pll(const char *hse_clk, const char *hsi_clk)
|
||||
{
|
||||
unsigned long pllcfgr = readl(base + STM32F4_RCC_PLLCFGR);
|
||||
|
||||
unsigned long pllm = pllcfgr & 0x3f;
|
||||
unsigned long plln = (pllcfgr >> 6) & 0x1ff;
|
||||
unsigned long pllp = BIT(((pllcfgr >> 16) & 3) + 1);
|
||||
const char *pllsrc = pllcfgr & BIT(22) ? hse_clk : hsi_clk;
|
||||
unsigned long pllq = (pllcfgr >> 24) & 0xf;
|
||||
|
||||
clk_register_fixed_factor(NULL, "vco", pllsrc, 0, plln, pllm);
|
||||
clk_register_fixed_factor(NULL, "pll", "vco", 0, 1, pllp);
|
||||
clk_register_fixed_factor(NULL, "pll48", "vco", 0, 1, pllq);
|
||||
}
|
||||
|
||||
/*
|
||||
* Converts the primary and secondary indices (as they appear in DT) to an
|
||||
* offset into our struct clock array.
|
||||
*/
|
||||
static int stm32f4_rcc_lookup_clk_idx(u8 primary, u8 secondary)
|
||||
{
|
||||
u64 table[ARRAY_SIZE(stm32f42xx_gate_map)];
|
||||
|
||||
if (primary == 1) {
|
||||
if (WARN_ON(secondary > FCLK))
|
||||
return -EINVAL;
|
||||
return secondary;
|
||||
}
|
||||
|
||||
memcpy(table, stm32f42xx_gate_map, sizeof(table));
|
||||
|
||||
/* only bits set in table can be used as indices */
|
||||
if (WARN_ON(secondary > 8 * sizeof(table) ||
|
||||
0 == (table[BIT_ULL_WORD(secondary)] &
|
||||
BIT_ULL_MASK(secondary))))
|
||||
return -EINVAL;
|
||||
|
||||
/* mask out bits above our current index */
|
||||
table[BIT_ULL_WORD(secondary)] &=
|
||||
GENMASK_ULL(secondary % BITS_PER_LONG_LONG, 0);
|
||||
|
||||
return FCLK + hweight64(table[0]) +
|
||||
(BIT_ULL_WORD(secondary) >= 1 ? hweight64(table[1]) : 0) +
|
||||
(BIT_ULL_WORD(secondary) >= 2 ? hweight64(table[2]) : 0);
|
||||
}
|
||||
|
||||
static struct clk *
|
||||
stm32f4_rcc_lookup_clk(struct of_phandle_args *clkspec, void *data)
|
||||
{
|
||||
int i = stm32f4_rcc_lookup_clk_idx(clkspec->args[0], clkspec->args[1]);
|
||||
|
||||
if (i < 0)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
return clks[i];
|
||||
}
|
||||
|
||||
static const char *sys_parents[] __initdata = { "hsi", NULL, "pll" };
|
||||
|
||||
static const struct clk_div_table ahb_div_table[] = {
|
||||
{ 0x0, 1 }, { 0x1, 1 }, { 0x2, 1 }, { 0x3, 1 },
|
||||
{ 0x4, 1 }, { 0x5, 1 }, { 0x6, 1 }, { 0x7, 1 },
|
||||
{ 0x8, 2 }, { 0x9, 4 }, { 0xa, 8 }, { 0xb, 16 },
|
||||
{ 0xc, 64 }, { 0xd, 128 }, { 0xe, 256 }, { 0xf, 512 },
|
||||
{ 0 },
|
||||
};
|
||||
|
||||
static const struct clk_div_table apb_div_table[] = {
|
||||
{ 0, 1 }, { 0, 1 }, { 0, 1 }, { 0, 1 },
|
||||
{ 4, 2 }, { 5, 4 }, { 6, 8 }, { 7, 16 },
|
||||
{ 0 },
|
||||
};
|
||||
|
||||
static void __init stm32f4_rcc_init(struct device_node *np)
|
||||
{
|
||||
const char *hse_clk;
|
||||
int n;
|
||||
|
||||
base = of_iomap(np, 0);
|
||||
if (!base) {
|
||||
pr_err("%s: unable to map resource", np->name);
|
||||
return;
|
||||
}
|
||||
|
||||
hse_clk = of_clk_get_parent_name(np, 0);
|
||||
|
||||
clk_register_fixed_rate_with_accuracy(NULL, "hsi", NULL, 0,
|
||||
16000000, 160000);
|
||||
stm32f4_rcc_register_pll(hse_clk, "hsi");
|
||||
|
||||
sys_parents[1] = hse_clk;
|
||||
clk_register_mux_table(
|
||||
NULL, "sys", sys_parents, ARRAY_SIZE(sys_parents), 0,
|
||||
base + STM32F4_RCC_CFGR, 0, 3, 0, NULL, &stm32f4_clk_lock);
|
||||
|
||||
clk_register_divider_table(NULL, "ahb_div", "sys",
|
||||
CLK_SET_RATE_PARENT, base + STM32F4_RCC_CFGR,
|
||||
4, 4, 0, ahb_div_table, &stm32f4_clk_lock);
|
||||
|
||||
clk_register_divider_table(NULL, "apb1_div", "ahb_div",
|
||||
CLK_SET_RATE_PARENT, base + STM32F4_RCC_CFGR,
|
||||
10, 3, 0, apb_div_table, &stm32f4_clk_lock);
|
||||
clk_register_apb_mul(NULL, "apb1_mul", "apb1_div",
|
||||
CLK_SET_RATE_PARENT, 12);
|
||||
|
||||
clk_register_divider_table(NULL, "apb2_div", "ahb_div",
|
||||
CLK_SET_RATE_PARENT, base + STM32F4_RCC_CFGR,
|
||||
13, 3, 0, apb_div_table, &stm32f4_clk_lock);
|
||||
clk_register_apb_mul(NULL, "apb2_mul", "apb2_div",
|
||||
CLK_SET_RATE_PARENT, 15);
|
||||
|
||||
clks[SYSTICK] = clk_register_fixed_factor(NULL, "systick", "ahb_div",
|
||||
0, 1, 8);
|
||||
clks[FCLK] = clk_register_fixed_factor(NULL, "fclk", "ahb_div",
|
||||
0, 1, 1);
|
||||
|
||||
for (n = 0; n < ARRAY_SIZE(stm32f4_gates); n++) {
|
||||
const struct stm32f4_gate_data *gd = &stm32f4_gates[n];
|
||||
unsigned int secondary =
|
||||
8 * (gd->offset - STM32F4_RCC_AHB1ENR) + gd->bit_idx;
|
||||
int idx = stm32f4_rcc_lookup_clk_idx(0, secondary);
|
||||
|
||||
if (idx < 0)
|
||||
goto fail;
|
||||
|
||||
clks[idx] = clk_register_gate(
|
||||
NULL, gd->name, gd->parent_name, gd->flags,
|
||||
base + gd->offset, gd->bit_idx, 0, &stm32f4_clk_lock);
|
||||
|
||||
if (IS_ERR(clks[n])) {
|
||||
pr_err("%s: Unable to register leaf clock %s\n",
|
||||
np->full_name, gd->name);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
of_clk_add_provider(np, stm32f4_rcc_lookup_clk, NULL);
|
||||
return;
|
||||
fail:
|
||||
iounmap(base);
|
||||
}
|
||||
CLK_OF_DECLARE(stm32f4_rcc, "st,stm32f42xx-rcc", stm32f4_rcc_init);
|
||||
|
|
@ -12,6 +12,7 @@
|
|||
#include <linux/clk-provider.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_data/clk-u300.h>
|
||||
|
||||
/* APP side SYSCON registers */
|
||||
/* CLK Control Register 16bit (R/W) */
|
||||
|
|
|
|||
|
|
@ -42,12 +42,12 @@
|
|||
|
||||
static DEFINE_SPINLOCK(clk_lock);
|
||||
|
||||
static inline u32 xgene_clk_read(void *csr)
|
||||
static inline u32 xgene_clk_read(void __iomem *csr)
|
||||
{
|
||||
return readl_relaxed(csr);
|
||||
}
|
||||
|
||||
static inline void xgene_clk_write(u32 data, void *csr)
|
||||
static inline void xgene_clk_write(u32 data, void __iomem *csr)
|
||||
{
|
||||
return writel_relaxed(data, csr);
|
||||
}
|
||||
|
|
@ -119,7 +119,7 @@ static unsigned long xgene_clk_pll_recalc_rate(struct clk_hw *hw,
|
|||
return fvco / nout;
|
||||
}
|
||||
|
||||
const struct clk_ops xgene_clk_pll_ops = {
|
||||
static const struct clk_ops xgene_clk_pll_ops = {
|
||||
.is_enabled = xgene_clk_pll_is_enabled,
|
||||
.recalc_rate = xgene_clk_pll_recalc_rate,
|
||||
};
|
||||
|
|
@ -167,7 +167,7 @@ static void xgene_pllclk_init(struct device_node *np, enum xgene_pll_type pll_ty
|
|||
{
|
||||
const char *clk_name = np->full_name;
|
||||
struct clk *clk;
|
||||
void *reg;
|
||||
void __iomem *reg;
|
||||
|
||||
reg = of_iomap(np, 0);
|
||||
if (reg == NULL) {
|
||||
|
|
@ -222,20 +222,22 @@ static int xgene_clk_enable(struct clk_hw *hw)
|
|||
struct xgene_clk *pclk = to_xgene_clk(hw);
|
||||
unsigned long flags = 0;
|
||||
u32 data;
|
||||
phys_addr_t reg;
|
||||
|
||||
if (pclk->lock)
|
||||
spin_lock_irqsave(pclk->lock, flags);
|
||||
|
||||
if (pclk->param.csr_reg != NULL) {
|
||||
pr_debug("%s clock enabled\n", pclk->name);
|
||||
reg = __pa(pclk->param.csr_reg);
|
||||
/* First enable the clock */
|
||||
data = xgene_clk_read(pclk->param.csr_reg +
|
||||
pclk->param.reg_clk_offset);
|
||||
data |= pclk->param.reg_clk_mask;
|
||||
xgene_clk_write(data, pclk->param.csr_reg +
|
||||
pclk->param.reg_clk_offset);
|
||||
pr_debug("%s clock PADDR base 0x%016LX clk offset 0x%08X mask 0x%08X value 0x%08X\n",
|
||||
pclk->name, __pa(pclk->param.csr_reg),
|
||||
pr_debug("%s clock PADDR base %pa clk offset 0x%08X mask 0x%08X value 0x%08X\n",
|
||||
pclk->name, ®,
|
||||
pclk->param.reg_clk_offset, pclk->param.reg_clk_mask,
|
||||
data);
|
||||
|
||||
|
|
@ -245,8 +247,8 @@ static int xgene_clk_enable(struct clk_hw *hw)
|
|||
data &= ~pclk->param.reg_csr_mask;
|
||||
xgene_clk_write(data, pclk->param.csr_reg +
|
||||
pclk->param.reg_csr_offset);
|
||||
pr_debug("%s CSR RESET PADDR base 0x%016LX csr offset 0x%08X mask 0x%08X value 0x%08X\n",
|
||||
pclk->name, __pa(pclk->param.csr_reg),
|
||||
pr_debug("%s CSR RESET PADDR base %pa csr offset 0x%08X mask 0x%08X value 0x%08X\n",
|
||||
pclk->name, ®,
|
||||
pclk->param.reg_csr_offset, pclk->param.reg_csr_mask,
|
||||
data);
|
||||
}
|
||||
|
|
@ -386,7 +388,7 @@ static long xgene_clk_round_rate(struct clk_hw *hw, unsigned long rate,
|
|||
return parent_rate / divider;
|
||||
}
|
||||
|
||||
const struct clk_ops xgene_clk_ops = {
|
||||
static const struct clk_ops xgene_clk_ops = {
|
||||
.enable = xgene_clk_enable,
|
||||
.disable = xgene_clk_disable,
|
||||
.is_enabled = xgene_clk_is_enabled,
|
||||
|
|
@ -456,7 +458,7 @@ static void __init xgene_devclk_init(struct device_node *np)
|
|||
parameters.csr_reg = NULL;
|
||||
parameters.divider_reg = NULL;
|
||||
for (i = 0; i < 2; i++) {
|
||||
void *map_res;
|
||||
void __iomem *map_res;
|
||||
rc = of_address_to_resource(np, i, &res);
|
||||
if (rc != 0) {
|
||||
if (i == 0) {
|
||||
|
|
|
|||
1694
drivers/clk/clk.c
1694
drivers/clk/clk.c
File diff suppressed because it is too large
Load diff
6
drivers/clk/hisilicon/Kconfig
Normal file
6
drivers/clk/hisilicon/Kconfig
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
config COMMON_CLK_HI6220
|
||||
bool "Hi6220 Clock Driver"
|
||||
depends on ARCH_HISI || COMPILE_TEST
|
||||
default ARCH_HISI
|
||||
help
|
||||
Build the Hisilicon Hi6220 clock driver based on the common clock framework.
|
||||
|
|
@ -2,8 +2,9 @@
|
|||
# Hisilicon Clock specific Makefile
|
||||
#
|
||||
|
||||
obj-y += clk.o clkgate-separated.o
|
||||
obj-y += clk.o clkgate-separated.o clkdivider-hi6220.o
|
||||
|
||||
obj-$(CONFIG_ARCH_HI3xxx) += clk-hi3620.o
|
||||
obj-$(CONFIG_ARCH_HIP04) += clk-hip04.o
|
||||
obj-$(CONFIG_ARCH_HIX5HD2) += clk-hix5hd2.o
|
||||
obj-$(CONFIG_COMMON_CLK_HI6220) += clk-hi6220.o
|
||||
|
|
|
|||
|
|
@ -38,44 +38,44 @@
|
|||
#include "clk.h"
|
||||
|
||||
/* clock parent list */
|
||||
static const char *timer0_mux_p[] __initdata = { "osc32k", "timerclk01", };
|
||||
static const char *timer1_mux_p[] __initdata = { "osc32k", "timerclk01", };
|
||||
static const char *timer2_mux_p[] __initdata = { "osc32k", "timerclk23", };
|
||||
static const char *timer3_mux_p[] __initdata = { "osc32k", "timerclk23", };
|
||||
static const char *timer4_mux_p[] __initdata = { "osc32k", "timerclk45", };
|
||||
static const char *timer5_mux_p[] __initdata = { "osc32k", "timerclk45", };
|
||||
static const char *timer6_mux_p[] __initdata = { "osc32k", "timerclk67", };
|
||||
static const char *timer7_mux_p[] __initdata = { "osc32k", "timerclk67", };
|
||||
static const char *timer8_mux_p[] __initdata = { "osc32k", "timerclk89", };
|
||||
static const char *timer9_mux_p[] __initdata = { "osc32k", "timerclk89", };
|
||||
static const char *uart0_mux_p[] __initdata = { "osc26m", "pclk", };
|
||||
static const char *uart1_mux_p[] __initdata = { "osc26m", "pclk", };
|
||||
static const char *uart2_mux_p[] __initdata = { "osc26m", "pclk", };
|
||||
static const char *uart3_mux_p[] __initdata = { "osc26m", "pclk", };
|
||||
static const char *uart4_mux_p[] __initdata = { "osc26m", "pclk", };
|
||||
static const char *spi0_mux_p[] __initdata = { "osc26m", "rclk_cfgaxi", };
|
||||
static const char *spi1_mux_p[] __initdata = { "osc26m", "rclk_cfgaxi", };
|
||||
static const char *spi2_mux_p[] __initdata = { "osc26m", "rclk_cfgaxi", };
|
||||
static const char *const timer0_mux_p[] __initconst = { "osc32k", "timerclk01", };
|
||||
static const char *const timer1_mux_p[] __initconst = { "osc32k", "timerclk01", };
|
||||
static const char *const timer2_mux_p[] __initconst = { "osc32k", "timerclk23", };
|
||||
static const char *const timer3_mux_p[] __initconst = { "osc32k", "timerclk23", };
|
||||
static const char *const timer4_mux_p[] __initconst = { "osc32k", "timerclk45", };
|
||||
static const char *const timer5_mux_p[] __initconst = { "osc32k", "timerclk45", };
|
||||
static const char *const timer6_mux_p[] __initconst = { "osc32k", "timerclk67", };
|
||||
static const char *const timer7_mux_p[] __initconst = { "osc32k", "timerclk67", };
|
||||
static const char *const timer8_mux_p[] __initconst = { "osc32k", "timerclk89", };
|
||||
static const char *const timer9_mux_p[] __initconst = { "osc32k", "timerclk89", };
|
||||
static const char *const uart0_mux_p[] __initconst = { "osc26m", "pclk", };
|
||||
static const char *const uart1_mux_p[] __initconst = { "osc26m", "pclk", };
|
||||
static const char *const uart2_mux_p[] __initconst = { "osc26m", "pclk", };
|
||||
static const char *const uart3_mux_p[] __initconst = { "osc26m", "pclk", };
|
||||
static const char *const uart4_mux_p[] __initconst = { "osc26m", "pclk", };
|
||||
static const char *const spi0_mux_p[] __initconst = { "osc26m", "rclk_cfgaxi", };
|
||||
static const char *const spi1_mux_p[] __initconst = { "osc26m", "rclk_cfgaxi", };
|
||||
static const char *const spi2_mux_p[] __initconst = { "osc26m", "rclk_cfgaxi", };
|
||||
/* share axi parent */
|
||||
static const char *saxi_mux_p[] __initdata = { "armpll3", "armpll2", };
|
||||
static const char *pwm0_mux_p[] __initdata = { "osc32k", "osc26m", };
|
||||
static const char *pwm1_mux_p[] __initdata = { "osc32k", "osc26m", };
|
||||
static const char *sd_mux_p[] __initdata = { "armpll2", "armpll3", };
|
||||
static const char *mmc1_mux_p[] __initdata = { "armpll2", "armpll3", };
|
||||
static const char *mmc1_mux2_p[] __initdata = { "osc26m", "mmc1_div", };
|
||||
static const char *g2d_mux_p[] __initdata = { "armpll2", "armpll3", };
|
||||
static const char *venc_mux_p[] __initdata = { "armpll2", "armpll3", };
|
||||
static const char *vdec_mux_p[] __initdata = { "armpll2", "armpll3", };
|
||||
static const char *vpp_mux_p[] __initdata = { "armpll2", "armpll3", };
|
||||
static const char *edc0_mux_p[] __initdata = { "armpll2", "armpll3", };
|
||||
static const char *ldi0_mux_p[] __initdata = { "armpll2", "armpll4",
|
||||
static const char *const saxi_mux_p[] __initconst = { "armpll3", "armpll2", };
|
||||
static const char *const pwm0_mux_p[] __initconst = { "osc32k", "osc26m", };
|
||||
static const char *const pwm1_mux_p[] __initconst = { "osc32k", "osc26m", };
|
||||
static const char *const sd_mux_p[] __initconst = { "armpll2", "armpll3", };
|
||||
static const char *const mmc1_mux_p[] __initconst = { "armpll2", "armpll3", };
|
||||
static const char *const mmc1_mux2_p[] __initconst = { "osc26m", "mmc1_div", };
|
||||
static const char *const g2d_mux_p[] __initconst = { "armpll2", "armpll3", };
|
||||
static const char *const venc_mux_p[] __initconst = { "armpll2", "armpll3", };
|
||||
static const char *const vdec_mux_p[] __initconst = { "armpll2", "armpll3", };
|
||||
static const char *const vpp_mux_p[] __initconst = { "armpll2", "armpll3", };
|
||||
static const char *const edc0_mux_p[] __initconst = { "armpll2", "armpll3", };
|
||||
static const char *const ldi0_mux_p[] __initconst = { "armpll2", "armpll4",
|
||||
"armpll3", "armpll5", };
|
||||
static const char *edc1_mux_p[] __initdata = { "armpll2", "armpll3", };
|
||||
static const char *ldi1_mux_p[] __initdata = { "armpll2", "armpll4",
|
||||
static const char *const edc1_mux_p[] __initconst = { "armpll2", "armpll3", };
|
||||
static const char *const ldi1_mux_p[] __initconst = { "armpll2", "armpll4",
|
||||
"armpll3", "armpll5", };
|
||||
static const char *rclk_hsic_p[] __initdata = { "armpll3", "armpll2", };
|
||||
static const char *mmc2_mux_p[] __initdata = { "armpll2", "armpll3", };
|
||||
static const char *mmc3_mux_p[] __initdata = { "armpll2", "armpll3", };
|
||||
static const char *const rclk_hsic_p[] __initconst = { "armpll3", "armpll2", };
|
||||
static const char *const mmc2_mux_p[] __initconst = { "armpll2", "armpll3", };
|
||||
static const char *const mmc3_mux_p[] __initconst = { "armpll2", "armpll3", };
|
||||
|
||||
|
||||
/* fixed rate clocks */
|
||||
|
|
|
|||
284
drivers/clk/hisilicon/clk-hi6220.c
Normal file
284
drivers/clk/hisilicon/clk-hi6220.c
Normal file
|
|
@ -0,0 +1,284 @@
|
|||
/*
|
||||
* Hisilicon Hi6220 clock driver
|
||||
*
|
||||
* Copyright (c) 2015 Hisilicon Limited.
|
||||
*
|
||||
* Author: Bintian Wang <bintian.wang@huawei.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <dt-bindings/clock/hi6220-clock.h>
|
||||
|
||||
#include "clk.h"
|
||||
|
||||
|
||||
/* clocks in AO (always on) controller */
|
||||
static struct hisi_fixed_rate_clock hi6220_fixed_rate_clks[] __initdata = {
|
||||
{ HI6220_REF32K, "ref32k", NULL, CLK_IS_ROOT, 32764, },
|
||||
{ HI6220_CLK_TCXO, "clk_tcxo", NULL, CLK_IS_ROOT, 19200000, },
|
||||
{ HI6220_MMC1_PAD, "mmc1_pad", NULL, CLK_IS_ROOT, 100000000, },
|
||||
{ HI6220_MMC2_PAD, "mmc2_pad", NULL, CLK_IS_ROOT, 100000000, },
|
||||
{ HI6220_MMC0_PAD, "mmc0_pad", NULL, CLK_IS_ROOT, 200000000, },
|
||||
{ HI6220_PLL_BBP, "bbppll0", NULL, CLK_IS_ROOT, 245760000, },
|
||||
{ HI6220_PLL_GPU, "gpupll", NULL, CLK_IS_ROOT, 1000000000,},
|
||||
{ HI6220_PLL1_DDR, "ddrpll1", NULL, CLK_IS_ROOT, 1066000000,},
|
||||
{ HI6220_PLL_SYS, "syspll", NULL, CLK_IS_ROOT, 1200000000,},
|
||||
{ HI6220_PLL_SYS_MEDIA, "media_syspll", NULL, CLK_IS_ROOT, 1200000000,},
|
||||
{ HI6220_DDR_SRC, "ddr_sel_src", NULL, CLK_IS_ROOT, 1200000000,},
|
||||
{ HI6220_PLL_MEDIA, "media_pll", NULL, CLK_IS_ROOT, 1440000000,},
|
||||
{ HI6220_PLL_DDR, "ddrpll0", NULL, CLK_IS_ROOT, 1600000000,},
|
||||
};
|
||||
|
||||
static struct hisi_fixed_factor_clock hi6220_fixed_factor_clks[] __initdata = {
|
||||
{ HI6220_300M, "clk_300m", "syspll", 1, 4, 0, },
|
||||
{ HI6220_150M, "clk_150m", "clk_300m", 1, 2, 0, },
|
||||
{ HI6220_PICOPHY_SRC, "picophy_src", "clk_150m", 1, 4, 0, },
|
||||
{ HI6220_MMC0_SRC_SEL, "mmc0srcsel", "mmc0_sel", 1, 8, 0, },
|
||||
{ HI6220_MMC1_SRC_SEL, "mmc1srcsel", "mmc1_sel", 1, 8, 0, },
|
||||
{ HI6220_MMC2_SRC_SEL, "mmc2srcsel", "mmc2_sel", 1, 8, 0, },
|
||||
{ HI6220_VPU_CODEC, "vpucodec", "codec_jpeg_aclk", 1, 2, 0, },
|
||||
{ HI6220_MMC0_SMP, "mmc0_sample", "mmc0_sel", 1, 8, 0, },
|
||||
{ HI6220_MMC1_SMP, "mmc1_sample", "mmc1_sel", 1, 8, 0, },
|
||||
{ HI6220_MMC2_SMP, "mmc2_sample", "mmc2_sel", 1, 8, 0, },
|
||||
};
|
||||
|
||||
static struct hisi_gate_clock hi6220_separated_gate_clks_ao[] __initdata = {
|
||||
{ HI6220_WDT0_PCLK, "wdt0_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 12, 0, },
|
||||
{ HI6220_WDT1_PCLK, "wdt1_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 13, 0, },
|
||||
{ HI6220_WDT2_PCLK, "wdt2_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 14, 0, },
|
||||
{ HI6220_TIMER0_PCLK, "timer0_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 15, 0, },
|
||||
{ HI6220_TIMER1_PCLK, "timer1_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 16, 0, },
|
||||
{ HI6220_TIMER2_PCLK, "timer2_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 17, 0, },
|
||||
{ HI6220_TIMER3_PCLK, "timer3_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 18, 0, },
|
||||
{ HI6220_TIMER4_PCLK, "timer4_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 19, 0, },
|
||||
{ HI6220_TIMER5_PCLK, "timer5_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 20, 0, },
|
||||
{ HI6220_TIMER6_PCLK, "timer6_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 21, 0, },
|
||||
{ HI6220_TIMER7_PCLK, "timer7_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 22, 0, },
|
||||
{ HI6220_TIMER8_PCLK, "timer8_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 23, 0, },
|
||||
{ HI6220_UART0_PCLK, "uart0_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 24, 0, },
|
||||
};
|
||||
|
||||
static void __init hi6220_clk_ao_init(struct device_node *np)
|
||||
{
|
||||
struct hisi_clock_data *clk_data_ao;
|
||||
|
||||
clk_data_ao = hisi_clk_init(np, HI6220_AO_NR_CLKS);
|
||||
if (!clk_data_ao)
|
||||
return;
|
||||
|
||||
hisi_clk_register_fixed_rate(hi6220_fixed_rate_clks,
|
||||
ARRAY_SIZE(hi6220_fixed_rate_clks), clk_data_ao);
|
||||
|
||||
hisi_clk_register_fixed_factor(hi6220_fixed_factor_clks,
|
||||
ARRAY_SIZE(hi6220_fixed_factor_clks), clk_data_ao);
|
||||
|
||||
hisi_clk_register_gate_sep(hi6220_separated_gate_clks_ao,
|
||||
ARRAY_SIZE(hi6220_separated_gate_clks_ao), clk_data_ao);
|
||||
}
|
||||
CLK_OF_DECLARE(hi6220_clk_ao, "hisilicon,hi6220-aoctrl", hi6220_clk_ao_init);
|
||||
|
||||
|
||||
/* clocks in sysctrl */
|
||||
static const char *mmc0_mux0_p[] __initdata = { "pll_ddr_gate", "syspll", };
|
||||
static const char *mmc0_mux1_p[] __initdata = { "mmc0_mux0", "pll_media_gate", };
|
||||
static const char *mmc0_src_p[] __initdata = { "mmc0srcsel", "mmc0_div", };
|
||||
static const char *mmc1_mux0_p[] __initdata = { "pll_ddr_gate", "syspll", };
|
||||
static const char *mmc1_mux1_p[] __initdata = { "mmc1_mux0", "pll_media_gate", };
|
||||
static const char *mmc1_src_p[] __initdata = { "mmc1srcsel", "mmc1_div", };
|
||||
static const char *mmc2_mux0_p[] __initdata = { "pll_ddr_gate", "syspll", };
|
||||
static const char *mmc2_mux1_p[] __initdata = { "mmc2_mux0", "pll_media_gate", };
|
||||
static const char *mmc2_src_p[] __initdata = { "mmc2srcsel", "mmc2_div", };
|
||||
static const char *mmc0_sample_in[] __initdata = { "mmc0_sample", "mmc0_pad", };
|
||||
static const char *mmc1_sample_in[] __initdata = { "mmc1_sample", "mmc1_pad", };
|
||||
static const char *mmc2_sample_in[] __initdata = { "mmc2_sample", "mmc2_pad", };
|
||||
static const char *uart1_src[] __initdata = { "clk_tcxo", "clk_150m", };
|
||||
static const char *uart2_src[] __initdata = { "clk_tcxo", "clk_150m", };
|
||||
static const char *uart3_src[] __initdata = { "clk_tcxo", "clk_150m", };
|
||||
static const char *uart4_src[] __initdata = { "clk_tcxo", "clk_150m", };
|
||||
static const char *hifi_src[] __initdata = { "syspll", "pll_media_gate", };
|
||||
|
||||
static struct hisi_gate_clock hi6220_separated_gate_clks_sys[] __initdata = {
|
||||
{ HI6220_MMC0_CLK, "mmc0_clk", "mmc0_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x200, 0, 0, },
|
||||
{ HI6220_MMC0_CIUCLK, "mmc0_ciuclk", "mmc0_smp_in", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x200, 0, 0, },
|
||||
{ HI6220_MMC1_CLK, "mmc1_clk", "mmc1_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x200, 1, 0, },
|
||||
{ HI6220_MMC1_CIUCLK, "mmc1_ciuclk", "mmc1_smp_in", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x200, 1, 0, },
|
||||
{ HI6220_MMC2_CLK, "mmc2_clk", "mmc2_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x200, 2, 0, },
|
||||
{ HI6220_MMC2_CIUCLK, "mmc2_ciuclk", "mmc2_smp_in", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x200, 2, 0, },
|
||||
{ HI6220_USBOTG_HCLK, "usbotg_hclk", "clk_bus", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x200, 4, 0, },
|
||||
{ HI6220_CLK_PICOPHY, "clk_picophy", "cs_dapb", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x200, 5, 0, },
|
||||
{ HI6220_HIFI, "hifi_clk", "hifi_div", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x210, 0, 0, },
|
||||
{ HI6220_DACODEC_PCLK, "dacodec_pclk", "clk_bus", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x210, 5, 0, },
|
||||
{ HI6220_EDMAC_ACLK, "edmac_aclk", "clk_bus", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x220, 2, 0, },
|
||||
{ HI6220_CS_ATB, "cs_atb", "cs_atb_div", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 0, 0, },
|
||||
{ HI6220_I2C0_CLK, "i2c0_clk", "clk_150m", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 1, 0, },
|
||||
{ HI6220_I2C1_CLK, "i2c1_clk", "clk_150m", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 2, 0, },
|
||||
{ HI6220_I2C2_CLK, "i2c2_clk", "clk_150m", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 3, 0, },
|
||||
{ HI6220_I2C3_CLK, "i2c3_clk", "clk_150m", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 4, 0, },
|
||||
{ HI6220_UART1_PCLK, "uart1_pclk", "uart1_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 5, 0, },
|
||||
{ HI6220_UART2_PCLK, "uart2_pclk", "uart2_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 6, 0, },
|
||||
{ HI6220_UART3_PCLK, "uart3_pclk", "uart3_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 7, 0, },
|
||||
{ HI6220_UART4_PCLK, "uart4_pclk", "uart4_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 8, 0, },
|
||||
{ HI6220_SPI_CLK, "spi_clk", "clk_150m", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 9, 0, },
|
||||
{ HI6220_TSENSOR_CLK, "tsensor_clk", "clk_bus", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 12, 0, },
|
||||
{ HI6220_MMU_CLK, "mmu_clk", "ddrc_axi1", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x240, 11, 0, },
|
||||
{ HI6220_HIFI_SEL, "hifi_sel", "hifi_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 0, 0, },
|
||||
{ HI6220_MMC0_SYSPLL, "mmc0_syspll", "syspll", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 1, 0, },
|
||||
{ HI6220_MMC1_SYSPLL, "mmc1_syspll", "syspll", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 2, 0, },
|
||||
{ HI6220_MMC2_SYSPLL, "mmc2_syspll", "syspll", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 3, 0, },
|
||||
{ HI6220_MMC0_SEL, "mmc0_sel", "mmc0_mux1", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 6, 0, },
|
||||
{ HI6220_MMC1_SEL, "mmc1_sel", "mmc1_mux1", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 7, 0, },
|
||||
{ HI6220_BBPPLL_SEL, "bbppll_sel", "pll0_bbp_gate", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 9, 0, },
|
||||
{ HI6220_MEDIA_PLL_SRC, "media_pll_src", "pll_media_gate", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 10, 0, },
|
||||
{ HI6220_MMC2_SEL, "mmc2_sel", "mmc2_mux1", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 11, 0, },
|
||||
{ HI6220_CS_ATB_SYSPLL, "cs_atb_syspll", "syspll", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 12, 0, },
|
||||
};
|
||||
|
||||
static struct hisi_mux_clock hi6220_mux_clks_sys[] __initdata = {
|
||||
{ HI6220_MMC0_SRC, "mmc0_src", mmc0_src_p, ARRAY_SIZE(mmc0_src_p), CLK_SET_RATE_PARENT, 0x4, 0, 1, 0, },
|
||||
{ HI6220_MMC0_SMP_IN, "mmc0_smp_in", mmc0_sample_in, ARRAY_SIZE(mmc0_sample_in), CLK_SET_RATE_PARENT, 0x4, 0, 1, 0, },
|
||||
{ HI6220_MMC1_SRC, "mmc1_src", mmc1_src_p, ARRAY_SIZE(mmc1_src_p), CLK_SET_RATE_PARENT, 0x4, 2, 1, 0, },
|
||||
{ HI6220_MMC1_SMP_IN, "mmc1_smp_in", mmc1_sample_in, ARRAY_SIZE(mmc1_sample_in), CLK_SET_RATE_PARENT, 0x4, 2, 1, 0, },
|
||||
{ HI6220_MMC2_SRC, "mmc2_src", mmc2_src_p, ARRAY_SIZE(mmc2_src_p), CLK_SET_RATE_PARENT, 0x4, 4, 1, 0, },
|
||||
{ HI6220_MMC2_SMP_IN, "mmc2_smp_in", mmc2_sample_in, ARRAY_SIZE(mmc2_sample_in), CLK_SET_RATE_PARENT, 0x4, 4, 1, 0, },
|
||||
{ HI6220_HIFI_SRC, "hifi_src", hifi_src, ARRAY_SIZE(hifi_src), CLK_SET_RATE_PARENT, 0x400, 0, 1, CLK_MUX_HIWORD_MASK,},
|
||||
{ HI6220_UART1_SRC, "uart1_src", uart1_src, ARRAY_SIZE(uart1_src), CLK_SET_RATE_PARENT, 0x400, 1, 1, CLK_MUX_HIWORD_MASK,},
|
||||
{ HI6220_UART2_SRC, "uart2_src", uart2_src, ARRAY_SIZE(uart2_src), CLK_SET_RATE_PARENT, 0x400, 2, 1, CLK_MUX_HIWORD_MASK,},
|
||||
{ HI6220_UART3_SRC, "uart3_src", uart3_src, ARRAY_SIZE(uart3_src), CLK_SET_RATE_PARENT, 0x400, 3, 1, CLK_MUX_HIWORD_MASK,},
|
||||
{ HI6220_UART4_SRC, "uart4_src", uart4_src, ARRAY_SIZE(uart4_src), CLK_SET_RATE_PARENT, 0x400, 4, 1, CLK_MUX_HIWORD_MASK,},
|
||||
{ HI6220_MMC0_MUX0, "mmc0_mux0", mmc0_mux0_p, ARRAY_SIZE(mmc0_mux0_p), CLK_SET_RATE_PARENT, 0x400, 5, 1, CLK_MUX_HIWORD_MASK,},
|
||||
{ HI6220_MMC1_MUX0, "mmc1_mux0", mmc1_mux0_p, ARRAY_SIZE(mmc1_mux0_p), CLK_SET_RATE_PARENT, 0x400, 11, 1, CLK_MUX_HIWORD_MASK,},
|
||||
{ HI6220_MMC2_MUX0, "mmc2_mux0", mmc2_mux0_p, ARRAY_SIZE(mmc2_mux0_p), CLK_SET_RATE_PARENT, 0x400, 12, 1, CLK_MUX_HIWORD_MASK,},
|
||||
{ HI6220_MMC0_MUX1, "mmc0_mux1", mmc0_mux1_p, ARRAY_SIZE(mmc0_mux1_p), CLK_SET_RATE_PARENT, 0x400, 13, 1, CLK_MUX_HIWORD_MASK,},
|
||||
{ HI6220_MMC1_MUX1, "mmc1_mux1", mmc1_mux1_p, ARRAY_SIZE(mmc1_mux1_p), CLK_SET_RATE_PARENT, 0x400, 14, 1, CLK_MUX_HIWORD_MASK,},
|
||||
{ HI6220_MMC2_MUX1, "mmc2_mux1", mmc2_mux1_p, ARRAY_SIZE(mmc2_mux1_p), CLK_SET_RATE_PARENT, 0x400, 15, 1, CLK_MUX_HIWORD_MASK,},
|
||||
};
|
||||
|
||||
static struct hi6220_divider_clock hi6220_div_clks_sys[] __initdata = {
|
||||
{ HI6220_CLK_BUS, "clk_bus", "clk_300m", CLK_SET_RATE_PARENT, 0x490, 0, 4, 7, },
|
||||
{ HI6220_MMC0_DIV, "mmc0_div", "mmc0_syspll", CLK_SET_RATE_PARENT, 0x494, 0, 6, 7, },
|
||||
{ HI6220_MMC1_DIV, "mmc1_div", "mmc1_syspll", CLK_SET_RATE_PARENT, 0x498, 0, 6, 7, },
|
||||
{ HI6220_MMC2_DIV, "mmc2_div", "mmc2_syspll", CLK_SET_RATE_PARENT, 0x49c, 0, 6, 7, },
|
||||
{ HI6220_HIFI_DIV, "hifi_div", "hifi_sel", CLK_SET_RATE_PARENT, 0x4a0, 0, 4, 7, },
|
||||
{ HI6220_BBPPLL0_DIV, "bbppll0_div", "bbppll_sel", CLK_SET_RATE_PARENT, 0x4a0, 8, 6, 15,},
|
||||
{ HI6220_CS_DAPB, "cs_dapb", "picophy_src", CLK_SET_RATE_PARENT, 0x4a0, 24, 2, 31,},
|
||||
{ HI6220_CS_ATB_DIV, "cs_atb_div", "cs_atb_syspll", CLK_SET_RATE_PARENT, 0x4a4, 0, 4, 7, },
|
||||
};
|
||||
|
||||
static void __init hi6220_clk_sys_init(struct device_node *np)
|
||||
{
|
||||
struct hisi_clock_data *clk_data;
|
||||
|
||||
clk_data = hisi_clk_init(np, HI6220_SYS_NR_CLKS);
|
||||
if (!clk_data)
|
||||
return;
|
||||
|
||||
hisi_clk_register_gate_sep(hi6220_separated_gate_clks_sys,
|
||||
ARRAY_SIZE(hi6220_separated_gate_clks_sys), clk_data);
|
||||
|
||||
hisi_clk_register_mux(hi6220_mux_clks_sys,
|
||||
ARRAY_SIZE(hi6220_mux_clks_sys), clk_data);
|
||||
|
||||
hi6220_clk_register_divider(hi6220_div_clks_sys,
|
||||
ARRAY_SIZE(hi6220_div_clks_sys), clk_data);
|
||||
}
|
||||
CLK_OF_DECLARE(hi6220_clk_sys, "hisilicon,hi6220-sysctrl", hi6220_clk_sys_init);
|
||||
|
||||
|
||||
/* clocks in media controller */
|
||||
static const char *clk_1000_1200_src[] __initdata = { "pll_gpu_gate", "media_syspll_src", };
|
||||
static const char *clk_1440_1200_src[] __initdata = { "media_syspll_src", "media_pll_src", };
|
||||
static const char *clk_1000_1440_src[] __initdata = { "pll_gpu_gate", "media_pll_src", };
|
||||
|
||||
static struct hisi_gate_clock hi6220_separated_gate_clks_media[] __initdata = {
|
||||
{ HI6220_DSI_PCLK, "dsi_pclk", "vpucodec", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 0, 0, },
|
||||
{ HI6220_G3D_PCLK, "g3d_pclk", "vpucodec", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 1, 0, },
|
||||
{ HI6220_ACLK_CODEC_VPU, "aclk_codec_vpu", "ade_core_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 3, 0, },
|
||||
{ HI6220_ISP_SCLK, "isp_sclk", "isp_sclk_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 5, 0, },
|
||||
{ HI6220_ADE_CORE, "ade_core", "ade_core_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 6, 0, },
|
||||
{ HI6220_MED_MMU, "media_mmu", "mmu_clk", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 8, 0, },
|
||||
{ HI6220_CFG_CSI4PHY, "cfg_csi4phy", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 9, 0, },
|
||||
{ HI6220_CFG_CSI2PHY, "cfg_csi2phy", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 10, 0, },
|
||||
{ HI6220_ISP_SCLK_GATE, "isp_sclk_gate", "media_pll_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 11, 0, },
|
||||
{ HI6220_ISP_SCLK_GATE1, "isp_sclk_gate1", "media_pll_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 12, 0, },
|
||||
{ HI6220_ADE_CORE_GATE, "ade_core_gate", "media_pll_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 14, 0, },
|
||||
{ HI6220_CODEC_VPU_GATE, "codec_vpu_gate", "clk_1000_1440", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 15, 0, },
|
||||
{ HI6220_MED_SYSPLL, "media_syspll_src", "media_syspll", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 17, 0, },
|
||||
};
|
||||
|
||||
static struct hisi_mux_clock hi6220_mux_clks_media[] __initdata = {
|
||||
{ HI6220_1440_1200, "clk_1440_1200", clk_1440_1200_src, ARRAY_SIZE(clk_1440_1200_src), CLK_SET_RATE_PARENT, 0x51c, 0, 1, 0, },
|
||||
{ HI6220_1000_1200, "clk_1000_1200", clk_1000_1200_src, ARRAY_SIZE(clk_1000_1200_src), CLK_SET_RATE_PARENT, 0x51c, 1, 1, 0, },
|
||||
{ HI6220_1000_1440, "clk_1000_1440", clk_1000_1440_src, ARRAY_SIZE(clk_1000_1440_src), CLK_SET_RATE_PARENT, 0x51c, 6, 1, 0, },
|
||||
};
|
||||
|
||||
static struct hi6220_divider_clock hi6220_div_clks_media[] __initdata = {
|
||||
{ HI6220_CODEC_JPEG, "codec_jpeg_aclk", "media_pll_src", CLK_SET_RATE_PARENT, 0xcbc, 0, 4, 23, },
|
||||
{ HI6220_ISP_SCLK_SRC, "isp_sclk_src", "isp_sclk_gate", CLK_SET_RATE_PARENT, 0xcbc, 8, 4, 15, },
|
||||
{ HI6220_ISP_SCLK1, "isp_sclk1", "isp_sclk_gate1", CLK_SET_RATE_PARENT, 0xcbc, 24, 4, 31, },
|
||||
{ HI6220_ADE_CORE_SRC, "ade_core_src", "ade_core_gate", CLK_SET_RATE_PARENT, 0xcc0, 16, 3, 23, },
|
||||
{ HI6220_ADE_PIX_SRC, "ade_pix_src", "clk_1440_1200", CLK_SET_RATE_PARENT, 0xcc0, 24, 6, 31, },
|
||||
{ HI6220_G3D_CLK, "g3d_clk", "clk_1000_1200", CLK_SET_RATE_PARENT, 0xcc4, 8, 4, 15, },
|
||||
{ HI6220_CODEC_VPU_SRC, "codec_vpu_src", "codec_vpu_gate", CLK_SET_RATE_PARENT, 0xcc4, 24, 6, 31, },
|
||||
};
|
||||
|
||||
static void __init hi6220_clk_media_init(struct device_node *np)
|
||||
{
|
||||
struct hisi_clock_data *clk_data;
|
||||
|
||||
clk_data = hisi_clk_init(np, HI6220_MEDIA_NR_CLKS);
|
||||
if (!clk_data)
|
||||
return;
|
||||
|
||||
hisi_clk_register_gate_sep(hi6220_separated_gate_clks_media,
|
||||
ARRAY_SIZE(hi6220_separated_gate_clks_media), clk_data);
|
||||
|
||||
hisi_clk_register_mux(hi6220_mux_clks_media,
|
||||
ARRAY_SIZE(hi6220_mux_clks_media), clk_data);
|
||||
|
||||
hi6220_clk_register_divider(hi6220_div_clks_media,
|
||||
ARRAY_SIZE(hi6220_div_clks_media), clk_data);
|
||||
}
|
||||
CLK_OF_DECLARE(hi6220_clk_media, "hisilicon,hi6220-mediactrl", hi6220_clk_media_init);
|
||||
|
||||
|
||||
/* clocks in pmctrl */
|
||||
static struct hisi_gate_clock hi6220_gate_clks_power[] __initdata = {
|
||||
{ HI6220_PLL_GPU_GATE, "pll_gpu_gate", "gpupll", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x8, 0, 0, },
|
||||
{ HI6220_PLL1_DDR_GATE, "pll1_ddr_gate", "ddrpll1", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x10, 0, 0, },
|
||||
{ HI6220_PLL_DDR_GATE, "pll_ddr_gate", "ddrpll0", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x18, 0, 0, },
|
||||
{ HI6220_PLL_MEDIA_GATE, "pll_media_gate", "media_pll", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x38, 0, 0, },
|
||||
{ HI6220_PLL0_BBP_GATE, "pll0_bbp_gate", "bbppll0", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x48, 0, 0, },
|
||||
};
|
||||
|
||||
static struct hi6220_divider_clock hi6220_div_clks_power[] __initdata = {
|
||||
{ HI6220_DDRC_SRC, "ddrc_src", "ddr_sel_src", CLK_SET_RATE_PARENT, 0x5a8, 0, 4, 0, },
|
||||
{ HI6220_DDRC_AXI1, "ddrc_axi1", "ddrc_src", CLK_SET_RATE_PARENT, 0x5a8, 8, 2, 0, },
|
||||
};
|
||||
|
||||
static void __init hi6220_clk_power_init(struct device_node *np)
|
||||
{
|
||||
struct hisi_clock_data *clk_data;
|
||||
|
||||
clk_data = hisi_clk_init(np, HI6220_POWER_NR_CLKS);
|
||||
if (!clk_data)
|
||||
return;
|
||||
|
||||
hisi_clk_register_gate(hi6220_gate_clks_power,
|
||||
ARRAY_SIZE(hi6220_gate_clks_power), clk_data);
|
||||
|
||||
hi6220_clk_register_divider(hi6220_div_clks_power,
|
||||
ARRAY_SIZE(hi6220_div_clks_power), clk_data);
|
||||
}
|
||||
CLK_OF_DECLARE(hi6220_clk_power, "hisilicon,hi6220-pmctrl", hi6220_clk_power_init);
|
||||
|
|
@ -46,15 +46,15 @@ static struct hisi_fixed_rate_clock hix5hd2_fixed_rate_clks[] __initdata = {
|
|||
{ HIX5HD2_FIXED_83M, "83m", NULL, CLK_IS_ROOT, 83333333, },
|
||||
};
|
||||
|
||||
static const char *sfc_mux_p[] __initdata = {
|
||||
static const char *const sfc_mux_p[] __initconst = {
|
||||
"24m", "150m", "200m", "100m", "75m", };
|
||||
static u32 sfc_mux_table[] = {0, 4, 5, 6, 7};
|
||||
|
||||
static const char *sdio_mux_p[] __initdata = {
|
||||
static const char *const sdio_mux_p[] __initconst = {
|
||||
"75m", "100m", "50m", "15m", };
|
||||
static u32 sdio_mux_table[] = {0, 1, 2, 3};
|
||||
|
||||
static const char *fephy_mux_p[] __initdata = { "25m", "125m"};
|
||||
static const char *const fephy_mux_p[] __initconst = { "25m", "125m"};
|
||||
static u32 fephy_mux_table[] = {0, 1};
|
||||
|
||||
|
||||
|
|
@ -252,8 +252,9 @@ static struct clk_ops clk_complex_ops = {
|
|||
.disable = clk_complex_disable,
|
||||
};
|
||||
|
||||
void __init hix5hd2_clk_register_complex(struct hix5hd2_complex_clock *clks,
|
||||
int nums, struct hisi_clock_data *data)
|
||||
static void __init
|
||||
hix5hd2_clk_register_complex(struct hix5hd2_complex_clock *clks, int nums,
|
||||
struct hisi_clock_data *data)
|
||||
{
|
||||
void __iomem *base = data->base;
|
||||
int i;
|
||||
|
|
|
|||
|
|
@ -232,3 +232,32 @@ void __init hisi_clk_register_gate_sep(struct hisi_gate_clock *clks,
|
|||
data->clk_data.clks[clks[i].id] = clk;
|
||||
}
|
||||
}
|
||||
|
||||
void __init hi6220_clk_register_divider(struct hi6220_divider_clock *clks,
|
||||
int nums, struct hisi_clock_data *data)
|
||||
{
|
||||
struct clk *clk;
|
||||
void __iomem *base = data->base;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nums; i++) {
|
||||
clk = hi6220_register_clkdiv(NULL, clks[i].name,
|
||||
clks[i].parent_name,
|
||||
clks[i].flags,
|
||||
base + clks[i].offset,
|
||||
clks[i].shift,
|
||||
clks[i].width,
|
||||
clks[i].mask_bit,
|
||||
&hisi_clk_lock);
|
||||
if (IS_ERR(clk)) {
|
||||
pr_err("%s: failed to register clock %s\n",
|
||||
__func__, clks[i].name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (clks[i].alias)
|
||||
clk_register_clkdev(clk, clks[i].alias, NULL);
|
||||
|
||||
data->clk_data.clks[clks[i].id] = clk;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ struct hisi_fixed_factor_clock {
|
|||
struct hisi_mux_clock {
|
||||
unsigned int id;
|
||||
const char *name;
|
||||
const char **parent_names;
|
||||
const char *const *parent_names;
|
||||
u8 num_parents;
|
||||
unsigned long flags;
|
||||
unsigned long offset;
|
||||
|
|
@ -79,6 +79,18 @@ struct hisi_divider_clock {
|
|||
const char *alias;
|
||||
};
|
||||
|
||||
struct hi6220_divider_clock {
|
||||
unsigned int id;
|
||||
const char *name;
|
||||
const char *parent_name;
|
||||
unsigned long flags;
|
||||
unsigned long offset;
|
||||
u8 shift;
|
||||
u8 width;
|
||||
u32 mask_bit;
|
||||
const char *alias;
|
||||
};
|
||||
|
||||
struct hisi_gate_clock {
|
||||
unsigned int id;
|
||||
const char *name;
|
||||
|
|
@ -94,18 +106,23 @@ struct clk *hisi_register_clkgate_sep(struct device *, const char *,
|
|||
const char *, unsigned long,
|
||||
void __iomem *, u8,
|
||||
u8, spinlock_t *);
|
||||
struct clk *hi6220_register_clkdiv(struct device *dev, const char *name,
|
||||
const char *parent_name, unsigned long flags, void __iomem *reg,
|
||||
u8 shift, u8 width, u32 mask_bit, spinlock_t *lock);
|
||||
|
||||
struct hisi_clock_data __init *hisi_clk_init(struct device_node *, int);
|
||||
void __init hisi_clk_register_fixed_rate(struct hisi_fixed_rate_clock *,
|
||||
int, struct hisi_clock_data *);
|
||||
void __init hisi_clk_register_fixed_factor(struct hisi_fixed_factor_clock *,
|
||||
int, struct hisi_clock_data *);
|
||||
void __init hisi_clk_register_mux(struct hisi_mux_clock *, int,
|
||||
struct hisi_clock_data *);
|
||||
void __init hisi_clk_register_divider(struct hisi_divider_clock *,
|
||||
struct hisi_clock_data *hisi_clk_init(struct device_node *, int);
|
||||
void hisi_clk_register_fixed_rate(struct hisi_fixed_rate_clock *,
|
||||
int, struct hisi_clock_data *);
|
||||
void hisi_clk_register_fixed_factor(struct hisi_fixed_factor_clock *,
|
||||
int, struct hisi_clock_data *);
|
||||
void hisi_clk_register_mux(struct hisi_mux_clock *, int,
|
||||
struct hisi_clock_data *);
|
||||
void hisi_clk_register_divider(struct hisi_divider_clock *,
|
||||
int, struct hisi_clock_data *);
|
||||
void hisi_clk_register_gate(struct hisi_gate_clock *,
|
||||
int, struct hisi_clock_data *);
|
||||
void hisi_clk_register_gate_sep(struct hisi_gate_clock *,
|
||||
int, struct hisi_clock_data *);
|
||||
void hi6220_clk_register_divider(struct hi6220_divider_clock *,
|
||||
int, struct hisi_clock_data *);
|
||||
void __init hisi_clk_register_gate(struct hisi_gate_clock *,
|
||||
int, struct hisi_clock_data *);
|
||||
void __init hisi_clk_register_gate_sep(struct hisi_gate_clock *,
|
||||
int, struct hisi_clock_data *);
|
||||
#endif /* __HISI_CLK_H */
|
||||
|
|
|
|||
156
drivers/clk/hisilicon/clkdivider-hi6220.c
Normal file
156
drivers/clk/hisilicon/clkdivider-hi6220.c
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* Hisilicon hi6220 SoC divider clock driver
|
||||
*
|
||||
* Copyright (c) 2015 Hisilicon Limited.
|
||||
*
|
||||
* Author: Bintian Wang <bintian.wang@huawei.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#define div_mask(width) ((1 << (width)) - 1)
|
||||
|
||||
/**
|
||||
* struct hi6220_clk_divider - divider clock for hi6220
|
||||
*
|
||||
* @hw: handle between common and hardware-specific interfaces
|
||||
* @reg: register containing divider
|
||||
* @shift: shift to the divider bit field
|
||||
* @width: width of the divider bit field
|
||||
* @mask: mask for setting divider rate
|
||||
* @table: the div table that the divider supports
|
||||
* @lock: register lock
|
||||
*/
|
||||
struct hi6220_clk_divider {
|
||||
struct clk_hw hw;
|
||||
void __iomem *reg;
|
||||
u8 shift;
|
||||
u8 width;
|
||||
u32 mask;
|
||||
const struct clk_div_table *table;
|
||||
spinlock_t *lock;
|
||||
};
|
||||
|
||||
#define to_hi6220_clk_divider(_hw) \
|
||||
container_of(_hw, struct hi6220_clk_divider, hw)
|
||||
|
||||
static unsigned long hi6220_clkdiv_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
unsigned int val;
|
||||
struct hi6220_clk_divider *dclk = to_hi6220_clk_divider(hw);
|
||||
|
||||
val = readl_relaxed(dclk->reg) >> dclk->shift;
|
||||
val &= div_mask(dclk->width);
|
||||
|
||||
return divider_recalc_rate(hw, parent_rate, val, dclk->table,
|
||||
CLK_DIVIDER_ROUND_CLOSEST);
|
||||
}
|
||||
|
||||
static long hi6220_clkdiv_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *prate)
|
||||
{
|
||||
struct hi6220_clk_divider *dclk = to_hi6220_clk_divider(hw);
|
||||
|
||||
return divider_round_rate(hw, rate, prate, dclk->table,
|
||||
dclk->width, CLK_DIVIDER_ROUND_CLOSEST);
|
||||
}
|
||||
|
||||
static int hi6220_clkdiv_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
int value;
|
||||
unsigned long flags = 0;
|
||||
u32 data;
|
||||
struct hi6220_clk_divider *dclk = to_hi6220_clk_divider(hw);
|
||||
|
||||
value = divider_get_val(rate, parent_rate, dclk->table,
|
||||
dclk->width, CLK_DIVIDER_ROUND_CLOSEST);
|
||||
|
||||
if (dclk->lock)
|
||||
spin_lock_irqsave(dclk->lock, flags);
|
||||
|
||||
data = readl_relaxed(dclk->reg);
|
||||
data &= ~(div_mask(dclk->width) << dclk->shift);
|
||||
data |= value << dclk->shift;
|
||||
data |= dclk->mask;
|
||||
|
||||
writel_relaxed(data, dclk->reg);
|
||||
|
||||
if (dclk->lock)
|
||||
spin_unlock_irqrestore(dclk->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct clk_ops hi6220_clkdiv_ops = {
|
||||
.recalc_rate = hi6220_clkdiv_recalc_rate,
|
||||
.round_rate = hi6220_clkdiv_round_rate,
|
||||
.set_rate = hi6220_clkdiv_set_rate,
|
||||
};
|
||||
|
||||
struct clk *hi6220_register_clkdiv(struct device *dev, const char *name,
|
||||
const char *parent_name, unsigned long flags, void __iomem *reg,
|
||||
u8 shift, u8 width, u32 mask_bit, spinlock_t *lock)
|
||||
{
|
||||
struct hi6220_clk_divider *div;
|
||||
struct clk *clk;
|
||||
struct clk_init_data init;
|
||||
struct clk_div_table *table;
|
||||
u32 max_div, min_div;
|
||||
int i;
|
||||
|
||||
/* allocate the divider */
|
||||
div = kzalloc(sizeof(*div), GFP_KERNEL);
|
||||
if (!div)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
/* Init the divider table */
|
||||
max_div = div_mask(width) + 1;
|
||||
min_div = 1;
|
||||
|
||||
table = kcalloc(max_div + 1, sizeof(*table), GFP_KERNEL);
|
||||
if (!table) {
|
||||
kfree(div);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
for (i = 0; i < max_div; i++) {
|
||||
table[i].div = min_div + i;
|
||||
table[i].val = table[i].div - 1;
|
||||
}
|
||||
|
||||
init.name = name;
|
||||
init.ops = &hi6220_clkdiv_ops;
|
||||
init.flags = flags;
|
||||
init.parent_names = parent_name ? &parent_name : NULL;
|
||||
init.num_parents = parent_name ? 1 : 0;
|
||||
|
||||
/* struct hi6220_clk_divider assignments */
|
||||
div->reg = reg;
|
||||
div->shift = shift;
|
||||
div->width = width;
|
||||
div->mask = mask_bit ? BIT(mask_bit) : 0;
|
||||
div->lock = lock;
|
||||
div->hw.init = &init;
|
||||
div->table = table;
|
||||
|
||||
/* register the clock */
|
||||
clk = clk_register(dev, &div->hw);
|
||||
if (IS_ERR(clk)) {
|
||||
kfree(table);
|
||||
kfree(div);
|
||||
}
|
||||
|
||||
return clk;
|
||||
}
|
||||
|
|
@ -37,7 +37,8 @@
|
|||
* Main PLL or any other PLLs in the device such as ARM PLL, DDR PLL
|
||||
* or PA PLL available on keystone2. These PLLs are controlled by
|
||||
* this register. Main PLL is controlled by a PLL controller.
|
||||
* @pllm: PLL register map address
|
||||
* @pllm: PLL register map address for multiplier bits
|
||||
* @pllod: PLL register map address for post divider bits
|
||||
* @pll_ctl0: PLL controller map address
|
||||
* @pllm_lower_mask: multiplier lower mask
|
||||
* @pllm_upper_mask: multiplier upper mask
|
||||
|
|
@ -53,6 +54,7 @@ struct clk_pll_data {
|
|||
u32 phy_pllm;
|
||||
u32 phy_pll_ctl0;
|
||||
void __iomem *pllm;
|
||||
void __iomem *pllod;
|
||||
void __iomem *pll_ctl0;
|
||||
u32 pllm_lower_mask;
|
||||
u32 pllm_upper_mask;
|
||||
|
|
@ -102,7 +104,11 @@ static unsigned long clk_pllclk_recalc(struct clk_hw *hw,
|
|||
/* read post divider from od bits*/
|
||||
postdiv = ((val & pll_data->clkod_mask) >>
|
||||
pll_data->clkod_shift) + 1;
|
||||
else
|
||||
else if (pll_data->pllod) {
|
||||
postdiv = readl(pll_data->pllod);
|
||||
postdiv = ((postdiv & pll_data->clkod_mask) >>
|
||||
pll_data->clkod_shift) + 1;
|
||||
} else
|
||||
postdiv = pll_data->postdiv;
|
||||
|
||||
rate /= (prediv + 1);
|
||||
|
|
@ -172,12 +178,21 @@ static void __init _of_pll_clk_init(struct device_node *node, bool pllctrl)
|
|||
/* assume the PLL has output divider register bits */
|
||||
pll_data->clkod_mask = CLKOD_MASK;
|
||||
pll_data->clkod_shift = CLKOD_SHIFT;
|
||||
|
||||
/*
|
||||
* Check if there is an post-divider register. If not
|
||||
* assume od bits are part of control register.
|
||||
*/
|
||||
i = of_property_match_string(node, "reg-names",
|
||||
"post-divider");
|
||||
pll_data->pllod = of_iomap(node, i);
|
||||
}
|
||||
|
||||
i = of_property_match_string(node, "reg-names", "control");
|
||||
pll_data->pll_ctl0 = of_iomap(node, i);
|
||||
if (!pll_data->pll_ctl0) {
|
||||
pr_err("%s: ioremap failed\n", __func__);
|
||||
iounmap(pll_data->pllod);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
|
@ -193,6 +208,7 @@ static void __init _of_pll_clk_init(struct device_node *node, bool pllctrl)
|
|||
pll_data->pllm = of_iomap(node, i);
|
||||
if (!pll_data->pllm) {
|
||||
iounmap(pll_data->pll_ctl0);
|
||||
iounmap(pll_data->pllod);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
4
drivers/clk/mediatek/Makefile
Normal file
4
drivers/clk/mediatek/Makefile
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
obj-y += clk-mtk.o clk-pll.o clk-gate.o
|
||||
obj-$(CONFIG_RESET_CONTROLLER) += reset.o
|
||||
obj-y += clk-mt8135.o
|
||||
obj-y += clk-mt8173.o
|
||||
137
drivers/clk/mediatek/clk-gate.c
Normal file
137
drivers/clk/mediatek/clk-gate.c
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* Copyright (c) 2014 MediaTek Inc.
|
||||
* Author: James Liao <jamesjj.liao@mediatek.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/clkdev.h>
|
||||
|
||||
#include "clk-mtk.h"
|
||||
#include "clk-gate.h"
|
||||
|
||||
static int mtk_cg_bit_is_cleared(struct clk_hw *hw)
|
||||
{
|
||||
struct mtk_clk_gate *cg = to_clk_gate(hw);
|
||||
u32 val;
|
||||
|
||||
regmap_read(cg->regmap, cg->sta_ofs, &val);
|
||||
|
||||
val &= BIT(cg->bit);
|
||||
|
||||
return val == 0;
|
||||
}
|
||||
|
||||
static int mtk_cg_bit_is_set(struct clk_hw *hw)
|
||||
{
|
||||
struct mtk_clk_gate *cg = to_clk_gate(hw);
|
||||
u32 val;
|
||||
|
||||
regmap_read(cg->regmap, cg->sta_ofs, &val);
|
||||
|
||||
val &= BIT(cg->bit);
|
||||
|
||||
return val != 0;
|
||||
}
|
||||
|
||||
static void mtk_cg_set_bit(struct clk_hw *hw)
|
||||
{
|
||||
struct mtk_clk_gate *cg = to_clk_gate(hw);
|
||||
|
||||
regmap_write(cg->regmap, cg->set_ofs, BIT(cg->bit));
|
||||
}
|
||||
|
||||
static void mtk_cg_clr_bit(struct clk_hw *hw)
|
||||
{
|
||||
struct mtk_clk_gate *cg = to_clk_gate(hw);
|
||||
|
||||
regmap_write(cg->regmap, cg->clr_ofs, BIT(cg->bit));
|
||||
}
|
||||
|
||||
static int mtk_cg_enable(struct clk_hw *hw)
|
||||
{
|
||||
mtk_cg_clr_bit(hw);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mtk_cg_disable(struct clk_hw *hw)
|
||||
{
|
||||
mtk_cg_set_bit(hw);
|
||||
}
|
||||
|
||||
static int mtk_cg_enable_inv(struct clk_hw *hw)
|
||||
{
|
||||
mtk_cg_set_bit(hw);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mtk_cg_disable_inv(struct clk_hw *hw)
|
||||
{
|
||||
mtk_cg_clr_bit(hw);
|
||||
}
|
||||
|
||||
const struct clk_ops mtk_clk_gate_ops_setclr = {
|
||||
.is_enabled = mtk_cg_bit_is_cleared,
|
||||
.enable = mtk_cg_enable,
|
||||
.disable = mtk_cg_disable,
|
||||
};
|
||||
|
||||
const struct clk_ops mtk_clk_gate_ops_setclr_inv = {
|
||||
.is_enabled = mtk_cg_bit_is_set,
|
||||
.enable = mtk_cg_enable_inv,
|
||||
.disable = mtk_cg_disable_inv,
|
||||
};
|
||||
|
||||
struct clk *mtk_clk_register_gate(
|
||||
const char *name,
|
||||
const char *parent_name,
|
||||
struct regmap *regmap,
|
||||
int set_ofs,
|
||||
int clr_ofs,
|
||||
int sta_ofs,
|
||||
u8 bit,
|
||||
const struct clk_ops *ops)
|
||||
{
|
||||
struct mtk_clk_gate *cg;
|
||||
struct clk *clk;
|
||||
struct clk_init_data init = {};
|
||||
|
||||
cg = kzalloc(sizeof(*cg), GFP_KERNEL);
|
||||
if (!cg)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init.name = name;
|
||||
init.flags = CLK_SET_RATE_PARENT;
|
||||
init.parent_names = parent_name ? &parent_name : NULL;
|
||||
init.num_parents = parent_name ? 1 : 0;
|
||||
init.ops = ops;
|
||||
|
||||
cg->regmap = regmap;
|
||||
cg->set_ofs = set_ofs;
|
||||
cg->clr_ofs = clr_ofs;
|
||||
cg->sta_ofs = sta_ofs;
|
||||
cg->bit = bit;
|
||||
|
||||
cg->hw.init = &init;
|
||||
|
||||
clk = clk_register(NULL, &cg->hw);
|
||||
if (IS_ERR(clk))
|
||||
kfree(cg);
|
||||
|
||||
return clk;
|
||||
}
|
||||
49
drivers/clk/mediatek/clk-gate.h
Normal file
49
drivers/clk/mediatek/clk-gate.h
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright (c) 2014 MediaTek Inc.
|
||||
* Author: James Liao <jamesjj.liao@mediatek.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef __DRV_CLK_GATE_H
|
||||
#define __DRV_CLK_GATE_H
|
||||
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
|
||||
struct mtk_clk_gate {
|
||||
struct clk_hw hw;
|
||||
struct regmap *regmap;
|
||||
int set_ofs;
|
||||
int clr_ofs;
|
||||
int sta_ofs;
|
||||
u8 bit;
|
||||
};
|
||||
|
||||
static inline struct mtk_clk_gate *to_clk_gate(struct clk_hw *hw)
|
||||
{
|
||||
return container_of(hw, struct mtk_clk_gate, hw);
|
||||
}
|
||||
|
||||
extern const struct clk_ops mtk_clk_gate_ops_setclr;
|
||||
extern const struct clk_ops mtk_clk_gate_ops_setclr_inv;
|
||||
|
||||
struct clk *mtk_clk_register_gate(
|
||||
const char *name,
|
||||
const char *parent_name,
|
||||
struct regmap *regmap,
|
||||
int set_ofs,
|
||||
int clr_ofs,
|
||||
int sta_ofs,
|
||||
u8 bit,
|
||||
const struct clk_ops *ops);
|
||||
|
||||
#endif /* __DRV_CLK_GATE_H */
|
||||
644
drivers/clk/mediatek/clk-mt8135.c
Normal file
644
drivers/clk/mediatek/clk-mt8135.c
Normal file
|
|
@ -0,0 +1,644 @@
|
|||
/*
|
||||
* Copyright (c) 2014 MediaTek Inc.
|
||||
* Author: James Liao <jamesjj.liao@mediatek.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <dt-bindings/clock/mt8135-clk.h>
|
||||
|
||||
#include "clk-mtk.h"
|
||||
#include "clk-gate.h"
|
||||
|
||||
static DEFINE_SPINLOCK(mt8135_clk_lock);
|
||||
|
||||
static const struct mtk_fixed_factor root_clk_alias[] __initconst = {
|
||||
FACTOR(CLK_TOP_DSI0_LNTC_DSICLK, "dsi0_lntc_dsiclk", "clk_null", 1, 1),
|
||||
FACTOR(CLK_TOP_HDMITX_CLKDIG_CTS, "hdmitx_clkdig_cts", "clk_null", 1, 1),
|
||||
FACTOR(CLK_TOP_CLKPH_MCK, "clkph_mck", "clk_null", 1, 1),
|
||||
FACTOR(CLK_TOP_CPUM_TCK_IN, "cpum_tck_in", "clk_null", 1, 1),
|
||||
};
|
||||
|
||||
static const struct mtk_fixed_factor top_divs[] __initconst = {
|
||||
FACTOR(CLK_TOP_MAINPLL_806M, "mainpll_806m", "mainpll", 1, 2),
|
||||
FACTOR(CLK_TOP_MAINPLL_537P3M, "mainpll_537p3m", "mainpll", 1, 3),
|
||||
FACTOR(CLK_TOP_MAINPLL_322P4M, "mainpll_322p4m", "mainpll", 1, 5),
|
||||
FACTOR(CLK_TOP_MAINPLL_230P3M, "mainpll_230p3m", "mainpll", 1, 7),
|
||||
|
||||
FACTOR(CLK_TOP_UNIVPLL_624M, "univpll_624m", "univpll", 1, 2),
|
||||
FACTOR(CLK_TOP_UNIVPLL_416M, "univpll_416m", "univpll", 1, 3),
|
||||
FACTOR(CLK_TOP_UNIVPLL_249P6M, "univpll_249p6m", "univpll", 1, 5),
|
||||
FACTOR(CLK_TOP_UNIVPLL_178P3M, "univpll_178p3m", "univpll", 1, 7),
|
||||
FACTOR(CLK_TOP_UNIVPLL_48M, "univpll_48m", "univpll", 1, 26),
|
||||
|
||||
FACTOR(CLK_TOP_MMPLL_D2, "mmpll_d2", "mmpll", 1, 2),
|
||||
FACTOR(CLK_TOP_MMPLL_D3, "mmpll_d3", "mmpll", 1, 3),
|
||||
FACTOR(CLK_TOP_MMPLL_D5, "mmpll_d5", "mmpll", 1, 5),
|
||||
FACTOR(CLK_TOP_MMPLL_D7, "mmpll_d7", "mmpll", 1, 7),
|
||||
FACTOR(CLK_TOP_MMPLL_D4, "mmpll_d4", "mmpll_d2", 1, 2),
|
||||
FACTOR(CLK_TOP_MMPLL_D6, "mmpll_d6", "mmpll_d3", 1, 2),
|
||||
|
||||
FACTOR(CLK_TOP_SYSPLL_D2, "syspll_d2", "mainpll_806m", 1, 1),
|
||||
FACTOR(CLK_TOP_SYSPLL_D4, "syspll_d4", "mainpll_806m", 1, 2),
|
||||
FACTOR(CLK_TOP_SYSPLL_D6, "syspll_d6", "mainpll_806m", 1, 3),
|
||||
FACTOR(CLK_TOP_SYSPLL_D8, "syspll_d8", "mainpll_806m", 1, 4),
|
||||
FACTOR(CLK_TOP_SYSPLL_D10, "syspll_d10", "mainpll_806m", 1, 5),
|
||||
FACTOR(CLK_TOP_SYSPLL_D12, "syspll_d12", "mainpll_806m", 1, 6),
|
||||
FACTOR(CLK_TOP_SYSPLL_D16, "syspll_d16", "mainpll_806m", 1, 8),
|
||||
FACTOR(CLK_TOP_SYSPLL_D24, "syspll_d24", "mainpll_806m", 1, 12),
|
||||
|
||||
FACTOR(CLK_TOP_SYSPLL_D3, "syspll_d3", "mainpll_537p3m", 1, 1),
|
||||
|
||||
FACTOR(CLK_TOP_SYSPLL_D2P5, "syspll_d2p5", "mainpll_322p4m", 2, 1),
|
||||
FACTOR(CLK_TOP_SYSPLL_D5, "syspll_d5", "mainpll_322p4m", 1, 1),
|
||||
|
||||
FACTOR(CLK_TOP_SYSPLL_D3P5, "syspll_d3p5", "mainpll_230p3m", 2, 1),
|
||||
|
||||
FACTOR(CLK_TOP_UNIVPLL1_D2, "univpll1_d2", "univpll_624m", 1, 2),
|
||||
FACTOR(CLK_TOP_UNIVPLL1_D4, "univpll1_d4", "univpll_624m", 1, 4),
|
||||
FACTOR(CLK_TOP_UNIVPLL1_D6, "univpll1_d6", "univpll_624m", 1, 6),
|
||||
FACTOR(CLK_TOP_UNIVPLL1_D8, "univpll1_d8", "univpll_624m", 1, 8),
|
||||
FACTOR(CLK_TOP_UNIVPLL1_D10, "univpll1_d10", "univpll_624m", 1, 10),
|
||||
|
||||
FACTOR(CLK_TOP_UNIVPLL2_D2, "univpll2_d2", "univpll_416m", 1, 2),
|
||||
FACTOR(CLK_TOP_UNIVPLL2_D4, "univpll2_d4", "univpll_416m", 1, 4),
|
||||
FACTOR(CLK_TOP_UNIVPLL2_D6, "univpll2_d6", "univpll_416m", 1, 6),
|
||||
FACTOR(CLK_TOP_UNIVPLL2_D8, "univpll2_d8", "univpll_416m", 1, 8),
|
||||
|
||||
FACTOR(CLK_TOP_UNIVPLL_D3, "univpll_d3", "univpll_416m", 1, 1),
|
||||
FACTOR(CLK_TOP_UNIVPLL_D5, "univpll_d5", "univpll_249p6m", 1, 1),
|
||||
FACTOR(CLK_TOP_UNIVPLL_D7, "univpll_d7", "univpll_178p3m", 1, 1),
|
||||
FACTOR(CLK_TOP_UNIVPLL_D10, "univpll_d10", "univpll_249p6m", 1, 2),
|
||||
FACTOR(CLK_TOP_UNIVPLL_D26, "univpll_d26", "univpll_48m", 1, 1),
|
||||
|
||||
FACTOR(CLK_TOP_APLL, "apll_ck", "audpll", 1, 1),
|
||||
FACTOR(CLK_TOP_APLL_D4, "apll_d4", "audpll", 1, 4),
|
||||
FACTOR(CLK_TOP_APLL_D8, "apll_d8", "audpll", 1, 8),
|
||||
FACTOR(CLK_TOP_APLL_D16, "apll_d16", "audpll", 1, 16),
|
||||
FACTOR(CLK_TOP_APLL_D24, "apll_d24", "audpll", 1, 24),
|
||||
|
||||
FACTOR(CLK_TOP_LVDSPLL_D2, "lvdspll_d2", "lvdspll", 1, 2),
|
||||
FACTOR(CLK_TOP_LVDSPLL_D4, "lvdspll_d4", "lvdspll", 1, 4),
|
||||
FACTOR(CLK_TOP_LVDSPLL_D8, "lvdspll_d8", "lvdspll", 1, 8),
|
||||
|
||||
FACTOR(CLK_TOP_LVDSTX_CLKDIG_CT, "lvdstx_clkdig_cts", "lvdspll", 1, 1),
|
||||
FACTOR(CLK_TOP_VPLL_DPIX, "vpll_dpix_ck", "lvdspll", 1, 1),
|
||||
|
||||
FACTOR(CLK_TOP_TVHDMI_H, "tvhdmi_h_ck", "tvdpll", 1, 1),
|
||||
|
||||
FACTOR(CLK_TOP_HDMITX_CLKDIG_D2, "hdmitx_clkdig_d2", "hdmitx_clkdig_cts", 1, 2),
|
||||
FACTOR(CLK_TOP_HDMITX_CLKDIG_D3, "hdmitx_clkdig_d3", "hdmitx_clkdig_cts", 1, 3),
|
||||
|
||||
FACTOR(CLK_TOP_TVHDMI_D2, "tvhdmi_d2", "tvhdmi_h_ck", 1, 2),
|
||||
FACTOR(CLK_TOP_TVHDMI_D4, "tvhdmi_d4", "tvhdmi_h_ck", 1, 4),
|
||||
|
||||
FACTOR(CLK_TOP_MEMPLL_MCK_D4, "mempll_mck_d4", "clkph_mck", 1, 4),
|
||||
};
|
||||
|
||||
static const char * const axi_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"syspll_d3",
|
||||
"syspll_d4",
|
||||
"syspll_d6",
|
||||
"univpll_d5",
|
||||
"univpll2_d2",
|
||||
"syspll_d3p5"
|
||||
};
|
||||
|
||||
static const char * const smi_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"clkph_mck",
|
||||
"syspll_d2p5",
|
||||
"syspll_d3",
|
||||
"syspll_d8",
|
||||
"univpll_d5",
|
||||
"univpll1_d2",
|
||||
"univpll1_d6",
|
||||
"mmpll_d3",
|
||||
"mmpll_d4",
|
||||
"mmpll_d5",
|
||||
"mmpll_d6",
|
||||
"mmpll_d7",
|
||||
"vdecpll",
|
||||
"lvdspll"
|
||||
};
|
||||
|
||||
static const char * const mfg_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"univpll1_d4",
|
||||
"syspll_d2",
|
||||
"syspll_d2p5",
|
||||
"syspll_d3",
|
||||
"univpll_d5",
|
||||
"univpll1_d2",
|
||||
"mmpll_d2",
|
||||
"mmpll_d3",
|
||||
"mmpll_d4",
|
||||
"mmpll_d5",
|
||||
"mmpll_d6",
|
||||
"mmpll_d7"
|
||||
};
|
||||
|
||||
static const char * const irda_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"univpll2_d8",
|
||||
"univpll1_d6"
|
||||
};
|
||||
|
||||
static const char * const cam_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"syspll_d3",
|
||||
"syspll_d3p5",
|
||||
"syspll_d4",
|
||||
"univpll_d5",
|
||||
"univpll2_d2",
|
||||
"univpll_d7",
|
||||
"univpll1_d4"
|
||||
};
|
||||
|
||||
static const char * const aud_intbus_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"syspll_d6",
|
||||
"univpll_d10"
|
||||
};
|
||||
|
||||
static const char * const jpg_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"syspll_d5",
|
||||
"syspll_d4",
|
||||
"syspll_d3",
|
||||
"univpll_d7",
|
||||
"univpll2_d2",
|
||||
"univpll_d5"
|
||||
};
|
||||
|
||||
static const char * const disp_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"syspll_d3p5",
|
||||
"syspll_d3",
|
||||
"univpll2_d2",
|
||||
"univpll_d5",
|
||||
"univpll1_d2",
|
||||
"lvdspll",
|
||||
"vdecpll"
|
||||
};
|
||||
|
||||
static const char * const msdc30_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"syspll_d6",
|
||||
"syspll_d5",
|
||||
"univpll1_d4",
|
||||
"univpll2_d4",
|
||||
"msdcpll"
|
||||
};
|
||||
|
||||
static const char * const usb20_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"univpll2_d6",
|
||||
"univpll1_d10"
|
||||
};
|
||||
|
||||
static const char * const venc_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"syspll_d3",
|
||||
"syspll_d8",
|
||||
"univpll_d5",
|
||||
"univpll1_d6",
|
||||
"mmpll_d4",
|
||||
"mmpll_d5",
|
||||
"mmpll_d6"
|
||||
};
|
||||
|
||||
static const char * const spi_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"syspll_d6",
|
||||
"syspll_d8",
|
||||
"syspll_d10",
|
||||
"univpll1_d6",
|
||||
"univpll1_d8"
|
||||
};
|
||||
|
||||
static const char * const uart_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"univpll2_d8"
|
||||
};
|
||||
|
||||
static const char * const mem_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"clkph_mck"
|
||||
};
|
||||
|
||||
static const char * const camtg_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"univpll_d26",
|
||||
"univpll1_d6",
|
||||
"syspll_d16",
|
||||
"syspll_d8"
|
||||
};
|
||||
|
||||
static const char * const audio_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"syspll_d24"
|
||||
};
|
||||
|
||||
static const char * const fix_parents[] __initconst = {
|
||||
"rtc32k",
|
||||
"clk26m",
|
||||
"univpll_d5",
|
||||
"univpll_d7",
|
||||
"univpll1_d2",
|
||||
"univpll1_d4",
|
||||
"univpll1_d6",
|
||||
"univpll1_d8"
|
||||
};
|
||||
|
||||
static const char * const vdec_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"vdecpll",
|
||||
"clkph_mck",
|
||||
"syspll_d2p5",
|
||||
"syspll_d3",
|
||||
"syspll_d3p5",
|
||||
"syspll_d4",
|
||||
"syspll_d5",
|
||||
"syspll_d6",
|
||||
"syspll_d8",
|
||||
"univpll1_d2",
|
||||
"univpll2_d2",
|
||||
"univpll_d7",
|
||||
"univpll_d10",
|
||||
"univpll2_d4",
|
||||
"lvdspll"
|
||||
};
|
||||
|
||||
static const char * const ddrphycfg_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"axi_sel",
|
||||
"syspll_d12"
|
||||
};
|
||||
|
||||
static const char * const dpilvds_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"lvdspll",
|
||||
"lvdspll_d2",
|
||||
"lvdspll_d4",
|
||||
"lvdspll_d8"
|
||||
};
|
||||
|
||||
static const char * const pmicspi_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"univpll2_d6",
|
||||
"syspll_d8",
|
||||
"syspll_d10",
|
||||
"univpll1_d10",
|
||||
"mempll_mck_d4",
|
||||
"univpll_d26",
|
||||
"syspll_d24"
|
||||
};
|
||||
|
||||
static const char * const smi_mfg_as_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"smi_sel",
|
||||
"mfg_sel",
|
||||
"mem_sel"
|
||||
};
|
||||
|
||||
static const char * const gcpu_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"syspll_d4",
|
||||
"univpll_d7",
|
||||
"syspll_d5",
|
||||
"syspll_d6"
|
||||
};
|
||||
|
||||
static const char * const dpi1_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"tvhdmi_h_ck",
|
||||
"tvhdmi_d2",
|
||||
"tvhdmi_d4"
|
||||
};
|
||||
|
||||
static const char * const cci_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"mainpll_537p3m",
|
||||
"univpll_d3",
|
||||
"syspll_d2p5",
|
||||
"syspll_d3",
|
||||
"syspll_d5"
|
||||
};
|
||||
|
||||
static const char * const apll_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"apll_ck",
|
||||
"apll_d4",
|
||||
"apll_d8",
|
||||
"apll_d16",
|
||||
"apll_d24"
|
||||
};
|
||||
|
||||
static const char * const hdmipll_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"hdmitx_clkdig_cts",
|
||||
"hdmitx_clkdig_d2",
|
||||
"hdmitx_clkdig_d3"
|
||||
};
|
||||
|
||||
static const struct mtk_composite top_muxes[] __initconst = {
|
||||
/* CLK_CFG_0 */
|
||||
MUX_GATE(CLK_TOP_AXI_SEL, "axi_sel", axi_parents,
|
||||
0x0140, 0, 3, INVALID_MUX_GATE_BIT),
|
||||
MUX_GATE(CLK_TOP_SMI_SEL, "smi_sel", smi_parents, 0x0140, 8, 4, 15),
|
||||
MUX_GATE(CLK_TOP_MFG_SEL, "mfg_sel", mfg_parents, 0x0140, 16, 4, 23),
|
||||
MUX_GATE(CLK_TOP_IRDA_SEL, "irda_sel", irda_parents, 0x0140, 24, 2, 31),
|
||||
/* CLK_CFG_1 */
|
||||
MUX_GATE(CLK_TOP_CAM_SEL, "cam_sel", cam_parents, 0x0144, 0, 3, 7),
|
||||
MUX_GATE(CLK_TOP_AUD_INTBUS_SEL, "aud_intbus_sel", aud_intbus_parents,
|
||||
0x0144, 8, 2, 15),
|
||||
MUX_GATE(CLK_TOP_JPG_SEL, "jpg_sel", jpg_parents, 0x0144, 16, 3, 23),
|
||||
MUX_GATE(CLK_TOP_DISP_SEL, "disp_sel", disp_parents, 0x0144, 24, 3, 31),
|
||||
/* CLK_CFG_2 */
|
||||
MUX_GATE(CLK_TOP_MSDC30_1_SEL, "msdc30_1_sel", msdc30_parents, 0x0148, 0, 3, 7),
|
||||
MUX_GATE(CLK_TOP_MSDC30_2_SEL, "msdc30_2_sel", msdc30_parents, 0x0148, 8, 3, 15),
|
||||
MUX_GATE(CLK_TOP_MSDC30_3_SEL, "msdc30_3_sel", msdc30_parents, 0x0148, 16, 3, 23),
|
||||
MUX_GATE(CLK_TOP_MSDC30_4_SEL, "msdc30_4_sel", msdc30_parents, 0x0148, 24, 3, 31),
|
||||
/* CLK_CFG_3 */
|
||||
MUX_GATE(CLK_TOP_USB20_SEL, "usb20_sel", usb20_parents, 0x014c, 0, 2, 7),
|
||||
/* CLK_CFG_4 */
|
||||
MUX_GATE(CLK_TOP_VENC_SEL, "venc_sel", venc_parents, 0x0150, 8, 3, 15),
|
||||
MUX_GATE(CLK_TOP_SPI_SEL, "spi_sel", spi_parents, 0x0150, 16, 3, 23),
|
||||
MUX_GATE(CLK_TOP_UART_SEL, "uart_sel", uart_parents, 0x0150, 24, 2, 31),
|
||||
/* CLK_CFG_6 */
|
||||
MUX_GATE(CLK_TOP_MEM_SEL, "mem_sel", mem_parents, 0x0158, 0, 2, 7),
|
||||
MUX_GATE(CLK_TOP_CAMTG_SEL, "camtg_sel", camtg_parents, 0x0158, 8, 3, 15),
|
||||
MUX_GATE(CLK_TOP_AUDIO_SEL, "audio_sel", audio_parents, 0x0158, 24, 2, 31),
|
||||
/* CLK_CFG_7 */
|
||||
MUX_GATE(CLK_TOP_FIX_SEL, "fix_sel", fix_parents, 0x015c, 0, 3, 7),
|
||||
MUX_GATE(CLK_TOP_VDEC_SEL, "vdec_sel", vdec_parents, 0x015c, 8, 4, 15),
|
||||
MUX_GATE(CLK_TOP_DDRPHYCFG_SEL, "ddrphycfg_sel", ddrphycfg_parents,
|
||||
0x015c, 16, 2, 23),
|
||||
MUX_GATE(CLK_TOP_DPILVDS_SEL, "dpilvds_sel", dpilvds_parents, 0x015c, 24, 3, 31),
|
||||
/* CLK_CFG_8 */
|
||||
MUX_GATE(CLK_TOP_PMICSPI_SEL, "pmicspi_sel", pmicspi_parents, 0x0164, 0, 3, 7),
|
||||
MUX_GATE(CLK_TOP_MSDC30_0_SEL, "msdc30_0_sel", msdc30_parents, 0x0164, 8, 3, 15),
|
||||
MUX_GATE(CLK_TOP_SMI_MFG_AS_SEL, "smi_mfg_as_sel", smi_mfg_as_parents,
|
||||
0x0164, 16, 2, 23),
|
||||
MUX_GATE(CLK_TOP_GCPU_SEL, "gcpu_sel", gcpu_parents, 0x0164, 24, 3, 31),
|
||||
/* CLK_CFG_9 */
|
||||
MUX_GATE(CLK_TOP_DPI1_SEL, "dpi1_sel", dpi1_parents, 0x0168, 0, 2, 7),
|
||||
MUX_GATE(CLK_TOP_CCI_SEL, "cci_sel", cci_parents, 0x0168, 8, 3, 15),
|
||||
MUX_GATE(CLK_TOP_APLL_SEL, "apll_sel", apll_parents, 0x0168, 16, 3, 23),
|
||||
MUX_GATE(CLK_TOP_HDMIPLL_SEL, "hdmipll_sel", hdmipll_parents, 0x0168, 24, 2, 31),
|
||||
};
|
||||
|
||||
static const struct mtk_gate_regs infra_cg_regs = {
|
||||
.set_ofs = 0x0040,
|
||||
.clr_ofs = 0x0044,
|
||||
.sta_ofs = 0x0048,
|
||||
};
|
||||
|
||||
#define GATE_ICG(_id, _name, _parent, _shift) { \
|
||||
.id = _id, \
|
||||
.name = _name, \
|
||||
.parent_name = _parent, \
|
||||
.regs = &infra_cg_regs, \
|
||||
.shift = _shift, \
|
||||
.ops = &mtk_clk_gate_ops_setclr, \
|
||||
}
|
||||
|
||||
static const struct mtk_gate infra_clks[] __initconst = {
|
||||
GATE_ICG(CLK_INFRA_PMIC_WRAP, "pmic_wrap_ck", "axi_sel", 23),
|
||||
GATE_ICG(CLK_INFRA_PMICSPI, "pmicspi_ck", "pmicspi_sel", 22),
|
||||
GATE_ICG(CLK_INFRA_CCIF1_AP_CTRL, "ccif1_ap_ctrl", "axi_sel", 21),
|
||||
GATE_ICG(CLK_INFRA_CCIF0_AP_CTRL, "ccif0_ap_ctrl", "axi_sel", 20),
|
||||
GATE_ICG(CLK_INFRA_KP, "kp_ck", "axi_sel", 16),
|
||||
GATE_ICG(CLK_INFRA_CPUM, "cpum_ck", "cpum_tck_in", 15),
|
||||
GATE_ICG(CLK_INFRA_M4U, "m4u_ck", "mem_sel", 8),
|
||||
GATE_ICG(CLK_INFRA_MFGAXI, "mfgaxi_ck", "axi_sel", 7),
|
||||
GATE_ICG(CLK_INFRA_DEVAPC, "devapc_ck", "axi_sel", 6),
|
||||
GATE_ICG(CLK_INFRA_AUDIO, "audio_ck", "aud_intbus_sel", 5),
|
||||
GATE_ICG(CLK_INFRA_MFG_BUS, "mfg_bus_ck", "axi_sel", 2),
|
||||
GATE_ICG(CLK_INFRA_SMI, "smi_ck", "smi_sel", 1),
|
||||
GATE_ICG(CLK_INFRA_DBGCLK, "dbgclk_ck", "axi_sel", 0),
|
||||
};
|
||||
|
||||
static const struct mtk_gate_regs peri0_cg_regs = {
|
||||
.set_ofs = 0x0008,
|
||||
.clr_ofs = 0x0010,
|
||||
.sta_ofs = 0x0018,
|
||||
};
|
||||
|
||||
static const struct mtk_gate_regs peri1_cg_regs = {
|
||||
.set_ofs = 0x000c,
|
||||
.clr_ofs = 0x0014,
|
||||
.sta_ofs = 0x001c,
|
||||
};
|
||||
|
||||
#define GATE_PERI0(_id, _name, _parent, _shift) { \
|
||||
.id = _id, \
|
||||
.name = _name, \
|
||||
.parent_name = _parent, \
|
||||
.regs = &peri0_cg_regs, \
|
||||
.shift = _shift, \
|
||||
.ops = &mtk_clk_gate_ops_setclr, \
|
||||
}
|
||||
|
||||
#define GATE_PERI1(_id, _name, _parent, _shift) { \
|
||||
.id = _id, \
|
||||
.name = _name, \
|
||||
.parent_name = _parent, \
|
||||
.regs = &peri1_cg_regs, \
|
||||
.shift = _shift, \
|
||||
.ops = &mtk_clk_gate_ops_setclr, \
|
||||
}
|
||||
|
||||
static const struct mtk_gate peri_gates[] __initconst = {
|
||||
/* PERI0 */
|
||||
GATE_PERI0(CLK_PERI_I2C5, "i2c5_ck", "axi_sel", 31),
|
||||
GATE_PERI0(CLK_PERI_I2C4, "i2c4_ck", "axi_sel", 30),
|
||||
GATE_PERI0(CLK_PERI_I2C3, "i2c3_ck", "axi_sel", 29),
|
||||
GATE_PERI0(CLK_PERI_I2C2, "i2c2_ck", "axi_sel", 28),
|
||||
GATE_PERI0(CLK_PERI_I2C1, "i2c1_ck", "axi_sel", 27),
|
||||
GATE_PERI0(CLK_PERI_I2C0, "i2c0_ck", "axi_sel", 26),
|
||||
GATE_PERI0(CLK_PERI_UART3, "uart3_ck", "axi_sel", 25),
|
||||
GATE_PERI0(CLK_PERI_UART2, "uart2_ck", "axi_sel", 24),
|
||||
GATE_PERI0(CLK_PERI_UART1, "uart1_ck", "axi_sel", 23),
|
||||
GATE_PERI0(CLK_PERI_UART0, "uart0_ck", "axi_sel", 22),
|
||||
GATE_PERI0(CLK_PERI_IRDA, "irda_ck", "irda_sel", 21),
|
||||
GATE_PERI0(CLK_PERI_NLI, "nli_ck", "axi_sel", 20),
|
||||
GATE_PERI0(CLK_PERI_MD_HIF, "md_hif_ck", "axi_sel", 19),
|
||||
GATE_PERI0(CLK_PERI_AP_HIF, "ap_hif_ck", "axi_sel", 18),
|
||||
GATE_PERI0(CLK_PERI_MSDC30_3, "msdc30_3_ck", "msdc30_4_sel", 17),
|
||||
GATE_PERI0(CLK_PERI_MSDC30_2, "msdc30_2_ck", "msdc30_3_sel", 16),
|
||||
GATE_PERI0(CLK_PERI_MSDC30_1, "msdc30_1_ck", "msdc30_2_sel", 15),
|
||||
GATE_PERI0(CLK_PERI_MSDC20_2, "msdc20_2_ck", "msdc30_1_sel", 14),
|
||||
GATE_PERI0(CLK_PERI_MSDC20_1, "msdc20_1_ck", "msdc30_0_sel", 13),
|
||||
GATE_PERI0(CLK_PERI_AP_DMA, "ap_dma_ck", "axi_sel", 12),
|
||||
GATE_PERI0(CLK_PERI_USB1, "usb1_ck", "usb20_sel", 11),
|
||||
GATE_PERI0(CLK_PERI_USB0, "usb0_ck", "usb20_sel", 10),
|
||||
GATE_PERI0(CLK_PERI_PWM, "pwm_ck", "axi_sel", 9),
|
||||
GATE_PERI0(CLK_PERI_PWM7, "pwm7_ck", "axi_sel", 8),
|
||||
GATE_PERI0(CLK_PERI_PWM6, "pwm6_ck", "axi_sel", 7),
|
||||
GATE_PERI0(CLK_PERI_PWM5, "pwm5_ck", "axi_sel", 6),
|
||||
GATE_PERI0(CLK_PERI_PWM4, "pwm4_ck", "axi_sel", 5),
|
||||
GATE_PERI0(CLK_PERI_PWM3, "pwm3_ck", "axi_sel", 4),
|
||||
GATE_PERI0(CLK_PERI_PWM2, "pwm2_ck", "axi_sel", 3),
|
||||
GATE_PERI0(CLK_PERI_PWM1, "pwm1_ck", "axi_sel", 2),
|
||||
GATE_PERI0(CLK_PERI_THERM, "therm_ck", "axi_sel", 1),
|
||||
GATE_PERI0(CLK_PERI_NFI, "nfi_ck", "axi_sel", 0),
|
||||
/* PERI1 */
|
||||
GATE_PERI1(CLK_PERI_USBSLV, "usbslv_ck", "axi_sel", 8),
|
||||
GATE_PERI1(CLK_PERI_USB1_MCU, "usb1_mcu_ck", "axi_sel", 7),
|
||||
GATE_PERI1(CLK_PERI_USB0_MCU, "usb0_mcu_ck", "axi_sel", 6),
|
||||
GATE_PERI1(CLK_PERI_GCPU, "gcpu_ck", "gcpu_sel", 5),
|
||||
GATE_PERI1(CLK_PERI_FHCTL, "fhctl_ck", "clk26m", 4),
|
||||
GATE_PERI1(CLK_PERI_SPI1, "spi1_ck", "spi_sel", 3),
|
||||
GATE_PERI1(CLK_PERI_AUXADC, "auxadc_ck", "clk26m", 2),
|
||||
GATE_PERI1(CLK_PERI_PERI_PWRAP, "peri_pwrap_ck", "axi_sel", 1),
|
||||
GATE_PERI1(CLK_PERI_I2C6, "i2c6_ck", "axi_sel", 0),
|
||||
};
|
||||
|
||||
static const char * const uart_ck_sel_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"uart_sel",
|
||||
};
|
||||
|
||||
static const struct mtk_composite peri_clks[] __initconst = {
|
||||
MUX(CLK_PERI_UART0_SEL, "uart0_ck_sel", uart_ck_sel_parents, 0x40c, 0, 1),
|
||||
MUX(CLK_PERI_UART1_SEL, "uart1_ck_sel", uart_ck_sel_parents, 0x40c, 1, 1),
|
||||
MUX(CLK_PERI_UART2_SEL, "uart2_ck_sel", uart_ck_sel_parents, 0x40c, 2, 1),
|
||||
MUX(CLK_PERI_UART3_SEL, "uart3_ck_sel", uart_ck_sel_parents, 0x40c, 3, 1),
|
||||
};
|
||||
|
||||
static void __init mtk_topckgen_init(struct device_node *node)
|
||||
{
|
||||
struct clk_onecell_data *clk_data;
|
||||
void __iomem *base;
|
||||
int r;
|
||||
|
||||
base = of_iomap(node, 0);
|
||||
if (!base) {
|
||||
pr_err("%s(): ioremap failed\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
clk_data = mtk_alloc_clk_data(CLK_TOP_NR_CLK);
|
||||
|
||||
mtk_clk_register_factors(root_clk_alias, ARRAY_SIZE(root_clk_alias), clk_data);
|
||||
mtk_clk_register_factors(top_divs, ARRAY_SIZE(top_divs), clk_data);
|
||||
mtk_clk_register_composites(top_muxes, ARRAY_SIZE(top_muxes), base,
|
||||
&mt8135_clk_lock, clk_data);
|
||||
|
||||
clk_prepare_enable(clk_data->clks[CLK_TOP_CCI_SEL]);
|
||||
|
||||
r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
|
||||
if (r)
|
||||
pr_err("%s(): could not register clock provider: %d\n",
|
||||
__func__, r);
|
||||
}
|
||||
CLK_OF_DECLARE(mtk_topckgen, "mediatek,mt8135-topckgen", mtk_topckgen_init);
|
||||
|
||||
static void __init mtk_infrasys_init(struct device_node *node)
|
||||
{
|
||||
struct clk_onecell_data *clk_data;
|
||||
int r;
|
||||
|
||||
clk_data = mtk_alloc_clk_data(CLK_INFRA_NR_CLK);
|
||||
|
||||
mtk_clk_register_gates(node, infra_clks, ARRAY_SIZE(infra_clks),
|
||||
clk_data);
|
||||
|
||||
clk_prepare_enable(clk_data->clks[CLK_INFRA_M4U]);
|
||||
|
||||
r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
|
||||
if (r)
|
||||
pr_err("%s(): could not register clock provider: %d\n",
|
||||
__func__, r);
|
||||
|
||||
mtk_register_reset_controller(node, 2, 0x30);
|
||||
}
|
||||
CLK_OF_DECLARE(mtk_infrasys, "mediatek,mt8135-infracfg", mtk_infrasys_init);
|
||||
|
||||
static void __init mtk_pericfg_init(struct device_node *node)
|
||||
{
|
||||
struct clk_onecell_data *clk_data;
|
||||
int r;
|
||||
void __iomem *base;
|
||||
|
||||
base = of_iomap(node, 0);
|
||||
if (!base) {
|
||||
pr_err("%s(): ioremap failed\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
clk_data = mtk_alloc_clk_data(CLK_PERI_NR_CLK);
|
||||
|
||||
mtk_clk_register_gates(node, peri_gates, ARRAY_SIZE(peri_gates),
|
||||
clk_data);
|
||||
mtk_clk_register_composites(peri_clks, ARRAY_SIZE(peri_clks), base,
|
||||
&mt8135_clk_lock, clk_data);
|
||||
|
||||
r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
|
||||
if (r)
|
||||
pr_err("%s(): could not register clock provider: %d\n",
|
||||
__func__, r);
|
||||
|
||||
mtk_register_reset_controller(node, 2, 0);
|
||||
}
|
||||
CLK_OF_DECLARE(mtk_pericfg, "mediatek,mt8135-pericfg", mtk_pericfg_init);
|
||||
|
||||
#define MT8135_PLL_FMAX (2000 * MHZ)
|
||||
#define CON0_MT8135_RST_BAR BIT(27)
|
||||
|
||||
#define PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, _pcw_shift) { \
|
||||
.id = _id, \
|
||||
.name = _name, \
|
||||
.reg = _reg, \
|
||||
.pwr_reg = _pwr_reg, \
|
||||
.en_mask = _en_mask, \
|
||||
.flags = _flags, \
|
||||
.rst_bar_mask = CON0_MT8135_RST_BAR, \
|
||||
.fmax = MT8135_PLL_FMAX, \
|
||||
.pcwbits = _pcwbits, \
|
||||
.pd_reg = _pd_reg, \
|
||||
.pd_shift = _pd_shift, \
|
||||
.tuner_reg = _tuner_reg, \
|
||||
.pcw_reg = _pcw_reg, \
|
||||
.pcw_shift = _pcw_shift, \
|
||||
}
|
||||
|
||||
static const struct mtk_pll_data plls[] = {
|
||||
PLL(CLK_APMIXED_ARMPLL1, "armpll1", 0x200, 0x218, 0x80000001, 0, 21, 0x204, 24, 0x0, 0x204, 0),
|
||||
PLL(CLK_APMIXED_ARMPLL2, "armpll2", 0x2cc, 0x2e4, 0x80000001, 0, 21, 0x2d0, 24, 0x0, 0x2d0, 0),
|
||||
PLL(CLK_APMIXED_MAINPLL, "mainpll", 0x21c, 0x234, 0xf0000001, HAVE_RST_BAR, 21, 0x21c, 6, 0x0, 0x220, 0),
|
||||
PLL(CLK_APMIXED_UNIVPLL, "univpll", 0x238, 0x250, 0xf3000001, HAVE_RST_BAR, 7, 0x238, 6, 0x0, 0x238, 9),
|
||||
PLL(CLK_APMIXED_MMPLL, "mmpll", 0x254, 0x26c, 0xf0000001, HAVE_RST_BAR, 21, 0x254, 6, 0x0, 0x258, 0),
|
||||
PLL(CLK_APMIXED_MSDCPLL, "msdcpll", 0x278, 0x290, 0x80000001, 0, 21, 0x278, 6, 0x0, 0x27c, 0),
|
||||
PLL(CLK_APMIXED_TVDPLL, "tvdpll", 0x294, 0x2ac, 0x80000001, 0, 31, 0x294, 6, 0x0, 0x298, 0),
|
||||
PLL(CLK_APMIXED_LVDSPLL, "lvdspll", 0x2b0, 0x2c8, 0x80000001, 0, 21, 0x2b0, 6, 0x0, 0x2b4, 0),
|
||||
PLL(CLK_APMIXED_AUDPLL, "audpll", 0x2e8, 0x300, 0x80000001, 0, 31, 0x2e8, 6, 0x2f8, 0x2ec, 0),
|
||||
PLL(CLK_APMIXED_VDECPLL, "vdecpll", 0x304, 0x31c, 0x80000001, 0, 21, 0x2b0, 6, 0x0, 0x308, 0),
|
||||
};
|
||||
|
||||
static void __init mtk_apmixedsys_init(struct device_node *node)
|
||||
{
|
||||
struct clk_onecell_data *clk_data;
|
||||
|
||||
clk_data = mtk_alloc_clk_data(CLK_APMIXED_NR_CLK);
|
||||
if (!clk_data)
|
||||
return;
|
||||
|
||||
mtk_clk_register_plls(node, plls, ARRAY_SIZE(plls), clk_data);
|
||||
}
|
||||
CLK_OF_DECLARE(mtk_apmixedsys, "mediatek,mt8135-apmixedsys",
|
||||
mtk_apmixedsys_init);
|
||||
830
drivers/clk/mediatek/clk-mt8173.c
Normal file
830
drivers/clk/mediatek/clk-mt8173.c
Normal file
|
|
@ -0,0 +1,830 @@
|
|||
/*
|
||||
* Copyright (c) 2014 MediaTek Inc.
|
||||
* Author: James Liao <jamesjj.liao@mediatek.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
|
||||
#include "clk-mtk.h"
|
||||
#include "clk-gate.h"
|
||||
|
||||
#include <dt-bindings/clock/mt8173-clk.h>
|
||||
|
||||
static DEFINE_SPINLOCK(mt8173_clk_lock);
|
||||
|
||||
static const struct mtk_fixed_factor root_clk_alias[] __initconst = {
|
||||
FACTOR(CLK_TOP_CLKPH_MCK_O, "clkph_mck_o", "clk_null", 1, 1),
|
||||
FACTOR(CLK_TOP_DPI, "dpi_ck", "clk_null", 1, 1),
|
||||
FACTOR(CLK_TOP_USB_SYSPLL_125M, "usb_syspll_125m", "clk_null", 1, 1),
|
||||
FACTOR(CLK_TOP_HDMITX_DIG_CTS, "hdmitx_dig_cts", "clk_null", 1, 1),
|
||||
};
|
||||
|
||||
static const struct mtk_fixed_factor top_divs[] __initconst = {
|
||||
FACTOR(CLK_TOP_ARMCA7PLL_754M, "armca7pll_754m", "armca7pll", 1, 2),
|
||||
FACTOR(CLK_TOP_ARMCA7PLL_502M, "armca7pll_502m", "armca7pll", 1, 3),
|
||||
|
||||
FACTOR(CLK_TOP_MAIN_H546M, "main_h546m", "mainpll", 1, 2),
|
||||
FACTOR(CLK_TOP_MAIN_H364M, "main_h364m", "mainpll", 1, 3),
|
||||
FACTOR(CLK_TOP_MAIN_H218P4M, "main_h218p4m", "mainpll", 1, 5),
|
||||
FACTOR(CLK_TOP_MAIN_H156M, "main_h156m", "mainpll", 1, 7),
|
||||
|
||||
FACTOR(CLK_TOP_TVDPLL_445P5M, "tvdpll_445p5m", "tvdpll", 1, 4),
|
||||
FACTOR(CLK_TOP_TVDPLL_594M, "tvdpll_594m", "tvdpll", 1, 3),
|
||||
|
||||
FACTOR(CLK_TOP_UNIV_624M, "univ_624m", "univpll", 1, 2),
|
||||
FACTOR(CLK_TOP_UNIV_416M, "univ_416m", "univpll", 1, 3),
|
||||
FACTOR(CLK_TOP_UNIV_249P6M, "univ_249p6m", "univpll", 1, 5),
|
||||
FACTOR(CLK_TOP_UNIV_178P3M, "univ_178p3m", "univpll", 1, 7),
|
||||
FACTOR(CLK_TOP_UNIV_48M, "univ_48m", "univpll", 1, 26),
|
||||
|
||||
FACTOR(CLK_TOP_CLKRTC_EXT, "clkrtc_ext", "clk32k", 1, 1),
|
||||
FACTOR(CLK_TOP_CLKRTC_INT, "clkrtc_int", "clk26m", 1, 793),
|
||||
FACTOR(CLK_TOP_FPC, "fpc_ck", "clk26m", 1, 1),
|
||||
|
||||
FACTOR(CLK_TOP_HDMITXPLL_D2, "hdmitxpll_d2", "hdmitx_dig_cts", 1, 2),
|
||||
FACTOR(CLK_TOP_HDMITXPLL_D3, "hdmitxpll_d3", "hdmitx_dig_cts", 1, 3),
|
||||
|
||||
FACTOR(CLK_TOP_ARMCA7PLL_D2, "armca7pll_d2", "armca7pll_754m", 1, 1),
|
||||
FACTOR(CLK_TOP_ARMCA7PLL_D3, "armca7pll_d3", "armca7pll_502m", 1, 1),
|
||||
|
||||
FACTOR(CLK_TOP_APLL1, "apll1_ck", "apll1", 1, 1),
|
||||
FACTOR(CLK_TOP_APLL2, "apll2_ck", "apll2", 1, 1),
|
||||
|
||||
FACTOR(CLK_TOP_DMPLL, "dmpll_ck", "clkph_mck_o", 1, 1),
|
||||
FACTOR(CLK_TOP_DMPLL_D2, "dmpll_d2", "clkph_mck_o", 1, 2),
|
||||
FACTOR(CLK_TOP_DMPLL_D4, "dmpll_d4", "clkph_mck_o", 1, 4),
|
||||
FACTOR(CLK_TOP_DMPLL_D8, "dmpll_d8", "clkph_mck_o", 1, 8),
|
||||
FACTOR(CLK_TOP_DMPLL_D16, "dmpll_d16", "clkph_mck_o", 1, 16),
|
||||
|
||||
FACTOR(CLK_TOP_LVDSPLL_D2, "lvdspll_d2", "lvdspll", 1, 2),
|
||||
FACTOR(CLK_TOP_LVDSPLL_D4, "lvdspll_d4", "lvdspll", 1, 4),
|
||||
FACTOR(CLK_TOP_LVDSPLL_D8, "lvdspll_d8", "lvdspll", 1, 8),
|
||||
|
||||
FACTOR(CLK_TOP_MMPLL, "mmpll_ck", "mmpll", 1, 1),
|
||||
FACTOR(CLK_TOP_MMPLL_D2, "mmpll_d2", "mmpll", 1, 2),
|
||||
|
||||
FACTOR(CLK_TOP_MSDCPLL, "msdcpll_ck", "msdcpll", 1, 1),
|
||||
FACTOR(CLK_TOP_MSDCPLL_D2, "msdcpll_d2", "msdcpll", 1, 2),
|
||||
FACTOR(CLK_TOP_MSDCPLL_D4, "msdcpll_d4", "msdcpll", 1, 4),
|
||||
FACTOR(CLK_TOP_MSDCPLL2, "msdcpll2_ck", "msdcpll2", 1, 1),
|
||||
FACTOR(CLK_TOP_MSDCPLL2_D2, "msdcpll2_d2", "msdcpll2", 1, 2),
|
||||
FACTOR(CLK_TOP_MSDCPLL2_D4, "msdcpll2_d4", "msdcpll2", 1, 4),
|
||||
|
||||
FACTOR(CLK_TOP_SYSPLL_D2, "syspll_d2", "main_h546m", 1, 1),
|
||||
FACTOR(CLK_TOP_SYSPLL1_D2, "syspll1_d2", "main_h546m", 1, 2),
|
||||
FACTOR(CLK_TOP_SYSPLL1_D4, "syspll1_d4", "main_h546m", 1, 4),
|
||||
FACTOR(CLK_TOP_SYSPLL1_D8, "syspll1_d8", "main_h546m", 1, 8),
|
||||
FACTOR(CLK_TOP_SYSPLL1_D16, "syspll1_d16", "main_h546m", 1, 16),
|
||||
FACTOR(CLK_TOP_SYSPLL_D3, "syspll_d3", "main_h364m", 1, 1),
|
||||
FACTOR(CLK_TOP_SYSPLL2_D2, "syspll2_d2", "main_h364m", 1, 2),
|
||||
FACTOR(CLK_TOP_SYSPLL2_D4, "syspll2_d4", "main_h364m", 1, 4),
|
||||
FACTOR(CLK_TOP_SYSPLL_D5, "syspll_d5", "main_h218p4m", 1, 1),
|
||||
FACTOR(CLK_TOP_SYSPLL3_D2, "syspll3_d2", "main_h218p4m", 1, 2),
|
||||
FACTOR(CLK_TOP_SYSPLL3_D4, "syspll3_d4", "main_h218p4m", 1, 4),
|
||||
FACTOR(CLK_TOP_SYSPLL_D7, "syspll_d7", "main_h156m", 1, 1),
|
||||
FACTOR(CLK_TOP_SYSPLL4_D2, "syspll4_d2", "main_h156m", 1, 2),
|
||||
FACTOR(CLK_TOP_SYSPLL4_D4, "syspll4_d4", "main_h156m", 1, 4),
|
||||
|
||||
FACTOR(CLK_TOP_TVDPLL, "tvdpll_ck", "tvdpll_594m", 1, 1),
|
||||
FACTOR(CLK_TOP_TVDPLL_D2, "tvdpll_d2", "tvdpll_594m", 1, 2),
|
||||
FACTOR(CLK_TOP_TVDPLL_D4, "tvdpll_d4", "tvdpll_594m", 1, 4),
|
||||
FACTOR(CLK_TOP_TVDPLL_D8, "tvdpll_d8", "tvdpll_594m", 1, 8),
|
||||
FACTOR(CLK_TOP_TVDPLL_D16, "tvdpll_d16", "tvdpll_594m", 1, 16),
|
||||
|
||||
FACTOR(CLK_TOP_UNIVPLL_D2, "univpll_d2", "univ_624m", 1, 1),
|
||||
FACTOR(CLK_TOP_UNIVPLL1_D2, "univpll1_d2", "univ_624m", 1, 2),
|
||||
FACTOR(CLK_TOP_UNIVPLL1_D4, "univpll1_d4", "univ_624m", 1, 4),
|
||||
FACTOR(CLK_TOP_UNIVPLL1_D8, "univpll1_d8", "univ_624m", 1, 8),
|
||||
FACTOR(CLK_TOP_UNIVPLL_D3, "univpll_d3", "univ_416m", 1, 1),
|
||||
FACTOR(CLK_TOP_UNIVPLL2_D2, "univpll2_d2", "univ_416m", 1, 2),
|
||||
FACTOR(CLK_TOP_UNIVPLL2_D4, "univpll2_d4", "univ_416m", 1, 4),
|
||||
FACTOR(CLK_TOP_UNIVPLL2_D8, "univpll2_d8", "univ_416m", 1, 8),
|
||||
FACTOR(CLK_TOP_UNIVPLL_D5, "univpll_d5", "univ_249p6m", 1, 1),
|
||||
FACTOR(CLK_TOP_UNIVPLL3_D2, "univpll3_d2", "univ_249p6m", 1, 2),
|
||||
FACTOR(CLK_TOP_UNIVPLL3_D4, "univpll3_d4", "univ_249p6m", 1, 4),
|
||||
FACTOR(CLK_TOP_UNIVPLL3_D8, "univpll3_d8", "univ_249p6m", 1, 8),
|
||||
FACTOR(CLK_TOP_UNIVPLL_D7, "univpll_d7", "univ_178p3m", 1, 1),
|
||||
FACTOR(CLK_TOP_UNIVPLL_D26, "univpll_d26", "univ_48m", 1, 1),
|
||||
FACTOR(CLK_TOP_UNIVPLL_D52, "univpll_d52", "univ_48m", 1, 2),
|
||||
|
||||
FACTOR(CLK_TOP_VCODECPLL, "vcodecpll_ck", "vcodecpll", 1, 3),
|
||||
FACTOR(CLK_TOP_VCODECPLL_370P5, "vcodecpll_370p5", "vcodecpll", 1, 4),
|
||||
|
||||
FACTOR(CLK_TOP_VENCPLL, "vencpll_ck", "vencpll", 1, 1),
|
||||
FACTOR(CLK_TOP_VENCPLL_D2, "vencpll_d2", "vencpll", 1, 2),
|
||||
FACTOR(CLK_TOP_VENCPLL_D4, "vencpll_d4", "vencpll", 1, 4),
|
||||
};
|
||||
|
||||
static const char * const axi_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"syspll1_d2",
|
||||
"syspll_d5",
|
||||
"syspll1_d4",
|
||||
"univpll_d5",
|
||||
"univpll2_d2",
|
||||
"dmpll_d2",
|
||||
"dmpll_d4"
|
||||
};
|
||||
|
||||
static const char * const mem_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"dmpll_ck"
|
||||
};
|
||||
|
||||
static const char * const ddrphycfg_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"syspll1_d8"
|
||||
};
|
||||
|
||||
static const char * const mm_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"vencpll_d2",
|
||||
"main_h364m",
|
||||
"syspll1_d2",
|
||||
"syspll_d5",
|
||||
"syspll1_d4",
|
||||
"univpll1_d2",
|
||||
"univpll2_d2",
|
||||
"dmpll_d2"
|
||||
};
|
||||
|
||||
static const char * const pwm_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"univpll2_d4",
|
||||
"univpll3_d2",
|
||||
"univpll1_d4"
|
||||
};
|
||||
|
||||
static const char * const vdec_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"vcodecpll_ck",
|
||||
"tvdpll_445p5m",
|
||||
"univpll_d3",
|
||||
"vencpll_d2",
|
||||
"syspll_d3",
|
||||
"univpll1_d2",
|
||||
"mmpll_d2",
|
||||
"dmpll_d2",
|
||||
"dmpll_d4"
|
||||
};
|
||||
|
||||
static const char * const venc_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"vcodecpll_ck",
|
||||
"tvdpll_445p5m",
|
||||
"univpll_d3",
|
||||
"vencpll_d2",
|
||||
"syspll_d3",
|
||||
"univpll1_d2",
|
||||
"univpll2_d2",
|
||||
"dmpll_d2",
|
||||
"dmpll_d4"
|
||||
};
|
||||
|
||||
static const char * const mfg_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"mmpll_ck",
|
||||
"dmpll_ck",
|
||||
"clk26m",
|
||||
"clk26m",
|
||||
"clk26m",
|
||||
"clk26m",
|
||||
"clk26m",
|
||||
"clk26m",
|
||||
"syspll_d3",
|
||||
"syspll1_d2",
|
||||
"syspll_d5",
|
||||
"univpll_d3",
|
||||
"univpll1_d2",
|
||||
"univpll_d5",
|
||||
"univpll2_d2"
|
||||
};
|
||||
|
||||
static const char * const camtg_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"univpll_d26",
|
||||
"univpll2_d2",
|
||||
"syspll3_d2",
|
||||
"syspll3_d4",
|
||||
"univpll1_d4"
|
||||
};
|
||||
|
||||
static const char * const uart_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"univpll2_d8"
|
||||
};
|
||||
|
||||
static const char * const spi_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"syspll3_d2",
|
||||
"syspll1_d4",
|
||||
"syspll4_d2",
|
||||
"univpll3_d2",
|
||||
"univpll2_d4",
|
||||
"univpll1_d8"
|
||||
};
|
||||
|
||||
static const char * const usb20_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"univpll1_d8",
|
||||
"univpll3_d4"
|
||||
};
|
||||
|
||||
static const char * const usb30_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"univpll3_d2",
|
||||
"usb_syspll_125m",
|
||||
"univpll2_d4"
|
||||
};
|
||||
|
||||
static const char * const msdc50_0_h_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"syspll1_d2",
|
||||
"syspll2_d2",
|
||||
"syspll4_d2",
|
||||
"univpll_d5",
|
||||
"univpll1_d4"
|
||||
};
|
||||
|
||||
static const char * const msdc50_0_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"msdcpll_ck",
|
||||
"msdcpll_d2",
|
||||
"univpll1_d4",
|
||||
"syspll2_d2",
|
||||
"syspll_d7",
|
||||
"msdcpll_d4",
|
||||
"vencpll_d4",
|
||||
"tvdpll_ck",
|
||||
"univpll_d2",
|
||||
"univpll1_d2",
|
||||
"mmpll_ck",
|
||||
"msdcpll2_ck",
|
||||
"msdcpll2_d2",
|
||||
"msdcpll2_d4"
|
||||
};
|
||||
|
||||
static const char * const msdc30_1_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"univpll2_d2",
|
||||
"msdcpll_d4",
|
||||
"univpll1_d4",
|
||||
"syspll2_d2",
|
||||
"syspll_d7",
|
||||
"univpll_d7",
|
||||
"vencpll_d4"
|
||||
};
|
||||
|
||||
static const char * const msdc30_2_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"univpll2_d2",
|
||||
"msdcpll_d4",
|
||||
"univpll1_d4",
|
||||
"syspll2_d2",
|
||||
"syspll_d7",
|
||||
"univpll_d7",
|
||||
"vencpll_d2"
|
||||
};
|
||||
|
||||
static const char * const msdc30_3_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"msdcpll2_ck",
|
||||
"msdcpll2_d2",
|
||||
"univpll2_d2",
|
||||
"msdcpll2_d4",
|
||||
"msdcpll_d4",
|
||||
"univpll1_d4",
|
||||
"syspll2_d2",
|
||||
"syspll_d7",
|
||||
"univpll_d7",
|
||||
"vencpll_d4",
|
||||
"msdcpll_ck",
|
||||
"msdcpll_d2",
|
||||
"msdcpll_d4"
|
||||
};
|
||||
|
||||
static const char * const audio_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"syspll3_d4",
|
||||
"syspll4_d4",
|
||||
"syspll1_d16"
|
||||
};
|
||||
|
||||
static const char * const aud_intbus_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"syspll1_d4",
|
||||
"syspll4_d2",
|
||||
"univpll3_d2",
|
||||
"univpll2_d8",
|
||||
"dmpll_d4",
|
||||
"dmpll_d8"
|
||||
};
|
||||
|
||||
static const char * const pmicspi_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"syspll1_d8",
|
||||
"syspll3_d4",
|
||||
"syspll1_d16",
|
||||
"univpll3_d4",
|
||||
"univpll_d26",
|
||||
"dmpll_d8",
|
||||
"dmpll_d16"
|
||||
};
|
||||
|
||||
static const char * const scp_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"syspll1_d2",
|
||||
"univpll_d5",
|
||||
"syspll_d5",
|
||||
"dmpll_d2",
|
||||
"dmpll_d4"
|
||||
};
|
||||
|
||||
static const char * const atb_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"syspll1_d2",
|
||||
"univpll_d5",
|
||||
"dmpll_d2"
|
||||
};
|
||||
|
||||
static const char * const venc_lt_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"univpll_d3",
|
||||
"vcodecpll_ck",
|
||||
"tvdpll_445p5m",
|
||||
"vencpll_d2",
|
||||
"syspll_d3",
|
||||
"univpll1_d2",
|
||||
"univpll2_d2",
|
||||
"syspll1_d2",
|
||||
"univpll_d5",
|
||||
"vcodecpll_370p5",
|
||||
"dmpll_ck"
|
||||
};
|
||||
|
||||
static const char * const dpi0_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"tvdpll_d2",
|
||||
"tvdpll_d4",
|
||||
"clk26m",
|
||||
"clk26m",
|
||||
"tvdpll_d8",
|
||||
"tvdpll_d16"
|
||||
};
|
||||
|
||||
static const char * const irda_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"univpll2_d4",
|
||||
"syspll2_d4"
|
||||
};
|
||||
|
||||
static const char * const cci400_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"vencpll_ck",
|
||||
"armca7pll_754m",
|
||||
"armca7pll_502m",
|
||||
"univpll_d2",
|
||||
"syspll_d2",
|
||||
"msdcpll_ck",
|
||||
"dmpll_ck"
|
||||
};
|
||||
|
||||
static const char * const aud_1_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"apll1_ck",
|
||||
"univpll2_d4",
|
||||
"univpll2_d8"
|
||||
};
|
||||
|
||||
static const char * const aud_2_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"apll2_ck",
|
||||
"univpll2_d4",
|
||||
"univpll2_d8"
|
||||
};
|
||||
|
||||
static const char * const mem_mfg_in_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"mmpll_ck",
|
||||
"dmpll_ck",
|
||||
"clk26m"
|
||||
};
|
||||
|
||||
static const char * const axi_mfg_in_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"axi_sel",
|
||||
"dmpll_d2"
|
||||
};
|
||||
|
||||
static const char * const scam_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"syspll3_d2",
|
||||
"univpll2_d4",
|
||||
"dmpll_d4"
|
||||
};
|
||||
|
||||
static const char * const spinfi_ifr_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"univpll2_d8",
|
||||
"univpll3_d4",
|
||||
"syspll4_d2",
|
||||
"univpll2_d4",
|
||||
"univpll3_d2",
|
||||
"syspll1_d4",
|
||||
"univpll1_d4"
|
||||
};
|
||||
|
||||
static const char * const hdmi_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"hdmitx_dig_cts",
|
||||
"hdmitxpll_d2",
|
||||
"hdmitxpll_d3"
|
||||
};
|
||||
|
||||
static const char * const dpilvds_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"lvdspll",
|
||||
"lvdspll_d2",
|
||||
"lvdspll_d4",
|
||||
"lvdspll_d8",
|
||||
"fpc_ck"
|
||||
};
|
||||
|
||||
static const char * const msdc50_2_h_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"syspll1_d2",
|
||||
"syspll2_d2",
|
||||
"syspll4_d2",
|
||||
"univpll_d5",
|
||||
"univpll1_d4"
|
||||
};
|
||||
|
||||
static const char * const hdcp_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"syspll4_d2",
|
||||
"syspll3_d4",
|
||||
"univpll2_d4"
|
||||
};
|
||||
|
||||
static const char * const hdcp_24m_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"univpll_d26",
|
||||
"univpll_d52",
|
||||
"univpll2_d8"
|
||||
};
|
||||
|
||||
static const char * const rtc_parents[] __initconst = {
|
||||
"clkrtc_int",
|
||||
"clkrtc_ext",
|
||||
"clk26m",
|
||||
"univpll3_d8"
|
||||
};
|
||||
|
||||
static const char * const i2s0_m_ck_parents[] __initconst = {
|
||||
"apll1_div1",
|
||||
"apll2_div1"
|
||||
};
|
||||
|
||||
static const char * const i2s1_m_ck_parents[] __initconst = {
|
||||
"apll1_div2",
|
||||
"apll2_div2"
|
||||
};
|
||||
|
||||
static const char * const i2s2_m_ck_parents[] __initconst = {
|
||||
"apll1_div3",
|
||||
"apll2_div3"
|
||||
};
|
||||
|
||||
static const char * const i2s3_m_ck_parents[] __initconst = {
|
||||
"apll1_div4",
|
||||
"apll2_div4"
|
||||
};
|
||||
|
||||
static const char * const i2s3_b_ck_parents[] __initconst = {
|
||||
"apll1_div5",
|
||||
"apll2_div5"
|
||||
};
|
||||
|
||||
static const struct mtk_composite top_muxes[] __initconst = {
|
||||
/* CLK_CFG_0 */
|
||||
MUX(CLK_TOP_AXI_SEL, "axi_sel", axi_parents, 0x0040, 0, 3),
|
||||
MUX(CLK_TOP_MEM_SEL, "mem_sel", mem_parents, 0x0040, 8, 1),
|
||||
MUX_GATE(CLK_TOP_DDRPHYCFG_SEL, "ddrphycfg_sel", ddrphycfg_parents, 0x0040, 16, 1, 23),
|
||||
MUX_GATE(CLK_TOP_MM_SEL, "mm_sel", mm_parents, 0x0040, 24, 4, 31),
|
||||
/* CLK_CFG_1 */
|
||||
MUX_GATE(CLK_TOP_PWM_SEL, "pwm_sel", pwm_parents, 0x0050, 0, 2, 7),
|
||||
MUX_GATE(CLK_TOP_VDEC_SEL, "vdec_sel", vdec_parents, 0x0050, 8, 4, 15),
|
||||
MUX_GATE(CLK_TOP_VENC_SEL, "venc_sel", venc_parents, 0x0050, 16, 4, 23),
|
||||
MUX_GATE(CLK_TOP_MFG_SEL, "mfg_sel", mfg_parents, 0x0050, 24, 4, 31),
|
||||
/* CLK_CFG_2 */
|
||||
MUX_GATE(CLK_TOP_CAMTG_SEL, "camtg_sel", camtg_parents, 0x0060, 0, 3, 7),
|
||||
MUX_GATE(CLK_TOP_UART_SEL, "uart_sel", uart_parents, 0x0060, 8, 1, 15),
|
||||
MUX_GATE(CLK_TOP_SPI_SEL, "spi_sel", spi_parents, 0x0060, 16, 3, 23),
|
||||
MUX_GATE(CLK_TOP_USB20_SEL, "usb20_sel", usb20_parents, 0x0060, 24, 2, 31),
|
||||
/* CLK_CFG_3 */
|
||||
MUX_GATE(CLK_TOP_USB30_SEL, "usb30_sel", usb30_parents, 0x0070, 0, 2, 7),
|
||||
MUX_GATE(CLK_TOP_MSDC50_0_H_SEL, "msdc50_0_h_sel", msdc50_0_h_parents, 0x0070, 8, 3, 15),
|
||||
MUX_GATE(CLK_TOP_MSDC50_0_SEL, "msdc50_0_sel", msdc50_0_parents, 0x0070, 16, 4, 23),
|
||||
MUX_GATE(CLK_TOP_MSDC30_1_SEL, "msdc30_1_sel", msdc30_1_parents, 0x0070, 24, 3, 31),
|
||||
/* CLK_CFG_4 */
|
||||
MUX_GATE(CLK_TOP_MSDC30_2_SEL, "msdc30_2_sel", msdc30_2_parents, 0x0080, 0, 3, 7),
|
||||
MUX_GATE(CLK_TOP_MSDC30_3_SEL, "msdc30_3_sel", msdc30_3_parents, 0x0080, 8, 4, 15),
|
||||
MUX_GATE(CLK_TOP_AUDIO_SEL, "audio_sel", audio_parents, 0x0080, 16, 2, 23),
|
||||
MUX_GATE(CLK_TOP_AUD_INTBUS_SEL, "aud_intbus_sel", aud_intbus_parents, 0x0080, 24, 3, 31),
|
||||
/* CLK_CFG_5 */
|
||||
MUX_GATE(CLK_TOP_PMICSPI_SEL, "pmicspi_sel", pmicspi_parents, 0x0090, 0, 3, 7 /* 7:5 */),
|
||||
MUX_GATE(CLK_TOP_SCP_SEL, "scp_sel", scp_parents, 0x0090, 8, 3, 15),
|
||||
MUX_GATE(CLK_TOP_ATB_SEL, "atb_sel", atb_parents, 0x0090, 16, 2, 23),
|
||||
MUX_GATE(CLK_TOP_VENC_LT_SEL, "venclt_sel", venc_lt_parents, 0x0090, 24, 4, 31),
|
||||
/* CLK_CFG_6 */
|
||||
MUX_GATE(CLK_TOP_DPI0_SEL, "dpi0_sel", dpi0_parents, 0x00a0, 0, 3, 7),
|
||||
MUX_GATE(CLK_TOP_IRDA_SEL, "irda_sel", irda_parents, 0x00a0, 8, 2, 15),
|
||||
MUX_GATE(CLK_TOP_CCI400_SEL, "cci400_sel", cci400_parents, 0x00a0, 16, 3, 23),
|
||||
MUX_GATE(CLK_TOP_AUD_1_SEL, "aud_1_sel", aud_1_parents, 0x00a0, 24, 2, 31),
|
||||
/* CLK_CFG_7 */
|
||||
MUX_GATE(CLK_TOP_AUD_2_SEL, "aud_2_sel", aud_2_parents, 0x00b0, 0, 2, 7),
|
||||
MUX_GATE(CLK_TOP_MEM_MFG_IN_SEL, "mem_mfg_in_sel", mem_mfg_in_parents, 0x00b0, 8, 2, 15),
|
||||
MUX_GATE(CLK_TOP_AXI_MFG_IN_SEL, "axi_mfg_in_sel", axi_mfg_in_parents, 0x00b0, 16, 2, 23),
|
||||
MUX_GATE(CLK_TOP_SCAM_SEL, "scam_sel", scam_parents, 0x00b0, 24, 2, 31),
|
||||
/* CLK_CFG_12 */
|
||||
MUX_GATE(CLK_TOP_SPINFI_IFR_SEL, "spinfi_ifr_sel", spinfi_ifr_parents, 0x00c0, 0, 3, 7),
|
||||
MUX_GATE(CLK_TOP_HDMI_SEL, "hdmi_sel", hdmi_parents, 0x00c0, 8, 2, 15),
|
||||
MUX_GATE(CLK_TOP_DPILVDS_SEL, "dpilvds_sel", dpilvds_parents, 0x00c0, 24, 3, 31),
|
||||
/* CLK_CFG_13 */
|
||||
MUX_GATE(CLK_TOP_MSDC50_2_H_SEL, "msdc50_2_h_sel", msdc50_2_h_parents, 0x00d0, 0, 3, 7),
|
||||
MUX_GATE(CLK_TOP_HDCP_SEL, "hdcp_sel", hdcp_parents, 0x00d0, 8, 2, 15),
|
||||
MUX_GATE(CLK_TOP_HDCP_24M_SEL, "hdcp_24m_sel", hdcp_24m_parents, 0x00d0, 16, 2, 23),
|
||||
MUX(CLK_TOP_RTC_SEL, "rtc_sel", rtc_parents, 0x00d0, 24, 2),
|
||||
|
||||
DIV_GATE(CLK_TOP_APLL1_DIV0, "apll1_div0", "aud_1_sel", 0x12c, 8, 0x120, 4, 24),
|
||||
DIV_GATE(CLK_TOP_APLL1_DIV1, "apll1_div1", "aud_1_sel", 0x12c, 9, 0x124, 8, 0),
|
||||
DIV_GATE(CLK_TOP_APLL1_DIV2, "apll1_div2", "aud_1_sel", 0x12c, 10, 0x124, 8, 8),
|
||||
DIV_GATE(CLK_TOP_APLL1_DIV3, "apll1_div3", "aud_1_sel", 0x12c, 11, 0x124, 8, 16),
|
||||
DIV_GATE(CLK_TOP_APLL1_DIV4, "apll1_div4", "aud_1_sel", 0x12c, 12, 0x124, 8, 24),
|
||||
DIV_GATE(CLK_TOP_APLL1_DIV5, "apll1_div5", "apll1_div4", 0x12c, 13, 0x12c, 4, 0),
|
||||
|
||||
DIV_GATE(CLK_TOP_APLL2_DIV0, "apll2_div0", "aud_2_sel", 0x12c, 16, 0x120, 4, 28),
|
||||
DIV_GATE(CLK_TOP_APLL2_DIV1, "apll2_div1", "aud_2_sel", 0x12c, 17, 0x128, 8, 0),
|
||||
DIV_GATE(CLK_TOP_APLL2_DIV2, "apll2_div2", "aud_2_sel", 0x12c, 18, 0x128, 8, 8),
|
||||
DIV_GATE(CLK_TOP_APLL2_DIV3, "apll2_div3", "aud_2_sel", 0x12c, 19, 0x128, 8, 16),
|
||||
DIV_GATE(CLK_TOP_APLL2_DIV4, "apll2_div4", "aud_2_sel", 0x12c, 20, 0x128, 8, 24),
|
||||
DIV_GATE(CLK_TOP_APLL2_DIV5, "apll2_div5", "apll2_div4", 0x12c, 21, 0x12c, 4, 4),
|
||||
|
||||
MUX(CLK_TOP_I2S0_M_SEL, "i2s0_m_ck_sel", i2s0_m_ck_parents, 0x120, 4, 1),
|
||||
MUX(CLK_TOP_I2S1_M_SEL, "i2s1_m_ck_sel", i2s1_m_ck_parents, 0x120, 5, 1),
|
||||
MUX(CLK_TOP_I2S2_M_SEL, "i2s2_m_ck_sel", i2s2_m_ck_parents, 0x120, 6, 1),
|
||||
MUX(CLK_TOP_I2S3_M_SEL, "i2s3_m_ck_sel", i2s3_m_ck_parents, 0x120, 7, 1),
|
||||
MUX(CLK_TOP_I2S3_B_SEL, "i2s3_b_ck_sel", i2s3_b_ck_parents, 0x120, 8, 1),
|
||||
};
|
||||
|
||||
static const struct mtk_gate_regs infra_cg_regs = {
|
||||
.set_ofs = 0x0040,
|
||||
.clr_ofs = 0x0044,
|
||||
.sta_ofs = 0x0048,
|
||||
};
|
||||
|
||||
#define GATE_ICG(_id, _name, _parent, _shift) { \
|
||||
.id = _id, \
|
||||
.name = _name, \
|
||||
.parent_name = _parent, \
|
||||
.regs = &infra_cg_regs, \
|
||||
.shift = _shift, \
|
||||
.ops = &mtk_clk_gate_ops_setclr, \
|
||||
}
|
||||
|
||||
static const struct mtk_gate infra_clks[] __initconst = {
|
||||
GATE_ICG(CLK_INFRA_DBGCLK, "infra_dbgclk", "axi_sel", 0),
|
||||
GATE_ICG(CLK_INFRA_SMI, "infra_smi", "mm_sel", 1),
|
||||
GATE_ICG(CLK_INFRA_AUDIO, "infra_audio", "aud_intbus_sel", 5),
|
||||
GATE_ICG(CLK_INFRA_GCE, "infra_gce", "axi_sel", 6),
|
||||
GATE_ICG(CLK_INFRA_L2C_SRAM, "infra_l2c_sram", "axi_sel", 7),
|
||||
GATE_ICG(CLK_INFRA_M4U, "infra_m4u", "mem_sel", 8),
|
||||
GATE_ICG(CLK_INFRA_CPUM, "infra_cpum", "clk_null", 15),
|
||||
GATE_ICG(CLK_INFRA_KP, "infra_kp", "axi_sel", 16),
|
||||
GATE_ICG(CLK_INFRA_CEC, "infra_cec", "clk26m", 18),
|
||||
GATE_ICG(CLK_INFRA_PMICSPI, "infra_pmicspi", "pmicspi_sel", 22),
|
||||
GATE_ICG(CLK_INFRA_PMICWRAP, "infra_pmicwrap", "axi_sel", 23),
|
||||
};
|
||||
|
||||
static const struct mtk_gate_regs peri0_cg_regs = {
|
||||
.set_ofs = 0x0008,
|
||||
.clr_ofs = 0x0010,
|
||||
.sta_ofs = 0x0018,
|
||||
};
|
||||
|
||||
static const struct mtk_gate_regs peri1_cg_regs = {
|
||||
.set_ofs = 0x000c,
|
||||
.clr_ofs = 0x0014,
|
||||
.sta_ofs = 0x001c,
|
||||
};
|
||||
|
||||
#define GATE_PERI0(_id, _name, _parent, _shift) { \
|
||||
.id = _id, \
|
||||
.name = _name, \
|
||||
.parent_name = _parent, \
|
||||
.regs = &peri0_cg_regs, \
|
||||
.shift = _shift, \
|
||||
.ops = &mtk_clk_gate_ops_setclr, \
|
||||
}
|
||||
|
||||
#define GATE_PERI1(_id, _name, _parent, _shift) { \
|
||||
.id = _id, \
|
||||
.name = _name, \
|
||||
.parent_name = _parent, \
|
||||
.regs = &peri1_cg_regs, \
|
||||
.shift = _shift, \
|
||||
.ops = &mtk_clk_gate_ops_setclr, \
|
||||
}
|
||||
|
||||
static const struct mtk_gate peri_gates[] __initconst = {
|
||||
/* PERI0 */
|
||||
GATE_PERI0(CLK_PERI_NFI, "peri_nfi", "axi_sel", 0),
|
||||
GATE_PERI0(CLK_PERI_THERM, "peri_therm", "axi_sel", 1),
|
||||
GATE_PERI0(CLK_PERI_PWM1, "peri_pwm1", "axi_sel", 2),
|
||||
GATE_PERI0(CLK_PERI_PWM2, "peri_pwm2", "axi_sel", 3),
|
||||
GATE_PERI0(CLK_PERI_PWM3, "peri_pwm3", "axi_sel", 4),
|
||||
GATE_PERI0(CLK_PERI_PWM4, "peri_pwm4", "axi_sel", 5),
|
||||
GATE_PERI0(CLK_PERI_PWM5, "peri_pwm5", "axi_sel", 6),
|
||||
GATE_PERI0(CLK_PERI_PWM6, "peri_pwm6", "axi_sel", 7),
|
||||
GATE_PERI0(CLK_PERI_PWM7, "peri_pwm7", "axi_sel", 8),
|
||||
GATE_PERI0(CLK_PERI_PWM, "peri_pwm", "axi_sel", 9),
|
||||
GATE_PERI0(CLK_PERI_USB0, "peri_usb0", "usb20_sel", 10),
|
||||
GATE_PERI0(CLK_PERI_USB1, "peri_usb1", "usb20_sel", 11),
|
||||
GATE_PERI0(CLK_PERI_AP_DMA, "peri_ap_dma", "axi_sel", 12),
|
||||
GATE_PERI0(CLK_PERI_MSDC30_0, "peri_msdc30_0", "msdc50_0_sel", 13),
|
||||
GATE_PERI0(CLK_PERI_MSDC30_1, "peri_msdc30_1", "msdc30_1_sel", 14),
|
||||
GATE_PERI0(CLK_PERI_MSDC30_2, "peri_msdc30_2", "msdc30_2_sel", 15),
|
||||
GATE_PERI0(CLK_PERI_MSDC30_3, "peri_msdc30_3", "msdc30_3_sel", 16),
|
||||
GATE_PERI0(CLK_PERI_NLI_ARB, "peri_nli_arb", "axi_sel", 17),
|
||||
GATE_PERI0(CLK_PERI_IRDA, "peri_irda", "irda_sel", 18),
|
||||
GATE_PERI0(CLK_PERI_UART0, "peri_uart0", "axi_sel", 19),
|
||||
GATE_PERI0(CLK_PERI_UART1, "peri_uart1", "axi_sel", 20),
|
||||
GATE_PERI0(CLK_PERI_UART2, "peri_uart2", "axi_sel", 21),
|
||||
GATE_PERI0(CLK_PERI_UART3, "peri_uart3", "axi_sel", 22),
|
||||
GATE_PERI0(CLK_PERI_I2C0, "peri_i2c0", "axi_sel", 23),
|
||||
GATE_PERI0(CLK_PERI_I2C1, "peri_i2c1", "axi_sel", 24),
|
||||
GATE_PERI0(CLK_PERI_I2C2, "peri_i2c2", "axi_sel", 25),
|
||||
GATE_PERI0(CLK_PERI_I2C3, "peri_i2c3", "axi_sel", 26),
|
||||
GATE_PERI0(CLK_PERI_I2C4, "peri_i2c4", "axi_sel", 27),
|
||||
GATE_PERI0(CLK_PERI_AUXADC, "peri_auxadc", "clk26m", 28),
|
||||
GATE_PERI0(CLK_PERI_SPI0, "peri_spi0", "spi_sel", 29),
|
||||
GATE_PERI0(CLK_PERI_I2C5, "peri_i2c5", "axi_sel", 30),
|
||||
GATE_PERI0(CLK_PERI_NFIECC, "peri_nfiecc", "axi_sel", 31),
|
||||
/* PERI1 */
|
||||
GATE_PERI1(CLK_PERI_SPI, "peri_spi", "spi_sel", 0),
|
||||
GATE_PERI1(CLK_PERI_IRRX, "peri_irrx", "spi_sel", 1),
|
||||
GATE_PERI1(CLK_PERI_I2C6, "peri_i2c6", "axi_sel", 2),
|
||||
};
|
||||
|
||||
static const char * const uart_ck_sel_parents[] __initconst = {
|
||||
"clk26m",
|
||||
"uart_sel",
|
||||
};
|
||||
|
||||
static const struct mtk_composite peri_clks[] __initconst = {
|
||||
MUX(CLK_PERI_UART0_SEL, "uart0_ck_sel", uart_ck_sel_parents, 0x40c, 0, 1),
|
||||
MUX(CLK_PERI_UART1_SEL, "uart1_ck_sel", uart_ck_sel_parents, 0x40c, 1, 1),
|
||||
MUX(CLK_PERI_UART2_SEL, "uart2_ck_sel", uart_ck_sel_parents, 0x40c, 2, 1),
|
||||
MUX(CLK_PERI_UART3_SEL, "uart3_ck_sel", uart_ck_sel_parents, 0x40c, 3, 1),
|
||||
};
|
||||
|
||||
static void __init mtk_topckgen_init(struct device_node *node)
|
||||
{
|
||||
struct clk_onecell_data *clk_data;
|
||||
void __iomem *base;
|
||||
int r;
|
||||
|
||||
base = of_iomap(node, 0);
|
||||
if (!base) {
|
||||
pr_err("%s(): ioremap failed\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
clk_data = mtk_alloc_clk_data(CLK_TOP_NR_CLK);
|
||||
|
||||
mtk_clk_register_factors(root_clk_alias, ARRAY_SIZE(root_clk_alias), clk_data);
|
||||
mtk_clk_register_factors(top_divs, ARRAY_SIZE(top_divs), clk_data);
|
||||
mtk_clk_register_composites(top_muxes, ARRAY_SIZE(top_muxes), base,
|
||||
&mt8173_clk_lock, clk_data);
|
||||
|
||||
clk_prepare_enable(clk_data->clks[CLK_TOP_CCI400_SEL]);
|
||||
|
||||
r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
|
||||
if (r)
|
||||
pr_err("%s(): could not register clock provider: %d\n",
|
||||
__func__, r);
|
||||
}
|
||||
CLK_OF_DECLARE(mtk_topckgen, "mediatek,mt8173-topckgen", mtk_topckgen_init);
|
||||
|
||||
static void __init mtk_infrasys_init(struct device_node *node)
|
||||
{
|
||||
struct clk_onecell_data *clk_data;
|
||||
int r;
|
||||
|
||||
clk_data = mtk_alloc_clk_data(CLK_INFRA_NR_CLK);
|
||||
|
||||
mtk_clk_register_gates(node, infra_clks, ARRAY_SIZE(infra_clks),
|
||||
clk_data);
|
||||
|
||||
r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
|
||||
if (r)
|
||||
pr_err("%s(): could not register clock provider: %d\n",
|
||||
__func__, r);
|
||||
|
||||
mtk_register_reset_controller(node, 2, 0x30);
|
||||
}
|
||||
CLK_OF_DECLARE(mtk_infrasys, "mediatek,mt8173-infracfg", mtk_infrasys_init);
|
||||
|
||||
static void __init mtk_pericfg_init(struct device_node *node)
|
||||
{
|
||||
struct clk_onecell_data *clk_data;
|
||||
int r;
|
||||
void __iomem *base;
|
||||
|
||||
base = of_iomap(node, 0);
|
||||
if (!base) {
|
||||
pr_err("%s(): ioremap failed\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
clk_data = mtk_alloc_clk_data(CLK_PERI_NR_CLK);
|
||||
|
||||
mtk_clk_register_gates(node, peri_gates, ARRAY_SIZE(peri_gates),
|
||||
clk_data);
|
||||
mtk_clk_register_composites(peri_clks, ARRAY_SIZE(peri_clks), base,
|
||||
&mt8173_clk_lock, clk_data);
|
||||
|
||||
r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
|
||||
if (r)
|
||||
pr_err("%s(): could not register clock provider: %d\n",
|
||||
__func__, r);
|
||||
|
||||
mtk_register_reset_controller(node, 2, 0);
|
||||
}
|
||||
CLK_OF_DECLARE(mtk_pericfg, "mediatek,mt8173-pericfg", mtk_pericfg_init);
|
||||
|
||||
#define MT8173_PLL_FMAX (3000UL * MHZ)
|
||||
|
||||
#define CON0_MT8173_RST_BAR BIT(24)
|
||||
|
||||
#define PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, _pd_reg, _pd_shift, \
|
||||
_tuner_reg, _pcw_reg, _pcw_shift) { \
|
||||
.id = _id, \
|
||||
.name = _name, \
|
||||
.reg = _reg, \
|
||||
.pwr_reg = _pwr_reg, \
|
||||
.en_mask = _en_mask, \
|
||||
.flags = _flags, \
|
||||
.rst_bar_mask = CON0_MT8173_RST_BAR, \
|
||||
.fmax = MT8173_PLL_FMAX, \
|
||||
.pcwbits = _pcwbits, \
|
||||
.pd_reg = _pd_reg, \
|
||||
.pd_shift = _pd_shift, \
|
||||
.tuner_reg = _tuner_reg, \
|
||||
.pcw_reg = _pcw_reg, \
|
||||
.pcw_shift = _pcw_shift, \
|
||||
}
|
||||
|
||||
static const struct mtk_pll_data plls[] = {
|
||||
PLL(CLK_APMIXED_ARMCA15PLL, "armca15pll", 0x200, 0x20c, 0x00000001, 0, 21, 0x204, 24, 0x0, 0x204, 0),
|
||||
PLL(CLK_APMIXED_ARMCA7PLL, "armca7pll", 0x210, 0x21c, 0x00000001, 0, 21, 0x214, 24, 0x0, 0x214, 0),
|
||||
PLL(CLK_APMIXED_MAINPLL, "mainpll", 0x220, 0x22c, 0xf0000101, HAVE_RST_BAR, 21, 0x220, 4, 0x0, 0x224, 0),
|
||||
PLL(CLK_APMIXED_UNIVPLL, "univpll", 0x230, 0x23c, 0xfe000001, HAVE_RST_BAR, 7, 0x230, 4, 0x0, 0x234, 14),
|
||||
PLL(CLK_APMIXED_MMPLL, "mmpll", 0x240, 0x24c, 0x00000001, 0, 21, 0x244, 24, 0x0, 0x244, 0),
|
||||
PLL(CLK_APMIXED_MSDCPLL, "msdcpll", 0x250, 0x25c, 0x00000001, 0, 21, 0x250, 4, 0x0, 0x254, 0),
|
||||
PLL(CLK_APMIXED_VENCPLL, "vencpll", 0x260, 0x26c, 0x00000001, 0, 21, 0x260, 4, 0x0, 0x264, 0),
|
||||
PLL(CLK_APMIXED_TVDPLL, "tvdpll", 0x270, 0x27c, 0x00000001, 0, 21, 0x270, 4, 0x0, 0x274, 0),
|
||||
PLL(CLK_APMIXED_MPLL, "mpll", 0x280, 0x28c, 0x00000001, 0, 21, 0x280, 4, 0x0, 0x284, 0),
|
||||
PLL(CLK_APMIXED_VCODECPLL, "vcodecpll", 0x290, 0x29c, 0x00000001, 0, 21, 0x290, 4, 0x0, 0x294, 0),
|
||||
PLL(CLK_APMIXED_APLL1, "apll1", 0x2a0, 0x2b0, 0x00000001, 0, 31, 0x2a0, 4, 0x2a4, 0x2a4, 0),
|
||||
PLL(CLK_APMIXED_APLL2, "apll2", 0x2b4, 0x2c4, 0x00000001, 0, 31, 0x2b4, 4, 0x2b8, 0x2b8, 0),
|
||||
PLL(CLK_APMIXED_LVDSPLL, "lvdspll", 0x2d0, 0x2dc, 0x00000001, 0, 21, 0x2d0, 4, 0x0, 0x2d4, 0),
|
||||
PLL(CLK_APMIXED_MSDCPLL2, "msdcpll2", 0x2f0, 0x2fc, 0x00000001, 0, 21, 0x2f0, 4, 0x0, 0x2f4, 0),
|
||||
};
|
||||
|
||||
static void __init mtk_apmixedsys_init(struct device_node *node)
|
||||
{
|
||||
struct clk_onecell_data *clk_data;
|
||||
|
||||
clk_data = mtk_alloc_clk_data(CLK_APMIXED_NR_CLK);
|
||||
if (!clk_data)
|
||||
return;
|
||||
|
||||
mtk_clk_register_plls(node, plls, ARRAY_SIZE(plls), clk_data);
|
||||
|
||||
clk_prepare_enable(clk_data->clks[CLK_APMIXED_ARMCA15PLL]);
|
||||
}
|
||||
CLK_OF_DECLARE(mtk_apmixedsys, "mediatek,mt8173-apmixedsys",
|
||||
mtk_apmixedsys_init);
|
||||
220
drivers/clk/mediatek/clk-mtk.c
Normal file
220
drivers/clk/mediatek/clk-mtk.c
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
/*
|
||||
* Copyright (c) 2014 MediaTek Inc.
|
||||
* Author: James Liao <jamesjj.liao@mediatek.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
|
||||
#include "clk-mtk.h"
|
||||
#include "clk-gate.h"
|
||||
|
||||
struct clk_onecell_data *mtk_alloc_clk_data(unsigned int clk_num)
|
||||
{
|
||||
int i;
|
||||
struct clk_onecell_data *clk_data;
|
||||
|
||||
clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
|
||||
if (!clk_data)
|
||||
return NULL;
|
||||
|
||||
clk_data->clks = kcalloc(clk_num, sizeof(*clk_data->clks), GFP_KERNEL);
|
||||
if (!clk_data->clks)
|
||||
goto err_out;
|
||||
|
||||
clk_data->clk_num = clk_num;
|
||||
|
||||
for (i = 0; i < clk_num; i++)
|
||||
clk_data->clks[i] = ERR_PTR(-ENOENT);
|
||||
|
||||
return clk_data;
|
||||
err_out:
|
||||
kfree(clk_data);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void mtk_clk_register_factors(const struct mtk_fixed_factor *clks, int num,
|
||||
struct clk_onecell_data *clk_data)
|
||||
{
|
||||
int i;
|
||||
struct clk *clk;
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
const struct mtk_fixed_factor *ff = &clks[i];
|
||||
|
||||
clk = clk_register_fixed_factor(NULL, ff->name, ff->parent_name,
|
||||
CLK_SET_RATE_PARENT, ff->mult, ff->div);
|
||||
|
||||
if (IS_ERR(clk)) {
|
||||
pr_err("Failed to register clk %s: %ld\n",
|
||||
ff->name, PTR_ERR(clk));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (clk_data)
|
||||
clk_data->clks[ff->id] = clk;
|
||||
}
|
||||
}
|
||||
|
||||
int mtk_clk_register_gates(struct device_node *node, const struct mtk_gate *clks,
|
||||
int num, struct clk_onecell_data *clk_data)
|
||||
{
|
||||
int i;
|
||||
struct clk *clk;
|
||||
struct regmap *regmap;
|
||||
|
||||
if (!clk_data)
|
||||
return -ENOMEM;
|
||||
|
||||
regmap = syscon_node_to_regmap(node);
|
||||
if (IS_ERR(regmap)) {
|
||||
pr_err("Cannot find regmap for %s: %ld\n", node->full_name,
|
||||
PTR_ERR(regmap));
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
const struct mtk_gate *gate = &clks[i];
|
||||
|
||||
clk = mtk_clk_register_gate(gate->name, gate->parent_name,
|
||||
regmap,
|
||||
gate->regs->set_ofs,
|
||||
gate->regs->clr_ofs,
|
||||
gate->regs->sta_ofs,
|
||||
gate->shift, gate->ops);
|
||||
|
||||
if (IS_ERR(clk)) {
|
||||
pr_err("Failed to register clk %s: %ld\n",
|
||||
gate->name, PTR_ERR(clk));
|
||||
continue;
|
||||
}
|
||||
|
||||
clk_data->clks[gate->id] = clk;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct clk *mtk_clk_register_composite(const struct mtk_composite *mc,
|
||||
void __iomem *base, spinlock_t *lock)
|
||||
{
|
||||
struct clk *clk;
|
||||
struct clk_mux *mux = NULL;
|
||||
struct clk_gate *gate = NULL;
|
||||
struct clk_divider *div = NULL;
|
||||
struct clk_hw *mux_hw = NULL, *gate_hw = NULL, *div_hw = NULL;
|
||||
const struct clk_ops *mux_ops = NULL, *gate_ops = NULL, *div_ops = NULL;
|
||||
const char * const *parent_names;
|
||||
const char *parent;
|
||||
int num_parents;
|
||||
int ret;
|
||||
|
||||
if (mc->mux_shift >= 0) {
|
||||
mux = kzalloc(sizeof(*mux), GFP_KERNEL);
|
||||
if (!mux)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
mux->reg = base + mc->mux_reg;
|
||||
mux->mask = BIT(mc->mux_width) - 1;
|
||||
mux->shift = mc->mux_shift;
|
||||
mux->lock = lock;
|
||||
|
||||
mux_hw = &mux->hw;
|
||||
mux_ops = &clk_mux_ops;
|
||||
|
||||
parent_names = mc->parent_names;
|
||||
num_parents = mc->num_parents;
|
||||
} else {
|
||||
parent = mc->parent;
|
||||
parent_names = &parent;
|
||||
num_parents = 1;
|
||||
}
|
||||
|
||||
if (mc->gate_shift >= 0) {
|
||||
gate = kzalloc(sizeof(*gate), GFP_KERNEL);
|
||||
if (!gate) {
|
||||
ret = -ENOMEM;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
gate->reg = base + mc->gate_reg;
|
||||
gate->bit_idx = mc->gate_shift;
|
||||
gate->flags = CLK_GATE_SET_TO_DISABLE;
|
||||
gate->lock = lock;
|
||||
|
||||
gate_hw = &gate->hw;
|
||||
gate_ops = &clk_gate_ops;
|
||||
}
|
||||
|
||||
if (mc->divider_shift >= 0) {
|
||||
div = kzalloc(sizeof(*div), GFP_KERNEL);
|
||||
if (!div) {
|
||||
ret = -ENOMEM;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
div->reg = base + mc->divider_reg;
|
||||
div->shift = mc->divider_shift;
|
||||
div->width = mc->divider_width;
|
||||
div->lock = lock;
|
||||
|
||||
div_hw = &div->hw;
|
||||
div_ops = &clk_divider_ops;
|
||||
}
|
||||
|
||||
clk = clk_register_composite(NULL, mc->name, parent_names, num_parents,
|
||||
mux_hw, mux_ops,
|
||||
div_hw, div_ops,
|
||||
gate_hw, gate_ops,
|
||||
mc->flags);
|
||||
|
||||
if (IS_ERR(clk)) {
|
||||
kfree(gate);
|
||||
kfree(mux);
|
||||
}
|
||||
|
||||
return clk;
|
||||
err_out:
|
||||
kfree(mux);
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
void mtk_clk_register_composites(const struct mtk_composite *mcs,
|
||||
int num, void __iomem *base, spinlock_t *lock,
|
||||
struct clk_onecell_data *clk_data)
|
||||
{
|
||||
struct clk *clk;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
const struct mtk_composite *mc = &mcs[i];
|
||||
|
||||
clk = mtk_clk_register_composite(mc, base, lock);
|
||||
|
||||
if (IS_ERR(clk)) {
|
||||
pr_err("Failed to register clk %s: %ld\n",
|
||||
mc->name, PTR_ERR(clk));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (clk_data)
|
||||
clk_data->clks[mc->id] = clk;
|
||||
}
|
||||
}
|
||||
169
drivers/clk/mediatek/clk-mtk.h
Normal file
169
drivers/clk/mediatek/clk-mtk.h
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
* Copyright (c) 2014 MediaTek Inc.
|
||||
* Author: James Liao <jamesjj.liao@mediatek.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef __DRV_CLK_MTK_H
|
||||
#define __DRV_CLK_MTK_H
|
||||
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
|
||||
#define MAX_MUX_GATE_BIT 31
|
||||
#define INVALID_MUX_GATE_BIT (MAX_MUX_GATE_BIT + 1)
|
||||
|
||||
#define MHZ (1000 * 1000)
|
||||
|
||||
struct mtk_fixed_factor {
|
||||
int id;
|
||||
const char *name;
|
||||
const char *parent_name;
|
||||
int mult;
|
||||
int div;
|
||||
};
|
||||
|
||||
#define FACTOR(_id, _name, _parent, _mult, _div) { \
|
||||
.id = _id, \
|
||||
.name = _name, \
|
||||
.parent_name = _parent, \
|
||||
.mult = _mult, \
|
||||
.div = _div, \
|
||||
}
|
||||
|
||||
extern void mtk_clk_register_factors(const struct mtk_fixed_factor *clks,
|
||||
int num, struct clk_onecell_data *clk_data);
|
||||
|
||||
struct mtk_composite {
|
||||
int id;
|
||||
const char *name;
|
||||
const char * const *parent_names;
|
||||
const char *parent;
|
||||
unsigned flags;
|
||||
|
||||
uint32_t mux_reg;
|
||||
uint32_t divider_reg;
|
||||
uint32_t gate_reg;
|
||||
|
||||
signed char mux_shift;
|
||||
signed char mux_width;
|
||||
signed char gate_shift;
|
||||
|
||||
signed char divider_shift;
|
||||
signed char divider_width;
|
||||
|
||||
signed char num_parents;
|
||||
};
|
||||
|
||||
#define MUX_GATE(_id, _name, _parents, _reg, _shift, _width, _gate) { \
|
||||
.id = _id, \
|
||||
.name = _name, \
|
||||
.mux_reg = _reg, \
|
||||
.mux_shift = _shift, \
|
||||
.mux_width = _width, \
|
||||
.gate_reg = _reg, \
|
||||
.gate_shift = _gate, \
|
||||
.divider_shift = -1, \
|
||||
.parent_names = _parents, \
|
||||
.num_parents = ARRAY_SIZE(_parents), \
|
||||
.flags = CLK_SET_RATE_PARENT, \
|
||||
}
|
||||
|
||||
#define MUX(_id, _name, _parents, _reg, _shift, _width) { \
|
||||
.id = _id, \
|
||||
.name = _name, \
|
||||
.mux_reg = _reg, \
|
||||
.mux_shift = _shift, \
|
||||
.mux_width = _width, \
|
||||
.gate_shift = -1, \
|
||||
.divider_shift = -1, \
|
||||
.parent_names = _parents, \
|
||||
.num_parents = ARRAY_SIZE(_parents), \
|
||||
.flags = CLK_SET_RATE_PARENT, \
|
||||
}
|
||||
|
||||
#define DIV_GATE(_id, _name, _parent, _gate_reg, _gate_shift, _div_reg, _div_width, _div_shift) { \
|
||||
.id = _id, \
|
||||
.parent = _parent, \
|
||||
.name = _name, \
|
||||
.divider_reg = _div_reg, \
|
||||
.divider_shift = _div_shift, \
|
||||
.divider_width = _div_width, \
|
||||
.gate_reg = _gate_reg, \
|
||||
.gate_shift = _gate_shift, \
|
||||
.mux_shift = -1, \
|
||||
.flags = 0, \
|
||||
}
|
||||
|
||||
struct clk *mtk_clk_register_composite(const struct mtk_composite *mc,
|
||||
void __iomem *base, spinlock_t *lock);
|
||||
|
||||
void mtk_clk_register_composites(const struct mtk_composite *mcs,
|
||||
int num, void __iomem *base, spinlock_t *lock,
|
||||
struct clk_onecell_data *clk_data);
|
||||
|
||||
struct mtk_gate_regs {
|
||||
u32 sta_ofs;
|
||||
u32 clr_ofs;
|
||||
u32 set_ofs;
|
||||
};
|
||||
|
||||
struct mtk_gate {
|
||||
int id;
|
||||
const char *name;
|
||||
const char *parent_name;
|
||||
const struct mtk_gate_regs *regs;
|
||||
int shift;
|
||||
const struct clk_ops *ops;
|
||||
};
|
||||
|
||||
int mtk_clk_register_gates(struct device_node *node, const struct mtk_gate *clks,
|
||||
int num, struct clk_onecell_data *clk_data);
|
||||
|
||||
struct clk_onecell_data *mtk_alloc_clk_data(unsigned int clk_num);
|
||||
|
||||
#define HAVE_RST_BAR BIT(0)
|
||||
|
||||
struct mtk_pll_data {
|
||||
int id;
|
||||
const char *name;
|
||||
uint32_t reg;
|
||||
uint32_t pwr_reg;
|
||||
uint32_t en_mask;
|
||||
uint32_t pd_reg;
|
||||
uint32_t tuner_reg;
|
||||
int pd_shift;
|
||||
unsigned int flags;
|
||||
const struct clk_ops *ops;
|
||||
u32 rst_bar_mask;
|
||||
unsigned long fmax;
|
||||
int pcwbits;
|
||||
uint32_t pcw_reg;
|
||||
int pcw_shift;
|
||||
};
|
||||
|
||||
void __init mtk_clk_register_plls(struct device_node *node,
|
||||
const struct mtk_pll_data *plls, int num_plls,
|
||||
struct clk_onecell_data *clk_data);
|
||||
|
||||
#ifdef CONFIG_RESET_CONTROLLER
|
||||
void mtk_register_reset_controller(struct device_node *np,
|
||||
unsigned int num_regs, int regofs);
|
||||
#else
|
||||
static inline void mtk_register_reset_controller(struct device_node *np,
|
||||
unsigned int num_regs, int regofs)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __DRV_CLK_MTK_H */
|
||||
332
drivers/clk/mediatek/clk-pll.c
Normal file
332
drivers/clk/mediatek/clk-pll.c
Normal file
|
|
@ -0,0 +1,332 @@
|
|||
/*
|
||||
* Copyright (c) 2014 MediaTek Inc.
|
||||
* Author: James Liao <jamesjj.liao@mediatek.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include "clk-mtk.h"
|
||||
|
||||
#define REG_CON0 0
|
||||
#define REG_CON1 4
|
||||
|
||||
#define CON0_BASE_EN BIT(0)
|
||||
#define CON0_PWR_ON BIT(0)
|
||||
#define CON0_ISO_EN BIT(1)
|
||||
#define CON0_PCW_CHG BIT(31)
|
||||
|
||||
#define AUDPLL_TUNER_EN BIT(31)
|
||||
|
||||
#define POSTDIV_MASK 0x7
|
||||
#define INTEGER_BITS 7
|
||||
|
||||
/*
|
||||
* MediaTek PLLs are configured through their pcw value. The pcw value describes
|
||||
* a divider in the PLL feedback loop which consists of 7 bits for the integer
|
||||
* part and the remaining bits (if present) for the fractional part. Also they
|
||||
* have a 3 bit power-of-two post divider.
|
||||
*/
|
||||
|
||||
struct mtk_clk_pll {
|
||||
struct clk_hw hw;
|
||||
void __iomem *base_addr;
|
||||
void __iomem *pd_addr;
|
||||
void __iomem *pwr_addr;
|
||||
void __iomem *tuner_addr;
|
||||
void __iomem *pcw_addr;
|
||||
const struct mtk_pll_data *data;
|
||||
};
|
||||
|
||||
static inline struct mtk_clk_pll *to_mtk_clk_pll(struct clk_hw *hw)
|
||||
{
|
||||
return container_of(hw, struct mtk_clk_pll, hw);
|
||||
}
|
||||
|
||||
static int mtk_pll_is_prepared(struct clk_hw *hw)
|
||||
{
|
||||
struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
|
||||
|
||||
return (readl(pll->base_addr + REG_CON0) & CON0_BASE_EN) != 0;
|
||||
}
|
||||
|
||||
static unsigned long __mtk_pll_recalc_rate(struct mtk_clk_pll *pll, u32 fin,
|
||||
u32 pcw, int postdiv)
|
||||
{
|
||||
int pcwbits = pll->data->pcwbits;
|
||||
int pcwfbits;
|
||||
u64 vco;
|
||||
u8 c = 0;
|
||||
|
||||
/* The fractional part of the PLL divider. */
|
||||
pcwfbits = pcwbits > INTEGER_BITS ? pcwbits - INTEGER_BITS : 0;
|
||||
|
||||
vco = (u64)fin * pcw;
|
||||
|
||||
if (pcwfbits && (vco & GENMASK(pcwfbits - 1, 0)))
|
||||
c = 1;
|
||||
|
||||
vco >>= pcwfbits;
|
||||
|
||||
if (c)
|
||||
vco++;
|
||||
|
||||
return ((unsigned long)vco + postdiv - 1) / postdiv;
|
||||
}
|
||||
|
||||
static void mtk_pll_set_rate_regs(struct mtk_clk_pll *pll, u32 pcw,
|
||||
int postdiv)
|
||||
{
|
||||
u32 con1, pd, val;
|
||||
int pll_en;
|
||||
|
||||
/* set postdiv */
|
||||
pd = readl(pll->pd_addr);
|
||||
pd &= ~(POSTDIV_MASK << pll->data->pd_shift);
|
||||
pd |= (ffs(postdiv) - 1) << pll->data->pd_shift;
|
||||
writel(pd, pll->pd_addr);
|
||||
|
||||
pll_en = readl(pll->base_addr + REG_CON0) & CON0_BASE_EN;
|
||||
|
||||
/* set pcw */
|
||||
val = readl(pll->pcw_addr);
|
||||
|
||||
val &= ~GENMASK(pll->data->pcw_shift + pll->data->pcwbits - 1,
|
||||
pll->data->pcw_shift);
|
||||
val |= pcw << pll->data->pcw_shift;
|
||||
writel(val, pll->pcw_addr);
|
||||
|
||||
con1 = readl(pll->base_addr + REG_CON1);
|
||||
|
||||
if (pll_en)
|
||||
con1 |= CON0_PCW_CHG;
|
||||
|
||||
writel(con1, pll->base_addr + REG_CON1);
|
||||
if (pll->tuner_addr)
|
||||
writel(con1 + 1, pll->tuner_addr);
|
||||
|
||||
if (pll_en)
|
||||
udelay(20);
|
||||
}
|
||||
|
||||
/*
|
||||
* mtk_pll_calc_values - calculate good values for a given input frequency.
|
||||
* @pll: The pll
|
||||
* @pcw: The pcw value (output)
|
||||
* @postdiv: The post divider (output)
|
||||
* @freq: The desired target frequency
|
||||
* @fin: The input frequency
|
||||
*
|
||||
*/
|
||||
static void mtk_pll_calc_values(struct mtk_clk_pll *pll, u32 *pcw, u32 *postdiv,
|
||||
u32 freq, u32 fin)
|
||||
{
|
||||
unsigned long fmin = 1000 * MHZ;
|
||||
u64 _pcw;
|
||||
u32 val;
|
||||
|
||||
if (freq > pll->data->fmax)
|
||||
freq = pll->data->fmax;
|
||||
|
||||
for (val = 0; val < 4; val++) {
|
||||
*postdiv = 1 << val;
|
||||
if (freq * *postdiv >= fmin)
|
||||
break;
|
||||
}
|
||||
|
||||
/* _pcw = freq * postdiv / fin * 2^pcwfbits */
|
||||
_pcw = ((u64)freq << val) << (pll->data->pcwbits - INTEGER_BITS);
|
||||
do_div(_pcw, fin);
|
||||
|
||||
*pcw = (u32)_pcw;
|
||||
}
|
||||
|
||||
static int mtk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
|
||||
u32 pcw = 0;
|
||||
u32 postdiv;
|
||||
|
||||
mtk_pll_calc_values(pll, &pcw, &postdiv, rate, parent_rate);
|
||||
mtk_pll_set_rate_regs(pll, pcw, postdiv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long mtk_pll_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
|
||||
u32 postdiv;
|
||||
u32 pcw;
|
||||
|
||||
postdiv = (readl(pll->pd_addr) >> pll->data->pd_shift) & POSTDIV_MASK;
|
||||
postdiv = 1 << postdiv;
|
||||
|
||||
pcw = readl(pll->pcw_addr) >> pll->data->pcw_shift;
|
||||
pcw &= GENMASK(pll->data->pcwbits - 1, 0);
|
||||
|
||||
return __mtk_pll_recalc_rate(pll, parent_rate, pcw, postdiv);
|
||||
}
|
||||
|
||||
static long mtk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *prate)
|
||||
{
|
||||
struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
|
||||
u32 pcw = 0;
|
||||
int postdiv;
|
||||
|
||||
mtk_pll_calc_values(pll, &pcw, &postdiv, rate, *prate);
|
||||
|
||||
return __mtk_pll_recalc_rate(pll, *prate, pcw, postdiv);
|
||||
}
|
||||
|
||||
static int mtk_pll_prepare(struct clk_hw *hw)
|
||||
{
|
||||
struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
|
||||
u32 r;
|
||||
|
||||
r = readl(pll->pwr_addr) | CON0_PWR_ON;
|
||||
writel(r, pll->pwr_addr);
|
||||
udelay(1);
|
||||
|
||||
r = readl(pll->pwr_addr) & ~CON0_ISO_EN;
|
||||
writel(r, pll->pwr_addr);
|
||||
udelay(1);
|
||||
|
||||
r = readl(pll->base_addr + REG_CON0);
|
||||
r |= pll->data->en_mask;
|
||||
writel(r, pll->base_addr + REG_CON0);
|
||||
|
||||
if (pll->tuner_addr) {
|
||||
r = readl(pll->tuner_addr) | AUDPLL_TUNER_EN;
|
||||
writel(r, pll->tuner_addr);
|
||||
}
|
||||
|
||||
udelay(20);
|
||||
|
||||
if (pll->data->flags & HAVE_RST_BAR) {
|
||||
r = readl(pll->base_addr + REG_CON0);
|
||||
r |= pll->data->rst_bar_mask;
|
||||
writel(r, pll->base_addr + REG_CON0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mtk_pll_unprepare(struct clk_hw *hw)
|
||||
{
|
||||
struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
|
||||
u32 r;
|
||||
|
||||
if (pll->data->flags & HAVE_RST_BAR) {
|
||||
r = readl(pll->base_addr + REG_CON0);
|
||||
r &= ~pll->data->rst_bar_mask;
|
||||
writel(r, pll->base_addr + REG_CON0);
|
||||
}
|
||||
|
||||
if (pll->tuner_addr) {
|
||||
r = readl(pll->tuner_addr) & ~AUDPLL_TUNER_EN;
|
||||
writel(r, pll->tuner_addr);
|
||||
}
|
||||
|
||||
r = readl(pll->base_addr + REG_CON0);
|
||||
r &= ~CON0_BASE_EN;
|
||||
writel(r, pll->base_addr + REG_CON0);
|
||||
|
||||
r = readl(pll->pwr_addr) | CON0_ISO_EN;
|
||||
writel(r, pll->pwr_addr);
|
||||
|
||||
r = readl(pll->pwr_addr) & ~CON0_PWR_ON;
|
||||
writel(r, pll->pwr_addr);
|
||||
}
|
||||
|
||||
static const struct clk_ops mtk_pll_ops = {
|
||||
.is_prepared = mtk_pll_is_prepared,
|
||||
.prepare = mtk_pll_prepare,
|
||||
.unprepare = mtk_pll_unprepare,
|
||||
.recalc_rate = mtk_pll_recalc_rate,
|
||||
.round_rate = mtk_pll_round_rate,
|
||||
.set_rate = mtk_pll_set_rate,
|
||||
};
|
||||
|
||||
static struct clk *mtk_clk_register_pll(const struct mtk_pll_data *data,
|
||||
void __iomem *base)
|
||||
{
|
||||
struct mtk_clk_pll *pll;
|
||||
struct clk_init_data init = {};
|
||||
struct clk *clk;
|
||||
const char *parent_name = "clk26m";
|
||||
|
||||
pll = kzalloc(sizeof(*pll), GFP_KERNEL);
|
||||
if (!pll)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
pll->base_addr = base + data->reg;
|
||||
pll->pwr_addr = base + data->pwr_reg;
|
||||
pll->pd_addr = base + data->pd_reg;
|
||||
pll->pcw_addr = base + data->pcw_reg;
|
||||
if (data->tuner_reg)
|
||||
pll->tuner_addr = base + data->tuner_reg;
|
||||
pll->hw.init = &init;
|
||||
pll->data = data;
|
||||
|
||||
init.name = data->name;
|
||||
init.ops = &mtk_pll_ops;
|
||||
init.parent_names = &parent_name;
|
||||
init.num_parents = 1;
|
||||
|
||||
clk = clk_register(NULL, &pll->hw);
|
||||
|
||||
if (IS_ERR(clk))
|
||||
kfree(pll);
|
||||
|
||||
return clk;
|
||||
}
|
||||
|
||||
void __init mtk_clk_register_plls(struct device_node *node,
|
||||
const struct mtk_pll_data *plls, int num_plls, struct clk_onecell_data *clk_data)
|
||||
{
|
||||
void __iomem *base;
|
||||
int r, i;
|
||||
struct clk *clk;
|
||||
|
||||
base = of_iomap(node, 0);
|
||||
if (!base) {
|
||||
pr_err("%s(): ioremap failed\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_plls; i++) {
|
||||
const struct mtk_pll_data *pll = &plls[i];
|
||||
|
||||
clk = mtk_clk_register_pll(pll, base);
|
||||
|
||||
if (IS_ERR(clk)) {
|
||||
pr_err("Failed to register clk %s: %ld\n",
|
||||
pll->name, PTR_ERR(clk));
|
||||
continue;
|
||||
}
|
||||
|
||||
clk_data->clks[pll->id] = clk;
|
||||
}
|
||||
|
||||
r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
|
||||
if (r)
|
||||
pr_err("%s(): could not register clock provider: %d\n",
|
||||
__func__, r);
|
||||
}
|
||||
97
drivers/clk/mediatek/reset.c
Normal file
97
drivers/clk/mediatek/reset.c
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Copyright (c) 2014 MediaTek Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset-controller.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "clk-mtk.h"
|
||||
|
||||
struct mtk_reset {
|
||||
struct regmap *regmap;
|
||||
int regofs;
|
||||
struct reset_controller_dev rcdev;
|
||||
};
|
||||
|
||||
static int mtk_reset_assert(struct reset_controller_dev *rcdev,
|
||||
unsigned long id)
|
||||
{
|
||||
struct mtk_reset *data = container_of(rcdev, struct mtk_reset, rcdev);
|
||||
|
||||
return regmap_update_bits(data->regmap, data->regofs + ((id / 32) << 2),
|
||||
BIT(id % 32), ~0);
|
||||
}
|
||||
|
||||
static int mtk_reset_deassert(struct reset_controller_dev *rcdev,
|
||||
unsigned long id)
|
||||
{
|
||||
struct mtk_reset *data = container_of(rcdev, struct mtk_reset, rcdev);
|
||||
|
||||
return regmap_update_bits(data->regmap, data->regofs + ((id / 32) << 2),
|
||||
BIT(id % 32), 0);
|
||||
}
|
||||
|
||||
static int mtk_reset(struct reset_controller_dev *rcdev,
|
||||
unsigned long id)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = mtk_reset_assert(rcdev, id);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return mtk_reset_deassert(rcdev, id);
|
||||
}
|
||||
|
||||
static struct reset_control_ops mtk_reset_ops = {
|
||||
.assert = mtk_reset_assert,
|
||||
.deassert = mtk_reset_deassert,
|
||||
.reset = mtk_reset,
|
||||
};
|
||||
|
||||
void mtk_register_reset_controller(struct device_node *np,
|
||||
unsigned int num_regs, int regofs)
|
||||
{
|
||||
struct mtk_reset *data;
|
||||
int ret;
|
||||
struct regmap *regmap;
|
||||
|
||||
regmap = syscon_node_to_regmap(np);
|
||||
if (IS_ERR(regmap)) {
|
||||
pr_err("Cannot find regmap for %s: %ld\n", np->full_name,
|
||||
PTR_ERR(regmap));
|
||||
return;
|
||||
}
|
||||
|
||||
data = kzalloc(sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
data->regmap = regmap;
|
||||
data->regofs = regofs;
|
||||
data->rcdev.owner = THIS_MODULE;
|
||||
data->rcdev.nr_resets = num_regs * 32;
|
||||
data->rcdev.ops = &mtk_reset_ops;
|
||||
data->rcdev.of_node = np;
|
||||
|
||||
ret = reset_controller_register(&data->rcdev);
|
||||
if (ret) {
|
||||
pr_err("could not register reset controller: %d\n", ret);
|
||||
kfree(data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
6
drivers/clk/meson/Makefile
Normal file
6
drivers/clk/meson/Makefile
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#
|
||||
# Makefile for Meson specific clk
|
||||
#
|
||||
|
||||
obj-y += clkc.o clk-pll.o clk-cpu.o
|
||||
obj-y += meson8b-clkc.o
|
||||
242
drivers/clk/meson/clk-cpu.c
Normal file
242
drivers/clk/meson/clk-cpu.c
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Endless Mobile, Inc.
|
||||
* Author: Carlo Caione <carlo@endlessm.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* CPU clock path:
|
||||
*
|
||||
* +-[/N]-----|3|
|
||||
* MUX2 +--[/3]-+----------|2| MUX1
|
||||
* [sys_pll]---|1| |--[/2]------------|1|-|1|
|
||||
* | |---+------------------|0| | |----- [a5_clk]
|
||||
* +--|0| | |
|
||||
* [xtal]---+-------------------------------|0|
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/clk-provider.h>
|
||||
|
||||
#define MESON_CPU_CLK_CNTL1 0x00
|
||||
#define MESON_CPU_CLK_CNTL 0x40
|
||||
|
||||
#define MESON_CPU_CLK_MUX1 BIT(7)
|
||||
#define MESON_CPU_CLK_MUX2 BIT(0)
|
||||
|
||||
#define MESON_N_WIDTH 9
|
||||
#define MESON_N_SHIFT 20
|
||||
#define MESON_SEL_WIDTH 2
|
||||
#define MESON_SEL_SHIFT 2
|
||||
|
||||
#include "clkc.h"
|
||||
|
||||
struct meson_clk_cpu {
|
||||
struct notifier_block clk_nb;
|
||||
const struct clk_div_table *div_table;
|
||||
struct clk_hw hw;
|
||||
void __iomem *base;
|
||||
u16 reg_off;
|
||||
};
|
||||
#define to_meson_clk_cpu_hw(_hw) container_of(_hw, struct meson_clk_cpu, hw)
|
||||
#define to_meson_clk_cpu_nb(_nb) container_of(_nb, struct meson_clk_cpu, clk_nb)
|
||||
|
||||
static long meson_clk_cpu_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *prate)
|
||||
{
|
||||
struct meson_clk_cpu *clk_cpu = to_meson_clk_cpu_hw(hw);
|
||||
|
||||
return divider_round_rate(hw, rate, prate, clk_cpu->div_table,
|
||||
MESON_N_WIDTH, CLK_DIVIDER_ROUND_CLOSEST);
|
||||
}
|
||||
|
||||
static int meson_clk_cpu_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct meson_clk_cpu *clk_cpu = to_meson_clk_cpu_hw(hw);
|
||||
unsigned int div, sel, N = 0;
|
||||
u32 reg;
|
||||
|
||||
div = DIV_ROUND_UP(parent_rate, rate);
|
||||
|
||||
if (div <= 3) {
|
||||
sel = div - 1;
|
||||
} else {
|
||||
sel = 3;
|
||||
N = div / 2;
|
||||
}
|
||||
|
||||
reg = readl(clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL1);
|
||||
reg = PARM_SET(MESON_N_WIDTH, MESON_N_SHIFT, reg, N);
|
||||
writel(reg, clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL1);
|
||||
|
||||
reg = readl(clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL);
|
||||
reg = PARM_SET(MESON_SEL_WIDTH, MESON_SEL_SHIFT, reg, sel);
|
||||
writel(reg, clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long meson_clk_cpu_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct meson_clk_cpu *clk_cpu = to_meson_clk_cpu_hw(hw);
|
||||
unsigned int N, sel;
|
||||
unsigned int div = 1;
|
||||
u32 reg;
|
||||
|
||||
reg = readl(clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL1);
|
||||
N = PARM_GET(MESON_N_WIDTH, MESON_N_SHIFT, reg);
|
||||
|
||||
reg = readl(clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL);
|
||||
sel = PARM_GET(MESON_SEL_WIDTH, MESON_SEL_SHIFT, reg);
|
||||
|
||||
if (sel < 3)
|
||||
div = sel + 1;
|
||||
else
|
||||
div = 2 * N;
|
||||
|
||||
return parent_rate / div;
|
||||
}
|
||||
|
||||
static int meson_clk_cpu_pre_rate_change(struct meson_clk_cpu *clk_cpu,
|
||||
struct clk_notifier_data *ndata)
|
||||
{
|
||||
u32 cpu_clk_cntl;
|
||||
|
||||
/* switch MUX1 to xtal */
|
||||
cpu_clk_cntl = readl(clk_cpu->base + clk_cpu->reg_off
|
||||
+ MESON_CPU_CLK_CNTL);
|
||||
cpu_clk_cntl &= ~MESON_CPU_CLK_MUX1;
|
||||
writel(cpu_clk_cntl, clk_cpu->base + clk_cpu->reg_off
|
||||
+ MESON_CPU_CLK_CNTL);
|
||||
udelay(100);
|
||||
|
||||
/* switch MUX2 to sys-pll */
|
||||
cpu_clk_cntl |= MESON_CPU_CLK_MUX2;
|
||||
writel(cpu_clk_cntl, clk_cpu->base + clk_cpu->reg_off
|
||||
+ MESON_CPU_CLK_CNTL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int meson_clk_cpu_post_rate_change(struct meson_clk_cpu *clk_cpu,
|
||||
struct clk_notifier_data *ndata)
|
||||
{
|
||||
u32 cpu_clk_cntl;
|
||||
|
||||
/* switch MUX1 to divisors' output */
|
||||
cpu_clk_cntl = readl(clk_cpu->base + clk_cpu->reg_off
|
||||
+ MESON_CPU_CLK_CNTL);
|
||||
cpu_clk_cntl |= MESON_CPU_CLK_MUX1;
|
||||
writel(cpu_clk_cntl, clk_cpu->base + clk_cpu->reg_off
|
||||
+ MESON_CPU_CLK_CNTL);
|
||||
udelay(100);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This clock notifier is called when the frequency of the of the parent
|
||||
* PLL clock is to be changed. We use the xtal input as temporary parent
|
||||
* while the PLL frequency is stabilized.
|
||||
*/
|
||||
static int meson_clk_cpu_notifier_cb(struct notifier_block *nb,
|
||||
unsigned long event, void *data)
|
||||
{
|
||||
struct clk_notifier_data *ndata = data;
|
||||
struct meson_clk_cpu *clk_cpu = to_meson_clk_cpu_nb(nb);
|
||||
int ret = 0;
|
||||
|
||||
if (event == PRE_RATE_CHANGE)
|
||||
ret = meson_clk_cpu_pre_rate_change(clk_cpu, ndata);
|
||||
else if (event == POST_RATE_CHANGE)
|
||||
ret = meson_clk_cpu_post_rate_change(clk_cpu, ndata);
|
||||
|
||||
return notifier_from_errno(ret);
|
||||
}
|
||||
|
||||
static const struct clk_ops meson_clk_cpu_ops = {
|
||||
.recalc_rate = meson_clk_cpu_recalc_rate,
|
||||
.round_rate = meson_clk_cpu_round_rate,
|
||||
.set_rate = meson_clk_cpu_set_rate,
|
||||
};
|
||||
|
||||
struct clk *meson_clk_register_cpu(const struct clk_conf *clk_conf,
|
||||
void __iomem *reg_base,
|
||||
spinlock_t *lock)
|
||||
{
|
||||
struct clk *clk;
|
||||
struct clk *pclk;
|
||||
struct meson_clk_cpu *clk_cpu;
|
||||
struct clk_init_data init;
|
||||
int ret;
|
||||
|
||||
clk_cpu = kzalloc(sizeof(*clk_cpu), GFP_KERNEL);
|
||||
if (!clk_cpu)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
clk_cpu->base = reg_base;
|
||||
clk_cpu->reg_off = clk_conf->reg_off;
|
||||
clk_cpu->div_table = clk_conf->conf.div_table;
|
||||
clk_cpu->clk_nb.notifier_call = meson_clk_cpu_notifier_cb;
|
||||
|
||||
init.name = clk_conf->clk_name;
|
||||
init.ops = &meson_clk_cpu_ops;
|
||||
init.flags = clk_conf->flags | CLK_GET_RATE_NOCACHE;
|
||||
init.flags |= CLK_SET_RATE_PARENT;
|
||||
init.parent_names = clk_conf->clks_parent;
|
||||
init.num_parents = 1;
|
||||
|
||||
clk_cpu->hw.init = &init;
|
||||
|
||||
pclk = __clk_lookup(clk_conf->clks_parent[0]);
|
||||
if (!pclk) {
|
||||
pr_err("%s: could not lookup parent clock %s\n",
|
||||
__func__, clk_conf->clks_parent[0]);
|
||||
ret = -EINVAL;
|
||||
goto free_clk;
|
||||
}
|
||||
|
||||
ret = clk_notifier_register(pclk, &clk_cpu->clk_nb);
|
||||
if (ret) {
|
||||
pr_err("%s: failed to register clock notifier for %s\n",
|
||||
__func__, clk_conf->clk_name);
|
||||
goto free_clk;
|
||||
}
|
||||
|
||||
clk = clk_register(NULL, &clk_cpu->hw);
|
||||
if (IS_ERR(clk)) {
|
||||
ret = PTR_ERR(clk);
|
||||
goto unregister_clk_nb;
|
||||
}
|
||||
|
||||
return clk;
|
||||
|
||||
unregister_clk_nb:
|
||||
clk_notifier_unregister(pclk, &clk_cpu->clk_nb);
|
||||
free_clk:
|
||||
kfree(clk_cpu);
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
227
drivers/clk/meson/clk-pll.c
Normal file
227
drivers/clk/meson/clk-pll.c
Normal file
|
|
@ -0,0 +1,227 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Endless Mobile, Inc.
|
||||
* Author: Carlo Caione <carlo@endlessm.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* In the most basic form, a Meson PLL is composed as follows:
|
||||
*
|
||||
* PLL
|
||||
* +------------------------------+
|
||||
* | |
|
||||
* in -----[ /N ]---[ *M ]---[ >>OD ]----->> out
|
||||
* | ^ ^ |
|
||||
* +------------------------------+
|
||||
* | |
|
||||
* FREF VCO
|
||||
*
|
||||
* out = (in * M / N) >> OD
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include "clkc.h"
|
||||
|
||||
#define MESON_PLL_RESET BIT(29)
|
||||
#define MESON_PLL_LOCK BIT(31)
|
||||
|
||||
struct meson_clk_pll {
|
||||
struct clk_hw hw;
|
||||
void __iomem *base;
|
||||
struct pll_conf *conf;
|
||||
unsigned int rate_count;
|
||||
spinlock_t *lock;
|
||||
};
|
||||
#define to_meson_clk_pll(_hw) container_of(_hw, struct meson_clk_pll, hw)
|
||||
|
||||
static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct meson_clk_pll *pll = to_meson_clk_pll(hw);
|
||||
struct parm *p;
|
||||
unsigned long parent_rate_mhz = parent_rate / 1000000;
|
||||
unsigned long rate_mhz;
|
||||
u16 n, m, od;
|
||||
u32 reg;
|
||||
|
||||
p = &pll->conf->n;
|
||||
reg = readl(pll->base + p->reg_off);
|
||||
n = PARM_GET(p->width, p->shift, reg);
|
||||
|
||||
p = &pll->conf->m;
|
||||
reg = readl(pll->base + p->reg_off);
|
||||
m = PARM_GET(p->width, p->shift, reg);
|
||||
|
||||
p = &pll->conf->od;
|
||||
reg = readl(pll->base + p->reg_off);
|
||||
od = PARM_GET(p->width, p->shift, reg);
|
||||
|
||||
rate_mhz = (parent_rate_mhz * m / n) >> od;
|
||||
|
||||
return rate_mhz * 1000000;
|
||||
}
|
||||
|
||||
static long meson_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
struct meson_clk_pll *pll = to_meson_clk_pll(hw);
|
||||
const struct pll_rate_table *rate_table = pll->conf->rate_table;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < pll->rate_count; i++) {
|
||||
if (rate <= rate_table[i].rate)
|
||||
return rate_table[i].rate;
|
||||
}
|
||||
|
||||
/* else return the smallest value */
|
||||
return rate_table[0].rate;
|
||||
}
|
||||
|
||||
static const struct pll_rate_table *meson_clk_get_pll_settings(struct meson_clk_pll *pll,
|
||||
unsigned long rate)
|
||||
{
|
||||
const struct pll_rate_table *rate_table = pll->conf->rate_table;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < pll->rate_count; i++) {
|
||||
if (rate == rate_table[i].rate)
|
||||
return &rate_table[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int meson_clk_pll_wait_lock(struct meson_clk_pll *pll,
|
||||
struct parm *p_n)
|
||||
{
|
||||
int delay = 24000000;
|
||||
u32 reg;
|
||||
|
||||
while (delay > 0) {
|
||||
reg = readl(pll->base + p_n->reg_off);
|
||||
|
||||
if (reg & MESON_PLL_LOCK)
|
||||
return 0;
|
||||
delay--;
|
||||
}
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct meson_clk_pll *pll = to_meson_clk_pll(hw);
|
||||
struct parm *p;
|
||||
const struct pll_rate_table *rate_set;
|
||||
unsigned long old_rate;
|
||||
int ret = 0;
|
||||
u32 reg;
|
||||
|
||||
if (parent_rate == 0 || rate == 0)
|
||||
return -EINVAL;
|
||||
|
||||
old_rate = rate;
|
||||
|
||||
rate_set = meson_clk_get_pll_settings(pll, rate);
|
||||
if (!rate_set)
|
||||
return -EINVAL;
|
||||
|
||||
/* PLL reset */
|
||||
p = &pll->conf->n;
|
||||
reg = readl(pll->base + p->reg_off);
|
||||
writel(reg | MESON_PLL_RESET, pll->base + p->reg_off);
|
||||
|
||||
reg = PARM_SET(p->width, p->shift, reg, rate_set->n);
|
||||
writel(reg, pll->base + p->reg_off);
|
||||
|
||||
p = &pll->conf->m;
|
||||
reg = readl(pll->base + p->reg_off);
|
||||
reg = PARM_SET(p->width, p->shift, reg, rate_set->m);
|
||||
writel(reg, pll->base + p->reg_off);
|
||||
|
||||
p = &pll->conf->od;
|
||||
reg = readl(pll->base + p->reg_off);
|
||||
reg = PARM_SET(p->width, p->shift, reg, rate_set->od);
|
||||
writel(reg, pll->base + p->reg_off);
|
||||
|
||||
p = &pll->conf->n;
|
||||
ret = meson_clk_pll_wait_lock(pll, p);
|
||||
if (ret) {
|
||||
pr_warn("%s: pll did not lock, trying to restore old rate %lu\n",
|
||||
__func__, old_rate);
|
||||
meson_clk_pll_set_rate(hw, old_rate, parent_rate);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct clk_ops meson_clk_pll_ops = {
|
||||
.recalc_rate = meson_clk_pll_recalc_rate,
|
||||
.round_rate = meson_clk_pll_round_rate,
|
||||
.set_rate = meson_clk_pll_set_rate,
|
||||
};
|
||||
|
||||
static const struct clk_ops meson_clk_pll_ro_ops = {
|
||||
.recalc_rate = meson_clk_pll_recalc_rate,
|
||||
};
|
||||
|
||||
struct clk *meson_clk_register_pll(const struct clk_conf *clk_conf,
|
||||
void __iomem *reg_base,
|
||||
spinlock_t *lock)
|
||||
{
|
||||
struct clk *clk;
|
||||
struct meson_clk_pll *clk_pll;
|
||||
struct clk_init_data init;
|
||||
|
||||
clk_pll = kzalloc(sizeof(*clk_pll), GFP_KERNEL);
|
||||
if (!clk_pll)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
clk_pll->base = reg_base + clk_conf->reg_off;
|
||||
clk_pll->lock = lock;
|
||||
clk_pll->conf = clk_conf->conf.pll;
|
||||
|
||||
init.name = clk_conf->clk_name;
|
||||
init.flags = clk_conf->flags | CLK_GET_RATE_NOCACHE;
|
||||
|
||||
init.parent_names = &clk_conf->clks_parent[0];
|
||||
init.num_parents = 1;
|
||||
init.ops = &meson_clk_pll_ro_ops;
|
||||
|
||||
/* If no rate_table is specified we assume the PLL is read-only */
|
||||
if (clk_pll->conf->rate_table) {
|
||||
int len;
|
||||
|
||||
for (len = 0; clk_pll->conf->rate_table[len].rate != 0; )
|
||||
len++;
|
||||
|
||||
clk_pll->rate_count = len;
|
||||
init.ops = &meson_clk_pll_ops;
|
||||
}
|
||||
|
||||
clk_pll->hw.init = &init;
|
||||
|
||||
clk = clk_register(NULL, &clk_pll->hw);
|
||||
if (IS_ERR(clk))
|
||||
kfree(clk_pll);
|
||||
|
||||
return clk;
|
||||
}
|
||||
250
drivers/clk/meson/clkc.c
Normal file
250
drivers/clk/meson/clkc.c
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Endless Mobile, Inc.
|
||||
* Author: Carlo Caione <carlo@endlessm.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "clkc.h"
|
||||
|
||||
static DEFINE_SPINLOCK(clk_lock);
|
||||
|
||||
static struct clk **clks;
|
||||
static struct clk_onecell_data clk_data;
|
||||
|
||||
struct clk ** __init meson_clk_init(struct device_node *np,
|
||||
unsigned long nr_clks)
|
||||
{
|
||||
clks = kcalloc(nr_clks, sizeof(*clks), GFP_KERNEL);
|
||||
if (!clks)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
clk_data.clks = clks;
|
||||
clk_data.clk_num = nr_clks;
|
||||
of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
|
||||
|
||||
return clks;
|
||||
}
|
||||
|
||||
static void meson_clk_add_lookup(struct clk *clk, unsigned int id)
|
||||
{
|
||||
if (clks && id)
|
||||
clks[id] = clk;
|
||||
}
|
||||
|
||||
static struct clk * __init
|
||||
meson_clk_register_composite(const struct clk_conf *clk_conf,
|
||||
void __iomem *clk_base)
|
||||
{
|
||||
struct clk *clk;
|
||||
struct clk_mux *mux = NULL;
|
||||
struct clk_divider *div = NULL;
|
||||
struct clk_gate *gate = NULL;
|
||||
const struct clk_ops *mux_ops = NULL;
|
||||
const struct composite_conf *composite_conf;
|
||||
|
||||
composite_conf = clk_conf->conf.composite;
|
||||
|
||||
if (clk_conf->num_parents > 1) {
|
||||
mux = kzalloc(sizeof(*mux), GFP_KERNEL);
|
||||
if (!mux)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
mux->reg = clk_base + clk_conf->reg_off
|
||||
+ composite_conf->mux_parm.reg_off;
|
||||
mux->shift = composite_conf->mux_parm.shift;
|
||||
mux->mask = BIT(composite_conf->mux_parm.width) - 1;
|
||||
mux->flags = composite_conf->mux_flags;
|
||||
mux->lock = &clk_lock;
|
||||
mux->table = composite_conf->mux_table;
|
||||
mux_ops = (composite_conf->mux_flags & CLK_MUX_READ_ONLY) ?
|
||||
&clk_mux_ro_ops : &clk_mux_ops;
|
||||
}
|
||||
|
||||
if (MESON_PARM_APPLICABLE(&composite_conf->div_parm)) {
|
||||
div = kzalloc(sizeof(*div), GFP_KERNEL);
|
||||
if (!div) {
|
||||
clk = ERR_PTR(-ENOMEM);
|
||||
goto error;
|
||||
}
|
||||
|
||||
div->reg = clk_base + clk_conf->reg_off
|
||||
+ composite_conf->div_parm.reg_off;
|
||||
div->shift = composite_conf->div_parm.shift;
|
||||
div->width = composite_conf->div_parm.width;
|
||||
div->lock = &clk_lock;
|
||||
div->flags = composite_conf->div_flags;
|
||||
div->table = composite_conf->div_table;
|
||||
}
|
||||
|
||||
if (MESON_PARM_APPLICABLE(&composite_conf->gate_parm)) {
|
||||
gate = kzalloc(sizeof(*gate), GFP_KERNEL);
|
||||
if (!gate) {
|
||||
clk = ERR_PTR(-ENOMEM);
|
||||
goto error;
|
||||
}
|
||||
|
||||
gate->reg = clk_base + clk_conf->reg_off
|
||||
+ composite_conf->div_parm.reg_off;
|
||||
gate->bit_idx = composite_conf->gate_parm.shift;
|
||||
gate->flags = composite_conf->gate_flags;
|
||||
gate->lock = &clk_lock;
|
||||
}
|
||||
|
||||
clk = clk_register_composite(NULL, clk_conf->clk_name,
|
||||
clk_conf->clks_parent,
|
||||
clk_conf->num_parents,
|
||||
mux ? &mux->hw : NULL, mux_ops,
|
||||
div ? &div->hw : NULL, &clk_divider_ops,
|
||||
gate ? &gate->hw : NULL, &clk_gate_ops,
|
||||
clk_conf->flags);
|
||||
if (IS_ERR(clk))
|
||||
goto error;
|
||||
|
||||
return clk;
|
||||
|
||||
error:
|
||||
kfree(gate);
|
||||
kfree(div);
|
||||
kfree(mux);
|
||||
|
||||
return clk;
|
||||
}
|
||||
|
||||
static struct clk * __init
|
||||
meson_clk_register_fixed_factor(const struct clk_conf *clk_conf,
|
||||
void __iomem *clk_base)
|
||||
{
|
||||
struct clk *clk;
|
||||
const struct fixed_fact_conf *fixed_fact_conf;
|
||||
const struct parm *p;
|
||||
unsigned int mult, div;
|
||||
u32 reg;
|
||||
|
||||
fixed_fact_conf = &clk_conf->conf.fixed_fact;
|
||||
|
||||
mult = clk_conf->conf.fixed_fact.mult;
|
||||
div = clk_conf->conf.fixed_fact.div;
|
||||
|
||||
if (!mult) {
|
||||
mult = 1;
|
||||
p = &fixed_fact_conf->mult_parm;
|
||||
if (MESON_PARM_APPLICABLE(p)) {
|
||||
reg = readl(clk_base + clk_conf->reg_off + p->reg_off);
|
||||
mult = PARM_GET(p->width, p->shift, reg);
|
||||
}
|
||||
}
|
||||
|
||||
if (!div) {
|
||||
div = 1;
|
||||
p = &fixed_fact_conf->div_parm;
|
||||
if (MESON_PARM_APPLICABLE(p)) {
|
||||
reg = readl(clk_base + clk_conf->reg_off + p->reg_off);
|
||||
mult = PARM_GET(p->width, p->shift, reg);
|
||||
}
|
||||
}
|
||||
|
||||
clk = clk_register_fixed_factor(NULL,
|
||||
clk_conf->clk_name,
|
||||
clk_conf->clks_parent[0],
|
||||
clk_conf->flags,
|
||||
mult, div);
|
||||
|
||||
return clk;
|
||||
}
|
||||
|
||||
static struct clk * __init
|
||||
meson_clk_register_fixed_rate(const struct clk_conf *clk_conf,
|
||||
void __iomem *clk_base)
|
||||
{
|
||||
struct clk *clk;
|
||||
const struct fixed_rate_conf *fixed_rate_conf;
|
||||
const struct parm *r;
|
||||
unsigned long rate;
|
||||
u32 reg;
|
||||
|
||||
fixed_rate_conf = &clk_conf->conf.fixed_rate;
|
||||
rate = fixed_rate_conf->rate;
|
||||
|
||||
if (!rate) {
|
||||
r = &fixed_rate_conf->rate_parm;
|
||||
reg = readl(clk_base + clk_conf->reg_off + r->reg_off);
|
||||
rate = PARM_GET(r->width, r->shift, reg);
|
||||
}
|
||||
|
||||
rate *= 1000000;
|
||||
|
||||
clk = clk_register_fixed_rate(NULL,
|
||||
clk_conf->clk_name,
|
||||
clk_conf->num_parents
|
||||
? clk_conf->clks_parent[0] : NULL,
|
||||
clk_conf->flags, rate);
|
||||
|
||||
return clk;
|
||||
}
|
||||
|
||||
void __init meson_clk_register_clks(const struct clk_conf *clk_confs,
|
||||
size_t nr_confs,
|
||||
void __iomem *clk_base)
|
||||
{
|
||||
unsigned int i;
|
||||
struct clk *clk = NULL;
|
||||
|
||||
for (i = 0; i < nr_confs; i++) {
|
||||
const struct clk_conf *clk_conf = &clk_confs[i];
|
||||
|
||||
switch (clk_conf->clk_type) {
|
||||
case CLK_FIXED_RATE:
|
||||
clk = meson_clk_register_fixed_rate(clk_conf,
|
||||
clk_base);
|
||||
break;
|
||||
case CLK_FIXED_FACTOR:
|
||||
clk = meson_clk_register_fixed_factor(clk_conf,
|
||||
clk_base);
|
||||
break;
|
||||
case CLK_COMPOSITE:
|
||||
clk = meson_clk_register_composite(clk_conf,
|
||||
clk_base);
|
||||
break;
|
||||
case CLK_CPU:
|
||||
clk = meson_clk_register_cpu(clk_conf, clk_base,
|
||||
&clk_lock);
|
||||
break;
|
||||
case CLK_PLL:
|
||||
clk = meson_clk_register_pll(clk_conf, clk_base,
|
||||
&clk_lock);
|
||||
break;
|
||||
default:
|
||||
clk = NULL;
|
||||
}
|
||||
|
||||
if (!clk) {
|
||||
pr_err("%s: unknown clock type %d\n", __func__,
|
||||
clk_conf->clk_type);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (IS_ERR(clk)) {
|
||||
pr_warn("%s: Unable to create %s clock\n", __func__,
|
||||
clk_conf->clk_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
meson_clk_add_lookup(clk, clk_conf->clk_id);
|
||||
}
|
||||
}
|
||||
187
drivers/clk/meson/clkc.h
Normal file
187
drivers/clk/meson/clkc.h
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Endless Mobile, Inc.
|
||||
* Author: Carlo Caione <carlo@endlessm.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __CLKC_H
|
||||
#define __CLKC_H
|
||||
|
||||
#define PMASK(width) GENMASK(width - 1, 0)
|
||||
#define SETPMASK(width, shift) GENMASK(shift + width - 1, shift)
|
||||
#define CLRPMASK(width, shift) (~SETPMASK(width, shift))
|
||||
|
||||
#define PARM_GET(width, shift, reg) \
|
||||
(((reg) & SETPMASK(width, shift)) >> (shift))
|
||||
#define PARM_SET(width, shift, reg, val) \
|
||||
(((reg) & CLRPMASK(width, shift)) | (val << (shift)))
|
||||
|
||||
#define MESON_PARM_APPLICABLE(p) (!!((p)->width))
|
||||
|
||||
struct parm {
|
||||
u16 reg_off;
|
||||
u8 shift;
|
||||
u8 width;
|
||||
};
|
||||
#define PARM(_r, _s, _w) \
|
||||
{ \
|
||||
.reg_off = (_r), \
|
||||
.shift = (_s), \
|
||||
.width = (_w), \
|
||||
} \
|
||||
|
||||
struct pll_rate_table {
|
||||
unsigned long rate;
|
||||
u16 m;
|
||||
u16 n;
|
||||
u16 od;
|
||||
};
|
||||
#define PLL_RATE(_r, _m, _n, _od) \
|
||||
{ \
|
||||
.rate = (_r), \
|
||||
.m = (_m), \
|
||||
.n = (_n), \
|
||||
.od = (_od), \
|
||||
} \
|
||||
|
||||
struct pll_conf {
|
||||
const struct pll_rate_table *rate_table;
|
||||
struct parm m;
|
||||
struct parm n;
|
||||
struct parm od;
|
||||
};
|
||||
|
||||
struct fixed_fact_conf {
|
||||
unsigned int div;
|
||||
unsigned int mult;
|
||||
struct parm div_parm;
|
||||
struct parm mult_parm;
|
||||
};
|
||||
|
||||
struct fixed_rate_conf {
|
||||
unsigned long rate;
|
||||
struct parm rate_parm;
|
||||
};
|
||||
|
||||
struct composite_conf {
|
||||
struct parm mux_parm;
|
||||
struct parm div_parm;
|
||||
struct parm gate_parm;
|
||||
struct clk_div_table *div_table;
|
||||
u32 *mux_table;
|
||||
u8 mux_flags;
|
||||
u8 div_flags;
|
||||
u8 gate_flags;
|
||||
};
|
||||
|
||||
#define PNAME(x) static const char *x[]
|
||||
|
||||
enum clk_type {
|
||||
CLK_FIXED_FACTOR,
|
||||
CLK_FIXED_RATE,
|
||||
CLK_COMPOSITE,
|
||||
CLK_CPU,
|
||||
CLK_PLL,
|
||||
};
|
||||
|
||||
struct clk_conf {
|
||||
u16 reg_off;
|
||||
enum clk_type clk_type;
|
||||
unsigned int clk_id;
|
||||
const char *clk_name;
|
||||
const char **clks_parent;
|
||||
int num_parents;
|
||||
unsigned long flags;
|
||||
union {
|
||||
struct fixed_fact_conf fixed_fact;
|
||||
struct fixed_rate_conf fixed_rate;
|
||||
const struct composite_conf *composite;
|
||||
struct pll_conf *pll;
|
||||
const struct clk_div_table *div_table;
|
||||
} conf;
|
||||
};
|
||||
|
||||
#define FIXED_RATE_P(_ro, _ci, _cn, _f, _c) \
|
||||
{ \
|
||||
.reg_off = (_ro), \
|
||||
.clk_type = CLK_FIXED_RATE, \
|
||||
.clk_id = (_ci), \
|
||||
.clk_name = (_cn), \
|
||||
.flags = (_f), \
|
||||
.conf.fixed_rate.rate_parm = _c, \
|
||||
} \
|
||||
|
||||
#define FIXED_RATE(_ci, _cn, _f, _r) \
|
||||
{ \
|
||||
.clk_type = CLK_FIXED_RATE, \
|
||||
.clk_id = (_ci), \
|
||||
.clk_name = (_cn), \
|
||||
.flags = (_f), \
|
||||
.conf.fixed_rate.rate = (_r), \
|
||||
} \
|
||||
|
||||
#define PLL(_ro, _ci, _cn, _cp, _f, _c) \
|
||||
{ \
|
||||
.reg_off = (_ro), \
|
||||
.clk_type = CLK_PLL, \
|
||||
.clk_id = (_ci), \
|
||||
.clk_name = (_cn), \
|
||||
.clks_parent = (_cp), \
|
||||
.num_parents = ARRAY_SIZE(_cp), \
|
||||
.flags = (_f), \
|
||||
.conf.pll = (_c), \
|
||||
} \
|
||||
|
||||
#define FIXED_FACTOR_DIV(_ci, _cn, _cp, _f, _d) \
|
||||
{ \
|
||||
.clk_type = CLK_FIXED_FACTOR, \
|
||||
.clk_id = (_ci), \
|
||||
.clk_name = (_cn), \
|
||||
.clks_parent = (_cp), \
|
||||
.num_parents = ARRAY_SIZE(_cp), \
|
||||
.conf.fixed_fact.div = (_d), \
|
||||
} \
|
||||
|
||||
#define CPU(_ro, _ci, _cn, _cp, _dt) \
|
||||
{ \
|
||||
.reg_off = (_ro), \
|
||||
.clk_type = CLK_CPU, \
|
||||
.clk_id = (_ci), \
|
||||
.clk_name = (_cn), \
|
||||
.clks_parent = (_cp), \
|
||||
.num_parents = ARRAY_SIZE(_cp), \
|
||||
.conf.div_table = (_dt), \
|
||||
} \
|
||||
|
||||
#define COMPOSITE(_ro, _ci, _cn, _cp, _f, _c) \
|
||||
{ \
|
||||
.reg_off = (_ro), \
|
||||
.clk_type = CLK_COMPOSITE, \
|
||||
.clk_id = (_ci), \
|
||||
.clk_name = (_cn), \
|
||||
.clks_parent = (_cp), \
|
||||
.num_parents = ARRAY_SIZE(_cp), \
|
||||
.flags = (_f), \
|
||||
.conf.composite = (_c), \
|
||||
} \
|
||||
|
||||
struct clk **meson_clk_init(struct device_node *np, unsigned long nr_clks);
|
||||
void meson_clk_register_clks(const struct clk_conf *clk_confs,
|
||||
unsigned int nr_confs, void __iomem *clk_base);
|
||||
struct clk *meson_clk_register_cpu(const struct clk_conf *clk_conf,
|
||||
void __iomem *reg_base, spinlock_t *lock);
|
||||
struct clk *meson_clk_register_pll(const struct clk_conf *clk_conf,
|
||||
void __iomem *reg_base, spinlock_t *lock);
|
||||
|
||||
#endif /* __CLKC_H */
|
||||
196
drivers/clk/meson/meson8b-clkc.c
Normal file
196
drivers/clk/meson/meson8b-clkc.c
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Endless Mobile, Inc.
|
||||
* Author: Carlo Caione <carlo@endlessm.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/slab.h>
|
||||
#include <dt-bindings/clock/meson8b-clkc.h>
|
||||
|
||||
#include "clkc.h"
|
||||
|
||||
#define MESON8B_REG_CTL0_ADDR 0x0000
|
||||
#define MESON8B_REG_SYS_CPU_CNTL1 0x015c
|
||||
#define MESON8B_REG_HHI_MPEG 0x0174
|
||||
#define MESON8B_REG_MALI 0x01b0
|
||||
#define MESON8B_REG_PLL_FIXED 0x0280
|
||||
#define MESON8B_REG_PLL_SYS 0x0300
|
||||
#define MESON8B_REG_PLL_VID 0x0320
|
||||
|
||||
static const struct pll_rate_table sys_pll_rate_table[] = {
|
||||
PLL_RATE(312000000, 52, 1, 2),
|
||||
PLL_RATE(336000000, 56, 1, 2),
|
||||
PLL_RATE(360000000, 60, 1, 2),
|
||||
PLL_RATE(384000000, 64, 1, 2),
|
||||
PLL_RATE(408000000, 68, 1, 2),
|
||||
PLL_RATE(432000000, 72, 1, 2),
|
||||
PLL_RATE(456000000, 76, 1, 2),
|
||||
PLL_RATE(480000000, 80, 1, 2),
|
||||
PLL_RATE(504000000, 84, 1, 2),
|
||||
PLL_RATE(528000000, 88, 1, 2),
|
||||
PLL_RATE(552000000, 92, 1, 2),
|
||||
PLL_RATE(576000000, 96, 1, 2),
|
||||
PLL_RATE(600000000, 50, 1, 1),
|
||||
PLL_RATE(624000000, 52, 1, 1),
|
||||
PLL_RATE(648000000, 54, 1, 1),
|
||||
PLL_RATE(672000000, 56, 1, 1),
|
||||
PLL_RATE(696000000, 58, 1, 1),
|
||||
PLL_RATE(720000000, 60, 1, 1),
|
||||
PLL_RATE(744000000, 62, 1, 1),
|
||||
PLL_RATE(768000000, 64, 1, 1),
|
||||
PLL_RATE(792000000, 66, 1, 1),
|
||||
PLL_RATE(816000000, 68, 1, 1),
|
||||
PLL_RATE(840000000, 70, 1, 1),
|
||||
PLL_RATE(864000000, 72, 1, 1),
|
||||
PLL_RATE(888000000, 74, 1, 1),
|
||||
PLL_RATE(912000000, 76, 1, 1),
|
||||
PLL_RATE(936000000, 78, 1, 1),
|
||||
PLL_RATE(960000000, 80, 1, 1),
|
||||
PLL_RATE(984000000, 82, 1, 1),
|
||||
PLL_RATE(1008000000, 84, 1, 1),
|
||||
PLL_RATE(1032000000, 86, 1, 1),
|
||||
PLL_RATE(1056000000, 88, 1, 1),
|
||||
PLL_RATE(1080000000, 90, 1, 1),
|
||||
PLL_RATE(1104000000, 92, 1, 1),
|
||||
PLL_RATE(1128000000, 94, 1, 1),
|
||||
PLL_RATE(1152000000, 96, 1, 1),
|
||||
PLL_RATE(1176000000, 98, 1, 1),
|
||||
PLL_RATE(1200000000, 50, 1, 0),
|
||||
PLL_RATE(1224000000, 51, 1, 0),
|
||||
PLL_RATE(1248000000, 52, 1, 0),
|
||||
PLL_RATE(1272000000, 53, 1, 0),
|
||||
PLL_RATE(1296000000, 54, 1, 0),
|
||||
PLL_RATE(1320000000, 55, 1, 0),
|
||||
PLL_RATE(1344000000, 56, 1, 0),
|
||||
PLL_RATE(1368000000, 57, 1, 0),
|
||||
PLL_RATE(1392000000, 58, 1, 0),
|
||||
PLL_RATE(1416000000, 59, 1, 0),
|
||||
PLL_RATE(1440000000, 60, 1, 0),
|
||||
PLL_RATE(1464000000, 61, 1, 0),
|
||||
PLL_RATE(1488000000, 62, 1, 0),
|
||||
PLL_RATE(1512000000, 63, 1, 0),
|
||||
PLL_RATE(1536000000, 64, 1, 0),
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
|
||||
static const struct clk_div_table cpu_div_table[] = {
|
||||
{ .val = 1, .div = 1 },
|
||||
{ .val = 2, .div = 2 },
|
||||
{ .val = 3, .div = 3 },
|
||||
{ .val = 2, .div = 4 },
|
||||
{ .val = 3, .div = 6 },
|
||||
{ .val = 4, .div = 8 },
|
||||
{ .val = 5, .div = 10 },
|
||||
{ .val = 6, .div = 12 },
|
||||
{ .val = 7, .div = 14 },
|
||||
{ .val = 8, .div = 16 },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
|
||||
PNAME(p_xtal) = { "xtal" };
|
||||
PNAME(p_fclk_div) = { "fixed_pll" };
|
||||
PNAME(p_cpu_clk) = { "sys_pll" };
|
||||
PNAME(p_clk81) = { "fclk_div3", "fclk_div4", "fclk_div5" };
|
||||
PNAME(p_mali) = { "fclk_div3", "fclk_div4", "fclk_div5",
|
||||
"fclk_div7", "zero" };
|
||||
|
||||
static u32 mux_table_clk81[] = { 6, 5, 7 };
|
||||
static u32 mux_table_mali[] = { 6, 5, 7, 4, 0 };
|
||||
|
||||
static struct pll_conf pll_confs = {
|
||||
.m = PARM(0x00, 0, 9),
|
||||
.n = PARM(0x00, 9, 5),
|
||||
.od = PARM(0x00, 16, 2),
|
||||
};
|
||||
|
||||
static struct pll_conf sys_pll_conf = {
|
||||
.m = PARM(0x00, 0, 9),
|
||||
.n = PARM(0x00, 9, 5),
|
||||
.od = PARM(0x00, 16, 2),
|
||||
.rate_table = sys_pll_rate_table,
|
||||
};
|
||||
|
||||
static const struct composite_conf clk81_conf __initconst = {
|
||||
.mux_table = mux_table_clk81,
|
||||
.mux_flags = CLK_MUX_READ_ONLY,
|
||||
.mux_parm = PARM(0x00, 12, 3),
|
||||
.div_parm = PARM(0x00, 0, 7),
|
||||
.gate_parm = PARM(0x00, 7, 1),
|
||||
};
|
||||
|
||||
static const struct composite_conf mali_conf __initconst = {
|
||||
.mux_table = mux_table_mali,
|
||||
.mux_parm = PARM(0x00, 9, 3),
|
||||
.div_parm = PARM(0x00, 0, 7),
|
||||
.gate_parm = PARM(0x00, 8, 1),
|
||||
};
|
||||
|
||||
static const struct clk_conf meson8b_xtal_conf __initconst =
|
||||
FIXED_RATE_P(MESON8B_REG_CTL0_ADDR, CLKID_XTAL, "xtal",
|
||||
CLK_IS_ROOT, PARM(0x00, 4, 7));
|
||||
|
||||
static const struct clk_conf meson8b_clk_confs[] __initconst = {
|
||||
FIXED_RATE(CLKID_ZERO, "zero", CLK_IS_ROOT, 0),
|
||||
PLL(MESON8B_REG_PLL_FIXED, CLKID_PLL_FIXED, "fixed_pll",
|
||||
p_xtal, 0, &pll_confs),
|
||||
PLL(MESON8B_REG_PLL_VID, CLKID_PLL_VID, "vid_pll",
|
||||
p_xtal, 0, &pll_confs),
|
||||
PLL(MESON8B_REG_PLL_SYS, CLKID_PLL_SYS, "sys_pll",
|
||||
p_xtal, 0, &sys_pll_conf),
|
||||
FIXED_FACTOR_DIV(CLKID_FCLK_DIV2, "fclk_div2", p_fclk_div, 0, 2),
|
||||
FIXED_FACTOR_DIV(CLKID_FCLK_DIV3, "fclk_div3", p_fclk_div, 0, 3),
|
||||
FIXED_FACTOR_DIV(CLKID_FCLK_DIV4, "fclk_div4", p_fclk_div, 0, 4),
|
||||
FIXED_FACTOR_DIV(CLKID_FCLK_DIV5, "fclk_div5", p_fclk_div, 0, 5),
|
||||
FIXED_FACTOR_DIV(CLKID_FCLK_DIV7, "fclk_div7", p_fclk_div, 0, 7),
|
||||
CPU(MESON8B_REG_SYS_CPU_CNTL1, CLKID_CPUCLK, "a5_clk", p_cpu_clk,
|
||||
cpu_div_table),
|
||||
COMPOSITE(MESON8B_REG_HHI_MPEG, CLKID_CLK81, "clk81", p_clk81,
|
||||
CLK_SET_RATE_NO_REPARENT | CLK_IGNORE_UNUSED, &clk81_conf),
|
||||
COMPOSITE(MESON8B_REG_MALI, CLKID_MALI, "mali", p_mali,
|
||||
CLK_IGNORE_UNUSED, &mali_conf),
|
||||
};
|
||||
|
||||
static void __init meson8b_clkc_init(struct device_node *np)
|
||||
{
|
||||
void __iomem *clk_base;
|
||||
|
||||
if (!meson_clk_init(np, CLK_NR_CLKS))
|
||||
return;
|
||||
|
||||
/* XTAL */
|
||||
clk_base = of_iomap(np, 0);
|
||||
if (!clk_base) {
|
||||
pr_err("%s: Unable to map xtal base\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
meson_clk_register_clks(&meson8b_xtal_conf, 1, clk_base);
|
||||
iounmap(clk_base);
|
||||
|
||||
/* Generic clocks and PLLs */
|
||||
clk_base = of_iomap(np, 1);
|
||||
if (!clk_base) {
|
||||
pr_err("%s: Unable to map clk base\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
meson_clk_register_clks(meson8b_clk_confs,
|
||||
ARRAY_SIZE(meson8b_clk_confs),
|
||||
clk_base);
|
||||
}
|
||||
CLK_OF_DECLARE(meson8b_clock, "amlogic,meson8b-clkc", meson8b_clkc_init);
|
||||
|
|
@ -12,3 +12,5 @@ obj-$(CONFIG_MACH_MMP2_DT) += clk-of-mmp2.o
|
|||
obj-$(CONFIG_CPU_PXA168) += clk-pxa168.o
|
||||
obj-$(CONFIG_CPU_PXA910) += clk-pxa910.o
|
||||
obj-$(CONFIG_CPU_MMP2) += clk-mmp2.o
|
||||
|
||||
obj-y += clk-of-pxa1928.o
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ static void clk_apbc_unprepare(struct clk_hw *hw)
|
|||
spin_unlock_irqrestore(apbc->lock, flags);
|
||||
}
|
||||
|
||||
struct clk_ops clk_apbc_ops = {
|
||||
static struct clk_ops clk_apbc_ops = {
|
||||
.prepare = clk_apbc_prepare,
|
||||
.unprepare = clk_apbc_unprepare,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ static void clk_apmu_disable(struct clk_hw *hw)
|
|||
spin_unlock_irqrestore(apmu->lock, flags);
|
||||
}
|
||||
|
||||
struct clk_ops clk_apmu_ops = {
|
||||
static struct clk_ops clk_apmu_ops = {
|
||||
.enable = clk_apmu_enable,
|
||||
.disable = clk_apmu_disable,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -63,10 +63,8 @@ static struct mmp_clk_factor_masks uart_factor_masks = {
|
|||
};
|
||||
|
||||
static struct mmp_clk_factor_tbl uart_factor_tbl[] = {
|
||||
{.num = 14634, .den = 2165}, /*14.745MHZ */
|
||||
{.num = 8125, .den = 1536}, /*14.745MHZ */
|
||||
{.num = 3521, .den = 689}, /*19.23MHZ */
|
||||
{.num = 9679, .den = 5728}, /*58.9824MHZ */
|
||||
{.num = 15850, .den = 9451}, /*59.429MHZ */
|
||||
};
|
||||
|
||||
static const char *uart_parent[] = {"uart_pll", "vctcxo"};
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
#define APBC_TWSI4 0x7c
|
||||
#define APBC_TWSI5 0x80
|
||||
#define APBC_KPC 0x18
|
||||
#define APBC_TIMER 0x24
|
||||
#define APBC_UART0 0x2c
|
||||
#define APBC_UART1 0x30
|
||||
#define APBC_UART2 0x34
|
||||
|
|
@ -98,10 +99,8 @@ static struct mmp_clk_factor_masks uart_factor_masks = {
|
|||
};
|
||||
|
||||
static struct mmp_clk_factor_tbl uart_factor_tbl[] = {
|
||||
{.num = 14634, .den = 2165}, /*14.745MHZ */
|
||||
{.num = 8125, .den = 1536}, /*14.745MHZ */
|
||||
{.num = 3521, .den = 689}, /*19.23MHZ */
|
||||
{.num = 9679, .den = 5728}, /*58.9824MHZ */
|
||||
{.num = 15850, .den = 9451}, /*59.429MHZ */
|
||||
};
|
||||
|
||||
static void mmp2_pll_init(struct mmp2_clk_unit *pxa_unit)
|
||||
|
|
@ -134,6 +133,9 @@ static DEFINE_SPINLOCK(ssp2_lock);
|
|||
static DEFINE_SPINLOCK(ssp3_lock);
|
||||
static const char *ssp_parent_names[] = {"vctcxo_4", "vctcxo_2", "vctcxo", "pll1_16"};
|
||||
|
||||
static DEFINE_SPINLOCK(timer_lock);
|
||||
static const char *timer_parent_names[] = {"clk32", "vctcxo_2", "vctcxo_4", "vctcxo"};
|
||||
|
||||
static DEFINE_SPINLOCK(reset_lock);
|
||||
|
||||
static struct mmp_param_mux_clk apbc_mux_clks[] = {
|
||||
|
|
@ -145,6 +147,7 @@ static struct mmp_param_mux_clk apbc_mux_clks[] = {
|
|||
{0, "ssp1_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, APBC_SSP1, 4, 3, 0, &ssp1_lock},
|
||||
{0, "ssp2_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, APBC_SSP2, 4, 3, 0, &ssp2_lock},
|
||||
{0, "ssp3_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, APBC_SSP3, 4, 3, 0, &ssp3_lock},
|
||||
{0, "timer_mux", timer_parent_names, ARRAY_SIZE(timer_parent_names), CLK_SET_RATE_PARENT, APBC_TIMER, 4, 3, 0, &timer_lock},
|
||||
};
|
||||
|
||||
static struct mmp_param_gate_clk apbc_gate_clks[] = {
|
||||
|
|
@ -170,6 +173,7 @@ static struct mmp_param_gate_clk apbc_gate_clks[] = {
|
|||
{MMP2_CLK_SSP1, "ssp1_clk", "ssp1_mux", CLK_SET_RATE_PARENT, APBC_SSP1, 0x7, 0x3, 0x0, 0, &ssp1_lock},
|
||||
{MMP2_CLK_SSP2, "ssp2_clk", "ssp2_mux", CLK_SET_RATE_PARENT, APBC_SSP2, 0x7, 0x3, 0x0, 0, &ssp2_lock},
|
||||
{MMP2_CLK_SSP3, "ssp3_clk", "ssp3_mux", CLK_SET_RATE_PARENT, APBC_SSP3, 0x7, 0x3, 0x0, 0, &ssp3_lock},
|
||||
{MMP2_CLK_TIMER, "timer_clk", "timer_mux", CLK_SET_RATE_PARENT, APBC_TIMER, 0x7, 0x3, 0x0, 0, &timer_lock},
|
||||
};
|
||||
|
||||
static void mmp2_apb_periph_clk_init(struct mmp2_clk_unit *pxa_unit)
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
#define APBC_PWM1 0x10
|
||||
#define APBC_PWM2 0x14
|
||||
#define APBC_PWM3 0x18
|
||||
#define APBC_TIMER 0x34
|
||||
#define APBC_SSP0 0x81c
|
||||
#define APBC_SSP1 0x820
|
||||
#define APBC_SSP2 0x84c
|
||||
|
|
@ -58,6 +59,7 @@ static struct mmp_param_fixed_rate_clk fixed_rate_clks[] = {
|
|||
{PXA168_CLK_CLK32, "clk32", NULL, CLK_IS_ROOT, 32768},
|
||||
{PXA168_CLK_VCTCXO, "vctcxo", NULL, CLK_IS_ROOT, 26000000},
|
||||
{PXA168_CLK_PLL1, "pll1", NULL, CLK_IS_ROOT, 624000000},
|
||||
{PXA168_CLK_USB_PLL, "usb_pll", NULL, CLK_IS_ROOT, 480000000},
|
||||
};
|
||||
|
||||
static struct mmp_param_fixed_factor_clk fixed_factor_clks[] = {
|
||||
|
|
@ -70,6 +72,7 @@ static struct mmp_param_fixed_factor_clk fixed_factor_clks[] = {
|
|||
{PXA168_CLK_PLL1_24, "pll1_24", "pll1_12", 1, 2, 0},
|
||||
{PXA168_CLK_PLL1_48, "pll1_48", "pll1_24", 1, 2, 0},
|
||||
{PXA168_CLK_PLL1_96, "pll1_96", "pll1_48", 1, 2, 0},
|
||||
{PXA168_CLK_PLL1_192, "pll1_192", "pll1_96", 1, 2, 0},
|
||||
{PXA168_CLK_PLL1_13, "pll1_13", "pll1", 1, 13, 0},
|
||||
{PXA168_CLK_PLL1_13_1_5, "pll1_13_1_5", "pll1_13", 2, 3, 0},
|
||||
{PXA168_CLK_PLL1_2_1_5, "pll1_2_1_5", "pll1_2", 2, 3, 0},
|
||||
|
|
@ -119,6 +122,9 @@ static DEFINE_SPINLOCK(ssp3_lock);
|
|||
static DEFINE_SPINLOCK(ssp4_lock);
|
||||
static const char *ssp_parent_names[] = {"pll1_96", "pll1_48", "pll1_24", "pll1_12"};
|
||||
|
||||
static DEFINE_SPINLOCK(timer_lock);
|
||||
static const char *timer_parent_names[] = {"pll1_48", "clk32", "pll1_96", "pll1_192"};
|
||||
|
||||
static DEFINE_SPINLOCK(reset_lock);
|
||||
|
||||
static struct mmp_param_mux_clk apbc_mux_clks[] = {
|
||||
|
|
@ -130,6 +136,7 @@ static struct mmp_param_mux_clk apbc_mux_clks[] = {
|
|||
{0, "ssp2_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, APBC_SSP2, 4, 3, 0, &ssp2_lock},
|
||||
{0, "ssp3_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, APBC_SSP3, 4, 3, 0, &ssp3_lock},
|
||||
{0, "ssp4_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, APBC_SSP4, 4, 3, 0, &ssp4_lock},
|
||||
{0, "timer_mux", timer_parent_names, ARRAY_SIZE(timer_parent_names), CLK_SET_RATE_PARENT, APBC_TIMER, 4, 3, 0, &timer_lock},
|
||||
};
|
||||
|
||||
static struct mmp_param_gate_clk apbc_gate_clks[] = {
|
||||
|
|
@ -151,6 +158,7 @@ static struct mmp_param_gate_clk apbc_gate_clks[] = {
|
|||
{PXA168_CLK_SSP2, "ssp2_clk", "ssp2_mux", CLK_SET_RATE_PARENT, APBC_SSP2, 0x3, 0x3, 0x0, 0, &ssp2_lock},
|
||||
{PXA168_CLK_SSP3, "ssp3_clk", "ssp3_mux", CLK_SET_RATE_PARENT, APBC_SSP3, 0x3, 0x3, 0x0, 0, &ssp3_lock},
|
||||
{PXA168_CLK_SSP4, "ssp4_clk", "ssp4_mux", CLK_SET_RATE_PARENT, APBC_SSP4, 0x3, 0x3, 0x0, 0, &ssp4_lock},
|
||||
{PXA168_CLK_TIMER, "timer_clk", "timer_mux", CLK_SET_RATE_PARENT, APBC_TIMER, 0x3, 0x3, 0x0, 0, &timer_lock},
|
||||
};
|
||||
|
||||
static void pxa168_apb_periph_clk_init(struct pxa168_clk_unit *pxa_unit)
|
||||
|
|
|
|||
265
drivers/clk/mmp/clk-of-pxa1928.c
Normal file
265
drivers/clk/mmp/clk-of-pxa1928.c
Normal file
|
|
@ -0,0 +1,265 @@
|
|||
/*
|
||||
* pxa1928 clock framework source file
|
||||
*
|
||||
* Copyright (C) 2015 Linaro, Ltd.
|
||||
* Rob Herring <robh@kernel.org>
|
||||
*
|
||||
* Based on drivers/clk/mmp/clk-of-mmp2.c:
|
||||
* Copyright (C) 2012 Marvell
|
||||
* Chao Xie <xiechao.mail@gmail.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include <dt-bindings/clock/marvell,pxa1928.h>
|
||||
|
||||
#include "clk.h"
|
||||
#include "reset.h"
|
||||
|
||||
#define MPMU_UART_PLL 0x14
|
||||
|
||||
struct pxa1928_clk_unit {
|
||||
struct mmp_clk_unit unit;
|
||||
void __iomem *mpmu_base;
|
||||
void __iomem *apmu_base;
|
||||
void __iomem *apbc_base;
|
||||
void __iomem *apbcp_base;
|
||||
};
|
||||
|
||||
static struct mmp_param_fixed_rate_clk fixed_rate_clks[] = {
|
||||
{0, "clk32", NULL, CLK_IS_ROOT, 32768},
|
||||
{0, "vctcxo", NULL, CLK_IS_ROOT, 26000000},
|
||||
{0, "pll1_624", NULL, CLK_IS_ROOT, 624000000},
|
||||
{0, "pll5p", NULL, CLK_IS_ROOT, 832000000},
|
||||
{0, "pll5", NULL, CLK_IS_ROOT, 1248000000},
|
||||
{0, "usb_pll", NULL, CLK_IS_ROOT, 480000000},
|
||||
};
|
||||
|
||||
static struct mmp_param_fixed_factor_clk fixed_factor_clks[] = {
|
||||
{0, "pll1_d2", "pll1_624", 1, 2, 0},
|
||||
{0, "pll1_d9", "pll1_624", 1, 9, 0},
|
||||
{0, "pll1_d12", "pll1_624", 1, 12, 0},
|
||||
{0, "pll1_d16", "pll1_624", 1, 16, 0},
|
||||
{0, "pll1_d20", "pll1_624", 1, 20, 0},
|
||||
{0, "pll1_416", "pll1_624", 2, 3, 0},
|
||||
{0, "vctcxo_d2", "vctcxo", 1, 2, 0},
|
||||
{0, "vctcxo_d4", "vctcxo", 1, 4, 0},
|
||||
};
|
||||
|
||||
static struct mmp_clk_factor_masks uart_factor_masks = {
|
||||
.factor = 2,
|
||||
.num_mask = 0x1fff,
|
||||
.den_mask = 0x1fff,
|
||||
.num_shift = 16,
|
||||
.den_shift = 0,
|
||||
};
|
||||
|
||||
static struct mmp_clk_factor_tbl uart_factor_tbl[] = {
|
||||
{.num = 832, .den = 234}, /*58.5MHZ */
|
||||
{.num = 1, .den = 1}, /*26MHZ */
|
||||
};
|
||||
|
||||
static void pxa1928_pll_init(struct pxa1928_clk_unit *pxa_unit)
|
||||
{
|
||||
struct clk *clk;
|
||||
struct mmp_clk_unit *unit = &pxa_unit->unit;
|
||||
|
||||
mmp_register_fixed_rate_clks(unit, fixed_rate_clks,
|
||||
ARRAY_SIZE(fixed_rate_clks));
|
||||
|
||||
mmp_register_fixed_factor_clks(unit, fixed_factor_clks,
|
||||
ARRAY_SIZE(fixed_factor_clks));
|
||||
|
||||
clk = mmp_clk_register_factor("uart_pll", "pll1_416",
|
||||
CLK_SET_RATE_PARENT,
|
||||
pxa_unit->mpmu_base + MPMU_UART_PLL,
|
||||
&uart_factor_masks, uart_factor_tbl,
|
||||
ARRAY_SIZE(uart_factor_tbl), NULL);
|
||||
}
|
||||
|
||||
static DEFINE_SPINLOCK(uart0_lock);
|
||||
static DEFINE_SPINLOCK(uart1_lock);
|
||||
static DEFINE_SPINLOCK(uart2_lock);
|
||||
static DEFINE_SPINLOCK(uart3_lock);
|
||||
static const char *uart_parent_names[] = {"uart_pll", "vctcxo"};
|
||||
|
||||
static DEFINE_SPINLOCK(ssp0_lock);
|
||||
static DEFINE_SPINLOCK(ssp1_lock);
|
||||
static const char *ssp_parent_names[] = {"vctcxo_d4", "vctcxo_d2", "vctcxo", "pll1_d12"};
|
||||
|
||||
static DEFINE_SPINLOCK(reset_lock);
|
||||
|
||||
static struct mmp_param_mux_clk apbc_mux_clks[] = {
|
||||
{0, "uart0_mux", uart_parent_names, ARRAY_SIZE(uart_parent_names), CLK_SET_RATE_PARENT, PXA1928_CLK_UART0 * 4, 4, 3, 0, &uart0_lock},
|
||||
{0, "uart1_mux", uart_parent_names, ARRAY_SIZE(uart_parent_names), CLK_SET_RATE_PARENT, PXA1928_CLK_UART1 * 4, 4, 3, 0, &uart1_lock},
|
||||
{0, "uart2_mux", uart_parent_names, ARRAY_SIZE(uart_parent_names), CLK_SET_RATE_PARENT, PXA1928_CLK_UART2 * 4, 4, 3, 0, &uart2_lock},
|
||||
{0, "uart3_mux", uart_parent_names, ARRAY_SIZE(uart_parent_names), CLK_SET_RATE_PARENT, PXA1928_CLK_UART3 * 4, 4, 3, 0, &uart3_lock},
|
||||
{0, "ssp0_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, PXA1928_CLK_SSP0 * 4, 4, 3, 0, &ssp0_lock},
|
||||
{0, "ssp1_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, PXA1928_CLK_SSP1 * 4, 4, 3, 0, &ssp1_lock},
|
||||
};
|
||||
|
||||
static struct mmp_param_gate_clk apbc_gate_clks[] = {
|
||||
{PXA1928_CLK_TWSI0, "twsi0_clk", "vctcxo", CLK_SET_RATE_PARENT, PXA1928_CLK_TWSI0 * 4, 0x3, 0x3, 0x0, 0, &reset_lock},
|
||||
{PXA1928_CLK_TWSI1, "twsi1_clk", "vctcxo", CLK_SET_RATE_PARENT, PXA1928_CLK_TWSI1 * 4, 0x3, 0x3, 0x0, 0, &reset_lock},
|
||||
{PXA1928_CLK_TWSI2, "twsi2_clk", "vctcxo", CLK_SET_RATE_PARENT, PXA1928_CLK_TWSI2 * 4, 0x3, 0x3, 0x0, 0, &reset_lock},
|
||||
{PXA1928_CLK_TWSI3, "twsi3_clk", "vctcxo", CLK_SET_RATE_PARENT, PXA1928_CLK_TWSI3 * 4, 0x3, 0x3, 0x0, 0, &reset_lock},
|
||||
{PXA1928_CLK_TWSI4, "twsi4_clk", "vctcxo", CLK_SET_RATE_PARENT, PXA1928_CLK_TWSI4 * 4, 0x3, 0x3, 0x0, 0, &reset_lock},
|
||||
{PXA1928_CLK_TWSI5, "twsi5_clk", "vctcxo", CLK_SET_RATE_PARENT, PXA1928_CLK_TWSI5 * 4, 0x3, 0x3, 0x0, 0, &reset_lock},
|
||||
{PXA1928_CLK_GPIO, "gpio_clk", "vctcxo", CLK_SET_RATE_PARENT, PXA1928_CLK_GPIO * 4, 0x3, 0x3, 0x0, 0, &reset_lock},
|
||||
{PXA1928_CLK_KPC, "kpc_clk", "clk32", CLK_SET_RATE_PARENT, PXA1928_CLK_KPC * 4, 0x3, 0x3, 0x0, MMP_CLK_GATE_NEED_DELAY, NULL},
|
||||
{PXA1928_CLK_RTC, "rtc_clk", "clk32", CLK_SET_RATE_PARENT, PXA1928_CLK_RTC * 4, 0x83, 0x83, 0x0, MMP_CLK_GATE_NEED_DELAY, NULL},
|
||||
{PXA1928_CLK_PWM0, "pwm0_clk", "vctcxo", CLK_SET_RATE_PARENT, PXA1928_CLK_PWM0 * 4, 0x3, 0x3, 0x0, 0, &reset_lock},
|
||||
{PXA1928_CLK_PWM1, "pwm1_clk", "vctcxo", CLK_SET_RATE_PARENT, PXA1928_CLK_PWM1 * 4, 0x3, 0x3, 0x0, 0, &reset_lock},
|
||||
{PXA1928_CLK_PWM2, "pwm2_clk", "vctcxo", CLK_SET_RATE_PARENT, PXA1928_CLK_PWM2 * 4, 0x3, 0x3, 0x0, 0, &reset_lock},
|
||||
{PXA1928_CLK_PWM3, "pwm3_clk", "vctcxo", CLK_SET_RATE_PARENT, PXA1928_CLK_PWM3 * 4, 0x3, 0x3, 0x0, 0, &reset_lock},
|
||||
/* The gate clocks has mux parent. */
|
||||
{PXA1928_CLK_UART0, "uart0_clk", "uart0_mux", CLK_SET_RATE_PARENT, PXA1928_CLK_UART0 * 4, 0x3, 0x3, 0x0, 0, &uart0_lock},
|
||||
{PXA1928_CLK_UART1, "uart1_clk", "uart1_mux", CLK_SET_RATE_PARENT, PXA1928_CLK_UART1 * 4, 0x3, 0x3, 0x0, 0, &uart1_lock},
|
||||
{PXA1928_CLK_UART2, "uart2_clk", "uart2_mux", CLK_SET_RATE_PARENT, PXA1928_CLK_UART2 * 4, 0x3, 0x3, 0x0, 0, &uart2_lock},
|
||||
{PXA1928_CLK_UART3, "uart3_clk", "uart3_mux", CLK_SET_RATE_PARENT, PXA1928_CLK_UART3 * 4, 0x3, 0x3, 0x0, 0, &uart3_lock},
|
||||
{PXA1928_CLK_SSP0, "ssp0_clk", "ssp0_mux", CLK_SET_RATE_PARENT, PXA1928_CLK_SSP0 * 4, 0x3, 0x3, 0x0, 0, &ssp0_lock},
|
||||
{PXA1928_CLK_SSP1, "ssp1_clk", "ssp1_mux", CLK_SET_RATE_PARENT, PXA1928_CLK_SSP1 * 4, 0x3, 0x3, 0x0, 0, &ssp1_lock},
|
||||
};
|
||||
|
||||
static void pxa1928_apb_periph_clk_init(struct pxa1928_clk_unit *pxa_unit)
|
||||
{
|
||||
struct mmp_clk_unit *unit = &pxa_unit->unit;
|
||||
|
||||
mmp_register_mux_clks(unit, apbc_mux_clks, pxa_unit->apbc_base,
|
||||
ARRAY_SIZE(apbc_mux_clks));
|
||||
|
||||
mmp_register_gate_clks(unit, apbc_gate_clks, pxa_unit->apbc_base,
|
||||
ARRAY_SIZE(apbc_gate_clks));
|
||||
}
|
||||
|
||||
static DEFINE_SPINLOCK(sdh0_lock);
|
||||
static DEFINE_SPINLOCK(sdh1_lock);
|
||||
static DEFINE_SPINLOCK(sdh2_lock);
|
||||
static DEFINE_SPINLOCK(sdh3_lock);
|
||||
static DEFINE_SPINLOCK(sdh4_lock);
|
||||
static const char *sdh_parent_names[] = {"pll1_624", "pll5p", "pll5", "pll1_416"};
|
||||
|
||||
static DEFINE_SPINLOCK(usb_lock);
|
||||
|
||||
static struct mmp_param_mux_clk apmu_mux_clks[] = {
|
||||
{0, "sdh_mux", sdh_parent_names, ARRAY_SIZE(sdh_parent_names), CLK_SET_RATE_PARENT, PXA1928_CLK_SDH0 * 4, 8, 2, 0, &sdh0_lock},
|
||||
};
|
||||
|
||||
static struct mmp_param_div_clk apmu_div_clks[] = {
|
||||
{0, "sdh_div", "sdh_mux", 0, PXA1928_CLK_SDH0 * 4, 10, 4, CLK_DIVIDER_ONE_BASED, &sdh0_lock},
|
||||
};
|
||||
|
||||
static struct mmp_param_gate_clk apmu_gate_clks[] = {
|
||||
{PXA1928_CLK_USB, "usb_clk", "usb_pll", 0, PXA1928_CLK_USB * 4, 0x9, 0x9, 0x0, 0, &usb_lock},
|
||||
{PXA1928_CLK_HSIC, "hsic_clk", "usb_pll", 0, PXA1928_CLK_HSIC * 4, 0x9, 0x9, 0x0, 0, &usb_lock},
|
||||
/* The gate clocks has mux parent. */
|
||||
{PXA1928_CLK_SDH0, "sdh0_clk", "sdh_div", CLK_SET_RATE_PARENT, PXA1928_CLK_SDH0 * 4, 0x1b, 0x1b, 0x0, 0, &sdh0_lock},
|
||||
{PXA1928_CLK_SDH1, "sdh1_clk", "sdh_div", CLK_SET_RATE_PARENT, PXA1928_CLK_SDH1 * 4, 0x1b, 0x1b, 0x0, 0, &sdh1_lock},
|
||||
{PXA1928_CLK_SDH2, "sdh2_clk", "sdh_div", CLK_SET_RATE_PARENT, PXA1928_CLK_SDH2 * 4, 0x1b, 0x1b, 0x0, 0, &sdh2_lock},
|
||||
{PXA1928_CLK_SDH3, "sdh3_clk", "sdh_div", CLK_SET_RATE_PARENT, PXA1928_CLK_SDH3 * 4, 0x1b, 0x1b, 0x0, 0, &sdh3_lock},
|
||||
{PXA1928_CLK_SDH4, "sdh4_clk", "sdh_div", CLK_SET_RATE_PARENT, PXA1928_CLK_SDH4 * 4, 0x1b, 0x1b, 0x0, 0, &sdh4_lock},
|
||||
};
|
||||
|
||||
static void pxa1928_axi_periph_clk_init(struct pxa1928_clk_unit *pxa_unit)
|
||||
{
|
||||
struct mmp_clk_unit *unit = &pxa_unit->unit;
|
||||
|
||||
mmp_register_mux_clks(unit, apmu_mux_clks, pxa_unit->apmu_base,
|
||||
ARRAY_SIZE(apmu_mux_clks));
|
||||
|
||||
mmp_register_div_clks(unit, apmu_div_clks, pxa_unit->apmu_base,
|
||||
ARRAY_SIZE(apmu_div_clks));
|
||||
|
||||
mmp_register_gate_clks(unit, apmu_gate_clks, pxa_unit->apmu_base,
|
||||
ARRAY_SIZE(apmu_gate_clks));
|
||||
}
|
||||
|
||||
static void pxa1928_clk_reset_init(struct device_node *np,
|
||||
struct pxa1928_clk_unit *pxa_unit)
|
||||
{
|
||||
struct mmp_clk_reset_cell *cells;
|
||||
int i, base, nr_resets;
|
||||
|
||||
nr_resets = ARRAY_SIZE(apbc_gate_clks);
|
||||
cells = kcalloc(nr_resets, sizeof(*cells), GFP_KERNEL);
|
||||
if (!cells)
|
||||
return;
|
||||
|
||||
base = 0;
|
||||
for (i = 0; i < nr_resets; i++) {
|
||||
cells[base + i].clk_id = apbc_gate_clks[i].id;
|
||||
cells[base + i].reg =
|
||||
pxa_unit->apbc_base + apbc_gate_clks[i].offset;
|
||||
cells[base + i].flags = 0;
|
||||
cells[base + i].lock = apbc_gate_clks[i].lock;
|
||||
cells[base + i].bits = 0x4;
|
||||
}
|
||||
|
||||
mmp_clk_reset_register(np, cells, nr_resets);
|
||||
}
|
||||
|
||||
static void __init pxa1928_mpmu_clk_init(struct device_node *np)
|
||||
{
|
||||
struct pxa1928_clk_unit *pxa_unit;
|
||||
|
||||
pxa_unit = kzalloc(sizeof(*pxa_unit), GFP_KERNEL);
|
||||
if (!pxa_unit)
|
||||
return;
|
||||
|
||||
pxa_unit->mpmu_base = of_iomap(np, 0);
|
||||
if (!pxa_unit->mpmu_base) {
|
||||
pr_err("failed to map mpmu registers\n");
|
||||
return;
|
||||
}
|
||||
|
||||
pxa1928_pll_init(pxa_unit);
|
||||
}
|
||||
CLK_OF_DECLARE(pxa1928_mpmu_clk, "marvell,pxa1928-mpmu", pxa1928_mpmu_clk_init);
|
||||
|
||||
static void __init pxa1928_apmu_clk_init(struct device_node *np)
|
||||
{
|
||||
struct pxa1928_clk_unit *pxa_unit;
|
||||
|
||||
pxa_unit = kzalloc(sizeof(*pxa_unit), GFP_KERNEL);
|
||||
if (!pxa_unit)
|
||||
return;
|
||||
|
||||
pxa_unit->apmu_base = of_iomap(np, 0);
|
||||
if (!pxa_unit->apmu_base) {
|
||||
pr_err("failed to map apmu registers\n");
|
||||
return;
|
||||
}
|
||||
|
||||
mmp_clk_init(np, &pxa_unit->unit, PXA1928_APMU_NR_CLKS);
|
||||
|
||||
pxa1928_axi_periph_clk_init(pxa_unit);
|
||||
}
|
||||
CLK_OF_DECLARE(pxa1928_apmu_clk, "marvell,pxa1928-apmu", pxa1928_apmu_clk_init);
|
||||
|
||||
static void __init pxa1928_apbc_clk_init(struct device_node *np)
|
||||
{
|
||||
struct pxa1928_clk_unit *pxa_unit;
|
||||
|
||||
pxa_unit = kzalloc(sizeof(*pxa_unit), GFP_KERNEL);
|
||||
if (!pxa_unit)
|
||||
return;
|
||||
|
||||
pxa_unit->apbc_base = of_iomap(np, 0);
|
||||
if (!pxa_unit->apbc_base) {
|
||||
pr_err("failed to map apbc registers\n");
|
||||
return;
|
||||
}
|
||||
|
||||
mmp_clk_init(np, &pxa_unit->unit, PXA1928_APBC_NR_CLKS);
|
||||
|
||||
pxa1928_apb_periph_clk_init(pxa_unit);
|
||||
pxa1928_clk_reset_init(np, pxa_unit);
|
||||
}
|
||||
CLK_OF_DECLARE(pxa1928_apbc_clk, "marvell,pxa1928-apbc", pxa1928_apbc_clk_init);
|
||||
|
|
@ -35,6 +35,8 @@
|
|||
#define APBC_SSP0 0x1c
|
||||
#define APBC_SSP1 0x20
|
||||
#define APBC_SSP2 0x4c
|
||||
#define APBC_TIMER0 0x30
|
||||
#define APBC_TIMER1 0x44
|
||||
#define APBCP_TWSI1 0x28
|
||||
#define APBCP_UART2 0x1c
|
||||
#define APMU_SDH0 0x54
|
||||
|
|
@ -57,6 +59,7 @@ static struct mmp_param_fixed_rate_clk fixed_rate_clks[] = {
|
|||
{PXA910_CLK_CLK32, "clk32", NULL, CLK_IS_ROOT, 32768},
|
||||
{PXA910_CLK_VCTCXO, "vctcxo", NULL, CLK_IS_ROOT, 26000000},
|
||||
{PXA910_CLK_PLL1, "pll1", NULL, CLK_IS_ROOT, 624000000},
|
||||
{PXA910_CLK_USB_PLL, "usb_pll", NULL, CLK_IS_ROOT, 480000000},
|
||||
};
|
||||
|
||||
static struct mmp_param_fixed_factor_clk fixed_factor_clks[] = {
|
||||
|
|
@ -69,6 +72,7 @@ static struct mmp_param_fixed_factor_clk fixed_factor_clks[] = {
|
|||
{PXA910_CLK_PLL1_24, "pll1_24", "pll1_12", 1, 2, 0},
|
||||
{PXA910_CLK_PLL1_48, "pll1_48", "pll1_24", 1, 2, 0},
|
||||
{PXA910_CLK_PLL1_96, "pll1_96", "pll1_48", 1, 2, 0},
|
||||
{PXA910_CLK_PLL1_192, "pll1_192", "pll1_96", 1, 2, 0},
|
||||
{PXA910_CLK_PLL1_13, "pll1_13", "pll1", 1, 13, 0},
|
||||
{PXA910_CLK_PLL1_13_1_5, "pll1_13_1_5", "pll1_13", 2, 3, 0},
|
||||
{PXA910_CLK_PLL1_2_1_5, "pll1_2_1_5", "pll1_2", 2, 3, 0},
|
||||
|
|
@ -115,6 +119,10 @@ static DEFINE_SPINLOCK(ssp0_lock);
|
|||
static DEFINE_SPINLOCK(ssp1_lock);
|
||||
static const char *ssp_parent_names[] = {"pll1_96", "pll1_48", "pll1_24", "pll1_12"};
|
||||
|
||||
static DEFINE_SPINLOCK(timer0_lock);
|
||||
static DEFINE_SPINLOCK(timer1_lock);
|
||||
static const char *timer_parent_names[] = {"pll1_48", "clk32", "pll1_96"};
|
||||
|
||||
static DEFINE_SPINLOCK(reset_lock);
|
||||
|
||||
static struct mmp_param_mux_clk apbc_mux_clks[] = {
|
||||
|
|
@ -122,6 +130,8 @@ static struct mmp_param_mux_clk apbc_mux_clks[] = {
|
|||
{0, "uart1_mux", uart_parent_names, ARRAY_SIZE(uart_parent_names), CLK_SET_RATE_PARENT, APBC_UART1, 4, 3, 0, &uart1_lock},
|
||||
{0, "ssp0_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, APBC_SSP0, 4, 3, 0, &ssp0_lock},
|
||||
{0, "ssp1_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, APBC_SSP1, 4, 3, 0, &ssp1_lock},
|
||||
{0, "timer0_mux", timer_parent_names, ARRAY_SIZE(timer_parent_names), CLK_SET_RATE_PARENT, APBC_TIMER0, 4, 3, 0, &timer0_lock},
|
||||
{0, "timer1_mux", timer_parent_names, ARRAY_SIZE(timer_parent_names), CLK_SET_RATE_PARENT, APBC_TIMER1, 4, 3, 0, &timer1_lock},
|
||||
};
|
||||
|
||||
static struct mmp_param_mux_clk apbcp_mux_clks[] = {
|
||||
|
|
@ -142,6 +152,8 @@ static struct mmp_param_gate_clk apbc_gate_clks[] = {
|
|||
{PXA910_CLK_UART1, "uart1_clk", "uart1_mux", CLK_SET_RATE_PARENT, APBC_UART1, 0x3, 0x3, 0x0, 0, &uart1_lock},
|
||||
{PXA910_CLK_SSP0, "ssp0_clk", "ssp0_mux", CLK_SET_RATE_PARENT, APBC_SSP0, 0x3, 0x3, 0x0, 0, &ssp0_lock},
|
||||
{PXA910_CLK_SSP1, "ssp1_clk", "ssp1_mux", CLK_SET_RATE_PARENT, APBC_SSP1, 0x3, 0x3, 0x0, 0, &ssp1_lock},
|
||||
{PXA910_CLK_TIMER0, "timer0_clk", "timer0_mux", CLK_SET_RATE_PARENT, APBC_TIMER0, 0x3, 0x3, 0x0, 0, &timer0_lock},
|
||||
{PXA910_CLK_TIMER1, "timer1_clk", "timer1_mux", CLK_SET_RATE_PARENT, APBC_TIMER1, 0x3, 0x3, 0x0, 0, &timer1_lock},
|
||||
};
|
||||
|
||||
static struct mmp_param_gate_clk apbcp_gate_clks[] = {
|
||||
|
|
|
|||
|
|
@ -163,6 +163,7 @@ static const struct clk_gating_soc_desc a370_gating_desc[] __initconst = {
|
|||
{ "pex1", "pex1_en", 9, 0 },
|
||||
{ "sata0", NULL, 15, 0 },
|
||||
{ "sdio", NULL, 17, 0 },
|
||||
{ "crypto", NULL, 23, CLK_IGNORE_UNUSED },
|
||||
{ "tdm", NULL, 25, 0 },
|
||||
{ "ddr", NULL, 28, CLK_IGNORE_UNUSED },
|
||||
{ "sata1", NULL, 30, 0 },
|
||||
|
|
|
|||
|
|
@ -77,12 +77,12 @@ static void __init clk_misc_init(void)
|
|||
writel_relaxed(30 << BP_FRAC_IOFRAC, FRAC + SET);
|
||||
}
|
||||
|
||||
static const char *sel_pll[] __initdata = { "pll", "ref_xtal", };
|
||||
static const char *sel_cpu[] __initdata = { "ref_cpu", "ref_xtal", };
|
||||
static const char *sel_pix[] __initdata = { "ref_pix", "ref_xtal", };
|
||||
static const char *sel_io[] __initdata = { "ref_io", "ref_xtal", };
|
||||
static const char *cpu_sels[] __initdata = { "cpu_pll", "cpu_xtal", };
|
||||
static const char *emi_sels[] __initdata = { "emi_pll", "emi_xtal", };
|
||||
static const char *const sel_pll[] __initconst = { "pll", "ref_xtal", };
|
||||
static const char *const sel_cpu[] __initconst = { "ref_cpu", "ref_xtal", };
|
||||
static const char *const sel_pix[] __initconst = { "ref_pix", "ref_xtal", };
|
||||
static const char *const sel_io[] __initconst = { "ref_io", "ref_xtal", };
|
||||
static const char *const cpu_sels[] __initconst = { "cpu_pll", "cpu_xtal", };
|
||||
static const char *const emi_sels[] __initconst = { "emi_pll", "emi_xtal", };
|
||||
|
||||
enum imx23_clk {
|
||||
ref_xtal, pll, ref_cpu, ref_emi, ref_pix, ref_io, saif_sel,
|
||||
|
|
|
|||
|
|
@ -125,15 +125,15 @@ static void __init clk_misc_init(void)
|
|||
writel_relaxed(val, FRAC0);
|
||||
}
|
||||
|
||||
static const char *sel_cpu[] __initdata = { "ref_cpu", "ref_xtal", };
|
||||
static const char *sel_io0[] __initdata = { "ref_io0", "ref_xtal", };
|
||||
static const char *sel_io1[] __initdata = { "ref_io1", "ref_xtal", };
|
||||
static const char *sel_pix[] __initdata = { "ref_pix", "ref_xtal", };
|
||||
static const char *sel_gpmi[] __initdata = { "ref_gpmi", "ref_xtal", };
|
||||
static const char *sel_pll0[] __initdata = { "pll0", "ref_xtal", };
|
||||
static const char *cpu_sels[] __initdata = { "cpu_pll", "cpu_xtal", };
|
||||
static const char *emi_sels[] __initdata = { "emi_pll", "emi_xtal", };
|
||||
static const char *ptp_sels[] __initdata = { "ref_xtal", "pll0", };
|
||||
static const char *const sel_cpu[] __initconst = { "ref_cpu", "ref_xtal", };
|
||||
static const char *const sel_io0[] __initconst = { "ref_io0", "ref_xtal", };
|
||||
static const char *const sel_io1[] __initconst = { "ref_io1", "ref_xtal", };
|
||||
static const char *const sel_pix[] __initconst = { "ref_pix", "ref_xtal", };
|
||||
static const char *const sel_gpmi[] __initconst = { "ref_gpmi", "ref_xtal", };
|
||||
static const char *const sel_pll0[] __initconst = { "pll0", "ref_xtal", };
|
||||
static const char *const cpu_sels[] __initconst = { "cpu_pll", "cpu_xtal", };
|
||||
static const char *const emi_sels[] __initconst = { "emi_pll", "emi_xtal", };
|
||||
static const char *const ptp_sels[] __initconst = { "ref_xtal", "pll0", };
|
||||
|
||||
enum imx28_clk {
|
||||
ref_xtal, pll0, pll1, pll2, ref_cpu, ref_emi, ref_io0, ref_io1,
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ static inline struct clk *mxs_clk_gate(const char *name,
|
|||
}
|
||||
|
||||
static inline struct clk *mxs_clk_mux(const char *name, void __iomem *reg,
|
||||
u8 shift, u8 width, const char **parent_names, int num_parents)
|
||||
u8 shift, u8 width, const char *const *parent_names, int num_parents)
|
||||
{
|
||||
return clk_register_mux(NULL, name, parent_names, num_parents,
|
||||
CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
|
||||
|
|
|
|||
2
drivers/clk/nxp/Makefile
Normal file
2
drivers/clk/nxp/Makefile
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
obj-$(CONFIG_ARCH_LPC18XX) += clk-lpc18xx-cgu.o
|
||||
obj-$(CONFIG_ARCH_LPC18XX) += clk-lpc18xx-ccu.o
|
||||
293
drivers/clk/nxp/clk-lpc18xx-ccu.c
Normal file
293
drivers/clk/nxp/clk-lpc18xx-ccu.c
Normal file
|
|
@ -0,0 +1,293 @@
|
|||
/*
|
||||
* Clk driver for NXP LPC18xx/LPC43xx Clock Control Unit (CCU)
|
||||
*
|
||||
* Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include <dt-bindings/clock/lpc18xx-ccu.h>
|
||||
|
||||
/* Bit defines for CCU branch configuration register */
|
||||
#define LPC18XX_CCU_RUN BIT(0)
|
||||
#define LPC18XX_CCU_AUTO BIT(1)
|
||||
#define LPC18XX_CCU_DIV BIT(5)
|
||||
#define LPC18XX_CCU_DIVSTAT BIT(27)
|
||||
|
||||
/* CCU branch feature bits */
|
||||
#define CCU_BRANCH_IS_BUS BIT(0)
|
||||
#define CCU_BRANCH_HAVE_DIV2 BIT(1)
|
||||
|
||||
#define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw)
|
||||
|
||||
struct lpc18xx_branch_clk_data {
|
||||
const char **name;
|
||||
int num;
|
||||
};
|
||||
|
||||
struct lpc18xx_clk_branch {
|
||||
const char *base_name;
|
||||
const char *name;
|
||||
u16 offset;
|
||||
u16 flags;
|
||||
struct clk *clk;
|
||||
struct clk_gate gate;
|
||||
};
|
||||
|
||||
static struct lpc18xx_clk_branch clk_branches[] = {
|
||||
{"base_apb3_clk", "apb3_bus", CLK_APB3_BUS, CCU_BRANCH_IS_BUS},
|
||||
{"base_apb3_clk", "apb3_i2c1", CLK_APB3_I2C1, 0},
|
||||
{"base_apb3_clk", "apb3_dac", CLK_APB3_DAC, 0},
|
||||
{"base_apb3_clk", "apb3_adc0", CLK_APB3_ADC0, 0},
|
||||
{"base_apb3_clk", "apb3_adc1", CLK_APB3_ADC1, 0},
|
||||
{"base_apb3_clk", "apb3_can0", CLK_APB3_CAN0, 0},
|
||||
|
||||
{"base_apb1_clk", "apb1_bus", CLK_APB1_BUS, CCU_BRANCH_IS_BUS},
|
||||
{"base_apb1_clk", "apb1_mc_pwm", CLK_APB1_MOTOCON_PWM, 0},
|
||||
{"base_apb1_clk", "apb1_i2c0", CLK_APB1_I2C0, 0},
|
||||
{"base_apb1_clk", "apb1_i2s", CLK_APB1_I2S, 0},
|
||||
{"base_apb1_clk", "apb1_can1", CLK_APB1_CAN1, 0},
|
||||
|
||||
{"base_spifi_clk", "spifi", CLK_SPIFI, 0},
|
||||
|
||||
{"base_cpu_clk", "cpu_bus", CLK_CPU_BUS, CCU_BRANCH_IS_BUS},
|
||||
{"base_cpu_clk", "cpu_spifi", CLK_CPU_SPIFI, 0},
|
||||
{"base_cpu_clk", "cpu_gpio", CLK_CPU_GPIO, 0},
|
||||
{"base_cpu_clk", "cpu_lcd", CLK_CPU_LCD, 0},
|
||||
{"base_cpu_clk", "cpu_ethernet", CLK_CPU_ETHERNET, 0},
|
||||
{"base_cpu_clk", "cpu_usb0", CLK_CPU_USB0, 0},
|
||||
{"base_cpu_clk", "cpu_emc", CLK_CPU_EMC, 0},
|
||||
{"base_cpu_clk", "cpu_sdio", CLK_CPU_SDIO, 0},
|
||||
{"base_cpu_clk", "cpu_dma", CLK_CPU_DMA, 0},
|
||||
{"base_cpu_clk", "cpu_core", CLK_CPU_CORE, 0},
|
||||
{"base_cpu_clk", "cpu_sct", CLK_CPU_SCT, 0},
|
||||
{"base_cpu_clk", "cpu_usb1", CLK_CPU_USB1, 0},
|
||||
{"base_cpu_clk", "cpu_emcdiv", CLK_CPU_EMCDIV, CCU_BRANCH_HAVE_DIV2},
|
||||
{"base_cpu_clk", "cpu_flasha", CLK_CPU_FLASHA, CCU_BRANCH_HAVE_DIV2},
|
||||
{"base_cpu_clk", "cpu_flashb", CLK_CPU_FLASHB, CCU_BRANCH_HAVE_DIV2},
|
||||
{"base_cpu_clk", "cpu_m0app", CLK_CPU_M0APP, CCU_BRANCH_HAVE_DIV2},
|
||||
{"base_cpu_clk", "cpu_adchs", CLK_CPU_ADCHS, CCU_BRANCH_HAVE_DIV2},
|
||||
{"base_cpu_clk", "cpu_eeprom", CLK_CPU_EEPROM, CCU_BRANCH_HAVE_DIV2},
|
||||
{"base_cpu_clk", "cpu_wwdt", CLK_CPU_WWDT, 0},
|
||||
{"base_cpu_clk", "cpu_uart0", CLK_CPU_UART0, 0},
|
||||
{"base_cpu_clk", "cpu_uart1", CLK_CPU_UART1, 0},
|
||||
{"base_cpu_clk", "cpu_ssp0", CLK_CPU_SSP0, 0},
|
||||
{"base_cpu_clk", "cpu_timer0", CLK_CPU_TIMER0, 0},
|
||||
{"base_cpu_clk", "cpu_timer1", CLK_CPU_TIMER1, 0},
|
||||
{"base_cpu_clk", "cpu_scu", CLK_CPU_SCU, 0},
|
||||
{"base_cpu_clk", "cpu_creg", CLK_CPU_CREG, 0},
|
||||
{"base_cpu_clk", "cpu_ritimer", CLK_CPU_RITIMER, 0},
|
||||
{"base_cpu_clk", "cpu_uart2", CLK_CPU_UART2, 0},
|
||||
{"base_cpu_clk", "cpu_uart3", CLK_CPU_UART3, 0},
|
||||
{"base_cpu_clk", "cpu_timer2", CLK_CPU_TIMER2, 0},
|
||||
{"base_cpu_clk", "cpu_timer3", CLK_CPU_TIMER3, 0},
|
||||
{"base_cpu_clk", "cpu_ssp1", CLK_CPU_SSP1, 0},
|
||||
{"base_cpu_clk", "cpu_qei", CLK_CPU_QEI, 0},
|
||||
|
||||
{"base_periph_clk", "periph_bus", CLK_PERIPH_BUS, CCU_BRANCH_IS_BUS},
|
||||
{"base_periph_clk", "periph_core", CLK_PERIPH_CORE, 0},
|
||||
{"base_periph_clk", "periph_sgpio", CLK_PERIPH_SGPIO, 0},
|
||||
|
||||
{"base_usb0_clk", "usb0", CLK_USB0, 0},
|
||||
{"base_usb1_clk", "usb1", CLK_USB1, 0},
|
||||
{"base_spi_clk", "spi", CLK_SPI, 0},
|
||||
{"base_adchs_clk", "adchs", CLK_ADCHS, 0},
|
||||
|
||||
{"base_audio_clk", "audio", CLK_AUDIO, 0},
|
||||
{"base_uart3_clk", "apb2_uart3", CLK_APB2_UART3, 0},
|
||||
{"base_uart2_clk", "apb2_uart2", CLK_APB2_UART2, 0},
|
||||
{"base_uart1_clk", "apb0_uart1", CLK_APB0_UART1, 0},
|
||||
{"base_uart0_clk", "apb0_uart0", CLK_APB0_UART0, 0},
|
||||
{"base_ssp1_clk", "apb2_ssp1", CLK_APB2_SSP1, 0},
|
||||
{"base_ssp0_clk", "apb0_ssp0", CLK_APB0_SSP0, 0},
|
||||
{"base_sdio_clk", "sdio", CLK_SDIO, 0},
|
||||
};
|
||||
|
||||
static struct clk *lpc18xx_ccu_branch_clk_get(struct of_phandle_args *clkspec,
|
||||
void *data)
|
||||
{
|
||||
struct lpc18xx_branch_clk_data *clk_data = data;
|
||||
unsigned int offset = clkspec->args[0];
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(clk_branches); i++) {
|
||||
if (clk_branches[i].offset != offset)
|
||||
continue;
|
||||
|
||||
for (j = 0; j < clk_data->num; j++) {
|
||||
if (!strcmp(clk_branches[i].base_name, clk_data->name[j]))
|
||||
return clk_branches[i].clk;
|
||||
}
|
||||
}
|
||||
|
||||
pr_err("%s: invalid clock offset %d\n", __func__, offset);
|
||||
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
static int lpc18xx_ccu_gate_endisable(struct clk_hw *hw, bool enable)
|
||||
{
|
||||
struct clk_gate *gate = to_clk_gate(hw);
|
||||
u32 val;
|
||||
|
||||
/*
|
||||
* Divider field is write only, so divider stat field must
|
||||
* be read so divider field can be set accordingly.
|
||||
*/
|
||||
val = clk_readl(gate->reg);
|
||||
if (val & LPC18XX_CCU_DIVSTAT)
|
||||
val |= LPC18XX_CCU_DIV;
|
||||
|
||||
if (enable) {
|
||||
val |= LPC18XX_CCU_RUN;
|
||||
} else {
|
||||
/*
|
||||
* To safely disable a branch clock a squence of two separate
|
||||
* writes must be used. First write should set the AUTO bit
|
||||
* and the next write should clear the RUN bit.
|
||||
*/
|
||||
val |= LPC18XX_CCU_AUTO;
|
||||
clk_writel(val, gate->reg);
|
||||
|
||||
val &= ~LPC18XX_CCU_RUN;
|
||||
}
|
||||
|
||||
clk_writel(val, gate->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lpc18xx_ccu_gate_enable(struct clk_hw *hw)
|
||||
{
|
||||
return lpc18xx_ccu_gate_endisable(hw, true);
|
||||
}
|
||||
|
||||
static void lpc18xx_ccu_gate_disable(struct clk_hw *hw)
|
||||
{
|
||||
lpc18xx_ccu_gate_endisable(hw, false);
|
||||
}
|
||||
|
||||
static int lpc18xx_ccu_gate_is_enabled(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_gate *gate = to_clk_gate(hw);
|
||||
|
||||
return clk_readl(gate->reg) & LPC18XX_CCU_RUN;
|
||||
}
|
||||
|
||||
static const struct clk_ops lpc18xx_ccu_gate_ops = {
|
||||
.enable = lpc18xx_ccu_gate_enable,
|
||||
.disable = lpc18xx_ccu_gate_disable,
|
||||
.is_enabled = lpc18xx_ccu_gate_is_enabled,
|
||||
};
|
||||
|
||||
static void lpc18xx_ccu_register_branch_gate_div(struct lpc18xx_clk_branch *branch,
|
||||
void __iomem *reg_base,
|
||||
const char *parent)
|
||||
{
|
||||
const struct clk_ops *div_ops = NULL;
|
||||
struct clk_divider *div = NULL;
|
||||
struct clk_hw *div_hw = NULL;
|
||||
|
||||
if (branch->flags & CCU_BRANCH_HAVE_DIV2) {
|
||||
div = kzalloc(sizeof(*div), GFP_KERNEL);
|
||||
if (!div)
|
||||
return;
|
||||
|
||||
div->reg = branch->offset + reg_base;
|
||||
div->flags = CLK_DIVIDER_READ_ONLY;
|
||||
div->shift = 27;
|
||||
div->width = 1;
|
||||
|
||||
div_hw = &div->hw;
|
||||
div_ops = &clk_divider_ops;
|
||||
}
|
||||
|
||||
branch->gate.reg = branch->offset + reg_base;
|
||||
branch->gate.bit_idx = 0;
|
||||
|
||||
branch->clk = clk_register_composite(NULL, branch->name, &parent, 1,
|
||||
NULL, NULL,
|
||||
div_hw, div_ops,
|
||||
&branch->gate.hw, &lpc18xx_ccu_gate_ops, 0);
|
||||
if (IS_ERR(branch->clk)) {
|
||||
kfree(div);
|
||||
pr_warn("%s: failed to register %s\n", __func__, branch->name);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Grab essential branch clocks for CPU and SDRAM */
|
||||
switch (branch->offset) {
|
||||
case CLK_CPU_EMC:
|
||||
case CLK_CPU_CORE:
|
||||
case CLK_CPU_CREG:
|
||||
case CLK_CPU_EMCDIV:
|
||||
clk_prepare_enable(branch->clk);
|
||||
}
|
||||
}
|
||||
|
||||
static void lpc18xx_ccu_register_branch_clks(void __iomem *reg_base,
|
||||
const char *base_name)
|
||||
{
|
||||
const char *parent = base_name;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(clk_branches); i++) {
|
||||
if (strcmp(clk_branches[i].base_name, base_name))
|
||||
continue;
|
||||
|
||||
lpc18xx_ccu_register_branch_gate_div(&clk_branches[i], reg_base,
|
||||
parent);
|
||||
|
||||
if (clk_branches[i].flags & CCU_BRANCH_IS_BUS)
|
||||
parent = clk_branches[i].name;
|
||||
}
|
||||
}
|
||||
|
||||
static void __init lpc18xx_ccu_init(struct device_node *np)
|
||||
{
|
||||
struct lpc18xx_branch_clk_data *clk_data;
|
||||
void __iomem *reg_base;
|
||||
int i, ret;
|
||||
|
||||
reg_base = of_iomap(np, 0);
|
||||
if (!reg_base) {
|
||||
pr_warn("%s: failed to map address range\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
|
||||
if (!clk_data)
|
||||
return;
|
||||
|
||||
clk_data->num = of_property_count_strings(np, "clock-names");
|
||||
clk_data->name = kcalloc(clk_data->num, sizeof(char *), GFP_KERNEL);
|
||||
if (!clk_data->name) {
|
||||
kfree(clk_data);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < clk_data->num; i++) {
|
||||
ret = of_property_read_string_index(np, "clock-names", i,
|
||||
&clk_data->name[i]);
|
||||
if (ret) {
|
||||
pr_warn("%s: failed to get clock name at idx %d\n",
|
||||
__func__, i);
|
||||
continue;
|
||||
}
|
||||
|
||||
lpc18xx_ccu_register_branch_clks(reg_base, clk_data->name[i]);
|
||||
}
|
||||
|
||||
of_clk_add_provider(np, lpc18xx_ccu_branch_clk_get, clk_data);
|
||||
}
|
||||
CLK_OF_DECLARE(lpc18xx_ccu, "nxp,lpc1850-ccu", lpc18xx_ccu_init);
|
||||
635
drivers/clk/nxp/clk-lpc18xx-cgu.c
Normal file
635
drivers/clk/nxp/clk-lpc18xx-cgu.c
Normal file
|
|
@ -0,0 +1,635 @@
|
|||
/*
|
||||
* Clk driver for NXP LPC18xx/LPC43xx Clock Generation Unit (CGU)
|
||||
*
|
||||
* Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
|
||||
#include <dt-bindings/clock/lpc18xx-cgu.h>
|
||||
|
||||
/* Clock Generation Unit (CGU) registers */
|
||||
#define LPC18XX_CGU_XTAL_OSC_CTRL 0x018
|
||||
#define LPC18XX_CGU_PLL0USB_STAT 0x01c
|
||||
#define LPC18XX_CGU_PLL0USB_CTRL 0x020
|
||||
#define LPC18XX_CGU_PLL0USB_MDIV 0x024
|
||||
#define LPC18XX_CGU_PLL0USB_NP_DIV 0x028
|
||||
#define LPC18XX_CGU_PLL0AUDIO_STAT 0x02c
|
||||
#define LPC18XX_CGU_PLL0AUDIO_CTRL 0x030
|
||||
#define LPC18XX_CGU_PLL0AUDIO_MDIV 0x034
|
||||
#define LPC18XX_CGU_PLL0AUDIO_NP_DIV 0x038
|
||||
#define LPC18XX_CGU_PLL0AUDIO_FRAC 0x03c
|
||||
#define LPC18XX_CGU_PLL1_STAT 0x040
|
||||
#define LPC18XX_CGU_PLL1_CTRL 0x044
|
||||
#define LPC18XX_PLL1_CTRL_FBSEL BIT(6)
|
||||
#define LPC18XX_PLL1_CTRL_DIRECT BIT(7)
|
||||
#define LPC18XX_CGU_IDIV_CTRL(n) (0x048 + (n) * sizeof(u32))
|
||||
#define LPC18XX_CGU_BASE_CLK(id) (0x05c + (id) * sizeof(u32))
|
||||
#define LPC18XX_CGU_PLL_CTRL_OFFSET 0x4
|
||||
|
||||
/* PLL0 bits common to both audio and USB PLL */
|
||||
#define LPC18XX_PLL0_STAT_LOCK BIT(0)
|
||||
#define LPC18XX_PLL0_CTRL_PD BIT(0)
|
||||
#define LPC18XX_PLL0_CTRL_BYPASS BIT(1)
|
||||
#define LPC18XX_PLL0_CTRL_DIRECTI BIT(2)
|
||||
#define LPC18XX_PLL0_CTRL_DIRECTO BIT(3)
|
||||
#define LPC18XX_PLL0_CTRL_CLKEN BIT(4)
|
||||
#define LPC18XX_PLL0_MDIV_MDEC_MASK 0x1ffff
|
||||
#define LPC18XX_PLL0_MDIV_SELP_SHIFT 17
|
||||
#define LPC18XX_PLL0_MDIV_SELI_SHIFT 22
|
||||
#define LPC18XX_PLL0_MSEL_MAX BIT(15)
|
||||
|
||||
/* Register value that gives PLL0 post/pre dividers equal to 1 */
|
||||
#define LPC18XX_PLL0_NP_DIVS_1 0x00302062
|
||||
|
||||
enum {
|
||||
CLK_SRC_OSC32,
|
||||
CLK_SRC_IRC,
|
||||
CLK_SRC_ENET_RX_CLK,
|
||||
CLK_SRC_ENET_TX_CLK,
|
||||
CLK_SRC_GP_CLKIN,
|
||||
CLK_SRC_RESERVED1,
|
||||
CLK_SRC_OSC,
|
||||
CLK_SRC_PLL0USB,
|
||||
CLK_SRC_PLL0AUDIO,
|
||||
CLK_SRC_PLL1,
|
||||
CLK_SRC_RESERVED2,
|
||||
CLK_SRC_RESERVED3,
|
||||
CLK_SRC_IDIVA,
|
||||
CLK_SRC_IDIVB,
|
||||
CLK_SRC_IDIVC,
|
||||
CLK_SRC_IDIVD,
|
||||
CLK_SRC_IDIVE,
|
||||
CLK_SRC_MAX
|
||||
};
|
||||
|
||||
static const char *clk_src_names[CLK_SRC_MAX] = {
|
||||
[CLK_SRC_OSC32] = "osc32",
|
||||
[CLK_SRC_IRC] = "irc",
|
||||
[CLK_SRC_ENET_RX_CLK] = "enet_rx_clk",
|
||||
[CLK_SRC_ENET_TX_CLK] = "enet_tx_clk",
|
||||
[CLK_SRC_GP_CLKIN] = "gp_clkin",
|
||||
[CLK_SRC_OSC] = "osc",
|
||||
[CLK_SRC_PLL0USB] = "pll0usb",
|
||||
[CLK_SRC_PLL0AUDIO] = "pll0audio",
|
||||
[CLK_SRC_PLL1] = "pll1",
|
||||
[CLK_SRC_IDIVA] = "idiva",
|
||||
[CLK_SRC_IDIVB] = "idivb",
|
||||
[CLK_SRC_IDIVC] = "idivc",
|
||||
[CLK_SRC_IDIVD] = "idivd",
|
||||
[CLK_SRC_IDIVE] = "idive",
|
||||
};
|
||||
|
||||
static const char *clk_base_names[BASE_CLK_MAX] = {
|
||||
[BASE_SAFE_CLK] = "base_safe_clk",
|
||||
[BASE_USB0_CLK] = "base_usb0_clk",
|
||||
[BASE_PERIPH_CLK] = "base_periph_clk",
|
||||
[BASE_USB1_CLK] = "base_usb1_clk",
|
||||
[BASE_CPU_CLK] = "base_cpu_clk",
|
||||
[BASE_SPIFI_CLK] = "base_spifi_clk",
|
||||
[BASE_SPI_CLK] = "base_spi_clk",
|
||||
[BASE_PHY_RX_CLK] = "base_phy_rx_clk",
|
||||
[BASE_PHY_TX_CLK] = "base_phy_tx_clk",
|
||||
[BASE_APB1_CLK] = "base_apb1_clk",
|
||||
[BASE_APB3_CLK] = "base_apb3_clk",
|
||||
[BASE_LCD_CLK] = "base_lcd_clk",
|
||||
[BASE_ADCHS_CLK] = "base_adchs_clk",
|
||||
[BASE_SDIO_CLK] = "base_sdio_clk",
|
||||
[BASE_SSP0_CLK] = "base_ssp0_clk",
|
||||
[BASE_SSP1_CLK] = "base_ssp1_clk",
|
||||
[BASE_UART0_CLK] = "base_uart0_clk",
|
||||
[BASE_UART1_CLK] = "base_uart1_clk",
|
||||
[BASE_UART2_CLK] = "base_uart2_clk",
|
||||
[BASE_UART3_CLK] = "base_uart3_clk",
|
||||
[BASE_OUT_CLK] = "base_out_clk",
|
||||
[BASE_AUDIO_CLK] = "base_audio_clk",
|
||||
[BASE_CGU_OUT0_CLK] = "base_cgu_out0_clk",
|
||||
[BASE_CGU_OUT1_CLK] = "base_cgu_out1_clk",
|
||||
};
|
||||
|
||||
static u32 lpc18xx_cgu_pll0_src_ids[] = {
|
||||
CLK_SRC_OSC32, CLK_SRC_IRC, CLK_SRC_ENET_RX_CLK,
|
||||
CLK_SRC_ENET_TX_CLK, CLK_SRC_GP_CLKIN, CLK_SRC_OSC,
|
||||
CLK_SRC_PLL1, CLK_SRC_IDIVA, CLK_SRC_IDIVB, CLK_SRC_IDIVC,
|
||||
CLK_SRC_IDIVD, CLK_SRC_IDIVE,
|
||||
};
|
||||
|
||||
static u32 lpc18xx_cgu_pll1_src_ids[] = {
|
||||
CLK_SRC_OSC32, CLK_SRC_IRC, CLK_SRC_ENET_RX_CLK,
|
||||
CLK_SRC_ENET_TX_CLK, CLK_SRC_GP_CLKIN, CLK_SRC_OSC,
|
||||
CLK_SRC_PLL0USB, CLK_SRC_PLL0AUDIO, CLK_SRC_IDIVA,
|
||||
CLK_SRC_IDIVB, CLK_SRC_IDIVC, CLK_SRC_IDIVD, CLK_SRC_IDIVE,
|
||||
};
|
||||
|
||||
static u32 lpc18xx_cgu_idiva_src_ids[] = {
|
||||
CLK_SRC_OSC32, CLK_SRC_IRC, CLK_SRC_ENET_RX_CLK,
|
||||
CLK_SRC_ENET_TX_CLK, CLK_SRC_GP_CLKIN, CLK_SRC_OSC,
|
||||
CLK_SRC_PLL0USB, CLK_SRC_PLL0AUDIO, CLK_SRC_PLL1
|
||||
};
|
||||
|
||||
static u32 lpc18xx_cgu_idivbcde_src_ids[] = {
|
||||
CLK_SRC_OSC32, CLK_SRC_IRC, CLK_SRC_ENET_RX_CLK,
|
||||
CLK_SRC_ENET_TX_CLK, CLK_SRC_GP_CLKIN, CLK_SRC_OSC,
|
||||
CLK_SRC_PLL0AUDIO, CLK_SRC_PLL1, CLK_SRC_IDIVA,
|
||||
};
|
||||
|
||||
static u32 lpc18xx_cgu_base_irc_src_ids[] = {CLK_SRC_IRC};
|
||||
|
||||
static u32 lpc18xx_cgu_base_usb0_src_ids[] = {CLK_SRC_PLL0USB};
|
||||
|
||||
static u32 lpc18xx_cgu_base_common_src_ids[] = {
|
||||
CLK_SRC_OSC32, CLK_SRC_IRC, CLK_SRC_ENET_RX_CLK,
|
||||
CLK_SRC_ENET_TX_CLK, CLK_SRC_GP_CLKIN, CLK_SRC_OSC,
|
||||
CLK_SRC_PLL0AUDIO, CLK_SRC_PLL1, CLK_SRC_IDIVA,
|
||||
CLK_SRC_IDIVB, CLK_SRC_IDIVC, CLK_SRC_IDIVD, CLK_SRC_IDIVE,
|
||||
};
|
||||
|
||||
static u32 lpc18xx_cgu_base_all_src_ids[] = {
|
||||
CLK_SRC_OSC32, CLK_SRC_IRC, CLK_SRC_ENET_RX_CLK,
|
||||
CLK_SRC_ENET_TX_CLK, CLK_SRC_GP_CLKIN, CLK_SRC_OSC,
|
||||
CLK_SRC_PLL0USB, CLK_SRC_PLL0AUDIO, CLK_SRC_PLL1,
|
||||
CLK_SRC_IDIVA, CLK_SRC_IDIVB, CLK_SRC_IDIVC,
|
||||
CLK_SRC_IDIVD, CLK_SRC_IDIVE,
|
||||
};
|
||||
|
||||
struct lpc18xx_cgu_src_clk_div {
|
||||
u8 clk_id;
|
||||
u8 n_parents;
|
||||
struct clk_divider div;
|
||||
struct clk_mux mux;
|
||||
struct clk_gate gate;
|
||||
};
|
||||
|
||||
#define LPC1XX_CGU_SRC_CLK_DIV(_id, _width, _table) \
|
||||
{ \
|
||||
.clk_id = CLK_SRC_ ##_id, \
|
||||
.n_parents = ARRAY_SIZE(lpc18xx_cgu_ ##_table), \
|
||||
.div = { \
|
||||
.shift = 2, \
|
||||
.width = _width, \
|
||||
}, \
|
||||
.mux = { \
|
||||
.mask = 0x1f, \
|
||||
.shift = 24, \
|
||||
.table = lpc18xx_cgu_ ##_table, \
|
||||
}, \
|
||||
.gate = { \
|
||||
.bit_idx = 0, \
|
||||
.flags = CLK_GATE_SET_TO_DISABLE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
static struct lpc18xx_cgu_src_clk_div lpc18xx_cgu_src_clk_divs[] = {
|
||||
LPC1XX_CGU_SRC_CLK_DIV(IDIVA, 2, idiva_src_ids),
|
||||
LPC1XX_CGU_SRC_CLK_DIV(IDIVB, 4, idivbcde_src_ids),
|
||||
LPC1XX_CGU_SRC_CLK_DIV(IDIVC, 4, idivbcde_src_ids),
|
||||
LPC1XX_CGU_SRC_CLK_DIV(IDIVD, 4, idivbcde_src_ids),
|
||||
LPC1XX_CGU_SRC_CLK_DIV(IDIVE, 8, idivbcde_src_ids),
|
||||
};
|
||||
|
||||
struct lpc18xx_cgu_base_clk {
|
||||
u8 clk_id;
|
||||
u8 n_parents;
|
||||
struct clk_mux mux;
|
||||
struct clk_gate gate;
|
||||
};
|
||||
|
||||
#define LPC1XX_CGU_BASE_CLK(_id, _table, _flags) \
|
||||
{ \
|
||||
.clk_id = BASE_ ##_id ##_CLK, \
|
||||
.n_parents = ARRAY_SIZE(lpc18xx_cgu_ ##_table), \
|
||||
.mux = { \
|
||||
.mask = 0x1f, \
|
||||
.shift = 24, \
|
||||
.table = lpc18xx_cgu_ ##_table, \
|
||||
.flags = _flags, \
|
||||
}, \
|
||||
.gate = { \
|
||||
.bit_idx = 0, \
|
||||
.flags = CLK_GATE_SET_TO_DISABLE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
static struct lpc18xx_cgu_base_clk lpc18xx_cgu_base_clks[] = {
|
||||
LPC1XX_CGU_BASE_CLK(SAFE, base_irc_src_ids, CLK_MUX_READ_ONLY),
|
||||
LPC1XX_CGU_BASE_CLK(USB0, base_usb0_src_ids, 0),
|
||||
LPC1XX_CGU_BASE_CLK(PERIPH, base_common_src_ids, 0),
|
||||
LPC1XX_CGU_BASE_CLK(USB1, base_all_src_ids, 0),
|
||||
LPC1XX_CGU_BASE_CLK(CPU, base_common_src_ids, 0),
|
||||
LPC1XX_CGU_BASE_CLK(SPIFI, base_common_src_ids, 0),
|
||||
LPC1XX_CGU_BASE_CLK(SPI, base_common_src_ids, 0),
|
||||
LPC1XX_CGU_BASE_CLK(PHY_RX, base_common_src_ids, 0),
|
||||
LPC1XX_CGU_BASE_CLK(PHY_TX, base_common_src_ids, 0),
|
||||
LPC1XX_CGU_BASE_CLK(APB1, base_common_src_ids, 0),
|
||||
LPC1XX_CGU_BASE_CLK(APB3, base_common_src_ids, 0),
|
||||
LPC1XX_CGU_BASE_CLK(LCD, base_common_src_ids, 0),
|
||||
LPC1XX_CGU_BASE_CLK(ADCHS, base_common_src_ids, 0),
|
||||
LPC1XX_CGU_BASE_CLK(SDIO, base_common_src_ids, 0),
|
||||
LPC1XX_CGU_BASE_CLK(SSP0, base_common_src_ids, 0),
|
||||
LPC1XX_CGU_BASE_CLK(SSP1, base_common_src_ids, 0),
|
||||
LPC1XX_CGU_BASE_CLK(UART0, base_common_src_ids, 0),
|
||||
LPC1XX_CGU_BASE_CLK(UART1, base_common_src_ids, 0),
|
||||
LPC1XX_CGU_BASE_CLK(UART2, base_common_src_ids, 0),
|
||||
LPC1XX_CGU_BASE_CLK(UART3, base_common_src_ids, 0),
|
||||
LPC1XX_CGU_BASE_CLK(OUT, base_all_src_ids, 0),
|
||||
{ /* 21 reserved */ },
|
||||
{ /* 22 reserved */ },
|
||||
{ /* 23 reserved */ },
|
||||
{ /* 24 reserved */ },
|
||||
LPC1XX_CGU_BASE_CLK(AUDIO, base_common_src_ids, 0),
|
||||
LPC1XX_CGU_BASE_CLK(CGU_OUT0, base_all_src_ids, 0),
|
||||
LPC1XX_CGU_BASE_CLK(CGU_OUT1, base_all_src_ids, 0),
|
||||
};
|
||||
|
||||
struct lpc18xx_pll {
|
||||
struct clk_hw hw;
|
||||
void __iomem *reg;
|
||||
spinlock_t *lock;
|
||||
u8 flags;
|
||||
};
|
||||
|
||||
#define to_lpc_pll(hw) container_of(hw, struct lpc18xx_pll, hw)
|
||||
|
||||
struct lpc18xx_cgu_pll_clk {
|
||||
u8 clk_id;
|
||||
u8 n_parents;
|
||||
u8 reg_offset;
|
||||
struct clk_mux mux;
|
||||
struct clk_gate gate;
|
||||
struct lpc18xx_pll pll;
|
||||
const struct clk_ops *pll_ops;
|
||||
};
|
||||
|
||||
#define LPC1XX_CGU_CLK_PLL(_id, _table, _pll_ops) \
|
||||
{ \
|
||||
.clk_id = CLK_SRC_ ##_id, \
|
||||
.n_parents = ARRAY_SIZE(lpc18xx_cgu_ ##_table), \
|
||||
.reg_offset = LPC18XX_CGU_ ##_id ##_STAT, \
|
||||
.mux = { \
|
||||
.mask = 0x1f, \
|
||||
.shift = 24, \
|
||||
.table = lpc18xx_cgu_ ##_table, \
|
||||
}, \
|
||||
.gate = { \
|
||||
.bit_idx = 0, \
|
||||
.flags = CLK_GATE_SET_TO_DISABLE, \
|
||||
}, \
|
||||
.pll_ops = &lpc18xx_ ##_pll_ops, \
|
||||
}
|
||||
|
||||
/*
|
||||
* PLL0 uses a special register value encoding. The compute functions below
|
||||
* are taken or derived from the LPC1850 user manual (section 12.6.3.3).
|
||||
*/
|
||||
|
||||
/* Compute PLL0 multiplier from decoded version */
|
||||
static u32 lpc18xx_pll0_mdec2msel(u32 x)
|
||||
{
|
||||
int i;
|
||||
|
||||
switch (x) {
|
||||
case 0x18003: return 1;
|
||||
case 0x10003: return 2;
|
||||
default:
|
||||
for (i = LPC18XX_PLL0_MSEL_MAX + 1; x != 0x4000 && i > 0; i--)
|
||||
x = ((x ^ x >> 14) & 1) | (x << 1 & 0x7fff);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
/* Compute PLL0 decoded multiplier from binary version */
|
||||
static u32 lpc18xx_pll0_msel2mdec(u32 msel)
|
||||
{
|
||||
u32 i, x = 0x4000;
|
||||
|
||||
switch (msel) {
|
||||
case 0: return 0;
|
||||
case 1: return 0x18003;
|
||||
case 2: return 0x10003;
|
||||
default:
|
||||
for (i = msel; i <= LPC18XX_PLL0_MSEL_MAX; i++)
|
||||
x = ((x ^ x >> 1) & 1) << 14 | (x >> 1 & 0xffff);
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
/* Compute PLL0 bandwidth SELI reg from multiplier */
|
||||
static u32 lpc18xx_pll0_msel2seli(u32 msel)
|
||||
{
|
||||
u32 tmp;
|
||||
|
||||
if (msel > 16384) return 1;
|
||||
if (msel > 8192) return 2;
|
||||
if (msel > 2048) return 4;
|
||||
if (msel >= 501) return 8;
|
||||
if (msel >= 60) {
|
||||
tmp = 1024 / (msel + 9);
|
||||
return ((1024 == (tmp * (msel + 9))) == 0) ? tmp * 4 : (tmp + 1) * 4;
|
||||
}
|
||||
|
||||
return (msel & 0x3c) + 4;
|
||||
}
|
||||
|
||||
/* Compute PLL0 bandwidth SELP reg from multiplier */
|
||||
static u32 lpc18xx_pll0_msel2selp(u32 msel)
|
||||
{
|
||||
if (msel < 60)
|
||||
return (msel >> 1) + 1;
|
||||
|
||||
return 31;
|
||||
}
|
||||
|
||||
static unsigned long lpc18xx_pll0_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct lpc18xx_pll *pll = to_lpc_pll(hw);
|
||||
u32 ctrl, mdiv, msel, npdiv;
|
||||
|
||||
ctrl = clk_readl(pll->reg + LPC18XX_CGU_PLL0USB_CTRL);
|
||||
mdiv = clk_readl(pll->reg + LPC18XX_CGU_PLL0USB_MDIV);
|
||||
npdiv = clk_readl(pll->reg + LPC18XX_CGU_PLL0USB_NP_DIV);
|
||||
|
||||
if (ctrl & LPC18XX_PLL0_CTRL_BYPASS)
|
||||
return parent_rate;
|
||||
|
||||
if (npdiv != LPC18XX_PLL0_NP_DIVS_1) {
|
||||
pr_warn("%s: pre/post dividers not supported\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
msel = lpc18xx_pll0_mdec2msel(mdiv & LPC18XX_PLL0_MDIV_MDEC_MASK);
|
||||
if (msel)
|
||||
return 2 * msel * parent_rate;
|
||||
|
||||
pr_warn("%s: unable to calculate rate\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long lpc18xx_pll0_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *prate)
|
||||
{
|
||||
unsigned long m;
|
||||
|
||||
if (*prate < rate) {
|
||||
pr_warn("%s: pll dividers not supported\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
m = DIV_ROUND_UP_ULL(*prate, rate * 2);
|
||||
if (m <= 0 && m > LPC18XX_PLL0_MSEL_MAX) {
|
||||
pr_warn("%s: unable to support rate %lu\n", __func__, rate);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 2 * *prate * m;
|
||||
}
|
||||
|
||||
static int lpc18xx_pll0_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct lpc18xx_pll *pll = to_lpc_pll(hw);
|
||||
u32 ctrl, stat, m;
|
||||
int retry = 3;
|
||||
|
||||
if (parent_rate < rate) {
|
||||
pr_warn("%s: pll dividers not supported\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
m = DIV_ROUND_UP_ULL(parent_rate, rate * 2);
|
||||
if (m <= 0 && m > LPC18XX_PLL0_MSEL_MAX) {
|
||||
pr_warn("%s: unable to support rate %lu\n", __func__, rate);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
m = lpc18xx_pll0_msel2mdec(m);
|
||||
m |= lpc18xx_pll0_msel2selp(m) << LPC18XX_PLL0_MDIV_SELP_SHIFT;
|
||||
m |= lpc18xx_pll0_msel2seli(m) << LPC18XX_PLL0_MDIV_SELI_SHIFT;
|
||||
|
||||
/* Power down PLL, disable clk output and dividers */
|
||||
ctrl = clk_readl(pll->reg + LPC18XX_CGU_PLL0USB_CTRL);
|
||||
ctrl |= LPC18XX_PLL0_CTRL_PD;
|
||||
ctrl &= ~(LPC18XX_PLL0_CTRL_BYPASS | LPC18XX_PLL0_CTRL_DIRECTI |
|
||||
LPC18XX_PLL0_CTRL_DIRECTO | LPC18XX_PLL0_CTRL_CLKEN);
|
||||
clk_writel(ctrl, pll->reg + LPC18XX_CGU_PLL0USB_CTRL);
|
||||
|
||||
/* Configure new PLL settings */
|
||||
clk_writel(m, pll->reg + LPC18XX_CGU_PLL0USB_MDIV);
|
||||
clk_writel(LPC18XX_PLL0_NP_DIVS_1, pll->reg + LPC18XX_CGU_PLL0USB_NP_DIV);
|
||||
|
||||
/* Power up PLL and wait for lock */
|
||||
ctrl &= ~LPC18XX_PLL0_CTRL_PD;
|
||||
clk_writel(ctrl, pll->reg + LPC18XX_CGU_PLL0USB_CTRL);
|
||||
do {
|
||||
udelay(10);
|
||||
stat = clk_readl(pll->reg + LPC18XX_CGU_PLL0USB_STAT);
|
||||
if (stat & LPC18XX_PLL0_STAT_LOCK) {
|
||||
ctrl |= LPC18XX_PLL0_CTRL_CLKEN;
|
||||
clk_writel(ctrl, pll->reg + LPC18XX_CGU_PLL0USB_CTRL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
} while (retry--);
|
||||
|
||||
pr_warn("%s: unable to lock pll\n", __func__);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct clk_ops lpc18xx_pll0_ops = {
|
||||
.recalc_rate = lpc18xx_pll0_recalc_rate,
|
||||
.round_rate = lpc18xx_pll0_round_rate,
|
||||
.set_rate = lpc18xx_pll0_set_rate,
|
||||
};
|
||||
|
||||
static unsigned long lpc18xx_pll1_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct lpc18xx_pll *pll = to_lpc_pll(hw);
|
||||
u16 msel, nsel, psel;
|
||||
bool direct, fbsel;
|
||||
u32 stat, ctrl;
|
||||
|
||||
stat = clk_readl(pll->reg + LPC18XX_CGU_PLL1_STAT);
|
||||
ctrl = clk_readl(pll->reg + LPC18XX_CGU_PLL1_CTRL);
|
||||
|
||||
direct = (ctrl & LPC18XX_PLL1_CTRL_DIRECT) ? true : false;
|
||||
fbsel = (ctrl & LPC18XX_PLL1_CTRL_FBSEL) ? true : false;
|
||||
|
||||
msel = ((ctrl >> 16) & 0xff) + 1;
|
||||
nsel = ((ctrl >> 12) & 0x3) + 1;
|
||||
|
||||
if (direct || fbsel)
|
||||
return msel * (parent_rate / nsel);
|
||||
|
||||
psel = (ctrl >> 8) & 0x3;
|
||||
psel = 1 << psel;
|
||||
|
||||
return (msel / (2 * psel)) * (parent_rate / nsel);
|
||||
}
|
||||
|
||||
static const struct clk_ops lpc18xx_pll1_ops = {
|
||||
.recalc_rate = lpc18xx_pll1_recalc_rate,
|
||||
};
|
||||
|
||||
static struct lpc18xx_cgu_pll_clk lpc18xx_cgu_src_clk_plls[] = {
|
||||
LPC1XX_CGU_CLK_PLL(PLL0USB, pll0_src_ids, pll0_ops),
|
||||
LPC1XX_CGU_CLK_PLL(PLL0AUDIO, pll0_src_ids, pll0_ops),
|
||||
LPC1XX_CGU_CLK_PLL(PLL1, pll1_src_ids, pll1_ops),
|
||||
};
|
||||
|
||||
static void lpc18xx_fill_parent_names(const char **parent, u32 *id, int size)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
parent[i] = clk_src_names[id[i]];
|
||||
}
|
||||
|
||||
static struct clk *lpc18xx_cgu_register_div(struct lpc18xx_cgu_src_clk_div *clk,
|
||||
void __iomem *base, int n)
|
||||
{
|
||||
void __iomem *reg = base + LPC18XX_CGU_IDIV_CTRL(n);
|
||||
const char *name = clk_src_names[clk->clk_id];
|
||||
const char *parents[CLK_SRC_MAX];
|
||||
|
||||
clk->div.reg = reg;
|
||||
clk->mux.reg = reg;
|
||||
clk->gate.reg = reg;
|
||||
|
||||
lpc18xx_fill_parent_names(parents, clk->mux.table, clk->n_parents);
|
||||
|
||||
return clk_register_composite(NULL, name, parents, clk->n_parents,
|
||||
&clk->mux.hw, &clk_mux_ops,
|
||||
&clk->div.hw, &clk_divider_ops,
|
||||
&clk->gate.hw, &clk_gate_ops, 0);
|
||||
}
|
||||
|
||||
|
||||
static struct clk *lpc18xx_register_base_clk(struct lpc18xx_cgu_base_clk *clk,
|
||||
void __iomem *reg_base, int n)
|
||||
{
|
||||
void __iomem *reg = reg_base + LPC18XX_CGU_BASE_CLK(n);
|
||||
const char *name = clk_base_names[clk->clk_id];
|
||||
const char *parents[CLK_SRC_MAX];
|
||||
|
||||
if (clk->n_parents == 0)
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
clk->mux.reg = reg;
|
||||
clk->gate.reg = reg;
|
||||
|
||||
lpc18xx_fill_parent_names(parents, clk->mux.table, clk->n_parents);
|
||||
|
||||
/* SAFE_CLK can not be turned off */
|
||||
if (n == BASE_SAFE_CLK)
|
||||
return clk_register_composite(NULL, name, parents, clk->n_parents,
|
||||
&clk->mux.hw, &clk_mux_ops,
|
||||
NULL, NULL, NULL, NULL, 0);
|
||||
|
||||
return clk_register_composite(NULL, name, parents, clk->n_parents,
|
||||
&clk->mux.hw, &clk_mux_ops,
|
||||
NULL, NULL,
|
||||
&clk->gate.hw, &clk_gate_ops, 0);
|
||||
}
|
||||
|
||||
|
||||
static struct clk *lpc18xx_cgu_register_pll(struct lpc18xx_cgu_pll_clk *clk,
|
||||
void __iomem *base)
|
||||
{
|
||||
const char *name = clk_src_names[clk->clk_id];
|
||||
const char *parents[CLK_SRC_MAX];
|
||||
|
||||
clk->pll.reg = base;
|
||||
clk->mux.reg = base + clk->reg_offset + LPC18XX_CGU_PLL_CTRL_OFFSET;
|
||||
clk->gate.reg = base + clk->reg_offset + LPC18XX_CGU_PLL_CTRL_OFFSET;
|
||||
|
||||
lpc18xx_fill_parent_names(parents, clk->mux.table, clk->n_parents);
|
||||
|
||||
return clk_register_composite(NULL, name, parents, clk->n_parents,
|
||||
&clk->mux.hw, &clk_mux_ops,
|
||||
&clk->pll.hw, clk->pll_ops,
|
||||
&clk->gate.hw, &clk_gate_ops, 0);
|
||||
}
|
||||
|
||||
static void __init lpc18xx_cgu_register_source_clks(struct device_node *np,
|
||||
void __iomem *base)
|
||||
{
|
||||
const char *parents[CLK_SRC_MAX];
|
||||
struct clk *clk;
|
||||
int i;
|
||||
|
||||
/* Register the internal 12 MHz RC oscillator (IRC) */
|
||||
clk = clk_register_fixed_rate(NULL, clk_src_names[CLK_SRC_IRC],
|
||||
NULL, CLK_IS_ROOT, 12000000);
|
||||
if (IS_ERR(clk))
|
||||
pr_warn("%s: failed to register irc clk\n", __func__);
|
||||
|
||||
/* Register crystal oscillator controlller */
|
||||
parents[0] = of_clk_get_parent_name(np, 0);
|
||||
clk = clk_register_gate(NULL, clk_src_names[CLK_SRC_OSC], parents[0],
|
||||
0, base + LPC18XX_CGU_XTAL_OSC_CTRL,
|
||||
0, CLK_GATE_SET_TO_DISABLE, NULL);
|
||||
if (IS_ERR(clk))
|
||||
pr_warn("%s: failed to register osc clk\n", __func__);
|
||||
|
||||
/* Register all PLLs */
|
||||
for (i = 0; i < ARRAY_SIZE(lpc18xx_cgu_src_clk_plls); i++) {
|
||||
clk = lpc18xx_cgu_register_pll(&lpc18xx_cgu_src_clk_plls[i],
|
||||
base);
|
||||
if (IS_ERR(clk))
|
||||
pr_warn("%s: failed to register pll (%d)\n", __func__, i);
|
||||
}
|
||||
|
||||
/* Register all clock dividers A-E */
|
||||
for (i = 0; i < ARRAY_SIZE(lpc18xx_cgu_src_clk_divs); i++) {
|
||||
clk = lpc18xx_cgu_register_div(&lpc18xx_cgu_src_clk_divs[i],
|
||||
base, i);
|
||||
if (IS_ERR(clk))
|
||||
pr_warn("%s: failed to register div %d\n", __func__, i);
|
||||
}
|
||||
}
|
||||
|
||||
static struct clk *clk_base[BASE_CLK_MAX];
|
||||
static struct clk_onecell_data clk_base_data = {
|
||||
.clks = clk_base,
|
||||
.clk_num = BASE_CLK_MAX,
|
||||
};
|
||||
|
||||
static void __init lpc18xx_cgu_register_base_clks(void __iomem *reg_base)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = BASE_SAFE_CLK; i < BASE_CLK_MAX; i++) {
|
||||
clk_base[i] = lpc18xx_register_base_clk(&lpc18xx_cgu_base_clks[i],
|
||||
reg_base, i);
|
||||
if (IS_ERR(clk_base[i]) && PTR_ERR(clk_base[i]) != -ENOENT)
|
||||
pr_warn("%s: register base clk %d failed\n", __func__, i);
|
||||
}
|
||||
}
|
||||
|
||||
static void __init lpc18xx_cgu_init(struct device_node *np)
|
||||
{
|
||||
void __iomem *reg_base;
|
||||
|
||||
reg_base = of_iomap(np, 0);
|
||||
if (!reg_base) {
|
||||
pr_warn("%s: failed to map address range\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
lpc18xx_cgu_register_source_clks(np, reg_base);
|
||||
lpc18xx_cgu_register_base_clks(reg_base);
|
||||
|
||||
of_clk_add_provider(np, of_clk_src_onecell_get, &clk_base_data);
|
||||
}
|
||||
CLK_OF_DECLARE(lpc18xx_cgu, "nxp,lpc1850-cgu", lpc18xx_cgu_init);
|
||||
|
|
@ -6,9 +6,12 @@
|
|||
* version 2, as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "%s: " fmt, __func__
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "clk.h"
|
||||
|
|
@ -50,6 +53,18 @@
|
|||
#define PLL_CTRL4 0x10
|
||||
#define PLL_FRAC_CTRL4_BYPASS BIT(28)
|
||||
|
||||
#define MIN_PFD 9600000UL
|
||||
#define MIN_VCO_LA 400000000UL
|
||||
#define MAX_VCO_LA 1600000000UL
|
||||
#define MIN_VCO_FRAC_INT 600000000UL
|
||||
#define MAX_VCO_FRAC_INT 1600000000UL
|
||||
#define MIN_VCO_FRAC_FRAC 600000000UL
|
||||
#define MAX_VCO_FRAC_FRAC 2400000000UL
|
||||
#define MIN_OUTPUT_LA 8000000UL
|
||||
#define MAX_OUTPUT_LA 1600000000UL
|
||||
#define MIN_OUTPUT_FRAC 12000000UL
|
||||
#define MAX_OUTPUT_FRAC 1600000000UL
|
||||
|
||||
struct pistachio_clk_pll {
|
||||
struct clk_hw hw;
|
||||
void __iomem *base;
|
||||
|
|
@ -67,6 +82,12 @@ static inline void pll_writel(struct pistachio_clk_pll *pll, u32 val, u32 reg)
|
|||
writel(val, pll->base + reg);
|
||||
}
|
||||
|
||||
static inline void pll_lock(struct pistachio_clk_pll *pll)
|
||||
{
|
||||
while (!(pll_readl(pll, PLL_STATUS) & PLL_STATUS_LOCK))
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
static inline u32 do_div_round_closest(u64 dividend, u32 divisor)
|
||||
{
|
||||
dividend += divisor / 2;
|
||||
|
|
@ -124,6 +145,8 @@ static int pll_gf40lp_frac_enable(struct clk_hw *hw)
|
|||
val &= ~PLL_FRAC_CTRL4_BYPASS;
|
||||
pll_writel(pll, val, PLL_CTRL4);
|
||||
|
||||
pll_lock(pll);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -149,16 +172,29 @@ static int pll_gf40lp_frac_set_rate(struct clk_hw *hw, unsigned long rate,
|
|||
{
|
||||
struct pistachio_clk_pll *pll = to_pistachio_pll(hw);
|
||||
struct pistachio_pll_rate_table *params;
|
||||
bool was_enabled;
|
||||
u32 val;
|
||||
int enabled = pll_gf40lp_frac_is_enabled(hw);
|
||||
u32 val, vco, old_postdiv1, old_postdiv2;
|
||||
const char *name = __clk_get_name(hw->clk);
|
||||
|
||||
params = pll_get_params(pll, parent_rate, rate);
|
||||
if (!params)
|
||||
if (rate < MIN_OUTPUT_FRAC || rate > MAX_OUTPUT_FRAC)
|
||||
return -EINVAL;
|
||||
|
||||
was_enabled = pll_gf40lp_frac_is_enabled(hw);
|
||||
if (!was_enabled)
|
||||
pll_gf40lp_frac_enable(hw);
|
||||
params = pll_get_params(pll, parent_rate, rate);
|
||||
if (!params || !params->refdiv)
|
||||
return -EINVAL;
|
||||
|
||||
vco = params->fref * params->fbdiv / params->refdiv;
|
||||
if (vco < MIN_VCO_FRAC_FRAC || vco > MAX_VCO_FRAC_FRAC)
|
||||
pr_warn("%s: VCO %u is out of range %lu..%lu\n", name, vco,
|
||||
MIN_VCO_FRAC_FRAC, MAX_VCO_FRAC_FRAC);
|
||||
|
||||
val = params->fref / params->refdiv;
|
||||
if (val < MIN_PFD)
|
||||
pr_warn("%s: PFD %u is too low (min %lu)\n",
|
||||
name, val, MIN_PFD);
|
||||
if (val > vco / 16)
|
||||
pr_warn("%s: PFD %u is too high (max %u)\n",
|
||||
name, val, vco / 16);
|
||||
|
||||
val = pll_readl(pll, PLL_CTRL1);
|
||||
val &= ~((PLL_CTRL1_REFDIV_MASK << PLL_CTRL1_REFDIV_SHIFT) |
|
||||
|
|
@ -168,6 +204,19 @@ static int pll_gf40lp_frac_set_rate(struct clk_hw *hw, unsigned long rate,
|
|||
pll_writel(pll, val, PLL_CTRL1);
|
||||
|
||||
val = pll_readl(pll, PLL_CTRL2);
|
||||
|
||||
old_postdiv1 = (val >> PLL_FRAC_CTRL2_POSTDIV1_SHIFT) &
|
||||
PLL_FRAC_CTRL2_POSTDIV1_MASK;
|
||||
old_postdiv2 = (val >> PLL_FRAC_CTRL2_POSTDIV2_SHIFT) &
|
||||
PLL_FRAC_CTRL2_POSTDIV2_MASK;
|
||||
if (enabled &&
|
||||
(params->postdiv1 != old_postdiv1 ||
|
||||
params->postdiv2 != old_postdiv2))
|
||||
pr_warn("%s: changing postdiv while PLL is enabled\n", name);
|
||||
|
||||
if (params->postdiv2 > params->postdiv1)
|
||||
pr_warn("%s: postdiv2 should not exceed postdiv1\n", name);
|
||||
|
||||
val &= ~((PLL_FRAC_CTRL2_FRAC_MASK << PLL_FRAC_CTRL2_FRAC_SHIFT) |
|
||||
(PLL_FRAC_CTRL2_POSTDIV1_MASK <<
|
||||
PLL_FRAC_CTRL2_POSTDIV1_SHIFT) |
|
||||
|
|
@ -178,11 +227,8 @@ static int pll_gf40lp_frac_set_rate(struct clk_hw *hw, unsigned long rate,
|
|||
(params->postdiv2 << PLL_FRAC_CTRL2_POSTDIV2_SHIFT);
|
||||
pll_writel(pll, val, PLL_CTRL2);
|
||||
|
||||
while (!(pll_readl(pll, PLL_STATUS) & PLL_STATUS_LOCK))
|
||||
cpu_relax();
|
||||
|
||||
if (!was_enabled)
|
||||
pll_gf40lp_frac_disable(hw);
|
||||
if (enabled)
|
||||
pll_lock(pll);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -241,6 +287,8 @@ static int pll_gf40lp_laint_enable(struct clk_hw *hw)
|
|||
val &= ~PLL_INT_CTRL2_BYPASS;
|
||||
pll_writel(pll, val, PLL_CTRL2);
|
||||
|
||||
pll_lock(pll);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -266,18 +314,44 @@ static int pll_gf40lp_laint_set_rate(struct clk_hw *hw, unsigned long rate,
|
|||
{
|
||||
struct pistachio_clk_pll *pll = to_pistachio_pll(hw);
|
||||
struct pistachio_pll_rate_table *params;
|
||||
bool was_enabled;
|
||||
u32 val;
|
||||
int enabled = pll_gf40lp_laint_is_enabled(hw);
|
||||
u32 val, vco, old_postdiv1, old_postdiv2;
|
||||
const char *name = __clk_get_name(hw->clk);
|
||||
|
||||
params = pll_get_params(pll, parent_rate, rate);
|
||||
if (!params)
|
||||
if (rate < MIN_OUTPUT_LA || rate > MAX_OUTPUT_LA)
|
||||
return -EINVAL;
|
||||
|
||||
was_enabled = pll_gf40lp_laint_is_enabled(hw);
|
||||
if (!was_enabled)
|
||||
pll_gf40lp_laint_enable(hw);
|
||||
params = pll_get_params(pll, parent_rate, rate);
|
||||
if (!params || !params->refdiv)
|
||||
return -EINVAL;
|
||||
|
||||
vco = params->fref * params->fbdiv / params->refdiv;
|
||||
if (vco < MIN_VCO_LA || vco > MAX_VCO_LA)
|
||||
pr_warn("%s: VCO %u is out of range %lu..%lu\n", name, vco,
|
||||
MIN_VCO_LA, MAX_VCO_LA);
|
||||
|
||||
val = params->fref / params->refdiv;
|
||||
if (val < MIN_PFD)
|
||||
pr_warn("%s: PFD %u is too low (min %lu)\n",
|
||||
name, val, MIN_PFD);
|
||||
if (val > vco / 16)
|
||||
pr_warn("%s: PFD %u is too high (max %u)\n",
|
||||
name, val, vco / 16);
|
||||
|
||||
val = pll_readl(pll, PLL_CTRL1);
|
||||
|
||||
old_postdiv1 = (val >> PLL_INT_CTRL1_POSTDIV1_SHIFT) &
|
||||
PLL_INT_CTRL1_POSTDIV1_MASK;
|
||||
old_postdiv2 = (val >> PLL_INT_CTRL1_POSTDIV2_SHIFT) &
|
||||
PLL_INT_CTRL1_POSTDIV2_MASK;
|
||||
if (enabled &&
|
||||
(params->postdiv1 != old_postdiv1 ||
|
||||
params->postdiv2 != old_postdiv2))
|
||||
pr_warn("%s: changing postdiv while PLL is enabled\n", name);
|
||||
|
||||
if (params->postdiv2 > params->postdiv1)
|
||||
pr_warn("%s: postdiv2 should not exceed postdiv1\n", name);
|
||||
|
||||
val &= ~((PLL_CTRL1_REFDIV_MASK << PLL_CTRL1_REFDIV_SHIFT) |
|
||||
(PLL_CTRL1_FBDIV_MASK << PLL_CTRL1_FBDIV_SHIFT) |
|
||||
(PLL_INT_CTRL1_POSTDIV1_MASK << PLL_INT_CTRL1_POSTDIV1_SHIFT) |
|
||||
|
|
@ -288,11 +362,8 @@ static int pll_gf40lp_laint_set_rate(struct clk_hw *hw, unsigned long rate,
|
|||
(params->postdiv2 << PLL_INT_CTRL1_POSTDIV2_SHIFT);
|
||||
pll_writel(pll, val, PLL_CTRL1);
|
||||
|
||||
while (!(pll_readl(pll, PLL_STATUS) & PLL_STATUS_LOCK))
|
||||
cpu_relax();
|
||||
|
||||
if (!was_enabled)
|
||||
pll_gf40lp_laint_disable(hw);
|
||||
if (enabled)
|
||||
pll_lock(pll);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
#define _CLK_PXA_
|
||||
|
||||
#define PARENTS(name) \
|
||||
static const char *name ## _parents[] __initdata
|
||||
static const char *const name ## _parents[] __initconst
|
||||
#define MUX_RO_RATE_RO_OPS(name, clk_name) \
|
||||
static struct clk_hw name ## _mux_hw; \
|
||||
static struct clk_hw name ## _rate_hw; \
|
||||
|
|
@ -72,7 +72,7 @@ struct desc_clk_cken {
|
|||
const char *name;
|
||||
const char *dev_id;
|
||||
const char *con_id;
|
||||
const char **parent_names;
|
||||
const char * const *parent_names;
|
||||
struct clk_fixed_factor lp;
|
||||
struct clk_fixed_factor hp;
|
||||
struct clk_gate gate;
|
||||
|
|
|
|||
|
|
@ -231,7 +231,7 @@ static int rockchip_cpuclk_notifier_cb(struct notifier_block *nb,
|
|||
}
|
||||
|
||||
struct clk *rockchip_clk_register_cpuclk(const char *name,
|
||||
const char **parent_names, u8 num_parents,
|
||||
const char *const *parent_names, u8 num_parents,
|
||||
const struct rockchip_cpuclk_reg_data *reg_data,
|
||||
const struct rockchip_cpuclk_rate_table *rates,
|
||||
int nrates, void __iomem *reg_base, spinlock_t *lock)
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ static const struct clk_ops rockchip_mmc_clk_ops = {
|
|||
};
|
||||
|
||||
struct clk *rockchip_clk_register_mmc(const char *name,
|
||||
const char **parent_names, u8 num_parents,
|
||||
const char *const *parent_names, u8 num_parents,
|
||||
void __iomem *reg, int shift)
|
||||
{
|
||||
struct clk_init_data init;
|
||||
|
|
|
|||
|
|
@ -329,10 +329,10 @@ static const struct clk_ops rockchip_rk3066_pll_clk_ops = {
|
|||
*/
|
||||
|
||||
struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type,
|
||||
const char *name, const char **parent_names, u8 num_parents,
|
||||
void __iomem *base, int con_offset, int grf_lock_offset,
|
||||
int lock_shift, int mode_offset, int mode_shift,
|
||||
struct rockchip_pll_rate_table *rate_table,
|
||||
const char *name, const char *const *parent_names,
|
||||
u8 num_parents, void __iomem *base, int con_offset,
|
||||
int grf_lock_offset, int lock_shift, int mode_offset,
|
||||
int mode_shift, struct rockchip_pll_rate_table *rate_table,
|
||||
u8 clk_pll_flags, spinlock_t *lock)
|
||||
{
|
||||
const char *pll_parents[3];
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ enum rk3188_plls {
|
|||
apll, cpll, dpll, gpll,
|
||||
};
|
||||
|
||||
struct rockchip_pll_rate_table rk3188_pll_rates[] = {
|
||||
static struct rockchip_pll_rate_table rk3188_pll_rates[] = {
|
||||
RK3066_PLL_RATE(2208000000, 1, 92, 1),
|
||||
RK3066_PLL_RATE(2184000000, 1, 91, 1),
|
||||
RK3066_PLL_RATE(2160000000, 1, 90, 1),
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ enum rk3288_plls {
|
|||
apll, dpll, cpll, gpll, npll,
|
||||
};
|
||||
|
||||
struct rockchip_pll_rate_table rk3288_pll_rates[] = {
|
||||
static struct rockchip_pll_rate_table rk3288_pll_rates[] = {
|
||||
RK3066_PLL_RATE(2208000000, 1, 92, 1),
|
||||
RK3066_PLL_RATE(2184000000, 1, 91, 1),
|
||||
RK3066_PLL_RATE(2160000000, 1, 90, 1),
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@
|
|||
* sometimes without one of those components.
|
||||
*/
|
||||
static struct clk *rockchip_clk_register_branch(const char *name,
|
||||
const char **parent_names, u8 num_parents, void __iomem *base,
|
||||
const char *const *parent_names, u8 num_parents, void __iomem *base,
|
||||
int muxdiv_offset, u8 mux_shift, u8 mux_width, u8 mux_flags,
|
||||
u8 div_shift, u8 div_width, u8 div_flags,
|
||||
struct clk_div_table *div_table, int gate_offset,
|
||||
|
|
@ -103,8 +103,8 @@ static struct clk *rockchip_clk_register_branch(const char *name,
|
|||
}
|
||||
|
||||
static struct clk *rockchip_clk_register_frac_branch(const char *name,
|
||||
const char **parent_names, u8 num_parents, void __iomem *base,
|
||||
int muxdiv_offset, u8 div_flags,
|
||||
const char *const *parent_names, u8 num_parents,
|
||||
void __iomem *base, int muxdiv_offset, u8 div_flags,
|
||||
int gate_offset, u8 gate_shift, u8 gate_flags,
|
||||
unsigned long flags, spinlock_t *lock)
|
||||
{
|
||||
|
|
@ -297,7 +297,7 @@ void __init rockchip_clk_register_branches(
|
|||
}
|
||||
|
||||
void __init rockchip_clk_register_armclk(unsigned int lookup_id,
|
||||
const char *name, const char **parent_names,
|
||||
const char *name, const char *const *parent_names,
|
||||
u8 num_parents,
|
||||
const struct rockchip_cpuclk_reg_data *reg_data,
|
||||
const struct rockchip_cpuclk_rate_table *rates,
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ struct rockchip_pll_rate_table {
|
|||
struct rockchip_pll_clock {
|
||||
unsigned int id;
|
||||
const char *name;
|
||||
const char **parent_names;
|
||||
const char *const *parent_names;
|
||||
u8 num_parents;
|
||||
unsigned long flags;
|
||||
int con_offset;
|
||||
|
|
@ -140,10 +140,10 @@ struct rockchip_pll_clock {
|
|||
}
|
||||
|
||||
struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type,
|
||||
const char *name, const char **parent_names, u8 num_parents,
|
||||
void __iomem *base, int con_offset, int grf_lock_offset,
|
||||
int lock_shift, int reg_mode, int mode_shift,
|
||||
struct rockchip_pll_rate_table *rate_table,
|
||||
const char *name, const char *const *parent_names,
|
||||
u8 num_parents, void __iomem *base, int con_offset,
|
||||
int grf_lock_offset, int lock_shift, int reg_mode,
|
||||
int mode_shift, struct rockchip_pll_rate_table *rate_table,
|
||||
u8 clk_pll_flags, spinlock_t *lock);
|
||||
|
||||
struct rockchip_cpuclk_clksel {
|
||||
|
|
@ -173,16 +173,16 @@ struct rockchip_cpuclk_reg_data {
|
|||
};
|
||||
|
||||
struct clk *rockchip_clk_register_cpuclk(const char *name,
|
||||
const char **parent_names, u8 num_parents,
|
||||
const char *const *parent_names, u8 num_parents,
|
||||
const struct rockchip_cpuclk_reg_data *reg_data,
|
||||
const struct rockchip_cpuclk_rate_table *rates,
|
||||
int nrates, void __iomem *reg_base, spinlock_t *lock);
|
||||
|
||||
struct clk *rockchip_clk_register_mmc(const char *name,
|
||||
const char **parent_names, u8 num_parents,
|
||||
const char *const *parent_names, u8 num_parents,
|
||||
void __iomem *reg, int shift);
|
||||
|
||||
#define PNAME(x) static const char *x[] __initdata
|
||||
#define PNAME(x) static const char *const x[] __initconst
|
||||
|
||||
enum rockchip_clk_branch_type {
|
||||
branch_composite,
|
||||
|
|
@ -197,7 +197,7 @@ struct rockchip_clk_branch {
|
|||
unsigned int id;
|
||||
enum rockchip_clk_branch_type branch_type;
|
||||
const char *name;
|
||||
const char **parent_names;
|
||||
const char *const *parent_names;
|
||||
u8 num_parents;
|
||||
unsigned long flags;
|
||||
int muxdiv_offset;
|
||||
|
|
@ -403,7 +403,7 @@ void rockchip_clk_register_branches(struct rockchip_clk_branch *clk_list,
|
|||
void rockchip_clk_register_plls(struct rockchip_pll_clock *pll_list,
|
||||
unsigned int nr_pll, int grf_lock_offset);
|
||||
void rockchip_clk_register_armclk(unsigned int lookup_id, const char *name,
|
||||
const char **parent_names, u8 num_parents,
|
||||
const char *const *parent_names, u8 num_parents,
|
||||
const struct rockchip_cpuclk_reg_data *reg_data,
|
||||
const struct rockchip_cpuclk_rate_table *rates,
|
||||
int nrates);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
# Samsung Clock specific Makefile
|
||||
#
|
||||
|
||||
obj-$(CONFIG_COMMON_CLK) += clk.o clk-pll.o
|
||||
obj-$(CONFIG_COMMON_CLK) += clk.o clk-pll.o clk-cpu.o
|
||||
obj-$(CONFIG_SOC_EXYNOS3250) += clk-exynos3250.o
|
||||
obj-$(CONFIG_ARCH_EXYNOS4) += clk-exynos4.o
|
||||
obj-$(CONFIG_SOC_EXYNOS4415) += clk-exynos4415.o
|
||||
|
|
|
|||
349
drivers/clk/samsung/clk-cpu.c
Normal file
349
drivers/clk/samsung/clk-cpu.c
Normal file
|
|
@ -0,0 +1,349 @@
|
|||
/*
|
||||
* Copyright (c) 2014 Samsung Electronics Co., Ltd.
|
||||
* Author: Thomas Abraham <thomas.ab@samsung.com>
|
||||
*
|
||||
* Copyright (c) 2015 Samsung Electronics Co., Ltd.
|
||||
* Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This file contains the utility function to register CPU clock for Samsung
|
||||
* Exynos platforms. A CPU clock is defined as a clock supplied to a CPU or a
|
||||
* group of CPUs. The CPU clock is typically derived from a hierarchy of clock
|
||||
* blocks which includes mux and divider blocks. There are a number of other
|
||||
* auxiliary clocks supplied to the CPU domain such as the debug blocks and AXI
|
||||
* clock for CPU domain. The rates of these auxiliary clocks are related to the
|
||||
* CPU clock rate and this relation is usually specified in the hardware manual
|
||||
* of the SoC or supplied after the SoC characterization.
|
||||
*
|
||||
* The below implementation of the CPU clock allows the rate changes of the CPU
|
||||
* clock and the corresponding rate changes of the auxillary clocks of the CPU
|
||||
* domain. The platform clock driver provides a clock register configuration
|
||||
* for each configurable rate which is then used to program the clock hardware
|
||||
* registers to acheive a fast co-oridinated rate change for all the CPU domain
|
||||
* clocks.
|
||||
*
|
||||
* On a rate change request for the CPU clock, the rate change is propagated
|
||||
* upto the PLL supplying the clock to the CPU domain clock blocks. While the
|
||||
* CPU domain PLL is reconfigured, the CPU domain clocks are driven using an
|
||||
* alternate clock source. If required, the alternate clock source is divided
|
||||
* down in order to keep the output clock rate within the previous OPP limits.
|
||||
*/
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include "clk-cpu.h"
|
||||
|
||||
#define E4210_SRC_CPU 0x0
|
||||
#define E4210_STAT_CPU 0x200
|
||||
#define E4210_DIV_CPU0 0x300
|
||||
#define E4210_DIV_CPU1 0x304
|
||||
#define E4210_DIV_STAT_CPU0 0x400
|
||||
#define E4210_DIV_STAT_CPU1 0x404
|
||||
|
||||
#define E4210_DIV0_RATIO0_MASK 0x7
|
||||
#define E4210_DIV1_HPM_MASK (0x7 << 4)
|
||||
#define E4210_DIV1_COPY_MASK (0x7 << 0)
|
||||
#define E4210_MUX_HPM_MASK (1 << 20)
|
||||
#define E4210_DIV0_ATB_SHIFT 16
|
||||
#define E4210_DIV0_ATB_MASK (DIV_MASK << E4210_DIV0_ATB_SHIFT)
|
||||
|
||||
#define MAX_DIV 8
|
||||
#define DIV_MASK 7
|
||||
#define DIV_MASK_ALL 0xffffffff
|
||||
#define MUX_MASK 7
|
||||
|
||||
/*
|
||||
* Helper function to wait until divider(s) have stabilized after the divider
|
||||
* value has changed.
|
||||
*/
|
||||
static void wait_until_divider_stable(void __iomem *div_reg, unsigned long mask)
|
||||
{
|
||||
unsigned long timeout = jiffies + msecs_to_jiffies(10);
|
||||
|
||||
do {
|
||||
if (!(readl(div_reg) & mask))
|
||||
return;
|
||||
} while (time_before(jiffies, timeout));
|
||||
|
||||
if (!(readl(div_reg) & mask))
|
||||
return;
|
||||
|
||||
pr_err("%s: timeout in divider stablization\n", __func__);
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function to wait until mux has stabilized after the mux selection
|
||||
* value was changed.
|
||||
*/
|
||||
static void wait_until_mux_stable(void __iomem *mux_reg, u32 mux_pos,
|
||||
unsigned long mux_value)
|
||||
{
|
||||
unsigned long timeout = jiffies + msecs_to_jiffies(10);
|
||||
|
||||
do {
|
||||
if (((readl(mux_reg) >> mux_pos) & MUX_MASK) == mux_value)
|
||||
return;
|
||||
} while (time_before(jiffies, timeout));
|
||||
|
||||
if (((readl(mux_reg) >> mux_pos) & MUX_MASK) == mux_value)
|
||||
return;
|
||||
|
||||
pr_err("%s: re-parenting mux timed-out\n", __func__);
|
||||
}
|
||||
|
||||
/* common round rate callback useable for all types of CPU clocks */
|
||||
static long exynos_cpuclk_round_rate(struct clk_hw *hw,
|
||||
unsigned long drate, unsigned long *prate)
|
||||
{
|
||||
struct clk *parent = __clk_get_parent(hw->clk);
|
||||
*prate = __clk_round_rate(parent, drate);
|
||||
return *prate;
|
||||
}
|
||||
|
||||
/* common recalc rate callback useable for all types of CPU clocks */
|
||||
static unsigned long exynos_cpuclk_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
/*
|
||||
* The CPU clock output (armclk) rate is the same as its parent
|
||||
* rate. Although there exist certain dividers inside the CPU
|
||||
* clock block that could be used to divide the parent clock,
|
||||
* the driver does not make use of them currently, except during
|
||||
* frequency transitions.
|
||||
*/
|
||||
return parent_rate;
|
||||
}
|
||||
|
||||
static const struct clk_ops exynos_cpuclk_clk_ops = {
|
||||
.recalc_rate = exynos_cpuclk_recalc_rate,
|
||||
.round_rate = exynos_cpuclk_round_rate,
|
||||
};
|
||||
|
||||
/*
|
||||
* Helper function to set the 'safe' dividers for the CPU clock. The parameters
|
||||
* div and mask contain the divider value and the register bit mask of the
|
||||
* dividers to be programmed.
|
||||
*/
|
||||
static void exynos_set_safe_div(void __iomem *base, unsigned long div,
|
||||
unsigned long mask)
|
||||
{
|
||||
unsigned long div0;
|
||||
|
||||
div0 = readl(base + E4210_DIV_CPU0);
|
||||
div0 = (div0 & ~mask) | (div & mask);
|
||||
writel(div0, base + E4210_DIV_CPU0);
|
||||
wait_until_divider_stable(base + E4210_DIV_STAT_CPU0, mask);
|
||||
}
|
||||
|
||||
/* handler for pre-rate change notification from parent clock */
|
||||
static int exynos_cpuclk_pre_rate_change(struct clk_notifier_data *ndata,
|
||||
struct exynos_cpuclk *cpuclk, void __iomem *base)
|
||||
{
|
||||
const struct exynos_cpuclk_cfg_data *cfg_data = cpuclk->cfg;
|
||||
unsigned long alt_prate = clk_get_rate(cpuclk->alt_parent);
|
||||
unsigned long alt_div = 0, alt_div_mask = DIV_MASK;
|
||||
unsigned long div0, div1 = 0, mux_reg;
|
||||
|
||||
/* find out the divider values to use for clock data */
|
||||
while ((cfg_data->prate * 1000) != ndata->new_rate) {
|
||||
if (cfg_data->prate == 0)
|
||||
return -EINVAL;
|
||||
cfg_data++;
|
||||
}
|
||||
|
||||
spin_lock(cpuclk->lock);
|
||||
|
||||
/*
|
||||
* For the selected PLL clock frequency, get the pre-defined divider
|
||||
* values. If the clock for sclk_hpm is not sourced from apll, then
|
||||
* the values for DIV_COPY and DIV_HPM dividers need not be set.
|
||||
*/
|
||||
div0 = cfg_data->div0;
|
||||
if (test_bit(CLK_CPU_HAS_DIV1, &cpuclk->flags)) {
|
||||
div1 = cfg_data->div1;
|
||||
if (readl(base + E4210_SRC_CPU) & E4210_MUX_HPM_MASK)
|
||||
div1 = readl(base + E4210_DIV_CPU1) &
|
||||
(E4210_DIV1_HPM_MASK | E4210_DIV1_COPY_MASK);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the old parent clock speed is less than the clock speed of
|
||||
* the alternate parent, then it should be ensured that at no point
|
||||
* the armclk speed is more than the old_prate until the dividers are
|
||||
* set. Also workaround the issue of the dividers being set to lower
|
||||
* values before the parent clock speed is set to new lower speed
|
||||
* (this can result in too high speed of armclk output clocks).
|
||||
*/
|
||||
if (alt_prate > ndata->old_rate || ndata->old_rate > ndata->new_rate) {
|
||||
unsigned long tmp_rate = min(ndata->old_rate, ndata->new_rate);
|
||||
|
||||
alt_div = DIV_ROUND_UP(alt_prate, tmp_rate) - 1;
|
||||
WARN_ON(alt_div >= MAX_DIV);
|
||||
|
||||
if (test_bit(CLK_CPU_NEEDS_DEBUG_ALT_DIV, &cpuclk->flags)) {
|
||||
/*
|
||||
* In Exynos4210, ATB clock parent is also mout_core. So
|
||||
* ATB clock also needs to be mantained at safe speed.
|
||||
*/
|
||||
alt_div |= E4210_DIV0_ATB_MASK;
|
||||
alt_div_mask |= E4210_DIV0_ATB_MASK;
|
||||
}
|
||||
exynos_set_safe_div(base, alt_div, alt_div_mask);
|
||||
div0 |= alt_div;
|
||||
}
|
||||
|
||||
/* select sclk_mpll as the alternate parent */
|
||||
mux_reg = readl(base + E4210_SRC_CPU);
|
||||
writel(mux_reg | (1 << 16), base + E4210_SRC_CPU);
|
||||
wait_until_mux_stable(base + E4210_STAT_CPU, 16, 2);
|
||||
|
||||
/* alternate parent is active now. set the dividers */
|
||||
writel(div0, base + E4210_DIV_CPU0);
|
||||
wait_until_divider_stable(base + E4210_DIV_STAT_CPU0, DIV_MASK_ALL);
|
||||
|
||||
if (test_bit(CLK_CPU_HAS_DIV1, &cpuclk->flags)) {
|
||||
writel(div1, base + E4210_DIV_CPU1);
|
||||
wait_until_divider_stable(base + E4210_DIV_STAT_CPU1,
|
||||
DIV_MASK_ALL);
|
||||
}
|
||||
|
||||
spin_unlock(cpuclk->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* handler for post-rate change notification from parent clock */
|
||||
static int exynos_cpuclk_post_rate_change(struct clk_notifier_data *ndata,
|
||||
struct exynos_cpuclk *cpuclk, void __iomem *base)
|
||||
{
|
||||
const struct exynos_cpuclk_cfg_data *cfg_data = cpuclk->cfg;
|
||||
unsigned long div = 0, div_mask = DIV_MASK;
|
||||
unsigned long mux_reg;
|
||||
|
||||
/* find out the divider values to use for clock data */
|
||||
if (test_bit(CLK_CPU_NEEDS_DEBUG_ALT_DIV, &cpuclk->flags)) {
|
||||
while ((cfg_data->prate * 1000) != ndata->new_rate) {
|
||||
if (cfg_data->prate == 0)
|
||||
return -EINVAL;
|
||||
cfg_data++;
|
||||
}
|
||||
}
|
||||
|
||||
spin_lock(cpuclk->lock);
|
||||
|
||||
/* select mout_apll as the alternate parent */
|
||||
mux_reg = readl(base + E4210_SRC_CPU);
|
||||
writel(mux_reg & ~(1 << 16), base + E4210_SRC_CPU);
|
||||
wait_until_mux_stable(base + E4210_STAT_CPU, 16, 1);
|
||||
|
||||
if (test_bit(CLK_CPU_NEEDS_DEBUG_ALT_DIV, &cpuclk->flags)) {
|
||||
div |= (cfg_data->div0 & E4210_DIV0_ATB_MASK);
|
||||
div_mask |= E4210_DIV0_ATB_MASK;
|
||||
}
|
||||
|
||||
exynos_set_safe_div(base, div, div_mask);
|
||||
spin_unlock(cpuclk->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This notifier function is called for the pre-rate and post-rate change
|
||||
* notifications of the parent clock of cpuclk.
|
||||
*/
|
||||
static int exynos_cpuclk_notifier_cb(struct notifier_block *nb,
|
||||
unsigned long event, void *data)
|
||||
{
|
||||
struct clk_notifier_data *ndata = data;
|
||||
struct exynos_cpuclk *cpuclk;
|
||||
void __iomem *base;
|
||||
int err = 0;
|
||||
|
||||
cpuclk = container_of(nb, struct exynos_cpuclk, clk_nb);
|
||||
base = cpuclk->ctrl_base;
|
||||
|
||||
if (event == PRE_RATE_CHANGE)
|
||||
err = exynos_cpuclk_pre_rate_change(ndata, cpuclk, base);
|
||||
else if (event == POST_RATE_CHANGE)
|
||||
err = exynos_cpuclk_post_rate_change(ndata, cpuclk, base);
|
||||
|
||||
return notifier_from_errno(err);
|
||||
}
|
||||
|
||||
/* helper function to register a CPU clock */
|
||||
int __init exynos_register_cpu_clock(struct samsung_clk_provider *ctx,
|
||||
unsigned int lookup_id, const char *name, const char *parent,
|
||||
const char *alt_parent, unsigned long offset,
|
||||
const struct exynos_cpuclk_cfg_data *cfg,
|
||||
unsigned long num_cfgs, unsigned long flags)
|
||||
{
|
||||
struct exynos_cpuclk *cpuclk;
|
||||
struct clk_init_data init;
|
||||
struct clk *clk;
|
||||
int ret = 0;
|
||||
|
||||
cpuclk = kzalloc(sizeof(*cpuclk), GFP_KERNEL);
|
||||
if (!cpuclk)
|
||||
return -ENOMEM;
|
||||
|
||||
init.name = name;
|
||||
init.flags = CLK_SET_RATE_PARENT;
|
||||
init.parent_names = &parent;
|
||||
init.num_parents = 1;
|
||||
init.ops = &exynos_cpuclk_clk_ops;
|
||||
|
||||
cpuclk->hw.init = &init;
|
||||
cpuclk->ctrl_base = ctx->reg_base + offset;
|
||||
cpuclk->lock = &ctx->lock;
|
||||
cpuclk->flags = flags;
|
||||
cpuclk->clk_nb.notifier_call = exynos_cpuclk_notifier_cb;
|
||||
|
||||
cpuclk->alt_parent = __clk_lookup(alt_parent);
|
||||
if (!cpuclk->alt_parent) {
|
||||
pr_err("%s: could not lookup alternate parent %s\n",
|
||||
__func__, alt_parent);
|
||||
ret = -EINVAL;
|
||||
goto free_cpuclk;
|
||||
}
|
||||
|
||||
clk = __clk_lookup(parent);
|
||||
if (!clk) {
|
||||
pr_err("%s: could not lookup parent clock %s\n",
|
||||
__func__, parent);
|
||||
ret = -EINVAL;
|
||||
goto free_cpuclk;
|
||||
}
|
||||
|
||||
ret = clk_notifier_register(clk, &cpuclk->clk_nb);
|
||||
if (ret) {
|
||||
pr_err("%s: failed to register clock notifier for %s\n",
|
||||
__func__, name);
|
||||
goto free_cpuclk;
|
||||
}
|
||||
|
||||
cpuclk->cfg = kmemdup(cfg, sizeof(*cfg) * num_cfgs, GFP_KERNEL);
|
||||
if (!cpuclk->cfg) {
|
||||
pr_err("%s: could not allocate memory for cpuclk data\n",
|
||||
__func__);
|
||||
ret = -ENOMEM;
|
||||
goto unregister_clk_nb;
|
||||
}
|
||||
|
||||
clk = clk_register(NULL, &cpuclk->hw);
|
||||
if (IS_ERR(clk)) {
|
||||
pr_err("%s: could not register cpuclk %s\n", __func__, name);
|
||||
ret = PTR_ERR(clk);
|
||||
goto free_cpuclk_data;
|
||||
}
|
||||
|
||||
samsung_clk_add_lookup(ctx, clk, lookup_id);
|
||||
return 0;
|
||||
|
||||
free_cpuclk_data:
|
||||
kfree(cpuclk->cfg);
|
||||
unregister_clk_nb:
|
||||
clk_notifier_unregister(__clk_lookup(parent), &cpuclk->clk_nb);
|
||||
free_cpuclk:
|
||||
kfree(cpuclk);
|
||||
return ret;
|
||||
}
|
||||
73
drivers/clk/samsung/clk-cpu.h
Normal file
73
drivers/clk/samsung/clk-cpu.h
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright (c) 2014 Samsung Electronics Co., Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Common Clock Framework support for all PLL's in Samsung platforms
|
||||
*/
|
||||
|
||||
#ifndef __SAMSUNG_CLK_CPU_H
|
||||
#define __SAMSUNG_CLK_CPU_H
|
||||
|
||||
#include "clk.h"
|
||||
|
||||
/**
|
||||
* struct exynos_cpuclk_data: config data to setup cpu clocks.
|
||||
* @prate: frequency of the primary parent clock (in KHz).
|
||||
* @div0: value to be programmed in the div_cpu0 register.
|
||||
* @div1: value to be programmed in the div_cpu1 register.
|
||||
*
|
||||
* This structure holds the divider configuration data for dividers in the CPU
|
||||
* clock domain. The parent frequency at which these divider values are valid is
|
||||
* specified in @prate. The @prate is the frequency of the primary parent clock.
|
||||
* For CPU clock domains that do not have a DIV1 register, the @div1 member
|
||||
* value is not used.
|
||||
*/
|
||||
struct exynos_cpuclk_cfg_data {
|
||||
unsigned long prate;
|
||||
unsigned long div0;
|
||||
unsigned long div1;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct exynos_cpuclk: information about clock supplied to a CPU core.
|
||||
* @hw: handle between CCF and CPU clock.
|
||||
* @alt_parent: alternate parent clock to use when switching the speed
|
||||
* of the primary parent clock.
|
||||
* @ctrl_base: base address of the clock controller.
|
||||
* @lock: cpu clock domain register access lock.
|
||||
* @cfg: cpu clock rate configuration data.
|
||||
* @num_cfgs: number of array elements in @cfg array.
|
||||
* @clk_nb: clock notifier registered for changes in clock speed of the
|
||||
* primary parent clock.
|
||||
* @flags: configuration flags for the CPU clock.
|
||||
*
|
||||
* This structure holds information required for programming the CPU clock for
|
||||
* various clock speeds.
|
||||
*/
|
||||
struct exynos_cpuclk {
|
||||
struct clk_hw hw;
|
||||
struct clk *alt_parent;
|
||||
void __iomem *ctrl_base;
|
||||
spinlock_t *lock;
|
||||
const struct exynos_cpuclk_cfg_data *cfg;
|
||||
const unsigned long num_cfgs;
|
||||
struct notifier_block clk_nb;
|
||||
unsigned long flags;
|
||||
|
||||
/* The CPU clock registers has DIV1 configuration register */
|
||||
#define CLK_CPU_HAS_DIV1 (1 << 0)
|
||||
/* When ALT parent is active, debug clocks need safe divider values */
|
||||
#define CLK_CPU_NEEDS_DEBUG_ALT_DIV (1 << 1)
|
||||
};
|
||||
|
||||
extern int __init exynos_register_cpu_clock(struct samsung_clk_provider *ctx,
|
||||
unsigned int lookup_id, const char *name,
|
||||
const char *parent, const char *alt_parent,
|
||||
unsigned long offset,
|
||||
const struct exynos_cpuclk_cfg_data *cfg,
|
||||
unsigned long num_cfgs, unsigned long flags);
|
||||
|
||||
#endif /* __SAMSUNG_CLK_CPU_H */
|
||||
|
|
@ -19,6 +19,7 @@
|
|||
#include <linux/syscore_ops.h>
|
||||
|
||||
#include "clk.h"
|
||||
#include "clk-cpu.h"
|
||||
|
||||
/* Exynos4 clock controller register offsets */
|
||||
#define SRC_LEFTBUS 0x4200
|
||||
|
|
@ -534,7 +535,8 @@ static struct samsung_fixed_factor_clock exynos4x12_fixed_factor_clks[] __initda
|
|||
/* list of mux clocks supported in all exynos4 soc's */
|
||||
static struct samsung_mux_clock exynos4_mux_clks[] __initdata = {
|
||||
MUX_FA(CLK_MOUT_APLL, "mout_apll", mout_apll_p, SRC_CPU, 0, 1,
|
||||
CLK_SET_RATE_PARENT, 0, "mout_apll"),
|
||||
CLK_SET_RATE_PARENT | CLK_RECALC_NEW_RATES, 0,
|
||||
"mout_apll"),
|
||||
MUX(CLK_MOUT_HDMI, "mout_hdmi", mout_hdmi_p, SRC_TV, 0, 1),
|
||||
MUX(0, "mout_mfc1", sclk_evpll_p, SRC_MFC, 4, 1),
|
||||
MUX(0, "mout_mfc", mout_mfc_p, SRC_MFC, 8, 1),
|
||||
|
|
@ -1378,6 +1380,22 @@ static void __init exynos4x12_core_down_clock(void)
|
|||
__raw_writel(0x0, reg_base + E4X12_PWR_CTRL2);
|
||||
}
|
||||
|
||||
#define E4210_CPU_DIV0(apll, pclk_dbg, atb, periph, corem1, corem0) \
|
||||
(((apll) << 24) | ((pclk_dbg) << 20) | ((atb) << 16) | \
|
||||
((periph) << 12) | ((corem1) << 8) | ((corem0) << 4))
|
||||
#define E4210_CPU_DIV1(hpm, copy) \
|
||||
(((hpm) << 4) | ((copy) << 0))
|
||||
|
||||
static const struct exynos_cpuclk_cfg_data e4210_armclk_d[] __initconst = {
|
||||
{ 1200000, E4210_CPU_DIV0(7, 1, 4, 3, 7, 3), E4210_CPU_DIV1(0, 5), },
|
||||
{ 1000000, E4210_CPU_DIV0(7, 1, 4, 3, 7, 3), E4210_CPU_DIV1(0, 4), },
|
||||
{ 800000, E4210_CPU_DIV0(7, 1, 3, 3, 7, 3), E4210_CPU_DIV1(0, 3), },
|
||||
{ 500000, E4210_CPU_DIV0(7, 1, 3, 3, 7, 3), E4210_CPU_DIV1(0, 3), },
|
||||
{ 400000, E4210_CPU_DIV0(7, 1, 3, 3, 7, 3), E4210_CPU_DIV1(0, 3), },
|
||||
{ 200000, E4210_CPU_DIV0(0, 1, 1, 1, 3, 1), E4210_CPU_DIV1(0, 3), },
|
||||
{ 0 },
|
||||
};
|
||||
|
||||
/* register exynos4 clocks */
|
||||
static void __init exynos4_clk_init(struct device_node *np,
|
||||
enum exynos4_soc soc)
|
||||
|
|
@ -1455,6 +1473,10 @@ static void __init exynos4_clk_init(struct device_node *np,
|
|||
samsung_clk_register_fixed_factor(ctx,
|
||||
exynos4210_fixed_factor_clks,
|
||||
ARRAY_SIZE(exynos4210_fixed_factor_clks));
|
||||
exynos_register_cpu_clock(ctx, CLK_ARM_CLK, "armclk",
|
||||
mout_core_p4210[0], mout_core_p4210[1], 0x14200,
|
||||
e4210_armclk_d, ARRAY_SIZE(e4210_armclk_d),
|
||||
CLK_CPU_NEEDS_DEBUG_ALT_DIV | CLK_CPU_HAS_DIV1);
|
||||
} else {
|
||||
samsung_clk_register_mux(ctx, exynos4x12_mux_clks,
|
||||
ARRAY_SIZE(exynos4x12_mux_clks));
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ PNAME(mout_aud_pll_user_p) = {"fin_pll", "fout_aud_pll"};
|
|||
PNAME(mout_sclk_aud_i2s_p) = {"mout_aud_pll_user", "ioclk_i2s_cdclk"};
|
||||
PNAME(mout_sclk_aud_pcm_p) = {"mout_aud_pll_user", "ioclk_pcm_extclk"};
|
||||
|
||||
struct samsung_mux_clock aud_mux_clks[] __initdata = {
|
||||
static struct samsung_mux_clock aud_mux_clks[] __initdata = {
|
||||
MUX(AUD_MOUT_AUD_PLL_USER, "mout_aud_pll_user", mout_aud_pll_user_p,
|
||||
MUX_SEL_AUD, 0, 1),
|
||||
MUX(AUD_MOUT_SCLK_AUD_I2S, "mout_sclk_aud_i2s", mout_sclk_aud_i2s_p,
|
||||
|
|
@ -103,7 +103,7 @@ struct samsung_mux_clock aud_mux_clks[] __initdata = {
|
|||
MUX_SEL_AUD, 8, 1),
|
||||
};
|
||||
|
||||
struct samsung_div_clock aud_div_clks[] __initdata = {
|
||||
static struct samsung_div_clock aud_div_clks[] __initdata = {
|
||||
DIV(AUD_DOUT_ACLK_AUD_131, "dout_aclk_aud_131", "mout_aud_pll_user",
|
||||
DIV_AUD0, 0, 4),
|
||||
|
||||
|
|
@ -115,7 +115,7 @@ struct samsung_div_clock aud_div_clks[] __initdata = {
|
|||
DIV_AUD1, 12, 4),
|
||||
};
|
||||
|
||||
struct samsung_gate_clock aud_gate_clks[] __initdata = {
|
||||
static struct samsung_gate_clock aud_gate_clks[] __initdata = {
|
||||
GATE(AUD_SCLK_I2S, "sclk_aud_i2s", "dout_sclk_aud_i2s",
|
||||
EN_SCLK_AUD, 0, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(AUD_SCLK_PCM, "sclk_aud_pcm", "dout_sclk_aud_pcm",
|
||||
|
|
@ -135,7 +135,7 @@ struct samsung_gate_clock aud_gate_clks[] __initdata = {
|
|||
|
||||
static void __init exynos5260_clk_aud_init(struct device_node *np)
|
||||
{
|
||||
struct samsung_cmu_info cmu = {0};
|
||||
struct samsung_cmu_info cmu = { NULL };
|
||||
|
||||
cmu.mux_clks = aud_mux_clks;
|
||||
cmu.nr_mux_clks = ARRAY_SIZE(aud_mux_clks);
|
||||
|
|
@ -203,7 +203,7 @@ PNAME(mout_phyclk_mipi_dphy_4lmrxclk_esc0_user_p) = {"fin_pll",
|
|||
PNAME(mout_sclk_hdmi_spdif_p) = {"fin_pll", "ioclk_spdif_extclk",
|
||||
"dout_aclk_peri_aud", "phyclk_hdmi_phy_ref_cko"};
|
||||
|
||||
struct samsung_mux_clock disp_mux_clks[] __initdata = {
|
||||
static struct samsung_mux_clock disp_mux_clks[] __initdata = {
|
||||
MUX(DISP_MOUT_ACLK_DISP_333_USER, "mout_aclk_disp_333_user",
|
||||
mout_aclk_disp_333_user_p,
|
||||
MUX_SEL_DISP0, 0, 1),
|
||||
|
|
@ -272,7 +272,7 @@ struct samsung_mux_clock disp_mux_clks[] __initdata = {
|
|||
MUX_SEL_DISP4, 4, 2),
|
||||
};
|
||||
|
||||
struct samsung_div_clock disp_div_clks[] __initdata = {
|
||||
static struct samsung_div_clock disp_div_clks[] __initdata = {
|
||||
DIV(DISP_DOUT_PCLK_DISP_111, "dout_pclk_disp_111",
|
||||
"mout_aclk_disp_222_user",
|
||||
DIV_DISP, 8, 4),
|
||||
|
|
@ -285,7 +285,7 @@ struct samsung_div_clock disp_div_clks[] __initdata = {
|
|||
DIV_DISP, 16, 4),
|
||||
};
|
||||
|
||||
struct samsung_gate_clock disp_gate_clks[] __initdata = {
|
||||
static struct samsung_gate_clock disp_gate_clks[] __initdata = {
|
||||
GATE(DISP_MOUT_HDMI_PHY_PIXEL_USER, "sclk_hdmi_link_i_pixel",
|
||||
"mout_phyclk_hdmi_phy_pixel_clko_user",
|
||||
EN_SCLK_DISP0, 26, CLK_SET_RATE_PARENT, 0),
|
||||
|
|
@ -325,7 +325,7 @@ struct samsung_gate_clock disp_gate_clks[] __initdata = {
|
|||
|
||||
static void __init exynos5260_clk_disp_init(struct device_node *np)
|
||||
{
|
||||
struct samsung_cmu_info cmu = {0};
|
||||
struct samsung_cmu_info cmu = { NULL };
|
||||
|
||||
cmu.mux_clks = disp_mux_clks;
|
||||
cmu.nr_mux_clks = ARRAY_SIZE(disp_mux_clks);
|
||||
|
|
@ -363,13 +363,13 @@ static unsigned long egl_clk_regs[] __initdata = {
|
|||
PNAME(mout_egl_b_p) = {"mout_egl_pll", "dout_bus_pll"};
|
||||
PNAME(mout_egl_pll_p) = {"fin_pll", "fout_egl_pll"};
|
||||
|
||||
struct samsung_mux_clock egl_mux_clks[] __initdata = {
|
||||
static struct samsung_mux_clock egl_mux_clks[] __initdata = {
|
||||
MUX(EGL_MOUT_EGL_PLL, "mout_egl_pll", mout_egl_pll_p,
|
||||
MUX_SEL_EGL, 4, 1),
|
||||
MUX(EGL_MOUT_EGL_B, "mout_egl_b", mout_egl_b_p, MUX_SEL_EGL, 16, 1),
|
||||
};
|
||||
|
||||
struct samsung_div_clock egl_div_clks[] __initdata = {
|
||||
static struct samsung_div_clock egl_div_clks[] __initdata = {
|
||||
DIV(EGL_DOUT_EGL1, "dout_egl1", "mout_egl_b", DIV_EGL, 0, 3),
|
||||
DIV(EGL_DOUT_EGL2, "dout_egl2", "dout_egl1", DIV_EGL, 4, 3),
|
||||
DIV(EGL_DOUT_ACLK_EGL, "dout_aclk_egl", "dout_egl2", DIV_EGL, 8, 3),
|
||||
|
|
@ -389,7 +389,7 @@ static struct samsung_pll_clock egl_pll_clks[] __initdata = {
|
|||
|
||||
static void __init exynos5260_clk_egl_init(struct device_node *np)
|
||||
{
|
||||
struct samsung_cmu_info cmu = {0};
|
||||
struct samsung_cmu_info cmu = { NULL };
|
||||
|
||||
cmu.pll_clks = egl_pll_clks;
|
||||
cmu.nr_pll_clks = ARRAY_SIZE(egl_pll_clks);
|
||||
|
|
@ -433,7 +433,7 @@ PNAME(mout_phyclk_usbdrd30_pipe_pclk_user_p) = {"fin_pll",
|
|||
PNAME(mout_phyclk_usbdrd30_phyclock_user_p) = {"fin_pll",
|
||||
"phyclk_usbdrd30_udrd30_phyclock"};
|
||||
|
||||
struct samsung_mux_clock fsys_mux_clks[] __initdata = {
|
||||
static struct samsung_mux_clock fsys_mux_clks[] __initdata = {
|
||||
MUX(FSYS_MOUT_PHYCLK_USBDRD30_PHYCLOCK_USER,
|
||||
"mout_phyclk_usbdrd30_phyclock_user",
|
||||
mout_phyclk_usbdrd30_phyclock_user_p,
|
||||
|
|
@ -456,7 +456,7 @@ struct samsung_mux_clock fsys_mux_clks[] __initdata = {
|
|||
MUX_SEL_FSYS1, 16, 1),
|
||||
};
|
||||
|
||||
struct samsung_gate_clock fsys_gate_clks[] __initdata = {
|
||||
static struct samsung_gate_clock fsys_gate_clks[] __initdata = {
|
||||
GATE(FSYS_PHYCLK_USBHOST20, "phyclk_usbhost20_phyclock",
|
||||
"mout_phyclk_usbdrd30_phyclock_user",
|
||||
EN_SCLK_FSYS, 1, 0, 0),
|
||||
|
|
@ -491,7 +491,7 @@ struct samsung_gate_clock fsys_gate_clks[] __initdata = {
|
|||
|
||||
static void __init exynos5260_clk_fsys_init(struct device_node *np)
|
||||
{
|
||||
struct samsung_cmu_info cmu = {0};
|
||||
struct samsung_cmu_info cmu = { NULL };
|
||||
|
||||
cmu.mux_clks = fsys_mux_clks;
|
||||
cmu.nr_mux_clks = ARRAY_SIZE(fsys_mux_clks);
|
||||
|
|
@ -537,18 +537,18 @@ static unsigned long g2d_clk_regs[] __initdata = {
|
|||
|
||||
PNAME(mout_aclk_g2d_333_user_p) = {"fin_pll", "dout_aclk_g2d_333"};
|
||||
|
||||
struct samsung_mux_clock g2d_mux_clks[] __initdata = {
|
||||
static struct samsung_mux_clock g2d_mux_clks[] __initdata = {
|
||||
MUX(G2D_MOUT_ACLK_G2D_333_USER, "mout_aclk_g2d_333_user",
|
||||
mout_aclk_g2d_333_user_p,
|
||||
MUX_SEL_G2D, 0, 1),
|
||||
};
|
||||
|
||||
struct samsung_div_clock g2d_div_clks[] __initdata = {
|
||||
static struct samsung_div_clock g2d_div_clks[] __initdata = {
|
||||
DIV(G2D_DOUT_PCLK_G2D_83, "dout_pclk_g2d_83", "mout_aclk_g2d_333_user",
|
||||
DIV_G2D, 0, 3),
|
||||
};
|
||||
|
||||
struct samsung_gate_clock g2d_gate_clks[] __initdata = {
|
||||
static struct samsung_gate_clock g2d_gate_clks[] __initdata = {
|
||||
GATE(G2D_CLK_G2D, "clk_g2d", "mout_aclk_g2d_333_user",
|
||||
EN_IP_G2D, 4, 0, 0),
|
||||
GATE(G2D_CLK_JPEG, "clk_jpeg", "mout_aclk_g2d_333_user",
|
||||
|
|
@ -580,7 +580,7 @@ struct samsung_gate_clock g2d_gate_clks[] __initdata = {
|
|||
|
||||
static void __init exynos5260_clk_g2d_init(struct device_node *np)
|
||||
{
|
||||
struct samsung_cmu_info cmu = {0};
|
||||
struct samsung_cmu_info cmu = { NULL };
|
||||
|
||||
cmu.mux_clks = g2d_mux_clks;
|
||||
cmu.nr_mux_clks = ARRAY_SIZE(g2d_mux_clks);
|
||||
|
|
@ -617,17 +617,17 @@ static unsigned long g3d_clk_regs[] __initdata = {
|
|||
|
||||
PNAME(mout_g3d_pll_p) = {"fin_pll", "fout_g3d_pll"};
|
||||
|
||||
struct samsung_mux_clock g3d_mux_clks[] __initdata = {
|
||||
static struct samsung_mux_clock g3d_mux_clks[] __initdata = {
|
||||
MUX(G3D_MOUT_G3D_PLL, "mout_g3d_pll", mout_g3d_pll_p,
|
||||
MUX_SEL_G3D, 0, 1),
|
||||
};
|
||||
|
||||
struct samsung_div_clock g3d_div_clks[] __initdata = {
|
||||
static struct samsung_div_clock g3d_div_clks[] __initdata = {
|
||||
DIV(G3D_DOUT_PCLK_G3D, "dout_pclk_g3d", "dout_aclk_g3d", DIV_G3D, 0, 3),
|
||||
DIV(G3D_DOUT_ACLK_G3D, "dout_aclk_g3d", "mout_g3d_pll", DIV_G3D, 4, 3),
|
||||
};
|
||||
|
||||
struct samsung_gate_clock g3d_gate_clks[] __initdata = {
|
||||
static struct samsung_gate_clock g3d_gate_clks[] __initdata = {
|
||||
GATE(G3D_CLK_G3D, "clk_g3d", "dout_aclk_g3d", EN_IP_G3D, 2, 0, 0),
|
||||
GATE(G3D_CLK_G3D_HPM, "clk_g3d_hpm", "dout_aclk_g3d",
|
||||
EN_IP_G3D, 3, 0, 0),
|
||||
|
|
@ -641,7 +641,7 @@ static struct samsung_pll_clock g3d_pll_clks[] __initdata = {
|
|||
|
||||
static void __init exynos5260_clk_g3d_init(struct device_node *np)
|
||||
{
|
||||
struct samsung_cmu_info cmu = {0};
|
||||
struct samsung_cmu_info cmu = { NULL };
|
||||
|
||||
cmu.pll_clks = g3d_pll_clks;
|
||||
cmu.nr_pll_clks = ARRAY_SIZE(g3d_pll_clks);
|
||||
|
|
@ -694,7 +694,7 @@ PNAME(mout_aclk_m2m_400_user_p) = {"fin_pll", "dout_aclk_gscl_400"};
|
|||
PNAME(mout_aclk_gscl_fimc_user_p) = {"fin_pll", "dout_aclk_gscl_400"};
|
||||
PNAME(mout_aclk_csis_p) = {"dout_aclk_csis_200", "mout_aclk_gscl_fimc_user"};
|
||||
|
||||
struct samsung_mux_clock gscl_mux_clks[] __initdata = {
|
||||
static struct samsung_mux_clock gscl_mux_clks[] __initdata = {
|
||||
MUX(GSCL_MOUT_ACLK_GSCL_333_USER, "mout_aclk_gscl_333_user",
|
||||
mout_aclk_gscl_333_user_p,
|
||||
MUX_SEL_GSCL, 0, 1),
|
||||
|
|
@ -708,7 +708,7 @@ struct samsung_mux_clock gscl_mux_clks[] __initdata = {
|
|||
MUX_SEL_GSCL, 24, 1),
|
||||
};
|
||||
|
||||
struct samsung_div_clock gscl_div_clks[] __initdata = {
|
||||
static struct samsung_div_clock gscl_div_clks[] __initdata = {
|
||||
DIV(GSCL_DOUT_PCLK_M2M_100, "dout_pclk_m2m_100",
|
||||
"mout_aclk_m2m_400_user",
|
||||
DIV_GSCL, 0, 3),
|
||||
|
|
@ -717,7 +717,7 @@ struct samsung_div_clock gscl_div_clks[] __initdata = {
|
|||
DIV_GSCL, 4, 3),
|
||||
};
|
||||
|
||||
struct samsung_gate_clock gscl_gate_clks[] __initdata = {
|
||||
static struct samsung_gate_clock gscl_gate_clks[] __initdata = {
|
||||
GATE(GSCL_SCLK_CSIS0_WRAP, "sclk_csis0_wrap", "dout_aclk_csis_200",
|
||||
EN_SCLK_GSCL_FIMC, 0, CLK_SET_RATE_PARENT, 0),
|
||||
GATE(GSCL_SCLK_CSIS1_WRAP, "sclk_csis1_wrap", "dout_aclk_csis_200",
|
||||
|
|
@ -776,7 +776,7 @@ struct samsung_gate_clock gscl_gate_clks[] __initdata = {
|
|||
|
||||
static void __init exynos5260_clk_gscl_init(struct device_node *np)
|
||||
{
|
||||
struct samsung_cmu_info cmu = {0};
|
||||
struct samsung_cmu_info cmu = { NULL };
|
||||
|
||||
cmu.mux_clks = gscl_mux_clks;
|
||||
cmu.nr_mux_clks = ARRAY_SIZE(gscl_mux_clks);
|
||||
|
|
@ -813,14 +813,14 @@ static unsigned long isp_clk_regs[] __initdata = {
|
|||
PNAME(mout_isp_400_user_p) = {"fin_pll", "dout_aclk_isp1_400"};
|
||||
PNAME(mout_isp_266_user_p) = {"fin_pll", "dout_aclk_isp1_266"};
|
||||
|
||||
struct samsung_mux_clock isp_mux_clks[] __initdata = {
|
||||
static struct samsung_mux_clock isp_mux_clks[] __initdata = {
|
||||
MUX(ISP_MOUT_ISP_266_USER, "mout_isp_266_user", mout_isp_266_user_p,
|
||||
MUX_SEL_ISP0, 0, 1),
|
||||
MUX(ISP_MOUT_ISP_400_USER, "mout_isp_400_user", mout_isp_400_user_p,
|
||||
MUX_SEL_ISP0, 4, 1),
|
||||
};
|
||||
|
||||
struct samsung_div_clock isp_div_clks[] __initdata = {
|
||||
static struct samsung_div_clock isp_div_clks[] __initdata = {
|
||||
DIV(ISP_DOUT_PCLK_ISP_66, "dout_pclk_isp_66", "mout_kfc",
|
||||
DIV_ISP, 0, 3),
|
||||
DIV(ISP_DOUT_PCLK_ISP_133, "dout_pclk_isp_133", "mout_kfc",
|
||||
|
|
@ -832,7 +832,7 @@ struct samsung_div_clock isp_div_clks[] __initdata = {
|
|||
DIV(ISP_DOUT_SCLK_MPWM, "dout_sclk_mpwm", "mout_kfc", DIV_ISP, 20, 2),
|
||||
};
|
||||
|
||||
struct samsung_gate_clock isp_gate_clks[] __initdata = {
|
||||
static struct samsung_gate_clock isp_gate_clks[] __initdata = {
|
||||
GATE(ISP_CLK_GIC, "clk_isp_gic", "mout_aclk_isp1_266",
|
||||
EN_IP_ISP0, 15, 0, 0),
|
||||
|
||||
|
|
@ -895,7 +895,7 @@ struct samsung_gate_clock isp_gate_clks[] __initdata = {
|
|||
|
||||
static void __init exynos5260_clk_isp_init(struct device_node *np)
|
||||
{
|
||||
struct samsung_cmu_info cmu = {0};
|
||||
struct samsung_cmu_info cmu = { NULL };
|
||||
|
||||
cmu.mux_clks = isp_mux_clks;
|
||||
cmu.nr_mux_clks = ARRAY_SIZE(isp_mux_clks);
|
||||
|
|
@ -934,13 +934,13 @@ static unsigned long kfc_clk_regs[] __initdata = {
|
|||
PNAME(mout_kfc_pll_p) = {"fin_pll", "fout_kfc_pll"};
|
||||
PNAME(mout_kfc_p) = {"mout_kfc_pll", "dout_media_pll"};
|
||||
|
||||
struct samsung_mux_clock kfc_mux_clks[] __initdata = {
|
||||
static struct samsung_mux_clock kfc_mux_clks[] __initdata = {
|
||||
MUX(KFC_MOUT_KFC_PLL, "mout_kfc_pll", mout_kfc_pll_p,
|
||||
MUX_SEL_KFC0, 0, 1),
|
||||
MUX(KFC_MOUT_KFC, "mout_kfc", mout_kfc_p, MUX_SEL_KFC2, 0, 1),
|
||||
};
|
||||
|
||||
struct samsung_div_clock kfc_div_clks[] __initdata = {
|
||||
static struct samsung_div_clock kfc_div_clks[] __initdata = {
|
||||
DIV(KFC_DOUT_KFC1, "dout_kfc1", "mout_kfc", DIV_KFC, 0, 3),
|
||||
DIV(KFC_DOUT_KFC2, "dout_kfc2", "dout_kfc1", DIV_KFC, 4, 3),
|
||||
DIV(KFC_DOUT_KFC_ATCLK, "dout_kfc_atclk", "dout_kfc2", DIV_KFC, 8, 3),
|
||||
|
|
@ -959,7 +959,7 @@ static struct samsung_pll_clock kfc_pll_clks[] __initdata = {
|
|||
|
||||
static void __init exynos5260_clk_kfc_init(struct device_node *np)
|
||||
{
|
||||
struct samsung_cmu_info cmu = {0};
|
||||
struct samsung_cmu_info cmu = { NULL };
|
||||
|
||||
cmu.pll_clks = kfc_pll_clks;
|
||||
cmu.nr_pll_clks = ARRAY_SIZE(kfc_pll_clks);
|
||||
|
|
@ -993,18 +993,18 @@ static unsigned long mfc_clk_regs[] __initdata = {
|
|||
|
||||
PNAME(mout_aclk_mfc_333_user_p) = {"fin_pll", "dout_aclk_mfc_333"};
|
||||
|
||||
struct samsung_mux_clock mfc_mux_clks[] __initdata = {
|
||||
static struct samsung_mux_clock mfc_mux_clks[] __initdata = {
|
||||
MUX(MFC_MOUT_ACLK_MFC_333_USER, "mout_aclk_mfc_333_user",
|
||||
mout_aclk_mfc_333_user_p,
|
||||
MUX_SEL_MFC, 0, 1),
|
||||
};
|
||||
|
||||
struct samsung_div_clock mfc_div_clks[] __initdata = {
|
||||
static struct samsung_div_clock mfc_div_clks[] __initdata = {
|
||||
DIV(MFC_DOUT_PCLK_MFC_83, "dout_pclk_mfc_83", "mout_aclk_mfc_333_user",
|
||||
DIV_MFC, 0, 3),
|
||||
};
|
||||
|
||||
struct samsung_gate_clock mfc_gate_clks[] __initdata = {
|
||||
static struct samsung_gate_clock mfc_gate_clks[] __initdata = {
|
||||
GATE(MFC_CLK_MFC, "clk_mfc", "mout_aclk_mfc_333_user",
|
||||
EN_IP_MFC, 1, 0, 0),
|
||||
GATE(MFC_CLK_SMMU2_MFCM0, "clk_smmu2_mfcm0", "mout_aclk_mfc_333_user",
|
||||
|
|
@ -1015,7 +1015,7 @@ struct samsung_gate_clock mfc_gate_clks[] __initdata = {
|
|||
|
||||
static void __init exynos5260_clk_mfc_init(struct device_node *np)
|
||||
{
|
||||
struct samsung_cmu_info cmu = {0};
|
||||
struct samsung_cmu_info cmu = { NULL };
|
||||
|
||||
cmu.mux_clks = mfc_mux_clks;
|
||||
cmu.nr_mux_clks = ARRAY_SIZE(mfc_mux_clks);
|
||||
|
|
@ -1078,7 +1078,7 @@ PNAME(mout_mif_drex2x_p) = {"dout_mem_pll", "dout_bus_pll"};
|
|||
PNAME(mout_clkm_phy_p) = {"mout_mif_drex", "dout_media_pll"};
|
||||
PNAME(mout_clk2x_phy_p) = {"mout_mif_drex2x", "dout_media_pll"};
|
||||
|
||||
struct samsung_mux_clock mif_mux_clks[] __initdata = {
|
||||
static struct samsung_mux_clock mif_mux_clks[] __initdata = {
|
||||
MUX(MIF_MOUT_MEM_PLL, "mout_mem_pll", mout_mem_pll_p,
|
||||
MUX_SEL_MIF, 0, 1),
|
||||
MUX(MIF_MOUT_BUS_PLL, "mout_bus_pll", mout_bus_pll_p,
|
||||
|
|
@ -1095,7 +1095,7 @@ struct samsung_mux_clock mif_mux_clks[] __initdata = {
|
|||
MUX_SEL_MIF, 24, 1),
|
||||
};
|
||||
|
||||
struct samsung_div_clock mif_div_clks[] __initdata = {
|
||||
static struct samsung_div_clock mif_div_clks[] __initdata = {
|
||||
DIV(MIF_DOUT_MEDIA_PLL, "dout_media_pll", "mout_media_pll",
|
||||
DIV_MIF, 0, 3),
|
||||
DIV(MIF_DOUT_MEM_PLL, "dout_mem_pll", "mout_mem_pll",
|
||||
|
|
@ -1114,7 +1114,7 @@ struct samsung_div_clock mif_div_clks[] __initdata = {
|
|||
DIV_MIF, 28, 4),
|
||||
};
|
||||
|
||||
struct samsung_gate_clock mif_gate_clks[] __initdata = {
|
||||
static struct samsung_gate_clock mif_gate_clks[] __initdata = {
|
||||
GATE(MIF_CLK_LPDDR3PHY_WRAP0, "clk_lpddr3phy_wrap0", "dout_clk2x_phy",
|
||||
EN_IP_MIF, 12, CLK_IGNORE_UNUSED, 0),
|
||||
GATE(MIF_CLK_LPDDR3PHY_WRAP1, "clk_lpddr3phy_wrap1", "dout_clk2x_phy",
|
||||
|
|
@ -1162,7 +1162,7 @@ static struct samsung_pll_clock mif_pll_clks[] __initdata = {
|
|||
|
||||
static void __init exynos5260_clk_mif_init(struct device_node *np)
|
||||
{
|
||||
struct samsung_cmu_info cmu = {0};
|
||||
struct samsung_cmu_info cmu = { NULL };
|
||||
|
||||
cmu.pll_clks = mif_pll_clks;
|
||||
cmu.nr_pll_clks = ARRAY_SIZE(mif_pll_clks);
|
||||
|
|
@ -1221,7 +1221,7 @@ PNAME(mout_sclk_i2scod_p) = {"ioclk_i2s_cdclk", "fin_pll", "dout_aclk_peri_aud",
|
|||
PNAME(mout_sclk_spdif_p) = {"ioclk_spdif_extclk", "fin_pll",
|
||||
"dout_aclk_peri_aud", "phyclk_hdmi_phy_ref_cko"};
|
||||
|
||||
struct samsung_mux_clock peri_mux_clks[] __initdata = {
|
||||
static struct samsung_mux_clock peri_mux_clks[] __initdata = {
|
||||
MUX(PERI_MOUT_SCLK_PCM, "mout_sclk_pcm", mout_sclk_pcm_p,
|
||||
MUX_SEL_PERI1, 4, 2),
|
||||
MUX(PERI_MOUT_SCLK_I2SCOD, "mout_sclk_i2scod", mout_sclk_i2scod_p,
|
||||
|
|
@ -1230,12 +1230,12 @@ struct samsung_mux_clock peri_mux_clks[] __initdata = {
|
|||
MUX_SEL_PERI1, 20, 2),
|
||||
};
|
||||
|
||||
struct samsung_div_clock peri_div_clks[] __initdata = {
|
||||
static struct samsung_div_clock peri_div_clks[] __initdata = {
|
||||
DIV(PERI_DOUT_PCM, "dout_pcm", "mout_sclk_pcm", DIV_PERI, 0, 8),
|
||||
DIV(PERI_DOUT_I2S, "dout_i2s", "mout_sclk_i2scod", DIV_PERI, 8, 6),
|
||||
};
|
||||
|
||||
struct samsung_gate_clock peri_gate_clks[] __initdata = {
|
||||
static struct samsung_gate_clock peri_gate_clks[] __initdata = {
|
||||
GATE(PERI_SCLK_PCM1, "sclk_pcm1", "dout_pcm", EN_SCLK_PERI, 0,
|
||||
CLK_SET_RATE_PARENT, 0),
|
||||
GATE(PERI_SCLK_I2S, "sclk_i2s", "dout_i2s", EN_SCLK_PERI, 1,
|
||||
|
|
@ -1370,7 +1370,7 @@ struct samsung_gate_clock peri_gate_clks[] __initdata = {
|
|||
|
||||
static void __init exynos5260_clk_peri_init(struct device_node *np)
|
||||
{
|
||||
struct samsung_cmu_info cmu = {0};
|
||||
struct samsung_cmu_info cmu = { NULL };
|
||||
|
||||
cmu.mux_clks = peri_mux_clks;
|
||||
cmu.nr_mux_clks = ARRAY_SIZE(peri_mux_clks);
|
||||
|
|
@ -1432,7 +1432,7 @@ static unsigned long top_clk_regs[] __initdata = {
|
|||
};
|
||||
|
||||
/* fixed rate clocks generated inside the soc */
|
||||
struct samsung_fixed_rate_clock fixed_rate_clks[] __initdata = {
|
||||
static struct samsung_fixed_rate_clock fixed_rate_clks[] __initdata = {
|
||||
FRATE(PHYCLK_DPTX_PHY_CH3_TXD_CLK, "phyclk_dptx_phy_ch3_txd_clk", NULL,
|
||||
CLK_IS_ROOT, 270000000),
|
||||
FRATE(PHYCLK_DPTX_PHY_CH2_TXD_CLK, "phyclk_dptx_phy_ch2_txd_clk", NULL,
|
||||
|
|
@ -1519,7 +1519,7 @@ PNAME(mout_sclk_fsys_mmc1_sdclkin_b_p) = {"mout_sclk_fsys_mmc1_sdclkin_a",
|
|||
PNAME(mout_sclk_fsys_mmc2_sdclkin_b_p) = {"mout_sclk_fsys_mmc2_sdclkin_a",
|
||||
"mout_mediatop_pll_user"};
|
||||
|
||||
struct samsung_mux_clock top_mux_clks[] __initdata = {
|
||||
static struct samsung_mux_clock top_mux_clks[] __initdata = {
|
||||
MUX(TOP_MOUT_MEDIATOP_PLL_USER, "mout_mediatop_pll_user",
|
||||
mout_mediatop_pll_user_p,
|
||||
MUX_SEL_TOP_PLL0, 0, 1),
|
||||
|
|
@ -1679,7 +1679,7 @@ struct samsung_mux_clock top_mux_clks[] __initdata = {
|
|||
MUX_SEL_TOP_GSCL, 20, 1),
|
||||
};
|
||||
|
||||
struct samsung_div_clock top_div_clks[] __initdata = {
|
||||
static struct samsung_div_clock top_div_clks[] __initdata = {
|
||||
DIV(TOP_DOUT_ACLK_G2D_333, "dout_aclk_g2d_333", "mout_aclk_g2d_333",
|
||||
DIV_TOP_G2D_MFC, 0, 3),
|
||||
DIV(TOP_DOUT_ACLK_MFC_333, "dout_aclk_mfc_333", "mout_aclk_mfc_333",
|
||||
|
|
@ -1800,7 +1800,7 @@ struct samsung_div_clock top_div_clks[] __initdata = {
|
|||
|
||||
};
|
||||
|
||||
struct samsung_gate_clock top_gate_clks[] __initdata = {
|
||||
static struct samsung_gate_clock top_gate_clks[] __initdata = {
|
||||
GATE(TOP_SCLK_MMC0, "sclk_fsys_mmc0_sdclkin",
|
||||
"dout_sclk_fsys_mmc0_sdclkin_b",
|
||||
EN_SCLK_TOP, 7, CLK_SET_RATE_PARENT, 0),
|
||||
|
|
@ -1826,7 +1826,7 @@ static struct samsung_pll_clock top_pll_clks[] __initdata = {
|
|||
|
||||
static void __init exynos5260_clk_top_init(struct device_node *np)
|
||||
{
|
||||
struct samsung_cmu_info cmu = {0};
|
||||
struct samsung_cmu_info cmu = { NULL };
|
||||
|
||||
cmu.pll_clks = top_pll_clks;
|
||||
cmu.nr_pll_clks = ARRAY_SIZE(top_pll_clks);
|
||||
|
|
|
|||
|
|
@ -504,7 +504,7 @@ static struct samsung_fixed_factor_clock
|
|||
FFACTOR(0, "ff_dout_spll2", "mout_sclk_spll", 1, 2, 0),
|
||||
};
|
||||
|
||||
struct samsung_mux_clock exynos5800_mux_clks[] __initdata = {
|
||||
static struct samsung_mux_clock exynos5800_mux_clks[] __initdata = {
|
||||
MUX(0, "mout_aclk400_isp", mout_group3_5800_p, SRC_TOP0, 0, 3),
|
||||
MUX(0, "mout_aclk400_mscl", mout_group3_5800_p, SRC_TOP0, 4, 3),
|
||||
MUX(0, "mout_aclk400_wcore", mout_group2_5800_p, SRC_TOP0, 16, 3),
|
||||
|
|
@ -553,7 +553,7 @@ struct samsung_mux_clock exynos5800_mux_clks[] __initdata = {
|
|||
MUX(0, "mout_fimd1", mout_group2_p, SRC_DISP10, 4, 3),
|
||||
};
|
||||
|
||||
struct samsung_div_clock exynos5800_div_clks[] __initdata = {
|
||||
static struct samsung_div_clock exynos5800_div_clks[] __initdata = {
|
||||
DIV(0, "dout_aclk400_wcore", "mout_aclk400_wcore", DIV_TOP0, 16, 3),
|
||||
|
||||
DIV(0, "dout_aclk550_cam", "mout_aclk550_cam",
|
||||
|
|
@ -569,14 +569,14 @@ struct samsung_div_clock exynos5800_div_clks[] __initdata = {
|
|||
DIV(0, "dout_sclk_sw", "sclk_spll", DIV_TOP9, 24, 6),
|
||||
};
|
||||
|
||||
struct samsung_gate_clock exynos5800_gate_clks[] __initdata = {
|
||||
static struct samsung_gate_clock exynos5800_gate_clks[] __initdata = {
|
||||
GATE(CLK_ACLK550_CAM, "aclk550_cam", "mout_user_aclk550_cam",
|
||||
GATE_BUS_TOP, 24, 0, 0),
|
||||
GATE(CLK_ACLK432_SCALER, "aclk432_scaler", "mout_user_aclk432_scaler",
|
||||
GATE_BUS_TOP, 27, 0, 0),
|
||||
};
|
||||
|
||||
struct samsung_mux_clock exynos5420_mux_clks[] __initdata = {
|
||||
static struct samsung_mux_clock exynos5420_mux_clks[] __initdata = {
|
||||
MUX(0, "sclk_bpll", mout_bpll_p, TOP_SPARE2, 0, 1),
|
||||
MUX(0, "mout_aclk400_wcore_bpll", mout_aclk400_wcore_bpll_p,
|
||||
TOP_SPARE2, 4, 1),
|
||||
|
|
@ -606,7 +606,7 @@ struct samsung_mux_clock exynos5420_mux_clks[] __initdata = {
|
|||
MUX(0, "mout_fimd1", mout_group3_p, SRC_DISP10, 4, 1),
|
||||
};
|
||||
|
||||
struct samsung_div_clock exynos5420_div_clks[] __initdata = {
|
||||
static struct samsung_div_clock exynos5420_div_clks[] __initdata = {
|
||||
DIV(0, "dout_aclk400_wcore", "mout_aclk400_wcore_bpll",
|
||||
DIV_TOP0, 16, 3),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -835,6 +835,7 @@ static unsigned long cpif_clk_regs[] __initdata = {
|
|||
MPHY_PLL_CON1,
|
||||
MPHY_PLL_FREQ_DET,
|
||||
MUX_SEL_CPIF0,
|
||||
DIV_CPIF,
|
||||
ENABLE_SCLK_CPIF,
|
||||
};
|
||||
|
||||
|
|
@ -1389,7 +1390,7 @@ static struct samsung_gate_clock mif_gate_clks[] __initdata = {
|
|||
|
||||
/* ENABLE_ACLK_MIF2 */
|
||||
GATE(CLK_ACLK_MIFND_266, "aclk_mifnd_266", "div_aclk_mif_266",
|
||||
ENABLE_ACLK_MIF2, 20, 0, 0),
|
||||
ENABLE_ACLK_MIF2, 20, CLK_IGNORE_UNUSED, 0),
|
||||
GATE(CLK_ACLK_PPMU_DREX1S3, "aclk_ppmu_drex1s3", "div_aclk_drex1",
|
||||
ENABLE_ACLK_MIF2, 17, CLK_IGNORE_UNUSED, 0),
|
||||
GATE(CLK_ACLK_PPMU_DREX1S1, "aclk_ppmu_drex1s1", "div_aclk_drex1",
|
||||
|
|
@ -1832,39 +1833,39 @@ static struct samsung_gate_clock peris_gate_clks[] __initdata = {
|
|||
|
||||
/* ENABLE_PCLK_PERIS_SECURE_TZPC */
|
||||
GATE(CLK_PCLK_TZPC12, "pclk_tzpc12", "aclk_peris_66",
|
||||
ENABLE_PCLK_PERIS_SECURE_TZPC, 12, 0, 0),
|
||||
ENABLE_PCLK_PERIS_SECURE_TZPC, 12, CLK_IGNORE_UNUSED, 0),
|
||||
GATE(CLK_PCLK_TZPC11, "pclk_tzpc11", "aclk_peris_66",
|
||||
ENABLE_PCLK_PERIS_SECURE_TZPC, 11, 0, 0),
|
||||
ENABLE_PCLK_PERIS_SECURE_TZPC, 11, CLK_IGNORE_UNUSED, 0),
|
||||
GATE(CLK_PCLK_TZPC10, "pclk_tzpc10", "aclk_peris_66",
|
||||
ENABLE_PCLK_PERIS_SECURE_TZPC, 10, 0, 0),
|
||||
ENABLE_PCLK_PERIS_SECURE_TZPC, 10, CLK_IGNORE_UNUSED, 0),
|
||||
GATE(CLK_PCLK_TZPC9, "pclk_tzpc9", "aclk_peris_66",
|
||||
ENABLE_PCLK_PERIS_SECURE_TZPC, 9, 0, 0),
|
||||
ENABLE_PCLK_PERIS_SECURE_TZPC, 9, CLK_IGNORE_UNUSED, 0),
|
||||
GATE(CLK_PCLK_TZPC8, "pclk_tzpc8", "aclk_peris_66",
|
||||
ENABLE_PCLK_PERIS_SECURE_TZPC, 8, 0, 0),
|
||||
ENABLE_PCLK_PERIS_SECURE_TZPC, 8, CLK_IGNORE_UNUSED, 0),
|
||||
GATE(CLK_PCLK_TZPC7, "pclk_tzpc7", "aclk_peris_66",
|
||||
ENABLE_PCLK_PERIS_SECURE_TZPC, 7, 0, 0),
|
||||
ENABLE_PCLK_PERIS_SECURE_TZPC, 7, CLK_IGNORE_UNUSED, 0),
|
||||
GATE(CLK_PCLK_TZPC6, "pclk_tzpc6", "aclk_peris_66",
|
||||
ENABLE_PCLK_PERIS_SECURE_TZPC, 6, 0, 0),
|
||||
ENABLE_PCLK_PERIS_SECURE_TZPC, 6, CLK_IGNORE_UNUSED, 0),
|
||||
GATE(CLK_PCLK_TZPC5, "pclk_tzpc5", "aclk_peris_66",
|
||||
ENABLE_PCLK_PERIS_SECURE_TZPC, 5, 0, 0),
|
||||
ENABLE_PCLK_PERIS_SECURE_TZPC, 5, CLK_IGNORE_UNUSED, 0),
|
||||
GATE(CLK_PCLK_TZPC4, "pclk_tzpc4", "aclk_peris_66",
|
||||
ENABLE_PCLK_PERIS_SECURE_TZPC, 4, 0, 0),
|
||||
ENABLE_PCLK_PERIS_SECURE_TZPC, 4, CLK_IGNORE_UNUSED, 0),
|
||||
GATE(CLK_PCLK_TZPC3, "pclk_tzpc3", "aclk_peris_66",
|
||||
ENABLE_PCLK_PERIS_SECURE_TZPC, 3, 0, 0),
|
||||
ENABLE_PCLK_PERIS_SECURE_TZPC, 3, CLK_IGNORE_UNUSED, 0),
|
||||
GATE(CLK_PCLK_TZPC2, "pclk_tzpc2", "aclk_peris_66",
|
||||
ENABLE_PCLK_PERIS_SECURE_TZPC, 2, 0, 0),
|
||||
ENABLE_PCLK_PERIS_SECURE_TZPC, 2, CLK_IGNORE_UNUSED, 0),
|
||||
GATE(CLK_PCLK_TZPC1, "pclk_tzpc1", "aclk_peris_66",
|
||||
ENABLE_PCLK_PERIS_SECURE_TZPC, 1, 0, 0),
|
||||
ENABLE_PCLK_PERIS_SECURE_TZPC, 1, CLK_IGNORE_UNUSED, 0),
|
||||
GATE(CLK_PCLK_TZPC0, "pclk_tzpc0", "aclk_peris_66",
|
||||
ENABLE_PCLK_PERIS_SECURE_TZPC, 0, 0, 0),
|
||||
ENABLE_PCLK_PERIS_SECURE_TZPC, 0, CLK_IGNORE_UNUSED, 0),
|
||||
|
||||
/* ENABLE_PCLK_PERIS_SECURE_SECKEY_APBIF */
|
||||
GATE(CLK_PCLK_SECKEY_APBIF, "pclk_seckey_apbif", "aclk_peris_66",
|
||||
ENABLE_PCLK_PERIS_SECURE_SECKEY_APBIF, 0, 0, 0),
|
||||
ENABLE_PCLK_PERIS_SECURE_SECKEY_APBIF, 0, CLK_IGNORE_UNUSED, 0),
|
||||
|
||||
/* ENABLE_PCLK_PERIS_SECURE_CHIPID_APBIF */
|
||||
GATE(CLK_PCLK_CHIPID_APBIF, "pclk_chipid_apbif", "aclk_peris_66",
|
||||
ENABLE_PCLK_PERIS_SECURE_CHIPID_APBIF, 0, 0, 0),
|
||||
ENABLE_PCLK_PERIS_SECURE_CHIPID_APBIF, 0, CLK_IGNORE_UNUSED, 0),
|
||||
|
||||
/* ENABLE_PCLK_PERIS_SECURE_TOPRTC */
|
||||
GATE(CLK_PCLK_TOPRTC, "pclk_toprtc", "aclk_peris_66",
|
||||
|
|
@ -1895,11 +1896,11 @@ static struct samsung_gate_clock peris_gate_clks[] __initdata = {
|
|||
|
||||
/* ENABLE_SCLK_PERIS_SECURE_SECKEY */
|
||||
GATE(CLK_SCLK_SECKEY, "sclk_seckey", "oscclk_efuse_common",
|
||||
ENABLE_SCLK_PERIS_SECURE_SECKEY, 0, 0, 0),
|
||||
ENABLE_SCLK_PERIS_SECURE_SECKEY, 0, CLK_IGNORE_UNUSED, 0),
|
||||
|
||||
/* ENABLE_SCLK_PERIS_SECURE_CHIPID */
|
||||
GATE(CLK_SCLK_CHIPID, "sclk_chipid", "oscclk_efuse_common",
|
||||
ENABLE_SCLK_PERIS_SECURE_CHIPID, 0, 0, 0),
|
||||
ENABLE_SCLK_PERIS_SECURE_CHIPID, 0, CLK_IGNORE_UNUSED, 0),
|
||||
|
||||
/* ENABLE_SCLK_PERIS_SECURE_TOPRTC */
|
||||
GATE(CLK_SCLK_TOPRTC, "sclk_toprtc", "oscclk_efuse_common",
|
||||
|
|
@ -3286,10 +3287,10 @@ static struct samsung_pll_clock g3d_pll_clks[] __initdata = {
|
|||
|
||||
static struct samsung_mux_clock g3d_mux_clks[] __initdata = {
|
||||
/* MUX_SEL_G3D */
|
||||
MUX(CLK_MOUT_ACLK_G3D_400, "mout_aclk_g3d_400", mout_aclk_g3d_400_p,
|
||||
MUX_SEL_G3D, 8, 1),
|
||||
MUX(CLK_MOUT_G3D_PLL, "mout_g3d_pll", mout_g3d_pll_p,
|
||||
MUX_SEL_G3D, 0, 1),
|
||||
MUX_F(CLK_MOUT_ACLK_G3D_400, "mout_aclk_g3d_400", mout_aclk_g3d_400_p,
|
||||
MUX_SEL_G3D, 8, 1, CLK_SET_RATE_PARENT, 0),
|
||||
MUX_F(CLK_MOUT_G3D_PLL, "mout_g3d_pll", mout_g3d_pll_p,
|
||||
MUX_SEL_G3D, 0, 1, CLK_SET_RATE_PARENT, 0),
|
||||
};
|
||||
|
||||
static struct samsung_div_clock g3d_div_clks[] __initdata = {
|
||||
|
|
@ -3298,8 +3299,8 @@ static struct samsung_div_clock g3d_div_clks[] __initdata = {
|
|||
8, 2),
|
||||
DIV(CLK_DIV_PCLK_G3D, "div_pclk_g3d", "div_aclk_g3d", DIV_G3D,
|
||||
4, 3),
|
||||
DIV(CLK_DIV_ACLK_G3D, "div_aclk_g3d", "mout_aclk_g3d_400", DIV_G3D,
|
||||
0, 3),
|
||||
DIV_F(CLK_DIV_ACLK_G3D, "div_aclk_g3d", "mout_aclk_g3d_400", DIV_G3D,
|
||||
0, 3, CLK_SET_RATE_PARENT, 0),
|
||||
};
|
||||
|
||||
static struct samsung_gate_clock g3d_gate_clks[] __initdata = {
|
||||
|
|
@ -3309,9 +3310,9 @@ static struct samsung_gate_clock g3d_gate_clks[] __initdata = {
|
|||
GATE(CLK_ACLK_BTS_G3D0, "aclk_bts_g3d0", "div_aclk_g3d",
|
||||
ENABLE_ACLK_G3D, 6, 0, 0),
|
||||
GATE(CLK_ACLK_ASYNCAPBS_G3D, "aclk_asyncapbs_g3d", "div_pclk_g3d",
|
||||
ENABLE_ACLK_G3D, 5, 0, 0),
|
||||
ENABLE_ACLK_G3D, 5, CLK_IGNORE_UNUSED, 0),
|
||||
GATE(CLK_ACLK_ASYNCAPBM_G3D, "aclk_asyncapbm_g3d", "div_aclk_g3d",
|
||||
ENABLE_ACLK_G3D, 4, 0, 0),
|
||||
ENABLE_ACLK_G3D, 4, CLK_IGNORE_UNUSED, 0),
|
||||
GATE(CLK_ACLK_AHB2APB_G3DP, "aclk_ahb2apb_g3dp", "div_pclk_g3d",
|
||||
ENABLE_ACLK_G3D, 3, CLK_IGNORE_UNUSED, 0),
|
||||
GATE(CLK_ACLK_G3DNP_150, "aclk_g3dnp_150", "div_pclk_g3d",
|
||||
|
|
@ -3319,7 +3320,7 @@ static struct samsung_gate_clock g3d_gate_clks[] __initdata = {
|
|||
GATE(CLK_ACLK_G3DND_600, "aclk_g3dnd_600", "div_aclk_g3d",
|
||||
ENABLE_ACLK_G3D, 1, CLK_IGNORE_UNUSED, 0),
|
||||
GATE(CLK_ACLK_G3D, "aclk_g3d", "div_aclk_g3d",
|
||||
ENABLE_ACLK_G3D, 0, 0, 0),
|
||||
ENABLE_ACLK_G3D, 0, CLK_SET_RATE_PARENT, 0),
|
||||
|
||||
/* ENABLE_PCLK_G3D */
|
||||
GATE(CLK_PCLK_BTS_G3D1, "pclk_bts_g3d1", "div_pclk_g3d",
|
||||
|
|
@ -3582,7 +3583,7 @@ static struct samsung_pll_clock apollo_pll_clks[] __initdata = {
|
|||
static struct samsung_mux_clock apollo_mux_clks[] __initdata = {
|
||||
/* MUX_SEL_APOLLO0 */
|
||||
MUX_F(CLK_MOUT_APOLLO_PLL, "mout_apollo_pll", mout_apollo_pll_p,
|
||||
MUX_SEL_APOLLO0, 0, 1, 0, CLK_MUX_READ_ONLY),
|
||||
MUX_SEL_APOLLO0, 0, 1, CLK_SET_RATE_PARENT, 0),
|
||||
|
||||
/* MUX_SEL_APOLLO1 */
|
||||
MUX(CLK_MOUT_BUS_PLL_APOLLO_USER, "mout_bus_pll_apollo_user",
|
||||
|
|
@ -3590,7 +3591,7 @@ static struct samsung_mux_clock apollo_mux_clks[] __initdata = {
|
|||
|
||||
/* MUX_SEL_APOLLO2 */
|
||||
MUX_F(CLK_MOUT_APOLLO, "mout_apollo", mout_apollo_p, MUX_SEL_APOLLO2,
|
||||
0, 1, 0, CLK_MUX_READ_ONLY),
|
||||
0, 1, CLK_SET_RATE_PARENT, 0),
|
||||
};
|
||||
|
||||
static struct samsung_div_clock apollo_div_clks[] __initdata = {
|
||||
|
|
@ -3611,11 +3612,9 @@ static struct samsung_div_clock apollo_div_clks[] __initdata = {
|
|||
DIV_APOLLO0, 8, 3, CLK_GET_RATE_NOCACHE,
|
||||
CLK_DIVIDER_READ_ONLY),
|
||||
DIV_F(CLK_DIV_APOLLO2, "div_apollo2", "div_apollo1",
|
||||
DIV_APOLLO0, 4, 3, CLK_GET_RATE_NOCACHE,
|
||||
CLK_DIVIDER_READ_ONLY),
|
||||
DIV_APOLLO0, 4, 3, CLK_SET_RATE_PARENT, 0),
|
||||
DIV_F(CLK_DIV_APOLLO1, "div_apollo1", "mout_apollo",
|
||||
DIV_APOLLO0, 0, 3, CLK_GET_RATE_NOCACHE,
|
||||
CLK_DIVIDER_READ_ONLY),
|
||||
DIV_APOLLO0, 0, 3, CLK_SET_RATE_PARENT, 0),
|
||||
|
||||
/* DIV_APOLLO1 */
|
||||
DIV_F(CLK_DIV_SCLK_HPM_APOLLO, "div_sclk_hpm_apollo", "mout_apollo",
|
||||
|
|
@ -3666,7 +3665,8 @@ static struct samsung_gate_clock apollo_gate_clks[] __initdata = {
|
|||
GATE(CLK_SCLK_HPM_APOLLO, "sclk_hpm_apollo", "div_sclk_hpm_apollo",
|
||||
ENABLE_SCLK_APOLLO, 1, CLK_IGNORE_UNUSED, 0),
|
||||
GATE(CLK_SCLK_APOLLO, "sclk_apollo", "div_apollo2",
|
||||
ENABLE_SCLK_APOLLO, 0, CLK_IGNORE_UNUSED, 0),
|
||||
ENABLE_SCLK_APOLLO, 0,
|
||||
CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0),
|
||||
};
|
||||
|
||||
static struct samsung_cmu_info apollo_cmu_info __initdata = {
|
||||
|
|
@ -3775,7 +3775,7 @@ static struct samsung_pll_clock atlas_pll_clks[] __initdata = {
|
|||
static struct samsung_mux_clock atlas_mux_clks[] __initdata = {
|
||||
/* MUX_SEL_ATLAS0 */
|
||||
MUX_F(CLK_MOUT_ATLAS_PLL, "mout_atlas_pll", mout_atlas_pll_p,
|
||||
MUX_SEL_ATLAS0, 0, 1, 0, CLK_MUX_READ_ONLY),
|
||||
MUX_SEL_ATLAS0, 0, 1, CLK_SET_RATE_PARENT, 0),
|
||||
|
||||
/* MUX_SEL_ATLAS1 */
|
||||
MUX(CLK_MOUT_BUS_PLL_ATLAS_USER, "mout_bus_pll_atlas_user",
|
||||
|
|
@ -3783,7 +3783,7 @@ static struct samsung_mux_clock atlas_mux_clks[] __initdata = {
|
|||
|
||||
/* MUX_SEL_ATLAS2 */
|
||||
MUX_F(CLK_MOUT_ATLAS, "mout_atlas", mout_atlas_p, MUX_SEL_ATLAS2,
|
||||
0, 1, 0, CLK_MUX_READ_ONLY),
|
||||
0, 1, CLK_SET_RATE_PARENT, 0),
|
||||
};
|
||||
|
||||
static struct samsung_div_clock atlas_div_clks[] __initdata = {
|
||||
|
|
@ -3804,11 +3804,9 @@ static struct samsung_div_clock atlas_div_clks[] __initdata = {
|
|||
DIV_ATLAS0, 8, 3, CLK_GET_RATE_NOCACHE,
|
||||
CLK_DIVIDER_READ_ONLY),
|
||||
DIV_F(CLK_DIV_ATLAS2, "div_atlas2", "div_atlas1",
|
||||
DIV_ATLAS0, 4, 3, CLK_GET_RATE_NOCACHE,
|
||||
CLK_DIVIDER_READ_ONLY),
|
||||
DIV_ATLAS0, 4, 3, CLK_SET_RATE_PARENT, 0),
|
||||
DIV_F(CLK_DIV_ATLAS1, "div_atlas1", "mout_atlas",
|
||||
DIV_ATLAS0, 0, 3, CLK_GET_RATE_NOCACHE,
|
||||
CLK_DIVIDER_READ_ONLY),
|
||||
DIV_ATLAS0, 0, 3, CLK_SET_RATE_PARENT, 0),
|
||||
|
||||
/* DIV_ATLAS1 */
|
||||
DIV_F(CLK_DIV_SCLK_HPM_ATLAS, "div_sclk_hpm_atlas", "mout_atlas",
|
||||
|
|
@ -3885,7 +3883,8 @@ static struct samsung_gate_clock atlas_gate_clks[] __initdata = {
|
|||
GATE(CLK_ATCLK, "atclk", "div_atclk_atlas",
|
||||
ENABLE_SCLK_ATLAS, 1, CLK_IGNORE_UNUSED, 0),
|
||||
GATE(CLK_SCLK_ATLAS, "sclk_atlas", "div_atlas2",
|
||||
ENABLE_SCLK_ATLAS, 0, CLK_IGNORE_UNUSED, 0),
|
||||
ENABLE_SCLK_ATLAS, 0,
|
||||
CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0),
|
||||
};
|
||||
|
||||
static struct samsung_cmu_info atlas_cmu_info __initdata = {
|
||||
|
|
|
|||
|
|
@ -1156,7 +1156,7 @@ static const struct clk_ops samsung_pll2650xx_clk_min_ops = {
|
|||
};
|
||||
|
||||
static void __init _samsung_clk_register_pll(struct samsung_clk_provider *ctx,
|
||||
struct samsung_pll_clock *pll_clk,
|
||||
const struct samsung_pll_clock *pll_clk,
|
||||
void __iomem *base)
|
||||
{
|
||||
struct samsung_clk_pll *pll;
|
||||
|
|
@ -1303,7 +1303,7 @@ static void __init _samsung_clk_register_pll(struct samsung_clk_provider *ctx,
|
|||
}
|
||||
|
||||
void __init samsung_clk_register_pll(struct samsung_clk_provider *ctx,
|
||||
struct samsung_pll_clock *pll_list,
|
||||
const struct samsung_pll_clock *pll_list,
|
||||
unsigned int nr_pll, void __iomem *base)
|
||||
{
|
||||
int cnt;
|
||||
|
|
|
|||
|
|
@ -81,13 +81,13 @@ static int s3c24xx_clkout_set_parent(struct clk_hw *hw, u8 index)
|
|||
return ret;
|
||||
}
|
||||
|
||||
const struct clk_ops s3c24xx_clkout_ops = {
|
||||
static const struct clk_ops s3c24xx_clkout_ops = {
|
||||
.get_parent = s3c24xx_clkout_get_parent,
|
||||
.set_parent = s3c24xx_clkout_set_parent,
|
||||
.determine_rate = __clk_mux_determine_rate,
|
||||
};
|
||||
|
||||
struct clk *s3c24xx_register_clkout(struct device *dev, const char *name,
|
||||
static struct clk *s3c24xx_register_clkout(struct device *dev, const char *name,
|
||||
const char **parent_names, u8 num_parents,
|
||||
u8 shift, u32 mask)
|
||||
{
|
||||
|
|
@ -404,7 +404,7 @@ static struct s3c24xx_dclk_drv_data dclk_variants[] = {
|
|||
},
|
||||
};
|
||||
|
||||
static struct platform_device_id s3c24xx_dclk_driver_ids[] = {
|
||||
static const struct platform_device_id s3c24xx_dclk_driver_ids[] = {
|
||||
{
|
||||
.name = "s3c2410-dclk",
|
||||
.driver_data = (kernel_ulong_t)&dclk_variants[S3C2410],
|
||||
|
|
|
|||
|
|
@ -169,44 +169,44 @@ static inline void s5pv210_clk_sleep_init(void) { }
|
|||
#endif
|
||||
|
||||
/* Mux parent lists. */
|
||||
static const char *fin_pll_p[] __initdata = {
|
||||
static const char *const fin_pll_p[] __initconst = {
|
||||
"xxti",
|
||||
"xusbxti"
|
||||
};
|
||||
|
||||
static const char *mout_apll_p[] __initdata = {
|
||||
static const char *const mout_apll_p[] __initconst = {
|
||||
"fin_pll",
|
||||
"fout_apll"
|
||||
};
|
||||
|
||||
static const char *mout_mpll_p[] __initdata = {
|
||||
static const char *const mout_mpll_p[] __initconst = {
|
||||
"fin_pll",
|
||||
"fout_mpll"
|
||||
};
|
||||
|
||||
static const char *mout_epll_p[] __initdata = {
|
||||
static const char *const mout_epll_p[] __initconst = {
|
||||
"fin_pll",
|
||||
"fout_epll"
|
||||
};
|
||||
|
||||
static const char *mout_vpllsrc_p[] __initdata = {
|
||||
static const char *const mout_vpllsrc_p[] __initconst = {
|
||||
"fin_pll",
|
||||
"sclk_hdmi27m"
|
||||
};
|
||||
|
||||
static const char *mout_vpll_p[] __initdata = {
|
||||
static const char *const mout_vpll_p[] __initconst = {
|
||||
"mout_vpllsrc",
|
||||
"fout_vpll"
|
||||
};
|
||||
|
||||
static const char *mout_group1_p[] __initdata = {
|
||||
static const char *const mout_group1_p[] __initconst = {
|
||||
"dout_a2m",
|
||||
"mout_mpll",
|
||||
"mout_epll",
|
||||
"mout_vpll"
|
||||
};
|
||||
|
||||
static const char *mout_group2_p[] __initdata = {
|
||||
static const char *const mout_group2_p[] __initconst = {
|
||||
"xxti",
|
||||
"xusbxti",
|
||||
"sclk_hdmi27m",
|
||||
|
|
@ -218,7 +218,7 @@ static const char *mout_group2_p[] __initdata = {
|
|||
"mout_vpll",
|
||||
};
|
||||
|
||||
static const char *mout_audio0_p[] __initdata = {
|
||||
static const char *const mout_audio0_p[] __initconst = {
|
||||
"xxti",
|
||||
"pcmcdclk0",
|
||||
"sclk_hdmi27m",
|
||||
|
|
@ -230,7 +230,7 @@ static const char *mout_audio0_p[] __initdata = {
|
|||
"mout_vpll",
|
||||
};
|
||||
|
||||
static const char *mout_audio1_p[] __initdata = {
|
||||
static const char *const mout_audio1_p[] __initconst = {
|
||||
"i2scdclk1",
|
||||
"pcmcdclk1",
|
||||
"sclk_hdmi27m",
|
||||
|
|
@ -242,7 +242,7 @@ static const char *mout_audio1_p[] __initdata = {
|
|||
"mout_vpll",
|
||||
};
|
||||
|
||||
static const char *mout_audio2_p[] __initdata = {
|
||||
static const char *const mout_audio2_p[] __initconst = {
|
||||
"i2scdclk2",
|
||||
"pcmcdclk2",
|
||||
"sclk_hdmi27m",
|
||||
|
|
@ -254,63 +254,63 @@ static const char *mout_audio2_p[] __initdata = {
|
|||
"mout_vpll",
|
||||
};
|
||||
|
||||
static const char *mout_spdif_p[] __initdata = {
|
||||
static const char *const mout_spdif_p[] __initconst = {
|
||||
"dout_audio0",
|
||||
"dout_audio1",
|
||||
"dout_audio3",
|
||||
};
|
||||
|
||||
static const char *mout_group3_p[] __initdata = {
|
||||
static const char *const mout_group3_p[] __initconst = {
|
||||
"mout_apll",
|
||||
"mout_mpll"
|
||||
};
|
||||
|
||||
static const char *mout_group4_p[] __initdata = {
|
||||
static const char *const mout_group4_p[] __initconst = {
|
||||
"mout_mpll",
|
||||
"dout_a2m"
|
||||
};
|
||||
|
||||
static const char *mout_flash_p[] __initdata = {
|
||||
static const char *const mout_flash_p[] __initconst = {
|
||||
"dout_hclkd",
|
||||
"dout_hclkp"
|
||||
};
|
||||
|
||||
static const char *mout_dac_p[] __initdata = {
|
||||
static const char *const mout_dac_p[] __initconst = {
|
||||
"mout_vpll",
|
||||
"sclk_hdmiphy"
|
||||
};
|
||||
|
||||
static const char *mout_hdmi_p[] __initdata = {
|
||||
static const char *const mout_hdmi_p[] __initconst = {
|
||||
"sclk_hdmiphy",
|
||||
"dout_tblk"
|
||||
};
|
||||
|
||||
static const char *mout_mixer_p[] __initdata = {
|
||||
static const char *const mout_mixer_p[] __initconst = {
|
||||
"mout_dac",
|
||||
"mout_hdmi"
|
||||
};
|
||||
|
||||
static const char *mout_vpll_6442_p[] __initdata = {
|
||||
static const char *const mout_vpll_6442_p[] __initconst = {
|
||||
"fin_pll",
|
||||
"fout_vpll"
|
||||
};
|
||||
|
||||
static const char *mout_mixer_6442_p[] __initdata = {
|
||||
static const char *const mout_mixer_6442_p[] __initconst = {
|
||||
"mout_vpll",
|
||||
"dout_mixer"
|
||||
};
|
||||
|
||||
static const char *mout_d0sync_6442_p[] __initdata = {
|
||||
static const char *const mout_d0sync_6442_p[] __initconst = {
|
||||
"mout_dsys",
|
||||
"div_apll"
|
||||
};
|
||||
|
||||
static const char *mout_d1sync_6442_p[] __initdata = {
|
||||
static const char *const mout_d1sync_6442_p[] __initconst = {
|
||||
"mout_psys",
|
||||
"div_apll"
|
||||
};
|
||||
|
||||
static const char *mout_group2_6442_p[] __initdata = {
|
||||
static const char *const mout_group2_6442_p[] __initconst = {
|
||||
"fin_pll",
|
||||
"none",
|
||||
"none",
|
||||
|
|
@ -322,7 +322,7 @@ static const char *mout_group2_6442_p[] __initdata = {
|
|||
"mout_vpll",
|
||||
};
|
||||
|
||||
static const char *mout_audio0_6442_p[] __initdata = {
|
||||
static const char *const mout_audio0_6442_p[] __initconst = {
|
||||
"fin_pll",
|
||||
"pcmcdclk0",
|
||||
"none",
|
||||
|
|
@ -334,7 +334,7 @@ static const char *mout_audio0_6442_p[] __initdata = {
|
|||
"mout_vpll",
|
||||
};
|
||||
|
||||
static const char *mout_audio1_6442_p[] __initdata = {
|
||||
static const char *const mout_audio1_6442_p[] __initconst = {
|
||||
"i2scdclk1",
|
||||
"pcmcdclk1",
|
||||
"none",
|
||||
|
|
@ -347,7 +347,7 @@ static const char *mout_audio1_6442_p[] __initdata = {
|
|||
"fin_pll",
|
||||
};
|
||||
|
||||
static const char *mout_clksel_p[] __initdata = {
|
||||
static const char *const mout_clksel_p[] __initconst = {
|
||||
"fout_apll_clkout",
|
||||
"fout_mpll_clkout",
|
||||
"fout_epll",
|
||||
|
|
@ -370,7 +370,7 @@ static const char *mout_clksel_p[] __initdata = {
|
|||
"div_dclk"
|
||||
};
|
||||
|
||||
static const char *mout_clksel_6442_p[] __initdata = {
|
||||
static const char *const mout_clksel_6442_p[] __initconst = {
|
||||
"fout_apll_clkout",
|
||||
"fout_mpll_clkout",
|
||||
"fout_epll",
|
||||
|
|
@ -393,7 +393,7 @@ static const char *mout_clksel_6442_p[] __initdata = {
|
|||
"div_dclk"
|
||||
};
|
||||
|
||||
static const char *mout_clkout_p[] __initdata = {
|
||||
static const char *const mout_clkout_p[] __initconst = {
|
||||
"dout_clkout",
|
||||
"none",
|
||||
"xxti",
|
||||
|
|
@ -401,20 +401,20 @@ static const char *mout_clkout_p[] __initdata = {
|
|||
};
|
||||
|
||||
/* Common fixed factor clocks. */
|
||||
static struct samsung_fixed_factor_clock ffactor_clks[] __initdata = {
|
||||
static const struct samsung_fixed_factor_clock ffactor_clks[] __initconst = {
|
||||
FFACTOR(FOUT_APLL_CLKOUT, "fout_apll_clkout", "fout_apll", 1, 4, 0),
|
||||
FFACTOR(FOUT_MPLL_CLKOUT, "fout_mpll_clkout", "fout_mpll", 1, 2, 0),
|
||||
FFACTOR(DOUT_APLL_CLKOUT, "dout_apll_clkout", "dout_apll", 1, 4, 0),
|
||||
};
|
||||
|
||||
/* PLL input mux (fin_pll), which needs to be registered before PLLs. */
|
||||
static struct samsung_mux_clock early_mux_clks[] __initdata = {
|
||||
static const struct samsung_mux_clock early_mux_clks[] __initconst = {
|
||||
MUX_F(FIN_PLL, "fin_pll", fin_pll_p, OM_STAT, 0, 1,
|
||||
CLK_MUX_READ_ONLY, 0),
|
||||
};
|
||||
|
||||
/* Common clock muxes. */
|
||||
static struct samsung_mux_clock mux_clks[] __initdata = {
|
||||
static const struct samsung_mux_clock mux_clks[] __initconst = {
|
||||
MUX(MOUT_FLASH, "mout_flash", mout_flash_p, CLK_SRC0, 28, 1),
|
||||
MUX(MOUT_PSYS, "mout_psys", mout_group4_p, CLK_SRC0, 24, 1),
|
||||
MUX(MOUT_DSYS, "mout_dsys", mout_group4_p, CLK_SRC0, 20, 1),
|
||||
|
|
@ -427,7 +427,7 @@ static struct samsung_mux_clock mux_clks[] __initdata = {
|
|||
};
|
||||
|
||||
/* S5PV210-specific clock muxes. */
|
||||
static struct samsung_mux_clock s5pv210_mux_clks[] __initdata = {
|
||||
static const struct samsung_mux_clock s5pv210_mux_clks[] __initconst = {
|
||||
MUX(MOUT_VPLL, "mout_vpll", mout_vpll_p, CLK_SRC0, 12, 1),
|
||||
|
||||
MUX(MOUT_VPLLSRC, "mout_vpllsrc", mout_vpllsrc_p, CLK_SRC1, 28, 1),
|
||||
|
|
@ -472,7 +472,7 @@ static struct samsung_mux_clock s5pv210_mux_clks[] __initdata = {
|
|||
};
|
||||
|
||||
/* S5P6442-specific clock muxes. */
|
||||
static struct samsung_mux_clock s5p6442_mux_clks[] __initdata = {
|
||||
static const struct samsung_mux_clock s5p6442_mux_clks[] __initconst = {
|
||||
MUX(MOUT_VPLL, "mout_vpll", mout_vpll_6442_p, CLK_SRC0, 12, 1),
|
||||
|
||||
MUX(MOUT_FIMD, "mout_fimd", mout_group2_6442_p, CLK_SRC1, 20, 4),
|
||||
|
|
@ -504,7 +504,7 @@ static struct samsung_mux_clock s5p6442_mux_clks[] __initdata = {
|
|||
};
|
||||
|
||||
/* S5PV210-specific fixed rate clocks generated inside the SoC. */
|
||||
static struct samsung_fixed_rate_clock s5pv210_frate_clks[] __initdata = {
|
||||
static const struct samsung_fixed_rate_clock s5pv210_frate_clks[] __initconst = {
|
||||
FRATE(SCLK_HDMI27M, "sclk_hdmi27m", NULL, CLK_IS_ROOT, 27000000),
|
||||
FRATE(SCLK_HDMIPHY, "sclk_hdmiphy", NULL, CLK_IS_ROOT, 27000000),
|
||||
FRATE(SCLK_USBPHY0, "sclk_usbphy0", NULL, CLK_IS_ROOT, 48000000),
|
||||
|
|
@ -512,12 +512,12 @@ static struct samsung_fixed_rate_clock s5pv210_frate_clks[] __initdata = {
|
|||
};
|
||||
|
||||
/* S5P6442-specific fixed rate clocks generated inside the SoC. */
|
||||
static struct samsung_fixed_rate_clock s5p6442_frate_clks[] __initdata = {
|
||||
static const struct samsung_fixed_rate_clock s5p6442_frate_clks[] __initconst = {
|
||||
FRATE(SCLK_USBPHY0, "sclk_usbphy0", NULL, CLK_IS_ROOT, 30000000),
|
||||
};
|
||||
|
||||
/* Common clock dividers. */
|
||||
static struct samsung_div_clock div_clks[] __initdata = {
|
||||
static const struct samsung_div_clock div_clks[] __initconst = {
|
||||
DIV(DOUT_PCLKP, "dout_pclkp", "dout_hclkp", CLK_DIV0, 28, 3),
|
||||
DIV(DOUT_PCLKD, "dout_pclkd", "dout_hclkd", CLK_DIV0, 20, 3),
|
||||
DIV(DOUT_A2M, "dout_a2m", "mout_apll", CLK_DIV0, 4, 3),
|
||||
|
|
@ -549,7 +549,7 @@ static struct samsung_div_clock div_clks[] __initdata = {
|
|||
};
|
||||
|
||||
/* S5PV210-specific clock dividers. */
|
||||
static struct samsung_div_clock s5pv210_div_clks[] __initdata = {
|
||||
static const struct samsung_div_clock s5pv210_div_clks[] __initconst = {
|
||||
DIV(DOUT_HCLKP, "dout_hclkp", "mout_psys", CLK_DIV0, 24, 4),
|
||||
DIV(DOUT_HCLKD, "dout_hclkd", "mout_dsys", CLK_DIV0, 16, 4),
|
||||
DIV(DOUT_PCLKM, "dout_pclkm", "dout_hclkm", CLK_DIV0, 12, 3),
|
||||
|
|
@ -578,7 +578,7 @@ static struct samsung_div_clock s5pv210_div_clks[] __initdata = {
|
|||
};
|
||||
|
||||
/* S5P6442-specific clock dividers. */
|
||||
static struct samsung_div_clock s5p6442_div_clks[] __initdata = {
|
||||
static const struct samsung_div_clock s5p6442_div_clks[] __initconst = {
|
||||
DIV(DOUT_HCLKP, "dout_hclkp", "mout_d1sync", CLK_DIV0, 24, 4),
|
||||
DIV(DOUT_HCLKD, "dout_hclkd", "mout_d0sync", CLK_DIV0, 16, 4),
|
||||
|
||||
|
|
@ -586,7 +586,7 @@ static struct samsung_div_clock s5p6442_div_clks[] __initdata = {
|
|||
};
|
||||
|
||||
/* Common clock gates. */
|
||||
static struct samsung_gate_clock gate_clks[] __initdata = {
|
||||
static const struct samsung_gate_clock gate_clks[] __initconst = {
|
||||
GATE(CLK_ROTATOR, "rotator", "dout_hclkd", CLK_GATE_IP0, 29, 0, 0),
|
||||
GATE(CLK_FIMC2, "fimc2", "dout_hclkd", CLK_GATE_IP0, 26, 0, 0),
|
||||
GATE(CLK_FIMC1, "fimc1", "dout_hclkd", CLK_GATE_IP0, 25, 0, 0),
|
||||
|
|
@ -666,7 +666,7 @@ static struct samsung_gate_clock gate_clks[] __initdata = {
|
|||
};
|
||||
|
||||
/* S5PV210-specific clock gates. */
|
||||
static struct samsung_gate_clock s5pv210_gate_clks[] __initdata = {
|
||||
static const struct samsung_gate_clock s5pv210_gate_clks[] __initconst = {
|
||||
GATE(CLK_CSIS, "clk_csis", "dout_hclkd", CLK_GATE_IP0, 31, 0, 0),
|
||||
GATE(CLK_MFC, "mfc", "dout_hclkm", CLK_GATE_IP0, 16, 0, 0),
|
||||
GATE(CLK_G2D, "g2d", "dout_hclkd", CLK_GATE_IP0, 12, 0, 0),
|
||||
|
|
@ -728,7 +728,7 @@ static struct samsung_gate_clock s5pv210_gate_clks[] __initdata = {
|
|||
};
|
||||
|
||||
/* S5P6442-specific clock gates. */
|
||||
static struct samsung_gate_clock s5p6442_gate_clks[] __initdata = {
|
||||
static const struct samsung_gate_clock s5p6442_gate_clks[] __initconst = {
|
||||
GATE(CLK_JPEG, "jpeg", "dout_hclkd", CLK_GATE_IP0, 28, 0, 0),
|
||||
GATE(CLK_MFC, "mfc", "dout_hclkd", CLK_GATE_IP0, 16, 0, 0),
|
||||
GATE(CLK_G2D, "g2d", "dout_hclkd", CLK_GATE_IP0, 12, 0, 0),
|
||||
|
|
@ -748,14 +748,14 @@ static struct samsung_gate_clock s5p6442_gate_clks[] __initdata = {
|
|||
* Clock aliases for legacy clkdev look-up.
|
||||
* NOTE: Needed only to support legacy board files.
|
||||
*/
|
||||
static struct samsung_clock_alias s5pv210_aliases[] = {
|
||||
static const struct samsung_clock_alias s5pv210_aliases[] __initconst = {
|
||||
ALIAS(DOUT_APLL, NULL, "armclk"),
|
||||
ALIAS(DOUT_HCLKM, NULL, "hclk_msys"),
|
||||
ALIAS(MOUT_DMC0, NULL, "sclk_dmc0"),
|
||||
};
|
||||
|
||||
/* S5PV210-specific PLLs. */
|
||||
static struct samsung_pll_clock s5pv210_pll_clks[] __initdata = {
|
||||
static const struct samsung_pll_clock s5pv210_pll_clks[] __initconst = {
|
||||
[apll] = PLL(pll_4508, FOUT_APLL, "fout_apll", "fin_pll",
|
||||
APLL_LOCK, APLL_CON0, NULL),
|
||||
[mpll] = PLL(pll_4502, FOUT_MPLL, "fout_mpll", "fin_pll",
|
||||
|
|
@ -767,7 +767,7 @@ static struct samsung_pll_clock s5pv210_pll_clks[] __initdata = {
|
|||
};
|
||||
|
||||
/* S5P6442-specific PLLs. */
|
||||
static struct samsung_pll_clock s5p6442_pll_clks[] __initdata = {
|
||||
static const struct samsung_pll_clock s5p6442_pll_clks[] __initconst = {
|
||||
[apll] = PLL(pll_4502, FOUT_APLL, "fout_apll", "fin_pll",
|
||||
APLL_LOCK, APLL_CON0, NULL),
|
||||
[mpll] = PLL(pll_4502, FOUT_MPLL, "fout_mpll", "fin_pll",
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ void samsung_clk_add_lookup(struct samsung_clk_provider *ctx, struct clk *clk,
|
|||
|
||||
/* register a list of aliases */
|
||||
void __init samsung_clk_register_alias(struct samsung_clk_provider *ctx,
|
||||
struct samsung_clock_alias *list,
|
||||
const struct samsung_clock_alias *list,
|
||||
unsigned int nr_clk)
|
||||
{
|
||||
struct clk *clk;
|
||||
|
|
@ -132,7 +132,8 @@ void __init samsung_clk_register_alias(struct samsung_clk_provider *ctx,
|
|||
|
||||
/* register a list of fixed clocks */
|
||||
void __init samsung_clk_register_fixed_rate(struct samsung_clk_provider *ctx,
|
||||
struct samsung_fixed_rate_clock *list, unsigned int nr_clk)
|
||||
const struct samsung_fixed_rate_clock *list,
|
||||
unsigned int nr_clk)
|
||||
{
|
||||
struct clk *clk;
|
||||
unsigned int idx, ret;
|
||||
|
|
@ -161,7 +162,7 @@ void __init samsung_clk_register_fixed_rate(struct samsung_clk_provider *ctx,
|
|||
|
||||
/* register a list of fixed factor clocks */
|
||||
void __init samsung_clk_register_fixed_factor(struct samsung_clk_provider *ctx,
|
||||
struct samsung_fixed_factor_clock *list, unsigned int nr_clk)
|
||||
const struct samsung_fixed_factor_clock *list, unsigned int nr_clk)
|
||||
{
|
||||
struct clk *clk;
|
||||
unsigned int idx;
|
||||
|
|
@ -181,7 +182,7 @@ void __init samsung_clk_register_fixed_factor(struct samsung_clk_provider *ctx,
|
|||
|
||||
/* register a list of mux clocks */
|
||||
void __init samsung_clk_register_mux(struct samsung_clk_provider *ctx,
|
||||
struct samsung_mux_clock *list,
|
||||
const struct samsung_mux_clock *list,
|
||||
unsigned int nr_clk)
|
||||
{
|
||||
struct clk *clk;
|
||||
|
|
@ -213,7 +214,7 @@ void __init samsung_clk_register_mux(struct samsung_clk_provider *ctx,
|
|||
|
||||
/* register a list of div clocks */
|
||||
void __init samsung_clk_register_div(struct samsung_clk_provider *ctx,
|
||||
struct samsung_div_clock *list,
|
||||
const struct samsung_div_clock *list,
|
||||
unsigned int nr_clk)
|
||||
{
|
||||
struct clk *clk;
|
||||
|
|
@ -252,7 +253,7 @@ void __init samsung_clk_register_div(struct samsung_clk_provider *ctx,
|
|||
|
||||
/* register a list of gate clocks */
|
||||
void __init samsung_clk_register_gate(struct samsung_clk_provider *ctx,
|
||||
struct samsung_gate_clock *list,
|
||||
const struct samsung_gate_clock *list,
|
||||
unsigned int nr_clk)
|
||||
{
|
||||
struct clk *clk;
|
||||
|
|
@ -389,7 +390,7 @@ struct samsung_clk_provider * __init samsung_cmu_register_one(
|
|||
|
||||
ctx = samsung_clk_init(np, reg_base, cmu->nr_clk_ids);
|
||||
if (!ctx) {
|
||||
panic("%s: unable to alllocate ctx\n", __func__);
|
||||
panic("%s: unable to allocate ctx\n", __func__);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue