sun6i_drc.c (3091B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (C) 2016 Free Electrons 4 * 5 * Maxime Ripard <maxime.ripard@free-electrons.com> 6 */ 7 8#include <linux/clk.h> 9#include <linux/component.h> 10#include <linux/module.h> 11#include <linux/mod_devicetable.h> 12#include <linux/platform_device.h> 13#include <linux/regmap.h> 14#include <linux/reset.h> 15 16struct sun6i_drc { 17 struct clk *bus_clk; 18 struct clk *mod_clk; 19 struct reset_control *reset; 20}; 21 22static int sun6i_drc_bind(struct device *dev, struct device *master, 23 void *data) 24{ 25 struct sun6i_drc *drc; 26 int ret; 27 28 drc = devm_kzalloc(dev, sizeof(*drc), GFP_KERNEL); 29 if (!drc) 30 return -ENOMEM; 31 dev_set_drvdata(dev, drc); 32 33 drc->reset = devm_reset_control_get(dev, NULL); 34 if (IS_ERR(drc->reset)) { 35 dev_err(dev, "Couldn't get our reset line\n"); 36 return PTR_ERR(drc->reset); 37 } 38 39 ret = reset_control_deassert(drc->reset); 40 if (ret) { 41 dev_err(dev, "Couldn't deassert our reset line\n"); 42 return ret; 43 } 44 45 drc->bus_clk = devm_clk_get(dev, "ahb"); 46 if (IS_ERR(drc->bus_clk)) { 47 dev_err(dev, "Couldn't get our bus clock\n"); 48 ret = PTR_ERR(drc->bus_clk); 49 goto err_assert_reset; 50 } 51 clk_prepare_enable(drc->bus_clk); 52 53 drc->mod_clk = devm_clk_get(dev, "mod"); 54 if (IS_ERR(drc->mod_clk)) { 55 dev_err(dev, "Couldn't get our mod clock\n"); 56 ret = PTR_ERR(drc->mod_clk); 57 goto err_disable_bus_clk; 58 } 59 60 ret = clk_set_rate_exclusive(drc->mod_clk, 300000000); 61 if (ret) { 62 dev_err(dev, "Couldn't set the module clock frequency\n"); 63 goto err_disable_bus_clk; 64 } 65 66 clk_prepare_enable(drc->mod_clk); 67 68 return 0; 69 70err_disable_bus_clk: 71 clk_disable_unprepare(drc->bus_clk); 72err_assert_reset: 73 reset_control_assert(drc->reset); 74 return ret; 75} 76 77static void sun6i_drc_unbind(struct device *dev, struct device *master, 78 void *data) 79{ 80 struct sun6i_drc *drc = dev_get_drvdata(dev); 81 82 clk_rate_exclusive_put(drc->mod_clk); 83 clk_disable_unprepare(drc->mod_clk); 84 clk_disable_unprepare(drc->bus_clk); 85 reset_control_assert(drc->reset); 86} 87 88static const struct component_ops sun6i_drc_ops = { 89 .bind = sun6i_drc_bind, 90 .unbind = sun6i_drc_unbind, 91}; 92 93static int sun6i_drc_probe(struct platform_device *pdev) 94{ 95 return component_add(&pdev->dev, &sun6i_drc_ops); 96} 97 98static int sun6i_drc_remove(struct platform_device *pdev) 99{ 100 component_del(&pdev->dev, &sun6i_drc_ops); 101 102 return 0; 103} 104 105static const struct of_device_id sun6i_drc_of_table[] = { 106 { .compatible = "allwinner,sun6i-a31-drc" }, 107 { .compatible = "allwinner,sun6i-a31s-drc" }, 108 { .compatible = "allwinner,sun8i-a23-drc" }, 109 { .compatible = "allwinner,sun8i-a33-drc" }, 110 { .compatible = "allwinner,sun9i-a80-drc" }, 111 { } 112}; 113MODULE_DEVICE_TABLE(of, sun6i_drc_of_table); 114 115static struct platform_driver sun6i_drc_platform_driver = { 116 .probe = sun6i_drc_probe, 117 .remove = sun6i_drc_remove, 118 .driver = { 119 .name = "sun6i-drc", 120 .of_match_table = sun6i_drc_of_table, 121 }, 122}; 123module_platform_driver(sun6i_drc_platform_driver); 124 125MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); 126MODULE_DESCRIPTION("Allwinner A31 Dynamic Range Control (DRC) Driver"); 127MODULE_LICENSE("GPL");