Re: [PATCH] drivers: PMC MSP71xx LED driver

From: Marc St-Jean
Date: Tue Mar 13 2007 - 13:42:49 EST




Florian Fainelli wrote:
> Hi Marc,
>
> Your patch does not seem to use the Linux LED API (include/linux/leds.h),
> which is sometimes pretty unknown, but dramatically ease your work.
> Maybe it
> is a good idea converting it to this API if you find it relevant.

Hi Florian,

Thanks for pointing out the API. We were aware of the API which appeared
when updating to 2.6.16 or 17. At some point if we require a userland
API we will need to look at how to integrate support for it.

Currently our driver has a different goal which is to support concurrent
control of the LEDs (over TWI and GPIO) from kernel and non-kernel code.
The non-kernel code here isn't userland code but an RTOS running on a
second CPU.

> Also consider the ongoing LED-GPIO API which is being written by ARM
> people :
> http://marc.theaimsgroup.com/?l=linux-kernel&m=110873454720555&w=2
> <http://marc.theaimsgroup.com/?l=linux-kernel&m=110873454720555&w=2>
>
> My 2 cents

I wasn't aware of this work, we'll need to look into it. If it can
be adapted to provide the architecture level atomicity need for
separate OSes, it we should be able to adapt to it once in the kernel
tree.

Thanks,
Marc

