upload tizen1.0 source
[kernel/linux-2.6.36.git] / arch / arm / mach-s5pv310 / bcm4751.c
1 /*
2  *  bcm4751.c: Machine specific driver for GPS module
3  *
4  *  Copyright (C) 2009-2010 Samsung Electronics
5  *  Jaehoon Chung <jh80.chung@samsung.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License version 2 as
9  * published by the Free Software Foundation.
10  */
11
12 #include <linux/kernel.h>
13 #include <linux/module.h>
14 #include <linux/slab.h>
15 #include <linux/init.h>
16 #include <linux/platform_device.h>
17 #include <linux/mutex.h>
18 #include <linux/err.h>
19 #include <linux/delay.h>
20 #include <linux/rfkill.h>
21 #include <linux/regulator/machine.h>
22
23 #include <mach/gpio.h>
24
25 #include <plat/gpio-cfg.h>
26
27 #include "bcm4751.h"
28
29 struct bcm4751_data {
30         struct bcm4751_platform_data    *pdata;
31         struct rfkill                   *rfk;
32         bool                            in_use;
33         bool                            regulator_state;
34         struct regulator                *regulator;
35 };
36
37 static void bcm4751_enable(void *data)
38 {
39         struct bcm4751_data *bd = data;
40         struct bcm4751_platform_data *pdata = bd->pdata;
41
42         if (bd->regulator && !bd->regulator_state) {
43                 regulator_enable(bd->regulator);
44                 bd->regulator_state = true;
45         }
46
47         gpio_set_value(pdata->regpu, 1);
48
49         if (pdata->nrst)
50                 gpio_set_value(pdata->nrst, 1);
51
52         if (pdata->clk_int)
53                 gpio_set_value(pdata->clk_int, 1);
54
55         bd->in_use = true;
56 }
57
58 static void bcm4751_disable(void *data)
59 {
60         struct bcm4751_data *bd = data;
61         struct bcm4751_platform_data *pdata = bd->pdata;
62
63         gpio_set_value(pdata->regpu, 0);
64
65         if (pdata->clk_int)
66                 gpio_set_value(pdata->clk_int, 0);
67
68         if (bd->regulator && bd->regulator_state) {
69                 regulator_disable(bd->regulator);
70                 bd->regulator_state = false;
71         }
72
73         bd->in_use = false;
74 }
75
76 static int bcm4751_set_block(void *data, bool blocked)
77 {
78         if (!blocked) {
79                 printk(KERN_INFO "%s : Enable GPS chip\n", __func__);
80                 bcm4751_enable(data);
81         } else {
82                 printk(KERN_INFO "%s : Disable GPS chip\n", __func__);
83                 bcm4751_disable(data);
84         }
85         return 0;
86 }
87
88 static const struct rfkill_ops bcm4751_rfkill_ops = {
89         .set_block = bcm4751_set_block,
90 };
91
92 static int __devinit bcm4751_probe(struct platform_device *dev)
93 {
94         struct bcm4751_platform_data *pdata;
95         struct bcm4751_data *bd;
96         int gpio;
97         int ret = 0;
98
99         pdata = dev->dev.platform_data;
100         if (!pdata) {
101                 dev_err(&dev->dev, "No BCM4751 platform data.\n");
102                 return -EINVAL;
103         }
104
105         bd = kzalloc(sizeof(struct bcm4751_data), GFP_KERNEL);
106         if (!bd)
107                 return -ENOMEM;
108
109         bd->pdata = pdata;
110
111         if (pdata->regpu) {
112                 gpio = pdata->regpu;
113
114                 /* GPS_EN is low */
115                 gpio_request(gpio, "GPS_EN");
116                 gpio_direction_output(gpio, 0);
117         }
118
119         if (pdata->nrst) {
120                 gpio = pdata->nrst;
121                 /* GPS_nRST is high */
122                 gpio_request(gpio, "GPS_nRST");
123                 gpio_direction_output(gpio, 1);
124         }
125
126         /* GPS_UART_RXD */
127         if (pdata->uart_rxd)
128                 s3c_gpio_setpull(pdata->uart_rxd, S3C_GPIO_PULL_UP);
129
130         if (pdata->clk_int) {
131                 gpio = pdata->clk_int;
132                 gpio_request(gpio, "GPS_CLK_INT");
133                 gpio_direction_output(gpio, 0);
134         }
135
136         printk("%s: BCM4751 module is ready to start\n", __func__);
137
138         /* Register BCM4751 to RFKILL class */
139         bd->rfk = rfkill_alloc("bcm4751", &dev->dev, RFKILL_TYPE_GPS,
140                         &bcm4751_rfkill_ops, bd);
141         if (!bd->rfk) {
142                 ret = -ENOMEM;
143                 goto err_rfk_alloc;
144         }
145
146         bd->regulator = regulator_get(NULL, "gps_clk");
147         if (IS_ERR_OR_NULL(bd->regulator)) {
148                 printk("BCM4751 regulator_get error\n");
149                 goto err_regulator;
150         }
151
152         bd->regulator_state = false;
153
154         /*
155          * described by the comment in rfkill.h
156          * true : turn off
157          * false : turn on
158          */
159         rfkill_init_sw_state(bd->rfk, true);
160         bd->in_use = false;
161
162         ret = rfkill_register(bd->rfk);
163         if (ret)
164                 goto err_rfkill;
165
166         platform_set_drvdata(dev, bd);
167
168         printk("%s: RFKILL module is ready to start\n", __func__);
169
170         return 0;
171
172 err_rfkill:
173         rfkill_destroy(bd->rfk);
174 err_regulator:
175 err_rfk_alloc:
176         kfree(bd);
177         return ret;
178 }
179
180 static int __devexit bcm4751_remove(struct platform_device *dev)
181 {
182         struct bcm4751_data *bd = platform_get_drvdata(dev);
183         struct bcm4751_platform_data *pdata = bd->pdata;
184
185         rfkill_unregister(bd->rfk);
186         rfkill_destroy(bd->rfk);
187         gpio_free(pdata->regpu);
188         gpio_free(pdata->nrst);
189         kfree(bd);
190         platform_set_drvdata(dev, NULL);
191
192         return 0;
193 }
194
195 #ifdef CONFIG_PM
196 static int bcm4751_suspend(struct platform_device *dev, pm_message_t stata)
197 {
198         struct bcm4751_data *bd = platform_get_drvdata(dev);
199         struct bcm4751_platform_data *pdata = bd->pdata;
200
201         if (bd->in_use) {
202                 s5p_gpio_set_pdn_config(pdata->regpu, S3C_GPIO_PDNCFG_OUTPUT1);
203                 s5p_gpio_set_pdn_config(pdata->nrst, S3C_GPIO_PDNCFG_OUTPUT1);
204                 s5p_gpio_set_pdn_config(pdata->clk_int, S3C_GPIO_PDNCFG_OUTPUT1);
205         } else {
206                 s5p_gpio_set_pdn_config(pdata->regpu, S3C_GPIO_PDNCFG_OUTPUT0);
207                 s5p_gpio_set_pdn_config(pdata->nrst, S3C_GPIO_PDNCFG_OUTPUT0);
208                 s5p_gpio_set_pdn_config(pdata->clk_int, S3C_GPIO_PDNCFG_OUTPUT0);
209         }
210
211         return 0;
212 }
213
214 static int bcm4751_resume(struct platform_device *dev)
215 {
216         return 0;
217 }
218 #else
219 #define bcm4751_suspend         NULL
220 #define bcm4751_resume          NULL
221 #endif
222
223 static struct platform_driver bcm4751_driver = {
224         .probe          = bcm4751_probe,
225         .remove         = __devexit_p(bcm4751_remove),
226         .suspend        = bcm4751_suspend,
227         .resume         = bcm4751_resume,
228         .driver = {
229                 .name   = "bcm4751",
230         },
231 };
232
233 static int __init bcm4751_init(void)
234 {
235         return platform_driver_register(&bcm4751_driver);
236 }
237
238 static void __exit bcm4751_exit(void)
239 {
240         platform_driver_unregister(&bcm4751_driver);
241 }
242
243 module_init(bcm4751_init);
244 module_exit(bcm4751_exit);
245
246 MODULE_AUTHOR("Jaehoon Chung <jh80.chung@samsung.com>");
247 MODULE_DESCRIPTION("BCM4751 GPS module driver");
248 MODULE_LICENSE("GPL");