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