提交 e40dc662 编写于 作者: L Linus Torvalds

Merge tag 'leds_for_4.17-rc1' of...

Merge tag 'leds_for_4.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/j.anaszewski/linux-leds

Pull LED updates from Jacek Anaszewski:
 "New LED class driver:
   - add driver for Mellanox regmap LEDs

  Improvement to ledtrig-disk:
   - extend disk trigger for reads and writes

  Improvements and fixes to existing LED class drivers:
   - add more product/board names for PC Engines APU2
   - fix wrong dmi_match on PC Engines APU LEDs
   - clarify chips supported by LM355x driver
   - fix Kconfig text for MLXCPLD, SYSCON, MC13783, NETXBIG
   - allow leds-mlxcpld compilation for 32 bit arch"

* tag 'leds_for_4.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/j.anaszewski/linux-leds:
  leds: Fix wrong dmi_match on PC Engines APU LEDs
  leds: Extends disk trigger for reads and writes
  leds: Add more product/board names for PC Engines APU2
  leds: add driver for support Mellanox regmap LEDs for BMC and x86 platform
  leds: fix Kconfig text for MLXCPLD, SYSCON, MC13783, NETXBIG
  leds: Clarify supported chips by LM355x driver
  leds: leds-mlxcpld: Allow compilation for 32 bit arch
