xref: /openbmc/linux/drivers/mfd/exynos-lpass.c (revision fed64817)
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