* Re: FW: LED driver
2005-03-17 5:15 FW: LED driver srinivas.surabhi
2005-03-17 6:15 ` akash kaul
@ 2005-03-17 9:46 ` Hans Schillstrom
2005-03-17 11:35 ` Wolfgang Denk
1 sibling, 1 reply; 4+ messages in thread
From: Hans Schillstrom @ 2005-03-17 9:46 UTC (permalink / raw)
To: srinivas.surabhi; +Cc: linuxppc-embedded
[-- Attachment #1: Type: text/plain, Size: 1864 bytes --]
Hi,
I have made a driver for our mpc8270 board
It is based upon Denx led_driver and made for 2.6 kernel
(See atached file)
To control the leds you have to perfom some ioctl's
first create your devices
mknod led0 c 151 0 ...
Example howto use it:
void led_off( int file )
{
if (ioctl(file,STATUSLED_SET,0) < 0)
perror("led off: ");
}
void led_blink( int file )
{
if (ioctl(file,STATUSLED_SET,1) < 0)
perror("led blink: ");
}
void led_on( int file )
{
if (ioctl(file,STATUSLED_SET,2) < 0)
perror("led on: ");
}
void led_per( int file, int period )
{
if (ioctl(file,STATUSLED_PERIOD,period) < 0)
perror("led on: ");
}
...
int led0 = open("/dev/led0",O_RDWR)
...
led_blink(led0); /* turn on green LED */
..
/Hans
On Thu, 2005-03-17 at 06:15, srinivas.surabhi@wipro.com wrote:
>
> -----Original Message-----
> From: Srinivas Surabhi (WT01 - TELECOM SOLUTIONS)
> Sent: Thursday, March 17, 2005 7:53 AM
> To: linuxppc-embedded@ozlabs.org
> Subject: LED driver
>
> Hi,
>
> I am working on the MPC8270 board having connected GPIO 3 lines
> connected to 3 LEDs. So can any one help me in finding out the best
> solution of controlling the LEDs from MVlinux (kernel and user space) or
> any pointers who has done it before.
>
> Thanks & Rgds
> SS
>
>
>
>
> Confidentiality Notice
>
> The information contained in this electronic message and any attachments to this message are intended
> for the exclusive use of the addressee(s) and may contain confidential or privileged information. If
> you are not the intended recipient, please notify the sender at Wipro or Mailadmin@wipro.com immediately
> and destroy all copies of this message and any attachments.
> _______________________________________________
> Linuxppc-embedded mailing list
> Linuxppc-embedded@ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-embedded
>
[-- Attachment #2: rcb8270_led.c --]
[-- Type: text/x-c, Size: 17175 bytes --]
/******************************************************************************
*
* (C) Copyright 2004
* LGP Allgon AB
* Hans Schillstrom, hans.schillstrom@lgpallgon.com
*
* Based upon DENX status_led.c
*
* This Device driver handles LED blinks etc., Relay outputs and Alarm inputs
*
******************************************************************************/
/*
* Standard in kernel modules
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/devfs_fs_kernel.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/poll.h>
#include <linux/spinlock.h>
#include <asm/types.h>
#include <asm/uaccess.h>
#include <linux/interrupt.h>
#include <asm/immap_cpm2.h>
#include <asm/cpm2.h>
#include <asm/mpc8260.h>
#undef DEBUG
#ifdef DEBUG
# define debugk(fmt,args...) printk(fmt ,##args)
#else
# define debugk(fmt,args...)
#endif
/*
* Deal with CONFIG_MODVERSIONS
*/
#if CONFIG_MODVERSIONS==1
/* # define MODVERSIONS */
# include <linux/modversions.h>
#endif
/*
* For character devices
*/
#include <linux/fs.h> /* character device definitions */
/* ioctl's */
#define STATUSLED_GET _IOR('L', 1, unsigned long)
#define STATUSLED_SET _IOW('L', 2, unsigned long)
#define STATUSLED_PERIOD _IOW('L', 3, unsigned long)
#define STATUS_SWITCH_GET _IOR('L', 4, unsigned long)
#define STATUS_RESET_GET _IOR('L', 5, unsigned long)
#define STATUS_RESET_SET _IOW('L', 6, unsigned long)
#define STATUS_LED_PAR im_ioport.iop_pparc
#define STATUS_LED_DIR im_ioport.iop_pdirc
#define STATUS_LED_DAT im_ioport.iop_pdatc
#define STATUS_LED_OFF 0
#define STATUS_LED_BLINKING 1
#define STATUS_LED_ON 2
#define STATUS_RELAY_BIT0 0x00000008 /* RELAY 1 bit 28 */
#define STATUS_RELAY_PERIOD0 (HZ)
#define STATUS_RELAY_STATE0 STATUS_LED_OFF
#define STATUS_RELAY_BIT1 0x10000040 /* RELAY 2 bit 25 */
#define STATUS_RELAY_PERIOD1 (HZ)
#define STATUS_RELAY_STATE1 STATUS_LED_OFF
#define STATUS_LED_BIT 0x20000000 /* LED 0 is on PC.2 OPERATE_LED */
#define STATUS_LED_PERIOD (HZ)
#define STATUS_LED_STATE STATUS_LED_BLINKING
#define STATUS_LED_BIT1 0x10000000 /* LED 1 is on PC.3 ALARM_LED */
#define STATUS_LED_PERIOD1 (HZ)
#define STATUS_LED_STATE1 STATUS_LED_ON
#define STATUS_LED_BIT2 0x08000000 /* LED 2 is on PC.4 BOOT_LED */
#define STATUS_LED_PERIOD2 (HZ)
#define STATUS_LED_STATE2 STATUS_LED_OFF
#define STATUS_LED_ACTIVE 0 /* LED on for bit == 1 */
#define STATUS_LED_BOOT 2 /* LED 0 used for boot status */
#define PCMCIA_LED 0
#define STATUS_LED 1
#define FRONT_LED 2
#define LED_MAJOR 151 /* Reserverd for "Front panel LEDs" */
#define DRIVER_VERSION "$Revision: 1.0 $"
static void statusled_blink (unsigned long minor);
/*
* Device Declarations
* PB25 & PB28
*/
typedef struct {
uint iop_pdirx;
uint iop_pparx;
uint iop_psorx;
uint iop_podrx;
uint iop_pdatx;
char res1[12];
} iop ;
typedef struct {
unsigned long mask;
int state;
int period;
int flags;
int pol; // Polarity
iop *iptr;
struct timer_list timer;
} led_dev_t;
#define SL_FLAG_OPEN 1
led_dev_t led_dev[] = {
{ STATUS_LED_BIT,
STATUS_LED_STATE,
STATUS_LED_PERIOD,
0,
STATUS_LED_ACTIVE,
(iop*)&((cpm2_map_t *)IMAP_ADDR)->STATUS_LED_DIR,
{ function: statusled_blink, data: 0UL /* minor 0 */ },
},
#if defined(STATUS_LED_BIT1)
{ STATUS_LED_BIT1,
STATUS_LED_STATE1,
STATUS_LED_PERIOD1,
0,
STATUS_LED_ACTIVE,
(iop*)&((cpm2_map_t *)IMAP_ADDR)->im_ioport.iop_pdirc,
{ function: statusled_blink, data: 1UL /* minor 1 */ },
},
#endif /* STATUS_LED_BIT1 */
#if defined(STATUS_LED_BIT2)
{ STATUS_LED_BIT2,
STATUS_LED_STATE2,
STATUS_LED_PERIOD2,
0,
STATUS_LED_ACTIVE,
(iop*)&((cpm2_map_t *)IMAP_ADDR)->im_ioport.iop_pdirc,
{ function: statusled_blink, data: 2UL /* minor 2 */ },
},
#endif /* STATUS_RELAY_0 */
{ STATUS_RELAY_BIT0,
STATUS_RELAY_STATE0,
STATUS_RELAY_PERIOD0,
0,
1,
(iop*)&((cpm2_map_t *)IMAP_ADDR)->im_ioport.iop_pdirb,
{ function: statusled_blink, data: 2UL /* minor 2 */ },
},
{ STATUS_RELAY_BIT1,
STATUS_RELAY_STATE1,
STATUS_RELAY_PERIOD1,
0,
1,
(iop*)&((cpm2_map_t *)IMAP_ADDR)->im_ioport.iop_pdirb,
{ function: statusled_blink, data: 2UL /* minor 2 */ },
},
};
#define MAX_LED_DEV (sizeof(led_dev)/sizeof(led_dev_t))
/*
* On the IVM* we handle 2 additional devices with this driver:
* MAX_LED_DEV+0 => "Interlock Switch" and "Device Reset Monitor":
* Both are input pins, which are polled periodically,
* and the user can wait with select() for a status change.
* MAX_LED_DEV+1 => "Device Reset Enable", an output device we can set or reset.
*/
#if defined(CONFIG_IVMS8) || defined(CONFIG_IVML24)
# define MAX_IO_DEV 2
static int io_dev_flags[MAX_IO_DEV] = { 0, }; /* additional I/O devices */
#else
# define MAX_IO_DEV 0
#endif /* CONFIG_IVMS8, CONFIG_IVML24 */
static volatile cpm2_map_t *limmr = 0;
/*
* The name for our device, as it will appear in /proc/devices
*/
#define DEVICE_NAME "status_led"
/*
* Prototypes
*/
static int statusled_init (void) __init;
static int statusled_open(struct inode *, struct file *);
static int statusled_release(struct inode *, struct file *);
# if defined(CONFIG_IVMS8) || defined(CONFIG_IVML24)
static ssize_t statusled_read(struct file *, char *, size_t, loff_t *);
static unsigned int statusled_poll (struct file *, poll_table *);
# endif /* CONFIG_IVMS8, CONFIG_IVML24 */
int init_module(void);
void cleanup_module(void);
static ssize_t statusled_ioctl(struct inode *, struct file *,
unsigned int, unsigned long);
static struct file_operations statusled_fops = {
owner: THIS_MODULE,
open: statusled_open,
release: statusled_release,
ioctl: statusled_ioctl,
# if defined(CONFIG_IVMS8) || defined(CONFIG_IVML24)
read: statusled_read,
poll: statusled_poll,
# endif /* CONFIG_IVMS8, CONFIG_IVML24 */
};
static int Major;
/*
* Initialize the driver - Register the character device
*/
static int __init statusled_init (void)
{
int i;
if (!limmr) { /* init CPM ptr */
limmr = (volatile cpm2_map_t *)IMAP_ADDR;
}
/*
* Register the character device
*/
if ((i = register_chrdev(LED_MAJOR, DEVICE_NAME, &statusled_fops)) < 0) {
limmr = NULL;
printk("Unable to get major %d for status LED driver: rc=%d\n",
LED_MAJOR, i);
return (i);
}
Major = LED_MAJOR;
printk (KERN_INFO
"Status LED driver " DRIVER_VERSION " initialized\n");
for (i=0; i<MAX_LED_DEV; ++i) {
led_dev[i].iptr->iop_pparx &= ~(led_dev[i].mask);
#ifdef STATUS_LED_ODR
led_dev[i].iptr->iop_podrx &= ~(led_dev[i].mask);
#endif
if (led_dev[i].state == STATUS_LED_ON) {
if( led_dev[i].pol == 0 ) /* start with LED on */
led_dev[i].iptr->iop_pdatx &= ~(led_dev[i].mask);
else
led_dev[i].iptr->iop_pdatx |= led_dev[i].mask ;
} else {
if( led_dev[i].pol == 0 ) /* start with LED off */
led_dev[i].iptr->iop_pdatx |= led_dev[i].mask ;
else
led_dev[i].iptr->iop_pdatx &= ~(led_dev[i].mask);
}
led_dev[i].iptr->iop_pdirx |= led_dev[i].mask;
statusled_blink(i);
}
#if defined(CONFIG_IVMS8) || defined(CONFIG_IVML24)
/*
* We use the same function (originally for the ILOCK device only)
* to poll both the ILOCK and RESET_MON ports
*/
io_timer.function = status_io_poll;
io_timer.data = STATUS_ILOCK_PERIOD;
last_io_state = (char)status_io_test();
changed_io_state = 0;
init_timer(&io_timer);
io_timer.expires = jiffies + io_timer.data;
add_timer(&io_timer);
#endif /* CONFIG_IVMS8, CONFIG_IVML24 */
return (0);
}
/*
* called whenever a process attempts to open the device
*/
static int statusled_open (struct inode *inode, struct file *file)
{
int minor = MINOR(inode->i_rdev);
debugk ("statusled_open(%p,%p): minor %d\n", inode, file, minor);
/*
* Allow for MAX_LED_DEV status LEDs
* On IVMS8 and IVML24: allow for additional I/O devices
*/
if (minor >= MAX_LED_DEV + MAX_IO_DEV)
return (-ENXIO);
if (!limmr)
return (-ENODEV);
/*
* exclusive open only
*/
if (minor < MAX_LED_DEV) {
if (led_dev[minor].flags & SL_FLAG_OPEN)
return -EBUSY;
led_dev[minor].flags |= SL_FLAG_OPEN;
}
/*
* Make sure that the module isn't removed while
* the file is open by incrementing the usage count
*/
debugk ("LED_OPEN: minor %d dir=0x%x par=0x%x dat=0x%x\n",
minor,
limmr->STATUS_LED_DIR, limmr->STATUS_LED_PAR, limmr->STATUS_LED_DAT);
return 0;
}
/*
* Called when a process closes the device.
* Doesn't have a return value in version 2.0.x because it can't fail,
* but in version 2.2.x it is allowed to fail
*/
static int statusled_release (struct inode *inode, struct file *file)
{
int minor = MINOR(inode->i_rdev);
debugk ("statusled_release(%p,%p)\n", inode, file);
if (!limmr)
return (-ENODEV);
/* We're now ready for our next caller */
if (minor < MAX_LED_DEV) {
led_dev[minor].flags &= ~SL_FLAG_OPEN;
}
debugk ("LED_CLOSE: dir=0x%x par=0x%x dat=0x%x\n",
limmr->STATUS_LED_DIR, limmr->STATUS_LED_PAR, limmr->STATUS_LED_DAT);
return 0;
}
#if defined(CONFIG_IVMS8) || defined(CONFIG_IVML24)
/*
* read entry point:
* Only supported for interlock switch (return ENXIO for LED devices).
* For the ilock switch, block until the next status change happens;
* then return exactly one byte containing the current state
* (0x00 switch open, 0x01 switch closed).
*/
static ssize_t statusled_read (struct file *file,
char *buf, size_t count, loff_t *ppos)
{
DECLARE_WAITQUEUE(wait, current);
int minor = MINOR(file->f_dentry->d_inode->i_rdev);
int rc = 0;
char c;
if (minor != MAX_LED_DEV)
return (-ENXIO);
add_wait_queue(&statusled_wait, &wait);
current->state = TASK_INTERRUPTIBLE;
for (;;) {
if (changed_io_state)
break;
if (file->f_flags & O_NONBLOCK) {
rc = -EAGAIN;
goto OUT;
}
if (signal_pending(current)) {
rc = -ERESTARTSYS;
goto OUT;
}
schedule ();
}
spin_lock (&statusled_lock);
c = (char)status_io_test ();
changed_io_state = 0;
spin_unlock (&statusled_lock);
/* Copy out */
if ((rc = verify_area(VERIFY_WRITE, buf, 1)) != 0) {
goto OUT;
}
rc = 1; copy_to_user((void *)buf, (void*)(&c), rc);
OUT:
current->state = TASK_RUNNING;
remove_wait_queue(&statusled_wait, &wait);
return (rc);
}
static unsigned int statusled_poll (struct file *file, poll_table *wait)
{
poll_wait(file, &statusled_wait, wait);
if (changed_io_state)
return POLLIN | POLLRDNORM;
return 0;
}
#endif /* CONFIG_IVMS8, CONFIG_IVML24 */
/*
* ioctl entry point:
*/
static ssize_t statusled_ioctl (
struct inode *inode,
struct file *file,
unsigned int cmd,
unsigned long arg)
{
int minor = MINOR(inode->i_rdev);
int n;
if (!limmr)
return (-ENODEV);
debugk ("IOCTL: minor=%d, cmd=%s, arg=%ld\n",
minor,
(cmd == STATUSLED_GET ) ? "STATUSLED_GET" :
(cmd == STATUSLED_SET ) ? "STATUSLED_SET" :
(cmd == STATUSLED_PERIOD) ? "STATUSLED_PERIOD" :
"<unknown>",
arg);
if (minor < MAX_LED_DEV) {
switch (cmd) {
case STATUSLED_GET:
switch (led_dev[minor].state) {
case STATUS_LED_OFF: n = 0;
break;
case STATUS_LED_ON: n = -1;
break;
default: n = led_dev[minor].period;
break;
}
return (copy_to_user((int *)arg, &n, sizeof (int)));
case STATUSLED_SET:
switch (arg) {
case STATUS_LED_OFF:
debugk ("IOCTL: was %d, set %ld=OFF\n",
led_dev[minor].state, arg);
if (led_dev[minor].state == STATUS_LED_BLINKING)
del_timer(&led_dev[minor].timer);
led_dev[minor].state = arg;
if( led_dev[minor].pol == 0 )
led_dev[minor].iptr->iop_pdatx |= led_dev[minor].mask ;
else
led_dev[minor].iptr->iop_pdatx &= ~(led_dev[minor].mask);
return (0);
case STATUS_LED_ON:
debugk ("IOCTL: was %d, set %ld=ON\n",
led_dev[minor].state, arg);
if (led_dev[minor].state == STATUS_LED_BLINKING)
del_timer(&led_dev[minor].timer);
led_dev[minor].state = arg;
if( led_dev[minor].pol == 0 )
led_dev[minor].iptr->iop_pdatx &= ~(led_dev[minor].mask);
else
led_dev[minor].iptr->iop_pdatx |= led_dev[minor].mask ;
return (0);
case STATUS_LED_BLINKING:
debugk ("IOCTL: was %d, set %ld=BLINKING\n",
led_dev[minor].state, arg);
if (led_dev[minor].state == STATUS_LED_BLINKING)
return (0);
led_dev[minor].state = arg; /* must come first! */
/* start blinking */
statusled_blink (minor);
return (0);
}
return (-EINVAL);
case STATUSLED_PERIOD:
led_dev[minor].period = arg;
debugk ("IOCTL: set PERIOD=%d\n", arg);
return (0);
}
}
#if defined(CONFIG_IVMS8) || defined(CONFIG_IVML24)
else {
int m = minor - MAX_LED_DEV;
switch (m) {
case 0: /* Interlock switch and device reset monitor */
if (cmd == STATUS_SWITCH_GET) {
n = status_io_test ();
return (copy_to_user((int *)arg, &n, sizeof (int)));
}
break;
case 1: /* Device reset enable output */
if (cmd == STATUS_RESET_GET) {
if (limmr->im_ioport.iop_pcdat & STATUS_RESET_ENA) {
n = 1;
} else {
n = 0;
}
return (copy_to_user((int *)arg, &n, sizeof (int)));
}
if (cmd == STATUS_RESET_SET) {
if (arg == 0) {
limmr->im_ioport.iop_pcdat &= ~STATUS_RESET_ENA;
} else {
limmr->im_ioport.iop_pcdat |= STATUS_RESET_ENA;
}
return (0);
}
break;
default:
break;
}
}
#endif
return (-EINVAL);
}
#if defined(CONFIG_IVMS8) || defined(CONFIG_IVML24)
/*
* Test status of interlock switch
*/
static int status_io_test (void)
{
int n = 0;
#if defined(CONFIG_IVMS8)
spin_lock (&statusled_lock);
if ((limmr->im_siu_conf.sc_sipend & STATUS_ILOCK_SWITCH) != 0)
n |= STATUS_ILOCK_BIT;
limmr->im_siu_conf.sc_sipend = STATUS_ILOCK_SWITCH;
spin_unlock (&statusled_lock);
#elif defined(CONFIG_IVML24)
if ((limmr->im_cpm.cp_pbdat & STATUS_ILOCK_SWITCH) != 0)
n |= STATUS_ILOCK_BIT;
#endif
if ((limmr->im_ioport.iop_pcdat & STATUS_RESET_MON) != 0)
n |= STATUS_RESET_MON_BIT;
return (n);
}
static void status_io_poll (unsigned long period)
{
char c;
spin_lock (&statusled_lock);
c = (char)status_io_test ();
if (c != last_io_state) {
last_io_state = c;
changed_io_state = 1;
}
spin_unlock (&statusled_lock);
if (changed_io_state)
wake_up_interruptible(&statusled_wait);
init_timer(&io_timer);
io_timer.expires = jiffies + io_timer.data;
add_timer(&io_timer);
}
#endif /* CONFIG_IVMS8, CONFIG_IVML24 */
/*
* Timer controlled blink entry point.
*
* We delete the timer when the status is changed to non-blinking
* or when the module is unloaded.
*/
static void statusled_blink (unsigned long minor)
{
unsigned long flags;
if (!limmr)
return;
if (led_dev[minor].state != STATUS_LED_BLINKING) {
/* don't change any more */
return;
}
limmr->STATUS_LED_DAT ^= led_dev[minor].mask;
save_flags(flags);
cli();
init_timer(&led_dev[minor].timer);
led_dev[minor].timer.expires = jiffies + led_dev[minor].period;
add_timer(&led_dev[minor].timer);
restore_flags(flags);
}
/******************************
**** Module Declarations *****
**************************** */
module_init (statusled_init);
#ifdef MODULE
/*
* Cleanup - unregister the driver
*/
void statusled_cleanup (void)
{
int minor, ret;
/*
* Cleanup timer
*/
for (minor=0; minor<MAX_LED_DEV; ++minor) {
if (led_dev[minor].state == STATUS_LED_BLINKING)
del_timer(&led_dev[minor].timer);
if( led_dev[minor].pol == 0 )
led_dev[minor].iptr->iop_pdatx |= led_dev[minor].mask ;
else
led_dev[minor].iptr->iop_pdatx &= ~(led_dev[minor].mask);
led_dev[minor].state = STATUS_LED_OFF;
}
#if defined(CONFIG_IVMS8) || defined(CONFIG_IVML24)
del_timer(&io_timer);
#endif /* CONFIG_IVMS8, CONFIG_IVML24 */
/*
* Unregister the device
*/
ret = unregister_chrdev (Major, DEVICE_NAME);
/*
* If there's an error, report it
*/
if (ret < 0) {
printk ("unregister_chrdev: error %d\n", ret);
}
}
module_exit (statusled_cleanup);
MODULE_DESCRIPTION("RCB8270 led & relay driver");
MODULE_AUTHOR("Hans Schillstrom LPG Allgon AB <hans.schillstrom@pwav.com>");
MODULE_LICENSE("GPL");
#endif /* MODULE */
^ permalink raw reply [flat|nested] 4+ messages in thread