Commit ef382228 authored by Stephen Boyd's avatar Stephen Boyd
Browse files

Merge tag 'samsung-clk-6.4' of...

Merge tag 'samsung-clk-6.4' of https://git.kernel.org/pub/scm/linux/kernel/git/krzk/linux into clk-samsung

Pull Samsung SoC clk drivers updates from Krzysztof Kozlowski:

 - Exynos850: Add CMU_G3D clock controller for the Mali GPU.  This
   brings new PLLs and few cleanups/simplifications in core Exynos clock
   controller code, so they can be easier re-used in Exynos850 clock
   controller driver.
   New CMU_G3D clock controller needs Devicetree bindings header changes
   with clock indices which are pulled from Samsung SoC repository.

 - Extract Exynos5433 (ARM64) clock controller power management code to
   common driver parts, so later it can be re-used by other Exynos clock
   controller drivers.  This only prepares for such re-usage, which is
   expected to come later for Exynos850.

 - Exynos850: make PMU_ALIVE_PCLK clock critical, because it is needed
   for core block - Power Management Unit.

 - Cleanup: remove() callback returns void.

* tag 'samsung-clk-6.4' of https://git.kernel.org/pub/scm/linux/kernel/git/krzk/linux:
  clk: samsung: exynos850: Make PMU_ALIVE_PCLK critical
  clk: samsung: Convert to platform remove callback returning void
  clk: samsung: exynos5433: Extract PM support to common ARM64 layer
  clk: samsung: Extract parent clock enabling to common function
  clk: samsung: Extract clocks registration to common function
  clk: samsung: exynos850: Add AUD and HSI main gate clocks
  clk: samsung: exynos850: Implement CMU_G3D domain
  clk: samsung: clk-pll: Implement pll0818x PLL type
  clk: samsung: Set dev in samsung_clk_init()
  clk: samsung: Don't pass reg_base to samsung_clk_register_pll()
  clk: samsung: Remove np argument from samsung_clk_init()
  dt-bindings: clock: exynos850: Add AUD and HSI main gate clocks
  dt-bindings: clock: exynos850: Add Exynos850 CMU_G3D
parents fe15c26e babb3e6a
Loading
Loading
Loading
Loading
+19 −0
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ properties:
      - samsung,exynos850-cmu-cmgp
      - samsung,exynos850-cmu-core
      - samsung,exynos850-cmu-dpu
      - samsung,exynos850-cmu-g3d
      - samsung,exynos850-cmu-hsi
      - samsung,exynos850-cmu-is
      - samsung,exynos850-cmu-mfcmscl
@@ -169,6 +170,24 @@ allOf:
            - const: oscclk
            - const: dout_dpu

  - if:
      properties:
        compatible:
          contains:
            const: samsung,exynos850-cmu-g3d

    then:
      properties:
        clocks:
          items:
            - description: External reference clock (26 MHz)
            - description: G3D clock (from CMU_TOP)

        clock-names:
          items:
            - const: oscclk
            - const: dout_g3d_switch

  - if:
      properties:
        compatible:
+213 −16
Original line number Diff line number Diff line
@@ -10,6 +10,9 @@
 */
#include <linux/clk.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>

#include "clk-exynos-arm64.h"

@@ -21,6 +24,19 @@
#define GATE_OFF_START		0x2000
#define GATE_OFF_END		0x2fff

struct exynos_arm64_cmu_data {
	struct samsung_clk_reg_dump *clk_save;
	unsigned int nr_clk_save;
	const struct samsung_clk_reg_dump *clk_suspend;
	unsigned int nr_clk_suspend;

	struct clk *clk;
	struct clk **pclks;
	int nr_pclks;

	struct samsung_clk_provider *ctx;
};

/**
 * exynos_arm64_init_clocks - Set clocks initial configuration
 * @np:			CMU device tree node with "reg" property (CMU addr)
@@ -56,6 +72,83 @@ static void __init exynos_arm64_init_clocks(struct device_node *np,
	iounmap(reg_base);
}

/**
 * exynos_arm64_enable_bus_clk - Enable parent clock of specified CMU
 *
 * @dev:	Device object; may be NULL if this function is not being
 *		called from platform driver probe function
 * @np:		CMU device tree node
 * @cmu:	CMU data
 *
 * Keep CMU parent clock running (needed for CMU registers access).
 *
 * Return: 0 on success or a negative error code on failure.
 */
static int __init exynos_arm64_enable_bus_clk(struct device *dev,
		struct device_node *np, const struct samsung_cmu_info *cmu)
{
	struct clk *parent_clk;

	if (!cmu->clk_name)
		return 0;

