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