......@@ -8988,6 +8988,7 @@ M: Vadim Pasternak <vadimp@mellanox.com>
L: linux-leds@vger.kernel.org
S: Supported
F: drivers/leds/leds-mlxcpld.c
F: drivers/leds/leds-mlxreg.c
F: Documentation/leds/leds-mlxcpld.txt
MELLANOX PLATFORM DRIVER
......
......@@ -5240,7 +5240,7 @@ void ata_qc_complete(struct ata_queued_cmd *qc)
struct ata_port *ap = qc->ap;
/* Trigger the LED (if available) */
ledtrig_disk_activity();
ledtrig_disk_activity(!!(qc->tf.flags & ATA_TFLAG_WRITE));
/* XXX: New EH and old EH use different mechanisms to
* synchronize EH with regular execution path.
......
......@@ -187,7 +187,7 @@ static ide_startstop_t ide_do_rw_disk(ide_drive_t *drive, struct request *rq,
BUG_ON(drive->dev_flags & IDE_DFLAG_BLOCKED);
BUG_ON(blk_rq_is_passthrough(rq));
ledtrig_disk_activity();
ledtrig_disk_activity(rq_data_dir(rq) == WRITE);
pr_debug("%s: %sing: block=%llu, sectors=%u\n",
drive->name, rq_data_dir(rq) == READ ? "read" : "writ",
......
......@@ -69,7 +69,7 @@ config LEDS_APU
module will be called leds-apu.
config LEDS_AS3645A
tristate "AS3645A LED flash controller support"
tristate "AS3645A and LM3555 LED flash controllers support"
depends on I2C && LEDS_CLASS_FLASH
depends on V4L2_FLASH_LED_CLASS || !V4L2_FLASH_LED_CLASS
help
......@@ -521,7 +521,7 @@ config LEDS_MC13783
depends on LEDS_CLASS
depends on MFD_MC13XXX
help
This option enable support for on-chip LED drivers found
This option enables support for on-chip LED drivers found
on Freescale Semiconductor MC13783/MC13892/MC34708 PMIC.
config LEDS_NS2
......@@ -544,7 +544,7 @@ config LEDS_NETXBIG
depends on MACH_KIRKWOOD
default y
help
This option enable support for LEDs found on the LaCie 2Big
This option enables support for LEDs found on the LaCie 2Big
and 5Big Network v2 boards. The LEDs are wired to a CPLD and are
controlled through a GPIO extension bus.
......@@ -594,12 +594,12 @@ config LEDS_MAX8997
MAXIM MAX8997 PMIC.
config LEDS_LM355x
tristate "LED support for LM355x Chips, LM3554 and LM3556"
tristate "LED support for LM3554 and LM3556 chips"
depends on LEDS_CLASS && I2C
select REGMAP_I2C
help
This option enables support for LEDs connected to LM355x.
LM355x includes Torch, Flash and Indicator functions.
This option enables support for LEDs connected to LM3554
and LM3556. It includes Torch, Flash and Indicator functions.
config LEDS_OT200
tristate "LED support for the Bachmann OT200"
......@@ -674,7 +674,7 @@ config LEDS_SYSCON
depends on MFD_SYSCON
depends on OF
help
This option enabled support for the LEDs on syscon type
This option enables support for the LEDs on syscon type
devices. This will only work with device tree enabled
devices.
......@@ -688,11 +688,20 @@ config LEDS_PM8058
config LEDS_MLXCPLD
tristate "LED support for the Mellanox boards"
depends on X86_64 && DMI
depends on X86 && DMI
depends on LEDS_CLASS
help
This option enables support for the LEDs on the Mellanox
boards. Say Y to enable these.
config LEDS_MLXREG
tristate "LED support for the Mellanox switches management control"
depends on LEDS_CLASS
help
This option enabled support for the LEDs on the Mellanox
boards. Say Y to enabled these.
This option enables support for the LEDs on the Mellanox Ethernet and
InfiniBand switches. The driver can be activated by the platform device
device add call. Say Y to enable these. To compile this driver as a
module, choose 'M' here: the module will be called leds-mlxreg.
config LEDS_USER
tristate "Userspace LED support"
......
......@@ -72,6 +72,7 @@ obj-$(CONFIG_LEDS_IS31FL319X) += leds-is31fl319x.o
obj-$(CONFIG_LEDS_IS31FL32XX) += leds-is31fl32xx.o
obj-$(CONFIG_LEDS_PM8058) += leds-pm8058.o
obj-$(CONFIG_LEDS_MLXCPLD) += leds-mlxcpld.o
obj-$(CONFIG_LEDS_MLXREG) += leds-mlxreg.o
obj-$(CONFIG_LEDS_NIC78BX) += leds-nic78bx.o
obj-$(CONFIG_LEDS_MT6323) += leds-mt6323.o
obj-$(CONFIG_LEDS_LM3692X) += leds-lm3692x.o
......
......@@ -110,6 +110,7 @@ static const struct dmi_system_id apu_led_dmi_table[] __initconst = {
DMI_MATCH(DMI_PRODUCT_NAME, "APU")
}
},
/* PC Engines APU2 with "Legacy" bios < 4.0.8 */
{
.ident = "apu2",
.matches = {
......@@ -117,6 +118,22 @@ static const struct dmi_system_id apu_led_dmi_table[] __initconst = {
DMI_MATCH(DMI_BOARD_NAME, "APU2")
}
},
/* PC Engines APU2 with "Legacy" bios >= 4.0.8 */
{
.ident = "apu2",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
DMI_MATCH(DMI_BOARD_NAME, "apu2")
}
},
/* PC Engines APU2 with "Mainline" bios */
{
.ident = "apu2",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
DMI_MATCH(DMI_BOARD_NAME, "PC Engines apu2")
}
},
{}
};
MODULE_DEVICE_TABLE(dmi, apu_led_dmi_table);
......@@ -206,12 +223,14 @@ static int __init apu_led_probe(struct platform_device *pdev)
apu_led->pdev = pdev;
if (dmi_match(DMI_BOARD_NAME, "APU")) {
if (dmi_match(DMI_PRODUCT_NAME, "APU")) {
apu_led->profile = apu1_led_profile;
apu_led->platform = APU1_LED_PLATFORM;
apu_led->num_led_instances = ARRAY_SIZE(apu1_led_profile);
apu_led->iosize = APU1_IOSIZE;
} else if (dmi_match(DMI_BOARD_NAME, "APU2")) {
} else if (dmi_match(DMI_BOARD_NAME, "APU2") ||
dmi_match(DMI_BOARD_NAME, "apu2") ||
dmi_match(DMI_BOARD_NAME, "PC Engines apu2")) {
apu_led->profile = apu2_led_profile;
apu_led->platform = APU2_LED_PLATFORM;
apu_led->num_led_instances = ARRAY_SIZE(apu2_led_profile);
......@@ -237,7 +256,10 @@ static int __init apu_led_init(void)
pr_err("No PC Engines board detected\n");
return -ENODEV;
}
if (!(dmi_match(DMI_PRODUCT_NAME, "APU") || dmi_match(DMI_PRODUCT_NAME, "APU2"))) {
if (!(dmi_match(DMI_PRODUCT_NAME, "APU") ||
dmi_match(DMI_PRODUCT_NAME, "APU2") ||
dmi_match(DMI_PRODUCT_NAME, "apu2") ||
dmi_match(DMI_PRODUCT_NAME, "PC Engines apu2"))) {
pr_err("Unknown PC Engines board: %s\n",
dmi_get_system_info(DMI_PRODUCT_NAME));
return -ENODEV;
......
// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
//
// Copyright (c) 2018 Mellanox Technologies. All rights reserved.
// Copyright (c) 2018 Vadim Pasternak <vadimp@mellanox.com>
#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/platform_data/mlxreg.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
/* Codes for LEDs. */
#define MLXREG_LED_OFFSET_BLINK_3HZ 0x01 /* Offset from solid: 3Hz blink */
#define MLXREG_LED_OFFSET_BLINK_6HZ 0x02 /* Offset from solid: 6Hz blink */
#define MLXREG_LED_IS_OFF 0x00 /* Off */
#define MLXREG_LED_RED_SOLID 0x05 /* Solid red */
#define MLXREG_LED_GREEN_SOLID 0x0D /* Solid green */
#define MLXREG_LED_AMBER_SOLID 0x09 /* Solid amber */
#define MLXREG_LED_BLINK_3HZ 167 /* ~167 msec off/on - HW support */
#define MLXREG_LED_BLINK_6HZ 83 /* ~83 msec off/on - HW support */
/**
* struct mlxreg_led_data - led control data:
*
* @data: led configuration data;
* @led_classdev: led class data;
* @base_color: base led color (other colors have constant offset from base);
* @led_data: led data;
* @data_parent: pointer to private device control data of parent;
*/
struct mlxreg_led_data {
struct mlxreg_core_data *data;
struct led_classdev led_cdev;
u8 base_color;
void *data_parent;
char led_cdev_name[MLXREG_CORE_LABEL_MAX_SIZE];
};
#define cdev_to_priv(c) container_of(c, struct mlxreg_led_data, led_cdev)
/**
* struct mlxreg_led_priv_data - platform private data:
*
* @pdev: platform device;
* @pdata: platform data;
* @access_lock: mutex for attribute IO access;
*/
struct mlxreg_led_priv_data {
struct platform_device *pdev;
struct mlxreg_core_platform_data *pdata;
struct mutex access_lock; /* protect IO operations */
};
static int
mlxreg_led_store_hw(struct mlxreg_led_data *led_data, u8 vset)
{
struct mlxreg_led_priv_data *priv = led_data->data_parent;
struct mlxreg_core_platform_data *led_pdata = priv->pdata;
struct mlxreg_core_data *data = led_data->data;
u32 regval;
u32 nib;
int ret;
/*
* Each LED is controlled through low or high nibble of the relevant
* register byte. Register offset is specified by off parameter.
* Parameter vset provides color code: 0x0 for off, 0x5 for solid red,
* 0x6 for 3Hz blink red, 0xd for solid green, 0xe for 3Hz blink
* green.
* Parameter mask specifies which nibble is used for specific LED: mask
* 0xf0 - lower nibble is to be used (bits from 0 to 3), mask 0x0f -
* higher nibble (bits from 4 to 7).
*/
mutex_lock(&priv->access_lock);
ret = regmap_read(led_pdata->regmap, data->reg, &regval);
if (ret)
goto access_error;
nib = (ror32(data->mask, data->bit) == 0xf0) ? rol32(vset, data->bit) :
rol32(vset, data->bit + 4);
regval = (regval & data->mask) | nib;
ret = regmap_write(led_pdata->regmap, data->reg, regval);
access_error:
mutex_unlock(&priv->access_lock);
return ret;
}
static enum led_brightness
mlxreg_led_get_hw(struct mlxreg_led_data *led_data)
{
struct mlxreg_led_priv_data *priv = led_data->data_parent;
struct mlxreg_core_platform_data *led_pdata = priv->pdata;
struct mlxreg_core_data *data = led_data->data;
u32 regval;
int err;
/*
* Each LED is controlled through low or high nibble of the relevant
* register byte. Register offset is specified by off parameter.
* Parameter vset provides color code: 0x0 for off, 0x5 for solid red,
* 0x6 for 3Hz blink red, 0xd for solid green, 0xe for 3Hz blink
* green.
* Parameter mask specifies which nibble is used for specific LED: mask
* 0xf0 - lower nibble is to be used (bits from 0 to 3), mask 0x0f -
* higher nibble (bits from 4 to 7).
*/
err = regmap_read(led_pdata->regmap, data->reg, &regval);
if (err < 0) {
dev_warn(led_data->led_cdev.dev, "Failed to get current brightness, error: %d\n",
err);
/* Assume the LED is OFF */
return LED_OFF;
}
regval = regval & ~data->mask;
regval = (ror32(data->mask, data->bit) == 0xf0) ? ror32(regval,
data->bit) : ror32(regval, data->bit + 4);
if (regval >= led_data->base_color &&
regval <= (led_data->base_color + MLXREG_LED_OFFSET_BLINK_6HZ))
return LED_FULL;
return LED_OFF;
}
static int
mlxreg_led_brightness_set(struct led_classdev *cled, enum led_brightness value)
{
struct mlxreg_led_data *led_data = cdev_to_priv(cled);
if (value)
return mlxreg_led_store_hw(led_data, led_data->base_color);
else
return mlxreg_led_store_hw(led_data, MLXREG_LED_IS_OFF);
}
static enum led_brightness
mlxreg_led_brightness_get(struct led_classdev *cled)
{
struct mlxreg_led_data *led_data = cdev_to_priv(cled);
return mlxreg_led_get_hw(led_data);
}
static int
mlxreg_led_blink_set(struct led_classdev *cled, unsigned long *delay_on,
unsigned long *delay_off)
{
struct mlxreg_led_data *led_data = cdev_to_priv(cled);
int err;
/*
* HW supports two types of blinking: full (6Hz) and half (3Hz).
* For delay on/off zero LED is setting to solid color. For others
* combination blinking is to be controlled by the software timer.
*/
if (!(*delay_on == 0 && *delay_off == 0) &&
!(*delay_on == MLXREG_LED_BLINK_3HZ &&
*delay_off == MLXREG_LED_BLINK_3HZ) &&
!(*delay_on == MLXREG_LED_BLINK_6HZ &&
*delay_off == MLXREG_LED_BLINK_6HZ))
return -EINVAL;
if (*delay_on == MLXREG_LED_BLINK_6HZ)
err = mlxreg_led_store_hw(led_data, led_data->base_color +
MLXREG_LED_OFFSET_BLINK_6HZ);
else if (*delay_on == MLXREG_LED_BLINK_3HZ)
err = mlxreg_led_store_hw(led_data, led_data->base_color +
MLXREG_LED_OFFSET_BLINK_3HZ);
else
err = mlxreg_led_store_hw(led_data, led_data->base_color);
return err;
}
static int mlxreg_led_config(struct mlxreg_led_priv_data *priv)
{
struct mlxreg_core_platform_data *led_pdata = priv->pdata;
struct mlxreg_core_data *data = led_pdata->data;
struct mlxreg_led_data *led_data;
struct led_classdev *led_cdev;
enum led_brightness brightness;
int i;
int err;
for (i = 0; i < led_pdata->counter; i++, data++) {
led_data = devm_kzalloc(&priv->pdev->dev, sizeof(*led_data),
GFP_KERNEL);
if (!led_data)
return -ENOMEM;
led_cdev = &led_data->led_cdev;
led_data->data_parent = priv;
if (strstr(data->label, "red") ||
strstr(data->label, "orange")) {
brightness = LED_OFF;
led_data->base_color = MLXREG_LED_RED_SOLID;
} else if (strstr(data->label, "amber")) {
brightness = LED_OFF;
led_data->base_color = MLXREG_LED_AMBER_SOLID;
} else {
brightness = LED_OFF;
led_data->base_color = MLXREG_LED_GREEN_SOLID;
}
sprintf(led_data->led_cdev_name, "%s:%s", "mlxreg",
data->label);
led_cdev->name = led_data->led_cdev_name;
led_cdev->brightness = brightness;
led_cdev->max_brightness = LED_ON;
led_cdev->brightness_set_blocking =
mlxreg_led_brightness_set;
led_cdev->brightness_get = mlxreg_led_brightness_get;
led_cdev->blink_set = mlxreg_led_blink_set;
led_cdev->flags = LED_CORE_SUSPENDRESUME;
led_data->data = data;
err = devm_led_classdev_register(&priv->pdev->dev, led_cdev);
if (err)
return err;
if (led_cdev->brightness)
mlxreg_led_brightness_set(led_cdev,
led_cdev->brightness);
dev_info(led_cdev->dev, "label: %s, mask: 0x%02x, offset:0x%02x\n",
data->label, data->mask, data->reg);
}
return 0;
}
static int mlxreg_led_probe(struct platform_device *pdev)
{
struct mlxreg_core_platform_data *led_pdata;
struct mlxreg_led_priv_data *priv;
led_pdata = dev_get_platdata(&pdev->dev);
if (!led_pdata) {
dev_err(&pdev->dev, "Failed to get platform data.\n");
return -EINVAL;
}
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
mutex_init(&priv->access_lock);
priv->pdev = pdev;
priv->pdata = led_pdata;
return mlxreg_led_config(priv);
}
static int mlxreg_led_remove(struct platform_device *pdev)
{
struct mlxreg_led_priv_data *priv = dev_get_drvdata(&pdev->dev);
mutex_destroy(&priv->access_lock);
return 0;
}
static struct platform_driver mlxreg_led_driver = {
.driver = {
.name = "leds-mlxreg",
},
.probe = mlxreg_led_probe,
.remove = mlxreg_led_remove,
};
module_platform_driver(mlxreg_led_driver);
MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>");
MODULE_DESCRIPTION("Mellanox LED regmap driver");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_ALIAS("platform:leds-mlxreg");
......@@ -18,9 +18,11 @@
#define BLINK_DELAY 30
DEFINE_LED_TRIGGER(ledtrig_disk);
DEFINE_LED_TRIGGER(ledtrig_disk_read);
DEFINE_LED_TRIGGER(ledtrig_disk_write);
DEFINE_LED_TRIGGER(ledtrig_ide);
void ledtrig_disk_activity(void)
void ledtrig_disk_activity(bool write)
{
unsigned long blink_delay = BLINK_DELAY;
......@@ -28,12 +30,20 @@ void ledtrig_disk_activity(void)
&blink_delay, &blink_delay, 0);
led_trigger_blink_oneshot(ledtrig_ide,
&blink_delay, &blink_delay, 0);
if (write)
led_trigger_blink_oneshot(ledtrig_disk_write,
&blink_delay, &blink_delay, 0);
else
led_trigger_blink_oneshot(ledtrig_disk_read,
&blink_delay, &blink_delay, 0);
}
EXPORT_SYMBOL(ledtrig_disk_activity);
static int __init ledtrig_disk_init(void)
{
led_trigger_register_simple("disk-activity", &ledtrig_disk);
led_trigger_register_simple("disk-read", &ledtrig_disk_read);
led_trigger_register_simple("disk-write", &ledtrig_disk_write);
led_trigger_register_simple("ide-disk", &ledtrig_ide);
return 0;
......
......@@ -346,9 +346,9 @@ static inline void *led_get_trigger_data(struct led_classdev *led_cdev)
/* Trigger specific functions */
#ifdef CONFIG_LEDS_TRIGGER_DISK
extern void ledtrig_disk_activity(void);
extern void ledtrig_disk_activity(bool write);
#else
static inline void ledtrig_disk_activity(void) {}
static inline void ledtrig_disk_activity(bool write) {}
#endif
#ifdef CONFIG_LEDS_TRIGGER_MTD
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册