Merge branch 'for-linus' of git://git.o-hand.com/linux-rpurdie-backlight
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 6 Apr 2009 20:24:00 +0000 (13:24 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 6 Apr 2009 20:24:00 +0000 (13:24 -0700)
* 'for-linus' of git://git.o-hand.com/linux-rpurdie-backlight:
  backlight: Adds HP Jornada 700 series backlight driver
  backlight: Add HP Jornada 700 series LCD driver
  backlight: fix pwm_bl.c when multiple PWM backlights exist
  backlight: mbp_nvidia_bl - Add a debug switch
  backlight: Add support for MacBook 5, MacBook Air 2, and MacBook Pro 5

35 files changed:
Documentation/powerpc/dts-bindings/gpio/led.txt
block/blk-core.c
block/blk-sysfs.c
block/cfq-iosched.c
block/elevator.c
drivers/leds/Kconfig
drivers/leds/Makefile
drivers/leds/led-class.c
drivers/leds/led-triggers.c
drivers/leds/leds-bd2802.c [new file with mode: 0644]
drivers/leds/leds-dac124s085.c [new file with mode: 0644]
drivers/leds/leds-gpio.c
drivers/leds/leds-h1940.c
drivers/leds/leds-pca9532.c
drivers/leds/leds-pwm.c [new file with mode: 0644]
drivers/leds/leds-rb532.c [new file with mode: 0644]
drivers/leds/leds-s3c24xx.c
drivers/leds/leds.h
drivers/leds/ledtrig-default-on.c
drivers/leds/ledtrig-gpio.c [new file with mode: 0644]
drivers/leds/ledtrig-heartbeat.c
drivers/leds/ledtrig-ide-disk.c
drivers/leds/ledtrig-timer.c
fs/buffer.c
fs/direct-io.c
fs/jbd/commit.c
fs/jbd2/commit.c
include/linux/backing-dev.h
include/linux/bio.h
include/linux/blkdev.h
include/linux/fs.h
include/linux/leds-bd2802.h [new file with mode: 0644]
include/linux/leds.h
include/linux/leds_pwm.h [new file with mode: 0644]
mm/backing-dev.c

index ff51f4c0fa9d19bbc23c05773a229acbb18a1b78..4fe14deedc0a3e5aa3988288873c5e4f9a2ee01c 100644 (file)
@@ -1,15 +1,43 @@
-LED connected to GPIO
+LEDs connected to GPIO lines
 
 Required properties:
-- compatible : should be "gpio-led".
-- label : (optional) the label for this LED. If omitted, the label is
+- compatible : should be "gpio-leds".
+
+Each LED is represented as a sub-node of the gpio-leds device.  Each
+node's name represents the name of the corresponding LED.
+
+LED sub-node properties:
+- gpios :  Should specify the LED's GPIO, see "Specifying GPIO information
+  for devices" in Documentation/powerpc/booting-without-of.txt.  Active
+  low LEDs should be indicated using flags in the GPIO specifier.
+- label :  (optional) The label for this LED.  If omitted, the label is
   taken from the node name (excluding the unit address).
-- gpios : should specify LED GPIO.
+- linux,default-trigger :  (optional) This parameter, if present, is a
+  string defining the trigger assigned to the LED.  Current triggers are:
+    "backlight" - LED will act as a back-light, controlled by the framebuffer
+                 system
+    "default-on" - LED will turn on
+    "heartbeat" - LED "double" flashes at a load average based rate
+    "ide-disk" - LED indicates disk activity
+    "timer" - LED flashes at a fixed, configurable rate
 
-Example:
+Examples:
 
-led@0 {
-       compatible = "gpio-led";
-       label = "hdd";
-       gpios = <&mcu_pio 0 1>;
+leds {
+       compatible = "gpio-leds";
+       hdd {
+               label = "IDE Activity";
+               gpios = <&mcu_pio 0 1>; /* Active low */
+               linux,default-trigger = "ide-disk";
+       };
 };
+
+run-control {
+       compatible = "gpio-leds";
+       red {
+               gpios = <&mpc8572 6 0>;
+       };
+       green {
+               gpios = <&mpc8572 7 0>;
+       };
+}
index 996ed906d8ca518c62dba691ad7b579ee794e1a0..25572802dac2bccc42fff74b91a915b7bd001693 100644 (file)
@@ -484,11 +484,11 @@ static int blk_init_free_list(struct request_queue *q)
 {
        struct request_list *rl = &q->rq;
 
-       rl->count[READ] = rl->count[WRITE] = 0;
-       rl->starved[READ] = rl->starved[WRITE] = 0;
+       rl->count[BLK_RW_SYNC] = rl->count[BLK_RW_ASYNC] = 0;
+       rl->starved[BLK_RW_SYNC] = rl->starved[BLK_RW_ASYNC] = 0;
        rl->elvpriv = 0;
-       init_waitqueue_head(&rl->wait[READ]);
-       init_waitqueue_head(&rl->wait[WRITE]);
+       init_waitqueue_head(&rl->wait[BLK_RW_SYNC]);
+       init_waitqueue_head(&rl->wait[BLK_RW_ASYNC]);
 
        rl->rq_pool = mempool_create_node(BLKDEV_MIN_RQ, mempool_alloc_slab,
                                mempool_free_slab, request_cachep, q->node);
@@ -699,18 +699,18 @@ static void ioc_set_batching(struct request_queue *q, struct io_context *ioc)
        ioc->last_waited = jiffies;
 }
 
-static void __freed_request(struct request_queue *q, int rw)
+static void __freed_request(struct request_queue *q, int sync)
 {
        struct request_list *rl = &q->rq;
 
-       if (rl->count[rw] < queue_congestion_off_threshold(q))
-               blk_clear_queue_congested(q, rw);
+       if (rl->count[sync] < queue_congestion_off_threshold(q))
+               blk_clear_queue_congested(q, sync);
 
-       if (rl->count[rw] + 1 <= q->nr_requests) {
-               if (waitqueue_active(&rl->wait[rw]))
-                       wake_up(&rl->wait[rw]);
+       if (rl->count[sync] + 1 <= q->nr_requests) {
+               if (waitqueue_active(&rl->wait[sync]))
+                       wake_up(&rl->wait[sync]);
 
-               blk_clear_queue_full(q, rw);
+               blk_clear_queue_full(q, sync);
        }
 }
 
@@ -718,18 +718,18 @@ static void __freed_request(struct request_queue *q, int rw)
  * A request has just been released.  Account for it, update the full and
  * congestion status, wake up any waiters.   Called under q->queue_lock.
  */
-static void freed_request(struct request_queue *q, int rw, int priv)
+static void freed_request(struct request_queue *q, int sync, int priv)
 {
        struct request_list *rl = &q->rq;
 
-       rl->count[rw]--;
+       rl->count[sync]--;
        if (priv)
                rl->elvpriv--;
 
-       __freed_request(q, rw);
+       __freed_request(q, sync);
 
-       if (unlikely(rl->starved[rw ^ 1]))
-               __freed_request(q, rw ^ 1);
+       if (unlikely(rl->starved[sync ^ 1]))
+               __freed_request(q, sync ^ 1);
 }
 
 /*
@@ -743,15 +743,15 @@ static struct request *get_request(struct request_queue *q, int rw_flags,
        struct request *rq = NULL;
        struct request_list *rl = &q->rq;
        struct io_context *ioc = NULL;
-       const int rw = rw_flags & 0x01;
+       const bool is_sync = rw_is_sync(rw_flags) != 0;
        int may_queue, priv;
 
        may_queue = elv_may_queue(q, rw_flags);
        if (may_queue == ELV_MQUEUE_NO)
                goto rq_starved;
 
-       if (rl->count[rw]+1 >= queue_congestion_on_threshold(q)) {
-               if (rl->count[rw]+1 >= q->nr_requests) {
+       if (rl->count[is_sync]+1 >= queue_congestion_on_threshold(q)) {
+               if (rl->count[is_sync]+1 >= q->nr_requests) {
                        ioc = current_io_context(GFP_ATOMIC, q->node);
                        /*
                         * The queue will fill after this allocation, so set
@@ -759,9 +759,9 @@ static struct request *get_request(struct request_queue *q, int rw_flags,
                         * This process will be allowed to complete a batch of
                         * requests, others will be blocked.
                         */
-                       if (!blk_queue_full(q, rw)) {
+                       if (!blk_queue_full(q, is_sync)) {
                                ioc_set_batching(q, ioc);
-                               blk_set_queue_full(q, rw);
+                               blk_set_queue_full(q, is_sync);
                        } else {
                                if (may_queue != ELV_MQUEUE_MUST
                                                && !ioc_batching(q, ioc)) {
@@ -774,7 +774,7 @@ static struct request *get_request(struct request_queue *q, int rw_flags,
                                }
                        }
                }
-               blk_set_queue_congested(q, rw);
+               blk_set_queue_congested(q, is_sync);
        }
 
        /*
@@ -782,11 +782,11 @@ static struct request *get_request(struct request_queue *q, int rw_flags,
         * limit of requests, otherwise we could have thousands of requests
         * allocated with any setting of ->nr_requests
         */
-       if (rl->count[rw] >= (3 * q->nr_requests / 2))
+       if (rl->count[is_sync] >= (3 * q->nr_requests / 2))
                goto out;
 
-       rl->count[rw]++;
-       rl->starved[rw] = 0;
+       rl->count[is_sync]++;
+       rl->starved[is_sync] = 0;
 
        priv = !test_bit(QUEUE_FLAG_ELVSWITCH, &q->queue_flags);
        if (priv)
@@ -804,7 +804,7 @@ static struct request *get_request(struct request_queue *q, int rw_flags,
                 * wait queue, but this is pretty rare.
                 */
                spin_lock_irq(q->queue_lock);
-               freed_request(q, rw, priv);
+               freed_request(q, is_sync, priv);
 
                /*
                 * in the very unlikely event that allocation failed and no
@@ -814,8 +814,8 @@ static struct request *get_request(struct request_queue *q, int rw_flags,
                 * rq mempool into READ and WRITE
                 */
 rq_starved:
-               if (unlikely(rl->count[rw] == 0))
-                       rl->starved[rw] = 1;
+               if (unlikely(rl->count[is_sync] == 0))
+                       rl->starved[is_sync] = 1;
 
                goto out;
        }
@@ -829,7 +829,7 @@ rq_starved:
        if (ioc_batching(q, ioc))
                ioc->nr_batch_requests--;
 
-       trace_block_getrq(q, bio, rw);
+       trace_block_getrq(q, bio, rw_flags & 1);
 out:
        return rq;
 }
@@ -843,7 +843,7 @@ out:
 static struct request *get_request_wait(struct request_queue *q, int rw_flags,
                                        struct bio *bio)
 {
-       const int rw = rw_flags & 0x01;
+       const bool is_sync = rw_is_sync(rw_flags) != 0;
        struct request *rq;
 
        rq = get_request(q, rw_flags, bio, GFP_NOIO);
@@ -852,10 +852,10 @@ static struct request *get_request_wait(struct request_queue *q, int rw_flags,
                struct io_context *ioc;
                struct request_list *rl = &q->rq;
 
-               prepare_to_wait_exclusive(&rl->wait[rw], &wait,
+               prepare_to_wait_exclusive(&rl->wait[is_sync], &wait,
                                TASK_UNINTERRUPTIBLE);
 
-               trace_block_sleeprq(q, bio, rw);
+               trace_block_sleeprq(q, bio, rw_flags & 1);
 
                __generic_unplug_device(q);
                spin_unlock_irq(q->queue_lock);
@@ -871,7 +871,7 @@ static struct request *get_request_wait(struct request_queue *q, int rw_flags,
                ioc_set_batching(q, ioc);
 
                spin_lock_irq(q->queue_lock);
-               finish_wait(&rl->wait[rw], &wait);
+               finish_wait(&rl->wait[is_sync], &wait);
 
                rq = get_request(q, rw_flags, bio, GFP_NOIO);
        };
@@ -1070,14 +1070,14 @@ void __blk_put_request(struct request_queue *q, struct request *req)
         * it didn't come out of our reserved rq pools
         */
        if (req->cmd_flags & REQ_ALLOCED) {
-               int rw = rq_data_dir(req);
+               int is_sync = rq_is_sync(req) != 0;
                int priv = req->cmd_flags & REQ_ELVPRIV;
 
                BUG_ON(!list_empty(&req->queuelist));
                BUG_ON(!hlist_unhashed(&req->hash));
 
                blk_free_request(q, req);
-               freed_request(q, rw, priv);
+               freed_request(q, is_sync, priv);
        }
 }
 EXPORT_SYMBOL_GPL(__blk_put_request);
@@ -1128,6 +1128,8 @@ void init_request_from_bio(struct request *req, struct bio *bio)
                req->cmd_flags |= REQ_UNPLUG;
        if (bio_rw_meta(bio))
                req->cmd_flags |= REQ_RW_META;
+       if (bio_noidle(bio))
+               req->cmd_flags |= REQ_NOIDLE;
 
        req->errors = 0;
        req->hard_sector = req->sector = bio->bi_sector;
@@ -1136,6 +1138,15 @@ void init_request_from_bio(struct request *req, struct bio *bio)
        blk_rq_bio_prep(req->q, req, bio);
 }
 
+/*
+ * Only disabling plugging for non-rotational devices if it does tagging
+ * as well, otherwise we do need the proper merging
+ */
+static inline bool queue_should_plug(struct request_queue *q)
+{
+       return !(blk_queue_nonrot(q) && blk_queue_tagged(q));
+}
+
 static int __make_request(struct request_queue *q, struct bio *bio)
 {
        struct request *req;
@@ -1242,11 +1253,11 @@ get_rq:
        if (test_bit(QUEUE_FLAG_SAME_COMP, &q->queue_flags) ||
            bio_flagged(bio, BIO_CPU_AFFINE))
                req->cpu = blk_cpu_to_group(smp_processor_id());
-       if (!blk_queue_nonrot(q) && elv_queue_empty(q))
+       if (queue_should_plug(q) && elv_queue_empty(q))
                blk_plug_device(q);
        add_request(q, req);
 out:
-       if (unplug || blk_queue_nonrot(q))
+       if (unplug || !queue_should_plug(q))
                __generic_unplug_device(q);
        spin_unlock_irq(q->queue_lock);
        return 0;
index e29ddfc73cf475d99f95d2daa7d0be58a474d01b..3ff9bba3379a84891ddbc97450fcdbddf6e42a1a 100644 (file)
@@ -48,28 +48,28 @@ queue_requests_store(struct request_queue *q, const char *page, size_t count)
        q->nr_requests = nr;
        blk_queue_congestion_threshold(q);
 
-       if (rl->count[READ] >= queue_congestion_on_threshold(q))
-               blk_set_queue_congested(q, READ);
-       else if (rl->count[READ] < queue_congestion_off_threshold(q))
-               blk_clear_queue_congested(q, READ);
-
-       if (rl->count[WRITE] >= queue_congestion_on_threshold(q))
-               blk_set_queue_congested(q, WRITE);
-       else if (rl->count[WRITE] < queue_congestion_off_threshold(q))
-               blk_clear_queue_congested(q, WRITE);
-
-       if (rl->count[READ] >= q->nr_requests) {
-               blk_set_queue_full(q, READ);
-       } else if (rl->count[READ]+1 <= q->nr_requests) {
-               blk_clear_queue_full(q, READ);
-               wake_up(&rl->wait[READ]);
+       if (rl->count[BLK_RW_SYNC] >= queue_congestion_on_threshold(q))
+               blk_set_queue_congested(q, BLK_RW_SYNC);
+       else if (rl->count[BLK_RW_SYNC] < queue_congestion_off_threshold(q))
+               blk_clear_queue_congested(q, BLK_RW_SYNC);
+
+       if (rl->count[BLK_RW_ASYNC] >= queue_congestion_on_threshold(q))
+               blk_set_queue_congested(q, BLK_RW_ASYNC);
+       else if (rl->count[BLK_RW_ASYNC] < queue_congestion_off_threshold(q))
+               blk_clear_queue_congested(q, BLK_RW_ASYNC);
+
+       if (rl->count[BLK_RW_SYNC] >= q->nr_requests) {
+               blk_set_queue_full(q, BLK_RW_SYNC);
+       } else if (rl->count[BLK_RW_SYNC]+1 <= q->nr_requests) {
+               blk_clear_queue_full(q, BLK_RW_SYNC);
+               wake_up(&rl->wait[BLK_RW_SYNC]);
        }
 
-       if (rl->count[WRITE] >= q->nr_requests) {
-               blk_set_queue_full(q, WRITE);
-       } else if (rl->count[WRITE]+1 <= q->nr_requests) {
-               blk_clear_queue_full(q, WRITE);
-               wake_up(&rl->wait[WRITE]);
+       if (rl->count[BLK_RW_ASYNC] >= q->nr_requests) {
+               blk_set_queue_full(q, BLK_RW_ASYNC);
+       } else if (rl->count[BLK_RW_ASYNC]+1 <= q->nr_requests) {
+               blk_clear_queue_full(q, BLK_RW_ASYNC);
+               wake_up(&rl->wait[BLK_RW_ASYNC]);
        }
        spin_unlock_irq(q->queue_lock);
        return ret;
index 664ebfd092ec21f95cedc56b6a5259874db0399c..9e809345f71abd718cecc27f6fd4ea068ed4775c 100644 (file)
@@ -1992,8 +1992,10 @@ static void cfq_completed_request(struct request_queue *q, struct request *rq)
                }
                if (cfq_slice_used(cfqq) || cfq_class_idle(cfqq))
                        cfq_slice_expired(cfqd, 1);
-               else if (sync && RB_EMPTY_ROOT(&cfqq->sort_list))
+               else if (sync && !rq_noidle(rq) &&
+                        RB_EMPTY_ROOT(&cfqq->sort_list)) {
                        cfq_arm_slice_timer(cfqd);
+               }
        }
 
        if (!cfqd->rq_in_driver)
index 98259eda0ef66d4051cc5da958c0191f353b8158..ca6788a0195ac6d3e117009dcaecb7dd952c593c 100644 (file)
@@ -677,7 +677,7 @@ void elv_insert(struct request_queue *q, struct request *rq, int where)
        }
 
        if (unplug_it && blk_queue_plugged(q)) {
-               int nrq = q->rq.count[READ] + q->rq.count[WRITE]
+               int nrq = q->rq.count[BLK_RW_SYNC] + q->rq.count[BLK_RW_ASYNC]
                        - q->in_flight;
 
                if (nrq >= q->unplug_thresh)
index d9db17624f129fc88a3d1e50f49d69661e39ba99..9b60b6b684d9cd7c5a62c843d827e6071491a51f 100644 (file)
@@ -31,6 +31,13 @@ config LEDS_LOCOMO
          This option enables support for the LEDs on Sharp Locomo.
          Zaurus models SL-5500 and SL-5600.
 
+config LEDS_MIKROTIK_RB532
+       tristate "LED Support for Mikrotik Routerboard 532"
+       depends on LEDS_CLASS && MIKROTIK_RB532
+       help
+         This option enables support for the so called "User LED" of
+         Mikrotik's Routerboard 532.
+
 config LEDS_S3C24XX
        tristate "LED Support for Samsung S3C24XX GPIO LEDs"
        depends on LEDS_CLASS && ARCH_S3C2410
@@ -117,11 +124,40 @@ config LEDS_GPIO
        help
          This option enables support for the LEDs connected to GPIO
          outputs. To be useful the particular board must have LEDs
-         and they must be connected to the GPIO lines.
+         and they must be connected to the GPIO lines.  The LEDs must be
+         defined as platform devices and/or OpenFirmware platform devices.
+         The code to use these bindings can be selected below.
+
+config LEDS_GPIO_PLATFORM
+       bool "Platform device bindings for GPIO LEDs"
+       depends on LEDS_GPIO
+       default y
+       help
+         Let the leds-gpio driver drive LEDs which have been defined as
+         platform devices.  If you don't know what this means, say yes.
+
+config LEDS_GPIO_OF
+       bool "OpenFirmware platform device bindings for GPIO LEDs"
+       depends on LEDS_GPIO && OF_DEVICE
+       default y
+       help
+         Let the leds-gpio driver drive LEDs which have been defined as
+         of_platform devices.  For instance, LEDs which are listed in a "dts"
+         file.
+
+config LEDS_LP5521
+       tristate "LED Support for the LP5521 LEDs"
+       depends on LEDS_CLASS && I2C
+       help
+         If you say 'Y' here you get support for the National Semiconductor
+         LP5521 LED driver used in n8x0 boards.
+
+         This driver can be built as a module by choosing 'M'. The module
+         will be called leds-lp5521.
 
 config LEDS_CLEVO_MAIL
-       tristate "Mail LED on Clevo notebook (EXPERIMENTAL)"
-       depends on LEDS_CLASS && X86 && SERIO_I8042 && DMI && EXPERIMENTAL
+       tristate "Mail LED on Clevo notebook"
+       depends on LEDS_CLASS && X86 && SERIO_I8042 && DMI
        help
          This driver makes the mail LED accessible from userspace
          programs through the leds subsystem. This LED have three
@@ -171,6 +207,26 @@ config LEDS_DA903X
          This option enables support for on-chip LED drivers found
          on Dialog Semiconductor DA9030/DA9034 PMICs.
 
+config LEDS_DAC124S085
+       tristate "LED Support for DAC124S085 SPI DAC"
+       depends on LEDS_CLASS && SPI
+       help
+         This option enables support for DAC124S085 SPI DAC from NatSemi,
+         which can be used to control up to four LEDs.
+
+config LEDS_PWM
+       tristate "PWM driven LED Support"
+       depends on LEDS_CLASS && HAVE_PWM
+       help
+         This option enables support for pwm driven LEDs
+
+config LEDS_BD2802
+       tristate "LED driver for BD2802 RGB LED"
+       depends on LEDS_CLASS && I2C
+       help
+         This option enables support for BD2802GU RGB LED driver chips
+         accessed via the I2C bus.
+
 comment "LED Triggers"
 
 config LEDS_TRIGGERS
@@ -216,6 +272,19 @@ config LEDS_TRIGGER_BACKLIGHT
 
          If unsure, say N.
 
+config LEDS_TRIGGER_GPIO
+       tristate "LED GPIO Trigger"
+       depends on LEDS_TRIGGERS
+       depends on GPIOLIB
+       help
+         This allows LEDs to be controlled by gpio events. It's good
+         when using gpios as switches and triggering the needed LEDs
+         from there. One use case is n810's keypad LEDs that could
+         be triggered by this trigger when user slides up to show
+         keypad.
+
+         If unsure, say N.
+
 config LEDS_TRIGGER_DEFAULT_ON
        tristate "LED Default ON Trigger"
        depends on LEDS_TRIGGERS
index 9d76f0f160a43323101ead59a16db80d0865ce14..2d41c4dcf92ff9a8db515aac17aee9dc9482db96 100644 (file)
@@ -6,7 +6,9 @@ obj-$(CONFIG_LEDS_TRIGGERS)             += led-triggers.o
 
 # LED Platform Drivers
 obj-$(CONFIG_LEDS_ATMEL_PWM)           += leds-atmel-pwm.o
+obj-$(CONFIG_LEDS_BD2802)              += leds-bd2802.o
 obj-$(CONFIG_LEDS_LOCOMO)              += leds-locomo.o
+obj-$(CONFIG_LEDS_MIKROTIK_RB532)      += leds-rb532.o
 obj-$(CONFIG_LEDS_S3C24XX)             += leds-s3c24xx.o
 obj-$(CONFIG_LEDS_AMS_DELTA)           += leds-ams-delta.o
 obj-$(CONFIG_LEDS_NET48XX)             += leds-net48xx.o
@@ -24,10 +26,15 @@ obj-$(CONFIG_LEDS_FSG)                      += leds-fsg.o
 obj-$(CONFIG_LEDS_PCA955X)             += leds-pca955x.o
 obj-$(CONFIG_LEDS_DA903X)              += leds-da903x.o
 obj-$(CONFIG_LEDS_WM8350)              += leds-wm8350.o
+obj-$(CONFIG_LEDS_PWM)                 += leds-pwm.o
+
+# LED SPI Drivers
+obj-$(CONFIG_LEDS_DAC124S085)          += leds-dac124s085.o
 
 # LED Triggers
 obj-$(CONFIG_LEDS_TRIGGER_TIMER)       += ledtrig-timer.o
 obj-$(CONFIG_LEDS_TRIGGER_IDE_DISK)    += ledtrig-ide-disk.o
 obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT)   += ledtrig-heartbeat.o
 obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT)   += ledtrig-backlight.o
+obj-$(CONFIG_LEDS_TRIGGER_GPIO)                += ledtrig-gpio.o
 obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON)  += ledtrig-default-on.o
index 52f82e3ea13aae5b04a603938fba9dbbd0654712..f2cc13d76810f457c752d6f92f53759980f465ea 100644 (file)
@@ -64,7 +64,16 @@ static ssize_t led_brightness_store(struct device *dev,
        return ret;
 }
 
+static ssize_t led_max_brightness_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%u\n", led_cdev->max_brightness);
+}
+
 static DEVICE_ATTR(brightness, 0644, led_brightness_show, led_brightness_store);
