sonic-buildimage/platform/broadcom/sonic-platform-modules-cel/dx010/modules/leds-dx010.c

290 lines
7.6 KiB
C

/*
* leds-dx010-status.c - Driver for Seastone DX010 front panel LEDs
*
* Copyright (C) 2017 Celestica Corp.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#define DRIVER_NAME "leds_dx010"
#define FRONT_LED_STAT 0x303
static int dx010_led_blink_stat(struct led_classdev *led_cdev,
unsigned long *delay_on,
unsigned long *delay_off)
{
unsigned char led;
if (!(*delay_on == 0 && *delay_off == 0) &&
!(*delay_on == 250 && *delay_off == 250) &&
!(*delay_on == 500 && *delay_off == 500))
return -EINVAL;
led = inb(FRONT_LED_STAT);
led &= 0xfc;
if ((*delay_on == 250) && (*delay_off == 250))
led |= 0x02;
else if ((*delay_on == 500) && (*delay_off == 500))
led |= 0x01;
outb(led, FRONT_LED_STAT);
return 0;
}
static ssize_t dx010_led_blink_show_stat(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct led_classdev *leddev = dev_get_drvdata(dev);
unsigned char led;
const char *msg;
led = inb(FRONT_LED_STAT);
led &= 0x03;
switch (led)
{
case 0:
msg = "No blinking, turn on";
break;
case 1:
msg = "1 Hz is blinking";
break;
case 2:
msg = "4 Hz is blinking";
break;
case 3:
msg = "No blinking, turn off";
break;
default:
msg = "Unknown error";
break;
}
return sprintf(buf, "%s\n", msg);
}
static ssize_t dx010_led_blink_store_stat(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
int ret;
struct led_classdev *leddev = dev_get_drvdata(dev);
unsigned long blink_state;
unsigned char led;
ret = kstrtoul(buf, 10, &blink_state);
if (ret)
return ret;
led = inb(FRONT_LED_STAT);
led &= 0xfc;
switch (blink_state)
{
case 0:
led |= 0x03;
break;
case 1:
break;
case 250:
led |= 0x02;
break;
case 500:
led |= 0x01;
break;
default:
return -EINVAL;
break;
}
outb(led, FRONT_LED_STAT);
return size;
}
static DEVICE_ATTR(blink, 0644, dx010_led_blink_show_stat, dx010_led_blink_store_stat);
static void dx010_led_brightness_set_stat(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
unsigned char led;
led = inb(FRONT_LED_STAT);
led &= 0xfc;
if (!brightness)
led |= 0x03;
outb( led, FRONT_LED_STAT);
}
enum led_brightness dx010_led_brightness_get_p2(struct led_classdev *led_cdev)
{
unsigned char led;
led = inb(FRONT_LED_STAT);
return (led & 0x08) ? LED_OFF : LED_FULL;
}
static void dx010_led_brightness_set_p2(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
unsigned char led;
led = inb(FRONT_LED_STAT);
led &= 0xf7;
if (!brightness)
led |= 0x08;
outb( led, FRONT_LED_STAT);
}
enum led_brightness dx010_led_brightness_get_p1(struct led_classdev *led_cdev)
{
unsigned char led;
led = inb(FRONT_LED_STAT);
return (led & 0x04) ? LED_OFF : LED_FULL;
}
static void dx010_led_brightness_set_p1(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
unsigned char led;
led = inb(FRONT_LED_STAT);
led &= 0xfb;
if (!brightness)
led |= 0x04;
outb( led, FRONT_LED_STAT);
}
static struct led_classdev dx010_leds[] = {
{
.name = "dx010:green:p-1",
.brightness = LED_OFF,
.max_brightness = 1,
.brightness_get = dx010_led_brightness_get_p1,
.brightness_set = dx010_led_brightness_set_p1,
},
{
.name = "dx010:green:p-2",
.brightness = LED_OFF,
.max_brightness = 1,
.brightness_get = dx010_led_brightness_get_p2,
.brightness_set = dx010_led_brightness_set_p2,
},
{
.name = "dx010:green:stat",
.brightness = LED_OFF,
.max_brightness = 1,
.brightness_set = dx010_led_brightness_set_stat,
.blink_set = dx010_led_blink_stat,
.flags = LED_CORE_SUSPENDRESUME,
},
};
static struct resource dx010_led_resources[] = {
{
.flags = IORESOURCE_IO,
},
};
static void dx010_led_dev_release( struct device * dev)
{
return;
}
static struct platform_device dx010_lpc_dev = {
.name = DRIVER_NAME,
.id = -1,
.num_resources = ARRAY_SIZE(dx010_led_resources),
.resource = dx010_led_resources,
.dev = {
.release = dx010_led_dev_release,
}
};
static int dx010_led_drv_probe(struct platform_device *pdev)
{
int i, ret;
for (i = 0; i < ARRAY_SIZE(dx010_leds); i++) {
ret = led_classdev_register(&pdev->dev, &dx010_leds[i]);
if (ret < 0)
goto exit;
}
ret = device_create_file(&pdev->dev, &dev_attr_blink);
if (ret)
{
for (i = 0; i < ARRAY_SIZE(dx010_leds); i++)
led_classdev_unregister(&dx010_leds[i]);
}
exit:
return ret;
}
static int dx010_led_drv_remove(struct platform_device *pdev)
{
int i;
for (i = 0; i < ARRAY_SIZE(dx010_leds); i++)
led_classdev_unregister(&dx010_leds[i]);
device_remove_file(&pdev->dev, &dev_attr_blink);
return 0;
}
static struct platform_driver dx010_led_drv = {
.probe = dx010_led_drv_probe,
.remove = __exit_p(dx010_led_drv_remove),
.driver = {
.name = DRIVER_NAME,
},
};
int dx010_led_init(void)
{
platform_device_register(&dx010_lpc_dev);
platform_driver_register(&dx010_led_drv);
return 0;
}
void dx010_led_exit(void)
{
platform_driver_unregister(&dx010_led_drv);
platform_device_unregister(&dx010_lpc_dev);
}
module_init(dx010_led_init);
module_exit(dx010_led_exit);
MODULE_AUTHOR("Abhisit Sangjan <asang@celestica.com>");
MODULE_DESCRIPTION("Celestica SeaStone DX010 LEDs Front Panel Status Driver");
MODULE_LICENSE("GPL");