diff options
-rw-r--r-- | io-module/Makefile | 5 | ||||
-rw-r--r-- | io-module/mts_io.c | 1334 | ||||
-rw-r--r-- | io-module/mts_io.h | 7 | ||||
-rwxr-xr-x | io-tool/mts-io-sysfs | 241 | ||||
-rw-r--r-- | io-tool/mts-io-sysfs-inc.sh | 92 | ||||
-rwxr-xr-x | io-tool/sysfs-tests | 181 |
6 files changed, 1860 insertions, 0 deletions
diff --git a/io-module/Makefile b/io-module/Makefile new file mode 100644 index 0000000..c82afc5 --- /dev/null +++ b/io-module/Makefile @@ -0,0 +1,5 @@ +obj-m := mts_io.o + +clean: + rm -f *.ko + diff --git a/io-module/mts_io.c b/io-module/mts_io.c new file mode 100644 index 0000000..a10b6c3 --- /dev/null +++ b/io-module/mts_io.c @@ -0,0 +1,1334 @@ +/* + * IO Controller for the Sparkcell Platform + * + * Copyright (C) 2010 by Multi-Tech Systems + * + * Author: James Maki <jmaki@multitech.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; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +/* + * See arch/arm/mach-at91board-sam9g20ek.c for an alternate approach to + * setting LEDs (ek_leds) and adding platform devices and platform drivers. + * + */ +#include <linux/delay.h> +#include <linux/ioctl.h> +#include <linux/input.h> +#include <linux/cdev.h> +#include <linux/clk.h> +#include <linux/sched.h> +#include <linux/reboot.h> +#include <linux/uaccess.h> +#include <linux/gpio.h> +#include <linux/sched.h> +#include <linux/workqueue.h> +#include <linux/platform_device.h> +#include <linux/device.h> +#include <linux/bitops.h> + +#include "mts_io.h" + +#define DRIVER_VERSION "v0.2.0" +#define DRIVER_AUTHOR "James Maki <jmaki@multitech.com>" +#define DRIVER_DESC "IO Controller for the SparkCell Platform" +#define DRIVER_NAME "mts-io" + +#define PLATFORM_NAME "sparkcell" + +#if DEBUG +# define dbg(fmt, args...) printk(KERN_DEBUG __FILE__ ": " fmt "\n" , ##args) +#else +# define dbg(fmt, args...) do {} while (0) +#endif +#define error(fmt, args...) printk(KERN_ERR __FILE__ ": " fmt "\n" , ##args) +#define info(fmt, args...) printk(KERN_INFO fmt "\n" , ##args) + +#define BOARD_REVA 0 +#define BOARD_REVB 1 +#define BOARD_REVC 2 + +#define LED_CD_BIT BIT(0) +#define EXTSERIAL_RI_BIT BIT(1) +#define EXTSERIAL_DSR_BIT BIT(2) +#define CS0_BIT3 BIT(3) +#define LED_SIG1_BIT BIT(4) +#define LED_SIG2_BIT BIT(5) +#define LED_SIG3_BIT BIT(6) +#define EXTSERIAL_DCD_BIT BIT(7) + +#define GPIO_CS0 AT91_PIN_PB3 +#define GPIO_CS1 AT91_PIN_PC5 +#define GPIO_CS2 AT91_PIN_PC4 +#define GPIO_CS3 AT91_PIN_PC3 +#define GPIO_ENIO AT91_PIN_PC15 +#define GPIO_ETH0_ENABLED AT91_PIN_PB31 +#define GPIO_RADIO_RESET AT91_PIN_PB30 +#define GPIO_OPTRST AT91_PIN_PA22 +#define GPIO_LS_LED AT91_PIN_PC9 +#define GPIO_SERCLK AT91_PIN_PB2 +#define GPIO_SERDOUT AT91_PIN_PB1 +#define GPIO_SERDIN AT91_PIN_PB0 +#define GPIO_VER0 AT91_PIN_PA23 +#define GPIO_VER1 AT91_PIN_PA24 +#define GPIO_RSERSRC AT91_PIN_PC7 +#define GPIO_DBSERON AT91_PIN_PC6 +#define GPIO_DTR1 AT91_PIN_PC10 +#define GPIO_RXD1MON AT91_PIN_PC8 +#define GPIO_EXTIN0 AT91_PIN_PB13 +#define GPIO_EXTIN1 AT91_PIN_PB12 +#define GPIO_EXTIN2 AT91_PIN_PB11 +#define GPIO_EXTIN3 AT91_PIN_PB10 +#define GPIO_EXTIN4 AT91_PIN_PB9 +#define GPIO_EXTIN5 AT91_PIN_PB8 +#define GPIO_GPS_NINT2 AT91_PIN_PC2 +#define GPIO_STATUS_LED AT91_PIN_PA30 + +#define LED_STATUS_CONTROLLABLE 1 +#define LED_LS_CONTROLLABLE 0 + +enum { + LED_OFF, + LED_ON, + LED_FLASHING, +}; + +static u8 board_rev = BOARD_REVA; +static u8 cs0_bits = 0xFF; +static DEFINE_MUTEX(mts_io_mutex); +#if LED_STATUS_CONTROLLABLE +static int led_mode_status = LED_OFF; +#endif + +static pid_t reset_pid = -1; +static pid_t reset_count = 0; +static int reset_short_signal = SIGUSR1; +static int reset_long_signal = SIGUSR2; + + +#define BLINK_PER_SEC 8 +#define BLINK_INTERVAL (HZ / BLINK_PER_SEC) +#define RESET_HOLD_COUNT (BLINK_PER_SEC * 3) + +static void blink_callback(struct work_struct *ignored); + +static DECLARE_DELAYED_WORK(blink_work, blink_callback); + +static void blink_callback(struct work_struct *ignored) +{ + unsigned long reset_pressed = !at91_get_gpio_value(GPIO_OPTRST); + struct pid *vpid = NULL; + + mutex_lock(&mts_io_mutex); + + if (reset_pid > 0) { + vpid = find_vpid(reset_pid); + } + + if (vpid) { + if (reset_pressed) { + reset_count++; + } else if (reset_count > 0 && reset_count < RESET_HOLD_COUNT) { + kill_pid(vpid, reset_short_signal, 1); + reset_count = 0; + } + + if (reset_count >= RESET_HOLD_COUNT) { + reset_count = 0; + kill_pid(vpid, reset_long_signal, 1); + } + } else { + reset_count = 0; + } + +#if LED_STATUS_CONTROLLABLE + if (led_mode_status == LED_FLASHING) { + at91_set_gpio_value(GPIO_STATUS_LED, !at91_get_gpio_value(GPIO_STATUS_LED)); + } +#endif + + mutex_unlock(&mts_io_mutex); + + schedule_delayed_work(&blink_work, BLINK_INTERVAL); +} + +static void serial_writeb(u8 data, int cs) +{ + int i; + + if (board_rev == BOARD_REVA) { + if (cs == GPIO_CS0) { + cs = 2; + } else if (cs == GPIO_CS1) { + cs = 3; + } else if (cs == GPIO_CS2) { + cs = 4; + } else if (cs == GPIO_CS3) { + cs = 5; + } else { + return; + } + at91_set_gpio_value(GPIO_CS0, cs & 1); + at91_set_gpio_value(GPIO_CS1, cs & 2); + at91_set_gpio_value(GPIO_CS2, cs & 4); + } else { + at91_set_gpio_value(cs, 0); + } + + for (i = 7; i >= 0; i--) { + at91_set_gpio_value(GPIO_SERDOUT, data & (1 << i)); + at91_set_gpio_value(GPIO_SERCLK, 1); + at91_set_gpio_value(GPIO_SERCLK, 0); + } + + if (board_rev == BOARD_REVA) { + at91_set_gpio_value(GPIO_CS0, 1); + at91_set_gpio_value(GPIO_CS1, 1); + at91_set_gpio_value(GPIO_CS2, 1); + } else { + at91_set_gpio_value(cs, 1); + } +} + +static u16 serial_readw(int cs) +{ + int i; + u16 data; + + at91_set_gpio_value(GPIO_SERCLK, 1); + + /* set low so we don't power down temp sensor */ + at91_set_gpio_value(GPIO_SERDOUT, 0); + + if (board_rev == BOARD_REVA) { + if (cs == GPIO_CS0) { + cs = 2; + } else if (cs == GPIO_CS1) { + cs = 3; + } else if (cs == GPIO_CS2) { + cs = 4; + } else if (cs == GPIO_CS3) { + cs = 5; + } else { + cs = 2; + } + at91_set_gpio_value(GPIO_CS0, cs & 1); + at91_set_gpio_value(GPIO_CS1, cs & 2); + at91_set_gpio_value(GPIO_CS2, cs & 4); + } else { + at91_set_gpio_value(cs, 0); + } + + data = 0; + for (i = 15; i >= 0; i--) { + at91_set_gpio_value(GPIO_SERCLK, 0); + at91_set_gpio_value(GPIO_SERCLK, 1); + data |= (((u16) at91_get_gpio_value(GPIO_SERDIN)) << i); + } + + if (board_rev == BOARD_REVA) { + at91_set_gpio_value(GPIO_CS0, 1); + at91_set_gpio_value(GPIO_CS1, 1); + at91_set_gpio_value(GPIO_CS2, 1); + } else { + at91_set_gpio_value(cs, 1); + } + + at91_set_gpio_value(GPIO_SERCLK, 0); + + return data; +} + +static int radio_reset(void) +{ + int ret; + + ret = at91_set_gpio_value(GPIO_RADIO_RESET, 0); + if (ret) { + return ret; + } + + udelay(1000); + + ret = at91_set_gpio_value(GPIO_RADIO_RESET, 1); + + return ret; +} + +static int board_temperature(void) +{ + int temp; + + temp = serial_readw(GPIO_CS2); + + /* + * If sign bit set, convert raw value to negative integer. + * @see: ADT7302 datasheet + */ + if (temp & 0x2000) { + temp = temp - 16384; + } + + temp = temp / 32 + 1 * ((temp % 32) >= 16); + + return temp; +} + +static ssize_t show_radio_reset(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int value; + + mutex_lock(&mts_io_mutex); + + value = at91_get_gpio_value(GPIO_RADIO_RESET); + + mutex_unlock(&mts_io_mutex); + + if (value < 0) { + return value; + } + + return sprintf(buf, "%d\n", value); +} + +static ssize_t store_radio_reset(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int value; + int err; + + if (sscanf(buf, "%i", &value) != 1) { + return -EINVAL; + } + if (value != 0) { + return -EINVAL; + } + + mutex_lock(&mts_io_mutex); + + err = radio_reset(); + + mutex_unlock(&mts_io_mutex); + + if (err) { + return err; + } + + return count; +} + +static struct device_attribute dev_attr_radio_reset = { + .attr = { + .name = "radio-reset", + .mode = 0644, + }, + .show = show_radio_reset, + .store = store_radio_reset, +}; + +static ssize_t show_eth0_enabled(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int value; + + mutex_lock(&mts_io_mutex); + + value = at91_get_gpio_value(GPIO_ETH0_ENABLED); + + mutex_unlock(&mts_io_mutex); + + if (value < 0) { + return value; + } + + return sprintf(buf, "%d\n", value); +} + +static ssize_t store_eth0_enabled(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int value; + int err; + + if (sscanf(buf, "%i", &value) != 1) { + return -EINVAL; + } + + mutex_lock(&mts_io_mutex); + + err = at91_set_gpio_value(GPIO_ETH0_ENABLED, value); + + mutex_unlock(&mts_io_mutex); + + if (err) { + return err; + } + + return count; +} + +static struct device_attribute dev_attr_eth0_enabled = { + .attr = { + .name = "eth0-enabled", + .mode = 0644, + }, + .show = show_eth0_enabled, + .store = store_eth0_enabled, +}; + +static ssize_t show_extserial_dcd(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int value; + + mutex_lock(&mts_io_mutex); + + value = !(cs0_bits & EXTSERIAL_DCD_BIT); + + mutex_unlock(&mts_io_mutex); + + return sprintf(buf, "%d\n", value); +} + +static ssize_t store_extserial_dcd(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int value; + + if (sscanf(buf, "%i", &value) != 1) { + return -EINVAL; + } + + mutex_lock(&mts_io_mutex); + + if (value) { + cs0_bits &= ~EXTSERIAL_DCD_BIT; + } else { + cs0_bits |= EXTSERIAL_DCD_BIT; + } + serial_writeb(cs0_bits, GPIO_CS0); + + mutex_unlock(&mts_io_mutex); + + return count; +} + +static struct device_attribute dev_attr_extserial_dcd = { + .attr = { + .name = "extserial-dcd", + .mode = 0644, + }, + .show = show_extserial_dcd, + .store = store_extserial_dcd, +}; + +static ssize_t show_extserial_ri(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int value; + + mutex_lock(&mts_io_mutex); + + value = !(cs0_bits & EXTSERIAL_RI_BIT); + + mutex_unlock(&mts_io_mutex); + + return sprintf(buf, "%d\n", value); +} + +static ssize_t store_extserial_ri(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int value; + + if (sscanf(buf, "%i", &value) != 1) { + return -EINVAL; + } + + mutex_lock(&mts_io_mutex); + + if (value) { + cs0_bits &= ~EXTSERIAL_RI_BIT; + } else { + cs0_bits |= EXTSERIAL_RI_BIT; + } + serial_writeb(cs0_bits, GPIO_CS0); + + mutex_unlock(&mts_io_mutex); + + return count; +} + +static struct device_attribute dev_attr_extserial_ri = { + .attr = { + .name = "extserial-ri", + .mode = 0644, + }, + .show = show_extserial_ri, + .store = store_extserial_ri, +}; + +static ssize_t show_extserial_dsr(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int value; + + mutex_lock(&mts_io_mutex); + + value = !(cs0_bits & EXTSERIAL_DSR_BIT); + + mutex_unlock(&mts_io_mutex); + + return sprintf(buf, "%d\n", value); +} + +static ssize_t store_extserial_dsr(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int value; + + if (sscanf(buf, "%i", &value) != 1) { + return -EINVAL; + } + + mutex_lock(&mts_io_mutex); + + if (value) { + cs0_bits &= ~EXTSERIAL_DSR_BIT; + } else { + cs0_bits |= EXTSERIAL_DSR_BIT; + } + serial_writeb(cs0_bits, GPIO_CS0); + + mutex_unlock(&mts_io_mutex); + + return count; +} + +static struct device_attribute dev_attr_extserial_dsr = { + .attr = { + .name = "extserial-dsr", + .mode = 0644, + }, + .show = show_extserial_dsr, + .store = store_extserial_dsr, +}; + +static ssize_t show_led_sig1(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int value; + + mutex_lock(&mts_io_mutex); + + value = !(cs0_bits & LED_SIG1_BIT); + + mutex_unlock(&mts_io_mutex); + + return sprintf(buf, "%d\n", value); +} + +static ssize_t store_led_sig1(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int value; + + if (sscanf(buf, "%i", &value) != 1) { + return -EINVAL; + } + + mutex_lock(&mts_io_mutex); + + if (value) { + cs0_bits &= ~LED_SIG1_BIT; + } else { + cs0_bits |= LED_SIG1_BIT; + } + serial_writeb(cs0_bits, GPIO_CS0); + + mutex_unlock(&mts_io_mutex); + + return count; +} + +static struct device_attribute dev_attr_led_sig1 = { + .attr = { + .name = "led-sig1", + .mode = 0644, + }, + .show = show_led_sig1, + .store = store_led_sig1, +}; + +static struct device_attribute dev_attr_led_sdk_c = { + .attr = { + .name = "led-sdk-c", + .mode = 0644, + }, + .show = show_led_sig1, + .store = store_led_sig1, +}; + +static ssize_t show_led_sig2(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int value; + + mutex_lock(&mts_io_mutex); + + value = !(cs0_bits & LED_SIG2_BIT); + + mutex_unlock(&mts_io_mutex); + + return sprintf(buf, "%d\n", value); +} + +static ssize_t store_led_sig2(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int value; + + if (sscanf(buf, "%i", &value) != 1) { + return -EINVAL; + } + + mutex_lock(&mts_io_mutex); + + if (value) { + cs0_bits &= ~LED_SIG2_BIT; + } else { + cs0_bits |= LED_SIG2_BIT; + } + serial_writeb(cs0_bits, GPIO_CS0); + + mutex_unlock(&mts_io_mutex); + + return count; +} + +static struct device_attribute dev_attr_led_sig2 = { + .attr = { + .name = "led-sig2", + .mode = 0644, + }, + .show = show_led_sig2, + .store = store_led_sig2, +}; + +static struct device_attribute dev_attr_led_sdk_d = { + .attr = { + .name = "led-sdk-d", + .mode = 0644, + }, + .show = show_led_sig2, + .store = store_led_sig2, +}; + +static ssize_t show_led_sig3(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int value; + + mutex_lock(&mts_io_mutex); + + value = !(cs0_bits & LED_SIG3_BIT); + + mutex_unlock(&mts_io_mutex); + + return sprintf(buf, "%d\n", value); +} + +static ssize_t store_led_sig3(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int value; + + if (sscanf(buf, "%i", &value) != 1) { + return -EINVAL; + } + + mutex_lock(&mts_io_mutex); + + if (value) { + cs0_bits &= ~LED_SIG3_BIT; + } else { + cs0_bits |= LED_SIG3_BIT; + } + serial_writeb(cs0_bits, GPIO_CS0); + + mutex_unlock(&mts_io_mutex); + + return count; +} + +static struct device_attribute dev_attr_led_sig3 = { + .attr = { + .name = "led-sig3", + .mode = 0644, + }, + .show = show_led_sig3, + .store = store_led_sig3, +}; + +static struct device_attribute dev_attr_led_sdk_e = { + .attr = { + .name = "led-sdk-e", + .mode = 0644, + }, + .show = show_led_sig3, + .store = store_led_sig3, +}; + +static ssize_t show_led_cd(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int value; + + mutex_lock(&mts_io_mutex); + + value = !(cs0_bits & LED_CD_BIT); + + mutex_unlock(&mts_io_mutex); + + return sprintf(buf, "%d\n", value); +} + +static ssize_t store_led_cd(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int value; + + if (sscanf(buf, "%i", &value) != 1) { + return -EINVAL; + } + + mutex_lock(&mts_io_mutex); + + if (value) { + cs0_bits &= ~LED_CD_BIT; + } else { + cs0_bits |= LED_CD_BIT; + } + serial_writeb(cs0_bits, GPIO_CS0); + + mutex_unlock(&mts_io_mutex); + + return count; +} + +static struct device_attribute dev_attr_led_cd = { + .attr = { + .name = "led-cd", + .mode = 0644, + }, + .show = show_led_cd, + .store = store_led_cd, +}; + +static struct device_attribute dev_attr_led_sdk_b = { + .attr = { + .name = "led-sdk-b", + .mode = 0644, + }, + .show = show_led_cd, + .store = store_led_cd, +}; + +#if LED_STATUS_CONTROLLABLE +static ssize_t show_led_status(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", led_mode_status); +} + +static ssize_t store_led_status(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int value; + int ret; + + if (sscanf(buf, "%i", &value) != 1) { + return -EINVAL; + } + + mutex_lock(&mts_io_mutex); + + switch (value) { + case LED_OFF: + led_mode_status = LED_OFF; + ret = at91_set_gpio_value(GPIO_STATUS_LED, 1); + + break; + + case LED_ON: + led_mode_status = LED_ON; + ret = at91_set_gpio_value(GPIO_STATUS_LED, 0); + + break; + + case LED_FLASHING: + led_mode_status = LED_FLASHING; + ret = at91_set_gpio_value(GPIO_STATUS_LED, 0); + + break; + + default: + ret = -EINVAL; + } + + mutex_unlock(&mts_io_mutex); + + return count; +} + +static struct device_attribute dev_attr_led_status = { + .attr = { + .name = "led-status", + .mode = 0644, + }, + .show = show_led_status, + .store = store_led_status, +}; + +static struct device_attribute dev_attr_led_sdk_a = { + .attr = { + .name = "led-sdk-a", + .mode = 0644, + }, + .show = show_led_status, + .store = store_led_status, +}; +#endif + +#if LED_LS_CONTROLLABLE +static ssize_t show_led_ls(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int value; + + mutex_lock(&mts_io_mutex); + + value = at91_get_gpio_value(GPIO_LS_LED); + + mutex_unlock(&mts_io_mutex); + + if (value < 0) { + return value; + } + + return sprintf(buf, "%d\n", !value); +} + +static ssize_t store_led_ls(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int value; + int err; + + if (sscanf(buf, "%i", &value) != 1) { + return -EINVAL; + } + + mutex_lock(&mts_io_mutex); + + err = at91_set_gpio_value(GPIO_LS_LED, !value); + + mutex_unlock(&mts_io_mutex); + + if (err) { + return err; + } + + return count; +} + +static struct device_attribute dev_attr_led_ls = { + .attr = { + .name = "led-ls", + .mode = 0644, + }, + .show = show_led_ls, + .store = store_led_ls, +}; +#endif + +static ssize_t show_reset(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int value; + + mutex_lock(&mts_io_mutex); + + value = !at91_get_gpio_value(GPIO_OPTRST); + + mutex_unlock(&mts_io_mutex); + + if (value < 0) { + return value; + } + + return sprintf(buf, "%d\n", value); +} + +static struct device_attribute dev_attr_reset = { + .attr = { + .name = "reset", + .mode = 0444, + }, + .show = show_reset, +}; + +static ssize_t show_reset_monitor(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + + mutex_lock(&mts_io_mutex); + + ret = sprintf(buf, "%d %d %d\n", reset_pid, reset_short_signal, reset_long_signal); + + mutex_unlock(&mts_io_mutex); + + return ret; +} + +static ssize_t store_reset_monitor(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + pid_t pid; + int short_signal; + int long_signal; + + if (sscanf(buf, "%i %i %i", &pid, &short_signal, &long_signal) != 3) { + return -EINVAL; + } + + mutex_lock(&mts_io_mutex); + + reset_pid = pid; + reset_short_signal = short_signal; + reset_long_signal = long_signal; + + mutex_unlock(&mts_io_mutex); + + return count; +} + +static struct device_attribute dev_attr_reset_monitor = { + .attr = { + .name = "reset-monitor", + .mode = 0644, + }, + .show = show_reset_monitor, + .store = store_reset_monitor, +}; + +static ssize_t show_board_temperature(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + + mutex_lock(&mts_io_mutex); + + ret = sprintf(buf, "%d\n", board_temperature()); + + mutex_unlock(&mts_io_mutex); + + return ret; +} + +static struct device_attribute dev_attr_board_temperature = { + .attr = { + .name = "board-temperature", + .mode = 0444, + }, + .show = show_board_temperature, +}; + +static struct attribute *platform_attributes[] = { + &dev_attr_radio_reset.attr, + &dev_attr_eth0_enabled.attr, + &dev_attr_extserial_dcd.attr, + &dev_attr_extserial_ri.attr, + &dev_attr_led_sig1.attr, + &dev_attr_led_sdk_c.attr, + &dev_attr_led_sig2.attr, + &dev_attr_led_sdk_d.attr, + &dev_attr_led_sig3.attr, + &dev_attr_led_sdk_e.attr, + &dev_attr_led_cd.attr, + &dev_attr_led_sdk_b.attr, +#if LED_STATUS_CONTROLLABLE + &dev_attr_led_status.attr, + &dev_attr_led_sdk_a.attr, +#endif +#if LED_LS_CONTROLLABLE + &dev_attr_led_ls.attr, +#endif + &dev_attr_reset.attr, + &dev_attr_reset_monitor.attr, + &dev_attr_board_temperature.attr, + NULL, +}; + +static struct attribute_group platform_attribute_group = { + .attrs = platform_attributes +}; + +static struct platform_device *mts_io_platform_device; + +enum { + DIR_INPUT, + DIR_OUTPUT, +}; + +struct gpio_pin_init { + char name[32]; + unsigned long pin; + int direction; + int output_value; + int use_pullup; + int board_rev_added; +}; + +static struct gpio_pin_init init_pins[] = { + /* + * GPIO_{VER0,VER1} need to be first in the list. + */ + { + .name = "GPIO_VER0", + .pin = GPIO_VER0, + .direction = DIR_INPUT, + .output_value = 0, + .use_pullup = 0, + .board_rev_added = BOARD_REVA, + }, + { + .name = "GPIO_VER1", + .pin = GPIO_VER1, + .direction = DIR_INPUT, + .output_value = 0, + .use_pullup = 0, + .board_rev_added = BOARD_REVA, + }, + { + .name = "GPIO_CS0", + .pin = GPIO_CS0, + .direction = DIR_OUTPUT, + .output_value = 1, + .use_pullup = 1, + .board_rev_added = BOARD_REVA, + }, + { + .name = "GPIO_CS1", + .pin = GPIO_CS1, + .direction = DIR_OUTPUT, + .output_value = 1, + .use_pullup = 1, + .board_rev_added = BOARD_REVA, + }, + { + .name = "GPIO_CS2", + .pin = GPIO_CS2, + .direction = DIR_OUTPUT, + .output_value = 1, + .use_pullup = 1, + .board_rev_added = BOARD_REVA, + }, + { + .name = "GPIO_CS3", + .pin = GPIO_CS3, + .direction = DIR_OUTPUT, + .output_value = 1, + .use_pullup = 1, + .board_rev_added = BOARD_REVC, + }, + { + .name = "GPIO_ENIO", + .pin = GPIO_ENIO, + .direction = DIR_OUTPUT, + .output_value = 1, + .use_pullup = 0, + .board_rev_added = BOARD_REVA, + }, + { + .name = "GPIO_ETH0_ENABLED", + .pin = AT91_PIN_PB31, + .direction = DIR_OUTPUT, + .output_value = 1, + .use_pullup = 0, + .board_rev_added = BOARD_REVA, + }, + { + .name = "GPIO_RADIO_RESET", + .pin = GPIO_RADIO_RESET, + .direction = DIR_OUTPUT, + .output_value = 1, + .use_pullup = 0, + .board_rev_added = BOARD_REVA, + }, + { + .name = "GPIO_OPTRST", + .pin = GPIO_OPTRST, + .direction = DIR_INPUT, + .output_value = 0, + .use_pullup = 0, + .board_rev_added = BOARD_REVA, + }, +#if LED_LS_CONTROLLABLE + { + .name = "GPIO_LS_LED", + .pin = GPIO_LS_LED, + .direction = DIR_OUTPUT, + .output_value = 1, + .use_pullup = 0, + .board_rev_added = BOARD_REVA, + }, +#endif + { + .name = "GPIO_SERCLK", + .pin = GPIO_SERCLK, + .direction = DIR_OUTPUT, + .output_value = 0, + .use_pullup = 0, + .board_rev_added = BOARD_REVA, + }, + { + .name = "GPIO_SERDOUT", + .pin = GPIO_SERDOUT, + .direction = DIR_OUTPUT, + .output_value = 0, + .use_pullup = 1, + .board_rev_added = BOARD_REVA, + }, + { + .name = "GPIO_SERDIN", + .pin = GPIO_SERDIN, + .direction = DIR_INPUT, + .output_value = 0, + .use_pullup = 1, + .board_rev_added = BOARD_REVA, + }, +#if LED_STATUS_CONTROLLABLE + { + .name = "GPIO_STATUS_LED", + .pin = GPIO_STATUS_LED, + .direction = DIR_OUTPUT, + .output_value = 1, + .use_pullup = 0, + .board_rev_added = BOARD_REVA, + }, +#endif + { + .name = "GPIO_RSERSRC", + .pin = GPIO_RSERSRC, + .direction = DIR_OUTPUT, + .output_value = 1, + .use_pullup = 0, + .board_rev_added = BOARD_REVB, + }, + { + .name = "GPIO_DBSERON", + .pin = GPIO_DBSERON, + .direction = DIR_OUTPUT, + .output_value = 1, + .use_pullup = 0, + .board_rev_added = BOARD_REVB, + }, + { + .name = "GPIO_DTR1", + .pin = GPIO_DTR1, + .direction = DIR_INPUT, + .output_value = 0, + .use_pullup = 0, + .board_rev_added = BOARD_REVB, + }, + { + .name = "GPIO_RXD1MON", + .pin = GPIO_RXD1MON, + .direction = DIR_INPUT, + .output_value = 0, + .use_pullup = 0, + .board_rev_added = BOARD_REVB, + }, + { + .name = "GPIO_EXTIN0", + .pin = GPIO_EXTIN0, + .direction = DIR_INPUT, + .output_value = 0, + .use_pullup = 0, + .board_rev_added = BOARD_REVB, + }, + { + .name = "GPIO_EXTIN1", + .pin = GPIO_EXTIN1, + .direction = DIR_INPUT, + .output_value = 0, + .use_pullup = 0, + .board_rev_added = BOARD_REVB, + }, + { + .name = "GPIO_EXTIN2", + .pin = GPIO_EXTIN2, + .direction = DIR_INPUT, + .output_value = 0, + .use_pullup = 0, + .board_rev_added = BOARD_REVB, + }, +#if 0 + { + .name = "GPIO_EXTIN3", + .pin = GPIO_EXTIN3, + .direction = DIR_INPUT, + .output_value = 0, + .use_pullup = 0, + .board_rev_added = BOARD_REVC, + }, + { + .name = "GPIO_EXTIN4", + .pin = GPIO_EXTIN4, + .direction = DIR_INPUT, + .output_value = 0, + .use_pullup = 0, + .board_rev_added = BOARD_REVC, + }, + { + .name = "GPIO_EXTIN5", + .pin = GPIO_EXTIN5, + .direction = DIR_INPUT, + .output_value = 0, + .use_pullup = 0, + .board_rev_added = BOARD_REVC, + }, + { + .name = "GPIO_GPS_NINT2", + .pin = GPIO_GPS_NINT2, + .direction = DIR_INPUT, + .output_value = 0, + .use_pullup = 0, + .board_rev_added = BOARD_REVC, + }, +#endif +}; + +static int __init mts_io_init(void) +{ + int i; + int ret; + + info(DRIVER_NAME ": init"); + + mts_io_platform_device = platform_device_alloc(PLATFORM_NAME, -1); + if (!mts_io_platform_device) { + ret = -ENOMEM; + goto error_platform_device_alloc; + } + + ret = platform_device_add(mts_io_platform_device); + if (ret) { + goto error_platform_device_add; + } + + ret = sysfs_create_group(&mts_io_platform_device->dev.kobj, + &platform_attribute_group); + if (ret) { + goto error_sysfs_create_group; + } + + for (i = 0; i < ARRAY_SIZE(init_pins); i++) { + gpio_request(init_pins[i].pin, init_pins[i].name); + + if (board_rev >= init_pins[i].board_rev_added) { + if (init_pins[i].direction == DIR_OUTPUT) { + at91_set_gpio_output(init_pins[i].pin, init_pins[i].output_value); + } else { + at91_set_gpio_input(init_pins[i].pin, init_pins[i].use_pullup); + } + } + + if (i == 1) { + board_rev = (at91_get_gpio_value(GPIO_VER1) << 1) | at91_get_gpio_value(GPIO_VER0); + board_rev ^= 0x03; + info(DRIVER_NAME ": board rev: %d", board_rev); + } + } + + serial_writeb(cs0_bits, GPIO_CS0); + serial_writeb(0xFF, GPIO_CS1); + + /* + * enable serial device data output bus + */ + at91_set_gpio_value(GPIO_ENIO, 0); + + blink_callback(NULL); + + return 0; + +error_sysfs_create_group: + platform_device_del(mts_io_platform_device); +error_platform_device_add: + platform_device_put(mts_io_platform_device); +error_platform_device_alloc: + error(DRIVER_NAME " init failed: %d", ret); + + return ret; +} + +static void __exit mts_io_exit(void) +{ + int i; + + cancel_delayed_work_sync(&blink_work); + + if (board_rev >= init_pins[i].board_rev_added) { + for (i = 0; i < ARRAY_SIZE(init_pins); i++) { + at91_set_gpio_input(init_pins[i].pin, init_pins[i].use_pullup); + } + } + + sysfs_remove_group(&mts_io_platform_device->dev.kobj, + &platform_attribute_group); + platform_device_unregister(mts_io_platform_device); + + info(DRIVER_NAME ": exiting"); +} + +module_init(mts_io_init); +module_exit(mts_io_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_VERSION(DRIVER_VERSION); +MODULE_LICENSE("GPL"); + + + diff --git a/io-module/mts_io.h b/io-module/mts_io.h new file mode 100644 index 0000000..6cd1189 --- /dev/null +++ b/io-module/mts_io.h @@ -0,0 +1,7 @@ + +#ifndef __MTS_IO_H +#define __MTS_IO_H + + +#endif /* ~__MTS_IO_H */ + diff --git a/io-tool/mts-io-sysfs b/io-tool/mts-io-sysfs new file mode 100755 index 0000000..b32b005 --- /dev/null +++ b/io-tool/mts-io-sysfs @@ -0,0 +1,241 @@ +#!/usr/bin/env bash +# +# vim: set sw=2 ts=2 expandtab: +# +# Copyright (C) 2010 by Multi-Tech Systems +# +# Author: James Maki <jmaki@multitech.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; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +PATH=${PATH}:. + +source /usr/lib/mts-io-sysfs/mts-io-sysfs-inc.sh + +trap "exit_handler" EXIT +exit_handler() { + local exit_code=$? + if [ ${exit_code} -ne 0 ]; then + log_error "exiting with ${exit_code}" + fi +} + +reset_short_handler() { + echo "reset short signal received" +} +reset_long_handler() { + echo "reset long signal received" +} +RESET_SHORT_CMD="reset_short_handler" +RESET_LONG_CMD="reset_long_handler" +RESET_SHORT_SIGNAL=$(kill -l SIGUSR1) +RESET_LONG_SIGNAL=$(kill -l SIGUSR2) + +PROGRAM=$(basename $0) +VERSION="0.0.2" + +print_version() { + printf "${PROGRAM} ${VERSION}\n" + printf "Copyright (C) 2010 by Multi-Tech Systems\n" +cat <<EOF +This program is free software; you may redistribute it under the terms of +the GNU General Public License version 2 or (at your option) any later version. +This program has absolutely no warranty. +EOF +} + +usage() { + local out=1 + if [ $# -eq 1 ]; then + out=${1} + fi + + printf "Usage: ${PROGRAM} [ OPTIONS ] OBJECT [--] [ ARGUMENTS ]\n" >&${out} + printf "where OBJECT := {\n" >&${out} + printf " show SHOW-NAME |\n" >&${out} + printf " store STORE-NAME { value } |\n" >&${out} + printf " reset-monitor-trap [short-cmd cmd signame] [long-cmd cmd signame]\n" >&${out} + printf " }\n" >&${out} + printf "\n" >&${out} + printf " SHOW-NAME := {\n" >&${out} + printf " board-temperature |\n" >&${out} + printf " eth0-enabled |\n" >&${out} + printf " extserial-dcd |\n" >&${out} + printf " extserial-ri |\n" >&${out} + printf " led-cd |\n" >&${out} + printf " led-sdk-b |\n" >&${out} + printf " led-sig1 |\n" >&${out} + printf " led-sdk-c |\n" >&${out} + printf " led-sig2 |\n" >&${out} + printf " led-sdk-d |\n" >&${out} + printf " led-sig3 |\n" >&${out} + printf " led-sdk-e |\n" >&${out} +if [ "${MTS_IO_CONTROLS_STATUS_LED}" = "true" ]; then + printf " led-status |\n" >&${out} + printf " led-sdk-a |\n" >&${out} +fi +if [ "${MTS_IO_CONTROLS_LS_LED}" = "true" ]; then + printf " led-ls |\n" >&${out} +fi + printf " radio-reset |\n" >&${out} + printf " reset |\n" >&${out} + printf " reset-monitor\n" >&${out} + printf " }\n" >&${out} + printf "\n" >&${out} + printf " STORE-NAME := {\n" >&${out} + printf " eth0-enabled BOOLEAN |\n" >&${out} + printf " extserial-dcd BOOLEAN |\n" >&${out} + printf " extserial-ri BOOLEAN |\n" >&${out} + printf " led-cd BOOLEAN |\n" >&${out} + printf " led-sdk-b BOOLEAN |\n" >&${out} + printf " led-sig1 BOOLEAN |\n" >&${out} + printf " led-sdk-c BOOLEAN |\n" >&${out} + printf " led-sig2 BOOLEAN |\n" >&${out} + printf " led-sdk-d BOOLEAN |\n" >&${out} + printf " led-sig3 BOOLEAN |\n" >&${out} + printf " led-sdk-e BOOLEAN |\n" >&${out} +if [ "${MTS_IO_CONTROLS_STATUS_LED}" = "true" ]; then + printf " led-status LED-VALUE |\n" >&${out} + printf " led-sdk-a LED-VALUE |\n" >&${out} +fi +if [ "${MTS_IO_CONTROLS_LS_LED}" = "true" ]; then + printf " led-ls BOOLEAN |\n" >&${out} +fi + printf " radio-reset { 0 } |\n" >&${out} + printf " reset-monitor { pid short-signal long-signal }\n" >&${out} + printf " }\n" >&${out} + printf "\n" >&${out} + printf " OPTIONS := {\n" >&${out} + printf " --verbose\n" >&${out} + printf " }\n" >&${out} + printf "\n" >&${out} + printf " BOOLEAN := { OFF | ON }\n" >&${out} + printf " LED-VALUE := { OFF | ON | LED-FLASHING }\n" >&${out} + printf " OFF := 0\n" >&${out} + printf " ON := 1\n" >&${out} + printf " LED-FLASHING := 2\n" >&${out} + printf "\n" >&${out} +} + +ARGS=$(getopt -o "" --long verbose,version,help -n $0 -- "$@") +if [ $? != 0 ]; then + usage 2 + exit 1 +fi +eval set -- "${ARGS}" + +while true; do + case "$1" in + --version) + print_version + exit 0 + shift + ;; + + --help) + usage 1 + exit 0 + shift + ;; + + --verbose) + VERBOSE=true + shift + ;; + --) + shift + break + ;; + *) + usage 2 + exit 1 + ;; + esac +done + +if [ $# -lt 1 ]; then + usage 2 + exit 1 +fi + +cmd=${1} +shift + +case "${cmd}" in +show) + show "$@" + ;; +store) + store "$@" + ;; +show-trigger) + show_trigger "$@" + ;; +store-trigger) + store_trigger "$@" + ;; +reset-monitor-trap) + while true; do + if [ $# -eq 0 ]; then + break + fi + + if [ $# -ne 3 ]; then + usage 2 + exit 1 + fi + + case "${1}" in + short-cmd) + RESET_SHORT_CMD=${2} + RESET_SHORT_SIGNAL=$(kill -l ${3}) + if [ $? -ne 0 ]; then + exit 1 + fi + ;; + long-cmd) + RESET_LONG_CMD=${2} + RESET_LONG_SIGNAL=$(kill -l ${3}) + if [ $? -ne 0 ]; then + exit 1 + fi + ;; + *) + usage 2 + exit 1 + ;; + esac + + shift; shift; shift + done + + trap "${RESET_SHORT_CMD}" ${RESET_SHORT_SIGNAL} + trap "${RESET_LONG_CMD}" ${RESET_LONG_SIGNAL} + + store reset-monitor "$$ ${RESET_SHORT_SIGNAL} ${RESET_LONG_SIGNAL}" + + while true; do + sleep 1 + done + ;; +*) + usage 2 + exit 1 + ;; +esac + +exit 0 + diff --git a/io-tool/mts-io-sysfs-inc.sh b/io-tool/mts-io-sysfs-inc.sh new file mode 100644 index 0000000..f3fde5f --- /dev/null +++ b/io-tool/mts-io-sysfs-inc.sh @@ -0,0 +1,92 @@ +#!/usr/bin/env bash +# +# vim: set sw=2 ts=2 expandtab: +# +# Copyright (C) 2010 by Multi-Tech Systems +# +# Author: James Maki <jmaki@multitech.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; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +VERBOSE=false + +PLATFORM_NAME=sparkcell +SYSFS_PLATFORM_DIR=/sys/devices/platform/${PLATFORM_NAME} + +MTS_IO_CONTROLS_STATUS_LED=true +MTS_IO_CONTROLS_LS_LED=false + +LEDS_GPIO_DIR=/sys/devices/platform/leds-gpio/leds + +log_info() { + if [ "${VERBOSE}" = "true" ]; then + echo "info:" "$@" + fi +} + +log_error() { + echo "error:" "$@" >&2 +} + +store() { + if [ $# -ne 2 ]; then + log_error "store name value" + exit 99 + fi + + local name=${1} + local value=${2} + + log_info "setting ${name} to '${value}'" + echo -n "${value}" > ${SYSFS_PLATFORM_DIR}/${name} +} + +show() { + if [ $# -ne 1 ]; then + log_error "show name" + exit 99 + fi + + local name=${1} + + cat ${SYSFS_PLATFORM_DIR}/${name} +} + +store_trigger() { + if [ $# -ne 2 ]; then + log_error "store_trigger name value" + exit 99 + fi + + local name=${1} + local value=${2} + + log_info "setting ${name} to '${value}'" + echo -n "${value}" > cat ${LEDS_GPIO_DIR}/${name}/trigger +} + +show_trigger() { + if [ $# -ne 1 ]; then + log_error "show_trigger name" + exit 99 + fi + + local name=${1} + + cat ${LEDS_GPIO_DIR}/${name}/trigger +} + + diff --git a/io-tool/sysfs-tests b/io-tool/sysfs-tests new file mode 100755 index 0000000..cb65c57 --- /dev/null +++ b/io-tool/sysfs-tests @@ -0,0 +1,181 @@ +#!/usr/bin/env bash + +PATH=${PATH}:. + +source /usr/lib/mts-io-sysfs/mts-io-sysfs-inc.sh + +yesno() { + if [ $# -ne 1 ]; then + echo "yesno question" + exit 99 + fi + + local question=${1} + + while : + do + echo -n "${question}? [y/n] " + read line + local line=$(echo ${line} | tr A-Z a-z) + case "${line}" in + y) + return 0 + ;; + n) + return 1 + ;; + *) + ;; + esac + done +} + +store_assert() { + if [ $# -ne 2 ]; then + log_error "store_assert name value" + exit 99 + fi + + local name=${1} + local value=${2} + + local old_value="$(cat ${SYSFS_PLATFORM_DIR}/${name})" + store ${name} "${value}" + local new_value="$(cat ${SYSFS_PLATFORM_DIR}/${name})" + + if [ "${new_value}" != "${value}" ]; then + log_error "${name}: '${new_value}' != '${value}'" + exit 1 + fi +} + +show_assert() { + if [ $# -ne 2 ]; then + log_error "show_assert name value" + exit 99 + fi + + local name=${1} + local expected_value=${2} + + log_info "checking if ${name} is '${expected_value}'" + + local actual_value="$(show ${name})" + + if [ "${expected_value}" != "${actual_value}" ]; then + log_error "${name}: '${expected_value}' != '${actual_value}'" + exit 1 + fi +} + +show_assert_in_set() { + if [ $# -lt 2 ]; then + log_error "show_assert_in_set name value0 [value1 ...]" + exit 99 + fi + + local name=${1} + shift + + log_info "checking if ${name} is in set [$*]" + + local actual_value="$(show ${name})" + + for value in "$@"; do + if [ "${value}" = "${actual_value}" ]; then + log_info "${name} is '${value}'" + return + fi + done + + exit 1 +} + +VERBOSE=true + +echo none > /sys/devices/platform/leds-gpio/leds/status/trigger + +modprobe -r mts-io +modprobe mts-io + +sleep 2 + +show_assert radio-reset 1 +log_info "resetting radio..." +store radio-reset 0 +sleep 2 +show_assert radio-reset 1 +log_info "radio reset" + +show_assert eth0-enabled 1 +store_assert eth0-enabled 0 +sleep 2 +if ! yesno "Are the ethernet LEDs off"; then + exit 1 +fi +store_assert eth0-enabled 1 +sleep 2 +if ! yesno "Are the ethernet LEDs on"; then + exit 1 +fi + +show_assert extserial-dcd 0 +store_assert extserial-dcd 1 +store_assert extserial-dcd 0 + +show_assert extserial-ri 0 +store_assert extserial-ri 1 +store_assert extserial-ri 0 + +show_assert led-sig1 0 +store_assert led-sig1 1 +store_assert led-sig1 0 + +show_assert led-sdk-c 0 +store_assert led-sdk-c 1 +store_assert led-sdk-c 0 + +show_assert led-sig2 0 +store_assert led-sig2 1 +store_assert led-sig2 0 + +show_assert led-sdk-d 0 +store_assert led-sdk-d 1 +store_assert led-sdk-d 0 + +show_assert led-sig3 0 +store_assert led-sig3 1 +store_assert led-sig3 0 + +show_assert led-sdk-e 0 +store_assert led-sdk-e 1 +store_assert led-sdk-e 0 + +show_assert led-cd 0 +store_assert led-cd 1 +store_assert led-cd 0 + +show_assert led-sdk-b 0 +store_assert led-sdk-b 1 +store_assert led-sdk-b 0 + +show_assert led-status 0 +store_assert led-status 1 +store_assert led-status 0 + +show_assert led-sdk-a 0 +store_assert led-sdk-a 1 +store_assert led-sdk-a 0 + +show_assert reset 0 +store reset 1 +show_assert reset 0 + +show_assert_in_set board-temperature {30..40} +store board-temperature 1000 +show_assert_in_set board-temperature {30..40} + +show_assert reset-monitor "-1 10 12" + +exit 0 + |