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 void __iomem *base; 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 struct platform_device *pdev = to_platform_device(dev); 74 const struct platform_device_id *id = platform_get_device_id(pdev); 75 76 if (id) 77 if (!strcmp(id->name, (const char *)data)) 78 return 1; 79 80 return !strcmp(dev_name(dev), (const char *)data); 81 } 82 83 struct regmap *syscon_regmap_lookup_by_pdevname(const char *s) 84 { 85 struct device *dev; 86 struct syscon *syscon; 87 88 dev = driver_find_device(&syscon_driver.driver, NULL, (void *)s, 89 syscon_match_pdevname); 90 if (!dev) 91 return ERR_PTR(-EPROBE_DEFER); 92 93 syscon = dev_get_drvdata(dev); 94 95 return syscon->regmap; 96 } 97 EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_pdevname); 98 99 struct regmap *syscon_regmap_lookup_by_phandle(struct device_node *np, 100 const char *property) 101 { 102 struct device_node *syscon_np; 103 struct regmap *regmap; 104 105 syscon_np = of_parse_phandle(np, property, 0); 106 if (!syscon_np) 107 return ERR_PTR(-ENODEV); 108 109 regmap = syscon_node_to_regmap(syscon_np); 110 of_node_put(syscon_np); 111 112 return regmap; 113 } 114 EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_phandle); 115 116 static const struct of_device_id of_syscon_match[] = { 117 { .compatible = "syscon", }, 118 { }, 119 }; 120 121 static struct regmap_config syscon_regmap_config = { 122 .reg_bits = 32, 123 .val_bits = 32, 124 .reg_stride = 4, 125 }; 126 127 static int syscon_probe(struct platform_device *pdev) 128 { 129 struct device *dev = &pdev->dev; 130 struct syscon *syscon; 131 struct resource *res; 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 syscon->base = devm_ioremap(dev, res->start, resource_size(res)); 142 if (!syscon->base) 143 return -ENOMEM; 144 145 syscon_regmap_config.max_register = res->end - res->start - 3; 146 syscon->regmap = devm_regmap_init_mmio(dev, syscon->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 #ifdef CONFIG_ARCH_CLPS711X 163 { "clps711x-syscon", }, 164 #endif 165 { } 166 }; 167 168 static struct platform_driver syscon_driver = { 169 .driver = { 170 .name = "syscon", 171 .owner = THIS_MODULE, 172 .of_match_table = of_syscon_match, 173 }, 174 .probe = syscon_probe, 175 .id_table = syscon_ids, 176 }; 177 178 static int __init syscon_init(void) 179 { 180 return platform_driver_register(&syscon_driver); 181 } 182 postcore_initcall(syscon_init); 183 184 static void __exit syscon_exit(void) 185 { 186 platform_driver_unregister(&syscon_driver); 187 } 188 module_exit(syscon_exit); 189 190 MODULE_AUTHOR("Dong Aisheng <dong.aisheng@linaro.org>"); 191 MODULE_DESCRIPTION("System Control driver"); 192 MODULE_LICENSE("GPL v2"); 193