Commit | Line | Data |
---|---|---|
31e54086 PM |
1 | /* |
2 | * This program is free software; you can redistribute it and/or modify | |
3 | * it under the terms of the GNU General Public License version 2 as | |
4 | * published by the Free Software Foundation. | |
5 | * | |
6 | * This program is distributed in the hope that it will be useful, | |
7 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
8 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
9 | * GNU General Public License for more details. | |
10 | * | |
11 | * Copyright (C) 2012 ARM Limited | |
12 | */ | |
13 | ||
14 | #define DRVNAME "vexpress-regulator" | |
15 | #define pr_fmt(fmt) DRVNAME ": " fmt | |
16 | ||
17 | #include <linux/device.h> | |
18 | #include <linux/err.h> | |
19 | #include <linux/module.h> | |
20 | #include <linux/of_device.h> | |
21 | #include <linux/regulator/driver.h> | |
22 | #include <linux/regulator/machine.h> | |
23 | #include <linux/regulator/of_regulator.h> | |
24 | #include <linux/vexpress.h> | |
25 | ||
26 | struct vexpress_regulator { | |
27 | struct regulator_desc desc; | |
28 | struct regulator_dev *regdev; | |
29 | struct vexpress_config_func *func; | |
30 | }; | |
31 | ||
32 | static int vexpress_regulator_get_voltage(struct regulator_dev *regdev) | |
33 | { | |
34 | struct vexpress_regulator *reg = rdev_get_drvdata(regdev); | |
35 | u32 uV; | |
36 | int err = vexpress_config_read(reg->func, 0, &uV); | |
37 | ||
38 | return err ? err : uV; | |
39 | } | |
40 | ||
41 | static int vexpress_regulator_set_voltage(struct regulator_dev *regdev, | |
42 | int min_uV, int max_uV, unsigned *selector) | |
43 | { | |
44 | struct vexpress_regulator *reg = rdev_get_drvdata(regdev); | |
45 | ||
46 | return vexpress_config_write(reg->func, 0, min_uV); | |
47 | } | |
48 | ||
49 | static struct regulator_ops vexpress_regulator_ops_ro = { | |
50 | .get_voltage = vexpress_regulator_get_voltage, | |
51 | }; | |
52 | ||
53 | static struct regulator_ops vexpress_regulator_ops = { | |
54 | .get_voltage = vexpress_regulator_get_voltage, | |
55 | .set_voltage = vexpress_regulator_set_voltage, | |
56 | }; | |
57 | ||
58 | static int vexpress_regulator_probe(struct platform_device *pdev) | |
59 | { | |
60 | int err; | |
61 | struct vexpress_regulator *reg; | |
62 | struct regulator_init_data *init_data; | |
63 | struct regulator_config config = { }; | |
64 | ||
65 | reg = devm_kzalloc(&pdev->dev, sizeof(*reg), GFP_KERNEL); | |
66 | if (!reg) { | |
67 | err = -ENOMEM; | |
68 | goto error_kzalloc; | |
69 | } | |
70 | ||
71 | reg->func = vexpress_config_func_get_by_dev(&pdev->dev); | |
72 | if (!reg->func) { | |
73 | err = -ENXIO; | |
74 | goto error_get_func; | |
75 | } | |
76 | ||
77 | reg->desc.name = dev_name(&pdev->dev); | |
78 | reg->desc.type = REGULATOR_VOLTAGE; | |
79 | reg->desc.owner = THIS_MODULE; | |
80 | reg->desc.continuous_voltage_range = true; | |
81 | ||
82 | init_data = of_get_regulator_init_data(&pdev->dev, pdev->dev.of_node); | |
83 | if (!init_data) { | |
84 | err = -EINVAL; | |
85 | goto error_get_regulator_init_data; | |
86 | } | |
87 | ||
88 | init_data->constraints.apply_uV = 0; | |
89 | if (init_data->constraints.min_uV && init_data->constraints.max_uV) | |
90 | reg->desc.ops = &vexpress_regulator_ops; | |
91 | else | |
92 | reg->desc.ops = &vexpress_regulator_ops_ro; | |
93 | ||
94 | config.dev = &pdev->dev; | |
95 | config.init_data = init_data; | |
96 | config.driver_data = reg; | |
97 | config.of_node = pdev->dev.of_node; | |
98 | ||
abf92a3d | 99 | reg->regdev = devm_regulator_register(&pdev->dev, ®->desc, &config); |
31e54086 PM |
100 | if (IS_ERR(reg->regdev)) { |
101 | err = PTR_ERR(reg->regdev); | |
102 | goto error_regulator_register; | |
103 | } | |
104 | ||
105 | platform_set_drvdata(pdev, reg); | |
106 | ||
107 | return 0; | |
108 | ||
109 | error_regulator_register: | |
110 | error_get_regulator_init_data: | |
111 | vexpress_config_func_put(reg->func); | |
112 | error_get_func: | |
113 | error_kzalloc: | |
114 | return err; | |
115 | } | |
116 | ||
8dc995f5 | 117 | static int vexpress_regulator_remove(struct platform_device *pdev) |
31e54086 PM |
118 | { |
119 | struct vexpress_regulator *reg = platform_get_drvdata(pdev); | |
120 | ||
121 | vexpress_config_func_put(reg->func); | |
31e54086 PM |
122 | |
123 | return 0; | |
124 | } | |
125 | ||
126 | static struct of_device_id vexpress_regulator_of_match[] = { | |
127 | { .compatible = "arm,vexpress-volt", }, | |
9f4e45f7 | 128 | { } |
31e54086 PM |
129 | }; |
130 | ||
131 | static struct platform_driver vexpress_regulator_driver = { | |
132 | .probe = vexpress_regulator_probe, | |
5eb9f2b9 | 133 | .remove = vexpress_regulator_remove, |
31e54086 PM |
134 | .driver = { |
135 | .name = DRVNAME, | |
136 | .owner = THIS_MODULE, | |
137 | .of_match_table = vexpress_regulator_of_match, | |
138 | }, | |
139 | }; | |
140 | ||
141 | module_platform_driver(vexpress_regulator_driver); | |
142 | ||
143 | MODULE_AUTHOR("Pawel Moll <pawel.moll@arm.com>"); | |
144 | MODULE_DESCRIPTION("Versatile Express regulator"); | |
145 | MODULE_LICENSE("GPL"); | |
146 | MODULE_ALIAS("platform:vexpress-regulator"); |