1 /* 2 * System Control Driver 3 * 4 * Copyright (C) 2012 Freescale Semiconductor, Inc. 5 * Copyright (C) 2012 Linaro Ltd. 6 * 7 * Author: Dong Aisheng <dong.aisheng@linaro.org> 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 2 of the License, or 12 * (at your option) any later version. 13 */ 14 15 #include <linux/err.h> 16 #include <linux/io.h> 17 #include <linux/module.h> 18 #include <linux/of.h> 19 #include <linux/of_address.h> 20 #include <linux/of_platform.h> 21 #include <linux/platform_device.h> 22 #include <linux/regmap.h> 23 #include <linux/mfd/syscon.h> 24 25 static struct platform_driver syscon_driver; 26 27 struct syscon { 28 struct device *dev; 29 void __iomem *base; 30 struct regmap *regmap; 31 }; 32 33 static int syscon_match(struct device *dev, void *data) 34 { 35 struct syscon *syscon = dev_get_drvdata(dev); 36 struct device_node *dn = data; 37 38 return (syscon->dev->of_node == dn) ? 1 : 0; 39 } 40 41 struct regmap *syscon_node_to_regmap(struct device_node *np) 42 { 43 struct syscon *syscon; 44 struct device *dev; 45 46 dev = driver_find_device(&syscon_driver.driver, NULL, np, 47 syscon_match); 48 if (!dev) 49 return ERR_PTR(-EPROBE_DEFER); 50 51 syscon = dev_get_drvdata(dev); 52 53 return syscon->regmap; 54 } 55 EXPORT_SYMBOL_GPL(syscon_node_to_regmap); 56 57 struct regmap *syscon_regmap_lookup_by_compatible(const char *s) 58 { 59 struct device_node *syscon_np; 60 struct regmap *regmap; 61 62 syscon_np = of_find_compatible_node(NULL, NULL, s); 63 if (!syscon_np) 64 return ERR_PTR(-ENODEV); 65 66 regmap = syscon_node_to_regmap(syscon_np); 67 of_node_put(syscon_np); 68 69 return regmap; 70 } 71 EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_compatible); 72 73 struct regmap *syscon_regmap_lookup_by_phandle(struct device_node *np, 74 const char *property) 75 { 76 struct device_node *syscon_np; 77 struct regmap *regmap; 78 79 syscon_np = of_parse_phandle(np, property, 0); 80 if (!syscon_np) 81 return ERR_PTR(-ENODEV); 82 83 regmap = syscon_node_to_regmap(syscon_np); 84 of_node_put(syscon_np); 85 86 return regmap; 87 } 88 EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_phandle); 89 90 static const struct of_device_id of_syscon_match[] = { 91 { .compatible = "syscon", }, 92 { }, 93 }; 94 95 static struct regmap_config syscon_regmap_config = { 96 .reg_bits = 32, 97 .val_bits = 32, 98 .reg_stride = 4, 99 }; 100 101 static int syscon_probe(struct platform_device *pdev) 102 { 103 struct device *dev = &pdev->dev; 104 struct device_node *np = dev->of_node; 105 struct syscon *syscon; 106 struct resource res; 107 int ret; 108 109 if (!np) 110 return -ENOENT; 111 112 syscon = devm_kzalloc(dev, sizeof(struct syscon), 113 GFP_KERNEL); 114 if (!syscon) 115 return -ENOMEM; 116 117 syscon->base = of_iomap(np, 0); 118 if (!syscon->base) 119 return -EADDRNOTAVAIL; 120 121 ret = of_address_to_resource(np, 0, &res); 122 if (ret) 123 return ret; 124 125 syscon_regmap_config.max_register = res.end - res.start - 3; 126 syscon->regmap = devm_regmap_init_mmio(dev, syscon->base, 127 &syscon_regmap_config); 128 if (IS_ERR(syscon->regmap)) { 129 dev_err(dev, "regmap init failed\n"); 130 return PTR_ERR(syscon->regmap); 131 } 132 133 syscon->dev = dev; 134 platform_set_drvdata(pdev, syscon); 135 136 dev_info(dev, "syscon regmap start 0x%x end 0x%x registered\n", 137 res.start, res.end); 138 139 return 0; 140 } 141 142 static int syscon_remove(struct platform_device *pdev) 143 { 144 struct syscon *syscon; 145 146 syscon = platform_get_drvdata(pdev); 147 iounmap(syscon->base); 148 platform_set_drvdata(pdev, NULL); 149 150 return 0; 151 } 152 153 static struct platform_driver syscon_driver = { 154 .driver = { 155 .name = "syscon", 156 .owner = THIS_MODULE, 157 .of_match_table = of_syscon_match, 158 }, 159 .probe = syscon_probe, 160 .remove = syscon_remove, 161 }; 162 163 static int __init syscon_init(void) 164 { 165 return platform_driver_register(&syscon_driver); 166 } 167 postcore_initcall(syscon_init); 168 169 static void __exit syscon_exit(void) 170 { 171 platform_driver_unregister(&syscon_driver); 172 } 173 module_exit(syscon_exit); 174 175 MODULE_AUTHOR("Dong Aisheng <dong.aisheng@linaro.org>"); 176 MODULE_DESCRIPTION("System Control driver"); 177 MODULE_LICENSE("GPL v2"); 178