/* * MTS-IO Controller * * Copyright (C) 2013 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 "mts_io.h" #define DRIVER_VERSION "v0.8.2" #define DRIVER_AUTHOR "James Maki " #define DRIVER_DESC "MTS-IO Controller" #define DRIVER_NAME "mts-io" #define PLATFORM_NAME "mts-io" #define DEBUG 0 #define __log(level, name, format, args...) \ printk(level "[" name "] " DRIVER_NAME ":%s:%d: " format "\n" , \ __func__ , __LINE__ , ## args) #define log_emerg(format, args...) __log(KERN_EMERG, "EMERG", format , ## args) #define log_alert(format, args...) __log(KERN_ALERT, "ALERT", format , ## args) #define log_crit(format, args...) __log(KERN_CRIT, "CRIT", format , ## args) #define log_error(format, args...) __log(KERN_ERR, "ERROR", format , ## args) #define log_warning(format, args...) __log(KERN_WARNING, "WARNING", format , ## args) #define log_notice(format, args...) __log(KERN_NOTICE, "NOTICE", format , ## args) #define log_info(format, args...) __log(KERN_INFO, "INFO", format , ## args) #if DEBUG # define log_debug(format, args...) __log(KERN_DEBUG, "DEBUG", format , ## args) #else # define log_debug(format, args...) do {} while (0) #endif #define LED_STATUS_CONTROLLABLE_MTCDP 0 #define LED_LS_CONTROLLABLE 0 #define USBH2_PS_CONTROLLABLE 0 static int led_mode_status = LED_OFF; #define SOUT_LED_CD_BIT BIT(0) #define SOUT_EXTSERIAL_RI_BIT BIT(1) #define SOUT_EXTSERIAL_DSR_BIT BIT(2) #define SOUT_LED_DTR BIT(3) #define SOUT_LED_SIG1_BIT BIT(4) #define SOUT_LED_SIG2_BIT BIT(5) #define SOUT_LED_SIG3_BIT BIT(6) #define SOUT_EXTSERIAL_DCD_BIT BIT(7) /* on-board EEPROM */ extern uint8_t mts_id_eeprom[512]; static struct mts_id_eeprom_layout id_eeprom; /* daughter card EEPROM */ #ifdef MTOCGD2 extern uint8_t mts_dc_eeprom[512]; #else uint8_t mts_dc_eeprom[512] = {}; #endif static struct mts_dc_eeprom_layout dc_eeprom; bool daughter_card_capable = false; bool has_daughter_card = false; static uint8_t mts_product_id; static uint8_t mts_dc_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 attribute_group *attr_group; static struct gpio_pin *gpio_pins; static struct gpio_pin gpio_pins_mtcdp_0_0[] = { { .name = "ENIO", .pin = AT91_PIN_PC15, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, }, { .name = "ETH0_ENABLED", .attr_name = "eth0-enabled", .pin = AT91_PIN_PB31, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, }, { .name = "RADIO_RESET", .attr_name = "radio-reset", .pin = AT91_PIN_PB30, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, }, { .name = "DEVICE_RESET", .attr_name = "reset", .pin = AT91_PIN_PA22, .direction = GPIO_DIR_INPUT, .output_value = 0, .use_pullup = 0, .active_low = 1, }, { .name = "LS_LED", .attr_name = "led-ls", .pin = AT91_PIN_PC9, #if LED_LS_CONTROLLABLE .direction = GPIO_DIR_OUTPUT, #else .direction = GPIO_DIR_INPUT, #endif .output_value = 1, .use_pullup = 0, .active_low = 1, }, #if LED_STATUS_CONTROLLABLE_MTCDP { .name = "STATUS_LED", .attr_name = "led-status", .pin = AT91_PIN_PA30, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, }, #endif { .name = "RSERSRC", .attr_name = "rsersrc", .pin = AT91_PIN_PC7, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "DTR1", .attr_name = "extserial-dtr", .pin = AT91_PIN_PC10, .direction = GPIO_DIR_INPUT, .output_value = 0, .use_pullup = 0, .active_low = 1, }, { }, }; static struct gpio_pin gpio_pins_mtcdp_1_0[] = { { .name = "ENIO", .pin = AT91_PIN_PC15, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, }, { .name = "ETH0_ENABLED", .attr_name = "eth0-enabled", .pin = AT91_PIN_PB31, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, }, { .name = "RADIO_RESET", .attr_name = "radio-reset", .pin = AT91_PIN_PB30, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, }, { .name = "DEVICE_RESET", .attr_name = "reset", .pin = AT91_PIN_PA22, .direction = GPIO_DIR_INPUT, .output_value = 0, .use_pullup = 0, .active_low = 1, }, { .name = "LS_LED", .attr_name = "led-ls", .pin = AT91_PIN_PC9, #if LED_LS_CONTROLLABLE .direction = GPIO_DIR_OUTPUT, #else .direction = GPIO_DIR_INPUT, #endif .output_value = 1, .use_pullup = 0, .active_low = 1, }, #if LED_STATUS_CONTROLLABLE_MTCDP { .name = "STATUS_LED", .attr_name = "led-status", .pin = AT91_PIN_PA30, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, }, #endif { .name = "RSERSRC", .attr_name = "rsersrc", .pin = AT91_PIN_PC7, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "TXD1", .pin = AT91_PIN_PB17, .direction = GPIO_DIR_INPUT, .output_value = 0, .use_pullup = 0, }, { .name = "DTR1", .attr_name = "extserial-dtr", .pin = AT91_PIN_PB18, .direction = GPIO_DIR_INPUT, .output_value = 0, .use_pullup = 0, .active_low = 1, }, { .name = "USBH2_PS_OC", .attr_name = "usbh2-ps-oc", .pin = AT91_PIN_PB19, .direction = GPIO_DIR_INPUT, .output_value = 0, .use_pullup = 0, .active_low = 1, }, #if USBH2_PS_CONTROLLABLE { .name = "USBH2_PS_ENABLED", .attr_name = "usbh2-ps-enabled", .pin = AT91_PIN_PB20, .direction = GPIO_DIR_OUTPUT, .output_value = 0, .use_pullup = 0, .active_low = 1, }, #endif { .name = "NDC_RESET", .attr_name = "ndc-reset", .pin = AT91_PIN_PB21, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, }, { .name = "ADC0", .pin = AT91_PIN_PC0, .direction = GPIO_DIR_INPUT, .output_value = 0, .use_pullup = 0, }, { .name = "ADC1", .pin = AT91_PIN_PC1, .direction = GPIO_DIR_INPUT, .output_value = 0, .use_pullup = 0, }, { .name = "ADC2", .pin = AT91_PIN_PC2, .direction = GPIO_DIR_INPUT, .output_value = 0, .use_pullup = 0, }, { .name = "ADC3", .pin = AT91_PIN_PC3, .direction = GPIO_DIR_INPUT, .output_value = 0, .use_pullup = 0, }, { }, }; static struct gpio_pin gpio_pins_mt100eocg_0_0[] = { { .name = "ENIO", .pin = AT91_PIN_PC15, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, }, { .name = "ETH0_ENABLED", .attr_name = "eth0-enabled", .pin = AT91_PIN_PB31, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, }, { .name = "RADIO_RESET", .attr_name = "radio-reset", .pin = AT91_PIN_PB30, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, }, { .name = "DEVICE_RESET", .attr_name = "reset", .pin = AT91_PIN_PA22, .direction = GPIO_DIR_INPUT, .output_value = 0, .use_pullup = 0, .active_low = 1, }, { .name = "LED3", .attr_name = "led3", .pin = AT91_PIN_PC9, #if LED_LS_CONTROLLABLE .direction = GPIO_DIR_OUTPUT, #else .direction = GPIO_DIR_INPUT, #endif .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "LED2", .attr_name = "led2", .pin = AT91_PIN_PA30, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "RSERSRC", .attr_name = "rsersrc", .pin = AT91_PIN_PC7, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "TXD1", .pin = AT91_PIN_PB17, .direction = GPIO_DIR_INPUT, .output_value = 0, .use_pullup = 0, }, { .name = "DTR1", .attr_name = "extserial-dtr", .pin = AT91_PIN_PB18, .direction = GPIO_DIR_INPUT, .output_value = 0, .use_pullup = 0, .active_low = 1, }, { .name = "DCD1", .attr_name = "extserial-dcd", .pin = AT91_PIN_PB3, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "GPIO11", .attr_name = "gpio11", .pin = AT91_PIN_PB19, .direction = GPIO_DIR_OD, .output_value = 1, .use_pullup = 1, }, { .name = "GPIO12", .attr_name = "gpio12", .pin = AT91_PIN_PB20, .direction = GPIO_DIR_OD, .output_value = 1, .use_pullup = 1, }, { .name = "ADC0", .pin = AT91_PIN_PC0, .direction = GPIO_DIR_INPUT, .output_value = 0, .use_pullup = 0, }, { .name = "ADC1", .pin = AT91_PIN_PC1, .direction = GPIO_DIR_INPUT, .output_value = 0, .use_pullup = 0, }, { .name = "ADC2", .pin = AT91_PIN_PC2, .direction = GPIO_DIR_INPUT, .output_value = 0, .use_pullup = 0, }, { .name = "ADC3", .pin = AT91_PIN_PC3, .direction = GPIO_DIR_INPUT, .output_value = 0, .use_pullup = 0, }, { }, }; static struct gpio_pin gpio_pins_mtr2_0_0[] = { { .name = "NETH_RST", .attr_name = "eth-switch-enabled", .pin = AT91_PIN_PC6, .direction = GPIO_DIR_OD, .output_value = 1, .use_pullup = 0, }, { .name = "RADIO_RESET", .attr_name = "radio-reset", .pin = AT91_PIN_PC5, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, }, { .name = "RADIO_RESET", .attr_name = "radio-power", .pin = AT91_PIN_PC5, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, }, { .name = "DEVICE_RESET", .attr_name = "reset", .pin = AT91_PIN_PC4, .direction = GPIO_DIR_INPUT, .output_value = 0, .use_pullup = 0, .active_low = 1, }, { .name = "LS_LED", .attr_name = "led-ls", .pin = AT91_PIN_PA14, #if LED_LS_CONTROLLABLE .direction = GPIO_DIR_OUTPUT, #else .direction = GPIO_DIR_INPUT, #endif .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "STATUS_LED", .attr_name = "led-status", .pin = AT91_PIN_PA24, .direction = GPIO_DIR_OUTPUT, .output_value = 0, .use_pullup = 0, .active_low = 1, }, { .name = "LED7", .attr_name = "led-cd", .pin = AT91_PIN_PA25, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "LED7", .attr_name = "led-c", .pin = AT91_PIN_PA25, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "LED10", .attr_name = "led-sig1", .pin = AT91_PIN_PA26, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "LED10", .attr_name = "led-d", .pin = AT91_PIN_PA26, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "LED11", .attr_name = "led-sig2", .pin = AT91_PIN_PA27, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "LED11", .attr_name = "led-e", .pin = AT91_PIN_PA27, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "LED12", .attr_name = "led-sig3", .pin = AT91_PIN_PA28, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "LED12", .attr_name = "led-f", .pin = AT91_PIN_PA28, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "LED13", .attr_name = "led-wifi", .pin = AT91_PIN_PA29, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "LED13", .attr_name = "led-b", .pin = AT91_PIN_PA29, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "UART3_DTR", .attr_name = "extserial-dtr", .pin = AT91_PIN_PC12, .direction = GPIO_DIR_INPUT, .output_value = 0, .use_pullup = 0, .active_low = 1, }, { .name = "UART3_DSR", .attr_name = "extserial-dsr", .pin = AT91_PIN_PC11, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "UART3_DCD", .attr_name = "extserial-dcd", .pin = AT91_PIN_PC10, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "UART3_RI", .attr_name = "extserial-ri", .pin = AT91_PIN_PC13, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "NDC_RESET", .attr_name = "ndc-reset", .pin = AT91_PIN_PC3, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, }, { .name = "NDC_EEPROM_WRITE_PROTECT", .attr_name = "ndc-eeprom-wp", .pin = AT91_PIN_PC26, .direction = GPIO_DIR_OUTPUT, .output_value = 0, .use_pullup = 0, }, { .name = "BT_EN", .attr_name = "bt-enabled", .pin = AT91_PIN_PD21, .direction = GPIO_DIR_OUTPUT, .output_value = 0, .use_pullup = 0, }, { .name = "WLAN_EN", .attr_name = "wlan-enabled", .pin = AT91_PIN_PC1, .direction = GPIO_DIR_OUTPUT, .output_value = 0, .use_pullup = 0, }, { .name = "SERIAL_MODE0", .attr_name = "serial_mode", .pin = AT91_PIN_PC23, .direction = GPIO_DIR_OUTPUT, .output_value = 0, .use_pullup = 0, }, { .name = "SERIAL_MODE1", .attr_name = "serial_mode", .pin = AT91_PIN_PC24, .direction = GPIO_DIR_OUTPUT, .output_value = 0, .use_pullup = 0, }, { .name = "SERIAL_MODE2", .attr_name = "serial_mode", .pin = AT91_PIN_PC25, .direction = GPIO_DIR_OUTPUT, .output_value = 0, .use_pullup = 0, }, { .name = "RS4XX_TERM_RES", .attr_name = "rs4xx-term-res", .pin = AT91_PIN_PC26, .direction = GPIO_DIR_OUTPUT, .output_value = 0, .use_pullup = 0, }, { .name = "NDC_GPIO1", .attr_name = "dc-gpio1", .pin = AT91_PIN_PC0, .direction = GPIO_DIR_OUTPUT, .output_value = 0, .use_pullup = 0, }, { .name = "NDC_GPIO2", .attr_name = "dc-gpio2", .pin = AT91_PIN_PC14, .direction = GPIO_DIR_OUTPUT, .output_value = 0, .use_pullup = 0, }, { .name = "NDC_GPIO3", .attr_name = "dc-gpio3", .pin = AT91_PIN_PC29, .direction = GPIO_DIR_OUTPUT, .output_value = 0, .use_pullup = 0, }, { .name = "NDC_GPIO4", .attr_name = "dc-gpio4", .pin = AT91_PIN_PC30, .direction = GPIO_DIR_OUTPUT, .output_value = 0, .use_pullup = 0, }, { .name = "NDC_INTERRUPT1", .attr_name = "dc-int1", .pin = AT91_PIN_PC20, .direction = GPIO_DIR_INPUT, .output_value = 0, .use_pullup = 0, }, { .name = "NDC_INTERRUPT2", .attr_name = "dc-int2", .pin = AT91_PIN_PC21, .direction = GPIO_DIR_INPUT, .output_value = 0, .use_pullup = 0, }, { }, }; static struct gpio_pin gpio_pins_mtr_0_0[] = { { .name = "NETH_RST", .attr_name = "eth0-enabled", .pin = AT91_PIN_PC6, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, }, { .name = "PWRMON", .attr_name = "radio-power", .pin = AT91_PIN_PA23, .direction = GPIO_DIR_INPUT, .output_value = 0, .use_pullup = 0, .active_low = 0, }, { .name = "3G_RST", .attr_name = "radio-reset", .pin = AT91_PIN_PA22, .direction = GPIO_DIR_OD, .output_value = 1, .use_pullup = 0, }, { .name = "3G_ONOFF", .attr_name = "radio-enabled", .pin = AT91_PIN_PA21, .direction = GPIO_DIR_OD, .output_value = 1, .use_pullup = 0, }, { .name = "DEVICE_RESET", .attr_name = "reset", .pin = AT91_PIN_PC4, .direction = GPIO_DIR_INPUT, .output_value = 0, .use_pullup = 0, .active_low = 1, }, { .name = "LS_LED", .attr_name = "led-ls", .pin = AT91_PIN_PC16, #if LED_LS_CONTROLLABLE .direction = GPIO_DIR_OUTPUT, #else .direction = GPIO_DIR_INPUT, #endif .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "STATUS_LED", .attr_name = "led-status", .pin = AT91_PIN_PC21, .direction = GPIO_DIR_OUTPUT, .output_value = 0, .use_pullup = 0, .active_low = 1, }, { .name = "LED3", .attr_name = "led-wifi", .pin = AT91_PIN_PC15, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "LED3", .attr_name = "led-b", .pin = AT91_PIN_PC15, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "LED4", .attr_name = "led-cd", .pin = AT91_PIN_PC20, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "LED4", .attr_name = "led-c", .pin = AT91_PIN_PC20, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "LED6", .attr_name = "led-sig1", .pin = AT91_PIN_PC19, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "LED6", .attr_name = "led-d", .pin = AT91_PIN_PC19, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "LED7", .attr_name = "led-sig2", .pin = AT91_PIN_PC18, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "LED7", .attr_name = "led-e", .pin = AT91_PIN_PC18, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "LED8", .attr_name = "led-sig3", .pin = AT91_PIN_PC17, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "LED8", .attr_name = "led-f", .pin = AT91_PIN_PC17, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "RI_B", .attr_name = "extserial-ri", .pin = AT91_PIN_PC25, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "DTR_B", .attr_name = "extserial-dtr", .pin = AT91_PIN_PC26, .direction = GPIO_DIR_INPUT, .output_value = 0, .use_pullup = 0, .active_low = 1, }, { .name = "DSR_B", .attr_name = "extserial-dsr", .pin = AT91_PIN_PC27, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "DCD_B", .attr_name = "extserial-dcd", .pin = AT91_PIN_PC28, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "BT_EN", .attr_name = "bt-enabled", .pin = AT91_PIN_PA28, .direction = GPIO_DIR_OUTPUT, .output_value = 0, .use_pullup = 0, }, { .name = "WLAN_EN", .attr_name = "wlan-enabled", .pin = AT91_PIN_PA27, .direction = GPIO_DIR_OUTPUT, .output_value = 0, .use_pullup = 0, }, { }, }; static struct gpio_pin gpio_pins_mtr_0_1[] = { { .name = "NETH_RST", .attr_name = "eth0-enabled", .pin = AT91_PIN_PC6, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, }, { .name = "PWRMON", .attr_name = "radio-power", .pin = AT91_PIN_PA23, .direction = GPIO_DIR_INPUT, .output_value = 0, .use_pullup = 0, .active_low = 0, }, { .name = "3G_RST", .attr_name = "radio-reset", .pin = AT91_PIN_PA22, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 1, }, { .name = "3G_ONOFF", .attr_name = "radio-enabled", .pin = AT91_PIN_PA21, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 1, }, { .name = "DEVICE_RESET", .attr_name = "reset", .pin = AT91_PIN_PC4, .direction = GPIO_DIR_INPUT, .output_value = 0, .use_pullup = 0, .active_low = 1, }, { .name = "LS_LED", .attr_name = "led-ls", .pin = AT91_PIN_PC16, #if LED_LS_CONTROLLABLE .direction = GPIO_DIR_OUTPUT, #else .direction = GPIO_DIR_INPUT, #endif .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "STATUS_LED", .attr_name = "led-status", .pin = AT91_PIN_PC21, .direction = GPIO_DIR_OUTPUT, .output_value = 0, .use_pullup = 0, .active_low = 1, }, { .name = "LED3", .attr_name = "led-wifi", .pin = AT91_PIN_PC15, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "LED3", .attr_name = "led-b", .pin = AT91_PIN_PC15, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "LED4", .attr_name = "led-cd", .pin = AT91_PIN_PC20, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "LED4", .attr_name = "led-c", .pin = AT91_PIN_PC20, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "LED6", .attr_name = "led-sig1", .pin = AT91_PIN_PC19, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "LED6", .attr_name = "led-d", .pin = AT91_PIN_PC19, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "LED7", .attr_name = "led-sig2", .pin = AT91_PIN_PC18, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "LED7", .attr_name = "led-e", .pin = AT91_PIN_PC18, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "LED8", .attr_name = "led-sig3", .pin = AT91_PIN_PC17, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "LED8", .attr_name = "led-f", .pin = AT91_PIN_PC17, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "RI_B", .attr_name = "extserial-ri", .pin = AT91_PIN_PC25, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "DTR_B", .attr_name = "extserial-dtr", .pin = AT91_PIN_PC26, .direction = GPIO_DIR_INPUT, .output_value = 0, .use_pullup = 0, .active_low = 1, }, { .name = "DSR_B", .attr_name = "extserial-dsr", .pin = AT91_PIN_PC27, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "DCD_B", .attr_name = "extserial-dcd", .pin = AT91_PIN_PC28, .direction = GPIO_DIR_OUTPUT, .output_value = 1, .use_pullup = 0, .active_low = 1, }, { .name = "BT_EN", .attr_name = "bt-enabled", .pin = AT91_PIN_PA28, .direction = GPIO_DIR_OUTPUT, .output_value = 0, .use_pullup = 0, }, { .name = "WLAN_EN", .attr_name = "wlan-enabled", .pin = AT91_PIN_PA27, .direction = GPIO_DIR_OUTPUT, .output_value = 0, .use_pullup = 0, }, { }, }; struct gpio_pin *gpio_pin_by_name(const char *name) { struct gpio_pin *pin; for (pin = gpio_pins; *pin->name; pin++) { if (!strcmp(pin->name, name)) { return pin; } } log_error("pin named %s not found", name); return NULL; } struct gpio_pin *gpio_pin_by_attr_name(const char *name) { struct gpio_pin *pin; for (pin = gpio_pins; *pin->name; pin++) { if (!strcmp(pin->attr_name, name)) { return pin; } } log_error("pin with attr name %s not found", name); return NULL; } static struct spi_device *spi_sout_dev; static u8 spi_sout_value; static DEFINE_MUTEX(spi_sout_mutex); static unsigned int sout_max_speed_hz = 1 * 1000 * 1000; module_param(sout_max_speed_hz, uint, S_IRUGO); MODULE_PARM_DESC( sout_max_speed_hz, "Maximum clock rate to be used with this device (default: 1 MHz)" ); static struct spi_device *spi_dout_dev; static u8 spi_dout_value; static DEFINE_MUTEX(spi_dout_mutex); static unsigned int dout_max_speed_hz = 1 * 1000 * 1000; module_param(dout_max_speed_hz, uint, S_IRUGO); MODULE_PARM_DESC( dout_max_speed_hz, "Maximum clock rate to be used with this device (default: 1 MHz)" ); static struct spi_device *spi_din_dev; static unsigned int din_max_speed_hz = 1 * 1000 * 1000; module_param(din_max_speed_hz, uint, S_IRUGO); MODULE_PARM_DESC( din_max_speed_hz, "Maximum clock rate to be used with this device (default: 1 MHz)" ); static struct spi_device *spi_dc_dout_dev; static u8 spi_dc_dout_value; static DEFINE_MUTEX(spi_dc_dout_mutex); static unsigned int dc_dout_max_speed_hz = 1 * 1000 * 1000; module_param(dc_dout_max_speed_hz, uint, S_IRUGO); MODULE_PARM_DESC( dc_dout_max_speed_hz, "Maximum clock rate to be used with this device (default: 1 MHz)" ); static struct spi_device *spi_dc_din_dev; static unsigned int dc_din_max_speed_hz = 1 * 1000 * 1000; module_param(dc_din_max_speed_hz, uint, S_IRUGO); MODULE_PARM_DESC( dc_din_max_speed_hz, "Maximum clock rate to be used with this device (default: 1 MHz)" ); static struct spi_device *spi_dc_adc_dev; static unsigned int dc_adc_max_speed_hz = 20 * 1000 * 1000; module_param(dc_adc_max_speed_hz, uint, S_IRUGO); MODULE_PARM_DESC( dc_adc_max_speed_hz, "Maximum clock rate to be used with this device (default: 20 MHz)" ); static struct spi_device *spi_board_temp_dev; static unsigned int board_temp_max_speed_hz = 1 * 1000 * 1000; module_param(board_temp_max_speed_hz, uint, S_IRUGO); MODULE_PARM_DESC( board_temp_max_speed_hz, "Maximum clock rate to be used with this device (default: 1 MHz)" ); static inline int spi_writen(struct spi_device *spi, const u8 *buf, size_t len) { int tmp; u8 *tx; tx = kmalloc(len, GFP_KERNEL); if (!tx) { return -ENOMEM; } memcpy(tx, buf, len); tmp = spi_write(spi, tx, len); kfree(tx); return tmp; } static inline int spi_readn(struct spi_device *spi, u8 *buf, size_t len) { int tmp; u8 *rx; rx = kmalloc(len, GFP_KERNEL); if (!rx) { return -ENOMEM; } tmp = spi_read(spi, rx, len); memcpy(buf, rx, len); kfree(rx); return tmp; } #define ADC_SHTIME_DEFAULT 0x05 #define ADC_STARTUP_DEFAULT 0x04 #define ADC_PRESCALE_DEFAULT 0x3F #define ADC_MODE_DEFAULT \ ((ADC_SHTIME_DEFAULT & 0x0F) << 24) | \ ((ADC_STARTUP_DEFAULT & 0x1F) << 16) | \ ((ADC_PRESCALE_DEFAULT & 0x3F) << 8) #define ADC_CR_OFFSET 0x00 #define ADC_MR_OFFSET 0x04 #define ADC_CHER_OFFSET 0x10 #define ADC_CHDR_OFFSET 0x14 #define ADC_CHSR_OFFSET 0x18 #define ADC_SR_OFFSET 0x1C #define ADC_LDCR_OFFSET 0x20 #define ADC_IER_OFFSET 0x14 #define ADC_IDR_OFFSET 0x28 #define ADC_IMR_OFFSET 0x2C #define ADC_CDR0_OFFSET 0x30 #define ADC_CDR1_OFFSET 0x34 #define ADC_CDR2_OFFSET 0x38 #define ADC_CDR3_OFFSET 0x3C void __iomem *adc_base; struct clk *adc_clk; #define ADC_CONVERT_RESET(base) writel(0x01, (base) + ADC_CR_OFFSET) #define ADC_CONVERT_START(base) writel(0x02, (base) + ADC_CR_OFFSET) #define BLINK_PER_SEC 8 #define BLINK_INTERVAL (HZ / BLINK_PER_SEC) #define RESET_HOLD_COUNT (BLINK_PER_SEC * 3) #define RESET_LONG_HOLD_COUNT (BLINK_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 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) { 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 = !at91_get_gpio_value(pin->pin); } 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; } if (led_mode_status == LED_FLASHING) { pin = gpio_pin_by_name("STATUS_LED"); if (pin) { at91_set_gpio_value(pin->pin, !at91_get_gpio_value(pin->pin)); } } mutex_unlock(&mts_io_mutex); schedule_delayed_work(&blink_work, BLINK_INTERVAL); } static int radio_off_telit(void) { int value, ret; struct gpio_pin *pwrmon_pin = gpio_pin_by_name("PWRMON"); struct gpio_pin *onoff_pin = gpio_pin_by_name("3G_ONOFF"); struct gpio_pin *rst_pin = gpio_pin_by_name("3G_RST"); if (!onoff_pin || !pwrmon_pin || !rst_pin) { return -ENODEV; } value = at91_get_gpio_value(pwrmon_pin->pin); if(value == 0) { log_error("radio is already off"); return -EINVAL; } // drive on/off pin low for at least 3 sec log_info("shutting down radio"); ret = at91_set_gpio_output_with_pullup(onoff_pin->pin, 0, onoff_pin->use_pullup); if (ret) { return ret; } msleep(3500); // set on/off pin high ret = at91_set_gpio_output_with_pullup(onoff_pin->pin, 1, onoff_pin->use_pullup); if (ret) { return ret; } // wait for radio to power off msleep(5000); // check that power is low value = at91_get_gpio_value(pwrmon_pin->pin); if(value != 0) { log_warning("radio is still on. performing radio reset."); //Perform Hard Reset ret = at91_set_gpio_output_with_pullup(rst_pin->pin, 0, rst_pin->use_pullup); if (ret) { return ret; } msleep(500); // set pin high ret = at91_set_gpio_output_with_pullup(rst_pin->pin, 1, rst_pin->use_pullup); if (ret) { return ret; } } else { log_info("radio has been shut down"); } return ret; } static int radio_on_telit(void) { int value, ret; struct gpio_pin *pwrmon_pin = gpio_pin_by_name("PWRMON"); struct gpio_pin *onoff_pin = gpio_pin_by_name("3G_ONOFF"); struct gpio_pin *rst_pin = gpio_pin_by_name("3G_RST"); if (!onoff_pin || !pwrmon_pin || !rst_pin) { return -ENODEV; } value = at91_get_gpio_value(pwrmon_pin->pin); if(value != 0) { log_error("radio is already on"); return -EINVAL; } // drive on/off pin low for at least 5 sec log_info("turning on radio"); ret = at91_set_gpio_output_with_pullup(onoff_pin->pin, 0, onoff_pin->use_pullup); if (ret) { return ret; } msleep(5500); // set on/off pin high ret = at91_set_gpio_output_with_pullup(onoff_pin->pin, 1, onoff_pin->use_pullup); if (ret) { return ret; } msleep(200); // check that power is high value = at91_get_gpio_value(pwrmon_pin->pin); if(value == 0) { log_warning("radio is still off. performing radio reset"); //Perform Hard Reset ret = at91_set_gpio_output_with_pullup(rst_pin->pin, 0, rst_pin->use_pullup); if (ret) { return ret; } msleep(500); // set pin high ret = at91_set_gpio_output_with_pullup(rst_pin->pin, 1, rst_pin->use_pullup); if (ret) { return ret; } } else { log_info("radio has been turned on"); } return ret; } static ssize_t mts_attr_store_radio_power_telit(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) { mutex_lock(&mts_io_mutex); err = radio_off_telit(); mutex_unlock(&mts_io_mutex); } else { mutex_lock(&mts_io_mutex); err = radio_on_telit(); mutex_unlock(&mts_io_mutex); } if (err) { return err; } return count; } static int radio_reset_telit(void) { int ret; struct gpio_pin *rst_pin = gpio_pin_by_name("3G_RST"); struct gpio_pin *onoff_pin = gpio_pin_by_name("3G_ONOFF"); if (!rst_pin || !onoff_pin) { return -ENODEV; } // drive reset pin low for 500ms ret = at91_set_gpio_output_with_pullup(rst_pin->pin, 0, rst_pin->use_pullup); if (ret) { return ret; } msleep(500); // set pin high ret = at91_set_gpio_output_with_pullup(rst_pin->pin, 1, rst_pin->use_pullup); if (ret) { return ret; } // wait for 2 sec before toggling on/off pin msleep(2000); // drive on/off pin low for 6 sec ret = at91_set_gpio_output_with_pullup(onoff_pin->pin, 0, onoff_pin->use_pullup); if (ret) { return ret; } msleep(6000); // set on/off pin high ret = at91_set_gpio_output_with_pullup(onoff_pin->pin, 1, onoff_pin->use_pullup); if (ret) { return ret; } return ret; } static int radio_reset(void) { int ret; struct gpio_pin *pin = gpio_pin_by_name("RADIO_RESET"); if (!pin) { return -ENODEV; } ret = at91_set_gpio_value(pin->pin, 0); if (ret) { return ret; } mdelay(250); ret = at91_set_gpio_value(pin->pin, 1); return ret; } static int ndc_reset(void) { int ret; struct gpio_pin *pin = gpio_pin_by_name("NDC_RESET"); if (!pin) { return -ENODEV; } ret = at91_set_gpio_value(pin->pin, 0); if (ret) { return ret; } mdelay(1); ret = at91_set_gpio_value(pin->pin, 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 mts_attr_show_gpio_pin(struct device *dev, struct device_attribute *attr, char *buf) { int value; struct gpio_pin *pin = gpio_pin_by_attr_name(attr->attr.name); if (!pin) { return -ENODEV; } mutex_lock(&mts_io_mutex); value = at91_get_gpio_value(pin->pin); mutex_unlock(&mts_io_mutex); if (value < 0) { return value; } if (pin->active_low) { value = !value; } return sprintf(buf, "%d\n", value); } static ssize_t mts_attr_store_gpio_pin(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int value; int err; struct gpio_pin *pin = gpio_pin_by_attr_name(attr->attr.name); if (!pin) { return -ENODEV; } if (sscanf(buf, "%i", &value) != 1) { return -EINVAL; } if (pin->active_low) { value = !value; } mutex_lock(&mts_io_mutex); err = at91_set_gpio_value(pin->pin, value); mutex_unlock(&mts_io_mutex); if (err) { return err; } return count; } 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; 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 ssize_t mts_attr_store_radio_reset_telit(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_telit(); mutex_unlock(&mts_io_mutex); if (err) { return err; } return count; } 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_radio_reset, "radio-reset", mts_attr_show_gpio_pin, mts_attr_store_radio_reset); 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; if (sscanf(buf, "%i", &value) != 1) { return -EINVAL; } if (value != 0) { return -EINVAL; } mutex_lock(&mts_io_mutex); err = ndc_reset(); mutex_unlock(&mts_io_mutex); if (err) { return err; } return count; } static DEVICE_ATTR_MTS(dev_attr_ndc_reset, "ndc-reset", mts_attr_show_gpio_pin, mts_attr_store_ndc_reset); 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 ssize_t mts_attr_store_sout(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int value; u8 bit; if (!spi_sout_dev) { log_notice("sout device not present"); return -ENODEV; } if (!strcmp(attr->attr.name, "extserial-ri")) { bit = SOUT_EXTSERIAL_RI_BIT; } else if (!strcmp(attr->attr.name, "extserial-dsr")) { bit = SOUT_EXTSERIAL_DSR_BIT; } else if (!strcmp(attr->attr.name, "extserial-dcd")) { bit = SOUT_EXTSERIAL_DCD_BIT; } else if (!strcmp(attr->attr.name, "led-cd") || !strcmp(attr->attr.name, "led-sdk-b")) { bit = SOUT_LED_CD_BIT; } else if (!strcmp(attr->attr.name, "led-dtr") || !strcmp(attr->attr.name, "led-sdk-f")) { bit = SOUT_LED_DTR; } else if (!strcmp(attr->attr.name, "led-sig1") || !strcmp(attr->attr.name, "led-sdk-c")) { bit = SOUT_LED_SIG1_BIT; } else if (!strcmp(attr->attr.name, "led-sig2") || !strcmp(attr->attr.name, "led-sdk-d")) { bit = SOUT_LED_SIG2_BIT; } else if (!strcmp(attr->attr.name, "led-sig3") || !strcmp(attr->attr.name, "led-sdk-e")) { bit = SOUT_LED_SIG3_BIT; } else { log_notice("sout attr does not exist"); return -ENOENT; } if (sscanf(buf, "%i", &value) != 1) { log_notice("sout attr invalid argument"); return -EINVAL; } mutex_lock(&spi_sout_mutex); if (value) { spi_sout_value &= ~bit; } else { spi_sout_value |= bit; } spi_writen(spi_sout_dev, &spi_sout_value, 1); mutex_unlock(&spi_sout_mutex); return count; } static ssize_t mts_attr_show_sout(struct device *dev, struct device_attribute *attr, char *buf) { int value; u8 bit; if (!spi_sout_dev) { log_error("sout device not present"); return -ENODEV; } if (!strcmp(attr->attr.name, "extserial-ri")) { bit = SOUT_EXTSERIAL_RI_BIT; } else if (!strcmp(attr->attr.name, "extserial-dsr")) { bit = SOUT_EXTSERIAL_DSR_BIT; } else if (!strcmp(attr->attr.name, "extserial-dcd")) { bit = SOUT_EXTSERIAL_DCD_BIT; } else if (!strcmp(attr->attr.name, "led-cd") || !strcmp(attr->attr.name, "led-sdk-b")) { bit = SOUT_LED_CD_BIT; } else if (!strcmp(attr->attr.name, "led-dtr") || !strcmp(attr->attr.name, "led-sdk-f")) { bit = SOUT_LED_DTR; } else if (!strcmp(attr->attr.name, "led-sig1") || !strcmp(attr->attr.name, "led-sdk-c")) { bit = SOUT_LED_SIG1_BIT; } else if (!strcmp(attr->attr.name, "led-sig2") || !strcmp(attr->attr.name, "led-sdk-d")) { bit = SOUT_LED_SIG2_BIT; } else if (!strcmp(attr->attr.name, "led-sig3") || !strcmp(attr->attr.name, "led-sdk-e")) { bit = SOUT_LED_SIG3_BIT; } else { log_notice("sout attr does not exist"); return -ENOENT; } mutex_lock(&spi_sout_mutex); value = spi_sout_value & bit ? 0 : 1; mutex_unlock(&spi_sout_mutex); return sprintf(buf, "%d\n", value); } static DEVICE_ATTR_MTS(dev_attr_extserial_dcd, "extserial-dcd", mts_attr_show_sout, mts_attr_store_sout); static DEVICE_ATTR_MTS(dev_attr_extserial_ri, "extserial-ri", mts_attr_show_sout, mts_attr_store_sout); static DEVICE_ATTR_MTS(dev_attr_extserial_dsr, "extserial-dsr", mts_attr_show_sout, mts_attr_store_sout); static DEVICE_ATTR_MTS(dev_attr_led_cd, "led-cd", mts_attr_show_sout, mts_attr_store_sout); static DEVICE_ATTR_MTS(dev_attr_led_sdk_b, "led-sdk-b", mts_attr_show_sout, mts_attr_store_sout); static DEVICE_ATTR_MTS(dev_attr_led_sig1, "led-sig1", mts_attr_show_sout, mts_attr_store_sout); static DEVICE_ATTR_MTS(dev_attr_led_sdk_c, "led-sdk-c", mts_attr_show_sout, mts_attr_store_sout); static DEVICE_ATTR_MTS(dev_attr_led_sig2, "led-sig2", mts_attr_show_sout, mts_attr_store_sout); static DEVICE_ATTR_MTS(dev_attr_led_sdk_d, "led-sdk-d", mts_attr_show_sout, mts_attr_store_sout); static DEVICE_ATTR_MTS(dev_attr_led_sig3, "led-sig3", mts_attr_show_sout, mts_attr_store_sout); static DEVICE_ATTR_MTS(dev_attr_led_sdk_e, "led-sdk-e", mts_attr_show_sout, mts_attr_store_sout); static DEVICE_ATTR_MTS(dev_attr_led_dtr, "led-dtr", mts_attr_show_sout, mts_attr_store_sout); static DEVICE_ATTR_MTS(dev_attr_led_sdk_f, "led-sdk-f", mts_attr_show_sout, mts_attr_store_sout); static ssize_t mts_attr_store_dout(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int value; u8 bit; if (!spi_dout_dev) { log_notice("dout device not present"); return -ENODEV; } if ((!strcmp(attr->attr.name, "dout0")) || (!strcmp(attr->attr.name, "gpo1"))) { bit = BIT(0); } else if ((!strcmp(attr->attr.name, "dout1")) || (!strcmp(attr->attr.name, "gpo2"))) { bit = BIT(1); } else if ((!strcmp(attr->attr.name, "dout2")) || (!strcmp(attr->attr.name, "gpo3"))) { bit = BIT(2); } else if ((!strcmp(attr->attr.name, "dout3")) || (!strcmp(attr->attr.name, "gpo4"))) { bit = BIT(3); } else if ((!strcmp(attr->attr.name, "dout4")) || (!strcmp(attr->attr.name, "led1"))) { bit = BIT(4); } else if ((!strcmp(attr->attr.name, "dout5")) || (!strcmp(attr->attr.name, "led4"))) { bit = BIT(5); } else if ((!strcmp(attr->attr.name, "dout6")) || (!strcmp(attr->attr.name, "led5"))) { bit = BIT(6); } else if ((!strcmp(attr->attr.name, "dout7")) || (!strcmp(attr->attr.name, "led6"))) { bit = BIT(7); } else { log_notice("dout attr does not exist"); return -ENOENT; } if (sscanf(buf, "%i", &value) != 1) { log_notice("dout attr invalid argument"); return -EINVAL; } mutex_lock(&spi_dout_mutex); if (value) { spi_dout_value &= ~bit; } else { spi_dout_value |= bit; } spi_writen(spi_dout_dev, &spi_dout_value, 1); mutex_unlock(&spi_dout_mutex); return count; } static ssize_t mts_attr_show_dout(struct device *dev, struct device_attribute *attr, char *buf) { int value; u8 bit; if (!spi_dout_dev) { log_error("dout device not present"); return -ENODEV; } if ((!strcmp(attr->attr.name, "dout0")) || (!strcmp(attr->attr.name, "gpo1"))) { bit = BIT(0); } else if ((!strcmp(attr->attr.name, "dout1")) || (!strcmp(attr->attr.name, "gpo2"))) { bit = BIT(1); } else if ((!strcmp(attr->attr.name, "dout2")) || (!strcmp(attr->attr.name, "gpo3"))) { bit = BIT(2); } else if ((!strcmp(attr->attr.name, "dout3")) || (!strcmp(attr->attr.name, "gpo4"))) { bit = BIT(3); } else if ((!strcmp(attr->attr.name, "dout4")) || (!strcmp(attr->attr.name, "led1"))) { bit = BIT(4); } else if ((!strcmp(attr->attr.name, "dout5")) || (!strcmp(attr->attr.name, "led4"))) { bit = BIT(5); } else if ((!strcmp(attr->attr.name, "dout6")) || (!strcmp(attr->attr.name, "led5"))) { bit = BIT(6); } else if ((!strcmp(attr->attr.name, "dout7")) || (!strcmp(attr->attr.name, "led6"))) { bit = BIT(7); } else { log_notice("dout attr does not exist"); return -ENOENT; } mutex_lock(&spi_dout_mutex); value = spi_dout_value & bit ? 0 : 1; mutex_unlock(&spi_dout_mutex); return sprintf(buf, "%d\n", value); } static DEVICE_ATTR_MTS(dev_attr_dout0, "dout0", mts_attr_show_dout, mts_attr_store_dout); static DEVICE_ATTR_MTS(dev_attr_dout1, "dout1", mts_attr_show_dout, mts_attr_store_dout); static DEVICE_ATTR_MTS(dev_attr_dout2, "dout2", mts_attr_show_dout, mts_attr_store_dout); static DEVICE_ATTR_MTS(dev_attr_dout3, "dout3", mts_attr_show_dout, mts_attr_store_dout); static DEVICE_ATTR_MTS(dev_attr_dout4, "dout4", mts_attr_show_dout, mts_attr_store_dout); static DEVICE_ATTR_MTS(dev_attr_dout5, "dout5", mts_attr_show_dout, mts_attr_store_dout); static DEVICE_ATTR_MTS(dev_attr_dout6, "dout6", mts_attr_show_dout, mts_attr_store_dout); static DEVICE_ATTR_MTS(dev_attr_dout7, "dout7", mts_attr_show_dout, mts_attr_store_dout); static ssize_t mts_attr_show_din(struct device *dev, struct device_attribute *attr, char *buf) { int tmp; u8 bit; u8 byte; if (!spi_din_dev) { log_error("din device not present"); return -ENODEV; } if ((!strcmp(attr->attr.name, "din0")) || (!strcmp(attr->attr.name, "gpi5"))) { bit = BIT(0); } else if ((!strcmp(attr->attr.name, "din1")) || (!strcmp(attr->attr.name, "gpi6"))) { bit = BIT(1); } else if ((!strcmp(attr->attr.name, "din2")) || (!strcmp(attr->attr.name, "gpi7"))) { bit = BIT(2); } else if ((!strcmp(attr->attr.name, "din3")) || (!strcmp(attr->attr.name, "gpi8"))) { bit = BIT(3); } else if ((!strcmp(attr->attr.name, "din4")) || (!strcmp(attr->attr.name, "gpi9"))) { bit = BIT(4); } else if ((!strcmp(attr->attr.name, "din5")) || (!strcmp(attr->attr.name, "gpi10"))) { bit = BIT(5); } else if (!strcmp(attr->attr.name, "din6")) { bit = BIT(6); } else if (!strcmp(attr->attr.name, "din7")) { bit = BIT(7); } else { log_notice("din attr does not exist"); return -ENOENT; } tmp = spi_readn(spi_din_dev, &byte, 1); if (tmp) { log_error("spi_read failed %d", tmp); return tmp; } tmp = byte & bit ? 1 : 0; return sprintf(buf, "%d\n", tmp); } static DEVICE_ATTR_RO_MTS(dev_attr_din0, "din0", mts_attr_show_din); static DEVICE_ATTR_RO_MTS(dev_attr_din1, "din1", mts_attr_show_din); static DEVICE_ATTR_RO_MTS(dev_attr_din2, "din2", mts_attr_show_din); static DEVICE_ATTR_RO_MTS(dev_attr_din3, "din3", mts_attr_show_din); static DEVICE_ATTR_RO_MTS(dev_attr_din4, "din4", mts_attr_show_din); static DEVICE_ATTR_RO_MTS(dev_attr_din5, "din5", mts_attr_show_din); static DEVICE_ATTR_RO_MTS(dev_attr_din6, "din6", mts_attr_show_din); static DEVICE_ATTR_RO_MTS(dev_attr_din7, "din7", mts_attr_show_din); 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 ssize_t mts_attr_show_led_status(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%d\n", led_mode_status); } static ssize_t mts_attr_store_led_status(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int value; int ret; struct gpio_pin *pin = gpio_pin_by_name("STATUS_LED"); if (!pin) { return -ENODEV; } 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(pin->pin, 1); break; case LED_ON: led_mode_status = LED_ON; ret = at91_set_gpio_value(pin->pin, 0); break; case LED_FLASHING: led_mode_status = LED_FLASHING; ret = at91_set_gpio_value(pin->pin, 0); break; default: ret = -EINVAL; } mutex_unlock(&mts_io_mutex); return count; } static DEVICE_ATTR_MTS(dev_attr_led_status, "led-status", mts_attr_show_led_status, mts_attr_store_led_status); static DEVICE_ATTR_MTS(dev_attr_led_a_gpio, "led-a", mts_attr_show_led_status, mts_attr_store_led_status); #if LED_STATUS_CONTROLLABLE_MTCDP static DEVICE_ATTR_MTS(dev_attr_led_sdk_a, "led-sdk-a", mts_attr_show_led_status, mts_attr_store_led_status); #endif #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); static DEVICE_ATTR_RO_MTS(dev_attr_reset, "reset", mts_attr_show_gpio_pin); static DEVICE_ATTR_MTS(dev_attr_rs4xx_term_res, "rs4xx-term-res", mts_attr_show_gpio_pin, mts_attr_store_gpio_pin); 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 ssize_t mts_attr_show_board_temperature(struct device *dev, struct device_attribute *attr, char *buf) { int tmp; u16 temp_raw; if (!spi_board_temp_dev) { log_notice("spi_board_temp device not present"); return -ENODEV; } tmp = spi_readn(spi_board_temp_dev, (u8 *) buf, 2); if (tmp) { log_error("spi_readn failed %d", tmp); return tmp; } temp_raw = ((u8 *) buf)[0] << 8 | ((u8 *) buf)[1]; log_debug("temp: 0x%04X", temp_raw); return sprintf(buf, "%d\n", ADT7302_to_celsius(temp_raw)); } static DEVICE_ATTR_RO_MTS(dev_attr_board_temperature, "board-temperature", mts_attr_show_board_temperature); static ssize_t mts_attr_show_adc(struct device *dev, struct device_attribute *attr, char *buf) { int offset; u32 value; u32 chan_mask; if (!DEVICE_CAPA(id_eeprom.capa, CAPA_ADC)) { log_debug("ADC not available"); return -ENODEV; } if (!strcmp(attr->attr.name, "adc0")) { offset = ADC_CDR0_OFFSET; chan_mask = 0x01; } else if (!strcmp(attr->attr.name, "adc1")) { offset = ADC_CDR1_OFFSET; chan_mask = 0x02; } else if (!strcmp(attr->attr.name, "adc2")) { offset = ADC_CDR2_OFFSET; chan_mask = 0x04; } else if (!strcmp(attr->attr.name, "adc3")) { offset = ADC_CDR3_OFFSET; chan_mask = 0x08; } else { log_notice("adc attr does not exist"); return -ENOENT; } mutex_lock(&mts_io_mutex); // disable all channels and enable the one we want writel(0x0F, adc_base + ADC_CHDR_OFFSET); writel(chan_mask, adc_base + ADC_CHER_OFFSET); ADC_CONVERT_START(adc_base); // wait for conversion to complete (EOC bit set) value = 0; while (value != chan_mask) { value = readl(adc_base + ADC_SR_OFFSET) & chan_mask; log_debug("ADC_SR EOC [%X]", value); } // read result value = readl(adc_base + offset); mutex_unlock(&mts_io_mutex); return sprintf(buf, "%lu\n", (unsigned long) value); } static DEVICE_ATTR_RO_MTS(dev_attr_adc0, "adc0", mts_attr_show_adc); static DEVICE_ATTR_RO_MTS(dev_attr_adc1, "adc1", mts_attr_show_adc); static DEVICE_ATTR_RO_MTS(dev_attr_adc2, "adc2", mts_attr_show_adc); static DEVICE_ATTR_RO_MTS(dev_attr_adc3, "adc3", mts_attr_show_adc); static DEVICE_ATTR_RO_MTS(dev_attr_usbh2_ps_oc, "usbh2-ps-oc", mts_attr_show_gpio_pin); #if USBH2_PS_CONTROLLABLE static DEVICE_ATTR_MTS(dev_attr_usbh2_ps_enabled, "usbh2-ps-enabled", mts_attr_show_gpio_pin, mts_attr_store_gpio_pin); #endif static DEVICE_ATTR_MTS(dev_attr_gpo1, "gpo1", mts_attr_show_dout, mts_attr_store_dout); static DEVICE_ATTR_MTS(dev_attr_gpo2, "gpo2", mts_attr_show_dout, mts_attr_store_dout); static DEVICE_ATTR_MTS(dev_attr_gpo3, "gpo3", mts_attr_show_dout, mts_attr_store_dout); static DEVICE_ATTR_MTS(dev_attr_gpo4, "gpo4", mts_attr_show_dout, mts_attr_store_dout); static DEVICE_ATTR_RO_MTS(dev_attr_gpi5, "gpi5", mts_attr_show_din); static DEVICE_ATTR_RO_MTS(dev_attr_gpi6, "gpi6", mts_attr_show_din); static DEVICE_ATTR_RO_MTS(dev_attr_gpi7, "gpi7", mts_attr_show_din); static DEVICE_ATTR_RO_MTS(dev_attr_gpi8, "gpi8", mts_attr_show_din); static DEVICE_ATTR_RO_MTS(dev_attr_gpi9, "gpi9", mts_attr_show_din); static DEVICE_ATTR_RO_MTS(dev_attr_gpi10, "gpi10", mts_attr_show_din); static DEVICE_ATTR_MTS(dev_attr_led1, "led1", mts_attr_show_dout, mts_attr_store_dout); static DEVICE_ATTR_MTS(dev_attr_led2, "led2", mts_attr_show_gpio_pin, mts_attr_store_gpio_pin); #if LED_LS_CONTROLLABLE static DEVICE_ATTR_MTS(dev_attr_led3, "led3", mts_attr_show_gpio_pin, mts_attr_store_gpio_pin); #else static DEVICE_ATTR_RO_MTS(dev_attr_led3, "led3", mts_attr_show_gpio_pin); #endif static DEVICE_ATTR_MTS(dev_attr_led4, "led4", mts_attr_show_dout, mts_attr_store_dout); static DEVICE_ATTR_MTS(dev_attr_led5, "led5", mts_attr_show_dout, mts_attr_store_dout); static DEVICE_ATTR_MTS(dev_attr_led6, "led6", mts_attr_show_dout, mts_attr_store_dout); static DEVICE_ATTR_MTS(dev_attr_gpio11, "gpio11", mts_attr_show_gpio_pin, mts_attr_store_gpio_pin); static DEVICE_ATTR_MTS(dev_attr_gpio12, "gpio12", mts_attr_show_gpio_pin, mts_attr_store_gpio_pin); static DEVICE_ATTR_MTS(dev_attr_rsersrc, "rsersrc", 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); static ssize_t mts_attr_show_serial_mode(struct device *dev, struct device_attribute *attr, char *buf) { int ret; int smode0; int smode1; int smode2; struct gpio_pin *pin_smode0 = gpio_pin_by_name("SERIAL_MODE0"); struct gpio_pin *pin_smode1 = gpio_pin_by_name("SERIAL_MODE1"); struct gpio_pin *pin_smode2 = gpio_pin_by_name("SERIAL_MODE2"); if (!pin_smode0 || !pin_smode1 || !pin_smode2) return -ENODEV; mutex_lock(&mts_io_mutex); smode0 = at91_get_gpio_value(pin_smode0->pin); smode1 = at91_get_gpio_value(pin_smode1->pin); smode2 = at91_get_gpio_value(pin_smode2->pin); if (smode2 == 0 && smode1 == 0 && smode0 == 1) ret = sprintf(buf, "rs232\n"); else if (smode2 == 0 && smode1 == 1 && smode0 == 0) ret = sprintf(buf, "rs485\n"); else if (smode2 == 1 && smode1 == 0 && smode0 == 0) ret = sprintf(buf, "rs422\n"); else if (smode2 == 0 && smode1 == 0 && smode0 == 0) ret = sprintf(buf, "loopback\n"); else ret = sprintf(buf, "error\n"); mutex_unlock(&mts_io_mutex); return ret; } static ssize_t mts_attr_store_serial_mode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int smode0; int smode1; int smode2; struct gpio_pin *pin_smode0 = gpio_pin_by_name("SERIAL_MODE0"); struct gpio_pin *pin_smode1 = gpio_pin_by_name("SERIAL_MODE1"); struct gpio_pin *pin_smode2 = gpio_pin_by_name("SERIAL_MODE2"); if (!pin_smode0 || !pin_smode1 || !pin_smode2) return -ENODEV; if (!strcasecmp(buf, "rs232")) { smode2 = 0; smode1 = 0; smode0 = 1; } else if (!strcasecmp(buf, "rs485")) { smode2 = 0; smode1 = 1; smode0 = 0; } else if (!strcasecmp(buf, "rs422")) { smode2 = 1; smode1 = 0; smode0 = 0; } else if (!strcasecmp(buf, "loopback")) { smode2 = 0; smode1 = 0; smode0 = 0; } else { return -EINVAL; } mutex_lock(&mts_io_mutex); at91_set_gpio_value(pin_smode2->pin, smode2); at91_set_gpio_value(pin_smode1->pin, smode1); at91_set_gpio_value(pin_smode0->pin, smode0); mutex_unlock(&mts_io_mutex); return count; } 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_MTS(dev_attr_serial_mode, "serial-mode", mts_attr_show_serial_mode, mts_attr_store_serial_mode); static DEVICE_ATTR_MTS(dev_attr_eth_switch_enabled, "eth-switch-enabled", mts_attr_show_gpio_pin, mts_attr_store_gpio_pin); static DEVICE_ATTR_MTS(dev_attr_radio_power_telit, "radio-power", mts_attr_show_gpio_pin, mts_attr_store_radio_power_telit); static DEVICE_ATTR_MTS(dev_attr_radio_reset_telit, "radio-reset", mts_attr_show_gpio_pin, mts_attr_store_radio_reset_telit); 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); struct gpio_pin *dc_gpio_pin_by_attr_name(const char *name) { struct gpio_pin *pin; char *pin_attr_name; if (!strcmp(name, "led1")) { pin_attr_name = "dc-gpio1"; } else if (!strcmp(name, "led2")) { pin_attr_name = "dc-gpio2"; } else if (!strcmp(name, "dout-enable")) { pin_attr_name = "dc-gpio3"; } else { log_error("daughter card attribute %s not available", name); return NULL; } for (pin = gpio_pins; *pin->name; pin++) { if (!strcmp(pin->attr_name, pin_attr_name)) { return pin; } } log_error("pin with attr name %s not found", name); return NULL; } static ssize_t mts_attr_show_dc_gpio_pin(struct device *dev, struct device_attribute *attr, char *buf) { int value; struct gpio_pin *pin = dc_gpio_pin_by_attr_name(attr->attr.name); if (!pin) { return -ENODEV; } mutex_lock(&mts_io_mutex); value = at91_get_gpio_value(pin->pin); mutex_unlock(&mts_io_mutex); if (value < 0) { return value; } if (pin->active_low) { value = !value; } return sprintf(buf, "%d\n", value); } static ssize_t mts_attr_store_dc_gpio_pin(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int value; int err; struct gpio_pin *pin = dc_gpio_pin_by_attr_name(attr->attr.name); if (!pin) { return -ENODEV; } if (sscanf(buf, "%i", &value) != 1) { return -EINVAL; } if (pin->active_low) { value = !value; } mutex_lock(&mts_io_mutex); err = at91_set_gpio_value(pin->pin, value); mutex_unlock(&mts_io_mutex); if (err) { return err; } return count; } static ssize_t mts_attr_show_dc_din(struct device *dev, struct device_attribute *attr, char *buf) { int tmp; u8 bit; u8 byte; if (!spi_dc_din_dev) { log_error("dc din device not present"); return -ENODEV; } if (!strcmp(attr->attr.name, "din0")) { bit = BIT(0); } else if (!strcmp(attr->attr.name, "din1")) { bit = BIT(1); } else if (!strcmp(attr->attr.name, "din2")) { bit = BIT(2); } else if (!strcmp(attr->attr.name, "din3")) { bit = BIT(3); } else { log_error("dc din attr does not exist"); return -ENOENT; } tmp = spi_readn(spi_dc_din_dev, &byte, 1); if (tmp) { log_error("spi_read failed %d", tmp); return tmp; } tmp = byte & bit ? 1 : 0; return sprintf(buf, "%d\n", tmp); } static ssize_t mts_attr_store_dc_dout(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int value; u8 bit; if (!spi_dc_dout_dev) { log_error("dc dout device not present"); return -ENODEV; } if (!strcmp(attr->attr.name, "dout0")) { bit = BIT(0); } else if (!strcmp(attr->attr.name, "dout1")) { bit = BIT(1); } else if (!strcmp(attr->attr.name, "dout2")) { bit = BIT(2); } else if (!strcmp(attr->attr.name, "dout3")) { bit = BIT(3); } else { log_error("dc dout attr does not exist"); return -ENOENT; } if (sscanf(buf, "%i", &value) != 1) { log_error("dc dout attr invalid argument"); return -EINVAL; } mutex_lock(&spi_dc_dout_mutex); if (value) { spi_dc_dout_value &= ~bit; } else { spi_dc_dout_value |= bit; } spi_writen(spi_dc_dout_dev, &spi_dc_dout_value, 1); mutex_unlock(&spi_dc_dout_mutex); return count; } static ssize_t mts_attr_show_dc_dout(struct device *dev, struct device_attribute *attr, char *buf) { int value; u8 bit; if (!spi_dc_dout_dev) { log_error("dc dout device not present"); return -ENODEV; } if (!strcmp(attr->attr.name, "dout0")) { bit = BIT(0); } else if (!strcmp(attr->attr.name, "dout1")) { bit = BIT(1); } else if (!strcmp(attr->attr.name, "dout2")) { bit = BIT(2); } else if (!strcmp(attr->attr.name, "dout3")) { bit = BIT(3); } else { log_error("dc dout attr does not exist"); return -ENOENT; } mutex_lock(&spi_dc_dout_mutex); value = spi_dc_dout_value & bit ? 0 : 1; mutex_unlock(&spi_dc_dout_mutex); return sprintf(buf, "%d\n", value); } static ssize_t mts_attr_show_dc_adc(struct device *dev, struct device_attribute *attr, char *buf) { int tmp; int tx_data; int rx_data; int channel; int channel_mask = 0x0180; /* 0b 0000 0001 1000 0000 */ int manual_mode = 0x1840; /* 0b 0001 1000 0100 0000 */ uint8_t tx[2]; uint8_t rx[2]; if (!spi_dc_adc_dev) { log_error("dc adc device not present"); return -ENODEV; } memset(tx, 0, sizeof(tx)); memset(rx, 0, sizeof(rx)); if (!strcmp(attr->attr.name, "adc0")) { channel = 0; } else if (!strcmp(attr->attr.name, "adc1")) { channel = 1; } else if (! strcmp(attr->attr.name, "adc2")) { channel = 2; } else { log_error("dc adc attr does not exist"); return -ENOENT; } /* 1st transfer to set up (5V reference, channel to read from) */ tx_data = manual_mode | ((channel << 7) & channel_mask); tx[0] = tx_data >> 8; tx[1] = tx_data & 0xFF; tmp = spi_writen(spi_dc_adc_dev, tx, 2); if (tmp) { log_error("spi_write failed %d", tmp); return tmp; } /* 2nd transfer to clock chip for ADC conversion * this can be a throw-away read or an empty write, * the ADC just needs the clock running so it can convert */ tx[0] = 0; tx[1] = 0; tmp = spi_writen(spi_dc_adc_dev, tx, 2); if (tmp) { log_error("2nd spi_write failed %d", tmp); return tmp; } /* 3rd transfer to read data */ tmp = spi_readn(spi_dc_adc_dev, rx, 2); if (tmp) { log_error("spi_read failed %d", tmp); return tmp; } rx_data = ((rx[0] & 0x0F) << 8) | (rx[1] & 0xFF); return sprintf(buf, "%lu\n", (unsigned long) rx_data); } /* MTDC-GPIOB */ static DEVICE_ATTR_RO_MTS(dev_attr_dc_din0, "din0", mts_attr_show_dc_din); static DEVICE_ATTR_RO_MTS(dev_attr_dc_din1, "din1", mts_attr_show_dc_din); static DEVICE_ATTR_RO_MTS(dev_attr_dc_din2, "din2", mts_attr_show_dc_din); static DEVICE_ATTR_RO_MTS(dev_attr_dc_din3, "din3", mts_attr_show_dc_din); static DEVICE_ATTR_MTS(dev_attr_dc_dout0, "dout0", mts_attr_show_dc_dout, mts_attr_store_dc_dout); static DEVICE_ATTR_MTS(dev_attr_dc_dout1, "dout1", mts_attr_show_dc_dout, mts_attr_store_dc_dout); static DEVICE_ATTR_MTS(dev_attr_dc_dout2, "dout2", mts_attr_show_dc_dout, mts_attr_store_dc_dout); static DEVICE_ATTR_MTS(dev_attr_dc_dout3, "dout3", mts_attr_show_dc_dout, mts_attr_store_dc_dout); static DEVICE_ATTR_RO_MTS(dev_attr_dc_adc0, "adc0", mts_attr_show_dc_adc); static DEVICE_ATTR_RO_MTS(dev_attr_dc_adc1, "adc1", mts_attr_show_dc_adc); static DEVICE_ATTR_RO_MTS(dev_attr_dc_adc2, "adc2", mts_attr_show_dc_adc); static DEVICE_ATTR_MTS(dev_attr_dc_led1, "led1", mts_attr_show_dc_gpio_pin, mts_attr_store_dc_gpio_pin); static DEVICE_ATTR_MTS(dev_attr_dc_led2, "led2", mts_attr_show_dc_gpio_pin, mts_attr_store_dc_gpio_pin); static DEVICE_ATTR_MTS(dev_attr_dc_oe, "dout-enable", mts_attr_show_dc_gpio_pin, mts_attr_store_dc_gpio_pin); static struct attribute *mt100eocg_platform_attributes[] = { &dev_attr_vendor_id.attr, &dev_attr_product_id.attr, &dev_attr_device_id.attr, &dev_attr_hw_version.attr, &dev_attr_imei.attr, &dev_attr_eth_mac.attr, &dev_attr_extserial_dtr.attr, &dev_attr_extserial_dcd_gpio.attr, &dev_attr_rsersrc.attr, &dev_attr_radio_reset.attr, &dev_attr_eth0_enabled.attr, &dev_attr_gpio11.attr, &dev_attr_gpio12.attr, &dev_attr_gpo1.attr, &dev_attr_gpo2.attr, &dev_attr_gpo3.attr, &dev_attr_gpo4.attr, &dev_attr_led1.attr, &dev_attr_led2.attr, &dev_attr_led3.attr, &dev_attr_led4.attr, &dev_attr_led5.attr, &dev_attr_led6.attr, &dev_attr_gpi5.attr, &dev_attr_gpi6.attr, &dev_attr_gpi7.attr, &dev_attr_gpi8.attr, &dev_attr_gpi9.attr, &dev_attr_gpi10.attr, &dev_attr_board_temperature.attr, &dev_attr_adc0.attr, &dev_attr_adc1.attr, &dev_attr_adc2.attr, &dev_attr_adc3.attr, NULL, }; static struct attribute_group mt100eocg_platform_attribute_group = { .attrs = mt100eocg_platform_attributes }; struct attribute *mtr2_platform_attributes[] = { &dev_attr_vendor_id.attr, &dev_attr_product_id.attr, &dev_attr_device_id.attr, &dev_attr_hw_version.attr, &dev_attr_imei.attr, &dev_attr_eth_mac.attr, &dev_attr_wifi_mac.attr, &dev_attr_reset.attr, &dev_attr_reset_monitor.attr, &dev_attr_radio_power.attr, &dev_attr_radio_reset.attr, &dev_attr_ndc_reset.attr, &dev_attr_extserial_dtr.attr, &dev_attr_extserial_dsr_gpio.attr, &dev_attr_extserial_ri_gpio.attr, &dev_attr_extserial_dcd_gpio.attr, &dev_attr_eth_switch_enabled.attr, &dev_attr_bt_enabled.attr, &dev_attr_wlan_enabled.attr, &dev_attr_serial_mode.attr, &dev_attr_rs4xx_term_res.attr, &dev_attr_led_status.attr, &dev_attr_led_wifi_gpio.attr, &dev_attr_led_cd_gpio.attr, &dev_attr_led_sig1_gpio.attr, &dev_attr_led_sig2_gpio.attr, &dev_attr_led_sig3_gpio.attr, &dev_attr_led_a_gpio.attr, &dev_attr_led_b_gpio.attr, &dev_attr_led_c_gpio.attr, &dev_attr_led_d_gpio.attr, &dev_attr_led_e_gpio.attr, &dev_attr_led_f_gpio.attr, &dev_attr_board_temperature.attr, /* extra space for the daughter card attributes */ NULL, // index 34 NULL, // index 35 NULL, // index 36 NULL, // index 37 NULL, // index 38 NULL, // index 39 NULL, // index 40 NULL, // index 41 NULL, // index 42 NULL, // index 43 NULL, // index 44 NULL, // index 45 NULL, // index 46 NULL, // index 47 NULL, // index 48 NULL, // index 49 NULL, // index 50 NULL, // index 51 NULL, }; static struct attribute *mtr2_daughter_card_attributes[] = { &dev_attr_dc_din0.attr, &dev_attr_dc_din1.attr, &dev_attr_dc_din2.attr, &dev_attr_dc_din3.attr, &dev_attr_dc_dout0.attr, &dev_attr_dc_dout1.attr, &dev_attr_dc_dout2.attr, &dev_attr_dc_dout3.attr, &dev_attr_dc_adc0.attr, &dev_attr_dc_adc1.attr, &dev_attr_dc_adc2.attr, &dev_attr_dc_led1.attr, &dev_attr_dc_led2.attr, &dev_attr_dc_oe.attr, NULL, }; static struct attribute_group mtr2_platform_attribute_group = { .attrs = mtr2_platform_attributes }; bool mtr2_add_daughter_card_attributes(void) { size_t platform_attrs_size = sizeof(mtr2_platform_attributes) / sizeof(struct attribute *); size_t daughter_card_attrs_size = sizeof(mtr2_daughter_card_attributes) / sizeof(struct attribute *); size_t platform_attrs_index; size_t daughter_card_attrs_index; size_t copy_length = daughter_card_attrs_size - 1; /* don't need to copy the NULL at the end */ for (platform_attrs_index = 0; platform_attrs_index < platform_attrs_size; platform_attrs_index++) { if (! mtr2_platform_attributes[platform_attrs_index]) { break; } } if (platform_attrs_size < platform_attrs_index + daughter_card_attrs_size) { log_error("not enough room for MTR2 daughter card attributes!"); return false; } for (daughter_card_attrs_index = 0; daughter_card_attrs_index < copy_length; daughter_card_attrs_index++, platform_attrs_index++) { mtr2_platform_attributes[platform_attrs_index] = mtr2_daughter_card_attributes[daughter_card_attrs_index]; } return true; } static struct attribute *mtcdp_platform_attributes[] = { &dev_attr_vendor_id.attr, &dev_attr_product_id.attr, &dev_attr_device_id.attr, &dev_attr_hw_version.attr, &dev_attr_imei.attr, &dev_attr_eth_mac.attr, &dev_attr_reset.attr, &dev_attr_reset_monitor.attr, &dev_attr_radio_reset.attr, &dev_attr_ndc_reset.attr, &dev_attr_eth0_enabled.attr, &dev_attr_extserial_dtr.attr, &dev_attr_led_ls.attr, #if LED_STATUS_CONTROLLABLE_MTCDP &dev_attr_led_status.attr, &dev_attr_led_sdk_a.attr, #endif &dev_attr_usbh2_ps_oc.attr, #if USBH2_PS_CONTROLLABLE &dev_attr_usbh2_ps_enabled.attr, #endif &dev_attr_extserial_dcd.attr, &dev_attr_extserial_ri.attr, &dev_attr_extserial_dsr.attr, &dev_attr_led_cd.attr, &dev_attr_led_sdk_b.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_dtr.attr, &dev_attr_led_sdk_f.attr, &dev_attr_dout0.attr, &dev_attr_dout1.attr, &dev_attr_dout2.attr, &dev_attr_dout3.attr, &dev_attr_dout4.attr, &dev_attr_dout5.attr, &dev_attr_dout6.attr, &dev_attr_dout7.attr, &dev_attr_din0.attr, &dev_attr_din1.attr, &dev_attr_din2.attr, &dev_attr_din3.attr, &dev_attr_din4.attr, &dev_attr_din5.attr, &dev_attr_din6.attr, &dev_attr_din7.attr, &dev_attr_board_temperature.attr, &dev_attr_adc0.attr, &dev_attr_adc1.attr, &dev_attr_adc2.attr, &dev_attr_adc3.attr, NULL, }; static struct attribute_group mtcdp_platform_attribute_group = { .attrs = mtcdp_platform_attributes }; static struct attribute *mtr_platform_attributes[] = { &dev_attr_vendor_id.attr, &dev_attr_product_id.attr, &dev_attr_device_id.attr, &dev_attr_hw_version.attr, &dev_attr_imei.attr, &dev_attr_eth_mac.attr, &dev_attr_wifi_mac.attr, &dev_attr_reset.attr, &dev_attr_reset_monitor.attr, &dev_attr_radio_power_telit.attr, &dev_attr_radio_reset_telit.attr, &dev_attr_extserial_ri_gpio.attr, &dev_attr_extserial_dtr.attr, &dev_attr_extserial_dsr_gpio.attr, &dev_attr_extserial_dcd_gpio.attr, &dev_attr_eth0_enabled.attr, &dev_attr_bt_enabled.attr, &dev_attr_wlan_enabled.attr, &dev_attr_led_status.attr, &dev_attr_led_sig1_gpio.attr, &dev_attr_led_sig2_gpio.attr, &dev_attr_led_sig3_gpio.attr, &dev_attr_led_cd_gpio.attr, &dev_attr_led_wifi_gpio.attr, &dev_attr_led_a_gpio.attr, &dev_attr_led_b_gpio.attr, &dev_attr_led_c_gpio.attr, &dev_attr_led_d_gpio.attr, &dev_attr_led_e_gpio.attr, &dev_attr_led_f_gpio.attr, NULL, }; static struct attribute_group mtr_platform_attribute_group = { .attrs = mtr_platform_attributes }; static struct platform_device *mts_io_platform_device; static int __devinit mts_spi_sout_probe(struct spi_device *spi) { int tmp; spi->max_speed_hz = sout_max_speed_hz; spi->mode = 0; log_debug("sout_max_speed_hz: %d", sout_max_speed_hz); tmp = spi_setup(spi); if (tmp < 0) { log_error("spi_setup sout failed"); return tmp; } spi_sout_value = 0xFF; spi_writen(spi, &spi_sout_value, 1); spi_sout_dev = spi; return 0; } static int mts_spi_sout_remove(struct spi_device *spi) { spi_sout_dev = NULL; return 0; } static struct spi_driver mts_spi_sout_driver = { .driver = { .name = "mts-io-sout", .bus = &spi_bus_type, .owner = THIS_MODULE, }, .probe = mts_spi_sout_probe, .remove = __devexit_p(mts_spi_sout_remove), }; static int __devinit mts_spi_dout_probe(struct spi_device *spi) { int tmp; if (!DEVICE_CAPA(id_eeprom.capa, CAPA_DOUT)) { log_debug("digital outputs not available"); return -ENODEV; } spi->max_speed_hz = dout_max_speed_hz; spi->mode = 0; log_debug("dout_max_speed_hz: %d", dout_max_speed_hz); tmp = spi_setup(spi); if (tmp < 0) { log_error("spi_setup dout failed"); return tmp; } spi_dout_value = 0x00; spi_writen(spi, &spi_dout_value, 1); spi_dout_dev = spi; return 0; } static int mts_spi_dout_remove(struct spi_device *spi) { spi_dout_dev = NULL; return 0; } static struct spi_driver mts_spi_dout_driver = { .driver = { .name = "mts-io-dout", .bus = &spi_bus_type, .owner = THIS_MODULE, }, .probe = mts_spi_dout_probe, .remove = __devexit_p(mts_spi_dout_remove), }; static int __devinit mts_spi_din_probe(struct spi_device *spi) { int tmp; if (!DEVICE_CAPA(id_eeprom.capa, CAPA_DIN)) { log_debug("digital inputs not available"); return -ENODEV; } spi->max_speed_hz = din_max_speed_hz; spi->mode = SPI_CPOL; log_debug("din_max_speed_hz: %d", din_max_speed_hz); tmp = spi_setup(spi); if (tmp < 0) { log_error("spi_setup din failed"); return tmp; } spi_din_dev = spi; return 0; } static int mts_spi_din_remove(struct spi_device *spi) { spi_din_dev = NULL; return 0; } static struct spi_driver mts_spi_din_driver = { .driver = { .name = "mts-io-din", .bus = &spi_bus_type, .owner = THIS_MODULE, }, .probe = mts_spi_din_probe, .remove = __devexit_p(mts_spi_din_remove), }; static int __devinit mts_spi_dc_dout_probe(struct spi_device *spi) { int tmp; if (! has_daughter_card || mts_dc_product_id != MTDC_GPIOB_0_0) { log_error("daughter card digital outputs not available"); return -ENODEV; } spi->max_speed_hz = dc_dout_max_speed_hz; spi->mode = 0; log_debug("dc_dout_max_speed_hz: %d", dc_dout_max_speed_hz); tmp = spi_setup(spi); if (tmp < 0) { log_error("spi_setup dc dout failed"); return tmp; } spi_dc_dout_value = 0x00; spi_writen(spi, &spi_dc_dout_value, 1); spi_dc_dout_dev = spi; return 0; } static int mts_spi_dc_dout_remove(struct spi_device *spi) { spi_dc_dout_dev = NULL; return 0; } static struct spi_driver mts_spi_dc_dout_driver = { .driver = { .name = "mts-io-dc-dout", .bus = &spi_bus_type, .owner = THIS_MODULE, }, .probe = mts_spi_dc_dout_probe, .remove = __devexit_p(mts_spi_dc_dout_remove), }; static int __devinit mts_spi_dc_din_probe(struct spi_device *spi) { int tmp; if (! has_daughter_card || mts_dc_product_id != MTDC_GPIOB_0_0) { log_error("daughter card digital inputs not available"); return -ENODEV; } spi->max_speed_hz = dc_din_max_speed_hz; spi->mode = SPI_CPOL; log_debug("dc_din_max_speed_hz: %d", dc_din_max_speed_hz); tmp = spi_setup(spi); if (tmp < 0) { log_error("spi_setup daughter card din failed"); return tmp; } spi_dc_din_dev = spi; return 0; } static int mts_spi_dc_din_remove(struct spi_device *spi) { spi_dc_din_dev = NULL; return 0; } static struct spi_driver mts_spi_dc_din_driver = { .driver = { .name = "mts-io-dc-din", .bus = &spi_bus_type, .owner = THIS_MODULE, }, .probe = mts_spi_dc_din_probe, .remove = __devexit_p(mts_spi_dc_din_remove), }; static int __devinit mts_spi_dc_adc_probe(struct spi_device *spi) { int tmp; if (! has_daughter_card || mts_dc_product_id != MTDC_GPIOB_0_0) { log_error("daughter card analog to digital not available"); return -ENODEV; } spi->max_speed_hz = dc_adc_max_speed_hz; spi->mode = 0; log_debug("dc_adc_max_speed_hz: %d", dc_adc_max_speed_hz); log_debug("dc_adc_mode: %d", spi->mode); tmp = spi_setup(spi); if (tmp < 0) { log_error("spi_setup daughter card adc failed"); return tmp; } spi_dc_adc_dev = spi; return 0; } static int mts_spi_dc_adc_remove(struct spi_device *spi) { spi_dc_adc_dev = NULL; return 0; } static struct spi_driver mts_spi_dc_adc_driver = { .driver = { .name = "mts-io-dc-adc", .bus = &spi_bus_type, .owner = THIS_MODULE, }, .probe = mts_spi_dc_adc_probe, .remove = __devexit_p(mts_spi_dc_adc_remove), }; static int __devinit mts_spi_board_temp_probe(struct spi_device *spi) { int tmp; spi->max_speed_hz = board_temp_max_speed_hz; spi->mode = SPI_CPOL | SPI_CPHA; log_debug("board_temp_max_speed_hz: %d", board_temp_max_speed_hz); tmp = spi_setup(spi); if (tmp < 0) { log_error("spi_setup board-temp failed"); return tmp; } spi_board_temp_dev = spi; return 0; } static int mts_spi_board_temp_remove(struct spi_device *spi) { spi_board_temp_dev = NULL; 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 mts_dc_eeprom_load(void) { memcpy(&dc_eeprom, mts_dc_eeprom, sizeof(mts_dc_eeprom)); if (mts_dc_eeprom[0] == 0xFF) { log_error("uninitialized eeprom on daughter card"); return -EIO; } else if (mts_dc_eeprom[0] == 0x00) { log_info("no daughter card inserted"); return 0; } has_daughter_card = true; log_info("daughter card vendor-id: %.32s", dc_eeprom.vendor_id); log_info("daughter card product-id: %.32s", dc_eeprom.product_id); log_info("daughter card device-id: %.32s", dc_eeprom.device_id); log_info("daughter card hw-version: %.32s", dc_eeprom.hw_version); /* TODO: only show the mac address if this is the ethernet card */ log_info("daughter card mac-addr: %02X:%02X:%02X:%02X:%02X:%02X", dc_eeprom.mac_addr[0], dc_eeprom.mac_addr[1], dc_eeprom.mac_addr[2], dc_eeprom.mac_addr[3], dc_eeprom.mac_addr[4], dc_eeprom.mac_addr[5]); return 0; } 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; daughter_card_capable = true; 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; daughter_card_capable = true; 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 { 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"); 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]); 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; log_info("init: " DRIVER_VERSION); ret = mts_id_eeprom_load(); if (ret) { goto error1; } if (daughter_card_capable) { mts_dc_product_id = MTDC_NONE; ret = mts_dc_eeprom_load(); if (ret) { /* error reading the EEPROM from the daughter card */ log_error("error reading daughter card eeprom: %d", ret); log_error("unable to initialize daughter card"); goto error1; } else if (has_daughter_card) { /* no error and we have a daughter card */ if (strstr(dc_eeprom.product_id, PRODUCT_ID_MTDC_GPIOB)) { mts_dc_product_id = MTDC_GPIOB_0_0; } switch(mts_dc_product_id) { case MTDC_GPIOB_0_0: log_debug("adding GPIO daughter card attributes"); if (! mtr2_add_daughter_card_attributes()) { log_error("failed to add GPIO daughter card attributes"); goto error1; } else { log_info("successfully added GPIO daughter card attributes"); } log_debug("registering daughter card dout driver"); ret = spi_register_driver(&mts_spi_dc_dout_driver); if (ret) { log_error("failed to register dc dout driver"); spi_unregister_driver(&mts_spi_dc_dout_driver); goto error1; } log_debug("registering daughter card din driver"); ret = spi_register_driver(&mts_spi_dc_din_driver); if (ret) { log_error("failed to register dc din driver"); spi_unregister_driver(&mts_spi_dc_din_driver); goto error1; } log_debug("registering daughter card adc driver"); ret = spi_register_driver(&mts_spi_dc_adc_driver); if (ret) { log_error("failed to register dc adc driver"); spi_unregister_driver(&mts_spi_dc_adc_driver); goto error1; } break; default: log_info("daughter card '%s' currently unsupported", dc_eeprom.product_id); } } } 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; } } #ifdef CONFIG_ARCH_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++) { gpio_request(pin->pin, pin->name); if (pin->direction == GPIO_DIR_OD) { log_info("Setting pin %s to multi-drive\n", pin->name); at91_set_multi_drive(pin->pin, true); } if (pin->direction == GPIO_DIR_OUTPUT || pin->direction == GPIO_DIR_OD) { at91_set_gpio_output_with_pullup(pin->pin, pin->output_value, pin->use_pullup); } else { at91_set_gpio_input(pin->pin, pin->use_pullup); } } if ( has_spi_sout || has_spi_dout || has_spi_din ) { pin = gpio_pin_by_name("ENIO"); if (pin) { at91_set_gpio_value(pin->pin, 0); } } // No blink_callback for MT100EOCG if ( mts_product_id != MT100EOCG_0_0 ) { blink_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); return ret; } static void __exit mts_io_exit(void) { if ( mts_product_id != MT100EOCG_0_0 ) { cancel_delayed_work_sync(&blink_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); if (has_daughter_card) { switch (mts_dc_product_id) { case MTDC_GPIOB_0_0: spi_unregister_driver(&mts_spi_dc_dout_driver); spi_unregister_driver(&mts_spi_dc_din_driver); spi_unregister_driver(&mts_spi_dc_adc_driver); break; default: break; } } 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); 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-dc-dout"); MODULE_ALIAS("mts-io-dc-din"); MODULE_ALIAS("mts-io-dc-adc");