summaryrefslogtreecommitdiff
path: root/io-module/mts_io.c
diff options
context:
space:
mode:
Diffstat (limited to 'io-module/mts_io.c')
-rw-r--r--io-module/mts_io.c1334
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");
+
+
+