/* * MTS-IO Controller * * Copyright (C) 2014 by Multi-Tech Systems * * Authors: James Maki * Jesse Gilles * * 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 #include #include #include #include "mts_io.h" #define DRIVER_VERSION "v0.9.1" #define DRIVER_AUTHOR "James Maki " #define DRIVER_DESC "MTS-IO Controller" #define DRIVER_NAME "mts-io" #define PLATFORM_NAME "mts-io" #define LED_LS_CONTROLLABLE 0 #define AT91SAM9X5_BASE_ADC 0xf804c000 /* on-board EEPROM */ extern uint8_t mts_id_eeprom[512]; static struct mts_id_eeprom_layout id_eeprom; // NUM_AP should be defined from the board code // it should be set to the value of CONFIG_MTS_NUM_ACCESSORY_PORTS // arch/arm/mach-at91/board-dt-sam9.c // if it is 0 or undefined, there is no accessory card support on this HW #ifdef CONFIG_MTS_NUM_ACCESSORY_PORTS #ifndef NUM_AP #define NUM_AP CONFIG_MTS_NUM_ACCESSORY_PORTS #endif #else #define NUM_AP 0 #endif #if NUM_AP > 0 /* accessory card EEPROMs */ extern uint8_t mts_ap_eeprom[NUM_AP][512]; static struct mts_ap_eeprom_layout ap_eeprom[NUM_AP]; #endif static struct ap_info* port_info[NUM_AP]; static struct attribute **device_attrs; static size_t device_attrs_size; static size_t device_attrs_max_size; static uint8_t mts_product_id; static uint8_t has_spi_sout; static uint8_t has_spi_din; static uint8_t has_spi_dout; static uint8_t has_spi_temp; static struct platform_device *mts_io_platform_device; static struct attribute_group *attr_group; static struct gpio_pin *gpio_pins; static DEFINE_MUTEX(mts_io_mutex); /* generic GPIO support */ #include "gpio.c" /* AT91 built-in ADC */ #include "adc.c" /* SPI-based stuff */ #include "spi.c" /* accessory card support */ #include "mtac.c" #include "mtac_gpiob.c" #include "mtac_mfser.c" /* telit radio reset handling */ #include "telit_radio.c" /* reset button handling */ #define RESET_CHECK_PER_SEC 8 #define RESET_INTERVAL (HZ / RESET_CHECK_PER_SEC) #define RESET_HOLD_COUNT (RESET_CHECK_PER_SEC * 3) #define RESET_LONG_HOLD_COUNT (RESET_CHECK_PER_SEC * 30) 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 int reset_extra_long_signal = SIGHUP; static void reset_callback(struct work_struct *ignored); static DECLARE_DELAYED_WORK(reset_work, reset_callback); static void reset_callback(struct work_struct *ignored) { struct gpio_pin *pin; int reset_pressed = 0; struct pid *vpid = NULL; mutex_lock(&mts_io_mutex); pin = gpio_pin_by_name("DEVICE_RESET"); if (pin) { reset_pressed = !gpio_get_value(pin->pin.gpio); } if (reset_pid > 0) { vpid = find_vpid(reset_pid); } if (vpid) { if (reset_pressed) { reset_count++; } else { //Reset button has not been pressed if (reset_count > 0 && reset_count < RESET_HOLD_COUNT) { kill_pid(vpid, reset_short_signal, 1); reset_count = 0; } else if (reset_count >= RESET_HOLD_COUNT && reset_count < RESET_LONG_HOLD_COUNT) { reset_count = 0; kill_pid(vpid, reset_long_signal, 1); } } if (reset_count >= RESET_LONG_HOLD_COUNT) { reset_count = 0; kill_pid(vpid, reset_extra_long_signal, 1); } } else { reset_count = 0; } mutex_unlock(&mts_io_mutex); schedule_delayed_work(&reset_work, RESET_INTERVAL); } static ssize_t mts_attr_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 %d\n", reset_pid, reset_short_signal, reset_long_signal, reset_extra_long_signal); mutex_unlock(&mts_io_mutex); return ret; } static ssize_t mts_attr_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; int extra_long_signal; int result = sscanf(buf, "%i %i %i %i", &pid, &short_signal, &long_signal, &extra_long_signal); if (result < 3 || result > 4) { return -EINVAL; } if(result == 3) { mutex_lock(&mts_io_mutex); reset_pid = pid; reset_short_signal = short_signal; reset_long_signal = long_signal; mutex_unlock(&mts_io_mutex); } else { mutex_lock(&mts_io_mutex); reset_pid = pid; reset_short_signal = short_signal; reset_long_signal = long_signal; reset_extra_long_signal = extra_long_signal; mutex_unlock(&mts_io_mutex); } return count; } static DEVICE_ATTR_MTS(dev_attr_reset_monitor, "reset-monitor", mts_attr_show_reset_monitor, mts_attr_store_reset_monitor); static DEVICE_ATTR_RO_MTS(dev_attr_reset, "reset", mts_attr_show_gpio_pin); /* active-low socket modem reset */ static ssize_t mts_attr_store_radio_reset(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int value; int err; struct gpio_pin *pin; if (sscanf(buf, "%i", &value) != 1) { return -EINVAL; } if (value != 0) { return -EINVAL; } pin = gpio_pin_by_name("RADIO_RESET"); if (!pin) { return -ENODEV; } mutex_lock(&mts_io_mutex); // 250ms low reset err = reset_gpio_pin(pin, 250, 0); mutex_unlock(&mts_io_mutex); if (err) { return err; } return count; } static ssize_t mts_attr_store_ndc_reset(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int value; int err; struct gpio_pin *pin; if (sscanf(buf, "%i", &value) != 1) { return -EINVAL; } if (value != 0) { return -EINVAL; } pin = gpio_pin_by_name("NDC_RESET"); if (!pin) { return -ENODEV; } mutex_lock(&mts_io_mutex); // 1ms low reset err = reset_gpio_pin(pin, 1, 0); mutex_unlock(&mts_io_mutex); if (err) { return err; } return count; } static DEVICE_ATTR_MTS(dev_attr_radio_reset, "radio-reset", mts_attr_show_gpio_pin, mts_attr_store_radio_reset); static DEVICE_ATTR_MTS(dev_attr_ndc_reset, "ndc-reset", mts_attr_show_gpio_pin, mts_attr_store_ndc_reset); /* shared gpio attributes */ static DEVICE_ATTR_MTS(dev_attr_radio_power, "radio-power", mts_attr_show_gpio_pin, mts_attr_store_gpio_pin); static DEVICE_ATTR_MTS(dev_attr_eth0_enabled, "eth0-enabled", mts_attr_show_gpio_pin, mts_attr_store_gpio_pin); static DEVICE_ATTR_MTS(dev_attr_bt_enabled, "bt-enabled", mts_attr_show_gpio_pin, mts_attr_store_gpio_pin); static DEVICE_ATTR_MTS(dev_attr_wlan_enabled, "wlan-enabled", mts_attr_show_gpio_pin, mts_attr_store_gpio_pin); static DEVICE_ATTR_RO_MTS(dev_attr_extserial_dtr, "extserial-dtr", mts_attr_show_gpio_pin); static DEVICE_ATTR_MTS(dev_attr_extserial_dsr_gpio, "extserial-dsr", mts_attr_show_gpio_pin, mts_attr_store_gpio_pin); static DEVICE_ATTR_MTS(dev_attr_extserial_ri_gpio, "extserial-ri", mts_attr_show_gpio_pin, mts_attr_store_gpio_pin); static DEVICE_ATTR_MTS(dev_attr_extserial_dcd_gpio, "extserial-dcd", mts_attr_show_gpio_pin, mts_attr_store_gpio_pin); /* shared gpio-based LEDs */ static DEVICE_ATTR_MTS(dev_attr_led_status, "led-status", mts_attr_show_gpio_pin, mts_attr_store_gpio_pin); static DEVICE_ATTR_MTS(dev_attr_led_a_gpio, "led-a", mts_attr_show_gpio_pin, mts_attr_store_gpio_pin); #if LED_LS_CONTROLLABLE static DEVICE_ATTR_MTS(dev_attr_led_ls, "led-ls", mts_attr_show_gpio_pin, mts_attr_store_gpio_pin); #else static DEVICE_ATTR_RO_MTS(dev_attr_led_ls, "led-ls", mts_attr_show_gpio_pin); #endif static DEVICE_ATTR_MTS(dev_attr_led_wifi_gpio, "led-wifi", mts_attr_show_gpio_pin, mts_attr_store_gpio_pin); static DEVICE_ATTR_MTS(dev_attr_led_b_gpio, "led-b", mts_attr_show_gpio_pin, mts_attr_store_gpio_pin); static DEVICE_ATTR_MTS(dev_attr_led_cd_gpio, "led-cd", mts_attr_show_gpio_pin, mts_attr_store_gpio_pin); static DEVICE_ATTR_MTS(dev_attr_led_c_gpio, "led-c", mts_attr_show_gpio_pin, mts_attr_store_gpio_pin); static DEVICE_ATTR_MTS(dev_attr_led_sig1_gpio, "led-sig1", mts_attr_show_gpio_pin, mts_attr_store_gpio_pin); static DEVICE_ATTR_MTS(dev_attr_led_sig2_gpio, "led-sig2", mts_attr_show_gpio_pin, mts_attr_store_gpio_pin); static DEVICE_ATTR_MTS(dev_attr_led_sig3_gpio, "led-sig3", mts_attr_show_gpio_pin, mts_attr_store_gpio_pin); static DEVICE_ATTR_MTS(dev_attr_led_d_gpio, "led-d", mts_attr_show_gpio_pin, mts_attr_store_gpio_pin); static DEVICE_ATTR_MTS(dev_attr_led_e_gpio, "led-e", mts_attr_show_gpio_pin, mts_attr_store_gpio_pin); static DEVICE_ATTR_MTS(dev_attr_led_f_gpio, "led-f", mts_attr_show_gpio_pin, mts_attr_store_gpio_pin); /* eeprom info */ static ssize_t mts_attr_show_product_info(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t value; if (strcmp(attr->attr.name, "vendor-id") == 0) { value = sprintf(buf, "%.32s\n", id_eeprom.vendor_id); } else if (strcmp(attr->attr.name, "product-id") == 0) { value = sprintf(buf, "%.32s\n", id_eeprom.product_id); } else if (strcmp(attr->attr.name, "device-id") == 0) { value = sprintf(buf, "%.32s\n", id_eeprom.device_id); } else if (strcmp(attr->attr.name, "hw-version") == 0) { value = sprintf(buf, "%.32s\n", id_eeprom.hw_version); } else if (strcmp(attr->attr.name, "imei") == 0) { value = sprintf(buf, "%.32s\n", id_eeprom.imei); } else if (strcmp(attr->attr.name, "mac-wifi") == 0) { value = sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X\n", id_eeprom.mac_wifi[0], id_eeprom.mac_wifi[1], id_eeprom.mac_wifi[2], id_eeprom.mac_wifi[3], id_eeprom.mac_wifi[4], id_eeprom.mac_wifi[5]); } else if (strcmp(attr->attr.name, "mac-eth") == 0) { value = sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X\n", id_eeprom.mac_addr[0], id_eeprom.mac_addr[1], id_eeprom.mac_addr[2], id_eeprom.mac_addr[3], id_eeprom.mac_addr[4], id_eeprom.mac_addr[5]); } else { log_error("attribute '%s' not found", attr->attr.name); value = -1; } return value; } static DEVICE_ATTR_RO_MTS(dev_attr_vendor_id, "vendor-id", mts_attr_show_product_info); static DEVICE_ATTR_RO_MTS(dev_attr_product_id, "product-id", mts_attr_show_product_info); static DEVICE_ATTR_RO_MTS(dev_attr_device_id, "device-id", mts_attr_show_product_info); static DEVICE_ATTR_RO_MTS(dev_attr_hw_version, "hw-version", mts_attr_show_product_info); static DEVICE_ATTR_RO_MTS(dev_attr_imei, "imei", mts_attr_show_product_info); static DEVICE_ATTR_RO_MTS(dev_attr_wifi_mac, "mac-wifi", mts_attr_show_product_info); static DEVICE_ATTR_RO_MTS(dev_attr_eth_mac, "mac-eth", mts_attr_show_product_info); /* include per-device pins and attributes */ //#include "mtr2.c" #include "mtr.c" #include "mtr2d2.c" /* currently not supported #include "mtcdp.c" #include "mt100eocg.c" */ static bool load_port(int port) { int port_index = port - 1; memcpy(&ap_eeprom[port_index], mts_ap_eeprom[port_index], sizeof(mts_ap_eeprom[port_index])); if (mts_ap_eeprom[port_index][0] == 0xFF) { log_error("uninitialized eeprom on accessory card %d", port); } else if (mts_ap_eeprom[port_index][0] == 0x00) { log_info("no accessory card inserted in port %d", port); } else { if (strstr(ap_eeprom[port_index].product_id, PRODUCT_ID_MTAC_GPIOB)) { port_info[port_index] = &gpiob_info; } else if (strstr(ap_eeprom[port_index].product_id, PRODUCT_ID_MTAC_MFSER)) { port_info[port_index] = &mfser_info; } else { log_error("unknown accessory card [%s] in port %d", ap_eeprom[port_index].product_id, port); return false; } log_info("accessory card %d vendor-id: %.32s", port, ap_eeprom[port_index].vendor_id); log_info("accessory card %d product-id: %.32s", port, ap_eeprom[port_index].product_id); log_info("accessory card %d device-id: %.32s", port, ap_eeprom[port_index].device_id); log_info("accessory card %d hw-version: %.32s", port, ap_eeprom[port_index].hw_version); if (strncmp(ap_eeprom[port_index].product_id, PRODUCT_ID_MTAC_ETH, strlen(PRODUCT_ID_MTAC_ETH)) == 0) { log_info("accessory card %d mac-addr: %02X:%02X:%02X:%02X:%02X:%02X", port, ap_eeprom[port_index].mac_addr[0], ap_eeprom[port_index].mac_addr[1], ap_eeprom[port_index].mac_addr[2], ap_eeprom[port_index].mac_addr[3], ap_eeprom[port_index].mac_addr[4], ap_eeprom[port_index].mac_addr[5]); } if (! port_info[port_index]->setup(port)) { log_error("accessory port %d setup failed", port); port_info[port_index]->teardown(port); return false; } } return true; } static void init_accessory_ports(void) { int i; for (i = 1; i <= NUM_AP; i++) { if (! load_port(i)) { log_error("failed to load accessory card in port %d", i); } } } static int mts_id_eeprom_load(void) { memcpy(&id_eeprom, mts_id_eeprom, sizeof(mts_id_eeprom)); mts_product_id = MTCDP_E1_DK_1_0; has_spi_sout = 1; has_spi_din = 1; has_spi_dout = 1; has_spi_temp = 1; if (mts_id_eeprom[0] == 0xFF) { log_error("uninitialized eeprom"); return -EIO; /* } else if (mts_id_eeprom[0] == 0x00) { strncpy(id_eeprom.vendor_id, VENDOR_ID_MULTITECH, sizeof(id_eeprom.vendor_id) - 1); strncpy(id_eeprom.product_id, PRODUCT_ID_MTCDP_E1_DK, sizeof(id_eeprom.product_id) - 1); strncpy(id_eeprom.device_id, "", sizeof(id_eeprom.device_id) - 1); strncpy(id_eeprom.hw_version, HW_VERSION_MTCDP_0_0, sizeof(id_eeprom.hw_version) - 1); DEVICE_CAPA_SET(id_eeprom.capa, CAPA_GPS); attr_group = &mtcdp_platform_attribute_group; gpio_pins = gpio_pins_mtcdp_0_0; mts_product_id = MTCDP_E1_DK_0_0; log_info("detected board %s", HW_VERSION_MTCDP_0_0); } else if (strncmp(id_eeprom.product_id, PRODUCT_ID_MT100EOCG, strlen(PRODUCT_ID_MT100EOCG)) == 0) { attr_group = &mt100eocg_platform_attribute_group; gpio_pins = gpio_pins_mt100eocg_0_0; mts_product_id = MT100EOCG_0_0; has_spi_sout = 0; has_spi_din = 1; has_spi_dout = 1; has_spi_temp = 1; log_info("detected board %s", HW_VERSION_MT100EOCG_0_0); } else if (strncmp(id_eeprom.hw_version, HW_VERSION_MTR2_0_0, strlen(HW_VERSION_MTR2_0_0)) == 0) { attr_group = &mtr2_platform_attribute_group; gpio_pins = gpio_pins_mtr2_0_0; mts_product_id = MTR2_0_0; has_spi_sout = 0; has_spi_din = 0; has_spi_dout = 0; has_spi_temp = 1; log_info("detected board %s", HW_VERSION_MTR2_0_0); */ } else if (strncmp(id_eeprom.hw_version, HW_VERSION_MTR_0_0, strlen(HW_VERSION_MTR_0_0)) == 0) { attr_group = &mtr_platform_attribute_group; gpio_pins = gpio_pins_mtr_0_0; mts_product_id = MTR_0_0; has_spi_sout = 0; has_spi_din = 0; has_spi_dout = 0; has_spi_temp = 0; log_info("detected board %s", HW_VERSION_MTR_0_0); } else if (strncmp(id_eeprom.hw_version, HW_VERSION_MTR_0_1, strlen(HW_VERSION_MTR_0_1)) == 0) { attr_group = &mtr_platform_attribute_group; gpio_pins = gpio_pins_mtr_0_1; mts_product_id = MTR_0_1; has_spi_sout = 0; has_spi_din = 0; has_spi_dout = 0; has_spi_temp = 0; log_info("detected board %s", HW_VERSION_MTR_0_1); /* } else if (strncmp(id_eeprom.hw_version, HW_VERSION_MTOCGD2_0_0, strlen(HW_VERSION_MTOCGD2_0_0)) == 0) { attr_group = &mtr2_platform_attribute_group; gpio_pins = gpio_pins_mtr2_0_0; mts_product_id = MTOCGD2_0_0; has_spi_sout = 0; has_spi_din = 0; has_spi_dout = 0; has_spi_temp = 1; log_info("detected board %s", HW_VERSION_MTOCGD2_0_0); */ } else if (strncmp(id_eeprom.hw_version, HW_VERSION_MTOCGD_0_0, strlen(HW_VERSION_MTOCGD_0_0)) == 0) { attr_group = &mtr_platform_attribute_group; gpio_pins = gpio_pins_mtr_0_0; mts_product_id = MTOCGD_0_0; has_spi_sout = 0; has_spi_din = 0; has_spi_dout = 0; has_spi_temp = 0; log_info("detected board %s", HW_VERSION_MTOCGD_0_0); } else if (strncmp(id_eeprom.hw_version, HW_VERSION_MTOCGD_0_1, strlen(HW_VERSION_MTOCGD_0_1)) == 0) { attr_group = &mtr_platform_attribute_group; gpio_pins = gpio_pins_mtr_0_1; mts_product_id = MTOCGD_0_1; has_spi_sout = 0; has_spi_din = 0; has_spi_dout = 0; has_spi_temp = 0; log_info("detected board %s", HW_VERSION_MTOCGD_0_1); } else if (strncmp(id_eeprom.hw_version, HW_VERSION_MTR2D2_0_0, strlen(HW_VERSION_MTR2D2_0_0)) == 0) { // need to put any accessory card attributes into this list so they show up in sysfs // the port_info->setup callback does this device_attrs = mtr2d2_platform_attributes; device_attrs_size = mtr2d2_platform_attributes_size; device_attrs_max_size = mtr2d2_platform_attributes_max_size; attr_group = &mtr2d2_platform_attribute_group; gpio_pins = gpio_pins_mtr2d2_0_0; mts_product_id = MTR2D2_0_0; has_spi_sout = 0; has_spi_din = 0; has_spi_dout = 0; has_spi_temp = 0; log_info("detected board %s", HW_VERSION_MTR2D2_0_0); /* } else { attr_group = &mtcdp_platform_attribute_group; gpio_pins = gpio_pins_mtcdp_1_0; mts_product_id = MTCDP_E1_DK_1_0; has_spi_sout = 1; has_spi_din = 1; has_spi_dout = 1; has_spi_temp = 1; log_info("detected board %s", HW_VERSION_MTCDP_1_0); */ } log_info("sizeof: %lu", (unsigned long) sizeof(struct mts_id_eeprom_layout)); log_info("vendor-id: %.32s", id_eeprom.vendor_id); log_info("product-id: %.32s", id_eeprom.product_id); log_info("device-id: %.32s", id_eeprom.device_id); log_info("hw-version: %.32s", id_eeprom.hw_version); log_info("mac-addr: %02X:%02X:%02X:%02X:%02X:%02X", id_eeprom.mac_addr[0], id_eeprom.mac_addr[1], id_eeprom.mac_addr[2], id_eeprom.mac_addr[3], id_eeprom.mac_addr[4], id_eeprom.mac_addr[5]); log_info("imei: %.32s", id_eeprom.imei); log_info("capa-gps: %s", DEVICE_CAPA(id_eeprom.capa, CAPA_GPS) ? "yes" : "no"); log_info("capa-din: %s", DEVICE_CAPA(id_eeprom.capa, CAPA_DIN) ? "yes" : "no"); log_info("capa-dout: %s", DEVICE_CAPA(id_eeprom.capa, CAPA_DOUT) ? "yes" : "no"); log_info("capa-adc: %s", DEVICE_CAPA(id_eeprom.capa, CAPA_ADC) ? "yes" : "no"); log_info("capa-wifi: %s", DEVICE_CAPA(id_eeprom.capa, CAPA_WIFI) ? "yes" : "no"); log_info("capa-bluetooth: %s", DEVICE_CAPA(id_eeprom.capa, CAPA_BLUETOOTH) ? "yes" : "no"); if (DEVICE_CAPA(id_eeprom.capa, CAPA_BLUETOOTH)) { log_info("mac-bluetooth: %02X:%02X:%02X:%02X:%02X:%02X", id_eeprom.mac_bluetooth[0], id_eeprom.mac_bluetooth[1], id_eeprom.mac_bluetooth[2], id_eeprom.mac_bluetooth[3], id_eeprom.mac_bluetooth[4], id_eeprom.mac_bluetooth[5]); } if (DEVICE_CAPA(id_eeprom.capa, CAPA_WIFI)) { log_info("mac-wifi: %02X:%02X:%02X:%02X:%02X:%02X", id_eeprom.mac_wifi[0], id_eeprom.mac_wifi[1], id_eeprom.mac_wifi[2], id_eeprom.mac_wifi[3], id_eeprom.mac_wifi[4], id_eeprom.mac_wifi[5]); } return 0; } static int __init mts_io_init(void) { struct gpio_pin *pin; int ret; size_t device_attributes_size; size_t card_attributes_size; int i; log_info("init: " DRIVER_VERSION); ret = mts_id_eeprom_load(); if (ret) { goto error1; } if (NUM_AP) { for (i = 0; i < NUM_AP; i++) { port_info[i] = NULL; } init_accessory_ports(); } 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; } /* preserve backwards compatibility with old mtcdp platform name */ ret = sysfs_create_link(&mts_io_platform_device->dev.parent->kobj, &mts_io_platform_device->dev.kobj, "mtcdp"); if (ret) { log_error("sysfs_create_link failed: %d", ret); goto error3; } ret = sysfs_create_group(&mts_io_platform_device->dev.kobj, attr_group); if (ret) { goto error4; } if ( has_spi_sout ) { ret = spi_register_driver(&mts_spi_sout_driver); if (ret) { goto error5; } } if ( has_spi_dout ) { ret = spi_register_driver(&mts_spi_dout_driver); if (ret) { goto error6; } } if ( has_spi_din ) { ret = spi_register_driver(&mts_spi_din_driver); if (ret) { goto error7; } } if ( has_spi_temp ) { ret = spi_register_driver(&mts_spi_board_temp_driver); if (ret) { goto error8; } } /* ADC Setup */ #ifdef CONFIG_SOC_AT91SAM9X5 adc_base = ioremap(AT91SAM9X5_BASE_ADC, SZ_16K); #else adc_base = ioremap(AT91SAM9260_BASE_ADC, SZ_16K); #endif if (!adc_base) { goto error9; } adc_clk = clk_get(NULL, "adc_clk"); if (!adc_clk) { goto error10; } clk_enable(adc_clk); ADC_CONVERT_RESET(adc_base); writel(ADC_MODE_DEFAULT, adc_base + ADC_MR_OFFSET); writel(0x000F0F0F, adc_base + ADC_IDR_OFFSET); if (!DEVICE_CAPA(id_eeprom.capa, CAPA_ADC)) { writel(0x0F, adc_base + ADC_CHDR_OFFSET); } for (pin = gpio_pins; *pin->name; pin++) { ret = gpio_request_one(pin->pin.gpio, pin->pin.flags, pin->pin.label); if (ret) { log_debug("could not request pin %s (%d) but it could have already been requested under a different pin name", pin->name, ret); } } if ( has_spi_sout || has_spi_dout || has_spi_din ) { pin = gpio_pin_by_name("ENIO"); if (pin) { gpio_set_value(pin->pin.gpio, 0); } } // No reset_callback for MT100EOCG if ( mts_product_id != MT100EOCG_0_0 ) { reset_callback(NULL); } return 0; error10: iounmap(adc_base); error9: if ( has_spi_temp ) { spi_unregister_driver(&mts_spi_board_temp_driver); } error8: if ( has_spi_din ) { spi_unregister_driver(&mts_spi_din_driver); } error7: if ( has_spi_dout ) { spi_unregister_driver(&mts_spi_dout_driver); } error6: if ( has_spi_sout ) { spi_unregister_driver(&mts_spi_sout_driver); } error5: sysfs_remove_group(&mts_io_platform_device->dev.kobj, attr_group); error4: sysfs_remove_link(&mts_io_platform_device->dev.parent->kobj, "mtcdp"); error3: platform_device_del(mts_io_platform_device); error2: platform_device_put(mts_io_platform_device); error1: log_error("init failed: %d", ret); for (i = 0; i < NUM_AP; i++) { if (port_info[i]) { port_info[i]->teardown(i); } } return ret; } static void __exit mts_io_exit(void) { int i; if ( mts_product_id != MT100EOCG_0_0 ) { cancel_delayed_work_sync(&reset_work); } iounmap(adc_base); clk_disable(adc_clk); clk_put(adc_clk); if (has_spi_temp) spi_unregister_driver(&mts_spi_board_temp_driver); if (has_spi_din) spi_unregister_driver(&mts_spi_din_driver); if (has_spi_dout) spi_unregister_driver(&mts_spi_dout_driver); if (has_spi_sout) spi_unregister_driver(&mts_spi_sout_driver); sysfs_remove_group(&mts_io_platform_device->dev.kobj, attr_group); sysfs_remove_link(&mts_io_platform_device->dev.parent->kobj, "mtcdp"); platform_device_unregister(mts_io_platform_device); for (i = 0; i < NUM_AP; i++) { if (port_info[i]) { port_info[i]->teardown(i); } } log_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"); MODULE_ALIAS("mts-io-sout"); MODULE_ALIAS("mts-io-board-temp"); MODULE_ALIAS("mts-io-dout"); MODULE_ALIAS("mts-io-din"); MODULE_ALIAS("mts-io-ap1-dout"); MODULE_ALIAS("mts-io-ap1-din"); MODULE_ALIAS("mts-io-ap1-adc"); MODULE_ALIAS("mts-io-ap2-dout"); MODULE_ALIAS("mts-io-ap2-din"); MODULE_ALIAS("mts-io-ap2-adc");