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