	if (dev) {
		struct exynos_arm64_cmu_data *data;

		parent_clk = clk_get(dev, cmu->clk_name);
		data = dev_get_drvdata(dev);
		if (data)
			data->clk = parent_clk;
	} else {
		parent_clk = of_clk_get_by_name(np, cmu->clk_name);
	}

	if (IS_ERR(parent_clk))
		return PTR_ERR(parent_clk);

	return clk_prepare_enable(parent_clk);
}

static int __init exynos_arm64_cmu_prepare_pm(struct device *dev,
		const struct samsung_cmu_info *cmu)
{
	struct exynos_arm64_cmu_data *data = dev_get_drvdata(dev);
	int i;

	data->clk_save = samsung_clk_alloc_reg_dump(cmu->clk_regs,
						    cmu->nr_clk_regs);
	if (!data->clk_save)
		return -ENOMEM;

	data->nr_clk_save = cmu->nr_clk_regs;
	data->clk_suspend = cmu->suspend_regs;
	data->nr_clk_suspend = cmu->nr_suspend_regs;
	data->nr_pclks = of_clk_get_parent_count(dev->of_node);
	if (!data->nr_pclks)
		return 0;

	data->pclks = devm_kcalloc(dev, sizeof(struct clk *), data->nr_pclks,
				   GFP_KERNEL);
	if (!data->pclks) {
		kfree(data->clk_save);
		return -ENOMEM;
	}

	for (i = 0; i < data->nr_pclks; i++) {
		struct clk *clk = of_clk_get(dev->of_node, i);

		if (IS_ERR(clk)) {
			kfree(data->clk_save);
			while (--i >= 0)
				clk_put(data->pclks[i]);
			return PTR_ERR(clk);
		}
		data->pclks[i] = clk;
	}

	return 0;
}

/**
 * exynos_arm64_register_cmu - Register specified Exynos CMU domain
 * @dev:	Device object; may be NULL if this function is not being
@@ -72,23 +165,127 @@ static void __init exynos_arm64_init_clocks(struct device_node *np,
void __init exynos_arm64_register_cmu(struct device *dev,
		struct device_node *np, const struct samsung_cmu_info *cmu)
{
	/* Keep CMU parent clock running (needed for CMU registers access) */
	if (cmu->clk_name) {
		struct clk *parent_clk;
	int err;

		if (dev)
			parent_clk = clk_get(dev, cmu->clk_name);
		else
			parent_clk = of_clk_get_by_name(np, cmu->clk_name);
	/*
	 * Try to boot even if the parent clock enablement fails, as it might be
	 * already enabled by bootloader.
	 */
	err = exynos_arm64_enable_bus_clk(dev, np, cmu);
	if (err)
		pr_err("%s: could not enable bus clock %s; err = %d\n",
		       __func__, cmu->clk_name, err);

		if (IS_ERR(parent_clk)) {
			pr_err("%s: could not find bus clock %s; err = %ld\n",
			       __func__, cmu->clk_name, PTR_ERR(parent_clk));
		} else {
			clk_prepare_enable(parent_clk);
		}
	exynos_arm64_init_clocks(np, cmu->clk_regs, cmu->nr_clk_regs);
	samsung_cmu_register_one(np, cmu);
}

/**
 * exynos_arm64_register_cmu_pm - Register Exynos CMU domain with PM support
 *
 * @pdev:	Platform device object
 * @set_manual:	If true, set gate clocks to manual mode
 *
 * It's a version of exynos_arm64_register_cmu() with PM support. Should be
 * called from probe function of platform driver.
 *
 * Return: 0 on success, or negative error code on error.
 */
int __init exynos_arm64_register_cmu_pm(struct platform_device *pdev,
					bool set_manual)
{
	const struct samsung_cmu_info *cmu;
	struct device *dev = &pdev->dev;
	struct device_node *np = dev->of_node;
	struct exynos_arm64_cmu_data *data;
	void __iomem *reg_base;
	int ret;

	cmu = of_device_get_match_data(dev);

	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
	if (!data)
		return -ENOMEM;

	platform_set_drvdata(pdev, data);

	ret = exynos_arm64_cmu_prepare_pm(dev, cmu);
	if (ret)
		return ret;

	/*
	 * Try to boot even if the parent clock enablement fails, as it might be
	 * already enabled by bootloader.
	 */
	ret = exynos_arm64_enable_bus_clk(dev, NULL, cmu);
	if (ret)
		dev_err(dev, "%s: could not enable bus clock %s; err = %d\n",
		       __func__, cmu->clk_name, ret);

	if (set_manual)
		exynos_arm64_init_clocks(np, cmu->clk_regs, cmu->nr_clk_regs);
	samsung_cmu_register_one(np, cmu);

	reg_base = devm_platform_ioremap_resource(pdev, 0);
	if (IS_ERR(reg_base))
		return PTR_ERR(reg_base);

	data->ctx = samsung_clk_init(dev, reg_base, cmu->nr_clk_ids);

	/*
	 * Enable runtime PM here to allow the clock core using runtime PM
	 * for the registered clocks. Additionally, we increase the runtime
	 * PM usage count before registering the clocks, to prevent the
	 * clock core from runtime suspending the device.
	 */
	pm_runtime_get_noresume(dev);
	pm_runtime_set_active(dev);
	pm_runtime_enable(dev);

	samsung_cmu_register_clocks(data->ctx, cmu);
	samsung_clk_of_add_provider(dev->of_node, data->ctx);
	pm_runtime_put_sync(dev);

	return 0;
}

