diff --git a/Documentation/devicetree/bindings/clock/clk-exynos-audss.txt b/Documentation/devicetree/bindings/clock/clk-exynos-audss.txt new file mode 100644 index 000000000000..a1201802f90d --- /dev/null +++ b/Documentation/devicetree/bindings/clock/clk-exynos-audss.txt @@ -0,0 +1,64 @@ +* Samsung Audio Subsystem Clock Controller + +The Samsung Audio Subsystem clock controller generates and supplies clocks +to Audio Subsystem block available in the S5PV210 and Exynos SoCs. The clock +binding described here is applicable to all SoC's in Exynos family. + +Required Properties: + +- compatible: should be one of the following: + - "samsung,exynos4210-audss-clock" - controller compatible with all Exynos4 SoCs. + - "samsung,exynos5250-audss-clock" - controller compatible with all Exynos5 SoCs. + +- reg: physical base address and length of the controller's register set. + +- #clock-cells: should be 1. + +The following is the list of clocks generated by the controller. Each clock is +assigned an identifier and client nodes use this identifier to specify the +clock which they consume. Some of the clocks are available only on a particular +Exynos4 SoC and this is specified where applicable. + +Provided clocks: + +Clock ID SoC (if specific) +----------------------------------------------- + +mout_audss 0 +mout_i2s 1 +dout_srp 2 +dout_aud_bus 3 +dout_i2s 4 +srp_clk 5 +i2s_bus 6 +sclk_i2s 7 +pcm_bus 8 +sclk_pcm 9 + +Example 1: An example of a clock controller node is listed below. + +clock_audss: audss-clock-controller@3810000 { + compatible = "samsung,exynos5250-audss-clock"; + reg = <0x03810000 0x0C>; + #clock-cells = <1>; +}; + +Example 2: I2S controller node that consumes the clock generated by the clock + controller. Refer to the standard clock bindings for information + about 'clocks' and 'clock-names' property. + +i2s0: i2s@03830000 { + compatible = "samsung,i2s-v5"; + reg = <0x03830000 0x100>; + dmas = <&pdma0 10 + &pdma0 9 + &pdma0 8>; + dma-names = "tx", "rx", "tx-sec"; + clocks = <&clock_audss EXYNOS_I2S_BUS>, + <&clock_audss EXYNOS_I2S_BUS>, + <&clock_audss EXYNOS_SCLK_I2S>, + <&clock_audss EXYNOS_MOUT_AUDSS>, + <&clock_audss EXYNOS_MOUT_I2S>; + clock-names = "iis", "i2s_opclk0", "i2s_opclk1", + "mout_audss", "mout_i2s"; +}; diff --git a/drivers/clk/samsung/Makefile b/drivers/clk/samsung/Makefile index b7c232e67425..187681013bdb 100644 --- a/drivers/clk/samsung/Makefile +++ b/drivers/clk/samsung/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_COMMON_CLK) += clk.o clk-pll.o obj-$(CONFIG_ARCH_EXYNOS4) += clk-exynos4.o obj-$(CONFIG_SOC_EXYNOS5250) += clk-exynos5250.o obj-$(CONFIG_SOC_EXYNOS5440) += clk-exynos5440.o +obj-$(CONFIG_ARCH_EXYNOS) += clk-exynos-audss.o diff --git a/drivers/clk/samsung/clk-exynos-audss.c b/drivers/clk/samsung/clk-exynos-audss.c new file mode 100644 index 000000000000..9b1bbd52fd1f --- /dev/null +++ b/drivers/clk/samsung/clk-exynos-audss.c @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * Author: Padmavathi Venna + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Common Clock Framework support for Audio Subsystem Clock Controller. +*/ + +#include +#include +#include +#include +#include + +#include + +static DEFINE_SPINLOCK(lock); +static struct clk **clk_table; +static void __iomem *reg_base; +static struct clk_onecell_data clk_data; + +#define ASS_CLK_SRC 0x0 +#define ASS_CLK_DIV 0x4 +#define ASS_CLK_GATE 0x8 + +static unsigned long reg_save[][2] = { + {ASS_CLK_SRC, 0}, + {ASS_CLK_DIV, 0}, + {ASS_CLK_GATE, 0}, +}; + +/* list of all parent clock list */ +static const char *mout_audss_p[] = { "fin_pll", "fout_epll" }; +static const char *mout_i2s_p[] = { "mout_audss", "cdclk0", "sclk_audio0" }; + +#ifdef CONFIG_PM_SLEEP +static int exynos_audss_clk_suspend(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(reg_save); i++) + reg_save[i][1] = readl(reg_base + reg_save[i][0]); + + return 0; +} + +static void exynos_audss_clk_resume(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(reg_save); i++) + writel(reg_save[i][1], reg_base + reg_save[i][0]); +} + +static struct syscore_ops exynos_audss_clk_syscore_ops = { + .suspend = exynos_audss_clk_suspend, + .resume = exynos_audss_clk_resume, +}; +#endif /* CONFIG_PM_SLEEP */ + +/* register exynos_audss clocks */ +void __init exynos_audss_clk_init(struct device_node *np) +{ + reg_base = of_iomap(np, 0); + if (!reg_base) { + pr_err("%s: failed to map audss registers\n", __func__); + return; + } + + clk_table = kzalloc(sizeof(struct clk *) * EXYNOS_AUDSS_MAX_CLKS, + GFP_KERNEL); + if (!clk_table) { + pr_err("%s: could not allocate clk lookup table\n", __func__); + return; + } + + clk_data.clks = clk_table; + clk_data.clk_num = EXYNOS_AUDSS_MAX_CLKS; + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); + + clk_table[EXYNOS_MOUT_AUDSS] = clk_register_mux(NULL, "mout_audss", + mout_audss_p, ARRAY_SIZE(mout_audss_p), 0, + reg_base + ASS_CLK_SRC, 0, 1, 0, &lock); + + clk_table[EXYNOS_MOUT_I2S] = clk_register_mux(NULL, "mout_i2s", + mout_i2s_p, ARRAY_SIZE(mout_i2s_p), 0, + reg_base + ASS_CLK_SRC, 2, 2, 0, &lock); + + clk_table[EXYNOS_DOUT_SRP] = clk_register_divider(NULL, "dout_srp", + "mout_audss", 0, reg_base + ASS_CLK_DIV, 0, 4, + 0, &lock); + + clk_table[EXYNOS_DOUT_AUD_BUS] = clk_register_divider(NULL, + "dout_aud_bus", "dout_srp", 0, + reg_base + ASS_CLK_DIV, 4, 4, 0, &lock); + + clk_table[EXYNOS_DOUT_I2S] = clk_register_divider(NULL, "dout_i2s", + "mout_i2s", 0, reg_base + ASS_CLK_DIV, 8, 4, 0, + &lock); + + clk_table[EXYNOS_SRP_CLK] = clk_register_gate(NULL, "srp_clk", + "dout_srp", CLK_SET_RATE_PARENT, + reg_base + ASS_CLK_GATE, 0, 0, &lock); + + clk_table[EXYNOS_I2S_BUS] = clk_register_gate(NULL, "i2s_bus", + "dout_aud_bus", CLK_SET_RATE_PARENT, + reg_base + ASS_CLK_GATE, 2, 0, &lock); + + clk_table[EXYNOS_SCLK_I2S] = clk_register_gate(NULL, "sclk_i2s", + "dout_i2s", CLK_SET_RATE_PARENT, + reg_base + ASS_CLK_GATE, 3, 0, &lock); + + clk_table[EXYNOS_PCM_BUS] = clk_register_gate(NULL, "pcm_bus", + "sclk_pcm", CLK_SET_RATE_PARENT, + reg_base + ASS_CLK_GATE, 4, 0, &lock); + + clk_table[EXYNOS_SCLK_PCM] = clk_register_gate(NULL, "sclk_pcm", + "div_pcm0", CLK_SET_RATE_PARENT, + reg_base + ASS_CLK_GATE, 5, 0, &lock); + +#ifdef CONFIG_PM_SLEEP + register_syscore_ops(&exynos_audss_clk_syscore_ops); +#endif + + pr_info("Exynos: Audss: clock setup completed\n"); +} +CLK_OF_DECLARE(exynos4210_audss_clk, "samsung,exynos4210-audss-clock", + exynos_audss_clk_init); +CLK_OF_DECLARE(exynos5250_audss_clk, "samsung,exynos5250-audss-clock", + exynos_audss_clk_init); diff --git a/include/dt-bindings/clk/exynos-audss-clk.h b/include/dt-bindings/clk/exynos-audss-clk.h new file mode 100644 index 000000000000..8279f427c60f --- /dev/null +++ b/include/dt-bindings/clk/exynos-audss-clk.h @@ -0,0 +1,25 @@ +/* + * This header provides constants for Samsung audio subsystem + * clock controller. + * + * The constants defined in this header are being used in dts + * and exynos audss driver. + */ + +#ifndef _DT_BINDINGS_CLK_EXYNOS_AUDSS_H +#define _DT_BINDINGS_CLK_EXYNOS_AUDSS_H + +#define EXYNOS_MOUT_AUDSS 0 +#define EXYNOS_MOUT_I2S 1 +#define EXYNOS_DOUT_SRP 2 +#define EXYNOS_DOUT_AUD_BUS 3 +#define EXYNOS_DOUT_I2S 4 +#define EXYNOS_SRP_CLK 5 +#define EXYNOS_I2S_BUS 6 +#define EXYNOS_SCLK_I2S 7 +#define EXYNOS_PCM_BUS 8 +#define EXYNOS_SCLK_PCM 9 + +#define EXYNOS_AUDSS_MAX_CLKS 10 + +#endif