/* * MTCDP IO Controller * * Copyright (C) 2010 by Multi-Tech Systems * * Author: James Maki * * 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 * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mts_io.h" #define DRIVER_VERSION "v0.2.0" #define DRIVER_AUTHOR "James Maki " #define DRIVER_DESC "MTCDP IO Controller" #define DRIVER_NAME "mts-io" #define PLATFORM_NAME "mtcdp" #define DEBUG 0 #if DEBUG # define dbg(fmt, args...) printk(KERN_DEBUG DRIVER_NAME ": " fmt "\n" , ##args) #else # define dbg(fmt, args...) do {} while (0) #endif #define error(fmt, args...) printk(KERN_ERR DRIVER_NAME ": " fmt "\n" , ##args) #define info(fmt, args...) printk(KERN_INFO DRIVER_NAME ": " fmt "\n" , ##args) enum { LED_OFF, LED_ON, LED_FLASHING, }; #define BOARD_REVA 0 #define BOARD_REVB 1 #define BOARD_REVC 2 static u8 board_rev = BOARD_REVA; #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_VER0 AT91_PIN_PA23 #define GPIO_VER1 AT91_PIN_PA24 #define GPIO_STATUS_LED AT91_PIN_PA30 #define GPIO_LS_LED AT91_PIN_PC9 #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_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 LED_STATUS_CONTROLLABLE 0 #define LED_LS_CONTROLLABLE 0 #if LED_STATUS_CONTROLLABLE static int led_mode_status = LED_OFF; #endif static struct spi_device *spi_sregout; static u8 spi_sregout_byte; static DEFINE_MUTEX(spi_sregout_mutex); static struct spi_device *spi_temp; static int spi_sregout_write(struct spi_device *spi, const u8 value) { return spi_write(spi, &value, 1); } static int spi_temp_read(struct spi_device *spi, u16 *value) { int tmp; u8 buf[2] = {0, 0}; tmp = spi_read(spi, buf, 2); *value = buf[0] << 8 | buf[1]; return tmp; } #define BLINK_PER_SEC 8 #define BLINK_INTERVAL (HZ / BLINK_PER_SEC) #define RESET_HOLD_COUNT (BLINK_PER_SEC * 3) static pid_t reset_pid = -1; static pid_t reset_count = 0; static int reset_short_signal = SIGUSR1; static int reset_long_signal = SIGUSR2; static DEFINE_MUTEX(mts_io_mutex); 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 int radio_reset(void) { int ret; ret = at91_set_gpio_value(GPIO_RADIO_RESET, 0); if (ret) { return ret; } mdelay(250); ret = at91_set_gpio_value(GPIO_RADIO_RESET, 1); return ret; } static int ADT7302_to_celsius(int value) { if (value & 0x2000) { value = value - 16384; } value = value / 32 + 1 * ((value % 32) >= 16); return value; } 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; if (!spi_sregout) { error("device not present"); return -ENODEV; } mutex_lock(&spi_sregout_mutex); value = spi_sregout_byte & EXTSERIAL_DCD_BIT ? 0 : 1; mutex_unlock(&spi_sregout_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 (!spi_sregout) { error("device not present"); return -ENODEV; } if (sscanf(buf, "%i", &value) != 1) { return -EINVAL; } mutex_lock(&spi_sregout_mutex); if (value) { spi_sregout_byte &= ~EXTSERIAL_DCD_BIT; } else { spi_sregout_byte |= EXTSERIAL_DCD_BIT; } spi_sregout_write(spi_sregout, spi_sregout_byte); mutex_unlock(&spi_sregout_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; if (!spi_sregout) { error("device not present"); return -ENODEV; } mutex_lock(&spi_sregout_mutex); value = spi_sregout_byte & EXTSERIAL_RI_BIT ? 0 : 1; mutex_unlock(&spi_sregout_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 (!spi_sregout) { error("device not present"); return -ENODEV; } if (sscanf(buf, "%i", &value) != 1) { return -EINVAL; } mutex_lock(&spi_sregout_mutex); if (value) { spi_sregout_byte &= ~EXTSERIAL_RI_BIT; } else { spi_sregout_byte |= EXTSERIAL_RI_BIT; } spi_sregout_write(spi_sregout, spi_sregout_byte); mutex_unlock(&spi_sregout_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; if (!spi_sregout) { error("device not present"); return -ENODEV; } mutex_lock(&spi_sregout_mutex); value = spi_sregout_byte & EXTSERIAL_DSR_BIT ? 0 : 1; mutex_unlock(&spi_sregout_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 (!spi_sregout) { error("device not present"); return -ENODEV; } if (sscanf(buf, "%i", &value) != 1) { return -EINVAL; } mutex_lock(&spi_sregout_mutex); if (value) { spi_sregout_byte &= ~EXTSERIAL_DSR_BIT; } else { spi_sregout_byte |= EXTSERIAL_DSR_BIT; } spi_sregout_write(spi_sregout, spi_sregout_byte); mutex_unlock(&spi_sregout_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; if (!spi_sregout) { error("device not present"); return -ENODEV; } mutex_lock(&spi_sregout_mutex); value = spi_sregout_byte & LED_SIG1_BIT ? 0 : 1; mutex_unlock(&spi_sregout_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 (!spi_sregout) { error("device not present"); return -ENODEV; } if (sscanf(buf, "%i", &value) != 1) { return -EINVAL; } mutex_lock(&spi_sregout_mutex); if (value) { spi_sregout_byte &= ~LED_SIG1_BIT; } else { spi_sregout_byte |= LED_SIG1_BIT; } spi_sregout_write(spi_sregout, spi_sregout_byte); mutex_unlock(&spi_sregout_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; if (!spi_sregout) { error("device not present"); return -ENODEV; } mutex_lock(&spi_sregout_mutex); value = spi_sregout_byte & LED_SIG2_BIT ? 0 : 1; mutex_unlock(&spi_sregout_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 (!spi_sregout) { error("device not present"); return -ENODEV; } if (sscanf(buf, "%i", &value) != 1) { return -EINVAL; } mutex_lock(&spi_sregout_mutex); if (value) { spi_sregout_byte &= ~LED_SIG2_BIT; } else { spi_sregout_byte |= LED_SIG2_BIT; } spi_sregout_write(spi_sregout, spi_sregout_byte); mutex_unlock(&spi_sregout_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; if (!spi_sregout) { error("device not present"); return -ENODEV; } mutex_lock(&spi_sregout_mutex); value = spi_sregout_byte & LED_SIG3_BIT ? 0 : 1; mutex_unlock(&spi_sregout_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 (!spi_sregout) { error("device not present"); return -ENODEV; } if (sscanf(buf, "%i", &value) != 1) { return -EINVAL; } mutex_lock(&spi_sregout_mutex); if (value) { spi_sregout_byte &= ~LED_SIG3_BIT; } else { spi_sregout_byte |= LED_SIG3_BIT; } spi_sregout_write(spi_sregout, spi_sregout_byte); mutex_unlock(&spi_sregout_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; if (!spi_sregout) { error("device not present"); return -ENODEV; } mutex_lock(&spi_sregout_mutex); value = spi_sregout_byte & LED_CD_BIT ? 0 : 1; mutex_unlock(&spi_sregout_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 (!spi_sregout) { error("device not present"); return -ENODEV; } if (sscanf(buf, "%i", &value) != 1) { return -EINVAL; } mutex_lock(&spi_sregout_mutex); if (value) { spi_sregout_byte &= ~LED_CD_BIT; } else { spi_sregout_byte |= LED_CD_BIT; } spi_sregout_write(spi_sregout, spi_sregout_byte); mutex_unlock(&spi_sregout_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 tmp; u16 temp_raw; tmp = spi_temp_read(spi_temp, &temp_raw); if (tmp) { error("spi_read temp failed %d", tmp); return tmp; } dbg("temp: 0x%04X", temp_raw); return sprintf(buf, "%d\n", ADT7302_to_celsius(temp_raw)); } 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_extserial_dsr.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_info { char name[32]; unsigned long pin; int direction; int output_value; int use_pullup; int board_rev_added; }; static struct gpio_pin_info gpio_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_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 #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 __devinit mts_spi_sregout_probe(struct spi_device *spi) { int tmp; spi->max_speed_hz = 1 * 1000 * 1000; spi->mode = 0; tmp = spi_setup(spi); if (tmp < 0) { return tmp; } spi_sregout_byte = 0xFF; spi_sregout_write(spi, spi_sregout_byte); spi_sregout = spi; return 0; } static int mts_spi_sregout_remove(struct spi_device *spi) { return 0; } static struct spi_driver mts_spi_sregout_driver = { .driver = { .name = "mts-io-sregout", .bus = &spi_bus_type, .owner = THIS_MODULE, }, .probe = mts_spi_sregout_probe, .remove = __devexit_p(mts_spi_sregout_remove), }; static int __devinit mts_spi_board_temp_probe(struct spi_device *spi) { int tmp; spi->max_speed_hz = 1 * 1000 * 1000; spi->mode = SPI_CPOL | SPI_CPHA; tmp = spi_setup(spi); if (tmp < 0) { error("spi_setup board-tmp"); return tmp; } spi_temp = spi; return 0; } static int mts_spi_board_temp_remove(struct spi_device *spi) { return 0; } static struct spi_driver mts_spi_board_temp_driver = { .driver = { .name = "mts-io-board-temp", .bus = &spi_bus_type, .owner = THIS_MODULE, }, .probe = mts_spi_board_temp_probe, .remove = __devexit_p(mts_spi_board_temp_remove), }; static int __init mts_io_init(void) { int i; int ret; info("init"); mts_io_platform_device = platform_device_alloc(PLATFORM_NAME, -1); if (!mts_io_platform_device) { ret = -ENOMEM; goto error1; } ret = platform_device_add(mts_io_platform_device); if (ret) { goto error2; } ret = sysfs_create_group(&mts_io_platform_device->dev.kobj, &platform_attribute_group); if (ret) { goto error3; } for (i = 0; i < ARRAY_SIZE(gpio_pins); i++) { gpio_request(gpio_pins[i].pin, gpio_pins[i].name); if (board_rev >= gpio_pins[i].board_rev_added) { if (gpio_pins[i].direction == DIR_OUTPUT) { at91_set_gpio_output(gpio_pins[i].pin, gpio_pins[i].output_value); } else { at91_set_gpio_input(gpio_pins[i].pin, gpio_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("board rev: %d", board_rev); if (board_rev == BOARD_REVA) { error("rev A boards are not supported"); ret = -EIO; goto error3; } } } at91_set_gpio_value(GPIO_ENIO, 0); ret = spi_register_driver(&mts_spi_sregout_driver); if (ret) { goto error3; } ret = spi_register_driver(&mts_spi_board_temp_driver); if (ret) { goto error4; } blink_callback(NULL); return 0; error4: spi_unregister_driver(&mts_spi_sregout_driver); error3: platform_device_del(mts_io_platform_device); error2: platform_device_put(mts_io_platform_device); error1: error("init failed: %d", ret); return ret; } static void __exit mts_io_exit(void) { int i; cancel_delayed_work_sync(&blink_work); spi_unregister_driver(&mts_spi_board_temp_driver); spi_unregister_driver(&mts_spi_sregout_driver); for (i = 0; i < ARRAY_SIZE(gpio_pins); i++) { if (board_rev >= gpio_pins[i].board_rev_added) { at91_set_gpio_input(gpio_pins[i].pin, gpio_pins[i].use_pullup); } } sysfs_remove_group(&mts_io_platform_device->dev.kobj, &platform_attribute_group); platform_device_unregister(mts_io_platform_device); info("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");