diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2009-12-17 18:55:08 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-12-17 18:55:08 -0500 |
| commit | 9209e4bd4b3b9ee3a104313b426380e8d1f59260 (patch) | |
| tree | 8c2d3f01d7e325ce9b26e144ccc6ffcabdbb132b | |
| parent | a695bc683609a2b815915e4cfcd93e4ccc0e2cae (diff) | |
| parent | e45906203d27296b47e56efc1219f9a200bfc2db (diff) | |
Merge branch 'for-linus' of git://git.o-hand.com/linux-rpurdie-leds
* 'for-linus' of git://git.o-hand.com/linux-rpurdie-leds:
leds: leds-pwm: Set led_classdev max_brightness
leds: leds-lp3944.h - remove unneeded includes
leds: use default-on trigger for Cobalt Qube
leds: drivers/leds/leds-ss4200.c: fix return statement
leds: leds-pca9532.h- indent with tabs, not spaces
leds: Add LED class driver for regulator driven LEDs.
leds: leds-cobalt-qube.c: use resource_size()
leds: leds-cobalt-raq.c - use resource_size()
leds: Add driver for ADP5520/ADP5501 MFD PMICs
leds: Add driver for LT3593 controlled LEDs
leds-ss4200: Check pci_enable_device return
leds: leds-alix2c - take port address from MSR
leds: LED driver for Intel NAS SS4200 series (v5)
| -rw-r--r-- | drivers/leds/Kconfig | 33 | ||||
| -rw-r--r-- | drivers/leds/Makefile | 4 | ||||
| -rw-r--r-- | drivers/leds/leds-adp5520.c | 230 | ||||
| -rw-r--r-- | drivers/leds/leds-alix2.c | 115 | ||||
| -rw-r--r-- | drivers/leds/leds-cobalt-qube.c | 4 | ||||
| -rw-r--r-- | drivers/leds/leds-cobalt-raq.c | 2 | ||||
| -rw-r--r-- | drivers/leds/leds-lt3593.c | 217 | ||||
| -rw-r--r-- | drivers/leds/leds-pwm.c | 5 | ||||
| -rw-r--r-- | drivers/leds/leds-regulator.c | 242 | ||||
| -rw-r--r-- | drivers/leds/leds-ss4200.c | 556 | ||||
| -rw-r--r-- | include/linux/leds-lp3944.h | 3 | ||||
| -rw-r--r-- | include/linux/leds-pca9532.h | 2 | ||||
| -rw-r--r-- | include/linux/leds-regulator.h | 46 |
13 files changed, 1418 insertions, 41 deletions
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index e4f599f20e38..8a0e1ec95e4a 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig | |||
| @@ -229,6 +229,12 @@ config LEDS_PWM | |||
| 229 | help | 229 | help |
| 230 | This option enables support for pwm driven LEDs | 230 | This option enables support for pwm driven LEDs |
| 231 | 231 | ||
| 232 | config LEDS_REGULATOR | ||
| 233 | tristate "REGULATOR driven LED support" | ||
| 234 | depends on LEDS_CLASS && REGULATOR | ||
| 235 | help | ||
| 236 | This option enables support for regulator driven LEDs. | ||
| 237 | |||
| 232 | config LEDS_BD2802 | 238 | config LEDS_BD2802 |
| 233 | tristate "LED driver for BD2802 RGB LED" | 239 | tristate "LED driver for BD2802 RGB LED" |
| 234 | depends on LEDS_CLASS && I2C | 240 | depends on LEDS_CLASS && I2C |
| @@ -236,6 +242,33 @@ config LEDS_BD2802 | |||
| 236 | This option enables support for BD2802GU RGB LED driver chips | 242 | This option enables support for BD2802GU RGB LED driver chips |
| 237 | accessed via the I2C bus. | 243 | accessed via the I2C bus. |
| 238 | 244 | ||
| 245 | config LEDS_INTEL_SS4200 | ||
| 246 | tristate "LED driver for Intel NAS SS4200 series" | ||
| 247 | depends on LEDS_CLASS && PCI && DMI | ||
| 248 | help | ||
| 249 | This option enables support for the Intel SS4200 series of | ||
| 250 | Network Attached Storage servers. You may control the hard | ||
| 251 | drive or power LEDs on the front panel. Using this driver | ||
| 252 | can stop the front LED from blinking after startup. | ||
| 253 | |||
| 254 | config LEDS_LT3593 | ||
| 255 | tristate "LED driver for LT3593 controllers" | ||
| 256 | depends on LEDS_CLASS && GENERIC_GPIO | ||
| 257 | help | ||
| 258 | This option enables support for LEDs driven by a Linear Technology | ||
| 259 | LT3593 controller. This controller uses a special one-wire pulse | ||
| 260 | coding protocol to set the brightness. | ||
| 261 | |||
| 262 | config LEDS_ADP5520 | ||
| 263 | tristate "LED Support for ADP5520/ADP5501 PMIC" | ||
| 264 | depends on LEDS_CLASS && PMIC_ADP5520 | ||
| 265 | help | ||
| 266 | This option enables support for on-chip LED drivers found | ||
| 267 | on Analog Devices ADP5520/ADP5501 PMICs. | ||
| 268 | |||
| 269 | To compile this driver as a module, choose M here: the module will | ||
| 270 | be called leds-adp5520. | ||
| 271 | |||
| 239 | comment "LED Triggers" | 272 | comment "LED Triggers" |
| 240 | 273 | ||
| 241 | config LEDS_TRIGGERS | 274 | config LEDS_TRIGGERS |
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 46d72704d606..9e63869d7c0d 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile | |||
| @@ -29,6 +29,10 @@ obj-$(CONFIG_LEDS_DA903X) += leds-da903x.o | |||
| 29 | obj-$(CONFIG_LEDS_WM831X_STATUS) += leds-wm831x-status.o | 29 | obj-$(CONFIG_LEDS_WM831X_STATUS) += leds-wm831x-status.o |
| 30 | obj-$(CONFIG_LEDS_WM8350) += leds-wm8350.o | 30 | obj-$(CONFIG_LEDS_WM8350) += leds-wm8350.o |
| 31 | obj-$(CONFIG_LEDS_PWM) += leds-pwm.o | 31 | obj-$(CONFIG_LEDS_PWM) += leds-pwm.o |
| 32 | obj-$(CONFIG_LEDS_REGULATOR) += leds-regulator.o | ||
| 33 | obj-$(CONFIG_LEDS_INTEL_SS4200) += leds-ss4200.o | ||
| 34 | obj-$(CONFIG_LEDS_LT3593) += leds-lt3593.o | ||
| 35 | obj-$(CONFIG_LEDS_ADP5520) += leds-adp5520.o | ||
| 32 | 36 | ||
| 33 | # LED SPI Drivers | 37 | # LED SPI Drivers |
| 34 | obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o | 38 | obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o |
diff --git a/drivers/leds/leds-adp5520.c b/drivers/leds/leds-adp5520.c new file mode 100644 index 000000000000..a8f315902131 --- /dev/null +++ b/drivers/leds/leds-adp5520.c | |||
| @@ -0,0 +1,230 @@ | |||
| 1 | /* | ||
| 2 | * LEDs driver for Analog Devices ADP5520/ADP5501 MFD PMICs | ||
| 3 | * | ||
| 4 | * Copyright 2009 Analog Devices Inc. | ||
| 5 | * | ||
| 6 | * Loosely derived from leds-da903x: | ||
| 7 | * Copyright (C) 2008 Compulab, Ltd. | ||
| 8 | * Mike Rapoport <mike@compulab.co.il> | ||
| 9 | * | ||
| 10 | * Copyright (C) 2006-2008 Marvell International Ltd. | ||
| 11 | * Eric Miao <eric.miao@marvell.com> | ||
| 12 | * | ||
| 13 | * Licensed under the GPL-2 or later. | ||
| 14 | */ | ||
| 15 | |||
| 16 | #include <linux/module.h> | ||
| 17 | #include <linux/kernel.h> | ||
| 18 | #include <linux/init.h> | ||
| 19 | #include <linux/platform_device.h> | ||
| 20 | #include <linux/leds.h> | ||
| 21 | #include <linux/workqueue.h> | ||
| 22 | #include <linux/mfd/adp5520.h> | ||
| 23 | |||
| 24 | struct adp5520_led { | ||
| 25 | struct led_classdev cdev; | ||
| 26 | struct work_struct work; | ||
| 27 | struct device *master; | ||
| 28 | enum led_brightness new_brightness; | ||
| 29 | int id; | ||
| 30 | int flags; | ||
| 31 | }; | ||
| 32 | |||
| 33 | static void adp5520_led_work(struct work_struct *work) | ||
| 34 | { | ||
| 35 | struct adp5520_led *led = container_of(work, struct adp5520_led, work); | ||
| 36 | adp5520_write(led->master, ADP5520_LED1_CURRENT + led->id - 1, | ||
| 37 | led->new_brightness >> 2); | ||
| 38 | } | ||
| 39 | |||
| 40 | static void adp5520_led_set(struct led_classdev *led_cdev, | ||
| 41 | enum led_brightness value) | ||
| 42 | { | ||
| 43 | struct adp5520_led *led; | ||
| 44 | |||
| 45 | led = container_of(led_cdev, struct adp5520_led, cdev); | ||
| 46 | led->new_brightness = value; | ||
| 47 | schedule_work(&led->work); | ||
| 48 | } | ||
| 49 | |||
| 50 | static int adp5520_led_setup(struct adp5520_led *led) | ||
| 51 | { | ||
| 52 | struct device *dev = led->master; | ||
| 53 | int flags = led->flags; | ||
| 54 | int ret = 0; | ||
| 55 | |||
| 56 | switch (led->id) { | ||
| 57 | case FLAG_ID_ADP5520_LED1_ADP5501_LED0: | ||
| 58 | ret |= adp5520_set_bits(dev, ADP5520_LED_TIME, | ||
| 59 | (flags >> ADP5520_FLAG_OFFT_SHIFT) & | ||
| 60 | ADP5520_FLAG_OFFT_MASK); | ||
| 61 | ret |= adp5520_set_bits(dev, ADP5520_LED_CONTROL, | ||
| 62 | ADP5520_LED1_EN); | ||
| 63 | break; | ||
| 64 | case FLAG_ID_ADP5520_LED2_ADP5501_LED1: | ||
| 65 | ret |= adp5520_set_bits(dev, ADP5520_LED_TIME, | ||
| 66 | ((flags >> ADP5520_FLAG_OFFT_SHIFT) & | ||
| 67 | ADP5520_FLAG_OFFT_MASK) << 2); | ||
| 68 | ret |= adp5520_clr_bits(dev, ADP5520_LED_CONTROL, | ||
| 69 | ADP5520_R3_MODE); | ||
| 70 | ret |= adp5520_set_bits(dev, ADP5520_LED_CONTROL, | ||
| 71 | ADP5520_LED2_EN); | ||
| 72 | break; | ||
| 73 | case FLAG_ID_ADP5520_LED3_ADP5501_LED2: | ||
| 74 | ret |= adp5520_set_bits(dev, ADP5520_LED_TIME, | ||
| 75 | ((flags >> ADP5520_FLAG_OFFT_SHIFT) & | ||
| 76 | ADP5520_FLAG_OFFT_MASK) << 4); | ||
| 77 | ret |= adp5520_clr_bits(dev, ADP5520_LED_CONTROL, | ||
| 78 | ADP5520_C3_MODE); | ||
| 79 | ret |= adp5520_set_bits(dev, ADP5520_LED_CONTROL, | ||
| 80 | ADP5520_LED3_EN); | ||
| 81 | break; | ||
| 82 | } | ||
| 83 | |||
| 84 | return ret; | ||
| 85 | } | ||
| 86 | |||
| 87 | static int __devinit adp5520_led_prepare(struct platform_device *pdev) | ||
| 88 | { | ||
| 89 | struct adp5520_leds_platform_data *pdata = pdev->dev.platform_data; | ||
| 90 | struct device *dev = pdev->dev.parent; | ||
| 91 | int ret = 0; | ||
| 92 | |||
| 93 | ret |= adp5520_write(dev, ADP5520_LED1_CURRENT, 0); | ||
| 94 | ret |= adp5520_write(dev, ADP5520_LED2_CURRENT, 0); | ||
| 95 | ret |= adp5520_write(dev, ADP5520_LED3_CURRENT, 0); | ||
| 96 | ret |= adp5520_write(dev, ADP5520_LED_TIME, pdata->led_on_time << 6); | ||
| 97 | ret |= adp5520_write(dev, ADP5520_LED_FADE, FADE_VAL(pdata->fade_in, | ||
| 98 | pdata->fade_out)); | ||
| 99 | |||
| 100 | return ret; | ||
| 101 | } | ||
| 102 | |||
| 103 | static int __devinit adp5520_led_probe(struct platform_device *pdev) | ||
| 104 | { | ||
| 105 | struct adp5520_leds_platform_data *pdata = pdev->dev.platform_data; | ||
| 106 | struct adp5520_led *led, *led_dat; | ||
| 107 | struct led_info *cur_led; | ||
| 108 | int ret, i; | ||
| 109 | |||
| 110 | if (pdata == NULL) { | ||
| 111 | dev_err(&pdev->dev, "missing platform data\n"); | ||
| 112 | return -ENODEV; | ||
| 113 | } | ||
| 114 | |||
| 115 | if (pdata->num_leds > ADP5520_01_MAXLEDS) { | ||
| 116 | dev_err(&pdev->dev, "can't handle more than %d LEDS\n", | ||
| 117 | ADP5520_01_MAXLEDS); | ||
| 118 | return -EFAULT; | ||
| 119 | } | ||
| 120 | |||
| 121 | led = kzalloc(sizeof(*led) * pdata->num_leds, GFP_KERNEL); | ||
| 122 | if (led == NULL) { | ||
| 123 | dev_err(&pdev->dev, "failed to alloc memory\n"); | ||
| 124 | return -ENOMEM; | ||
| 125 | } | ||
| 126 | |||
| 127 | ret = adp5520_led_prepare(pdev); | ||
| 128 | |||
| 129 | if (ret) { | ||
| 130 | dev_err(&pdev->dev, "failed to write\n"); | ||
| 131 | goto err_free; | ||
| 132 | } | ||
| 133 | |||
| 134 | for (i = 0; i < pdata->num_leds; ++i) { | ||
| 135 | cur_led = &pdata->leds[i]; | ||
| 136 | led_dat = &led[i]; | ||
| 137 | |||
| 138 | led_dat->cdev.name = cur_led->name; | ||
| 139 | led_dat->cdev.default_trigger = cur_led->default_trigger; | ||
| 140 | led_dat->cdev.brightness_set = adp5520_led_set; | ||
| 141 | led_dat->cdev.brightness = LED_OFF; | ||
| 142 | |||
| 143 | if (cur_led->flags & ADP5520_FLAG_LED_MASK) | ||
| 144 | led_dat->flags = cur_led->flags; | ||
| 145 | else | ||
| 146 | led_dat->flags = i + 1; | ||
| 147 | |||
| 148 | led_dat->id = led_dat->flags & ADP5520_FLAG_LED_MASK; | ||
| 149 | |||
| 150 | led_dat->master = pdev->dev.parent; | ||
| 151 | led_dat->new_brightness = LED_OFF; | ||
| 152 | |||
| 153 | INIT_WORK(&led_dat->work, adp5520_led_work); | ||
| 154 | |||
| 155 | ret = led_classdev_register(led_dat->master, &led_dat->cdev); | ||
| 156 | if (ret) { | ||
| 157 | dev_err(&pdev->dev, "failed to register LED %d\n", | ||
| 158 | led_dat->id); | ||
| 159 | goto err; | ||
| 160 | } | ||
| 161 | |||
| 162 | ret = adp5520_led_setup(led_dat); | ||
| 163 | if (ret) { | ||
| 164 | dev_err(&pdev->dev, "failed to write\n"); | ||
| 165 | i++; | ||
| 166 | goto err; | ||
| 167 | } | ||
| 168 | } | ||
| 169 | |||
| 170 | platform_set_drvdata(pdev, led); | ||
| 171 | return 0; | ||
| 172 | |||
| 173 | err: | ||
| 174 | if (i > 0) { | ||
| 175 | for (i = i - 1; i >= 0; i--) { | ||
| 176 | led_classdev_unregister(&led[i].cdev); | ||
| 177 | cancel_work_sync(&led[i].work); | ||
| 178 | } | ||
| 179 | } | ||
| 180 | |||
| 181 | err_free: | ||
| 182 | kfree(led); | ||
| 183 | return ret; | ||
| 184 | } | ||
| 185 | |||
| 186 | static int __devexit adp5520_led_remove(struct platform_device *pdev) | ||
| 187 | { | ||
| 188 | struct adp5520_leds_platform_data *pdata = pdev->dev.platform_data; | ||
| 189 | struct adp5520_led *led; | ||
| 190 | int i; | ||
| 191 | |||
| 192 | led = platform_get_drvdata(pdev); | ||
| 193 | |||
| 194 | adp5520_clr_bits(led->master, ADP5520_LED_CONTROL, | ||
| 195 | ADP5520_LED1_EN | ADP5520_LED2_EN | ADP5520_LED3_EN); | ||
| 196 | |||
| 197 | for (i = 0; i < pdata->num_leds; i++) { | ||
| 198 | led_classdev_unregister(&led[i].cdev); | ||
| 199 | cancel_work_sync(&led[i].work); | ||
| 200 | } | ||
| 201 | |||
| 202 | kfree(led); | ||
| 203 | return 0; | ||
| 204 | } | ||
| 205 | |||
| 206 | static struct platform_driver adp5520_led_driver = { | ||
| 207 | .driver = { | ||
| 208 | .name = "adp5520-led", | ||
| 209 | .owner = THIS_MODULE, | ||
| 210 | }, | ||
| 211 | .probe = adp5520_led_probe, | ||
| 212 | .remove = __devexit_p(adp5520_led_remove), | ||
| 213 | }; | ||
| 214 | |||
| 215 | static int __init adp5520_led_init(void) | ||
| 216 | { | ||
| 217 | return platform_driver_register(&adp5520_led_driver); | ||
| 218 | } | ||
| 219 | module_init(adp5520_led_init); | ||
| 220 | |||
| 221 | static void __exit adp5520_led_exit(void) | ||
| 222 | { | ||
| 223 | platform_driver_unregister(&adp5520_led_driver); | ||
| 224 | } | ||
| 225 | module_exit(adp5520_led_exit); | ||
| 226 | |||
| 227 | MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); | ||
| 228 | MODULE_DESCRIPTION("LEDS ADP5520(01) Driver"); | ||
| 229 | MODULE_LICENSE("GPL"); | ||
| 230 | MODULE_ALIAS("platform:adp5520-led"); | ||
diff --git a/drivers/leds/leds-alix2.c b/drivers/leds/leds-alix2.c index 731d4eef3425..f59ffadf5125 100644 --- a/drivers/leds/leds-alix2.c +++ b/drivers/leds/leds-alix2.c | |||
| @@ -11,11 +11,24 @@ | |||
| 11 | #include <linux/module.h> | 11 | #include <linux/module.h> |
| 12 | #include <linux/platform_device.h> | 12 | #include <linux/platform_device.h> |
| 13 | #include <linux/string.h> | 13 | #include <linux/string.h> |
| 14 | #include <linux/pci.h> | ||
| 14 | 15 | ||
| 15 | static int force = 0; | 16 | static int force = 0; |
| 16 | module_param(force, bool, 0444); | 17 | module_param(force, bool, 0444); |
| 17 | MODULE_PARM_DESC(force, "Assume system has ALIX.2/ALIX.3 style LEDs"); | 18 | MODULE_PARM_DESC(force, "Assume system has ALIX.2/ALIX.3 style LEDs"); |
| 18 | 19 | ||
| 20 | #define MSR_LBAR_GPIO 0x5140000C | ||
| 21 | #define CS5535_GPIO_SIZE 256 | ||
| 22 | |||
| 23 | static u32 gpio_base; | ||
| 24 | |||
| 25 | static struct pci_device_id divil_pci[] = { | ||
| 26 | { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_ISA) }, | ||
| 27 | { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA) }, | ||
| 28 | { } /* NULL entry */ | ||
| 29 | }; | ||
| 30 | MODULE_DEVICE_TABLE(pci, divil_pci); | ||
| 31 | |||
| 19 | struct alix_led { | 32 | struct alix_led { |
| 20 | struct led_classdev cdev; | 33 | struct led_classdev cdev; |
| 21 | unsigned short port; | 34 | unsigned short port; |
| @@ -30,9 +43,9 @@ static void alix_led_set(struct led_classdev *led_cdev, | |||
| 30 | container_of(led_cdev, struct alix_led, cdev); | 43 | container_of(led_cdev, struct alix_led, cdev); |
| 31 | 44 | ||
| 32 | if (brightness) | 45 | if (brightness) |
| 33 | outl(led_dev->on_value, led_dev->port); | 46 | outl(led_dev->on_value, gpio_base + led_dev->port); |
| 34 | else | 47 | else |
| 35 | outl(led_dev->off_value, led_dev->port); | 48 | outl(led_dev->off_value, gpio_base + led_dev->port); |
| 36 | } | 49 | } |
| 37 | 50 | ||
| 38 | static struct alix_led alix_leds[] = { | 51 | static struct alix_led alix_leds[] = { |
| @@ -41,7 +54,7 @@ static struct alix_led alix_leds[] = { | |||
| 41 | .name = "alix:1", | 54 | .name = "alix:1", |
| 42 | .brightness_set = alix_led_set, | 55 | .brightness_set = alix_led_set, |
| 43 | }, | 56 | }, |
| 44 | .port = 0x6100, | 57 | .port = 0x00, |
| 45 | .on_value = 1 << 22, | 58 | .on_value = 1 << 22, |
| 46 | .off_value = 1 << 6, | 59 | .off_value = 1 << 6, |
| 47 | }, | 60 | }, |
| @@ -50,7 +63,7 @@ static struct alix_led alix_leds[] = { | |||
| 50 | .name = "alix:2", | 63 | .name = "alix:2", |
| 51 | .brightness_set = alix_led_set, | 64 | .brightness_set = alix_led_set, |
| 52 | }, | 65 | }, |
| 53 | .port = 0x6180, | 66 | .port = 0x80, |
| 54 | .on_value = 1 << 25, | 67 | .on_value = 1 << 25, |
| 55 | .off_value = 1 << 9, | 68 | .off_value = 1 << 9, |
| 56 | }, | 69 | }, |
| @@ -59,7 +72,7 @@ static struct alix_led alix_leds[] = { | |||
| 59 | .name = "alix:3", | 72 | .name = "alix:3", |
| 60 | .brightness_set = alix_led_set, | 73 | .brightness_set = alix_led_set, |
| 61 | }, | 74 | }, |
| 62 | .port = 0x6180, | 75 | .port = 0x80, |
| 63 | .on_value = 1 << 27, | 76 | .on_value = 1 << 27, |
| 64 | .off_value = 1 << 11, | 77 | .off_value = 1 << 11, |
| 65 | }, | 78 | }, |
| @@ -101,64 +114,104 @@ static struct platform_driver alix_led_driver = { | |||
| 101 | }, | 114 | }, |
| 102 | }; | 115 | }; |
| 103 | 116 | ||
| 104 | static int __init alix_present(void) | 117 | static int __init alix_present(unsigned long bios_phys, |
| 118 | const char *alix_sig, | ||
| 119 | size_t alix_sig_len) | ||
| 105 | { | 120 | { |
| 106 | const unsigned long bios_phys = 0x000f0000; | ||
| 107 | const size_t bios_len = 0x00010000; | 121 | const size_t bios_len = 0x00010000; |
| 108 | const char alix_sig[] = "PC Engines ALIX."; | ||
| 109 | const size_t alix_sig_len = sizeof(alix_sig) - 1; | ||
| 110 | |||
| 111 | const char *bios_virt; | 122 | const char *bios_virt; |
| 112 | const char *scan_end; | 123 | const char *scan_end; |
| 113 | const char *p; | 124 | const char *p; |
| 114 | int ret = 0; | 125 | char name[64]; |
| 115 | 126 | ||
| 116 | if (force) { | 127 | if (force) { |
| 117 | printk(KERN_NOTICE "%s: forced to skip BIOS test, " | 128 | printk(KERN_NOTICE "%s: forced to skip BIOS test, " |
| 118 | "assume system has ALIX.2 style LEDs\n", | 129 | "assume system has ALIX.2 style LEDs\n", |
| 119 | KBUILD_MODNAME); | 130 | KBUILD_MODNAME); |
| 120 | ret = 1; | 131 | return 1; |
| 121 | goto out; | ||
| 122 | } | 132 | } |
| 123 | 133 | ||
| 124 | bios_virt = phys_to_virt(bios_phys); | 134 | bios_virt = phys_to_virt(bios_phys); |
| 125 | scan_end = bios_virt + bios_len - (alix_sig_len + 2); | 135 | scan_end = bios_virt + bios_len - (alix_sig_len + 2); |
| 126 | for (p = bios_virt; p < scan_end; p++) { | 136 | for (p = bios_virt; p < scan_end; p++) { |
| 127 | const char *tail; | 137 | const char *tail; |
| 138 | char *a; | ||
| 128 | 139 | ||
| 129 | if (memcmp(p, alix_sig, alix_sig_len) != 0) { | 140 | if (memcmp(p, alix_sig, alix_sig_len) != 0) |
| 130 | continue; | 141 | continue; |
| 131 | } | 142 | |
| 143 | memcpy(name, p, sizeof(name)); | ||
| 144 | |||
| 145 | /* remove the first \0 character from string */ | ||
| 146 | a = strchr(name, '\0'); | ||
| 147 | if (a) | ||
| 148 | *a = ' '; | ||
| 149 | |||
| 150 | /* cut the string at a newline */ | ||
| 151 | a = strchr(name, '\r'); | ||
| 152 | if (a) | ||
| 153 | *a = '\0'; | ||
| 132 | 154 | ||
| 133 | tail = p + alix_sig_len; | 155 | tail = p + alix_sig_len; |
| 134 | if ((tail[0] == '2' || tail[0] == '3') && tail[1] == '\0') { | 156 | if ((tail[0] == '2' || tail[0] == '3')) { |
| 135 | printk(KERN_INFO | 157 | printk(KERN_INFO |
| 136 | "%s: system is recognized as \"%s\"\n", | 158 | "%s: system is recognized as \"%s\"\n", |
| 137 | KBUILD_MODNAME, p); | 159 | KBUILD_MODNAME, name); |
| 138 | ret = 1; | 160 | return 1; |
| 139 | break; | ||
| 140 | } | 161 | } |
| 141 | } | 162 | } |
| 142 | 163 | ||
| 143 | out: | 164 | return 0; |
| 144 | return ret; | ||
| 145 | } | 165 | } |
| 146 | 166 | ||
| 147 | static struct platform_device *pdev; | 167 | static struct platform_device *pdev; |
| 148 | 168 | ||
| 149 | static int __init alix_led_init(void) | 169 | static int __init alix_pci_led_init(void) |
| 150 | { | 170 | { |
| 151 | int ret; | 171 | u32 low, hi; |
| 152 | 172 | ||
| 153 | if (!alix_present()) { | 173 | if (pci_dev_present(divil_pci) == 0) { |
| 154 | ret = -ENODEV; | 174 | printk(KERN_WARNING KBUILD_MODNAME": DIVIL not found\n"); |
| 155 | goto out; | 175 | return -ENODEV; |
| 156 | } | 176 | } |
| 157 | 177 | ||
| 158 | /* enable output on GPIO for LED 1,2,3 */ | 178 | /* Grab the GPIO I/O range */ |
| 159 | outl(1 << 6, 0x6104); | 179 | rdmsr(MSR_LBAR_GPIO, low, hi); |
| 160 | outl(1 << 9, 0x6184); | 180 | |
| 161 | outl(1 << 11, 0x6184); | 181 | /* Check the mask and whether GPIO is enabled (sanity check) */ |
| 182 | if (hi != 0x0000f001) { | ||
| 183 | printk(KERN_WARNING KBUILD_MODNAME": GPIO not enabled\n"); | ||
| 184 | return -ENODEV; | ||
| 185 | } | ||
| 186 | |||
| 187 | /* Mask off the IO base address */ | ||
| 188 | gpio_base = low & 0x0000ff00; | ||
| 189 | |||
| 190 | if (!request_region(gpio_base, CS5535_GPIO_SIZE, KBUILD_MODNAME)) { | ||
| 191 | printk(KERN_ERR KBUILD_MODNAME": can't allocate I/O for GPIO\n"); | ||
| 192 | return -ENODEV; | ||
| 193 | } | ||
| 194 | |||
| 195 | /* Set GPIO function to output */ | ||
| 196 | outl(1 << 6, gpio_base + 0x04); | ||
| 197 | outl(1 << 9, gpio_base + 0x84); | ||
| 198 | outl(1 << 11, gpio_base + 0x84); | ||
| 199 | |||
| 200 | return 0; | ||
| 201 | } | ||
| 202 | |||
| 203 | static int __init alix_led_init(void) | ||
| 204 | { | ||
| 205 | int ret = -ENODEV; | ||
| 206 | const char tinybios_sig[] = "PC Engines ALIX."; | ||
| 207 | const char coreboot_sig[] = "PC Engines\0ALIX."; | ||
| 208 | |||
| 209 | if (alix_present(0xf0000, tinybios_sig, sizeof(tinybios_sig) - 1) || | ||
| 210 | alix_present(0x500, coreboot_sig, sizeof(coreboot_sig) - 1)) | ||
| 211 | ret = alix_pci_led_init(); | ||
| 212 | |||
| 213 | if (ret < 0) | ||
| 214 | return ret; | ||
| 162 | 215 | ||
| 163 | pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0); | 216 | pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0); |
| 164 | if (!IS_ERR(pdev)) { | 217 | if (!IS_ERR(pdev)) { |
| @@ -168,7 +221,6 @@ static int __init alix_led_init(void) | |||
| 168 | } else | 221 | } else |
| 169 | ret = PTR_ERR(pdev); | 222 | ret = PTR_ERR(pdev); |
| 170 | 223 | ||
| 171 | out: | ||
| 172 | return ret; | 224 | return ret; |
| 173 | } | 225 | } |
| 174 | 226 | ||
| @@ -176,6 +228,7 @@ static void __exit alix_led_exit(void) | |||
| 176 | { | 228 | { |
| 177 | platform_device_unregister(pdev); | 229 | platform_device_unregister(pdev); |
| 178 | platform_driver_unregister(&alix_led_driver); | 230 | platform_driver_unregister(&alix_led_driver); |
| 231 | release_region(gpio_base, CS5535_GPIO_SIZE); | ||
| 179 | } | 232 | } |
| 180 | 233 | ||
| 181 | module_init(alix_led_init); | 234 | module_init(alix_led_init); |
diff --git a/drivers/leds/leds-cobalt-qube.c b/drivers/leds/leds-cobalt-qube.c index 8816806accd2..da5fb016b1a5 100644 --- a/drivers/leds/leds-cobalt-qube.c +++ b/drivers/leds/leds-cobalt-qube.c | |||
| @@ -31,7 +31,7 @@ static struct led_classdev qube_front_led = { | |||
| 31 | .name = "qube::front", | 31 | .name = "qube::front", |
| 32 | .brightness = LED_FULL, | 32 | .brightness = LED_FULL, |
| 33 | .brightness_set = qube_front_led_set, | 33 | .brightness_set = qube_front_led_set, |
| 34 | .default_trigger = "ide-disk", | 34 | .default_trigger = "default-on", |
| 35 | }; | 35 | }; |
| 36 | 36 | ||
| 37 | static int __devinit cobalt_qube_led_probe(struct platform_device *pdev) | 37 | static int __devinit cobalt_qube_led_probe(struct platform_device *pdev) |
| @@ -43,7 +43,7 @@ static int __devinit cobalt_qube_led_probe(struct platform_device *pdev) | |||
| 43 | if (!res) | 43 | if (!res) |
| 44 | return -EBUSY; | 44 | return -EBUSY; |
| 45 | 45 | ||
| 46 | led_port = ioremap(res->start, res->end - res->start + 1); | 46 | led_port = ioremap(res->start, resource_size(res)); |
| 47 | if (!led_port) | 47 | if (!led_port) |
| 48 | return -ENOMEM; | 48 | return -ENOMEM; |
| 49 | 49 | ||
diff --git a/drivers/leds/leds-cobalt-raq.c b/drivers/leds/leds-cobalt-raq.c index defc212105f3..438d48384636 100644 --- a/drivers/leds/leds-cobalt-raq.c +++ b/drivers/leds/leds-cobalt-raq.c | |||
| @@ -84,7 +84,7 @@ static int __devinit cobalt_raq_led_probe(struct platform_device *pdev) | |||
| 84 | if (!res) | 84 | if (!res) |
| 85 | return -EBUSY; | 85 | return -EBUSY; |
| 86 | 86 | ||
| 87 | led_port = ioremap(res->start, res->end - res->start + 1); | 87 | led_port = ioremap(res->start, resource_size(res)); |
| 88 | if (!led_port) | 88 | if (!led_port) |
| 89 | return -ENOMEM; | 89 | return -ENOMEM; |
| 90 | 90 | ||
diff --git a/drivers/leds/leds-lt3593.c b/drivers/leds/leds-lt3593.c new file mode 100644 index 000000000000..fee40a841959 --- /dev/null +++ b/drivers/leds/leds-lt3593.c | |||
| @@ -0,0 +1,217 @@ | |||
| 1 | /* | ||
| 2 | * LEDs driver for LT3593 controllers | ||
| 3 | * | ||
| 4 | * See the datasheet at http://cds.linear.com/docs/Datasheet/3593f.pdf | ||
| 5 | * | ||
| 6 | * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de> | ||
| 7 | * | ||
| 8 | * Based on leds-gpio.c, | ||
| 9 | * | ||
| 10 | * Copyright (C) 2007 8D Technologies inc. | ||
| 11 | * Raphael Assenat <raph@8d.com> | ||
| 12 | * Copyright (C) 2008 Freescale Semiconductor, Inc. | ||
| 13 | * | ||
| 14 | * This program is free software; you can redistribute it and/or modify | ||
| 15 | * it under the terms of the GNU General Public License version 2 as | ||
| 16 | * published by the Free Software Foundation. | ||
| 17 | */ | ||
| 18 | |||
| 19 | #include <linux/kernel.h> | ||
| 20 | #include <linux/init.h> | ||
| 21 | #include <linux/platform_device.h> | ||
| 22 | #include <linux/leds.h> | ||
| 23 | #include <linux/workqueue.h> | ||
| 24 | #include <linux/delay.h> | ||
| 25 | #include <linux/gpio.h> | ||
| 26 | |||
| 27 | struct lt3593_led_data { | ||
| 28 | struct led_classdev cdev; | ||
| 29 | unsigned gpio; | ||
| 30 | struct work_struct work; | ||
| 31 | u8 new_level; | ||
| 32 | }; | ||
| 33 | |||
| 34 | static void lt3593_led_work(struct work_struct *work) | ||
| 35 | { | ||
| 36 | int pulses; | ||
| 37 | struct lt3593_led_data *led_dat = | ||
| 38 | container_of(work, struct lt3593_led_data, work); | ||
| 39 | |||
| 40 | /* | ||
| 41 | * The LT3593 resets its internal current level register to the maximum | ||
| 42 | * level on the first falling edge on the control pin. Each following | ||
| 43 | * falling edge decreases the current level by 625uA. Up to 32 pulses | ||
| 44 | * can be sent, so the maximum power reduction is 20mA. | ||
| 45 | * After a timeout of 128us, the value is taken from the register and | ||
| 46 | * applied is to the output driver. | ||
| 47 | */ | ||
| 48 | |||
| 49 | if (led_dat->new_level == 0) { | ||
| 50 | gpio_set_value_cansleep(led_dat->gpio, 0); | ||
| 51 | return; | ||
| 52 | } | ||
| 53 | |||
| 54 | pulses = 32 - (led_dat->new_level * 32) / 255; | ||
| 55 | |||
| 56 | if (pulses == 0) { | ||
| 57 | gpio_set_value_cansleep(led_dat->gpio, 0); | ||
| 58 | mdelay(1); | ||
| 59 | gpio_set_value_cansleep(led_dat->gpio, 1); | ||
| 60 | return; | ||
| 61 | } | ||
| 62 | |||
| 63 | gpio_set_value_cansleep(led_dat->gpio, 1); | ||
| 64 | |||
| 65 | while (pulses--) { | ||
| 66 | gpio_set_value_cansleep(led_dat->gpio, 0); | ||
| 67 | udelay(1); | ||
| 68 | gpio_set_value_cansleep(led_dat->gpio, 1); | ||
| 69 | udelay(1); | ||
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 73 | static void lt3593_led_set(struct led_classdev *led_cdev, | ||
| 74 | enum led_brightness value) | ||
| 75 | { | ||
| 76 | struct lt3593_led_data *led_dat = | ||
| 77 | container_of(led_cdev, struct lt3593_led_data, cdev); | ||
| 78 | |||
| 79 | led_dat->new_level = value; | ||
| 80 | schedule_work(&led_dat->work); | ||
| 81 | } | ||
| 82 | |||
| 83 | static int __devinit create_lt3593_led(const struct gpio_led *template, | ||
| 84 | struct lt3593_led_data *led_dat, struct device *parent) | ||
| 85 | { | ||
| 86 | int ret, state; | ||
| 87 | |||
| 88 | /* skip leds on GPIOs that aren't available */ | ||
| 89 | if (!gpio_is_valid(template->gpio)) { | ||
| 90 | printk(KERN_INFO "%s: skipping unavailable LT3593 LED at gpio %d (%s)\n", | ||
| 91 | KBUILD_MODNAME, template->gpio, template->name); | ||
| 92 | return 0; | ||
| 93 | } | ||
| 94 | |||
| 95 | ret = gpio_request(template->gpio, template->name); | ||
| 96 | if (ret < 0) | ||
| 97 | return ret; | ||
| 98 | |||
| 99 | led_dat->cdev.name = template->name; | ||
| 100 | led_dat->cdev.default_trigger = template->default_trigger; | ||
| 101 | led_dat->gpio = template->gpio; | ||
| 102 | |||
| 103 | led_dat->cdev.brightness_set = lt3593_led_set; | ||
| 104 | |||
| 105 | state = (template->default_state == LEDS_GPIO_DEFSTATE_ON); | ||
| 106 | led_dat->cdev.brightness = state ? LED_FULL : LED_OFF; | ||
| 107 | |||
| 108 | if (!template->retain_state_suspended) | ||
| 109 | led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; | ||
| 110 | |||
| 111 | ret = gpio_direction_output(led_dat->gpio, state); | ||
| 112 | if (ret < 0) | ||
| 113 | goto err; | ||
| 114 | |||
| 115 | INIT_WORK(&led_dat->work, lt3593_led_work); | ||
| 116 | |||
| 117 | ret = led_classdev_register(parent, &led_dat->cdev); | ||
| 118 | if (ret < 0) | ||
| 119 | goto err; | ||
| 120 | |||
| 121 | printk(KERN_INFO "%s: registered LT3593 LED '%s' at GPIO %d\n", | ||
| 122 | KBUILD_MODNAME, template->name, template->gpio); | ||
| 123 | |||
| 124 | return 0; | ||
| 125 | |||
| 126 | err: | ||
| 127 | gpio_free(led_dat->gpio); | ||
| 128 | return ret; | ||
| 129 | } | ||
| 130 | |||
| 131 | static void delete_lt3593_led(struct lt3593_led_data *led) | ||
| 132 | { | ||
| 133 | if (!gpio_is_valid(led->gpio)) | ||
| 134 | return; | ||
| 135 | |||
| 136 | led_classdev_unregister(&led->cdev); | ||
| 137 | cancel_work_sync(&led->work); | ||
| 138 | gpio_free(led->gpio); | ||
| 139 | } | ||
| 140 | |||
| 141 | static int __devinit lt3593_led_probe(struct platform_device *pdev) | ||
| 142 | { | ||
| 143 | struct gpio_led_platform_data *pdata = pdev->dev.platform_data; | ||
| 144 | struct lt3593_led_data *leds_data; | ||
| 145 | int i, ret = 0; | ||
| 146 | |||
| 147 | if (!pdata) | ||
| 148 | return -EBUSY; | ||
| 149 | |||
| 150 | leds_data = kzalloc(sizeof(struct lt3593_led_data) * pdata->num_leds, | ||
| 151 | GFP_KERNEL); | ||
| 152 | if (!leds_data) | ||
| 153 | return -ENOMEM; | ||
| 154 | |||
| 155 | for (i = 0; i < pdata->num_leds; i++) { | ||
| 156 | ret = create_lt3593_led(&pdata->leds[i], &leds_data[i], | ||
| 157 | &pdev->dev); | ||
| 158 | if (ret < 0) | ||
| 159 | goto err; | ||
| 160 | } | ||
| 161 | |||
| 162 | platform_set_drvdata(pdev, leds_data); | ||
| 163 | |||
| 164 | return 0; | ||
| 165 | |||
| 166 | err: | ||
| 167 | for (i = i - 1; i >= 0; i--) | ||
| 168 | delete_lt3593_led(&leds_data[i]); | ||
| 169 | |||
| 170 | kfree(leds_data); | ||
| 171 | |||
| 172 | return ret; | ||
| 173 | } | ||
| 174 | |||
| 175 | static int __devexit lt3593_led_remove(struct platform_device *pdev) | ||
| 176 | { | ||
| 177 | int i; | ||
| 178 | struct gpio_led_platform_data *pdata = pdev->dev.platform_data; | ||
| 179 | struct lt3593_led_data *leds_data; | ||
| 180 | |||
| 181 | leds_data = platform_get_drvdata(pdev); | ||
| 182 | |||
| 183 | for (i = 0; i < pdata->num_leds; i++) | ||
| 184 | delete_lt3593_led(&leds_data[i]); | ||
| 185 | |||
| 186 | kfree(leds_data); | ||
| 187 | |||
| 188 | return 0; | ||
| 189 | } | ||
| 190 | |||
| 191 | static struct platform_driver lt3593_led_driver = { | ||
| 192 | .probe = lt3593_led_probe, | ||
| 193 | .remove = __devexit_p(lt3593_led_remove), | ||
| 194 | .driver = { | ||
| 195 | .name = "leds-lt3593", | ||
| 196 | .owner = THIS_MODULE, | ||
| 197 | }, | ||
| 198 | }; | ||
| 199 | |||
| 200 | MODULE_ALIAS("platform:leds-lt3593"); | ||
| 201 | |||
| 202 | static int __init lt3593_led_init(void) | ||
| 203 | { | ||
| 204 | return platform_driver_register(<3593_led_driver); | ||
| 205 | } | ||
| 206 | |||
| 207 | static void __exit lt3593_led_exit(void) | ||
| 208 | { | ||
| 209 | platform_driver_unregister(<3593_led_driver); | ||
| 210 | } | ||
| 211 | |||
| 212 | module_init(lt3593_led_init); | ||
| 213 | module_exit(lt3593_led_exit); | ||
| 214 | |||
| 215 | MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>"); | ||
| 216 | MODULE_DESCRIPTION("LED driver for LT3593 controllers"); | ||
| 217 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c index cdfdc8714e10..88b1dd091cfb 100644 --- a/drivers/leds/leds-pwm.c +++ b/drivers/leds/leds-pwm.c | |||
| @@ -27,7 +27,6 @@ struct led_pwm_data { | |||
| 27 | struct pwm_device *pwm; | 27 | struct pwm_device *pwm; |
| 28 | unsigned int active_low; | 28 | unsigned int active_low; |
| 29 | unsigned int period; | 29 | unsigned int period; |
| 30 | unsigned int max_brightness; | ||
| 31 | }; | 30 | }; |
| 32 | 31 | ||
| 33 | static void led_pwm_set(struct led_classdev *led_cdev, | 32 | static void led_pwm_set(struct led_classdev *led_cdev, |
| @@ -35,7 +34,7 @@ static void led_pwm_set(struct led_classdev *led_cdev, | |||
| 35 | { | 34 | { |
| 36 | struct led_pwm_data *led_dat = | 35 | struct led_pwm_data *led_dat = |
| 37 | container_of(led_cdev, struct led_pwm_data, cdev); | 36 | container_of(led_cdev, struct led_pwm_data, cdev); |
| 38 | unsigned int max = led_dat->max_brightness; | 37 | unsigned int max = led_dat->cdev.max_brightness; |
| 39 | unsigned int period = led_dat->period; | 38 | unsigned int period = led_dat->period; |
| 40 | 39 | ||
| 41 | if (brightness == 0) { | 40 | if (brightness == 0) { |
| @@ -77,10 +76,10 @@ static int led_pwm_probe(struct platform_device *pdev) | |||
| 77 | led_dat->cdev.name = cur_led->name; | 76 | led_dat->cdev.name = cur_led->name; |
| 78 | led_dat->cdev.default_trigger = cur_led->default_trigger; | 77 | led_dat->cdev.default_trigger = cur_led->default_trigger; |
| 79 | led_dat->active_low = cur_led->active_low; | 78 | led_dat->active_low = cur_led->active_low; |
| 80 | led_dat->max_brightness = cur_led->max_brightness; | ||
| 81 | led_dat->period = cur_led->pwm_period_ns; | 79 | led_dat->period = cur_led->pwm_period_ns; |
| 82 | led_dat->cdev.brightness_set = led_pwm_set; | 80 | led_dat->cdev.brightness_set = led_pwm_set; |
| 83 | led_dat->cdev.brightness = LED_OFF; | 81 | led_dat->cdev.brightness = LED_OFF; |
| 82 | led_dat->cdev.max_brightness = cur_led->max_brightness; | ||
| 84 | led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; | 83 | led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; |
| 85 | 84 | ||
| 86 | ret = led_classdev_register(&pdev->dev, &led_dat->cdev); | 85 | ret = led_classdev_register(&pdev->dev, &led_dat->cdev); |
diff --git a/drivers/leds/leds-regulator.c b/drivers/leds/leds-regulator.c new file mode 100644 index 000000000000..7f00de3ef922 --- /dev/null +++ b/drivers/leds/leds-regulator.c | |||
| @@ -0,0 +1,242 @@ | |||
| 1 | /* | ||
| 2 | * leds-regulator.c - LED class driver for regulator driven LEDs. | ||
| 3 | * | ||
| 4 | * Copyright (C) 2009 Antonio Ospite <ospite@studenti.unina.it> | ||
| 5 | * | ||
| 6 | * Inspired by leds-wm8350 driver. | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License version 2 as | ||
| 10 | * published by the Free Software Foundation. | ||
| 11 | * | ||
| 12 | */ | ||
| 13 | |||
| 14 | #include <linux/module.h> | ||
| 15 | #include <linux/err.h> | ||
| 16 | #include <linux/workqueue.h> | ||
| 17 | #include <linux/leds.h> | ||
| 18 | #include <linux/leds-regulator.h> | ||
| 19 | #include <linux/platform_device.h> | ||
| 20 | #include <linux/regulator/consumer.h> | ||
| 21 | |||
| 22 | #define to_regulator_led(led_cdev) \ | ||
| 23 | container_of(led_cdev, struct regulator_led, cdev) | ||
| 24 | |||
| 25 | struct regulator_led { | ||
| 26 | struct led_classdev cdev; | ||
| 27 | enum led_brightness value; | ||
| 28 | int enabled; | ||
| 29 | struct mutex mutex; | ||
| 30 | struct work_struct work; | ||
| 31 | |||
| 32 | struct regulator *vcc; | ||
| 33 | }; | ||
| 34 | |||
| 35 | static inline int led_regulator_get_max_brightness(struct regulator *supply) | ||
| 36 | { | ||
| 37 | int ret; | ||
| 38 | int voltage = regulator_list_voltage(supply, 0); | ||
| 39 | |||
| 40 | if (voltage <= 0) | ||
| 41 | return 1; | ||
| 42 | |||
| 43 | /* even if regulator can't change voltages, | ||
| 44 | * we still assume it can change status | ||
| 45 | * and the LED can be turned on and off. | ||
| 46 | */ | ||
| 47 | ret = regulator_set_voltage(supply, voltage, voltage); | ||
| 48 | if (ret < 0) | ||
| 49 | return 1; | ||
| 50 | |||
| 51 | return regulator_count_voltages(supply); | ||
| 52 | } | ||
| 53 | |||
| 54 | static int led_regulator_get_voltage(struct regulator *supply, | ||
| 55 | enum led_brightness brightness) | ||
| 56 | { | ||
| 57 | if (brightness == 0) | ||
| 58 | return -EINVAL; | ||
| 59 | |||
| 60 | return regulator_list_voltage(supply, brightness - 1); | ||
| 61 | } | ||
| 62 | |||
| 63 | |||
| 64 | static void regulator_led_enable(struct regulator_led *led) | ||
| 65 | { | ||
| 66 | int ret; | ||
| 67 | |||
| 68 | if (led->enabled) | ||
| 69 | return; | ||
| 70 | |||
| 71 | ret = regulator_enable(led->vcc); | ||
| 72 | if (ret != 0) { | ||
| 73 | dev_err(led->cdev.dev, "Failed to enable vcc: %d\n", ret); | ||
| 74 | return; | ||
| 75 | } | ||
| 76 | |||
| 77 | led->enabled = 1; | ||
| 78 | } | ||
| 79 | |||
| 80 | static void regulator_led_disable(struct regulator_led *led) | ||
| 81 | { | ||
| 82 | int ret; | ||
| 83 | |||
| 84 | if (!led->enabled) | ||
| 85 | return; | ||
| 86 | |||
| 87 | ret = regulator_disable(led->vcc); | ||
| 88 | if (ret != 0) { | ||
| 89 | dev_err(led->cdev.dev, "Failed to disable vcc: %d\n", ret); | ||
| 90 | return; | ||
| 91 | } | ||
| 92 | |||
| 93 | led->enabled = 0; | ||
| 94 | } | ||
| 95 | |||
| 96 | static void regulator_led_set_value(struct regulator_led *led) | ||
| 97 | { | ||
| 98 | int voltage; | ||
| 99 | int ret; | ||
| 100 | |||
| 101 | mutex_lock(&led->mutex); | ||
| 102 | |||
| 103 | if (led->value == LED_OFF) { | ||
| 104 | regulator_led_disable(led); | ||
| 105 | goto out; | ||
| 106 | } | ||
| 107 | |||
| 108 | if (led->cdev.max_brightness > 1) { | ||
| 109 | voltage = led_regulator_get_voltage(led->vcc, led->value); | ||
| 110 | dev_dbg(led->cdev.dev, "brightness: %d voltage: %d\n", | ||
| 111 | led->value, voltage); | ||
| 112 | |||
| 113 | ret = regulator_set_voltage(led->vcc, voltage, voltage); | ||
| 114 | if (ret != 0) | ||
| 115 | dev_err(led->cdev.dev, "Failed to set voltage %d: %d\n", | ||
| 116 | voltage, ret); | ||
| 117 | } | ||
| 118 | |||
| 119 | regulator_led_enable(led); | ||
| 120 | |||
| 121 | out: | ||
| 122 | mutex_unlock(&led->mutex); | ||
| 123 | } | ||
| 124 | |||
| 125 | static void led_work(struct work_struct *work) | ||
| 126 | { | ||
| 127 | struct regulator_led *led; | ||
| 128 | |||
| 129 | led = container_of(work, struct regulator_led, work); | ||
| 130 | regulator_led_set_value(led); | ||
| 131 | } | ||
| 132 | |||
| 133 | static void regulator_led_brightness_set(struct led_classdev *led_cdev, | ||
| 134 | enum led_brightness value) | ||
| 135 | { | ||
| 136 | struct regulator_led *led = to_regulator_led(led_cdev); | ||
| 137 | |||
| 138 | led->value = value; | ||
| 139 | schedule_work(&led->work); | ||
| 140 | } | ||
| 141 | |||
| 142 | static int __devinit regulator_led_probe(struct platform_device *pdev) | ||
| 143 | { | ||
| 144 | struct led_regulator_platform_data *pdata = pdev->dev.platform_data; | ||
| 145 | struct regulator_led *led; | ||
| 146 | struct regulator *vcc; | ||
| 147 | int ret = 0; | ||
| 148 | |||
| 149 | if (pdata == NULL) { | ||
| 150 | dev_err(&pdev->dev, "no platform data\n"); | ||
| 151 | return -ENODEV; | ||
| 152 | } | ||
| 153 | |||
| 154 | vcc = regulator_get_exclusive(&pdev->dev, "vled"); | ||
| 155 | if (IS_ERR(vcc)) { | ||
| 156 | dev_err(&pdev->dev, "Cannot get vcc for %s\n", pdata->name); | ||
| 157 | return PTR_ERR(vcc); | ||
| 158 | } | ||
| 159 | |||
| 160 | led = kzalloc(sizeof(*led), GFP_KERNEL); | ||
| 161 | if (led == NULL) { | ||
| 162 | ret = -ENOMEM; | ||
| 163 | goto err_vcc; | ||
| 164 | } | ||
| 165 | |||
| 166 | led->cdev.max_brightness = led_regulator_get_max_brightness(vcc); | ||
| 167 | if (pdata->brightness > led->cdev.max_brightness) { | ||
| 168 | dev_err(&pdev->dev, "Invalid default brightness %d\n", | ||
| 169 | pdata->brightness); | ||
| 170 | ret = -EINVAL; | ||
| 171 | goto err_led; | ||
| 172 | } | ||
| 173 | led->value = pdata->brightness; | ||
| 174 | |||
| 175 | led->cdev.brightness_set = regulator_led_brightness_set; | ||
| 176 | led->cdev.name = pdata->name; | ||
| 177 | led->cdev.flags |= LED_CORE_SUSPENDRESUME; | ||
| 178 | led->vcc = vcc; | ||
| 179 | |||
| 180 | mutex_init(&led->mutex); | ||
| 181 | INIT_WORK(&led->work, led_work); | ||
| 182 | |||
| 183 | platform_set_drvdata(pdev, led); | ||
| 184 | |||
| 185 | ret = led_classdev_register(&pdev->dev, &led->cdev); | ||
| 186 | if (ret < 0) { | ||
| 187 | cancel_work_sync(&led->work); | ||
| 188 | goto err_led; | ||
| 189 | } | ||
| 190 | |||
| 191 | /* to expose the default value to userspace */ | ||
| 192 | led->cdev.brightness = led->value; | ||
| 193 | |||
| 194 | /* Set the default led status */ | ||
| 195 | regulator_led_set_value(led); | ||
| 196 | |||
| 197 | return 0; | ||
| 198 | |||
| 199 | err_led: | ||
| 200 | kfree(led); | ||
| 201 | err_vcc: | ||
| 202 | regulator_put(vcc); | ||
| 203 | return ret; | ||
| 204 | } | ||
| 205 | |||
| 206 | static int __devexit regulator_led_remove(struct platform_device *pdev) | ||
| 207 | { | ||
| 208 | struct regulator_led *led = platform_get_drvdata(pdev); | ||
| 209 | |||
| 210 | led_classdev_unregister(&led->cdev); | ||
| 211 | cancel_work_sync(&led->work); | ||
| 212 | regulator_led_disable(led); | ||
| 213 | regulator_put(led->vcc); | ||
| 214 | kfree(led); | ||
| 215 | return 0; | ||
| 216 | } | ||
| 217 | |||
| 218 | static struct platform_driver regulator_led_driver = { | ||
| 219 | .driver = { | ||
| 220 | .name = "leds-regulator", | ||
| 221 | .owner = THIS_MODULE, | ||
| 222 | }, | ||
| 223 | .probe = regulator_led_probe, | ||
| 224 | .remove = __devexit_p(regulator_led_remove), | ||
| 225 | }; | ||
| 226 | |||
| 227 | static int __init regulator_led_init(void) | ||
| 228 | { | ||
| 229 | return platform_driver_register(®ulator_led_driver); | ||
| 230 | } | ||
| 231 | module_init(regulator_led_init); | ||
| 232 | |||
| 233 | static void __exit regulator_led_exit(void) | ||
| 234 | { | ||
| 235 | platform_driver_unregister(®ulator_led_driver); | ||
| 236 | } | ||
| 237 | module_exit(regulator_led_exit); | ||
| 238 | |||
| 239 | MODULE_AUTHOR("Antonio Ospite <ospite@studenti.unina.it>"); | ||
| 240 | MODULE_DESCRIPTION("Regulator driven LED driver"); | ||
| 241 | MODULE_LICENSE("GPL"); | ||
| 242 | MODULE_ALIAS("platform:leds-regulator"); | ||
diff --git a/drivers/leds/leds-ss4200.c b/drivers/leds/leds-ss4200.c new file mode 100644 index 000000000000..97f04984c1ca --- /dev/null +++ b/drivers/leds/leds-ss4200.c | |||
| @@ -0,0 +1,556 @@ | |||
| 1 | /* | ||
| 2 | * SS4200-E Hardware API | ||
| 3 | * Copyright (c) 2009, Intel Corporation. | ||
| 4 | * Copyright IBM Corporation, 2009 | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms and conditions of the GNU General Public License, | ||
| 8 | * version 2, as published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 13 | * more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU General Public License along with | ||
| 16 | * this program; if not, write to the Free Software Foundation, Inc., | ||
| 17 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 18 | * | ||
| 19 | * Author: Dave Hansen <dave@sr71.net> | ||
| 20 | */ | ||
| 21 | |||
| 22 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
| 23 | |||
| 24 | #include <linux/dmi.h> | ||
| 25 | #include <linux/init.h> | ||
| 26 | #include <linux/ioport.h> | ||
| 27 | #include <linux/kernel.h> | ||
| 28 | #include <linux/leds.h> | ||
| 29 | #include <linux/module.h> | ||
| 30 | #include <linux/pci.h> | ||
| 31 | #include <linux/types.h> | ||
| 32 | #include <linux/uaccess.h> | ||
| 33 | |||
| 34 | MODULE_AUTHOR("Rodney Girod <rgirod@confocus.com>, Dave Hansen <dave@sr71.net>"); | ||
| 35 | MODULE_DESCRIPTION("Intel NAS/Home Server ICH7 GPIO Driver"); | ||
| 36 | MODULE_LICENSE("GPL"); | ||
| 37 | |||
| 38 | /* | ||
| 39 | * ICH7 LPC/GPIO PCI Config register offsets | ||
| 40 | */ | ||
| 41 | #define PMBASE 0x040 | ||
| 42 | #define GPIO_BASE 0x048 | ||
| 43 | #define GPIO_CTRL 0x04c | ||
| 44 | #define GPIO_EN 0x010 | ||
| 45 | |||
| 46 | /* | ||
| 47 | * The ICH7 GPIO register block is 64 bytes in size. | ||
| 48 | */ | ||
| 49 | #define ICH7_GPIO_SIZE 64 | ||
| 50 | |||
| 51 | /* | ||
| 52 | * Define register offsets within the ICH7 register block. | ||
| 53 | */ | ||
| 54 | #define GPIO_USE_SEL 0x000 | ||
| 55 | #define GP_IO_SEL 0x004 | ||
| 56 | #define GP_LVL 0x00c | ||
| 57 | #define GPO_BLINK 0x018 | ||
| 58 | #define GPI_INV 0x030 | ||
| 59 | #define GPIO_USE_SEL2 0x034 | ||
| 60 | #define GP_IO_SEL2 0x038 | ||
| 61 | #define GP_LVL2 0x03c | ||
| 62 | |||
| 63 | /* | ||
| 64 | * PCI ID of the Intel ICH7 LPC Device within which the GPIO block lives. | ||
| 65 | */ | ||
| 66 | static struct pci_device_id ich7_lpc_pci_id[] = | ||
| 67 | { | ||
| 68 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_0) }, | ||
| 69 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_1) }, | ||
| 70 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_30) }, | ||
| 71 | { } /* NULL entry */ | ||
| 72 | }; | ||
| 73 | |||
| 74 | MODULE_DEVICE_TABLE(pci, ich7_lpc_pci_id); | ||
| 75 | |||
| 76 | static int __init ss4200_led_dmi_callback(const struct dmi_system_id *id) | ||
| 77 | { | ||
| 78 | pr_info("detected '%s'\n", id->ident); | ||
| 79 | return 1; | ||
| 80 | } | ||
| 81 | |||
| 82 | static unsigned int __initdata nodetect; | ||
| 83 | module_param_named(nodetect, nodetect, bool, 0); | ||
| 84 | MODULE_PARM_DESC(nodetect, "Skip DMI-based hardware detection"); | ||
| 85 | |||
| 86 | /* | ||
| 87 | * struct nas_led_whitelist - List of known good models | ||
| 88 | * | ||
| 89 | * Contains the known good models this driver is compatible with. | ||
| 90 | * When adding a new model try to be as strict as possible. This | ||
| 91 | * makes it possible to keep the false positives (the model is | ||
| 92 | * detected as working, but in reality it is not) as low as | ||
| 93 | * possible. | ||
| 94 | */ | ||
| 95 | static struct dmi_system_id __initdata nas_led_whitelist[] = { | ||
| 96 | { | ||
| 97 | .callback = ss4200_led_dmi_callback, | ||
| 98 | .ident = "Intel SS4200-E", | ||
| 99 | .matches = { | ||
| 100 | DMI_MATCH(DMI_SYS_VENDOR, "Intel"), | ||
| 101 | DMI_MATCH(DMI_PRODUCT_NAME, "SS4200-E"), | ||
| 102 | DMI_MATCH(DMI_PRODUCT_VERSION, "1.00.00") | ||
| 103 | } | ||
| 104 | }, | ||
| 105 | }; | ||
| 106 | |||
| 107 | /* | ||
| 108 | * Base I/O address assigned to the Power Management register block | ||
| 109 | */ | ||
| 110 | static u32 g_pm_io_base; | ||
| 111 | |||
| 112 | /* | ||
| 113 | * Base I/O address assigned to the ICH7 GPIO register block | ||
| 114 | */ | ||
| 115 | static u32 nas_gpio_io_base; | ||
| 116 | |||
| 117 | /* | ||
| 118 | * When we successfully register a region, we are returned a resource. | ||
| 119 | * We use these to identify which regions we need to release on our way | ||
| 120 | * back out. | ||
| 121 | */ | ||
| 122 | static struct resource *gp_gpio_resource; | ||
| 123 | |||
| 124 | struct nasgpio_led { | ||
| 125 | char *name; | ||
| 126 | u32 gpio_bit; | ||
| 127 | struct led_classdev led_cdev; | ||
| 128 | }; | ||
| 129 | |||
| 130 | /* | ||
| 131 | * gpio_bit(s) are the ICH7 GPIO bit assignments | ||
| 132 | */ | ||
| 133 | static struct nasgpio_led nasgpio_leds[] = { | ||
| 134 | { .name = "hdd1:blue:sata", .gpio_bit = 0 }, | ||
| 135 | { .name = "hdd1:amber:sata", .gpio_bit = 1 }, | ||
| 136 | { .name = "hdd2:blue:sata", .gpio_bit = 2 }, | ||
| 137 | { .name = "hdd2:amber:sata", .gpio_bit = 3 }, | ||
| 138 | { .name = "hdd3:blue:sata", .gpio_bit = 4 }, | ||
| 139 | { .name = "hdd3:amber:sata", .gpio_bit = 5 }, | ||
| 140 | { .name = "hdd4:blue:sata", .gpio_bit = 6 }, | ||
| 141 | { .name = "hdd4:amber:sata", .gpio_bit = 7 }, | ||
| 142 | { .name = "power:blue:power", .gpio_bit = 27}, | ||
| 143 | { .name = "power:amber:power", .gpio_bit = 28}, | ||
| 144 | }; | ||
| 145 | |||
| 146 | #define NAS_RECOVERY 0x00000400 /* GPIO10 */ | ||
| 147 | |||
| 148 | static struct nasgpio_led * | ||
| 149 | led_classdev_to_nasgpio_led(struct led_classdev *led_cdev) | ||
| 150 | { | ||
| 151 | return container_of(led_cdev, struct nasgpio_led, led_cdev); | ||
| 152 | } | ||
| 153 | |||
| 154 | static struct nasgpio_led *get_led_named(char *name) | ||
| 155 | { | ||
| 156 | int i; | ||
| 157 | for (i = 0; i < ARRAY_SIZE(nasgpio_leds); i++) { | ||
| 158 | if (strcmp(nasgpio_leds[i].name, name)) | ||
| 159 | continue; | ||
| 160 | return &nasgpio_leds[i]; | ||
| 161 | } | ||
| 162 | return NULL; | ||
| 163 | } | ||
| 164 | |||
| 165 | /* | ||
| 166 | * This protects access to the gpio ports. | ||
| 167 | */ | ||
| 168 | static DEFINE_SPINLOCK(nasgpio_gpio_lock); | ||
| 169 | |||
| 170 | /* | ||
| 171 | * There are two gpio ports, one for blinking and the other | ||
| 172 | * for power. @port tells us if we're doing blinking or | ||
| 173 | * power control. | ||
| 174 | * | ||
| 175 | * Caller must hold nasgpio_gpio_lock | ||
| 176 | */ | ||
| 177 | static void __nasgpio_led_set_attr(struct led_classdev *led_cdev, | ||
| 178 | u32 port, u32 value) | ||
| 179 | { | ||
| 180 | struct nasgpio_led *led = led_classdev_to_nasgpio_led(led_cdev); | ||
| 181 | u32 gpio_out; | ||
| 182 | |||
| 183 | gpio_out = inl(nas_gpio_io_base + port); | ||
| 184 | if (value) | ||
| 185 | gpio_out |= (1<<led->gpio_bit); | ||
| 186 | else | ||
| 187 | gpio_out &= ~(1<<led->gpio_bit); | ||
| 188 | |||
| 189 | outl(gpio_out, nas_gpio_io_base + port); | ||
| 190 | } | ||
| 191 | |||
| 192 | static void nasgpio_led_set_attr(struct led_classdev *led_cdev, | ||
| 193 | u32 port, u32 value) | ||
| 194 | { | ||
| 195 | spin_lock(&nasgpio_gpio_lock); | ||
| 196 | __nasgpio_led_set_attr(led_cdev, port, value); | ||
| 197 | spin_unlock(&nasgpio_gpio_lock); | ||
| 198 | } | ||
| 199 | |||
| 200 | u32 nasgpio_led_get_attr(struct led_classdev *led_cdev, u32 port) | ||
| 201 | { | ||
| 202 | struct nasgpio_led *led = led_classdev_to_nasgpio_led(led_cdev); | ||
| 203 | u32 gpio_in; | ||
| 204 | |||
| 205 | spin_lock(&nasgpio_gpio_lock); | ||
| 206 | gpio_in = inl(nas_gpio_io_base + port); | ||
| 207 | spin_unlock(&nasgpio_gpio_lock); | ||
| 208 | if (gpio_in & (1<<led->gpio_bit)) | ||
| 209 | return 1; | ||
| 210 | return 0; | ||
| 211 | } | ||
| 212 | |||
| 213 | /* | ||
| 214 | * There is actual brightness control in the hardware, | ||
| 215 | * but it is via smbus commands and not implemented | ||
| 216 | * in this driver. | ||
| 217 | */ | ||
| 218 | static void nasgpio_led_set_brightness(struct led_classdev *led_cdev, | ||
| 219 | enum led_brightness brightness) | ||
| 220 | { | ||
| 221 | u32 setting = 0; | ||
| 222 | if (brightness >= LED_HALF) | ||
| 223 | setting = 1; | ||
| 224 | /* | ||
| 225 | * Hold the lock across both operations. This ensures | ||
| 226 | * consistency so that both the "turn off blinking" | ||
| 227 | * and "turn light off" operations complete as a set. | ||
| 228 | */ | ||
| 229 | spin_lock(&nasgpio_gpio_lock); | ||
| 230 | /* | ||
| 231 | * LED class documentation asks that past blink state | ||
| 232 | * be disabled when brightness is turned to zero. | ||
| 233 | */ | ||
| 234 | if (brightness == 0) | ||
| 235 | __nasgpio_led_set_attr(led_cdev, GPO_BLINK, 0); | ||
| 236 | __nasgpio_led_set_attr(led_cdev, GP_LVL, setting); | ||
| 237 | spin_unlock(&nasgpio_gpio_lock); | ||
| 238 | } | ||
| 239 | |||
| 240 | static int nasgpio_led_set_blink(struct led_classdev *led_cdev, | ||
| 241 | unsigned long *delay_on, | ||
| 242 | unsigned long *delay_off) | ||
| 243 | { | ||
| 244 | u32 setting = 1; | ||
| 245 | if (!(*delay_on == 0 && *delay_off == 0) && | ||
| 246 | !(*delay_on == 500 && *delay_off == 500)) | ||
| 247 | return -EINVAL; | ||
| 248 | /* | ||
| 249 | * These are very approximate. | ||
| 250 | */ | ||
| 251 | *delay_on = 500; | ||
| 252 | *delay_off = 500; | ||
| 253 | |||
| 254 | nasgpio_led_set_attr(led_cdev, GPO_BLINK, setting); | ||
| 255 | |||
| 256 | return 0; | ||
| 257 | } | ||
| 258 | |||
| 259 | |||
| 260 | /* | ||
| 261 | * Initialize the ICH7 GPIO registers for NAS usage. The BIOS should have | ||
| 262 | * already taken care of this, but we will do so in a non destructive manner | ||
| 263 | * so that we have what we need whether the BIOS did it or not. | ||
| 264 | */ | ||
| 265 | static int __devinit ich7_gpio_init(struct device *dev) | ||
| 266 | { | ||
| 267 | int i; | ||
| 268 | u32 config_data = 0; | ||
| 269 | u32 all_nas_led = 0; | ||
| 270 | |||
| 271 | for (i = 0; i < ARRAY_SIZE(nasgpio_leds); i++) | ||
| 272 | all_nas_led |= (1<<nasgpio_leds[i].gpio_bit); | ||
| 273 | |||
| 274 | spin_lock(&nasgpio_gpio_lock); | ||
| 275 | /* | ||
| 276 | * We need to enable all of the GPIO lines used by the NAS box, | ||
| 277 | * so we will read the current Use Selection and add our usage | ||
| 278 | * to it. This should be benign with regard to the original | ||
| 279 | * BIOS configuration. | ||
| 280 | */ | ||
| 281 | config_data = inl(nas_gpio_io_base + GPIO_USE_SEL); | ||
| 282 | dev_dbg(dev, ": Data read from GPIO_USE_SEL = 0x%08x\n", config_data); | ||
| 283 | config_data |= all_nas_led + NAS_RECOVERY; | ||
| 284 | outl(config_data, nas_gpio_io_base + GPIO_USE_SEL); | ||
| 285 | config_data = inl(nas_gpio_io_base + GPIO_USE_SEL); | ||
| 286 | dev_dbg(dev, ": GPIO_USE_SEL = 0x%08x\n\n", config_data); | ||
| 287 | |||
| 288 | /* | ||
| 289 | * The LED GPIO outputs need to be configured for output, so we | ||
| 290 | * will ensure that all LED lines are cleared for output and the | ||
| 291 | * RECOVERY line ready for input. This too should be benign with | ||
| 292 | * regard to BIOS configuration. | ||
| 293 | */ | ||
| 294 | config_data = inl(nas_gpio_io_base + GP_IO_SEL); | ||
| 295 | dev_dbg(dev, ": Data read from GP_IO_SEL = 0x%08x\n", | ||
| 296 | config_data); | ||
| 297 | config_data &= ~all_nas_led; | ||
| 298 | config_data |= NAS_RECOVERY; | ||
| 299 | outl(config_data, nas_gpio_io_base + GP_IO_SEL); | ||
| 300 | config_data = inl(nas_gpio_io_base + GP_IO_SEL); | ||
| 301 | dev_dbg(dev, ": GP_IO_SEL = 0x%08x\n", config_data); | ||
| 302 | |||
| 303 | /* | ||
| 304 | * In our final system, the BIOS will initialize the state of all | ||
| 305 | * of the LEDs. For now, we turn them all off (or Low). | ||
| 306 | */ | ||
| 307 | config_data = inl(nas_gpio_io_base + GP_LVL); | ||
| 308 | dev_dbg(dev, ": Data read from GP_LVL = 0x%08x\n", config_data); | ||
| 309 | /* | ||
| 310 | * In our final system, the BIOS will initialize the blink state of all | ||
| 311 | * of the LEDs. For now, we turn blink off for all of them. | ||
| 312 | */ | ||
| 313 | config_data = inl(nas_gpio_io_base + GPO_BLINK); | ||
| 314 | dev_dbg(dev, ": Data read from GPO_BLINK = 0x%08x\n", config_data); | ||
| 315 | |||
| 316 | /* | ||
| 317 | * At this moment, I am unsure if anything needs to happen with GPI_INV | ||
| 318 | */ | ||
| 319 | config_data = inl(nas_gpio_io_base + GPI_INV); | ||
| 320 | dev_dbg(dev, ": Data read from GPI_INV = 0x%08x\n", config_data); | ||
| 321 | |||
| 322 | spin_unlock(&nasgpio_gpio_lock); | ||
| 323 | return 0; | ||
| 324 | } | ||
| 325 | |||
| 326 | static void ich7_lpc_cleanup(struct device *dev) | ||
| 327 | { | ||
| 328 | /* | ||
| 329 | * If we were given exclusive use of the GPIO | ||
| 330 | * I/O Address range, we must return it. | ||
| 331 | */ | ||
| 332 | if (gp_gpio_resource) { | ||
| 333 | dev_dbg(dev, ": Releasing GPIO I/O addresses\n"); | ||
| 334 | release_region(nas_gpio_io_base, ICH7_GPIO_SIZE); | ||
| 335 | gp_gpio_resource = NULL; | ||
| 336 | } | ||
| 337 | } | ||
| 338 | |||
| 339 | /* | ||
| 340 | * The OS has determined that the LPC of the Intel ICH7 Southbridge is present | ||
| 341 | * so we can retrive the required operational information and prepare the GPIO. | ||
| 342 | */ | ||
| 343 | static struct pci_dev *nas_gpio_pci_dev; | ||
| 344 | static int __devinit ich7_lpc_probe(struct pci_dev *dev, | ||
| 345 | const struct pci_device_id *id) | ||
| 346 | { | ||
| 347 | int status; | ||
| 348 | u32 gc = 0; | ||
| 349 | |||
| 350 | status = pci_enable_device(dev); | ||
| 351 | if (status) { | ||
| 352 | dev_err(&dev->dev, "pci_enable_device failed\n"); | ||
| 353 | return -EIO; | ||
| 354 | } | ||
| 355 | |||
| 356 | nas_gpio_pci_dev = dev; | ||
| 357 | status = pci_read_config_dword(dev, PMBASE, &g_pm_io_base); | ||
| 358 | if (status) | ||
| 359 | goto out; | ||
| 360 | g_pm_io_base &= 0x00000ff80; | ||
| 361 | |||
| 362 | status = pci_read_config_dword(dev, GPIO_CTRL, &gc); | ||
| 363 | if (!(GPIO_EN & gc)) { | ||
| 364 | status = -EEXIST; | ||
| 365 | dev_info(&dev->dev, | ||
| 366 | "ERROR: The LPC GPIO Block has not been enabled.\n"); | ||
| 367 | goto out; | ||
| 368 | } | ||
| 369 | |||
| 370 | status = pci_read_config_dword(dev, GPIO_BASE, &nas_gpio_io_base); | ||
| 371 | if (0 > status) { | ||
| 372 | dev_info(&dev->dev, "Unable to read GPIOBASE.\n"); | ||
| 373 | goto out; | ||
| 374 | } | ||
| 375 | dev_dbg(&dev->dev, ": GPIOBASE = 0x%08x\n", nas_gpio_io_base); | ||
| 376 | nas_gpio_io_base &= 0x00000ffc0; | ||
| 377 | |||
| 378 | /* | ||
| 379 | * Insure that we have exclusive access to the GPIO I/O address range. | ||
| 380 | */ | ||
| 381 | gp_gpio_resource = request_region(nas_gpio_io_base, ICH7_GPIO_SIZE, | ||
| 382 | KBUILD_MODNAME); | ||
| 383 | if (NULL == gp_gpio_resource) { | ||
| 384 | dev_info(&dev->dev, | ||
| 385 | "ERROR Unable to register GPIO I/O addresses.\n"); | ||
| 386 | status = -1; | ||
| 387 | goto out; | ||
| 388 | } | ||
| 389 | |||
| 390 | /* | ||
| 391 | * Initialize the GPIO for NAS/Home Server Use | ||
| 392 | */ | ||
| 393 | ich7_gpio_init(&dev->dev); | ||
| 394 | |||
| 395 | out: | ||
| 396 | if (status) { | ||
| 397 | ich7_lpc_cleanup(&dev->dev); | ||
| 398 | pci_disable_device(dev); | ||
| 399 | } | ||
| 400 | return status; | ||
| 401 | } | ||
| 402 | |||
| 403 | static void ich7_lpc_remove(struct pci_dev *dev) | ||
| 404 | { | ||
| 405 | ich7_lpc_cleanup(&dev->dev); | ||
| 406 | pci_disable_device(dev); | ||
| 407 | } | ||
| 408 | |||
| 409 | /* | ||
| 410 | * pci_driver structure passed to the PCI modules | ||
| 411 | */ | ||
| 412 | static struct pci_driver nas_gpio_pci_driver = { | ||
| 413 | .name = KBUILD_MODNAME, | ||
| 414 | .id_table = ich7_lpc_pci_id, | ||
| 415 | .probe = ich7_lpc_probe, | ||
| 416 | .remove = ich7_lpc_remove, | ||
| 417 | }; | ||
| 418 | |||
| 419 | static struct led_classdev *get_classdev_for_led_nr(int nr) | ||
| 420 | { | ||
| 421 | struct nasgpio_led *nas_led = &nasgpio_leds[nr]; | ||
| 422 | struct led_classdev *led = &nas_led->led_cdev; | ||
| 423 | return led; | ||
| 424 | } | ||
| 425 | |||
| 426 | |||
| 427 | static void set_power_light_amber_noblink(void) | ||
| 428 | { | ||
| 429 | struct nasgpio_led *amber = get_led_named("power:amber:power"); | ||
| 430 | struct nasgpio_led *blue = get_led_named("power:blue:power"); | ||
| 431 | |||
| 432 | if (!amber || !blue) | ||
| 433 | return; | ||
| 434 | /* | ||
| 435 | * LED_OFF implies disabling future blinking | ||
| 436 | */ | ||
| 437 | pr_debug("setting blue off and amber on\n"); | ||
| 438 | |||
| 439 | nasgpio_led_set_brightness(&blue->led_cdev, LED_OFF); | ||
| 440 | nasgpio_led_set_brightness(&amber->led_cdev, LED_FULL); | ||
| 441 | } | ||
| 442 | |||
| 443 | static ssize_t nas_led_blink_show(struct device *dev, | ||
| 444 | struct device_attribute *attr, char *buf) | ||
| 445 | { | ||
| 446 | struct led_classdev *led = dev_get_drvdata(dev); | ||
| 447 | int blinking = 0; | ||
| 448 | if (nasgpio_led_get_attr(led, GPO_BLINK)) | ||
| 449 | blinking = 1; | ||
| 450 | return sprintf(buf, "%u\n", blinking); | ||
| 451 | } | ||
| 452 | |||
| 453 | static ssize_t nas_led_blink_store(struct device *dev, | ||
| 454 | struct device_attribute *attr, | ||
| 455 | const char *buf, size_t size) | ||
| 456 | { | ||
| 457 | int ret; | ||
| 458 | struct led_classdev *led = dev_get_drvdata(dev); | ||
| 459 | unsigned long blink_state; | ||
| 460 | |||
| 461 | ret = strict_strtoul(buf, 10, &blink_state); | ||
| 462 | if (ret) | ||
| 463 | return ret; | ||
| 464 | |||
| 465 | nasgpio_led_set_attr(led, GPO_BLINK, blink_state); | ||
| 466 | |||
| 467 | return size; | ||
| 468 | } | ||
| 469 | |||
| 470 | static DEVICE_ATTR(blink, 0644, nas_led_blink_show, nas_led_blink_store); | ||
| 471 | |||
| 472 | static int register_nasgpio_led(int led_nr) | ||
| 473 | { | ||
| 474 | int ret; | ||
| 475 | struct nasgpio_led *nas_led = &nasgpio_leds[led_nr]; | ||
| 476 | struct led_classdev *led = get_classdev_for_led_nr(led_nr); | ||
| 477 | |||
| 478 | led->name = nas_led->name; | ||
| 479 | led->brightness = LED_OFF; | ||
| 480 | if (nasgpio_led_get_attr(led, GP_LVL)) | ||
| 481 | led->brightness = LED_FULL; | ||
| 482 | led->brightness_set = nasgpio_led_set_brightness; | ||
| 483 | led->blink_set = nasgpio_led_set_blink; | ||
| 484 | ret = led_classdev_register(&nas_gpio_pci_dev->dev, led); | ||
| 485 | if (ret) | ||
| 486 | return ret; | ||
| 487 | ret = device_create_file(led->dev, &dev_attr_blink); | ||
| 488 | if (ret) | ||
| 489 | led_classdev_unregister(led); | ||
| 490 | return ret; | ||
| 491 | } | ||
| 492 | |||
| 493 | static void unregister_nasgpio_led(int led_nr) | ||
| 494 | { | ||
| 495 | struct led_classdev *led = get_classdev_for_led_nr(led_nr); | ||
| 496 | led_classdev_unregister(led); | ||
| 497 | device_remove_file(led->dev, &dev_attr_blink); | ||
| 498 | } | ||
| 499 | /* | ||
| 500 | * module load/initialization | ||
| 501 | */ | ||
| 502 | static int __init nas_gpio_init(void) | ||
| 503 | { | ||
| 504 | int i; | ||
| 505 | int ret = 0; | ||
| 506 | int nr_devices = 0; | ||
| 507 | |||
| 508 | nr_devices = dmi_check_system(nas_led_whitelist); | ||
| 509 | if (nodetect) { | ||
| 510 | pr_info("skipping hardware autodetection\n"); | ||
| 511 | pr_info("Please send 'dmidecode' output to dave@sr71.net\n"); | ||
| 512 | nr_devices++; | ||
| 513 | } | ||
| 514 | |||
| 515 | if (nr_devices <= 0) { | ||
| 516 | pr_info("no LED devices found\n"); | ||
| 517 | return -ENODEV; | ||
| 518 | } | ||
| 519 | |||
| 520 | pr_info("registering PCI driver\n"); | ||
| 521 | ret = pci_register_driver(&nas_gpio_pci_driver); | ||
| 522 | if (ret) | ||
| 523 | return ret; | ||
| 524 | for (i = 0; i < ARRAY_SIZE(nasgpio_leds); i++) { | ||
| 525 | ret = register_nasgpio_led(i); | ||
| 526 | if (ret) | ||
| 527 | goto out_err; | ||
| 528 | } | ||
| 529 | /* | ||
| 530 | * When the system powers on, the BIOS leaves the power | ||
| 531 | * light blue and blinking. This will turn it solid | ||
| 532 | * amber once the driver is loaded. | ||
| 533 | */ | ||
| 534 | set_power_light_amber_noblink(); | ||
| 535 | return 0; | ||
| 536 | out_err: | ||
| 537 | for (; i >= 0; i--) | ||
| 538 | unregister_nasgpio_led(i); | ||
| 539 | pci_unregister_driver(&nas_gpio_pci_driver); | ||
| 540 | return ret; | ||
| 541 | } | ||
| 542 | |||
| 543 | /* | ||
| 544 | * module unload | ||
| 545 | */ | ||
| 546 | static void __exit nas_gpio_exit(void) | ||
| 547 | { | ||
| 548 | int i; | ||
| 549 | pr_info("Unregistering driver\n"); | ||
| 550 | for (i = 0; i < ARRAY_SIZE(nasgpio_leds); i++) | ||
| 551 | unregister_nasgpio_led(i); | ||
| 552 | pci_unregister_driver(&nas_gpio_pci_driver); | ||
| 553 | } | ||
| 554 | |||
| 555 | module_init(nas_gpio_init); | ||
| 556 | module_exit(nas_gpio_exit); | ||
diff --git a/include/linux/leds-lp3944.h b/include/linux/leds-lp3944.h index afc9f9fd70f5..2618aa9063bc 100644 --- a/include/linux/leds-lp3944.h +++ b/include/linux/leds-lp3944.h | |||
| @@ -12,9 +12,6 @@ | |||
| 12 | #ifndef __LINUX_LEDS_LP3944_H | 12 | #ifndef __LINUX_LEDS_LP3944_H |
| 13 | #define __LINUX_LEDS_LP3944_H | 13 | #define __LINUX_LEDS_LP3944_H |
| 14 | 14 | ||
| 15 | #include <linux/leds.h> | ||
| 16 | #include <linux/workqueue.h> | ||
| 17 | |||
| 18 | #define LP3944_LED0 0 | 15 | #define LP3944_LED0 0 |
| 19 | #define LP3944_LED1 1 | 16 | #define LP3944_LED1 1 |
| 20 | #define LP3944_LED2 2 | 17 | #define LP3944_LED2 2 |
diff --git a/include/linux/leds-pca9532.h b/include/linux/leds-pca9532.h index 96eea90f01a8..f158eb1149aa 100644 --- a/include/linux/leds-pca9532.h +++ b/include/linux/leds-pca9532.h | |||
| @@ -32,7 +32,7 @@ struct pca9532_led { | |||
| 32 | struct i2c_client *client; | 32 | struct i2c_client *client; |
| 33 | char *name; | 33 | char *name; |
| 34 | struct led_classdev ldev; | 34 | struct led_classdev ldev; |
| 35 | struct work_struct work; | 35 | struct work_struct work; |
| 36 | enum pca9532_type type; | 36 | enum pca9532_type type; |
| 37 | enum pca9532_state state; | 37 | enum pca9532_state state; |
| 38 | }; | 38 | }; |
diff --git a/include/linux/leds-regulator.h b/include/linux/leds-regulator.h new file mode 100644 index 000000000000..5a8eb389aab8 --- /dev/null +++ b/include/linux/leds-regulator.h | |||
| @@ -0,0 +1,46 @@ | |||
| 1 | /* | ||
| 2 | * leds-regulator.h - platform data structure for regulator driven LEDs. | ||
| 3 | * | ||
| 4 | * Copyright (C) 2009 Antonio Ospite <ospite@studenti.unina.it> | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License version 2 as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | */ | ||
| 11 | |||
| 12 | #ifndef __LINUX_LEDS_REGULATOR_H | ||
| 13 | #define __LINUX_LEDS_REGULATOR_H | ||
| 14 | |||
| 15 | /* | ||
| 16 | * Use "vled" as supply id when declaring the regulator consumer: | ||
| 17 | * | ||
| 18 | * static struct regulator_consumer_supply pcap_regulator_VVIB_consumers [] = { | ||
| 19 | * { .dev_name = "leds-regulator.0", supply = "vled" }, | ||
| 20 | * }; | ||
| 21 | * | ||
| 22 | * If you have several regulator driven LEDs, you can append a numerical id to | ||
| 23 | * .dev_name as done above, and use the same id when declaring the platform | ||
| 24 | * device: | ||
| 25 | * | ||
| 26 | * static struct led_regulator_platform_data a780_vibrator_data = { | ||
| 27 | * .name = "a780::vibrator", | ||
| 28 | * }; | ||
| 29 | * | ||
| 30 | * static struct platform_device a780_vibrator = { | ||
| 31 | * .name = "leds-regulator", | ||
| 32 | * .id = 0, | ||
| 33 | * .dev = { | ||
| 34 | * .platform_data = &a780_vibrator_data, | ||
| 35 | * }, | ||
| 36 | * }; | ||
| 37 | */ | ||
| 38 | |||
| 39 | #include <linux/leds.h> | ||
| 40 | |||
| 41 | struct led_regulator_platform_data { | ||
| 42 | char *name; /* LED name as expected by LED class */ | ||
| 43 | enum led_brightness brightness; /* initial brightness value */ | ||
| 44 | }; | ||
| 45 | |||
| 46 | #endif /* __LINUX_LEDS_REGULATOR_H */ | ||
