Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit baa39cd2 authored by Mike Turquette's avatar Mike Turquette
Browse files

Merge tag 'for_3.14/samsung-clk' of...

Merge tag 'for_3.14/samsung-clk' of git://git.kernel.org/pub/scm/linux/kernel/git/tfiga/samsung-clk into clk-next-samsung

(A bit late) first round of Samsung clock patches for v3.14.
parents ada12c47 59d711e9
Loading
Loading
Loading
Loading
+35 −4
Original line number Diff line number Diff line
@@ -8,12 +8,29 @@ 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.

  - "samsung,exynos5250-audss-clock" - controller compatible with Exynos5250
    SoCs.
  - "samsung,exynos5420-audss-clock" - controller compatible with Exynos5420
    SoCs.
- reg: physical base address and length of the controller's register set.

- #clock-cells: should be 1.

- clocks:
  - pll_ref: Fixed rate PLL reference clock, parent of mout_audss. "fin_pll"
    is used if not specified.
  - pll_in: Input PLL to the AudioSS block, parent of mout_audss. "fout_epll"
    is used if not specified.
  - cdclk: External i2s clock, parent of mout_i2s. "cdclk0" is used if not
    specified.
  - sclk_audio: Audio bus clock, parent of mout_i2s. "sclk_audio0" is used if
    not specified.
  - sclk_pcm_in: PCM clock, parent of sclk_pcm.  "sclk_pcm0" is used if not
    specified.

- clock-names: Aliases for the above clocks. They should be "pll_ref",
  "pll_in", "cdclk", "sclk_audio", and "sclk_pcm_in" respectively.

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
@@ -34,16 +51,30 @@ i2s_bus 6
sclk_i2s        7
pcm_bus         8
sclk_pcm        9
adma            10      Exynos5420

Example 1: An example of a clock controller node using the default input
	   clock names is listed below.

clock_audss: audss-clock-controller@3810000 {
	compatible = "samsung,exynos5250-audss-clock";
	reg = <0x03810000 0x0C>;
	#clock-cells = <1>;
};

Example 1: An example of a clock controller node is listed below.
Example 2: An example of a clock controller node with the input clocks
           specified.

clock_audss: audss-clock-controller@3810000 {
	compatible = "samsung,exynos5250-audss-clock";
	reg = <0x03810000 0x0C>;
	#clock-cells = <1>;
	clocks = <&clock 1>, <&clock 7>, <&clock 138>, <&clock 160>,
		<&ext_i2s_clk>;
	clock-names = "pll_ref", "pll_in", "sclk_audio", "sclk_pcm_in", "cdclk";
};

Example 2: I2S controller node that consumes the clock generated by the clock
Example 3: 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.

+3 −0
Original line number Diff line number Diff line
@@ -62,6 +62,7 @@ clock which they consume.
  div_i2s1		157
  div_i2s2		158
  sclk_hdmiphy		159
  div_pcm0		160


   [Peripheral Clock Gates]
@@ -159,6 +160,8 @@ clock which they consume.
  mixer			343
  hdmi			344
  g2d			345
  mdma0			346
  smmu_mdma0		347


   [Clock Muxes]
