From: Yassine Oudjana <y.oudjana@xxxxxxxxxxxxxx> Add a driver for MT6735 pericfg clock gates and resets. Signed-off-by: Yassine Oudjana <y.oudjana@xxxxxxxxxxxxxx> --- MAINTAINERS | 1 + drivers/clk/mediatek/Kconfig | 7 + drivers/clk/mediatek/Makefile | 1 + drivers/clk/mediatek/clk-mt6735-pericfg.c | 360 ++++++++++++++++++++++ 4 files changed, 369 insertions(+) create mode 100644 drivers/clk/mediatek/clk-mt6735-pericfg.c diff --git a/MAINTAINERS b/MAINTAINERS index 8662f12f34a2..5d90c2f2a587 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12444,6 +12444,7 @@ L: linux-mediatek@xxxxxxxxxxxxxxxxxxx (moderated for non-subscribers) S: Maintained F: drivers/clk/mediatek/clk-mt6735-apmixed.c F: drivers/clk/mediatek/clk-mt6735-infracfg.c +F: drivers/clk/mediatek/clk-mt6735-pericfg.c F: drivers/clk/mediatek/clk-mt6735-topckgen.c F: include/dt-bindings/clock/mediatek,mt6735-apmixedsys.h F: include/dt-bindings/clock/mediatek,mt6735-infracfg.h diff --git a/drivers/clk/mediatek/Kconfig b/drivers/clk/mediatek/Kconfig index 62195e5d90a0..698ff2995460 100644 --- a/drivers/clk/mediatek/Kconfig +++ b/drivers/clk/mediatek/Kconfig @@ -131,6 +131,13 @@ config COMMON_CLK_MT6735_INFRACFG help This driver supports MediaTek MT6735 infracfg clocks and resets. +config COMMON_CLK_MT6735_PERICFG + tristate "Clock driver for MediaTek MT6735 pericfg" + depends on ARCH_MEDIATEK || COMPILE_TEST + select COMMON_CLK_MEDIATEK + help + This driver supports MediaTek MT6735 pericfg clocks and resets. + config COMMON_CLK_MT6735_TOPCKGEN tristate "Clock driver for MediaTek MT6735 topckgen" depends on ARCH_MEDIATEK || COMPILE_TEST diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile index e5c1da6e2711..b1a4d18e382d 100644 --- a/drivers/clk/mediatek/Makefile +++ b/drivers/clk/mediatek/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_COMMON_CLK_MEDIATEK) += clk-mtk.o clk-pll.o clk-gate.o clk-apmixed. obj-$(CONFIG_COMMON_CLK_MT6735_APMIXED) += clk-mt6735-apmixed.o obj-$(CONFIG_COMMON_CLK_MT6735_INFRACFG) += clk-mt6735-infracfg.o +obj-$(CONFIG_COMMON_CLK_MT6735_PERICFG) += clk-mt6735-pericfg.o obj-$(CONFIG_COMMON_CLK_MT6735_TOPCKGEN) += clk-mt6735-topckgen.o obj-$(CONFIG_COMMON_CLK_MT6765) += clk-mt6765.o obj-$(CONFIG_COMMON_CLK_MT6765_AUDIOSYS) += clk-mt6765-audio.o diff --git a/drivers/clk/mediatek/clk-mt6735-pericfg.c b/drivers/clk/mediatek/clk-mt6735-pericfg.c new file mode 100644 index 000000000000..8a01aa63a81e --- /dev/null +++ b/drivers/clk/mediatek/clk-mt6735-pericfg.c @@ -0,0 +1,360 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2022 Yassine Oudjana <y.oudjana@xxxxxxxxxxxxxx> + */ + +#include <linux/clk-provider.h> +#include <linux/platform_device.h> + +#include "clk-gate.h" +#include "clk-mtk.h" + +#include <dt-bindings/clock/mediatek,mt6735-pericfg.h> + +#define PERI_GLOBALCON_RST0 0x00 +#define PERI_GLOBALCON_PDN0_SET 0x08 +#define PERI_GLOBALCON_PDN0_CLR 0x10 +#define PERI_GLOBALCON_PDN0_STA 0x18 + +struct mt6735_pericfg { + struct clk_onecell_data *clk_data; + struct mtk_reset *reset_data; +}; + +static struct mtk_gate_regs peri_cg_regs = { + .set_ofs = PERI_GLOBALCON_PDN0_SET, + .clr_ofs = PERI_GLOBALCON_PDN0_CLR, + .sta_ofs = PERI_GLOBALCON_PDN0_STA, +}; + +static const struct mtk_gate pericfg_gates[] = { + { + .id = DISP_PWM, + .name = "disp_pwm", + .parent_name = "disppwm_sel", + .regs = &peri_cg_regs, + .shift = 0, + .ops = &mtk_clk_gate_ops_setclr + }, + { + .id = THERM, + .name = "therm", + .parent_name = "axi_sel", + .regs = &peri_cg_regs, + .shift = 1, + .ops = &mtk_clk_gate_ops_setclr + }, + { + .id = PWM1, + .name = "pwm1", + .parent_name = "axi_sel", + .regs = &peri_cg_regs, + .shift = 2, + .ops = &mtk_clk_gate_ops_setclr + }, + { + .id = PWM2, + .name = "pwm2", + .parent_name = "axi_sel", + .regs = &peri_cg_regs, + .shift = 3, + .ops = &mtk_clk_gate_ops_setclr + }, + { + .id = PWM3, + .name = "pwm3", + .parent_name = "axi_sel", + .regs = &peri_cg_regs, + .shift = 4, + .ops = &mtk_clk_gate_ops_setclr + }, + { + .id = PWM4, + .name = "pwm4", + .parent_name = "axi_sel", + .regs = &peri_cg_regs, + .shift = 5, + .ops = &mtk_clk_gate_ops_setclr + }, + { + .id = PWM5, + .name = "pwm5", + .parent_name = "axi_sel", + .regs = &peri_cg_regs, + .shift = 6, + .ops = &mtk_clk_gate_ops_setclr + }, + { + .id = PWM6, + .name = "pwm6", + .parent_name = "axi_sel", + .regs = &peri_cg_regs, + .shift = 7, + .ops = &mtk_clk_gate_ops_setclr + }, + { + .id = PWM7, + .name = "pwm7", + .parent_name = "axi_sel", + .regs = &peri_cg_regs, + .shift = 8, + .ops = &mtk_clk_gate_ops_setclr + }, + { + .id = PWM, + .name = "pwm", + .parent_name = "axi_sel", + .regs = &peri_cg_regs, + .shift = 9, + .ops = &mtk_clk_gate_ops_setclr + }, + { + .id = USB0, + .name = "usb0", + .parent_name = "usb20_sel", + .regs = &peri_cg_regs, + .shift = 10, + .ops = &mtk_clk_gate_ops_setclr + }, + { + .id = IRDA, + .name = "irda", + .parent_name = "irda_sel", + .regs = &peri_cg_regs, + .shift = 11, + .ops = &mtk_clk_gate_ops_setclr + }, + { + .id = APDMA, + .name = "apdma", + .parent_name = "axi_sel", + .regs = &peri_cg_regs, + .shift = 12, + .ops = &mtk_clk_gate_ops_setclr + }, + { + .id = MSDC30_0, + .name = "msdc30_0", + .parent_name = "msdc30_0_sel", + .regs = &peri_cg_regs, + .shift = 13, + .ops = &mtk_clk_gate_ops_setclr + }, + { + .id = MSDC30_1, + .name = "msdc30_1", + .parent_name = "msdc30_1_sel", + .regs = &peri_cg_regs, + .shift = 14, + .ops = &mtk_clk_gate_ops_setclr + }, + { + .id = MSDC30_2, + .name = "msdc30_2", + .parent_name = "msdc30_2_sel", + .regs = &peri_cg_regs, + .shift = 15, + .ops = &mtk_clk_gate_ops_setclr + }, + { + .id = MSDC30_3, + .name = "msdc30_3", + .parent_name = "msdc30_3_sel", + .regs = &peri_cg_regs, + .shift = 16, + .ops = &mtk_clk_gate_ops_setclr + }, + { + .id = UART0, + .name = "uart0", + .parent_name = "uart_sel", + .regs = &peri_cg_regs, + .shift = 17, + .ops = &mtk_clk_gate_ops_setclr + }, + { + .id = UART1, + .name = "uart1", + .parent_name = "uart_sel", + .regs = &peri_cg_regs, + .shift = 18, + .ops = &mtk_clk_gate_ops_setclr + }, + { + .id = UART2, + .name = "uart2", + .parent_name = "uart_sel", + .regs = &peri_cg_regs, + .shift = 19, + .ops = &mtk_clk_gate_ops_setclr + }, + { + .id = UART3, + .name = "uart3", + .parent_name = "uart_sel", + .regs = &peri_cg_regs, + .shift = 20, + .ops = &mtk_clk_gate_ops_setclr + }, + { + .id = UART4, + .name = "uart4", + .parent_name = "uart_sel", + .regs = &peri_cg_regs, + .shift = 21, + .ops = &mtk_clk_gate_ops_setclr + }, + { + .id = BTIF, + .name = "btif", + .parent_name = "axi_sel", + .regs = &peri_cg_regs, + .shift = 22, + .ops = &mtk_clk_gate_ops_setclr + }, + { + .id = I2C0, + .name = "i2c0", + .parent_name = "axi_sel", + .regs = &peri_cg_regs, + .shift = 23, + .ops = &mtk_clk_gate_ops_setclr + }, + { + .id = I2C1, + .name = "i2c1", + .parent_name = "axi_sel", + .regs = &peri_cg_regs, + .shift = 24, + .ops = &mtk_clk_gate_ops_setclr + }, + { + .id = I2C2, + .name = "i2c2", + .parent_name = "axi_sel", + .regs = &peri_cg_regs, + .shift = 25, + .ops = &mtk_clk_gate_ops_setclr + }, + { + .id = I2C3, + .name = "i2c3", + .parent_name = "axi_sel", + .regs = &peri_cg_regs, + .shift = 26, + .ops = &mtk_clk_gate_ops_setclr + }, + { + .id = AUXADC, + .name = "auxadc", + .parent_name = "axi_sel", + .regs = &peri_cg_regs, + .shift = 27, + .ops = &mtk_clk_gate_ops_setclr + }, + { + .id = SPI0, + .name = "spi0", + .parent_name = "spi_sel", + .regs = &peri_cg_regs, + .shift = 28, + .ops = &mtk_clk_gate_ops_setclr + }, + { + .id = IRTX, + .name = "IRTX", + .parent_name = "irtx_sel", + .regs = &peri_cg_regs, + .shift = 29, + .ops = &mtk_clk_gate_ops_setclr + }, +}; + +int clk_mt6735_pericfg_probe(struct platform_device *pdev) +{ + void __iomem *base; + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct mt6735_pericfg *pericfg; + int ret; + + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + pericfg = devm_kmalloc(&pdev->dev, sizeof(struct mt6735_pericfg), + GFP_KERNEL); + if (!pericfg) + return -ENOMEM; + + pericfg->clk_data = mtk_alloc_clk_data(ARRAY_SIZE(pericfg_gates)); + if (!pericfg->clk_data) + return -ENOMEM; + platform_set_drvdata(pdev, pericfg); + + ret = mtk_clk_register_gates_with_dev(pdev->dev.of_node, pericfg_gates, + ARRAY_SIZE(pericfg_gates), + pericfg->clk_data, &pdev->dev); + if (ret) { + dev_err(&pdev->dev, "Failed to register gates: %pe\n", + ERR_PTR(ret)); + goto free_clk_data; + } + + ret = of_clk_add_provider(pdev->dev.of_node, of_clk_src_onecell_get, + pericfg->clk_data); + if (ret) { + dev_err(&pdev->dev, + "Failed to register clock provider: %pe\n", + ERR_PTR(ret)); + goto unregister_gates; + } + + pericfg->reset_data = mtk_register_reset_controller(pdev->dev.of_node, 2, + PERI_GLOBALCON_RST0); + if (IS_ERR(pericfg->reset_data)) { + dev_err(&pdev->dev, "Failed to register reset controller: %pe\n", + pericfg->reset_data); + return PTR_ERR(pericfg->reset_data); + } + + return 0; +unregister_gates: + mtk_clk_unregister_gates(pericfg_gates, ARRAY_SIZE(pericfg_gates), + pericfg->clk_data); +free_clk_data: + mtk_free_clk_data(pericfg->clk_data); + + return ret; +} + +int clk_mt6735_pericfg_remove(struct platform_device *pdev) +{ + struct mt6735_pericfg *pericfg = platform_get_drvdata(pdev); + + mtk_unregister_reset_controller(pericfg->reset_data); + of_clk_del_provider(pdev->dev.of_node); + mtk_clk_unregister_gates(pericfg_gates, ARRAY_SIZE(pericfg_gates), + pericfg->clk_data); + mtk_free_clk_data(pericfg->clk_data); + + return 0; +} + +static const struct of_device_id of_match_mt6735_pericfg[] = { + { .compatible = "mediatek,mt6735-pericfg" }, + { /* sentinel */ } +}; + +static struct platform_driver clk_mt6735_pericfg = { + .probe = clk_mt6735_pericfg_probe, + .remove = clk_mt6735_pericfg_remove, + .driver = { + .name = "clk-mt6735-pericfg", + .of_match_table = of_match_mt6735_pericfg, + }, +}; +module_platform_driver(clk_mt6735_pericfg); + +MODULE_AUTHOR("Yassine Oudjana <y.oudjana@xxxxxxxxxxxxxx>"); +MODULE_DESCRIPTION("Mediatek MT6735 pericfg clock driver"); +MODULE_LICENSE("GPL"); -- 2.36.0