diff options
| -rw-r--r-- | configure.ac | 2 | ||||
| -rw-r--r-- | io-module/adc.c | 126 | ||||
| -rw-r--r-- | io-module/machine/mt100eocg.c | 252 | ||||
| -rw-r--r-- | io-module/mts-io.c | 60 | ||||
| -rw-r--r-- | io-module/mts_io_module.h | 2 | ||||
| -rw-r--r-- | io-module/spi.c | 597 | 
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, +}; | 
