blob: a00fb2f0a3a84921ea0b9a996458d1ec4c59aa0f [file] [log] [blame]
Rohit Vaswanib18208a2011-07-12 13:04:28 -07001/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/init.h>
14#include <linux/module.h>
15#include <linux/fs.h>
16#include <linux/slab.h>
17#include <linux/miscdevice.h>
18#include <linux/delay.h>
19#include <linux/io.h>
20#include <linux/uaccess.h>
21#include <linux/regulator/pm8058-xo.h>
22#include <linux/platform_device.h>
23
24#define FSM_XO_IOC_MAGIC 0x93
25#define FSM_XO_IOC_CLKBUF _IO(FSM_XO_IOC_MAGIC, 1)
26
27#define FSM_XO_DEVICE_READY 0x01
28#define FSM_XO_DEVICE_OFF 0x00
29
30#define pr_fmt(fmt) "%s: " fmt, __func__
31
32/* enum for TCXO clock output buffer definition */
33enum clk_buffer_type {
34 XO_BUFFER_A0 = 0,
35 XO_BUFFER_A1 = 1,
36 XO_BUFFER_LAST
37};
38
39/*
40 * This user request structure is used to exchange the pmic device data
41 * requested to user space applications. The pointer to this structure is
42 * passed to the the ioctl function.
43*/
44struct fsm_xo_req {
45 enum clk_buffer_type clkBuffer;
46 u8 clkBufEnable;
47};
48
49struct fsm_xo_priv_t {
50 struct mutex lock;
51 struct regulator *a0;
52 struct regulator *a1;
53 u8 a0_enabled;
54 u8 a1_enabled;
55};
56
57static struct fsm_xo_priv_t *fsm_xo_priv;
58
59static int fsm_xo_open(struct inode *inode, struct file *filp)
60{
61 if (fsm_xo_priv == NULL)
62 return -ENODEV;
63
64 filp->private_data = fsm_xo_priv;
65
66 return 0;
67}
68
69static int fsm_xo_release(struct inode *inode, struct file *filp)
70{
71 filp->private_data = NULL;
72
73 return 0;
74}
75
76static inline int fsm_xo_enable_a0(void)
77{
78 int err = 0;
79
80 if (!fsm_xo_priv->a0_enabled) {
81 err = regulator_enable(fsm_xo_priv->a0);
82 if (err != 0)
83 pr_err("Error = %d enabling xo buffer a0\n", err);
84 else
85 fsm_xo_priv->a0_enabled = 1;
86 }
87 return err;
88}
89
90static inline int fsm_xo_disable_a0(void)
91{
92 int err = 0;
93
94 if (fsm_xo_priv->a0_enabled) {
95 err = regulator_disable(fsm_xo_priv->a0);
96 if (err != 0)
97 pr_err("Error = %d disabling xo buffer a0\n", err);
98 else
99 fsm_xo_priv->a0_enabled = 0;
100 }
101 return err;
102}
103
104static inline int fsm_xo_enable_a1(void)
105{
106 int err = 0;
107
108 if (!fsm_xo_priv->a1_enabled) {
109 err = regulator_enable(fsm_xo_priv->a1);
110 if (err != 0)
111 pr_err("Error = %d enabling xo buffer a1\n", err);
112 else
113 fsm_xo_priv->a1_enabled = 1;
114 }
115 return err;
116}
117
118static inline int fsm_xo_disable_a1(void)
119{
120 int err = 0;
121
122 if (fsm_xo_priv->a1_enabled) {
123 err = regulator_disable(fsm_xo_priv->a1);
124 if (err != 0)
125 pr_err("Error = %d disabling xo buffer a1\n", err);
126 else
127 fsm_xo_priv->a1_enabled = 0;
128 }
129 return err;
130}
131static long
132fsm_xo_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
133{
134 int err = 0;
135 struct fsm_xo_req req;
136
137 /* Verify user arguments. */
138 if (_IOC_TYPE(cmd) != FSM_XO_IOC_MAGIC)
139 return -ENOTTY;
140
141 /* Lock for access */
142 if (mutex_lock_interruptible(&fsm_xo_priv->lock))
143 return -ERESTARTSYS;
144
145 switch (cmd) {
146 case FSM_XO_IOC_CLKBUF:
147 if (arg == 0) {
148 pr_err("user space arg not supplied\n");
149 err = -EFAULT;
150 break;
151 }
152
153 if (copy_from_user(&req, (void __user *)arg,
154 sizeof(req))) {
155 pr_err("Error copying from user space\n");
156 err = -EFAULT;
157 break;
158 }
159
160 if (req.clkBuffer == XO_BUFFER_A0) {
161 if (req.clkBufEnable)
162 err = fsm_xo_enable_a0();
163 else
164 err = fsm_xo_disable_a0();
165 } else if (req.clkBuffer == XO_BUFFER_A1) {
166 if (req.clkBufEnable)
167 err = fsm_xo_enable_a1();
168 else
169 err = fsm_xo_disable_a1();
170 } else {
171 pr_err("Invalid ioctl argument.\n");
172 err = -ENOTTY;
173 }
174 break;
175 default:
176 pr_err("Invalid ioctl command.\n");
177 err = -ENOTTY;
178 break;
179 }
180
181 mutex_unlock(&fsm_xo_priv->lock);
182 return err;
183}
184
185static const struct file_operations fsm_xo_fops = {
186 .owner = THIS_MODULE,
187 .unlocked_ioctl = fsm_xo_ioctl,
188 .open = fsm_xo_open,
189 .release = fsm_xo_release
190};
191
192static struct miscdevice fsm_xo_dev = {
193 .minor = MISC_DYNAMIC_MINOR,
194 .name = "fsm_xo",
195 .fops = &fsm_xo_fops
196};
197
198static int fsm_xo_probe(struct platform_device *pdev)
199{
200 int ret = 0;
201
202 /* Initialize */
203 fsm_xo_priv = kzalloc(sizeof(struct fsm_xo_priv_t), GFP_KERNEL);
204
205 if (fsm_xo_priv == NULL) {
206 pr_alert("Not enough memory to initialize device\n");
207 return -ENOMEM;
208 }
209
210 fsm_xo_priv->a0 = regulator_get(&pdev->dev, "a0_clk_buffer");
211 if (IS_ERR(fsm_xo_priv->a0)) {
212 pr_err("Error getting a0_clk_buffer\n");
213 ret = PTR_ERR(fsm_xo_priv->a0);
214 fsm_xo_priv->a0 = NULL;
215 goto err;
216 }
217 fsm_xo_priv->a1 = regulator_get(&pdev->dev, "a1_clk_buffer");
218 if (IS_ERR(fsm_xo_priv->a1)) {
219 pr_err("Error getting a1_clk_buffer\n");
220 ret = PTR_ERR(fsm_xo_priv->a1);
221 fsm_xo_priv->a1 = NULL;
222 goto err;
223 }
224
225 fsm_xo_priv->a0_enabled = 0;
226 fsm_xo_priv->a1_enabled = 0;
227
228 mutex_init(&fsm_xo_priv->lock);
229
230 ret = misc_register(&fsm_xo_dev);
231 if (ret < 0)
232 goto err;
233
234 return 0;
235
236err:
237 if (fsm_xo_priv->a0)
238 regulator_put(fsm_xo_priv->a0);
239 if (fsm_xo_priv->a1)
240 regulator_put(fsm_xo_priv->a1);
241
242 kfree(fsm_xo_priv);
243 fsm_xo_priv = NULL;
244
245 return ret;
246}
247
248static int __devexit fsm_xo_remove(struct platform_device *pdev)
249{
250 if (fsm_xo_priv && fsm_xo_priv->a0)
251 regulator_put(fsm_xo_priv->a0);
252 if (fsm_xo_priv && fsm_xo_priv->a1)
253 regulator_put(fsm_xo_priv->a1);
254
255 kfree(fsm_xo_priv);
256 fsm_xo_priv = NULL;
257
258 misc_deregister(&fsm_xo_dev);
259 return 0;
260}
261
262static struct platform_driver fsm_xo_driver = {
263 .probe = fsm_xo_probe,
264 .remove = fsm_xo_remove,
265 .driver = {
266 .name = "fsm_xo_driver",
267 }
268};
269
270static int __init fsm_xo_init(void)
271{
272 return platform_driver_register(&fsm_xo_driver);
273}
274
275static void __exit fsm_xo_exit(void)
276{
277 platform_driver_unregister(&fsm_xo_driver);
278}
279
280module_init(fsm_xo_init);
281module_exit(fsm_xo_exit);
282
283MODULE_LICENSE("GPL v2");
284MODULE_DESCRIPTION("Provide userspace access to XO buffers in PMIC8058.");
285MODULE_VERSION("1.00");