summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Klug <john.klug@multitech.com>2020-03-10 12:05:07 -0500
committerJohn Klug <john.klug@multitech.com>2020-03-10 12:05:07 -0500
commita11f397797b2c0e98a26ecb3b22adba833417d06 (patch)
treee28fe47342de9fb87d1b7f2e4bc9140150d5fe17
parent937d21665a614dee618e48b51389d3a1e27709bf (diff)
parent5be5a4ffc7b619b68f5542a158c810c453a19b65 (diff)
downloadmts-io-a11f397797b2c0e98a26ecb3b22adba833417d06.tar.gz
mts-io-a11f397797b2c0e98a26ecb3b22adba833417d06.tar.bz2
mts-io-a11f397797b2c0e98a26ecb3b22adba833417d06.zip
Merge remote-tracking branch 'origin/mt100eocg'
-rw-r--r--configure.ac2
-rw-r--r--io-module/adc.c126
-rw-r--r--io-module/machine/mt100eocg.c252
-rw-r--r--io-module/mts-io.c60
-rw-r--r--io-module/mts_io_module.h2
-rw-r--r--io-module/spi.c597
6 files changed, 1028 insertions, 11 deletions
diff --git a/configure.ac b/configure.ac
index e584de0..fbb7973 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,4 +1,4 @@
-AC_INIT([mts-io], [4.3.0])
+AC_INIT([mts-io], [4.3.1])
AC_CONFIG_SRCDIR([util/mts_util_lora2_reset.c])
AM_INIT_AUTOMAKE
AM_CONFIG_HEADER([config.h])
diff --git a/io-module/adc.c b/io-module/adc.c
new file mode 100644
index 0000000..3d5f3d0
--- /dev/null
+++ b/io-module/adc.c
@@ -0,0 +1,126 @@
+#define AT91SAM9X5_BASE_ADC 0xf804c000
+#define AT91SAM9260_BASE_ADC 0xfffe0000
+
+#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)
+
+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 int mts_io_board_adc_probe(struct platform_device *pdev)
+{
+ int err;
+ adc_clk = clk_get(&pdev->dev, "adc_clk");
+ if (adc_clk) {
+ err = clk_prepare_enable(adc_clk);
+ if (err) {
+ log_error("clock failed to prepare+enable: %d\n", err);
+ return err;
+ }
+ #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) {
+ log_error("ioremap failed.\n");
+ return -ENOMEM;
+ }
+ ADC_CONVERT_RESET(adc_base);
+ writel(ADC_MODE_DEFAULT, adc_base + ADC_MR_OFFSET);
+ writel(0x000F0F0F, adc_base + ADC_IDR_OFFSET);
+ writel(0x0F, adc_base + ADC_CHDR_OFFSET);
+ }
+ return 0;
+}
+
+static int mts_io_board_adc_remove(struct platform_device *pdev)
+{
+ if (adc_clk) {
+ int ret;
+ clk_disable_unprepare(adc_clk);
+ }
+ return 0;
+}
+
diff --git a/io-module/machine/mt100eocg.c b/io-module/machine/mt100eocg.c
new file mode 100644
index 0000000..c9d2961
--- /dev/null
+++ b/io-module/machine/mt100eocg.c
@@ -0,0 +1,252 @@
+#include "at91gpio.h"
+/* Used for both MT100EOCG 0.0 */
+static struct gpio_pin gpio_pins_mt100eocg_0_0[] = {
+ {
+ .name = "ADC0",
+ .pin = {
+ .label = "adc0",
+ .gpio = AT91_PIN_PC0,
+ .flags = GPIOF_IN,
+ },
+ },
+ {
+ .name = "ADC1",
+ .pin = {
+ .label = "adc1",
+ .gpio = AT91_PIN_PC1,
+ .flags = GPIOF_IN,
+ },
+ },
+ {
+ .name = "ADC2",
+ .pin = {
+ .label = "adc2",
+ .gpio = AT91_PIN_PC2,
+ .flags = GPIOF_IN,
+ },
+ },
+ {
+ .name = "ADC3",
+ .pin = {
+ .label = "adc3",
+ .gpio = AT91_PIN_PC3,
+ .flags = GPIOF_IN,
+ },
+ },
+ {
+ .name = "DTR1",
+ .pin = {
+ .label = "extserial-dtr",
+ .gpio = AT91_PIN_PB18,
+ .flags = GPIOF_IN,
+ },
+ .active_low = 1,
+ },
+ {
+ .name = "DCD1",
+ .pin = {
+ .label = "extserial-dcd",
+ .gpio = AT91_PIN_PB3,
+ .flags = GPIOF_OUT_INIT_HIGH,
+ },
+ .active_low = 1,
+ },
+ {
+ .name = "ETH_RESET",
+ .pin = {
+ .label = "eth-reset",
+ .gpio = AT91_PIN_PB31,
+ .flags = GPIOF_OUT_INIT_HIGH,
+ },
+ },
+ {
+ .name = "ENIO",
+ .pin = {
+ .label = "enio",
+ .gpio = AT91_PIN_PC15,
+ .flags = GPIOF_OUT_INIT_LOW,
+ },
+ },
+ {
+ .name = "DEVICE_RESET",
+ .pin = {
+ .label = "reset",
+ .gpio = AT91_PIN_PA22,
+ .flags = GPIOF_IN,
+ },
+ .active_low = 1,
+ },
+ {
+ .name = "RSERSRC",
+ .pin = {
+ .label = "rsersrc",
+ .gpio = AT91_PIN_PC7,
+ .flags = GPIOF_OUT_INIT_HIGH,
+ },
+ .active_low = 1,
+ },
+ {
+ .name = "RADIO_RESET",
+ .pin = {
+ .label = "radio-reset",
+ .gpio = AT91_PIN_PB30,
+ .flags = GPIOF_OUT_INIT_HIGH,
+ },
+ },
+ {
+ .name = "GPIO11",
+ .pin = {
+ .label = "gpio11",
+ .gpio = AT91_PIN_PB19,
+ .flags = GPIOF_OPEN_DRAIN | GPIOF_INIT_HIGH,
+ },
+ },
+ {
+ .name = "GPIO12",
+ .pin = {
+ .label = "gpio12",
+ .gpio = AT91_PIN_PB20,
+ .flags = GPIOF_OPEN_DRAIN | GPIOF_INIT_HIGH,
+ },
+ },
+ {
+ .name = "LED2",
+ .pin = {
+ .label = "led2",
+ .gpio = AT91_PIN_PA30,
+ .flags = GPIOF_OUT_INIT_HIGH,
+ },
+ .active_low = 1,
+ },
+ {
+ .name = "LED3",
+ .pin = {
+ .label = "led3",
+ .gpio = AT91_PIN_PC9,
+#if LED_LS_CONTROLLABLE
+ .flags = GPIOF_OUT_INIT_HIGH,
+#else
+ .flags = GPIOF_IN,
+#endif
+ },
+ .active_low = 1,
+ },
+ /*
+ {
+ .name = "TXD1",
+ .pin = {
+ .gpio = AT91_PIN_PB17,
+ .flags = GPIOF_IN,
+ },
+ },*/
+ { },
+};
+
+/* mt100eocg specific attributes */
+static DEVICE_ATTR_RO_MTS(dev_attr_extserial_dtr, "extserial-dtr",
+ mts_attr_show_gpio_pin);
+
+static DEVICE_ATTR_MTS(dev_attr_extserial_dcd_gpio, "extserial-dcd",
+ mts_attr_show_gpio_pin, mts_attr_store_gpio_pin);
+
+static DEVICE_ATTR_MTS(dev_attr_eth_reset_mt100, "eth-reset",
+ mts_attr_show_gpio_pin, mts_attr_store_gpio_pin);
+
+static DEVICE_ATTR_MTS(dev_attr_enio, "enio",
+ mts_attr_show_gpio_pin, mts_attr_store_gpio_pin);
+
+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_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 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_has_radio.attr,
+ &dev_attr_reset.attr,
+ &dev_attr_reset_monitor.attr,
+ &dev_attr_reset_monitor_intervals.attr,
+ &dev_attr_extserial_dtr.attr,
+ &dev_attr_extserial_dcd_gpio.attr,
+ &dev_attr_rsersrc.attr, // AT91_PIN_PC7
+ &dev_attr_radio_reset.attr, // AT91_PIN_PB30
+ &dev_attr_eth_reset_mt100.attr,
+ &dev_attr_gpio11.attr, // AT91_PIN_PB19
+ &dev_attr_gpio12.attr, // AT91_PIN_PB20
+ &dev_attr_enio.attr,
+// SPI
+ &dev_attr_gpo1.attr,
+ &dev_attr_gpo2.attr,
+ &dev_attr_gpo3.attr,
+ &dev_attr_gpo4.attr,
+//
+// SPI
+ &dev_attr_led1.attr,
+//
+ &dev_attr_led2.attr, // AT91_PIN_PA30
+ &dev_attr_led3.attr, // AT91_PIN_PC9
+// SPI
+ &dev_attr_led4.attr,
+ &dev_attr_led5.attr,
+ &dev_attr_led6.attr,
+//
+// SPI
+ &dev_attr_gpi5.attr,
+ &dev_attr_gpi6.attr,
+ &dev_attr_gpi7.attr,
+ &dev_attr_gpi8.attr,
+ &dev_attr_gpi9.attr,
+ &dev_attr_gpi10.attr,
+//
+// SPI
+ &dev_attr_board_temperature.attr,
+//
+// adc
+ &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
+};
diff --git a/io-module/mts-io.c b/io-module/mts-io.c
index 73d6376..f9ce122 100644
--- a/io-module/mts-io.c
+++ b/io-module/mts-io.c
@@ -4,6 +4,7 @@
* Copyright (C) 2014 by Multi-Tech Systems
* Copyright (C) 2016 by Multi-Tech Systems
* Copyright (C) 2019 by Multi-Tech Systems
+ * Copyright (C) 2020 by Multi-Tech Systems
*
* 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
@@ -20,7 +21,7 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
-
+#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/ioctl.h>
#include <linux/input.h>
@@ -71,6 +72,11 @@ static const struct of_device_id mts_io_dt_ids[] = {
};
MODULE_DEVICE_TABLE(of, mts_io_dt_ids);
+/* on-board EEPROM */
+static struct mts_id_eeprom_layout id_eeprom;
+
+#include "adc.c"
+
/*
* We must call platform_set_drvdata, or else the
* devres_head for the driver has junk in it, and
@@ -83,7 +89,11 @@ MODULE_DEVICE_TABLE(of, mts_io_dt_ids);
*/
static int mts_io_probe(struct platform_device *pdev)
{
- return 0;
+ int ret = 0;
+ if (!DEVICE_CAPA(id_eeprom.capa, CAPA_ADC)) {
+ ret = mts_io_board_adc_probe(pdev);
+ }
+ return ret;
}
static int mts_io_remove(struct platform_device *pdev)
@@ -104,8 +114,6 @@ static struct platform_driver mts_io_driver = {
};
-/* on-board EEPROM */
-static struct mts_id_eeprom_layout id_eeprom;
static uint8_t mts_hw_version;
struct platform_device *mts_io_platform_device;
EXPORT_SYMBOL(mts_io_platform_device);
@@ -128,6 +136,8 @@ static time_t time_now_secs(void);
/* generic GPIO support */
#include "gpio.c"
+#include "spi.c"
+
/* generic Button support */
//#include "buttons.c"
@@ -622,6 +632,7 @@ static int get_radio_model_from_product_id(void) {
#include "machine/mtr.c"
#include "machine/mths.c"
#include "machine/mtcpm.c"
+#include "machine/mt100eocg.c"
/* include capabilities sub-directory support */
#include "mts_capab.c"
@@ -861,11 +872,17 @@ mts_id_eeprom_load(void)
log_info("detected board %s", tmp);
} else if (strncmp(id_eeprom.hw_version, HW_VERSION_MTCPM_0_0, strlen(HW_VERSION_MTCPM_0_0)) == 0) {
- attr_group = &mtcpm_platform_attribute_group;
- gpio_pins = gpio_pins_mtcpm;
- set_buttons(default_buttons);
- mts_hw_version = MTCPM_0_0;
- log_info("detected board %s", HW_VERSION_MTCPM_0_0);
+ attr_group = &mtcpm_platform_attribute_group;
+ gpio_pins = gpio_pins_mtcpm;
+ set_buttons(default_buttons);
+ mts_hw_version = MTCPM_0_0;
+ log_info("detected board %s", HW_VERSION_MTCPM_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_hw_version = MT100EOCG_0_0;
+ set_buttons(default_buttons);
+ log_info("detected board %s", HW_VERSION_MT100EOCG_0_0);
} else {
int i;
@@ -1007,6 +1024,23 @@ static int __init mts_io_init(void)
return ret;
}
+ if (DEVICE_CAPA(id_eeprom.capa, CAPA_DOUT)) {
+ ret = spi_register_driver(&mts_spi_dout_driver);
+ if (ret) {
+ printk(KERN_ERR "mts-io:mts-io-dout: probe failed: %d\n", ret);
+ }
+ }
+ if (DEVICE_CAPA(id_eeprom.capa, CAPA_DIN)) {
+ ret = spi_register_driver(&mts_spi_din_driver);
+ if (ret) {
+ printk(KERN_ERR "mts-io:mts-io-din: probe failed: %d\n", ret);
+ }
+ }
+ ret = spi_register_driver(&mts_spi_board_temp_driver);
+ if (ret) {
+ printk(KERN_ERR "mts-io:mts-io-board-temp: probe failed: %d\n", ret);
+ }
+
if (DEVICE_CAPA(id_eeprom.capa, CAPA_LORA) && attr_group_lora) {
mts_load_lora_port();
}
@@ -1050,6 +1084,14 @@ static int __init mts_io_init(void)
static void __exit mts_io_exit(void)
{
+ if (DEVICE_CAPA(id_eeprom.capa, CAPA_DOUT)) {
+ spi_unregister_driver(&mts_spi_dout_driver);
+ }
+ if (DEVICE_CAPA(id_eeprom.capa, CAPA_DIN)) {
+ spi_unregister_driver(&mts_spi_din_driver);
+ }
+ spi_unregister_driver(&mts_spi_board_temp_driver);
+
struct gpio_pin *pin;
/* delete radio_reset_timer */
del_timer(&radio_reset_timer);
diff --git a/io-module/mts_io_module.h b/io-module/mts_io_module.h
index 2d4b8f5..2c5976a 100644
--- a/io-module/mts_io_module.h
+++ b/io-module/mts_io_module.h
@@ -5,7 +5,7 @@
* MTAC cards.
*/
-#define DRIVER_VERSION "v4.3.0"
+#define DRIVER_VERSION "v4.3.1"
#define DRIVER_AUTHOR "Multitech Systems"
#define DRIVER_DESC "MTS-IO Controller"
#define DRIVER_NAME "mts-io"
diff --git a/io-module/spi.c b/io-module/spi.c
new file mode 100644
index 0000000..1244cfc
--- /dev/null
+++ b/io-module/spi.c
@@ -0,0 +1,597 @@
+/* SPI devices, functions, and attributes */
+
+static int ADT7302_to_celsius(int value)
+{
+ if (value & 0x2000) {
+ value = value - 16384;
+ }
+
+ value = value / 32 + 1 * ((value % 32) >= 16);
+
+ return value;
+}
+
+/* SPI Devices */
+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_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)"
+);
+
+/* Generic SPI functions */
+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;
+}
+
+/* ----------------------------------------------------------------------------
+ *
+ * SPI-based attribute show/store functions
+ *
+ * ----------------------------------------------------------------------------
+*/
+
+#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)
+
+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 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 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 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));
+}
+
+/* ----------------------------------------------------------------------------
+ *
+ * SPI-based attributes
+ *
+ * ----------------------------------------------------------------------------
+*/
+
+static DEVICE_ATTR_RO_MTS(dev_attr_board_temperature, "board-temperature",
+ mts_attr_show_board_temperature);
+
+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 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 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_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);
+
+/* SPI driver setup */
+static int 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 = mts_spi_sout_remove,
+};
+
+static int mts_spi_dout_probe(struct spi_device *spi)
+{
+ int tmp;
+
+ 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 = mts_spi_dout_remove,
+};
+
+static int mts_spi_din_probe(struct spi_device *spi)
+{
+ int tmp;
+ 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 = mts_spi_din_remove,
+};
+
+static int 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 = mts_spi_board_temp_remove,
+};