+3 −1
Original line number Diff line number Diff line
@@ -88,6 +88,8 @@
		compatible = "samsung,exynos5250-audss-clock";
		reg = <0x03810000 0x0C>;
		#clock-cells = <1>;
		clocks = <&clock 1>, <&clock 7>, <&clock 138>, <&clock 160>;
		clock-names = "pll_ref", "pll_in", "sclk_audio", "sclk_pcm_in";
	};

	timer {
@@ -559,7 +561,7 @@
			compatible = "arm,pl330", "arm,primecell";
			reg = <0x10800000 0x1000>;
			interrupts = <0 33 0>;
			clocks = <&clock 271>;
			clocks = <&clock 346>;
			clock-names = "apb_pclk";
			#dma-cells = <1>;
			#dma-channels = <8>;
+2 −2
Original line number Diff line number Diff line
@@ -76,8 +76,8 @@
		compatible = "samsung,exynos5420-audss-clock";
		reg = <0x03810000 0x0C>;
		#clock-cells = <1>;
		clocks = <&clock 148>;
		clock-names = "sclk_audio";
		clocks = <&clock 1>, <&clock 5>, <&clock 148>, <&clock 149>;
		clock-names = "pll_ref", "pll_in", "sclk_audio", "sclk_pcm_in";
	};

	codec@11000000 {
+137 −24
Original line number Diff line number Diff line
@@ -14,9 +14,17 @@
#include <linux/clk-provider.h>
#include <linux/of_address.h>
#include <linux/syscore_ops.h>
#include <linux/module.h>
#include <linux/platform_device.h>

#include <dt-bindings/clk/exynos-audss-clk.h>

enum exynos_audss_clk_type {
	TYPE_EXYNOS4210,
	TYPE_EXYNOS5250,
	TYPE_EXYNOS5420,
};

static DEFINE_SPINLOCK(lock);
static struct clk **clk_table;
static void __iomem *reg_base;
@@ -26,17 +34,13 @@ static struct clk_onecell_data clk_data;
#define ASS_CLK_DIV 0x4
#define ASS_CLK_GATE 0x8

#ifdef CONFIG_PM_SLEEP
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;
@@ -61,31 +65,69 @@ static struct syscore_ops exynos_audss_clk_syscore_ops = {
};
#endif /* CONFIG_PM_SLEEP */

static const struct of_device_id exynos_audss_clk_of_match[] = {
	{ .compatible = "samsung,exynos4210-audss-clock",
	  .data = (void *)TYPE_EXYNOS4210, },
	{ .compatible = "samsung,exynos5250-audss-clock",
	  .data = (void *)TYPE_EXYNOS5250, },
	{ .compatible = "samsung,exynos5420-audss-clock",
	  .data = (void *)TYPE_EXYNOS5420, },
	{},
};

/* register exynos_audss clocks */
static void __init exynos_audss_clk_init(struct device_node *np)
static int exynos_audss_clk_probe(struct platform_device *pdev)
{
	reg_base = of_iomap(np, 0);
	if (!reg_base) {
		pr_err("%s: failed to map audss registers\n", __func__);
		return;
	int i, ret = 0;
	struct resource *res;
	const char *mout_audss_p[] = {"fin_pll", "fout_epll"};
	const char *mout_i2s_p[] = {"mout_audss", "cdclk0", "sclk_audio0"};
	const char *sclk_pcm_p = "sclk_pcm0";
	struct clk *pll_ref, *pll_in, *cdclk, *sclk_audio, *sclk_pcm_in;
	const struct of_device_id *match;
	enum exynos_audss_clk_type variant;

	match = of_match_node(exynos_audss_clk_of_match, pdev->dev.of_node);
	if (!match)
		return -EINVAL;
	variant = (enum exynos_audss_clk_type)match->data;

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	reg_base = devm_ioremap_resource(&pdev->dev, res);
	if (IS_ERR(reg_base)) {
		dev_err(&pdev->dev, "failed to map audss registers\n");
		return PTR_ERR(reg_base);
	}

	clk_table = kzalloc(sizeof(struct clk *) * EXYNOS_AUDSS_MAX_CLKS,
	clk_table = devm_kzalloc(&pdev->dev,
				sizeof(struct clk *) * EXYNOS_AUDSS_MAX_CLKS,
				GFP_KERNEL);
	if (!clk_table) {
		pr_err("%s: could not allocate clk lookup table\n", __func__);
		return;
	}
	if (!clk_table)
		return -ENOMEM;

	clk_data.clks = clk_table;
	if (variant == TYPE_EXYNOS5420)
		clk_data.clk_num = EXYNOS_AUDSS_MAX_CLKS;
	of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);

	else
		clk_data.clk_num = EXYNOS_AUDSS_MAX_CLKS - 1;

	pll_ref = devm_clk_get(&pdev->dev, "pll_ref");
	pll_in = devm_clk_get(&pdev->dev, "pll_in");
	if (!IS_ERR(pll_ref))
		mout_audss_p[0] = __clk_get_name(pll_ref);
	if (!IS_ERR(pll_in))
		mout_audss_p[1] = __clk_get_name(pll_in);
	clk_table[EXYNOS_MOUT_AUDSS] = clk_register_mux(NULL, "mout_audss",
				mout_audss_p, ARRAY_SIZE(mout_audss_p),
				CLK_SET_RATE_NO_REPARENT,
				reg_base + ASS_CLK_SRC, 0, 1, 0, &lock);

	cdclk = devm_clk_get(&pdev->dev, "cdclk");
	sclk_audio = devm_clk_get(&pdev->dev, "sclk_audio");
	if (!IS_ERR(cdclk))
		mout_i2s_p[1] = __clk_get_name(cdclk);
	if (!IS_ERR(sclk_audio))
		mout_i2s_p[2] = __clk_get_name(sclk_audio);
	clk_table[EXYNOS_MOUT_I2S] = clk_register_mux(NULL, "mout_i2s",
				mout_i2s_p, ARRAY_SIZE(mout_i2s_p),
				CLK_SET_RATE_NO_REPARENT,
@@ -119,17 +161,88 @@ static void __init exynos_audss_clk_init(struct device_node *np)
				 "sclk_pcm", CLK_SET_RATE_PARENT,
				reg_base + ASS_CLK_GATE, 4, 0, &lock);

	sclk_pcm_in = devm_clk_get(&pdev->dev, "sclk_pcm_in");
	if (!IS_ERR(sclk_pcm_in))
		sclk_pcm_p = __clk_get_name(sclk_pcm_in);
	clk_table[EXYNOS_SCLK_PCM] = clk_register_gate(NULL, "sclk_pcm",
				"div_pcm0", CLK_SET_RATE_PARENT,
				sclk_pcm_p, CLK_SET_RATE_PARENT,
				reg_base + ASS_CLK_GATE, 5, 0, &lock);

	if (variant == TYPE_EXYNOS5420) {
		clk_table[EXYNOS_ADMA] = clk_register_gate(NULL, "adma",
				"dout_srp", CLK_SET_RATE_PARENT,
				reg_base + ASS_CLK_GATE, 9, 0, &lock);
	}

	for (i = 0; i < clk_data.clk_num; i++) {
		if (IS_ERR(clk_table[i])) {
			dev_err(&pdev->dev, "failed to register clock %d\n", i);
			ret = PTR_ERR(clk_table[i]);
			goto unregister;
		}
	}

	ret = of_clk_add_provider(pdev->dev.of_node, of_clk_src_onecell_get,
					&clk_data);
	if (ret) {
		dev_err(&pdev->dev, "failed to add clock provider\n");
		goto unregister;
	}

#ifdef CONFIG_PM_SLEEP
	register_syscore_ops(&exynos_audss_clk_syscore_ops);
#endif

	pr_info("Exynos: Audss: clock setup completed\n");
	dev_info(&pdev->dev, "setup completed\n");

	return 0;

unregister:
	for (i = 0; i < clk_data.clk_num; i++) {
		if (!IS_ERR(clk_table[i]))
			clk_unregister(clk_table[i]);
	}
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);

	return ret;
}

static int exynos_audss_clk_remove(struct platform_device *pdev)
{
	int i;

	of_clk_del_provider(pdev->dev.of_node);

	for (i = 0; i < clk_data.clk_num; i++) {
		if (!IS_ERR(clk_table[i]))
			clk_unregister(clk_table[i]);
	}

	return 0;
}

static struct platform_driver exynos_audss_clk_driver = {
	.driver	= {
		.name = "exynos-audss-clk",
		.owner = THIS_MODULE,
		.of_match_table = exynos_audss_clk_of_match,
	},
	.probe = exynos_audss_clk_probe,
	.remove = exynos_audss_clk_remove,
};

static int __init exynos_audss_clk_init(void)
{
	return platform_driver_register(&exynos_audss_clk_driver);
}
core_initcall(exynos_audss_clk_init);

static void __exit exynos_audss_clk_exit(void)
{
	platform_driver_unregister(&exynos_audss_clk_driver);
}
module_exit(exynos_audss_clk_exit);

MODULE_AUTHOR("Padmavathi Venna <padma.v@samsung.com>");
MODULE_DESCRIPTION("Exynos Audio Subsystem Clock Controller");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:exynos-audss-clk");
Loading