/drivers/ril/ril.c
C | 423 lines | 337 code | 61 blank | 25 comment | 62 complexity | 397e7a141fa701e23ac50759b04a065e MD5 | raw file
Possible License(s): LGPL-2.0, AGPL-1.0, GPL-2.0
1#include <linux/delay.h> 2#include <linux/fs.h> 3#include <linux/gpio.h> 4#include <linux/init.h> 5#include <linux/interrupt.h> 6#include <linux/module.h> 7#include <linux/platform_device.h> 8#include <linux/switch.h> 9#include <linux/uaccess.h> 10#include <linux/workqueue.h> 11#include <../../arch/arm/mach-tegra/include/mach/board-cardhu-misc.h> 12 13#include "pm-irq.h" 14#include "ril.h" 15#include "ril_proximity.h" 16#include "ril_wakeup.h" 17 18MODULE_DESCRIPTION(DRIVER_DESC); 19MODULE_LICENSE("GPL"); 20 21//**** external symbols 22 23 24//**** constants 25 26#define _ATTR_MODE S_IRUSR | S_IWUSR | S_IRGRP 27 28 29//**** local variable declaration 30 31static struct workqueue_struct *workqueue; 32static struct device *dev; 33static struct class *ril_class; 34static dev_t ril_dev; 35static int ril_major = 0; 36static int ril_minor = 0; 37int project_id = 0; 38 39static struct gpio ril_gpios_TF300TG[] = { 40 { MOD_VBUS_ON, GPIOF_OUT_INIT_LOW, "BB_VBUS" }, 41 { USB_SW_SEL, GPIOF_OUT_INIT_LOW, "BB_SW_SEL" }, 42 { SAR_DET_3G, GPIOF_IN, "BB_SAR_DET" }, 43 { SIM_CARD_DET, GPIOF_IN, "BB_SIM_DET" }, 44}; 45 46static struct gpio ril_gpios_TF300TL[] = { 47 { MOD_VBAT_ON, GPIOF_OUT_INIT_LOW, "BB_VBAT"}, 48 { MOD_VBUS_ON, GPIOF_OUT_INIT_LOW, "BB_VBUS"}, 49 { USB_SW_SEL, GPIOF_OUT_INIT_LOW, "BB_SW_SEL"}, 50 { SAR_DET_3G, GPIOF_IN, "BB_SAR_DET" }, 51 { SIM_CARD_DET, GPIOF_IN, "BB_SIM_DET" }, 52 { MOD_POWER_KEY, GPIOF_OUT_INIT_LOW, "BB_MOD_PWR"}, 53 { DL_MODE, GPIOF_OUT_INIT_LOW, "BB_DL_MODE"}, 54 { AP_TO_MOD_RST, GPIOF_OUT_INIT_LOW, "BB_MOD_RST"}, 55 { MOD_WAKE_AP, GPIOF_IN, "BB_MOD_WAKE_AP"}, 56 { MOD_WAKE_IND, GPIOF_OUT_INIT_HIGH, "BB_MOD_WAKE_IND"}, 57 { MOD_HANG, GPIOF_IN, "BB_MOD_HANG"}, 58 /* no use now */ 59 { DL_COMPLETE, GPIOF_OUT_INIT_LOW, "BB_DL_COMPLETE"}, 60 { MOD_SUS_REQ, GPIOF_OUT_INIT_LOW, "BB_MOD_SUS_REQ"}, 61 { AP_WAKE_IND, GPIOF_OUT_INIT_LOW, "BB_AP_WAKE_IND"}, 62 { AP_WAKE_MOD, GPIOF_OUT_INIT_LOW, "BB_AP_WAKE_MOD"}, 63}; 64 65//**** IRQ event handler 66 67irqreturn_t ril_ipc_sar_det_irq(int irq, void *dev_id) 68{ 69 return ril_proximity_interrupt_handle(irq, dev_id); 70} 71 72irqreturn_t ril_ipc_sim_det_irq(int irq, void *dev_id) 73{ 74 // TODO: implement SIM hot-plug here 75 return IRQ_HANDLED; 76} 77 78//**** sysfs callback functions 79static int store_gpio(size_t count, const char *buf, int gpio, char *gpio_name) 80{ 81 int enable; 82 83 if (sscanf(buf, "%u", &enable) != 1) 84 return -EINVAL; 85 86 if ((enable != 1) && (enable != 0)) 87 return -EINVAL; 88 89 gpio_set_value(gpio, enable); 90 RIL_INFO("Set %s to %d\n", gpio_name, enable); 91 return count; 92} 93 94/* TF300TG sysfs functions */ 95static ssize_t show_vbus_state(struct device *class, 96 struct device_attribute *attr, char *buf) 97{ 98 return sprintf(buf, "%d\n", gpio_get_value(MOD_VBUS_ON)); 99} 100 101static ssize_t store_vbus_state(struct device *class, struct device_attribute *attr, 102 const char *buf, size_t count) 103{ 104 return store_gpio(count, buf, MOD_VBUS_ON, "MOD_VBUS_ON"); 105} 106 107/* TF300TL sysfs functions */ 108static ssize_t show_power_state(struct device *class, struct device_attribute *attr, 109 char *buf) 110{ 111 return sprintf(buf, "%d\n", gpio_get_value(MOD_POWER_KEY)); 112} 113 114static ssize_t store_power_state(struct device *class, struct device_attribute *attr, 115 const char *buf, size_t count) 116{ 117 return store_gpio(count, buf, MOD_POWER_KEY, "MOD_POWER_KEY"); 118} 119 120static ssize_t show_vbat_state(struct device *class, struct device_attribute *attr, 121 char *buf) 122{ 123 return sprintf(buf, "%d\n", gpio_get_value(MOD_VBAT_ON)); 124} 125 126static ssize_t store_vbat_state(struct device *class, struct device_attribute *attr, 127 const char *buf, size_t count) 128{ 129 return store_gpio(count, buf, MOD_VBAT_ON, "MOD_VBAT_ON"); 130} 131 132static ssize_t show_reset_state(struct device *class, struct device_attribute *attr, 133 char *buf) 134{ 135 return sprintf(buf, "%d\n", gpio_get_value(AP_TO_MOD_RST)); 136} 137 138static ssize_t store_reset_state(struct device *class, struct device_attribute *attr, 139 const char *buf, size_t count) 140{ 141 return store_gpio(count, buf, AP_TO_MOD_RST, "AP_TO_MOD_RST"); 142} 143 144static ssize_t show_download_state(struct device *class, struct device_attribute *attr, 145 char *buf) 146{ 147 return sprintf(buf, "%d\n", gpio_get_value(DL_MODE)); 148} 149 150static ssize_t store_download_state(struct device *class, struct device_attribute *attr, 151 const char *buf, size_t count) 152{ 153 return store_gpio(count, buf, DL_MODE, "DL_MODE"); 154} 155 156 157//**** sysfs list 158/* TF300TG sysfs array */ 159static struct device_attribute device_attr_TF300TG[] = { 160 __ATTR(bb_vbus, _ATTR_MODE, show_vbus_state, store_vbus_state), 161 __ATTR_NULL, 162}; 163 164/* TF300TL sysfs array */ 165static struct device_attribute device_attr_TF300TL[] = { 166 __ATTR(mod_power, _ATTR_MODE, show_power_state, store_power_state), 167 __ATTR(vbat, _ATTR_MODE, show_vbat_state, store_vbat_state), 168 __ATTR(mod_rst, _ATTR_MODE, show_reset_state, store_reset_state), 169 __ATTR(dl_mode, _ATTR_MODE, show_download_state, store_download_state), 170 __ATTR_NULL, 171}; 172 173//**** initialize and finalize 174 175static int create_ril_files(void) 176{ 177 int rc = 0, i = 0; 178 179 rc = alloc_chrdev_region(&ril_dev, ril_minor, 1, "ril"); 180 ril_major = MAJOR(ril_dev); 181 if (rc < 0) { 182 RIL_ERR("allocate char device failed\n"); 183 goto failed; 184 } 185 RIL_INFO("rc = %d, ril_major = %d\n", rc, ril_major); 186 187 ril_class = class_create(THIS_MODULE, "ril"); 188 if (IS_ERR(ril_class)) { 189 RIL_ERR("ril_class create fail\n"); 190 rc = PTR_ERR(ril_class); 191 goto create_class_failed; 192 } 193 194 dev = device_create(ril_class, NULL, MKDEV(ril_major, ril_minor), 195 NULL, "files"); 196 if (IS_ERR(dev)) { 197 RIL_ERR("dev create fail\n"); 198 rc = PTR_ERR(dev); 199 goto create_device_failed; 200 } 201 202 if (project_id == TEGRA3_PROJECT_TF300TG) { 203 for (i = 0; i < (ARRAY_SIZE(device_attr_TF300TG) - 1); i++) { 204 rc = device_create_file(dev, &device_attr_TF300TG[i]); 205 if (rc < 0) { 206 RIL_ERR("create file of [%d] failed, err = %d\n", i, rc); 207 goto create_files_failed; 208 } 209 } 210 } else if (project_id == TEGRA3_PROJECT_TF300TL) { 211 for (i = 0; i < (ARRAY_SIZE(device_attr_TF300TL) - 1); i++) { 212 rc = device_create_file(dev, &device_attr_TF300TL[i]); 213 if (rc < 0) { 214 RIL_ERR("create file of [%d] failed, err = %d\n", i, rc); 215 goto create_files_failed; 216 } 217 } 218 } 219 220 RIL_INFO("add_ril_dev success\n"); 221 return 0; 222 223create_files_failed: 224 if (project_id == TEGRA3_PROJECT_TF300TG) { 225 while (i--) 226 device_remove_file(dev, &device_attr_TF300TG[i]); 227 } else if (project_id == TEGRA3_PROJECT_TF300TL) { 228 while (i--) 229 device_remove_file(dev, &device_attr_TF300TL[i]); 230 } 231create_device_failed: 232 class_destroy(ril_class); 233create_class_failed: 234 unregister_chrdev_region(ril_dev, 1); 235failed: 236 return rc; 237} 238 239static void remove_ril_files(void) 240{ 241 int i; 242 if (project_id == TEGRA3_PROJECT_TF300TG) { 243 for (i = 0; i < (ARRAY_SIZE(device_attr_TF300TG) - 1); i++) 244 device_remove_file(dev, &device_attr_TF300TG[i]); 245 } else if (project_id == TEGRA3_PROJECT_TF300TL) { 246 for (i = 0; i < (ARRAY_SIZE(device_attr_TF300TL) - 1); i++) 247 device_remove_file(dev, &device_attr_TF300TL[i]); 248 } 249 device_destroy(ril_class, MKDEV(ril_major, ril_minor)); 250 class_destroy(ril_class); 251 unregister_chrdev_region(ril_dev, 1); 252} 253 254static void power_on_TF300TL(void) 255{ 256 RIL_INFO("TF300TL power_on\n"); 257 gpio_set_value(MOD_VBAT_ON, 1); 258 RIL_INFO("Set MOD_VBAT_ON to %d\n", gpio_get_value(MOD_VBAT_ON)); 259 mdelay(100); 260 gpio_set_value(MOD_POWER_KEY, 1); 261 RIL_INFO("Set MOD_POWER_KEY to %d\n", gpio_get_value(MOD_POWER_KEY)); 262 msleep(200); 263 gpio_set_value(USB_SW_SEL, 1); 264 RIL_INFO("Set USB_SW_SEL to %d\n", gpio_get_value(USB_SW_SEL)); 265 mdelay(50); 266 gpio_set_value(MOD_VBUS_ON, 1); 267 RIL_INFO("Set MOD_VBUS_ON to %d\n", gpio_get_value(MOD_VBUS_ON)); 268} 269 270static int __init ril_init(void) 271{ 272 int err, i; 273 RIL_INFO("RIL init\n"); 274 275 project_id = tegra3_get_project_id(); 276 277 /* enable and request gpio(s) */ 278 if (project_id == TEGRA3_PROJECT_TF300TG) { 279 RIL_INFO("project_id = TF300TG\n"); 280 for (i = 0; i < ARRAY_SIZE(ril_gpios_TF300TG); i++) { 281 tegra_gpio_enable(ril_gpios_TF300TG[i].gpio); 282 } 283 err = gpio_request_array(ril_gpios_TF300TG, 284 ARRAY_SIZE(ril_gpios_TF300TG)); 285 if (err < 0) { 286 pr_err("%s - request gpio(s) failed\n", __func__); 287 return err; 288 } 289 } else if (project_id == TEGRA3_PROJECT_TF300TL) { 290 RIL_INFO("project_id = TF300TL\n"); 291 for (i = 0; i < ARRAY_SIZE(ril_gpios_TF300TL); i++) { 292 tegra_gpio_enable(ril_gpios_TF300TL[i].gpio); 293 } 294 err = gpio_request_array(ril_gpios_TF300TL, 295 ARRAY_SIZE(ril_gpios_TF300TL)); 296 if (err < 0) { 297 pr_err("%s - request gpio(s) failed\n", __func__); 298 return err; 299 } 300 mdelay(100); 301 power_on_TF300TL(); 302 } else { 303 RIL_ERR("Ril driver doesn't support this project\n"); 304 return -1; 305 } 306 307 /* create device file(s) */ 308 err = create_ril_files(); 309 if (err < 0) 310 goto failed1; 311 312 /* request ril irq(s) */ 313 err = request_irq(gpio_to_irq(SAR_DET_3G), 314 ril_ipc_sar_det_irq, 315 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, 316 "IPC_SAR_DET_3G", 317 NULL); 318 if (err < 0) { 319 pr_err("%s - request irq IPC_SAR_DET_3G failed\n", 320 __func__); 321 goto failed2; 322 } 323 disable_irq(gpio_to_irq(SAR_DET_3G)); 324 325 err = request_irq(gpio_to_irq(SIM_CARD_DET), 326 ril_ipc_sim_det_irq, 327 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, 328 "IPC_SIM_CARD_DET", 329 NULL); 330 if (err < 0) { 331 pr_err("%s - request irq IPC_SIM_CARD_DET failed\n", 332 __func__); 333 goto failed3; 334 } 335 tegra_pm_irq_set_wake_type(gpio_to_irq(SIM_CARD_DET), 336 IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING); 337 enable_irq_wake(gpio_to_irq(SIM_CARD_DET)); 338 339 /* init work queue */ 340 workqueue = create_singlethread_workqueue 341 ("ril_workqueue"); 342 if (!workqueue) { 343 pr_err("%s - cannot create workqueue\n", __func__); 344 err = -1; 345 goto failed4; 346 } 347 348 /* register proximity switch */ 349 err = ril_proximity_init(dev, workqueue); 350 if (err < 0) { 351 pr_err("%s - register proximity switch failed\n", 352 __func__); 353 goto failed5; 354 } 355 356 /* wakeup control (TF-300TL only) */ 357 if (TEGRA3_PROJECT_TF300TL == project_id) { 358 err = init_wakeup_control(workqueue); 359 if (err < 0) { 360 RIL_ERR("init wakeup_control failed\n"); 361 goto failed6; 362 } 363 } 364 365 RIL_INFO("RIL init successfully\n"); 366 return 0; 367 368failed6: 369 ril_proximity_exit(); 370failed5: 371 destroy_workqueue(workqueue); 372failed4: 373 free_irq(gpio_to_irq(SIM_CARD_DET), NULL); 374failed3: 375 free_irq(gpio_to_irq(SAR_DET_3G), NULL); 376failed2: 377 remove_ril_files(); 378failed1: 379 if (project_id == TEGRA3_PROJECT_TF300TG) { 380 gpio_free_array(ril_gpios_TF300TG, 381 ARRAY_SIZE(ril_gpios_TF300TG)); 382 } else if (project_id == TEGRA3_PROJECT_TF300TL) { 383 gpio_free_array(ril_gpios_TF300TL, 384 ARRAY_SIZE(ril_gpios_TF300TL)); 385 } 386 return err; 387} 388 389static void __exit ril_exit(void) 390{ 391 RIL_INFO("RIL exit\n"); 392 393 /* free irq(s) */ 394 free_irq(gpio_to_irq(SAR_DET_3G), NULL); 395 free_irq(gpio_to_irq(SIM_CARD_DET), NULL); 396 397 /* free gpio(s) */ 398 if (project_id == TEGRA3_PROJECT_TF300TG) { 399 gpio_free_array(ril_gpios_TF300TG, 400 ARRAY_SIZE(ril_gpios_TF300TG)); 401 } else if (project_id == TEGRA3_PROJECT_TF300TL) { 402 gpio_free_array(ril_gpios_TF300TL, 403 ARRAY_SIZE(ril_gpios_TF300TL)); 404 } 405 406 /* delete device file(s) */ 407 remove_ril_files(); 408 409 /* unregister proximity switch */ 410 ril_proximity_exit(); 411 412 /* destroy workqueue */ 413 destroy_workqueue(workqueue); 414 415 /* free resources for wakeup control*/ 416 if (TEGRA3_PROJECT_TF300TL == project_id) { 417 free_wakeup_control(); 418 } 419 420} 421 422module_init(ril_init); 423module_exit(ril_exit);