1c9fd3ce1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2c695ababSSylwester Nawrocki /*
3c695ababSSylwester Nawrocki * Copyright (C) 2015 - 2016 Samsung Electronics Co., Ltd.
4c695ababSSylwester Nawrocki *
5c695ababSSylwester Nawrocki * Authors: Inha Song <ideal.song@samsung.com>
6c695ababSSylwester Nawrocki * Sylwester Nawrocki <s.nawrocki@samsung.com>
7c695ababSSylwester Nawrocki *
8c695ababSSylwester Nawrocki * Samsung Exynos SoC series Low Power Audio Subsystem driver.
9c695ababSSylwester Nawrocki *
10c695ababSSylwester Nawrocki * This module provides regmap for the Top SFR region and instantiates
11c695ababSSylwester Nawrocki * devices for IP blocks like DMAC, I2S, UART.
12c695ababSSylwester Nawrocki */
13c695ababSSylwester Nawrocki
148f1be5bdSMarek Szyprowski #include <linux/clk.h>
15c695ababSSylwester Nawrocki #include <linux/delay.h>
16c695ababSSylwester Nawrocki #include <linux/io.h>
17c695ababSSylwester Nawrocki #include <linux/module.h>
18c695ababSSylwester Nawrocki #include <linux/of.h>
19c695ababSSylwester Nawrocki #include <linux/of_platform.h>
20c695ababSSylwester Nawrocki #include <linux/platform_device.h>
2190f44717SMarek Szyprowski #include <linux/pm_runtime.h>
22c695ababSSylwester Nawrocki #include <linux/regmap.h>
23f7f6c060SKrzysztof Kozlowski #include <linux/soc/samsung/exynos-regs-pmu.h>
24c695ababSSylwester Nawrocki #include <linux/types.h>
25c695ababSSylwester Nawrocki
26c695ababSSylwester Nawrocki /* LPASS Top register definitions */
27c695ababSSylwester Nawrocki #define SFR_LPASS_CORE_SW_RESET 0x08
28c695ababSSylwester Nawrocki #define LPASS_SB_SW_RESET BIT(11)
29c695ababSSylwester Nawrocki #define LPASS_UART_SW_RESET BIT(10)
30c695ababSSylwester Nawrocki #define LPASS_PCM_SW_RESET BIT(9)
31c695ababSSylwester Nawrocki #define LPASS_I2S_SW_RESET BIT(8)
32c695ababSSylwester Nawrocki #define LPASS_WDT1_SW_RESET BIT(4)
33c695ababSSylwester Nawrocki #define LPASS_WDT0_SW_RESET BIT(3)
34c695ababSSylwester Nawrocki #define LPASS_TIMER_SW_RESET BIT(2)
35c695ababSSylwester Nawrocki #define LPASS_MEM_SW_RESET BIT(1)
36c695ababSSylwester Nawrocki #define LPASS_DMA_SW_RESET BIT(0)
37c695ababSSylwester Nawrocki
38c695ababSSylwester Nawrocki #define SFR_LPASS_INTR_CA5_MASK 0x48
39c695ababSSylwester Nawrocki #define SFR_LPASS_INTR_CPU_MASK 0x58
40c695ababSSylwester Nawrocki #define LPASS_INTR_APM BIT(9)
41c695ababSSylwester Nawrocki #define LPASS_INTR_MIF BIT(8)
42c695ababSSylwester Nawrocki #define LPASS_INTR_TIMER BIT(7)
43c695ababSSylwester Nawrocki #define LPASS_INTR_DMA BIT(6)
44c695ababSSylwester Nawrocki #define LPASS_INTR_GPIO BIT(5)
45c695ababSSylwester Nawrocki #define LPASS_INTR_I2S BIT(4)
46c695ababSSylwester Nawrocki #define LPASS_INTR_PCM BIT(3)
47c695ababSSylwester Nawrocki #define LPASS_INTR_SLIMBUS BIT(2)
48c695ababSSylwester Nawrocki #define LPASS_INTR_UART BIT(1)
49c695ababSSylwester Nawrocki #define LPASS_INTR_SFR BIT(0)
50c695ababSSylwester Nawrocki
51c695ababSSylwester Nawrocki struct exynos_lpass {
52c695ababSSylwester Nawrocki /* pointer to the LPASS TOP regmap */
53c695ababSSylwester Nawrocki struct regmap *top;
548f1be5bdSMarek Szyprowski struct clk *sfr0_clk;
55c695ababSSylwester Nawrocki };
56c695ababSSylwester Nawrocki
exynos_lpass_core_sw_reset(struct exynos_lpass * lpass,int mask)57c695ababSSylwester Nawrocki static void exynos_lpass_core_sw_reset(struct exynos_lpass *lpass, int mask)
58c695ababSSylwester Nawrocki {
59c695ababSSylwester Nawrocki unsigned int val = 0;
60c695ababSSylwester Nawrocki
61c695ababSSylwester Nawrocki regmap_read(lpass->top, SFR_LPASS_CORE_SW_RESET, &val);
62c695ababSSylwester Nawrocki
63c695ababSSylwester Nawrocki val &= ~mask;
64c695ababSSylwester Nawrocki regmap_write(lpass->top, SFR_LPASS_CORE_SW_RESET, val);
65c695ababSSylwester Nawrocki
66c695ababSSylwester Nawrocki usleep_range(100, 150);
67c695ababSSylwester Nawrocki
68c695ababSSylwester Nawrocki val |= mask;
69c695ababSSylwester Nawrocki regmap_write(lpass->top, SFR_LPASS_CORE_SW_RESET, val);
70c695ababSSylwester Nawrocki }
71c695ababSSylwester Nawrocki
exynos_lpass_enable(struct exynos_lpass * lpass)72c695ababSSylwester Nawrocki static void exynos_lpass_enable(struct exynos_lpass *lpass)
73c695ababSSylwester Nawrocki {
748f1be5bdSMarek Szyprowski clk_prepare_enable(lpass->sfr0_clk);
758f1be5bdSMarek Szyprowski
76c695ababSSylwester Nawrocki /* Unmask SFR, DMA and I2S interrupt */
77c695ababSSylwester Nawrocki regmap_write(lpass->top, SFR_LPASS_INTR_CA5_MASK,
78c695ababSSylwester Nawrocki LPASS_INTR_SFR | LPASS_INTR_DMA | LPASS_INTR_I2S);
79c695ababSSylwester Nawrocki
80c695ababSSylwester Nawrocki regmap_write(lpass->top, SFR_LPASS_INTR_CPU_MASK,
813f2d347eSBeomho Seo LPASS_INTR_SFR | LPASS_INTR_DMA | LPASS_INTR_I2S |
823f2d347eSBeomho Seo LPASS_INTR_UART);
83c695ababSSylwester Nawrocki
84c695ababSSylwester Nawrocki exynos_lpass_core_sw_reset(lpass, LPASS_I2S_SW_RESET);
85c695ababSSylwester Nawrocki exynos_lpass_core_sw_reset(lpass, LPASS_DMA_SW_RESET);
86c695ababSSylwester Nawrocki exynos_lpass_core_sw_reset(lpass, LPASS_MEM_SW_RESET);
873f2d347eSBeomho Seo exynos_lpass_core_sw_reset(lpass, LPASS_UART_SW_RESET);
88c695ababSSylwester Nawrocki }
89c695ababSSylwester Nawrocki
exynos_lpass_disable(struct exynos_lpass * lpass)90c695ababSSylwester Nawrocki static void exynos_lpass_disable(struct exynos_lpass *lpass)
91c695ababSSylwester Nawrocki {
92c695ababSSylwester Nawrocki /* Mask any unmasked IP interrupt sources */
93c695ababSSylwester Nawrocki regmap_write(lpass->top, SFR_LPASS_INTR_CPU_MASK, 0);
94c695ababSSylwester Nawrocki regmap_write(lpass->top, SFR_LPASS_INTR_CA5_MASK, 0);
958f1be5bdSMarek Szyprowski
968f1be5bdSMarek Szyprowski clk_disable_unprepare(lpass->sfr0_clk);
97c695ababSSylwester Nawrocki }
98c695ababSSylwester Nawrocki
99c695ababSSylwester Nawrocki static const struct regmap_config exynos_lpass_reg_conf = {
100c695ababSSylwester Nawrocki .reg_bits = 32,
101c695ababSSylwester Nawrocki .reg_stride = 4,
102c695ababSSylwester Nawrocki .val_bits = 32,
103c695ababSSylwester Nawrocki .max_register = 0xfc,
104c695ababSSylwester Nawrocki .fast_io = true,
105c695ababSSylwester Nawrocki };
106c695ababSSylwester Nawrocki
exynos_lpass_probe(struct platform_device * pdev)107c695ababSSylwester Nawrocki static int exynos_lpass_probe(struct platform_device *pdev)
108c695ababSSylwester Nawrocki {
109c695ababSSylwester Nawrocki struct device *dev = &pdev->dev;
110c695ababSSylwester Nawrocki struct exynos_lpass *lpass;
111c695ababSSylwester Nawrocki void __iomem *base_top;
112c695ababSSylwester Nawrocki
113c695ababSSylwester Nawrocki lpass = devm_kzalloc(dev, sizeof(*lpass), GFP_KERNEL);
114c695ababSSylwester Nawrocki if (!lpass)
115c695ababSSylwester Nawrocki return -ENOMEM;
116c695ababSSylwester Nawrocki
117*fed64817SYangtao Li base_top = devm_platform_ioremap_resource(pdev, 0);
118c695ababSSylwester Nawrocki if (IS_ERR(base_top))
119c695ababSSylwester Nawrocki return PTR_ERR(base_top);
120c695ababSSylwester Nawrocki
1218f1be5bdSMarek Szyprowski lpass->sfr0_clk = devm_clk_get(dev, "sfr0_ctrl");
1228f1be5bdSMarek Szyprowski if (IS_ERR(lpass->sfr0_clk))
1238f1be5bdSMarek Szyprowski return PTR_ERR(lpass->sfr0_clk);
1248f1be5bdSMarek Szyprowski
125c695ababSSylwester Nawrocki lpass->top = regmap_init_mmio(dev, base_top,
126c695ababSSylwester Nawrocki &exynos_lpass_reg_conf);
127c695ababSSylwester Nawrocki if (IS_ERR(lpass->top)) {
128c695ababSSylwester Nawrocki dev_err(dev, "LPASS top regmap initialization failed\n");
129c695ababSSylwester Nawrocki return PTR_ERR(lpass->top);
130c695ababSSylwester Nawrocki }
131c695ababSSylwester Nawrocki
132c695ababSSylwester Nawrocki platform_set_drvdata(pdev, lpass);
13390f44717SMarek Szyprowski pm_runtime_set_active(dev);
13490f44717SMarek Szyprowski pm_runtime_enable(dev);
135c695ababSSylwester Nawrocki exynos_lpass_enable(lpass);
136c695ababSSylwester Nawrocki
13711ee55d9SBenjamin Gaignard return devm_of_platform_populate(dev);
138c695ababSSylwester Nawrocki }
139c695ababSSylwester Nawrocki
exynos_lpass_remove(struct platform_device * pdev)140c414df12SMarek Szyprowski static int exynos_lpass_remove(struct platform_device *pdev)
141c414df12SMarek Szyprowski {
142c414df12SMarek Szyprowski struct exynos_lpass *lpass = platform_get_drvdata(pdev);
143c414df12SMarek Szyprowski
144c414df12SMarek Szyprowski exynos_lpass_disable(lpass);
14590f44717SMarek Szyprowski pm_runtime_disable(&pdev->dev);
14690f44717SMarek Szyprowski if (!pm_runtime_status_suspended(&pdev->dev))
14790f44717SMarek Szyprowski exynos_lpass_disable(lpass);
148c414df12SMarek Szyprowski regmap_exit(lpass->top);
149c414df12SMarek Szyprowski
150c414df12SMarek Szyprowski return 0;
151c414df12SMarek Szyprowski }
152c414df12SMarek Szyprowski
exynos_lpass_suspend(struct device * dev)15322a96b85SArnd Bergmann static int __maybe_unused exynos_lpass_suspend(struct device *dev)
154c695ababSSylwester Nawrocki {
155c695ababSSylwester Nawrocki struct exynos_lpass *lpass = dev_get_drvdata(dev);
156c695ababSSylwester Nawrocki
157c695ababSSylwester Nawrocki exynos_lpass_disable(lpass);
158c695ababSSylwester Nawrocki
159c695ababSSylwester Nawrocki return 0;
160c695ababSSylwester Nawrocki }
161c695ababSSylwester Nawrocki
exynos_lpass_resume(struct device * dev)16222a96b85SArnd Bergmann static int __maybe_unused exynos_lpass_resume(struct device *dev)
163c695ababSSylwester Nawrocki {
164c695ababSSylwester Nawrocki struct exynos_lpass *lpass = dev_get_drvdata(dev);
165c695ababSSylwester Nawrocki
166c695ababSSylwester Nawrocki exynos_lpass_enable(lpass);
167c695ababSSylwester Nawrocki
168c695ababSSylwester Nawrocki return 0;
169c695ababSSylwester Nawrocki }
170c695ababSSylwester Nawrocki
17190f44717SMarek Szyprowski static const struct dev_pm_ops lpass_pm_ops = {
17290f44717SMarek Szyprowski SET_RUNTIME_PM_OPS(exynos_lpass_suspend, exynos_lpass_resume, NULL)
17390f44717SMarek Szyprowski SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
17490f44717SMarek Szyprowski pm_runtime_force_resume)
17590f44717SMarek Szyprowski };
176c695ababSSylwester Nawrocki
177c695ababSSylwester Nawrocki static const struct of_device_id exynos_lpass_of_match[] = {
178c695ababSSylwester Nawrocki { .compatible = "samsung,exynos5433-lpass" },
179c695ababSSylwester Nawrocki { },
180c695ababSSylwester Nawrocki };
181c695ababSSylwester Nawrocki MODULE_DEVICE_TABLE(of, exynos_lpass_of_match);
182c695ababSSylwester Nawrocki
183c695ababSSylwester Nawrocki static struct platform_driver exynos_lpass_driver = {
184c695ababSSylwester Nawrocki .driver = {
185c695ababSSylwester Nawrocki .name = "exynos-lpass",
186c695ababSSylwester Nawrocki .pm = &lpass_pm_ops,
187c695ababSSylwester Nawrocki .of_match_table = exynos_lpass_of_match,
188c695ababSSylwester Nawrocki },
189c695ababSSylwester Nawrocki .probe = exynos_lpass_probe,
190c414df12SMarek Szyprowski .remove = exynos_lpass_remove,
191c695ababSSylwester Nawrocki };
192c695ababSSylwester Nawrocki module_platform_driver(exynos_lpass_driver);
193c695ababSSylwester Nawrocki
194c695ababSSylwester Nawrocki MODULE_DESCRIPTION("Samsung Low Power Audio Subsystem driver");
195c695ababSSylwester Nawrocki MODULE_LICENSE("GPL v2");
196