linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] drivers: PMC MSP71xx LED driver
@ 2007-03-12 18:50 Marc St-Jean
  2007-03-13  8:33 ` Florian Fainelli
  0 siblings, 1 reply; 7+ messages in thread
From: Marc St-Jean @ 2007-03-12 18:50 UTC (permalink / raw)
  To: linux-kernel; +Cc: linux-mips

[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@linux-mips.org list.

Thanks,
Marc

Signed-off-by: Marc St-Jean <Marc_St-Jean@pmc-sierra.com>
---
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@wabtec.com>
+ * 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__ */

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH] drivers: PMC MSP71xx LED driver
  2007-03-12 18:50 [PATCH] drivers: PMC MSP71xx LED driver Marc St-Jean
@ 2007-03-13  8:33 ` Florian Fainelli
  0 siblings, 0 replies; 7+ messages in thread
From: Florian Fainelli @ 2007-03-13  8:33 UTC (permalink / raw)
  To: Marc St-Jean; +Cc: linux-kernel, linux-mips

[-- Attachment #1: Type: text/plain, Size: 29731 bytes --]

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.

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

My 2 cents

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@linux-mips.org list.
>
> Thanks,
> Marc
>
> Signed-off-by: Marc St-Jean <Marc_St-Jean@pmc-sierra.com>
> ---
> 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@wabtec.com>
> + * 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
---------------------------------------------

[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] drivers: PMC MSP71xx LED driver
@ 2007-03-13 17:42 Marc St-Jean
  0 siblings, 0 replies; 7+ messages in thread
From: Marc St-Jean @ 2007-03-13 17:42 UTC (permalink / raw)
  To: Florian Fainelli; +Cc: Marc St-Jean, linux-kernel, linux-mips



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@linux-mips.org list.
>  >
>  > Thanks,
>  > Marc
>  >
>  > Signed-off-by: Marc St-Jean <Marc_St-Jean@pmc-sierra.com>
>  > ---
>  > 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@wabtec.com>
>  > + * 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
> ---------------------------------------------
> 

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] drivers: PMC MSP71xx LED driver
  2007-03-09 22:20 Marc St-Jean
@ 2007-03-10  6:13 ` Andrew Morton
  0 siblings, 0 replies; 7+ messages in thread
From: Andrew Morton @ 2007-03-10  6:13 UTC (permalink / raw)
  To: Marc St-Jean; +Cc: stjeanma, linux-kernel, linux-mips

> On Fri, 9 Mar 2007 14:20:56 -0800  Marc St-Jean <Marc_St-Jean@pmc-sierra.com> wrote:
> 
> 
> >  > +typedef enum {
> >  > +     MSP_LED_INPUT = 0,
> >  > +     MSP_LED_OUTPUT,
> >  > +} msp_led_direction_t;
> > 
> > No typedefs, please.   Convert this to
> > 
> > enum msp_led_direction {
> >         ...
> > };
> 
> Alright I'll change it but it wasn't mentioned in the review of
> the previous drivers and they've been resubmitted with some.
> A quick search shows several drivers typedef'ing enums with and
> without *_t suffixes.

Poeple do bad things, and it sometimes gets missed.

> Is there a new style rule or are only core kernel types allowed to
> use _t?

The general rule is: no typedefs.

typedefs make sense when the underlying type can change: u64, size_t,
resource_size_t, etc.  We also use them for really hairy definitions like
filler_t.  Nothing else.  Please don't use them just to save typing.

> 
> >  > +/* Output modes */
> >  > +typedef enum {
> >  > +     MSP_LED_OFF = 0,                /* Off steady */
> >  > +     MSP_LED_ON,                             /* On steady */
> >  > +     MSP_LED_BLINK,                  /* On for initialPeriod, off 
> > for finalPeriod */
> >  > +     MSP_LED_BLINK_INVERT,   /* Off for initialPeriod, on for 
> > finalPeriod */
> >  > +} msp_led_mode_t;
> > 
> > Ditto.
> > 
> >  > +/* For non-LED pins, these macros set HI and LO accordingly */
> >  > +#define msp_led_pin_hi       msp_led_turn_off
> >  > +#define msp_led_pin_lo       msp_led_turn_on
> > 
> > eww.
> > 
> > static inline wrapper functions are preferred.  Write code in C, not cpp
> > where possible.
> 
> I agree the wrappers are cleaner but don't understand how this relates
> to C++.

cpp == C preprocessor.

It would be better to do

/*
 * nice comment goes here 
 */
static inline void msp_led_pin_hi(...)
{
	msp_led_turn_off(...);
}


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] drivers: PMC MSP71xx LED driver
@ 2007-03-09 22:20 Marc St-Jean
  2007-03-10  6:13 ` Andrew Morton
  0 siblings, 1 reply; 7+ messages in thread
From: Marc St-Jean @ 2007-03-09 22:20 UTC (permalink / raw)
  To: Andrew Morton; +Cc: Marc St-Jean, linux-kernel, linux-mips