+static DEVICE_ATTR(max_brightness, 0444, led_max_brightness_show, NULL);
 #ifdef CONFIG_LEDS_TRIGGERS
 static DEVICE_ATTR(trigger, 0644, led_trigger_show, led_trigger_store);
 #endif
@@ -138,6 +147,13 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
        list_add_tail(&led_cdev->node, &leds_list);
        up_write(&leds_list_lock);
 
+       if (!led_cdev->max_brightness)
+               led_cdev->max_brightness = LED_FULL;
+
+       rc = device_create_file(led_cdev->dev, &dev_attr_max_brightness);
+       if (rc)
+               goto err_out_attr_max;
+
        led_update_brightness(led_cdev);
 
 #ifdef CONFIG_LEDS_TRIGGERS
@@ -155,9 +171,11 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
 
 #ifdef CONFIG_LEDS_TRIGGERS
 err_out_led_list:
+       device_remove_file(led_cdev->dev, &dev_attr_max_brightness);
+#endif
+err_out_attr_max:
        device_remove_file(led_cdev->dev, &dev_attr_brightness);
        list_del(&led_cdev->node);
-#endif
 err_out:
        device_unregister(led_cdev->dev);
        return rc;
@@ -172,6 +190,7 @@ EXPORT_SYMBOL_GPL(led_classdev_register);
  */
 void led_classdev_unregister(struct led_classdev *led_cdev)
 {
+       device_remove_file(led_cdev->dev, &dev_attr_max_brightness);
        device_remove_file(led_cdev->dev, &dev_attr_brightness);
 #ifdef CONFIG_LEDS_TRIGGERS
        device_remove_file(led_cdev->dev, &dev_attr_trigger);
index f910eaffe3a6f057cd835412204c304f1bd0af38..d8ddd9ef89949da41b8b7b1f9e840b302772ff19 100644 (file)
@@ -156,12 +156,20 @@ EXPORT_SYMBOL_GPL(led_trigger_set_default);
 int led_trigger_register(struct led_trigger *trigger)
 {
        struct led_classdev *led_cdev;
+       struct led_trigger *trig;
 
        rwlock_init(&trigger->leddev_list_lock);
        INIT_LIST_HEAD(&trigger->led_cdevs);
 
-       /* Add to the list of led triggers */
        down_write(&triggers_list_lock);
+       /* Make sure the trigger's name isn't already in use */
+       list_for_each_entry(trig, &trigger_list, next_trig) {
+               if (!strcmp(trig->name, trigger->name)) {
+                       up_write(&triggers_list_lock);
+                       return -EEXIST;
+               }
+       }
+       /* Add to the list of led triggers */
        list_add_tail(&trigger->next_trig, &trigger_list);
        up_write(&triggers_list_lock);
 
diff --git a/drivers/leds/leds-bd2802.c b/drivers/leds/leds-bd2802.c
new file mode 100644 (file)
index 0000000..4149ecb
--- /dev/null
@@ -0,0 +1,765 @@
+/*
+ * leds-bd2802.c - RGB LED Driver
+ *
+ * Copyright (C) 2009 Samsung Electronics
+ * Kim Kyuwon <q1.kim@samsung.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.
+ *
+ * Datasheet: http://www.rohm.com/products/databook/driver/pdf/bd2802gu-e.pdf
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/leds.h>
+#include <linux/leds-bd2802.h>
+
+
+#define LED_CTL(rgb2en, rgb1en) ((rgb2en) << 4 | ((rgb1en) << 0))
+
+#define BD2802_LED_OFFSET              0xa
+#define BD2802_COLOR_OFFSET            0x3
+
+#define BD2802_REG_CLKSETUP            0x00
+#define BD2802_REG_CONTROL             0x01
+#define BD2802_REG_HOURSETUP           0x02
+#define BD2802_REG_CURRENT1SETUP       0x03
+#define BD2802_REG_CURRENT2SETUP       0x04
+#define BD2802_REG_WAVEPATTERN         0x05
+
+#define BD2802_CURRENT_032             0x10 /* 3.2mA */
+#define BD2802_CURRENT_000             0x00 /* 0.0mA */
+
+#define BD2802_PATTERN_FULL            0x07
+#define BD2802_PATTERN_HALF            0x03
+
+enum led_ids {
+       LED1,
+       LED2,
+       LED_NUM,
+};
+
+enum led_colors {
+       RED,
+       GREEN,
+       BLUE,
+};
+
+enum led_bits {
+       BD2802_OFF,
+       BD2802_BLINK,
+       BD2802_ON,
+};
+
+/*
+ * State '0' : 'off'
+ * State '1' : 'blink'
+ * State '2' : 'on'.
+ */
+struct led_state {
+       unsigned r:2;
+       unsigned g:2;
+       unsigned b:2;
+};
+
+struct bd2802_led {
+       struct bd2802_led_platform_data *pdata;
+       struct i2c_client               *client;
+       struct rw_semaphore             rwsem;
+       struct work_struct              work;
+
+       struct led_state                led[2];
+
+       /*
+        * Making led_classdev as array is not recommended, because array
+        * members prevent using 'container_of' macro. So repetitive works
+        * are needed.
+        */
+       struct led_classdev             cdev_led1r;
+       struct led_classdev             cdev_led1g;
+       struct led_classdev             cdev_led1b;
+       struct led_classdev             cdev_led2r;
+       struct led_classdev             cdev_led2g;
+       struct led_classdev             cdev_led2b;
+
+       /*
+        * Advanced Configuration Function(ADF) mode:
+        * In ADF mode, user can set registers of BD2802GU directly,
+        * therefore BD2802GU doesn't enter reset state.
+        */
+       int                             adf_on;
+
+       enum led_ids                    led_id;
+       enum led_colors                 color;
+       enum led_bits                   state;
+};
+
+
+/*--------------------------------------------------------------*/
+/*     BD2802GU helper functions                                       */
+/*--------------------------------------------------------------*/
+
+static inline int bd2802_is_rgb_off(struct bd2802_led *led, enum led_ids id,
+                                                       enum led_colors color)
+{
+       switch (color) {
+       case RED:
+               return !led->led[id].r;
+       case GREEN:
+               return !led->led[id].g;
+       case BLUE:
+               return !led->led[id].b;
+       default:
+               dev_err(&led->client->dev, "%s: Invalid color\n", __func__);
+               return -EINVAL;
+       }
+}
+
+static inline int bd2802_is_led_off(struct bd2802_led *led, enum led_ids id)
+{
+       if (led->led[id].r || led->led[id].g || led->led[id].b)
+               return 0;
+
+       return 1;
+}
+
+static inline int bd2802_is_all_off(struct bd2802_led *led)
+{
+       int i;
+
+       for (i = 0; i < LED_NUM; i++)
+               if (!bd2802_is_led_off(led, i))
+                       return 0;
+
+       return 1;
+}
+
+static inline u8 bd2802_get_base_offset(enum led_ids id, enum led_colors color)
+{
+       return id * BD2802_LED_OFFSET + color * BD2802_COLOR_OFFSET;
+}
+
+static inline u8 bd2802_get_reg_addr(enum led_ids id, enum led_colors color,
+                                                               u8 reg_offset)
+{
+       return reg_offset + bd2802_get_base_offset(id, color);
+}
+
+
+/*--------------------------------------------------------------*/
+/*     BD2802GU core functions                                 */
+/*--------------------------------------------------------------*/
+
+static int bd2802_write_byte(struct i2c_client *client, u8 reg, u8 val)
+{
+       int ret = i2c_smbus_write_byte_data(client, reg, val);
+       if (ret >= 0)
+               return 0;
+
+       dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n",
+                                               __func__, reg, val, ret);
+
+       return ret;
+}
+
+static void bd2802_update_state(struct bd2802_led *led, enum led_ids id,
+                               enum led_colors color, enum led_bits led_bit)
+{
+       int i;
+       u8 value;
+
+       for (i = 0; i < LED_NUM; i++) {
+               if (i == id) {
+                       switch (color) {
+                       case RED:
+                               led->led[i].r = led_bit;
+                               break;
+                       case GREEN:
+                               led->led[i].g = led_bit;
+                               break;
+                       case BLUE:
+                               led->led[i].b = led_bit;
+                               break;
+                       default:
+                               dev_err(&led->client->dev,
+                                       "%s: Invalid color\n", __func__);
+                               return;
+                       }
+               }
+       }
+
+       if (led_bit == BD2802_BLINK || led_bit == BD2802_ON)
+               return;
+
+       if (!bd2802_is_led_off(led, id))
+               return;
+
+       if (bd2802_is_all_off(led) && !led->adf_on) {
+               gpio_set_value(led->pdata->reset_gpio, 0);
+               return;
+       }
+
+       /*
+        * In this case, other led is turned on, and current led is turned
+        * off. So set RGB LED Control register to stop the current RGB LED
+        */
+       value = (id == LED1) ? LED_CTL(1, 0) : LED_CTL(0, 1);
+       bd2802_write_byte(led->client, BD2802_REG_CONTROL, value);
+}
+
+static void bd2802_configure(struct bd2802_led *led)
+{
+       struct bd2802_led_platform_data *pdata = led->pdata;
+       u8 reg;
+
+       reg = bd2802_get_reg_addr(LED1, RED, BD2802_REG_HOURSETUP);
+       bd2802_write_byte(led->client, reg, pdata->rgb_time);
+
+       reg = bd2802_get_reg_addr(LED2, RED, BD2802_REG_HOURSETUP);
+       bd2802_write_byte(led->client, reg, pdata->rgb_time);
+}
+
+static void bd2802_reset_cancel(struct bd2802_led *led)
+{
+       gpio_set_value(led->pdata->reset_gpio, 1);
+       udelay(100);
+       bd2802_configure(led);
+}
+
+static void bd2802_enable(struct bd2802_led *led, enum led_ids id)
+{
+       enum led_ids other_led = (id == LED1) ? LED2 : LED1;
+       u8 value, other_led_on;
+
+       other_led_on = !bd2802_is_led_off(led, other_led);
+       if (id == LED1)
+               value = LED_CTL(other_led_on, 1);
+       else
+               value = LED_CTL(1 , other_led_on);
+
+       bd2802_write_byte(led->client, BD2802_REG_CONTROL, value);
+}
+
+static void bd2802_set_on(struct bd2802_led *led, enum led_ids id,
+                                                       enum led_colors color)
+{
+       u8 reg;
+
+       if (bd2802_is_all_off(led) && !led->adf_on)
+               bd2802_reset_cancel(led);
+
+       reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT1SETUP);
+       bd2802_write_byte(led->client, reg, BD2802_CURRENT_032);
+       reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT2SETUP);
+       bd2802_write_byte(led->client, reg, BD2802_CURRENT_000);
+       reg = bd2802_get_reg_addr(id, color, BD2802_REG_WAVEPATTERN);
+       bd2802_write_byte(led->client, reg, BD2802_PATTERN_FULL);
+
+       bd2802_enable(led, id);
+       bd2802_update_state(led, id, color, BD2802_ON);
+}
+
+static void bd2802_set_blink(struct bd2802_led *led, enum led_ids id,
+                                                       enum led_colors color)
+{
+       u8 reg;
+
+       if (bd2802_is_all_off(led) && !led->adf_on)
+               bd2802_reset_cancel(led);
+
+       reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT1SETUP);
+       bd2802_write_byte(led->client, reg, BD2802_CURRENT_000);
+       reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT2SETUP);
+       bd2802_write_byte(led->client, reg, BD2802_CURRENT_032);
+       reg = bd2802_get_reg_addr(id, color, BD2802_REG_WAVEPATTERN);
+       bd2802_write_byte(led->client, reg, BD2802_PATTERN_HALF);
+
+       bd2802_enable(led, id);
+       bd2802_update_state(led, id, color, BD2802_BLINK);
+}
+
+static void bd2802_turn_on(struct bd2802_led *led, enum led_ids id,
+                               enum led_colors color, enum led_bits led_bit)
+{
+       if (led_bit == BD2802_OFF) {
+               dev_err(&led->client->dev,
+                                       "Only 'blink' and 'on' are allowed\n");
+               return;
+       }
+
+       if (led_bit == BD2802_BLINK)
+               bd2802_set_blink(led, id, color);
+       else
+               bd2802_set_on(led, id, color);
+}
+
+static void bd2802_turn_off(struct bd2802_led *led, enum led_ids id,
+                                                       enum led_colors color)
+{
+       u8 reg;
+
+       if (bd2802_is_rgb_off(led, id, color))
+               return;
+
+       reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT1SETUP);
+       bd2802_write_byte(led->client, reg, BD2802_CURRENT_000);
+       reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT2SETUP);
+       bd2802_write_byte(led->client, reg, BD2802_CURRENT_000);
+
+       bd2802_update_state(led, id, color, BD2802_OFF);
+}
+
+static void bd2802_restore_state(struct bd2802_led *led)
+{
+       int i;
+
+       for (i = 0; i < LED_NUM; i++) {
+               if (led->led[i].r)
+                       bd2802_turn_on(led, i, RED, led->led[i].r);
+               if (led->led[i].g)
+                       bd2802_turn_on(led, i, GREEN, led->led[i].g);
+               if (led->led[i].b)
+                       bd2802_turn_on(led, i, BLUE, led->led[i].b);
+       }
+}
+
+#define BD2802_SET_REGISTER(reg_addr, reg_name)                                \
+static ssize_t bd2802_store_reg##reg_addr(struct device *dev,          \
+       struct device_attribute *attr, const char *buf, size_t count)   \
+{                                                                      \
+       struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));\
+       unsigned long val;                                              \
+       int ret;                                                        \
+       if (!count)                                                     \
+               return -EINVAL;                                         \
+       ret = strict_strtoul(buf, 16, &val);                            \
+       if (ret)                                                        \
+               return ret;                                             \
+       down_write(&led->rwsem);                                        \
+       bd2802_write_byte(led->client, reg_addr, (u8) val);             \
+       up_write(&led->rwsem);                                          \
+       return count;                                                   \
+}                                                                      \
+static struct device_attribute bd2802_reg##reg_addr##_attr = {         \
+       .attr = {.name = reg_name, .mode = 0644, .owner = THIS_MODULE}, \
+       .store = bd2802_store_reg##reg_addr,                            \
+};
+
+BD2802_SET_REGISTER(0x00, "0x00");
+BD2802_SET_REGISTER(0x01, "0x01");
+BD2802_SET_REGISTER(0x02, "0x02");
+BD2802_SET_REGISTER(0x03, "0x03");
+BD2802_SET_REGISTER(0x04, "0x04");
+BD2802_SET_REGISTER(0x05, "0x05");
+BD2802_SET_REGISTER(0x06, "0x06");
+BD2802_SET_REGISTER(0x07, "0x07");
+BD2802_SET_REGISTER(0x08, "0x08");
+BD2802_SET_REGISTER(0x09, "0x09");
+BD2802_SET_REGISTER(0x0a, "0x0a");
+BD2802_SET_REGISTER(0x0b, "0x0b");
+BD2802_SET_REGISTER(0x0c, "0x0c");
+BD2802_SET_REGISTER(0x0d, "0x0d");
+BD2802_SET_REGISTER(0x0e, "0x0e");
+BD2802_SET_REGISTER(0x0f, "0x0f");
+BD2802_SET_REGISTER(0x10, "0x10");
+BD2802_SET_REGISTER(0x11, "0x11");
+BD2802_SET_REGISTER(0x12, "0x12");
+BD2802_SET_REGISTER(0x13, "0x13");
+BD2802_SET_REGISTER(0x14, "0x14");
+BD2802_SET_REGISTER(0x15, "0x15");
+
+static struct device_attribute *bd2802_addr_attributes[] = {
+       &bd2802_reg0x00_attr,
+       &bd2802_reg0x01_attr,
+       &bd2802_reg0x02_attr,
+       &bd2802_reg0x03_attr,
+       &bd2802_reg0x04_attr,
+       &bd2802_reg0x05_attr,
+       &bd2802_reg0x06_attr,
+       &bd2802_reg0x07_attr,
+       &bd2802_reg0x08_attr,
+       &bd2802_reg0x09_attr,
+       &bd2802_reg0x0a_attr,
+       &bd2802_reg0x0b_attr,
+       &bd2802_reg0x0c_attr,
+       &bd2802_reg0x0d_attr,
+       &bd2802_reg0x0e_attr,
+       &bd2802_reg0x0f_attr,
+       &bd2802_reg0x10_attr,
+       &bd2802_reg0x11_attr,
+       &bd2802_reg0x12_attr,
+       &bd2802_reg0x13_attr,
+       &bd2802_reg0x14_attr,
+       &bd2802_reg0x15_attr,
+};
+
+static void bd2802_enable_adv_conf(struct bd2802_led *led)
+{
+       int i, ret;
+
+       for (i = 0; i < ARRAY_SIZE(bd2802_addr_attributes); i++) {
+               ret = device_create_file(&led->client->dev,
+                                               bd2802_addr_attributes[i]);
+               if (ret) {
+                       dev_err(&led->client->dev, "failed to sysfs file %s\n",
+                                       bd2802_addr_attributes[i]->attr.name);
+                       goto failed_remove_files;
+               }
+       }
+
+       if (bd2802_is_all_off(led))
+               bd2802_reset_cancel(led);
+
+       led->adf_on = 1;
+
+       return;
+
+failed_remove_files:
+       for (i--; i >= 0; i--)
+               device_remove_file(&led->client->dev,
+                                               bd2802_addr_attributes[i]);
+}
+
+static void bd2802_disable_adv_conf(struct bd2802_led *led)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(bd2802_addr_attributes); i++)
+               device_remove_file(&led->client->dev,
+                                               bd2802_addr_attributes[i]);
+
+       if (bd2802_is_all_off(led))
+               gpio_set_value(led->pdata->reset_gpio, 0);
+
+       led->adf_on = 0;
+}
+
+static ssize_t bd2802_show_adv_conf(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));
+       ssize_t ret;
+
+       down_read(&led->rwsem);
+       if (led->adf_on)
+               ret = sprintf(buf, "on\n");
+       else
+               ret = sprintf(buf, "off\n");
+       up_read(&led->rwsem);
+
+       return ret;
+}
+
+static ssize_t bd2802_store_adv_conf(struct device *dev,
+       struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));
+
+       if (!count)
+               return -EINVAL;
+
+       down_write(&led->rwsem);
+       if (!led->adf_on && !strncmp(buf, "on", 2))
+               bd2802_enable_adv_conf(led);
+       else if (led->adf_on && !strncmp(buf, "off", 3))
+               bd2802_disable_adv_conf(led);
+       up_write(&led->rwsem);
+
+       return count;
+}
+
+static struct device_attribute bd2802_adv_conf_attr = {
+       .attr = {
+               .name = "advanced_configuration",
+               .mode = 0644,
+               .owner = THIS_MODULE
+       },
+       .show = bd2802_show_adv_conf,
+       .store = bd2802_store_adv_conf,
+};
+
+static void bd2802_led_work(struct work_struct *work)
+{
+       struct bd2802_led *led = container_of(work, struct bd2802_led, work);
+
+       if (led->state)
+               bd2802_turn_on(led, led->led_id, led->color, led->state);
+       else
+               bd2802_turn_off(led, led->led_id, led->color);
+}
+
+#define BD2802_CONTROL_RGBS(name, id, clr)                             \
+static void bd2802_set_##name##_brightness(struct led_classdev *led_cdev,\
+                                       enum led_brightness value)      \
+{                                                                      \
+       struct bd2802_led *led =                                        \
+               container_of(led_cdev, struct bd2802_led, cdev_##name); \
+       led->led_id = id;                                               \
+       led->color = clr;                                               \
+       if (value == LED_OFF)                                           \
+               led->state = BD2802_OFF;                                \
+       else                                                            \
+               led->state = BD2802_ON;                                 \
+       schedule_work(&led->work);                                      \
+}                                                                      \
+static int bd2802_set_##name##_blink(struct led_classdev *led_cdev,    \
+               unsigned long *delay_on, unsigned long *delay_off)      \
+{                                                                      \
+       struct bd2802_led *led =                                        \
+               container_of(led_cdev, struct bd2802_led, cdev_##name); \
+       if (*delay_on == 0 || *delay_off == 0)                          \
+               return -EINVAL;                                         \
+       led->led_id = id;                                               \
+       led->color = clr;                                               \
+       led->state = BD2802_BLINK;                                      \
+       schedule_work(&led->work);                                      \
+       return 0;                                                       \
+}
+
+BD2802_CONTROL_RGBS(led1r, LED1, RED);
+BD2802_CONTROL_RGBS(led1g, LED1, GREEN);
+BD2802_CONTROL_RGBS(led1b, LED1, BLUE);
+BD2802_CONTROL_RGBS(led2r, LED2, RED);
+BD2802_CONTROL_RGBS(led2g, LED2, GREEN);
+BD2802_CONTROL_RGBS(led2b, LED2, BLUE);
+
+static int bd2802_register_led_classdev(struct bd2802_led *led)
+{
+       int ret;
+
+       INIT_WORK(&led->work, bd2802_led_work);
+
+       led->cdev_led1r.name = "led1_R";
+       led->cdev_led1r.brightness = LED_OFF;
+       led->cdev_led1r.brightness_set = bd2802_set_led1r_brightness;
+       led->cdev_led1r.blink_set = bd2802_set_led1r_blink;
+       led->cdev_led1r.flags |= LED_CORE_SUSPENDRESUME;
+
+       ret = led_classdev_register(&led->client->dev, &led->cdev_led1r);
+       if (ret < 0) {
+               dev_err(&led->client->dev, "couldn't register LED %s\n",
+                                                       led->cdev_led1r.name);
+               goto failed_unregister_led1_R;
+       }
+
+       led->cdev_led1g.name = "led1_G";
+       led->cdev_led1g.brightness = LED_OFF;
+       led->cdev_led1g.brightness_set = bd2802_set_led1g_brightness;
+       led->cdev_led1g.blink_set = bd2802_set_led1g_blink;
+       led->cdev_led1g.flags |= LED_CORE_SUSPENDRESUME;
+
+       ret = led_classdev_register(&led->client->dev, &led->cdev_led1g);
+       if (ret < 0) {
+               dev_err(&led->client->dev, "couldn't register LED %s\n",
+                                                       led->cdev_led1g.name);
+               goto failed_unregister_led1_G;
+       }
+
+       led->cdev_led1b.name = "led1_B";
+       led->cdev_led1b.brightness = LED_OFF;
+       led->cdev_led1b.brightness_set = bd2802_set_led1b_brightness;
+       led->cdev_led1b.blink_set = bd2802_set_led1b_blink;
+       led->cdev_led1b.flags |= LED_CORE_SUSPENDRESUME;
+
+       ret = led_classdev_register(&led->client->dev, &led->cdev_led1b);
+       if (ret < 0) {
+               dev_err(&led->client->dev, "couldn't register LED %s\n",
+                                                       led->cdev_led1b.name);
+               goto failed_unregister_led1_B;
+       }
+
+       led->cdev_led2r.name = "led2_R";
+       led->cdev_led2r.brightness = LED_OFF;
+       led->cdev_led2r.brightness_set = bd2802_set_led2r_brightness;
+       led->cdev_led2r.blink_set = bd2802_set_led2r_blink;
+       led->cdev_led2r.flags |= LED_CORE_SUSPENDRESUME;
+
+       ret = led_classdev_register(&led->client->dev, &led->cdev_led2r);
+       if (ret < 0) {
+               dev_err(&led->client->dev, "couldn't register LED %s\n",
+                                                       led->cdev_led2r.name);
+               goto failed_unregister_led2_R;
+       }
+
+       led->cdev_led2g.name = "led2_G";
+       led->cdev_led2g.brightness = LED_OFF;
+       led->cdev_led2g.brightness_set = bd2802_set_led2g_brightness;
+       led->cdev_led2g.blink_set = bd2802_set_led2g_blink;
+       led->cdev_led2g.flags |= LED_CORE_SUSPENDRESUME;
+
+       ret = led_classdev_register(&led->client->dev, &led->cdev_led2g);
+       if (ret < 0) {
+               dev_err(&led->client->dev, "couldn't register LED %s\n",
+                                                       led->cdev_led2g.name);
+               goto failed_unregister_led2_G;
+       }
+
+       led->cdev_led2b.name = "led2_B";
+       led->cdev_led2b.brightness = LED_OFF;
+       led->cdev_led2b.brightness_set = bd2802_set_led2b_brightness;
+       led->cdev_led2b.blink_set = bd2802_set_led2b_blink;
+       led->cdev_led2b.flags |= LED_CORE_SUSPENDRESUME;
+
+       ret = led_classdev_register(&led->client->dev, &led->cdev_led2b);
+       if (ret < 0) {
+               dev_err(&led->client->dev, "couldn't register LED %s\n",
+                                                       led->cdev_led2b.name);
+               goto failed_unregister_led2_B;
+       }
+
+       return 0;
+
+failed_unregister_led2_B:
+       led_classdev_unregister(&led->cdev_led2g);
+failed_unregister_led2_G:
+       led_classdev_unregister(&led->cdev_led2r);
+failed_unregister_led2_R:
+       led_classdev_unregister(&led->cdev_led1b);
+failed_unregister_led1_B:
+       led_classdev_unregister(&led->cdev_led1g);
+failed_unregister_led1_G:
+       led_classdev_unregister(&led->cdev_led1r);
+failed_unregister_led1_R:
+
+       return ret;
+}
+
+static void bd2802_unregister_led_classdev(struct bd2802_led *led)
+{
+       cancel_work_sync(&led->work);
+       led_classdev_unregister(&led->cdev_led1r);
+}
+
+static int __devinit bd2802_probe(struct i2c_client *client,
+                       const struct i2c_device_id *id)
+{
+       struct bd2802_led *led;
+       struct bd2802_led_platform_data *pdata;
+       int ret;
+
+       led = kzalloc(sizeof(struct bd2802_led), GFP_KERNEL);
+       if (!led) {
+               dev_err(&client->dev, "failed to allocate driver data\n");
+               return -ENOMEM;
+       }
+
+       led->client = client;
+       pdata = led->pdata = client->dev.platform_data;
+       i2c_set_clientdata(client, led);
+
+       /* Configure RESET GPIO (L: RESET, H: RESET cancel) */
+       gpio_request(pdata->reset_gpio, "RGB_RESETB");
+       gpio_direction_output(pdata->reset_gpio, 1);
+
+       /* Tacss = min 0.1ms */
+       udelay(100);
+
+       /* Detect BD2802GU */
+       ret = bd2802_write_byte(client, BD2802_REG_CLKSETUP, 0x00);
+       if (ret < 0) {
+               dev_err(&client->dev, "failed to detect device\n");
+               goto failed_free;
+       } else
+               dev_info(&client->dev, "return 0x%02x\n", ret);
+
+       /* To save the power, reset BD2802 after detecting */
+       gpio_set_value(led->pdata->reset_gpio, 0);
+
+       init_rwsem(&led->rwsem);
+
+       ret = device_create_file(&client->dev, &bd2802_adv_conf_attr);
+       if (ret) {
+               dev_err(&client->dev, "failed to create sysfs file %s\n",
+                                       bd2802_adv_conf_attr.attr.name);
+               goto failed_free;
+       }
+
+       ret = bd2802_register_led_classdev(led);
+       if (ret < 0)
+               goto failed_unregister_dev_file;
+
+       return 0;
+
+failed_unregister_dev_file:
+       device_remove_file(&client->dev, &bd2802_adv_conf_attr);
+failed_free:
+       i2c_set_clientdata(client, NULL);
+       kfree(led);
+
+       return ret;
+}
+
+static int __exit bd2802_remove(struct i2c_client *client)
+{
+       struct bd2802_led *led = i2c_get_clientdata(client);
+
+       bd2802_unregister_led_classdev(led);
+       gpio_set_value(led->pdata->reset_gpio, 0);
+       if (led->adf_on)
+               bd2802_disable_adv_conf(led);
+       device_remove_file(&client->dev, &bd2802_adv_conf_attr);
+       i2c_set_clientdata(client, NULL);
+       kfree(led);
+
+       return 0;
+}
+
+static int bd2802_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+       struct bd2802_led *led = i2c_get_clientdata(client);
+
+       gpio_set_value(led->pdata->reset_gpio, 0);
+
+       return 0;
+}
+
+static int bd2802_resume(struct i2c_client *client)
+{
+       struct bd2802_led *led = i2c_get_clientdata(client);
+
+       if (!bd2802_is_all_off(led) || led->adf_on) {
+               gpio_set_value(led->pdata->reset_gpio, 1);
+               udelay(100);
+               bd2802_restore_state(led);
+       }
+
+       return 0;
+}
+
+static const struct i2c_device_id bd2802_id[] = {
+       { "BD2802", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, bd2802_id);
+
+static struct i2c_driver bd2802_i2c_driver = {
+       .driver = {
+               .name   = "BD2802",
+       },
+       .probe          = bd2802_probe,
+       .remove         = __exit_p(bd2802_remove),
+       .suspend        = bd2802_suspend,
+       .resume         = bd2802_resume,
+       .id_table       = bd2802_id,
+};
+
+static int __init bd2802_init(void)
+{
+       return i2c_add_driver(&bd2802_i2c_driver);
+}
+module_init(bd2802_init);
+
+static void __exit bd2802_exit(void)
+{
+       i2c_del_driver(&bd2802_i2c_driver);
+}
+module_exit(bd2802_exit);
+
+MODULE_AUTHOR("Kim Kyuwon <q1.kim@samsung.com>");
+MODULE_DESCRIPTION("BD2802 LED driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/leds-dac124s085.c b/drivers/leds/leds-dac124s085.c
new file mode 100644 (file)
index 0000000..098d9aa
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2008
+ * Guennadi Liakhovetski, DENX Software Engineering, <lg@denx.de>
+ *
+ * This file is subject to the terms and conditions of version 2 of
+ * the GNU General Public License.  See the file COPYING in the main
+ * directory of this archive for more details.
+ *
+ * LED driver for the DAC124S085 SPI DAC
+ */
+
+#include <linux/gfp.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <linux/spi/spi.h>
+
+struct dac124s085_led {
+       struct led_classdev     ldev;
+       struct spi_device       *spi;
+       int                     id;
+       int                     brightness;
+       char                    name[sizeof("dac124s085-3")];
+
+       struct mutex            mutex;
+       struct work_struct      work;
+       spinlock_t              lock;
+};
+
+struct dac124s085 {
+       struct dac124s085_led leds[4];
+};
+
+#define REG_WRITE              (0 << 12)
+#define REG_WRITE_UPDATE       (1 << 12)
+#define ALL_WRITE_UPDATE       (2 << 12)
+#define POWER_DOWN_OUTPUT      (3 << 12)
+
+static void dac124s085_led_work(struct work_struct *work)
+{
+       struct dac124s085_led *led = container_of(work, struct dac124s085_led,
+                                                 work);
+       u16 word;
+
+       mutex_lock(&led->mutex);
+       word = cpu_to_le16(((led->id) << 14) | REG_WRITE_UPDATE |
+                          (led->brightness & 0xfff));
+       spi_write(led->spi, (const u8 *)&word, sizeof(word));
+       mutex_unlock(&led->mutex);
+}
+
+static void dac124s085_set_brightness(struct led_classdev *ldev,
+                                     enum led_brightness brightness)
+{
+       struct dac124s085_led *led = container_of(ldev, struct dac124s085_led,
+                                                 ldev);
+
+       spin_lock(&led->lock);
+       led->brightness = brightness;
+       schedule_work(&led->work);
+       spin_unlock(&led->lock);
+}
+
+static int dac124s085_probe(struct spi_device *spi)
+{
+       struct dac124s085       *dac;
+       struct dac124s085_led   *led;
+       int i, ret;
+
+       dac = kzalloc(sizeof(*dac), GFP_KERNEL);
+       if (!dac)
+               return -ENOMEM;
+
+       spi->bits_per_word = 16;
+
+       for (i = 0; i < ARRAY_SIZE(dac->leds); i++) {
+               led             = dac->leds + i;
+               led->id         = i;
+               led->brightness = LED_OFF;
+               led->spi        = spi;
+               snprintf(led->name, sizeof(led->name), "dac124s085-%d", i);
+               spin_lock_init(&led->lock);
+               INIT_WORK(&led->work, dac124s085_led_work);
+               mutex_init(&led->mutex);
+               led->ldev.name = led->name;
+               led->ldev.brightness = LED_OFF;
+               led->ldev.max_brightness = 0xfff;
+               led->ldev.brightness_set = dac124s085_set_brightness;
+               ret = led_classdev_register(&spi->dev, &led->ldev);
+               if (ret < 0)
+                       goto eledcr;
+       }
+
+       spi_set_drvdata(spi, dac);
+
+       return 0;
+
+eledcr:
+       while (i--)
+               led_classdev_unregister(&dac->leds[i].ldev);
+
+       spi_set_drvdata(spi, NULL);
+       kfree(dac);
+       return ret;
+}
+
+static int dac124s085_remove(struct spi_device *spi)
+{
+       struct dac124s085       *dac = spi_get_drvdata(spi);
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(dac->leds); i++) {
+               led_classdev_unregister(&dac->leds[i].ldev);
+               cancel_work_sync(&dac->leds[i].work);
+       }
+
+       spi_set_drvdata(spi, NULL);
+       kfree(dac);
+
+       return 0;
+}
+
+static struct spi_driver dac124s085_driver = {
+       .probe          = dac124s085_probe,
+       .remove         = dac124s085_remove,
+       .driver = {
+               .name   = "dac124s085",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init dac124s085_leds_init(void)
+{
+       return spi_register_driver(&dac124s085_driver);
+}
+
+static void __exit dac124s085_leds_exit(void)
+{
+       spi_unregister_driver(&dac124s085_driver);
+}
+
+module_init(dac124s085_leds_init);
+module_exit(dac124s085_leds_exit);
+
+MODULE_AUTHOR("Guennadi Liakhovetski <lg@denx.de>");
+MODULE_DESCRIPTION("DAC124S085 LED driver");
+MODULE_LICENSE("GPL v2");
index 2e3df08b649b6c2a3a2dd83b62233bc3505a1459..102ef4a14c5fea075c40226b3914cffe61f8b2d0 100644 (file)
@@ -3,6 +3,7 @@
  *
  * Copyright (C) 2007 8D Technologies inc.
  * Raphael Assenat <raph@8d.com>
+ * Copyright (C) 2008 Freescale Semiconductor, Inc.
  *
  * 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
@@ -71,11 +72,67 @@ static int gpio_blink_set(struct led_classdev *led_cdev,
        return led_dat->platform_gpio_blink_set(led_dat->gpio, delay_on, delay_off);
 }
 
+static int __devinit create_gpio_led(const struct gpio_led *template,
+       struct gpio_led_data *led_dat, struct device *parent,
+       int (*blink_set)(unsigned, unsigned long *, unsigned long *))
+{
+       int ret;
+
+       /* skip leds that aren't available */
+       if (!gpio_is_valid(template->gpio)) {
+               printk(KERN_INFO "Skipping unavilable LED gpio %d (%s)\n", 
+                               template->gpio, template->name);
+               return;
+       }
+
+       ret = gpio_request(template->gpio, template->name);
+       if (ret < 0)
+               return ret;
+
+       led_dat->cdev.name = template->name;
+       led_dat->cdev.default_trigger = template->default_trigger;
+       led_dat->gpio = template->gpio;
+       led_dat->can_sleep = gpio_cansleep(template->gpio);
+       led_dat->active_low = template->active_low;
+       if (blink_set) {
+               led_dat->platform_gpio_blink_set = blink_set;
+               led_dat->cdev.blink_set = gpio_blink_set;
+       }
+       led_dat->cdev.brightness_set = gpio_led_set;
+       led_dat->cdev.brightness = LED_OFF;
+       if (!template->retain_state_suspended)
+               led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
+
+       ret = gpio_direction_output(led_dat->gpio, led_dat->active_low);
+       if (ret < 0)
+               goto err;
+
+       INIT_WORK(&led_dat->work, gpio_led_work);
+
+       ret = led_classdev_register(parent, &led_dat->cdev);
+       if (ret < 0)
+               goto err;
+
+       return 0;
+err:
+       gpio_free(led_dat->gpio);
+       return ret;
+}
+
+static void delete_gpio_led(struct gpio_led_data *led)
+{
+       if (!gpio_is_valid(led->gpio))
+               return;
+       led_classdev_unregister(&led->cdev);
+       cancel_work_sync(&led->work);
+       gpio_free(led->gpio);
+}
+
+#ifdef CONFIG_LEDS_GPIO_PLATFORM
 static int gpio_led_probe(struct platform_device *pdev)
 {
        struct gpio_led_platform_data *pdata = pdev->dev.platform_data;
-       struct gpio_led *cur_led;
-       struct gpio_led_data *leds_data, *led_dat;
+       struct gpio_led_data *leds_data;
        int i, ret = 0;
 
        if (!pdata)
@@ -87,35 +144,10 @@ static int gpio_led_probe(struct platform_device *pdev)
                return -ENOMEM;
 
        for (i = 0; i < pdata->num_leds; i++) {
-               cur_led = &pdata->leds[i];
-               led_dat = &leds_data[i];
-
-               ret = gpio_request(cur_led->gpio, cur_led->name);
+               ret = create_gpio_led(&pdata->leds[i], &leds_data[i],
+                                     &pdev->dev, pdata->gpio_blink_set);
                if (ret < 0)
                        goto err;
-
-               led_dat->cdev.name = cur_led->name;
-               led_dat->cdev.default_trigger = cur_led->default_trigger;
-               led_dat->gpio = cur_led->gpio;
-               led_dat->can_sleep = gpio_cansleep(cur_led->gpio);
-               led_dat->active_low = cur_led->active_low;
-               if (pdata->gpio_blink_set) {
-                       led_dat->platform_gpio_blink_set = pdata->gpio_blink_set;
-                       led_dat->cdev.blink_set = gpio_blink_set;
-               }
-               led_dat->cdev.brightness_set = gpio_led_set;
-               led_dat->cdev.brightness = LED_OFF;
-               led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
-
-               gpio_direction_output(led_dat->gpio, led_dat->active_low);
-
-               INIT_WORK(&led_dat->work, gpio_led_work);
-
-               ret = led_classdev_register(&pdev->dev, &led_dat->cdev);
-               if (ret < 0) {
-                       gpio_free(led_dat->gpio);
-                       goto err;
-               }
        }
 
        platform_set_drvdata(pdev, leds_data);
@@ -123,13 +155,8 @@ static int gpio_led_probe(struct platform_device *pdev)
        return 0;
 
 err:
-       if (i > 0) {
-               for (i = i - 1; i >= 0; i--) {
-                       led_classdev_unregister(&leds_data[i].cdev);
-                       cancel_work_sync(&leds_data[i].work);
-                       gpio_free(leds_data[i].gpio);
-               }
-       }
+       for (i = i - 1; i >= 0; i--)
+               delete_gpio_led(&leds_data[i]);
 
        kfree(leds_data);
 
@@ -144,11 +171,8 @@ static int __devexit gpio_led_remove(struct platform_device *pdev)
 
        leds_data = platform_get_drvdata(pdev);
 
-       for (i = 0; i < pdata->num_leds; i++) {
-               led_classdev_unregister(&leds_data[i].cdev);
-               cancel_work_sync(&leds_data[i].work);
-               gpio_free(leds_data[i].gpio);
-       }
+       for (i = 0; i < pdata->num_leds; i++)
+               delete_gpio_led(&leds_data[i]);
 
        kfree(leds_data);
 
@@ -164,20 +188,133 @@ static struct platform_driver gpio_led_driver = {
        },
 };
 
+MODULE_ALIAS("platform:leds-gpio");
+#endif /* CONFIG_LEDS_GPIO_PLATFORM */
+
+/* Code to create from OpenFirmware platform devices */
+#ifdef CONFIG_LEDS_GPIO_OF
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+
+struct gpio_led_of_platform_data {
+       int num_leds;
+       struct gpio_led_data led_data[];
+};
+
+static int __devinit of_gpio_leds_probe(struct of_device *ofdev,
+                                       const struct of_device_id *match)
+{
+       struct device_node *np = ofdev->node, *child;
+       struct gpio_led led;
+       struct gpio_led_of_platform_data *pdata;
+       int count = 0, ret;
+
+       /* count LEDs defined by this device, so we know how much to allocate */
+       for_each_child_of_node(np, child)
+               count++;
+       if (!count)
+               return 0; /* or ENODEV? */
+
+       pdata = kzalloc(sizeof(*pdata) + sizeof(struct gpio_led_data) * count,
+                       GFP_KERNEL);
+       if (!pdata)
+               return -ENOMEM;
+
+       memset(&led, 0, sizeof(led));
+       for_each_child_of_node(np, child) {
+               enum of_gpio_flags flags;
+
+               led.gpio = of_get_gpio_flags(child, 0, &flags);
+               led.active_low = flags & OF_GPIO_ACTIVE_LOW;
+               led.name = of_get_property(child, "label", NULL) ? : child->name;
+               led.default_trigger =
+                       of_get_property(child, "linux,default-trigger", NULL);
+
+               ret = create_gpio_led(&led, &pdata->led_data[pdata->num_leds++],
+                                     &ofdev->dev, NULL);
+               if (ret < 0) {
+                       of_node_put(child);
+                       goto err;
+               }
+       }
+
+       dev_set_drvdata(&ofdev->dev, pdata);
+
+       return 0;
+
+err:
+       for (count = pdata->num_leds - 2; count >= 0; count--)
+               delete_gpio_led(&pdata->led_data[count]);
+
+       kfree(pdata);
+
+       return ret;
+}
+
+static int __devexit of_gpio_leds_remove(struct of_device *ofdev)
+{
+       struct gpio_led_of_platform_data *pdata = dev_get_drvdata(&ofdev->dev);
+       int i;
+
+       for (i = 0; i < pdata->num_leds; i++)
+               delete_gpio_led(&pdata->led_data[i]);
+
+       kfree(pdata);
+
+       dev_set_drvdata(&ofdev->dev, NULL);
+
+       return 0;
+}
+
+static const struct of_device_id of_gpio_leds_match[] = {
+       { .compatible = "gpio-leds", },
+       {},
+};
+
+static struct of_platform_driver of_gpio_leds_driver = {
+       .driver = {
+               .name = "of_gpio_leds",
+               .owner = THIS_MODULE,
+       },
+       .match_table = of_gpio_leds_match,
+       .probe = of_gpio_leds_probe,
+       .remove = __devexit_p(of_gpio_leds_remove),
+};
+#endif
+
 static int __init gpio_led_init(void)
 {
-       return platform_driver_register(&gpio_led_driver);
+       int ret;
+
+#ifdef CONFIG_LEDS_GPIO_PLATFORM       
+       ret = platform_driver_register(&gpio_led_driver);
+       if (ret)
+               return ret;
+#endif
+#ifdef CONFIG_LEDS_GPIO_OF
+       ret = of_register_platform_driver(&of_gpio_leds_driver);
+#endif
+#ifdef CONFIG_LEDS_GPIO_PLATFORM       
+       if (ret)
+               platform_driver_unregister(&gpio_led_driver);
+#endif
+
+       return ret;
 }
 
 static void __exit gpio_led_exit(void)
 {
+#ifdef CONFIG_LEDS_GPIO_PLATFORM
        platform_driver_unregister(&gpio_led_driver);
+#endif
+#ifdef CONFIG_LEDS_GPIO_OF
+       of_unregister_platform_driver(&of_gpio_leds_driver);
+#endif
 }
 
 module_init(gpio_led_init);
 module_exit(gpio_led_exit);
 
-MODULE_AUTHOR("Raphael Assenat <raph@8d.com>");
+MODULE_AUTHOR("Raphael Assenat <raph@8d.com>, Trent Piepho <tpiepho@freescale.com>");
 MODULE_DESCRIPTION("GPIO LED driver");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:leds-gpio");
index 11b77a70bbcb5e3d563e20f25f2493075de626c3..1aa46a390a0dfa780ec13d25c76e61b9128394a0 100644 (file)
@@ -104,7 +104,7 @@ static struct led_classdev h1940_blueled = {
        .default_trigger        = "h1940-bluetooth",
 };
 
-static int __init h1940leds_probe(struct platform_device *pdev)
+static int __devinit h1940leds_probe(struct platform_device *pdev)
 {
        int ret;
 
index bd3b431c9710eccc207228b6daacc049c3e04463..3937244fdcab8bf31562337430996a6cd7a943c2 100644 (file)
@@ -169,7 +169,7 @@ static int pca9532_event(struct input_dev *dev, unsigned int type,
 {
        struct pca9532_data *data = input_get_drvdata(dev);
 
-       if (type != EV_SND && (code != SND_BELL || code != SND_TONE))
+       if (!(type == EV_SND && (code == SND_BELL || code == SND_TONE)))
                return -1;
 
        /* XXX: allow different kind of beeps with psc/pwm modifications */
diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c
new file mode 100644 (file)
index 0000000..cdfdc87
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * linux/drivers/leds-pwm.c
+ *
+ * simple PWM based LED control
+ *
+ * Copyright 2009 Luotao Fu @ Pengutronix (l.fu@pengutronix.de)
+ *
+ * based on leds-gpio.c by Raphael Assenat <raph@8d.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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/fb.h>
+#include <linux/leds.h>
+#include <linux/err.h>
+#include <linux/pwm.h>
+#include <linux/leds_pwm.h>
+
+struct led_pwm_data {
+       struct led_classdev     cdev;
+       struct pwm_device       *pwm;
+       unsigned int            active_low;
+       unsigned int            period;
+       unsigned int            max_brightness;
+};
+
+static void led_pwm_set(struct led_classdev *led_cdev,
+       enum led_brightness brightness)
+{
+       struct led_pwm_data *led_dat =
+               container_of(led_cdev, struct led_pwm_data, cdev);
+       unsigned int max = led_dat->max_brightness;
+       unsigned int period =  led_dat->period;
+
+       if (brightness == 0) {
+               pwm_config(led_dat->pwm, 0, period);
+               pwm_disable(led_dat->pwm);
+       } else {
+               pwm_config(led_dat->pwm, brightness * period / max, period);
+               pwm_enable(led_dat->pwm);
+       }
+}
+
+static int led_pwm_probe(struct platform_device *pdev)
+{
+       struct led_pwm_platform_data *pdata = pdev->dev.platform_data;
+       struct led_pwm *cur_led;
+       struct led_pwm_data *leds_data, *led_dat;
+       int i, ret = 0;
+
+       if (!pdata)
+               return -EBUSY;
+
+       leds_data = kzalloc(sizeof(struct led_pwm_data) * pdata->num_leds,
+                               GFP_KERNEL);
+       if (!leds_data)
+               return -ENOMEM;
+
+       for (i = 0; i < pdata->num_leds; i++) {
+               cur_led = &pdata->leds[i];
+               led_dat = &leds_data[i];
+
+               led_dat->pwm = pwm_request(cur_led->pwm_id,
+                               cur_led->name);
+               if (IS_ERR(led_dat->pwm)) {
+                       dev_err(&pdev->dev, "unable to request PWM %d\n",
+                                       cur_led->pwm_id);
+                       goto err;
+               }
+
+               led_dat->cdev.name = cur_led->name;
+               led_dat->cdev.default_trigger = cur_led->default_trigger;
+               led_dat->active_low = cur_led->active_low;
+               led_dat->max_brightness = cur_led->max_brightness;
+               led_dat->period = cur_led->pwm_period_ns;
+               led_dat->cdev.brightness_set = led_pwm_set;
+               led_dat->cdev.brightness = LED_OFF;
+               led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
+
+               ret = led_classdev_register(&pdev->dev, &led_dat->cdev);
+               if (ret < 0) {
+                       pwm_free(led_dat->pwm);
+                       goto err;
+               }
+       }
+
+       platform_set_drvdata(pdev, leds_data);
+
+       return 0;
+
+err:
+       if (i > 0) {
+               for (i = i - 1; i >= 0; i--) {
+                       led_classdev_unregister(&leds_data[i].cdev);
+                       pwm_free(leds_data[i].pwm);
+               }
+       }
+
+       kfree(leds_data);
+
+       return ret;
+}
+
+static int __devexit led_pwm_remove(struct platform_device *pdev)
+{
+       int i;
+       struct led_pwm_platform_data *pdata = pdev->dev.platform_data;
+       struct led_pwm_data *leds_data;
+
+       leds_data = platform_get_drvdata(pdev);
+
+       for (i = 0; i < pdata->num_leds; i++) {
+               led_classdev_unregister(&leds_data[i].cdev);
+               pwm_free(leds_data[i].pwm);
+       }
+
+       kfree(leds_data);
+
+       return 0;
+}
+
+static struct platform_driver led_pwm_driver = {
+       .probe          = led_pwm_probe,
+       .remove         = __devexit_p(led_pwm_remove),
+       .driver         = {
+               .name   = "leds_pwm",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init led_pwm_init(void)
+{
+       return platform_driver_register(&led_pwm_driver);
+}
+
+static void __exit led_pwm_exit(void)
+{
+       platform_driver_unregister(&led_pwm_driver);
+}
+
+module_init(led_pwm_init);
+module_exit(led_pwm_exit);
+
+MODULE_AUTHOR("Luotao Fu <l.fu@pengutronix.de>");
+MODULE_DESCRIPTION("PWM LED driver for PXA");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:leds-pwm");
diff --git a/drivers/leds/leds-rb532.c b/drivers/leds/leds-rb532.c
new file mode 100644 (file)
index 0000000..c3525f3
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * LEDs driver for the "User LED" on Routerboard532
+ *
+ * Copyright (C) 2009 Phil Sutter <n0-1@freewrt.org>
+ *
+ * Based on leds-cobalt-qube.c by Florian Fainelly and
+ * rb-diag.c (my own standalone driver for both LED and
+ * button of Routerboard532).
+ */
+
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <asm/mach-rc32434/gpio.h>
+#include <asm/mach-rc32434/rb.h>
+
+static void rb532_led_set(struct led_classdev *cdev,
+                          enum led_brightness brightness)
+{
+       if (brightness)
+               set_latch_u5(LO_ULED, 0);
+
+       else
+               set_latch_u5(0, LO_ULED);
+}
+
+static enum led_brightness rb532_led_get(struct led_classdev *cdev)
+{
+       return (get_latch_u5() & LO_ULED) ? LED_FULL : LED_OFF;
+}
+
+static struct led_classdev rb532_uled = {
+       .name = "uled",
+       .brightness_set = rb532_led_set,
+       .brightness_get = rb532_led_get,
+       .default_trigger = "nand-disk",
+};
+
+static int __devinit rb532_led_probe(struct platform_device *pdev)
+{
+       return led_classdev_register(&pdev->dev, &rb532_uled);
+}
+
+static int __devexit rb532_led_remove(struct platform_device *pdev)
+{
+       led_classdev_unregister(&rb532_uled);
+       return 0;
+}
+
+static struct platform_driver rb532_led_driver = {
+       .probe = rb532_led_probe,
+       .remove = __devexit_p(rb532_led_remove),
+       .driver = {
+               .name = "rb532-led",
+               .owner = THIS_MODULE,
+       },
+};
+
+static int __init rb532_led_init(void)
+{
+       return platform_driver_register(&rb532_led_driver);
+}
+
+static void __exit rb532_led_exit(void)
+{
+       platform_driver_unregister(&rb532_led_driver);
+}
+
+module_init(rb532_led_init);
+module_exit(rb532_led_exit);
+
+MODULE_ALIAS("platform:rb532-led");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("User LED support for Routerboard532");
+MODULE_AUTHOR("Phil Sutter <n0-1@freewrt.org>");
index 4d81131542ae68e71e203dae1f9e63194547ffef..aa2e7ae0cdaeede5d97cc34e66dfe26e0e718194 100644 (file)
@@ -102,14 +102,11 @@ static int s3c24xx_led_probe(struct platform_device *dev)
        ret = led_classdev_register(&dev->dev, &led->cdev);
        if (ret < 0) {
                dev_err(&dev->dev, "led_classdev_register failed\n");
-               goto exit_err1;
+               kfree(led);
+               return ret;
        }
 
        return 0;
-
- exit_err1:
-       kfree(led);
-       return ret;
 }
 
 static struct platform_driver s3c24xx_led_driver = {
index 5edbf52c4fa7e6834e9125855bd4c1801e2c2809..2dd8ecbfdc3155250d3608897db8d1d0047fc317 100644 (file)
@@ -20,8 +20,8 @@
 static inline void led_set_brightness(struct led_classdev *led_cdev,
                                        enum led_brightness value)
 {
-       if (value > LED_FULL)
-               value = LED_FULL;
+       if (value > led_cdev->max_brightness)
+               value = led_cdev->max_brightness;
        led_cdev->brightness = value;
        if (!(led_cdev->flags & LED_SUSPENDED))
                led_cdev->brightness_set(led_cdev, value);
index 92995e40cfa43fd6ba9ea1b454f5172044119193..a4ef54b9d508efffe60b0cd4db2213479680547e 100644 (file)
@@ -19,7 +19,7 @@
 
 static void defon_trig_activate(struct led_classdev *led_cdev)
 {
-       led_set_brightness(led_cdev, LED_FULL);
+       led_set_brightness(led_cdev, led_cdev->max_brightness);
 }
 
 static struct led_trigger defon_led_trigger = {
diff --git a/drivers/leds/ledtrig-gpio.c b/drivers/leds/ledtrig-gpio.c
new file mode 100644 (file)
index 0000000..a247ae6
--- /dev/null
@@ -0,0 +1,239 @@
+/*
+ * ledtrig-gio.c - LED Trigger Based on GPIO events
+ *
+ * Copyright 2009 Felipe Balbi <me@felipebalbi.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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/leds.h>
+#include "leds.h"
+
+struct gpio_trig_data {
+       struct led_classdev *led;
+       struct work_struct work;
+
+       unsigned desired_brightness;    /* desired brightness when led is on */
+       unsigned inverted;              /* true when gpio is inverted */
+       unsigned gpio;                  /* gpio that triggers the leds */
+};
+
+static irqreturn_t gpio_trig_irq(int irq, void *_led)
+{
+       struct led_classdev *led = _led;
+       struct gpio_trig_data *gpio_data = led->trigger_data;
+
+       /* just schedule_work since gpio_get_value can sleep */
+       schedule_work(&gpio_data->work);
+
+       return IRQ_HANDLED;
+};
+
+static void gpio_trig_work(struct work_struct *work)
+{
+       struct gpio_trig_data *gpio_data = container_of(work,
+                       struct gpio_trig_data, work);
+       int tmp;
+
+       if (!gpio_data->gpio)
+              return;
+
+       tmp = gpio_get_value(gpio_data->gpio);
+       if (gpio_data->inverted)
+              tmp = !tmp;
+
+       if (tmp) {
+               if (gpio_data->desired_brightness)
+                       led_set_brightness(gpio_data->led,
+                                       gpio_data->desired_brightness);
+               else
+                       led_set_brightness(gpio_data->led, LED_FULL);
+       } else {
+               led_set_brightness(gpio_data->led, LED_OFF);
+       }
+}
+
+static ssize_t gpio_trig_brightness_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct led_classdev *led = dev_get_drvdata(dev);
+       struct gpio_trig_data *gpio_data = led->trigger_data;
+
+       return sprintf(buf, "%u\n", gpio_data->desired_brightness);
+}
+
+static ssize_t gpio_trig_brightness_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t n)
+{
+       struct led_classdev *led = dev_get_drvdata(dev);
+       struct gpio_trig_data *gpio_data = led->trigger_data;
+       unsigned desired_brightness;
+       int ret;
+
+       ret = sscanf(buf, "%u", &desired_brightness);
+       if (ret < 1 || desired_brightness > 255) {
+               dev_err(dev, "invalid value\n");
+               return -EINVAL;
+       }
+
+       gpio_data->desired_brightness = desired_brightness;
+
+       return n;
+}
+static DEVICE_ATTR(desired_brightness, 0644, gpio_trig_brightness_show,
+               gpio_trig_brightness_store);
+
+static ssize_t gpio_trig_inverted_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct led_classdev *led = dev_get_drvdata(dev);
+       struct gpio_trig_data *gpio_data = led->trigger_data;
+
+       return sprintf(buf, "%s\n", gpio_data->inverted ? "yes" : "no");
+}
+
+static ssize_t gpio_trig_inverted_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t n)
+{
+       struct led_classdev *led = dev_get_drvdata(dev);
+       struct gpio_trig_data *gpio_data = led->trigger_data;
+       unsigned inverted;
+       int ret;
+
+       ret = sscanf(buf, "%u", &inverted);
+       if (ret < 1) {
+               dev_err(dev, "invalid value\n");
+               return -EINVAL;
+       }
+
+       gpio_data->inverted = !!inverted;
+
+       return n;
+}
+static DEVICE_ATTR(inverted, 0644, gpio_trig_inverted_show,
+               gpio_trig_inverted_store);
+
+static ssize_t gpio_trig_gpio_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct led_classdev *led = dev_get_drvdata(dev);
+       struct gpio_trig_data *gpio_data = led->trigger_data;
+
+       return sprintf(buf, "%u\n", gpio_data->gpio);
+}
+
+static ssize_t gpio_trig_gpio_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t n)
+{
+       struct led_classdev *led = dev_get_drvdata(dev);
+       struct gpio_trig_data *gpio_data = led->trigger_data;
+       unsigned gpio;
+       int ret;
+
+       ret = sscanf(buf, "%u", &gpio);
+       if (ret < 1) {
+               dev_err(dev, "couldn't read gpio number\n");
+               flush_work(&gpio_data->work);
+               return -EINVAL;
+       }
+
+       if (!gpio) {
+               free_irq(gpio_to_irq(gpio_data->gpio), led);
+               return n;
+       }
+
+       if (gpio_data->gpio > 0 && gpio_data->gpio != gpio)
+               free_irq(gpio_to_irq(gpio_data->gpio), led);
+
+       gpio_data->gpio = gpio;
+       ret = request_irq(gpio_to_irq(gpio), gpio_trig_irq,
+                       IRQF_SHARED | IRQF_TRIGGER_RISING
+                       | IRQF_TRIGGER_FALLING, "ledtrig-gpio", led);
+       if (ret)
+               dev_err(dev, "request_irq failed with error %d\n", ret);
+
+       return ret ? ret : n;
+}
+static DEVICE_ATTR(gpio, 0644, gpio_trig_gpio_show, gpio_trig_gpio_store);
+
+static void gpio_trig_activate(struct led_classdev *led)
+{
+       struct gpio_trig_data *gpio_data;
+       int ret;
+
+       gpio_data = kzalloc(sizeof(*gpio_data), GFP_KERNEL);
+       if (!gpio_data)
+               return;
+
+       ret = device_create_file(led->dev, &dev_attr_gpio);
+       if (ret)
+               goto err_gpio;
+
+       ret = device_create_file(led->dev, &dev_attr_inverted);
+       if (ret)
+               goto err_inverted;
+
+       ret = device_create_file(led->dev, &dev_attr_desired_brightness);
+       if (ret)
+               goto err_brightness;
+
+       gpio_data->led = led;
+       led->trigger_data = gpio_data;
+       INIT_WORK(&gpio_data->work, gpio_trig_work);
+
+       return;
+
+err_brightness:
+       device_remove_file(led->dev, &dev_attr_inverted);
+
+err_inverted:
+       device_remove_file(led->dev, &dev_attr_gpio);
+
+err_gpio:
+       kfree(gpio_data);
+}
+
+static void gpio_trig_deactivate(struct led_classdev *led)
+{
+       struct gpio_trig_data *gpio_data = led->trigger_data;
+
+       if (gpio_data) {
+               device_remove_file(led->dev, &dev_attr_gpio);
+               device_remove_file(led->dev, &dev_attr_inverted);
+               device_remove_file(led->dev, &dev_attr_desired_brightness);
+               flush_work(&gpio_data->work);
+               free_irq(gpio_to_irq(gpio_data->gpio),led);
+               kfree(gpio_data);
+       }
+}
+
+static struct led_trigger gpio_led_trigger = {
+       .name           = "gpio",
+       .activate       = gpio_trig_activate,
+       .deactivate     = gpio_trig_deactivate,
+};
+
+static int __init gpio_trig_init(void)
+{
+       return led_trigger_register(&gpio_led_trigger);
+}
+module_init(gpio_trig_init);
+
+static void __exit gpio_trig_exit(void)
+{
+       led_trigger_unregister(&gpio_led_trigger);
+}
+module_exit(gpio_trig_exit);
+
+MODULE_AUTHOR("Felipe Balbi <me@felipebalbi.com>");
+MODULE_DESCRIPTION("GPIO LED trigger");
+MODULE_LICENSE("GPL");
index 4bf8cec8b8c14173b68f78d4d3126648e2092051..c1c1ea6f817b1eb7ddb7f2433cbdc917e20d164e 100644 (file)
@@ -47,7 +47,7 @@ static void led_heartbeat_function(unsigned long data)
                        msecs_to_jiffies(heartbeat_data->period);
                delay = msecs_to_jiffies(70);
                heartbeat_data->phase++;
-               brightness = LED_FULL;
+               brightness = led_cdev->max_brightness;
                break;
        case 1:
                delay = heartbeat_data->period / 4 - msecs_to_jiffies(70);
@@ -56,7 +56,7 @@ static void led_heartbeat_function(unsigned long data)
        case 2:
                delay = msecs_to_jiffies(70);
                heartbeat_data->phase++;
-               brightness = LED_FULL;
+               brightness = led_cdev->max_brightness;
                break;
        default:
                delay = heartbeat_data->period - heartbeat_data->period / 4 -
index 883a577b1b97aaaa189a3d82fc461939ffa286da..ec099fcbcb00ecf01c4f8ef4313710230428b15b 100644 (file)
@@ -37,7 +37,8 @@ static void ledtrig_ide_timerfunc(unsigned long data)
 {
        if (ide_lastactivity != ide_activity) {
                ide_lastactivity = ide_activity;
-               led_trigger_event(ledtrig_ide, LED_FULL);
+               /* INT_MAX will set each LED to its maximum brightness */
+               led_trigger_event(ledtrig_ide, INT_MAX);
                mod_timer(&ledtrig_ide_timer, jiffies + msecs_to_jiffies(10));
        } else {
                led_trigger_event(ledtrig_ide, LED_OFF);
index 3d6531396dda094e41eb7f00b80d20acbbd244cd..3b83406de7520b8052b73906992f2fa34e1b3891 100644 (file)
@@ -166,7 +166,7 @@ static void timer_trig_activate(struct led_classdev *led_cdev)
 
        timer_data->brightness_on = led_get_brightness(led_cdev);
        if (timer_data->brightness_on == LED_OFF)
-               timer_data->brightness_on = LED_FULL;
+               timer_data->brightness_on = led_cdev->max_brightness;
        led_cdev->trigger_data = timer_data;
 
        init_timer(&timer_data->timer);
index 5d55a896ff78f2a79ab9bc2dee72dae77ddb93eb..6e35762b6169be2a30a69462764b71967369a1d8 100644 (file)
@@ -737,7 +737,7 @@ static int fsync_buffers_list(spinlock_t *lock, struct list_head *list)
 {
        struct buffer_head *bh;
        struct list_head tmp;
-       struct address_space *mapping;
+       struct address_space *mapping, *prev_mapping = NULL;
        int err = 0, err2;
 
        INIT_LIST_HEAD(&tmp);
@@ -762,7 +762,18 @@ static int fsync_buffers_list(spinlock_t *lock, struct list_head *list)
                                 * contents - it is a noop if I/O is still in
                                 * flight on potentially older contents.
                                 */
-                               ll_rw_block(SWRITE_SYNC, 1, &bh);
+                               ll_rw_block(SWRITE_SYNC_PLUG, 1, &bh);
+
+                               /*
+                                * Kick off IO for the previous mapping. Note
+                                * that we will not run the very last mapping,
+                                * wait_on_buffer() will do that for us
+                                * through sync_buffer().
+                                */
+                               if (prev_mapping && prev_mapping != mapping)
+                                       blk_run_address_space(prev_mapping);
+                               prev_mapping = mapping;
+
                                brelse(bh);
                                spin_lock(lock);
                        }
@@ -2957,12 +2968,13 @@ void ll_rw_block(int rw, int nr, struct buffer_head *bhs[])
        for (i = 0; i < nr; i++) {
                struct buffer_head *bh = bhs[i];
 
-               if (rw == SWRITE || rw == SWRITE_SYNC)
+               if (rw == SWRITE || rw == SWRITE_SYNC || rw == SWRITE_SYNC_PLUG)
                        lock_buffer(bh);
                else if (!trylock_buffer(bh))
                        continue;
 
-               if (rw == WRITE || rw == SWRITE || rw == SWRITE_SYNC) {
+               if (rw == WRITE || rw == SWRITE || rw == SWRITE_SYNC ||
+                   rw == SWRITE_SYNC_PLUG) {
                        if (test_clear_buffer_dirty(bh)) {
                                bh->b_end_io = end_buffer_write_sync;
                                get_bh(bh);
@@ -2998,7 +3010,7 @@ int sync_dirty_buffer(struct buffer_head *bh)
        if (test_clear_buffer_dirty(bh)) {
                get_bh(bh);
                bh->b_end_io = end_buffer_write_sync;
-               ret = submit_bh(WRITE, bh);
+               ret = submit_bh(WRITE_SYNC, bh);
                wait_on_buffer(bh);
                if (buffer_eopnotsupp(bh)) {
                        clear_buffer_eopnotsupp(bh);
index b6d43908ff7a890f741c3076308c1a9da965f59b..da258e7249cc718b8a46ddec08ba04a62e7e4790 100644 (file)
@@ -1126,7 +1126,7 @@ __blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
        int acquire_i_mutex = 0;
 
        if (rw & WRITE)
-               rw = WRITE_SYNC;
+               rw = WRITE_ODIRECT;
 
        if (bdev)
                bdev_blkbits = blksize_bits(bdev_hardsect_size(bdev));
index f8077b9c898160513b1e958a532401653ac96123..a8e8513a78a94db46246a0dbee992e43f1861d5d 100644 (file)
@@ -351,8 +351,13 @@ void journal_commit_transaction(journal_t *journal)
        spin_lock(&journal->j_state_lock);
        commit_transaction->t_state = T_LOCKED;
 
+       /*
+        * Use plugged writes here, since we want to submit several before
+        * we unplug the device. We don't do explicit unplugging in here,
+        * instead we rely on sync_buffer() doing the unplug for us.
+        */
        if (commit_transaction->t_synchronous_commit)
-               write_op = WRITE_SYNC;
+               write_op = WRITE_SYNC_PLUG;
        spin_lock(&commit_transaction->t_handle_lock);
        while (commit_transaction->t_updates) {
                DEFINE_WAIT(wait);
index 4ea72377c7a28797b609c77084b03be21485800d..073c8c3df7cd03af77a6eec17167d8f9c431b7e7 100644 (file)
@@ -138,7 +138,7 @@ static int journal_submit_commit_record(journal_t *journal,
                set_buffer_ordered(bh);
                barrier_done = 1;
        }
-       ret = submit_bh(WRITE_SYNC, bh);
+       ret = submit_bh(WRITE_SYNC_PLUG, bh);
        if (barrier_done)
                clear_buffer_ordered(bh);
 
@@ -159,7 +159,7 @@ static int journal_submit_commit_record(journal_t *journal,
                lock_buffer(bh);
                set_buffer_uptodate(bh);
                clear_buffer_dirty(bh);
-               ret = submit_bh(WRITE_SYNC, bh);
+               ret = submit_bh(WRITE_SYNC_PLUG, bh);
        }
        *cbh = bh;
        return ret;
@@ -190,7 +190,7 @@ retry:
                set_buffer_uptodate(bh);
                bh->b_end_io = journal_end_buffer_io_sync;
 
-               ret = submit_bh(WRITE_SYNC, bh);
+               ret = submit_bh(WRITE_SYNC_PLUG, bh);
                if (ret) {
                        unlock_buffer(bh);
                        return ret;
@@ -402,8 +402,13 @@ void jbd2_journal_commit_transaction(journal_t *journal)
        spin_lock(&journal->j_state_lock);
        commit_transaction->t_state = T_LOCKED;
 
+       /*
+        * Use plugged writes here, since we want to submit several before
+        * we unplug the device. We don't do explicit unplugging in here,
+        * instead we rely on sync_buffer() doing the unplug for us.
+        */
        if (commit_transaction->t_synchronous_commit)
-               write_op = WRITE_SYNC;
+               write_op = WRITE_SYNC_PLUG;
        stats.u.run.rs_wait = commit_transaction->t_max_wait;
        stats.u.run.rs_locked = jiffies;
        stats.u.run.rs_running = jbd2_time_diff(commit_transaction->t_start,
index bee52abb8a4dbfd46e53f650d7d7dbca881a9169..0ec2c594868e657ad20cfaffc21227bf7ee2e18b 100644 (file)
@@ -24,8 +24,8 @@ struct dentry;
  */
 enum bdi_state {
        BDI_pdflush,            /* A pdflush thread is working this device */
-       BDI_write_congested,    /* The write queue is getting full */
-       BDI_read_congested,     /* The read queue is getting full */
+       BDI_async_congested,    /* The async (write) queue is getting full */
+       BDI_sync_congested,     /* The sync queue is getting full */
        BDI_unused,             /* Available bits start here */
 };
 
@@ -215,18 +215,18 @@ static inline int bdi_congested(struct backing_dev_info *bdi, int bdi_bits)
 
 static inline int bdi_read_congested(struct backing_dev_info *bdi)
 {
-       return bdi_congested(bdi, 1 << BDI_read_congested);
+       return bdi_congested(bdi, 1 << BDI_sync_congested);
 }
 
 static inline int bdi_write_congested(struct backing_dev_info *bdi)
 {
-       return bdi_congested(bdi, 1 << BDI_write_congested);
+       return bdi_congested(bdi, 1 << BDI_async_congested);
 }
 
 static inline int bdi_rw_congested(struct backing_dev_info *bdi)
 {
-       return bdi_congested(bdi, (1 << BDI_read_congested)|
-                                 (1 << BDI_write_congested));
+       return bdi_congested(bdi, (1 << BDI_sync_congested) |
+                                 (1 << BDI_async_congested));
 }
 
 void clear_bdi_congested(struct backing_dev_info *bdi, int rw);
index b05b1d4d17d2210c67f3ee5ea17b8528e5caf8b6..b900d2c67d29ad2dc490977a4d836a684d26fd04 100644 (file)
@@ -145,20 +145,21 @@ struct bio {
  * bit 2 -- barrier
  *     Insert a serialization point in the IO queue, forcing previously
  *     submitted IO to be completed before this one is issued.
- * bit 3 -- synchronous I/O hint: the block layer will unplug immediately
- *     Note that this does NOT indicate that the IO itself is sync, just
- *     that the block layer will not postpone issue of this IO by plugging.
- * bit 4 -- metadata request
+ * bit 3 -- synchronous I/O hint.
+ * bit 4 -- Unplug the device immediately after submitting this bio.
+ * bit 5 -- metadata request
  *     Used for tracing to differentiate metadata and data IO. May also
  *     get some preferential treatment in the IO scheduler
- * bit 5 -- discard sectors
+ * bit 6 -- discard sectors
  *     Informs the lower level device that this range of sectors is no longer
  *     used by the file system and may thus be freed by the device. Used
  *     for flash based storage.
- * bit 6 -- fail fast device errors
- * bit 7 -- fail fast transport errors
- * bit 8 -- fail fast driver errors
+ * bit 7 -- fail fast device errors
+ * bit 8 -- fail fast transport errors
+ * bit 9 -- fail fast driver errors
  *     Don't want driver retries for any fast fail whatever the reason.
+ * bit 10 -- Tell the IO scheduler not to wait for more requests after this
+       one has been submitted, even if it is a SYNC request.
  */
 #define BIO_RW         0       /* Must match RW in req flags (blkdev.h) */
 #define BIO_RW_AHEAD   1       /* Must match FAILFAST in req flags */
@@ -170,6 +171,7 @@ struct bio {
 #define BIO_RW_FAILFAST_DEV            7
 #define BIO_RW_FAILFAST_TRANSPORT      8
 #define BIO_RW_FAILFAST_DRIVER         9
+#define BIO_RW_NOIDLE  10
 
 #define bio_rw_flagged(bio, flag)      ((bio)->bi_rw & (1 << (flag)))
 
@@ -188,6 +190,7 @@ struct bio {
 #define bio_rw_ahead(bio)      bio_rw_flagged(bio, BIO_RW_AHEAD)
 #define bio_rw_meta(bio)       bio_rw_flagged(bio, BIO_RW_META)
 #define bio_discard(bio)       bio_rw_flagged(bio, BIO_RW_DISCARD)
+#define bio_noidle(bio)                bio_rw_flagged(bio, BIO_RW_NOIDLE)
 
 /*
  * upper 16 bits of bi_rw define the io priority of this bio
index 465d6babc847a2603d4f23a5e842cd7dc07b308e..e03660964e02002d1724a7fdaef14243407d620e 100644 (file)
@@ -38,6 +38,10 @@ struct request;
 typedef void (rq_end_io_fn)(struct request *, int);
 
 struct request_list {
+       /*
+        * count[], starved[], and wait[] are indexed by
+        * BLK_RW_SYNC/BLK_RW_ASYNC
+        */
        int count[2];
        int starved[2];
        int elvpriv;
@@ -66,6 +70,11 @@ enum rq_cmd_type_bits {
        REQ_TYPE_ATA_PC,
 };
 
+enum {
+       BLK_RW_ASYNC    = 0,
+       BLK_RW_SYNC     = 1,
+};
+
 /*
  * For request of type REQ_TYPE_LINUX_BLOCK, rq->cmd[0] is the opcode being
  * sent down (similar to how REQ_TYPE_BLOCK_PC means that ->cmd[] holds a
@@ -103,12 +112,13 @@ enum rq_flag_bits {
        __REQ_QUIET,            /* don't worry about errors */
        __REQ_PREEMPT,          /* set for "ide_preempt" requests */
        __REQ_ORDERED_COLOR,    /* is before or after barrier */
-       __REQ_RW_SYNC,          /* request is sync (O_DIRECT) */
+       __REQ_RW_SYNC,          /* request is sync (sync write or read) */
        __REQ_ALLOCED,          /* request came from our alloc pool */
        __REQ_RW_META,          /* metadata io request */
        __REQ_COPY_USER,        /* contains copies of user pages */
        __REQ_INTEGRITY,        /* integrity metadata has been remapped */
        __REQ_UNPLUG,           /* unplug queue on submission */
+       __REQ_NOIDLE,           /* Don't anticipate more IO after this one */
        __REQ_NR_BITS,          /* stops here */
 };
 
@@ -136,6 +146,7 @@ enum rq_flag_bits {
 #define REQ_COPY_USER  (1 << __REQ_COPY_USER)
 #define REQ_INTEGRITY  (1 << __REQ_INTEGRITY)
 #define REQ_UNPLUG     (1 << __REQ_UNPLUG)
+#define REQ_NOIDLE     (1 << __REQ_NOIDLE)
 
 #define BLK_MAX_CDB    16
 
@@ -438,8 +449,8 @@ struct request_queue
 #define QUEUE_FLAG_CLUSTER     0       /* cluster several segments into 1 */
 #define QUEUE_FLAG_QUEUED      1       /* uses generic tag queueing */
 #define QUEUE_FLAG_STOPPED     2       /* queue is stopped */
-#define        QUEUE_FLAG_READFULL     3       /* read queue has been filled */
-#define QUEUE_FLAG_WRITEFULL   4       /* write queue has been filled */
+#define        QUEUE_FLAG_SYNCFULL     3       /* read queue has been filled */
+#define QUEUE_FLAG_ASYNCFULL   4       /* write queue has been filled */
 #define QUEUE_FLAG_DEAD                5       /* queue being torn down */
 #define QUEUE_FLAG_REENTER     6       /* Re-entrancy avoidance */
 #define QUEUE_FLAG_PLUGGED     7       /* queue is plugged */
@@ -611,32 +622,42 @@ enum {
 #define rq_data_dir(rq)                ((rq)->cmd_flags & 1)
 
 /*
- * We regard a request as sync, if it's a READ or a SYNC write.
+ * We regard a request as sync, if either a read or a sync write
  */
-#define rq_is_sync(rq)         (rq_data_dir((rq)) == READ || (rq)->cmd_flags & REQ_RW_SYNC)
+static inline bool rw_is_sync(unsigned int rw_flags)
+{
+       return !(rw_flags & REQ_RW) || (rw_flags & REQ_RW_SYNC);
+}
+
+static inline bool rq_is_sync(struct request *rq)
+{
+       return rw_is_sync(rq->cmd_flags);
+}
+
 #define rq_is_meta(rq)         ((rq)->cmd_flags & REQ_RW_META)
+#define rq_noidle(rq)          ((rq)->cmd_flags & REQ_NOIDLE)
 
-static inline int blk_queue_full(struct request_queue *q, int rw)
+static inline int blk_queue_full(struct request_queue *q, int sync)
 {
-       if (rw == READ)
-               return test_bit(QUEUE_FLAG_READFULL, &q->queue_flags);
-       return test_bit(QUEUE_FLAG_WRITEFULL, &q->queue_flags);
+       if (sync)
+               return test_bit(QUEUE_FLAG_SYNCFULL, &q->queue_flags);
+       return test_bit(QUEUE_FLAG_ASYNCFULL, &q->queue_flags);
 }
 
-static inline void blk_set_queue_full(struct request_queue *q, int rw)
+static inline void blk_set_queue_full(struct request_queue *q, int sync)
 {
-       if (rw == READ)
-               queue_flag_set(QUEUE_FLAG_READFULL, q);
+       if (sync)
+               queue_flag_set(QUEUE_FLAG_SYNCFULL, q);
        else
-               queue_flag_set(QUEUE_FLAG_WRITEFULL, q);
+               queue_flag_set(QUEUE_FLAG_ASYNCFULL, q);
 }
 
-static inline void blk_clear_queue_full(struct request_queue *q, int rw)
+static inline void blk_clear_queue_full(struct request_queue *q, int sync)
 {
-       if (rw == READ)
-               queue_flag_clear(QUEUE_FLAG_READFULL, q);
+       if (sync)
+               queue_flag_clear(QUEUE_FLAG_SYNCFULL, q);
        else
-               queue_flag_clear(QUEUE_FLAG_WRITEFULL, q);
+               queue_flag_clear(QUEUE_FLAG_ASYNCFULL, q);
 }
 
 
index a09e17c8f5fd9137720b008196aa29f45ff17bfb..cae5720f431c5625ee6f306fdc7b0b2c6d2d06bd 100644 (file)
@@ -95,8 +95,12 @@ struct inodes_stat_t {
 #define SWRITE 3       /* for ll_rw_block() - wait for buffer lock */
 #define READ_SYNC      (READ | (1 << BIO_RW_SYNCIO) | (1 << BIO_RW_UNPLUG))
 #define READ_META      (READ | (1 << BIO_RW_META))
-#define WRITE_SYNC     (WRITE | (1 << BIO_RW_SYNCIO) | (1 << BIO_RW_UNPLUG))
-#define SWRITE_SYNC    (SWRITE | (1 << BIO_RW_SYNCIO) | (1 << BIO_RW_UNPLUG))
+#define WRITE_SYNC_PLUG        (WRITE | (1 << BIO_RW_SYNCIO) | (1 << BIO_RW_NOIDLE))
+#define WRITE_SYNC     (WRITE_SYNC_PLUG | (1 << BIO_RW_UNPLUG))
+#define WRITE_ODIRECT  (WRITE | (1 << BIO_RW_SYNCIO) | (1 << BIO_RW_UNPLUG))
+#define SWRITE_SYNC_PLUG       \
+                       (SWRITE | (1 << BIO_RW_SYNCIO) | (1 << BIO_RW_NOIDLE))
+#define SWRITE_SYNC    (SWRITE_SYNC_PLUG | (1 << BIO_RW_UNPLUG))
 #define WRITE_BARRIER  (WRITE | (1 << BIO_RW_BARRIER))
 #define DISCARD_NOBARRIER (1 << BIO_RW_DISCARD)
 #define DISCARD_BARRIER ((1 << BIO_RW_DISCARD) | (1 << BIO_RW_BARRIER))
diff --git a/include/linux/leds-bd2802.h b/include/linux/leds-bd2802.h
new file mode 100644 (file)
index 0000000..42f854a
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * leds-bd2802.h - RGB LED Driver
+ *
+ * Copyright (C) 2009 Samsung Electronics
+ * Kim Kyuwon <q1.kim@samsung.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.
+ *
+ * Datasheet: http://www.rohm.com/products/databook/driver/pdf/bd2802gu-e.pdf
+ *
+ */
+#ifndef _LEDS_BD2802_H_
+#define _LEDS_BD2802_H_
+
+struct bd2802_led_platform_data{
+       int     reset_gpio;
+       u8      rgb_time;
+};
+
+#define RGB_TIME(slopedown, slopeup, waveform) \
+       ((slopedown) << 6 | (slopeup) << 4 | (waveform))
+
+#endif /* _LEDS_BD2802_H_ */
+
index 24489da701e331b1a4d1810b40afd7209775e265..376fe07732ea9261dc81f3ff7411d49ac9051fb3 100644 (file)
@@ -30,6 +30,7 @@ enum led_brightness {
 struct led_classdev {
        const char              *name;
        int                      brightness;
+       int                      max_brightness;
        int                      flags;
 
        /* Lower 16 bits reflect status */
@@ -140,7 +141,8 @@ struct gpio_led {
        const char *name;
        const char *default_trigger;
        unsigned        gpio;
-       u8              active_low;
+       u8              active_low : 1;
+       u8              retain_state_suspended : 1;
 };
 
 struct gpio_led_platform_data {
diff --git a/include/linux/leds_pwm.h b/include/linux/leds_pwm.h
new file mode 100644 (file)
index 0000000..33a0711
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * PWM LED driver data - see drivers/leds/leds-pwm.c
+ */
+#ifndef __LINUX_LEDS_PWM_H
+#define __LINUX_LEDS_PWM_H
+
+struct led_pwm {
+       const char      *name;
+       const char      *default_trigger;
+       unsigned        pwm_id;
+       u8              active_low;
+       unsigned        max_brightness;
+       unsigned        pwm_period_ns;
+};
+
+struct led_pwm_platform_data {
+       int                     num_leds;
+       struct led_pwm  *leds;
+};
+
+#endif
index be68c956a66079930475aa2b25eea170d4bd1dc7..493b468a503541fd65b64872eece6fb7228e36fa 100644 (file)
@@ -284,12 +284,12 @@ static wait_queue_head_t congestion_wqh[2] = {
        };
 
 
-void clear_bdi_congested(struct backing_dev_info *bdi, int rw)
+void clear_bdi_congested(struct backing_dev_info *bdi, int sync)
 {
        enum bdi_state bit;
-       wait_queue_head_t *wqh = &congestion_wqh[rw];
+       wait_queue_head_t *wqh = &congestion_wqh[sync];
 
-       bit = (rw == WRITE) ? BDI_write_congested : BDI_read_congested;
+       bit = sync ? BDI_sync_congested : BDI_async_congested;
        clear_bit(bit, &bdi->state);
        smp_mb__after_clear_bit();
        if (waitqueue_active(wqh))
@@ -297,11 +297,11 @@ void clear_bdi_congested(struct backing_dev_info *bdi, int rw)
 }
 EXPORT_SYMBOL(clear_bdi_congested);
 
-void set_bdi_congested(struct backing_dev_info *bdi, int rw)
+void set_bdi_congested(struct backing_dev_info *bdi, int sync)
 {
        enum bdi_state bit;
 
-       bit = (rw == WRITE) ? BDI_write_congested : BDI_read_congested;
+       bit = sync ? BDI_sync_congested : BDI_async_congested;
        set_bit(bit, &bdi->state);
 }
 EXPORT_SYMBOL(set_bdi_congested);