diff options
Diffstat (limited to 'drivers/clk/bcm')
-rw-r--r-- | drivers/clk/bcm/Kconfig | 6 | ||||
-rw-r--r-- | drivers/clk/bcm/Makefile | 4 | ||||
-rw-r--r-- | drivers/clk/bcm/clk-bcm2835.c | 1575 | ||||
-rw-r--r-- | drivers/clk/bcm/clk-cygnus.c | 155 | ||||
-rw-r--r-- | drivers/clk/bcm/clk-iproc-pll.c | 196 | ||||
-rw-r--r-- | drivers/clk/bcm/clk-iproc.h | 22 | ||||
-rw-r--r-- | drivers/clk/bcm/clk-ns2.c | 288 | ||||
-rw-r--r-- | drivers/clk/bcm/clk-nsp.c | 139 |
8 files changed, 2219 insertions, 166 deletions
diff --git a/drivers/clk/bcm/Kconfig b/drivers/clk/bcm/Kconfig index 88febf53b276..85260fb96b36 100644 --- a/drivers/clk/bcm/Kconfig +++ b/drivers/clk/bcm/Kconfig @@ -1,6 +1,6 @@ config CLK_BCM_KONA bool "Broadcom Kona CCU clock support" - depends on ARCH_BCM_MOBILE + depends on ARCH_BCM_MOBILE || COMPILE_TEST depends on COMMON_CLK default y help @@ -9,10 +9,8 @@ config CLK_BCM_KONA in the BCM281xx and BCM21664 families. config COMMON_CLK_IPROC - bool "Broadcom iProc clock support" - depends on ARCH_BCM_IPROC + bool depends on COMMON_CLK - default ARCH_BCM_IPROC help Enable common clock framework support for Broadcom SoCs based on the iProc architecture diff --git a/drivers/clk/bcm/Makefile b/drivers/clk/bcm/Makefile index 8a7a477862c7..3fc95060d875 100644 --- a/drivers/clk/bcm/Makefile +++ b/drivers/clk/bcm/Makefile @@ -3,4 +3,8 @@ 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_BCM2835) += clk-bcm2835.o +obj-$(CONFIG_COMMON_CLK_IPROC) += clk-ns2.o obj-$(CONFIG_ARCH_BCM_CYGNUS) += clk-cygnus.o +obj-$(CONFIG_ARCH_BCM_NSP) += clk-nsp.o +obj-$(CONFIG_ARCH_BCM_5301X) += clk-nsp.o diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c new file mode 100644 index 000000000000..39bf5820297e --- /dev/null +++ b/drivers/clk/bcm/clk-bcm2835.c @@ -0,0 +1,1575 @@ +/* + * Copyright (C) 2010,2015 Broadcom + * Copyright (C) 2012 Stephen Warren + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** + * DOC: BCM2835 CPRMAN (clock manager for the "audio" domain) + * + * The clock tree on the 2835 has several levels. There's a root + * oscillator running at 19.2Mhz. After the oscillator there are 5 + * PLLs, roughly divided as "camera", "ARM", "core", "DSI displays", + * and "HDMI displays". Those 5 PLLs each can divide their output to + * produce up to 4 channels. Finally, there is the level of clocks to + * be consumed by other hardware components (like "H264" or "HDMI + * state machine"), which divide off of some subset of the PLL + * channels. + * + * All of the clocks in the tree are exposed in the DT, because the DT + * may want to make assignments of the final layer of clocks to the + * PLL channels, and some components of the hardware will actually + * skip layers of the tree (for example, the pixel clock comes + * directly from the PLLH PIX channel without using a CM_*CTL clock + * generator). + */ + +#include <linux/clk-provider.h> +#include <linux/clkdev.h> +#include <linux/clk/bcm2835.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <dt-bindings/clock/bcm2835.h> + +#define CM_PASSWORD 0x5a000000 + +#define CM_GNRICCTL 0x000 +#define CM_GNRICDIV 0x004 +# define CM_DIV_FRAC_BITS 12 + +#define CM_VPUCTL 0x008 +#define CM_VPUDIV 0x00c +#define CM_SYSCTL 0x010 +#define CM_SYSDIV 0x014 +#define CM_PERIACTL 0x018 +#define CM_PERIADIV 0x01c +#define CM_PERIICTL 0x020 +#define CM_PERIIDIV 0x024 +#define CM_H264CTL 0x028 +#define CM_H264DIV 0x02c +#define CM_ISPCTL 0x030 +#define CM_ISPDIV 0x034 +#define CM_V3DCTL 0x038 +#define CM_V3DDIV 0x03c +#define CM_CAM0CTL 0x040 +#define CM_CAM0DIV 0x044 +#define CM_CAM1CTL 0x048 +#define CM_CAM1DIV 0x04c +#define CM_CCP2CTL 0x050 +#define CM_CCP2DIV 0x054 +#define CM_DSI0ECTL 0x058 +#define CM_DSI0EDIV 0x05c +#define CM_DSI0PCTL 0x060 +#define CM_DSI0PDIV 0x064 +#define CM_DPICTL 0x068 +#define CM_DPIDIV 0x06c +#define CM_GP0CTL 0x070 +#define CM_GP0DIV 0x074 +#define CM_GP1CTL 0x078 +#define CM_GP1DIV 0x07c +#define CM_GP2CTL 0x080 +#define CM_GP2DIV 0x084 +#define CM_HSMCTL 0x088 +#define CM_HSMDIV 0x08c +#define CM_OTPCTL 0x090 +#define CM_OTPDIV 0x094 +#define CM_PWMCTL 0x0a0 +#define CM_PWMDIV 0x0a4 +#define CM_SMICTL 0x0b0 +#define CM_SMIDIV 0x0b4 +#define CM_TSENSCTL 0x0e0 +#define CM_TSENSDIV 0x0e4 +#define CM_TIMERCTL 0x0e8 +#define CM_TIMERDIV 0x0ec +#define CM_UARTCTL 0x0f0 +#define CM_UARTDIV 0x0f4 +#define CM_VECCTL 0x0f8 +#define CM_VECDIV 0x0fc +#define CM_PULSECTL 0x190 +#define CM_PULSEDIV 0x194 +#define CM_SDCCTL 0x1a8 +#define CM_SDCDIV 0x1ac +#define CM_ARMCTL 0x1b0 +#define CM_EMMCCTL 0x1c0 +#define CM_EMMCDIV 0x1c4 + +/* General bits for the CM_*CTL regs */ +# define CM_ENABLE BIT(4) +# define CM_KILL BIT(5) +# define CM_GATE_BIT 6 +# define CM_GATE BIT(CM_GATE_BIT) +# define CM_BUSY BIT(7) +# define CM_BUSYD BIT(8) +# define CM_SRC_SHIFT 0 +# define CM_SRC_BITS 4 +# define CM_SRC_MASK 0xf +# define CM_SRC_GND 0 +# define CM_SRC_OSC 1 +# define CM_SRC_TESTDEBUG0 2 +# define CM_SRC_TESTDEBUG1 3 +# define CM_SRC_PLLA_CORE 4 +# define CM_SRC_PLLA_PER 4 +# define CM_SRC_PLLC_CORE0 5 +# define CM_SRC_PLLC_PER 5 +# define CM_SRC_PLLC_CORE1 8 +# define CM_SRC_PLLD_CORE 6 +# define CM_SRC_PLLD_PER 6 +# define CM_SRC_PLLH_AUX 7 +# define CM_SRC_PLLC_CORE1 8 +# define CM_SRC_PLLC_CORE2 9 + +#define CM_OSCCOUNT 0x100 + +#define CM_PLLA 0x104 +# define CM_PLL_ANARST BIT(8) +# define CM_PLLA_HOLDPER BIT(7) +# define CM_PLLA_LOADPER BIT(6) +# define CM_PLLA_HOLDCORE BIT(5) +# define CM_PLLA_LOADCORE BIT(4) +# define CM_PLLA_HOLDCCP2 BIT(3) +# define CM_PLLA_LOADCCP2 BIT(2) +# define CM_PLLA_HOLDDSI0 BIT(1) +# define CM_PLLA_LOADDSI0 BIT(0) + +#define CM_PLLC 0x108 +# define CM_PLLC_HOLDPER BIT(7) +# define CM_PLLC_LOADPER BIT(6) +# define CM_PLLC_HOLDCORE2 BIT(5) +# define CM_PLLC_LOADCORE2 BIT(4) +# define CM_PLLC_HOLDCORE1 BIT(3) +# define CM_PLLC_LOADCORE1 BIT(2) +# define CM_PLLC_HOLDCORE0 BIT(1) +# define CM_PLLC_LOADCORE0 BIT(0) + +#define CM_PLLD 0x10c +# define CM_PLLD_HOLDPER BIT(7) +# define CM_PLLD_LOADPER BIT(6) +# define CM_PLLD_HOLDCORE BIT(5) +# define CM_PLLD_LOADCORE BIT(4) +# define CM_PLLD_HOLDDSI1 BIT(3) +# define CM_PLLD_LOADDSI1 BIT(2) +# define CM_PLLD_HOLDDSI0 BIT(1) +# define CM_PLLD_LOADDSI0 BIT(0) + +#define CM_PLLH 0x110 +# define CM_PLLH_LOADRCAL BIT(2) +# define CM_PLLH_LOADAUX BIT(1) +# define CM_PLLH_LOADPIX BIT(0) + +#define CM_LOCK 0x114 +# define CM_LOCK_FLOCKH BIT(12) +# define CM_LOCK_FLOCKD BIT(11) +# define CM_LOCK_FLOCKC BIT(10) +# define CM_LOCK_FLOCKB BIT(9) +# define CM_LOCK_FLOCKA BIT(8) + +#define CM_EVENT 0x118 +#define CM_DSI1ECTL 0x158 +#define CM_DSI1EDIV 0x15c +#define CM_DSI1PCTL 0x160 +#define CM_DSI1PDIV 0x164 +#define CM_DFTCTL 0x168 +#define CM_DFTDIV 0x16c + +#define CM_PLLB 0x170 +# define CM_PLLB_HOLDARM BIT(1) +# define CM_PLLB_LOADARM BIT(0) + +#define A2W_PLLA_CTRL 0x1100 +#define A2W_PLLC_CTRL 0x1120 +#define A2W_PLLD_CTRL 0x1140 +#define A2W_PLLH_CTRL 0x1160 +#define A2W_PLLB_CTRL 0x11e0 +# define A2W_PLL_CTRL_PRST_DISABLE BIT(17) +# define A2W_PLL_CTRL_PWRDN BIT(16) +# define A2W_PLL_CTRL_PDIV_MASK 0x000007000 +# define A2W_PLL_CTRL_PDIV_SHIFT 12 +# define A2W_PLL_CTRL_NDIV_MASK 0x0000003ff +# define A2W_PLL_CTRL_NDIV_SHIFT 0 + +#define A2W_PLLA_ANA0 0x1010 +#define A2W_PLLC_ANA0 0x1030 +#define A2W_PLLD_ANA0 0x1050 +#define A2W_PLLH_ANA0 0x1070 +#define A2W_PLLB_ANA0 0x10f0 + +#define A2W_PLL_KA_SHIFT 7 +#define A2W_PLL_KA_MASK GENMASK(9, 7) +#define A2W_PLL_KI_SHIFT 19 +#define A2W_PLL_KI_MASK GENMASK(21, 19) +#define A2W_PLL_KP_SHIFT 15 +#define A2W_PLL_KP_MASK GENMASK(18, 15) + +#define A2W_PLLH_KA_SHIFT 19 +#define A2W_PLLH_KA_MASK GENMASK(21, 19) +#define A2W_PLLH_KI_LOW_SHIFT 22 +#define A2W_PLLH_KI_LOW_MASK GENMASK(23, 22) +#define A2W_PLLH_KI_HIGH_SHIFT 0 +#define A2W_PLLH_KI_HIGH_MASK GENMASK(0, 0) +#define A2W_PLLH_KP_SHIFT 1 +#define A2W_PLLH_KP_MASK GENMASK(4, 1) + +#define A2W_XOSC_CTRL 0x1190 +# define A2W_XOSC_CTRL_PLLB_ENABLE BIT(7) +# define A2W_XOSC_CTRL_PLLA_ENABLE BIT(6) +# define A2W_XOSC_CTRL_PLLD_ENABLE BIT(5) +# define A2W_XOSC_CTRL_DDR_ENABLE BIT(4) +# define A2W_XOSC_CTRL_CPR1_ENABLE BIT(3) +# define A2W_XOSC_CTRL_USB_ENABLE BIT(2) +# define A2W_XOSC_CTRL_HDMI_ENABLE BIT(1) +# define A2W_XOSC_CTRL_PLLC_ENABLE BIT(0) + +#define A2W_PLLA_FRAC 0x1200 +#define A2W_PLLC_FRAC 0x1220 +#define A2W_PLLD_FRAC 0x1240 +#define A2W_PLLH_FRAC 0x1260 +#define A2W_PLLB_FRAC 0x12e0 +# define A2W_PLL_FRAC_MASK ((1 << A2W_PLL_FRAC_BITS) - 1) +# define A2W_PLL_FRAC_BITS 20 + +#define A2W_PLL_CHANNEL_DISABLE BIT(8) +#define A2W_PLL_DIV_BITS 8 +#define A2W_PLL_DIV_SHIFT 0 + +#define A2W_PLLA_DSI0 0x1300 +#define A2W_PLLA_CORE 0x1400 +#define A2W_PLLA_PER 0x1500 +#define A2W_PLLA_CCP2 0x1600 + +#define A2W_PLLC_CORE2 0x1320 +#define A2W_PLLC_CORE1 0x1420 +#define A2W_PLLC_PER 0x1520 +#define A2W_PLLC_CORE0 0x1620 + +#define A2W_PLLD_DSI0 0x1340 +#define A2W_PLLD_CORE 0x1440 +#define A2W_PLLD_PER 0x1540 +#define A2W_PLLD_DSI1 0x1640 + +#define A2W_PLLH_AUX 0x1360 +#define A2W_PLLH_RCAL 0x1460 +#define A2W_PLLH_PIX 0x1560 +#define A2W_PLLH_STS 0x1660 + +#define A2W_PLLH_CTRLR 0x1960 +#define A2W_PLLH_FRACR 0x1a60 +#define A2W_PLLH_AUXR 0x1b60 +#define A2W_PLLH_RCALR 0x1c60 +#define A2W_PLLH_PIXR 0x1d60 +#define A2W_PLLH_STSR 0x1e60 + +#define A2W_PLLB_ARM 0x13e0 +#define A2W_PLLB_SP0 0x14e0 +#define A2W_PLLB_SP1 0x15e0 +#define A2W_PLLB_SP2 0x16e0 + +#define LOCK_TIMEOUT_NS 100000000 +#define BCM2835_MAX_FB_RATE 1750000000u + +struct bcm2835_cprman { + struct device *dev; + void __iomem *regs; + spinlock_t regs_lock; + const char *osc_name; + + struct clk_onecell_data onecell; + struct clk *clks[BCM2835_CLOCK_COUNT]; +}; + +static inline void cprman_write(struct bcm2835_cprman *cprman, u32 reg, u32 val) +{ + writel(CM_PASSWORD | val, cprman->regs + reg); +} + +static inline u32 cprman_read(struct bcm2835_cprman *cprman, u32 reg) +{ + return readl(cprman->regs + reg); +} + +/* + * These are fixed clocks. They're probably not all root clocks and it may + * be possible to turn them on and off but until this is mapped out better + * it's the only way they can be used. + */ +void __init bcm2835_init_clocks(void) +{ + struct clk *clk; + int ret; + + clk = clk_register_fixed_rate(NULL, "apb_pclk", NULL, CLK_IS_ROOT, + 126000000); + if (IS_ERR(clk)) + pr_err("apb_pclk not registered\n"); + + clk = clk_register_fixed_rate(NULL, "uart0_pclk", NULL, CLK_IS_ROOT, + 3000000); + if (IS_ERR(clk)) + pr_err("uart0_pclk not registered\n"); + ret = clk_register_clkdev(clk, NULL, "20201000.uart"); + if (ret) + pr_err("uart0_pclk alias not registered\n"); + + clk = clk_register_fixed_rate(NULL, "uart1_pclk", NULL, CLK_IS_ROOT, + 125000000); + if (IS_ERR(clk)) + pr_err("uart1_pclk not registered\n"); + ret = clk_register_clkdev(clk, NULL, "20215000.uart"); + if (ret) + pr_err("uart1_pclk alias not registered\n"); +} + +struct bcm2835_pll_data { + const char *name; + u32 cm_ctrl_reg; + u32 a2w_ctrl_reg; + u32 frac_reg; + u32 ana_reg_base; + u32 reference_enable_mask; + /* Bit in CM_LOCK to indicate when the PLL has locked. */ + u32 lock_mask; + + const struct bcm2835_pll_ana_bits *ana; + + unsigned long min_rate; + unsigned long max_rate; + /* + * Highest rate for the VCO before we have to use the + * pre-divide-by-2. + */ + unsigned long max_fb_rate; +}; + +struct bcm2835_pll_ana_bits { + u32 mask0; + u32 set0; + u32 mask1; + u32 set1; + u32 mask3; + u32 set3; + u32 fb_prediv_mask; +}; + +static const struct bcm2835_pll_ana_bits bcm2835_ana_default = { + .mask0 = 0, + .set0 = 0, + .mask1 = ~(A2W_PLL_KI_MASK | A2W_PLL_KP_MASK), + .set1 = (2 << A2W_PLL_KI_SHIFT) | (8 << A2W_PLL_KP_SHIFT), + .mask3 = ~A2W_PLL_KA_MASK, + .set3 = (2 << A2W_PLL_KA_SHIFT), + .fb_prediv_mask = BIT(14), +}; + +static const struct bcm2835_pll_ana_bits bcm2835_ana_pllh = { + .mask0 = ~(A2W_PLLH_KA_MASK | A2W_PLLH_KI_LOW_MASK), + .set0 = (2 << A2W_PLLH_KA_SHIFT) | (2 << A2W_PLLH_KI_LOW_SHIFT), + .mask1 = ~(A2W_PLLH_KI_HIGH_MASK | A2W_PLLH_KP_MASK), + .set1 = (6 << A2W_PLLH_KP_SHIFT), + .mask3 = 0, + .set3 = 0, + .fb_prediv_mask = BIT(11), +}; + +/* + * PLLA is the auxiliary PLL, used to drive the CCP2 (Compact Camera + * Port 2) transmitter clock. + * + * It is in the PX LDO power domain, which is on when the AUDIO domain + * is on. + */ +static const struct bcm2835_pll_data bcm2835_plla_data = { + .name = "plla", + .cm_ctrl_reg = CM_PLLA, + .a2w_ctrl_reg = A2W_PLLA_CTRL, + .frac_reg = A2W_PLLA_FRAC, + .ana_reg_base = A2W_PLLA_ANA0, + .reference_enable_mask = A2W_XOSC_CTRL_PLLA_ENABLE, + .lock_mask = CM_LOCK_FLOCKA, + + .ana = &bcm2835_ana_default, + + .min_rate = 600000000u, + .max_rate = 2400000000u, + .max_fb_rate = BCM2835_MAX_FB_RATE, +}; + +/* PLLB is used for the ARM's clock. */ +static const struct bcm2835_pll_data bcm2835_pllb_data = { + .name = "pllb", + .cm_ctrl_reg = CM_PLLB, + .a2w_ctrl_reg = A2W_PLLB_CTRL, + .frac_reg = A2W_PLLB_FRAC, + .ana_reg_base = A2W_PLLB_ANA0, + .reference_enable_mask = A2W_XOSC_CTRL_PLLB_ENABLE, + .lock_mask = CM_LOCK_FLOCKB, + + .ana = &bcm2835_ana_default, + + .min_rate = 600000000u, + .max_rate = 3000000000u, + .max_fb_rate = BCM2835_MAX_FB_RATE, +}; + +/* + * PLLC is the core PLL, used to drive the core VPU clock. + * + * It is in the PX LDO power domain, which is on when the AUDIO domain + * is on. +*/ +static const struct bcm2835_pll_data bcm2835_pllc_data = { + .name = "pllc", + .cm_ctrl_reg = CM_PLLC, + .a2w_ctrl_reg = A2W_PLLC_CTRL, + .frac_reg = A2W_PLLC_FRAC, + .ana_reg_base = A2W_PLLC_ANA0, + .reference_enable_mask = A2W_XOSC_CTRL_PLLC_ENABLE, + .lock_mask = CM_LOCK_FLOCKC, + + .ana = &bcm2835_ana_default, + + .min_rate = 600000000u, + .max_rate = 3000000000u, + .max_fb_rate = BCM2835_MAX_FB_RATE, +}; + +/* + * PLLD is the display PLL, used to drive DSI display panels. + * + * It is in the PX LDO power domain, which is on when the AUDIO domain + * is on. + */ +static const struct bcm2835_pll_data bcm2835_plld_data = { + .name = "plld", + .cm_ctrl_reg = CM_PLLD, + .a2w_ctrl_reg = A2W_PLLD_CTRL, + .frac_reg = A2W_PLLD_FRAC, + .ana_reg_base = A2W_PLLD_ANA0, + .reference_enable_mask = A2W_XOSC_CTRL_DDR_ENABLE, + .lock_mask = CM_LOCK_FLOCKD, + + .ana = &bcm2835_ana_default, + + .min_rate = 600000000u, + .max_rate = 2400000000u, + .max_fb_rate = BCM2835_MAX_FB_RATE, +}; + +/* + * PLLH is used to supply the pixel clock or the AUX clock for the TV + * encoder. + * + * It is in the HDMI power domain. + */ +static const struct bcm2835_pll_data bcm2835_pllh_data = { + "pllh", + .cm_ctrl_reg = CM_PLLH, + .a2w_ctrl_reg = A2W_PLLH_CTRL, + .frac_reg = A2W_PLLH_FRAC, + .ana_reg_base = A2W_PLLH_ANA0, + .reference_enable_mask = A2W_XOSC_CTRL_PLLC_ENABLE, + .lock_mask = CM_LOCK_FLOCKH, + + .ana = &bcm2835_ana_pllh, + + .min_rate = 600000000u, + .max_rate = 3000000000u, + .max_fb_rate = BCM2835_MAX_FB_RATE, +}; + +struct bcm2835_pll_divider_data { + const char *name; + const struct bcm2835_pll_data *source_pll; + u32 cm_reg; + u32 a2w_reg; + + u32 load_mask; + u32 hold_mask; + u32 fixed_divider; +}; + +static const struct bcm2835_pll_divider_data bcm2835_plla_core_data = { + .name = "plla_core", + .source_pll = &bcm2835_plla_data, + .cm_reg = CM_PLLA, + .a2w_reg = A2W_PLLA_CORE, + .load_mask = CM_PLLA_LOADCORE, + .hold_mask = CM_PLLA_HOLDCORE, + .fixed_divider = 1, +}; + +static const struct bcm2835_pll_divider_data bcm2835_plla_per_data = { + .name = "plla_per", + .source_pll = &bcm2835_plla_data, + .cm_reg = CM_PLLA, + .a2w_reg = A2W_PLLA_PER, + .load_mask = CM_PLLA_LOADPER, + .hold_mask = CM_PLLA_HOLDPER, + .fixed_divider = 1, +}; + +static const struct bcm2835_pll_divider_data bcm2835_pllb_arm_data = { + .name = "pllb_arm", + .source_pll = &bcm2835_pllb_data, + .cm_reg = CM_PLLB, + .a2w_reg = A2W_PLLB_ARM, + .load_mask = CM_PLLB_LOADARM, + .hold_mask = CM_PLLB_HOLDARM, + .fixed_divider = 1, +}; + +static const struct bcm2835_pll_divider_data bcm2835_pllc_core0_data = { + .name = "pllc_core0", + .source_pll = &bcm2835_pllc_data, + .cm_reg = CM_PLLC, + .a2w_reg = A2W_PLLC_CORE0, + .load_mask = CM_PLLC_LOADCORE0, + .hold_mask = CM_PLLC_HOLDCORE0, + .fixed_divider = 1, +}; + +static const struct bcm2835_pll_divider_data bcm2835_pllc_core1_data = { + .name = "pllc_core1", .source_pll = &bcm2835_pllc_data, + .cm_reg = CM_PLLC, A2W_PLLC_CORE1, + .load_mask = CM_PLLC_LOADCORE1, + .hold_mask = CM_PLLC_HOLDCORE1, + .fixed_divider = 1, +}; + +static const struct bcm2835_pll_divider_data bcm2835_pllc_core2_data = { + .name = "pllc_core2", + .source_pll = &bcm2835_pllc_data, + .cm_reg = CM_PLLC, + .a2w_reg = A2W_PLLC_CORE2, + .load_mask = CM_PLLC_LOADCORE2, + .hold_mask = CM_PLLC_HOLDCORE2, + .fixed_divider = 1, +}; + +static const struct bcm2835_pll_divider_data bcm2835_pllc_per_data = { + .name = "pllc_per", + .source_pll = &bcm2835_pllc_data, + .cm_reg = CM_PLLC, + .a2w_reg = A2W_PLLC_PER, + .load_mask = CM_PLLC_LOADPER, + .hold_mask = CM_PLLC_HOLDPER, + .fixed_divider = 1, +}; + +static const struct bcm2835_pll_divider_data bcm2835_plld_core_data = { + .name = "plld_core", + .source_pll = &bcm2835_plld_data, + .cm_reg = CM_PLLD, + .a2w_reg = A2W_PLLD_CORE, + .load_mask = CM_PLLD_LOADCORE, + .hold_mask = CM_PLLD_HOLDCORE, + .fixed_divider = 1, +}; + +static const struct bcm2835_pll_divider_data bcm2835_plld_per_data = { + .name = "plld_per", + .source_pll = &bcm2835_plld_data, + .cm_reg = CM_PLLD, + .a2w_reg = A2W_PLLD_PER, + .load_mask = CM_PLLD_LOADPER, + .hold_mask = CM_PLLD_HOLDPER, + .fixed_divider = 1, +}; + +static const struct bcm2835_pll_divider_data bcm2835_pllh_rcal_data = { + .name = "pllh_rcal", + .source_pll = &bcm2835_pllh_data, + .cm_reg = CM_PLLH, + .a2w_reg = A2W_PLLH_RCAL, + .load_mask = CM_PLLH_LOADRCAL, + .hold_mask = 0, + .fixed_divider = 10, +}; + +static const struct bcm2835_pll_divider_data bcm2835_pllh_aux_data = { + .name = "pllh_aux", + .source_pll = &bcm2835_pllh_data, + .cm_reg = CM_PLLH, + .a2w_reg = A2W_PLLH_AUX, + .load_mask = CM_PLLH_LOADAUX, + .hold_mask = 0, + .fixed_divider = 10, +}; + +static const struct bcm2835_pll_divider_data bcm2835_pllh_pix_data = { + .name = "pllh_pix", + .source_pll = &bcm2835_pllh_data, + .cm_reg = CM_PLLH, + .a2w_reg = A2W_PLLH_PIX, + .load_mask = CM_PLLH_LOADPIX, + .hold_mask = 0, + .fixed_divider = 10, +}; + +struct bcm2835_clock_data { + const char *name; + + const char *const *parents; + int num_mux_parents; + + u32 ctl_reg; + u32 div_reg; + + /* Number of integer bits in the divider */ + u32 int_bits; + /* Number of fractional bits in the divider */ + u32 frac_bits; + + bool is_vpu_clock; +}; + +static const char *const bcm2835_clock_per_parents[] = { + "gnd", + "xosc", + "testdebug0", + "testdebug1", + "plla_per", + "pllc_per", + "plld_per", + "pllh_aux", +}; + +static const char *const bcm2835_clock_vpu_parents[] = { + "gnd", + "xosc", + "testdebug0", + "testdebug1", + "plla_core", + "pllc_core0", + "plld_core", + "pllh_aux", + "pllc_core1", + "pllc_core2", +}; + +static const char *const bcm2835_clock_osc_parents[] = { + "gnd", + "xosc", + "testdebug0", + "testdebug1" +}; + +/* + * Used for a 1Mhz clock for the system clocksource, and also used by + * the watchdog timer and the camera pulse generator. + */ +static const struct bcm2835_clock_data bcm2835_clock_timer_data = { + .name = "timer", + .num_mux_parents = ARRAY_SIZE(bcm2835_clock_osc_parents), + .parents = bcm2835_clock_osc_parents, + .ctl_reg = CM_TIMERCTL, + .div_reg = CM_TIMERDIV, + .int_bits = 6, + .frac_bits = 12, +}; + +/* One Time Programmable Memory clock. Maximum 10Mhz. */ +static const struct bcm2835_clock_data bcm2835_clock_otp_data = { + .name = "otp", + .num_mux_parents = ARRAY_SIZE(bcm2835_clock_osc_parents), + .parents = bcm2835_clock_osc_parents, + .ctl_reg = CM_OTPCTL, + .div_reg = CM_OTPDIV, + .int_bits = 4, + .frac_bits = 0, +}; + +/* + * VPU clock. This doesn't have an enable bit, since it drives the + * bus for everything else, and is special so it doesn't need to be + * gated for rate changes. It is also known as "clk_audio" in various + * hardware documentation. + */ +static const struct bcm2835_clock_data bcm2835_clock_vpu_data = { + .name = "vpu", + .num_mux_parents = ARRAY_SIZE(bcm2835_clock_vpu_parents), + .parents = bcm2835_clock_vpu_parents, + .ctl_reg = CM_VPUCTL, + .div_reg = CM_VPUDIV, + .int_bits = 12, + .frac_bits = 8, + .is_vpu_clock = true, +}; + +static const struct bcm2835_clock_data bcm2835_clock_v3d_data = { + .name = "v3d", + .num_mux_parents = ARRAY_SIZE(bcm2835_clock_vpu_parents), + .parents = bcm2835_clock_vpu_parents, + .ctl_reg = CM_V3DCTL, + .div_reg = CM_V3DDIV, + .int_bits = 4, + .frac_bits = 8, +}; + +static const struct bcm2835_clock_data bcm2835_clock_isp_data = { + .name = "isp", + .num_mux_parents = ARRAY_SIZE(bcm2835_clock_vpu_parents), + .parents = bcm2835_clock_vpu_parents, + .ctl_reg = CM_ISPCTL, + .div_reg = CM_ISPDIV, + .int_bits = 4, + .frac_bits = 8, +}; + +static const struct bcm2835_clock_data bcm2835_clock_h264_data = { + .name = "h264", + .num_mux_parents = ARRAY_SIZE(bcm2835_clock_vpu_parents), + .parents = bcm2835_clock_vpu_parents, + .ctl_reg = CM_H264CTL, + .div_reg = CM_H264DIV, + .int_bits = 4, + .frac_bits = 8, +}; + +/* TV encoder clock. Only operating frequency is 108Mhz. */ +static const struct bcm2835_clock_data bcm2835_clock_vec_data = { + .name = "vec", + .num_mux_parents = ARRAY_SIZE(bcm2835_clock_per_parents), + .parents = bcm2835_clock_per_parents, + .ctl_reg = CM_VECCTL, + .div_reg = CM_VECDIV, + .int_bits = 4, + .frac_bits = 0, +}; + +static const struct bcm2835_clock_data bcm2835_clock_uart_data = { + .name = "uart", + .num_mux_parents = ARRAY_SIZE(bcm2835_clock_per_parents), + .parents = bcm2835_clock_per_parents, + .ctl_reg = CM_UARTCTL, + .div_reg = CM_UARTDIV, + .int_bits = 10, + .frac_bits = 12, +}; + +/* HDMI state machine */ +static const struct bcm2835_clock_data bcm2835_clock_hsm_data = { + .name = "hsm", + .num_mux_parents = ARRAY_SIZE(bcm2835_clock_per_parents), + .parents = bcm2835_clock_per_parents, + .ctl_reg = CM_HSMCTL, + .div_reg = CM_HSMDIV, + .int_bits = 4, + .frac_bits = 8, +}; + +/* + * Secondary SDRAM clock. Used for low-voltage modes when the PLL in + * the SDRAM controller can't be used. + */ +static const struct bcm2835_clock_data bcm2835_clock_sdram_data = { + .name = "sdram", + .num_mux_parents = ARRAY_SIZE(bcm2835_clock_vpu_parents), + .parents = bcm2835_clock_vpu_parents, + .ctl_reg = CM_SDCCTL, + .div_reg = CM_SDCDIV, + .int_bits = 6, + .frac_bits = 0, +}; + +/* Clock for the temperature sensor. Generally run at 2Mhz, max 5Mhz. */ +static const struct bcm2835_clock_data bcm2835_clock_tsens_data = { + .name = "tsens", + .num_mux_parents = ARRAY_SIZE(bcm2835_clock_osc_parents), + .parents = bcm2835_clock_osc_parents, + .ctl_reg = CM_TSENSCTL, + .div_reg = CM_TSENSDIV, + .int_bits = 5, + .frac_bits = 0, +}; + +/* Arasan EMMC clock */ +static const struct bcm2835_clock_data bcm2835_clock_emmc_data = { + .name = "emmc", + .num_mux_parents = ARRAY_SIZE(bcm2835_clock_per_parents), + .parents = bcm2835_clock_per_parents, + .ctl_reg = CM_EMMCCTL, + .div_reg = CM_EMMCDIV, + .int_bits = 4, + .frac_bits = 8, +}; + +struct bcm2835_pll { + struct clk_hw hw; + struct bcm2835_cprman *cprman; + const struct bcm2835_pll_data *data; +}; + +static int bcm2835_pll_is_on(struct clk_hw *hw) +{ + struct bcm2835_pll *pll = container_of(hw, struct bcm2835_pll, hw); + struct bcm2835_cprman *cprman = pll->cprman; + const struct bcm2835_pll_data *data = pll->data; + + return cprman_read(cprman, data->a2w_ctrl_reg) & + A2W_PLL_CTRL_PRST_DISABLE; +} + +static void bcm2835_pll_choose_ndiv_and_fdiv(unsigned long rate, + unsigned long parent_rate, + u32 *ndiv, u32 *fdiv) +{ + u64 div; + + div = (u64)rate << A2W_PLL_FRAC_BITS; + do_div(div, parent_rate); + + *ndiv = div >> A2W_PLL_FRAC_BITS; + *fdiv = div & ((1 << A2W_PLL_FRAC_BITS) - 1); +} + +static long bcm2835_pll_rate_from_divisors(unsigned long parent_rate, + u32 ndiv, u32 fdiv, u32 pdiv) +{ + u64 rate; + + if (pdiv == 0) + return 0; + + rate = (u64)parent_rate * ((ndiv << A2W_PLL_FRAC_BITS) + fdiv); + do_div(rate, pdiv); + return rate >> A2W_PLL_FRAC_BITS; +} + +static long bcm2835_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + u32 ndiv, fdiv; + + bcm2835_pll_choose_ndiv_and_fdiv(rate, *parent_rate, &ndiv, &fdiv); + + return bcm2835_pll_rate_from_divisors(*parent_rate, ndiv, fdiv, 1); +} + +static unsigned long bcm2835_pll_get_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct bcm2835_pll *pll = container_of(hw, struct bcm2835_pll, hw); + struct bcm2835_cprman *cprman = pll->cprman; + const struct bcm2835_pll_data *data = pll->data; + u32 a2wctrl = cprman_read(cprman, data->a2w_ctrl_reg); + u32 ndiv, pdiv, fdiv; + bool using_prediv; + + if (parent_rate == 0) + return 0; + + fdiv = cprman_read(cprman, data->frac_reg) & A2W_PLL_FRAC_MASK; + ndiv = (a2wctrl & A2W_PLL_CTRL_NDIV_MASK) >> A2W_PLL_CTRL_NDIV_SHIFT; + pdiv = (a2wctrl & A2W_PLL_CTRL_PDIV_MASK) >> A2W_PLL_CTRL_PDIV_SHIFT; + using_prediv = cprman_read(cprman, data->ana_reg_base + 4) & + data->ana->fb_prediv_mask; + + if (using_prediv) + ndiv *= 2; + + return bcm2835_pll_rate_from_divisors(parent_rate, ndiv, fdiv, pdiv); +} + +static void bcm2835_pll_off(struct clk_hw *hw) +{ + struct bcm2835_pll *pll = container_of(hw, struct bcm2835_pll, hw); + struct bcm2835_cprman *cprman = pll->cprman; + const struct bcm2835_pll_data *data = pll->data; + + cprman_write(cprman, data->cm_ctrl_reg, CM_PLL_ANARST); + cprman_write(cprman, data->a2w_ctrl_reg, A2W_PLL_CTRL_PWRDN); +} + +static int bcm2835_pll_on(struct clk_hw *hw) +{ + struct bcm2835_pll *pll = container_of(hw, struct bcm2835_pll, hw); + struct bcm2835_cprman *cprman = pll->cprman; + const struct bcm2835_pll_data *data = pll->data; + ktime_t timeout; + + /* Take the PLL out of reset. */ + cprman_write(cprman, data->cm_ctrl_reg, + cprman_read(cprman, data->cm_ctrl_reg) & ~CM_PLL_ANARST); + + /* Wait for the PLL to lock. */ + timeout = ktime_add_ns(ktime_get(), LOCK_TIMEOUT_NS); + while (!(cprman_read(cprman, CM_LOCK) & data->lock_mask)) { + if (ktime_after(ktime_get(), timeout)) { + dev_err(cprman->dev, "%s: couldn't lock PLL\n", + clk_hw_get_name(hw)); + return -ETIMEDOUT; + } + + cpu_relax(); + } + + return 0; +} + +static void +bcm2835_pll_write_ana(struct bcm2835_cprman *cprman, u32 ana_reg_base, u32 *ana) +{ + int i; + + /* + * ANA register setup is done as a series of writes to + * ANA3-ANA0, in that order. This lets us write all 4 + * registers as a single cycle of the serdes interface (taking + * 100 xosc clocks), whereas if we were to update ana0, 1, and + * 3 individually through their partial-write registers, each + * would be their own serdes cycle. + */ + for (i = 3; i >= 0; i--) + cprman_write(cprman, ana_reg_base + i * 4, ana[i]); +} + +static int bcm2835_pll_set_rate(struct clk_hw *hw, + unsigned long rate, unsigned long parent_rate) +{ + struct bcm2835_pll *pll = container_of(hw, struct bcm2835_pll, hw); + struct bcm2835_cprman *cprman = pll->cprman; + const struct bcm2835_pll_data *data = pll->data; + bool was_using_prediv, use_fb_prediv, do_ana_setup_first; + u32 ndiv, fdiv, a2w_ctl; + u32 ana[4]; + int i; + + if (rate < data->min_rate || rate > data->max_rate) { + dev_err(cprman->dev, "%s: rate out of spec: %lu vs (%lu, %lu)\n", + clk_hw_get_name(hw), rate, + data->min_rate, data->max_rate); + return -EINVAL; + } + + if (rate > data->max_fb_rate) { + use_fb_prediv = true; + rate /= 2; + } else { + use_fb_prediv = false; + } + + bcm2835_pll_choose_ndiv_and_fdiv(rate, parent_rate, &ndiv, &fdiv); + + for (i = 3; i >= 0; i--) + ana[i] = cprman_read(cprman, data->ana_reg_base + i * 4); + + was_using_prediv = ana[1] & data->ana->fb_prediv_mask; + + ana[0] &= ~data->ana->mask0; + ana[0] |= data->ana->set0; + ana[1] &= ~data->ana->mask1; + ana[1] |= data->ana->set1; + ana[3] &= ~data->ana->mask3; + ana[3] |= data->ana->set3; + + if (was_using_prediv && !use_fb_prediv) { + ana[1] &= ~data->ana->fb_prediv_mask; + do_ana_setup_first = true; + } else if (!was_using_prediv && use_fb_prediv) { + ana[1] |= data->ana->fb_prediv_mask; + do_ana_setup_first = false; + } else { + do_ana_setup_first = true; + } + + /* Unmask the reference clock from the oscillator. */ + cprman_write(cprman, A2W_XOSC_CTRL, + cprman_read(cprman, A2W_XOSC_CTRL) | + data->reference_enable_mask); + + if (do_ana_setup_first) + bcm2835_pll_write_ana(cprman, data->ana_reg_base, ana); + + /* Set the PLL multiplier from the oscillator. */ + cprman_write(cprman, data->frac_reg, fdiv); + + a2w_ctl = cprman_read(cprman, data->a2w_ctrl_reg); + a2w_ctl &= ~A2W_PLL_CTRL_NDIV_MASK; + a2w_ctl |= ndiv << A2W_PLL_CTRL_NDIV_SHIFT; + a2w_ctl &= ~A2W_PLL_CTRL_PDIV_MASK; + a2w_ctl |= 1 << A2W_PLL_CTRL_PDIV_SHIFT; + cprman_write(cprman, data->a2w_ctrl_reg, a2w_ctl); + + if (!do_ana_setup_first) + bcm2835_pll_write_ana(cprman, data->ana_reg_base, ana); + + return 0; +} + +static const struct clk_ops bcm2835_pll_clk_ops = { + .is_prepared = bcm2835_pll_is_on, + .prepare = bcm2835_pll_on, + .unprepare = bcm2835_pll_off, + .recalc_rate = bcm2835_pll_get_rate, + .set_rate = bcm2835_pll_set_rate, + .round_rate = bcm2835_pll_round_rate, +}; + +struct bcm2835_pll_divider { + struct clk_divider div; + struct bcm2835_cprman *cprman; + const struct bcm2835_pll_divider_data *data; +}; + +static struct bcm2835_pll_divider * +bcm2835_pll_divider_from_hw(struct clk_hw *hw) +{ + return container_of(hw, struct bcm2835_pll_divider, div.hw); +} + +static int bcm2835_pll_divider_is_on(struct clk_hw *hw) +{ + struct bcm2835_pll_divider *divider = bcm2835_pll_divider_from_hw(hw); + struct bcm2835_cprman *cprman = divider->cprman; + const struct bcm2835_pll_divider_data *data = divider->data; + + return !(cprman_read(cprman, data->a2w_reg) & A2W_PLL_CHANNEL_DISABLE); +} + +static long bcm2835_pll_divider_round_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long *parent_rate) +{ + return clk_divider_ops.round_rate(hw, rate, parent_rate); +} + +static unsigned long bcm2835_pll_divider_get_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct bcm2835_pll_divider *divider = bcm2835_pll_divider_from_hw(hw); + struct bcm2835_cprman *cprman = divider->cprman; + const struct bcm2835_pll_divider_data *data = divider->data; + u32 div = cprman_read(cprman, data->a2w_reg); + + div &= (1 << A2W_PLL_DIV_BITS) - 1; + if (div == 0) + div = 256; + + return parent_rate / div; +} + +static void bcm2835_pll_divider_off(struct clk_hw *hw) +{ + struct bcm2835_pll_divider *divider = bcm2835_pll_divider_from_hw(hw); + struct bcm2835_cprman *cprman = divider->cprman; + const struct bcm2835_pll_divider_data *data = divider->data; + + cprman_write(cprman, data->cm_reg, + (cprman_read(cprman, data->cm_reg) & + ~data->load_mask) | data->hold_mask); + cprman_write(cprman, data->a2w_reg, A2W_PLL_CHANNEL_DISABLE); +} + +static int bcm2835_pll_divider_on(struct clk_hw *hw) +{ + struct bcm2835_pll_divider *divider = bcm2835_pll_divider_from_hw(hw); + struct bcm2835_cprman *cprman = divider->cprman; + const struct bcm2835_pll_divider_data *data = divider->data; + + cprman_write(cprman, data->a2w_reg, + cprman_read(cprman, data->a2w_reg) & + ~A2W_PLL_CHANNEL_DISABLE); + + cprman_write(cprman, data->cm_reg, + cprman_read(cprman, data->cm_reg) & ~data->hold_mask); + + return 0; +} + +static int bcm2835_pll_divider_set_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate) +{ + struct bcm2835_pll_divider *divider = bcm2835_pll_divider_from_hw(hw); + struct bcm2835_cprman *cprman = divider->cprman; + const struct bcm2835_pll_divider_data *data = divider->data; + u32 cm; + int ret; + + ret = clk_divider_ops.set_rate(hw, rate, parent_rate); + if (ret) + return ret; + + cm = cprman_read(cprman, data->cm_reg); + cprman_write(cprman, data->cm_reg, cm | data->load_mask); + cprman_write(cprman, data->cm_reg, cm & ~data->load_mask); + + return 0; +} + +static const struct clk_ops bcm2835_pll_divider_clk_ops = { + .is_prepared = bcm2835_pll_divider_is_on, + .prepare = bcm2835_pll_divider_on, + .unprepare = bcm2835_pll_divider_off, + .recalc_rate = bcm2835_pll_divider_get_rate, + .set_rate = bcm2835_pll_divider_set_rate, + .round_rate = bcm2835_pll_divider_round_rate, +}; + +/* + * The CM dividers do fixed-point division, so we can't use the + * generic integer divider code like the PLL dividers do (and we can't + * fake it by having some fixed shifts preceding it in the clock tree, + * because we'd run out of bits in a 32-bit unsigned long). + */ +struct bcm2835_clock { + struct clk_hw hw; + struct bcm2835_cprman *cprman; + const struct bcm2835_clock_data *data; +}; + +static struct bcm2835_clock *bcm2835_clock_from_hw(struct clk_hw *hw) +{ + return container_of(hw, struct bcm2835_clock, hw); +} + +static int bcm2835_clock_is_on(struct clk_hw *hw) +{ + struct bcm2835_clock *clock = bcm2835_clock_from_hw(hw); + struct bcm2835_cprman *cprman = clock->cprman; + const struct bcm2835_clock_data *data = clock->data; + + return (cprman_read(cprman, data->ctl_reg) & CM_ENABLE) != 0; +} + +static u32 bcm2835_clock_choose_div(struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate) +{ + struct bcm2835_clock *clock = bcm2835_clock_from_hw(hw); + const struct bcm2835_clock_data *data = clock->data; + u32 unused_frac_mask = GENMASK(CM_DIV_FRAC_BITS - data->frac_bits, 0); + u64 temp = (u64)parent_rate << CM_DIV_FRAC_BITS; + u32 div; + + do_div(temp, rate); + div = temp; + + /* Round and mask off the unused bits */ + if (unused_frac_mask != 0) { + div += unused_frac_mask >> 1; + div &= ~unused_frac_mask; + } + + /* Clamp to the limits. */ + div = max(div, unused_frac_mask + 1); + div = min_t(u32, div, GENMASK(data->int_bits + CM_DIV_FRAC_BITS - 1, + CM_DIV_FRAC_BITS - data->frac_bits)); + + return div; +} + +static long bcm2835_clock_rate_from_divisor(struct bcm2835_clock *clock, + unsigned long parent_rate, + u32 div) +{ + const struct bcm2835_clock_data *data = clock->data; + u64 temp; + + /* + * The divisor is a 12.12 fixed point field, but only some of + * the bits are populated in any given clock. + */ + div >>= CM_DIV_FRAC_BITS - data->frac_bits; + div &= (1 << (data->int_bits + data->frac_bits)) - 1; + + if (div == 0) + return 0; + + temp = (u64)parent_rate << data->frac_bits; + + do_div(temp, div); + + return temp; +} + +static long bcm2835_clock_round_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long *parent_rate) +{ + struct bcm2835_clock *clock = bcm2835_clock_from_hw(hw); + u32 div = bcm2835_clock_choose_div(hw, rate, *parent_rate); + + return bcm2835_clock_rate_from_divisor(clock, *parent_rate, div); +} + +static unsigned long bcm2835_clock_get_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct bcm2835_clock *clock = bcm2835_clock_from_hw(hw); + struct bcm2835_cprman *cprman = clock->cprman; + const struct bcm2835_clock_data *data = clock->data; + u32 div = cprman_read(cprman, data->div_reg); + + return bcm2835_clock_rate_from_divisor(clock, parent_rate, div); +} + +static void bcm2835_clock_wait_busy(struct bcm2835_clock *clock) +{ + struct bcm2835_cprman *cprman = clock->cprman; + const struct bcm2835_clock_data *data = clock->data; + ktime_t timeout = ktime_add_ns(ktime_get(), LOCK_TIMEOUT_NS); + + while (cprman_read(cprman, data->ctl_reg) & CM_BUSY) { + if (ktime_after(ktime_get(), timeout)) { + dev_err(cprman->dev, "%s: couldn't lock PLL\n", + clk_hw_get_name(&clock->hw)); + return; + } + cpu_relax(); + } +} + +static void bcm2835_clock_off(struct clk_hw *hw) +{ + struct bcm2835_clock *clock = bcm2835_clock_from_hw(hw); + struct bcm2835_cprman *cprman = clock->cprman; + const struct bcm2835_clock_data *data = clock->data; + + spin_lock(&cprman->regs_lock); + cprman_write(cprman, data->ctl_reg, + cprman_read(cprman, data->ctl_reg) & ~CM_ENABLE); + spin_unlock(&cprman->regs_lock); + + /* BUSY will remain high until the divider completes its cycle. */ + bcm2835_clock_wait_busy(clock); +} + +static int bcm2835_clock_on(struct clk_hw *hw) +{ + struct bcm2835_clock *clock = bcm2835_clock_from_hw(hw); + struct bcm2835_cprman *cprman = clock->cprman; + const struct bcm2835_clock_data *data = clock->data; + + spin_lock(&cprman->regs_lock); + cprman_write(cprman, data->ctl_reg, + cprman_read(cprman, data->ctl_reg) | + CM_ENABLE | + CM_GATE); + spin_unlock(&cprman->regs_lock); + + return 0; +} + +static int bcm2835_clock_set_rate(struct clk_hw *hw, + unsigned long rate, unsigned long parent_rate) +{ + struct bcm2835_clock *clock = bcm2835_clock_from_hw(hw); + struct bcm2835_cprman *cprman = clock->cprman; + const struct bcm2835_clock_data *data = clock->data; + u32 div = bcm2835_clock_choose_div(hw, rate, parent_rate); + + cprman_write(cprman, data->div_reg, div); + + return 0; +} + +static const struct clk_ops bcm2835_clock_clk_ops = { + .is_prepared = bcm2835_clock_is_on, + .prepare = bcm2835_clock_on, + .unprepare = bcm2835_clock_off, + .recalc_rate = bcm2835_clock_get_rate, + .set_rate = bcm2835_clock_set_rate, + .round_rate = bcm2835_clock_round_rate, +}; + +static int bcm2835_vpu_clock_is_on(struct clk_hw *hw) +{ + return true; +} + +/* + * The VPU clock can never be disabled (it doesn't have an ENABLE + * bit), so it gets its own set of clock ops. + */ +static const struct clk_ops bcm2835_vpu_clock_clk_ops = { + .is_prepared = bcm2835_vpu_clock_is_on, + .recalc_rate = bcm2835_clock_get_rate, + .set_rate = bcm2835_clock_set_rate, + .round_rate = bcm2835_clock_round_rate, +}; + +static struct clk *bcm2835_register_pll(struct bcm2835_cprman *cprman, + const struct bcm2835_pll_data *data) +{ + struct bcm2835_pll *pll; + struct clk_init_data init; + + memset(&init, 0, sizeof(init)); + + /* All of the PLLs derive from the external oscillator. */ + init.parent_names = &cprman->osc_name; + init.num_parents = 1; + init.name = data->name; + init.ops = &bcm2835_pll_clk_ops; + init.flags = CLK_IGNORE_UNUSED; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return NULL; + + pll->cprman = cprman; + pll->data = data; + pll->hw.init = &init; + + return devm_clk_register(cprman->dev, &pll->hw); +} + +static struct clk * +bcm2835_register_pll_divider(struct bcm2835_cprman *cprman, + const struct bcm2835_pll_divider_data *data) +{ + struct bcm2835_pll_divider *divider; + struct clk_init_data init; + struct clk *clk; + const char *divider_name; + + if (data->fixed_divider != 1) { + divider_name = devm_kasprintf(cprman->dev, GFP_KERNEL, + "%s_prediv", data->name); + if (!divider_name) + return NULL; + } else { + divider_name = data->name; + } + + memset(&init, 0, sizeof(init)); + + init.parent_names = &data->source_pll->name; + init.num_parents = 1; + init.name = divider_name; + init.ops = &bcm2835_pll_divider_clk_ops; + init.flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED; + + divider = devm_kzalloc(cprman->dev, sizeof(*divider), GFP_KERNEL); + if (!divider) + return NULL; + + divider->div.reg = cprman->regs + data->a2w_reg; + divider->div.shift = A2W_PLL_DIV_SHIFT; + divider->div.width = A2W_PLL_DIV_BITS; + divider->div.flags = 0; + divider->div.lock = &cprman->regs_lock; + divider->div.hw.init = &init; + divider->div.table = NULL; + + divider->cprman = cprman; + divider->data = data; + + clk = devm_clk_register(cprman->dev, ÷r->div.hw); + if (IS_ERR(clk)) + return clk; + + /* + * PLLH's channels have a fixed divide by 10 afterwards, which + * is what our consumers are actually using. + */ + if (data->fixed_divider != 1) { + return clk_register_fixed_factor(cprman->dev, data->name, + divider_name, + CLK_SET_RATE_PARENT, + 1, + data->fixed_divider); + } + + return clk; +} + +static struct clk *bcm2835_register_clock(struct bcm2835_cprman *cprman, + const struct bcm2835_clock_data *data) +{ + struct bcm2835_clock *clock; + struct clk_init_data init; + const char *parent; + + /* + * Most of the clock generators have a mux field, so we + * instantiate a generic mux as our parent to handle it. + */ + if (data->num_mux_parents) { + const char *parents[1 << CM_SRC_BITS]; + int i; + + parent = devm_kasprintf(cprman->dev, GFP_KERNEL, + "mux_%s", data->name); + if (!parent) + return NULL; + + /* + * Replace our "xosc" references with the oscillator's + * actual name. + */ + for (i = 0; i < data->num_mux_parents; i++) { + if (strcmp(data->parents[i], "xosc") == 0) + parents[i] = cprman->osc_name; + else + parents[i] = data->parents[i]; + } + + clk_register_mux(cprman->dev, parent, + parents, data->num_mux_parents, + CLK_SET_RATE_PARENT, + cprman->regs + data->ctl_reg, + CM_SRC_SHIFT, CM_SRC_BITS, + 0, &cprman->regs_lock); + } else { + parent = data->parents[0]; + } + + memset(&init, 0, sizeof(init)); + init.parent_names = &parent; + init.num_parents = 1; + init.name = data->name; + init.flags = CLK_IGNORE_UNUSED; + + if (data->is_vpu_clock) { + init.ops = &bcm2835_vpu_clock_clk_ops; + } else { + init.ops = &bcm2835_clock_clk_ops; + init.flags |= CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE; + } + + clock = devm_kzalloc(cprman->dev, sizeof(*clock), GFP_KERNEL); + if (!clock) + return NULL; + + clock->cprman = cprman; + clock->data = data; + clock->hw.init = &init; + + return devm_clk_register(cprman->dev, &clock->hw); +} + +static int bcm2835_clk_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct clk **clks; + struct bcm2835_cprman *cprman; + struct resource *res; + + cprman = devm_kzalloc(dev, sizeof(*cprman), GFP_KERNEL); + if (!cprman) + return -ENOMEM; + + spin_lock_init(&cprman->regs_lock); + cprman->dev = dev; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + cprman->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(cprman->regs)) + return PTR_ERR(cprman->regs); + + cprman->osc_name = of_clk_get_parent_name(dev->of_node, 0); + if (!cprman->osc_name) + return -ENODEV; + + platform_set_drvdata(pdev, cprman); + + cprman->onecell.clk_num = BCM2835_CLOCK_COUNT; + cprman->onecell.clks = cprman->clks; + clks = cprman->clks; + + clks[BCM2835_PLLA] = bcm2835_register_pll(cprman, &bcm2835_plla_data); + clks[BCM2835_PLLB] = bcm2835_register_pll(cprman, &bcm2835_pllb_data); + clks[BCM2835_PLLC] = bcm2835_register_pll(cprman, &bcm2835_pllc_data); + clks[BCM2835_PLLD] = bcm2835_register_pll(cprman, &bcm2835_plld_data); + clks[BCM2835_PLLH] = bcm2835_register_pll(cprman, &bcm2835_pllh_data); + + clks[BCM2835_PLLA_CORE] = + bcm2835_register_pll_divider(cprman, &bcm2835_plla_core_data); + clks[BCM2835_PLLA_PER] = + bcm2835_register_pll_divider(cprman, &bcm2835_plla_per_data); + clks[BCM2835_PLLC_CORE0] = + bcm2835_register_pll_divider(cprman, &bcm2835_pllc_core0_data); + clks[BCM2835_PLLC_CORE1] = + bcm2835_register_pll_divider(cprman, &bcm2835_pllc_core1_data); + clks[BCM2835_PLLC_CORE2] = + bcm2835_register_pll_divider(cprman, &bcm2835_pllc_core2_data); + clks[BCM2835_PLLC_PER] = + bcm2835_register_pll_divider(cprman, &bcm2835_pllc_per_data); + clks[BCM2835_PLLD_CORE] = + bcm2835_register_pll_divider(cprman, &bcm2835_plld_core_data); + clks[BCM2835_PLLD_PER] = + bcm2835_register_pll_divider(cprman, &bcm2835_plld_per_data); + clks[BCM2835_PLLH_RCAL] = + bcm2835_register_pll_divider(cprman, &bcm2835_pllh_rcal_data); + clks[BCM2835_PLLH_AUX] = + bcm2835_register_pll_divider(cprman, &bcm2835_pllh_aux_data); + clks[BCM2835_PLLH_PIX] = + bcm2835_register_pll_divider(cprman, &bcm2835_pllh_pix_data); + + clks[BCM2835_CLOCK_TIMER] = + bcm2835_register_clock(cprman, &bcm2835_clock_timer_data); + clks[BCM2835_CLOCK_OTP] = + bcm2835_register_clock(cprman, &bcm2835_clock_otp_data); + clks[BCM2835_CLOCK_TSENS] = + bcm2835_register_clock(cprman, &bcm2835_clock_tsens_data); + clks[BCM2835_CLOCK_VPU] = + bcm2835_register_clock(cprman, &bcm2835_clock_vpu_data); + clks[BCM2835_CLOCK_V3D] = + bcm2835_register_clock(cprman, &bcm2835_clock_v3d_data); + clks[BCM2835_CLOCK_ISP] = + bcm2835_register_clock(cprman, &bcm2835_clock_isp_data); + clks[BCM2835_CLOCK_H264] = + bcm2835_register_clock(cprman, &bcm2835_clock_h264_data); + clks[BCM2835_CLOCK_V3D] = + bcm2835_register_clock(cprman, &bcm2835_clock_v3d_data); + clks[BCM2835_CLOCK_SDRAM] = + bcm2835_register_clock(cprman, &bcm2835_clock_sdram_data); + clks[BCM2835_CLOCK_UART] = + bcm2835_register_clock(cprman, &bcm2835_clock_uart_data); + clks[BCM2835_CLOCK_VEC] = + bcm2835_register_clock(cprman, &bcm2835_clock_vec_data); + clks[BCM2835_CLOCK_HSM] = + bcm2835_register_clock(cprman, &bcm2835_clock_hsm_data); + clks[BCM2835_CLOCK_EMMC] = + bcm2835_register_clock(cprman, &bcm2835_clock_emmc_data); + + /* + * CM_PERIICTL (and CM_PERIACTL, CM_SYSCTL and CM_VPUCTL if + * you have the debug bit set in the power manager, which we + * don't bother exposing) are individual gates off of the + * non-stop vpu clock. + */ + clks[BCM2835_CLOCK_PERI_IMAGE] = + clk_register_gate(dev, "peri_image", "vpu", + CLK_IGNORE_UNUSED | CLK_SET_RATE_GATE, + cprman->regs + CM_PERIICTL, CM_GATE_BIT, + 0, &cprman->regs_lock); + + return of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, + &cprman->onecell); +} + +static const struct of_device_id bcm2835_clk_of_match[] = { + { .compatible = "brcm,bcm2835-cprman", }, + {} +}; +MODULE_DEVICE_TABLE(of, bcm2835_clk_of_match); + +static struct platform_driver bcm2835_clk_driver = { + .driver = { + .name = "bcm2835-clk", + .of_match_table = bcm2835_clk_of_match, + }, + .probe = bcm2835_clk_probe, +}; + +builtin_platform_driver(bcm2835_clk_driver); + +MODULE_AUTHOR("Eric Anholt <eric@anholt.net>"); +MODULE_DESCRIPTION("BCM2835 clock driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/bcm/clk-cygnus.c b/drivers/clk/bcm/clk-cygnus.c index 316c60337661..3a228b6d4fee 100644 --- a/drivers/clk/bcm/clk-cygnus.c +++ b/drivers/clk/bcm/clk-cygnus.c @@ -23,28 +23,30 @@ #include <dt-bindings/clock/bcm-cygnus.h> #include "clk-iproc.h" -#define reg_val(o, s, w) { .offset = o, .shift = s, .width = w, } +#define REG_VAL(o, s, w) { .offset = o, .shift = s, .width = w, } -#define aon_val(o, pw, ps, is) { .offset = o, .pwr_width = pw, \ +#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 SW_CTRL_VAL(o, s) { .offset = o, .shift = s, } -#define asiu_div_val(o, es, hs, hw, ls, lw) \ +#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, \ +#define RESET_VAL(o, rs, prs) { .offset = o, .reset_shift = rs, \ + .p_reset_shift = prs } + +#define DF_VAL(o, kis, kiw, kps, kpw, kas, kaw) { .offset = o, .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 VCO_CTRL_VAL(uo, lo) { .u_offset = uo, .l_offset = lo } -#define enable_val(o, es, hs, bs) { .offset = o, .enable_shift = es, \ +#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 } +#define ASIU_GATE_VAL(o, es) { .offset = o, .en_shift = es } static void __init cygnus_armpll_init(struct device_node *node) { @@ -55,52 +57,53 @@ 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), + .aon = AON_VAL(0x0, 2, 1, 0), + .reset = RESET_VAL(0x0, 11, 10), + .dig_filter = DF_VAL(0x0, 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), + .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), + .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), + .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), + .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), + .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), + .enable = ENABLE_VAL(0x4, 11, 5, 17), + .mdiv = REG_VAL(0x24, 20, 8), }, }; @@ -113,51 +116,52 @@ 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), + .aon = AON_VAL(0x0, 2, 5, 4), + .reset = RESET_VAL(0x0, 31, 30), + .dig_filter = DF_VAL(0x0, 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), + .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), + .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), + .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), + .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), + .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), + .enable = ENABLE_VAL(0x0, 12, 6, 18), + .mdiv = REG_VAL(0xc, 20, 8), }, }; @@ -189,52 +193,53 @@ static const struct iproc_pll_vco_param mipipll_vco_params[] = { 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), + .aon = AON_VAL(0x0, 4, 17, 16), + .asiu = ASIU_GATE_VAL(0x0, 3), + .reset = RESET_VAL(0x0, 11, 10), + .dig_filter = DF_VAL(0x0, 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), + .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), + .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), + .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), + .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), + .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), + .enable = ENABLE_VAL(0x4, 17, 11, 23), + .mdiv = REG_VAL(0x24, 20, 8), }, }; @@ -247,15 +252,15 @@ static void __init cygnus_mipipll_clk_init(struct device_node *node) 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), + [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), + [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) diff --git a/drivers/clk/bcm/clk-iproc-pll.c b/drivers/clk/bcm/clk-iproc-pll.c index 2dda4e8295a9..afd5891ac9e6 100644 --- a/drivers/clk/bcm/clk-iproc-pll.c +++ b/drivers/clk/bcm/clk-iproc-pll.c @@ -74,7 +74,8 @@ struct iproc_clk { }; struct iproc_pll { - void __iomem *pll_base; + void __iomem *status_base; + void __iomem *control_base; void __iomem *pwr_base; void __iomem *asiu_base; @@ -127,7 +128,7 @@ static int pll_wait_for_lock(struct iproc_pll *pll) const struct iproc_pll_ctrl *ctrl = pll->ctrl; for (i = 0; i < LOCK_DELAY; i++) { - u32 val = readl(pll->pll_base + ctrl->status.offset); + u32 val = readl(pll->status_base + ctrl->status.offset); if (val & (1 << ctrl->status.shift)) return 0; @@ -137,6 +138,18 @@ static int pll_wait_for_lock(struct iproc_pll *pll) return -EIO; } +static void iproc_pll_write(const struct iproc_pll *pll, void __iomem *base, + const u32 offset, u32 val) +{ + const struct iproc_pll_ctrl *ctrl = pll->ctrl; + + writel(val, base + offset); + + if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK && + (base == pll->status_base || base == pll->control_base))) + val = readl(base + offset); +} + static void __pll_disable(struct iproc_pll *pll) { const struct iproc_pll_ctrl *ctrl = pll->ctrl; @@ -145,17 +158,25 @@ static void __pll_disable(struct iproc_pll *pll) 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); + iproc_pll_write(pll, pll->asiu_base, ctrl->asiu.offset, val); + } + + if (ctrl->flags & IPROC_CLK_EMBED_PWRCTRL) { + val = readl(pll->control_base + ctrl->aon.offset); + val |= bit_mask(ctrl->aon.pwr_width) << ctrl->aon.pwr_shift; + iproc_pll_write(pll, pll->control_base, ctrl->aon.offset, val); } - /* 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); + if (pll->pwr_base) { + /* latch input value so core power can be shut down */ + val = readl(pll->pwr_base + ctrl->aon.offset); + val |= 1 << ctrl->aon.iso_shift; + iproc_pll_write(pll, pll->pwr_base, ctrl->aon.offset, val); - /* power down the core */ - val &= ~(bit_mask(ctrl->aon.pwr_width) << ctrl->aon.pwr_shift); - writel(val, pll->pwr_base + ctrl->aon.offset); + /* power down the core */ + val &= ~(bit_mask(ctrl->aon.pwr_width) << ctrl->aon.pwr_shift); + iproc_pll_write(pll, pll->pwr_base, ctrl->aon.offset, val); + } } static int __pll_enable(struct iproc_pll *pll) @@ -163,17 +184,25 @@ 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); + if (ctrl->flags & IPROC_CLK_EMBED_PWRCTRL) { + val = readl(pll->control_base + ctrl->aon.offset); + val &= ~(bit_mask(ctrl->aon.pwr_width) << ctrl->aon.pwr_shift); + iproc_pll_write(pll, pll->control_base, ctrl->aon.offset, val); + } + + if (pll->pwr_base) { + /* 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); + iproc_pll_write(pll, pll->pwr_base, ctrl->aon.offset, val); + } /* 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); + iproc_pll_write(pll, pll->asiu_base, ctrl->asiu.offset, val); } return 0; @@ -185,11 +214,9 @@ static void __pll_put_in_reset(struct iproc_pll *pll) 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 = readl(pll->control_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); + iproc_pll_write(pll, pll->control_base, reset->offset, val); } static void __pll_bring_out_reset(struct iproc_pll *pll, unsigned int kp, @@ -198,17 +225,19 @@ static void __pll_bring_out_reset(struct iproc_pll *pll, unsigned int kp, u32 val; const struct iproc_pll_ctrl *ctrl = pll->ctrl; const struct iproc_pll_reset_ctrl *reset = &ctrl->reset; + const struct iproc_pll_dig_filter_ctrl *dig_filter = &ctrl->dig_filter; + + val = readl(pll->control_base + dig_filter->offset); + val &= ~(bit_mask(dig_filter->ki_width) << dig_filter->ki_shift | + bit_mask(dig_filter->kp_width) << dig_filter->kp_shift | + bit_mask(dig_filter->ka_width) << dig_filter->ka_shift); + val |= ki << dig_filter->ki_shift | kp << dig_filter->kp_shift | + ka << dig_filter->ka_shift; + iproc_pll_write(pll, pll->control_base, dig_filter->offset, val); - 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 = readl(pll->control_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); + iproc_pll_write(pll, pll->control_base, reset->offset, val); } static int pll_set_rate(struct iproc_clk *clk, unsigned int rate_index, @@ -263,10 +292,9 @@ static int pll_set_rate(struct iproc_clk *clk, unsigned int rate_index, /* 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); + iproc_pll_write(pll, pll->control_base, ctrl->vco_ctrl.u_offset, 0); + + val = readl(pll->control_base + ctrl->vco_ctrl.l_offset); if (rate >= VCO_LOW && rate < VCO_MID) val |= (1 << PLL_VCO_LOW_SHIFT); @@ -276,36 +304,29 @@ static int pll_set_rate(struct iproc_clk *clk, unsigned int rate_index, 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); + iproc_pll_write(pll, pll->control_base, ctrl->vco_ctrl.l_offset, val); /* program integer part of NDIV */ - val = readl(pll->pll_base + ctrl->ndiv_int.offset); + val = readl(pll->control_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); + iproc_pll_write(pll, pll->control_base, ctrl->ndiv_int.offset, val); /* program fractional part of NDIV */ if (ctrl->flags & IPROC_CLK_PLL_HAS_NDIV_FRAC) { - val = readl(pll->pll_base + ctrl->ndiv_frac.offset); + val = readl(pll->control_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); + iproc_pll_write(pll, pll->control_base, ctrl->ndiv_frac.offset, + val); } /* program PDIV */ - val = readl(pll->pll_base + ctrl->pdiv.offset); + val = readl(pll->control_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); + iproc_pll_write(pll, pll->control_base, ctrl->pdiv.offset, val); __pll_bring_out_reset(pll, kp, ka, ki); @@ -345,14 +366,14 @@ static unsigned long iproc_pll_recalc_rate(struct clk_hw *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; + u64 ndiv, ndiv_int, ndiv_frac; + unsigned int pdiv; if (parent_rate == 0) return 0; /* PLL needs to be locked */ - val = readl(pll->pll_base + ctrl->status.offset); + val = readl(pll->status_base + ctrl->status.offset); if ((val & (1 << ctrl->status.shift)) == 0) { clk->rate = 0; return 0; @@ -363,25 +384,22 @@ static unsigned long iproc_pll_recalc_rate(struct clk_hw *hw, * * ((ndiv_int + ndiv_frac / 2^20) * (parent clock rate / pdiv) */ - val = readl(pll->pll_base + ctrl->ndiv_int.offset); + val = readl(pll->control_base + ctrl->ndiv_int.offset); ndiv_int = (val >> ctrl->ndiv_int.shift) & bit_mask(ctrl->ndiv_int.width); - ndiv = (u64)ndiv_int << ctrl->ndiv_int.shift; + ndiv = ndiv_int << 20; if (ctrl->flags & IPROC_CLK_PLL_HAS_NDIV_FRAC) { - val = readl(pll->pll_base + ctrl->ndiv_frac.offset); + val = readl(pll->control_base + ctrl->ndiv_frac.offset); ndiv_frac = (val >> ctrl->ndiv_frac.shift) & bit_mask(ctrl->ndiv_frac.width); - - if (ndiv_frac != 0) - ndiv = ((u64)ndiv_int << ctrl->ndiv_int.shift) | - ndiv_frac; + ndiv += ndiv_frac; } - val = readl(pll->pll_base + ctrl->pdiv.offset); + val = readl(pll->control_base + ctrl->pdiv.offset); pdiv = (val >> ctrl->pdiv.shift) & bit_mask(ctrl->pdiv.width); - clk->rate = (ndiv * parent_rate) >> ctrl->ndiv_int.shift; + clk->rate = (ndiv * parent_rate) >> 20; if (pdiv == 0) clk->rate *= 2; @@ -443,16 +461,14 @@ static int iproc_clk_enable(struct clk_hw *hw) u32 val; /* channel enable is active low */ - val = readl(pll->pll_base + ctrl->enable.offset); + val = readl(pll->control_base + ctrl->enable.offset); val &= ~(1 << ctrl->enable.enable_shift); - writel(val, pll->pll_base + ctrl->enable.offset); + iproc_pll_write(pll, pll->control_base, ctrl->enable.offset, val); /* also make sure channel is not held */ - val = readl(pll->pll_base + ctrl->enable.offset); + val = readl(pll->control_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); + iproc_pll_write(pll, pll->control_base, ctrl->enable.offset, val); return 0; } @@ -467,11 +483,9 @@ static void iproc_clk_disable(struct clk_hw *hw) if (ctrl->flags & IPROC_CLK_AON) return; - val = readl(pll->pll_base + ctrl->enable.offset); + val = readl(pll->control_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); + iproc_pll_write(pll, pll->control_base, ctrl->enable.offset, val); } static unsigned long iproc_clk_recalc_rate(struct clk_hw *hw, @@ -486,7 +500,7 @@ static unsigned long iproc_clk_recalc_rate(struct clk_hw *hw, if (parent_rate == 0) return 0; - val = readl(pll->pll_base + ctrl->mdiv.offset); + val = readl(pll->control_base + ctrl->mdiv.offset); mdiv = (val >> ctrl->mdiv.shift) & bit_mask(ctrl->mdiv.width); if (mdiv == 0) mdiv = 256; @@ -533,16 +547,14 @@ static int iproc_clk_set_rate(struct clk_hw *hw, unsigned long rate, if (div > 256) return -EINVAL; - val = readl(pll->pll_base + ctrl->mdiv.offset); + val = readl(pll->control_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); + iproc_pll_write(pll, pll->control_base, ctrl->mdiv.offset, val); clk->rate = parent_rate / div; return 0; @@ -567,11 +579,10 @@ static void iproc_pll_sw_cfg(struct iproc_pll *pll) if (ctrl->flags & IPROC_CLK_PLL_NEEDS_SW_CFG) { u32 val; - val = readl(pll->pll_base + ctrl->sw_ctrl.offset); + val = readl(pll->control_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); + iproc_pll_write(pll, pll->control_base, ctrl->sw_ctrl.offset, + val); } } @@ -606,13 +617,12 @@ void __init iproc_pll_clk_setup(struct device_node *node, if (WARN_ON(!pll->clks)) goto err_clks; - pll->pll_base = of_iomap(node, 0); - if (WARN_ON(!pll->pll_base)) + pll->control_base = of_iomap(node, 0); + if (WARN_ON(!pll->control_base)) goto err_pll_iomap; + /* Some SoCs do not require the pwr_base, thus failing is not fatal */ 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) { @@ -621,6 +631,16 @@ void __init iproc_pll_clk_setup(struct device_node *node, goto err_asiu_iomap; } + if (pll_ctrl->flags & IPROC_CLK_PLL_SPLIT_STAT_CTRL) { + /* Some SoCs have a split status/control. If this does not + * exist, assume they are unified. + */ + pll->status_base = of_iomap(node, 2); + if (!pll->status_base) + goto err_status_iomap; + } else + pll->status_base = pll->control_base; + /* initialize and register the PLL itself */ pll->ctrl = pll_ctrl; @@ -691,14 +711,18 @@ err_clk_register: clk_unregister(pll->clk_data.clks[i]); err_pll_register: + if (pll->status_base != pll->control_base) + iounmap(pll->status_base); + +err_status_iomap: if (pll->asiu_base) iounmap(pll->asiu_base); err_asiu_iomap: - iounmap(pll->pwr_base); + if (pll->pwr_base) + iounmap(pll->pwr_base); -err_pwr_iomap: - iounmap(pll->pll_base); + iounmap(pll->control_base); err_pll_iomap: kfree(pll->clks); diff --git a/drivers/clk/bcm/clk-iproc.h b/drivers/clk/bcm/clk-iproc.h index d834b7abd5c6..8988de70a98c 100644 --- a/drivers/clk/bcm/clk-iproc.h +++ b/drivers/clk/bcm/clk-iproc.h @@ -49,6 +49,18 @@ #define IPROC_CLK_PLL_NEEDS_SW_CFG BIT(4) /* + * Some PLLs use a different way to control clock power, via the PWRDWN bit in + * the PLL control register + */ +#define IPROC_CLK_EMBED_PWRCTRL BIT(5) + +/* + * Some PLLs have separate registers for Status and Control. Identify this to + * let the driver know if additional registers need to be used + */ +#define IPROC_CLK_PLL_SPLIT_STAT_CTRL BIT(6) + +/* * Parameters for VCO frequency configuration * * VCO frequency = @@ -88,12 +100,19 @@ struct iproc_pll_aon_pwr_ctrl { }; /* - * Control of the PLL reset, with Ki, Kp, and Ka parameters + * Control of the PLL reset */ struct iproc_pll_reset_ctrl { unsigned int offset; unsigned int reset_shift; unsigned int p_reset_shift; +}; + +/* + * Control of the Ki, Kp, and Ka parameters + */ +struct iproc_pll_dig_filter_ctrl { + unsigned int offset; unsigned int ki_shift; unsigned int ki_width; unsigned int kp_shift; @@ -123,6 +142,7 @@ struct iproc_pll_ctrl { struct iproc_pll_aon_pwr_ctrl aon; struct iproc_asiu_gate asiu; struct iproc_pll_reset_ctrl reset; + struct iproc_pll_dig_filter_ctrl dig_filter; struct iproc_pll_sw_ctrl sw_ctrl; struct iproc_clk_reg_op ndiv_int; struct iproc_clk_reg_op ndiv_frac; diff --git a/drivers/clk/bcm/clk-ns2.c b/drivers/clk/bcm/clk-ns2.c new file mode 100644 index 000000000000..a564e9248814 --- /dev/null +++ b/drivers/clk/bcm/clk-ns2.c @@ -0,0 +1,288 @@ +/* + * Copyright (C) 2015 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/of_address.h> + +#include <dt-bindings/clock/bcm-ns2.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 RESET_VAL(o, rs, prs) { .offset = o, .reset_shift = rs, \ + .p_reset_shift = prs } + +#define DF_VAL(o, kis, kiw, kps, kpw, kas, kaw) { .offset = o, .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 } + +static const struct iproc_pll_ctrl genpll_scr = { + .flags = IPROC_CLK_AON | IPROC_CLK_PLL_SPLIT_STAT_CTRL, + .aon = AON_VAL(0x0, 1, 15, 12), + .reset = RESET_VAL(0x4, 2, 1), + .dig_filter = DF_VAL(0x0, 9, 3, 5, 4, 2, 3), + .ndiv_int = REG_VAL(0x8, 4, 10), + .pdiv = REG_VAL(0x8, 0, 4), + .vco_ctrl = VCO_CTRL_VAL(0x10, 0xc), + .status = REG_VAL(0x0, 27, 1), +}; + + +static const struct iproc_clk_ctrl genpll_scr_clk[] = { + /* bypass_shift, the last value passed into ENABLE_VAL(), is not defined + * in NS2. However, it doesn't appear to be used anywhere, so setting + * it to 0. + */ + [BCM_NS2_GENPLL_SCR_SCR_CLK] = { + .channel = BCM_NS2_GENPLL_SCR_SCR_CLK, + .flags = IPROC_CLK_AON, + .enable = ENABLE_VAL(0x0, 18, 12, 0), + .mdiv = REG_VAL(0x18, 0, 8), + }, + [BCM_NS2_GENPLL_SCR_FS_CLK] = { + .channel = BCM_NS2_GENPLL_SCR_FS_CLK, + .flags = IPROC_CLK_AON, + .enable = ENABLE_VAL(0x0, 19, 13, 0), + .mdiv = REG_VAL(0x18, 8, 8), + }, + [BCM_NS2_GENPLL_SCR_AUDIO_CLK] = { + .channel = BCM_NS2_GENPLL_SCR_AUDIO_CLK, + .flags = IPROC_CLK_AON, + .enable = ENABLE_VAL(0x0, 20, 14, 0), + .mdiv = REG_VAL(0x14, 0, 8), + }, + [BCM_NS2_GENPLL_SCR_CH3_UNUSED] = { + .channel = BCM_NS2_GENPLL_SCR_CH3_UNUSED, + .flags = IPROC_CLK_AON, + .enable = ENABLE_VAL(0x0, 21, 15, 0), + .mdiv = REG_VAL(0x14, 8, 8), + }, + [BCM_NS2_GENPLL_SCR_CH4_UNUSED] = { + .channel = BCM_NS2_GENPLL_SCR_CH4_UNUSED, + .flags = IPROC_CLK_AON, + .enable = ENABLE_VAL(0x0, 22, 16, 0), + .mdiv = REG_VAL(0x14, 16, 8), + }, + [BCM_NS2_GENPLL_SCR_CH5_UNUSED] = { + .channel = BCM_NS2_GENPLL_SCR_CH5_UNUSED, + .flags = IPROC_CLK_AON, + .enable = ENABLE_VAL(0x0, 23, 17, 0), + .mdiv = REG_VAL(0x14, 24, 8), + }, +}; + +static void __init ns2_genpll_scr_clk_init(struct device_node *node) +{ + iproc_pll_clk_setup(node, &genpll_scr, NULL, 0, genpll_scr_clk, + ARRAY_SIZE(genpll_scr_clk)); +} +CLK_OF_DECLARE(ns2_genpll_src_clk, "brcm,ns2-genpll-scr", + ns2_genpll_scr_clk_init); + +static const struct iproc_pll_ctrl genpll_sw = { + .flags = IPROC_CLK_AON | IPROC_CLK_PLL_SPLIT_STAT_CTRL, + .aon = AON_VAL(0x0, 2, 9, 8), + .reset = RESET_VAL(0x4, 2, 1), + .dig_filter = DF_VAL(0x0, 9, 3, 5, 4, 2, 3), + .ndiv_int = REG_VAL(0x8, 4, 10), + .pdiv = REG_VAL(0x8, 0, 4), + .vco_ctrl = VCO_CTRL_VAL(0x10, 0xc), + .status = REG_VAL(0x0, 13, 1), +}; + +static const struct iproc_clk_ctrl genpll_sw_clk[] = { + /* bypass_shift, the last value passed into ENABLE_VAL(), is not defined + * in NS2. However, it doesn't appear to be used anywhere, so setting + * it to 0. + */ + [BCM_NS2_GENPLL_SW_RPE_CLK] = { + .channel = BCM_NS2_GENPLL_SW_RPE_CLK, + .flags = IPROC_CLK_AON, + .enable = ENABLE_VAL(0x0, 18, 12, 0), + .mdiv = REG_VAL(0x18, 0, 8), + }, + [BCM_NS2_GENPLL_SW_250_CLK] = { + .channel = BCM_NS2_GENPLL_SW_250_CLK, + .flags = IPROC_CLK_AON, + .enable = ENABLE_VAL(0x0, 19, 13, 0), + .mdiv = REG_VAL(0x18, 8, 8), + }, + [BCM_NS2_GENPLL_SW_NIC_CLK] = { + .channel = BCM_NS2_GENPLL_SW_NIC_CLK, + .flags = IPROC_CLK_AON, + .enable = ENABLE_VAL(0x0, 20, 14, 0), + .mdiv = REG_VAL(0x14, 0, 8), + }, + [BCM_NS2_GENPLL_SW_CHIMP_CLK] = { + .channel = BCM_NS2_GENPLL_SW_CHIMP_CLK, + .flags = IPROC_CLK_AON, + .enable = ENABLE_VAL(0x0, 21, 15, 0), + .mdiv = REG_VAL(0x14, 8, 8), + }, + [BCM_NS2_GENPLL_SW_PORT_CLK] = { + .channel = BCM_NS2_GENPLL_SW_PORT_CLK, + .flags = IPROC_CLK_AON, + .enable = ENABLE_VAL(0x0, 22, 16, 0), + .mdiv = REG_VAL(0x14, 16, 8), + }, + [BCM_NS2_GENPLL_SW_SDIO_CLK] = { + .channel = BCM_NS2_GENPLL_SW_SDIO_CLK, + .flags = IPROC_CLK_AON, + .enable = ENABLE_VAL(0x0, 23, 17, 0), + .mdiv = REG_VAL(0x14, 24, 8), + }, +}; + +static void __init ns2_genpll_sw_clk_init(struct device_node *node) +{ + iproc_pll_clk_setup(node, &genpll_sw, NULL, 0, genpll_sw_clk, + ARRAY_SIZE(genpll_sw_clk)); +} +CLK_OF_DECLARE(ns2_genpll_sw_clk, "brcm,ns2-genpll-sw", + ns2_genpll_sw_clk_init); + +static const struct iproc_pll_ctrl lcpll_ddr = { + .flags = IPROC_CLK_AON | IPROC_CLK_PLL_SPLIT_STAT_CTRL, + .aon = AON_VAL(0x0, 2, 1, 0), + .reset = RESET_VAL(0x4, 2, 1), + .dig_filter = DF_VAL(0x0, 9, 3, 5, 4, 1, 4), + .ndiv_int = REG_VAL(0x8, 4, 10), + .pdiv = REG_VAL(0x8, 0, 4), + .vco_ctrl = VCO_CTRL_VAL(0x10, 0xc), + .status = REG_VAL(0x0, 0, 1), +}; + +static const struct iproc_clk_ctrl lcpll_ddr_clk[] = { + /* bypass_shift, the last value passed into ENABLE_VAL(), is not defined + * in NS2. However, it doesn't appear to be used anywhere, so setting + * it to 0. + */ + [BCM_NS2_LCPLL_DDR_PCIE_SATA_USB_CLK] = { + .channel = BCM_NS2_LCPLL_DDR_PCIE_SATA_USB_CLK, + .flags = IPROC_CLK_AON, + .enable = ENABLE_VAL(0x0, 18, 12, 0), + .mdiv = REG_VAL(0x14, 0, 8), + }, + [BCM_NS2_LCPLL_DDR_DDR_CLK] = { + .channel = BCM_NS2_LCPLL_DDR_DDR_CLK, + .flags = IPROC_CLK_AON, + .enable = ENABLE_VAL(0x0, 19, 13, 0), + .mdiv = REG_VAL(0x14, 8, 8), + }, + [BCM_NS2_LCPLL_DDR_CH2_UNUSED] = { + .channel = BCM_NS2_LCPLL_DDR_CH2_UNUSED, + .flags = IPROC_CLK_AON, + .enable = ENABLE_VAL(0x0, 20, 14, 0), + .mdiv = REG_VAL(0x10, 0, 8), + }, + [BCM_NS2_LCPLL_DDR_CH3_UNUSED] = { + .channel = BCM_NS2_LCPLL_DDR_CH3_UNUSED, + .flags = IPROC_CLK_AON, + .enable = ENABLE_VAL(0x0, 21, 15, 0), + .mdiv = REG_VAL(0x10, 8, 8), + }, + [BCM_NS2_LCPLL_DDR_CH4_UNUSED] = { + .channel = BCM_NS2_LCPLL_DDR_CH4_UNUSED, + .flags = IPROC_CLK_AON, + .enable = ENABLE_VAL(0x0, 22, 16, 0), + .mdiv = REG_VAL(0x10, 16, 8), + }, + [BCM_NS2_LCPLL_DDR_CH5_UNUSED] = { + .channel = BCM_NS2_LCPLL_DDR_CH5_UNUSED, + .flags = IPROC_CLK_AON, + .enable = ENABLE_VAL(0x0, 23, 17, 0), + .mdiv = REG_VAL(0x10, 24, 8), + }, +}; + +static void __init ns2_lcpll_ddr_clk_init(struct device_node *node) +{ + iproc_pll_clk_setup(node, &lcpll_ddr, NULL, 0, lcpll_ddr_clk, + ARRAY_SIZE(lcpll_ddr_clk)); +} +CLK_OF_DECLARE(ns2_lcpll_ddr_clk, "brcm,ns2-lcpll-ddr", + ns2_lcpll_ddr_clk_init); + +static const struct iproc_pll_ctrl lcpll_ports = { + .flags = IPROC_CLK_AON | IPROC_CLK_PLL_SPLIT_STAT_CTRL, + .aon = AON_VAL(0x0, 2, 5, 4), + .reset = RESET_VAL(0x4, 2, 1), + .dig_filter = DF_VAL(0x0, 9, 3, 5, 4, 1, 4), + .ndiv_int = REG_VAL(0x8, 4, 10), + .pdiv = REG_VAL(0x8, 0, 4), + .vco_ctrl = VCO_CTRL_VAL(0x10, 0xc), + .status = REG_VAL(0x0, 0, 1), +}; + +static const struct iproc_clk_ctrl lcpll_ports_clk[] = { + /* bypass_shift, the last value passed into ENABLE_VAL(), is not defined + * in NS2. However, it doesn't appear to be used anywhere, so setting + * it to 0. + */ + [BCM_NS2_LCPLL_PORTS_WAN_CLK] = { + .channel = BCM_NS2_LCPLL_PORTS_WAN_CLK, + .flags = IPROC_CLK_AON, + .enable = ENABLE_VAL(0x0, 18, 12, 0), + .mdiv = REG_VAL(0x14, 0, 8), + }, + [BCM_NS2_LCPLL_PORTS_RGMII_CLK] = { + .channel = BCM_NS2_LCPLL_PORTS_RGMII_CLK, + .flags = IPROC_CLK_AON, + .enable = ENABLE_VAL(0x0, 19, 13, 0), + .mdiv = REG_VAL(0x14, 8, 8), + }, + [BCM_NS2_LCPLL_PORTS_CH2_UNUSED] = { + .channel = BCM_NS2_LCPLL_PORTS_CH2_UNUSED, + .flags = IPROC_CLK_AON, + .enable = ENABLE_VAL(0x0, 20, 14, 0), + .mdiv = REG_VAL(0x10, 0, 8), + }, + [BCM_NS2_LCPLL_PORTS_CH3_UNUSED] = { + .channel = BCM_NS2_LCPLL_PORTS_CH3_UNUSED, + .flags = IPROC_CLK_AON, + .enable = ENABLE_VAL(0x0, 21, 15, 0), + .mdiv = REG_VAL(0x10, 8, 8), + }, + [BCM_NS2_LCPLL_PORTS_CH4_UNUSED] = { + .channel = BCM_NS2_LCPLL_PORTS_CH4_UNUSED, + .flags = IPROC_CLK_AON, + .enable = ENABLE_VAL(0x0, 22, 16, 0), + .mdiv = REG_VAL(0x10, 16, 8), + }, + [BCM_NS2_LCPLL_PORTS_CH5_UNUSED] = { + .channel = BCM_NS2_LCPLL_PORTS_CH5_UNUSED, + .flags = IPROC_CLK_AON, + .enable = ENABLE_VAL(0x0, 23, 17, 0), + .mdiv = REG_VAL(0x10, 24, 8), + }, +}; + +static void __init ns2_lcpll_ports_clk_init(struct device_node *node) +{ + iproc_pll_clk_setup(node, &lcpll_ports, NULL, 0, lcpll_ports_clk, + ARRAY_SIZE(lcpll_ports_clk)); +} +CLK_OF_DECLARE(ns2_lcpll_ports_clk, "brcm,ns2-lcpll-ports", + ns2_lcpll_ports_clk_init); diff --git a/drivers/clk/bcm/clk-nsp.c b/drivers/clk/bcm/clk-nsp.c new file mode 100644 index 000000000000..cf66f640a47d --- /dev/null +++ b/drivers/clk/bcm/clk-nsp.c @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2015 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/of_address.h> + +#include <dt-bindings/clock/bcm-nsp.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 RESET_VAL(o, rs, prs) { .offset = o, .reset_shift = rs, \ + .p_reset_shift = prs } + +#define DF_VAL(o, kis, kiw, kps, kpw, kas, kaw) { .offset = o, .ki_shift = kis,\ + .ki_width = kiw, .kp_shift = kps, .kp_width = kpw, .ka_shift = kas, \ + .ka_width = kaw } + +#define ENABLE_VAL(o, es, hs, bs) { .offset = o, .enable_shift = es, \ + .hold_shift = hs, .bypass_shift = bs } + +static void __init nsp_armpll_init(struct device_node *node) +{ + iproc_armpll_setup(node); +} +CLK_OF_DECLARE(nsp_armpll, "brcm,nsp-armpll", nsp_armpll_init); + +static const struct iproc_pll_ctrl genpll = { + .flags = IPROC_CLK_PLL_HAS_NDIV_FRAC | IPROC_CLK_EMBED_PWRCTRL, + .aon = AON_VAL(0x0, 1, 12, 0), + .reset = RESET_VAL(0x0, 11, 10), + .dig_filter = DF_VAL(0x0, 4, 3, 0, 4, 7, 3), + .ndiv_int = REG_VAL(0x14, 20, 10), + .ndiv_frac = REG_VAL(0x14, 0, 20), + .pdiv = REG_VAL(0x18, 24, 3), + .status = REG_VAL(0x20, 12, 1), +}; + +static const struct iproc_clk_ctrl genpll_clk[] = { + [BCM_NSP_GENPLL_PHY_CLK] = { + .channel = BCM_NSP_GENPLL_PHY_CLK, + .flags = IPROC_CLK_AON, + .enable = ENABLE_VAL(0x4, 12, 6, 18), + .mdiv = REG_VAL(0x18, 16, 8), + }, + [BCM_NSP_GENPLL_ENET_SW_CLK] = { + .channel = BCM_NSP_GENPLL_ENET_SW_CLK, + .flags = IPROC_CLK_AON, + .enable = ENABLE_VAL(0x4, 13, 7, 19), + .mdiv = REG_VAL(0x18, 8, 8), + }, + [BCM_NSP_GENPLL_USB_PHY_REF_CLK] = { + .channel = BCM_NSP_GENPLL_USB_PHY_REF_CLK, + .flags = IPROC_CLK_AON, + .enable = ENABLE_VAL(0x4, 14, 8, 20), + .mdiv = REG_VAL(0x18, 0, 8), + }, + [BCM_NSP_GENPLL_IPROCFAST_CLK] = { + .channel = BCM_NSP_GENPLL_IPROCFAST_CLK, + .flags = IPROC_CLK_AON, + .enable = ENABLE_VAL(0x4, 15, 9, 21), + .mdiv = REG_VAL(0x1c, 16, 8), + }, + [BCM_NSP_GENPLL_SATA1_CLK] = { + .channel = BCM_NSP_GENPLL_SATA1_CLK, + .flags = IPROC_CLK_AON, + .enable = ENABLE_VAL(0x4, 16, 10, 22), + .mdiv = REG_VAL(0x1c, 8, 8), + }, + [BCM_NSP_GENPLL_SATA2_CLK] = { + .channel = BCM_NSP_GENPLL_SATA2_CLK, + .flags = IPROC_CLK_AON, + .enable = ENABLE_VAL(0x4, 17, 11, 23), + .mdiv = REG_VAL(0x1c, 0, 8), + }, +}; + +static void __init nsp_genpll_clk_init(struct device_node *node) +{ + iproc_pll_clk_setup(node, &genpll, NULL, 0, genpll_clk, + ARRAY_SIZE(genpll_clk)); +} +CLK_OF_DECLARE(nsp_genpll_clk, "brcm,nsp-genpll", nsp_genpll_clk_init); + +static const struct iproc_pll_ctrl lcpll0 = { + .flags = IPROC_CLK_PLL_HAS_NDIV_FRAC | IPROC_CLK_EMBED_PWRCTRL, + .aon = AON_VAL(0x0, 1, 24, 0), + .reset = RESET_VAL(0x0, 23, 22), + .dig_filter = DF_VAL(0x0, 16, 3, 12, 4, 19, 4), + .ndiv_int = REG_VAL(0x4, 20, 8), + .ndiv_frac = REG_VAL(0x4, 0, 20), + .pdiv = REG_VAL(0x4, 28, 3), + .status = REG_VAL(0x10, 12, 1), +}; + +static const struct iproc_clk_ctrl lcpll0_clk[] = { + [BCM_NSP_LCPLL0_PCIE_PHY_REF_CLK] = { + .channel = BCM_NSP_LCPLL0_PCIE_PHY_REF_CLK, + .flags = IPROC_CLK_AON, + .enable = ENABLE_VAL(0x0, 6, 3, 9), + .mdiv = REG_VAL(0x8, 24, 8), + }, + [BCM_NSP_LCPLL0_SDIO_CLK] = { + .channel = BCM_NSP_LCPLL0_SDIO_CLK, + .flags = IPROC_CLK_AON, + .enable = ENABLE_VAL(0x0, 7, 4, 10), + .mdiv = REG_VAL(0x8, 16, 8), + }, + [BCM_NSP_LCPLL0_DDR_PHY_CLK] = { + .channel = BCM_NSP_LCPLL0_DDR_PHY_CLK, + .flags = IPROC_CLK_AON, + .enable = ENABLE_VAL(0x0, 8, 5, 11), + .mdiv = REG_VAL(0x8, 8, 8), + }, +}; + +static void __init nsp_lcpll0_clk_init(struct device_node *node) +{ + iproc_pll_clk_setup(node, &lcpll0, NULL, 0, lcpll0_clk, + ARRAY_SIZE(lcpll0_clk)); +} +CLK_OF_DECLARE(nsp_lcpll0_clk, "brcm,nsp-lcpll0", nsp_lcpll0_clk_init); |