Andrew Morton wrote:
>  > On Mon, 26 Feb 2007 17:48:55 -0600 Marc St-Jean 
> <stjeanma@pmc-sierra.com> wrote:
>  > [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@linux-mips.org list.

Thanks for the feedback Andrew, I've implemented your recommendations.
A few comments/answers below.

[...]

>  > +     /* determine the progress into the current cycle, relative to 
> the POLL_PERIOD */
>  > +     initialPeriod = (u8)(*ledRegPtr >> MSP_LED_INITIALPERIOD_SHIFT);
>  > +     finalPeriod = (u8)(*ledRegPtr >> MSP_LED_FINALPERIOD_SHIFT);
>  > +     ledTimeOut = (u8)(*ledRegPtr >> MSP_LED_WATCHDOG_SHIFT);
>  > +     timer = (u8)(private_msp_led_register[ledId] >> 
> MSP_LED_WATCHDOG_SHIFT);
> 
> I assume all these (u8) casts are unneeded.
> 
>  > +     totalPeriod = (u16)initialPeriod + (u16)finalPeriod;
> 
> And here.

I assume the author didn't expect the integer promotion to occur until
after the addition.

[...]

> 
>  > +{
>  > +     int pin;
>  > +     u8 currDirectionBits, currDataBits, prevDataBits, 
> prevDirectionBits;
>  > +     currDirectionBits = currDataBits = prevDataBits = 
> prevDirectionBits = 0;
> 
> The unneeded initialisations here are just to suppress the incorrect gcc
> warning, yes?

No, initialization is needed as they are passed by reference to functions
setting/clearing bits.

> If so, that should at least be comented.  And try to avoid declarations o
> this form as well as multiple assignments.  So you want:
> 
>         u8 curr_direction_bits = 0;     /* Suppress gcc warning */
>         u8 curr_data_bits = 0;          /* Suppress gcc warning */
>         u8 prev_data_bits = 0;          /* Suppress gcc warning */
>         u8 prev_direction_bits = 0;     /* Suppress gcc warning */
> 
> the initialisation does cause extra ode to be generated and we usually just
> let te warning come out.  I think later gcc's fixed it.

OK, I've split them on to separate line but without the comment.

[...]

> 
>  > +void __init pmctwiled_setup(void)
>  > +{
>  > +     static int called;
>  > +     int dev;
>  > +
>  > +     /* check if already initialized */
>  > +     if( called )
>  > +             return;
> 
> This cannot happen (can it?)

Yes it can happen. Platform code can call pmctwiled_setup (that's why
the function was written) before the pmctwiled_init function runs.
This is so various sub-system init functions can ensure initialization
has occurred before setting start-up values.

If you have an idea on a better way to accomplish this I'm all ears.


>  > +     /* 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 (mspLedInitialInputState[dev] & (1 << pin)) 
> {                               
>  > +                             msp_led_disable(led);
>  > +                     } else {
>  > +                             msp_led_enable(led);
>  > +                             if (mspLedInitialPinState[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++;
>  > +}

[...]

>  > +typedef enum {
>  > +     MSP_LED_INPUT = 0,
>  > +     MSP_LED_OUTPUT,
>  > +} msp_led_direction_t;
> 
> No typedefs, please.   Convert this to
> 
> enum msp_led_direction {
>         ...
> };

Alright I'll change it but it wasn't mentioned in the review of
the previous drivers and they've been resubmitted with some.
A quick search shows several drivers typedef'ing enums with and
without *_t suffixes.

Is there a new style rule or are only core kernel types allowed to
use _t?


>  > +/* Output modes */
>  > +typedef enum {
>  > +     MSP_LED_OFF = 0,                /* Off steady */
>  > +     MSP_LED_ON,                             /* On steady */
>  > +     MSP_LED_BLINK,                  /* On for initialPeriod, off 
> for finalPeriod */
>  > +     MSP_LED_BLINK_INVERT,   /* Off for initialPeriod, on for 
> finalPeriod */
>  > +} msp_led_mode_t;
> 
> Ditto.
> 
>  > +/* For non-LED pins, these macros set HI and LO accordingly */
>  > +#define msp_led_pin_hi       msp_led_turn_off
>  > +#define msp_led_pin_lo       msp_led_turn_on
> 
> eww.
> 
> static inline wrapper functions are preferred.  Write code in C, not cpp
> where possible.

I agree the wrappers are cleaner but don't understand how this relates
to C++.

Marc

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] drivers: PMC MSP71xx LED driver
  2007-02-26 23:48 Marc St-Jean
@ 2007-03-05 10:17 ` Andrew Morton
  0 siblings, 0 replies; 7+ messages in thread
From: Andrew Morton @ 2007-03-05 10:17 UTC (permalink / raw)
  To: Marc St-Jean; +Cc: linux-kernel, linux-mips

> On Mon, 26 Feb 2007 17:48:55 -0600 Marc St-Jean <stjeanma@pmc-sierra.com> wrote:
> [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@linux-mips.org list.
> 
> Thanks,
> @@ -0,0 +1,464 @@
> +/*
> +    Special LED-over-TWI-PCA9554 driver for PMC Sierra's Garibaldi
> +    (and potentially other) boards
> +
> +    Based on pca9539.c Copyright (C) 2005 Ben Gardner <bgardner@wabtec.com>
> +
> +    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 <msp_led_macros.h>
> +
> +#define POLL_PERIOD msecs_to_jiffies(125) /* Poll at 125ms */
> +
> +/* The externally available "registers" */
> +/* TODO: We must somehow ensure these are in "shared memory" for the other VPE
> + *       to access */
> +u32 _msp_led_register[MSP_LED_COUNT];
> +
> +/* Internal polling data */
> +static struct i2c_client *pmctwiled_device[MSP_LED_NUM_DEVICES];
> +static int pmctwiled_running;
> +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 devId = address - PMCTWILED_BASEADDRESS;
> +
> +	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
> +		goto exit;
> +
> +	/* OK. For now, we presume we have a valid client. We now create the
> +	   client structure, even though we cannot fill it completely yet. */
> +	if (!(data = kzalloc(sizeof(struct pmctwiled_data), GFP_KERNEL))) {

It is more robust to use kzalloc(sizeof(*data), ...) here.

> +		err = -ENOMEM;
> +		goto exit;
> +	}
> +	memset (data, 0x00, sizeof(*data));

As you did here.

This memset is unneeded.

> +	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 )

Previous coding style comments apply.

> +		goto exit_kfree;
> +
> +	/* Found PCA9554 (probably) */
> +	strlcpy(new_client->name, "pca9554", I2C_NAME_SIZE);
> +	printk( "Detected PCA9554 I/O chip (device %d) at 0x%02x\n",
> +			devId, address);
> +
> +	/* Tell the I2C layer a new client has arrived */
> +	if ((err = i2c_attach_client(new_client)))
> +		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,
> +			(u8)mspLedInitialInputState[devId] );
> +	pmctwiled_device[devId] = new_client;
> +	
> +	return 0;
> +
> +exit_kfree:
> +	kfree(data);
> +exit:
> +	return err;
> +}
> +
> +static int pmctwiled_detach_client( struct i2c_client *client )
> +{
> +	int err;
> +	int devId = client->addr - PMCTWILED_BASEADDRESS;
> +
> +	/* Clear reference so poll thread doesn't use while detaching */
> +	pmctwiled_device[devId] = NULL;
> +
> +	/* Remove this device from the list of devices */
> +	if ((err = i2c_detach_client(client)))
> +		return err;

Please avoid compounding operations in this manner.  We prefer

	err = i2c_detach_client(client);
	if (err)
		return err;

> +	kfree(i2c_get_clientdata(client));
> +	
> +	return 0;
> +}
>
> ...
>
> +inline void mode_bits_update( u32 *ledRegPtr, msp_led_mode_t mode )
> +{
> +	/* TODO: validate mode*/
> +	*ledRegPtr |= MSP_LED_MODE_MASK;
> +	*ledRegPtr &= (~((u32) MSP_LED_MODE_MASK) | mode);
> +}

Lose the StudlyCaps.   led_reg_ptr here.

> +/**
> + * sync_led_timer_with_polling_count - Synchronizes the led timer with polling thread
> + * 									   count
> + * @ledId: Index for the private led register used to obtain timer information
> + * @ledRegPtr: 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.         

This comment looks awful in an 80-column display.

> + **/
> +inline int sync_led_timer_with_polling_count( int ledId, u32 *ledRegPtr )

This non-static inline function will cause two copies to be generated: one
in the caller in this C file, one for external callers.

It's too large to inline: delete the `inline' keyword.

> +{
> +	/* Timer variables */
> +	int isInInitialPeriod = 0;
> +	u8 timer, ledTimeOut,initialPeriod, finalPeriod;
> +	u16 totalPeriod;

No StudlyCaps, please.

> +	/* determine the progress into the current cycle, relative to the POLL_PERIOD */
> +	initialPeriod = (u8)(*ledRegPtr >> MSP_LED_INITIALPERIOD_SHIFT);
> +	finalPeriod = (u8)(*ledRegPtr >> MSP_LED_FINALPERIOD_SHIFT);
> +	ledTimeOut = (u8)(*ledRegPtr >> MSP_LED_WATCHDOG_SHIFT);
> +	timer = (u8)(private_msp_led_register[ledId] >> MSP_LED_WATCHDOG_SHIFT);

I assume all these (u8) casts are unneeded.

> +	totalPeriod = (u16)initialPeriod + (u16)finalPeriod;

And here.

> +	if (totalPeriod != 0) {
> +		isInInitialPeriod = (current_period % totalPeriod) < initialPeriod;
> +	}
> +
> +	/* if the ledTimeOut is set, adjust the current state to be either ON or OFF */
> +	if (ledTimeOut > 0) {
> +		if (timer >= ledTimeOut) {
> +			/* set the register to OFF state */
> +			mode_bits_update(ledRegPtr,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(ledId);
> +		}
> +		else {
> +			timer += 1;
> +		}
> +		/* update timer */
> +		*ledRegPtr &= ~(0xff << MSP_LED_WATCHDOG_SHIFT);
> +		*ledRegPtr |= (timer << MSP_LED_WATCHDOG_SHIFT);
> +	}
> +	
> +	return isInInitialPeriod;
> +}
> +
> +/**
> + * led_update - Sets the mode bits of the given led register
> + * @ledId - id pertaining to the led that needs update
> + * @prevDirectionBitsPtr - points to the previous direction bits on the bus
> + * @prevDataBitsPtr - points to the previous data bits on the bus
> + * @currDirectionBitsPtr - points to the new direction bits for bus update
> + * @currDataBitsPtr - 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.
> + **/
> +inline int led_update( int ledId, u8 *prevDirectionBitsPtr, u8 *prevDataBitsPtr, 
> +						u8 *currDirectionBitsPtr, u8 *currDataBitsPtr )

80-cols.

Remove inline.

> +	u32 currLedReg;
> +	msp_led_mode_t currMode, prevMode;
> +	msp_led_direction_t currDirection, prevDirection;
> +	int isInInitialPeriod;
> +	
> +	/* Read the shared memory into a temporary variable */
> +	int pin = ledId % MSP_LED_NUM_DEVICE_PINS;
> +	currLedReg = _msp_led_register[ledId];
> +				
> +	/* Check if the input direction has changed to output */
> +	prevDirection = (msp_led_direction_t)
> +				((private_msp_led_register[ledId] & MSP_LED_DIRECTION_MASK) >> 
> +				   MSP_LED_DIRECTION_SHIFT);
> +	currDirection = (msp_led_direction_t)
> +				((currLedReg & MSP_LED_DIRECTION_MASK) >>
> +				   MSP_LED_DIRECTION_SHIFT);
> +	if ((prevDirection == MSP_LED_INPUT) && (currDirection != MSP_LED_OUTPUT))
> +		return 0;
> +
> +	/* get the previous mode of the LED */
> +	prevMode = (msp_led_mode_t)(private_msp_led_register[ledId] & MSP_LED_MODE_MASK);
> +
> +	if (prevMode == MSP_LED_ON)
> +		*prevDataBitsPtr |= (1 << pin);
> +
> +	if (prevDirection == MSP_LED_INPUT)
> +		*prevDirectionBitsPtr |= (1 << pin);
> +
> +
> +	/* Update timer and obtain the current period */
> +	isInInitialPeriod = sync_led_timer_with_polling_count(ledId, &currLedReg);
> +
> +	/* get the current mode of the LED */
> +	currMode = (msp_led_mode_t)(currLedReg & MSP_LED_MODE_MASK);
> +
> +	switch (currMode) {
> +		case MSP_LED_BLINK:
> +			if (isInInitialPeriod) {
> +				*currDataBitsPtr |= (1 << pin);
> +				mode_bits_update(&currLedReg, MSP_LED_ON);
> +			} else {
> +				mode_bits_update(&currLedReg,MSP_LED_OFF);
> +			}
> +			break;
> +		case MSP_LED_BLINK_INVERT:
> +			if (!isInInitialPeriod) {
> +				*currDataBitsPtr |= (1 << pin);
> +				mode_bits_update(&currLedReg, MSP_LED_ON);
> +			} else {
> +				mode_bits_update(&currLedReg,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:
> +			*currDataBitsPtr |= (1 << pin);
> +			break;
> +	}

Indent the switch body one tabstop to the left.

> +	if (currDirection == MSP_LED_INPUT)
> +		*currDirectionBitsPtr |= (1 << pin);
> +
> +	/* save the current mode */
> +	private_msp_led_register[ledId] = currLedReg;
> +	
> +	return 1;
> +}
> +
> +/**
> + * device_update - Updates led device(s) on GPIO
> + * @devId - 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,
> + * currLedReg 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.
> + * 
> + **/
> +inline int device_update( int devId )

Coding-style, StudlyCaps, remove inline

> +{
> +	int pin;
> +	u8 currDirectionBits, currDataBits, prevDataBits, prevDirectionBits;
> +	currDirectionBits = currDataBits = prevDataBits = prevDirectionBits = 0;

The unneeded initialisations here are just to suppress the incorrect gcc
warning, yes?

If so, that should at least be comented.  And try to avoid declarations o
this form as well as multiple assignments.  So you want:

	u8 curr_direction_bits = 0;	/* Suppress gcc warning */
	u8 curr_data_bits = 0;		/* Suppress gcc warning */
	u8 prev_data_bits = 0;		/* Suppress gcc warning */
	u8 prev_direction_bits = 0;	/* Suppress gcc warning */

the initialisation does cause extra ode to be generated and we usually just
let te warning come out.  I think later gcc's fixed it.

> +	/* if the device wasn't detected */
> +	if (pmctwiled_device[devId] == NULL)
> +		return 0;					
> +
> +	/* iterate through each pin of the device and update register as necessary */
> +	for (pin=0; pin < MSP_LED_NUM_DEVICE_PINS; pin++) {

Put a space either side of '='

> +		int ledId = MSP_LED_DEVPIN(devId, pin);	
> +		led_update(ledId, &prevDirectionBits, &prevDataBits,
> +					&currDirectionBits, &currDataBits);
> +	}
> +	
> +	/* BUS OPERATIONS: if the previous state is different from the current state */
> +	if (currDataBits != prevDataBits) {
> +		i2c_smbus_write_byte_data(
> +			pmctwiled_device[devId], PCA9554_OUTPUT,
> +			~(currDataBits) );
> +	}
> +	if (currDirectionBits != prevDirectionBits) {
> +		i2c_smbus_write_byte_data(
> +			pmctwiled_device[devId], PCA9554_DIRECTION,
> +			currDirectionBits );
> +	}
> +	
> +	return 1;
> +} 
> +
> +static int pmctwiled_poll( void *data )
> +{
> +	pmctwiled_running = 1;
> +	current_period = 0;
> +	
> +	/* start the polling loop */
> +	while( pmctwiled_running ) {
> +		/* Starting Time */
> +		unsigned long pollEnd;
> +		unsigned long timeLeft;
> +		unsigned int pollStart = jiffies;
> +		
> +		/* update every device in here for the current period */
> +		int devId;
> +		for (devId = 0; devId < MSP_LED_NUM_DEVICES; devId++)
> +			device_update(devId);
> +		
> +		/* Ending Time */
> +		pollEnd = jiffies;
> +		if (pollEnd >= pollStart) {
> +			timeLeft = POLL_PERIOD - (pollEnd - pollStart);
> +		} else {
> +			timeLeft = POLL_PERIOD - ((0xffffffff - pollStart) + pollEnd);
> +			printk( "Warning: Delaying for %lu jiffies.  This may not be correct because of clock wrapping\n", timeLeft );
> +		}
> +		if (timeLeft > POLL_PERIOD) {
> +			printk( "Warning: Delay of %lu jiffies requested, defaulting back to %lu\n", timeLeft, POLL_PERIOD );
> +			timeLeft = POLL_PERIOD;
> +		}
> +		
> +		/* reshedule next polling interval */
> +		set_current_state(TASK_INTERRUPTIBLE);
> +		schedule_timeout(timeLeft);
> +		current_period++;
> +	}
> +
> +	return 0;
> +}

Remove pmctwiled_running, use kthread_should_stop()

> +void __init pmctwiled_setup(void)
> +{
> +	static int called;
> +	int dev;
> +
> +	/* check if already initialized */
> +	if( called )
> +		return;

This cannot happen (can it?)

> +	/* 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 (mspLedInitialInputState[dev] & (1 << pin)) {				
> +				msp_led_disable(led);
> +			} else {
> +				msp_led_enable(led);
> +				if (mspLedInitialPinState[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++;
> +}
>
>
> +++ b/include/asm-mips/pmc-sierra/msp71xx/msp_led_macros.h
> @@ -0,0 +1,282 @@
> +/*
> + * $Id: msp_led_macros.h,v 1.3 2006/12/18 18:16:19 stjeanma Exp $

Please remove all CVS IDs from all patches.  Once this code hits mainline
they become meaningless.

> +static const u8 mspLedInitialInputState[] = {
> +	/* 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 mspLedInitialPinState[] = {
> +	0, 0, 	/* No initial state, these are input only */
> +	(1 << 1),	/* PWR_GREEN LED on, all others off */
> +	0,	/* All off */
> +	0,	/* All off */
> +};

Move these into a .c file.

> +typedef enum {
> +	MSP_LED_INPUT = 0,
> +	MSP_LED_OUTPUT,
> +} msp_led_direction_t;

No typedefs, please.   Convert this to

enum msp_led_direction {
	...
};

> +/* Output modes */
> +typedef enum {
> +	MSP_LED_OFF = 0,		/* Off steady */
> +	MSP_LED_ON,				/* On steady */
> +	MSP_LED_BLINK,			/* On for initialPeriod, off for finalPeriod */
> +	MSP_LED_BLINK_INVERT,	/* Off for initialPeriod, on for finalPeriod */
> +} msp_led_mode_t;

Ditto.

> +/* For non-LED pins, these macros set HI and LO accordingly */
> +#define msp_led_pin_hi	msp_led_turn_off
> +#define msp_led_pin_lo	msp_led_turn_on

eww.

static inline wrapper functions are preferred.  Write code in C, not cpp
where possible.



^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH] drivers: PMC MSP71xx LED driver
@ 2007-02-26 23:48 Marc St-Jean
  2007-03-05 10:17 ` Andrew Morton
  0 siblings, 1 reply; 7+ messages in thread
From: Marc St-Jean @ 2007-02-26 23:48 UTC (permalink / raw)
  To: linux-kernel; +Cc: linux-mips

[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@linux-mips.org list.

Thanks,
Marc

Signed-off-by: Marc St-Jean <Marc_St-Jean@pmc-sierra.com>
---
 drivers/i2c/chips/Kconfig                            |    9 
 drivers/i2c/chips/Makefile                           |    1 
 drivers/i2c/chips/pmctwiled.c                        |  464 +++++++++++++++++++
 include/asm-mips/pmc-sierra/msp71xx/msp_led_macros.h |  282 +++++++++++
 4 files changed, 756 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..66de608
--- /dev/null
+++ b/drivers/i2c/chips/pmctwiled.c
@@ -0,0 +1,464 @@
+/*
+    Special LED-over-TWI-PCA9554 driver for PMC Sierra's Garibaldi
+    (and potentially other) boards
+
+    Based on pca9539.c Copyright (C) 2005 Ben Gardner <bgardner@wabtec.com>
+
+    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 <msp_led_macros.h>
+
+#define POLL_PERIOD msecs_to_jiffies(125) /* Poll at 125ms */
+
+/* The externally available "registers" */
+/* TODO: We must somehow ensure these are in "shared memory" for the other VPE
+ *       to access */
+u32 _msp_led_register[MSP_LED_COUNT];
+
+/* Internal polling data */
+static struct i2c_client *pmctwiled_device[MSP_LED_NUM_DEVICES];
+static int pmctwiled_running;
+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 devId = address - PMCTWILED_BASEADDRESS;
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		goto exit;
+
+	/* OK. For now, we presume we have a valid client. We now create the
+	   client structure, even though we cannot fill it completely yet. */
+	if (!(data = kzalloc(sizeof(struct pmctwiled_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto exit;
+	}
+	memset (data, 0x00, sizeof(*data));
+	
+	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( "Detected PCA9554 I/O chip (device %d) at 0x%02x\n",
+			devId, address);
+
+	/* Tell the I2C layer a new client has arrived */
+	if ((err = i2c_attach_client(new_client)))
+		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,
+			(u8)mspLedInitialInputState[devId] );
+	pmctwiled_device[devId] = new_client;
+	
+	return 0;
+
+exit_kfree:
+	kfree(data);
+exit:
+	return err;
+}
+
+static int pmctwiled_detach_client( struct i2c_client *client )
+{
+	int err;
+	int devId = client->addr - PMCTWILED_BASEADDRESS;
+
+	/* Clear reference so poll thread doesn't use while detaching */
+	pmctwiled_device[devId] = NULL;
+
+	/* Remove this device from the list of devices */
+	if ((err = i2c_detach_client(client)))
+		return err;
+
+	kfree(i2c_get_clientdata(client));
+	
+	return 0;
+}
+
+/**
+ * mode_bits_update - Sets the mode bits of the given led register
+ * @ledRegPtr: 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 *ledRegPtr, msp_led_mode_t mode )
+{
+	/* TODO: validate mode*/
+	*ledRegPtr |= MSP_LED_MODE_MASK;
+	*ledRegPtr &= (~((u32) MSP_LED_MODE_MASK) | mode);
+}
+
+/**
+ * sync_led_timer_with_polling_count - Synchronizes the led timer with polling thread
+ * 									   count
+ * @ledId: Index for the private led register used to obtain timer information
+ * @ledRegPtr: 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.         
+ * 
+ **/
+inline int sync_led_timer_with_polling_count( int ledId, u32 *ledRegPtr )
+{
+	/* Timer variables */
+	int isInInitialPeriod = 0;
+	u8 timer, ledTimeOut,initialPeriod, finalPeriod;
+	u16 totalPeriod;
+
+	/* determine the progress into the current cycle, relative to the POLL_PERIOD */
+	initialPeriod = (u8)(*ledRegPtr >> MSP_LED_INITIALPERIOD_SHIFT);
+	finalPeriod = (u8)(*ledRegPtr >> MSP_LED_FINALPERIOD_SHIFT);
+	ledTimeOut = (u8)(*ledRegPtr >> MSP_LED_WATCHDOG_SHIFT);
+	timer = (u8)(private_msp_led_register[ledId] >> MSP_LED_WATCHDOG_SHIFT);
+
+	totalPeriod = (u16)initialPeriod + (u16)finalPeriod;
+	if (totalPeriod != 0) {
+		isInInitialPeriod = (current_period % totalPeriod) < initialPeriod;
+	}
+
+	/* if the ledTimeOut is set, adjust the current state to be either ON or OFF */
+	if (ledTimeOut > 0) {
+		if (timer >= ledTimeOut) {
+			/* set the register to OFF state */
+			mode_bits_update(ledRegPtr,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(ledId);
+		}
+		else {
+			timer += 1;
+		}
+		/* update timer */
+		*ledRegPtr &= ~(0xff << MSP_LED_WATCHDOG_SHIFT);
+		*ledRegPtr |= (timer << MSP_LED_WATCHDOG_SHIFT);
+	}
+	
+	return isInInitialPeriod;
+}
+
+/**
+ * led_update - Sets the mode bits of the given led register
+ * @ledId - id pertaining to the led that needs update
+ * @prevDirectionBitsPtr - points to the previous direction bits on the bus
+ * @prevDataBitsPtr - points to the previous data bits on the bus
+ * @currDirectionBitsPtr - points to the new direction bits for bus update
+ * @currDataBitsPtr - 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.
+ **/
+inline int led_update( int ledId, u8 *prevDirectionBitsPtr, u8 *prevDataBitsPtr, 
+						u8 *currDirectionBitsPtr, u8 *currDataBitsPtr )
+{
+	u32 currLedReg;
+	msp_led_mode_t currMode, prevMode;
+	msp_led_direction_t currDirection, prevDirection;
+	int isInInitialPeriod;
+	
+	/* Read the shared memory into a temporary variable */
+	int pin = ledId % MSP_LED_NUM_DEVICE_PINS;
+	currLedReg = _msp_led_register[ledId];
+				
+	/* Check if the input direction has changed to output */
+	prevDirection = (msp_led_direction_t)
+				((private_msp_led_register[ledId] & MSP_LED_DIRECTION_MASK) >> 
+				   MSP_LED_DIRECTION_SHIFT);
+	currDirection = (msp_led_direction_t)
+				((currLedReg & MSP_LED_DIRECTION_MASK) >>
+				   MSP_LED_DIRECTION_SHIFT);
+	if ((prevDirection == MSP_LED_INPUT) && (currDirection != MSP_LED_OUTPUT))
+		return 0;
+
+	/* get the previous mode of the LED */
+	prevMode = (msp_led_mode_t)(private_msp_led_register[ledId] & MSP_LED_MODE_MASK);
+
+	if (prevMode == MSP_LED_ON)
+		*prevDataBitsPtr |= (1 << pin);
+
+	if (prevDirection == MSP_LED_INPUT)
+		*prevDirectionBitsPtr |= (1 << pin);
+
+
+	/* Update timer and obtain the current period */
+	isInInitialPeriod = sync_led_timer_with_polling_count(ledId, &currLedReg);
+
+	/* get the current mode of the LED */
+	currMode = (msp_led_mode_t)(currLedReg & MSP_LED_MODE_MASK);
+
+	switch (currMode) {
+		case MSP_LED_BLINK:
+			if (isInInitialPeriod) {
+				*currDataBitsPtr |= (1 << pin);
+				mode_bits_update(&currLedReg, MSP_LED_ON);
+			} else {
+				mode_bits_update(&currLedReg,MSP_LED_OFF);
+			}
+			break;
+		case MSP_LED_BLINK_INVERT:
+			if (!isInInitialPeriod) {
+				*currDataBitsPtr |= (1 << pin);
+				mode_bits_update(&currLedReg, MSP_LED_ON);
+			} else {
+				mode_bits_update(&currLedReg,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:
+			*currDataBitsPtr |= (1 << pin);
+			break;
+	}
+
+	if (currDirection == MSP_LED_INPUT)
+		*currDirectionBitsPtr |= (1 << pin);
+
+	/* save the current mode */
+	private_msp_led_register[ledId] = currLedReg;
+	
+	return 1;
+}
+
+/**
+ * device_update - Updates led device(s) on GPIO
+ * @devId - 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,
+ * currLedReg 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.
+ * 
+ **/
+inline int device_update( int devId )
+{
+	int pin;
+	u8 currDirectionBits, currDataBits, prevDataBits, prevDirectionBits;
+	currDirectionBits = currDataBits = prevDataBits = prevDirectionBits = 0;
+	
+	/* if the device wasn't detected */
+	if (pmctwiled_device[devId] == NULL)
+		return 0;					
+
+	/* iterate through each pin of the device and update register as necessary */
+	for (pin=0; pin < MSP_LED_NUM_DEVICE_PINS; pin++) {
+		int ledId = MSP_LED_DEVPIN(devId, pin);	
+		led_update(ledId, &prevDirectionBits, &prevDataBits,
+					&currDirectionBits, &currDataBits);
+	}
+	
+	/* BUS OPERATIONS: if the previous state is different from the current state */
+	if (currDataBits != prevDataBits) {
+		i2c_smbus_write_byte_data(
+			pmctwiled_device[devId], PCA9554_OUTPUT,
+			~(currDataBits) );
+	}
+	if (currDirectionBits != prevDirectionBits) {
+		i2c_smbus_write_byte_data(
+			pmctwiled_device[devId], PCA9554_DIRECTION,
+			currDirectionBits );
+	}
+	
+	return 1;
+} 
+
+static int pmctwiled_poll( void *data )
+{
+	pmctwiled_running = 1;
+	current_period = 0;
+	
+	/* start the polling loop */
+	while( pmctwiled_running ) {
+		/* Starting Time */
+		unsigned long pollEnd;
+		unsigned long timeLeft;
+		unsigned int pollStart = jiffies;
+		
+		/* update every device in here for the current period */
+		int devId;
+		for (devId = 0; devId < MSP_LED_NUM_DEVICES; devId++)
+			device_update(devId);
+		
+		/* Ending Time */
+		pollEnd = jiffies;
+		if (pollEnd >= pollStart) {
+			timeLeft = POLL_PERIOD - (pollEnd - pollStart);
+		} else {
+			timeLeft = POLL_PERIOD - ((0xffffffff - pollStart) + pollEnd);
+			printk( "Warning: Delaying for %lu jiffies.  This may not be correct because of clock wrapping\n", timeLeft );
+		}
+		if (timeLeft > POLL_PERIOD) {
+			printk( "Warning: Delay of %lu jiffies requested, defaulting back to %lu\n", timeLeft, POLL_PERIOD );
+			timeLeft = POLL_PERIOD;
+		}
+		
+		/* reshedule next polling interval */
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(timeLeft);
+		current_period++;
+	}
+
+	return 0;
+}
+
+void __init pmctwiled_setup(void)
+{
+	static int called;
+	int dev;
+
+	/* check if already initialized */
+	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 (mspLedInitialInputState[dev] & (1 << pin)) {				
+				msp_led_disable(led);
+			} else {
+				msp_led_enable(led);
+				if (mspLedInitialPinState[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( "Could not start polling thread\n" );
+		return -ENOMEM;
+	}
+	
+	return i2c_add_driver( &pmctwiled_driver );
+}
+
+static void __exit pmctwiled_exit( void )
+{
+	/* stop the polling thread */
+	pmctwiled_running = 0;
+	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..7f4de2f
--- /dev/null
+++ b/include/asm-mips/pmc-sierra/msp71xx/msp_led_macros.h
@@ -0,0 +1,282 @@
+/*
+ * $Id: msp_led_macros.h,v 1.3 2006/12/18 18:16:19 stjeanma Exp $
+ *
+ * Macros for external SMP-safe access to the PMC Athena (MSP7120) reference
+ * board LEDs (over TWI)
+ *
+ * Copyright 2005 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 Garibaldi 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
+ 
+/* For each device, which pins will be in "input" mode:
+ * One byte per device:
+ *  0 = Output
+ *  1 = Input
+ */
+static const u8 mspLedInitialInputState[] = {
+	/* 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 mspLedInitialPinState[] = {
+	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 */
+
+/* ----- 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_t   |
+ * +-------+-----------------------------+
+ * |  6:0  | Mode                        |
+ * |       |   See msp_led_mode_t        |
+ * +-------+-----------------------------+
+ *
+ * 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 */
+typedef enum {
+	MSP_LED_INPUT = 0,
+	MSP_LED_OUTPUT,
+} msp_led_direction_t;
+
+/* Output modes */
+typedef enum {
+	MSP_LED_OFF = 0,		/* Off steady */
+	MSP_LED_ON,				/* On steady */
+	MSP_LED_BLINK,			/* On for initialPeriod, off for finalPeriod */
+	MSP_LED_BLINK_INVERT,	/* Off for initialPeriod, on for finalPeriod */
+} msp_led_mode_t;
+
+#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,
+					msp_led_mode_t mode, u8 initialPeriod,
+					u8 finalPeriod, u8 watchdogTimeout )
+{
+	set_value_reg32( &_msp_led_register[led],
+			0xffffff7f,
+			watchdogTimeout << MSP_LED_WATCHDOG_SHIFT |
+			initialPeriod << MSP_LED_INITIALPERIOD_SHIFT |
+			finalPeriod << MSP_LED_FINALPERIOD_SHIFT | 
+			((u8)mode & MSP_LED_MODE_MASK)
+		);
+}
+
+static inline void msp_led_set_direction( u16 led,
+					msp_led_direction_t 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 );
+}
+
+/* Turns the LED off */
+static inline void msp_led_turn_off( u16 led )
+{
+	msp_led_set_mode( led, MSP_LED_OFF, 0, 0, 0 );
+}
+
+/* For non-LED pins, these macros set HI and LO accordingly */
+#define msp_led_pin_hi	msp_led_turn_off
+#define msp_led_pin_lo	msp_led_turn_on
+
+/* Blinks a single LED
+ * Period is specified in 125ms chunks */
+static inline void msp_led_blink( u16 led, u8 initialPeriod, u8 finalPeriod )
+{
+	msp_led_set_mode( led, MSP_LED_BLINK, initialPeriod, 
+				finalPeriod, 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 led1Period, u8 led2Period )
+{
+	msp_led_set_mode( led1, MSP_LED_BLINK, led1Period, 
+				led2Period, 0 );
+	msp_led_set_mode( led2, MSP_LED_BLINK_INVERT, led1Period, 
+				led2Period, 0 );
+}
+
+/* Stops both alternating LEDs from blinking, leaving 'onLed' on and 'offLed'
+ * off */
+static inline void msp_led_alternate_stop( u16 onLed, u16 offLed )
+{
+	msp_led_turn_on( onLed );
+	msp_led_turn_off( offLed );
+}
+
+/* Starts the LED blinking at the specified rate until the watchdogTimeout
+ * (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 initialPeriod,
+					  u8 finalPeriod, u8 watchdogTimeout )
+{
+	msp_led_set_mode( led, MSP_LED_BLINK, initialPeriod, 
+				finalPeriod, watchdogTimeout );
+}
+
+/* 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 watchdogTimeout )
+{
+	set_value_reg32( &_msp_led_register[led],
+		0xff << MSP_LED_WATCHDOG_SHIFT,
+		watchdogTimeout << 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__ */

^ permalink raw reply related	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2007-03-13 17:42 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-03-12 18:50 [PATCH] drivers: PMC MSP71xx LED driver Marc St-Jean
2007-03-13  8:33 ` Florian Fainelli
  -- strict thread matches above, loose matches on Subject: below --
2007-03-13 17:42 Marc St-Jean
2007-03-09 22:20 Marc St-Jean
2007-03-10  6:13 ` Andrew Morton
2007-02-26 23:48 Marc St-Jean
2007-03-05 10:17 ` Andrew Morton

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).