blob: cbf3b7afafc29506ddc838982f6189614a9ac7e3 [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
Rohit Vaswanib6068332011-07-15 10:41:22 -070013#define pr_fmt(fmt) "%s: " fmt, __func__
14
Rohit Vaswanib18208a2011-07-12 13:04:28 -070015#include <linux/init.h>
16#include <linux/module.h>
17#include <linux/fs.h>
18#include <linux/slab.h>
19#include <linux/miscdevice.h>
20#include <linux/delay.h>
21#include <linux/io.h>
22#include <linux/uaccess.h>
23#include <linux/regulator/pm8058-xo.h>
24#include <linux/platform_device.h>
25
26#define FSM_XO_IOC_MAGIC 0x93
27#define FSM_XO_IOC_CLKBUF _IO(FSM_XO_IOC_MAGIC, 1)
28
29#define FSM_XO_DEVICE_READY 0x01
30#define FSM_XO_DEVICE_OFF 0x00
31
Rohit Vaswanib18208a2011-07-12 13:04:28 -070032/* 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
Rohit Vaswani7beff902011-08-15 13:42:31 -0700228 /* Enable the clock buffers. AMSS depends on this on the FSM. */
229 fsm_xo_enable_a0();
230 fsm_xo_enable_a1();
231
Rohit Vaswanib18208a2011-07-12 13:04:28 -0700232 mutex_init(&fsm_xo_priv->lock);
233
234 ret = misc_register(&fsm_xo_dev);
235 if (ret < 0)
236 goto err;
237
238 return 0;
239
240err:
241 if (fsm_xo_priv->a0)
242 regulator_put(fsm_xo_priv->a0);
243 if (fsm_xo_priv->a1)
244 regulator_put(fsm_xo_priv->a1);
245
246 kfree(fsm_xo_priv);
247 fsm_xo_priv = NULL;
248
249 return ret;
250}
251
252static int __devexit fsm_xo_remove(struct platform_device *pdev)
253{
254 if (fsm_xo_priv && fsm_xo_priv->a0)
255 regulator_put(fsm_xo_priv->a0);
256 if (fsm_xo_priv && fsm_xo_priv->a1)
257 regulator_put(fsm_xo_priv->a1);
258
259 kfree(fsm_xo_priv);
260 fsm_xo_priv = NULL;
261
262 misc_deregister(&fsm_xo_dev);
263 return 0;
264}
265
266static struct platform_driver fsm_xo_driver = {
267 .probe = fsm_xo_probe,
268 .remove = fsm_xo_remove,
269 .driver = {
270 .name = "fsm_xo_driver",
271 }
272};
273
274static int __init fsm_xo_init(void)
275{
276 return platform_driver_register(&fsm_xo_driver);
277}
278
279static void __exit fsm_xo_exit(void)
280{
281 platform_driver_unregister(&fsm_xo_driver);
282}
283
284module_init(fsm_xo_init);
285module_exit(fsm_xo_exit);
286
287MODULE_LICENSE("GPL v2");
288MODULE_DESCRIPTION("Provide userspace access to XO buffers in PMIC8058.");
289MODULE_VERSION("1.00");