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 16 struct sun6i_drc { 17 struct clk *bus_clk; 18 struct clk *mod_clk; 19 struct reset_control *reset; 20 }; 21 22 static 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 70 err_disable_bus_clk: 71 clk_disable_unprepare(drc->bus_clk); 72 err_assert_reset: 73 reset_control_assert(drc->reset); 74 return ret; 75 } 76 77 static 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 88 static const struct component_ops sun6i_drc_ops = { 89 .bind = sun6i_drc_bind, 90 .unbind = sun6i_drc_unbind, 91 }; 92 93 static int sun6i_drc_probe(struct platform_device *pdev) 94 { 95 return component_add(&pdev->dev, &sun6i_drc_ops); 96 } 97 98 static int sun6i_drc_remove(struct platform_device *pdev) 99 { 100 component_del(&pdev->dev, &sun6i_drc_ops); 101 102 return 0; 103 } 104 105 static 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 }; 113 MODULE_DEVICE_TABLE(of, sun6i_drc_of_table); 114 115 static 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 }; 123 module_platform_driver(sun6i_drc_platform_driver); 124 125 MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); 126 MODULE_DESCRIPTION("Allwinner A31 Dynamic Range Control (DRC) Driver"); 127 MODULE_LICENSE("GPL"); 128