/* 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, save[3]; int i; int count, diff; if (!spi_board_temp_dev) { log_notice("spi_board_temp device not present"); return -ENODEV; } for(count = 3; count > 0; count--) { for (i=0;i<3;i++) { 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]; save[i] = temp_raw; } log_debug("temp: 0x%04X", temp_raw); /* If temp reading is very small, use the last * reading, because it could bounce around more. */ if (save[2] < 50) break; diff = save[2] - save[1]; if (diff < 0) diff = -diff; if (diff == 0) break; /* If temperature difference of the last two readings is less * than 10%, use the last reading */ if ((save[2] / diff) > 10) break; /* If we have tried 3 times and we still have more than a 10% * difference, we give up */ } if (count == 0) log_info("temp=%d, count=%d, diff=%d, save[0]=%d, save[1]=%d\n", temp_raw,count,diff,save[0],save[1]); 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, };