18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2017 Samsung Electronics Co., Ltd.
48c2ecf20Sopenharmony_ci * Author: Marek Szyprowski <m.szyprowski@samsung.com>
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Common Clock Framework support for Exynos4412 ISP module.
78c2ecf20Sopenharmony_ci*/
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <dt-bindings/clock/exynos4.h>
108c2ecf20Sopenharmony_ci#include <linux/slab.h>
118c2ecf20Sopenharmony_ci#include <linux/clk.h>
128c2ecf20Sopenharmony_ci#include <linux/clk-provider.h>
138c2ecf20Sopenharmony_ci#include <linux/of.h>
148c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
158c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include "clk.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci/* Exynos4x12 specific registers, which belong to ISP power domain */
208c2ecf20Sopenharmony_ci#define E4X12_DIV_ISP0		0x0300
218c2ecf20Sopenharmony_ci#define E4X12_DIV_ISP1		0x0304
228c2ecf20Sopenharmony_ci#define E4X12_GATE_ISP0		0x0800
238c2ecf20Sopenharmony_ci#define E4X12_GATE_ISP1		0x0804
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/*
268c2ecf20Sopenharmony_ci * Support for CMU save/restore across system suspends
278c2ecf20Sopenharmony_ci */
288c2ecf20Sopenharmony_cistatic struct samsung_clk_reg_dump *exynos4x12_save_isp;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic const unsigned long exynos4x12_clk_isp_save[] __initconst = {
318c2ecf20Sopenharmony_ci	E4X12_DIV_ISP0,
328c2ecf20Sopenharmony_ci	E4X12_DIV_ISP1,
338c2ecf20Sopenharmony_ci	E4X12_GATE_ISP0,
348c2ecf20Sopenharmony_ci	E4X12_GATE_ISP1,
358c2ecf20Sopenharmony_ci};
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic struct samsung_div_clock exynos4x12_isp_div_clks[] = {
388c2ecf20Sopenharmony_ci	DIV(CLK_ISP_DIV_ISP0, "div_isp0", "aclk200", E4X12_DIV_ISP0, 0, 3),
398c2ecf20Sopenharmony_ci	DIV(CLK_ISP_DIV_ISP1, "div_isp1", "aclk200", E4X12_DIV_ISP0, 4, 3),
408c2ecf20Sopenharmony_ci	DIV(CLK_ISP_DIV_MCUISP0, "div_mcuisp0", "aclk400_mcuisp",
418c2ecf20Sopenharmony_ci	    E4X12_DIV_ISP1, 4, 3),
428c2ecf20Sopenharmony_ci	DIV(CLK_ISP_DIV_MCUISP1, "div_mcuisp1", "div_mcuisp0",
438c2ecf20Sopenharmony_ci	    E4X12_DIV_ISP1, 8, 3),
448c2ecf20Sopenharmony_ci	DIV(0, "div_mpwm", "div_isp1", E4X12_DIV_ISP1, 0, 3),
458c2ecf20Sopenharmony_ci};
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic struct samsung_gate_clock exynos4x12_isp_gate_clks[] = {
488c2ecf20Sopenharmony_ci	GATE(CLK_ISP_FIMC_ISP, "isp", "aclk200", E4X12_GATE_ISP0, 0, 0, 0),
498c2ecf20Sopenharmony_ci	GATE(CLK_ISP_FIMC_DRC, "drc", "aclk200", E4X12_GATE_ISP0, 1, 0, 0),
508c2ecf20Sopenharmony_ci	GATE(CLK_ISP_FIMC_FD, "fd", "aclk200", E4X12_GATE_ISP0, 2, 0, 0),
518c2ecf20Sopenharmony_ci	GATE(CLK_ISP_FIMC_LITE0, "lite0", "aclk200", E4X12_GATE_ISP0, 3, 0, 0),
528c2ecf20Sopenharmony_ci	GATE(CLK_ISP_FIMC_LITE1, "lite1", "aclk200", E4X12_GATE_ISP0, 4, 0, 0),
538c2ecf20Sopenharmony_ci	GATE(CLK_ISP_MCUISP, "mcuisp", "aclk200", E4X12_GATE_ISP0, 5, 0, 0),
548c2ecf20Sopenharmony_ci	GATE(CLK_ISP_GICISP, "gicisp", "aclk200", E4X12_GATE_ISP0, 7, 0, 0),
558c2ecf20Sopenharmony_ci	GATE(CLK_ISP_SMMU_ISP, "smmu_isp", "aclk200", E4X12_GATE_ISP0, 8, 0, 0),
568c2ecf20Sopenharmony_ci	GATE(CLK_ISP_SMMU_DRC, "smmu_drc", "aclk200", E4X12_GATE_ISP0, 9, 0, 0),
578c2ecf20Sopenharmony_ci	GATE(CLK_ISP_SMMU_FD, "smmu_fd", "aclk200", E4X12_GATE_ISP0, 10, 0, 0),
588c2ecf20Sopenharmony_ci	GATE(CLK_ISP_SMMU_LITE0, "smmu_lite0", "aclk200", E4X12_GATE_ISP0, 11,
598c2ecf20Sopenharmony_ci	     0, 0),
608c2ecf20Sopenharmony_ci	GATE(CLK_ISP_SMMU_LITE1, "smmu_lite1", "aclk200", E4X12_GATE_ISP0, 12,
618c2ecf20Sopenharmony_ci	     0, 0),
628c2ecf20Sopenharmony_ci	GATE(CLK_ISP_PPMUISPMX, "ppmuispmx", "aclk200", E4X12_GATE_ISP0, 20,
638c2ecf20Sopenharmony_ci	     0, 0),
648c2ecf20Sopenharmony_ci	GATE(CLK_ISP_PPMUISPX, "ppmuispx", "aclk200", E4X12_GATE_ISP0, 21,
658c2ecf20Sopenharmony_ci	     0, 0),
668c2ecf20Sopenharmony_ci	GATE(CLK_ISP_MCUCTL_ISP, "mcuctl_isp", "aclk200", E4X12_GATE_ISP0, 23,
678c2ecf20Sopenharmony_ci	     0, 0),
688c2ecf20Sopenharmony_ci	GATE(CLK_ISP_MPWM_ISP, "mpwm_isp", "aclk200", E4X12_GATE_ISP0, 24,
698c2ecf20Sopenharmony_ci	     0, 0),
708c2ecf20Sopenharmony_ci	GATE(CLK_ISP_I2C0_ISP, "i2c0_isp", "aclk200", E4X12_GATE_ISP0, 25,
718c2ecf20Sopenharmony_ci	     0, 0),
728c2ecf20Sopenharmony_ci	GATE(CLK_ISP_I2C1_ISP, "i2c1_isp", "aclk200", E4X12_GATE_ISP0, 26,
738c2ecf20Sopenharmony_ci	     0, 0),
748c2ecf20Sopenharmony_ci	GATE(CLK_ISP_MTCADC_ISP, "mtcadc_isp", "aclk200", E4X12_GATE_ISP0, 27,
758c2ecf20Sopenharmony_ci	     0, 0),
768c2ecf20Sopenharmony_ci	GATE(CLK_ISP_PWM_ISP, "pwm_isp", "aclk200", E4X12_GATE_ISP0, 28, 0, 0),
778c2ecf20Sopenharmony_ci	GATE(CLK_ISP_WDT_ISP, "wdt_isp", "aclk200", E4X12_GATE_ISP0, 30, 0, 0),
788c2ecf20Sopenharmony_ci	GATE(CLK_ISP_UART_ISP, "uart_isp", "aclk200", E4X12_GATE_ISP0, 31,
798c2ecf20Sopenharmony_ci	     0, 0),
808c2ecf20Sopenharmony_ci	GATE(CLK_ISP_ASYNCAXIM, "asyncaxim", "aclk200", E4X12_GATE_ISP1, 0,
818c2ecf20Sopenharmony_ci	     0, 0),
828c2ecf20Sopenharmony_ci	GATE(CLK_ISP_SMMU_ISPCX, "smmu_ispcx", "aclk200", E4X12_GATE_ISP1, 4,
838c2ecf20Sopenharmony_ci	     0, 0),
848c2ecf20Sopenharmony_ci	GATE(CLK_ISP_SPI0_ISP, "spi0_isp", "aclk200", E4X12_GATE_ISP1, 12,
858c2ecf20Sopenharmony_ci	     0, 0),
868c2ecf20Sopenharmony_ci	GATE(CLK_ISP_SPI1_ISP, "spi1_isp", "aclk200", E4X12_GATE_ISP1, 13,
878c2ecf20Sopenharmony_ci	     0, 0),
888c2ecf20Sopenharmony_ci};
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic int __maybe_unused exynos4x12_isp_clk_suspend(struct device *dev)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	struct samsung_clk_provider *ctx = dev_get_drvdata(dev);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	samsung_clk_save(ctx->reg_base, exynos4x12_save_isp,
958c2ecf20Sopenharmony_ci			 ARRAY_SIZE(exynos4x12_clk_isp_save));
968c2ecf20Sopenharmony_ci	return 0;
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistatic int __maybe_unused exynos4x12_isp_clk_resume(struct device *dev)
1008c2ecf20Sopenharmony_ci{
1018c2ecf20Sopenharmony_ci	struct samsung_clk_provider *ctx = dev_get_drvdata(dev);
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	samsung_clk_restore(ctx->reg_base, exynos4x12_save_isp,
1048c2ecf20Sopenharmony_ci			    ARRAY_SIZE(exynos4x12_clk_isp_save));
1058c2ecf20Sopenharmony_ci	return 0;
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_cistatic int __init exynos4x12_isp_clk_probe(struct platform_device *pdev)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	struct samsung_clk_provider *ctx;
1118c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
1128c2ecf20Sopenharmony_ci	struct device_node *np = dev->of_node;
1138c2ecf20Sopenharmony_ci	struct resource *res;
1148c2ecf20Sopenharmony_ci	void __iomem *reg_base;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1178c2ecf20Sopenharmony_ci	reg_base = devm_ioremap_resource(dev, res);
1188c2ecf20Sopenharmony_ci	if (IS_ERR(reg_base)) {
1198c2ecf20Sopenharmony_ci		dev_err(dev, "failed to map registers\n");
1208c2ecf20Sopenharmony_ci		return PTR_ERR(reg_base);
1218c2ecf20Sopenharmony_ci	}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	exynos4x12_save_isp = samsung_clk_alloc_reg_dump(exynos4x12_clk_isp_save,
1248c2ecf20Sopenharmony_ci					ARRAY_SIZE(exynos4x12_clk_isp_save));
1258c2ecf20Sopenharmony_ci	if (!exynos4x12_save_isp)
1268c2ecf20Sopenharmony_ci		return -ENOMEM;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	ctx = samsung_clk_init(np, reg_base, CLK_NR_ISP_CLKS);
1298c2ecf20Sopenharmony_ci	ctx->dev = dev;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, ctx);
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	pm_runtime_set_active(dev);
1348c2ecf20Sopenharmony_ci	pm_runtime_enable(dev);
1358c2ecf20Sopenharmony_ci	pm_runtime_get_sync(dev);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	samsung_clk_register_div(ctx, exynos4x12_isp_div_clks,
1388c2ecf20Sopenharmony_ci				 ARRAY_SIZE(exynos4x12_isp_div_clks));
1398c2ecf20Sopenharmony_ci	samsung_clk_register_gate(ctx, exynos4x12_isp_gate_clks,
1408c2ecf20Sopenharmony_ci				  ARRAY_SIZE(exynos4x12_isp_gate_clks));
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	samsung_clk_of_add_provider(np, ctx);
1438c2ecf20Sopenharmony_ci	pm_runtime_put(dev);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	return 0;
1468c2ecf20Sopenharmony_ci}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_cistatic const struct of_device_id exynos4x12_isp_clk_of_match[] = {
1498c2ecf20Sopenharmony_ci	{ .compatible = "samsung,exynos4412-isp-clock", },
1508c2ecf20Sopenharmony_ci	{ },
1518c2ecf20Sopenharmony_ci};
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_cistatic const struct dev_pm_ops exynos4x12_isp_pm_ops = {
1548c2ecf20Sopenharmony_ci	SET_RUNTIME_PM_OPS(exynos4x12_isp_clk_suspend,
1558c2ecf20Sopenharmony_ci			   exynos4x12_isp_clk_resume, NULL)
1568c2ecf20Sopenharmony_ci	SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
1578c2ecf20Sopenharmony_ci				     pm_runtime_force_resume)
1588c2ecf20Sopenharmony_ci};
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistatic struct platform_driver exynos4x12_isp_clk_driver __refdata = {
1618c2ecf20Sopenharmony_ci	.driver	= {
1628c2ecf20Sopenharmony_ci		.name = "exynos4x12-isp-clk",
1638c2ecf20Sopenharmony_ci		.of_match_table = exynos4x12_isp_clk_of_match,
1648c2ecf20Sopenharmony_ci		.suppress_bind_attrs = true,
1658c2ecf20Sopenharmony_ci		.pm = &exynos4x12_isp_pm_ops,
1668c2ecf20Sopenharmony_ci	},
1678c2ecf20Sopenharmony_ci	.probe = exynos4x12_isp_clk_probe,
1688c2ecf20Sopenharmony_ci};
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_cistatic int __init exynos4x12_isp_clk_init(void)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	return platform_driver_register(&exynos4x12_isp_clk_driver);
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_cicore_initcall(exynos4x12_isp_clk_init);
175