leds-ns2.c revision a209f7669870c461932515eba147b961692de37f
111efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot/* 211efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot * leds-ns2.c - Driver for the Network Space v2 (and parents) dual-GPIO LED 311efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot * 411efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot * Copyright (C) 2010 LaCie 511efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot * 611efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot * Author: Simon Guinot <sguinot@lacie.com> 711efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot * 811efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot * Based on leds-gpio.c by Raphael Assenat <raph@8d.com> 911efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot * 1011efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot * This program is free software; you can redistribute it and/or modify 1111efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot * it under the terms of the GNU General Public License as published by 1211efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot * the Free Software Foundation; either version 2 of the License, or 1311efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot * (at your option) any later version. 1411efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot * 1511efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot * This program is distributed in the hope that it will be useful, 1611efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot * but WITHOUT ANY WARRANTY; without even the implied warranty of 1711efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1811efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot * GNU General Public License for more details. 1911efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot * 2011efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot * You should have received a copy of the GNU General Public License 2111efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot * along with this program; if not, write to the Free Software 2211efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 2311efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot */ 2411efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 2511efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot#include <linux/kernel.h> 2611efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot#include <linux/init.h> 2711efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot#include <linux/platform_device.h> 2811efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot#include <linux/slab.h> 2911efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot#include <linux/gpio.h> 3011efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot#include <linux/leds.h> 3154f4dedb5368fff81b722b551e2f15a75175d7b7Paul Gortmaker#include <linux/module.h> 3211efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot#include <mach/leds-ns2.h> 3311efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 3411efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot/* 3511efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot * The Network Space v2 dual-GPIO LED is wired to a CPLD and can blink in 3611efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot * relation with the SATA activity. This capability is exposed through the 3711efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot * "sata" sysfs attribute. 3811efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot * 3911efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot * The following array detail the different LED registers and the combination 4011efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot * of their possible values: 4111efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot * 4211efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot * cmd_led | slow_led | /SATA active | LED state 4311efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot * | | | 4411efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot * 1 | 0 | x | off 4511efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot * - | 1 | x | on 4611efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot * 0 | 0 | 1 | on 4711efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot * 0 | 0 | 0 | blink (rate 300ms) 4811efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot */ 4911efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 5011efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinotenum ns2_led_modes { 5111efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot NS_V2_LED_OFF, 5211efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot NS_V2_LED_ON, 5311efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot NS_V2_LED_SATA, 5411efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot}; 5511efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 5611efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinotstruct ns2_led_mode_value { 5711efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot enum ns2_led_modes mode; 5811efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot int cmd_level; 5911efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot int slow_level; 6011efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot}; 6111efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 6211efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinotstatic struct ns2_led_mode_value ns2_led_modval[] = { 6311efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot { NS_V2_LED_OFF , 1, 0 }, 6411efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot { NS_V2_LED_ON , 0, 1 }, 6511efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot { NS_V2_LED_ON , 1, 1 }, 6611efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot { NS_V2_LED_SATA, 0, 0 }, 6711efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot}; 6811efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 6911efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinotstruct ns2_led_data { 7011efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot struct led_classdev cdev; 7111efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot unsigned cmd; 7211efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot unsigned slow; 7311efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot unsigned char sata; /* True when SATA mode active. */ 7411efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot rwlock_t rw_lock; /* Lock GPIOs. */ 7511efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot}; 7611efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 7711efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinotstatic int ns2_led_get_mode(struct ns2_led_data *led_dat, 7811efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot enum ns2_led_modes *mode) 7911efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot{ 8011efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot int i; 8111efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot int ret = -EINVAL; 8211efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot int cmd_level; 8311efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot int slow_level; 8411efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 85f539dfedbd169e5ed47912bb517c75976ab556f3Simon Guinot read_lock_irq(&led_dat->rw_lock); 8611efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 8711efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot cmd_level = gpio_get_value(led_dat->cmd); 8811efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot slow_level = gpio_get_value(led_dat->slow); 8911efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 9011efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot for (i = 0; i < ARRAY_SIZE(ns2_led_modval); i++) { 9111efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot if (cmd_level == ns2_led_modval[i].cmd_level && 9211efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot slow_level == ns2_led_modval[i].slow_level) { 9311efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot *mode = ns2_led_modval[i].mode; 9411efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot ret = 0; 9511efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot break; 9611efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot } 9711efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot } 9811efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 99f539dfedbd169e5ed47912bb517c75976ab556f3Simon Guinot read_unlock_irq(&led_dat->rw_lock); 10011efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 10111efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot return ret; 10211efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot} 10311efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 10411efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinotstatic void ns2_led_set_mode(struct ns2_led_data *led_dat, 10511efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot enum ns2_led_modes mode) 10611efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot{ 10711efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot int i; 108f539dfedbd169e5ed47912bb517c75976ab556f3Simon Guinot unsigned long flags; 10911efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 110f539dfedbd169e5ed47912bb517c75976ab556f3Simon Guinot write_lock_irqsave(&led_dat->rw_lock, flags); 11111efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 11211efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot for (i = 0; i < ARRAY_SIZE(ns2_led_modval); i++) { 11311efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot if (mode == ns2_led_modval[i].mode) { 11411efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot gpio_set_value(led_dat->cmd, 11511efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot ns2_led_modval[i].cmd_level); 11611efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot gpio_set_value(led_dat->slow, 11711efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot ns2_led_modval[i].slow_level); 11811efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot } 11911efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot } 12011efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 121f539dfedbd169e5ed47912bb517c75976ab556f3Simon Guinot write_unlock_irqrestore(&led_dat->rw_lock, flags); 12211efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot} 12311efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 12411efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinotstatic void ns2_led_set(struct led_classdev *led_cdev, 12511efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot enum led_brightness value) 12611efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot{ 12711efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot struct ns2_led_data *led_dat = 12811efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot container_of(led_cdev, struct ns2_led_data, cdev); 12911efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot enum ns2_led_modes mode; 13011efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 13111efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot if (value == LED_OFF) 13211efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot mode = NS_V2_LED_OFF; 13311efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot else if (led_dat->sata) 13411efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot mode = NS_V2_LED_SATA; 13511efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot else 13611efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot mode = NS_V2_LED_ON; 13711efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 13811efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot ns2_led_set_mode(led_dat, mode); 13911efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot} 14011efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 14111efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinotstatic ssize_t ns2_led_sata_store(struct device *dev, 14211efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot struct device_attribute *attr, 14311efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot const char *buff, size_t count) 14411efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot{ 145e5971bbc1caf0ccbab3fea74fe37abb649d1c7cfSimon Guinot struct led_classdev *led_cdev = dev_get_drvdata(dev); 146e5971bbc1caf0ccbab3fea74fe37abb649d1c7cfSimon Guinot struct ns2_led_data *led_dat = 147e5971bbc1caf0ccbab3fea74fe37abb649d1c7cfSimon Guinot container_of(led_cdev, struct ns2_led_data, cdev); 14811efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot int ret; 14911efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot unsigned long enable; 15011efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot enum ns2_led_modes mode; 15111efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 15211efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot ret = strict_strtoul(buff, 10, &enable); 15311efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot if (ret < 0) 15411efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot return ret; 15511efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 15611efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot enable = !!enable; 15711efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 15811efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot if (led_dat->sata == enable) 15911efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot return count; 16011efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 16111efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot ret = ns2_led_get_mode(led_dat, &mode); 16211efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot if (ret < 0) 16311efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot return ret; 16411efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 16511efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot if (enable && mode == NS_V2_LED_ON) 16611efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot ns2_led_set_mode(led_dat, NS_V2_LED_SATA); 16711efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot if (!enable && mode == NS_V2_LED_SATA) 16811efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot ns2_led_set_mode(led_dat, NS_V2_LED_ON); 16911efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 17011efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot led_dat->sata = enable; 17111efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 17211efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot return count; 17311efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot} 17411efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 17511efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinotstatic ssize_t ns2_led_sata_show(struct device *dev, 17611efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot struct device_attribute *attr, char *buf) 17711efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot{ 178e5971bbc1caf0ccbab3fea74fe37abb649d1c7cfSimon Guinot struct led_classdev *led_cdev = dev_get_drvdata(dev); 179e5971bbc1caf0ccbab3fea74fe37abb649d1c7cfSimon Guinot struct ns2_led_data *led_dat = 180e5971bbc1caf0ccbab3fea74fe37abb649d1c7cfSimon Guinot container_of(led_cdev, struct ns2_led_data, cdev); 18111efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 18211efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot return sprintf(buf, "%d\n", led_dat->sata); 18311efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot} 18411efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 18511efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinotstatic DEVICE_ATTR(sata, 0644, ns2_led_sata_show, ns2_led_sata_store); 18611efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 18711efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinotstatic int __devinit 18811efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinotcreate_ns2_led(struct platform_device *pdev, struct ns2_led_data *led_dat, 18911efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot const struct ns2_led *template) 19011efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot{ 19111efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot int ret; 19211efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot enum ns2_led_modes mode; 19311efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 19411efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot ret = gpio_request(template->cmd, template->name); 19511efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot if (ret == 0) { 19611efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot ret = gpio_direction_output(template->cmd, 19711efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot gpio_get_value(template->cmd)); 19811efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot if (ret) 19911efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot gpio_free(template->cmd); 20011efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot } 20111efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot if (ret) { 20211efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot dev_err(&pdev->dev, "%s: failed to setup command GPIO\n", 20311efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot template->name); 20411efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot } 20511efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 20611efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot ret = gpio_request(template->slow, template->name); 20711efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot if (ret == 0) { 20811efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot ret = gpio_direction_output(template->slow, 20911efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot gpio_get_value(template->slow)); 21011efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot if (ret) 21111efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot gpio_free(template->slow); 21211efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot } 21311efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot if (ret) { 21411efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot dev_err(&pdev->dev, "%s: failed to setup slow GPIO\n", 21511efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot template->name); 21611efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot goto err_free_cmd; 21711efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot } 21811efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 21911efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot rwlock_init(&led_dat->rw_lock); 22011efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 22111efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot led_dat->cdev.name = template->name; 22211efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot led_dat->cdev.default_trigger = template->default_trigger; 22311efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot led_dat->cdev.blink_set = NULL; 22411efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot led_dat->cdev.brightness_set = ns2_led_set; 22511efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; 22611efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot led_dat->cmd = template->cmd; 22711efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot led_dat->slow = template->slow; 22811efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 22911efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot ret = ns2_led_get_mode(led_dat, &mode); 23011efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot if (ret < 0) 23111efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot goto err_free_slow; 23211efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 23311efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot /* Set LED initial state. */ 23411efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot led_dat->sata = (mode == NS_V2_LED_SATA) ? 1 : 0; 23511efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot led_dat->cdev.brightness = 23611efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot (mode == NS_V2_LED_OFF) ? LED_OFF : LED_FULL; 23711efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 23811efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot ret = led_classdev_register(&pdev->dev, &led_dat->cdev); 23911efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot if (ret < 0) 24011efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot goto err_free_slow; 24111efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 24211efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot ret = device_create_file(led_dat->cdev.dev, &dev_attr_sata); 24311efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot if (ret < 0) 24411efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot goto err_free_cdev; 24511efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 24611efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot return 0; 24711efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 24811efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinoterr_free_cdev: 24911efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot led_classdev_unregister(&led_dat->cdev); 25011efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinoterr_free_slow: 25111efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot gpio_free(led_dat->slow); 25211efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinoterr_free_cmd: 25311efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot gpio_free(led_dat->cmd); 25411efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 25511efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot return ret; 25611efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot} 25711efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 258b8cd742acfd78a4689148fb80cf74bc26e7f1f3cArnd Bergmannstatic void delete_ns2_led(struct ns2_led_data *led_dat) 25911efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot{ 26011efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot device_remove_file(led_dat->cdev.dev, &dev_attr_sata); 26111efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot led_classdev_unregister(&led_dat->cdev); 26211efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot gpio_free(led_dat->cmd); 26311efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot gpio_free(led_dat->slow); 26411efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot} 26511efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 26611efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinotstatic int __devinit ns2_led_probe(struct platform_device *pdev) 26711efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot{ 26811efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot struct ns2_led_platform_data *pdata = pdev->dev.platform_data; 26911efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot struct ns2_led_data *leds_data; 27011efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot int i; 27111efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot int ret; 27211efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 27311efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot if (!pdata) 27411efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot return -EINVAL; 27511efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 276a209f7669870c461932515eba147b961692de37fBryan Wu leds_data = devm_kzalloc(&pdev->dev, sizeof(struct ns2_led_data) * 27711efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot pdata->num_leds, GFP_KERNEL); 27811efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot if (!leds_data) 27911efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot return -ENOMEM; 28011efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 28111efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot for (i = 0; i < pdata->num_leds; i++) { 28211efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot ret = create_ns2_led(pdev, &leds_data[i], &pdata->leds[i]); 283a209f7669870c461932515eba147b961692de37fBryan Wu if (ret < 0) { 284a209f7669870c461932515eba147b961692de37fBryan Wu for (i = i - 1; i >= 0; i--) 285a209f7669870c461932515eba147b961692de37fBryan Wu delete_ns2_led(&leds_data[i]); 286a209f7669870c461932515eba147b961692de37fBryan Wu return ret; 287a209f7669870c461932515eba147b961692de37fBryan Wu } 28811efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot } 28911efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 29011efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot platform_set_drvdata(pdev, leds_data); 29111efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 29211efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot return 0; 29311efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot} 29411efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 29511efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinotstatic int __devexit ns2_led_remove(struct platform_device *pdev) 29611efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot{ 29711efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot int i; 29811efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot struct ns2_led_platform_data *pdata = pdev->dev.platform_data; 29911efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot struct ns2_led_data *leds_data; 30011efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 30111efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot leds_data = platform_get_drvdata(pdev); 30211efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 30311efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot for (i = 0; i < pdata->num_leds; i++) 30411efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot delete_ns2_led(&leds_data[i]); 30511efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 30611efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot platform_set_drvdata(pdev, NULL); 30711efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 30811efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot return 0; 30911efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot} 31011efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 31111efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinotstatic struct platform_driver ns2_led_driver = { 31211efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot .probe = ns2_led_probe, 31311efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot .remove = __devexit_p(ns2_led_remove), 31411efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot .driver = { 31511efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot .name = "leds-ns2", 31611efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot .owner = THIS_MODULE, 31711efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot }, 31811efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot}; 31911efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 320892a8843fbef07a7f2ab62d5f7ff5c16ea0903b0Axel Linmodule_platform_driver(ns2_led_driver); 32111efe71f6564e59ba9acfc636ac6f423e059dc69Simon Guinot 32211efe71f6564e59ba9acfc636ac6f423e059dc69Simon GuinotMODULE_AUTHOR("Simon Guinot <sguinot@lacie.com>"); 32311efe71f6564e59ba9acfc636ac6f423e059dc69Simon GuinotMODULE_DESCRIPTION("Network Space v2 LED driver"); 32411efe71f6564e59ba9acfc636ac6f423e059dc69Simon GuinotMODULE_LICENSE("GPL"); 325892a8843fbef07a7f2ab62d5f7ff5c16ea0903b0Axel LinMODULE_ALIAS("platform:leds-ns2"); 326