int exynos_arm64_cmu_suspend(struct device *dev)
{
	struct exynos_arm64_cmu_data *data = dev_get_drvdata(dev);
	int i;

	samsung_clk_save(data->ctx->reg_base, data->clk_save,
			 data->nr_clk_save);

	for (i = 0; i < data->nr_pclks; i++)
		clk_prepare_enable(data->pclks[i]);

	/* For suspend some registers have to be set to certain values */
	samsung_clk_restore(data->ctx->reg_base, data->clk_suspend,
			    data->nr_clk_suspend);

	for (i = 0; i < data->nr_pclks; i++)
		clk_disable_unprepare(data->pclks[i]);

	clk_disable_unprepare(data->clk);

	return 0;
}

int exynos_arm64_cmu_resume(struct device *dev)
{
	struct exynos_arm64_cmu_data *data = dev_get_drvdata(dev);
	int i;

	clk_prepare_enable(data->clk);

	for (i = 0; i < data->nr_pclks; i++)
		clk_prepare_enable(data->pclks[i]);

	samsung_clk_restore(data->ctx->reg_base, data->clk_save,
			    data->nr_clk_save);

	for (i = 0; i < data->nr_pclks; i++)
		clk_disable_unprepare(data->pclks[i]);

	return 0;
}
+3 −0
Original line number Diff line number Diff line
@@ -16,5 +16,8 @@

void exynos_arm64_register_cmu(struct device *dev,
		struct device_node *np, const struct samsung_cmu_info *cmu);
int exynos_arm64_register_cmu_pm(struct platform_device *pdev, bool set_manual);
int exynos_arm64_cmu_suspend(struct device *dev);
int exynos_arm64_cmu_resume(struct device *dev);

#endif /* __CLK_EXYNOS_ARM64_H */
+2 −4
Original line number Diff line number Diff line
@@ -268,7 +268,7 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
	return ret;
}

static int exynos_audss_clk_remove(struct platform_device *pdev)
static void exynos_audss_clk_remove(struct platform_device *pdev)
{
	of_clk_del_provider(pdev->dev.of_node);

@@ -277,8 +277,6 @@ static int exynos_audss_clk_remove(struct platform_device *pdev)

	if (!IS_ERR(epll))
		clk_disable_unprepare(epll);

	return 0;
}

static const struct dev_pm_ops exynos_audss_clk_pm_ops = {
@@ -295,7 +293,7 @@ static struct platform_driver exynos_audss_clk_driver = {
		.pm = &exynos_audss_clk_pm_ops,
	},
	.probe = exynos_audss_clk_probe,
	.remove = exynos_audss_clk_remove,
	.remove_new = exynos_audss_clk_remove,
};

module_platform_driver(exynos_audss_clk_driver);
+2 −4
Original line number Diff line number Diff line
@@ -196,15 +196,13 @@ static int exynos_clkout_probe(struct platform_device *pdev)
	return ret;
}

static int exynos_clkout_remove(struct platform_device *pdev)
static void exynos_clkout_remove(struct platform_device *pdev)
{
	struct exynos_clkout *clkout = platform_get_drvdata(pdev);

	of_clk_del_provider(clkout->np);
	clk_hw_unregister(clkout->data.hws[0]);
	iounmap(clkout->reg);

	return 0;
}

static int __maybe_unused exynos_clkout_suspend(struct device *dev)
@@ -235,7 +233,7 @@ static struct platform_driver exynos_clkout_driver = {
		.pm = &exynos_clkout_pm_ops,
	},
	.probe = exynos_clkout_probe,
	.remove = exynos_clkout_remove,
	.remove_new = exynos_clkout_remove,
};
module_platform_driver(exynos_clkout_driver);

Loading