> Le lundi 12 mars 2007, Marc St-Jean a écrit :
> > [PATCH] drivers: PMC MSP71xx LED driver
> >
> > Patch to add LED driver for the PMC-Sierra MSP71xx devices.
> >
> > This patch references some platform support files previously
> > submitted to the linux-mips@xxxxxxxxxxxxxx list.
> >
> > Thanks,
> > Marc
> >
> > Signed-off-by: Marc St-Jean <Marc_St-Jean@xxxxxxxxxxxxxx>
> > ---
> > Re-posting patch with recommended changes:
> > -Cleanup on style and formatting for comments, macros, etc.
> > -Removed unnecessary memset.
> > -Removed unnecessary inlines.
> > -Moved some driver private data structures from .h to .c.
> > -Made use of schedule_timeout_interruptible() call instead
> > of multiple calls.
> > -Added calls to kthread_should_stop and try_to_freeze().
> >
> > drivers/i2c/chips/Kconfig | 9
> > drivers/i2c/chips/Makefile | 1
> > drivers/i2c/chips/pmctwiled.c | 524
> > +++++++++++++++++++
> include/asm-mips/pmc-sierra/msp71xx/msp_led_macros.h |
> > 273 +++++++++ 4 files changed, 807 insertions(+)
> >
> > diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig
> > index 87ee3ce..3bef46b 100644
> > --- a/drivers/i2c/chips/Kconfig
> > +++ b/drivers/i2c/chips/Kconfig
> > @@ -50,6 +50,15 @@ config SENSORS_PCF8574
> > These devices are hard to detect and rarely found on mainstream
> > hardware. If unsure, say N.
> >
> > +config SENSORS_PMCTWILED
> > + tristate "PMC Led-over-TWI driver"
> > + depends on I2C && PMC_MSP
> > + help
> > + The new VPE-safe backend driver for all the LEDs on the 7120
> platform.
> > +
> > + While you may build this as a module, it is recommended you
> build it
> > + into the kernel monolithic so all drivers may access it at
> all times.
> > +
> > config SENSORS_PCA9539
> > tristate "Philips PCA9539 16-bit I/O port"
> > depends on I2C && EXPERIMENTAL
> > diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile
> > index 779868e..4e79e27 100644
> > --- a/drivers/i2c/chips/Makefile
> > +++ b/drivers/i2c/chips/Makefile
> > @@ -10,6 +10,7 @@ obj-$(CONFIG_SENSORS_M41T00) += m41t00.o
> > obj-$(CONFIG_SENSORS_PCA9539) += pca9539.o
> > obj-$(CONFIG_SENSORS_PCF8574) += pcf8574.o
> > obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
> > +obj-$(CONFIG_SENSORS_PMCTWILED) += pmctwiled.o
> > obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o
> > obj-$(CONFIG_TPS65010) += tps65010.o
> >
> > diff --git a/drivers/i2c/chips/pmctwiled.c
> b/drivers/i2c/chips/pmctwiled.c
> > new file mode 100644
> > index 0000000..69845a5
> > --- /dev/null
> > +++ b/drivers/i2c/chips/pmctwiled.c
> > @@ -0,0 +1,524 @@
> > +/*
> > + * Special LED-over-TWI-PCA9554 driver for the PMC Sierra
> > + * Residential Gateway demo board (and potentially others).
> > + *
> > + * Based on pca9539.c Copyright (C) 2005 Ben Gardner
> <bgardner@xxxxxxxxxx>
> > + * Modified by Copyright 2006-2007 PMC-Sierra, Inc.
> > + *
> > + * 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; version 2 of the License.
> > + */
> > +
> > +#include <linux/module.h>
> > +#include <linux/init.h>
> > +#include <linux/kthread.h>
> > +#include <linux/i2c.h>
> > +#include <linux/freezer.h>
> > +
> > +#include <msp_led_macros.h>
> > +
> > +/*
> > + * The externally available "registers"
> > + *
> > + * TODO: We must ensure these are in "shared memory" for the other VPE
> > + * to access
> > + */
> > +u32 msp_led_register[MSP_LED_COUNT];
> > +
> > +#ifdef CONFIG_PMC_MSP7120_GW
> > +/*
> > + * For each device, which pins will be in "input" mode:
> > + * One byte per device:
> > + * 0 = Output
> > + * 1 = Input
> > + */
> > +static const u8 msp_led_initial_input_state[] = {
> > + /* No outputs on device 0 or 1, these are inputs only */
> > + MSP_LED_INPUT_MODE, MSP_LED_INPUT_MODE,
> > + /* All outputs on device 2 through 4 */
> > + MSP_LED_OUTPUT_MODE, MSP_LED_OUTPUT_MODE, MSP_LED_OUTPUT_MODE,
> > +};
> > +
> > +/*
> > + * For each device, which output pins should start on and off:
> > + * One byte per device:
> > + * 0 = OFF = HI
> > + * 1 = ON = Lo
> > + */
> > +static const u8 msp_led_initial_pin_state[] = {
> > + 0, 0, /* No initial state, these are input only */
> > + 1 << 1, /* PWR_GREEN LED on, all others off */
> > + 0, /* All off */
> > + 0, /* All off */
> > +};
> > +#endif /* CONFIG_PMC_MSP7120_GW */
> > +
> > +/* Internal polling data */
> > +#define POLL_PERIOD msecs_to_jiffies(125) /* Poll at 125ms */
> > +
> > +static struct i2c_client *pmctwiled_device[MSP_LED_NUM_DEVICES];
> > +static struct task_struct *pmctwiled_pollthread;
> > +static u32 private_msp_led_register[MSP_LED_COUNT];
> > +static u16 current_period;
> > +
> > +/* Addresses to scan */
> > +#define PMCTWILED_BASEADDRESS 0x38
> > +
> > +static unsigned short normal_i2c[] = {
> > + PMCTWILED_BASEADDRESS + 0,
> > + PMCTWILED_BASEADDRESS + 1,
> > + PMCTWILED_BASEADDRESS + 2,
> > + PMCTWILED_BASEADDRESS + 3,
> > + PMCTWILED_BASEADDRESS + 4,
> > + I2C_CLIENT_END
> > +};
> > +
> > +/* Insmod parameters */
> > +I2C_CLIENT_INSMOD_1(pmctwiled);
> > +
> > +enum pca9554_cmd {
> > + PCA9554_INPUT = 0,
> > + PCA9554_OUTPUT = 1,
> > + PCA9554_INVERT = 2,
> > + PCA9554_DIRECTION = 3,
> > +};
> > +
> > +static int pmctwiled_attach_adapter(struct i2c_adapter *adapter);
> > +static int pmctwiled_detect(struct i2c_adapter *adapter,
> > + int address, int kind);
> > +static int pmctwiled_detach_client(struct i2c_client *client);
> > +
> > +/* This is the driver that will be inserted */
> > +static struct i2c_driver pmctwiled_driver = {
> > + .driver = {
> > + .name = "pmctwiled",
> > + },
> > + .attach_adapter = pmctwiled_attach_adapter,
> > + .detach_client = pmctwiled_detach_client,
> > +};
> > +
> > +struct pmctwiled_data {
> > + struct i2c_client client;
> > +};
> > +
> > +static int pmctwiled_attach_adapter(struct i2c_adapter *adapter)
> > +{
> > + return i2c_probe(adapter, &addr_data, pmctwiled_detect);
> > +}
> > +
> > +/* This function is called by i2c_probe */
> > +static int pmctwiled_detect(struct i2c_adapter *adapter,
> > + int address, int kind)
> > +{
> > + struct i2c_client *new_client = NULL; /* client structure */
> > + struct pmctwiled_data *data = NULL; /* local data structure */
> > + int err = 0;
> > + int dev_id = address - PMCTWILED_BASEADDRESS;
> > +
> > + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
> > + goto exit;
> > +
> > + /*
> > + * For now, we presume we have a valid client. We now create the
> > + * client structure, even though we cannot fill it completely yet.
> > + */
> > + data = kzalloc(sizeof(*data), GFP_KERNEL);
> > + if (!data) {
> > + err = -ENOMEM;
> > + goto exit;
> > + }
> > +
> > + new_client = &data->client;
> > + i2c_set_clientdata(new_client, data);
> > + new_client->addr = address;
> > + new_client->adapter = adapter;
> > + new_client->driver = &pmctwiled_driver;
> > + new_client->flags = 0;
> > +
> > + /*
> > + * Detection:
> > + * The pca9554 only has 4 registers (0-3).
> > + * All other reads should fail.
> > + */
> > + if (i2c_smbus_read_byte_data(new_client, 3) < 0 ||
> > + i2c_smbus_read_byte_data(new_client, 4) >= 0)
> > + goto exit_kfree;
> > +
> > + /* Found PCA9554 (probably) */
> > + strlcpy(new_client->name, "pca9554", I2C_NAME_SIZE);
> > + printk(KERN_WARNING
> > + "Detected PCA9554 I/O chip (device %d) at 0x%02x\n",
> > + dev_id, address);
> > +
> > + /* Tell the I2C layer a new client has arrived */
> > + err = i2c_attach_client(new_client);
> > + if (err)
> > + goto exit_kfree;
> > +
> > + /*
> > + * Register this in the list of available devices, and set up the
> > + * initial state
> > + */
> > + i2c_smbus_write_byte_data(new_client, PCA9554_OUTPUT,
> > + i2c_smbus_read_byte_data(new_client,
> PCA9554_INPUT));
> > + i2c_smbus_write_byte_data(new_client, PCA9554_DIRECTION,
> > + msp_led_initial_input_state[dev_id]);
> > + pmctwiled_device[dev_id] = new_client;
> > +
> > + return 0;
> > +
> > +exit_kfree:
> > + kfree(data);
> > +exit:
> > + return err;
> > +}
> > +
> > +static int pmctwiled_detach_client(struct i2c_client *client)
> > +{
> > + int err;
> > + int dev_id = client->addr - PMCTWILED_BASEADDRESS;
> > +
> > + /* Clear reference so poll thread doesn't use while detaching */
> > + pmctwiled_device[dev_id] = NULL;
> > +
> > + /* Remove this device from the list of devices */
> > + err = i2c_detach_client(client);
> > + if (err)
> > + return err;
> > +
> > + kfree(i2c_get_clientdata(client));
> > +
> > + return 0;
> > +}
> > +
> > +/*
> > + * mode_bits_update - Sets the mode bits of the given led register
> > + * @led_reg_ptr: Pointer to the led register value that needs to be
> > updated + * with the given mode.
> > + * @mode: new mode value to be set to the register
> > + *
> > + * Sets the given mode value to the given led register.
> > + */
> > +inline void mode_bits_update(u32 *led_reg_ptr, enum msp_led_mode mode)
> > +{
> > + /* TODO: validate mode */
> > + *led_reg_ptr |= MSP_LED_MODE_MASK;
> > + *led_reg_ptr &= (~((u32) MSP_LED_MODE_MASK) | mode);
> > +}
> > +
> > +/*
> > + * sync_led_timer_with_polling_count - Synchronizes the led timer with
> > + * polling thread count
> > + * @led_id: Index for the private led register used to obtain timer
> > + * information
> > + * @led_reg_ptr: Pointer to the new led register value
> > + *
> > + * returns 0: If led is in its final period
> > + * returns 1: If led is in its initial period
> > + *
> > + * This function determines if the led timer is in its initial
> period or
> > + * the final, relative to the TWI-polling thread count (current_period)
> > + * and updates the previous timer value in the private led
> register. If
> > + * the state of the led needs to be turned off (i.e. when the led has
> > + * timed out) then the mode bits in the current led register pointer is
> > + * set to MSP_LED_OFF and the other data bits are set to 0.
> > + */
> > +int sync_led_timer_with_polling_count(int led_id, u32 *led_reg_ptr)
> > +{
> > + /* Timer variables */
> > + int is_in_initial_period = 0;
> > + u8 timer, led_timeout, initial_period, final_period;
> > + u16 total_period;
> > +
> > + /*
> > + * Determine the progress into the current cycle, relative to
> > + * the POLL_PERIOD
> > + */
> > + initial_period = *led_reg_ptr >> MSP_LED_INITIALPERIOD_SHIFT;
> > + final_period = *led_reg_ptr >> MSP_LED_FINALPERIOD_SHIFT;
> > + led_timeout = *led_reg_ptr >> MSP_LED_WATCHDOG_SHIFT;
> > + timer = private_msp_led_register[led_id] >>
> MSP_LED_WATCHDOG_SHIFT;
> > +
> > + total_period = initial_period + final_period;
> > + if (total_period != 0)
> > + is_in_initial_period = (current_period % total_period) <
> > + initial_period;
> > +
> > + /*
> > + * if the led_timeout is set, adjust the current state to be
> either
> > + * ON or OFF
> > + */
> > + if (led_timeout > 0) {
> > + if (timer >= led_timeout) {
> > + /* set the register to OFF state */
> > + mode_bits_update(led_reg_ptr, MSP_LED_OFF);
> > + timer = 0;
> > +
> > + /*
> > + * TODO:
> > + * This introduces a race condition with other
> > + * thread using shared memory and must be fixed.
> > + */
> > + msp_led_turn_off(led_id);
> > + } else
> > + timer += 1;
> > +
> > + /* update timer */
> > + *led_reg_ptr &= ~(0xff << MSP_LED_WATCHDOG_SHIFT);
> > + *led_reg_ptr |= (timer << MSP_LED_WATCHDOG_SHIFT);
> > + }
> > +
> > + return is_in_initial_period;
> > +}
> > +
> > +/*
> > + * led_update - Sets the mode bits of the given led register
> > + * @led_id - id pertaining to the led that needs update
> > + * @prev_direction_bits_ptr - points to the previous direction bits
> on the
> > bus + * @prev_data_bits_ptr - points to the previous data bits on the
> bus +
> > * @curr_direction_bits_ptr - points to the new direction bits for bus
> > update + * @curr_data_bits_ptr - points to the new data bits for bus
> update
> > + *
> > + * returns 1 - led is in output mode
> > + * 0 - led is in input mode and hasn't been updated
> > + *
> > + * Sets the given mode value to the given led register.
> > + */
> > +int led_update(int led_id,
> > + u8 *prev_direction_bits_ptr, u8 *prev_data_bits_ptr,
> > + u8 *curr_direction_bits_ptr, u8 *curr_data_bits_ptr)
> > +{
> > + u32 curr_led_reg;
> > + enum msp_led_mode curr_mode, prev_mode;
> > + enum msp_led_direction curr_direction, prev_direction;
> > + int is_in_initial_period;
> > +
> > + /* Read the shared memory into a temporary variable */
> > + int pin = led_id % MSP_LED_NUM_DEVICE_PINS;
> > + curr_led_reg = msp_led_register[led_id];
> > +
> > + /* Check if the input direction has changed to output */
> > + prev_direction = (enum msp_led_direction)
> > + ((private_msp_led_register[led_id] &
> > + MSP_LED_DIRECTION_MASK) >>
> MSP_LED_DIRECTION_SHIFT);
> > + curr_direction = (enum msp_led_direction)((curr_led_reg &
> > + MSP_LED_DIRECTION_MASK) >>
> MSP_LED_DIRECTION_SHIFT);
> > + if ((prev_direction == MSP_LED_INPUT) &&
> > + (curr_direction != MSP_LED_OUTPUT))
> > + return 0;
> > +
> > + /* get the previous mode of the LED */
> > + prev_mode = (enum msp_led_mode)(private_msp_led_register[led_id] &
> > + MSP_LED_MODE_MASK);
> > +
> > + if (prev_mode == MSP_LED_ON)
> > + *prev_data_bits_ptr |= 1 << pin;
> > +
> > + if (prev_direction == MSP_LED_INPUT)
> > + *prev_direction_bits_ptr |= 1 << pin;
> > +
> > + /* Update timer and obtain the current period */
> > + is_in_initial_period = sync_led_timer_with_polling_count(
> > + led_id, &curr_led_reg);
> > +
> > + /* get the current mode of the LED */
> > + curr_mode = (enum msp_led_mode)(curr_led_reg & MSP_LED_MODE_MASK);
> > +
> > + switch (curr_mode) {
> > + case MSP_LED_BLINK:
> > + if (is_in_initial_period) {
> > + *curr_data_bits_ptr |= 1 << pin;
> > + mode_bits_update(&curr_led_reg, MSP_LED_ON);
> > + } else
> > + mode_bits_update(&curr_led_reg,MSP_LED_OFF);
> > + break;
> > + case MSP_LED_BLINK_INVERT:
> > + if (!is_in_initial_period) {
> > + *curr_data_bits_ptr |= 1 << pin;
> > + mode_bits_update(&curr_led_reg, MSP_LED_ON);
> > + } else
> > + mode_bits_update(&curr_led_reg,MSP_LED_OFF);
> > + break;
> > + case MSP_LED_OFF:
> > + /*
> > + * Assuming that the led be turned off when set to
> > + * output mode
> > + */
> > + break;
> > + case MSP_LED_ON:
> > + *curr_data_bits_ptr |= 1 << pin;
> > + break;
> > + }
> > +
> > + if (curr_direction == MSP_LED_INPUT)
> > + *curr_direction_bits_ptr |= 1 << pin;
> > +
> > + /* save the current mode */
> > + private_msp_led_register[led_id] = curr_led_reg;
> > +
> > + return 1;
> > +}
> > +
> > +/*
> > + * device_update - Updates led device(s) on GPIO
> > + * @dev_id - id pertaining to the device that needs update
> > + *
> > + * returns 1 - device exists
> > + * 0 - device does not exist
> > + *
> > + * Every pin connected to the GPIO is updated if the state of the
> pin has
> > + * changed from its previous value stored in the memory register. A
> > temporary + * variable, curr_led_reg is used to store the current
> value of
> > the register + * corresponding to the pin under focus. curLedReg
> gets its
> > value from the + * global shared memory registers for leds. This
> value is
> > compared with the + * previous value to determine if a change to the led
> > pin is required. The + * previous values are stored in the private led
> > register,
> > + * private_msp_led_register.
> > + */
> > +int device_update(int dev_id)
> > +{
> > + int pin;
> > + u8 curr_direction_bits = 0;
> > + u8 curr_data_bits = 0;
> > + u8 prev_data_bits = 0;
> > + u8 prev_direction_bits = 0;
> > +
> > + /* if the device wasn't detected */
> > + if (pmctwiled_device[dev_id] == NULL)
> > + return 0;
> > +
> > + /* iterate through each pin of the device and update as
> necessary */
> > + for (pin = 0; pin < MSP_LED_NUM_DEVICE_PINS; pin++) {
> > + int led_id = MSP_LED_DEVPIN(dev_id, pin);
> > + led_update(led_id, &prev_direction_bits, &prev_data_bits,
> > + &curr_direction_bits, &curr_data_bits);
> > + }
> > +
> > + /*
> > + * BUS OPERATIONS: if the previous state is different from the
> > + * current state
> > + */
> > + if (curr_data_bits != prev_data_bits)
> > + i2c_smbus_write_byte_data(pmctwiled_device[dev_id],
> > + PCA9554_OUTPUT, ~(curr_data_bits));
> > +
> > + if (curr_direction_bits != prev_direction_bits)
> > + i2c_smbus_write_byte_data(pmctwiled_device[dev_id],
> > + PCA9554_DIRECTION, curr_direction_bits);
> > +
> > + return 1;
> > +}
> > +
> > +static int pmctwiled_poll(void *data)
> > +{
> > + current_period = 0;
> > +
> > + /* start the polling loop */
> > + do {
> > + /* Starting Time */
> > + unsigned long poll_end;
> > + unsigned long time_left;
> > + unsigned int poll_start = jiffies;
> > +
> > + /* update every device in here for the current period */
> > + int dev_id;
> > + for (dev_id = 0; dev_id < MSP_LED_NUM_DEVICES; dev_id++)
> > + device_update(dev_id);
> > +
> > + /* Ending Time */
> > + poll_end = jiffies;
> > + if (poll_end >= poll_start) {
> > + time_left = POLL_PERIOD - (poll_end - poll_start);
> > + } else {
> > + time_left = POLL_PERIOD - ((0xffffffff -
> poll_start) +
> > + poll_end);
> > + printk(KERN_WARNING
> > + "Warning: Delaying for %lu jiffies.
> This may "
> > + "not be correct because of clock
> wrapping\n",
> > + time_left);
> > + }
> > + if (time_left > POLL_PERIOD) {
> > + printk(KERN_WARNING
> > + "Warning: Delay of %lu jiffies
> requested, "
> > + "defaulting back to %lu\n",
> > + time_left, POLL_PERIOD);
> > + time_left = POLL_PERIOD;
> > + }
> > +
> > + /* reshedule next polling interval */
> > + schedule_timeout_interruptible(time_left);
> > +
> > + /* make swsusp happy with our thread */
> > + try_to_freeze();
> > +
> > + current_period++;
> > + } while (!kthread_should_stop());
> > +
> > + return 0;
> > +}
> > +
> > +void __init pmctwiled_setup(void)
> > +{
> > + static int called;
> > + int dev;
> > +
> > + /* check if already initialized from platform initialization */
> > + if (called)
> > + return;
> > +
> > + /* initialize LEDs to default state */
> > + for (dev = 0; dev < MSP_LED_NUM_DEVICES; dev++) {
> > + int pin;
> > + pmctwiled_device[dev] = NULL;
> > +
> > + for (pin = 0; pin < 8; pin++) {
> > + int led = MSP_LED_DEVPIN(dev, pin);
> > + if (msp_led_initial_input_state[dev] & (1 <<
> pin)) {
> > + msp_led_disable(led);
> > + } else {
> > + msp_led_enable(led);
> > + if (msp_led_initial_pin_state[dev] & (1
> << pin))
> > + msp_led_turn_on(led);
> > + else
> > + msp_led_turn_off(led);
> > + }
> > +
> > + /* Initialize the private led register memory */
> > + private_msp_led_register[led] = 0;
> > + }
> > + }
> > +
> > + /* indicate initialised */
> > + called++;
> > +}
> > +
> > +static int __init pmctwiled_init(void)
> > +{
> > + /* setup twi led interface */
> > + pmctwiled_setup();
> > +
> > + /* start the polling thread */
> > + pmctwiled_pollthread = kthread_run(pmctwiled_poll, NULL,
> > + "PMCTwiLedPoller");
> > + if (pmctwiled_pollthread == NULL) {
> > + printk(KERN_ERR "Could not start polling thread\n");
> > + return -ENOMEM;
> > + }
> > +
> > + return i2c_add_driver(&pmctwiled_driver);
> > +}
> > +
> > +static void __exit pmctwiled_exit(void)
> > +{
> > + /* stop the polling thread */
> > + kthread_stop(pmctwiled_pollthread);
> > +
> > + i2c_del_driver(&pmctwiled_driver);
> > +}
> > +
> > +MODULE_DESCRIPTION("PMC TWI-LED driver");
> > +MODULE_LICENSE("GPL");
> > +
> > +module_init(pmctwiled_init);
> > +module_exit(pmctwiled_exit);
> > diff --git a/include/asm-mips/pmc-sierra/msp71xx/msp_led_macros.h
> > b/include/asm-mips/pmc-sierra/msp71xx/msp_led_macros.h new file mode
> 100644
> > index 0000000..b5fb683
> > --- /dev/null
> > +++ b/include/asm-mips/pmc-sierra/msp71xx/msp_led_macros.h
> > @@ -0,0 +1,273 @@
> > +/*
> > + * Macros for external SMP-safe access to the PMC MSP7120
> > + * Residential Gateway demo board LEDs (over TWI)
> > + *
> > + * Copyright 2006-2007 PMC-Sierra, Inc.
> > + *
> > + * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR
> IMPLIED
> > + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
> WARRANTIES
> > OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
> > DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR
> ANY
> > DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
> > DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF
> SUBSTITUTE
> > GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS
> > INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY,
> WHETHER
> > IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR
> > OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE,
> EVEN IF
> > ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *
> > + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA.
> > + */
> > +
> > +#ifndef __MSP_LED_MACROS_H__
> > +#define __MSP_LED_MACROS_H__
> > +
> > +/* For ll/sc macros */
> > +#include <asm/regops.h>
> > +
> > +/* Generic macros for board setup */
> > +#define MSP_LED_DEVPIN(DEVICE, PIN) ((DEVICE * 8) + PIN)
> > +
> > +/* ----- Per-board configuration ----- */
> > +
> > +/* TODO: Maybe break this out into one file per board? */
> > +
> > +#ifdef CONFIG_PMC_MSP7120_GW
> > +/* Specific LEDs and PINs which can be controlled on the RG demo
> board: */
> > +#define MSP_LED_PWRSTANDBY_RED MSP_LED_DEVPIN(2, 0)
> > +#define MSP_LED_PWRSTANDBY_GREEN MSP_LED_DEVPIN(2, 1)
> > +#define MSP_LED_LAN1_10 MSP_LED_DEVPIN(2, 2)
> > +#define MSP_LED_LAN1_100 MSP_LED_DEVPIN(2, 3)
> > +#define MSP_LED_LAN2_10 MSP_LED_DEVPIN(2, 4)
> > +#define MSP_LED_LAN2_100 MSP_LED_DEVPIN(2, 5)
> > +#define MSP_LED_LAN3_10 MSP_LED_DEVPIN(2, 6)
> > +#define MSP_LED_LAN3_100 MSP_LED_DEVPIN(2, 7)
> > +#define MSP_LED_LAN4_10 MSP_LED_DEVPIN(3, 0)
> > +#define MSP_LED_LAN4_100 MSP_LED_DEVPIN(3, 1)
> > +#define MSP_LED_LAN5_10 MSP_LED_DEVPIN(3, 2)
> > +#define MSP_LED_LAN5_100 MSP_LED_DEVPIN(3, 3)
> > +#define MSP_LED_RFU_10 MSP_LED_LAN5_10
> > +#define MSP_LED_RFU_100 MSP_LED_LAN5_100
> > +#define MSP_PIN_FLASH_RESETB MSP_LED_DEVPIN(3, 4)
> > +#define MSP_LED_PSTN MSP_LED_DEVPIN(3, 5)
> > +#define MSP_PIN_FLASH_BANK MSP_LED_DEVPIN(3, 6)
> > +#define MSP_PIN_USB_HOST_DEV MSP_LED_DEVPIN(3, 7)
> > +#define MSP_LED_PHONE1 MSP_LED_DEVPIN(4, 0)
> > +#define MSP_LED_PHONE2 MSP_LED_DEVPIN(4, 1)
> > +#define MSP_LED_USB MSP_LED_DEVPIN(4, 2)
> > +#define MSP_LED_WIRELESS MSP_LED_DEVPIN(4, 3)
> > +#define MSP_LED_DSL_RED MSP_LED_DEVPIN(4, 4)
> > +#define MSP_LED_DSL_GREEN MSP_LED_DEVPIN(4, 5)
> > +#define MSP_LED_INTERNET_RED MSP_LED_DEVPIN(4, 6)
> > +#define MSP_LED_INTERNET_GREEN MSP_LED_DEVPIN(4, 7)
> > +
> > +#define MSP_LED_NUM_DEVICES 5
> > +#define MSP_LED_NUM_DEVICE_PINS 8
> > +#define MSP_LED_COUNT
> (MSP_LED_NUM_DEVICE_PINS * \
> > + MSP_LED_NUM_DEVICES)
> > +#define MSP_LED_INPUT_MODE 0xff
> > +#define MSP_LED_OUTPUT_MODE 0x00
> > +#endif /* CONFIG_PMC_MSP7120_GW */
> > +
> > +/* ----- End of Per-board configuration ----- */
> > +
> > +/* Definitions for LED blink rate value */
> > +#define MSP_LED_RATE_MAX 0xff
> > +
> > +/* -- The actual LED register list -- */
> > +extern u32 msp_led_register[];
> > +
> > +/*
> > + * Each 'register' has the following format:
> > + *
> > + * +-------+-----------------------------+
> > + * | BITS | DESCRIPTION |
> > + * +-------+-----------------------------+
> > + * | 31:24 | Watchdog timer |
> > + * | | Set to non-zero to start |
> > + * | | or to kick, this number |
> > + * | | will be decremented every |
> > + * | | 125ms, if it reaches zero |
> > + * | | the LED will be turned off|
> > + * +-------+-----------------------------+
> > + * | 23:16 | Initial Period |
> > + * | | 125ms increments |
> > + * +-------+-----------------------------+
> > + * | 15:8 | Final Period |
> > + * | | 125ms increments |
> > + * +-------+-----------------------------+
> > + * | 7:7 | Direction |
> > + * | | See msp_led_direction |
> > + * +-------+-----------------------------+
> > + * | 6:0 | Mode |
> > + * | | See msp_led_mode |
> > + * +-------+-----------------------------+
> > + *
> > + * NOTE: You should probably not affect these registers directly but
> use
> > + * the macros in this file. That said, if you need to touch them,
> be sure
> > + * to use ll/sc instructions (or the macros in regops.h) so that values
> > are + * preserved safely.
> > + */
> > +
> > +/* Direction modes */
> > +enum msp_led_direction {
> > + MSP_LED_INPUT = 0,
> > + MSP_LED_OUTPUT,
> > +};
> > +
> > +/* Output modes */
> > +enum msp_led_mode {
> > + MSP_LED_OFF = 0,/* Off steady */
> > + MSP_LED_ON, /* On steady */
> > + MSP_LED_BLINK, /* On for initial_period, off for final_period */
> > + MSP_LED_BLINK_INVERT,
> > + /* Off for initial_period, on for final_period */
> > +};
> > +
> > +#define MSP_LED_MODE_MASK 0x7f
> > +#define MSP_LED_DIRECTION_MASK 0x80
> > +#define MSP_LED_DIRECTION_SHIFT 7
> > +#define MSP_LED_INITIALPERIOD_SHIFT 8
> > +#define MSP_LED_FINALPERIOD_SHIFT 16
> > +#define MSP_LED_WATCHDOG_SHIFT 24
> > +
> > +/* -- Public API functions -- */
> > +
> > +/* Low-level macro, explicitly sets the specified LED with the
> values */
> > +static inline void msp_led_set_mode(u16 led,
> > + enum msp_led_mode mode, u8
> initial_period,
> > + u8 final_period, u8 watchdog_timeout)
> > +{
> > + set_value_reg32(&msp_led_register[led],
> > + 0xffffff7f,
> > + watchdog_timeout << MSP_LED_WATCHDOG_SHIFT |
> > + initial_period << MSP_LED_INITIALPERIOD_SHIFT |
> > + final_period << MSP_LED_FINALPERIOD_SHIFT |
> > + ((u8)mode & MSP_LED_MODE_MASK));
> > +}
> > +
> > +static inline void msp_led_set_direction(u16 led,
> > + enum msp_led_direction direction)
> > +{
> > + set_value_reg32(&msp_led_register[led],
> > + 0x00000080,
> > + ((u8)direction << MSP_LED_DIRECTION_SHIFT));
> > +}
> > +
> > +/* Turns the LED on */
> > +static inline void msp_led_turn_on(u16 led)
> > +{
> > + msp_led_set_mode(led, MSP_LED_ON, 0, 0, 0);
> > +}
> > +
> > +/* Set pin LO */
> > +static inline void msp_led_pin_lo(u16 pin)
> > +{
> > + msp_led_set_mode(pin, MSP_LED_ON, 0, 0, 0);
> > +}
> > +
> > +/* Turns the LED off */
> > +static inline void msp_led_turn_off(u16 led)
> > +{
> > + msp_led_set_mode(led, MSP_LED_OFF, 0, 0, 0);
> > +}
> > +
> > +/* Set pin HI */
> > +static inline void msp_led_pin_hi(u16 pin)
> > +{
> > + msp_led_set_mode(pin, MSP_LED_OFF, 0, 0, 0);
> > +}
> > +
> > +/*
> > + * Blinks a single LED
> > + * Period is specified in 125ms chunks
> > + */
> > +static inline void msp_led_blink(u16 led, u8 initial_period, u8
> > final_period) +{
> > + msp_led_set_mode(led, MSP_LED_BLINK, initial_period,
> > + final_period, 0);
> > +}
> > +
> > +static inline void msp_led_blink_2Hz(u16 led)
> > +{
> > + msp_led_set_mode(led, MSP_LED_BLINK, 2, 2, 0);
> > +}
> > +
> > +static inline void msp_led_blink_4Hz(u16 led)
> > +{
> > + msp_led_set_mode(led, MSP_LED_BLINK, 1, 1, 0);
> > +}
> > +
> > +/*
> > + * Blinks one LED, then the other
> > + * Period is specified in 125ms chunks
> > + */
> > +static inline void msp_led_alternate(u16 led1, u16 led2,
> > + u8 led1_period, u8 led2_period)
> > +{
> > + msp_led_set_mode(led1, MSP_LED_BLINK, led1_period,
> > + led2_period, 0);
> > + msp_led_set_mode(led2, MSP_LED_BLINK_INVERT, led1_period,
> > + led2_period, 0);
> > +}
> > +
> > +/*
> > + * Stops both alternating LEDs from blinking, leaving 'on_led' on
> > + * and 'off_led' off.
> > + */
> > +static inline void msp_led_alternate_stop(u16 on_led, u16 off_led)
> > +{
> > + msp_led_turn_on(on_led);
> > + msp_led_turn_off(off_led);
> > +}
> > +
> > +/*
> > + * Starts the LED blinking at the specified rate until the
> > watchdog_timeout + * (specified in 125ms increments) expires, when
> the LED
> > is turned off. + *
> > + * This can also be used to kick the watchdog.
> > + *
> > + * Calling any other 'msp_led_...' macro will disable the watchdog,
> > + * as will kicking this watchdog with a watchdogtimeout value of 0.
> > + * When the watchdog is disabled, the LED will blink forever.
> > + */
> > +static inline void msp_led_watchdog_init(u16 led, u8 initial_period,
> > + u8 final_period, u8
> watchdog_timeout)
> > +{
> > + msp_led_set_mode(led, MSP_LED_BLINK, initial_period,
> > + final_period, watchdog_timeout);
> > +}
> > +
> > +/*
> > + * Kicks a 'watchdog' LED. If the LED is already blinking or on,
> > + * it will start the watchdog countdown. If the LED is already off or
> > + * the wathdog timeout given is 0, it will ensure the LED is off and
> > + * the watchdog timer has stopped.
> > + */
> > +static inline void msp_led_watchdog_kick(u16 led, u8 watchdog_timeout)
> > +{
> > + set_value_reg32(&msp_led_register[led],
> > + 0xff << MSP_LED_WATCHDOG_SHIFT,
> > + watchdog_timeout << MSP_LED_WATCHDOG_SHIFT);
> > +}
> > +
> > +/*
> > + * Set the direction of the led pins.
> > + */
> > +static inline void msp_led_enable(u16 led)
> > +{
> > + msp_led_set_direction(led, MSP_LED_OUTPUT);
> > +}
> > +
> > +static inline void msp_led_disable(u16 led)
> > +{
> > + msp_led_set_direction(led, MSP_LED_INPUT);
> > +}
> > +
> > +#endif /* !__MSP_LED_MACROS_H__ */
>
>
>
> --
> Cordialement, Florian Fainelli
> ---------------------------------------------
>
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/