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