diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-10-31 17:46:07 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-10-31 17:46:07 -0700 |
commit | 094803e0aab3fe75bbf8202a8f4b5280eaade375 (patch) | |
tree | 278528ca9245a767fcfcfa97d977bd5714c082fd /drivers | |
parent | 32087d4eeca14b82660dab288b1d659963b954bd (diff) | |
parent | d8805e633e054c816c47cb6e727c81f156d9253d (diff) |
Merge branch 'akpm' (Andrew's incoming)
Quoth Andrew:
- Most of MM. Still waiting for the poweroc guys to get off their
butts and review some threaded hugepages patches.
- alpha
- vfs bits
- drivers/misc
- a few core kerenl tweaks
- printk() features
- MAINTAINERS updates
- backlight merge
- leds merge
- various lib/ updates
- checkpatch updates
* akpm: (127 commits)
epoll: fix spurious lockdep warnings
checkpatch: add a --strict check for utf-8 in commit logs
kernel.h/checkpatch: mark strict_strto<foo> and simple_strto<foo> as obsolete
llist-return-whether-list-is-empty-before-adding-in-llist_add-fix
wireless: at76c50x: follow rename pack_hex_byte to hex_byte_pack
fat: follow rename pack_hex_byte() to hex_byte_pack()
security: follow rename pack_hex_byte() to hex_byte_pack()
kgdb: follow rename pack_hex_byte() to hex_byte_pack()
lib: rename pack_hex_byte() to hex_byte_pack()
lib/string.c: fix strim() semantics for strings that have only blanks
lib/idr.c: fix comment for ida_get_new_above()
lib/percpu_counter.c: enclose hotplug only variables in hotplug ifdef
lib/bitmap.c: quiet sparse noise about address space
lib/spinlock_debug.c: print owner on spinlock lockup
lib/kstrtox: common code between kstrto*() and simple_strto*() functions
drivers/leds/leds-lp5521.c: check if reset is successful
leds: turn the blink_timer off before starting to blink
leds: save the delay values after a successful call to blink_set()
drivers/leds/leds-gpio.c: use gpio_get_value_cansleep() when initializing
drivers/leds/leds-lm3530.c: add __devexit_p where needed
...
Diffstat (limited to 'drivers')
36 files changed, 738 insertions, 368 deletions
diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index a61e7815a2a..6460487e41b 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -27,8 +27,7 @@ static struct class *hwmon_class; -static DEFINE_IDR(hwmon_idr); -static DEFINE_SPINLOCK(idr_lock); +static DEFINE_IDA(hwmon_ida); /** * hwmon_device_register - register w/ hwmon @@ -42,30 +41,17 @@ static DEFINE_SPINLOCK(idr_lock); struct device *hwmon_device_register(struct device *dev) { struct device *hwdev; - int id, err; - -again: - if (unlikely(idr_pre_get(&hwmon_idr, GFP_KERNEL) == 0)) - return ERR_PTR(-ENOMEM); - - spin_lock(&idr_lock); - err = idr_get_new(&hwmon_idr, NULL, &id); - spin_unlock(&idr_lock); + int id; - if (unlikely(err == -EAGAIN)) - goto again; - else if (unlikely(err)) - return ERR_PTR(err); + id = ida_simple_get(&hwmon_ida, 0, 0, GFP_KERNEL); + if (id < 0) + return ERR_PTR(id); - id = id & MAX_ID_MASK; hwdev = device_create(hwmon_class, dev, MKDEV(0, 0), NULL, HWMON_ID_FORMAT, id); - if (IS_ERR(hwdev)) { - spin_lock(&idr_lock); - idr_remove(&hwmon_idr, id); - spin_unlock(&idr_lock); - } + if (IS_ERR(hwdev)) + ida_simple_remove(&hwmon_ida, id); return hwdev; } @@ -81,9 +67,7 @@ void hwmon_device_unregister(struct device *dev) if (likely(sscanf(dev_name(dev), HWMON_ID_FORMAT, &id) == 1)) { device_unregister(dev); - spin_lock(&idr_lock); - idr_remove(&hwmon_idr, id); - spin_unlock(&idr_lock); + ida_simple_remove(&hwmon_ida, id); } else dev_dbg(dev->parent, "hwmon_device_unregister() failed: bad class ID!\n"); diff --git a/drivers/hwmon/ibmaem.c b/drivers/hwmon/ibmaem.c index c316294c48b..783d0c17b76 100644 --- a/drivers/hwmon/ibmaem.c +++ b/drivers/hwmon/ibmaem.c @@ -88,8 +88,7 @@ #define AEM_MIN_POWER_INTERVAL 200 #define UJ_PER_MJ 1000L -static DEFINE_IDR(aem_idr); -static DEFINE_SPINLOCK(aem_idr_lock); +static DEFINE_IDA(aem_ida); static struct platform_driver aem_driver = { .driver = { @@ -356,38 +355,6 @@ static void aem_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data) complete(&data->read_complete); } -/* ID functions */ - -/* Obtain an id */ -static int aem_idr_get(int *id) -{ - int i, err; - -again: - if (unlikely(!idr_pre_get(&aem_idr, GFP_KERNEL))) - return -ENOMEM; - - spin_lock(&aem_idr_lock); - err = idr_get_new(&aem_idr, NULL, &i); - spin_unlock(&aem_idr_lock); - - if (unlikely(err == -EAGAIN)) - goto again; - else if (unlikely(err)) - return err; - - *id = i & MAX_ID_MASK; - return 0; -} - -/* Release an object ID */ -static void aem_idr_put(int id) -{ - spin_lock(&aem_idr_lock); - idr_remove(&aem_idr, id); - spin_unlock(&aem_idr_lock); -} - /* Sensor support functions */ /* Read a sensor value */ @@ -530,7 +497,7 @@ static void aem_delete(struct aem_data *data) ipmi_destroy_user(data->ipmi.user); platform_set_drvdata(data->pdev, NULL); platform_device_unregister(data->pdev); - aem_idr_put(data->id); + ida_simple_remove(&aem_ida, data->id); kfree(data); } @@ -587,7 +554,8 @@ static int aem_init_aem1_inst(struct aem_ipmi_data *probe, u8 module_handle) data->power_period[i] = AEM_DEFAULT_POWER_INTERVAL; /* Create sub-device for this fw instance */ - if (aem_idr_get(&data->id)) + data->id = ida_simple_get(&aem_ida, 0, 0, GFP_KERNEL); + if (data->id < 0) goto id_err; data->pdev = platform_device_alloc(DRVNAME, data->id); @@ -638,7 +606,7 @@ ipmi_err: platform_set_drvdata(data->pdev, NULL); platform_device_unregister(data->pdev); dev_err: - aem_idr_put(data->id); + ida_simple_remove(&aem_ida, data->id); id_err: kfree(data); @@ -720,7 +688,8 @@ static int aem_init_aem2_inst(struct aem_ipmi_data *probe, data->power_period[i] = AEM_DEFAULT_POWER_INTERVAL; /* Create sub-device for this fw instance */ - if (aem_idr_get(&data->id)) + data->id = ida_simple_get(&aem_ida, 0, 0, GFP_KERNEL); + if (data->id < 0) goto id_err; data->pdev = platform_device_alloc(DRVNAME, data->id); @@ -771,7 +740,7 @@ ipmi_err: platform_set_drvdata(data->pdev, NULL); platform_device_unregister(data->pdev); dev_err: - aem_idr_put(data->id); + ida_simple_remove(&aem_ida, data->id); id_err: kfree(data); diff --git a/drivers/infiniband/core/umem.c b/drivers/infiniband/core/umem.c index b645e558876..9155f91d66b 100644 --- a/drivers/infiniband/core/umem.c +++ b/drivers/infiniband/core/umem.c @@ -136,7 +136,7 @@ struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr, down_write(¤t->mm->mmap_sem); - locked = npages + current->mm->locked_vm; + locked = npages + current->mm->pinned_vm; lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; if ((locked > lock_limit) && !capable(CAP_IPC_LOCK)) { @@ -206,7 +206,7 @@ out: __ib_umem_release(context->device, umem, 0); kfree(umem); } else - current->mm->locked_vm = locked; + current->mm->pinned_vm = locked; up_write(¤t->mm->mmap_sem); if (vma_list) @@ -222,7 +222,7 @@ static void ib_umem_account(struct work_struct *work) struct ib_umem *umem = container_of(work, struct ib_umem, work); down_write(&umem->mm->mmap_sem); - umem->mm->locked_vm -= umem->diff; + umem->mm->pinned_vm -= umem->diff; up_write(&umem->mm->mmap_sem); mmput(umem->mm); kfree(umem); diff --git a/drivers/infiniband/hw/ipath/ipath_user_pages.c b/drivers/infiniband/hw/ipath/ipath_user_pages.c index cfed5399f07..dc66c450691 100644 --- a/drivers/infiniband/hw/ipath/ipath_user_pages.c +++ b/drivers/infiniband/hw/ipath/ipath_user_pages.c @@ -79,7 +79,7 @@ static int __ipath_get_user_pages(unsigned long start_page, size_t num_pages, goto bail_release; } - current->mm->locked_vm += num_pages; + current->mm->pinned_vm += num_pages; ret = 0; goto bail; @@ -178,7 +178,7 @@ void ipath_release_user_pages(struct page **p, size_t num_pages) __ipath_release_user_pages(p, num_pages, 1); - current->mm->locked_vm -= num_pages; + current->mm->pinned_vm -= num_pages; up_write(¤t->mm->mmap_sem); } @@ -195,7 +195,7 @@ static void user_pages_account(struct work_struct *_work) container_of(_work, struct ipath_user_pages_work, work); down_write(&work->mm->mmap_sem); - work->mm->locked_vm -= work->num_pages; + work->mm->pinned_vm -= work->num_pages; up_write(&work->mm->mmap_sem); mmput(work->mm); kfree(work); diff --git a/drivers/infiniband/hw/qib/qib_user_pages.c b/drivers/infiniband/hw/qib/qib_user_pages.c index 7689e49c13c..2bc1d2b9629 100644 --- a/drivers/infiniband/hw/qib/qib_user_pages.c +++ b/drivers/infiniband/hw/qib/qib_user_pages.c @@ -74,7 +74,7 @@ static int __qib_get_user_pages(unsigned long start_page, size_t num_pages, goto bail_release; } - current->mm->locked_vm += num_pages; + current->mm->pinned_vm += num_pages; ret = 0; goto bail; @@ -151,7 +151,7 @@ void qib_release_user_pages(struct page **p, size_t num_pages) __qib_release_user_pages(p, num_pages, 1); if (current->mm) { - current->mm->locked_vm -= num_pages; + current->mm->pinned_vm -= num_pages; up_write(¤t->mm->mmap_sem); } } diff --git a/drivers/isdn/hisax/callc.c b/drivers/isdn/hisax/callc.c index 37e685eafd2..c4897e1075d 100644 --- a/drivers/isdn/hisax/callc.c +++ b/drivers/isdn/hisax/callc.c @@ -65,7 +65,7 @@ hisax_findcard(int driverid) return (struct IsdnCardState *) 0; } -static __attribute__((format(printf, 3, 4))) void +static __printf(3, 4) void link_debug(struct Channel *chanp, int direction, char *fmt, ...) { va_list args; @@ -1068,7 +1068,7 @@ init_d_st(struct Channel *chanp) return 0; } -static __attribute__((format(printf, 2, 3))) void +static __printf(2, 3) void callc_debug(struct FsmInst *fi, char *fmt, ...) { va_list args; diff --git a/drivers/isdn/hisax/hisax.h b/drivers/isdn/hisax/hisax.h index 0a5c42a3f12..aff45a11a92 100644 --- a/drivers/isdn/hisax/hisax.h +++ b/drivers/isdn/hisax/hisax.h @@ -1287,9 +1287,9 @@ int jiftime(char *s, long mark); int HiSax_command(isdn_ctrl * ic); int HiSax_writebuf_skb(int id, int chan, int ack, struct sk_buff *skb); -__attribute__((format(printf, 3, 4))) +__printf(3, 4) void HiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt, ...); -__attribute__((format(printf, 3, 0))) +__printf(3, 0) void VHiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt, va_list args); void HiSax_reportcard(int cardnr, int sel); int QuickHex(char *txt, u_char * p, int cnt); diff --git a/drivers/isdn/hisax/isdnl1.h b/drivers/isdn/hisax/isdnl1.h index 425d86116f2..66ddcab19bb 100644 --- a/drivers/isdn/hisax/isdnl1.h +++ b/drivers/isdn/hisax/isdnl1.h @@ -21,7 +21,7 @@ #define B_XMTBUFREADY 1 #define B_ACKPENDING 2 -__attribute__((format(printf, 2, 3))) +__printf(2, 3) void debugl1(struct IsdnCardState *cs, char *fmt, ...); void DChannel_proc_xmt(struct IsdnCardState *cs); void DChannel_proc_rcv(struct IsdnCardState *cs); diff --git a/drivers/isdn/hisax/isdnl3.c b/drivers/isdn/hisax/isdnl3.c index ad291f21b20..1c24e4457b6 100644 --- a/drivers/isdn/hisax/isdnl3.c +++ b/drivers/isdn/hisax/isdnl3.c @@ -66,7 +66,7 @@ static char *strL3Event[] = "EV_TIMEOUT", }; -static __attribute__((format(printf, 2, 3))) void +static __printf(2, 3) void l3m_debug(struct FsmInst *fi, char *fmt, ...) { va_list args; diff --git a/drivers/isdn/hisax/st5481_d.c b/drivers/isdn/hisax/st5481_d.c index 44082637a09..db247b79e56 100644 --- a/drivers/isdn/hisax/st5481_d.c +++ b/drivers/isdn/hisax/st5481_d.c @@ -167,7 +167,7 @@ static struct FsmNode L1FnList[] __initdata = {ST_L1_F8, EV_IND_RSY, l1_ignore}, }; -static __attribute__((format(printf, 2, 3))) +static __printf(2, 3) void l1m_debug(struct FsmInst *fi, char *fmt, ...) { va_list args; @@ -270,7 +270,7 @@ static char *strDoutEvent[] = "EV_DOUT_UNDERRUN", }; -static __attribute__((format(printf, 2, 3))) +static __printf(2, 3) void dout_debug(struct FsmInst *fi, char *fmt, ...) { va_list args; diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index dc7caaddecf..ff203a42186 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -375,6 +375,18 @@ config LEDS_ASIC3 cannot be used. This driver supports hardware blinking with an on+off period from 62ms to 125s. Say Y to enable LEDs on the HP iPAQ hx4700. +config LEDS_RENESAS_TPU + bool "LED support for Renesas TPU" + depends on LEDS_CLASS && HAVE_CLK && GENERIC_GPIO + help + This option enables build of the LED TPU platform driver, + suitable to drive any TPU channel on newer Renesas SoCs. + The driver controls the GPIO pin connected to the LED via + the GPIO framework and expects the LED to be connected to + a pin that can be driven in both GPIO mode and using TPU + pin function. The latter to support brightness control. + Brightness control is supported but hardware blinking is not. + config LEDS_TRIGGERS bool "LED Trigger support" depends on LEDS_CLASS diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index a0a1b89d78a..e4f6bf56888 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -42,6 +42,7 @@ obj-$(CONFIG_LEDS_MC13783) += leds-mc13783.o obj-$(CONFIG_LEDS_NS2) += leds-ns2.o obj-$(CONFIG_LEDS_NETXBIG) += leds-netxbig.o obj-$(CONFIG_LEDS_ASIC3) += leds-asic3.o +obj-$(CONFIG_LEDS_RENESAS_TPU) += leds-renesas-tpu.o # LED SPI Drivers obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index dc3d3d83191..661b692573e 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -267,9 +267,14 @@ void led_blink_set(struct led_classdev *led_cdev, unsigned long *delay_on, unsigned long *delay_off) { + del_timer_sync(&led_cdev->blink_timer); + if (led_cdev->blink_set && - !led_cdev->blink_set(led_cdev, delay_on, delay_off)) + !led_cdev->blink_set(led_cdev, delay_on, delay_off)) { + led_cdev->blink_delay_on = *delay_on; + led_cdev->blink_delay_off = *delay_off; return; + } /* blink with 1 Hz as default if nothing specified */ if (!*delay_on && !*delay_off) diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c index 4bebae73334..6f1ff93d7ce 100644 --- a/drivers/leds/led-triggers.c +++ b/drivers/leds/led-triggers.c @@ -261,9 +261,12 @@ void led_trigger_register_simple(const char *name, struct led_trigger **tp) if (trigger) { trigger->name = name; err = led_trigger_register(trigger); - if (err < 0) + if (err < 0) { + kfree(trigger); + trigger = NULL; printk(KERN_WARNING "LED trigger %s failed to register" " (%d)\n", name, err); + } } else printk(KERN_WARNING "LED trigger %s failed to register" " (no memory)\n", name); diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index 3d8bc327a68..504cc26c7e4 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -121,7 +121,7 @@ static int __devinit create_gpio_led(const struct gpio_led *template, } led_dat->cdev.brightness_set = gpio_led_set; if (template->default_state == LEDS_GPIO_DEFSTATE_KEEP) - state = !!gpio_get_value(led_dat->gpio) ^ led_dat->active_low; + state = !!gpio_get_value_cansleep(led_dat->gpio) ^ led_dat->active_low; else state = (template->default_state == LEDS_GPIO_DEFSTATE_ON); led_dat->cdev.brightness = state ? LED_FULL : LED_OFF; diff --git a/drivers/leds/leds-lm3530.c b/drivers/leds/leds-lm3530.c index 3dd7090a9a9..4dc510fdfa0 100644 --- a/drivers/leds/leds-lm3530.c +++ b/drivers/leds/leds-lm3530.c @@ -421,7 +421,6 @@ err_class_register: err_reg_init: regulator_put(drvdata->regulator); err_regulator_get: - i2c_set_clientdata(client, NULL); kfree(drvdata); err_out: return err; @@ -449,7 +448,7 @@ MODULE_DEVICE_TABLE(i2c, lm3530_id); static struct i2c_driver lm3530_i2c_driver = { .probe = lm3530_probe, - .remove = lm3530_remove, + .remove = __devexit_p(lm3530_remove), .id_table = lm3530_id, .driver = { .name = LM3530_NAME, diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 9fc122c81f0..cb641f1b334 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -97,6 +97,9 @@ /* Status */ #define LP5521_EXT_CLK_USED 0x08 +/* default R channel current register value */ +#define LP5521_REG_R_CURR_DEFAULT 0xAF + struct lp5521_engine { int id; u8 mode; @@ -175,14 +178,14 @@ static int lp5521_set_engine_mode(struct lp5521_engine *engine, u8 mode) mode = LP5521_CMD_DIRECT; ret = lp5521_read(client, LP5521_REG_OP_MODE, &engine_state); + if (ret < 0) + return ret; /* set mode only for this engine */ engine_state &= ~(engine->engine_mask); mode &= engine->engine_mask; engine_state |= mode; - ret |= lp5521_write(client, LP5521_REG_OP_MODE, engine_state); - - return ret; + return lp5521_write(client, LP5521_REG_OP_MODE, engine_state); } static int lp5521_load_program(struct lp5521_engine *eng, const u8 *pattern) @@ -643,6 +646,7 @@ static int __devinit lp5521_probe(struct i2c_client *client, struct lp5521_chip *chip; struct lp5521_platform_data *pdata; int ret, i, led; + u8 buf; chip = kzalloc(sizeof(*chip), GFP_KERNEL); if (!chip) @@ -681,6 +685,20 @@ static int __devinit lp5521_probe(struct i2c_client *client, * Exact value is not available. 10 - 20ms * appears to be enough for reset. */ + + /* + * Make sure that the chip is reset by reading back the r channel + * current reg. This is dummy read is required on some platforms - + * otherwise further access to the R G B channels in the + * LP5521_REG_ENABLE register will not have any effect - strange! + */ + lp5521_read(client, LP5521_REG_R_CURRENT, &buf); + if (buf != LP5521_REG_R_CURR_DEFAULT) { + dev_err(&client->dev, "error in reseting chip\n"); + goto fail2; + } + usleep_range(10000, 20000); + ret = lp5521_detect(client); if (ret) { diff --git a/drivers/leds/leds-renesas-tpu.c b/drivers/leds/leds-renesas-tpu.c new file mode 100644 index 00000000000..3ee540eb127 --- /dev/null +++ b/drivers/leds/leds-renesas-tpu.c @@ -0,0 +1,357 @@ +/* + * LED control using Renesas TPU + * + * Copyright (C) 2011 Magnus Damm + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> +#include <linux/printk.h> +#include <linux/ioport.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/leds.h> +#include <linux/platform_data/leds-renesas-tpu.h> +#include <linux/gpio.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/pm_runtime.h> +#include <linux/workqueue.h> + +enum r_tpu_pin { R_TPU_PIN_UNUSED, R_TPU_PIN_GPIO, R_TPU_PIN_GPIO_FN }; +enum r_tpu_timer { R_TPU_TIMER_UNUSED, R_TPU_TIMER_ON }; + +struct r_tpu_priv { + struct led_classdev ldev; + void __iomem *mapbase; + struct clk *clk; + struct platform_device *pdev; + enum r_tpu_pin pin_state; + enum r_tpu_timer timer_state; + unsigned long min_rate; + unsigned int refresh_rate; + struct work_struct work; + enum led_brightness new_brightness; +}; + +static DEFINE_SPINLOCK(r_tpu_lock); + +#define TSTR -1 /* Timer start register (shared register) */ +#define TCR 0 /* Timer control register (+0x00) */ +#define TMDR 1 /* Timer mode register (+0x04) */ +#define TIOR 2 /* Timer I/O control register (+0x08) */ +#define TIER 3 /* Timer interrupt enable register (+0x0c) */ +#define TSR 4 /* Timer status register (+0x10) */ +#define TCNT 5 /* Timer counter (+0x14) */ +#define TGRA 6 /* Timer general register A (+0x18) */ +#define TGRB 7 /* Timer general register B (+0x1c) */ +#define TGRC 8 /* Timer general register C (+0x20) */ +#define TGRD 9 /* Timer general register D (+0x24) */ + +static inline unsigned short r_tpu_read(struct r_tpu_priv *p, int reg_nr) +{ + struct led_renesas_tpu_config *cfg = p->pdev->dev.platform_data; + void __iomem *base = p->mapbase; + unsigned long offs = reg_nr << 2; + + if (reg_nr == TSTR) + return ioread16(base - cfg->channel_offset); + + return ioread16(base + offs); +} + +static inline void r_tpu_write(struct r_tpu_priv *p, int reg_nr, + unsigned short value) +{ + struct led_renesas_tpu_config *cfg = p->pdev->dev.platform_data; + void __iomem *base = p->mapbase; + unsigned long offs = reg_nr << 2; + + if (reg_nr == TSTR) { + iowrite16(value, base - cfg->channel_offset); + return; + } + + iowrite16(value, base + offs); +} + +static void r_tpu_start_stop_ch(struct r_tpu_priv *p, int start) +{ + struct led_renesas_tpu_config *cfg = p->pdev->dev.platform_data; + unsigned long flags, value; + + /* start stop register shared by multiple timer channels */ + spin_lock_irqsave(&r_tpu_lock, flags); + value = r_tpu_read(p, TSTR); + + if (start) + value |= 1 << cfg->timer_bit; + else + value &= ~(1 << cfg->timer_bit); + + r_tpu_write(p, TSTR, value); + spin_unlock_irqrestore(&r_tpu_lock, flags); +} + +static int r_tpu_enable(struct r_tpu_priv *p, enum led_brightness brightness) +{ + struct led_renesas_tpu_config *cfg = p->pdev->dev.platform_data; + int prescaler[] = { 1, 4, 16, 64 }; + int k, ret; + unsigned long rate, tmp; + + if (p->timer_state == R_TPU_TIMER_ON) + return 0; + + /* wake up device and enable clock */ + pm_runtime_get_sync(&p->pdev->dev); + ret = clk_enable(p->clk); + if (ret) { + dev_err(&p->pdev->dev, "cannot enable clock\n"); + return ret; + } + + /* make sure channel is disabled */ + r_tpu_start_stop_ch(p, 0); + + /* get clock rate after enabling it */ + rate = clk_get_rate(p->clk); + + /* pick the lowest acceptable rate */ + for (k = 0; k < ARRAY_SIZE(prescaler); k++) + if ((rate / prescaler[k]) < p->min_rate) + break; + + if (!k) { + dev_err(&p->pdev->dev, "clock rate mismatch\n"); + goto err0; + } + dev_dbg(&p->pdev->dev, "rate = %lu, prescaler %u\n", + rate, prescaler[k - 1]); + + /* clear TCNT on TGRB match, count on rising edge, set prescaler */ + r_tpu_write(p, TCR, 0x0040 | (k - 1)); + + /* output 0 until TGRA, output 1 until TGRB */ + r_tpu_write(p, TIOR, 0x0002); + + rate /= prescaler[k - 1] * p->refresh_rate; + r_tpu_write(p, TGRB, rate); + dev_dbg(&p->pdev->dev, "TRGB = 0x%04lx\n", rate); + + tmp = (cfg->max_brightness - brightness) * rate; + r_tpu_write(p, TGRA, tmp / cfg->max_brightness); + dev_dbg(&p->pdev->dev, "TRGA = 0x%04lx\n", tmp / cfg->max_brightness); + + /* PWM mode */ + r_tpu_write(p, TMDR, 0x0002); + + /* enable channel */ + r_tpu_start_stop_ch(p, 1); + + p->timer_state = R_TPU_TIMER_ON; + return 0; + err0: + clk_disable(p->clk); + pm_runtime_put_sync(&p->pdev->dev); + return -ENOTSUPP; +} + +static void r_tpu_disable(struct r_tpu_priv *p) +{ + if (p->timer_state == R_TPU_TIMER_UNUSED) + return; + + /* disable channel */ + r_tpu_start_stop_ch(p, 0); + + /* stop clock and mark device as idle */ + clk_disable(p->clk); + pm_runtime_put_sync(&p->pdev->dev); + + p->timer_state = R_TPU_TIMER_UNUSED; +} + +static void r_tpu_set_pin(struct r_tpu_priv *p, enum r_tpu_pin new_state, + enum led_brightness brightness) +{ + struct led_renesas_tpu_config *cfg = p->pdev->dev.platform_data; + + if (p->pin_state == new_state) { + if (p->pin_state == R_TPU_PIN_GPIO) + gpio_set_value(cfg->pin_gpio, brightness); + return; + } + + if (p->pin_state == R_TPU_PIN_GPIO) + gpio_free(cfg->pin_gpio); + + if (p->pin_state == R_TPU_PIN_GPIO_FN) + gpio_free(cfg->pin_gpio_fn); + + if (new_state == R_TPU_PIN_GPIO) { + gpio_request(cfg->pin_gpio, cfg->name); + gpio_direction_output(cfg->pin_gpio, !!brightness); + } + if (new_state == R_TPU_PIN_GPIO_FN) + gpio_request(cfg->pin_gpio_fn, cfg->name); + + p->pin_state = new_state; +} + +static void r_tpu_work(struct work_struct *work) +{ + struct r_tpu_priv *p = container_of(work, struct r_tpu_priv, work); + enum led_brightness brightness = p->new_brightness; + + r_tpu_disable(p); + + /* off and maximum are handled as GPIO pins, in between P |