diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index 5d16fc4fa46c7855a835434112c6801bc2587098..cc9b9f7b5d826cd9c3a7cb987ba484ac63b3373c 100644 --- 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 diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 0e2c0ac5792db715b10ebc69a16a17445e8edcbb..a3b7438335ca0f3ddb03d31702e2787c801e1346 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -731,6 +731,19 @@ u64 ata_tf_read_block(const struct ata_taskfile *tf, struct ata_device *dev) return block; } +#ifdef CONFIG_ATA_LEDS +#define LIBATA_BLINK_DELAY 20 /* ms */ +static inline void ata_led_act(struct ata_port *ap) +{ + unsigned long led_delay = LIBATA_BLINK_DELAY; + + if (unlikely(!ap->ledtrig)) + return; + + led_trigger_blink_oneshot(ap->ledtrig, &led_delay, &led_delay, 0); +} +#endif + /** * ata_build_rw_tf - Build ATA taskfile for given read/write request * @tf: Target ATA taskfile @@ -4994,6 +5007,9 @@ struct ata_queued_cmd *ata_qc_new_init(struct ata_device *dev, int tag) if (tag < 0) return NULL; } +#ifdef CONFIG_ATA_LEDS + ata_led_act(ap); +#endif qc = __ata_qc_from_tag(ap, tag); qc->tag = tag; @@ -5894,6 +5910,9 @@ struct ata_port *ata_port_alloc(struct ata_host *host) #ifdef ATA_IRQ_TRAP ap->stats.unhandled_irq = 1; ap->stats.idle_irq = 1; +#endif +#ifdef CONFIG_ATA_LEDS + ap->ledtrig = kzalloc(sizeof(struct led_trigger), GFP_KERNEL); #endif ata_sff_port_init(ap); @@ -5916,6 +5935,12 @@ static void ata_host_release(struct device *gendev, void *res) kfree(ap->pmp_link); kfree(ap->slave_link); +#ifdef CONFIG_ATA_LEDS + if (ap->ledtrig) { + led_trigger_unregister(ap->ledtrig); + kfree(ap->ledtrig); + }; +#endif kfree(ap); host->ports[i] = NULL; } @@ -6362,7 +6387,23 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht) host->ports[i]->print_id = atomic_inc_return(&ata_print_id); host->ports[i]->local_port_no = i + 1; } +#ifdef CONFIG_ATA_LEDS + 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]); diff --git a/include/linux/libata.h b/include/linux/libata.h index 616eef4d81ea38af48cf809ecdbc2a721aff4640..29ef0938ce943825100d7bd9fa42559884e420de 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -38,6 +38,9 @@ #include <linux/acpi.h> #include <linux/cdrom.h> #include <linux/sched.h> +#ifdef CONFIG_ATA_LEDS +#include <linux/leds.h> +#endif /* * Define if arch has non-standard setup. This is a _PCI_ standard @@ -883,6 +886,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; };