/* * LED driver for the FIC Neo1973 GTA02 GSM phone * * (C) 2006-2007 by OpenMoko, Inc. * Author: Harald Welte * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * */ #include #include #include #include #include #include #include #include #include #define MAX_LEDS 3 #define COUNTER 256 struct gta02_led_priv { struct mutex mutex; struct led_classdev cdev; struct s3c2410_pwm pwm; unsigned int gpio; unsigned int has_pwm; }; struct gta02_led_bundle { int num_leds; struct gta02_led_priv led[MAX_LEDS]; }; static inline struct gta02_led_priv *to_priv(struct led_classdev *led_cdev) { return container_of(led_cdev, struct gta02_led_priv, cdev); } static inline struct gta02_led_bundle *to_bundle(struct led_classdev *led_cdev) { return dev_get_drvdata(led_cdev->dev); } static void gta02led_set(struct led_classdev *led_cdev, enum led_brightness value) { struct gta02_led_priv *lp = to_priv(led_cdev); /* * value == 255 -> 99% duty cycle (full power) * value == 128 -> 50% duty cycle (medium power) * value == 0 -> 0% duty cycle (zero power) */ mutex_lock(&lp->mutex); if (lp->has_pwm) { s3c2410_pwm_duty_cycle(value, &lp->pwm); } else { if (value) s3c2410_gpio_setpin(lp->gpio, 1); else s3c2410_gpio_setpin(lp->gpio, 0); } mutex_unlock(&lp->mutex); } #ifdef CONFIG_PM static int gta02led_suspend(struct platform_device *pdev, pm_message_t state) { struct gta02_led_bundle *bundle = platform_get_drvdata(pdev); int i; for (i = 0; i < bundle->num_leds; i++) led_classdev_suspend(&bundle->led[i].cdev); return 0; } static int gta02led_resume(struct platform_device *pdev) { struct gta02_led_bundle *bundle = platform_get_drvdata(pdev); int i; for (i = 0; i < bundle->num_leds; i++) led_classdev_resume(&bundle->led[i].cdev); return 0; } #endif static int __init gta02led_probe(struct platform_device *pdev) { int i, rc; struct gta02_led_bundle *bundle; if (!machine_is_neo1973_gta02()) return -EIO; bundle = kzalloc(sizeof(struct gta02_led_bundle), GFP_KERNEL); if (!bundle) return -ENOMEM; platform_set_drvdata(pdev, bundle); for (i = 0; i < pdev->num_resources; i++) { struct gta02_led_priv *lp; struct resource *r; if (i >= MAX_LEDS) break; r = platform_get_resource(pdev, 0, i); if (!r || !r->start || !r->name) continue; lp = &bundle->led[i]; lp->gpio = r->start; lp->cdev.name = r->name; lp->cdev.brightness_set = gta02led_set; switch (lp->gpio) { case S3C2410_GPB0: lp->has_pwm = 1; lp->pwm.timerid = PWM0; s3c2410_gpio_cfgpin(lp->gpio, S3C2410_GPB0_TOUT0); break; case S3C2410_GPB1: lp->has_pwm = 1; lp->pwm.timerid = PWM1; s3c2410_gpio_cfgpin(lp->gpio, S3C2410_GPB1_TOUT1); break; case S3C2410_GPB2: lp->has_pwm = 1; lp->pwm.timerid = PWM2; s3c2410_gpio_cfgpin(lp->gpio, S3C2410_GPB2_TOUT2); break; case S3C2410_GPB3: lp->has_pwm = 1; lp->pwm.timerid = PWM3; s3c2410_gpio_cfgpin(lp->gpio, S3C2410_GPB3_TOUT3); break; default: break; } lp->pwm.prescaler = 0; lp->pwm.divider = S3C2410_TCFG1_MUX3_DIV8; lp->pwm.counter = COUNTER; lp->pwm.comparer = COUNTER; s3c2410_pwm_enable(&lp->pwm); s3c2410_pwm_start(&lp->pwm); switch (lp->gpio) { case S3C2410_GPB0: case S3C2410_GPB1: case S3C2410_GPB2: lp->has_pwm = 0; s3c2410_gpio_cfgpin(lp->gpio, S3C2410_GPIO_OUTPUT); s3c2410_gpio_setpin(lp->gpio, 0); break; default: break; } mutex_init(&lp->mutex); rc = led_classdev_register(&pdev->dev, &lp->cdev); } return 0; } static int gta02led_remove(struct platform_device *pdev) { struct gta02_led_bundle *bundle = platform_get_drvdata(pdev); int i; for (i = 0; i < bundle->num_leds; i++) { struct gta02_led_priv *lp = &bundle->led[i]; if (lp->has_pwm) s3c2410_pwm_disable(&lp->pwm); led_classdev_unregister(&lp->cdev); mutex_destroy(&lp->mutex); } platform_set_drvdata(pdev, NULL); kfree(bundle); return 0; } static struct platform_driver gta02led_driver = { .probe = gta02led_probe, .remove = gta02led_remove, #ifdef CONFIG_PM .suspend = gta02led_suspend, .resume = gta02led_resume, #endif .driver = { .name = "gta02-led", }, }; static int __init gta02led_init(void) { return platform_driver_register(>a02led_driver); } static void __exit gta02led_exit(void) { platform_driver_unregister(>a02led_driver); } module_init(gta02led_init); module_exit(gta02led_exit); MODULE_AUTHOR("Harald Welte "); MODULE_DESCRIPTION("FIC Neo1973 GTA02 LED driver"); MODULE_LICENSE("GPL");