1 /* 2 * Copyright (c) 2013 Samsung Electronics Co., Ltd. 3 * Author: Padmavathi Venna <padma.v@samsung.com> 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License version 2 as 7 * published by the Free Software Foundation. 8 * 9 * Common Clock Framework support for Audio Subsystem Clock Controller. 10 */ 11 12 #include <linux/clkdev.h> 13 #include <linux/io.h> 14 #include <linux/clk-provider.h> 15 #include <linux/of_address.h> 16 #include <linux/syscore_ops.h> 17 18 #include <dt-bindings/clk/exynos-audss-clk.h> 19 20 static DEFINE_SPINLOCK(lock); 21 static struct clk **clk_table; 22 static void __iomem *reg_base; 23 static struct clk_onecell_data clk_data; 24 25 #define ASS_CLK_SRC 0x0 26 #define ASS_CLK_DIV 0x4 27 #define ASS_CLK_GATE 0x8 28 29 static unsigned long reg_save[][2] = { 30 {ASS_CLK_SRC, 0}, 31 {ASS_CLK_DIV, 0}, 32 {ASS_CLK_GATE, 0}, 33 }; 34 35 /* list of all parent clock list */ 36 static const char *mout_audss_p[] = { "fin_pll", "fout_epll" }; 37 static const char *mout_i2s_p[] = { "mout_audss", "cdclk0", "sclk_audio0" }; 38 39 #ifdef CONFIG_PM_SLEEP 40 static int exynos_audss_clk_suspend(void) 41 { 42 int i; 43 44 for (i = 0; i < ARRAY_SIZE(reg_save); i++) 45 reg_save[i][1] = readl(reg_base + reg_save[i][0]); 46 47 return 0; 48 } 49 50 static void exynos_audss_clk_resume(void) 51 { 52 int i; 53 54 for (i = 0; i < ARRAY_SIZE(reg_save); i++) 55 writel(reg_save[i][1], reg_base + reg_save[i][0]); 56 } 57 58 static struct syscore_ops exynos_audss_clk_syscore_ops = { 59 .suspend = exynos_audss_clk_suspend, 60 .resume = exynos_audss_clk_resume, 61 }; 62 #endif /* CONFIG_PM_SLEEP */ 63 64 /* register exynos_audss clocks */ 65 static void __init exynos_audss_clk_init(struct device_node *np) 66 { 67 reg_base = of_iomap(np, 0); 68 if (!reg_base) { 69 pr_err("%s: failed to map audss registers\n", __func__); 70 return; 71 } 72 73 clk_table = kzalloc(sizeof(struct clk *) * EXYNOS_AUDSS_MAX_CLKS, 74 GFP_KERNEL); 75 if (!clk_table) { 76 pr_err("%s: could not allocate clk lookup table\n", __func__); 77 return; 78 } 79 80 clk_data.clks = clk_table; 81 clk_data.clk_num = EXYNOS_AUDSS_MAX_CLKS; 82 of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); 83 84 clk_table[EXYNOS_MOUT_AUDSS] = clk_register_mux(NULL, "mout_audss", 85 mout_audss_p, ARRAY_SIZE(mout_audss_p), 86 CLK_SET_RATE_NO_REPARENT, 87 reg_base + ASS_CLK_SRC, 0, 1, 0, &lock); 88 89 clk_table[EXYNOS_MOUT_I2S] = clk_register_mux(NULL, "mout_i2s", 90 mout_i2s_p, ARRAY_SIZE(mout_i2s_p), 91 CLK_SET_RATE_NO_REPARENT, 92 reg_base + ASS_CLK_SRC, 2, 2, 0, &lock); 93 94 clk_table[EXYNOS_DOUT_SRP] = clk_register_divider(NULL, "dout_srp", 95 "mout_audss", 0, reg_base + ASS_CLK_DIV, 0, 4, 96 0, &lock); 97 98 clk_table[EXYNOS_DOUT_AUD_BUS] = clk_register_divider(NULL, 99 "dout_aud_bus", "dout_srp", 0, 100 reg_base + ASS_CLK_DIV, 4, 4, 0, &lock); 101 102 clk_table[EXYNOS_DOUT_I2S] = clk_register_divider(NULL, "dout_i2s", 103 "mout_i2s", 0, reg_base + ASS_CLK_DIV, 8, 4, 0, 104 &lock); 105 106 clk_table[EXYNOS_SRP_CLK] = clk_register_gate(NULL, "srp_clk", 107 "dout_srp", CLK_SET_RATE_PARENT, 108 reg_base + ASS_CLK_GATE, 0, 0, &lock); 109 110 clk_table[EXYNOS_I2S_BUS] = clk_register_gate(NULL, "i2s_bus", 111 "dout_aud_bus", CLK_SET_RATE_PARENT, 112 reg_base + ASS_CLK_GATE, 2, 0, &lock); 113 114 clk_table[EXYNOS_SCLK_I2S] = clk_register_gate(NULL, "sclk_i2s", 115 "dout_i2s", CLK_SET_RATE_PARENT, 116 reg_base + ASS_CLK_GATE, 3, 0, &lock); 117 118 clk_table[EXYNOS_PCM_BUS] = clk_register_gate(NULL, "pcm_bus", 119 "sclk_pcm", CLK_SET_RATE_PARENT, 120 reg_base + ASS_CLK_GATE, 4, 0, &lock); 121 122 clk_table[EXYNOS_SCLK_PCM] = clk_register_gate(NULL, "sclk_pcm", 123 "div_pcm0", CLK_SET_RATE_PARENT, 124 reg_base + ASS_CLK_GATE, 5, 0, &lock); 125 126 #ifdef CONFIG_PM_SLEEP 127 register_syscore_ops(&exynos_audss_clk_syscore_ops); 128 #endif 129 130 pr_info("Exynos: Audss: clock setup completed\n"); 131 } 132 CLK_OF_DECLARE(exynos4210_audss_clk, "samsung,exynos4210-audss-clock", 133 exynos_audss_clk_init); 134 CLK_OF_DECLARE(exynos5250_audss_clk, "samsung,exynos5250-audss-clock", 135 exynos_audss_clk_init); 136