summaryrefslogtreecommitdiffstats
path: root/arch/v850/kernel/rte_cb_leds.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/v850/kernel/rte_cb_leds.c')
-rw-r--r--arch/v850/kernel/rte_cb_leds.c138
1 files changed, 138 insertions, 0 deletions
diff --git a/arch/v850/kernel/rte_cb_leds.c b/arch/v850/kernel/rte_cb_leds.c
new file mode 100644
index 00000000000..b662ad83894
--- /dev/null
+++ b/arch/v850/kernel/rte_cb_leds.c
@@ -0,0 +1,138 @@
+/*
+ * include/asm-v850/rte_cb_leds.c -- Midas lab RTE-CB board LED device support
+ *
+ * Copyright (C) 2002,03 NEC Electronics Corporation
+ * Copyright (C) 2002,03 Miles Bader <miles@gnu.org>
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file COPYING in the main directory of this
+ * archive for more details.
+ *
+ * Written by Miles Bader <miles@gnu.org>
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+
+#include <asm/uaccess.h>
+
+#define LEDS_MINOR 169 /* Minor device number, using misc major. */
+
+/* The actual LED hardware is write-only, so we hold the contents here too. */
+static unsigned char leds_image[LED_NUM_DIGITS] = { 0 };
+
+/* Spinlock protecting the above leds. */
+static DEFINE_SPINLOCK(leds_lock);
+
+/* Common body of LED read/write functions, checks POS and LEN for
+ correctness, declares a variable using IMG_DECL, initialized pointing at
+ the POS position in the LED image buffer, and and iterates COPY_EXPR
+ until BUF is equal to the last buffer position; finally, sets LEN to be
+ the amount actually copied. IMG should be a variable declaration
+ (without an initializer or a terminating semicolon); POS, BUF, and LEN
+ should all be simple variables. */
+#define DO_LED_COPY(img_decl, pos, buf, len, copy_expr) \
+do { \
+ if (pos > LED_NUM_DIGITS) \
+ len = 0; \
+ else { \
+ if (pos + len > LED_NUM_DIGITS) \
+ len = LED_NUM_DIGITS - pos; \
+ \
+ if (len > 0) { \
+ int _flags; \
+ const char *_end = buf + len; \
+ img_decl = &leds_image[pos]; \
+ \
+ spin_lock_irqsave (leds_lock, _flags); \
+ do \
+ (copy_expr); \
+ while (buf != _end); \
+ spin_unlock_irqrestore (leds_lock, _flags); \
+ } \
+ } \
+} while (0)
+
+/* Read LEN bytes from LEDs at position POS, into BUF.
+ Returns actual amount read. */
+unsigned read_leds (unsigned pos, char *buf, unsigned len)
+{
+ DO_LED_COPY (const char *img, pos, buf, len, *buf++ = *img++);
+ return len;
+}
+
+/* Write LEN bytes to LEDs at position POS, from BUF.
+ Returns actual amount written. */
+unsigned write_leds (unsigned pos, const char *buf, unsigned len)
+{
+ /* We write the actual LED values backwards, because
+ increasing memory addresses reflect LEDs right-to-left. */
+ volatile char *led = &LED (LED_NUM_DIGITS - pos - 1);
+ /* We invert the value written to the hardware, because 1 = off,
+ and 0 = on. */
+ DO_LED_COPY (char *img, pos, buf, len,
+ *led-- = 0xFF ^ (*img++ = *buf++));
+ return len;
+}
+
+
+/* Device functions. */
+
+static ssize_t leds_dev_read (struct file *file, char *buf, size_t len,
+ loff_t *pos)
+{
+ char temp_buf[LED_NUM_DIGITS];
+ len = read_leds (*pos, temp_buf, len);
+ if (copy_to_user (buf, temp_buf, len))
+ return -EFAULT;
+ *pos += len;
+ return len;
+}
+
+static ssize_t leds_dev_write (struct file *file, const char *buf, size_t len,
+ loff_t *pos)
+{
+ char temp_buf[LED_NUM_DIGITS];
+ if (copy_from_user (temp_buf, buf, min_t(size_t, len, LED_NUM_DIGITS)))
+ return -EFAULT;
+ len = write_leds (*pos, temp_buf, len);
+ *pos += len;
+ return len;
+}
+
+static loff_t leds_dev_lseek (struct file *file, loff_t offs, int whence)
+{
+ if (whence == 1)
+ offs += file->f_pos; /* relative */
+ else if (whence == 2)
+ offs += LED_NUM_DIGITS; /* end-relative */
+
+ if (offs < 0 || offs > LED_NUM_DIGITS)
+ return -EINVAL;
+
+ file->f_pos = offs;
+
+ return 0;
+}
+
+static struct file_operations leds_fops = {
+ .read = leds_dev_read,
+ .write = leds_dev_write,
+ .llseek = leds_dev_lseek
+};
+
+static struct miscdevice leds_miscdev = {
+ .name = "leds",
+ .minor = LEDS_MINOR,
+ .fops = &leds_fops
+};
+
+int __init leds_dev_init (void)
+{
+ return misc_register (&leds_miscdev);
+}
+
+__initcall (leds_dev_init);