From 5843af891d0dabf9bb80039cfe807d01e9495154 Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Fri, 12 Dec 2014 13:38:33 +0100 Subject: libata: add ledtrig support This adds a LED trigger for each ATA port indicating disk activity. As this is needed only on specific platforms (NAS SoCs and such), these platforms should define ARCH_WANTS_LIBATA_LEDS if there are boards with LED(s) intended to indicate ATA disk activity and need the OS to take care of that. In that way, if not selected, LED trigger support not will be included in libata-core and both, codepaths and structures remain untouched. Signed-off-by: Daniel Golle --- drivers/ata/Kconfig | 16 ++++++++++++++ drivers/ata/libata-core.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++ include/linux/libata.h | 7 ++++++ 3 files changed, 79 insertions(+) --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -46,6 +46,22 @@ config ATA_VERBOSE_ERROR If unsure, say Y. +config ARCH_WANT_LIBATA_LEDS + bool + +config ATA_LEDS + bool "support ATA port LED triggers" + depends on ARCH_WANT_LIBATA_LEDS + select NEW_LEDS + select LEDS_CLASS + select LEDS_TRIGGERS + default y + help + This option adds a LED trigger for each registered ATA port. + It is used to drive disk activity leds connected via GPIO. + + If unsure, say N. + config ATA_ACPI bool "ATA ACPI Support" depends on ACPI --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -730,6 +730,7 @@ u64 ata_tf_read_block(const struct ata_t return block; } + /** * ata_build_rw_tf - Build ATA taskfile for given read/write request * @tf: Target ATA taskfile @@ -5112,6 +5113,30 @@ void swap_buf_le16(u16 *buf, unsigned in } /** + * ata_led_act - Trigger port activity LED + * @ap: indicating port + * + * Blinks any LEDs registered to the trigger. + * Commonly used with leds-gpio on NAS systems with disk activity + * indicator LEDs. + * + * LOCKING: + * None. + */ +static inline void ata_led_act(struct ata_port *ap) +{ +#if CONFIG_ATA_LEDS +#define LIBATA_BLINK_DELAY 20 /* ms */ + unsigned long led_delay = LIBATA_BLINK_DELAY; + + if (unlikely(!ap->ledtrig)) + return; + + led_trigger_blink_oneshot(ap->ledtrig, &led_delay, &led_delay, 0); +#endif /* CONFIG_ATA_LEDS */ +} + +/** * ata_qc_new_init - Request an available ATA command, and initialize it * @dev: Device from whom we request an available command structure * @tag: tag @@ -5135,6 +5160,9 @@ struct ata_queued_cmd *ata_qc_new_init(s if (tag < 0) return NULL; } +#if CONFIG_ATA_LEDS + ata_led_act(ap); +#endif qc = __ata_qc_from_tag(ap, tag); qc->tag = qc->hw_tag = tag; @@ -6071,6 +6099,9 @@ struct ata_port *ata_port_alloc(struct a ap->stats.unhandled_irq = 1; ap->stats.idle_irq = 1; #endif +#if CONFIG_ATA_LEDS + ap->ledtrig = kzalloc(sizeof(struct led_trigger), GFP_KERNEL); +#endif ata_sff_port_init(ap); return ap; @@ -6106,6 +6137,12 @@ static void ata_host_release(struct kref kfree(ap->pmp_link); kfree(ap->slave_link); +#if CONFIG_ATA_LEDS + if (ap->ledtrig) { + led_trigger_unregister(ap->ledtrig); + kfree(ap->ledtrig); + }; +#endif kfree(ap); host->ports[i] = NULL; } @@ -6570,6 +6607,25 @@ int ata_host_register(struct ata_host *h host->ports[i]->local_port_no = i + 1; } +#if CONFIG_ATA_LEDS + /* register LED triggers for all ports */ + for (i = 0; i < host->n_ports; i++) { + if (unlikely(!host->ports[i]->ledtrig)) + continue; + + snprintf(host->ports[i]->ledtrig_name, + sizeof(host->ports[i]->ledtrig_name), "ata%u", + host->ports[i]->print_id); + + host->ports[i]->ledtrig->name = host->ports[i]->ledtrig_name; + + if (led_trigger_register(host->ports[i]->ledtrig)) { + kfree(host->ports[i]->ledtrig); + host->ports[i]->ledtrig = NULL; + } + } +#endif + /* Create associated sysfs transport objects */ for (i = 0; i < host->n_ports; i++) { rc = ata_tport_add(host->dev,host->ports[i]); --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -39,6 +39,7 @@ #include #include #include +#include /* * Define if arch has non-standard setup. This is a _PCI_ standard @@ -898,6 +899,12 @@ struct ata_port { #ifdef CONFIG_ATA_ACPI struct ata_acpi_gtm __acpi_init_gtm; /* use ata_acpi_init_gtm() */ #endif + +#ifdef CONFIG_ATA_LEDS + struct led_trigger *ledtrig; + char ledtrig_name[8]; +#endif + /* owned by EH */ u8 sector_buf[ATA_SECT_SIZE] ____cacheline_aligned; };