18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci
38c2ecf20Sopenharmony_ci/*
48c2ecf20Sopenharmony_ci * Memory Mapped IO Fixed clock driver
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (C) 2018 Cadence Design Systems, Inc.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Authors:
98c2ecf20Sopenharmony_ci *	Jan Kotas <jank@cadence.com>
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/clk-provider.h>
138c2ecf20Sopenharmony_ci#include <linux/io.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/of_address.h>
168c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_cistatic struct clk_hw *fixed_mmio_clk_setup(struct device_node *node)
198c2ecf20Sopenharmony_ci{
208c2ecf20Sopenharmony_ci	struct clk_hw *clk;
218c2ecf20Sopenharmony_ci	const char *clk_name = node->name;
228c2ecf20Sopenharmony_ci	void __iomem *base;
238c2ecf20Sopenharmony_ci	u32 freq;
248c2ecf20Sopenharmony_ci	int ret;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	base = of_iomap(node, 0);
278c2ecf20Sopenharmony_ci	if (!base) {
288c2ecf20Sopenharmony_ci		pr_err("%pOFn: failed to map address\n", node);
298c2ecf20Sopenharmony_ci		return ERR_PTR(-EIO);
308c2ecf20Sopenharmony_ci	}
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	freq = readl(base);
338c2ecf20Sopenharmony_ci	iounmap(base);
348c2ecf20Sopenharmony_ci	of_property_read_string(node, "clock-output-names", &clk_name);
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	clk = clk_hw_register_fixed_rate(NULL, clk_name, NULL, 0, freq);
378c2ecf20Sopenharmony_ci	if (IS_ERR(clk)) {
388c2ecf20Sopenharmony_ci		pr_err("%pOFn: failed to register fixed rate clock\n", node);
398c2ecf20Sopenharmony_ci		return clk;
408c2ecf20Sopenharmony_ci	}
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	ret = of_clk_add_hw_provider(node, of_clk_hw_simple_get, clk);
438c2ecf20Sopenharmony_ci	if (ret) {
448c2ecf20Sopenharmony_ci		pr_err("%pOFn: failed to add clock provider\n", node);
458c2ecf20Sopenharmony_ci		clk_hw_unregister(clk);
468c2ecf20Sopenharmony_ci		clk = ERR_PTR(ret);
478c2ecf20Sopenharmony_ci	}
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	return clk;
508c2ecf20Sopenharmony_ci}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic void __init of_fixed_mmio_clk_setup(struct device_node *node)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	fixed_mmio_clk_setup(node);
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ciCLK_OF_DECLARE(fixed_mmio_clk, "fixed-mmio-clock", of_fixed_mmio_clk_setup);
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci/**
598c2ecf20Sopenharmony_ci * This is not executed when of_fixed_mmio_clk_setup succeeded.
608c2ecf20Sopenharmony_ci */
618c2ecf20Sopenharmony_cistatic int of_fixed_mmio_clk_probe(struct platform_device *pdev)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	struct clk_hw *clk;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	clk = fixed_mmio_clk_setup(pdev->dev.of_node);
668c2ecf20Sopenharmony_ci	if (IS_ERR(clk))
678c2ecf20Sopenharmony_ci		return PTR_ERR(clk);
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, clk);
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	return 0;
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic int of_fixed_mmio_clk_remove(struct platform_device *pdev)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	struct clk_hw *clk = platform_get_drvdata(pdev);
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	of_clk_del_provider(pdev->dev.of_node);
798c2ecf20Sopenharmony_ci	clk_hw_unregister_fixed_rate(clk);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	return 0;
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistatic const struct of_device_id of_fixed_mmio_clk_ids[] = {
858c2ecf20Sopenharmony_ci	{ .compatible = "fixed-mmio-clock" },
868c2ecf20Sopenharmony_ci	{ }
878c2ecf20Sopenharmony_ci};
888c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, of_fixed_mmio_clk_ids);
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic struct platform_driver of_fixed_mmio_clk_driver = {
918c2ecf20Sopenharmony_ci	.driver = {
928c2ecf20Sopenharmony_ci		.name = "of_fixed_mmio_clk",
938c2ecf20Sopenharmony_ci		.of_match_table = of_fixed_mmio_clk_ids,
948c2ecf20Sopenharmony_ci	},
958c2ecf20Sopenharmony_ci	.probe = of_fixed_mmio_clk_probe,
968c2ecf20Sopenharmony_ci	.remove = of_fixed_mmio_clk_remove,
978c2ecf20Sopenharmony_ci};
988c2ecf20Sopenharmony_cimodule_platform_driver(of_fixed_mmio_clk_driver);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jan Kotas <jank@cadence.com>");
1018c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Memory Mapped IO Fixed clock driver");
1028c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
103