12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 287d68730SDong Aisheng /* 387d68730SDong Aisheng * System Control Driver 487d68730SDong Aisheng * 587d68730SDong Aisheng * Copyright (C) 2012 Freescale Semiconductor, Inc. 687d68730SDong Aisheng * Copyright (C) 2012 Linaro Ltd. 787d68730SDong Aisheng * 887d68730SDong Aisheng * Author: Dong Aisheng <[email protected]> 987d68730SDong Aisheng */ 1087d68730SDong Aisheng 11*82f898f4SKrzysztof Kozlowski #include <linux/cleanup.h> 12a00406b7SFabrice Gasnier #include <linux/clk.h> 1387d68730SDong Aisheng #include <linux/err.h> 143bafc09eSBaolin Wang #include <linux/hwspinlock.h> 1587d68730SDong Aisheng #include <linux/io.h> 161345da73SPaul Gortmaker #include <linux/init.h> 17bdb0066dSPankaj Dubey #include <linux/list.h> 1887d68730SDong Aisheng #include <linux/of.h> 1987d68730SDong Aisheng #include <linux/of_address.h> 2087d68730SDong Aisheng #include <linux/of_platform.h> 2129f9b6cfSPawel Moll #include <linux/platform_data/syscon.h> 2287d68730SDong Aisheng #include <linux/platform_device.h> 2387d68730SDong Aisheng #include <linux/regmap.h> 247d1e3bd9SJeremy Kerr #include <linux/reset.h> 2575177deeSFabio Estevam #include <linux/mfd/syscon.h> 26bdb0066dSPankaj Dubey #include <linux/slab.h> 2787d68730SDong Aisheng 2887d68730SDong Aisheng static struct platform_driver syscon_driver; 2987d68730SDong Aisheng 30bdb0066dSPankaj Dubey static DEFINE_SPINLOCK(syscon_list_slock); 31bdb0066dSPankaj Dubey static LIST_HEAD(syscon_list); 32bdb0066dSPankaj Dubey 3387d68730SDong Aisheng struct syscon { 34bdb0066dSPankaj Dubey struct device_node *np; 3587d68730SDong Aisheng struct regmap *regmap; 367d1e3bd9SJeremy Kerr struct reset_control *reset; 37bdb0066dSPankaj Dubey struct list_head list; 3887d68730SDong Aisheng }; 3987d68730SDong Aisheng 40c131045dSPhilipp Zabel static const struct regmap_config syscon_regmap_config = { 41bdb0066dSPankaj Dubey .reg_bits = 32, 42bdb0066dSPankaj Dubey .val_bits = 32, 43bdb0066dSPankaj Dubey .reg_stride = 4, 44bdb0066dSPankaj Dubey }; 4587d68730SDong Aisheng 467d1e3bd9SJeremy Kerr static struct syscon *of_syscon_register(struct device_node *np, bool check_res) 47bdb0066dSPankaj Dubey { 48a00406b7SFabrice Gasnier struct clk *clk; 49bdb0066dSPankaj Dubey struct regmap *regmap; 50bdb0066dSPankaj Dubey void __iomem *base; 51db2fb60cSDamien Riegel u32 reg_io_width; 52bdb0066dSPankaj Dubey int ret; 53bdb0066dSPankaj Dubey struct regmap_config syscon_config = syscon_regmap_config; 54ca668f0eSPhilipp Zabel struct resource res; 557d1e3bd9SJeremy Kerr struct reset_control *reset; 56bdb0066dSPankaj Dubey 57*82f898f4SKrzysztof Kozlowski struct syscon *syscon __free(kfree) = kzalloc(sizeof(*syscon), GFP_KERNEL); 58bdb0066dSPankaj Dubey if (!syscon) 59bdb0066dSPankaj Dubey return ERR_PTR(-ENOMEM); 60bdb0066dSPankaj Dubey 61*82f898f4SKrzysztof Kozlowski if (of_address_to_resource(np, 0, &res)) 62*82f898f4SKrzysztof Kozlowski return ERR_PTR(-ENOMEM); 63ca668f0eSPhilipp Zabel 64452d0741SHector Martin base = of_iomap(np, 0); 65*82f898f4SKrzysztof Kozlowski if (!base) 66*82f898f4SKrzysztof Kozlowski return ERR_PTR(-ENOMEM); 67bdb0066dSPankaj Dubey 68ca4582c2SJason A. Donenfeld /* Parse the device's DT node for an endianness specification */ 69ca4582c2SJason A. Donenfeld if (of_property_read_bool(np, "big-endian")) 70ca4582c2SJason A. Donenfeld syscon_config.val_format_endian = REGMAP_ENDIAN_BIG; 71ca4582c2SJason A. Donenfeld else if (of_property_read_bool(np, "little-endian")) 72ca4582c2SJason A. Donenfeld syscon_config.val_format_endian = REGMAP_ENDIAN_LITTLE; 73ca4582c2SJason A. Donenfeld else if (of_property_read_bool(np, "native-endian")) 74ca4582c2SJason A. Donenfeld syscon_config.val_format_endian = REGMAP_ENDIAN_NATIVE; 75ca4582c2SJason A. Donenfeld 76db2fb60cSDamien Riegel /* 77db2fb60cSDamien Riegel * search for reg-io-width property in DT. If it is not provided, 78db2fb60cSDamien Riegel * default to 4 bytes. regmap_init_mmio will return an error if values 79db2fb60cSDamien Riegel * are invalid so there is no need to check them here. 80db2fb60cSDamien Riegel */ 81db2fb60cSDamien Riegel ret = of_property_read_u32(np, "reg-io-width", ®_io_width); 82db2fb60cSDamien Riegel if (ret) 83db2fb60cSDamien Riegel reg_io_width = 4; 84db2fb60cSDamien Riegel 853bafc09eSBaolin Wang ret = of_hwspin_lock_get_id(np, 0); 863bafc09eSBaolin Wang if (ret > 0 || (IS_ENABLED(CONFIG_HWSPINLOCK) && ret == 0)) { 873bafc09eSBaolin Wang syscon_config.use_hwlock = true; 883bafc09eSBaolin Wang syscon_config.hwlock_id = ret; 893bafc09eSBaolin Wang syscon_config.hwlock_mode = HWLOCK_IRQSTATE; 903bafc09eSBaolin Wang } else if (ret < 0) { 913bafc09eSBaolin Wang switch (ret) { 923bafc09eSBaolin Wang case -ENOENT: 933bafc09eSBaolin Wang /* Ignore missing hwlock, it's optional. */ 943bafc09eSBaolin Wang break; 953bafc09eSBaolin Wang default: 963bafc09eSBaolin Wang pr_err("Failed to retrieve valid hwlock: %d\n", ret); 97df561f66SGustavo A. R. Silva fallthrough; 983bafc09eSBaolin Wang case -EPROBE_DEFER: 993bafc09eSBaolin Wang goto err_regmap; 1003bafc09eSBaolin Wang } 1013bafc09eSBaolin Wang } 1023bafc09eSBaolin Wang 1037ff7d5ffSAndy Shevchenko syscon_config.name = kasprintf(GFP_KERNEL, "%pOFn@%pa", np, &res.start); 10441673c66SKunwu Chan if (!syscon_config.name) { 10541673c66SKunwu Chan ret = -ENOMEM; 10641673c66SKunwu Chan goto err_regmap; 10741673c66SKunwu Chan } 108db2fb60cSDamien Riegel syscon_config.reg_stride = reg_io_width; 109db2fb60cSDamien Riegel syscon_config.val_bits = reg_io_width * 8; 110ca668f0eSPhilipp Zabel syscon_config.max_register = resource_size(&res) - reg_io_width; 111db2fb60cSDamien Riegel 112bdb0066dSPankaj Dubey regmap = regmap_init_mmio(NULL, base, &syscon_config); 11356a11881SLimeng kfree(syscon_config.name); 114bdb0066dSPankaj Dubey if (IS_ERR(regmap)) { 115bdb0066dSPankaj Dubey pr_err("regmap init failed\n"); 116bdb0066dSPankaj Dubey ret = PTR_ERR(regmap); 117bdb0066dSPankaj Dubey goto err_regmap; 118bdb0066dSPankaj Dubey } 119bdb0066dSPankaj Dubey 1207d1e3bd9SJeremy Kerr if (check_res) { 121a00406b7SFabrice Gasnier clk = of_clk_get(np, 0); 122a00406b7SFabrice Gasnier if (IS_ERR(clk)) { 123a00406b7SFabrice Gasnier ret = PTR_ERR(clk); 124a00406b7SFabrice Gasnier /* clock is optional */ 125a00406b7SFabrice Gasnier if (ret != -ENOENT) 126a00406b7SFabrice Gasnier goto err_clk; 127a00406b7SFabrice Gasnier } else { 128a00406b7SFabrice Gasnier ret = regmap_mmio_attach_clk(regmap, clk); 129a00406b7SFabrice Gasnier if (ret) 1307d1e3bd9SJeremy Kerr goto err_attach_clk; 131a00406b7SFabrice Gasnier } 1327d1e3bd9SJeremy Kerr 1337d1e3bd9SJeremy Kerr reset = of_reset_control_get_optional_exclusive(np, NULL); 1347d1e3bd9SJeremy Kerr if (IS_ERR(reset)) { 1357d1e3bd9SJeremy Kerr ret = PTR_ERR(reset); 1367d1e3bd9SJeremy Kerr goto err_attach_clk; 1377d1e3bd9SJeremy Kerr } 1387d1e3bd9SJeremy Kerr 1397d1e3bd9SJeremy Kerr ret = reset_control_deassert(reset); 1407d1e3bd9SJeremy Kerr if (ret) 1417d1e3bd9SJeremy Kerr goto err_reset; 14239233b7cSPaul Cercueil } 143a00406b7SFabrice Gasnier 144bdb0066dSPankaj Dubey syscon->regmap = regmap; 145bdb0066dSPankaj Dubey syscon->np = np; 146bdb0066dSPankaj Dubey 147bdb0066dSPankaj Dubey spin_lock(&syscon_list_slock); 148bdb0066dSPankaj Dubey list_add_tail(&syscon->list, &syscon_list); 149bdb0066dSPankaj Dubey spin_unlock(&syscon_list_slock); 150bdb0066dSPankaj Dubey 151*82f898f4SKrzysztof Kozlowski return_ptr(syscon); 152bdb0066dSPankaj Dubey 1537d1e3bd9SJeremy Kerr err_reset: 1547d1e3bd9SJeremy Kerr reset_control_put(reset); 1557d1e3bd9SJeremy Kerr err_attach_clk: 156a00406b7SFabrice Gasnier if (!IS_ERR(clk)) 157a00406b7SFabrice Gasnier clk_put(clk); 158a00406b7SFabrice Gasnier err_clk: 159a00406b7SFabrice Gasnier regmap_exit(regmap); 160bdb0066dSPankaj Dubey err_regmap: 161bdb0066dSPankaj Dubey iounmap(base); 162bdb0066dSPankaj Dubey return ERR_PTR(ret); 16387d68730SDong Aisheng } 16487d68730SDong Aisheng 16539233b7cSPaul Cercueil static struct regmap *device_node_get_regmap(struct device_node *np, 1667d1e3bd9SJeremy Kerr bool check_res) 16787d68730SDong Aisheng { 168bdb0066dSPankaj Dubey struct syscon *entry, *syscon = NULL; 16987d68730SDong Aisheng 170bdb0066dSPankaj Dubey spin_lock(&syscon_list_slock); 17187d68730SDong Aisheng 172bdb0066dSPankaj Dubey list_for_each_entry(entry, &syscon_list, list) 173bdb0066dSPankaj Dubey if (entry->np == np) { 174bdb0066dSPankaj Dubey syscon = entry; 175bdb0066dSPankaj Dubey break; 176bdb0066dSPankaj Dubey } 177bdb0066dSPankaj Dubey 178bdb0066dSPankaj Dubey spin_unlock(&syscon_list_slock); 179bdb0066dSPankaj Dubey 180bdb0066dSPankaj Dubey if (!syscon) 1817d1e3bd9SJeremy Kerr syscon = of_syscon_register(np, check_res); 182bdb0066dSPankaj Dubey 183bdb0066dSPankaj Dubey if (IS_ERR(syscon)) 184bdb0066dSPankaj Dubey return ERR_CAST(syscon); 18587d68730SDong Aisheng 18687d68730SDong Aisheng return syscon->regmap; 18787d68730SDong Aisheng } 18839233b7cSPaul Cercueil 189769cb631SPeter Griffin /** 190769cb631SPeter Griffin * of_syscon_register_regmap() - Register regmap for specified device node 191769cb631SPeter Griffin * @np: Device tree node 192769cb631SPeter Griffin * @regmap: Pointer to regmap object 193769cb631SPeter Griffin * 194769cb631SPeter Griffin * Register an externally created regmap object with syscon for the specified 195769cb631SPeter Griffin * device tree node. This regmap will then be returned to client drivers using 196769cb631SPeter Griffin * the syscon_regmap_lookup_by_phandle() API. 197769cb631SPeter Griffin * 198769cb631SPeter Griffin * Return: 0 on success, negative error code on failure. 199769cb631SPeter Griffin */ 200769cb631SPeter Griffin int of_syscon_register_regmap(struct device_node *np, struct regmap *regmap) 201769cb631SPeter Griffin { 202769cb631SPeter Griffin struct syscon *entry, *syscon = NULL; 203769cb631SPeter Griffin int ret; 204769cb631SPeter Griffin 205769cb631SPeter Griffin if (!np || !regmap) 206769cb631SPeter Griffin return -EINVAL; 207769cb631SPeter Griffin 208769cb631SPeter Griffin syscon = kzalloc(sizeof(*syscon), GFP_KERNEL); 209769cb631SPeter Griffin if (!syscon) 210769cb631SPeter Griffin return -ENOMEM; 211769cb631SPeter Griffin 212769cb631SPeter Griffin /* check if syscon entry already exists */ 213769cb631SPeter Griffin spin_lock(&syscon_list_slock); 214769cb631SPeter Griffin 215769cb631SPeter Griffin list_for_each_entry(entry, &syscon_list, list) 216769cb631SPeter Griffin if (entry->np == np) { 217769cb631SPeter Griffin ret = -EEXIST; 218769cb631SPeter Griffin goto err_unlock; 219769cb631SPeter Griffin } 220769cb631SPeter Griffin 221769cb631SPeter Griffin syscon->regmap = regmap; 222769cb631SPeter Griffin syscon->np = np; 223769cb631SPeter Griffin 224769cb631SPeter Griffin /* register the regmap in syscon list */ 225769cb631SPeter Griffin list_add_tail(&syscon->list, &syscon_list); 226769cb631SPeter Griffin spin_unlock(&syscon_list_slock); 227769cb631SPeter Griffin 228769cb631SPeter Griffin return 0; 229769cb631SPeter Griffin 230769cb631SPeter Griffin err_unlock: 231769cb631SPeter Griffin spin_unlock(&syscon_list_slock); 232769cb631SPeter Griffin kfree(syscon); 233769cb631SPeter Griffin return ret; 234769cb631SPeter Griffin } 235769cb631SPeter Griffin EXPORT_SYMBOL_GPL(of_syscon_register_regmap); 236769cb631SPeter Griffin 23739233b7cSPaul Cercueil struct regmap *device_node_to_regmap(struct device_node *np) 23839233b7cSPaul Cercueil { 23939233b7cSPaul Cercueil return device_node_get_regmap(np, false); 24039233b7cSPaul Cercueil } 24139233b7cSPaul Cercueil EXPORT_SYMBOL_GPL(device_node_to_regmap); 24239233b7cSPaul Cercueil 24339233b7cSPaul Cercueil struct regmap *syscon_node_to_regmap(struct device_node *np) 24439233b7cSPaul Cercueil { 24539233b7cSPaul Cercueil if (!of_device_is_compatible(np, "syscon")) 24639233b7cSPaul Cercueil return ERR_PTR(-EINVAL); 24739233b7cSPaul Cercueil 24839233b7cSPaul Cercueil return device_node_get_regmap(np, true); 24939233b7cSPaul Cercueil } 25087d68730SDong Aisheng EXPORT_SYMBOL_GPL(syscon_node_to_regmap); 25187d68730SDong Aisheng 25287d68730SDong Aisheng struct regmap *syscon_regmap_lookup_by_compatible(const char *s) 25387d68730SDong Aisheng { 25487d68730SDong Aisheng struct device_node *syscon_np; 25587d68730SDong Aisheng struct regmap *regmap; 25687d68730SDong Aisheng 25787d68730SDong Aisheng syscon_np = of_find_compatible_node(NULL, NULL, s); 25887d68730SDong Aisheng if (!syscon_np) 25987d68730SDong Aisheng return ERR_PTR(-ENODEV); 26087d68730SDong Aisheng 26187d68730SDong Aisheng regmap = syscon_node_to_regmap(syscon_np); 26287d68730SDong Aisheng of_node_put(syscon_np); 26387d68730SDong Aisheng 26487d68730SDong Aisheng return regmap; 26587d68730SDong Aisheng } 26687d68730SDong Aisheng EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_compatible); 26787d68730SDong Aisheng 26887d68730SDong Aisheng struct regmap *syscon_regmap_lookup_by_phandle(struct device_node *np, 26987d68730SDong Aisheng const char *property) 27087d68730SDong Aisheng { 27187d68730SDong Aisheng struct device_node *syscon_np; 27287d68730SDong Aisheng struct regmap *regmap; 27387d68730SDong Aisheng 27445330bb4SPankaj Dubey if (property) 27587d68730SDong Aisheng syscon_np = of_parse_phandle(np, property, 0); 27645330bb4SPankaj Dubey else 27745330bb4SPankaj Dubey syscon_np = np; 27845330bb4SPankaj Dubey 27987d68730SDong Aisheng if (!syscon_np) 28087d68730SDong Aisheng return ERR_PTR(-ENODEV); 28187d68730SDong Aisheng 28287d68730SDong Aisheng regmap = syscon_node_to_regmap(syscon_np); 283d2b0680cSPeter Griffin 284d2b0680cSPeter Griffin if (property) 28587d68730SDong Aisheng of_node_put(syscon_np); 28687d68730SDong Aisheng 28787d68730SDong Aisheng return regmap; 28887d68730SDong Aisheng } 28987d68730SDong Aisheng EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_phandle); 29087d68730SDong Aisheng 2916a24f567SOrson Zhai struct regmap *syscon_regmap_lookup_by_phandle_args(struct device_node *np, 2926a24f567SOrson Zhai const char *property, 2936a24f567SOrson Zhai int arg_count, 2946a24f567SOrson Zhai unsigned int *out_args) 2956a24f567SOrson Zhai { 2966a24f567SOrson Zhai struct device_node *syscon_np; 2976a24f567SOrson Zhai struct of_phandle_args args; 2986a24f567SOrson Zhai struct regmap *regmap; 2996a24f567SOrson Zhai unsigned int index; 3006a24f567SOrson Zhai int rc; 3016a24f567SOrson Zhai 3026a24f567SOrson Zhai rc = of_parse_phandle_with_fixed_args(np, property, arg_count, 3036a24f567SOrson Zhai 0, &args); 3046a24f567SOrson Zhai if (rc) 3056a24f567SOrson Zhai return ERR_PTR(rc); 3066a24f567SOrson Zhai 3076a24f567SOrson Zhai syscon_np = args.np; 3086a24f567SOrson Zhai if (!syscon_np) 3096a24f567SOrson Zhai return ERR_PTR(-ENODEV); 3106a24f567SOrson Zhai 3116a24f567SOrson Zhai regmap = syscon_node_to_regmap(syscon_np); 3126a24f567SOrson Zhai for (index = 0; index < arg_count; index++) 3136a24f567SOrson Zhai out_args[index] = args.args[index]; 3146a24f567SOrson Zhai of_node_put(syscon_np); 3156a24f567SOrson Zhai 3166a24f567SOrson Zhai return regmap; 3176a24f567SOrson Zhai } 3186a24f567SOrson Zhai EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_phandle_args); 3196a24f567SOrson Zhai 32086b9d170SEnric Balletbo i Serra /* 32186b9d170SEnric Balletbo i Serra * It behaves the same as syscon_regmap_lookup_by_phandle() except where 32286b9d170SEnric Balletbo i Serra * there is no regmap phandle. In this case, instead of returning -ENODEV, 32386b9d170SEnric Balletbo i Serra * the function returns NULL. 32486b9d170SEnric Balletbo i Serra */ 32586b9d170SEnric Balletbo i Serra struct regmap *syscon_regmap_lookup_by_phandle_optional(struct device_node *np, 32686b9d170SEnric Balletbo i Serra const char *property) 32786b9d170SEnric Balletbo i Serra { 32886b9d170SEnric Balletbo i Serra struct regmap *regmap; 32986b9d170SEnric Balletbo i Serra 33086b9d170SEnric Balletbo i Serra regmap = syscon_regmap_lookup_by_phandle(np, property); 33186b9d170SEnric Balletbo i Serra if (IS_ERR(regmap) && PTR_ERR(regmap) == -ENODEV) 33286b9d170SEnric Balletbo i Serra return NULL; 33386b9d170SEnric Balletbo i Serra 33486b9d170SEnric Balletbo i Serra return regmap; 33586b9d170SEnric Balletbo i Serra } 33686b9d170SEnric Balletbo i Serra EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_phandle_optional); 33786b9d170SEnric Balletbo i Serra 338f791be49SBill Pemberton static int syscon_probe(struct platform_device *pdev) 33987d68730SDong Aisheng { 34087d68730SDong Aisheng struct device *dev = &pdev->dev; 34129f9b6cfSPawel Moll struct syscon_platform_data *pdata = dev_get_platdata(dev); 34287d68730SDong Aisheng struct syscon *syscon; 343c131045dSPhilipp Zabel struct regmap_config syscon_config = syscon_regmap_config; 3445ab3a89aSAlexander Shiyan struct resource *res; 345f10111ccSAlexander Shiyan void __iomem *base; 34687d68730SDong Aisheng 3475ab3a89aSAlexander Shiyan syscon = devm_kzalloc(dev, sizeof(*syscon), GFP_KERNEL); 34887d68730SDong Aisheng if (!syscon) 34987d68730SDong Aisheng return -ENOMEM; 35087d68730SDong Aisheng 3515ab3a89aSAlexander Shiyan res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 3525ab3a89aSAlexander Shiyan if (!res) 3535ab3a89aSAlexander Shiyan return -ENOENT; 3545ab3a89aSAlexander Shiyan 355f10111ccSAlexander Shiyan base = devm_ioremap(dev, res->start, resource_size(res)); 356f10111ccSAlexander Shiyan if (!base) 3575ab3a89aSAlexander Shiyan return -ENOMEM; 35887d68730SDong Aisheng 359f2a19c5bSAndy Shevchenko syscon_config.max_register = resource_size(res) - 4; 36029f9b6cfSPawel Moll if (pdata) 361c131045dSPhilipp Zabel syscon_config.name = pdata->label; 362c131045dSPhilipp Zabel syscon->regmap = devm_regmap_init_mmio(dev, base, &syscon_config); 36387d68730SDong Aisheng if (IS_ERR(syscon->regmap)) { 36487d68730SDong Aisheng dev_err(dev, "regmap init failed\n"); 36587d68730SDong Aisheng return PTR_ERR(syscon->regmap); 36687d68730SDong Aisheng } 36787d68730SDong Aisheng 36887d68730SDong Aisheng platform_set_drvdata(pdev, syscon); 36987d68730SDong Aisheng 37038d8974eSAlexander Shiyan dev_dbg(dev, "regmap %pR registered\n", res); 37187d68730SDong Aisheng 37287d68730SDong Aisheng return 0; 37387d68730SDong Aisheng } 37487d68730SDong Aisheng 3755ab3a89aSAlexander Shiyan static const struct platform_device_id syscon_ids[] = { 3765ab3a89aSAlexander Shiyan { "syscon", }, 3775ab3a89aSAlexander Shiyan { } 3785ab3a89aSAlexander Shiyan }; 37987d68730SDong Aisheng 38087d68730SDong Aisheng static struct platform_driver syscon_driver = { 38187d68730SDong Aisheng .driver = { 38287d68730SDong Aisheng .name = "syscon", 38387d68730SDong Aisheng }, 38487d68730SDong Aisheng .probe = syscon_probe, 3855ab3a89aSAlexander Shiyan .id_table = syscon_ids, 38687d68730SDong Aisheng }; 38787d68730SDong Aisheng 38887d68730SDong Aisheng static int __init syscon_init(void) 38987d68730SDong Aisheng { 39087d68730SDong Aisheng return platform_driver_register(&syscon_driver); 39187d68730SDong Aisheng } 39287d68730SDong Aisheng postcore_initcall(syscon_init); 393