diff options
author | James Maki <jmaki@multitech.com> | 2010-04-23 15:10:19 -0500 |
---|---|---|
committer | James Maki <jmaki@multitech.com> | 2010-04-23 15:10:19 -0500 |
commit | 34609050e110fdeaf9cd63888c5902031ca21b1f (patch) | |
tree | 1ac2e994ad8803c68d402a846111e7d19219ea63 /io-module/mts_io.c | |
download | mts-io-34609050e110fdeaf9cd63888c5902031ca21b1f.tar.gz mts-io-34609050e110fdeaf9cd63888c5902031ca21b1f.tar.bz2 mts-io-34609050e110fdeaf9cd63888c5902031ca21b1f.zip |
initial commit
Diffstat (limited to 'io-module/mts_io.c')
-rw-r--r-- | io-module/mts_io.c | 1334 |
1 files changed, 1334 insertions, 0 deletions
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"); + + + |