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