summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--io-module/Makefile1
-rw-r--r--io-module/mts_io.c675
-rw-r--r--io-module/mts_io.h33
3 files changed, 700 insertions, 9 deletions
diff --git a/io-module/Makefile b/io-module/Makefile
index 5d036f4..f95a55e 100644
--- a/io-module/Makefile
+++ b/io-module/Makefile
@@ -1,4 +1,5 @@
obj-m := mts_io.o
+CFLAGS_mts_io.o := ${DAUGHTER_CARD}
clean:
rm -f *.ko *.o
diff --git a/io-module/mts_io.c b/io-module/mts_io.c
index 89e1e0e..5a30e2e 100644
--- a/io-module/mts_io.c
+++ b/io-module/mts_io.c
@@ -83,10 +83,23 @@ static int led_mode_status = LED_OFF;
#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;
@@ -632,6 +645,14 @@ static struct gpio_pin gpio_pins_mtr2_0_0[] = {
.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,
@@ -679,6 +700,54 @@ static struct gpio_pin gpio_pins_mtr2_0_0[] = {
.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,
+ },
{ },
};
@@ -1159,6 +1228,32 @@ MODULE_PARM_DESC(
"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);
@@ -2489,6 +2584,281 @@ static DEVICE_ATTR_RO_MTS(dev_attr_wifi_mac, "mac-wifi",
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,
@@ -2536,7 +2906,7 @@ static struct attribute_group mt100eocg_platform_attribute_group = {
.attrs = mt100eocg_platform_attributes
};
-static struct attribute *mtr2_platform_attributes[] = {
+struct attribute *mtr2_platform_attributes[] = {
&dev_attr_vendor_id.attr,
&dev_attr_product_id.attr,
&dev_attr_device_id.attr,
@@ -2576,6 +2946,47 @@ static struct attribute *mtr2_platform_attributes[] = {
&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,
};
@@ -2583,6 +2994,32 @@ static struct attribute_group mtr2_platform_attribute_group = {
.attrs = mtr2_platform_attributes
};
+bool mtr2_add_daughter_card_attributes()
+{
+ 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,
@@ -2825,6 +3262,139 @@ static struct spi_driver mts_spi_din_driver = {
.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;
@@ -2863,6 +3433,36 @@ static struct spi_driver mts_spi_board_temp_driver = {
.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));
@@ -2901,6 +3501,7 @@ static int mts_id_eeprom_load(void)
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;
@@ -2928,6 +3529,7 @@ static int mts_id_eeprom_load(void)
attr_group = &mtr2_platform_attribute_group;
gpio_pins = gpio_pins_mtr2_0_0;
mts_product_id = MTOCGD3_0_0;
+ daughter_card_capable = true;
has_spi_sout = 0;
has_spi_din = 0;
has_spi_dout = 0;
@@ -3005,12 +3607,65 @@ static int __init mts_io_init(void)
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;
@@ -3168,6 +3823,19 @@ static void __exit mts_io_exit(void)
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");
@@ -3189,3 +3857,6 @@ 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");
diff --git a/io-module/mts_io.h b/io-module/mts_io.h
index f2ff2b4..efbd8f8 100644
--- a/io-module/mts_io.h
+++ b/io-module/mts_io.h
@@ -28,18 +28,21 @@ struct device_attribute _dev_name = { \
#define VENDOR_ID_MULTITECH "Multi-Tech Systems"
#define PRODUCT_ID_MTCDP_E1_DK "MTCDP-E1-DK"
#define PRODUCT_ID_MT100EOCG "MT100EOCG"
-#define PRODUCT_ID_MTR2 "MTR2"
-#define PRODUCT_ID_MTR "MTR"
-#define PRODUCT_ID_MTOCGD3 "MTOCGD3"
-#define PRODUCT_ID_MTOCGD "MTOCGD"
+#define PRODUCT_ID_MTR2 "MTR2"
+#define PRODUCT_ID_MTR "MTR"
+#define PRODUCT_ID_MTOCGD3 "MTOCGD3"
+#define PRODUCT_ID_MTOCGD "MTOCGD"
+
+#define PRODUCT_ID_MTDC_GPIOB "MTDC-GPIOB"
+
#define HW_VERSION_MTCBA2_2_0 "MTCBA2-2.0"
#define HW_VERSION_MTCDP_0_0 "MTCDP-0.0"
#define HW_VERSION_MTCDP_1_0 "MTCDP-1.0"
#define HW_VERSION_MT100EOCG_0_0 "MT100EOCG-0.0"
-#define HW_VERSION_MTR2_0_0 "MTR2-0.0"
-#define HW_VERSION_MTR_0_0 "MTR-0.0"
-#define HW_VERSION_MTR_0_1 "MTR-0.1"
+#define HW_VERSION_MTR2_0_0 "MTR2-0.0"
+#define HW_VERSION_MTR_0_0 "MTR-0.0"
+#define HW_VERSION_MTR_0_1 "MTR-0.1"
#define HW_VERSION_MTOCGD3_0_0 "MTOCGD3-0.0"
#define HW_VERSION_MTOCGD_0_0 "MTOCGD-0.0"
#define HW_VERSION_MTOCGD_0_1 "MTOCGD-0.1"
@@ -56,6 +59,11 @@ enum {
MTOCGD_0_1,
};
+enum {
+ MTDC_NONE,
+ MTDC_GPIOB_0_0,
+};
+
#define DEVICE_CAPA_INDEX(c) (((c) & 0xFF) >> 3)
#define DEVICE_CAPA_MASK(c) BIT((c) & 0x07)
@@ -81,6 +89,7 @@ do { \
#define CAPA_BLUETOOTH DEVICE_CAPA_VALUE(1, 7)
#define CAPA_WIFI DEVICE_CAPA_VALUE(1, 6)
+/* on-board EEPROM */
struct mts_id_eeprom_layout {
char vendor_id[32];
char product_id[32];
@@ -94,6 +103,16 @@ struct mts_id_eeprom_layout {
uint8_t reserved[302];
};
+/* daughter card EEPROM */
+struct mts_dc_eeprom_layout {
+ char vendor_id[32];
+ char product_id[32];
+ char device_id[32];
+ char hw_version[32];
+ uint8_t mac_addr[6];
+ uint8_t reserved[378];
+};
+
// GPIO pin types:input, output, open drain (1 = high Z, 0 = output low)
enum {
GPIO_DIR_INPUT,