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_data/syscon.h> 22 #include <linux/platform_device.h> 23 #include <linux/regmap.h> 24 #include <linux/mfd/syscon.h> 25 26 static struct platform_driver syscon_driver; 27 28 struct syscon { 29 struct regmap *regmap; 30 }; 31 32 static int syscon_match_node(struct device *dev, void *data) 33 { 34 struct device_node *dn = data; 35 36 return (dev->of_node == dn) ? 1 : 0; 37 } 38 39 struct regmap *syscon_node_to_regmap(struct device_node *np) 40 { 41 struct syscon *syscon; 42 struct device *dev; 43 44 dev = driver_find_device(&syscon_driver.driver, NULL, np, 45 syscon_match_node); 46 if (!dev) 47 return ERR_PTR(-EPROBE_DEFER); 48 49 syscon = dev_get_drvdata(dev); 50 51 return syscon->regmap; 52 } 53 EXPORT_SYMBOL_GPL(syscon_node_to_regmap); 54 55 struct regmap *syscon_regmap_lookup_by_compatible(const char *s) 56 { 57 struct device_node *syscon_np; 58 struct regmap *regmap; 59 60 syscon_np = of_find_compatible_node(NULL, NULL, s); 61 if (!syscon_np) 62 return ERR_PTR(-ENODEV); 63 64 regmap = syscon_node_to_regmap(syscon_np); 65 of_node_put(syscon_np); 66 67 return regmap; 68 } 69 EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_compatible); 70 71 static int syscon_match_pdevname(struct device *dev, void *data) 72 { 73 return !strcmp(dev_name(dev), (const char *)data); 74 } 75 76 struct regmap *syscon_regmap_lookup_by_pdevname(const char *s) 77 { 78 struct device *dev; 79 struct syscon *syscon; 80 81 dev = driver_find_device(&syscon_driver.driver, NULL, (void *)s, 82 syscon_match_pdevname); 83 if (!dev) 84 return ERR_PTR(-EPROBE_DEFER); 85 86 syscon = dev_get_drvdata(dev); 87 88 return syscon->regmap; 89 } 90 EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_pdevname); 91 92 struct regmap *syscon_regmap_lookup_by_phandle(struct device_node *np, 93 const char *property) 94 { 95 struct device_node *syscon_np; 96 struct regmap *regmap; 97 98 if (property) 99 syscon_np = of_parse_phandle(np, property, 0); 100 else 101 syscon_np = np; 102 103 if (!syscon_np) 104 return ERR_PTR(-ENODEV); 105 106 regmap = syscon_node_to_regmap(syscon_np); 107 of_node_put(syscon_np); 108 109 return regmap; 110 } 111 EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_phandle); 112 113 static const struct of_device_id of_syscon_match[] = { 114 { .compatible = "syscon", }, 115 { }, 116 }; 117 118 static struct regmap_config syscon_regmap_config = { 119 .reg_bits = 32, 120 .val_bits = 32, 121 .reg_stride = 4, 122 }; 123 124 static int syscon_probe(struct platform_device *pdev) 125 { 126 struct device *dev = &pdev->dev; 127 struct syscon_platform_data *pdata = dev_get_platdata(dev); 128 struct syscon *syscon; 129 struct resource *res; 130 void __iomem *base; 131 132 syscon = devm_kzalloc(dev, sizeof(*syscon), GFP_KERNEL); 133 if (!syscon) 134 return -ENOMEM; 135 136 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 137 if (!res) 138 return -ENOENT; 139 140 base = devm_ioremap(dev, res->start, resource_size(res)); 141 if (!base) 142 return -ENOMEM; 143 144 syscon_regmap_config.max_register = res->end - res->start - 3; 145 if (pdata) 146 syscon_regmap_config.name = pdata->label; 147 syscon->regmap = devm_regmap_init_mmio(dev, base, 148 &syscon_regmap_config); 149 if (IS_ERR(syscon->regmap)) { 150 dev_err(dev, "regmap init failed\n"); 151 return PTR_ERR(syscon->regmap); 152 } 153 154 platform_set_drvdata(pdev, syscon); 155 156 dev_dbg(dev, "regmap %pR registered\n", res); 157 158 return 0; 159 } 160 161 static const struct platform_device_id syscon_ids[] = { 162 { "syscon", }, 163 { } 164 }; 165 166 static struct platform_driver syscon_driver = { 167 .driver = { 168 .name = "syscon", 169 .owner = THIS_MODULE, 170 .of_match_table = of_syscon_match, 171 }, 172 .probe = syscon_probe, 173 .id_table = syscon_ids, 174 }; 175 176 static int __init syscon_init(void) 177 { 178 return platform_driver_register(&syscon_driver); 179 } 180 postcore_initcall(syscon_init); 181 182 static void __exit syscon_exit(void) 183 { 184 platform_driver_unregister(&syscon_driver); 185 } 186 module_exit(syscon_exit); 187 188 MODULE_AUTHOR("Dong Aisheng <dong.aisheng@linaro.org>"); 189 MODULE_DESCRIPTION("System Control driver"); 190 MODULE_LICENSE("GPL v2"); 191