• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  
syscon_match_node(struct device * dev,void * data)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  
syscon_node_to_regmap(struct device_node * np)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  
syscon_regmap_lookup_by_compatible(const char * s)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  
syscon_match_pdevname(struct device * dev,void * data)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  
syscon_regmap_lookup_by_pdevname(const char * s)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  
syscon_regmap_lookup_by_phandle(struct device_node * np,const char * property)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  
syscon_probe(struct platform_device * pdev)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  	{ }
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  
syscon_init(void)175  static int __init syscon_init(void)
176  {
177  	return platform_driver_register(&syscon_driver);
178  }
179  postcore_initcall(syscon_init);
180  
syscon_exit(void)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