diff options
Diffstat (limited to 'drivers/clk')
29 files changed, 3485 insertions, 433 deletions
diff --git a/drivers/clk/bcm/Kconfig b/drivers/clk/bcm/Kconfig index 8c83977a7dc4..784f12c72365 100644 --- a/drivers/clk/bcm/Kconfig +++ b/drivers/clk/bcm/Kconfig @@ -1,4 +1,15 @@ # SPDX-License-Identifier: GPL-2.0-only + +config CLK_BCM2711_DVP + tristate "Broadcom BCM2711 DVP support" + depends on ARCH_BCM2835 ||COMPILE_TEST + depends on COMMON_CLK + default ARCH_BCM2835 + select RESET_SIMPLE + help + Enable common clock framework support for the Broadcom BCM2711 + DVP Controller. + config CLK_BCM2835 bool "Broadcom BCM2835 clock support" depends on ARCH_BCM2835 || ARCH_BRCMSTB || COMPILE_TEST diff --git a/drivers/clk/bcm/Makefile b/drivers/clk/bcm/Makefile index 0070ddf6cdd2..edb66b44cb27 100644 --- a/drivers/clk/bcm/Makefile +++ b/drivers/clk/bcm/Makefile @@ -6,6 +6,7 @@ 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_CLK_BCM2711_DVP) += clk-bcm2711-dvp.o obj-$(CONFIG_CLK_BCM2835) += clk-bcm2835.o obj-$(CONFIG_CLK_BCM2835) += clk-bcm2835-aux.o obj-$(CONFIG_CLK_RASPBERRYPI) += clk-raspberrypi.o diff --git a/drivers/clk/bcm/clk-bcm2711-dvp.c b/drivers/clk/bcm/clk-bcm2711-dvp.c new file mode 100644 index 000000000000..8333e20dc9d2 --- /dev/null +++ b/drivers/clk/bcm/clk-bcm2711-dvp.c @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// Copyright 2020 Cerno + +#include <linux/clk-provider.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/reset-controller.h> +#include <linux/reset/reset-simple.h> + +#define DVP_HT_RPI_SW_INIT 0x04 +#define DVP_HT_RPI_MISC_CONFIG 0x08 + +#define NR_CLOCKS 2 +#define NR_RESETS 6 + +struct clk_dvp { + struct clk_hw_onecell_data *data; + struct reset_simple_data reset; +}; + +static const struct clk_parent_data clk_dvp_parent = { + .index = 0, +}; + +static int clk_dvp_probe(struct platform_device *pdev) +{ + struct clk_hw_onecell_data *data; + struct resource *res; + struct clk_dvp *dvp; + void __iomem *base; + int ret; + + dvp = devm_kzalloc(&pdev->dev, sizeof(*dvp), GFP_KERNEL); + if (!dvp) + return -ENOMEM; + platform_set_drvdata(pdev, dvp); + + dvp->data = devm_kzalloc(&pdev->dev, + struct_size(dvp->data, hws, NR_CLOCKS), + GFP_KERNEL); + if (!dvp->data) + return -ENOMEM; + data = dvp->data; + + base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(base)) + return PTR_ERR(base); + + dvp->reset.rcdev.owner = THIS_MODULE; + dvp->reset.rcdev.nr_resets = NR_RESETS; + dvp->reset.rcdev.ops = &reset_simple_ops; + dvp->reset.rcdev.of_node = pdev->dev.of_node; + dvp->reset.membase = base + DVP_HT_RPI_SW_INIT; + spin_lock_init(&dvp->reset.lock); + + ret = devm_reset_controller_register(&pdev->dev, &dvp->reset.rcdev); + if (ret) + return ret; + + data->hws[0] = clk_hw_register_gate_parent_data(&pdev->dev, + "hdmi0-108MHz", + &clk_dvp_parent, 0, + base + DVP_HT_RPI_MISC_CONFIG, 3, + CLK_GATE_SET_TO_DISABLE, + &dvp->reset.lock); + if (IS_ERR(data->hws[0])) + return PTR_ERR(data->hws[0]); + + data->hws[1] = clk_hw_register_gate_parent_data(&pdev->dev, + "hdmi1-108MHz", + &clk_dvp_parent, 0, + base + DVP_HT_RPI_MISC_CONFIG, 4, + CLK_GATE_SET_TO_DISABLE, + &dvp->reset.lock); + if (IS_ERR(data->hws[1])) { + ret = PTR_ERR(data->hws[1]); + goto unregister_clk0; + } + + data->num = NR_CLOCKS; + ret = of_clk_add_hw_provider(pdev->dev.of_node, of_clk_hw_onecell_get, + data); + if (ret) + goto unregister_clk1; + + return 0; + +unregister_clk1: + clk_hw_unregister_gate(data->hws[1]); + +unregister_clk0: + clk_hw_unregister_gate(data->hws[0]); + return ret; +}; + +static int clk_dvp_remove(struct platform_device *pdev) +{ + struct clk_dvp *dvp = platform_get_drvdata(pdev); + struct clk_hw_onecell_data *data = dvp->data; + + clk_hw_unregister_gate(data->hws[1]); + clk_hw_unregister_gate(data->hws[0]); + + return 0; +} + +static const struct of_device_id clk_dvp_dt_ids[] = { + { .compatible = "brcm,brcm2711-dvp", }, + { /* sentinel */ } +}; + +static struct platform_driver clk_dvp_driver = { + .probe = clk_dvp_probe, + .remove = clk_dvp_remove, + .driver = { + .name = "brcm2711-dvp", + .of_match_table = clk_dvp_dt_ids, + }, +}; +module_platform_driver(clk_dvp_driver); + +MODULE_AUTHOR("Maxime Ripard <maxime@cerno.tech>"); +MODULE_DESCRIPTION("BCM2711 DVP clock driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c index 6bb7efa12037..3439bc65bb4e 100644 --- a/drivers/clk/bcm/clk-bcm2835.c +++ b/drivers/clk/bcm/clk-bcm2835.c @@ -314,6 +314,7 @@ struct bcm2835_cprman { struct device *dev; void __iomem *regs; spinlock_t regs_lock; /* spinlock for all clocks */ + unsigned int soc; /* * Real names of cprman clock parents looked up through @@ -421,6 +422,7 @@ struct bcm2835_pll_data { u32 reference_enable_mask; /* Bit in CM_LOCK to indicate when the PLL has locked. */ u32 lock_mask; + u32 flags; const struct bcm2835_pll_ana_bits *ana; @@ -525,6 +527,20 @@ static int bcm2835_pll_is_on(struct clk_hw *hw) A2W_PLL_CTRL_PRST_DISABLE; } +static u32 bcm2835_pll_get_prediv_mask(struct bcm2835_cprman *cprman, + const struct bcm2835_pll_data *data) +{ + /* + * On BCM2711 there isn't a pre-divisor available in the PLL feedback + * loop. Bits 13:14 of ANA1 (PLLA,PLLB,PLLC,PLLD) have been re-purposed + * for to for VCO RANGE bits. + */ + if (cprman->soc & SOC_BCM2711) + return 0; + + return data->ana->fb_prediv_mask; +} + static void bcm2835_pll_choose_ndiv_and_fdiv(unsigned long rate, unsigned long parent_rate, u32 *ndiv, u32 *fdiv) @@ -582,7 +598,7 @@ static unsigned long bcm2835_pll_get_rate(struct clk_hw *hw, 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; + bcm2835_pll_get_prediv_mask(cprman, data); if (using_prediv) { ndiv *= 2; @@ -665,6 +681,7 @@ static int bcm2835_pll_set_rate(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; + u32 prediv_mask = bcm2835_pll_get_prediv_mask(cprman, data); bool was_using_prediv, use_fb_prediv, do_ana_setup_first; u32 ndiv, fdiv, a2w_ctl; u32 ana[4]; @@ -682,7 +699,7 @@ static int bcm2835_pll_set_rate(struct clk_hw *hw, 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; + was_using_prediv = ana[1] & prediv_mask; ana[0] &= ~data->ana->mask0; ana[0] |= data->ana->set0; @@ -692,10 +709,10 @@ static int bcm2835_pll_set_rate(struct clk_hw *hw, ana[3] |= data->ana->set3; if (was_using_prediv && !use_fb_prediv) { - ana[1] &= ~data->ana->fb_prediv_mask; + ana[1] &= ~prediv_mask; do_ana_setup_first = true; } else if (!was_using_prediv && use_fb_prediv) { - ana[1] |= data->ana->fb_prediv_mask; + ana[1] |= prediv_mask; do_ana_setup_first = false; } else { do_ana_setup_first = true; @@ -1310,7 +1327,7 @@ static struct clk_hw *bcm2835_register_pll(struct bcm2835_cprman *cprman, init.num_parents = 1; init.name = pll_data->name; init.ops = &bcm2835_pll_clk_ops; - init.flags = CLK_IGNORE_UNUSED; + init.flags = pll_data->flags | CLK_IGNORE_UNUSED; pll = kzalloc(sizeof(*pll), GFP_KERNEL); if (!pll) @@ -1684,10 +1701,33 @@ static const struct bcm2835_clk_desc clk_desc_array[] = { .fixed_divider = 1, .flags = CLK_SET_RATE_PARENT), - /* - * PLLB is used for the ARM's clock. Controlled by firmware, see - * clk-raspberrypi.c. - */ + /* PLLB is used for the ARM's clock. */ + [BCM2835_PLLB] = REGISTER_PLL( + SOC_ALL, + .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, + .flags = CLK_GET_RATE_NOCACHE), + [BCM2835_PLLB_ARM] = REGISTER_PLL_DIV( + SOC_ALL, + .name = "pllb_arm", + .source_pll = "pllb", + .cm_reg = CM_PLLB, + .a2w_reg = A2W_PLLB_ARM, + .load_mask = CM_PLLB_LOADARM, + .hold_mask = CM_PLLB_HOLDARM, + .fixed_divider = 1, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE), /* * PLLC is the core PLL, used to drive the core VPU clock. @@ -2238,6 +2278,7 @@ static int bcm2835_clk_probe(struct platform_device *pdev) platform_set_drvdata(pdev, cprman); cprman->onecell.num = asize; + cprman->soc = pdata->soc; hws = cprman->onecell.hws; for (i = 0; i < asize; i++) { diff --git a/drivers/clk/bcm/clk-bcm63xx-gate.c b/drivers/clk/bcm/clk-bcm63xx-gate.c index 98e884957db8..89297c57881e 100644 --- a/drivers/clk/bcm/clk-bcm63xx-gate.c +++ b/drivers/clk/bcm/clk-bcm63xx-gate.c @@ -6,6 +6,14 @@ #include <linux/of_device.h> #include <linux/platform_device.h> +#include <dt-bindings/clock/bcm3368-clock.h> +#include <dt-bindings/clock/bcm6318-clock.h> +#include <dt-bindings/clock/bcm6328-clock.h> +#include <dt-bindings/clock/bcm6358-clock.h> +#include <dt-bindings/clock/bcm6362-clock.h> +#include <dt-bindings/clock/bcm6368-clock.h> +#include <dt-bindings/clock/bcm63268-clock.h> + struct clk_bcm63xx_table_entry { const char * const name; u8 bit; @@ -20,126 +28,458 @@ struct clk_bcm63xx_hw { }; static const struct clk_bcm63xx_table_entry bcm3368_clocks[] = { - { .name = "mac", .bit = 3, }, - { .name = "tc", .bit = 5, }, - { .name = "us_top", .bit = 6, }, - { .name = "ds_top", .bit = 7, }, - { .name = "acm", .bit = 8, }, - { .name = "spi", .bit = 9, }, - { .name = "usbs", .bit = 10, }, - { .name = "bmu", .bit = 11, }, - { .name = "pcm", .bit = 12, }, - { .name = "ntp", .bit = 13, }, - { .name = "acp_b", .bit = 14, }, - { .name = "acp_a", .bit = 15, }, - { .name = "emusb", .bit = 17, }, - { .name = "enet0", .bit = 18, }, - { .name = "enet1", .bit = 19, }, - { .name = "usbsu", .bit = 20, }, - { .name = "ephy", .bit = 21, }, - { }, + { + .name = "mac", + .bit = BCM3368_CLK_MAC, + }, { + .name = "tc", + .bit = BCM3368_CLK_TC, + }, { + .name = "us_top", + .bit = BCM3368_CLK_US_TOP, + }, { + .name = "ds_top", + .bit = BCM3368_CLK_DS_TOP, + }, { + .name = "acm", + .bit = BCM3368_CLK_ACM, + }, { + .name = "spi", + .bit = BCM3368_CLK_SPI, + }, { + .name = "usbs", + .bit = BCM3368_CLK_USBS, + }, { + .name = "bmu", + .bit = BCM3368_CLK_BMU, + }, { + .name = "pcm", + .bit = BCM3368_CLK_PCM, + }, { + .name = "ntp", + .bit = BCM3368_CLK_NTP, + }, { + .name = "acp_b", + .bit = BCM3368_CLK_ACP_B, + }, { + .name = "acp_a", + .bit = BCM3368_CLK_ACP_A, + }, { + .name = "emusb", + .bit = BCM3368_CLK_EMUSB, + }, { + .name = "enet0", + .bit = BCM3368_CLK_ENET0, + }, { + .name = "enet1", + .bit = BCM3368_CLK_ENET1, + }, { + .name = "usbsu", + .bit = BCM3368_CLK_USBSU, + }, { + .name = "ephy", + .bit = BCM3368_CLK_EPHY, + }, { + /* sentinel */ + }, +}; + +static const struct clk_bcm63xx_table_entry bcm6318_clocks[] = { + { + .name = "adsl_asb", + .bit = BCM6318_CLK_ADSL_ASB, + }, { + .name = "usb_asb", + .bit = BCM6318_CLK_USB_ASB, + }, { + .name = "mips_asb", + .bit = BCM6318_CLK_MIPS_ASB, + }, { + .name = "pcie_asb", + .bit = BCM6318_CLK_PCIE_ASB, + }, { + .name = "phymips_asb", + .bit = BCM6318_CLK_PHYMIPS_ASB, + }, { + .name = "robosw_asb", + .bit = BCM6318_CLK_ROBOSW_ASB, + }, { + .name = "sar_asb", + .bit = BCM6318_CLK_SAR_ASB, + }, { + .name = "sdr_asb", + .bit = BCM6318_CLK_SDR_ASB, + }, { + .name = "swreg_asb", + .bit = BCM6318_CLK_SWREG_ASB, + }, { + .name = "periph_asb", + .bit = BCM6318_CLK_PERIPH_ASB, + }, { + .name = "cpubus160", + .bit = BCM6318_CLK_CPUBUS160, + }, { + .name = "adsl", + .bit = BCM6318_CLK_ADSL, + }, { + .name = "sar125", + .bit = BCM6318_CLK_SAR125, + }, { + .name = "mips", + .bit = BCM6318_CLK_MIPS, + .flags = CLK_IS_CRITICAL, + }, { + .name = "pcie", + .bit = BCM6318_CLK_PCIE, + }, { + .name = "robosw250", + .bit = BCM6318_CLK_ROBOSW250, + }, { + .name = "robosw025", + .bit = BCM6318_CLK_ROBOSW025, + }, { + .name = "sdr", + .bit = BCM6318_CLK_SDR, + .flags = CLK_IS_CRITICAL, + }, { + .name = "usbd", + .bit = BCM6318_CLK_USBD, + }, { + .name = "hsspi", + .bit = BCM6318_CLK_HSSPI, + }, { + .name = "pcie25", + .bit = BCM6318_CLK_PCIE25, + }, { + .name = "phymips", + .bit = BCM6318_CLK_PHYMIPS, + }, { + .name = "afe", + .bit = BCM6318_CLK_AFE, + }, { + .name = "qproc", + .bit = BCM6318_CLK_QPROC, + }, { + /* sentinel */ + }, +}; + +static const struct clk_bcm63xx_table_entry bcm6318_ubus_clocks[] = { + { + .name = "adsl-ubus", + .bit = BCM6318_UCLK_ADSL, + }, { + .name = "arb-ubus", + .bit = BCM6318_UCLK_ARB, + .flags = CLK_IS_CRITICAL, + }, { + .name = "mips-ubus", + .bit = BCM6318_UCLK_MIPS, + .flags = CLK_IS_CRITICAL, + }, { + .name = "pcie-ubus", + .bit = BCM6318_UCLK_PCIE, + }, { + .name = "periph-ubus", + .bit = BCM6318_UCLK_PERIPH, + .flags = CLK_IS_CRITICAL, + }, { + .name = "phymips-ubus", + .bit = BCM6318_UCLK_PHYMIPS, + }, { + .name = "robosw-ubus", + .bit = BCM6318_UCLK_ROBOSW, + }, { + .name = "sar-ubus", + .bit = BCM6318_UCLK_SAR, + }, { + .name = "sdr-ubus", + .bit = BCM6318_UCLK_SDR, + }, { + .name = "usb-ubus", + .bit = BCM6318_UCLK_USB, + }, { + /* sentinel */ + }, }; static const struct clk_bcm63xx_table_entry bcm6328_clocks[] = { - { .name = "phy_mips", .bit = 0, }, - { .name = "adsl_qproc", .bit = 1, }, - { .name = "adsl_afe", .bit = 2, }, - { .name = "adsl", .bit = 3, }, - { .name = "mips", .bit = 4, .flags = CLK_IS_CRITICAL, }, - { .name = "sar", .bit = 5, }, - { .name = "pcm", .bit = 6, }, - { .name = "usbd", .bit = 7, }, - { .name = "usbh", .bit = 8, }, - { .name = "hsspi", .bit = 9, }, - { .name = "pcie", .bit = 10, }, - { .name = "robosw", .bit = 11, }, - { }, + { + .name = "phy_mips", + .bit = BCM6328_CLK_PHYMIPS, + }, { + .name = "adsl_qproc", + .bit = BCM6328_CLK_ADSL_QPROC, + }, { + .name = "adsl_afe", + .bit = BCM6328_CLK_ADSL_AFE, + }, { + .name = "adsl", + .bit = BCM6328_CLK_ADSL, + }, { + .name = "mips", + .bit = BCM6328_CLK_MIPS, + .flags = CLK_IS_CRITICAL, + }, { + .name = "sar", + .bit = BCM6328_CLK_SAR, + }, { + .name = "pcm", + .bit = BCM6328_CLK_PCM, + }, { + .name = "usbd", + .bit = BCM6328_CLK_USBD, + }, { + .name = "usbh", + .bit = BCM6328_CLK_USBH, + }, { + .name = "hsspi", + .bit = BCM6328_CLK_HSSPI, + }, { + .name = "pcie", + .bit = BCM6328_CLK_PCIE, + }, { + .name = "robosw", + .bit = BCM6328_CLK_ROBOSW, + }, { + /* sentinel */ + }, }; static const struct clk_bcm63xx_table_entry bcm6358_clocks[] = { - { .name = "enet", .bit = 4, }, - { .name = "adslphy", .bit = 5, }, - { .name = "pcm", .bit = 8, }, - { .name = "spi", .bit = 9, }, - { .name = "usbs", .bit = 10, }, - { .name = "sar", .bit = 11, }, - { .name = "emusb", .bit = 17, }, - { .name = "enet0", .bit = 18, }, - { .name = "enet1", .bit = 19, }, - { .name = "usbsu", .bit = 20, }, - { .name = "ephy", .bit = 21, }, - { }, + { + .name = "enet", + .bit = BCM6358_CLK_ENET, + }, { + .name = "adslphy", + .bit = BCM6358_CLK_ADSLPHY, + }, { + .name = "pcm", + .bit = BCM6358_CLK_PCM, + }, { + .name = "spi", + .bit = BCM6358_CLK_SPI, + }, { + .name = "usbs", + .bit = BCM6358_CLK_USBS, + }, { + .name = "sar", + .bit = BCM6358_CLK_SAR, + }, { + .name = "emusb", + .bit = BCM6358_CLK_EMUSB, + }, { + .name = "enet0", + .bit = BCM6358_CLK_ENET0, + }, { + .name = "enet1", + .bit = BCM6358_CLK_ENET1, + }, { + .name = "usbsu", + .bit = BCM6358_CLK_USBSU, + }, { + .name = "ephy", + .bit = BCM6358_CLK_EPHY, + }, { + /* sentinel */ + }, }; static const struct clk_bcm63xx_table_entry bcm6362_clocks[] = { - { .name = "adsl_qproc", .bit = 1, }, - { .name = "adsl_afe", .bit = 2, }, - { .name = "adsl", .bit = 3, }, - { .name = "mips", .bit = 4, .flags = CLK_IS_CRITICAL, }, - { .name = "wlan_ocp", .bit = 5, }, - { .name = "swpkt_usb", .bit = 7, }, - { .name = "swpkt_sar", .bit = 8, }, - { .name = "sar", .bit = 9, }, - { .name = "robosw", .bit = 10, }, - { .name = "pcm", .bit = 11, }, - { .name = "usbd", .bit = 12, }, - { .name = "usbh", .bit = 13, }, - { .name = "ipsec", .bit = 14, }, - { .name = "spi", .bit = 15, }, - { .name = "hsspi", .bit = 16, }, - { .name = "pcie", .bit = 17, }, - { .name = "fap", .bit = 18, }, - { .name = "phymips", .bit = 19, }, - { .name = "nand", .bit = 20, }, - { }, + { + .name = "adsl_qproc", + .bit = BCM6362_CLK_ADSL_QPROC, + }, { + .name = "adsl_afe", + .bit = BCM6362_CLK_ADSL_AFE, + }, { + .name = "adsl", + .bit = BCM6362_CLK_ADSL, + }, { + .name = "mips", + .bit = BCM6362_CLK_MIPS, + .flags = CLK_IS_CRITICAL, + }, { + .name = "wlan_ocp", + .bit = BCM6362_CLK_WLAN_OCP, + }, { + .name = "swpkt_usb", + .bit = BCM6362_CLK_SWPKT_USB, + }, { + .name = "swpkt_sar", + .bit = BCM6362_CLK_SWPKT_SAR, + }, { + .name = "sar", + .bit = BCM6362_CLK_SAR, + }, { + .name = "robosw", + .bit = BCM6362_CLK_ROBOSW, + }, { + .name = "pcm", + .bit = BCM6362_CLK_PCM, + }, { + .name = "usbd", + .bit = BCM6362_CLK_USBD, + }, { + .name = "usbh", + .bit = BCM6362_CLK_USBH, + }, { + .name = "ipsec", + .bit = BCM6362_CLK_IPSEC, + }, { + .name = "spi", + .bit = BCM6362_CLK_SPI, + }, { + .name = "hsspi", + .bit = BCM6362_CLK_HSSPI, + }, { + .name = "pcie", + .bit = BCM6362_CLK_PCIE, + }, { + .name = "fap", + .bit = BCM6362_CLK_FAP, + }, { + .name = "phymips", + .bit = BCM6362_CLK_PHYMIPS, + }, { + .name = "nand", + .bit = BCM6362_CLK_NAND, + }, { + /* sentinel */ + }, }; static const struct clk_bcm63xx_table_entry bcm6368_clocks[] = { - { .name = "vdsl_qproc", .bit = 2, }, - { .name = "vdsl_afe", .bit = 3, }, - { .name = "vdsl_bonding", .bit = 4, }, - { .name = "vdsl", .bit = 5, }, - { .name = "phymips", .bit = 6, }, - { .name = "swpkt_usb", .bit = 7, }, - { .name = "swpkt_sar", .bit = 8, }, - { .name = "spi", .bit = 9, }, - { .name = "usbd", .bit = 10, }, - { .name = "sar", .bit = 11, }, - { .name = "robosw", .bit = 12, }, - { .name = "utopia", .bit = 13, }, - { .name = "pcm", .bit = 14, }, - { .name = "usbh", .bit = 15, }, - { .name = "disable_gless", .bit = 16, }, - { .name = "nand", .bit = 17, }, - { .name = "ipsec", .bit = 18, }, - { }, + { + .name = "vdsl_qproc", + .bit = BCM6368_CLK_VDSL_QPROC, + }, { + .name = "vdsl_afe", + .bit = BCM6368_CLK_VDSL_AFE, + }, { + .name = "vdsl_bonding", + .bit = BCM6368_CLK_VDSL_BONDING, + }, { + .name = "vdsl", + .bit = BCM6368_CLK_VDSL, + }, { + .name = "phymips", + .bit = BCM6368_CLK_PHYMIPS, + }, { + .name = "swpkt_usb", + .bit = BCM6368_CLK_SWPKT_USB, + }, { + .name = "swpkt_sar", + .bit = BCM6368_CLK_SWPKT_SAR, + }, { + .name = "spi", + .bit = BCM6368_CLK_SPI, + }, { + .name = "usbd", + .bit = BCM6368_CLK_USBD, + }, { + .name = "sar", + .bit = BCM6368_CLK_SAR, + }, { + .name = "robosw", + .bit = BCM6368_CLK_ROBOSW, + }, { + .name = "utopia", + .bit = BCM6368_CLK_UTOPIA, + }, { + .name = "pcm", + .bit = BCM6368_CLK_PCM, + }, { + .name = "usbh", + .bit = BCM6368_CLK_USBH, + }, { + .name = "disable_gless", + .bit = BCM6368_CLK_DIS_GLESS, + }, { + .name = "nand", + .bit = BCM6368_CLK_NAND, + }, { + .name = "ipsec", + .bit = BCM6368_CLK_IPSEC, + }, { + /* sentinel */ + }, }; static const struct clk_bcm63xx_table_entry bcm63268_clocks[] = { - { .name = "disable_gless", .bit = 0, }, - { .name = "vdsl_qproc", .bit = 1, }, - { .name = "vdsl_afe", .bit = 2, }, - { .name = "vdsl", .bit = 3, }, - { .name = "mips", .bit = 4, .flags = CLK_IS_CRITICAL, }, - { .name = "wlan_ocp", .bit = 5, }, - { .name = "dect", .bit = 6, }, - { .name = "fap0", .bit = 7, }, - { .name = "fap1", .bit = 8, }, - { .name = "sar", .bit = 9, }, - { .name = "robosw", .bit = 10, }, - { .name = "pcm", .bit = 11, }, - { .name = "usbd", .bit = 12, }, - { .name = "usbh", .bit = 13, }, - { .name = "ipsec", .bit = 14, }, - { .name = "spi", .bit = 15, }, - { .name = "hsspi", .bit = 16, }, - { .name = "pcie", .bit = 17, }, - { .name = "phymips", .bit = 18, }, - { .name = "gmac", .bit = 19, }, - { .name = "nand", .bit = 20, }, - { .name = "tbus", .bit = 27, }, - { .name = "robosw250", .bit = 31, }, - { }, + { + .name = "disable_gless", + .bit = BCM63268_CLK_DIS_GLESS, + }, { + .name = "vdsl_qproc", + .bit = BCM63268_CLK_VDSL_QPROC, + }, { + .name = "vdsl_afe", + .bit = BCM63268_CLK_VDSL_AFE, + }, { + .name = "vdsl", + .bit = BCM63268_CLK_VDSL, + }, { + .name = "mips", + .bit = BCM63268_CLK_MIPS, + .flags = CLK_IS_CRITICAL, + }, { + .name = "wlan_ocp", + .bit = BCM63268_CLK_WLAN_OCP, + }, { + .name = "dect", + .bit = BCM63268_CLK_DECT, + }, { + .name = "fap0", + .bit = BCM63268_CLK_FAP0, + }, { + .name = "fap1", + .bit = BCM63268_CLK_FAP1, + }, { + .name = "sar", + .bit = BCM63268_CLK_SAR, + }, { + .name = "robosw", + .bit = BCM63268_CLK_ROBOSW, + }, { + .name = "pcm", + .bit = BCM63268_CLK_PCM, + }, { + .name = "usbd", + .bit = BCM63268_CLK_USBD, + }, { + .name = "usbh", + .bit = BCM63268_CLK_USBH, + }, { + .name = "ipsec", + .bit = BCM63268_CLK_IPSEC, + }, { + .name = "spi", + .bit = BCM63268_CLK_SPI, + }, { + .name = "hsspi", + .bit = BCM63268_CLK_HSSPI, + }, { + .name = "pcie", + .bit = BCM63268_CLK_PCIE, + }, { + .name = "phymips", + .bit = BCM63268_CLK_PHYMIPS, + }, { + .name = "gmac", + .bit = BCM63268_CLK_GMAC, + }, { + .name = "nand", + .bit = BCM63268_CLK_NAND, + }, { + .name = "tbus", + .bit = BCM63268_CLK_TBUS, + }, { + .name = "robosw250", + .bit = BCM63268_CLK_ROBOSW250, + }, { + /* sentinel */ + }, }; static int clk_bcm63xx_probe(struct platform_device *pdev) @@ -155,6 +495,7 @@ static int clk_bcm63xx_probe(struct platform_device *pdev) for (entry = table; entry->name; entry++) maxbit = max_t(u8, maxbit, entry->bit); + maxbit++; hw = devm_kzalloc(&pdev->dev, struct_size(hw, data.hws, maxbit), GFP_KERNEL); @@ -217,6 +558,8 @@ static int clk_bcm63xx_remove(struct platform_device *pdev) static const struct of_device_id clk_bcm63xx_dt_ids[] = { { .compatible = "brcm,bcm3368-clocks", .data = &bcm3368_clocks, }, + { .compatible = "brcm,bcm6318-clocks", .data = &bcm6318_clocks, }, + { .compatible = "brcm,bcm6318-ubus-clocks", .data = &bcm6318_ubus_clocks, }, { .compatible = "brcm,bcm6328-clocks", .data = &bcm6328_clocks, }, { .compatible = "brcm,bcm6358-clocks", .data = &bcm6358_clocks, }, { .compatible = "brcm,bcm6362-clocks", .data = &bcm6362_clocks, }, diff --git a/drivers/clk/bcm/clk-raspberrypi.c b/drivers/clk/bcm/clk-raspberrypi.c index 1654fd0eedc9..5cc82954e1ce 100644 --- a/drivers/clk/bcm/clk-raspberrypi.c +++ b/drivers/clk/bcm/clk-raspberrypi.c @@ -18,30 +18,56 @@ #include <soc/bcm2835/raspberrypi-firmware.h> -#define RPI_FIRMWARE_ARM_CLK_ID 0x00000003 +enum rpi_firmware_clk_id { + RPI_FIRMWARE_EMMC_CLK_ID = 1, + RPI_FIRMWARE_UART_CLK_ID, + RPI_FIRMWARE_ARM_CLK_ID, + RPI_FIRMWARE_CORE_CLK_ID, + RPI_FIRMWARE_V3D_CLK_ID, + RPI_FIRMWARE_H264_CLK_ID, + RPI_FIRMWARE_ISP_CLK_ID, + RPI_FIRMWARE_SDRAM_CLK_ID, + RPI_FIRMWARE_PIXEL_CLK_ID, + RPI_FIRMWARE_PWM_CLK_ID, + RPI_FIRMWARE_HEVC_CLK_ID, + RPI_FIRMWARE_EMMC2_CLK_ID, + RPI_FIRMWARE_M2MC_CLK_ID, + RPI_FIRMWARE_PIXEL_BVB_CLK_ID, + RPI_FIRMWARE_NUM_CLK_ID, +}; + +static char *rpi_firmware_clk_names[] = { + [RPI_FIRMWARE_EMMC_CLK_ID] = "emmc", + [RPI_FIRMWARE_UART_CLK_ID] = "uart", + [RPI_FIRMWARE_ARM_CLK_ID] = "arm", + [RPI_FIRMWARE_CORE_CLK_ID] = "core", + [RPI_FIRMWARE_V3D_CLK_ID] = "v3d", + [RPI_FIRMWARE_H264_CLK_ID] = "h264", + [RPI_FIRMWARE_ISP_CLK_ID] = "isp", + [RPI_FIRMWARE_SDRAM_CLK_ID] = "sdram", + [RPI_FIRMWARE_PIXEL_CLK_ID] = "pixel", + [RPI_FIRMWARE_PWM_CLK_ID] = "pwm", + [RPI_FIRMWARE_HEVC_CLK_ID] = "hevc", + [RPI_FIRMWARE_EMMC2_CLK_ID] = "emmc2", + [RPI_FIRMWARE_M2MC_CLK_ID] = "m2mc", + [RPI_FIRMWARE_PIXEL_BVB_CLK_ID] = "pixel-bvb", +}; #define RPI_FIRMWARE_STATE_ENABLE_BIT BIT(0) #define RPI_FIRMWARE_STATE_WAIT_BIT BIT(1) -/* - * Even though the firmware interface alters 'pllb' the frequencies are - * provided as per 'pllb_arm'. We need to scale before passing them trough. - */ -#define RPI_FIRMWARE_PLLB_ARM_DIV_RATE 2 - -#define A2W_PLL_FRAC_BITS 20 - struct raspberrypi_clk { struct device *dev; struct rpi_firmware *firmware; struct platform_device *cpufreq; +}; - unsigned long min_rate; - unsigned long max_rate; +struct raspberrypi_clk_data { + struct clk_hw hw; - struct clk_hw pllb; - struct clk_hw *pllb_arm; - struct clk_lookup *pllb_arm_lookup; + unsigned int id; + + struct raspberrypi_clk *rpi; }; /* @@ -64,11 +90,12 @@ struct raspberrypi_firmware_prop { __le32 disable_turbo; } __packed; -static int raspberrypi_clock_property(struct rpi_firmware *firmware, u32 tag, - u32 clk, u32 *val) +static int raspberrypi_clock_property(struct rpi_firmware *firmware, + const struct raspberrypi_clk_data *data, + u32 tag, u32 *val) { struct raspberrypi_firmware_prop msg = { - .id = cpu_to_le32(clk), + .id = cpu_to_le32(data->id), .val = cpu_to_le32(*val), .disable_turbo = cpu_to_le32(1), }; @@ -83,16 +110,16 @@ static int raspberrypi_clock_property(struct rpi_firmware *firmware, u32 tag, return 0; } -static int raspberrypi_fw_pll_is_on(struct clk_hw *hw) +static int raspberrypi_fw_is_prepared(struct clk_hw *hw) { - struct raspberrypi_clk *rpi = container_of(hw, struct raspberrypi_clk, - pllb); + struct raspberrypi_clk_data *data = + container_of(hw, struct raspberrypi_clk_data, hw); + struct raspberrypi_clk *rpi = data->rpi; u32 val = 0; int ret; - ret = raspberrypi_clock_property(rpi->firmware, - RPI_FIRMWARE_GET_CLOCK_STATE, - RPI_FIRMWARE_ARM_CLK_ID, &val); + ret = raspberrypi_clock_property(rpi->firmware, data, + RPI_FIRMWARE_GET_CLOCK_STATE, &val); if (ret) return 0; @@ -100,36 +127,34 @@ static int raspberrypi_fw_pll_is_on(struct clk_hw *hw) } -static unsigned long raspberrypi_fw_pll_get_rate(struct clk_hw *hw, - unsigned long parent_rate) +static unsigned long raspberrypi_fw_get_rate(struct clk_hw *hw, + unsigned long parent_rate) { - struct raspberrypi_clk *rpi = container_of(hw, struct raspberrypi_clk, - pllb); + struct raspberrypi_clk_data *data = + container_of(hw, struct raspberrypi_clk_data, hw); + struct raspberrypi_clk *rpi = data->rpi; u32 val = 0; int ret; - ret = raspberrypi_clock_property(rpi->firmware, - RPI_FIRMWARE_GET_CLOCK_RATE, - RPI_FIRMWARE_ARM_CLK_ID, - &val); + ret = raspberrypi_clock_property(rpi->firmware, data, + RPI_FIRMWARE_GET_CLOCK_RATE, &val); if (ret) return ret; - return val * RPI_FIRMWARE_PLLB_ARM_DIV_RATE; + return val; } -static int raspberrypi_fw_pll_set_rate(struct clk_hw *hw, unsigned long rate, - unsigned long parent_rate) +static int raspberrypi_fw_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) { - struct raspberrypi_clk *rpi = container_of(hw, struct raspberrypi_clk, - pllb); - u32 new_rate = rate / RPI_FIRMWARE_PLLB_ARM_DIV_RATE; + struct raspberrypi_clk_data *data = + container_of(hw, struct raspberrypi_clk_data, hw); + struct raspberrypi_clk *rpi = data->rpi; + u32 _rate = rate; int ret; - ret = raspberrypi_clock_property(rpi->firmware, - RPI_FIRMWARE_SET_CLOCK_RATE, - RPI_FIRMWARE_ARM_CLK_ID, - &new_rate); + ret = raspberrypi_clock_property(rpi->firmware, data, + RPI_FIRMWARE_SET_CLOCK_RATE, &_rate); if (ret) dev_err_ratelimited(rpi->dev, "Failed to change %s frequency: %d", clk_hw_get_name(hw), ret); @@ -137,111 +162,128 @@ static int raspberrypi_fw_pll_set_rate(struct clk_hw *hw, unsigned long rate, return ret; } -/* - * Sadly there is no firmware rate rounding interface. We borrowed it from - * clk-bcm2835. - */ -static int raspberrypi_pll_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) +static int raspberrypi_fw_dumb_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { - struct raspberrypi_clk *rpi = container_of(hw, struct raspberrypi_clk, - pllb); - u64 div, final_rate; - u32 ndiv, fdiv; - - /* We can't use req->rate directly as it would overflow */ - final_rate = clamp(req->rate, rpi->min_rate, rpi->max_rate); - - div = (u64)final_rate << A2W_PLL_FRAC_BITS; - do_div(div, req->best_parent_rate); - - ndiv = div >> A2W_PLL_FRAC_BITS; - fdiv = div & ((1 << A2W_PLL_FRAC_BITS) - 1); - - final_rate = ((u64)req->best_parent_rate * - ((ndiv << A2W_PLL_FRAC_BITS) + fdiv)); - - req->rate = final_rate >> A2W_PLL_FRAC_BITS; - + /* + * The firmware will do the rounding but that isn't part of + * the interface with the firmware, so we just do our best + * here. + */ + req->rate = clamp(req->rate, req->min_rate, req->max_rate); return 0; } -static const struct clk_ops raspberrypi_firmware_pll_clk_ops = { - .is_prepared = raspberrypi_fw_pll_is_on, - .recalc_rate = raspberrypi_fw_pll_get_rate, - .set_rate = raspberrypi_fw_pll_set_rate, - .determine_rate = raspberrypi_pll_determine_rate, +static const struct clk_ops raspberrypi_firmware_clk_ops = { + .is_prepared = raspberrypi_fw_is_prepared, + .recalc_rate = raspberrypi_fw_get_rate, + .determine_rate = raspberrypi_fw_dumb_determine_rate, + .set_rate = raspberrypi_fw_set_rate, }; -static int raspberrypi_register_pllb(struct raspberrypi_clk *rpi) +static struct clk_hw *raspberrypi_clk_register(struct raspberrypi_clk *rpi, + unsigned int parent, + unsigned int id) { - u32 min_rate = 0, max_rate = 0; - struct clk_init_data init; + struct raspberrypi_clk_data *data; + struct clk_init_data init = {}; + u32 min_rate, max_rate; int ret; - memset(&init, 0, sizeof(init)); + data = devm_kzalloc(rpi->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return ERR_PTR(-ENOMEM); + data->rpi = rpi; + data->id = id; - /* All of the PLLs derive from the external oscillator. */ - init.parent_names = (const char *[]){ "osc" }; - init.num_parents = 1; - init.name = "pllb"; - init.ops = &raspberrypi_firmware_pll_clk_ops; - init.flags = CLK_GET_RATE_NOCACHE | CLK_IGNORE_UNUSED; + init.name = devm_kasprintf(rpi->dev, GFP_KERNEL, + "fw-clk-%s", + rpi_firmware_clk_names[id]); + init.ops = &raspberrypi_firmware_clk_ops; + init.flags = CLK_GET_RATE_NOCACHE; - /* Get min & max rates set by the firmware */ - ret = raspberrypi_clock_property(rpi->firmware, + data->hw.init = &init; + + ret = raspberrypi_clock_property(rpi->firmware, data, RPI_FIRMWARE_GET_MIN_CLOCK_RATE, - RPI_FIRMWARE_ARM_CLK_ID, &min_rate); if (ret) { - dev_err(rpi->dev, "Failed to get %s min freq: %d\n", - init.name, ret); - return ret; + dev_err(rpi->dev, "Failed to get clock %d min freq: %d", + id, ret); + return ERR_PTR(ret); } - ret = raspberrypi_clock_property(rpi->firmware, + ret = raspberrypi_clock_property(rpi->firmware, data, RPI_FIRMWARE_GET_MAX_CLOCK_RATE, - RPI_FIRMWARE_ARM_CLK_ID, &max_rate); if (ret) { - dev_err(rpi->dev, "Failed to get %s max freq: %d\n", - init.name, ret); - return ret; - } - - if (!min_rate || !max_rate) { - dev_err(rpi->dev, "Unexpected frequency range: min %u, max %u\n", - min_rate, max_rate); - return -EINVAL; + dev_err(rpi->dev, "Failed to get clock %d max freq: %d\n", + id, ret); + return ERR_PTR(ret); } - dev_info(rpi->dev, "CPU frequency range: min %u, max %u\n", - min_rate, max_rate); + ret = devm_clk_hw_register(rpi->dev, &data->hw); + if (ret) + return ERR_PTR(ret); - rpi->min_rate = min_rate * RPI_FIRMWARE_PLLB_ARM_DIV_RATE; - rpi->max_rate = max_rate * RPI_FIRMWARE_PLLB_ARM_DIV_RATE; + clk_hw_set_rate_range(&data->hw, min_rate, max_rate); - rpi->pllb.init = &init; + if (id == RPI_FIRMWARE_ARM_CLK_ID) { + ret = devm_clk_hw_register_clkdev(rpi->dev, &data->hw, + NULL, "cpu0"); + if (ret) { + dev_err(rpi->dev, "Failed to initialize clkdev\n"); + return ERR_PTR(ret); + } + } - return devm_clk_hw_register(rpi->dev, &rpi->pllb); + return &data->hw; } -static int raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi) +struct rpi_firmware_get_clocks_response { + u32 parent; + u32 id; +}; + +static int raspberrypi_discover_clocks(struct raspberrypi_clk *rpi, + struct clk_hw_onecell_data *data) { - rpi->pllb_arm = clk_hw_register_fixed_factor(rpi->dev, - "pllb_arm", "pllb", - CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, - 1, 2); - if (IS_ERR(rpi->pllb_arm)) { - dev_err(rpi->dev, "Failed to initialize pllb_arm\n"); - return PTR_ERR(rpi->pllb_arm); - } + struct rpi_firmware_get_clocks_response *clks; + int ret; - rpi->pllb_arm_lookup = clkdev_hw_create(rpi->pllb_arm, NULL, "cpu0"); - if (!rpi->pllb_arm_lookup) { - dev_err(rpi->dev, "Failed to initialize pllb_arm_lookup\n"); - clk_hw_unregister_fixed_factor(rpi->pllb_arm); + clks = devm_kcalloc(rpi->dev, + sizeof(*clks), RPI_FIRMWARE_NUM_CLK_ID, + GFP_KERNEL); + if (!clks) return -ENOMEM; + + ret = rpi_firmware_property(rpi->firmware, RPI_FIRMWARE_GET_CLOCKS, + clks, + sizeof(*clks) * RPI_FIRMWARE_NUM_CLK_ID); + if (ret) + return ret; + + while (clks->id) { + struct clk_hw *hw; + + switch (clks->id) { + case RPI_FIRMWARE_ARM_CLK_ID: + case RPI_FIRMWARE_CORE_CLK_ID: + case RPI_FIRMWARE_M2MC_CLK_ID: + case RPI_FIRMWARE_V3D_CLK_ID: + hw = raspberrypi_clk_register(rpi, clks->parent, + clks->id); + if (IS_ERR(hw)) + return PTR_ERR(hw); + + data->hws[clks->id] = hw; + data->num = clks->id + 1; + fallthrough; + + default: + clks++; + break; + } } return 0; @@ -249,14 +291,23 @@ static int raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi) static int raspberrypi_clk_probe(struct platform_device *pdev) { + struct clk_hw_onecell_data *clk_data; struct device_node *firmware_node; struct device *dev = &pdev->dev; struct rpi_firmware *firmware; struct raspberrypi_clk *rpi; int ret; - firmware_node = of_find_compatible_node(NULL, NULL, - "raspberrypi,bcm2835-firmware"); + /* + * We can be probed either through the an old-fashioned + * platform device registration or through a DT node that is a + * child of the firmware node. Handle both cases. + */ + if (dev->of_node) + firmware_node = of_get_parent(dev->of_node); + else + firmware_node = of_find_compatible_node(NULL, NULL, + "raspberrypi,bcm2835-firmware"); if (!firmware_node) { dev_err(dev, "Missing firmware node\n"); return -ENOENT; @@ -275,13 +326,18 @@ static int raspberrypi_clk_probe(struct platform_device *pdev) rpi->firmware = firmware; platform_set_drvdata(pdev, rpi); - ret = raspberrypi_register_pllb(rpi); - if (ret) { - dev_err(dev, "Failed to initialize pllb, %d\n", ret); + clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, + RPI_FIRMWARE_NUM_CLK_ID), + GFP_KERNEL); + if (!clk_data) + return -ENOMEM; + + ret = raspberrypi_discover_clocks(rpi, clk_data); + if (ret) return ret; - } - ret = raspberrypi_register_pllb_arm(rpi); + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, + clk_data); if (ret) return ret; @@ -300,9 +356,16 @@ static int raspberrypi_clk_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id raspberrypi_clk_match[] = { + { .compatible = "raspberrypi,firmware-clocks" }, + { }, +}; +MODULE_DEVICE_TABLE(of, raspberrypi_clk_match); + static struct platform_driver raspberrypi_clk_driver = { .driver = { .name = "raspberrypi-clk", + .of_match_table = raspberrypi_clk_match, }, .probe = raspberrypi_clk_probe, .remove = raspberrypi_clk_remove, diff --git a/drivers/clk/clk-versaclock5.c b/drivers/clk/clk-versaclock5.c index fa96659f8023..c90460e7ef21 100644 --- a/drivers/clk/clk-versaclock5.c +++ b/drivers/clk/clk-versaclock5.c @@ -24,6 +24,8 @@ #include <linux/regmap.h> #include <linux/slab.h> +#include <dt-bindings/clk/versaclock.h> + /* VersaClock5 registers */ #define VC5_OTP_CONTROL 0x00 @@ -89,6 +91,28 @@ /* Clock control register for clock 1,2 */ #define VC5_CLK_OUTPUT_CFG(idx, n) (0x60 + ((idx) * 0x2) + (n)) +#define VC5_CLK_OUTPUT_CFG0_CFG_SHIFT 5 +#define VC5_CLK_OUTPUT_CFG0_CFG_MASK GENMASK(7, VC5_CLK_OUTPUT_CFG0_CFG_SHIFT) + +#define VC5_CLK_OUTPUT_CFG0_CFG_LVPECL (VC5_LVPECL) +#define VC5_CLK_OUTPUT_CFG0_CFG_CMOS (VC5_CMOS) +#define VC5_CLK_OUTPUT_CFG0_CFG_HCSL33 (VC5_HCSL33) +#define VC5_CLK_OUTPUT_CFG0_CFG_LVDS (VC5_LVDS) +#define VC5_CLK_OUTPUT_CFG0_CFG_CMOS2 (VC5_CMOS2) +#define VC5_CLK_OUTPUT_CFG0_CFG_CMOSD (VC5_CMOSD) +#define VC5_CLK_OUTPUT_CFG0_CFG_HCSL25 (VC5_HCSL25) + +#define VC5_CLK_OUTPUT_CFG0_PWR_SHIFT 3 +#define VC5_CLK_OUTPUT_CFG0_PWR_MASK GENMASK(4, VC5_CLK_OUTPUT_CFG0_PWR_SHIFT) +#define VC5_CLK_OUTPUT_CFG0_PWR_18 (0<<VC5_CLK_OUTPUT_CFG0_PWR_SHIFT) +#define VC5_CLK_OUTPUT_CFG0_PWR_25 (2<<VC5_CLK_OUTPUT_CFG0_PWR_SHIFT) +#define VC5_CLK_OUTPUT_CFG0_PWR_33 (3<<VC5_CLK_OUTPUT_CFG0_PWR_SHIFT) +#define VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT 0 +#define VC5_CLK_OUTPUT_CFG0_SLEW_MASK GENMASK(1, VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT) +#define VC5_CLK_OUTPUT_CFG0_SLEW_80 (0<<VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT) +#define VC5_CLK_OUTPUT_CFG0_SLEW_85 (1<<VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT) +#define VC5_CLK_OUTPUT_CFG0_SLEW_90 (2<<VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT) +#define VC5_CLK_OUTPUT_CFG0_SLEW_100 (3<<VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT) #define VC5_CLK_OUTPUT_CFG1_EN_CLKBUF BIT(0) #define VC5_CLK_OE_SHDN 0x68 @@ -145,6 +169,14 @@ struct vc5_hw_data { unsigned int num; }; +struct vc5_out_data { + struct clk_hw hw; + struct vc5_driver_data *vc5; + unsigned int num; + unsigned int clk_output_cfg0; + unsigned int clk_output_cfg0_mask; +}; + struct vc5_driver_data { struct i2c_client *client; struct regmap *regmap; @@ -158,31 +190,7 @@ struct vc5_driver_data { struct clk_hw clk_pfd; struct vc5_hw_data clk_pll; struct vc5_hw_data clk_fod[VC5_MAX_FOD_NUM]; - struct vc5_hw_data clk_out[VC5_MAX_CLK_OUT_NUM]; -}; - -static const char * const vc5_mux_names[] = { - "mux" -}; - -static const char * const vc5_dbl_names[] = { - "dbl" -}; - -static const char * const vc5_pfd_names[] = { - "pfd" -}; - -static const char * const vc5_pll_names[] = { - "pll" -}; - -static const char * const vc5_fod_names[] = { - "fod0", "fod1", "fod2", "fod3", -}; - -static const char * const vc5_clk_out_names[] = { - "out0_sel_i2cb", "out1", "out2", "out3", "out4", + struct vc5_out_data clk_out[VC5_MAX_CLK_OUT_NUM]; }; /* @@ -565,7 +573,7 @@ static const struct clk_ops vc5_fod_ops = { static int vc5_clk_out_prepare(struct clk_hw *hw) { - struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw); + struct vc5_out_data *hwdata = container_of(hw, struct vc5_out_data, hw); struct vc5_driver_data *vc5 = hwdata->vc5; const u8 mask = VC5_OUT_DIV_CONTROL_SELB_NORM | VC5_OUT_DIV_CONTROL_SEL_EXT | @@ -591,12 +599,23 @@ static int vc5_clk_out_prepare(struct clk_hw *hw) regmap_update_bits(vc5->regmap, VC5_CLK_OUTPUT_CFG(hwdata->num, 1), VC5_CLK_OUTPUT_CFG1_EN_CLKBUF, VC5_CLK_OUTPUT_CFG1_EN_CLKBUF); + if (hwdata->clk_output_cfg0_mask) { + dev_dbg(&vc5->client->dev, "Update output %d mask 0x%0X val 0x%0X\n", + hwdata->num, hwdata->clk_output_cfg0_mask, + hwdata->clk_output_cfg0); + + regmap_update_bits(vc5->regmap, + VC5_CLK_OUTPUT_CFG(hwdata->num, 0), + hwdata->clk_output_cfg0_mask, + hwdata->clk_output_cfg0); + } + return 0; } static void vc5_clk_out_unprepare(struct clk_hw *hw) { - struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw); + struct vc5_out_data *hwdata = container_of(hw, struct vc5_out_data, hw); struct vc5_driver_data *vc5 = hwdata->vc5; /* Disable the clock buffer */ @@ -606,7 +625,7 @@ static void vc5_clk_out_unprepare(struct clk_hw *hw) static unsigned char vc5_clk_out_get_parent(struct clk_hw *hw) { - struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw); + struct vc5_out_data *hwdata = container_of(hw, struct vc5_out_data, hw); struct vc5_driver_data *vc5 = hwdata->vc5; const u8 mask = VC5_OUT_DIV_CONTROL_SELB_NORM | VC5_OUT_DIV_CONTROL_SEL_EXT | @@ -636,7 +655,7 @@ static unsigned char vc5_clk_out_get_parent(struct clk_hw *hw) static int vc5_clk_out_set_parent(struct clk_hw *hw, u8 index) { - struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw); + struct vc5_out_data *hwdata = container_of(hw, struct vc5_out_data, hw); struct vc5_driver_data *vc5 = hwdata->vc5; const u8 mask = VC5_OUT_DIV_CONTROL_RESET | VC5_OUT_DIV_CONTROL_SELB_NORM | @@ -690,10 +709,125 @@ static int vc5_map_index_to_output(const enum vc5_model model, } } +static int vc5_update_mode(struct device_node *np_output, + struct vc5_out_data *clk_out) +{ + u32 value; + + if (!of_property_read_u32(np_output, "idt,mode", &value)) { + clk_out->clk_output_cfg0_mask |= VC5_CLK_OUTPUT_CFG0_CFG_MASK; + switch (value) { + case VC5_CLK_OUTPUT_CFG0_CFG_LVPECL: + case VC5_CLK_OUTPUT_CFG0_CFG_CMOS: + case VC5_CLK_OUTPUT_CFG0_CFG_HCSL33: + case VC5_CLK_OUTPUT_CFG0_CFG_LVDS: + case VC5_CLK_OUTPUT_CFG0_CFG_CMOS2: + case VC5_CLK_OUTPUT_CFG0_CFG_CMOSD: + case VC5_CLK_OUTPUT_CFG0_CFG_HCSL25: + clk_out->clk_output_cfg0 |= + value << VC5_CLK_OUTPUT_CFG0_CFG_SHIFT; + break; + default: + return -EINVAL; + } + } + return 0; +} + +static int vc5_update_power(struct device_node *np_output, + struct vc5_out_data *clk_out) +{ + u32 value; + + if (!of_property_read_u32(np_output, + "idt,voltage-microvolts", &value)) { + clk_out->clk_output_cfg0_mask |= VC5_CLK_OUTPUT_CFG0_PWR_MASK; + switch (value) { + case 1800000: + clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_PWR_18; + break; + case 2500000: + clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_PWR_25; + break; + case 3300000: + clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_PWR_33; + break; + default: + return -EINVAL; + } + } + return 0; +} + +static int vc5_update_slew(struct device_node *np_output, + struct vc5_out_data *clk_out) +{ + u32 value; + + if (!of_property_read_u32(np_output, "idt,slew-percent", &value)) { + clk_out->clk_output_cfg0_mask |= VC5_CLK_OUTPUT_CFG0_SLEW_MASK; + switch (value) { + case 80: + clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_SLEW_80; + break; + case 85: + clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_SLEW_85; + break; + case 90: + clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_SLEW_90; + break; + case 100: + clk_out->clk_output_cfg0 |= + VC5_CLK_OUTPUT_CFG0_SLEW_100; + break; + default: + return -EINVAL; + } + } + return 0; +} + +static int vc5_get_output_config(struct i2c_client *client, + struct vc5_out_data *clk_out) +{ + struct device_node *np_output; + char *child_name; + int ret = 0; + + child_name = kasprintf(GFP_KERNEL, "OUT%d", clk_out->num + 1); + if (!child_name) + return -ENOMEM; + + np_output = of_get_child_by_name(client->dev.of_node, child_name); + kfree(child_name); + if (!np_output) + return 0; + + ret = vc5_update_mode(np_output, clk_out); + if (ret) + goto output_error; + + ret = vc5_update_power(np_output, clk_out); + if (ret) + goto output_error; + + ret = vc5_update_slew(np_output, clk_out); + +output_error: + if (ret) { + dev_err(&client->dev, + "Invalid clock output configuration OUT%d\n", + clk_out->num + 1); + } + + of_node_put(np_output); + + return ret; +} + static const struct of_device_id clk_vc5_of_match[]; -static int vc5_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int vc5_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct vc5_driver_data *vc5; struct clk_init_data init; @@ -702,7 +836,7 @@ static int vc5_probe(struct i2c_client *client, int ret; vc5 = devm_kzalloc(&client->dev, sizeof(*vc5), GFP_KERNEL); - if (vc5 == NULL) + if (!vc5) return -ENOMEM; i2c_set_clientdata(client, vc5); @@ -742,7 +876,7 @@ static int vc5_probe(struct i2c_client *client, if (!IS_ERR(vc5->pin_clkin)) { vc5->clk_mux_ins |= VC5_MUX_IN_CLKIN; parent_names[init.num_parents++] = - __clk_get_name(vc5->pin_clkin); + __clk_get_name(vc5->pin_clkin); } if (!init.num_parents) { @@ -750,115 +884,116 @@ static int vc5_probe(struct i2c_client *client, return -EINVAL; } - init.name = vc5_mux_names[0]; + init.name = kasprintf(GFP_KERNEL, "%pOFn.mux", client->dev.of_node); init.ops = &vc5_mux_ops; init.flags = 0; init.parent_names = parent_names; vc5->clk_mux.init = &init; ret = devm_clk_hw_register(&client->dev, &vc5->clk_mux); - if (ret) { - dev_err(&client->dev, "unable to register %s\n", init.name); - goto err_clk; - } + if (ret) + goto err_clk_register; + kfree(init.name); /* clock framework made a copy of the name */ if (vc5->chip_info->flags & VC5_HAS_PFD_FREQ_DBL) { /* Register frequency doubler */ memset(&init, 0, sizeof(init)); - init.name = vc5_dbl_names[0]; + init.name = kasprintf(GFP_KERNEL, "%pOFn.dbl", + client->dev.of_node); init.ops = &vc5_dbl_ops; init.flags = CLK_SET_RATE_PARENT; - init.parent_names = vc5_mux_names; + init.parent_names = parent_names; + parent_names[0] = clk_hw_get_name(&vc5->clk_mux); init.num_parents = 1; vc5->clk_mul.init = &init; ret = devm_clk_hw_register(&client->dev, &vc5->clk_mul); - if (ret) { - dev_err(&client->dev, "unable to register %s\n", - init.name); - goto err_clk; - } + if (ret) + goto err_clk_register; + kfree(init.name); /* clock framework made a copy of the name */ } /* Register PFD */ memset(&init, 0, sizeof(init)); - init.name = vc5_pfd_names[0]; + init.name = kasprintf(GFP_KERNEL, "%pOFn.pfd", client->dev.of_node); init.ops = &vc5_pfd_ops; init.flags = CLK_SET_RATE_PARENT; + init.parent_names = parent_names; if (vc5->chip_info->flags & VC5_HAS_PFD_FREQ_DBL) - init.parent_names = vc5_dbl_names; + parent_names[0] = clk_hw_get_name(&vc5->clk_mul); else - init.parent_names = vc5_mux_names; + parent_names[0] = clk_hw_get_name(&vc5->clk_mux); init.num_parents = 1; vc5->clk_pfd.init = &init; ret = devm_clk_hw_register(&client->dev, &vc5->clk_pfd); - if (ret) { - dev_err(&client->dev, "unable to register %s\n", init.name); - goto err_clk; - } + if (ret) + goto err_clk_register; + kfree(init.name); /* clock framework made a copy of the name */ /* Register PLL */ memset(&init, 0, sizeof(init)); - init.name = vc5_pll_names[0]; + init.name = kasprintf(GFP_KERNEL, "%pOFn.pll", client->dev.of_node); init.ops = &vc5_pll_ops; init.flags = CLK_SET_RATE_PARENT; - init.parent_names = vc5_pfd_names; + init.parent_names = parent_names; + parent_names[0] = clk_hw_get_name(&vc5->clk_pfd); init.num_parents = 1; vc5->clk_pll.num = 0; vc5->clk_pll.vc5 = vc5; vc5->clk_pll.hw.init = &init; ret = devm_clk_hw_register(&client->dev, &vc5->clk_pll.hw); - if (ret) { - dev_err(&client->dev, "unable to register %s\n", init.name); - goto err_clk; - } + if (ret) + goto err_clk_register; + kfree(init.name); /* clock framework made a copy of the name */ /* Register FODs */ for (n = 0; n < vc5->chip_info->clk_fod_cnt; n++) { idx = vc5_map_index_to_output(vc5->chip_info->model, n); memset(&init, 0, sizeof(init)); - init.name = vc5_fod_names[idx]; + init.name = kasprintf(GFP_KERNEL, "%pOFn.fod%d", + client->dev.of_node, idx); init.ops = &vc5_fod_ops; init.flags = CLK_SET_RATE_PARENT; - init.parent_names = vc5_pll_names; + init.parent_names = parent_names; + parent_names[0] = clk_hw_get_name(&vc5->clk_pll.hw); init.num_parents = 1; vc5->clk_fod[n].num = idx; vc5->clk_fod[n].vc5 = vc5; vc5->clk_fod[n].hw.init = &init; ret = devm_clk_hw_register(&client->dev, &vc5->clk_fod[n].hw); - if (ret) { - dev_err(&client->dev, "unable to register %s\n", - init.name); - goto err_clk; - } + if (ret) + goto err_clk_register; + kfree(init.name); /* clock framework made a copy of the name */ } /* Register MUX-connected OUT0_I2C_SELB output */ memset(&init, 0, sizeof(init)); - init.name = vc5_clk_out_names[0]; + init.name = kasprintf(GFP_KERNEL, "%pOFn.out0_sel_i2cb", + client->dev.of_node); init.ops = &vc5_clk_out_ops; init.flags = CLK_SET_RATE_PARENT; - init.parent_names = vc5_mux_names; + init.parent_names = parent_names; + parent_names[0] = clk_hw_get_name(&vc5->clk_mux); init.num_parents = 1; vc5->clk_out[0].num = idx; vc5->clk_out[0].vc5 = vc5; vc5->clk_out[0].hw.init = &init; ret = devm_clk_hw_register(&client->dev, &vc5->clk_out[0].hw); - if (ret) { - dev_err(&client->dev, "unable to register %s\n", - init.name); - goto err_clk; - } + if (ret) + goto err_clk_register; + kfree(init.name); /* clock framework made a copy of the name */ /* Register FOD-connected OUTx outputs */ for (n = 1; n < vc5->chip_info->clk_out_cnt; n++) { idx = vc5_map_index_to_output(vc5->chip_info->model, n - 1); - parent_names[0] = vc5_fod_names[idx]; + parent_names[0] = clk_hw_get_name(&vc5->clk_fod[idx].hw); if (n == 1) - parent_names[1] = vc5_mux_names[0]; + parent_names[1] = clk_hw_get_name(&vc5->clk_mux); else - parent_names[1] = vc5_clk_out_names[n - 1]; + parent_names[1] = + clk_hw_get_name(&vc5->clk_out[n - 1].hw); memset(&init, 0, sizeof(init)); - init.name = vc5_clk_out_names[idx + 1]; + init.name = kasprintf(GFP_KERNEL, "%pOFn.out%d", + client->dev.of_node, idx + 1); init.ops = &vc5_clk_out_ops; init.flags = CLK_SET_RATE_PARENT; init.parent_names = parent_names; @@ -866,13 +1001,15 @@ static int vc5_probe(struct i2c_client *client, vc5->clk_out[n].num = idx; vc5->clk_out[n].vc5 = vc5; vc5->clk_out[n].hw.init = &init; - ret = devm_clk_hw_register(&client->dev, - &vc5->clk_out[n].hw); - if (ret) { - dev_err(&client->dev, "unable to register %s\n", - init.name); + ret = devm_clk_hw_register(&client->dev, &vc5->clk_out[n].hw); + if (ret) + goto err_clk_register; + kfree(init.name); /* clock framework made a copy of the name */ + + /* Fetch Clock Output configuration from DT (if specified) */ + ret = vc5_get_output_config(client, &vc5->clk_out[n]); + if (ret) goto err_clk; - } } ret = of_clk_add_hw_provider(client->dev.of_node, vc5_of_clk_get, vc5); @@ -883,6 +1020,9 @@ static int vc5_probe(struct i2c_client *client, return 0; +err_clk_register: + dev_err(&client->dev, "unable to register %s\n", init.name); + kfree(init.name); /* clock framework made a copy of the name */ err_clk: if (vc5->chip_info->flags & VC5_HAS_INTERNAL_XTAL) clk_unregister_fixed_rate(vc5->pin_xin); diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 3f588ed06ce3..47c0ee9da462 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -1400,6 +1400,21 @@ int __clk_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) } EXPORT_SYMBOL_GPL(__clk_determine_rate); +/** + * clk_hw_round_rate() - round the given rate for a hw clk + * @hw: the hw clk for which we are rounding a rate + * @rate: the rate which is to be rounded + * + * Takes in a rate as input and rounds it to a rate that the clk can actually + * use. + * + * Context: prepare_lock must be held. + * For clk providers to call from within clk_ops such as .round_rate, + * .determine_rate. + * + * Return: returns rounded rate of hw clk if clk supports round_rate operation + * else returns the parent rate. + */ unsigned long clk_hw_round_rate(struct clk_hw *hw, unsigned long rate) { int ret; @@ -4120,6 +4135,7 @@ static int devm_clk_hw_match(struct device *dev, void *res, void *data) /** * devm_clk_unregister - resource managed clk_unregister() + * @dev: device that is unregistering the clock data * @clk: clock to unregister * * Deallocate a clock allocated with devm_clk_register(). Normally @@ -4309,6 +4325,8 @@ static void clk_core_reparent_orphans(void) * @node: Pointer to device tree node of clock provider * @get: Get clock callback. Returns NULL or a struct clk for the * given clock specifier + * @get_hw: Get clk_hw callback. Returns NULL, ERR_PTR or a + * struct clk_hw for the given clock specifier * @data: context pointer to be passed into @get callback */ struct of_clk_provider { diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig index cde6ca90a06b..058327310c25 100644 --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig @@ -37,6 +37,15 @@ config QCOM_CLK_APCS_MSM8916 Say Y if you want to support CPU frequency scaling on devices such as msm8916. +config QCOM_CLK_APCC_MSM8996 + tristate "MSM8996 CPU Clock Controller" + select QCOM_KRYO_L2_ACCESSORS + depends on ARM64 + help + Support for the CPU clock controller on msm8996 devices. + Say Y if you want to support CPU clock scaling using CPUfreq + drivers for dyanmic power management. + config QCOM_CLK_RPM tristate "RPM based Clock Controller" depends on MFD_QCOM_RPM @@ -89,6 +98,25 @@ config APQ_MMCC_8084 Say Y if you want to support multimedia devices such as display, graphics, video encode/decode, camera, etc. +config IPQ_APSS_PLL + tristate "IPQ APSS PLL" + help + Support for APSS PLL on ipq devices. The APSS PLL is the main + clock that feeds the CPUs on ipq based devices. + Say Y if you want to support CPU frequency scaling on ipq based + devices. + +config IPQ_APSS_6018 + tristate "IPQ APSS Clock Controller" + select IPQ_APSS_PLL + depends on QCOM_APCS_IPC || COMPILE_TEST + help + Support for APSS clock controller on IPQ platforms. The + APSS clock controller manages the Mux and enable block that feeds the + CPUs. + Say Y if you want to support CPU frequency scaling on + ipq based devices. + config IPQ_GCC_4019 tristate "IPQ4019 Global Clock Controller" help @@ -280,6 +308,15 @@ config SC_GCC_7180 Say Y if you want to use peripheral devices such as UART, SPI, I2C, USB, UFS, SDCC, etc. +config SC_LPASS_CORECC_7180 + tristate "SC7180 LPASS Core Clock Controller" + select SC_GCC_7180 + help + Support for the LPASS(Low Power Audio Subsystem) core clock controller + on SC7180 devices. + Say Y if you want to use LPASS clocks and power domains of the LPASS + core clock controller. + config SC_GPUCC_7180 tristate "SC7180 Graphics Clock Controller" select SC_GCC_7180 @@ -391,6 +428,22 @@ config SM_GCC_8250 Say Y if you want to use peripheral devices such as UART, SPI, I2C, USB, SD/UFS, PCIe etc. +config SM_GPUCC_8150 + tristate "SM8150 Graphics Clock Controller" + select SM_GCC_8150 + help + Support for the graphics clock controller on SM8150 devices. + Say Y if you want to support graphics controller devices and + functionality such as 3D graphics. + +config SM_GPUCC_8250 + tristate "SM8250 Graphics Clock Controller" + select SM_GCC_8250 + help + Support for the graphics clock controller on SM8250 devices. + Say Y if you want to support graphics controller devices and + functionality such as 3D graphics. + config SPMI_PMIC_CLKDIV tristate "SPMI PMIC clkdiv Support" depends on SPMI || COMPILE_TEST diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index 7ec8561a1270..9677e769e7e9 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -19,6 +19,8 @@ clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o # Keep alphabetically sorted by config obj-$(CONFIG_APQ_GCC_8084) += gcc-apq8084.o obj-$(CONFIG_APQ_MMCC_8084) += mmcc-apq8084.o +obj-$(CONFIG_IPQ_APSS_PLL) += apss-ipq-pll.o +obj-$(CONFIG_IPQ_APSS_6018) += apss-ipq6018.o obj-$(CONFIG_IPQ_GCC_4019) += gcc-ipq4019.o obj-$(CONFIG_IPQ_GCC_6018) += gcc-ipq6018.o obj-$(CONFIG_IPQ_GCC_806X) += gcc-ipq806x.o @@ -42,6 +44,7 @@ obj-$(CONFIG_MSM_MMCC_8996) += mmcc-msm8996.o obj-$(CONFIG_MSM_MMCC_8998) += mmcc-msm8998.o obj-$(CONFIG_QCOM_A53PLL) += a53-pll.o obj-$(CONFIG_QCOM_CLK_APCS_MSM8916) += apcs-msm8916.o +obj-$(CONFIG_QCOM_CLK_APCC_MSM8996) += clk-cpu-8996.o obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o obj-$(CONFIG_QCOM_CLK_RPMH) += clk-rpmh.o obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o @@ -51,6 +54,7 @@ obj-$(CONFIG_QCS_TURING_404) += turingcc-qcs404.o obj-$(CONFIG_SC_DISPCC_7180) += dispcc-sc7180.o obj-$(CONFIG_SC_GCC_7180) += gcc-sc7180.o obj-$(CONFIG_SC_GPUCC_7180) += gpucc-sc7180.o +obj-$(CONFIG_SC_LPASS_CORECC_7180) += lpasscorecc-sc7180.o obj-$(CONFIG_SC_MSS_7180) += mss-sc7180.o obj-$(CONFIG_SC_VIDEOCC_7180) += videocc-sc7180.o obj-$(CONFIG_SDM_CAMCC_845) += camcc-sdm845.o @@ -62,6 +66,8 @@ obj-$(CONFIG_SDM_LPASSCC_845) += lpasscc-sdm845.o obj-$(CONFIG_SDM_VIDEOCC_845) += videocc-sdm845.o obj-$(CONFIG_SM_GCC_8150) += gcc-sm8150.o obj-$(CONFIG_SM_GCC_8250) += gcc-sm8250.o +obj-$(CONFIG_SM_GPUCC_8150) += gpucc-sm8150.o +obj-$(CONFIG_SM_GPUCC_8250) += gpucc-sm8250.o obj-$(CONFIG_SPMI_PMIC_CLKDIV) += clk-spmi-pmic-div.o obj-$(CONFIG_KPSS_XCC) += kpss-xcc.o obj-$(CONFIG_QCOM_HFPLL) += hfpll.o diff --git a/drivers/clk/qcom/apss-ipq-pll.c b/drivers/clk/qcom/apss-ipq-pll.c new file mode 100644 index 000000000000..30be87fb222a --- /dev/null +++ b/drivers/clk/qcom/apss-ipq-pll.c @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018, The Linux Foundation. All rights reserved. +#include <linux/clk-provider.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#include "clk-alpha-pll.h" + +static const u8 ipq_pll_offsets[] = { + [PLL_OFF_L_VAL] = 0x08, + [PLL_OFF_ALPHA_VAL] = 0x10, + [PLL_OFF_USER_CTL] = 0x18, + [PLL_OFF_CONFIG_CTL] = 0x20, + [PLL_OFF_CONFIG_CTL_U] = 0x24, + [PLL_OFF_STATUS] = 0x28, + [PLL_OFF_TEST_CTL] = 0x30, + [PLL_OFF_TEST_CTL_U] = 0x34, +}; + +static struct clk_alpha_pll ipq_pll = { + .offset = 0x0, + .regs = ipq_pll_offsets, + .flags = SUPPORTS_DYNAMIC_UPDATE, + .clkr = { + .enable_reg = 0x0, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "a53pll", + .parent_data = &(const struct clk_parent_data) { + .fw_name = "xo", + }, + .num_parents = 1, + .ops = &clk_alpha_pll_huayra_ops, + }, + }, +}; + +static const struct alpha_pll_config ipq_pll_config = { + .l = 0x37, + .config_ctl_val = 0x04141200, + .config_ctl_hi_val = 0x0, + .early_output_mask = BIT(3), + .main_output_mask = BIT(0), +}; + +static const struct regmap_config ipq_pll_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x40, + .fast_io = true, +}; + +static int apss_ipq_pll_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct regmap *regmap; + void __iomem *base; + int ret; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + regmap = devm_regmap_init_mmio(dev, base, &ipq_pll_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + clk_alpha_pll_configure(&ipq_pll, regmap, &ipq_pll_config); + + ret = devm_clk_register_regmap(dev, &ipq_pll.clkr); + if (ret) + return ret; + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, + &ipq_pll.clkr.hw); +} + +static const struct of_device_id apss_ipq_pll_match_table[] = { + { .compatible = "qcom,ipq6018-a53pll" }, + { } +}; + +static struct platform_driver apss_ipq_pll_driver = { + .probe = apss_ipq_pll_probe, + .driver = { + .name = "qcom-ipq-apss-pll", + .of_match_table = apss_ipq_pll_match_table, + }, +}; +module_platform_driver(apss_ipq_pll_driver); + +MODULE_DESCRIPTION("Qualcomm technology Inc APSS ALPHA PLL Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/qcom/apss-ipq6018.c b/drivers/clk/qcom/apss-ipq6018.c new file mode 100644 index 000000000000..d78ff2f310bf --- /dev/null +++ b/drivers/clk/qcom/apss-ipq6018.c @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018, The Linux Foundation. All rights reserved. + */ + +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/clk-provider.h> +#include <linux/regmap.h> +#include <linux/module.h> + +#include <dt-bindings/clock/qcom,apss-ipq.h> + +#include "common.h" +#include "clk-regmap.h" +#include "clk-branch.h" +#include "clk-alpha-pll.h" +#include "clk-regmap-mux.h" + +enum { + P_XO, + P_APSS_PLL_EARLY, +}; + +static const struct clk_parent_data parents_apcs_alias0_clk_src[] = { + { .fw_name = "xo" }, + { .fw_name = "pll" }, +}; + +static const struct parent_map parents_apcs_alias0_clk_src_map[] = { + { P_XO, 0 }, + { P_APSS_PLL_EARLY, 5 }, +}; + +static struct clk_regmap_mux apcs_alias0_clk_src = { + .reg = 0x0050, + .width = 3, + .shift = 7, + .parent_map = parents_apcs_alias0_clk_src_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "apcs_alias0_clk_src", + .parent_data = parents_apcs_alias0_clk_src, + .num_parents = 2, + .ops = &clk_regmap_mux_closest_ops, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_branch apcs_alias0_core_clk = { + .halt_reg = 0x0058, + .clkr = { + .enable_reg = 0x0058, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "apcs_alias0_core_clk", + .parent_hws = (const struct clk_hw *[]){ + &apcs_alias0_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static const struct regmap_config apss_ipq6018_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x1000, + .fast_io = true, +}; + +static struct clk_regmap *apss_ipq6018_clks[] = { + [APCS_ALIAS0_CLK_SRC] = &apcs_alias0_clk_src.clkr, + [APCS_ALIAS0_CORE_CLK] = &apcs_alias0_core_clk.clkr, +}; + +static const struct qcom_cc_desc apss_ipq6018_desc = { + .config = &apss_ipq6018_regmap_config, + .clks = apss_ipq6018_clks, + .num_clks = ARRAY_SIZE(apss_ipq6018_clks), +}; + +static int apss_ipq6018_probe(struct platform_device *pdev) +{ + struct regmap *regmap; + + regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!regmap) + return -ENODEV; + + return qcom_cc_really_probe(pdev, &apss_ipq6018_desc, regmap); +} + +static struct platform_driver apss_ipq6018_driver = { + .probe = apss_ipq6018_probe, + .driver = { + .name = "qcom,apss-ipq6018-clk", + }, +}; + +module_platform_driver(apss_ipq6018_driver); + +MODULE_DESCRIPTION("QCOM APSS IPQ 6018 CLK Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c index 9b2dfa08acb2..26139ef005e4 100644 --- a/drivers/clk/qcom/clk-alpha-pll.c +++ b/drivers/clk/qcom/clk-alpha-pll.c @@ -56,7 +56,6 @@ #define PLL_STATUS(p) ((p)->offset + (p)->regs[PLL_OFF_STATUS]) #define PLL_OPMODE(p) ((p)->offset + (p)->regs[PLL_OFF_OPMODE]) #define PLL_FRAC(p) ((p)->offset + (p)->regs[PLL_OFF_FRAC]) -#define PLL_CAL_VAL(p) ((p)->offset + (p)->regs[PLL_OFF_CAL_VAL]) const u8 clk_alpha_pll_regs[][PLL_OFF_MAX_REGS] = { [CLK_ALPHA_PLL_TYPE_DEFAULT] = { @@ -112,22 +111,6 @@ const u8 clk_alpha_pll_regs[][PLL_OFF_MAX_REGS] = { [PLL_OFF_CONFIG_CTL_U1] = 0x20, [PLL_OFF_TEST_CTL] = 0x24, [PLL_OFF_TEST_CTL_U] = 0x28, - [PLL_OFF_STATUS] = 0x30, - [PLL_OFF_OPMODE] = 0x38, - [PLL_OFF_ALPHA_VAL] = 0x40, - [PLL_OFF_CAL_VAL] = 0x44, - }, - [CLK_ALPHA_PLL_TYPE_LUCID] = { - [PLL_OFF_L_VAL] = 0x04, - [PLL_OFF_CAL_L_VAL] = 0x08, - [PLL_OFF_USER_CTL] = 0x0c, - [PLL_OFF_USER_CTL_U] = 0x10, - [PLL_OFF_USER_CTL_U1] = 0x14, - [PLL_OFF_CONFIG_CTL] = 0x18, - [PLL_OFF_CONFIG_CTL_U] = 0x1c, - [PLL_OFF_CONFIG_CTL_U1] = 0x20, - [PLL_OFF_TEST_CTL] = 0x24, - [PLL_OFF_TEST_CTL_U] = 0x28, [PLL_OFF_TEST_CTL_U1] = 0x2c, [PLL_OFF_STATUS] = 0x30, [PLL_OFF_OPMODE] = 0x38, @@ -156,9 +139,12 @@ EXPORT_SYMBOL_GPL(clk_alpha_pll_regs); #define PLL_OUT_MASK 0x7 #define PLL_RATE_MARGIN 500 +/* TRION PLL specific settings and offsets */ +#define TRION_PLL_CAL_VAL 0x44 +#define TRION_PCAL_DONE BIT(26) + /* LUCID PLL specific settings and offsets */ -#define LUCID_PLL_CAL_VAL 0x44 -#define LUCID_PCAL_DONE BIT(26) +#define LUCID_PCAL_DONE BIT(27) #define pll_alpha_width(p) \ ((PLL_ALPHA_VAL_U(p) - PLL_ALPHA_VAL(p) == 4) ? \ @@ -912,14 +898,14 @@ const struct clk_ops clk_alpha_pll_hwfsm_ops = { }; EXPORT_SYMBOL_GPL(clk_alpha_pll_hwfsm_ops); -const struct clk_ops clk_trion_fixed_pll_ops = { +const struct clk_ops clk_alpha_pll_fixed_trion_ops = { .enable = clk_trion_pll_enable, .disable = clk_trion_pll_disable, .is_enabled = clk_trion_pll_is_enabled, .recalc_rate = clk_trion_pll_recalc_rate, .round_rate = clk_alpha_pll_round_rate, }; -EXPORT_SYMBOL_GPL(clk_trion_fixed_pll_ops); +EXPORT_SYMBOL_GPL(clk_alpha_pll_fixed_trion_ops); static unsigned long clk_alpha_pll_postdiv_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) @@ -1339,12 +1325,12 @@ clk_trion_pll_postdiv_set_rate(struct clk_hw *hw, unsigned long rate, val << PLL_POST_DIV_SHIFT); } -const struct clk_ops clk_trion_pll_postdiv_ops = { +const struct clk_ops clk_alpha_pll_postdiv_trion_ops = { .recalc_rate = clk_trion_pll_postdiv_recalc_rate, .round_rate = clk_trion_pll_postdiv_round_rate, .set_rate = clk_trion_pll_postdiv_set_rate, }; -EXPORT_SYMBOL_GPL(clk_trion_pll_postdiv_ops); +EXPORT_SYMBOL_GPL(clk_alpha_pll_postdiv_trion_ops); static long clk_alpha_pll_postdiv_fabia_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *prate) @@ -1399,13 +1385,13 @@ EXPORT_SYMBOL_GPL(clk_alpha_pll_postdiv_fabia_ops); * @regmap: register map * @config: configuration to apply for pll */ -void clk_lucid_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap, +void clk_trion_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap, const struct alpha_pll_config *config) { if (config->l) regmap_write(regmap, PLL_L_VAL(pll), config->l); - regmap_write(regmap, PLL_CAL_L_VAL(pll), LUCID_PLL_CAL_VAL); + regmap_write(regmap, PLL_CAL_L_VAL(pll), TRION_PLL_CAL_VAL); if (config->alpha) regmap_write(regmap, PLL_ALPHA_VAL(pll), config->alpha); @@ -1458,13 +1444,13 @@ void clk_lucid_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap, /* Place the PLL in STANDBY mode */ regmap_update_bits(regmap, PLL_MODE(pll), PLL_RESET_N, PLL_RESET_N); } -EXPORT_SYMBOL_GPL(clk_lucid_pll_configure); +EXPORT_SYMBOL_GPL(clk_trion_pll_configure); /* - * The Lucid PLL requires a power-on self-calibration which happens when the + * The TRION PLL requires a power-on self-calibration which happens when the * PLL comes out of reset. Calibrate in case it is not completed. */ -static int alpha_pll_lucid_prepare(struct clk_hw *hw) +static int __alpha_pll_trion_prepare(struct clk_hw *hw, u32 pcal_done) { struct clk_alpha_pll *pll = to_clk_alpha_pll(hw); u32 regval; @@ -1472,7 +1458,7 @@ static int alpha_pll_lucid_prepare(struct clk_hw *hw) /* Return early if calibration is not needed. */ regmap_read(pll->clkr.regmap, PLL_STATUS(pll), ®val); - if (regval & LUCID_PCAL_DONE) + if (regval & pcal_done) return 0; /* On/off to calibrate */ @@ -1483,7 +1469,17 @@ static int alpha_pll_lucid_prepare(struct clk_hw *hw) return ret; } -static int alpha_pll_lucid_set_rate(struct clk_hw *hw, unsigned long rate, +static int alpha_pll_trion_prepare(struct clk_hw *hw) +{ + return __alpha_pll_trion_prepare(hw, TRION_PCAL_DONE); +} + +static int alpha_pll_lucid_prepare(struct clk_hw *hw) +{ + return __alpha_pll_trion_prepare(hw, LUCID_PCAL_DONE); +} + +static int alpha_pll_trion_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long prate) { struct clk_alpha_pll *pll = to_clk_alpha_pll(hw); @@ -1537,25 +1533,27 @@ static int alpha_pll_lucid_set_rate(struct clk_hw *hw, unsigned long rate, return 0; } -const struct clk_ops clk_alpha_pll_lucid_ops = { - .prepare = alpha_pll_lucid_prepare, +const struct clk_ops clk_alpha_pll_trion_ops = { + .prepare = alpha_pll_trion_prepare, .enable = clk_trion_pll_enable, .disable = clk_trion_pll_disable, .is_enabled = clk_trion_pll_is_enabled, .recalc_rate = clk_trion_pll_recalc_rate, .round_rate = clk_alpha_pll_round_rate, - .set_rate = alpha_pll_lucid_set_rate, + .set_rate = alpha_pll_trion_set_rate, }; -EXPORT_SYMBOL_GPL(clk_alpha_pll_lucid_ops); +EXPORT_SYMBOL_GPL(clk_alpha_pll_trion_ops); -const struct clk_ops clk_alpha_pll_fixed_lucid_ops = { +const struct clk_ops clk_alpha_pll_lucid_ops = { + .prepare = alpha_pll_lucid_prepare, .enable = clk_trion_pll_enable, .disable = clk_trion_pll_disable, .is_enabled = clk_trion_pll_is_enabled, .recalc_rate = clk_trion_pll_recalc_rate, .round_rate = clk_alpha_pll_round_rate, + .set_rate = alpha_pll_trion_set_rate, }; -EXPORT_SYMBOL_GPL(clk_alpha_pll_fixed_lucid_ops); +EXPORT_SYMBOL_GPL(clk_alpha_pll_lucid_ops); const struct clk_ops clk_alpha_pll_postdiv_lucid_ops = { .recalc_rate = clk_alpha_pll_postdiv_fabia_recalc_rate, diff --git a/drivers/clk/qcom/clk-alpha-pll.h b/drivers/clk/qcom/clk-alpha-pll.h index 704674a153b6..d3201b87c0cd 100644 --- a/drivers/clk/qcom/clk-alpha-pll.h +++ b/drivers/clk/qcom/clk-alpha-pll.h @@ -14,7 +14,7 @@ enum { CLK_ALPHA_PLL_TYPE_BRAMMO, CLK_ALPHA_PLL_TYPE_FABIA, CLK_ALPHA_PLL_TYPE_TRION, - CLK_ALPHA_PLL_TYPE_LUCID, + CLK_ALPHA_PLL_TYPE_LUCID = CLK_ALPHA_PLL_TYPE_TRION, CLK_ALPHA_PLL_TYPE_MAX, }; @@ -47,6 +47,12 @@ struct pll_vco { u32 val; }; +#define VCO(a, b, c) { \ + .val = a,\ + .min_freq = b,\ + .max_freq = c,\ +} + /** * struct clk_alpha_pll - phase locked loop (PLL) * @offset: base address of registers @@ -128,18 +134,23 @@ extern const struct clk_ops clk_alpha_pll_fabia_ops; extern const struct clk_ops clk_alpha_pll_fixed_fabia_ops; extern const struct clk_ops clk_alpha_pll_postdiv_fabia_ops; +extern const struct clk_ops clk_alpha_pll_trion_ops; +extern const struct clk_ops clk_alpha_pll_fixed_trion_ops; +extern const struct clk_ops clk_alpha_pll_postdiv_trion_ops; + extern const struct clk_ops clk_alpha_pll_lucid_ops; -extern const struct clk_ops clk_alpha_pll_fixed_lucid_ops; +#define clk_alpha_pll_fixed_lucid_ops clk_alpha_pll_fixed_trion_ops extern const struct clk_ops clk_alpha_pll_postdiv_lucid_ops; void clk_alpha_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap, const struct alpha_pll_config *config); void clk_fabia_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap, const struct alpha_pll_config *config); -void clk_lucid_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap, +void clk_trion_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap, const struct alpha_pll_config *config); +#define clk_lucid_pll_configure(pll, regmap, config) \ + clk_trion_pll_configure(pll, regmap, config) + -extern const struct clk_ops clk_trion_fixed_pll_ops; -extern const struct clk_ops clk_trion_pll_postdiv_ops; #endif diff --git a/drivers/clk/qcom/clk-cpu-8996.c b/drivers/clk/qcom/clk-cpu-8996.c new file mode 100644 index 000000000000..4a4fde8dd12d --- /dev/null +++ b/drivers/clk/qcom/clk-cpu-8996.c @@ -0,0 +1,538 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ + +/* + * Each of the CPU clusters (Power and Perf) on msm8996 are + * clocked via 2 PLLs, a primary and alternate. There are also + * 2 Mux'es, a primary and secondary all connected together + * as shown below + * + * +-------+ + * XO | | + * +------------------>0 | + * | | + * PLL/2 | SMUX +----+ + * +------->1 | | + * | | | | + * | +-------+ | +-------+ + * | +---->0 | + * | | | + * +---------------+ | +----------->1 | CPU clk + * |Primary PLL +----+ PLL_EARLY | | +------> + * | +------+-----------+ +------>2 PMUX | + * +---------------+ | | | | + * | +------+ | +-->3 | + * +--^+ ACD +-----+ | +-------+ + * +---------------+ +------+ | + * |Alt PLL | | + * | +---------------------------+ + * +---------------+ PLL_EARLY + * + * The primary PLL is what drives the CPU clk, except for times + * when we are reprogramming the PLL itself (for rate changes) when + * we temporarily switch to an alternate PLL. + * + * The primary PLL operates on a single VCO range, between 600MHz + * and 3GHz. However the CPUs do support OPPs with frequencies + * between 300MHz and 600MHz. In order to support running the CPUs + * at those frequencies we end up having to lock the PLL at twice + * the rate and drive the CPU clk via the PLL/2 output and SMUX. + * + * So for frequencies above 600MHz we follow the following path + * Primary PLL --> PLL_EARLY --> PMUX(1) --> CPU clk + * and for frequencies between 300MHz and 600MHz we follow + * Primary PLL --> PLL/2 --> SMUX(1) --> PMUX(0) --> CPU clk + * + * ACD stands for Adaptive Clock Distribution and is used to + * detect voltage droops. + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <soc/qcom/kryo-l2-accessors.h> + +#include "clk-alpha-pll.h" +#include "clk-regmap.h" + +enum _pmux_input { + DIV_2_INDEX = 0, + PLL_INDEX, + ACD_INDEX, + ALT_INDEX, + NUM_OF_PMUX_INPUTS +}; + +#define DIV_2_THRESHOLD 600000000 +#define PWRCL_REG_OFFSET 0x0 +#define PERFCL_REG_OFFSET 0x80000 +#define MUX_OFFSET 0x40 +#define ALT_PLL_OFFSET 0x100 +#define SSSCTL_OFFSET 0x160 + +static const u8 prim_pll_regs[PLL_OFF_MAX_REGS] = { + [PLL_OFF_L_VAL] = 0x04, + [PLL_OFF_ALPHA_VAL] = 0x08, + [PLL_OFF_USER_CTL] = 0x10, + [PLL_OFF_CONFIG_CTL] = 0x18, + [PLL_OFF_CONFIG_CTL_U] = 0x1c, + [PLL_OFF_TEST_CTL] = 0x20, + [PLL_OFF_TEST_CTL_U] = 0x24, + [PLL_OFF_STATUS] = 0x28, +}; + +static const u8 alt_pll_regs[PLL_OFF_MAX_REGS] = { + [PLL_OFF_L_VAL] = 0x04, + [PLL_OFF_ALPHA_VAL] = 0x08, + [PLL_OFF_ALPHA_VAL_U] = 0x0c, + [PLL_OFF_USER_CTL] = 0x10, + [PLL_OFF_USER_CTL_U] = 0x14, + [PLL_OFF_CONFIG_CTL] = 0x18, + [PLL_OFF_TEST_CTL] = 0x20, + [PLL_OFF_TEST_CTL_U] = 0x24, + [PLL_OFF_STATUS] = 0x28, +}; + +/* PLLs */ + +static const struct alpha_pll_config hfpll_config = { + .l = 60, + .config_ctl_val = 0x200d4aa8, + .config_ctl_hi_val = 0x006, + .pre_div_mask = BIT(12), + .post_div_mask = 0x3 << 8, + .post_div_val = 0x1 << 8, + .main_output_mask = BIT(0), + .early_output_mask = BIT(3), +}; + +static struct clk_alpha_pll perfcl_pll = { + .offset = PERFCL_REG_OFFSET, + .regs = prim_pll_regs, + .flags = SUPPORTS_DYNAMIC_UPDATE | SUPPORTS_FSM_MODE, + .clkr.hw.init = &(struct clk_init_data){ + .name = "perfcl_pll", + .parent_names = (const char *[]){ "xo" }, + .num_parents = 1, + .ops = &clk_alpha_pll_huayra_ops, + }, +}; + +static struct clk_alpha_pll pwrcl_pll = { + .offset = PWRCL_REG_OFFSET, + .regs = prim_pll_regs, + .flags = SUPPORTS_DYNAMIC_UPDATE | SUPPORTS_FSM_MODE, + .clkr.hw.init = &(struct clk_init_data){ + .name = "pwrcl_pll", + .parent_names = (const char *[]){ "xo" }, + .num_parents = 1, + .ops = &clk_alpha_pll_huayra_ops, + }, +}; + +static const struct pll_vco alt_pll_vco_modes[] = { + VCO(3, 250000000, 500000000), + VCO(2, 500000000, 750000000), + VCO(1, 750000000, 1000000000), + VCO(0, 1000000000, 2150400000), +}; + +static const struct alpha_pll_config altpll_config = { + .l = 16, + .vco_val = 0x3 << 20, + .vco_mask = 0x3 << 20, + .config_ctl_val = 0x4001051b, + .post_div_mask = 0x3 << 8, + .post_div_val = 0x1 << 8, + .main_output_mask = BIT(0), + .early_output_mask = BIT(3), +}; + +static struct clk_alpha_pll perfcl_alt_pll = { + .offset = PERFCL_REG_OFFSET + ALT_PLL_OFFSET, + .regs = alt_pll_regs, + .vco_table = alt_pll_vco_modes, + .num_vco = ARRAY_SIZE(alt_pll_vco_modes), + .flags = SUPPORTS_OFFLINE_REQ | SUPPORTS_FSM_MODE, + .clkr.hw.init = &(struct clk_init_data) { + .name = "perfcl_alt_pll", + .parent_names = (const char *[]){ "xo" }, + .num_parents = 1, + .ops = &clk_alpha_pll_hwfsm_ops, + }, +}; + +static struct clk_alpha_pll pwrcl_alt_pll = { + .offset = PWRCL_REG_OFFSET + ALT_PLL_OFFSET, + .regs = alt_pll_regs, + .vco_table = alt_pll_vco_modes, + .num_vco = ARRAY_SIZE(alt_pll_vco_modes), + .flags = SUPPORTS_OFFLINE_REQ | SUPPORTS_FSM_MODE, + .clkr.hw.init = &(struct clk_init_data) { + .name = "pwrcl_alt_pll", + .parent_names = (const char *[]){ "xo" }, + .num_parents = 1, + .ops = &clk_alpha_pll_hwfsm_ops, + }, +}; + +struct clk_cpu_8996_mux { + u32 reg; + u8 shift; + u8 width; + struct notifier_block nb; + struct clk_hw *pll; + struct clk_hw *pll_div_2; + struct clk_regmap clkr; +}; + +static int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event, + void *data); + +#define to_clk_cpu_8996_mux_nb(_nb) \ + container_of(_nb, struct clk_cpu_8996_mux, nb) + +static inline struct clk_cpu_8996_mux *to_clk_cpu_8996_mux_hw(struct clk_hw *hw) +{ + return container_of(to_clk_regmap(hw), struct clk_cpu_8996_mux, clkr); +} + +static u8 clk_cpu_8996_mux_get_parent(struct clk_hw *hw) +{ + struct clk_regmap *clkr = to_clk_regmap(hw); + struct clk_cpu_8996_mux *cpuclk = to_clk_cpu_8996_mux_hw(hw); + u32 mask = GENMASK(cpuclk->width - 1, 0); + u32 val; + + regmap_read(clkr->regmap, cpuclk->reg, &val); + val >>= cpuclk->shift; + + return val & mask; +} + +static int clk_cpu_8996_mux_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_regmap *clkr = to_clk_regmap(hw); + struct clk_cpu_8996_mux *cpuclk = to_clk_cpu_8996_mux_hw(hw); + u32 mask = GENMASK(cpuclk->width + cpuclk->shift - 1, cpuclk->shift); + u32 val; + + val = index; + val <<= cpuclk->shift; + + return regmap_update_bits(clkr->regmap, cpuclk->reg, mask, val); +} + +static int clk_cpu_8996_mux_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct clk_cpu_8996_mux *cpuclk = to_clk_cpu_8996_mux_hw(hw); + struct clk_hw *parent = cpuclk->pll; + + if (cpuclk->pll_div_2 && req->rate < DIV_2_THRESHOLD) { + if (req->rate < (DIV_2_THRESHOLD / 2)) + return -EINVAL; + + parent = cpuclk->pll_div_2; + } + + req->best_parent_rate = clk_hw_round_rate(parent, req->rate); + req->best_parent_hw = parent; + + return 0; +} + +static const struct clk_ops clk_cpu_8996_mux_ops = { + .set_parent = clk_cpu_8996_mux_set_parent, + .get_parent = clk_cpu_8996_mux_get_parent, + .determine_rate = clk_cpu_8996_mux_determine_rate, +}; + +static struct clk_cpu_8996_mux pwrcl_smux = { + .reg = PWRCL_REG_OFFSET + MUX_OFFSET, + .shift = 2, + .width = 2, + .clkr.hw.init = &(struct clk_init_data) { + .name = "pwrcl_smux", + .parent_names = (const char *[]){ + "xo", + "pwrcl_pll_main", + }, + .num_parents = 2, + .ops = &clk_cpu_8996_mux_ops, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_cpu_8996_mux perfcl_smux = { + .reg = PERFCL_REG_OFFSET + MUX_OFFSET, + .shift = 2, + .width = 2, + .clkr.hw.init = &(struct clk_init_data) { + .name = "perfcl_smux", + .parent_names = (const char *[]){ + "xo", + "perfcl_pll_main", + }, + .num_parents = 2, + .ops = &clk_cpu_8996_mux_ops, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_cpu_8996_mux pwrcl_pmux = { + .reg = PWRCL_REG_OFFSET + MUX_OFFSET, + .shift = 0, + .width = 2, + .pll = &pwrcl_pll.clkr.hw, + .pll_div_2 = &pwrcl_smux.clkr.hw, + .nb.notifier_call = cpu_clk_notifier_cb, + .clkr.hw.init = &(struct clk_init_data) { + .name = "pwrcl_pmux", + .parent_names = (const char *[]){ + "pwrcl_smux", + "pwrcl_pll", + "pwrcl_pll_acd", + "pwrcl_alt_pll", + }, + .num_parents = 4, + .ops = &clk_cpu_8996_mux_ops, + /* CPU clock is critical and should never be gated */ + .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + }, +}; + +static struct clk_cpu_8996_mux perfcl_pmux = { + .reg = PERFCL_REG_OFFSET + MUX_OFFSET, + .shift = 0, + .width = 2, + .pll = &perfcl_pll.clkr.hw, + .pll_div_2 = &perfcl_smux.clkr.hw, + .nb.notifier_call = cpu_clk_notifier_cb, + .clkr.hw.init = &(struct clk_init_data) { + .name = "perfcl_pmux", + .parent_names = (const char *[]){ + "perfcl_smux", + "perfcl_pll", + "perfcl_pll_acd", + "perfcl_alt_pll", + }, + .num_parents = 4, + .ops = &clk_cpu_8996_mux_ops, + /* CPU clock is critical and should never be gated */ + .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + }, +}; + +static const struct regmap_config cpu_msm8996_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x80210, + .fast_io = true, + .val_format_endian = REGMAP_ENDIAN_LITTLE, +}; + +static struct clk_regmap *cpu_msm8996_clks[] = { + &perfcl_pll.clkr, + &pwrcl_pll.clkr, + &perfcl_alt_pll.clkr, + &pwrcl_alt_pll.clkr, + &perfcl_smux.clkr, + &pwrcl_smux.clkr, + &perfcl_pmux.clkr, + &pwrcl_pmux.clkr, +}; + +static int qcom_cpu_clk_msm8996_register_clks(struct device *dev, + struct regmap *regmap) +{ + int i, ret; + + perfcl_smux.pll = clk_hw_register_fixed_factor(dev, "perfcl_pll_main", + "perfcl_pll", + CLK_SET_RATE_PARENT, + 1, 2); + if (IS_ERR(perfcl_smux.pll)) { + dev_err(dev, "Failed to initialize perfcl_pll_main\n"); + return PTR_ERR(perfcl_smux.pll); + } + + pwrcl_smux.pll = clk_hw_register_fixed_factor(dev, "pwrcl_pll_main", + "pwrcl_pll", + CLK_SET_RATE_PARENT, + 1, 2); + if (IS_ERR(pwrcl_smux.pll)) { + dev_err(dev, "Failed to initialize pwrcl_pll_main\n"); + clk_hw_unregister(perfcl_smux.pll); + return PTR_ERR(pwrcl_smux.pll); + } + + for (i = 0; i < ARRAY_SIZE(cpu_msm8996_clks); i++) { + ret = devm_clk_register_regmap(dev, cpu_msm8996_clks[i]); + if (ret) { + clk_hw_unregister(perfcl_smux.pll); + clk_hw_unregister(pwrcl_smux.pll); + return ret; + } + } + + clk_alpha_pll_configure(&perfcl_pll, regmap, &hfpll_config); + clk_alpha_pll_configure(&pwrcl_pll, regmap, &hfpll_config); + clk_alpha_pll_configure(&perfcl_alt_pll, regmap, &altpll_config); + clk_alpha_pll_configure(&pwrcl_alt_pll, regmap, &altpll_config); + + /* Enable alt PLLs */ + clk_prepare_enable(pwrcl_alt_pll.clkr.hw.clk); + clk_prepare_enable(perfcl_alt_pll.clkr.hw.clk); + + clk_notifier_register(pwrcl_pmux.clkr.hw.clk, &pwrcl_pmux.nb); + clk_notifier_register(perfcl_pmux.clkr.hw.clk, &perfcl_pmux.nb); + + return ret; +} + +static int qcom_cpu_clk_msm8996_unregister_clks(void) +{ + int ret = 0; + + ret = clk_notifier_unregister(pwrcl_pmux.clkr.hw.clk, &pwrcl_pmux.nb); + if (ret) + return ret; + + ret = clk_notifier_unregister(perfcl_pmux.clkr.hw.clk, &perfcl_pmux.nb); + if (ret) + return ret; + + clk_hw_unregister(perfcl_smux.pll); + clk_hw_unregister(pwrcl_smux.pll); + + return 0; +} + +#define CPU_AFINITY_MASK 0xFFF +#define PWRCL_CPU_REG_MASK 0x3 +#define PERFCL_CPU_REG_MASK 0x103 + +#define L2ACDCR_REG 0x580ULL +#define L2ACDTD_REG 0x581ULL +#define L2ACDDVMRC_REG 0x584ULL +#define L2ACDSSCR_REG 0x589ULL + +static DEFINE_SPINLOCK(qcom_clk_acd_lock); +static void __iomem *base; + +static void qcom_cpu_clk_msm8996_acd_init(void __iomem *base) +{ + u64 hwid; + unsigned long flags; + + spin_lock_irqsave(&qcom_clk_acd_lock, flags); + + hwid = read_cpuid_mpidr() & CPU_AFINITY_MASK; + + kryo_l2_set_indirect_reg(L2ACDTD_REG, 0x00006a11); + kryo_l2_set_indirect_reg(L2ACDDVMRC_REG, 0x000e0f0f); + kryo_l2_set_indirect_reg(L2ACDSSCR_REG, 0x00000601); + + if (PWRCL_CPU_REG_MASK == (hwid | PWRCL_CPU_REG_MASK)) { + writel(0xf, base + PWRCL_REG_OFFSET + SSSCTL_OFFSET); + kryo_l2_set_indirect_reg(L2ACDCR_REG, 0x002c5ffd); + } + + if (PERFCL_CPU_REG_MASK == (hwid | PERFCL_CPU_REG_MASK)) { + kryo_l2_set_indirect_reg(L2ACDCR_REG, 0x002c5ffd); + writel(0xf, base + PERFCL_REG_OFFSET + SSSCTL_OFFSET); + } + + spin_unlock_irqrestore(&qcom_clk_acd_lock, flags); +} + +static int cpu_clk_notifier_cb(struct notifier_block *nb, unsigned long event, + void *data) +{ + struct clk_cpu_8996_mux *cpuclk = to_clk_cpu_8996_mux_nb(nb); + struct clk_notifier_data *cnd = data; + int ret; + + switch (event) { + case PRE_RATE_CHANGE: + ret = clk_cpu_8996_mux_set_parent(&cpuclk->clkr.hw, ALT_INDEX); + qcom_cpu_clk_msm8996_acd_init(base); + break; + case POST_RATE_CHANGE: + if (cnd->new_rate < DIV_2_THRESHOLD) + ret = clk_cpu_8996_mux_set_parent(&cpuclk->clkr.hw, + DIV_2_INDEX); + else + ret = clk_cpu_8996_mux_set_parent(&cpuclk->clkr.hw, + ACD_INDEX); + break; + default: + ret = 0; + break; + } + + return notifier_from_errno(ret); +}; + +static int qcom_cpu_clk_msm8996_driver_probe(struct platform_device *pdev) +{ + struct regmap *regmap; + struct clk_hw_onecell_data *data; + struct device *dev = &pdev->dev; + int ret; + + data = devm_kzalloc(dev, struct_size(data, hws, 2), GFP_KERNEL); + if (!data) + return -ENOMEM; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + regmap = devm_regmap_init_mmio(dev, base, &cpu_msm8996_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + ret = qcom_cpu_clk_msm8996_register_clks(dev, regmap); + if (ret) + return ret; + + qcom_cpu_clk_msm8996_acd_init(base); + + data->hws[0] = &pwrcl_pmux.clkr.hw; + data->hws[1] = &perfcl_pmux.clkr.hw; + data->num = 2; + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, data); +} + +static int qcom_cpu_clk_msm8996_driver_remove(struct platform_device *pdev) +{ + return qcom_cpu_clk_msm8996_unregister_clks(); +} + +static const struct of_device_id qcom_cpu_clk_msm8996_match_table[] = { + { .compatible = "qcom,msm8996-apcc" }, + {} +}; +MODULE_DEVICE_TABLE(of, qcom_cpu_clk_msm8996_match_table); + +static struct platform_driver qcom_cpu_clk_msm8996_driver = { + .probe = qcom_cpu_clk_msm8996_driver_probe, + .remove = qcom_cpu_clk_msm8996_driver_remove, + .driver = { + .name = "qcom-msm8996-apcc", + .of_match_table = qcom_cpu_clk_msm8996_match_table, + }, +}; +module_platform_driver(qcom_cpu_clk_msm8996_driver); + +MODULE_DESCRIPTION("QCOM MSM8996 CPU Clock Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/qcom/clk-smd-rpm.c b/drivers/clk/qcom/clk-smd-rpm.c index 52f63ad787ba..0e1dfa89489e 100644 --- a/drivers/clk/qcom/clk-smd-rpm.c +++ b/drivers/clk/qcom/clk-smd-rpm.c @@ -452,6 +452,55 @@ static const struct rpm_smd_clk_desc rpm_clk_msm8916 = { .num_clks = ARRAY_SIZE(msm8916_clks), }; +/* msm8936 */ +DEFINE_CLK_SMD_RPM(msm8936, pcnoc_clk, pcnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 0); +DEFINE_CLK_SMD_RPM(msm8936, snoc_clk, snoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 1); +DEFINE_CLK_SMD_RPM(msm8936, bimc_clk, bimc_a_clk, QCOM_SMD_RPM_MEM_CLK, 0); +DEFINE_CLK_SMD_RPM(msm8936, sysmmnoc_clk, sysmmnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 2); +DEFINE_CLK_SMD_RPM_QDSS(msm8936, qdss_clk, qdss_a_clk, QCOM_SMD_RPM_MISC_CLK, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8936, bb_clk1, bb_clk1_a, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8936, bb_clk2, bb_clk2_a, 2); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8936, rf_clk1, rf_clk1_a, 4); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8936, rf_clk2, rf_clk2_a, 5); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8936, bb_clk1_pin, bb_clk1_a_pin, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8936, bb_clk2_pin, bb_clk2_a_pin, 2); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8936, rf_clk1_pin, rf_clk1_a_pin, 4); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8936, rf_clk2_pin, rf_clk2_a_pin, 5); + +static struct clk_smd_rpm *msm8936_clks[] = { + [RPM_SMD_PCNOC_CLK] = &msm8936_pcnoc_clk, + [RPM_SMD_PCNOC_A_CLK] = &msm8936_pcnoc_a_clk, + [RPM_SMD_SNOC_CLK] = &msm8936_snoc_clk, + [RPM_SMD_SNOC_A_CLK] = &msm8936_snoc_a_clk, + [RPM_SMD_BIMC_CLK] = &msm8936_bimc_clk, + [RPM_SMD_BIMC_A_CLK] = &msm8936_bimc_a_clk, + [RPM_SMD_SYSMMNOC_CLK] = &msm8936_sysmmnoc_clk, + [RPM_SMD_SYSMMNOC_A_CLK] = &msm8936_sysmmnoc_a_clk, + [RPM_SMD_QDSS_CLK] = &msm8936_qdss_clk, + [RPM_SMD_QDSS_A_CLK] = &msm8936_qdss_a_clk, + [RPM_SMD_BB_CLK1] = &msm8936_bb_clk1, + [RPM_SMD_BB_CLK1_A] = &msm8936_bb_clk1_a, + [RPM_SMD_BB_CLK2] = &msm8936_bb_clk2, + [RPM_SMD_BB_CLK2_A] = &msm8936_bb_clk2_a, + [RPM_SMD_RF_CLK1] = &msm8936_rf_clk1, + [RPM_SMD_RF_CLK1_A] = &msm8936_rf_clk1_a, + [RPM_SMD_RF_CLK2] = &msm8936_rf_clk2, + [RPM_SMD_RF_CLK2_A] = &msm8936_rf_clk2_a, + [RPM_SMD_BB_CLK1_PIN] = &msm8936_bb_clk1_pin, + [RPM_SMD_BB_CLK1_A_PIN] = &msm8936_bb_clk1_a_pin, + [RPM_SMD_BB_CLK2_PIN] = &msm8936_bb_clk2_pin, + [RPM_SMD_BB_CLK2_A_PIN] = &msm8936_bb_clk2_a_pin, + [RPM_SMD_RF_CLK1_PIN] = &msm8936_rf_clk1_pin, + [RPM_SMD_RF_CLK1_A_PIN] = &msm8936_rf_clk1_a_pin, + [RPM_SMD_RF_CLK2_PIN] = &msm8936_rf_clk2_pin, + [RPM_SMD_RF_CLK2_A_PIN] = &msm8936_rf_clk2_a_pin, +}; + +static const struct rpm_smd_clk_desc rpm_clk_msm8936 = { + .clks = msm8936_clks, + .num_clks = ARRAY_SIZE(msm8936_clks), +}; + /* msm8974 */ DEFINE_CLK_SMD_RPM(msm8974, pnoc_clk, pnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 0); DEFINE_CLK_SMD_RPM(msm8974, snoc_clk, snoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 1); @@ -574,6 +623,175 @@ static const struct rpm_smd_clk_desc rpm_clk_msm8976 = { .num_clks = ARRAY_SIZE(msm8976_clks), }; +/* msm8992 */ +DEFINE_CLK_SMD_RPM(msm8992, pnoc_clk, pnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 0); +DEFINE_CLK_SMD_RPM(msm8992, ocmemgx_clk, ocmemgx_a_clk, QCOM_SMD_RPM_MEM_CLK, 2); +DEFINE_CLK_SMD_RPM(msm8992, bimc_clk, bimc_a_clk, QCOM_SMD_RPM_MEM_CLK, 0); +DEFINE_CLK_SMD_RPM(msm8992, cnoc_clk, cnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 2); +DEFINE_CLK_SMD_RPM(msm8992, gfx3d_clk_src, gfx3d_a_clk_src, QCOM_SMD_RPM_MEM_CLK, 1); +DEFINE_CLK_SMD_RPM(msm8992, snoc_clk, snoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8992, bb_clk1, bb_clk1_a, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8992, bb_clk1_pin, bb_clk1_a_pin, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8992, bb_clk2, bb_clk2_a, 2); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8992, bb_clk2_pin, bb_clk2_a_pin, 2); + +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8992, div_clk1, div_clk1_a, 11); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8992, div_clk2, div_clk2_a, 12); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8992, div_clk3, div_clk3_a, 13); +DEFINE_CLK_SMD_RPM(msm8992, ipa_clk, ipa_a_clk, QCOM_SMD_RPM_IPA_CLK, 0); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8992, ln_bb_clk, ln_bb_a_clk, 8); +DEFINE_CLK_SMD_RPM(msm8992, mmssnoc_ahb_clk, mmssnoc_ahb_a_clk, + QCOM_SMD_RPM_BUS_CLK, 3); +DEFINE_CLK_SMD_RPM_QDSS(msm8992, qdss_clk, qdss_a_clk, + QCOM_SMD_RPM_MISC_CLK, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8992, rf_clk1, rf_clk1_a, 4); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8992, rf_clk2, rf_clk2_a, 5); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8992, rf_clk1_pin, rf_clk1_a_pin, 4); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8992, rf_clk2_pin, rf_clk2_a_pin, 5); + +DEFINE_CLK_SMD_RPM(msm8992, ce1_clk, ce1_a_clk, QCOM_SMD_RPM_CE_CLK, 0); +DEFINE_CLK_SMD_RPM(msm8992, ce2_clk, ce2_a_clk, QCOM_SMD_RPM_CE_CLK, 1); + +static struct clk_smd_rpm *msm8992_clks[] = { + [RPM_SMD_PNOC_CLK] = &msm8992_pnoc_clk, + [RPM_SMD_PNOC_A_CLK] = &msm8992_pnoc_a_clk, + [RPM_SMD_OCMEMGX_CLK] = &msm8992_ocmemgx_clk, + [RPM_SMD_OCMEMGX_A_CLK] = &msm8992_ocmemgx_a_clk, + [RPM_SMD_BIMC_CLK] = &msm8992_bimc_clk, + [RPM_SMD_BIMC_A_CLK] = &msm8992_bimc_a_clk, + [RPM_SMD_CNOC_CLK] = &msm8992_cnoc_clk, + [RPM_SMD_CNOC_A_CLK] = &msm8992_cnoc_a_clk, + [RPM_SMD_GFX3D_CLK_SRC] = &msm8992_gfx3d_clk_src, + [RPM_SMD_GFX3D_A_CLK_SRC] = &msm8992_gfx3d_a_clk_src, + [RPM_SMD_SNOC_CLK] = &msm8992_snoc_clk, + [RPM_SMD_SNOC_A_CLK] = &msm8992_snoc_a_clk, + [RPM_SMD_BB_CLK1] = &msm8992_bb_clk1, + [RPM_SMD_BB_CLK1_A] = &msm8992_bb_clk1_a, + [RPM_SMD_BB_CLK1_PIN] = &msm8992_bb_clk1_pin, + [RPM_SMD_BB_CLK1_A_PIN] = &msm8992_bb_clk1_a_pin, + [RPM_SMD_BB_CLK2] = &msm8992_bb_clk2, + [RPM_SMD_BB_CLK2_A] = &msm8992_bb_clk2_a, + [RPM_SMD_BB_CLK2_PIN] = &msm8992_bb_clk2_pin, + [RPM_SMD_BB_CLK2_A_PIN] = &msm8992_bb_clk2_a_pin, + [RPM_SMD_DIV_CLK1] = &msm8992_div_clk1, + [RPM_SMD_DIV_A_CLK1] = &msm8992_div_clk1_a, + [RPM_SMD_DIV_CLK2] = &msm8992_div_clk2, + [RPM_SMD_DIV_A_CLK2] = &msm8992_div_clk2_a, + [RPM_SMD_DIV_CLK3] = &msm8992_div_clk3, + [RPM_SMD_DIV_A_CLK3] = &msm8992_div_clk3_a, + [RPM_SMD_IPA_CLK] = &msm8992_ipa_clk, + [RPM_SMD_IPA_A_CLK] = &msm8992_ipa_a_clk, + [RPM_SMD_LN_BB_CLK] = &msm8992_ln_bb_clk, + [RPM_SMD_LN_BB_A_CLK] = &msm8992_ln_bb_a_clk, + [RPM_SMD_MMSSNOC_AHB_CLK] = &msm8992_mmssnoc_ahb_clk, + [RPM_SMD_MMSSNOC_AHB_A_CLK] = &msm8992_mmssnoc_ahb_a_clk, + [RPM_SMD_QDSS_CLK] = &msm8992_qdss_clk, + [RPM_SMD_QDSS_A_CLK] = &msm8992_qdss_a_clk, + [RPM_SMD_RF_CLK1] = &msm8992_rf_clk1, + [RPM_SMD_RF_CLK1_A] = &msm8992_rf_clk1_a, + [RPM_SMD_RF_CLK2] = &msm8992_rf_clk2, + [RPM_SMD_RF_CLK2_A] = &msm8992_rf_clk2_a, + [RPM_SMD_RF_CLK1_PIN] = &msm8992_rf_clk1_pin, + [RPM_SMD_RF_CLK1_A_PIN] = &msm8992_rf_clk1_a_pin, + [RPM_SMD_RF_CLK2_PIN] = &msm8992_rf_clk2_pin, + [RPM_SMD_RF_CLK2_A_PIN] = &msm8992_rf_clk2_a_pin, + [RPM_SMD_CE1_CLK] = &msm8992_ce1_clk, + [RPM_SMD_CE1_A_CLK] = &msm8992_ce1_a_clk, + [RPM_SMD_CE2_CLK] = &msm8992_ce2_clk, + [RPM_SMD_CE2_A_CLK] = &msm8992_ce2_a_clk, +}; + +static const struct rpm_smd_clk_desc rpm_clk_msm8992 = { + .clks = msm8992_clks, + .num_clks = ARRAY_SIZE(msm8992_clks), +}; + +/* msm8994 */ +DEFINE_CLK_SMD_RPM(msm8994, pnoc_clk, pnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 0); +DEFINE_CLK_SMD_RPM(msm8994, ocmemgx_clk, ocmemgx_a_clk, QCOM_SMD_RPM_MEM_CLK, 2); +DEFINE_CLK_SMD_RPM(msm8994, bimc_clk, bimc_a_clk, QCOM_SMD_RPM_MEM_CLK, 0); +DEFINE_CLK_SMD_RPM(msm8994, cnoc_clk, cnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 2); +DEFINE_CLK_SMD_RPM(msm8994, gfx3d_clk_src, gfx3d_a_clk_src, QCOM_SMD_RPM_MEM_CLK, 1); +DEFINE_CLK_SMD_RPM(msm8994, snoc_clk, snoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8994, bb_clk1, bb_clk1_a, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8994, bb_clk1_pin, bb_clk1_a_pin, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8994, bb_clk2, bb_clk2_a, 2); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8994, bb_clk2_pin, bb_clk2_a_pin, 2); + +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8994, div_clk1, div_clk1_a, 11); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8994, div_clk2, div_clk2_a, 12); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8994, div_clk3, div_clk3_a, 13); +DEFINE_CLK_SMD_RPM(msm8994, ipa_clk, ipa_a_clk, QCOM_SMD_RPM_IPA_CLK, 0); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8994, ln_bb_clk, ln_bb_a_clk, 8); +DEFINE_CLK_SMD_RPM(msm8994, mmssnoc_ahb_clk, mmssnoc_ahb_a_clk, + QCOM_SMD_RPM_BUS_CLK, 3); +DEFINE_CLK_SMD_RPM_QDSS(msm8994, qdss_clk, qdss_a_clk, + QCOM_SMD_RPM_MISC_CLK, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8994, rf_clk1, rf_clk1_a, 4); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8994, rf_clk2, rf_clk2_a, 5); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8994, rf_clk1_pin, rf_clk1_a_pin, 4); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8994, rf_clk2_pin, rf_clk2_a_pin, 5); + +DEFINE_CLK_SMD_RPM(msm8994, ce1_clk, ce1_a_clk, QCOM_SMD_RPM_CE_CLK, 0); +DEFINE_CLK_SMD_RPM(msm8994, ce2_clk, ce2_a_clk, QCOM_SMD_RPM_CE_CLK, 1); +DEFINE_CLK_SMD_RPM(msm8994, ce3_clk, ce3_a_clk, QCOM_SMD_RPM_CE_CLK, 2); + +static struct clk_smd_rpm *msm8994_clks[] = { + [RPM_SMD_PNOC_CLK] = &msm8994_pnoc_clk, + [RPM_SMD_PNOC_A_CLK] = &msm8994_pnoc_a_clk, + [RPM_SMD_OCMEMGX_CLK] = &msm8994_ocmemgx_clk, + [RPM_SMD_OCMEMGX_A_CLK] = &msm8994_ocmemgx_a_clk, + [RPM_SMD_BIMC_CLK] = &msm8994_bimc_clk, + [RPM_SMD_BIMC_A_CLK] = &msm8994_bimc_a_clk, + [RPM_SMD_CNOC_CLK] = &msm8994_cnoc_clk, + [RPM_SMD_CNOC_A_CLK] = &msm8994_cnoc_a_clk, + [RPM_SMD_GFX3D_CLK_SRC] = &msm8994_gfx3d_clk_src, + [RPM_SMD_GFX3D_A_CLK_SRC] = &msm8994_gfx3d_a_clk_src, + [RPM_SMD_SNOC_CLK] = &msm8994_snoc_clk, + [RPM_SMD_SNOC_A_CLK] = &msm8994_snoc_a_clk, + [RPM_SMD_BB_CLK1] = &msm8994_bb_clk1, + [RPM_SMD_BB_CLK1_A] = &msm8994_bb_clk1_a, + [RPM_SMD_BB_CLK1_PIN] = &msm8994_bb_clk1_pin, + [RPM_SMD_BB_CLK1_A_PIN] = &msm8994_bb_clk1_a_pin, + [RPM_SMD_BB_CLK2] = &msm8994_bb_clk2, + [RPM_SMD_BB_CLK2_A] = &msm8994_bb_clk2_a, + [RPM_SMD_BB_CLK2_PIN] = &msm8994_bb_clk2_pin, + [RPM_SMD_BB_CLK2_A_PIN] = &msm8994_bb_clk2_a_pin, + [RPM_SMD_DIV_CLK1] = &msm8994_div_clk1, + [RPM_SMD_DIV_A_CLK1] = &msm8994_div_clk1_a, + [RPM_SMD_DIV_CLK2] = &msm8994_div_clk2, + [RPM_SMD_DIV_A_CLK2] = &msm8994_div_clk2_a, + [RPM_SMD_DIV_CLK3] = &msm8994_div_clk3, + [RPM_SMD_DIV_A_CLK3] = &msm8994_div_clk3_a, + [RPM_SMD_IPA_CLK] = &msm8994_ipa_clk, + [RPM_SMD_IPA_A_CLK] = &msm8994_ipa_a_clk, + [RPM_SMD_LN_BB_CLK] = &msm8994_ln_bb_clk, + [RPM_SMD_LN_BB_A_CLK] = &msm8994_ln_bb_a_clk, + [RPM_SMD_MMSSNOC_AHB_CLK] = &msm8994_mmssnoc_ahb_clk, + [RPM_SMD_MMSSNOC_AHB_A_CLK] = &msm8994_mmssnoc_ahb_a_clk, + [RPM_SMD_QDSS_CLK] = &msm8994_qdss_clk, + [RPM_SMD_QDSS_A_CLK] = &msm8994_qdss_a_clk, + [RPM_SMD_RF_CLK1] = &msm8994_rf_clk1, + [RPM_SMD_RF_CLK1_A] = &msm8994_rf_clk1_a, + [RPM_SMD_RF_CLK2] = &msm8994_rf_clk2, + [RPM_SMD_RF_CLK2_A] = &msm8994_rf_clk2_a, + [RPM_SMD_RF_CLK1_PIN] = &msm8994_rf_clk1_pin, + [RPM_SMD_RF_CLK1_A_PIN] = &msm8994_rf_clk1_a_pin, + [RPM_SMD_RF_CLK2_PIN] = &msm8994_rf_clk2_pin, + [RPM_SMD_RF_CLK2_A_PIN] = &msm8994_rf_clk2_a_pin, + [RPM_SMD_CE1_CLK] = &msm8994_ce1_clk, + [RPM_SMD_CE1_A_CLK] = &msm8994_ce1_a_clk, + [RPM_SMD_CE2_CLK] = &msm8994_ce2_clk, + [RPM_SMD_CE2_A_CLK] = &msm8994_ce2_a_clk, + [RPM_SMD_CE3_CLK] = &msm8994_ce3_clk, + [RPM_SMD_CE3_A_CLK] = &msm8994_ce3_a_clk, +}; + +static const struct rpm_smd_clk_desc rpm_clk_msm8994 = { + .clks = msm8994_clks, + .num_clks = ARRAY_SIZE(msm8994_clks), +}; + /* msm8996 */ DEFINE_CLK_SMD_RPM(msm8996, pcnoc_clk, pcnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 0); DEFINE_CLK_SMD_RPM(msm8996, snoc_clk, snoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 1); @@ -766,13 +984,92 @@ static const struct rpm_smd_clk_desc rpm_clk_msm8998 = { .num_clks = ARRAY_SIZE(msm8998_clks), }; +/* sdm660 */ +DEFINE_CLK_SMD_RPM_BRANCH(sdm660, bi_tcxo, bi_tcxo_a, QCOM_SMD_RPM_MISC_CLK, 0, + 19200000); +DEFINE_CLK_SMD_RPM(sdm660, snoc_clk, snoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 1); +DEFINE_CLK_SMD_RPM(sdm660, cnoc_clk, cnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 2); +DEFINE_CLK_SMD_RPM(sdm660, cnoc_periph_clk, cnoc_periph_a_clk, + QCOM_SMD_RPM_BUS_CLK, 0); +DEFINE_CLK_SMD_RPM(sdm660, bimc_clk, bimc_a_clk, QCOM_SMD_RPM_MEM_CLK, 0); +DEFINE_CLK_SMD_RPM(sdm660, mmssnoc_axi_clk, mmssnoc_axi_a_clk, + QCOM_SMD_RPM_MMAXI_CLK, 0); +DEFINE_CLK_SMD_RPM(sdm660, ipa_clk, ipa_a_clk, QCOM_SMD_RPM_IPA_CLK, 0); +DEFINE_CLK_SMD_RPM(sdm660, ce1_clk, ce1_a_clk, QCOM_SMD_RPM_CE_CLK, 0); +DEFINE_CLK_SMD_RPM(sdm660, aggre2_noc_clk, aggre2_noc_a_clk, + QCOM_SMD_RPM_AGGR_CLK, 2); +DEFINE_CLK_SMD_RPM_QDSS(sdm660, qdss_clk, qdss_a_clk, + QCOM_SMD_RPM_MISC_CLK, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER(sdm660, rf_clk1, rf_clk1_a, 4); +DEFINE_CLK_SMD_RPM_XO_BUFFER(sdm660, div_clk1, div_clk1_a, 11); +DEFINE_CLK_SMD_RPM_XO_BUFFER(sdm660, ln_bb_clk1, ln_bb_clk1_a, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER(sdm660, ln_bb_clk2, ln_bb_clk2_a, 2); +DEFINE_CLK_SMD_RPM_XO_BUFFER(sdm660, ln_bb_clk3, ln_bb_clk3_a, 3); + +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(sdm660, rf_clk1_pin, rf_clk1_a_pin, 4); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(sdm660, ln_bb_clk1_pin, + ln_bb_clk1_pin_a, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(sdm660, ln_bb_clk2_pin, + ln_bb_clk2_pin_a, 2); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(sdm660, ln_bb_clk3_pin, + ln_bb_clk3_pin_a, 3); +static struct clk_smd_rpm *sdm660_clks[] = { + [RPM_SMD_XO_CLK_SRC] = &sdm660_bi_tcxo, + [RPM_SMD_XO_A_CLK_SRC] = &sdm660_bi_tcxo_a, + [RPM_SMD_SNOC_CLK] = &sdm660_snoc_clk, + [RPM_SMD_SNOC_A_CLK] = &sdm660_snoc_a_clk, + [RPM_SMD_CNOC_CLK] = &sdm660_cnoc_clk, + [RPM_SMD_CNOC_A_CLK] = &sdm660_cnoc_a_clk, + [RPM_SMD_CNOC_PERIPH_CLK] = &sdm660_cnoc_periph_clk, + [RPM_SMD_CNOC_PERIPH_A_CLK] = &sdm660_cnoc_periph_a_clk, + [RPM_SMD_BIMC_CLK] = &sdm660_bimc_clk, + [RPM_SMD_BIMC_A_CLK] = &sdm660_bimc_a_clk, + [RPM_SMD_MMSSNOC_AXI_CLK] = &sdm660_mmssnoc_axi_clk, + [RPM_SMD_MMSSNOC_AXI_CLK_A] = &sdm660_mmssnoc_axi_a_clk, + [RPM_SMD_IPA_CLK] = &sdm660_ipa_clk, + [RPM_SMD_IPA_A_CLK] = &sdm660_ipa_a_clk, + [RPM_SMD_CE1_CLK] = &sdm660_ce1_clk, + [RPM_SMD_CE1_A_CLK] = &sdm660_ce1_a_clk, + [RPM_SMD_AGGR2_NOC_CLK] = &sdm660_aggre2_noc_clk, + [RPM_SMD_AGGR2_NOC_A_CLK] = &sdm660_aggre2_noc_a_clk, + [RPM_SMD_QDSS_CLK] = &sdm660_qdss_clk, + [RPM_SMD_QDSS_A_CLK] = &sdm660_qdss_a_clk, + [RPM_SMD_RF_CLK1] = &sdm660_rf_clk1, + [RPM_SMD_RF_CLK1_A] = &sdm660_rf_clk1_a, + [RPM_SMD_DIV_CLK1] = &sdm660_div_clk1, + [RPM_SMD_DIV_A_CLK1] = &sdm660_div_clk1_a, + [RPM_SMD_LN_BB_CLK] = &sdm660_ln_bb_clk1, + [RPM_SMD_LN_BB_A_CLK] = &sdm660_ln_bb_clk1_a, + [RPM_SMD_LN_BB_CLK2] = &sdm660_ln_bb_clk2, + [RPM_SMD_LN_BB_CLK2_A] = &sdm660_ln_bb_clk2_a, + [RPM_SMD_LN_BB_CLK3] = &sdm660_ln_bb_clk3, + [RPM_SMD_LN_BB_CLK3_A] = &sdm660_ln_bb_clk3_a, + [RPM_SMD_RF_CLK1_PIN] = &sdm660_rf_clk1_pin, + [RPM_SMD_RF_CLK1_A_PIN] = &sdm660_rf_clk1_a_pin, + [RPM_SMD_LN_BB_CLK1_PIN] = &sdm660_ln_bb_clk1_pin, + [RPM_SMD_LN_BB_CLK1_A_PIN] = &sdm660_ln_bb_clk1_pin_a, + [RPM_SMD_LN_BB_CLK2_PIN] = &sdm660_ln_bb_clk2_pin, + [RPM_SMD_LN_BB_CLK2_A_PIN] = &sdm660_ln_bb_clk2_pin_a, + [RPM_SMD_LN_BB_CLK3_PIN] = &sdm660_ln_bb_clk3_pin, + [RPM_SMD_LN_BB_CLK3_A_PIN] = &sdm660_ln_bb_clk3_pin_a, +}; + +static const struct rpm_smd_clk_desc rpm_clk_sdm660 = { + .clks = sdm660_clks, + .num_clks = ARRAY_SIZE(sdm660_clks), +}; + static const struct of_device_id rpm_smd_clk_match_table[] = { { .compatible = "qcom,rpmcc-msm8916", .data = &rpm_clk_msm8916 }, + { .compatible = "qcom,rpmcc-msm8936", .data = &rpm_clk_msm8936 }, { .compatible = "qcom,rpmcc-msm8974", .data = &rpm_clk_msm8974 }, { .compatible = "qcom,rpmcc-msm8976", .data = &rpm_clk_msm8976 }, + { .compatible = "qcom,rpmcc-msm8992", .data = &rpm_clk_msm8992 }, + { .compatible = "qcom,rpmcc-msm8994", .data = &rpm_clk_msm8994 }, { .compatible = "qcom,rpmcc-msm8996", .data = &rpm_clk_msm8996 }, { .compatible = "qcom,rpmcc-msm8998", .data = &rpm_clk_msm8998 }, { .compatible = "qcom,rpmcc-qcs404", .data = &rpm_clk_qcs404 }, + { .compatible = "qcom,rpmcc-sdm660", .data = &rpm_clk_sdm660 }, { } }; MODULE_DEVICE_TABLE(of, rpm_smd_clk_match_table); diff --git a/drivers/clk/qcom/gcc-ipq806x.c b/drivers/clk/qcom/gcc-ipq806x.c index a8456e09c44d..d6b7adb4be38 100644 --- a/drivers/clk/qcom/gcc-ipq806x.c +++ b/drivers/clk/qcom/gcc-ipq806x.c @@ -3089,7 +3089,7 @@ static int gcc_ipq806x_probe(struct platform_device *pdev) regmap_write(regmap, 0x3cf8, 8); regmap_write(regmap, 0x3d18, 8); - return 0; + return of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); } static struct platform_driver gcc_ipq806x_driver = { diff --git a/drivers/clk/qcom/gcc-ipq8074.c b/drivers/clk/qcom/gcc-ipq8074.c index e01f5f591d1e..ef2c9c4cf9ab 100644 --- a/drivers/clk/qcom/gcc-ipq8074.c +++ b/drivers/clk/qcom/gcc-ipq8074.c @@ -4316,6 +4316,62 @@ static struct clk_branch gcc_gp3_clk = { }, }; +static const struct freq_tbl ftbl_pcie_rchng_clk_src[] = { + F(19200000, P_XO, 1, 0, 0), + F(100000000, P_GPLL0, 8, 0, 0), + { } +}; + +struct clk_rcg2 pcie0_rchng_clk_src = { + .cmd_rcgr = 0x75070, + .freq_tbl = ftbl_pcie_rchng_clk_src, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "pcie0_rchng_clk_src", + .parent_hws = (const struct clk_hw *[]) { + &gpll0.clkr.hw }, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_branch gcc_pcie0_rchng_clk = { + .halt_reg = 0x75070, + .halt_bit = 31, + .clkr = { + .enable_reg = 0x75070, + .enable_mask = BIT(1), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pcie0_rchng_clk", + .parent_hws = (const struct clk_hw *[]){ + &pcie0_rchng_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie0_axi_s_bridge_clk = { + .halt_reg = 0x75048, + .halt_bit = 31, + .clkr = { + .enable_reg = 0x75048, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pcie0_axi_s_bridge_clk", + .parent_hws = (const struct clk_hw *[]){ + &pcie0_axi_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + static struct clk_hw *gcc_ipq8074_hws[] = { &gpll0_out_main_div2.hw, &gpll6_out_main_div2.hw, @@ -4551,6 +4607,9 @@ static struct clk_regmap *gcc_ipq8074_clks[] = { [GCC_GP1_CLK] = &gcc_gp1_clk.clkr, [GCC_GP2_CLK] = &gcc_gp2_clk.clkr, [GCC_GP3_CLK] = &gcc_gp3_clk.clkr, + [GCC_PCIE0_RCHNG_CLK_SRC] = &pcie0_rchng_clk_src.clkr, + [GCC_PCIE0_RCHNG_CLK] = &gcc_pcie0_rchng_clk.clkr, + [GCC_PCIE0_AXI_S_BRIDGE_CLK] = &gcc_pcie0_axi_s_bridge_clk.clkr, }; static const struct qcom_reset_map gcc_ipq8074_resets[] = { @@ -4678,6 +4737,7 @@ static const struct qcom_reset_map gcc_ipq8074_resets[] = { [GCC_PCIE0_AXI_SLAVE_ARES] = { 0x75040, 4 }, [GCC_PCIE0_AHB_ARES] = { 0x75040, 5 }, [GCC_PCIE0_AXI_MASTER_STICKY_ARES] = { 0x75040, 6 }, + [GCC_PCIE0_AXI_SLAVE_STICKY_ARES] = { 0x75040, 7 }, [GCC_PCIE1_PIPE_ARES] = { 0x76040, 0 }, [GCC_PCIE1_SLEEP_ARES] = { 0x76040, 1 }, [GCC_PCIE1_CORE_STICKY_ARES] = { 0x76040, 2 }, diff --git a/drivers/clk/qcom/gcc-sc7180.c b/drivers/clk/qcom/gcc-sc7180.c index ca4383e3a02a..68d8f7aaf64e 100644 --- a/drivers/clk/qcom/gcc-sc7180.c +++ b/drivers/clk/qcom/gcc-sc7180.c @@ -1061,7 +1061,7 @@ static struct clk_branch gcc_disp_gpll0_clk_src = { .hw = &gpll0.clkr.hw, }, .num_parents = 1, - .ops = &clk_branch2_ops, + .ops = &clk_branch2_aon_ops, }, }, }; @@ -2251,6 +2251,19 @@ static struct clk_branch gcc_mss_q6_memnoc_axi_clk = { }, }; +static struct clk_branch gcc_lpass_cfg_noc_sway_clk = { + .halt_reg = 0x47018, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x47018, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_lpass_cfg_noc_sway_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + static struct gdsc ufs_phy_gdsc = { .gdscr = 0x77004, .pd = { @@ -2428,6 +2441,7 @@ static struct clk_regmap *gcc_sc7180_clocks[] = { [GCC_MSS_Q6_MEMNOC_AXI_CLK] = &gcc_mss_q6_memnoc_axi_clk.clkr, [GCC_MSS_SNOC_AXI_CLK] = &gcc_mss_snoc_axi_clk.clkr, [GCC_SEC_CTRL_CLK_SRC] = &gcc_sec_ctrl_clk_src.clkr, + [GCC_LPASS_CFG_NOC_SWAY_CLK] = &gcc_lpass_cfg_noc_sway_clk.clkr, }; static const struct qcom_reset_map gcc_sc7180_resets[] = { diff --git a/drivers/clk/qcom/gcc-sdm660.c b/drivers/clk/qcom/gcc-sdm660.c index bf5730832ef3..f0b47b7d50ca 100644 --- a/drivers/clk/qcom/gcc-sdm660.c +++ b/drivers/clk/qcom/gcc-sdm660.c @@ -1715,6 +1715,9 @@ static struct clk_branch gcc_mss_cfg_ahb_clk = { static struct clk_branch gcc_mss_mnoc_bimc_axi_clk = { .halt_reg = 0x8a004, + .halt_check = BRANCH_HALT, + .hwcg_reg = 0x8a004, + .hwcg_bit = 1, .clkr = { .enable_reg = 0x8a004, .enable_mask = BIT(0), @@ -2402,6 +2405,7 @@ static const struct qcom_reset_map gcc_sdm660_resets[] = { [GCC_USB_20_BCR] = { 0x2f000 }, [GCC_USB_30_BCR] = { 0xf000 }, [GCC_USB_PHY_CFG_AHB2PHY_BCR] = { 0x6a000 }, + [GCC_MSS_RESTART] = { 0x79000 }, }; static const struct regmap_config gcc_sdm660_regmap_config = { diff --git a/drivers/clk/qcom/gcc-sdm845.c b/drivers/clk/qcom/gcc-sdm845.c index f6ce888098be..90f7febaf528 100644 --- a/drivers/clk/qcom/gcc-sdm845.c +++ b/drivers/clk/qcom/gcc-sdm845.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2018, 2020, The Linux Foundation. All rights reserved. */ #include <linux/kernel.h> @@ -1344,7 +1344,7 @@ static struct clk_branch gcc_disp_gpll0_clk_src = { "gpll0", }, .num_parents = 1, - .ops = &clk_branch2_ops, + .ops = &clk_branch2_aon_ops, }, }, }; diff --git a/drivers/clk/qcom/gcc-sm8150.c b/drivers/clk/qcom/gcc-sm8150.c index 72524cf11048..8e9b5b3cceaf 100644 --- a/drivers/clk/qcom/gcc-sm8150.c +++ b/drivers/clk/qcom/gcc-sm8150.c @@ -34,14 +34,8 @@ enum { P_SLEEP_CLK, }; -static const struct pll_vco trion_vco[] = { - { 249600000, 2000000000, 0 }, -}; - static struct clk_alpha_pll gpll0 = { .offset = 0x0, - .vco_table = trion_vco, - .num_vco = ARRAY_SIZE(trion_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_TRION], .clkr = { .enable_reg = 0x52000, @@ -53,7 +47,7 @@ static struct clk_alpha_pll gpll0 = { .name = "bi_tcxo", }, .num_parents = 1, - .ops = &clk_trion_fixed_pll_ops, + .ops = &clk_alpha_pll_fixed_trion_ops, }, }, }; @@ -79,14 +73,12 @@ static struct clk_alpha_pll_postdiv gpll0_out_even = { .hw = &gpll0.clkr.hw, }, .num_parents = 1, - .ops = &clk_trion_pll_postdiv_ops, + .ops = &clk_alpha_pll_postdiv_trion_ops, }, }; static struct clk_alpha_pll gpll7 = { .offset = 0x1a000, - .vco_table = trion_vco, - .num_vco = ARRAY_SIZE(trion_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_TRION], .clkr = { .enable_reg = 0x52000, @@ -98,15 +90,13 @@ static struct clk_alpha_pll gpll7 = { .name = "bi_tcxo", }, .num_parents = 1, - .ops = &clk_trion_fixed_pll_ops, + .ops = &clk_alpha_pll_fixed_trion_ops, }, }, }; static struct clk_alpha_pll gpll9 = { .offset = 0x1c000, - .vco_table = trion_vco, - .num_vco = ARRAY_SIZE(trion_vco), .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_TRION], .clkr = { .enable_reg = 0x52000, @@ -118,7 +108,7 @@ static struct clk_alpha_pll gpll9 = { .name = "bi_tcxo", }, .num_parents = 1, - .ops = &clk_trion_fixed_pll_ops, + .ops = &clk_alpha_pll_fixed_trion_ops, }, }, }; @@ -1617,6 +1607,7 @@ static struct clk_branch gcc_gpu_cfg_ahb_clk = { }; static struct clk_branch gcc_gpu_gpll0_clk_src = { + .halt_check = BRANCH_HALT_SKIP, .clkr = { .enable_reg = 0x52004, .enable_mask = BIT(15), @@ -1632,13 +1623,14 @@ static struct clk_branch gcc_gpu_gpll0_clk_src = { }; static struct clk_branch gcc_gpu_gpll0_div_clk_src = { + .halt_check = BRANCH_HALT_SKIP, .clkr = { .enable_reg = 0x52004, .enable_mask = BIT(16), .hw.init = &(struct clk_init_data){ .name = "gcc_gpu_gpll0_div_clk_src", .parent_hws = (const struct clk_hw *[]){ - &gcc_gpu_gpll0_clk_src.clkr.hw }, + &gpll0_out_even.clkr.hw }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -1729,6 +1721,7 @@ static struct clk_branch gcc_npu_cfg_ahb_clk = { }; static struct clk_branch gcc_npu_gpll0_clk_src = { + .halt_check = BRANCH_HALT_SKIP, .clkr = { .enable_reg = 0x52004, .enable_mask = BIT(18), @@ -1744,13 +1737,14 @@ static struct clk_branch gcc_npu_gpll0_clk_src = { }; static struct clk_branch gcc_npu_gpll0_div_clk_src = { + .halt_check = BRANCH_HALT_SKIP, .clkr = { .enable_reg = 0x52004, .enable_mask = BIT(19), .hw.init = &(struct clk_init_data){ .name = "gcc_npu_gpll0_div_clk_src", .parent_hws = (const struct clk_hw *[]){ - &gcc_npu_gpll0_clk_src.clkr.hw }, + &gpll0_out_even.clkr.hw }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, diff --git a/drivers/clk/qcom/gdsc.c b/drivers/clk/qcom/gdsc.c index 04944f11659b..bfc4ac02f9ea 100644 --- a/drivers/clk/qcom/gdsc.c +++ b/drivers/clk/qcom/gdsc.c @@ -6,6 +6,7 @@ #include <linux/bitops.h> #include <linux/delay.h> #include <linux/err.h> +#include <linux/export.h> #include <linux/jiffies.h> #include <linux/kernel.h> #include <linux/ktime.h> @@ -29,6 +30,7 @@ /* CFG_GDSCR */ #define GDSC_POWER_UP_COMPLETE BIT(16) #define GDSC_POWER_DOWN_COMPLETE BIT(15) +#define GDSC_RETAIN_FF_ENABLE BIT(11) #define CFG_GDSCR_OFFSET 0x4 /* Wait 2^n CXO cycles between all states. Here, n=2 (4 cycles). */ @@ -216,6 +218,14 @@ static inline void gdsc_assert_reset_aon(struct gdsc *sc) regmap_update_bits(sc->regmap, sc->clamp_io_ctrl, GMEM_RESET_MASK, 0); } + +static void gdsc_retain_ff_on(struct gdsc *sc) +{ + u32 mask = GDSC_RETAIN_FF_ENABLE; + + regmap_update_bits(sc->regmap, sc->gdscr, mask, mask); +} + static int gdsc_enable(struct generic_pm_domain *domain) { struct gdsc *sc = domain_to_gdsc(domain); @@ -268,6 +278,9 @@ static int gdsc_enable(struct generic_pm_domain *domain) udelay(1); } + if (sc->flags & RETAIN_FF_ENABLE) + gdsc_retain_ff_on(sc); + return 0; } @@ -433,3 +446,29 @@ void gdsc_unregister(struct gdsc_desc *desc) } of_genpd_del_provider(dev->of_node); } + +/* + * On SDM845+ the GPU GX domain is *almost* entirely controlled by the GMU + * running in the CX domain so the CPU doesn't need to know anything about the + * GX domain EXCEPT.... + * + * Hardware constraints dictate that the GX be powered down before the CX. If + * the GMU crashes it could leave the GX on. In order to successfully bring back + * the device the CPU needs to disable the GX headswitch. There being no sane + * way to reach in and touch that register from deep inside the GPU driver we + * need to set up the infrastructure to be able to ensure that the GPU can + * ensure that the GX is off during this super special case. We do this by + * defining a GX gdsc with a dummy enable function and a "default" disable + * function. + * + * This allows us to attach with genpd_dev_pm_attach_by_name() in the GPU + * driver. During power up, nothing will happen from the CPU (and the GMU will + * power up normally but during power down this will ensure that the GX domain + * is *really* off - this gives us a semi standard way of doing what we need. + */ +int gdsc_gx_do_nothing_enable(struct generic_pm_domain *domain) +{ + /* Do nothing but give genpd the impression that we were successful */ + return 0; +} +EXPORT_SYMBOL_GPL(gdsc_gx_do_nothing_enable); diff --git a/drivers/clk/qcom/gdsc.h b/drivers/clk/qcom/gdsc.h index c36fc26dcdff..bd537438c793 100644 --- a/drivers/clk/qcom/gdsc.h +++ b/drivers/clk/qcom/gdsc.h @@ -50,6 +50,7 @@ struct gdsc { #define AON_RESET BIT(4) #define POLL_CFG_GDSCR BIT(5) #define ALWAYS_ON BIT(6) +#define RETAIN_FF_ENABLE BIT(7) struct reset_controller_dev *rcdev; unsigned int *resets; unsigned int reset_count; @@ -68,6 +69,7 @@ struct gdsc_desc { int gdsc_register(struct gdsc_desc *desc, struct reset_controller_dev *, struct regmap *); void gdsc_unregister(struct gdsc_desc *desc); +int gdsc_gx_do_nothing_enable(struct generic_pm_domain *domain); #else static inline int gdsc_register(struct gdsc_desc *desc, struct reset_controller_dev *rcdev, diff --git a/drivers/clk/qcom/gpucc-sc7180.c b/drivers/clk/qcom/gpucc-sc7180.c index 7b656b6aeced..88a739b6fec3 100644 --- a/drivers/clk/qcom/gpucc-sc7180.c +++ b/drivers/clk/qcom/gpucc-sc7180.c @@ -170,37 +170,12 @@ static struct gdsc cx_gdsc = { .flags = VOTABLE, }; -/* - * On SC7180 the GPU GX domain is *almost* entirely controlled by the GMU - * running in the CX domain so the CPU doesn't need to know anything about the - * GX domain EXCEPT.... - * - * Hardware constraints dictate that the GX be powered down before the CX. If - * the GMU crashes it could leave the GX on. In order to successfully bring back - * the device the CPU needs to disable the GX headswitch. There being no sane - * way to reach in and touch that register from deep inside the GPU driver we - * need to set up the infrastructure to be able to ensure that the GPU can - * ensure that the GX is off during this super special case. We do this by - * defining a GX gdsc with a dummy enable function and a "default" disable - * function. - * - * This allows us to attach with genpd_dev_pm_attach_by_name() in the GPU - * driver. During power up, nothing will happen from the CPU (and the GMU will - * power up normally but during power down this will ensure that the GX domain - * is *really* off - this gives us a semi standard way of doing what we need. - */ -static int gx_gdsc_enable(struct generic_pm_domain *domain) -{ - /* Do nothing but give genpd the impression that we were successful */ - return 0; -} - static struct gdsc gx_gdsc = { .gdscr = 0x100c, .clamp_io_ctrl = 0x1508, .pd = { .name = "gx_gdsc", - .power_on = gx_gdsc_enable, + .power_on = gdsc_gx_do_nothing_enable, }, .pwrsts = PWRSTS_OFF_ON, .flags = CLAMP_IO, diff --git a/drivers/clk/qcom/gpucc-sdm845.c b/drivers/clk/qcom/gpucc-sdm845.c index e40efba1bf7d..5663698b306b 100644 --- a/drivers/clk/qcom/gpucc-sdm845.c +++ b/drivers/clk/qcom/gpucc-sdm845.c @@ -131,37 +131,12 @@ static struct gdsc gpu_cx_gdsc = { .flags = VOTABLE, }; -/* - * On SDM845 the GPU GX domain is *almost* entirely controlled by the GMU - * running in the CX domain so the CPU doesn't need to know anything about the - * GX domain EXCEPT.... - * - * Hardware constraints dictate that the GX be powered down before the CX. If - * the GMU crashes it could leave the GX on. In order to successfully bring back - * the device the CPU needs to disable the GX headswitch. There being no sane - * way to reach in and touch that register from deep inside the GPU driver we - * need to set up the infrastructure to be able to ensure that the GPU can - * ensure that the GX is off during this super special case. We do this by - * defining a GX gdsc with a dummy enable function and a "default" disable - * function. - * - * This allows us to attach with genpd_dev_pm_attach_by_name() in the GPU - * driver. During power up, nothing will happen from the CPU (and the GMU will - * power up normally but during power down this will ensure that the GX domain - * is *really* off - this gives us a semi standard way of doing what we need. - */ -static int gx_gdsc_enable(struct generic_pm_domain *domain) -{ - /* Do nothing but give genpd the impression that we were successful */ - return 0; -} - static struct gdsc gpu_gx_gdsc = { .gdscr = 0x100c, .clamp_io_ctrl = 0x1508, .pd = { .name = "gpu_gx_gdsc", - .power_on = gx_gdsc_enable, + .power_on = gdsc_gx_do_nothing_enable, }, .pwrsts = PWRSTS_OFF_ON, .flags = CLAMP_IO | AON_RESET | POLL_CFG_GDSCR, diff --git a/drivers/clk/qcom/gpucc-sm8150.c b/drivers/clk/qcom/gpucc-sm8150.c new file mode 100644 index 000000000000..27c40754b2c7 --- /dev/null +++ b/drivers/clk/qcom/gpucc-sm8150.c @@ -0,0 +1,320 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. + */ + +#include <linux/clk-provider.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#include <dt-bindings/clock/qcom,gpucc-sm8150.h> + +#include "common.h" +#include "clk-alpha-pll.h" +#include "clk-branch.h" +#include "clk-pll.h" +#include "clk-rcg.h" +#include "clk-regmap.h" +#include "reset.h" +#include "gdsc.h" + +enum { + P_BI_TCXO, + P_CORE_BI_PLL_TEST_SE, + P_GPLL0_OUT_MAIN, + P_GPLL0_OUT_MAIN_DIV, + P_GPU_CC_PLL1_OUT_MAIN, +}; + +static const struct pll_vco trion_vco[] = { + { 249600000, 2000000000, 0 }, +}; + +static struct alpha_pll_config gpu_cc_pll1_config = { + .l = 0x1a, + .alpha = 0xaaa, + .config_ctl_val = 0x20485699, + .config_ctl_hi_val = 0x00002267, + .config_ctl_hi1_val = 0x00000024, + .test_ctl_val = 0x00000000, + .test_ctl_hi_val = 0x00000002, + .test_ctl_hi1_val = 0x00000000, + .user_ctl_val = 0x00000000, + .user_ctl_hi_val = 0x00000805, + .user_ctl_hi1_val = 0x000000d0, +}; + +static struct clk_alpha_pll gpu_cc_pll1 = { + .offset = 0x100, + .vco_table = trion_vco, + .num_vco = ARRAY_SIZE(trion_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_TRION], + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_pll1", + .parent_data = &(const struct clk_parent_data){ + .fw_name = "bi_tcxo", + }, + .num_parents = 1, + .ops = &clk_alpha_pll_trion_ops, + }, + }, +}; + +static const struct parent_map gpu_cc_parent_map_0[] = { + { P_BI_TCXO, 0 }, + { P_GPU_CC_PLL1_OUT_MAIN, 3 }, + { P_GPLL0_OUT_MAIN, 5 }, + { P_GPLL0_OUT_MAIN_DIV, 6 }, +}; + +static const struct clk_parent_data gpu_cc_parent_data_0[] = { + { .fw_name = "bi_tcxo" }, + { .hw = &gpu_cc_pll1.clkr.hw }, + { .fw_name = "gcc_gpu_gpll0_clk_src" }, + { .fw_name = "gcc_gpu_gpll0_div_clk_src" }, +}; + +static const struct freq_tbl ftbl_gpu_cc_gmu_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(200000000, P_GPLL0_OUT_MAIN_DIV, 1.5, 0, 0), + F(500000000, P_GPU_CC_PLL1_OUT_MAIN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 gpu_cc_gmu_clk_src = { + .cmd_rcgr = 0x1120, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gpu_cc_parent_map_0, + .freq_tbl = ftbl_gpu_cc_gmu_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gpu_cc_gmu_clk_src", + .parent_data = gpu_cc_parent_data_0, + .num_parents = ARRAY_SIZE(gpu_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_branch gpu_cc_ahb_clk = { + .halt_reg = 0x1078, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x1078, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_crc_ahb_clk = { + .halt_reg = 0x107c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x107c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_crc_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_cx_apb_clk = { + .halt_reg = 0x1088, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1088, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_cx_apb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_cx_gmu_clk = { + .halt_reg = 0x1098, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1098, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_cx_gmu_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &gpu_cc_gmu_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_cx_snoc_dvm_clk = { + .halt_reg = 0x108c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x108c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_cx_snoc_dvm_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_cxo_aon_clk = { + .halt_reg = 0x1004, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1004, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_cxo_aon_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_cxo_clk = { + .halt_reg = 0x109c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x109c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_cxo_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_gx_gmu_clk = { + .halt_reg = 0x1064, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1064, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_gx_gmu_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &gpu_cc_gmu_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct gdsc gpu_cx_gdsc = { + .gdscr = 0x106c, + .gds_hw_ctrl = 0x1540, + .pd = { + .name = "gpu_cx_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = VOTABLE, +}; + +static struct gdsc gpu_gx_gdsc = { + .gdscr = 0x100c, + .clamp_io_ctrl = 0x1508, + .pd = { + .name = "gpu_gx_gdsc", + .power_on = gdsc_gx_do_nothing_enable, + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = CLAMP_IO | AON_RESET | POLL_CFG_GDSCR, +}; + +static struct clk_regmap *gpu_cc_sm8150_clocks[] = { + [GPU_CC_AHB_CLK] = &gpu_cc_ahb_clk.clkr, + [GPU_CC_CRC_AHB_CLK] = &gpu_cc_crc_ahb_clk.clkr, + [GPU_CC_CX_APB_CLK] = &gpu_cc_cx_apb_clk.clkr, + [GPU_CC_CX_GMU_CLK] = &gpu_cc_cx_gmu_clk.clkr, + [GPU_CC_CX_SNOC_DVM_CLK] = &gpu_cc_cx_snoc_dvm_clk.clkr, + [GPU_CC_CXO_AON_CLK] = &gpu_cc_cxo_aon_clk.clkr, + [GPU_CC_CXO_CLK] = &gpu_cc_cxo_clk.clkr, + [GPU_CC_GMU_CLK_SRC] = &gpu_cc_gmu_clk_src.clkr, + [GPU_CC_GX_GMU_CLK] = &gpu_cc_gx_gmu_clk.clkr, + [GPU_CC_PLL1] = &gpu_cc_pll1.clkr, +}; + +static const struct qcom_reset_map gpu_cc_sm8150_resets[] = { + [GPUCC_GPU_CC_CX_BCR] = { 0x1068 }, + [GPUCC_GPU_CC_GMU_BCR] = { 0x111c }, + [GPUCC_GPU_CC_GX_BCR] = { 0x1008 }, + [GPUCC_GPU_CC_SPDM_BCR] = { 0x1110 }, + [GPUCC_GPU_CC_XO_BCR] = { 0x1000 }, +}; + +static struct gdsc *gpu_cc_sm8150_gdscs[] = { + [GPU_CX_GDSC] = &gpu_cx_gdsc, + [GPU_GX_GDSC] = &gpu_gx_gdsc, +}; + +static const struct regmap_config gpu_cc_sm8150_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x8008, + .fast_io = true, +}; + +static const struct qcom_cc_desc gpu_cc_sm8150_desc = { + .config = &gpu_cc_sm8150_regmap_config, + .clks = gpu_cc_sm8150_clocks, + .num_clks = ARRAY_SIZE(gpu_cc_sm8150_clocks), + .resets = gpu_cc_sm8150_resets, + .num_resets = ARRAY_SIZE(gpu_cc_sm8150_resets), + .gdscs = gpu_cc_sm8150_gdscs, + .num_gdscs = ARRAY_SIZE(gpu_cc_sm8150_gdscs), +}; + +static const struct of_device_id gpu_cc_sm8150_match_table[] = { + { .compatible = "qcom,sm8150-gpucc" }, + { } +}; +MODULE_DEVICE_TABLE(of, gpu_cc_sm8150_match_table); + +static int gpu_cc_sm8150_probe(struct platform_device *pdev) +{ + struct regmap *regmap; + + regmap = qcom_cc_map(pdev, &gpu_cc_sm8150_desc); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + clk_trion_pll_configure(&gpu_cc_pll1, regmap, &gpu_cc_pll1_config); + + return qcom_cc_really_probe(pdev, &gpu_cc_sm8150_desc, regmap); +} + +static struct platform_driver gpu_cc_sm8150_driver = { + .probe = gpu_cc_sm8150_probe, + .driver = { + .name = "sm8150-gpucc", + .of_match_table = gpu_cc_sm8150_match_table, + }, +}; + +static int __init gpu_cc_sm8150_init(void) +{ + return platform_driver_register(&gpu_cc_sm8150_driver); +} +subsys_initcall(gpu_cc_sm8150_init); + +static void __exit gpu_cc_sm8150_exit(void) +{ + platform_driver_unregister(&gpu_cc_sm8150_driver); +} +module_exit(gpu_cc_sm8150_exit); + +MODULE_DESCRIPTION("QTI GPUCC SM8150 Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/qcom/gpucc-sm8250.c b/drivers/clk/qcom/gpucc-sm8250.c new file mode 100644 index 000000000000..3fa7d1f9ff98 --- /dev/null +++ b/drivers/clk/qcom/gpucc-sm8250.c @@ -0,0 +1,348 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. + */ + +#include <linux/clk-provider.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#include <dt-bindings/clock/qcom,gpucc-sm8250.h> + +#include "common.h" +#include "clk-alpha-pll.h" +#include "clk-branch.h" +#include "clk-pll.h" +#include "clk-rcg.h" +#include "clk-regmap.h" +#include "reset.h" +#include "gdsc.h" + +#define CX_GMU_CBCR_SLEEP_MASK 0xf +#define CX_GMU_CBCR_SLEEP_SHIFT 4 +#define CX_GMU_CBCR_WAKE_MASK 0xf +#define CX_GMU_CBCR_WAKE_SHIFT 8 + +enum { + P_BI_TCXO, + P_CORE_BI_PLL_TEST_SE, + P_GPLL0_OUT_MAIN, + P_GPLL0_OUT_MAIN_DIV, + P_GPU_CC_PLL0_OUT_MAIN, + P_GPU_CC_PLL1_OUT_MAIN, +}; + +static struct pll_vco lucid_vco[] = { + { 249600000, 2000000000, 0 }, +}; + +static const struct alpha_pll_config gpu_cc_pll1_config = { + .l = 0x1a, + .alpha = 0xaaa, + .config_ctl_val = 0x20485699, + .config_ctl_hi_val = 0x00002261, + .config_ctl_hi1_val = 0x029a699c, + .user_ctl_val = 0x00000000, + .user_ctl_hi_val = 0x00000805, + .user_ctl_hi1_val = 0x00000000, +}; + +static struct clk_alpha_pll gpu_cc_pll1 = { + .offset = 0x100, + .vco_table = lucid_vco, + .num_vco = ARRAY_SIZE(lucid_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID], + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_pll1", + .parent_data = &(const struct clk_parent_data){ + .fw_name = "bi_tcxo", + }, + .num_parents = 1, + .ops = &clk_alpha_pll_lucid_ops, + }, + }, +}; + +static const struct parent_map gpu_cc_parent_map_0[] = { + { P_BI_TCXO, 0 }, + { P_GPU_CC_PLL1_OUT_MAIN, 3 }, + { P_GPLL0_OUT_MAIN, 5 }, + { P_GPLL0_OUT_MAIN_DIV, 6 }, +}; + +static const struct clk_parent_data gpu_cc_parent_data_0[] = { + { .fw_name = "bi_tcxo" }, + { .hw = &gpu_cc_pll1.clkr.hw }, + { .fw_name = "gcc_gpu_gpll0_clk_src" }, + { .fw_name = "gcc_gpu_gpll0_div_clk_src" }, +}; + +static const struct freq_tbl ftbl_gpu_cc_gmu_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(200000000, P_GPLL0_OUT_MAIN_DIV, 1.5, 0, 0), + F(500000000, P_GPU_CC_PLL1_OUT_MAIN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 gpu_cc_gmu_clk_src = { + .cmd_rcgr = 0x1120, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gpu_cc_parent_map_0, + .freq_tbl = ftbl_gpu_cc_gmu_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gpu_cc_gmu_clk_src", + .parent_data = gpu_cc_parent_data_0, + .num_parents = ARRAY_SIZE(gpu_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_branch gpu_cc_ahb_clk = { + .halt_reg = 0x1078, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x1078, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_crc_ahb_clk = { + .halt_reg = 0x107c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x107c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_crc_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_cx_apb_clk = { + .halt_reg = 0x1088, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x1088, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_cx_apb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_cx_gmu_clk = { + .halt_reg = 0x1098, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1098, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_cx_gmu_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &gpu_cc_gmu_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_cx_snoc_dvm_clk = { + .halt_reg = 0x108c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x108c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_cx_snoc_dvm_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_cxo_aon_clk = { + .halt_reg = 0x1004, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x1004, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_cxo_aon_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_cxo_clk = { + .halt_reg = 0x109c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x109c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_cxo_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_gx_gmu_clk = { + .halt_reg = 0x1064, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1064, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_gx_gmu_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &gpu_cc_gmu_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_hlos1_vote_gpu_smmu_clk = { + .halt_reg = 0x5000, + .halt_check = BRANCH_VOTED, + .clkr = { + .enable_reg = 0x5000, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_hlos1_vote_gpu_smmu_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct gdsc gpu_cx_gdsc = { + .gdscr = 0x106c, + .gds_hw_ctrl = 0x1540, + .pd = { + .name = "gpu_cx_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = VOTABLE, +}; + +static struct gdsc gpu_gx_gdsc = { + .gdscr = 0x100c, + .clamp_io_ctrl = 0x1508, + .pd = { + .name = "gpu_gx_gdsc", + .power_on = gdsc_gx_do_nothing_enable, + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = CLAMP_IO | AON_RESET | POLL_CFG_GDSCR, +}; + +static struct clk_regmap *gpu_cc_sm8250_clocks[] = { + [GPU_CC_AHB_CLK] = &gpu_cc_ahb_clk.clkr, + [GPU_CC_CRC_AHB_CLK] = &gpu_cc_crc_ahb_clk.clkr, + [GPU_CC_CX_APB_CLK] = &gpu_cc_cx_apb_clk.clkr, + [GPU_CC_CX_GMU_CLK] = &gpu_cc_cx_gmu_clk.clkr, + [GPU_CC_CX_SNOC_DVM_CLK] = &gpu_cc_cx_snoc_dvm_clk.clkr, + [GPU_CC_CXO_AON_CLK] = &gpu_cc_cxo_aon_clk.clkr, + [GPU_CC_CXO_CLK] = &gpu_cc_cxo_clk.clkr, + [GPU_CC_GMU_CLK_SRC] = &gpu_cc_gmu_clk_src.clkr, + [GPU_CC_GX_GMU_CLK] = &gpu_cc_gx_gmu_clk.clkr, + [GPU_CC_PLL1] = &gpu_cc_pll1.clkr, + [GPU_CC_HLOS1_VOTE_GPU_SMMU_CLK] = &gpu_cc_hlos1_vote_gpu_smmu_clk.clkr, +}; + +static const struct qcom_reset_map gpu_cc_sm8250_resets[] = { + [GPUCC_GPU_CC_ACD_BCR] = { 0x1160 }, + [GPUCC_GPU_CC_CX_BCR] = { 0x1068 }, + [GPUCC_GPU_CC_GFX3D_AON_BCR] = { 0x10a0 }, + [GPUCC_GPU_CC_GMU_BCR] = { 0x111c }, + [GPUCC_GPU_CC_GX_BCR] = { 0x1008 }, + [GPUCC_GPU_CC_XO_BCR] = { 0x1000 }, +}; + +static struct gdsc *gpu_cc_sm8250_gdscs[] = { + [GPU_CX_GDSC] = &gpu_cx_gdsc, + [GPU_GX_GDSC] = &gpu_gx_gdsc, +}; + +static const struct regmap_config gpu_cc_sm8250_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x8008, + .fast_io = true, +}; + +static const struct qcom_cc_desc gpu_cc_sm8250_desc = { + .config = &gpu_cc_sm8250_regmap_config, + .clks = gpu_cc_sm8250_clocks, + .num_clks = ARRAY_SIZE(gpu_cc_sm8250_clocks), + .resets = gpu_cc_sm8250_resets, + .num_resets = ARRAY_SIZE(gpu_cc_sm8250_resets), + .gdscs = gpu_cc_sm8250_gdscs, + .num_gdscs = ARRAY_SIZE(gpu_cc_sm8250_gdscs), +}; + +static const struct of_device_id gpu_cc_sm8250_match_table[] = { + { .compatible = "qcom,sm8250-gpucc" }, + { } +}; +MODULE_DEVICE_TABLE(of, gpu_cc_sm8250_match_table); + +static int gpu_cc_sm8250_probe(struct platform_device *pdev) +{ + struct regmap *regmap; + unsigned int value, mask; + + regmap = qcom_cc_map(pdev, &gpu_cc_sm8250_desc); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + clk_lucid_pll_configure(&gpu_cc_pll1, regmap, &gpu_cc_pll1_config); + + /* + * Configure gpu_cc_cx_gmu_clk with recommended + * wakeup/sleep settings + */ + mask = CX_GMU_CBCR_WAKE_MASK << CX_GMU_CBCR_WAKE_SHIFT; + mask |= CX_GMU_CBCR_SLEEP_MASK << CX_GMU_CBCR_SLEEP_SHIFT; + value = 0xf << CX_GMU_CBCR_WAKE_SHIFT | 0xf << CX_GMU_CBCR_SLEEP_SHIFT; + regmap_update_bits(regmap, 0x1098, mask, value); + + return qcom_cc_really_probe(pdev, &gpu_cc_sm8250_desc, regmap); +} + +static struct platform_driver gpu_cc_sm8250_driver = { + .probe = gpu_cc_sm8250_probe, + .driver = { + .name = "sm8250-gpucc", + .of_match_table = gpu_cc_sm8250_match_table, + }, +}; + +static int __init gpu_cc_sm8250_init(void) +{ + return platform_driver_register(&gpu_cc_sm8250_driver); +} +subsys_initcall(gpu_cc_sm8250_init); + +static void __exit gpu_cc_sm8250_exit(void) +{ + platform_driver_unregister(&gpu_cc_sm8250_driver); +} +module_exit(gpu_cc_sm8250_exit); + +MODULE_DESCRIPTION("QTI GPU_CC SM8250 Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/qcom/lpasscorecc-sc7180.c b/drivers/clk/qcom/lpasscorecc-sc7180.c new file mode 100644 index 000000000000..d4c1864e1ee9 --- /dev/null +++ b/drivers/clk/qcom/lpasscorecc-sc7180.c @@ -0,0 +1,476 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ + +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/pm_clock.h> +#include <linux/pm_runtime.h> +#include <linux/of.h> +#include <linux/regmap.h> + +#include <dt-bindings/clock/qcom,lpasscorecc-sc7180.h> + +#include "clk-alpha-pll.h" +#include "clk-branch.h" +#include "clk-rcg.h" +#include "clk-regmap.h" +#include "common.h" +#include "gdsc.h" + +enum { + P_BI_TCXO, + P_LPASS_LPAAUDIO_DIG_PLL_OUT_ODD, + P_SLEEP_CLK, +}; + +static struct pll_vco fabia_vco[] = { + { 249600000, 2000000000, 0 }, +}; + +static const struct alpha_pll_config lpass_lpaaudio_dig_pll_config = { + .l = 0x20, + .alpha = 0x0, + .config_ctl_val = 0x20485699, + .config_ctl_hi_val = 0x00002067, + .test_ctl_val = 0x40000000, + .test_ctl_hi_val = 0x00000000, + .user_ctl_val = 0x00005105, + .user_ctl_hi_val = 0x00004805, +}; + +static const u8 clk_alpha_pll_regs_offset[][PLL_OFF_MAX_REGS] = { + [CLK_ALPHA_PLL_TYPE_FABIA] = { + [PLL_OFF_L_VAL] = 0x04, + [PLL_OFF_CAL_L_VAL] = 0x8, + [PLL_OFF_USER_CTL] = 0x0c, + [PLL_OFF_USER_CTL_U] = 0x10, + [PLL_OFF_USER_CTL_U1] = 0x14, + [PLL_OFF_CONFIG_CTL] = 0x18, + [PLL_OFF_CONFIG_CTL_U] = 0x1C, + [PLL_OFF_CONFIG_CTL_U1] = 0x20, + [PLL_OFF_TEST_CTL] = 0x24, + [PLL_OFF_TEST_CTL_U] = 0x28, + [PLL_OFF_STATUS] = 0x30, + [PLL_OFF_OPMODE] = 0x38, + [PLL_OFF_FRAC] = 0x40, + }, +}; + +static struct clk_alpha_pll lpass_lpaaudio_dig_pll = { + .offset = 0x1000, + .vco_table = fabia_vco, + .num_vco = ARRAY_SIZE(fabia_vco), + .regs = clk_alpha_pll_regs_offset[CLK_ALPHA_PLL_TYPE_FABIA], + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "lpass_lpaaudio_dig_pll", + .parent_data = &(const struct clk_parent_data){ + .fw_name = "bi_tcxo", + }, + .num_parents = 1, + .ops = &clk_alpha_pll_fabia_ops, + }, + }, +}; + +static const struct clk_div_table + post_div_table_lpass_lpaaudio_dig_pll_out_odd[] = { + { 0x5, 5 }, + { } +}; + +static struct clk_alpha_pll_postdiv lpass_lpaaudio_dig_pll_out_odd = { + .offset = 0x1000, + .post_div_shift = 12, + .post_div_table = post_div_table_lpass_lpaaudio_dig_pll_out_odd, + .num_post_div = + ARRAY_SIZE(post_div_table_lpass_lpaaudio_dig_pll_out_odd), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_FABIA], + .clkr.hw.init = &(struct clk_init_data){ + .name = "lpass_lpaaudio_dig_pll_out_odd", + .parent_data = &(const struct clk_parent_data){ + .hw = &lpass_lpaaudio_dig_pll.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_alpha_pll_postdiv_fabia_ops, + }, +}; + +static const struct parent_map lpass_core_cc_parent_map_0[] = { + { P_BI_TCXO, 0 }, + { P_LPASS_LPAAUDIO_DIG_PLL_OUT_ODD, 5 }, +}; + +static const struct clk_parent_data lpass_core_cc_parent_data_0[] = { + { .fw_name = "bi_tcxo" }, + { .hw = &lpass_lpaaudio_dig_pll_out_odd.clkr.hw }, +}; + +static const struct parent_map lpass_core_cc_parent_map_2[] = { + { P_BI_TCXO, 0 }, +}; + +static struct clk_rcg2 core_clk_src = { + .cmd_rcgr = 0x1d000, + .mnd_width = 8, + .hid_width = 5, + .parent_map = lpass_core_cc_parent_map_2, + .clkr.hw.init = &(struct clk_init_data){ + .name = "core_clk_src", + .parent_data = &(const struct clk_parent_data){ + .fw_name = "bi_tcxo", + }, + .num_parents = 1, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_ext_mclk0_clk_src[] = { + F(9600000, P_BI_TCXO, 2, 0, 0), + F(19200000, P_BI_TCXO, 1, 0, 0), + { } +}; + +static const struct freq_tbl ftbl_ext_lpaif_clk_src[] = { + F(256000, P_LPASS_LPAAUDIO_DIG_PLL_OUT_ODD, 15, 1, 32), + F(512000, P_LPASS_LPAAUDIO_DIG_PLL_OUT_ODD, 15, 1, 16), + F(768000, P_LPASS_LPAAUDIO_DIG_PLL_OUT_ODD, 10, 1, 16), + F(1024000, P_LPASS_LPAAUDIO_DIG_PLL_OUT_ODD, 15, 1, 8), + F(1536000, P_LPASS_LPAAUDIO_DIG_PLL_OUT_ODD, 10, 1, 8), + F(2048000, P_LPASS_LPAAUDIO_DIG_PLL_OUT_ODD, 15, 1, 4), + F(3072000, P_LPASS_LPAAUDIO_DIG_PLL_OUT_ODD, 10, 1, 4), + F(4096000, P_LPASS_LPAAUDIO_DIG_PLL_OUT_ODD, 15, 1, 2), + F(6144000, P_LPASS_LPAAUDIO_DIG_PLL_OUT_ODD, 10, 1, 2), + F(8192000, P_LPASS_LPAAUDIO_DIG_PLL_OUT_ODD, 15, 0, 0), + F(9600000, P_BI_TCXO, 2, 0, 0), + F(12288000, P_LPASS_LPAAUDIO_DIG_PLL_OUT_ODD, 10, 0, 0), + F(19200000, P_BI_TCXO, 1, 0, 0), + F(24576000, P_LPASS_LPAAUDIO_DIG_PLL_OUT_ODD, 5, 0, 0), + { } +}; + +static struct clk_rcg2 ext_mclk0_clk_src = { + .cmd_rcgr = 0x20000, + .mnd_width = 8, + .hid_width = 5, + .parent_map = lpass_core_cc_parent_map_0, + .freq_tbl = ftbl_ext_mclk0_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "ext_mclk0_clk_src", + .parent_data = lpass_core_cc_parent_data_0, + .num_parents = 2, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 lpaif_pri_clk_src = { + .cmd_rcgr = 0x10000, + .mnd_width = 16, + .hid_width = 5, + .parent_map = lpass_core_cc_parent_map_0, + .freq_tbl = ftbl_ext_lpaif_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "lpaif_pri_clk_src", + .parent_data = lpass_core_cc_parent_data_0, + .num_parents = 2, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 lpaif_sec_clk_src = { + .cmd_rcgr = 0x11000, + .mnd_width = 16, + .hid_width = 5, + .parent_map = lpass_core_cc_parent_map_0, + .freq_tbl = ftbl_ext_lpaif_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "lpaif_sec_clk_src", + .parent_data = lpass_core_cc_parent_data_0, + .num_parents = 2, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_branch lpass_audio_core_ext_mclk0_clk = { + .halt_reg = 0x20014, + .halt_check = BRANCH_HALT, + .hwcg_reg = 0x20014, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x20014, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "lpass_audio_core_ext_mclk0_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &ext_mclk0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch lpass_audio_core_lpaif_pri_ibit_clk = { + .halt_reg = 0x10018, + .halt_check = BRANCH_HALT, + .hwcg_reg = 0x10018, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x10018, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "lpass_audio_core_lpaif_pri_ibit_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &lpaif_pri_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch lpass_audio_core_lpaif_sec_ibit_clk = { + .halt_reg = 0x11018, + .halt_check = BRANCH_HALT, + .hwcg_reg = 0x11018, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x11018, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "lpass_audio_core_lpaif_sec_ibit_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &lpaif_sec_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch lpass_audio_core_sysnoc_mport_core_clk = { + .halt_reg = 0x23000, + .halt_check = BRANCH_HALT, + .hwcg_reg = 0x23000, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x23000, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "lpass_audio_core_sysnoc_mport_core_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &core_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_regmap *lpass_core_cc_sc7180_clocks[] = { + [EXT_MCLK0_CLK_SRC] = &ext_mclk0_clk_src.clkr, + [LPAIF_PRI_CLK_SRC] = &lpaif_pri_clk_src.clkr, + [LPAIF_SEC_CLK_SRC] = &lpaif_sec_clk_src.clkr, + [CORE_CLK_SRC] = &core_clk_src.clkr, + [LPASS_AUDIO_CORE_EXT_MCLK0_CLK] = &lpass_audio_core_ext_mclk0_clk.clkr, + [LPASS_AUDIO_CORE_LPAIF_PRI_IBIT_CLK] = + &lpass_audio_core_lpaif_pri_ibit_clk.clkr, + [LPASS_AUDIO_CORE_LPAIF_SEC_IBIT_CLK] = + &lpass_audio_core_lpaif_sec_ibit_clk.clkr, + [LPASS_AUDIO_CORE_SYSNOC_MPORT_CORE_CLK] = + &lpass_audio_core_sysnoc_mport_core_clk.clkr, + [LPASS_LPAAUDIO_DIG_PLL] = &lpass_lpaaudio_dig_pll.clkr, + [LPASS_LPAAUDIO_DIG_PLL_OUT_ODD] = &lpass_lpaaudio_dig_pll_out_odd.clkr, +}; + +static struct gdsc lpass_pdc_hm_gdsc = { + .gdscr = 0x3090, + .pd = { + .name = "lpass_pdc_hm_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = VOTABLE, +}; + +static struct gdsc lpass_audio_hm_gdsc = { + .gdscr = 0x9090, + .pd = { + .name = "lpass_audio_hm_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, +}; + +static struct gdsc lpass_core_hm_gdsc = { + .gdscr = 0x0, + .pd = { + .name = "lpass_core_hm_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = RETAIN_FF_ENABLE, +}; + +static struct gdsc *lpass_core_hm_sc7180_gdscs[] = { + [LPASS_CORE_HM_GDSCR] = &lpass_core_hm_gdsc, +}; + +static struct gdsc *lpass_audio_hm_sc7180_gdscs[] = { + [LPASS_PDC_HM_GDSCR] = &lpass_pdc_hm_gdsc, + [LPASS_AUDIO_HM_GDSCR] = &lpass_audio_hm_gdsc, +}; + +static struct regmap_config lpass_core_cc_sc7180_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .fast_io = true, +}; + +static const struct qcom_cc_desc lpass_core_hm_sc7180_desc = { + .config = &lpass_core_cc_sc7180_regmap_config, + .gdscs = lpass_core_hm_sc7180_gdscs, + .num_gdscs = ARRAY_SIZE(lpass_core_hm_sc7180_gdscs), +}; + +static const struct qcom_cc_desc lpass_core_cc_sc7180_desc = { + .config = &lpass_core_cc_sc7180_regmap_config, + .clks = lpass_core_cc_sc7180_clocks, + .num_clks = ARRAY_SIZE(lpass_core_cc_sc7180_clocks), +}; + +static const struct qcom_cc_desc lpass_audio_hm_sc7180_desc = { + .config = &lpass_core_cc_sc7180_regmap_config, + .gdscs = lpass_audio_hm_sc7180_gdscs, + .num_gdscs = ARRAY_SIZE(lpass_audio_hm_sc7180_gdscs), +}; + +static int lpass_core_cc_sc7180_probe(struct platform_device *pdev) +{ + const struct qcom_cc_desc *desc; + struct regmap *regmap; + int ret; + + lpass_core_cc_sc7180_regmap_config.name = "lpass_audio_cc"; + desc = &lpass_audio_hm_sc7180_desc; + ret = qcom_cc_probe_by_index(pdev, 1, desc); + if (ret) + return ret; + + lpass_core_cc_sc7180_regmap_config.name = "lpass_core_cc"; + regmap = qcom_cc_map(pdev, &lpass_core_cc_sc7180_desc); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + /* + * Keep the CLK always-ON + * LPASS_AUDIO_CORE_SYSNOC_SWAY_CORE_CLK + */ + regmap_update_bits(regmap, 0x24000, BIT(0), BIT(0)); + + /* PLL settings */ + regmap_write(regmap, 0x1008, 0x20); + regmap_update_bits(regmap, 0x1014, BIT(0), BIT(0)); + + clk_fabia_pll_configure(&lpass_lpaaudio_dig_pll, regmap, + &lpass_lpaaudio_dig_pll_config); + + return qcom_cc_really_probe(pdev, &lpass_core_cc_sc7180_desc, regmap); +} + +static int lpass_hm_core_probe(struct platform_device *pdev) +{ + const struct qcom_cc_desc *desc; + + lpass_core_cc_sc7180_regmap_config.name = "lpass_hm_core"; + desc = &lpass_core_hm_sc7180_desc; + + return qcom_cc_probe_by_index(pdev, 0, desc); +} + +static const struct of_device_id lpass_core_cc_sc7180_match_table[] = { + { + .compatible = "qcom,sc7180-lpasshm", + .data = lpass_hm_core_probe, + }, + { + .compatible = "qcom,sc7180-lpasscorecc", + .data = lpass_core_cc_sc7180_probe, + }, + { } +}; +MODULE_DEVICE_TABLE(of, lpass_core_cc_sc7180_match_table); + +static int lpass_core_sc7180_probe(struct platform_device *pdev) +{ + int (*clk_probe)(struct platform_device *p); + int ret; + + pm_runtime_enable(&pdev->dev); + ret = pm_clk_create(&pdev->dev); + if (ret) + return ret; + + ret = pm_clk_add(&pdev->dev, "iface"); + if (ret < 0) { + dev_err(&pdev->dev, "failed to acquire iface clock\n"); + goto disable_pm_runtime; + } + + clk_probe = of_device_get_match_data(&pdev->dev); + if (!clk_probe) + return -EINVAL; + + ret = clk_probe(pdev); + if (ret) + goto destroy_pm_clk; + + return 0; + +destroy_pm_clk: + pm_clk_destroy(&pdev->dev); + +disable_pm_runtime: + pm_runtime_disable(&pdev->dev); + + return ret; +} + +static const struct dev_pm_ops lpass_core_cc_pm_ops = { + SET_RUNTIME_PM_OPS(pm_clk_suspend, pm_clk_resume, NULL) +}; + +static struct platform_driver lpass_core_cc_sc7180_driver = { + .probe = lpass_core_sc7180_probe, + .driver = { + .name = "lpass_core_cc-sc7180", + .of_match_table = lpass_core_cc_sc7180_match_table, + .pm = &lpass_core_cc_pm_ops, + }, +}; + +static int __init lpass_core_cc_sc7180_init(void) +{ + return platform_driver_register(&lpass_core_cc_sc7180_driver); +} +subsys_initcall(lpass_core_cc_sc7180_init); + +static void __exit lpass_core_cc_sc7180_exit(void) +{ + platform_driver_unregister(&lpass_core_cc_sc7180_driver); +} +module_exit(lpass_core_cc_sc7180_exit); + +MODULE_DESCRIPTION("QTI LPASS_CORE_CC SC7180 Driver"); +MODULE_LICENSE("GPL v2"); |