diff options
Diffstat (limited to 'drivers')
82 files changed, 2423 insertions, 297 deletions
diff --git a/drivers/Makefile b/drivers/Makefile index 14cf9077bb2b..f3ebb30f1b7f 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_REGULATOR) += regulator/ # char/ comes before serial/ etc so that the VT console is the boot-time # default. +obj-y += tty/ obj-y += char/ # gpu/ comes after char for AGP vs DRM startup diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index 767107cce982..3951020e494a 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -4363,9 +4363,9 @@ out_unreg_blkdev: out_put_disk: while (dr--) { del_timer(&motor_off_timer[dr]); - put_disk(disks[dr]); if (disks[dr]->queue) blk_cleanup_queue(disks[dr]->queue); + put_disk(disks[dr]); } return err; } @@ -4573,8 +4573,8 @@ static void __exit floppy_module_exit(void) device_remove_file(&floppy_device[drive].dev, &dev_attr_cmos); platform_device_unregister(&floppy_device[drive]); } - put_disk(disks[drive]); blk_cleanup_queue(disks[drive]->queue); + put_disk(disks[drive]); } del_timer_sync(&fd_timeout); diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 3a9c01416839..ba53ec956c95 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -2,24 +2,10 @@ # Makefile for the kernel character device drivers. # -# -# This file contains the font map for the default (hardware) font -# -FONTMAPFILE = cp437.uni - -obj-y += mem.o random.o tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o tty_buffer.o tty_port.o - -obj-y += tty_mutex.o -obj-$(CONFIG_LEGACY_PTYS) += pty.o -obj-$(CONFIG_UNIX98_PTYS) += pty.o +obj-y += mem.o random.o obj-$(CONFIG_TTY_PRINTK) += ttyprintk.o obj-y += misc.o -obj-$(CONFIG_VT) += vt_ioctl.o vc_screen.o selection.o keyboard.o obj-$(CONFIG_BFIN_JTAG_COMM) += bfin_jtag_comm.o -obj-$(CONFIG_CONSOLE_TRANSLATIONS) += consolemap.o consolemap_deftbl.o -obj-$(CONFIG_HW_CONSOLE) += vt.o defkeymap.o -obj-$(CONFIG_AUDIT) += tty_audit.o -obj-$(CONFIG_MAGIC_SYSRQ) += sysrq.o obj-$(CONFIG_MVME147_SCC) += generic_serial.o vme_scc.o obj-$(CONFIG_MVME162_SCC) += generic_serial.o vme_scc.o obj-$(CONFIG_BVME6000_SCC) += generic_serial.o vme_scc.o @@ -41,8 +27,6 @@ obj-$(CONFIG_ISI) += isicom.o obj-$(CONFIG_SYNCLINK) += synclink.o obj-$(CONFIG_SYNCLINKMP) += synclinkmp.o obj-$(CONFIG_SYNCLINK_GT) += synclink_gt.o -obj-$(CONFIG_N_HDLC) += n_hdlc.o -obj-$(CONFIG_N_GSM) += n_gsm.o obj-$(CONFIG_AMIGA_BUILTIN_SERIAL) += amiserial.o obj-$(CONFIG_SX) += sx.o generic_serial.o obj-$(CONFIG_RIO) += rio/ generic_serial.o @@ -74,7 +58,6 @@ obj-$(CONFIG_PRINTER) += lp.o obj-$(CONFIG_APM_EMULATION) += apm-emulation.o obj-$(CONFIG_DTLK) += dtlk.o -obj-$(CONFIG_R3964) += n_r3964.o obj-$(CONFIG_APPLICOM) += applicom.o obj-$(CONFIG_SONYPI) += sonypi.o obj-$(CONFIG_RTC) += rtc.o @@ -115,28 +98,3 @@ obj-$(CONFIG_RAMOOPS) += ramoops.o obj-$(CONFIG_JS_RTC) += js-rtc.o js-rtc-y = rtc.o - -# Files generated that shall be removed upon make clean -clean-files := consolemap_deftbl.c defkeymap.c - -quiet_cmd_conmk = CONMK $@ - cmd_conmk = scripts/conmakehash $< > $@ - -$(obj)/consolemap_deftbl.c: $(src)/$(FONTMAPFILE) - $(call cmd,conmk) - -$(obj)/defkeymap.o: $(obj)/defkeymap.c - -# Uncomment if you're changing the keymap and have an appropriate -# loadkeys version for the map. By default, we'll use the shipped -# versions. -# GENERATE_KEYMAP := 1 - -ifdef GENERATE_KEYMAP - -$(obj)/defkeymap.c: $(obj)/%.c: $(src)/%.map - loadkeys --mktable $< > $@.tmp - sed -e 's/^static *//' $@.tmp > $@ - rm $@.tmp - -endif diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index a44611652282..d68d3aa1814b 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -616,13 +616,9 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev) /* get hold of clock */ p->clk = clk_get(&p->pdev->dev, "cmt_fck"); if (IS_ERR(p->clk)) { - dev_warn(&p->pdev->dev, "using deprecated clock lookup\n"); - p->clk = clk_get(&p->pdev->dev, cfg->clk); - if (IS_ERR(p->clk)) { - dev_err(&p->pdev->dev, "cannot get clock\n"); - ret = PTR_ERR(p->clk); - goto err1; - } + dev_err(&p->pdev->dev, "cannot get clock\n"); + ret = PTR_ERR(p->clk); + goto err1; } if (resource_size(res) == 6) { diff --git a/drivers/clocksource/sh_mtu2.c b/drivers/clocksource/sh_mtu2.c index ef7a5be8a09f..40630cb98237 100644 --- a/drivers/clocksource/sh_mtu2.c +++ b/drivers/clocksource/sh_mtu2.c @@ -287,13 +287,9 @@ static int sh_mtu2_setup(struct sh_mtu2_priv *p, struct platform_device *pdev) /* get hold of clock */ p->clk = clk_get(&p->pdev->dev, "mtu2_fck"); if (IS_ERR(p->clk)) { - dev_warn(&p->pdev->dev, "using deprecated clock lookup\n"); - p->clk = clk_get(&p->pdev->dev, cfg->clk); - if (IS_ERR(p->clk)) { - dev_err(&p->pdev->dev, "cannot get clock\n"); - ret = PTR_ERR(p->clk); - goto err1; - } + dev_err(&p->pdev->dev, "cannot get clock\n"); + ret = PTR_ERR(p->clk); + goto err1; } return sh_mtu2_register(p, (char *)dev_name(&p->pdev->dev), diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c index de715901b82a..36aba9923060 100644 --- a/drivers/clocksource/sh_tmu.c +++ b/drivers/clocksource/sh_tmu.c @@ -393,13 +393,9 @@ static int sh_tmu_setup(struct sh_tmu_priv *p, struct platform_device *pdev) /* get hold of clock */ p->clk = clk_get(&p->pdev->dev, "tmu_fck"); if (IS_ERR(p->clk)) { - dev_warn(&p->pdev->dev, "using deprecated clock lookup\n"); - p->clk = clk_get(&p->pdev->dev, cfg->clk); - if (IS_ERR(p->clk)) { - dev_err(&p->pdev->dev, "cannot get clock\n"); - ret = PTR_ERR(p->clk); - goto err1; - } + dev_err(&p->pdev->dev, "cannot get clock\n"); + ret = PTR_ERR(p->clk); + goto err1; } return sh_tmu_register(p, (char *)dev_name(&p->pdev->dev), diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 9dcb17d51aee..84eb607d6c03 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -577,17 +577,11 @@ static int ohci_update_phy_reg(struct fw_card *card, int addr, return ret; } -static int ar_context_add_page(struct ar_context *ctx) +static void ar_context_link_page(struct ar_context *ctx, + struct ar_buffer *ab, dma_addr_t ab_bus) { - struct device *dev = ctx->ohci->card.device; - struct ar_buffer *ab; - dma_addr_t uninitialized_var(ab_bus); size_t offset; - ab = dma_alloc_coherent(dev, PAGE_SIZE, &ab_bus, GFP_ATOMIC); - if (ab == NULL) - return -ENOMEM; - ab->next = NULL; memset(&ab->descriptor, 0, sizeof(ab->descriptor)); ab->descriptor.control = cpu_to_le16(DESCRIPTOR_INPUT_MORE | @@ -606,6 +600,19 @@ static int ar_context_add_page(struct ar_context *ctx) reg_write(ctx->ohci, CONTROL_SET(ctx->regs), CONTEXT_WAKE); flush_writes(ctx->ohci); +} + +static int ar_context_add_page(struct ar_context *ctx) +{ + struct device *dev = ctx->ohci->card.device; + struct ar_buffer *ab; + dma_addr_t uninitialized_var(ab_bus); + + ab = dma_alloc_coherent(dev, PAGE_SIZE, &ab_bus, GFP_ATOMIC); + if (ab == NULL) + return -ENOMEM; + + ar_context_link_page(ctx, ab, ab_bus); return 0; } @@ -730,16 +737,17 @@ static __le32 *handle_ar_packet(struct ar_context *ctx, __le32 *buffer) static void ar_context_tasklet(unsigned long data) { struct ar_context *ctx = (struct ar_context *)data; - struct fw_ohci *ohci = ctx->ohci; struct ar_buffer *ab; struct descriptor *d; void *buffer, *end; + __le16 res_count; ab = ctx->current_buffer; d = &ab->descriptor; - if (d->res_count == 0) { - size_t size, rest, offset; + res_count = ACCESS_ONCE(d->res_count); + if (res_count == 0) { + size_t size, size2, rest, pktsize, size3, offset; dma_addr_t start_bus; void *start; @@ -750,29 +758,63 @@ static void ar_context_tasklet(unsigned long data) */ offset = offsetof(struct ar_buffer, data); - start = buffer = ab; + start = ab; start_bus = le32_to_cpu(ab->descriptor.data_address) - offset; + buffer = ab->data; ab = ab->next; d = &ab->descriptor; - size = buffer + PAGE_SIZE - ctx->pointer; + size = start + PAGE_SIZE - ctx->pointer; + /* valid buffer data in the next page */ rest = le16_to_cpu(d->req_count) - le16_to_cpu(d->res_count); + /* what actually fits in this page */ + size2 = min(rest, (size_t)PAGE_SIZE - offset - size); memmove(buffer, ctx->pointer, size); - memcpy(buffer + size, ab->data, rest); - ctx->current_buffer = ab; - ctx->pointer = (void *) ab->data + rest; - end = buffer + size + rest; + memcpy(buffer + size, ab->data, size2); + + while (size > 0) { + void *next = handle_ar_packet(ctx, buffer); + pktsize = next - buffer; + if (pktsize >= size) { + /* + * We have handled all the data that was + * originally in this page, so we can now + * continue in the next page. + */ + buffer = next; + break; + } + /* move the next packet to the start of the buffer */ + memmove(buffer, next, size + size2 - pktsize); + size -= pktsize; + /* fill up this page again */ + size3 = min(rest - size2, + (size_t)PAGE_SIZE - offset - size - size2); + memcpy(buffer + size + size2, + (void *) ab->data + size2, size3); + size2 += size3; + } - while (buffer < end) - buffer = handle_ar_packet(ctx, buffer); + if (rest > 0) { + /* handle the packets that are fully in the next page */ + buffer = (void *) ab->data + + (buffer - (start + offset + size)); + end = (void *) ab->data + rest; + + while (buffer < end) + buffer = handle_ar_packet(ctx, buffer); - dma_free_coherent(ohci->card.device, PAGE_SIZE, - start, start_bus); - ar_context_add_page(ctx); + ctx->current_buffer = ab; + ctx->pointer = end; + + ar_context_link_page(ctx, start, start_bus); + } else { + ctx->pointer = start + PAGE_SIZE; + } } else { buffer = ctx->pointer; ctx->pointer = end = - (void *) ab + PAGE_SIZE - le16_to_cpu(d->res_count); + (void *) ab + PAGE_SIZE - le16_to_cpu(res_count); while (buffer < end) buffer = handle_ar_packet(ctx, buffer); diff --git a/drivers/hwmon/ltc4261.c b/drivers/hwmon/ltc4261.c index 267626178678..4b50601027d3 100644 --- a/drivers/hwmon/ltc4261.c +++ b/drivers/hwmon/ltc4261.c @@ -82,7 +82,7 @@ static struct ltc4261_data *ltc4261_update_device(struct device *dev) val = i2c_smbus_read_byte_data(client, i); if (unlikely(val < 0)) { dev_dbg(dev, - "Failed to read ADC value: error %d", + "Failed to read ADC value: error %d\n", val); ret = ERR_PTR(val); goto abort; @@ -230,8 +230,7 @@ static int ltc4261_probe(struct i2c_client *client, return -ENODEV; if (i2c_smbus_read_byte_data(client, LTC4261_STATUS) < 0) { - dev_err(&client->dev, "Failed to read register %d:%02x:%02x\n", - adapter->id, client->addr, LTC4261_STATUS); + dev_err(&client->dev, "Failed to read status register\n"); return -ENODEV; } diff --git a/drivers/isdn/hisax/isar.c b/drivers/isdn/hisax/isar.c index 40b914bded8c..2e72227bd071 100644 --- a/drivers/isdn/hisax/isar.c +++ b/drivers/isdn/hisax/isar.c @@ -1427,8 +1427,8 @@ modeisar(struct BCState *bcs, int mode, int bc) &bcs->hw.isar.reg->Flags)) bcs->hw.isar.dpath = 1; else { - printk(KERN_WARNING"isar modeisar analog funktions only with DP1\n"); - debugl1(cs, "isar modeisar analog funktions only with DP1"); + printk(KERN_WARNING"isar modeisar analog functions only with DP1\n"); + debugl1(cs, "isar modeisar analog functions only with DP1"); return(1); } break; diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index cc2a88d5192f..77b8fd20cd90 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -10,7 +10,7 @@ menuconfig NEW_LEDS if NEW_LEDS config LEDS_CLASS - tristate "LED Class Support" + bool "LED Class Support" help This option enables the led sysfs class in /sys/class/leds. You'll need this to do anything useful with LEDs. If unsure, say N. @@ -176,6 +176,24 @@ config LEDS_LP3944 To compile this driver as a module, choose M here: the module will be called leds-lp3944. +config LEDS_LP5521 + tristate "LED Support for N.S. LP5521 LED driver chip" + depends on LEDS_CLASS && I2C + help + If you say yes here you get support for the National Semiconductor + LP5521 LED driver. It is 3 channel chip with programmable engines. + Driver provides direct control via LED class and interface for + programming the engines. + +config LEDS_LP5523 + tristate "LED Support for N.S. LP5523 LED driver chip" + depends on LEDS_CLASS && I2C + help + If you say yes here you get support for the National Semiconductor + LP5523 LED driver. It is 9 channel chip with programmable engines. + Driver provides direct control via LED class and interface for + programming the engines. + config LEDS_CLEVO_MAIL tristate "Mail LED on Clevo notebook" depends on X86 && SERIO_I8042 && DMI diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 9c96db40ef6d..aae6989ff6b6 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -23,6 +23,8 @@ obj-$(CONFIG_LEDS_SUNFIRE) += leds-sunfire.o obj-$(CONFIG_LEDS_PCA9532) += leds-pca9532.o obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o obj-$(CONFIG_LEDS_LP3944) += leds-lp3944.o +obj-$(CONFIG_LEDS_LP5521) += leds-lp5521.o +obj-$(CONFIG_LEDS_LP5523) += leds-lp5523.o obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o obj-$(CONFIG_LEDS_HP6XX) += leds-hp6xx.o obj-$(CONFIG_LEDS_FSG) += leds-fsg.o diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index 260660076507..211e21f34bd5 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -81,6 +81,79 @@ static struct device_attribute led_class_attrs[] = { __ATTR_NULL, }; +static void led_timer_function(unsigned long data) +{ + struct led_classdev *led_cdev = (void *)data; + unsigned long brightness; + unsigned long delay; + + if (!led_cdev->blink_delay_on || !led_cdev->blink_delay_off) { + led_set_brightness(led_cdev, LED_OFF); + return; + } + + brightness = led_get_brightness(led_cdev); + if (!brightness) { + /* Time to switch the LED on. */ + brightness = led_cdev->blink_brightness; + delay = led_cdev->blink_delay_on; + } else { + /* Store the current brightness value to be able + * to restore it when the delay_off period is over. + */ + led_cdev->blink_brightness = brightness; + brightness = LED_OFF; + delay = led_cdev->blink_delay_off; + } + + led_set_brightness(led_cdev, brightness); + + mod_timer(&led_cdev->blink_timer, jiffies + msecs_to_jiffies(delay)); +} + +static void led_stop_software_blink(struct led_classdev *led_cdev) +{ + /* deactivate previous settings */ + del_timer_sync(&led_cdev->blink_timer); + led_cdev->blink_delay_on = 0; + led_cdev->blink_delay_off = 0; +} + +static void led_set_software_blink(struct led_classdev *led_cdev, + unsigned long delay_on, + unsigned long delay_off) +{ + int current_brightness; + + current_brightness = led_get_brightness(led_cdev); + if (current_brightness) + led_cdev->blink_brightness = current_brightness; + if (!led_cdev->blink_brightness) + led_cdev->blink_brightness = led_cdev->max_brightness; + + if (delay_on == led_cdev->blink_delay_on && + delay_off == led_cdev->blink_delay_off) + return; + + led_stop_software_blink(led_cdev); + + led_cdev->blink_delay_on = delay_on; + led_cdev->blink_delay_off = delay_off; + + /* never on - don't blink */ + if (!delay_on) + return; + + /* never off - just set to brightness */ + if (!delay_off) { + led_set_brightness(led_cdev, led_cdev->blink_brightness); + return; + } + + mod_timer(&led_cdev->blink_timer, jiffies + 1); +} + + /** * led_classdev_suspend - suspend an led_classdev. * @led_cdev: the led_classdev to suspend. @@ -148,6 +221,10 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) led_update_brightness(led_cdev); + init_timer(&led_cdev->blink_timer); + led_cdev->blink_timer.function = led_timer_function; + led_cdev->blink_timer.data = (unsigned long)led_cdev; + #ifdef CONFIG_LEDS_TRIGGERS led_trigger_set_default(led_cdev); #endif @@ -157,7 +234,6 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) return 0; } - EXPORT_SYMBOL_GPL(led_classdev_register); /** @@ -175,6 +251,9 @@ void led_classdev_unregister(struct led_classdev *led_cdev) up_write(&led_cdev->trigger_lock); #endif + /* Stop blinking */ + led_brightness_set(led_cdev, LED_OFF); + device_unregister(led_cdev->dev); down_write(&leds_list_lock); @@ -183,6 +262,30 @@ void led_classdev_unregister(struct led_classdev *led_cdev) } EXPORT_SYMBOL_GPL(led_classdev_unregister); +void led_blink_set(struct led_classdev *led_cdev, + unsigned long *delay_on, + unsigned long *delay_off) +{ + if (led_cdev->blink_set && + led_cdev->blink_set(led_cdev, delay_on, delay_off)) + return; + + /* blink with 1 Hz as default if nothing specified */ + if (!*delay_on && !*delay_off) + *delay_on = *delay_off = 500; + + led_set_software_blink(led_cdev, *delay_on, *delay_off); +} +EXPORT_SYMBOL(led_blink_set); + +void led_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + led_stop_software_blink(led_cdev); + led_cdev->brightness_set(led_cdev, brightness); +} +EXPORT_SYMBOL(led_brightness_set); + static int __init leds_init(void) { leds_class = class_create(THIS_MODULE, "leds"); diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c index f1c00db88b5e..c41eb6180c9c 100644 --- a/drivers/leds/led-triggers.c +++ b/drivers/leds/led-triggers.c @@ -113,7 +113,7 @@ void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trigger) if (led_cdev->trigger->deactivate) led_cdev->trigger->deactivate(led_cdev); led_cdev->trigger = NULL; - led_set_brightness(led_cdev, LED_OFF); + led_brightness_set(led_cdev, LED_OFF); } if (trigger) { write_lock_irqsave(&trigger->leddev_list_lock, flags); diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index ea57e05d08f3..4d9fa38d9ff6 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -316,7 +316,7 @@ static struct of_platform_driver of_gpio_leds_driver = { static int __init gpio_led_init(void) { - int ret; + int ret = 0; #ifdef CONFIG_LEDS_GPIO_PLATFORM ret = platform_driver_register(&gpio_led_driver); diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c new file mode 100644 index 000000000000..3782f31f06d2 --- /dev/null +++ b/drivers/leds/leds-lp5521.c @@ -0,0 +1,821 @@ +/* + * LP5521 LED chip driver. + * + * Copyright (C) 2010 Nokia Corporation + * + * Contact: Samu Onkalo <samu.p.onkalo@nokia.com> + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/mutex.h> +#include <linux/gpio.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/ctype.h> +#include <linux/spinlock.h> +#include <linux/wait.h> +#include <linux/leds.h> +#include <linux/leds-lp5521.h> +#include <linux/workqueue.h> +#include <linux/slab.h> + +#define LP5521_PROGRAM_LENGTH 32 /* in bytes */ + +#define LP5521_MAX_LEDS 3 /* Maximum number of LEDs */ +#define LP5521_MAX_ENGINES 3 /* Maximum number of engines */ + +#define LP5521_ENG_MASK_BASE 0x30 /* 00110000 */ +#define LP5521_ENG_STATUS_MASK 0x07 /* 00000111 */ + +#define LP5521_CMD_LOAD 0x15 /* 00010101 */ +#define LP5521_CMD_RUN 0x2a /* 00101010 */ +#define LP5521_CMD_DIRECT 0x3f /* 00111111 */ +#define LP5521_CMD_DISABLED 0x00 /* 00000000 */ + +/* Registers */ +#define LP5521_REG_ENABLE 0x00 +#define LP5521_REG_OP_MODE 0x01 +#define LP5521_REG_R_PWM 0x02 +#define LP5521_REG_G_PWM 0x03 +#define LP5521_REG_B_PWM 0x04 +#define LP5521_REG_R_CURRENT 0x05 +#define LP5521_REG_G_CURRENT 0x06 +#define LP5521_REG_B_CURRENT 0x07 +#define LP5521_REG_CONFIG 0x08 +#define LP5521_REG_R_CHANNEL_PC 0x09 +#define LP5521_REG_G_CHANNEL_PC 0x0A +#define LP5521_REG_B_CHANNEL_PC 0x0B +#define LP5521_REG_STATUS 0x0C +#define LP5521_REG_RESET 0x0D +#define LP5521_REG_GPO 0x0E +#define LP5521_REG_R_PROG_MEM 0x10 +#define LP5521_REG_G_PROG_MEM 0x30 +#define LP5521_REG_B_PROG_MEM 0x50 + +#define LP5521_PROG_MEM_BASE LP5521_REG_R_PROG_MEM +#define LP5521_PROG_MEM_SIZE 0x20 + +/* Base register to set LED current */ +#define LP5521_REG_LED_CURRENT_BASE LP5521_REG_R_CURRENT + +/* Base register to set the brightness */ +#define LP5521_REG_LED_PWM_BASE LP5521_REG_R_PWM + +/* Bits in ENABLE register */ +#define LP5521_MASTER_ENABLE 0x40 /* Chip master enable */ +#define LP5521_LOGARITHMIC_PWM 0x80 /* Logarithmic PWM adjustment */ +#define LP5521_EXEC_RUN 0x2A + +/* Bits in CONFIG register */ +#define LP5521_PWM_HF 0x40 /* PWM: 0 = 256Hz, 1 = 558Hz */ +#define LP5521_PWRSAVE_EN 0x20 /* 1 = Power save mode */ +#define LP5521_CP_MODE_OFF 0 /* Charge pump (CP) off */ +#define LP5521_CP_MODE_BYPASS 8 /* CP forced to bypass mode */ +#define LP5521_CP_MODE_1X5 0x10 /* CP forced to 1.5x mode */ +#define LP5521_CP_MODE_AUTO 0x18 /* Automatic mode selection */ +#define LP5521_R_TO_BATT 4 /* R out: 0 = CP, 1 = Vbat */ +#define LP5521_CLK_SRC_EXT 0 /* Ext-clk source (CLK_32K) */ +#define LP5521_CLK_INT 1 /* Internal clock */ +#define LP5521_CLK_AUTO 2 /* Automatic clock selection */ + +/* Status */ +#define LP5521_EXT_CLK_USED 0x08 + +struct lp5521_engine { + const struct attribute_group *attributes; + int id; + u8 mode; + u8 prog_page; + u8 engine_mask; +}; + +struct lp5521_led { + int id; + u8 chan_nr; + u8 led_current; + u8 max_current; + struct led_classdev cdev; + struct work_struct brightness_work; + u8 brightness; +}; + +struct lp5521_chip { + struct lp5521_platform_data *pdata; + struct mutex lock; /* Serialize control */ + struct i2c_client *client; + struct lp5521_engine engines[LP5521_MAX_ENGINES]; + struct lp5521_led leds[LP5521_MAX_LEDS]; + u8 num_channels; + u8 num_leds; +}; + +#define cdev_to_led(c) container_of(c, struct lp5521_led, cdev) +#define engine_to_lp5521(eng) container_of((eng), struct lp5521_chip, \ + engines[(eng)->id - 1]) +#define led_to_lp5521(led) container_of((led), struct lp5521_chip, \ + leds[(led)->id]) + +static void lp5521_led_brightness_work(struct work_struct *work); + +static inline int lp5521_write(struct i2c_client *client, u8 reg, u8 value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} + +static int lp5521_read(struct i2c_client *client, u8 reg, u8 *buf) +{ + s32 ret; + + ret = i2c_smbus_read_byte_data(client, reg); + if (ret < 0) + return -EIO; + + *buf = ret; + return 0; +} + +static int lp5521_set_engine_mode(struct lp5521_engine *engine, u8 mode) +{ + struct lp5521_chip *chip = engine_to_lp5521(engine); + struct i2c_client *client = chip->client; + int ret; + u8 engine_state; + + /* Only transition between RUN and DIRECT mode are handled here */ + if (mode == LP5521_CMD_LOAD) + return 0; + + if (mode == LP5521_CMD_DISABLED) + mode = LP5521_CMD_DIRECT; + + ret = lp5521_read(client, LP5521_REG_OP_MODE, &engine_state); + + /* set mode only for this engine */ + engine_state &= ~(engine->engine_mask); + mode &= engine->engine_mask; + engine_state |= mode; + ret |= lp5521_write(client, LP5521_REG_OP_MODE, engine_state); + + return ret; +} + +static int lp5521_load_program(struct lp5521_engine *eng, const u8 *pattern) +{ + struct lp5521_chip *chip = engine_to_lp5521(eng); + struct i2c_client *client = chip->client; + int ret; + int addr; + u8 mode; + + /* move current engine to direct mode and remember the state */ + ret = lp5521_set_engine_mode(eng, LP5521_CMD_DIRECT); + usleep_range(1000, 10000); + ret |= lp5521_read(client, LP5521_REG_OP_MODE, &mode); + + /* For loading, all the engines to load mode */ + lp5521_write(client, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT); + usleep_range(1000, 10000); + lp5521_write(client, LP5521_REG_OP_MODE, LP5521_CMD_LOAD); + usleep_range(1000, 10000); + + addr = LP5521_PROG_MEM_BASE + eng->prog_page * LP5521_PROG_MEM_SIZE; + i2c_smbus_write_i2c_block_data(client, + addr, + LP5521_PROG_MEM_SIZE, + pattern); + + ret |= lp5521_write(client, LP5521_REG_OP_MODE, mode); + return ret; +} + +static int lp5521_set_led_current(struct lp5521_chip *chip, int led, u8 curr) +{ + return lp5521_write(chip->client, + LP5521_REG_LED_CURRENT_BASE + chip->leds[led].chan_nr, + curr); +} + +static void lp5521_init_engine(struct lp5521_chip *chip, + const struct attribute_group *attr_group) +{ + int i; + for (i = 0; i < ARRAY_SIZE(chip->engines); i++) { + chip->engines[i].id = i + 1; + chip->engines[i].engine_mask = LP5521_ENG_MASK_BASE >> (i * 2); + chip->engines[i].prog_page = i; + chip->engines[i].attributes = &attr_group[i]; + } +} + +static int lp5521_configure(struct i2c_client *client, + const struct attribute_group *attr_group) +{ + struct lp5521_chip *chip = i2c_get_clientdata(client); + int ret; + + lp5521_init_engine(chip, attr_group); + + lp5521_write(client, LP5521_REG_RESET, 0xff); + + usleep_range(10000, 20000); + + /* Set all PWMs to direct control mode */ + ret = lp5521_write(client, LP5521_REG_OP_MODE, 0x3F); + + /* Enable auto-powersave, set charge pump to auto, red to battery */ + ret |= lp5521_write(client, LP5521_REG_CONFIG, + LP5521_PWRSAVE_EN | LP5521_CP_MODE_AUTO | LP5521_R_TO_BATT); + + /* Initialize all channels PWM to zero -> leds off */ + ret |= lp5521_write(client, LP5521_REG_R_PWM, 0); + ret |= lp5521_write(client, LP5521_REG_G_PWM, 0); + ret |= lp5521_write(client, LP5521_REG_B_PWM, 0); + + /* Set engines are set to run state when OP_MODE enables engines */ + ret |= lp5521_write(client, LP5521_REG_ENABLE, + LP5521_MASTER_ENABLE | LP5521_LOGARITHMIC_PWM | + LP5521_EXEC_RUN); + /* enable takes 500us */ + usleep_range(500, 20000); + + return ret; +} + +static int lp5521_run_selftest(struct lp5521_chip *chip, char *buf) +{ + int ret; + u8 status; + + ret = lp5521_read(chip->client, LP5521_REG_STATUS, &status); + if (ret < 0) + return ret; + + /* Check that ext clock is really in use if requested */ + if (chip->pdata && chip->pdata->clock_mode == LP5521_CLOCK_EXT) + if ((status & LP5521_EXT_CLK_USED) == 0) + return -EIO; + return 0; +} + +static void lp5521_set_brightness(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct lp5521_led *led = cdev_to_led(cdev); + led->brightness = (u8)brightness; + schedule_work(&led->brightness_work); +} + +static void lp5521_led_brightness_work(struct work_struct *work) +{ + struct lp5521_led *led = container_of(work, + struct lp5521_led, + brightness_work); + struct lp5521_chip *chip = led_to_lp5521(led); + struct i2c_client *client = chip->client; + + mutex_lock(&chip->lock); + lp5521_write(client, LP5521_REG_LED_PWM_BASE + led->chan_nr, + led->brightness); + mutex_unlock(&chip->lock); +} + +/* Detect the chip by setting its ENABLE register and reading it back. */ +static int lp5521_detect(struct i2c_client *client) +{ + int ret; + u8 buf; + + ret = lp5521_write(client, LP5521_REG_ENABLE, + LP5521_MASTER_ENABLE | LP5521_LOGARITHMIC_PWM); + if (ret) + return ret; + usleep_range(1000, 10000); + ret = lp5521_read(client, LP5521_REG_ENABLE, &buf); + if (ret) + return ret; + if (buf != (LP5521_MASTER_ENABLE | LP5521_LOGARITHMIC_PWM)) + return -ENODEV; + + return 0; +} + +/* Set engine mode and create appropriate sysfs attributes, if required. */ +static int lp5521_set_mode(struct lp5521_engine *engine, u8 mode) +{ + struct lp5521_chip *chip = engine_to_lp5521(engine); + struct i2c_client *client = chip->client; + struct device *dev = &client->dev; + int ret = 0; + + /* if in that mode already do nothing, except for run */ + if (mode == engine->mode && mode != LP5521_CMD_RUN) + return 0; + + if (mode == LP5521_CMD_RUN) { + ret = lp5521_set_engine_mode(engine, LP5521_CMD_RUN); + } else if (mode == LP5521_CMD_LOAD) { + lp5521_set_engine_mode(engine, LP5521_CMD_DISABLED); + lp5521_set_engine_mode(engine, LP5521_CMD_LOAD); + + ret = sysfs_create_group(&dev->kobj, engine->attributes); + if (ret) + return ret; + } else if (mode == LP5521_CMD_DISABLED) { + lp5521_set_engine_mode(engine, LP5521_CMD_DISABLED); + } + + /* remove load attribute from sysfs if not in load mode */ + if (engine->mode == LP5521_CMD_LOAD && mode != LP5521_CMD_LOAD) + sysfs_remove_group(&dev->kobj, engine->attributes); + + engine->mode = mode; + + return ret; +} + +static int lp5521_do_store_load(struct lp5521_engine *engine, + const char *buf, size_t len) +{ + struct lp5521_chip *chip = engine_to_lp5521(engine); + struct i2c_client *client = chip->client; + int ret, nrchars, offset = 0, i = 0; + char c[3]; + unsigned cmd; + u8 pattern[LP5521_PROGRAM_LENGTH] = {0}; + + while ((offset < len - 1) && (i < LP5521_PROGRAM_LENGTH)) { + /* separate sscanfs because length is working only for %s */ + ret = sscanf(buf + offset, "%2s%n ", c, &nrchars); + ret = sscanf(c, "%2x", &cmd); + if (ret != 1) + goto fail; + pattern[i] = (u8)cmd; + + offset += nrchars; + i++; + } + + /* Each instruction is 16bit long. Check that length is even */ + if (i % 2) + goto fail; + + mutex_lock(&chip->lock); + ret = lp5521_load_program(engine, pattern); + mutex_unlock(&chip->lock); + + if (ret) { + dev_err(&client->dev, "failed loading pattern\n"); + return ret; + } + + return len; +fail: + dev_err(&client->dev, "wrong pattern format\n"); + return -EINVAL; +} + +static ssize_t store_engine_load(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lp5521_chip *chip = i2c_get_clientdata(client); + return lp5521_do_store_load(&chip->engines[nr - 1], buf, len); +} + +#define store_load(nr) \ +static ssize_t store_engine##nr##_load(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t len) \ +{ \ + return store_engine_load(dev, attr, buf, len, nr); \ +} +store_load(1) +store_load(2) +store_load(3) + +static ssize_t show_engine_mode(struct device *dev, + struct device_attribute *attr, + char *buf, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lp5521_chip *chip = i2c_get_clientdata(client); + switch (chip->engines[nr - 1].mode) { + case LP5521_CMD_RUN: + return sprintf(buf, "run\n"); + case LP5521_CMD_LOAD: + return sprintf(buf, "load\n"); + case LP5521_CMD_DISABLED: + return sprintf(buf, "disabled\n"); + default: + return sprintf(buf, "disabled\n"); + } +} + +#define show_mode(nr) \ +static ssize_t show_engine##nr##_mode(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + return show_engine_mode(dev, attr, buf, nr); \ +} +show_mode(1) +show_mode(2) +show_mode(3) + +static ssize_t store_engine_mode(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lp5521_chip *chip = i2c_get_clientdata(client); + struct lp5521_engine *engine = &chip->engines[nr - 1]; + mutex_lock(&chip->lock); + + if (!strncmp(buf, "run", 3)) + lp5521_set_mode(engine, LP5521_CMD_RUN); + else if (!strncmp(buf, "load", 4)) + lp5521_set_mode(engine, LP5521_CMD_LOAD); + else if (!strncmp(buf, "disabled", 8)) + lp5521_set_mode(engine, LP5521_CMD_DISABLED); + + mutex_unlock(&chip->lock); + return len; +} + +#define store_mode(nr) \ +static ssize_t store_engine##nr##_mode(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t len) \ +{ \ + return store_engine_mode(dev, attr, buf, len, nr); \ +} +store_mode(1) +store_mode(2) +store_mode(3) + +static ssize_t show_max_current(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct lp5521_led *led = cdev_to_led(led_cdev); + + return sprintf(buf, "%d\n", led->max_current); +} + +static ssize_t show_current(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct lp5521_led *led = cdev_to_led(led_cdev); + + return sprintf(buf, "%d\n", led->led_current); +} + +static ssize_t store_current(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct lp5521_led *led = cdev_to_led(led_cdev); + struct lp5521_chip *chip = led_to_lp5521(led); + ssize_t ret; + unsigned long curr; + + if (strict_strtoul(buf, 0, &curr)) + return -EINVAL; + + if (curr > led->max_current) + return -EINVAL; + + mutex_lock(&chip->lock); + ret = lp5521_set_led_current(chip, led->id, curr); + mutex_unlock(&chip->lock); + + if (ret < 0) + return ret; + + led->led_current = (u8)curr; + + return len; +} + +static ssize_t lp5521_selftest(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lp5521_chip *chip = i2c_get_clientdata(client); + int ret; + + mutex_lock(&chip->lock); + ret = lp5521_run_selftest(chip, buf); + mutex_unlock(&chip->lock); + return sprintf(buf, "%s\n", ret ? "FAIL" : "OK"); +} + +/* led class device attributes */ +static DEVICE_ATTR(led_current, S_IRUGO | S_IWUGO, show_current, store_current); +static DEVICE_ATTR(max_current, S_IRUGO , show_max_current, NULL); + +static struct attribute *lp5521_led_attributes[] = { + &dev_attr_led_current.attr, + &dev_attr_max_current.attr, + NULL, +}; + +static struct attribute_group lp5521_led_attribute_group = { + .attrs = lp5521_led_attributes +}; + +/* device attributes */ +static DEVICE_ATTR(engine1_mode, S_IRUGO | S_IWUGO, + show_engine1_mode, store_engine1_mode); +static DEVICE_ATTR(engine2_mode, S_IRUGO | S_IWUGO, + show_engine2_mode, store_engine2_mode); +static DEVICE_ATTR(engine3_mode, S_IRUGO | S_IWUGO, + show_engine3_mode, store_engine3_mode); +static DEVICE_ATTR(engine1_load, S_IWUGO, NULL, store_engine1_load); +static DEVICE_ATTR(engine2_load, S_IWUGO, NULL, store_engine2_load); +static DEVICE_ATTR(engine3_load, S_IWUGO, NULL, store_engine3_load); +static DEVICE_ATTR(selftest, S_IRUGO, lp5521_selftest, NULL); + +static struct attribute *lp5521_attributes[] = { + &dev_attr_engine1_mode.attr, + &dev_attr_engine2_mode.attr, + &dev_attr_engine3_mode.attr, + &dev_attr_selftest.attr, + NULL +}; + +static struct attribute *lp5521_engine1_attributes[] = { + &dev_attr_engine1_load.attr, + NULL +}; + +static struct attribute *lp5521_engine2_attributes[] = { + &dev_attr_engine2_load.attr, + NULL +}; + +static struct attribute *lp5521_engine3_attributes[] = { + &dev_attr_engine3_load.attr, + NULL +}; + +static const struct attribute_group lp5521_group = { + .attrs = lp5521_attributes, +}; + +static const struct attribute_group lp5521_engine_group[] = { + {.attrs = lp5521_engine1_attributes }, + {.attrs = lp5521_engine2_attributes }, + {.attrs = lp5521_engine3_attributes }, +}; + +static int lp5521_register_sysfs(struct i2c_client *client) +{ + struct device *dev = &client->dev; + return sysfs_create_group(&dev->kobj, &lp5521_group); +} + +static void lp5521_unregister_sysfs(struct i2c_client *client) +{ + struct lp5521_chip *chip = i2c_get_clientdata(client); + struct device *dev = &client->dev; + int i; + + sysfs_remove_group(&dev->kobj, &lp5521_group); + + for (i = 0; i < ARRAY_SIZE(chip->engines); i++) { + if (chip->engines[i].mode == LP5521_CMD_LOAD) + sysfs_remove_group(&dev->kobj, + chip->engines[i].attributes); + } + + for (i = 0; i < chip->num_leds; i++) + sysfs_remove_group(&chip->leds[i].cdev.dev->kobj, + &lp5521_led_attribute_group); +} + +static int __init lp5521_init_led(struct lp5521_led *led, + struct i2c_client *client, + int chan, struct lp5521_platform_data *pdata) +{ + struct device *dev = &client->dev; + char name[32]; + int res; + + if (chan >= LP5521_MAX_LEDS) + return -EINVAL; + + if (pdata->led_config[chan].led_current == 0) + return 0; + + led->led_current = pdata->led_config[chan].led_current; + led->max_current = pdata->led_config[chan].max_current; + led->chan_nr = pdata->led_config[chan].chan_nr; + + if (led->chan_nr >= LP5521_MAX_LEDS) { + dev_err(dev, "Use channel numbers between 0 and %d\n", + LP5521_MAX_LEDS - 1); + return -EINVAL; + } + + snprintf(name, sizeof(name), "%s:channel%d", client->name, chan); + led->cdev.brightness_set = lp5521_set_brightness; + led->cdev.name = name; + res = led_classdev_register(dev, &led->cdev); + if (res < 0) { + dev_err(dev, "couldn't register led on channel %d\n", chan); + return res; + } + + res = sysfs_create_group(&led->cdev.dev->kobj, + &lp5521_led_attribute_group); + if (res < 0) { + dev_err(dev, "couldn't register current attribute\n"); + led_classdev_unregister(&led->cdev); + return res; + } + return 0; +} + +static int lp5521_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct lp5521_chip *chip; + struct lp5521_platform_data *pdata; + int ret, i, led; + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + i2c_set_clientdata(client, chip); + chip->client = client; + + pdata = client->dev.platform_data; + + if (!pdata) { + dev_err(&client->dev, "no platform data\n"); + ret = -EINVAL; + goto fail1; + } + + mutex_init(&chip->lock); + + chip->pdata = pdata; + + if (pdata->setup_resources) { + ret = pdata->setup_resources(); + if (ret < 0) + goto fail1; + } + + if (pdata->enable) { + pdata->enable(0); + usleep_range(1000, 10000); + pdata->enable(1); + usleep_range(1000, 10000); /* Spec says min 500us */ + } + + ret = lp5521_detect(client); + + if (ret) { + dev_err(&client->dev, "Chip not found\n"); + goto fail2; + } + + dev_info(&client->dev, "%s programmable led chip found\n", id->name); + + ret = lp5521_configure(client, lp5521_engine_group); + if (ret < 0) { + dev_err(&client->dev, "error configuring chip\n"); + goto fail2; + } + + /* Initialize leds */ + chip->num_channels = pdata->num_channels; + chip->num_leds = 0; + led = 0; + for (i = 0; i < pdata->num_channels; i++) { + /* Do not initialize channels that are not connected */ + if (pdata->led_config[i].led_current == 0) + continue; + + ret = lp5521_init_led(&chip->leds[led], client, i, pdata); + if (ret) { + dev_err(&client->dev, "error initializing leds\n"); + goto fail3; + } + chip->num_leds++; + + chip->leds[led].id = led; + /* Set initial LED current */ + lp5521_set_led_current(chip, led, + chip->leds[led].led_current); + + INIT_WORK(&(chip->leds[led].brightness_work), + lp5521_led_brightness_work); + + led++; + } + + ret = lp5521_register_sysfs(client); + if (ret) { + dev_err(&client->dev, "registering sysfs failed\n"); + goto fail3; + } + return ret; +fail3: + for (i = 0; i < chip->num_leds; i++) { + led_classdev_unregister(&chip->leds[i].cdev); + cancel_work_sync(&chip->leds[i].brightness_work); + } +fail2: + if (pdata->enable) + pdata->enable(0); + if (pdata->release_resources) + pdata->release_resources(); +fail1: + kfree(chip); + return ret; +} + +static int lp5521_remove(struct i2c_client *client) +{ + struct lp5521_chip *chip = i2c_get_clientdata(client); + int i; + + lp5521_unregister_sysfs(client); + + for (i = 0; i < chip->num_leds; i++) { + led_classdev_unregister(&chip->leds[i].cdev); + cancel_work_sync(&chip->leds[i].brightness_work); + } + + if (chip->pdata->enable) + chip->pdata->enable(0); + if (chip->pdata->release_resources) + chip->pdata->release_resources(); + kfree(chip); + return 0; +} + +static const struct i2c_device_id lp5521_id[] = { + { "lp5521", 0 }, /* Three channel chip */ + { } +}; +MODULE_DEVICE_TABLE(i2c, lp5521_id); + +static struct i2c_driver lp5521_driver = { + .driver = { + .name = "lp5521", + }, + .probe = lp5521_probe, + .remove = lp5521_remove, + .id_table = lp5521_id, +}; + +static int __init lp5521_init(void) +{ + int ret; + + ret = i2c_add_driver(&lp5521_driver); + + if (ret < 0) + printk(KERN_ALERT "Adding lp5521 driver failed\n"); + + return ret; +} + +static void __exit lp5521_exit(void) +{ + i2c_del_driver(&lp5521_driver); +} + +module_init(lp5521_init); +module_exit(lp5521_exit); + +MODULE_AUTHOR("Mathias Nyman, Yuri Zaporozhets, Samu Onkalo"); +MODULE_DESCRIPTION("LP5521 LED engine"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c new file mode 100644 index 000000000000..1e11fcc08b28 --- /dev/null +++ b/drivers/leds/leds-lp5523.c @@ -0,0 +1,1065 @@ +/* + * lp5523.c - LP5523 LED Driver + * + * Copyright (C) 2010 Nokia Corporation + * + * Contact: Samu Onkalo <samu.p.onkalo@nokia.com> + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/mutex.h> +#include <linux/gpio.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/ctype.h> +#include <linux/spinlock.h> +#include <linux/wait.h> +#include <linux/leds.h> +#include <linux/leds-lp5523.h> +#include <linux/workqueue.h> +#include <linux/slab.h> + +#define LP5523_REG_ENABLE 0x00 +#define LP5523_REG_OP_MODE 0x01 +#define LP5523_REG_RATIOMETRIC_MSB 0x02 +#define LP5523_REG_RATIOMETRIC_LSB 0x03 +#define LP5523_REG_ENABLE_LEDS_MSB 0x04 +#define LP5523_REG_ENABLE_LEDS_LSB 0x05 +#define LP5523_REG_LED_CNTRL_BASE 0x06 +#define LP5523_REG_LED_PWM_BASE 0x16 +#define LP5523_REG_LED_CURRENT_BASE 0x26 +#define LP5523_REG_CONFIG 0x36 +#define LP5523_REG_CHANNEL1_PC 0x37 +#define LP5523_REG_CHANNEL2_PC 0x38 +#define LP5523_REG_CHANNEL3_PC 0x39 +#define LP5523_REG_STATUS 0x3a +#define LP5523_REG_GPO 0x3b +#define LP5523_REG_VARIABLE 0x3c +#define LP5523_REG_RESET 0x3d +#define LP5523_REG_TEMP_CTRL 0x3e +#define LP5523_REG_TEMP_READ 0x3f +#define LP5523_REG_TEMP_WRITE 0x40 +#define LP5523_REG_LED_TEST_CTRL 0x41 +#define LP5523_REG_LED_TEST_ADC 0x42 +#define LP5523_REG_ENG1_VARIABLE 0x45 +#define LP5523_REG_ENG2_VARIABLE 0x46 +#define LP5523_REG_ENG3_VARIABLE 0x47 +#define LP5523_REG_MASTER_FADER1 0x48 +#define LP5523_REG_MASTER_FADER2 0x49 +#define LP5523_REG_MASTER_FADER3 0x4a +#define LP5523_REG_CH1_PROG_START 0x4c +#define LP5523_REG_CH2_PROG_START 0x4d +#define LP5523_REG_CH3_PROG_START 0x4e +#define LP5523_REG_PROG_PAGE_SEL 0x4f +#define LP5523_REG_PROG_MEM 0x50 + +#define LP5523_CMD_LOAD 0x15 /* 00010101 */ +#define LP5523_CMD_RUN 0x2a /* 00101010 */ +#define LP5523_CMD_DISABLED 0x00 /* 00000000 */ + +#define LP5523_ENABLE 0x40 +#define LP5523_AUTO_INC 0x40 +#define LP5523_PWR_SAVE 0x20 +#define LP5523_PWM_PWR_SAVE 0x04 +#define LP5523_CP_1 0x08 +#define LP5523_CP_1_5 0x10 +#define LP5523_CP_AUTO 0x18 +#define LP5523_INT_CLK 0x01 +#define LP5523_AUTO_CLK 0x02 +#define LP5523_EN_LEDTEST 0x80 +#define LP5523_LEDTEST_DONE 0x80 + +#define LP5523_DEFAULT_CURRENT 50 /* microAmps */ +#define LP5523_PROGRAM_LENGTH 32 /* in bytes */ +#define LP5523_PROGRAM_PAGES 6 +#define LP5523_ADC_SHORTCIRC_LIM 80 + +#define LP5523_LEDS 9 +#define LP5523_ENGINES 3 + +#define LP5523_ENG_MASK_BASE 0x30 /* 00110000 */ + +#define LP5523_ENG_STATUS_MASK 0x07 /* 00000111 */ + +#define LP5523_IRQ_FLAGS IRQF_TRIGGER_FALLING + +#define LP5523_EXT_CLK_USED 0x08 + +#define LED_ACTIVE(mux, led) (!!(mux & (0x0001 << led))) +#define SHIFT_MASK(id) (((id) - 1) * 2) + +struct lp5523_engine { + const struct attribute_group *attributes; + int id; + u8 mode; + u8 prog_page; + u8 mux_page; + u16 led_mux; + u8 engine_mask; +}; + +struct lp5523_led { + int id; + u8 chan_nr; + u8 led_current; + u8 max_current; + struct led_classdev cdev; + struct work_struct brightness_work; + u8 brightness; +}; + +struct lp5523_chip { + struct mutex lock; /* Serialize control */ + struct i2c_client *client; + struct lp5523_engine engines[LP5523_ENGINES]; + struct lp5523_led leds[LP5523_LEDS]; + struct lp5523_platform_data *pdata; + u8 num_channels; + u8 num_leds; +}; + +#define cdev_to_led(c) container_of(c, struct lp5523_led, cdev) + +static struct lp5523_chip *engine_to_lp5523(struct lp5523_engine *engine) +{ + return container_of(engine, struct lp5523_chip, + engines[engine->id - 1]); +} + +static struct lp5523_chip *led_to_lp5523(struct lp5523_led *led) +{ + return container_of(led, struct lp5523_chip, + leds[led->id]); +} + +static int lp5523_set_mode(struct lp5523_engine *engine, u8 mode); +static int lp5523_set_engine_mode(struct lp5523_engine *engine, u8 mode); +static int lp5523_load_program(struct lp5523_engine *engine, u8 *pattern); + +static void lp5523_led_brightness_work(struct work_struct *work); + +static int lp5523_write(struct i2c_client *client, u8 reg, u8 value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} + +static int lp5523_read(struct i2c_client *client, u8 reg, u8 *buf) +{ + s32 ret = i2c_smbus_read_byte_data(client, reg); + + if (ret < 0) + return -EIO; + + *buf = ret; + return 0; +} + +static int lp5523_detect(struct i2c_client *client) +{ + int ret; + u8 buf; + + ret = lp5523_write(client, LP5523_REG_ENABLE, 0x40); + if (ret) + return ret; + ret = lp5523_read(client, LP5523_REG_ENABLE, &buf); + if (ret) + return ret; + if (buf == 0x40) + return 0; + else + return -ENODEV; +} + +static int lp5523_configure(struct i2c_client *client) +{ + struct lp5523_chip *chip = i2c_get_clientdata(client); + int ret = 0; + u8 status; + + /* one pattern per engine setting led mux start and stop addresses */ + u8 pattern[][LP5523_PROGRAM_LENGTH] = { + { 0x9c, 0x30, 0x9c, 0xb0, 0x9d, 0x80, 0xd8, 0x00, 0}, + { 0x9c, 0x40, 0x9c, 0xc0, 0x9d, 0x80, 0xd8, 0x00, 0}, + { 0x9c, 0x50, 0x9c, 0xd0, 0x9d, 0x80, 0xd8, 0x00, 0}, + }; + + lp5523_write(client, LP5523_REG_RESET, 0xff); + + usleep_range(10000, 100000); + + ret |= lp5523_write(client, LP5523_REG_ENABLE, LP5523_ENABLE); + /* Chip startup time after reset is 500 us */ + usleep_range(1000, 10000); + + ret |= lp5523_write(client, LP5523_REG_CONFIG, + LP5523_AUTO_INC | LP5523_PWR_SAVE | + LP5523_CP_AUTO | LP5523_AUTO_CLK | + LP5523_PWM_PWR_SAVE); + + /* turn on all leds */ + ret |= lp5523_write(client, LP5523_REG_ENABLE_LEDS_MSB, 0x01); + ret |= lp5523_write(client, LP5523_REG_ENABLE_LEDS_LSB, 0xff); + + /* hardcode 32 bytes of memory for each engine from program memory */ + ret |= lp5523_write(client, LP5523_REG_CH1_PROG_START, 0x00); + ret |= lp5523_write(client, LP5523_REG_CH2_PROG_START, 0x10); + ret |= lp5523_write(client, LP5523_REG_CH3_PROG_START, 0x20); + + /* write led mux address space for each channel */ + ret |= lp5523_load_program(&chip->engines[0], pattern[0]); + ret |= lp5523_load_program(&chip->engines[1], pattern[1]); + ret |= lp5523_load_program(&chip->engines[2], pattern[2]); + + if (ret) { + dev_err(&client->dev, "could not load mux programs\n"); + return -1; + } + + /* set all engines exec state and mode to run 00101010 */ + ret |= lp5523_write(client, LP5523_REG_ENABLE, + (LP5523_CMD_RUN | LP5523_ENABLE)); + + ret |= lp5523_write(client, LP5523_REG_OP_MODE, LP5523_CMD_RUN); + + if (ret) { + dev_err(&client->dev, "could not start mux programs\n"); + return -1; + } + + /* Wait 3ms and check the engine status */ + usleep_range(3000, 20000); + lp5523_read(client, LP5523_REG_STATUS, &status); + status &= LP5523_ENG_STATUS_MASK; + + if (status == LP5523_ENG_STATUS_MASK) { + dev_dbg(&client->dev, "all engines configured\n"); + } else { + dev_info(&client->dev, "status == %x\n", status); + dev_err(&client->dev, "cound not configure LED engine\n"); + return -1; + } + + dev_info(&client->dev, "disabling engines\n"); + + ret |= lp5523_write(client, LP5523_REG_OP_MODE, LP5523_CMD_DISABLED); + + return ret; +} + +static int lp5523_set_engine_mode(struct lp5523_engine *engine, u8 mode) +{ + struct lp5523_chip *chip = engine_to_lp5523(engine); + struct i2c_client *client = chip->client; + int ret; + u8 engine_state; + + ret = lp5523_read(client, LP5523_REG_OP_MODE, &engine_state); + if (ret) + goto fail; + + engine_state &= ~(engine->engine_mask); + + /* set mode only for this engine */ + mode &= engine->engine_mask; + + engine_state |= mode; + + ret |= lp5523_write(client, LP5523_REG_OP_MODE, engine_state); +fail: + return ret; +} + +static int lp5523_load_mux(struct lp5523_engine *engine, u16 mux) +{ + struct lp5523_chip *chip = engine_to_lp5523(engine); + struct i2c_client *client = chip->client; + int ret = 0; + + ret |= lp5523_set_engine_mode(engine, LP5523_CMD_LOAD); + + ret |= lp5523_write(client, LP5523_REG_PROG_PAGE_SEL, engine->mux_page); + ret |= lp5523_write(client, LP5523_REG_PROG_MEM, + (u8)(mux >> 8)); + ret |= lp5523_write(client, LP5523_REG_PROG_MEM + 1, (u8)(mux)); + engine->led_mux = mux; + + return ret; +} + +static int lp5523_load_program(struct lp5523_engine *engine, u8 *pattern) +{ + struct lp5523_chip *chip = engine_to_lp5523(engine); + struct i2c_client *client = chip->client; + + int ret = 0; + + ret |= lp5523_set_engine_mode(engine, LP5523_CMD_LOAD); + + ret |= lp5523_write(client, LP5523_REG_PROG_PAGE_SEL, + engine->prog_page); + ret |= i2c_smbus_write_i2c_block_data(client, LP5523_REG_PROG_MEM, + LP5523_PROGRAM_LENGTH, pattern); + + return ret; +} + +static int lp5523_run_program(struct lp5523_engine *engine) +{ + struct lp5523_chip *chip = engine_to_lp5523(engine); + struct i2c_client *client = chip->client; + int ret; + + ret = lp5523_write(client, LP5523_REG_ENABLE, + LP5523_CMD_RUN | LP5523_ENABLE); + if (ret) + goto fail; + + ret = lp5523_set_engine_mode(engine, LP5523_CMD_RUN); +fail: + return ret; +} + +static int lp5523_mux_parse(const char *buf, u16 *mux, size_t len) +{ + int i; + u16 tmp_mux = 0; + len = len < LP5523_LEDS ? len : LP5523_LEDS; + for (i = 0; i < len; i++) { + switch (buf[i]) { + case '1': + tmp_mux |= (1 << i); + break; + case '0': + break; + case '\n': + i = len; + break; + default: + return -1; + } + } + *mux = tmp_mux; + + return 0; +} + +static void lp5523_mux_to_array(u16 led_mux, char *array) +{ + int i, pos = 0; + for (i = 0; i < LP5523_LEDS; i++) + pos += sprintf(array + pos, "%x", LED_ACTIVE(led_mux, i)); + + array[pos] = '\0'; +} + +/*--------------------------------------------------------------*/ +/* Sysfs interface */ +/*--------------------------------------------------------------*/ + +static ssize_t show_engine_leds(struct device *dev, + struct device_attribute *attr, + char *buf, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lp5523_chip *chip = i2c_get_clientdata(client); + char mux[LP5523_LEDS + 1]; + + lp5523_mux_to_array(chip->engines[nr - 1].led_mux, mux); + + return sprintf(buf, "%s\n", mux); +} + +#define show_leds(nr) \ +static ssize_t show_engine##nr##_leds(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + return show_engine_leds(dev, attr, buf, nr); \ +} +show_leds(1) +show_leds(2) +show_leds(3) + +static ssize_t store_engine_leds(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lp5523_chip *chip = i2c_get_clientdata(client); + u16 mux = 0; + + if (lp5523_mux_parse(buf, &mux, len)) + return -EINVAL; + + if (lp5523_load_mux(&chip->engines[nr - 1], mux)) + return -EINVAL; + + return len; +} + +#define store_leds(nr) \ +static ssize_t store_engine##nr##_leds(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t len) \ +{ \ + return store_engine_leds(dev, attr, buf, len, nr); \ +} +store_leds(1) +store_leds(2) +store_leds(3) + +static ssize_t lp5523_selftest(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lp5523_chip *chip = i2c_get_clientdata(client); + int i, ret, pos = 0; + int led = 0; + u8 status, adc, vdd; + + mutex_lock(&chip->lock); + + ret = lp5523_read(chip->client, LP5523_REG_STATUS, &status); + if (ret < 0) + goto fail; + + /* Check that ext clock is really in use if requested */ + if ((chip->pdata) && (chip->pdata->clock_mode == LP5523_CLOCK_EXT)) + if ((status & LP5523_EXT_CLK_USED) == 0) + goto fail; + + /* Measure VDD (i.e. VBAT) first (channel 16 corresponds to VDD) */ + lp5523_write(chip->client, LP5523_REG_LED_TEST_CTRL, + LP5523_EN_LEDTEST | 16); + usleep_range(3000, 10000); + ret = lp5523_read(chip->client, LP5523_REG_STATUS, &status); + if (!(status & LP5523_LEDTEST_DONE)) + usleep_range(3000, 10000); + + ret |= lp5523_read(chip->client, LP5523_REG_LED_TEST_ADC, &vdd); + vdd--; /* There may be some fluctuation in measurement */ + + for (i = 0; i < LP5523_LEDS; i++) { + /* Skip non-existing channels */ + if (chip->pdata->led_config[i].led_current == 0) + continue; + + /* Set default current */ + lp5523_write(chip->client, + LP5523_REG_LED_CURRENT_BASE + i, + chip->pdata->led_config[i].led_current); + + lp5523_write(chip->client, LP5523_REG_LED_PWM_BASE + i, 0xff); + /* let current stabilize 2ms before measurements start */ + usleep_range(2000, 10000); + lp5523_write(chip->client, + LP5523_REG_LED_TEST_CTRL, + LP5523_EN_LEDTEST | i); + /* ledtest takes 2.7ms */ + usleep_range(3000, 10000); + ret = lp5523_read(chip->client, LP5523_REG_STATUS, &status); + if (!(status & LP5523_LEDTEST_DONE)) + usleep_range(3000, 10000); + ret |= lp5523_read(chip->client, LP5523_REG_LED_TEST_ADC, &adc); + + if (adc >= vdd || adc < LP5523_ADC_SHORTCIRC_LIM) + pos += sprintf(buf + pos, "LED %d FAIL\n", i); + + lp5523_write(chip->client, LP5523_REG_LED_PWM_BASE + i, 0x00); + + /* Restore current */ + lp5523_write(chip->client, + LP5523_REG_LED_CURRENT_BASE + i, + chip->leds[led].led_current); + led++; + } + if (pos == 0) + pos = sprintf(buf, "OK\n"); + goto release_lock; +fail: + pos = sprintf(buf, "FAIL\n"); + +release_lock: + mutex_unlock(&chip->lock); + + return pos; +} + +static void lp5523_set_brightness(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct lp5523_led *led = cdev_to_led(cdev); + + led->brightness = (u8)brightness; + + schedule_work(&led->brightness_work); +} + +static void lp5523_led_brightness_work(struct work_struct *work) +{ + struct lp5523_led *led = container_of(work, + struct lp5523_led, + brightness_work); + struct lp5523_chip *chip = led_to_lp5523(led); + struct i2c_client *client = chip->client; + + mutex_lock(&chip->lock); + + lp5523_write(client, LP5523_REG_LED_PWM_BASE + led->chan_nr, + led->brightness); + + mutex_unlock(&chip->lock); +} + +static int lp5523_do_store_load(struct lp5523_engine *engine, + const char *buf, size_t len) +{ + struct lp5523_chip *chip = engine_to_lp5523(engine); + struct i2c_client *client = chip->client; + int ret, nrchars, offset = 0, i = 0; + char c[3]; + unsigned cmd; + u8 pattern[LP5523_PROGRAM_LENGTH] = {0}; + + while ((offset < len - 1) && (i < LP5523_PROGRAM_LENGTH)) { + /* separate sscanfs because length is working only for %s */ + ret = sscanf(buf + offset, "%2s%n ", c, &nrchars); + ret = sscanf(c, "%2x", &cmd); + if (ret != 1) + goto fail; + pattern[i] = (u8)cmd; + + offset += nrchars; + i++; + } + + /* Each instruction is 16bit long. Check that length is even */ + if (i % 2) + goto fail; + + mutex_lock(&chip->lock); + + ret = lp5523_load_program(engine, pattern); + mutex_unlock(&chip->lock); + + if (ret) { + dev_err(&client->dev, "failed loading pattern\n"); + return ret; + } + + return len; +fail: + dev_err(&client->dev, "wrong pattern format\n"); + return -EINVAL; +} + +static ssize_t store_engine_load(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lp5523_chip *chip = i2c_get_clientdata(client); + return lp5523_do_store_load(&chip->engines[nr - 1], buf, len); +} + +#define store_load(nr) \ +static ssize_t store_engine##nr##_load(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t len) \ +{ \ + return store_engine_load(dev, attr, buf, len, nr); \ +} +store_load(1) +store_load(2) +store_load(3) + +static ssize_t show_engine_mode(struct device *dev, + struct device_attribute *attr, + char *buf, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lp5523_chip *chip = i2c_get_clientdata(client); + switch (chip->engines[nr - 1].mode) { + case LP5523_CMD_RUN: + return sprintf(buf, "run\n"); + case LP5523_CMD_LOAD: + return sprintf(buf, "load\n"); + case LP5523_CMD_DISABLED: + return sprintf(buf, "disabled\n"); + default: + return sprintf(buf, "disabled\n"); + } +} + +#define show_mode(nr) \ +static ssize_t show_engine##nr##_mode(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + return show_engine_mode(dev, attr, buf, nr); \ +} +show_mode(1) +show_mode(2) +show_mode(3) + +static ssize_t store_engine_mode(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lp5523_chip *chip = i2c_get_clientdata(client); + struct lp5523_engine *engine = &chip->engines[nr - 1]; + mutex_lock(&chip->lock); + + if (!strncmp(buf, "run", 3)) + lp5523_set_mode(engine, LP5523_CMD_RUN); + else if (!strncmp(buf, "load", 4)) + lp5523_set_mode(engine, LP5523_CMD_LOAD); + else if (!strncmp(buf, "disabled", 8)) + lp5523_set_mode(engine, LP5523_CMD_DISABLED); + + mutex_unlock(&chip->lock); + return len; +} + +#define store_mode(nr) \ +static ssize_t store_engine##nr##_mode(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t len) \ +{ \ + return store_engine_mode(dev, attr, buf, len, nr); \ +} +store_mode(1) +store_mode(2) +store_mode(3) + +static ssize_t show_max_current(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct lp5523_led *led = cdev_to_led(led_cdev); + + return sprintf(buf, "%d\n", led->max_current); +} + +static ssize_t show_current(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct lp5523_led *led = cdev_to_led(led_cdev); + + return sprintf(buf, "%d\n", led->led_current); +} + +static ssize_t store_current(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct lp5523_led *led = cdev_to_led(led_cdev); + struct lp5523_chip *chip = led_to_lp5523(led); + ssize_t ret; + unsigned long curr; + + if (strict_strtoul(buf, 0, &curr)) + return -EINVAL; + + if (curr > led->max_current) + return -EINVAL; + + mutex_lock(&chip->lock); + ret = lp5523_write(chip->client, + LP5523_REG_LED_CURRENT_BASE + led->chan_nr, + (u8)curr); + mutex_unlock(&chip->lock); + + if (ret < 0) + return ret; + + led->led_current = (u8)curr; + + return len; +} + +/* led class device attributes */ +static DEVICE_ATTR(led_current, S_IRUGO | S_IWUGO, show_current, store_current); +static DEVICE_ATTR(max_current, S_IRUGO , show_max_current, NULL); + +static struct attribute *lp5523_led_attributes[] = { + &dev_attr_led_current.attr, + &dev_attr_max_current.attr, + NULL, +}; + +static struct attribute_group lp5523_led_attribute_group = { + .attrs = lp5523_led_attributes +}; + +/* device attributes */ +static DEVICE_ATTR(engine1_mode, S_IRUGO | S_IWUGO, + show_engine1_mode, store_engine1_mode); +static DEVICE_ATTR(engine2_mode, S_IRUGO | S_IWUGO, + show_engine2_mode, store_engine2_mode); +static DEVICE_ATTR(engine3_mode, S_IRUGO | S_IWUGO, + show_engine3_mode, store_engine3_mode); +static DEVICE_ATTR(engine1_leds, S_IRUGO | S_IWUGO, + show_engine1_leds, store_engine1_leds); +static DEVICE_ATTR(engine2_leds, S_IRUGO | S_IWUGO, + show_engine2_leds, store_engine2_leds); +static DEVICE_ATTR(engine3_leds, S_IRUGO | S_IWUGO, + show_engine3_leds, store_engine3_leds); +static DEVICE_ATTR(engine1_load, S_IWUGO, NULL, store_engine1_load); +static DEVICE_ATTR(engine2_load, S_IWUGO, NULL, store_engine2_load); +static DEVICE_ATTR(engine3_load, S_IWUGO, NULL, store_engine3_load); +static DEVICE_ATTR(selftest, S_IRUGO, lp5523_selftest, NULL); + +static struct attribute *lp5523_attributes[] = { + &dev_attr_engine1_mode.attr, + &dev_attr_engine2_mode.attr, + &dev_attr_engine3_mode.attr, + &dev_attr_selftest.attr, + NULL +}; + +static struct attribute *lp5523_engine1_attributes[] = { + &dev_attr_engine1_load.attr, + &dev_attr_engine1_leds.attr, + NULL +}; + +static struct attribute *lp5523_engine2_attributes[] = { + &dev_attr_engine2_load.attr, + &dev_attr_engine2_leds.attr, + NULL +}; + +static struct attribute *lp5523_engine3_attributes[] = { + &dev_attr_engine3_load.attr, + &dev_attr_engine3_leds.attr, + NULL +}; + +static const struct attribute_group lp5523_group = { + .attrs = lp5523_attributes, +}; + +static const struct attribute_group lp5523_engine_group[] = { + {.attrs = lp5523_engine1_attributes }, + {.attrs = lp5523_engine2_attributes }, + {.attrs = lp5523_engine3_attributes }, +}; + +static int lp5523_register_sysfs(struct i2c_client *client) +{ + struct device *dev = &client->dev; + int ret; + + ret = sysfs_create_group(&dev->kobj, &lp5523_group); + if (ret < 0) + return ret; + + return 0; +} + +static void lp5523_unregister_sysfs(struct i2c_client *client) +{ + struct lp5523_chip *chip = i2c_get_clientdata(client); + struct device *dev = &client->dev; + int i; + + sysfs_remove_group(&dev->kobj, &lp5523_group); + + for (i = 0; i < ARRAY_SIZE(chip->engines); i++) + if (chip->engines[i].mode == LP5523_CMD_LOAD) + sysfs_remove_group(&dev->kobj, &lp5523_engine_group[i]); + + for (i = 0; i < chip->num_leds; i++) + sysfs_remove_group(&chip->leds[i].cdev.dev->kobj, + &lp5523_led_attribute_group); +} + +/*--------------------------------------------------------------*/ +/* Set chip operating mode */ +/*--------------------------------------------------------------*/ +static int lp5523_set_mode(struct lp5523_engine *engine, u8 mode) +{ + /* engine to chip */ + struct lp5523_chip *chip = engine_to_lp5523(engine); + struct i2c_client *client = chip->client; + struct device *dev = &client->dev; + int ret = 0; + + /* if in that mode already do nothing, except for run */ + if (mode == engine->mode && mode != LP5523_CMD_RUN) + return 0; + + if (mode == LP5523_CMD_RUN) { + ret = lp5523_run_program(engine); + } else if (mode == LP5523_CMD_LOAD) { + lp5523_set_engine_mode(engine, LP5523_CMD_DISABLED); + lp5523_set_engine_mode(engine, LP5523_CMD_LOAD); + + ret = sysfs_create_group(&dev->kobj, engine->attributes); + if (ret) + return ret; + } else if (mode == LP5523_CMD_DISABLED) { + lp5523_set_engine_mode(engine, LP5523_CMD_DISABLED); + } + + /* remove load attribute from sysfs if not in load mode */ + if (engine->mode == LP5523_CMD_LOAD && mode != LP5523_CMD_LOAD) + sysfs_remove_group(&dev->kobj, engine->attributes); + + engine->mode = mode; + + return ret; +} + +/*--------------------------------------------------------------*/ +/* Probe, Attach, Remove */ +/*--------------------------------------------------------------*/ +static int __init lp5523_init_engine(struct lp5523_engine *engine, int id) +{ + if (id < 1 || id > LP5523_ENGINES) + return -1; + engine->id = id; + engine->engine_mask = LP5523_ENG_MASK_BASE >> SHIFT_MASK(id); + engine->prog_page = id - 1; + engine->mux_page = id + 2; + engine->attributes = &lp5523_engine_group[id - 1]; + + return 0; +} + +static int __init lp5523_init_led(struct lp5523_led *led, struct device *dev, + int chan, struct lp5523_platform_data *pdata) +{ + char name[32]; + int res; + + if (chan >= LP5523_LEDS) + return -EINVAL; + + if (pdata->led_config[chan].led_current) { + led->led_current = pdata->led_config[chan].led_current; + led->max_current = pdata->led_config[chan].max_current; + led->chan_nr = pdata->led_config[chan].chan_nr; + + if (led->chan_nr >= LP5523_LEDS) { + dev_err(dev, "Use channel numbers between 0 and %d\n", + LP5523_LEDS - 1); + return -EINVAL; + } + + snprintf(name, 32, "lp5523:channel%d", chan); + + led->cdev.name = name; + led->cdev.brightness_set = lp5523_set_brightness; + res = led_classdev_register(dev, &led->cdev); + if (res < 0) { + dev_err(dev, "couldn't register led on channel %d\n", + chan); + return res; + } + res = sysfs_create_group(&led->cdev.dev->kobj, + &lp5523_led_attribute_group); + if (res < 0) { + dev_err(dev, "couldn't register current attribute\n"); + led_classdev_unregister(&led->cdev); + return res; + } + } else { + led->led_current = 0; + } + return 0; +} + +static struct i2c_driver lp5523_driver; + +static int lp5523_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct lp5523_chip *chip; + struct lp5523_platform_data *pdata; + int ret, i, led; + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + i2c_set_clientdata(client, chip); + chip->client = client; + + pdata = client->dev.platform_data; + + if (!pdata) { + dev_err(&client->dev, "no platform data\n"); + ret = -EINVAL; + goto fail1; + } + + mutex_init(&chip->lock); + + chip->pdata = pdata; + + if (pdata->setup_resources) { + ret = pdata->setup_resources(); + if (ret < 0) + goto fail1; + } + + if (pdata->enable) { + pdata->enable(0); + usleep_range(1000, 10000); + pdata->enable(1); + usleep_range(1000, 10000); /* Spec says min 500us */ + } + + ret = lp5523_detect(client); + if (ret) + goto fail2; + + dev_info(&client->dev, "LP5523 Programmable led chip found\n"); + + /* Initialize engines */ + for (i = 0; i < ARRAY_SIZE(chip->engines); i++) { + ret = lp5523_init_engine(&chip->engines[i], i + 1); + if (ret) { + dev_err(&client->dev, "error initializing engine\n"); + goto fail2; + } + } + ret = lp5523_configure(client); + if (ret < 0) { + dev_err(&client->dev, "error configuring chip\n"); + goto fail2; + } + + /* Initialize leds */ + chip->num_channels = pdata->num_channels; + chip->num_leds = 0; + led = 0; + for (i = 0; i < pdata->num_channels; i++) { + /* Do not initialize channels that are not connected */ + if (pdata->led_config[i].led_current == 0) + continue; + + ret = lp5523_init_led(&chip->leds[led], &client->dev, i, pdata); + if (ret) { + dev_err(&client->dev, "error initializing leds\n"); + goto fail3; + } + chip->num_leds++; + + chip->leds[led].id = led; + /* Set LED current */ + lp5523_write(client, + LP5523_REG_LED_CURRENT_BASE + chip->leds[led].chan_nr, + chip->leds[led].led_current); + + INIT_WORK(&(chip->leds[led].brightness_work), + lp5523_led_brightness_work); + + led++; + } + + ret = lp5523_register_sysfs(client); + if (ret) { + dev_err(&client->dev, "registering sysfs failed\n"); + goto fail3; + } + return ret; +fail3: + for (i = 0; i < chip->num_leds; i++) { + led_classdev_unregister(&chip->leds[i].cdev); + cancel_work_sync(&chip->leds[i].brightness_work); + } +fail2: + if (pdata->enable) + pdata->enable(0); + if (pdata->release_resources) + pdata->release_resources(); +fail1: + kfree(chip); + return ret; +} + +static int lp5523_remove(struct i2c_client *client) +{ + struct lp5523_chip *chip = i2c_get_clientdata(client); + int i; + + lp5523_unregister_sysfs(client); + + for (i = 0; i < chip->num_leds; i++) { + led_classdev_unregister(&chip->leds[i].cdev); + cancel_work_sync(&chip->leds[i].brightness_work); + } + + if (chip->pdata->enable) + chip->pdata->enable(0); + if (chip->pdata->release_resources) + chip->pdata->release_resources(); + kfree(chip); + return 0; +} + +static const struct i2c_device_id lp5523_id[] = { + { "lp5523", 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, lp5523_id); + +static struct i2c_driver lp5523_driver = { + .driver = { + .name = "lp5523", + }, + .probe = lp5523_probe, + .remove = lp5523_remove, + .id_table = lp5523_id, +}; + +static int __init lp5523_init(void) +{ + int ret; + + ret = i2c_add_driver(&lp5523_driver); + + if (ret < 0) + printk(KERN_ALERT "Adding lp5523 driver failed\n"); + + return ret; +} + +static void __exit lp5523_exit(void) +{ + i2c_del_driver(&lp5523_driver); +} + +module_init(lp5523_init); +module_exit(lp5523_exit); + +MODULE_AUTHOR("Mathias Nyman <mathias.nyman@nokia.com>"); +MODULE_DESCRIPTION("LP5523 LED engine"); +MODULE_LICENSE("GPL"); diff --git a/drivers/leds/leds-net5501.c b/drivers/leds/leds-net5501.c index 3063f591f0dc..1739557a9038 100644 --- a/drivers/leds/leds-net5501.c +++ b/drivers/leds/leds-net5501.c @@ -92,3 +92,5 @@ unmap: } arch_initcall(soekris_init); + +MODULE_LICENSE("GPL"); diff --git a/drivers/leds/ledtrig-timer.c b/drivers/leds/ledtrig-timer.c index 82b77bd482ff..b09bcbeade9c 100644 --- a/drivers/leds/ledtrig-timer.c +++ b/drivers/leds/ledtrig-timer.c @@ -12,73 +12,25 @@ */ #include <linux/module.h> -#include <linux/jiffies.h> #include <linux/kernel.h> #include <linux/init.h> -#include <linux/list.h> -#include <linux/spinlock.h> #include <linux/device.h> -#include <linux/sysdev.h> -#include <linux/timer.h> #include <linux/ctype.h> #include <linux/leds.h> -#include <linux/slab.h> #include "leds.h" -struct timer_trig_data { - int brightness_on; /* LED brightness during "on" period. - * (LED_OFF < brightness_on <= LED_FULL) - */ - unsigned long delay_on; /* milliseconds on */ - unsigned long delay_off; /* milliseconds off */ - struct timer_list timer; -}; - -static void led_timer_function(unsigned long data) -{ - struct led_classdev *led_cdev = (struct led_classdev *) data; - struct timer_trig_data *timer_data = led_cdev->trigger_data; - unsigned long brightness; - unsigned long delay; - - if (!timer_data->delay_on || !timer_data->delay_off) { - led_set_brightness(led_cdev, LED_OFF); - return; - } - - brightness = led_get_brightness(led_cdev); - if (!brightness) { - /* Time to switch the LED on. */ - brightness = timer_data->brightness_on; - delay = timer_data->delay_on; - } else { - /* Store the current brightness value to be able - * to restore it when the delay_off period is over. - */ - timer_data->brightness_on = brightness; - brightness = LED_OFF; - delay = timer_data->delay_off; - } - - led_set_brightness(led_cdev, brightness); - - mod_timer(&timer_data->timer, jiffies + msecs_to_jiffies(delay)); -} - static ssize_t led_delay_on_show(struct device *dev, struct device_attribute *attr, char *buf) { struct led_classdev *led_cdev = dev_get_drvdata(dev); - struct timer_trig_data *timer_data = led_cdev->trigger_data; - return sprintf(buf, "%lu\n", timer_data->delay_on); + return sprintf(buf, "%lu\n", led_cdev->blink_delay_on); } static ssize_t led_delay_on_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct led_classdev *led_cdev = dev_get_drvdata(dev); - struct timer_trig_data *timer_data = led_cdev->trigger_data; int ret = -EINVAL; char *after; unsigned long state = simple_strtoul(buf, &after, 10); @@ -88,21 +40,7 @@ static ssize_t led_delay_on_store(struct device *dev, count++; if (count == size) { - if (timer_data->delay_on != state) { - /* the new value differs from the previous */ - timer_data->delay_on = state; - - /* deactivate previous settings */ - del_timer_sync(&timer_data->timer); - - /* try to activate hardware acceleration, if any */ - if (!led_cdev->blink_set || - led_cdev->blink_set(led_cdev, - &timer_data->delay_on, &timer_data->delay_off)) { - /* no hardware acceleration, blink via timer */ - mod_timer(&timer_data->timer, jiffies + 1); - } - } + led_blink_set(led_cdev, &state, &led_cdev->blink_delay_off); ret = count; } @@ -113,16 +51,14 @@ static ssize_t led_delay_off_show(struct device *dev, struct device_attribute *attr, char *buf) { struct led_classdev *led_cdev = dev_get_drvdata(dev); - struct timer_trig_data *timer_data = led_cdev->trigger_data; - return sprintf(buf, "%lu\n", timer_data->delay_off); + return sprintf(buf, "%lu\n", led_cdev->blink_delay_off); } static ssize_t led_delay_off_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct led_classdev *led_cdev = dev_get_drvdata(dev); - struct timer_trig_data *timer_data = led_cdev->trigger_data; int ret = -EINVAL; char *after; unsigned long state = simple_strtoul(buf, &after, 10); @@ -132,21 +68,7 @@ static ssize_t led_delay_off_store(struct device *dev, count++; if (count == size) { - if (timer_data->delay_off != state) { - /* the new value differs from the previous */ - timer_data->delay_off = state; - - /* deactivate previous settings */ - del_timer_sync(&timer_data->timer); - - /* try to activate hardware acceleration, if any */ - if (!led_cdev->blink_set || - led_cdev->blink_set(led_cdev, - &timer_data->delay_on, &timer_data->delay_off)) { - /* no hardware acceleration, blink via timer */ - mod_timer(&timer_data->timer, jiffies + 1); - } - } + led_blink_set(led_cdev, &led_cdev->blink_delay_on, &state); ret = count; } @@ -158,60 +80,34 @@ static DEVICE_ATTR(delay_off, 0644, led_delay_off_show, led_delay_off_store); static void timer_trig_activate(struct led_classdev *led_cdev) { - struct timer_trig_data *timer_data; int rc; - timer_data = kzalloc(sizeof(struct timer_trig_data), GFP_KERNEL); - if (!timer_data) - return; - - timer_data->brightness_on = led_get_brightness(led_cdev); - if (timer_data->brightness_on == LED_OFF) - timer_data->brightness_on = led_cdev->max_brightness; - led_cdev->trigger_data = timer_data; - - init_timer(&timer_data->timer); - timer_data->timer.function = led_timer_function; - timer_data->timer.data = (unsigned long) led_cdev; + led_cdev->trigger_data = NULL; rc = device_create_file(led_cdev->dev, &dev_attr_delay_on); if (rc) - goto err_out; + return; rc = device_create_file(led_cdev->dev, &dev_attr_delay_off); if (rc) goto err_out_delayon; - /* If there is hardware support for blinking, start one - * user friendly blink rate chosen by the driver. - */ - if (led_cdev->blink_set) - led_cdev->blink_set(led_cdev, - &timer_data->delay_on, &timer_data->delay_off); + led_cdev->trigger_data = (void *)1; return; err_out_delayon: device_remove_file(led_cdev->dev, &dev_attr_delay_on); -err_out: - led_cdev->trigger_data = NULL; - kfree(timer_data); } static void timer_trig_deactivate(struct led_classdev *led_cdev) { - struct timer_trig_data *timer_data = led_cdev->trigger_data; - unsigned long on = 0, off = 0; - - if (timer_data) { + if (led_cdev->trigger_data) { device_remove_file(led_cdev->dev, &dev_attr_delay_on); device_remove_file(led_cdev->dev, &dev_attr_delay_off); - del_timer_sync(&timer_data->timer); - kfree(timer_data); } - /* If there is hardware support for blinking, stop it */ - if (led_cdev->blink_set) - led_cdev->blink_set(led_cdev, &on, &off); + /* Stop blinking */ + led_brightness_set(led_cdev, LED_OFF); } static struct led_trigger timer_led_trigger = { diff --git a/drivers/macintosh/adb-iop.c b/drivers/macintosh/adb-iop.c index 444696625171..f5f4da3d0b67 100644 --- a/drivers/macintosh/adb-iop.c +++ b/drivers/macintosh/adb-iop.c @@ -80,7 +80,7 @@ static void adb_iop_end_req(struct adb_request *req, int state) static void adb_iop_complete(struct iop_msg *msg) { struct adb_request *req; - uint flags; + unsigned long flags; local_irq_save(flags); @@ -103,7 +103,7 @@ static void adb_iop_listen(struct iop_msg *msg) { struct adb_iopmsg *amsg = (struct adb_iopmsg *) msg->message; struct adb_request *req; - uint flags; + unsigned long flags; #ifdef DEBUG_ADB_IOP int i; #endif diff --git a/drivers/misc/apds9802als.c b/drivers/misc/apds9802als.c index f9b91ba8900c..0ed09358027e 100644 --- a/drivers/misc/apds9802als.c +++ b/drivers/misc/apds9802als.c @@ -123,7 +123,7 @@ static ssize_t als_sensing_range_store(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct als_data *data = i2c_get_clientdata(client); - unsigned int ret_val; + int ret_val; unsigned long val; if (strict_strtoul(buf, 10, &val)) diff --git a/drivers/misc/bh1770glc.c b/drivers/misc/bh1770glc.c index cee632e645e1..d79a972f2c79 100644 --- a/drivers/misc/bh1770glc.c +++ b/drivers/misc/bh1770glc.c @@ -649,7 +649,7 @@ static ssize_t bh1770_power_state_store(struct device *dev, { struct bh1770_chip *chip = dev_get_drvdata(dev); unsigned long value; - size_t ret; + ssize_t ret; if (strict_strtoul(buf, 0, &value)) return -EINVAL; @@ -659,8 +659,12 @@ static ssize_t bh1770_power_state_store(struct device *dev, pm_runtime_get_sync(dev); ret = bh1770_lux_rate(chip, chip->lux_rate_index); - ret |= bh1770_lux_interrupt_control(chip, BH1770_ENABLE); + if (ret < 0) { + pm_runtime_put(dev); + goto leave; + } + ret = bh1770_lux_interrupt_control(chip, BH1770_ENABLE); if (ret < 0) { pm_runtime_put(dev); goto leave; diff --git a/drivers/misc/isl29020.c b/drivers/misc/isl29020.c index 34fe835921c4..ca47e6285075 100644 --- a/drivers/misc/isl29020.c +++ b/drivers/misc/isl29020.c @@ -87,7 +87,7 @@ static ssize_t als_sensing_range_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); - unsigned int ret_val; + int ret_val; unsigned long val; if (strict_strtoul(buf, 10, &val)) @@ -106,6 +106,8 @@ static ssize_t als_sensing_range_store(struct device *dev, val = 4; ret_val = i2c_smbus_read_byte_data(client, 0x00); + if (ret_val < 0) + return ret_val; ret_val &= 0xFC; /*reset the bit before setting them */ ret_val |= val - 1; diff --git a/drivers/net/atlx/atl1.c b/drivers/net/atlx/atl1.c index 43579b3b24ac..53363108994e 100644 --- a/drivers/net/atlx/atl1.c +++ b/drivers/net/atlx/atl1.c @@ -3043,7 +3043,6 @@ static int __devinit atl1_probe(struct pci_dev *pdev, atl1_pcie_patch(adapter); /* assume we have no link for now */ netif_carrier_off(netdev); - netif_stop_queue(netdev); setup_timer(&adapter->phy_config_timer, atl1_phy_config, (unsigned long)adapter); diff --git a/drivers/net/bnx2x/bnx2x.h b/drivers/net/bnx2x/bnx2x.h index 9eea225decaf..863e73a85fbe 100644 --- a/drivers/net/bnx2x/bnx2x.h +++ b/drivers/net/bnx2x/bnx2x.h @@ -20,8 +20,8 @@ * (you will need to reboot afterwards) */ /* #define BNX2X_STOP_ON_ERROR */ -#define DRV_MODULE_VERSION "1.60.00-3" -#define DRV_MODULE_RELDATE "2010/10/19" +#define DRV_MODULE_VERSION "1.60.00-4" +#define DRV_MODULE_RELDATE "2010/11/01" #define BNX2X_BC_VER 0x040200 #define BNX2X_MULTI_QUEUE diff --git a/drivers/net/bnx2x/bnx2x_hsi.h b/drivers/net/bnx2x/bnx2x_hsi.h index 18c8e23a0e82..4cfd4e9b5586 100644 --- a/drivers/net/bnx2x/bnx2x_hsi.h +++ b/drivers/net/bnx2x/bnx2x_hsi.h @@ -244,7 +244,14 @@ struct port_hw_cfg { /* port 0: 0x12c port 1: 0x2bc */ u16 xgxs_config_tx[4]; /* 0x1A0 */ - u32 Reserved1[57]; /* 0x1A8 */ + u32 Reserved1[56]; /* 0x1A8 */ + u32 default_cfg; /* 0x288 */ + /* Enable BAM on KR */ +#define PORT_HW_CFG_ENABLE_BAM_ON_KR_MASK 0x00100000 +#define PORT_HW_CFG_ENABLE_BAM_ON_KR_SHIFT 20 +#define PORT_HW_CFG_ENABLE_BAM_ON_KR_DISABLED 0x00000000 +#define PORT_HW_CFG_ENABLE_BAM_ON_KR_ENABLED 0x00100000 + u32 speed_capability_mask2; /* 0x28C */ #define PORT_HW_CFG_SPEED_CAPABILITY2_D3_MASK 0x0000FFFF #define PORT_HW_CFG_SPEED_CAPABILITY2_D3_SHIFT 0 diff --git a/drivers/net/bnx2x/bnx2x_link.c b/drivers/net/bnx2x/bnx2x_link.c index 2326774df843..580919619252 100644 --- a/drivers/net/bnx2x/bnx2x_link.c +++ b/drivers/net/bnx2x/bnx2x_link.c @@ -610,7 +610,7 @@ static u8 bnx2x_bmac_enable(struct link_params *params, /* reset and unreset the BigMac */ REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR, (MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << port)); - udelay(10); + msleep(1); REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_SET, (MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << port)); @@ -3525,13 +3525,19 @@ static u8 bnx2x_8073_config_init(struct bnx2x_phy *phy, DP(NETIF_MSG_LINK, "Before rom RX_ALARM(port1): 0x%x\n", tmp1); /* Enable CL37 BAM */ - bnx2x_cl45_read(bp, phy, - MDIO_AN_DEVAD, - MDIO_AN_REG_8073_BAM, &val); - bnx2x_cl45_write(bp, phy, - MDIO_AN_DEVAD, - MDIO_AN_REG_8073_BAM, val | 1); + if (REG_RD(bp, params->shmem_base + + offsetof(struct shmem_region, dev_info. + port_hw_config[params->port].default_cfg)) & + PORT_HW_CFG_ENABLE_BAM_ON_KR_ENABLED) { + bnx2x_cl45_read(bp, phy, + MDIO_AN_DEVAD, + MDIO_AN_REG_8073_BAM, &val); + bnx2x_cl45_write(bp, phy, + MDIO_AN_DEVAD, + MDIO_AN_REG_8073_BAM, val | 1); + DP(NETIF_MSG_LINK, "Enable CL37 BAM on KR\n"); + } if (params->loopback_mode == LOOPBACK_EXT) { bnx2x_807x_force_10G(bp, phy); DP(NETIF_MSG_LINK, "Forced speed 10G on 807X\n"); @@ -5302,7 +5308,7 @@ static u8 bnx2x_848xx_cmn_config_init(struct bnx2x_phy *phy, { struct bnx2x *bp = params->bp; u16 autoneg_val, an_1000_val, an_10_100_val; - bnx2x_wait_reset_complete(bp, phy); + bnx2x_bits_en(bp, NIG_REG_LATCH_BC_0 + params->port*4, 1 << NIG_LATCH_BC_ENABLE_MI_INT); @@ -5431,6 +5437,7 @@ static u8 bnx2x_8481_config_init(struct bnx2x_phy *phy, /* HW reset */ bnx2x_ext_phy_hw_reset(bp, params->port); + bnx2x_wait_reset_complete(bp, phy); bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, MDIO_PMA_REG_CTRL, 1<<15); return bnx2x_848xx_cmn_config_init(phy, params, vars); @@ -5441,7 +5448,7 @@ static u8 bnx2x_848x3_config_init(struct bnx2x_phy *phy, struct link_vars *vars) { struct bnx2x *bp = params->bp; - u8 port = params->port, initialize = 1; + u8 port, initialize = 1; u16 val; u16 temp; u32 actual_phy_selection; @@ -5450,11 +5457,16 @@ static u8 bnx2x_848x3_config_init(struct bnx2x_phy *phy, /* This is just for MDIO_CTL_REG_84823_MEDIA register. */ msleep(1); + if (CHIP_IS_E2(bp)) + port = BP_PATH(bp); + else + port = params->port; bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_3, MISC_REGISTERS_GPIO_OUTPUT_HIGH, port); - msleep(200); /* 100 is not enough */ - + bnx2x_wait_reset_complete(bp, phy); + /* Wait for GPHY to come out of reset */ + msleep(50); /* BCM84823 requires that XGXS links up first @ 10G for normal behavior */ temp = vars->line_speed; @@ -5625,7 +5637,11 @@ static void bnx2x_848x3_link_reset(struct bnx2x_phy *phy, struct link_params *params) { struct bnx2x *bp = params->bp; - u8 port = params->port; + u8 port; + if (CHIP_IS_E2(bp)) + port = BP_PATH(bp); + else + port = params->port; bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_3, MISC_REGISTERS_GPIO_OUTPUT_LOW, port); @@ -6928,7 +6944,7 @@ u8 bnx2x_link_reset(struct link_params *params, struct link_vars *vars, u8 reset_ext_phy) { struct bnx2x *bp = params->bp; - u8 phy_index, port = params->port; + u8 phy_index, port = params->port, clear_latch_ind = 0; DP(NETIF_MSG_LINK, "Resetting the link of port %d\n", port); /* disable attentions */ vars->link_status = 0; @@ -6966,9 +6982,18 @@ u8 bnx2x_link_reset(struct link_params *params, struct link_vars *vars, params->phy[phy_index].link_reset( ¶ms->phy[phy_index], params); + if (params->phy[phy_index].flags & + FLAGS_REARM_LATCH_SIGNAL) + clear_latch_ind = 1; } } + if (clear_latch_ind) { + /* Clear latching indication */ + bnx2x_rearm_latch_signal(bp, port, 0); + bnx2x_bits_dis(bp, NIG_REG_LATCH_BC_0 + port*4, + 1 << NIG_LATCH_BC_ENABLE_MI_INT); + } if (params->phy[INT_PHY].link_reset) params->phy[INT_PHY].link_reset( ¶ms->phy[INT_PHY], params); @@ -6999,6 +7024,7 @@ static u8 bnx2x_8073_common_init_phy(struct bnx2x *bp, s8 port; s8 port_of_path = 0; + bnx2x_ext_phy_hw_reset(bp, 0); /* PART1 - Reset both phys */ for (port = PORT_MAX - 1; port >= PORT_0; port--) { u32 shmem_base, shmem2_base; @@ -7021,7 +7047,8 @@ static u8 bnx2x_8073_common_init_phy(struct bnx2x *bp, return -EINVAL; } /* disable attentions */ - bnx2x_bits_dis(bp, NIG_REG_MASK_INTERRUPT_PORT0 + port*4, + bnx2x_bits_dis(bp, NIG_REG_MASK_INTERRUPT_PORT0 + + port_of_path*4, (NIG_MASK_XGXS0_LINK_STATUS | NIG_MASK_XGXS0_LINK10G | NIG_MASK_SERDES0_LINK_STATUS | @@ -7132,7 +7159,7 @@ static u8 bnx2x_8726_common_init_phy(struct bnx2x *bp, (1<<(MISC_REGISTERS_GPIO_3 + MISC_REGISTERS_GPIO_PORT_SHIFT))); REG_WR(bp, MISC_REG_GPIO_EVENT_EN, val); - bnx2x_ext_phy_hw_reset(bp, 1); + bnx2x_ext_phy_hw_reset(bp, 0); msleep(5); for (port = 0; port < PORT_MAX; port++) { u32 shmem_base, shmem2_base; diff --git a/drivers/net/caif/caif_spi.c b/drivers/net/caif/caif_spi.c index 8427533fe313..8b4cea57a6c5 100644 --- a/drivers/net/caif/caif_spi.c +++ b/drivers/net/caif/caif_spi.c @@ -33,6 +33,9 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Daniel Martensson<daniel.martensson@stericsson.com>"); MODULE_DESCRIPTION("CAIF SPI driver"); +/* Returns the number of padding bytes for alignment. */ +#define PAD_POW2(x, pow) ((((x)&((pow)-1))==0) ? 0 : (((pow)-((x)&((pow)-1))))) + static int spi_loop; module_param(spi_loop, bool, S_IRUGO); MODULE_PARM_DESC(spi_loop, "SPI running in loopback mode."); @@ -41,7 +44,10 @@ MODULE_PARM_DESC(spi_loop, "SPI running in loopback mode."); module_param(spi_frm_align, int, S_IRUGO); MODULE_PARM_DESC(spi_frm_align, "SPI frame alignment."); -/* SPI padding options. */ +/* + * SPI padding options. + * Warning: must be a base of 2 (& operation used) and can not be zero ! + */ module_param(spi_up_head_align, int, S_IRUGO); MODULE_PARM_DESC(spi_up_head_align, "SPI uplink head alignment."); @@ -240,15 +246,13 @@ static ssize_t dbgfs_frame(struct file *file, char __user *user_buf, static const struct file_operations dbgfs_state_fops = { .open = dbgfs_open, .read = dbgfs_state, - .owner = THIS_MODULE, - .llseek = default_llseek, + .owner = THIS_MODULE }; static const struct file_operations dbgfs_frame_fops = { .open = dbgfs_open, .read = dbgfs_frame, - .owner = THIS_MODULE, - .llseek = default_llseek, + .owner = THIS_MODULE }; static inline void dev_debugfs_add(struct cfspi *cfspi) @@ -337,6 +341,9 @@ int cfspi_xmitfrm(struct cfspi *cfspi, u8 *buf, size_t len) u8 *dst = buf; caif_assert(buf); + if (cfspi->slave && !cfspi->slave_talked) + cfspi->slave_talked = true; + do { struct sk_buff *skb; struct caif_payload_info *info; @@ -357,8 +364,8 @@ int cfspi_xmitfrm(struct cfspi *cfspi, u8 *buf, size_t len) * Compute head offset i.e. number of bytes to add to * get the start of the payload aligned. */ - if (spi_up_head_align) { - spad = 1 + ((info->hdr_len + 1) & spi_up_head_align); + if (spi_up_head_align > 1) { + spad = 1 + PAD_POW2((info->hdr_len + 1), spi_up_head_align); *dst = (u8)(spad - 1); dst += spad; } @@ -373,7 +380,7 @@ int cfspi_xmitfrm(struct cfspi *cfspi, u8 *buf, size_t len) * Compute tail offset i.e. number of bytes to add to * get the complete CAIF frame aligned. */ - epad = (skb->len + spad) & spi_up_tail_align; + epad = PAD_POW2((skb->len + spad), spi_up_tail_align); dst += epad; dev_kfree_skb(skb); @@ -417,14 +424,14 @@ int cfspi_xmitlen(struct cfspi *cfspi) * Compute head offset i.e. number of bytes to add to * get the start of the payload aligned. */ - if (spi_up_head_align) - spad = 1 + ((info->hdr_len + 1) & spi_up_head_align); + if (spi_up_head_align > 1) + spad = 1 + PAD_POW2((info->hdr_len + 1), spi_up_head_align); /* * Compute tail offset i.e. number of bytes to add to * get the complete CAIF frame aligned. */ - epad = (skb->len + spad) & spi_up_tail_align; + epad = PAD_POW2((skb->len + spad), spi_up_tail_align); if ((skb->len + spad + epad + frm_len) <= CAIF_MAX_SPI_FRAME) { skb_queue_tail(&cfspi->chead, skb); @@ -433,6 +440,7 @@ int cfspi_xmitlen(struct cfspi *cfspi) } else { /* Put back packet. */ skb_queue_head(&cfspi->qhead, skb); + break; } } while (pkts <= CAIF_MAX_SPI_PKTS); @@ -453,6 +461,15 @@ static void cfspi_ss_cb(bool assert, struct cfspi_ifc *ifc) { struct cfspi *cfspi = (struct cfspi *)ifc->priv; + /* + * The slave device is the master on the link. Interrupts before the + * slave has transmitted are considered spurious. + */ + if (cfspi->slave && !cfspi->slave_talked) { + printk(KERN_WARNING "CFSPI: Spurious SS interrupt.\n"); + return; + } + if (!in_interrupt()) spin_lock(&cfspi->lock); if (assert) { @@ -465,7 +482,8 @@ static void cfspi_ss_cb(bool assert, struct cfspi_ifc *ifc) spin_unlock(&cfspi->lock); /* Wake up the xfer thread. */ - wake_up_interruptible(&cfspi->wait); + if (assert) + wake_up_interruptible(&cfspi->wait); } static void cfspi_xfer_done_cb(struct cfspi_ifc *ifc) @@ -523,7 +541,7 @@ int cfspi_rxfrm(struct cfspi *cfspi, u8 *buf, size_t len) * Compute head offset i.e. number of bytes added to * get the start of the payload aligned. */ - if (spi_down_head_align) { + if (spi_down_head_align > 1) { spad = 1 + *src; src += spad; } @@ -564,7 +582,7 @@ int cfspi_rxfrm(struct cfspi *cfspi, u8 *buf, size_t len) * Compute tail offset i.e. number of bytes added to * get the complete CAIF frame aligned. */ - epad = (pkt_len + spad) & spi_down_tail_align; + epad = PAD_POW2((pkt_len + spad), spi_down_tail_align); src += epad; } while ((src - buf) < len); @@ -625,11 +643,20 @@ int cfspi_spi_probe(struct platform_device *pdev) cfspi->ndev = ndev; cfspi->pdev = pdev; - /* Set flow info */ + /* Set flow info. */ cfspi->flow_off_sent = 0; cfspi->qd_low_mark = LOW_WATER_MARK; cfspi->qd_high_mark = HIGH_WATER_MARK; + /* Set slave info. */ + if (!strncmp(cfspi_spi_driver.driver.name, "cfspi_sspi", 10)) { + cfspi->slave = true; + cfspi->slave_talked = false; + } else { + cfspi->slave = false; + cfspi->slave_talked = false; + } + /* Assign the SPI device. */ cfspi->dev = dev; /* Assign the device ifc to this SPI interface. */ diff --git a/drivers/net/caif/caif_spi_slave.c b/drivers/net/caif/caif_spi_slave.c index 2111dbfea6fe..1b9943a4edab 100644 --- a/drivers/net/caif/caif_spi_slave.c +++ b/drivers/net/caif/caif_spi_slave.c @@ -36,10 +36,15 @@ static inline int forward_to_spi_cmd(struct cfspi *cfspi) #endif int spi_frm_align = 2; -int spi_up_head_align = 1; -int spi_up_tail_align; -int spi_down_head_align = 3; -int spi_down_tail_align = 1; + +/* + * SPI padding options. + * Warning: must be a base of 2 (& operation used) and can not be zero ! + */ +int spi_up_head_align = 1 << 1; +int spi_up_tail_align = 1 << 0; +int spi_down_head_align = 1 << 2; +int spi_down_tail_align = 1 << 1; #ifdef CONFIG_DEBUG_FS static inline void debugfs_store_prev(struct cfspi *cfspi) diff --git a/drivers/net/cxgb3/cxgb3_main.c b/drivers/net/cxgb3/cxgb3_main.c index 407d4e272075..046d846c652d 100644 --- a/drivers/net/cxgb3/cxgb3_main.c +++ b/drivers/net/cxgb3/cxgb3_main.c @@ -3341,7 +3341,6 @@ static int __devinit init_one(struct pci_dev *pdev, adapter->name = adapter->port[i]->name; __set_bit(i, &adapter->registered_device_map); - netif_tx_stop_all_queues(adapter->port[i]); } } if (!adapter->registered_device_map) { diff --git a/drivers/net/cxgb4/cxgb4_main.c b/drivers/net/cxgb4/cxgb4_main.c index f17703f410b3..f50bc98310f8 100644 --- a/drivers/net/cxgb4/cxgb4_main.c +++ b/drivers/net/cxgb4/cxgb4_main.c @@ -3736,7 +3736,6 @@ static int __devinit init_one(struct pci_dev *pdev, __set_bit(i, &adapter->registered_device_map); adapter->chan_map[adap2pinfo(adapter, i)->tx_chan] = i; - netif_tx_stop_all_queues(adapter->port[i]); } } if (!adapter->registered_device_map) { diff --git a/drivers/net/cxgb4vf/cxgb4vf_main.c b/drivers/net/cxgb4vf/cxgb4vf_main.c index 555ecc5a2e93..6de5e2e448a5 100644 --- a/drivers/net/cxgb4vf/cxgb4vf_main.c +++ b/drivers/net/cxgb4vf/cxgb4vf_main.c @@ -2600,7 +2600,6 @@ static int __devinit cxgb4vf_pci_probe(struct pci_dev *pdev, pi->xact_addr_filt = -1; pi->rx_offload = RX_CSO; netif_carrier_off(netdev); - netif_tx_stop_all_queues(netdev); netdev->irq = pdev->irq; netdev->features = (NETIF_F_SG | NETIF_F_TSO | NETIF_F_TSO6 | diff --git a/drivers/net/ibm_newemac/core.c b/drivers/net/ibm_newemac/core.c index 385dc3204cb7..06bb9b799458 100644 --- a/drivers/net/ibm_newemac/core.c +++ b/drivers/net/ibm_newemac/core.c @@ -2871,7 +2871,6 @@ static int __devinit emac_probe(struct platform_device *ofdev, SET_ETHTOOL_OPS(ndev, &emac_ethtool_ops); netif_carrier_off(ndev); - netif_stop_queue(ndev); err = register_netdev(ndev); if (err) { diff --git a/drivers/net/jme.c b/drivers/net/jme.c index d85edf3119c2..c57d9a43ceca 100644 --- a/drivers/net/jme.c +++ b/drivers/net/jme.c @@ -2955,11 +2955,7 @@ jme_init_one(struct pci_dev *pdev, * Tell stack that we are not ready to work until open() */ netif_carrier_off(netdev); - netif_stop_queue(netdev); - /* - * Register netdev - */ rc = register_netdev(netdev); if (rc) { pr_err("Cannot register net device\n"); diff --git a/drivers/net/netxen/netxen_nic_main.c b/drivers/net/netxen/netxen_nic_main.c index a75ba9517404..e1d30d7f2071 100644 --- a/drivers/net/netxen/netxen_nic_main.c +++ b/drivers/net/netxen/netxen_nic_main.c @@ -41,9 +41,6 @@ MODULE_DESCRIPTION("QLogic/NetXen (1/10) GbE Converged Ethernet Driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(NETXEN_NIC_LINUX_VERSIONID); -MODULE_FIRMWARE(NX_P2_MN_ROMIMAGE_NAME); -MODULE_FIRMWARE(NX_P3_CT_ROMIMAGE_NAME); -MODULE_FIRMWARE(NX_P3_MN_ROMIMAGE_NAME); MODULE_FIRMWARE(NX_UNIFIED_ROMIMAGE_NAME); char netxen_nic_driver_name[] = "netxen_nic"; diff --git a/drivers/net/qlcnic/qlcnic_main.c b/drivers/net/qlcnic/qlcnic_main.c index 7a298cdf9ab3..a3dcd04be22f 100644 --- a/drivers/net/qlcnic/qlcnic_main.c +++ b/drivers/net/qlcnic/qlcnic_main.c @@ -1450,7 +1450,6 @@ qlcnic_setup_netdev(struct qlcnic_adapter *adapter, netdev->irq = adapter->msix_entries[0].vector; netif_carrier_off(netdev); - netif_stop_queue(netdev); err = register_netdev(netdev); if (err) { diff --git a/drivers/net/smsc911x.h b/drivers/net/smsc911x.h index 52f38e12a879..50f712e99e96 100644 --- a/drivers/net/smsc911x.h +++ b/drivers/net/smsc911x.h @@ -22,7 +22,7 @@ #define __SMSC911X_H__ #define TX_FIFO_LOW_THRESHOLD ((u32)1600) -#define SMSC911X_EEPROM_SIZE ((u32)7) +#define SMSC911X_EEPROM_SIZE ((u32)128) #define USE_DEBUG 0 /* This is the maximum number of packets to be received every diff --git a/drivers/net/tulip/de2104x.c b/drivers/net/tulip/de2104x.c index 28e1ffb13db9..c78a50586c1d 100644 --- a/drivers/net/tulip/de2104x.c +++ b/drivers/net/tulip/de2104x.c @@ -2021,7 +2021,6 @@ static int __devinit de_init_one (struct pci_dev *pdev, de->media_timer.data = (unsigned long) de; netif_carrier_off(dev); - netif_stop_queue(dev); /* wake up device, assign resources */ rc = pci_enable_device(pdev); diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index ca7fc9df1ccf..c04d49e31f81 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -45,6 +45,7 @@ #include <linux/usb/usbnet.h> #include <linux/slab.h> #include <linux/kernel.h> +#include <linux/pm_runtime.h> #define DRIVER_VERSION "22-Aug-2005" @@ -1273,6 +1274,16 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod) struct usb_device *xdev; int status; const char *name; + struct usb_driver *driver = to_usb_driver(udev->dev.driver); + + /* usbnet already took usb runtime pm, so have to enable the feature + * for usb interface, otherwise usb_autopm_get_interface may return + * failure if USB_SUSPEND(RUNTIME_PM) is enabled. + */ + if (!driver->supports_autosuspend) { + driver->supports_autosuspend = 1; + pm_runtime_enable(&udev->dev); + } name = udev->dev.driver->name; info = (struct driver_info *) prod->driver_info; diff --git a/drivers/net/wireless/ipw2x00/libipw_module.c b/drivers/net/wireless/ipw2x00/libipw_module.c index 32dee2ce5d31..d5ef696298ee 100644 --- a/drivers/net/wireless/ipw2x00/libipw_module.c +++ b/drivers/net/wireless/ipw2x00/libipw_module.c @@ -54,6 +54,7 @@ #define DRV_DESCRIPTION "802.11 data/management/control stack" #define DRV_NAME "libipw" +#define DRV_PROCNAME "ieee80211" #define DRV_VERSION LIBIPW_VERSION #define DRV_COPYRIGHT "Copyright (C) 2004-2005 Intel Corporation <jketreno@linux.intel.com>" @@ -293,16 +294,16 @@ static int __init libipw_init(void) struct proc_dir_entry *e; libipw_debug_level = debug; - libipw_proc = proc_mkdir("ieee80211", init_net.proc_net); + libipw_proc = proc_mkdir(DRV_PROCNAME, init_net.proc_net); if (libipw_proc == NULL) { - LIBIPW_ERROR("Unable to create " DRV_NAME + LIBIPW_ERROR("Unable to create " DRV_PROCNAME " proc directory\n"); return -EIO; } e = proc_create("debug_level", S_IRUGO | S_IWUSR, libipw_proc, &debug_level_proc_fops); if (!e) { - remove_proc_entry(DRV_NAME, init_net.proc_net); + remove_proc_entry(DRV_PROCNAME, init_net.proc_net); libipw_proc = NULL; return -EIO; } @@ -319,7 +320,7 @@ static void __exit libipw_exit(void) #ifdef CONFIG_LIBIPW_DEBUG if (libipw_proc) { remove_proc_entry("debug_level", libipw_proc); - remove_proc_entry(DRV_NAME, init_net.proc_net); + remove_proc_entry(DRV_PROCNAME, init_net.proc_net); libipw_proc = NULL; } #endif /* CONFIG_LIBIPW_DEBUG */ diff --git a/drivers/net/wireless/rt2x00/Kconfig b/drivers/net/wireless/rt2x00/Kconfig index eea1ef2f502b..4396d4b9bfb9 100644 --- a/drivers/net/wireless/rt2x00/Kconfig +++ b/drivers/net/wireless/rt2x00/Kconfig @@ -221,9 +221,6 @@ config RT2X00_LIB_LEDS boolean default y if (RT2X00_LIB=y && LEDS_CLASS=y) || (RT2X00_LIB=m && LEDS_CLASS!=n) -comment "rt2x00 leds support disabled due to modularized LEDS_CLASS and built-in rt2x00" - depends on RT2X00_LIB=y && LEDS_CLASS=m - config RT2X00_LIB_DEBUGFS bool "Ralink debugfs support" depends on RT2X00_LIB && MAC80211_DEBUGFS diff --git a/drivers/rapidio/rio.c b/drivers/rapidio/rio.c index 68cf0c99138a..7b5080c45569 100644 --- a/drivers/rapidio/rio.c +++ b/drivers/rapidio/rio.c @@ -1159,11 +1159,11 @@ int __devinit rio_init_mports(void) list_for_each_entry(port, &rio_mports, node) { if (!request_mem_region(port->iores.start, - port->iores.end - port->iores.start, + resource_size(&port->iores), port->name)) { printk(KERN_ERR "RIO: Error requesting master port region 0x%016llx-0x%016llx\n", - (u64)port->iores.start, (u64)port->iores.end - 1); + (u64)port->iores.start, (u64)port->iores.end); rc = -ENOMEM; goto out; } diff --git a/drivers/rtc/rtc-ds1302.c b/drivers/rtc/rtc-ds1302.c index 359d1e04626c..f0d638922644 100644 --- a/drivers/rtc/rtc-ds1302.c +++ b/drivers/rtc/rtc-ds1302.c @@ -35,7 +35,7 @@ #ifdef CONFIG_SH_SECUREEDGE5410 #include <asm/rtc.h> -#include <mach/snapgear.h> +#include <mach/secureedge5410.h> #define RTC_RESET 0x1000 #define RTC_IODATA 0x0800 diff --git a/drivers/sh/clk/core.c b/drivers/sh/clk/core.c index fd0d1b98901c..09615b51d591 100644 --- a/drivers/sh/clk/core.c +++ b/drivers/sh/clk/core.c @@ -90,8 +90,8 @@ struct clk_rate_round_data { static long clk_rate_round_helper(struct clk_rate_round_data *rounder) { unsigned long rate_error, rate_error_prev = ~0UL; - unsigned long rate_best_fit = rounder->rate; unsigned long highest, lowest, freq; + long rate_best_fit = -ENOENT; int i; highest = 0; @@ -146,7 +146,7 @@ long clk_rate_table_round(struct clk *clk, }; if (clk->nr_freqs < 1) - return 0; + return -ENOSYS; return clk_rate_round_helper(&table_round); } @@ -541,6 +541,98 @@ long clk_round_rate(struct clk *clk, unsigned long rate) } EXPORT_SYMBOL_GPL(clk_round_rate); +long clk_round_parent(struct clk *clk, unsigned long target, + unsigned long *best_freq, unsigned long *parent_freq, + unsigned int div_min, unsigned int div_max) +{ + struct cpufreq_frequency_table *freq, *best = NULL; + unsigned long error = ULONG_MAX, freq_high, freq_low, div; + struct clk *parent = clk_get_parent(clk); + + if (!parent) { + *parent_freq = 0; + *best_freq = clk_round_rate(clk, target); + return abs(target - *best_freq); + } + + for (freq = parent->freq_table; freq->frequency != CPUFREQ_TABLE_END; + freq++) { + if (freq->frequency == CPUFREQ_ENTRY_INVALID) + continue; + + if (unlikely(freq->frequency / target <= div_min - 1)) { + unsigned long freq_max; + + freq_max = (freq->frequency + div_min / 2) / div_min; + if (error > target - freq_max) { + error = target - freq_max; + best = freq; + if (best_freq) + *best_freq = freq_max; + } + + pr_debug("too low freq %lu, error %lu\n", freq->frequency, + target - freq_max); + + if (!error) + break; + + continue; + } + + if (unlikely(freq->frequency / target >= div_max)) { + unsigned long freq_min; + + freq_min = (freq->frequency + div_max / 2) / div_max; + if (error > freq_min - target) { + error = freq_min - target; + best = freq; + if (best_freq) + *best_freq = freq_min; + } + + pr_debug("too high freq %lu, error %lu\n", freq->frequency, + freq_min - target); + + if (!error) + break; + + continue; + } + + div = freq->frequency / target; + freq_high = freq->frequency / div; + freq_low = freq->frequency / (div + 1); + + if (freq_high - target < error) { + error = freq_high - target; + best = freq; + if (best_freq) + *best_freq = freq_high; + } + + if (target - freq_low < error) { + error = target - freq_low; + best = freq; + if (best_freq) + *best_freq = freq_low; + } + + pr_debug("%u / %lu = %lu, / %lu = %lu, best %lu, parent %u\n", + freq->frequency, div, freq_high, div + 1, freq_low, + *best_freq, best->frequency); + + if (!error) + break; + } + + if (parent_freq) + *parent_freq = best->frequency; + + return error; +} +EXPORT_SYMBOL_GPL(clk_round_parent); + #ifdef CONFIG_PM static int clks_sysdev_suspend(struct sys_device *dev, pm_message_t state) { diff --git a/drivers/sh/intc/core.c b/drivers/sh/intc/core.c index 873a99ff8f64..e5e9e6735f7d 100644 --- a/drivers/sh/intc/core.c +++ b/drivers/sh/intc/core.c @@ -79,7 +79,7 @@ static void __init intc_register_irq(struct intc_desc *desc, * Register the IRQ position with the global IRQ map, then insert * it in to the radix tree. */ - irq_reserve_irqs(irq, 1); + irq_reserve_irq(irq); raw_spin_lock_irqsave(&intc_big_lock, flags); radix_tree_insert(&d->tree, enum_id, intc_irq_xlate_get(irq)); diff --git a/drivers/sh/intc/dynamic.c b/drivers/sh/intc/dynamic.c index 4187cce20ffd..a3677c9dfe36 100644 --- a/drivers/sh/intc/dynamic.c +++ b/drivers/sh/intc/dynamic.c @@ -60,5 +60,5 @@ void reserve_intc_vectors(struct intc_vect *vectors, unsigned int nr_vecs) int i; for (i = 0; i < nr_vecs; i++) - irq_reserve_irqs(evt2irq(vectors[i].vect), 1); + irq_reserve_irq(evt2irq(vectors[i].vect)); } diff --git a/drivers/staging/ath6kl/hif/sdio/linux_sdio/src/hif_scatter.c b/drivers/staging/ath6kl/hif/sdio/linux_sdio/src/hif_scatter.c index 22c6c6659f5b..ee8b47746a15 100644 --- a/drivers/staging/ath6kl/hif/sdio/linux_sdio/src/hif_scatter.c +++ b/drivers/staging/ath6kl/hif/sdio/linux_sdio/src/hif_scatter.c @@ -285,9 +285,9 @@ A_STATUS SetupHIFScatterSupport(HIF_DEVICE *device, HIF_DEVICE_SCATTER_SUPPORT_I do { /* check if host supports scatter requests and it meets our requirements */ - if (device->func->card->host->max_hw_segs < MAX_SCATTER_ENTRIES_PER_REQ) { + if (device->func->card->host->max_segs < MAX_SCATTER_ENTRIES_PER_REQ) { AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("HIF-SCATTER : host only supports scatter of : %d entries, need: %d \n", - device->func->card->host->max_hw_segs, MAX_SCATTER_ENTRIES_PER_REQ)); + device->func->card->host->max_segs, MAX_SCATTER_ENTRIES_PER_REQ)); status = A_ENOTSUP; break; } diff --git a/drivers/staging/ath6kl/os/linux/include/athendpack_linux.h b/drivers/staging/ath6kl/os/linux/include/athendpack_linux.h deleted file mode 100644 index e69de29bb2d1..000000000000 --- a/drivers/staging/ath6kl/os/linux/include/athendpack_linux.h +++ /dev/null diff --git a/drivers/staging/ath6kl/os/linux/include/athstartpack_linux.h b/drivers/staging/ath6kl/os/linux/include/athstartpack_linux.h deleted file mode 100644 index e69de29bb2d1..000000000000 --- a/drivers/staging/ath6kl/os/linux/include/athstartpack_linux.h +++ /dev/null diff --git a/drivers/staging/solo6x10/solo6010-v4l2-enc.c b/drivers/staging/solo6x10/solo6010-v4l2-enc.c index bbf3d9c4abb0..097e82bc7a63 100644 --- a/drivers/staging/solo6x10/solo6010-v4l2-enc.c +++ b/drivers/staging/solo6x10/solo6010-v4l2-enc.c @@ -766,7 +766,7 @@ static int solo_enc_open(struct file *file) &solo_enc->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_INTERLACED, - sizeof(struct videobuf_buffer), fh); + sizeof(struct videobuf_buffer), fh, NULL); spin_unlock(&solo_enc->lock); diff --git a/drivers/staging/solo6x10/solo6010-v4l2.c b/drivers/staging/solo6x10/solo6010-v4l2.c index 9731fa02b5e8..6ffd21de837d 100644 --- a/drivers/staging/solo6x10/solo6010-v4l2.c +++ b/drivers/staging/solo6x10/solo6010-v4l2.c @@ -437,7 +437,7 @@ static int solo_v4l2_open(struct file *file) &solo_dev->pdev->dev, &fh->slock, V4L2_BUF_TYPE_VIDEO_CAPTURE, SOLO_DISP_PIX_FIELD, - sizeof(struct videobuf_buffer), fh); + sizeof(struct videobuf_buffer), fh, NULL); return 0; } diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile new file mode 100644 index 000000000000..c43ef48b1a0f --- /dev/null +++ b/drivers/tty/Makefile @@ -0,0 +1,11 @@ +obj-y += tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o \ + tty_buffer.o tty_port.o tty_mutex.o +obj-$(CONFIG_LEGACY_PTYS) += pty.o +obj-$(CONFIG_UNIX98_PTYS) += pty.o +obj-$(CONFIG_AUDIT) += tty_audit.o +obj-$(CONFIG_MAGIC_SYSRQ) += sysrq.o +obj-$(CONFIG_N_HDLC) += n_hdlc.o +obj-$(CONFIG_N_GSM) += n_gsm.o +obj-$(CONFIG_R3964) += n_r3964.o + +obj-y += vt/ diff --git a/drivers/char/n_gsm.c b/drivers/tty/n_gsm.c index 04ef3ef0a422..04ef3ef0a422 100644 --- a/drivers/char/n_gsm.c +++ b/drivers/tty/n_gsm.c diff --git a/drivers/char/n_hdlc.c b/drivers/tty/n_hdlc.c index 47d32281032c..47d32281032c 100644 --- a/drivers/char/n_hdlc.c +++ b/drivers/tty/n_hdlc.c diff --git a/drivers/char/n_r3964.c b/drivers/tty/n_r3964.c index 88dda0c45ee0..88dda0c45ee0 100644 --- a/drivers/char/n_r3964.c +++ b/drivers/tty/n_r3964.c diff --git a/drivers/char/n_tty.c b/drivers/tty/n_tty.c index 428f4fe0b5f7..428f4fe0b5f7 100644 --- a/drivers/char/n_tty.c +++ b/drivers/tty/n_tty.c diff --git a/drivers/char/pty.c b/drivers/tty/pty.c index 923a48585501..923a48585501 100644 --- a/drivers/char/pty.c +++ b/drivers/tty/pty.c diff --git a/drivers/char/sysrq.c b/drivers/tty/sysrq.c index eaa5d3efa79d..eaa5d3efa79d 100644 --- a/drivers/char/sysrq.c +++ b/drivers/tty/sysrq.c diff --git a/drivers/char/tty_audit.c b/drivers/tty/tty_audit.c index f64582b0f623..f64582b0f623 100644 --- a/drivers/char/tty_audit.c +++ b/drivers/tty/tty_audit.c diff --git a/drivers/char/tty_buffer.c b/drivers/tty/tty_buffer.c index cc1e9850d655..cc1e9850d655 100644 --- a/drivers/char/tty_buffer.c +++ b/drivers/tty/tty_buffer.c diff --git a/drivers/char/tty_io.c b/drivers/tty/tty_io.c index c05c5af5aa04..c05c5af5aa04 100644 --- a/drivers/char/tty_io.c +++ b/drivers/tty/tty_io.c diff --git a/drivers/char/tty_ioctl.c b/drivers/tty/tty_ioctl.c index 0c1889971459..0c1889971459 100644 --- a/drivers/char/tty_ioctl.c +++ b/drivers/tty/tty_ioctl.c diff --git a/drivers/char/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 412f9775d19c..412f9775d19c 100644 --- a/drivers/char/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c diff --git a/drivers/char/tty_mutex.c b/drivers/tty/tty_mutex.c index 133697540c73..133697540c73 100644 --- a/drivers/char/tty_mutex.c +++ b/drivers/tty/tty_mutex.c diff --git a/drivers/char/tty_port.c b/drivers/tty/tty_port.c index 33d37d230f8f..33d37d230f8f 100644 --- a/drivers/char/tty_port.c +++ b/drivers/tty/tty_port.c diff --git a/drivers/char/.gitignore b/drivers/tty/vt/.gitignore index 83683a2d8e6a..83683a2d8e6a 100644 --- a/drivers/char/.gitignore +++ b/drivers/tty/vt/.gitignore diff --git a/drivers/tty/vt/Makefile b/drivers/tty/vt/Makefile new file mode 100644 index 000000000000..14a51c9960df --- /dev/null +++ b/drivers/tty/vt/Makefile @@ -0,0 +1,34 @@ +# +# This file contains the font map for the default (hardware) font +# +FONTMAPFILE = cp437.uni + +obj-$(CONFIG_VT) += vt_ioctl.o vc_screen.o \ + selection.o keyboard.o +obj-$(CONFIG_CONSOLE_TRANSLATIONS) += consolemap.o consolemap_deftbl.o +obj-$(CONFIG_HW_CONSOLE) += vt.o defkeymap.o + +# Files generated that shall be removed upon make clean +clean-files := consolemap_deftbl.c defkeymap.c + +quiet_cmd_conmk = CONMK $@ + cmd_conmk = scripts/conmakehash $< > $@ + +$(obj)/consolemap_deftbl.c: $(src)/$(FONTMAPFILE) + $(call cmd,conmk) + +$(obj)/defkeymap.o: $(obj)/defkeymap.c + +# Uncomment if you're changing the keymap and have an appropriate +# loadkeys version for the map. By default, we'll use the shipped +# versions. +# GENERATE_KEYMAP := 1 + +ifdef GENERATE_KEYMAP + +$(obj)/defkeymap.c: $(obj)/%.c: $(src)/%.map + loadkeys --mktable $< > $@.tmp + sed -e 's/^static *//' $@.tmp > $@ + rm $@.tmp + +endif diff --git a/drivers/char/consolemap.c b/drivers/tty/vt/consolemap.c index 45d3e80156d4..45d3e80156d4 100644 --- a/drivers/char/consolemap.c +++ b/drivers/tty/vt/consolemap.c diff --git a/drivers/char/cp437.uni b/drivers/tty/vt/cp437.uni index bc6163484f62..bc6163484f62 100644 --- a/drivers/char/cp437.uni +++ b/drivers/tty/vt/cp437.uni diff --git a/drivers/char/defkeymap.c_shipped b/drivers/tty/vt/defkeymap.c_shipped index d2208dfe3f67..d2208dfe3f67 100644 --- a/drivers/char/defkeymap.c_shipped +++ b/drivers/tty/vt/defkeymap.c_shipped diff --git a/drivers/char/defkeymap.map b/drivers/tty/vt/defkeymap.map index 50b30cace261..50b30cace261 100644 --- a/drivers/char/defkeymap.map +++ b/drivers/tty/vt/defkeymap.map diff --git a/drivers/char/keyboard.c b/drivers/tty/vt/keyboard.c index e95d7876ca6b..e95d7876ca6b 100644 --- a/drivers/char/keyboard.c +++ b/drivers/tty/vt/keyboard.c diff --git a/drivers/char/selection.c b/drivers/tty/vt/selection.c index ebae344ce910..ebae344ce910 100644 --- a/drivers/char/selection.c +++ b/drivers/tty/vt/selection.c diff --git a/drivers/char/vc_screen.c b/drivers/tty/vt/vc_screen.c index 273ab44cc91d..273ab44cc91d 100644 --- a/drivers/char/vc_screen.c +++ b/drivers/tty/vt/vc_screen.c diff --git a/drivers/char/vt.c b/drivers/tty/vt/vt.c index a8ec48ed14d9..a8ec48ed14d9 100644 --- a/drivers/char/vt.c +++ b/drivers/tty/vt/vt.c diff --git a/drivers/char/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c index 6b68a0fb4611..6b68a0fb4611 100644 --- a/drivers/char/vt_ioctl.c +++ b/drivers/tty/vt/vt_ioctl.c diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c index cb23355f52d3..fbe86ca95802 100644 --- a/drivers/usb/gadget/u_ether.c +++ b/drivers/usb/gadget/u_ether.c @@ -811,7 +811,6 @@ int gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN]) INFO(dev, "MAC %pM\n", net->dev_addr); INFO(dev, "HOST MAC %pM\n", dev->host_mac); - netif_stop_queue(net); the_dev = dev; } diff --git a/drivers/video/backlight/adp8860_bl.c b/drivers/video/backlight/adp8860_bl.c index 3ec24609151e..734c650a47c4 100644 --- a/drivers/video/backlight/adp8860_bl.c +++ b/drivers/video/backlight/adp8860_bl.c @@ -502,8 +502,10 @@ static ssize_t adp8860_bl_l1_daylight_max_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct adp8860_bl *data = dev_get_drvdata(dev); + int ret = strict_strtoul(buf, 10, &data->cached_daylight_max); + if (ret) + return ret; - strict_strtoul(buf, 10, &data->cached_daylight_max); return adp8860_store(dev, buf, count, ADP8860_BLMX1); } static DEVICE_ATTR(l1_daylight_max, 0664, adp8860_bl_l1_daylight_max_show, @@ -614,7 +616,7 @@ static ssize_t adp8860_bl_ambient_light_zone_store(struct device *dev, if (val == 0) { /* Enable automatic ambient light sensing */ adp8860_set_bits(data->client, ADP8860_MDCR, CMP_AUTOEN); - } else if ((val > 0) && (val < 6)) { + } else if ((val > 0) && (val <= 3)) { /* Disable automatic ambient light sensing */ adp8860_clr_bits(data->client, ADP8860_MDCR, CMP_AUTOEN); @@ -622,7 +624,7 @@ static ssize_t adp8860_bl_ambient_light_zone_store(struct device *dev, mutex_lock(&data->lock); adp8860_read(data->client, ADP8860_CFGR, ®_val); reg_val &= ~(CFGR_BLV_MASK << CFGR_BLV_SHIFT); - reg_val |= val << CFGR_BLV_SHIFT; + reg_val |= (val - 1) << CFGR_BLV_SHIFT; adp8860_write(data->client, ADP8860_CFGR, reg_val); mutex_unlock(&data->lock); } diff --git a/drivers/video/backlight/l4f00242t03.c b/drivers/video/backlight/l4f00242t03.c index 9093ef0fa869..c67801e57aaf 100644 --- a/drivers/video/backlight/l4f00242t03.c +++ b/drivers/video/backlight/l4f00242t03.c @@ -78,7 +78,7 @@ static int l4f00242t03_lcd_power_set(struct lcd_device *ld, int power) const u16 slpin = 0x10; const u16 disoff = 0x28; - if (power) { + if (power <= FB_BLANK_NORMAL) { if (priv->lcd_on) return 0; diff --git a/drivers/video/backlight/lms283gf05.c b/drivers/video/backlight/lms283gf05.c index abc43a0eb97d..5d3cf33953ac 100644 --- a/drivers/video/backlight/lms283gf05.c +++ b/drivers/video/backlight/lms283gf05.c @@ -129,7 +129,7 @@ static int lms283gf05_power_set(struct lcd_device *ld, int power) struct spi_device *spi = st->spi; struct lms283gf05_pdata *pdata = spi->dev.platform_data; - if (power) { + if (power <= FB_BLANK_NORMAL) { if (pdata) lms283gf05_reset(pdata->reset_gpio, pdata->reset_inverted); diff --git a/drivers/video/backlight/mbp_nvidia_bl.c b/drivers/video/backlight/mbp_nvidia_bl.c index 9fb533f6373e..1485f7345f49 100644 --- a/drivers/video/backlight/mbp_nvidia_bl.c +++ b/drivers/video/backlight/mbp_nvidia_bl.c @@ -335,6 +335,24 @@ static const struct dmi_system_id __initdata mbp_device_table[] = { }, .driver_data = (void *)&nvidia_chipset_data, }, + { + .callback = mbp_dmi_match, + .ident = "MacBookAir 3,1", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir3,1"), + }, + .driver_data = (void *)&nvidia_chipset_data, + }, + { + .callback = mbp_dmi_match, + .ident = "MacBookAir 3,2", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir3,2"), + }, + .driver_data = (void *)&nvidia_chipset_data, + }, { } }; diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c index 550443518891..21866ec69656 100644 --- a/drivers/video/backlight/pwm_bl.c +++ b/drivers/video/backlight/pwm_bl.c @@ -25,6 +25,7 @@ struct pwm_bl_data { struct pwm_device *pwm; struct device *dev; unsigned int period; + unsigned int lth_brightness; int (*notify)(struct device *, int brightness); }; @@ -48,7 +49,9 @@ static int pwm_backlight_update_status(struct backlight_device *bl) pwm_config(pb->pwm, 0, pb->period); pwm_disable(pb->pwm); } else { - pwm_config(pb->pwm, brightness * pb->period / max, pb->period); + brightness = pb->lth_brightness + + (brightness * (pb->period - pb->lth_brightness) / max); + pwm_config(pb->pwm, brightness, pb->period); pwm_enable(pb->pwm); } return 0; @@ -92,6 +95,8 @@ static int pwm_backlight_probe(struct platform_device *pdev) pb->period = data->pwm_period_ns; pb->notify = data->notify; + pb->lth_brightness = data->lth_brightness * + (data->pwm_period_ns / data->max_brightness); pb->dev = &pdev->dev; pb->pwm = pwm_request(data->pwm_id, "backlight"); diff --git a/drivers/video/backlight/s6e63m0.c b/drivers/video/backlight/s6e63m0.c index a3128c9cb7ad..5927db0da999 100644 --- a/drivers/video/backlight/s6e63m0.c +++ b/drivers/video/backlight/s6e63m0.c @@ -729,10 +729,10 @@ static ssize_t s6e63m0_sysfs_show_gamma_table(struct device *dev, return strlen(buf); } -static DEVICE_ATTR(gamma_table, 0644, +static DEVICE_ATTR(gamma_table, 0444, s6e63m0_sysfs_show_gamma_table, NULL); -static int __init s6e63m0_probe(struct spi_device *spi) +static int __devinit s6e63m0_probe(struct spi_device *spi) { int ret = 0; struct s6e63m0 *lcd = NULL; @@ -829,6 +829,9 @@ static int __devexit s6e63m0_remove(struct spi_device *spi) struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev); s6e63m0_power(lcd, FB_BLANK_POWERDOWN); + device_remove_file(&spi->dev, &dev_attr_gamma_table); + device_remove_file(&spi->dev, &dev_attr_gamma_mode); + backlight_device_unregister(lcd->bd); lcd_device_unregister(lcd->ld); kfree(lcd); |
