From: lmc Date: Wed, 27 Jul 2011 09:51:04 +0000 (+0800) Subject: newton: add support for the pwm led and update board_rk29_newton.c X-Git-Tag: firefly_0821_release~10074 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=54d1431f258335151ff74023d62c4a616279aa34;p=firefly-linux-kernel-4.4.55.git newton: add support for the pwm led and update board_rk29_newton.c --- diff --git a/arch/arm/configs/rk29_newton_defconfig b/arch/arm/configs/rk29_newton_defconfig index e8b7aa2dd03f..282bc8397431 100755 --- a/arch/arm/configs/rk29_newton_defconfig +++ b/arch/arm/configs/rk29_newton_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux kernel version: 2.6.32.27 -# Wed Jul 27 10:06:42 2011 +# Wed Jul 27 17:48:19 2011 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -1687,6 +1687,7 @@ CONFIG_LEDS_CLASS=y # CONFIG_LEDS_PCA9532 is not set CONFIG_LEDS_GPIO=y CONFIG_LEDS_GPIO_PLATFORM=y +CONFIG_LEDS_NEWTON_PWM=y # CONFIG_LEDS_LP3944 is not set # CONFIG_LEDS_PCA955X is not set # CONFIG_LEDS_BD2802 is not set diff --git a/arch/arm/mach-rk29/board-rk29-newton.c b/arch/arm/mach-rk29/board-rk29-newton.c index 0b09fe27af85..03b2837c2a43 100755 --- a/arch/arm/mach-rk29/board-rk29-newton.c +++ b/arch/arm/mach-rk29/board-rk29-newton.c @@ -1688,6 +1688,7 @@ struct gpio_led rk29_leds[] = { .gpio = RK29_PIN4_PB2, .default_trigger = "timer", .active_low = 0, + .retain_state_suspended = 1, .default_state = LEDS_GPIO_DEFSTATE_OFF, }, { @@ -1695,6 +1696,7 @@ struct gpio_led rk29_leds[] = { .gpio = RK29_PIN4_PB1, .default_trigger = "timer", .active_low = 0, + .retain_state_suspended = 1, .default_state = LEDS_GPIO_DEFSTATE_OFF, }, { @@ -1702,6 +1704,7 @@ struct gpio_led rk29_leds[] = { .gpio = RK29_PIN4_PB0, .default_trigger = "timer", .active_low = 0, + .retain_state_suspended = 1, .default_state = LEDS_GPIO_DEFSTATE_OFF, }, }; @@ -1719,6 +1722,35 @@ struct platform_device rk29_device_gpio_leds = { }, }; #endif + +#ifdef CONFIG_LEDS_NEWTON_PWM +static struct led_newton_pwm rk29_pwm_leds[] = { + { + .name = "power_led", + .pwm_id = 1, + .pwm_gpio = RK29_PIN5_PD2, + .pwm_iomux_name = GPIO5D2_PWM1_UART1SIRIN_NAME, + .pwm_iomux_pwm = GPIO5H_PWM1, + .pwm_iomux_gpio = GPIO5H_GPIO5D2, + .freq = 1000, + .period = 255, + }, +}; + +static struct led_newton_pwm_platform_data rk29_pwm_leds_pdata = { + .leds = &rk29_pwm_leds, + .num_leds = ARRAY_SIZE(rk29_pwm_leds), +}; + +static struct platform_device rk29_device_pwm_leds = { + .name = "leds_newton_pwm", + .id = -1, + .dev = { + .platform_data = &rk29_pwm_leds_pdata, + }, +}; + #endif + #ifdef CONFIG_USB_ANDROID struct usb_mass_storage_platform_data newton_mass_storage_pdata = { .nluns = 1, @@ -1747,6 +1779,9 @@ static void __init rk29_board_iomux_init(void) rk29_mux_api_set(GPIO4B1_FLASHDATA9_NAME,GPIO4L_GPIO4B1); rk29_mux_api_set(GPIO4B2_FLASHDATA10_NAME,GPIO4L_GPIO4B2); #endif + #ifdef CONFIG_LEDS_NEWTON_PWM + rk29_mux_api_set(GPIO5D2_PWM1_UART1SIRIN_NAME, GPIO5H_GPIO5D2); + #endif } static struct platform_device *devices[] __initdata = { @@ -1877,6 +1912,9 @@ static struct platform_device *devices[] __initdata = { #ifdef CONFIG_LEDS_GPIO_PLATFORM &rk29_device_gpio_leds, #endif +#ifdef CONFIG_LEDS_NEWTON_PWM + &rk29_device_pwm_leds, +#endif }; /***************************************************************************************** diff --git a/arch/arm/mach-rk29/include/mach/board.h b/arch/arm/mach-rk29/include/mach/board.h index 706abdda19e8..2e3dcfdc6230 100755 --- a/arch/arm/mach-rk29/include/mach/board.h +++ b/arch/arm/mach-rk29/include/mach/board.h @@ -22,6 +22,22 @@ #include #include +struct led_newton_pwm { + const char *name; + unsigned int pwm_id; + unsigned pwm_gpio; + char* pwm_iomux_name; + unsigned int pwm_iomux_pwm; + unsigned int pwm_iomux_gpio; + unsigned int freq;/**/ + unsigned int period;/*1-100*/ +}; + +struct led_newton_pwm_platform_data { + int num_leds; + struct led_newton_pwm* leds; +}; + struct irda_info{ u32 intr_pin; int (*iomux_init)(void); diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index dab9a1bdb4c1..a4e8e8fe4bed 100755 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -146,6 +146,14 @@ config LEDS_GPIO_OF of_platform devices. For instance, LEDs which are listed in a "dts" file. +config LEDS_NEWTON_PWM + bool "LED Support for newton pwm" + depends on LEDS_CLASS&&ARCH_RK29 + default n + help + Let the leds-gpio driver drive LEDs which have been defined as + platform devices. If you don't know what this means, say yes. + config LEDS_LP3944 tristate "LED Support for N.S. LP3944 (Fun Light) I2C chip" depends on LEDS_CLASS && I2C diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 0263ff237ea2..ace2dc6541e1 100755 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -30,6 +30,7 @@ obj-$(CONFIG_LEDS_WM831X_STATUS) += leds-wm831x-status.o obj-$(CONFIG_LEDS_WM8350) += leds-wm8350.o obj-$(CONFIG_LEDS_PWM) += leds-pwm.o obj-$(CONFIG_LEDS_ATT1272) += leds-att1272.o +obj-$(CONFIG_LEDS_NEWTON_PWM) += leds-newton-pwm.o # LED SPI Drivers obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o diff --git a/drivers/leds/leds-newton-pwm.c b/drivers/leds/leds-newton-pwm.c new file mode 100755 index 000000000000..00e3a7d53612 --- /dev/null +++ b/drivers/leds/leds-newton-pwm.c @@ -0,0 +1,304 @@ +/* + * linux/drivers/leds-newton-pwm.c + * + * simple PWM based LED control + * + * Copyright 2009 LMC @ rock-chips (lmc@rock-chips.com) + * + * based on leds-gpio.c by Raphael Assenat + * + * 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 +#include +#include +#include +#include + +#if 0 +#define DBG(x...) printk(KERN_INFO x) +#else +#define DBG(x...) +#endif + +#define pwm_write_reg(id, addr, val) __raw_writel(val, addr+(RK29_PWM_BASE+id*0x10)) +#define pwm_read_reg(id, addr) __raw_readl(addr+(RK29_PWM_BASE+id*0x10)) +#define LED_PWM_DIV PWM_DIV1024 /*for led the pwm per div 1024 on the register*/ +#define LED_PER_DIV 1000 +#define LED_PWM_FULL 255 + +static struct clk *pwm_clk; + +struct led_newton_pwm_data { + struct led_classdev cdev; + unsigned int pwm_id; + unsigned pwm_gpio; + char* pwm_iomux_name; + unsigned int pwm_iomux_pwm; + unsigned int pwm_iomux_gpio; + unsigned int freq;/**/ + unsigned int period;/*1-100*/ +}; + +static void led_newton_pwm_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct led_newton_pwm_data *led_dat = + container_of(led_cdev, struct led_newton_pwm_data, cdev); + unsigned int period = led_dat->period; + + DBG("Enter %s, brightness = %d,period =%d,freq =%d\n",__FUNCTION__,brightness,period, led_dat->freq); + + if (brightness == 0||period == 0) { + // iomux pwm to gpio + rk29_mux_api_set(led_dat->pwm_iomux_name, led_dat->pwm_iomux_gpio); + // set gpio to low level + gpio_set_value(led_dat->pwm_gpio,GPIO_LOW); + }else if(period == LED_PWM_FULL){ + // iomux pwm to gpio + rk29_mux_api_set(led_dat->pwm_iomux_name, led_dat->pwm_iomux_gpio); + // set gpio to high level + gpio_set_value(led_dat->pwm_gpio,GPIO_HIGH); + } else { + u32 divh,divTotal; + int id = led_dat->pwm_id; + unsigned long clkrate; + + clkrate = clk_get_rate(pwm_clk); + // iomux pwm + rk29_mux_api_set(led_dat->pwm_iomux_name, led_dat->pwm_iomux_pwm); + pwm_write_reg(id,PWM_REG_CTRL, LED_PWM_DIV|PWM_RESET); + divh = clkrate >> (1+(LED_PWM_DIV>>9));/*the register pre div*/ + divh = divh* led_dat->freq/LED_PER_DIV; + pwm_write_reg(id,PWM_REG_LRC,(divh == 0)?1:divh); + + divTotal =pwm_read_reg(id,PWM_REG_LRC); + divh = divTotal*period/LED_PWM_FULL; + pwm_write_reg(id, PWM_REG_HRC, divh?divh:1); + pwm_write_reg(id,PWM_REG_CNTR,0); + pwm_write_reg(id, PWM_REG_CTRL,pwm_read_reg(id,PWM_REG_CTRL)|LED_PWM_DIV|PWM_ENABLE|PWM_TimeEN); + } +} + +static ssize_t led_newton_freq_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct led_newton_pwm_data *led_dat = + container_of(led_cdev, struct led_newton_pwm_data, cdev); + + return sprintf(buf, "%u\n", led_dat->freq); +} + +static ssize_t led_newton_freq_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct led_newton_pwm_data *led_dat = + container_of(led_cdev, struct led_newton_pwm_data, cdev); + + ssize_t ret = -EINVAL; + char *after; + unsigned long state = simple_strtoul(buf, &after, 10); + size_t count = after - buf; + + if (*after && isspace(*after)) + count++; + + if (count == size) { + ret = count; + if(state) + led_dat->freq = state; + led_newton_pwm_set( led_cdev, led_cdev->brightness); + } + + return ret; +} + +static ssize_t led_newton_period_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct led_newton_pwm_data *led_dat = + container_of(led_cdev, struct led_newton_pwm_data, cdev); + + return sprintf(buf, "%u\n", led_dat->period); +} + +static ssize_t led_newton_period_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct led_newton_pwm_data *led_dat = + container_of(led_cdev, struct led_newton_pwm_data, cdev); + + ssize_t ret = -EINVAL; + char *after; + unsigned long state = simple_strtoul(buf, &after, 10); + size_t count = after - buf; + + if (*after && isspace(*after)) + count++; + + if (count == size) { + ret = count; + led_dat->period = state; + led_newton_pwm_set( led_cdev, led_cdev->brightness); + } + + return ret; +} + +static DEVICE_ATTR(freq, 0644, led_newton_freq_show, led_newton_freq_store); +static DEVICE_ATTR(period, 0644, led_newton_period_show, led_newton_period_store); + + +static int led_newton_pwm_probe(struct platform_device *pdev) +{ + struct led_newton_pwm_platform_data *pdata = pdev->dev.platform_data; + struct led_newton_pwm *cur_led; + struct led_newton_pwm_data *leds_data, *led_dat; + int i, ret = 0; + + if (!pdata) + return -EBUSY; + + leds_data = kzalloc(sizeof(struct led_newton_pwm_data) * pdata->num_leds, + GFP_KERNEL); + if (!leds_data) + return -ENOMEM; + + for (i = 0; i < pdata->num_leds; i++) { + cur_led = &pdata->leds[i]; + led_dat = &leds_data[i]; + + ret = gpio_request(cur_led->pwm_gpio,"pwm"); + + if (ret) { + dev_err(&pdev->dev,"failed to request pwm gpio\n"); + goto err; + } + + + if (cur_led->pwm_id >2) { + dev_err(&pdev->dev, "unable to request PWM %d\n", + cur_led->pwm_id); + goto err; + } + + led_dat->cdev.name = cur_led->name; + led_dat->pwm_id = cur_led->pwm_id; + led_dat->pwm_gpio = cur_led->pwm_gpio; + led_dat->pwm_iomux_name = cur_led->pwm_iomux_name; + led_dat->pwm_iomux_pwm = cur_led->pwm_iomux_pwm; + led_dat->pwm_iomux_gpio = cur_led->pwm_iomux_gpio; + if(cur_led->freq) + led_dat->freq = cur_led->freq; + else + led_dat->freq = 1; + led_dat->period = cur_led->period; + led_dat->cdev.brightness_set = led_newton_pwm_set; + led_dat->cdev.brightness = LED_FULL; + led_dat->cdev.flags = 0; + + ret = led_classdev_register(&pdev->dev, &led_dat->cdev); + if (ret < 0) { + goto err; + } + + ret = device_create_file(led_dat->cdev.dev, &dev_attr_freq); + if (ret) + goto err; + + ret = device_create_file(led_dat->cdev.dev, &dev_attr_period); + if(ret) + { + device_remove_file(led_dat->cdev.dev, &dev_attr_freq); + goto err; + } + } + + pwm_clk = clk_get(NULL, "pwm"); + clk_enable(pwm_clk); + platform_set_drvdata(pdev, leds_data); + + return 0; + +err: + if (i > 0) { + for (i = i - 1; i >= 0; i--) { + led_classdev_unregister(&leds_data[i].cdev); + gpio_free(leds_data[i].pwm_gpio); + } + } + + kfree(leds_data); + + return ret; +} + +static int __devexit led_newton_pwm_remove(struct platform_device *pdev) +{ + int i; + struct led_newton_pwm_platform_data *pdata = pdev->dev.platform_data; + struct led_newton_pwm_data *leds_data; + + leds_data = platform_get_drvdata(pdev); + + for (i = 0; i < pdata->num_leds; i++) { + led_classdev_unregister(&leds_data[i].cdev); + gpio_free(leds_data[i].pwm_gpio); + } + + kfree(leds_data); + + return 0; +} +int led_newton_pwm_suspend(struct platform_device *pdev, pm_message_t state) +{ + return 1; +} + +int led_newton_pwm_resume(struct platform_device *pdev) +{ + return 1; +} + +static struct platform_driver led_newton_pwm_driver = { + .probe = led_newton_pwm_probe, + .remove = __devexit_p(led_newton_pwm_remove), + .driver = { + .name = "leds_newton_pwm", + .owner = THIS_MODULE, + }, +}; + +static int __init led_newton_pwm_init(void) +{ + return platform_driver_register(&led_newton_pwm_driver); +} + +static void __exit led_newton_pwm_exit(void) +{ + platform_driver_unregister(&led_newton_pwm_driver); +} + +module_init(led_newton_pwm_init); +module_exit(led_newton_pwm_exit); + +MODULE_AUTHOR("Luotao Fu "); +MODULE_DESCRIPTION("PWM LED driver for PXA"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:leds-pwm"); +