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 clk_prepare_enable(drc->mod_clk); 60 61 return 0; 62 63 err_disable_bus_clk: 64 clk_disable_unprepare(drc->bus_clk); 65 err_assert_reset: 66 reset_control_assert(drc->reset); 67 return ret; 68 } 69 70 static void sun6i_drc_unbind(struct device *dev, struct device *master, 71 void *data) 72 { 73 struct sun6i_drc *drc = dev_get_drvdata(dev); 74 75 clk_disable_unprepare(drc->mod_clk); 76 clk_disable_unprepare(drc->bus_clk); 77 reset_control_assert(drc->reset); 78 } 79 80 static const struct component_ops sun6i_drc_ops = { 81 .bind = sun6i_drc_bind, 82 .unbind = sun6i_drc_unbind, 83 }; 84 85 static int sun6i_drc_probe(struct platform_device *pdev) 86 { 87 return component_add(&pdev->dev, &sun6i_drc_ops); 88 } 89 90 static int sun6i_drc_remove(struct platform_device *pdev) 91 { 92 component_del(&pdev->dev, &sun6i_drc_ops); 93 94 return 0; 95 } 96 97 static const struct of_device_id sun6i_drc_of_table[] = { 98 { .compatible = "allwinner,sun6i-a31-drc" }, 99 { .compatible = "allwinner,sun6i-a31s-drc" }, 100 { .compatible = "allwinner,sun8i-a23-drc" }, 101 { .compatible = "allwinner,sun8i-a33-drc" }, 102 { .compatible = "allwinner,sun9i-a80-drc" }, 103 { } 104 }; 105 MODULE_DEVICE_TABLE(of, sun6i_drc_of_table); 106 107 static struct platform_driver sun6i_drc_platform_driver = { 108 .probe = sun6i_drc_probe, 109 .remove = sun6i_drc_remove, 110 .driver = { 111 .name = "sun6i-drc", 112 .of_match_table = sun6i_drc_of_table, 113 }, 114 }; 115 module_platform_driver(sun6i_drc_platform_driver); 116 117 MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); 118 MODULE_DESCRIPTION("Allwinner A31 Dynamic Range Control (DRC) Driver"); 119 MODULE_LICENSE("GPL"); 120