xref: /openbmc/linux/drivers/clk/samsung/clk-exynos-audss.c (revision b37a4224104568198b93fb9831224cfe7d83fff8)
11241ef94SPadmavathi Venna /*
21241ef94SPadmavathi Venna  * Copyright (c) 2013 Samsung Electronics Co., Ltd.
31241ef94SPadmavathi Venna  * Author: Padmavathi Venna <padma.v@samsung.com>
41241ef94SPadmavathi Venna  *
51241ef94SPadmavathi Venna  * This program is free software; you can redistribute it and/or modify
61241ef94SPadmavathi Venna  * it under the terms of the GNU General Public License version 2 as
71241ef94SPadmavathi Venna  * published by the Free Software Foundation.
81241ef94SPadmavathi Venna  *
91241ef94SPadmavathi Venna  * Common Clock Framework support for Audio Subsystem Clock Controller.
101241ef94SPadmavathi Venna */
111241ef94SPadmavathi Venna 
121241ef94SPadmavathi Venna #include <linux/clkdev.h>
131241ef94SPadmavathi Venna #include <linux/io.h>
141241ef94SPadmavathi Venna #include <linux/clk-provider.h>
151241ef94SPadmavathi Venna #include <linux/of_address.h>
161241ef94SPadmavathi Venna #include <linux/syscore_ops.h>
17*b37a4224SAndrew Bresticker #include <linux/module.h>
18*b37a4224SAndrew Bresticker #include <linux/platform_device.h>
191241ef94SPadmavathi Venna 
201241ef94SPadmavathi Venna #include <dt-bindings/clk/exynos-audss-clk.h>
211241ef94SPadmavathi Venna 
221241ef94SPadmavathi Venna static DEFINE_SPINLOCK(lock);
231241ef94SPadmavathi Venna static struct clk **clk_table;
241241ef94SPadmavathi Venna static void __iomem *reg_base;
251241ef94SPadmavathi Venna static struct clk_onecell_data clk_data;
261241ef94SPadmavathi Venna 
271241ef94SPadmavathi Venna #define ASS_CLK_SRC 0x0
281241ef94SPadmavathi Venna #define ASS_CLK_DIV 0x4
291241ef94SPadmavathi Venna #define ASS_CLK_GATE 0x8
301241ef94SPadmavathi Venna 
313fd68c99SKrzysztof Kozlowski /* list of all parent clock list */
323fd68c99SKrzysztof Kozlowski static const char *mout_audss_p[] = { "fin_pll", "fout_epll" };
333fd68c99SKrzysztof Kozlowski static const char *mout_i2s_p[] = { "mout_audss", "cdclk0", "sclk_audio0" };
343fd68c99SKrzysztof Kozlowski 
353fd68c99SKrzysztof Kozlowski #ifdef CONFIG_PM_SLEEP
361241ef94SPadmavathi Venna static unsigned long reg_save[][2] = {
371241ef94SPadmavathi Venna 	{ASS_CLK_SRC,  0},
381241ef94SPadmavathi Venna 	{ASS_CLK_DIV,  0},
391241ef94SPadmavathi Venna 	{ASS_CLK_GATE, 0},
401241ef94SPadmavathi Venna };
411241ef94SPadmavathi Venna 
421241ef94SPadmavathi Venna static int exynos_audss_clk_suspend(void)
431241ef94SPadmavathi Venna {
441241ef94SPadmavathi Venna 	int i;
451241ef94SPadmavathi Venna 
461241ef94SPadmavathi Venna 	for (i = 0; i < ARRAY_SIZE(reg_save); i++)
471241ef94SPadmavathi Venna 		reg_save[i][1] = readl(reg_base + reg_save[i][0]);
481241ef94SPadmavathi Venna 
491241ef94SPadmavathi Venna 	return 0;
501241ef94SPadmavathi Venna }
511241ef94SPadmavathi Venna 
521241ef94SPadmavathi Venna static void exynos_audss_clk_resume(void)
531241ef94SPadmavathi Venna {
541241ef94SPadmavathi Venna 	int i;
551241ef94SPadmavathi Venna 
561241ef94SPadmavathi Venna 	for (i = 0; i < ARRAY_SIZE(reg_save); i++)
571241ef94SPadmavathi Venna 		writel(reg_save[i][1], reg_base + reg_save[i][0]);
581241ef94SPadmavathi Venna }
591241ef94SPadmavathi Venna 
601241ef94SPadmavathi Venna static struct syscore_ops exynos_audss_clk_syscore_ops = {
611241ef94SPadmavathi Venna 	.suspend	= exynos_audss_clk_suspend,
621241ef94SPadmavathi Venna 	.resume		= exynos_audss_clk_resume,
631241ef94SPadmavathi Venna };
641241ef94SPadmavathi Venna #endif /* CONFIG_PM_SLEEP */
651241ef94SPadmavathi Venna 
661241ef94SPadmavathi Venna /* register exynos_audss clocks */
67*b37a4224SAndrew Bresticker static int exynos_audss_clk_probe(struct platform_device *pdev)
681241ef94SPadmavathi Venna {
69*b37a4224SAndrew Bresticker 	int i, ret = 0;
70*b37a4224SAndrew Bresticker 	struct resource *res;
71*b37a4224SAndrew Bresticker 
72*b37a4224SAndrew Bresticker 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
73*b37a4224SAndrew Bresticker 	reg_base = devm_ioremap_resource(&pdev->dev, res);
74*b37a4224SAndrew Bresticker 	if (IS_ERR(reg_base)) {
75*b37a4224SAndrew Bresticker 		dev_err(&pdev->dev, "failed to map audss registers\n");
76*b37a4224SAndrew Bresticker 		return PTR_ERR(reg_base);
771241ef94SPadmavathi Venna 	}
781241ef94SPadmavathi Venna 
79*b37a4224SAndrew Bresticker 	clk_table = devm_kzalloc(&pdev->dev,
80*b37a4224SAndrew Bresticker 				sizeof(struct clk *) * EXYNOS_AUDSS_MAX_CLKS,
811241ef94SPadmavathi Venna 				GFP_KERNEL);
82*b37a4224SAndrew Bresticker 	if (!clk_table)
83*b37a4224SAndrew Bresticker 		return -ENOMEM;
841241ef94SPadmavathi Venna 
851241ef94SPadmavathi Venna 	clk_data.clks = clk_table;
861241ef94SPadmavathi Venna 	clk_data.clk_num = EXYNOS_AUDSS_MAX_CLKS;
871241ef94SPadmavathi Venna 
881241ef94SPadmavathi Venna 	clk_table[EXYNOS_MOUT_AUDSS] = clk_register_mux(NULL, "mout_audss",
89819c1de3SJames Hogan 				mout_audss_p, ARRAY_SIZE(mout_audss_p),
90819c1de3SJames Hogan 				CLK_SET_RATE_NO_REPARENT,
911241ef94SPadmavathi Venna 				reg_base + ASS_CLK_SRC, 0, 1, 0, &lock);
921241ef94SPadmavathi Venna 
931241ef94SPadmavathi Venna 	clk_table[EXYNOS_MOUT_I2S] = clk_register_mux(NULL, "mout_i2s",
94819c1de3SJames Hogan 				mout_i2s_p, ARRAY_SIZE(mout_i2s_p),
95819c1de3SJames Hogan 				CLK_SET_RATE_NO_REPARENT,
961241ef94SPadmavathi Venna 				reg_base + ASS_CLK_SRC, 2, 2, 0, &lock);
971241ef94SPadmavathi Venna 
981241ef94SPadmavathi Venna 	clk_table[EXYNOS_DOUT_SRP] = clk_register_divider(NULL, "dout_srp",
991241ef94SPadmavathi Venna 				"mout_audss", 0, reg_base + ASS_CLK_DIV, 0, 4,
1001241ef94SPadmavathi Venna 				0, &lock);
1011241ef94SPadmavathi Venna 
1021241ef94SPadmavathi Venna 	clk_table[EXYNOS_DOUT_AUD_BUS] = clk_register_divider(NULL,
1031241ef94SPadmavathi Venna 				"dout_aud_bus", "dout_srp", 0,
1041241ef94SPadmavathi Venna 				reg_base + ASS_CLK_DIV, 4, 4, 0, &lock);
1051241ef94SPadmavathi Venna 
1061241ef94SPadmavathi Venna 	clk_table[EXYNOS_DOUT_I2S] = clk_register_divider(NULL, "dout_i2s",
1071241ef94SPadmavathi Venna 				"mout_i2s", 0, reg_base + ASS_CLK_DIV, 8, 4, 0,
1081241ef94SPadmavathi Venna 				&lock);
1091241ef94SPadmavathi Venna 
1101241ef94SPadmavathi Venna 	clk_table[EXYNOS_SRP_CLK] = clk_register_gate(NULL, "srp_clk",
1111241ef94SPadmavathi Venna 				"dout_srp", CLK_SET_RATE_PARENT,
1121241ef94SPadmavathi Venna 				reg_base + ASS_CLK_GATE, 0, 0, &lock);
1131241ef94SPadmavathi Venna 
1141241ef94SPadmavathi Venna 	clk_table[EXYNOS_I2S_BUS] = clk_register_gate(NULL, "i2s_bus",
1151241ef94SPadmavathi Venna 				"dout_aud_bus", CLK_SET_RATE_PARENT,
1161241ef94SPadmavathi Venna 				reg_base + ASS_CLK_GATE, 2, 0, &lock);
1171241ef94SPadmavathi Venna 
1181241ef94SPadmavathi Venna 	clk_table[EXYNOS_SCLK_I2S] = clk_register_gate(NULL, "sclk_i2s",
1191241ef94SPadmavathi Venna 				"dout_i2s", CLK_SET_RATE_PARENT,
1201241ef94SPadmavathi Venna 				reg_base + ASS_CLK_GATE, 3, 0, &lock);
1211241ef94SPadmavathi Venna 
1221241ef94SPadmavathi Venna 	clk_table[EXYNOS_PCM_BUS] = clk_register_gate(NULL, "pcm_bus",
1231241ef94SPadmavathi Venna 				 "sclk_pcm", CLK_SET_RATE_PARENT,
1241241ef94SPadmavathi Venna 				reg_base + ASS_CLK_GATE, 4, 0, &lock);
1251241ef94SPadmavathi Venna 
1261241ef94SPadmavathi Venna 	clk_table[EXYNOS_SCLK_PCM] = clk_register_gate(NULL, "sclk_pcm",
1271241ef94SPadmavathi Venna 				"div_pcm0", CLK_SET_RATE_PARENT,
1281241ef94SPadmavathi Venna 				reg_base + ASS_CLK_GATE, 5, 0, &lock);
1291241ef94SPadmavathi Venna 
130*b37a4224SAndrew Bresticker 	for (i = 0; i < clk_data.clk_num; i++) {
131*b37a4224SAndrew Bresticker 		if (IS_ERR(clk_table[i])) {
132*b37a4224SAndrew Bresticker 			dev_err(&pdev->dev, "failed to register clock %d\n", i);
133*b37a4224SAndrew Bresticker 			ret = PTR_ERR(clk_table[i]);
134*b37a4224SAndrew Bresticker 			goto unregister;
135*b37a4224SAndrew Bresticker 		}
136*b37a4224SAndrew Bresticker 	}
137*b37a4224SAndrew Bresticker 
138*b37a4224SAndrew Bresticker 	ret = of_clk_add_provider(pdev->dev.of_node, of_clk_src_onecell_get,
139*b37a4224SAndrew Bresticker 					&clk_data);
140*b37a4224SAndrew Bresticker 	if (ret) {
141*b37a4224SAndrew Bresticker 		dev_err(&pdev->dev, "failed to add clock provider\n");
142*b37a4224SAndrew Bresticker 		goto unregister;
143*b37a4224SAndrew Bresticker 	}
144*b37a4224SAndrew Bresticker 
1451241ef94SPadmavathi Venna #ifdef CONFIG_PM_SLEEP
1461241ef94SPadmavathi Venna 	register_syscore_ops(&exynos_audss_clk_syscore_ops);
1471241ef94SPadmavathi Venna #endif
1481241ef94SPadmavathi Venna 
149*b37a4224SAndrew Bresticker 	dev_info(&pdev->dev, "setup completed\n");
150*b37a4224SAndrew Bresticker 
151*b37a4224SAndrew Bresticker 	return 0;
152*b37a4224SAndrew Bresticker 
153*b37a4224SAndrew Bresticker unregister:
154*b37a4224SAndrew Bresticker 	for (i = 0; i < clk_data.clk_num; i++) {
155*b37a4224SAndrew Bresticker 		if (!IS_ERR(clk_table[i]))
156*b37a4224SAndrew Bresticker 			clk_unregister(clk_table[i]);
1571241ef94SPadmavathi Venna 	}
158*b37a4224SAndrew Bresticker 
159*b37a4224SAndrew Bresticker 	return ret;
160*b37a4224SAndrew Bresticker }
161*b37a4224SAndrew Bresticker 
162*b37a4224SAndrew Bresticker static int exynos_audss_clk_remove(struct platform_device *pdev)
163*b37a4224SAndrew Bresticker {
164*b37a4224SAndrew Bresticker 	int i;
165*b37a4224SAndrew Bresticker 
166*b37a4224SAndrew Bresticker 	of_clk_del_provider(pdev->dev.of_node);
167*b37a4224SAndrew Bresticker 
168*b37a4224SAndrew Bresticker 	for (i = 0; i < clk_data.clk_num; i++) {
169*b37a4224SAndrew Bresticker 		if (!IS_ERR(clk_table[i]))
170*b37a4224SAndrew Bresticker 			clk_unregister(clk_table[i]);
171*b37a4224SAndrew Bresticker 	}
172*b37a4224SAndrew Bresticker 
173*b37a4224SAndrew Bresticker 	return 0;
174*b37a4224SAndrew Bresticker }
175*b37a4224SAndrew Bresticker 
176*b37a4224SAndrew Bresticker static const struct of_device_id exynos_audss_clk_of_match[] = {
177*b37a4224SAndrew Bresticker 	{ .compatible = "samsung,exynos4210-audss-clock", },
178*b37a4224SAndrew Bresticker 	{ .compatible = "samsung,exynos5250-audss-clock", },
179*b37a4224SAndrew Bresticker 	{},
180*b37a4224SAndrew Bresticker };
181*b37a4224SAndrew Bresticker 
182*b37a4224SAndrew Bresticker static struct platform_driver exynos_audss_clk_driver = {
183*b37a4224SAndrew Bresticker 	.driver	= {
184*b37a4224SAndrew Bresticker 		.name = "exynos-audss-clk",
185*b37a4224SAndrew Bresticker 		.owner = THIS_MODULE,
186*b37a4224SAndrew Bresticker 		.of_match_table = exynos_audss_clk_of_match,
187*b37a4224SAndrew Bresticker 	},
188*b37a4224SAndrew Bresticker 	.probe = exynos_audss_clk_probe,
189*b37a4224SAndrew Bresticker 	.remove = exynos_audss_clk_remove,
190*b37a4224SAndrew Bresticker };
191*b37a4224SAndrew Bresticker 
192*b37a4224SAndrew Bresticker static int __init exynos_audss_clk_init(void)
193*b37a4224SAndrew Bresticker {
194*b37a4224SAndrew Bresticker 	return platform_driver_register(&exynos_audss_clk_driver);
195*b37a4224SAndrew Bresticker }
196*b37a4224SAndrew Bresticker core_initcall(exynos_audss_clk_init);
197*b37a4224SAndrew Bresticker 
198*b37a4224SAndrew Bresticker static void __exit exynos_audss_clk_exit(void)
199*b37a4224SAndrew Bresticker {
200*b37a4224SAndrew Bresticker 	platform_driver_unregister(&exynos_audss_clk_driver);
201*b37a4224SAndrew Bresticker }
202*b37a4224SAndrew Bresticker module_exit(exynos_audss_clk_exit);
203*b37a4224SAndrew Bresticker 
204*b37a4224SAndrew Bresticker MODULE_AUTHOR("Padmavathi Venna <padma.v@samsung.com>");
205*b37a4224SAndrew Bresticker MODULE_DESCRIPTION("Exynos Audio Subsystem Clock Controller");
206*b37a4224SAndrew Bresticker MODULE_LICENSE("GPL v2");
207*b37a4224SAndrew Bresticker MODULE_ALIAS("platform:exynos-audss-clk");
208