Commit eacb7d5e authored by Andrey Filippov's avatar Andrey Filippov
Browse files

added writing to a magic register (was not very clear in the datasheet), added...

added writing to a magic register (was not very clear in the datasheet), added re-init after soft reset
parent d9cbcc3a
Loading
Loading
Loading
Loading
+55 −66
Original line number Original line Diff line number Diff line
Index: git/drivers/misc/Kconfig
Index: git/drivers/misc/Kconfig
===================================================================
===================================================================
--- git.orig/drivers/misc/Kconfig	2013-11-26 01:01:48.820127445 -0700
--- git.orig/drivers/misc/Kconfig	2013-11-26 16:00:21.216686641 -0700
+++ git/drivers/misc/Kconfig	2013-11-26 01:01:51.944127477 -0700
+++ git/drivers/misc/Kconfig	2013-11-26 16:00:24.260686672 -0700
@@ -549,6 +549,13 @@
@@ -549,6 +549,13 @@
 	  their requirements.
 	  their requirements.
 
 
@@ -18,8 +18,8 @@ Index: git/drivers/misc/Kconfig
 source "drivers/misc/eeprom/Kconfig"
 source "drivers/misc/eeprom/Kconfig"
Index: git/drivers/misc/Makefile
Index: git/drivers/misc/Makefile
===================================================================
===================================================================
--- git.orig/drivers/misc/Makefile	2013-11-26 01:01:48.820127445 -0700
--- git.orig/drivers/misc/Makefile	2013-11-26 16:00:21.216686641 -0700
+++ git/drivers/misc/Makefile	2013-11-26 01:01:51.944127477 -0700
+++ git/drivers/misc/Makefile	2013-11-26 16:00:24.260686672 -0700
@@ -55,3 +55,4 @@
@@ -55,3 +55,4 @@
 obj-$(CONFIG_LATTICE_ECP3_CONFIG)	+= lattice-ecp3-config.o
 obj-$(CONFIG_LATTICE_ECP3_CONFIG)	+= lattice-ecp3-config.o
 obj-$(CONFIG_SRAM)		+= sram.o
 obj-$(CONFIG_SRAM)		+= sram.o
@@ -28,8 +28,8 @@ Index: git/drivers/misc/Makefile
Index: git/drivers/misc/vsc330x.c
Index: git/drivers/misc/vsc330x.c
===================================================================
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ git/drivers/misc/vsc330x.c	2013-11-26 09:41:58.660451163 -0700
+++ git/drivers/misc/vsc330x.c	2013-11-27 16:34:08.697603838 -0700
@@ -0,0 +1,805 @@
@@ -0,0 +1,794 @@
+/*!***************************************************************************
+/*!***************************************************************************
+ *! FILE NAME  : vsc330x.c
+ *! FILE NAME  : vsc330x.c
+ *! DESCRIPTION: control of the VSC3304 4x4 crosspoint switch
+ *! DESCRIPTION: control of the VSC3304 4x4 crosspoint switch
@@ -132,7 +132,6 @@ Index: git/drivers/misc/vsc330x.c
+#define I2C_SOFTWARE_RESET         0x7a
+#define I2C_SOFTWARE_RESET         0x7a
+#define I2C_SOFTWARE_RESET_DATA    0x10	/* to reset, 0 - normal  */
+#define I2C_SOFTWARE_RESET_DATA    0x10	/* to reset, 0 - normal  */
+#define I2C_CURRENT_PAGE           0x7f
+#define I2C_CURRENT_PAGE           0x7f
+#define VSC330X_FEATURE_FREEZE_UPDATES   0x01
+#define PORT_PEFIX                 "port_"
+#define PORT_PEFIX                 "port_"
+#define ALL_PORTS                  "all"
+#define ALL_PORTS                  "all"
+#define MAX_PORTS                   16
+#define MAX_PORTS                   16
@@ -161,32 +160,27 @@ Index: git/drivers/misc/vsc330x.c
+MODULE_DEVICE_TABLE(i2c, vsc3304_id);
+MODULE_DEVICE_TABLE(i2c, vsc3304_id);
+
+
+struct vsc330x_data_t {
+struct vsc330x_data_t {
+	u8  features;
+	int  address_mode_data; // vsc3304 needs 6 to be written, otherwise weird modification  of bit 3
+	u8  last_page;
+	int  last_page;
+	u32 in_ports;
+	u32 in_ports;
+	u32 out_ports;
+	u32 out_ports;
+};
+};
+
+
+static const struct vsc330x_data_t vsc330x_data[] = {
+static const struct vsc330x_data_t vsc330x_data[] = {
+		{.features=0,
+		{.address_mode_data=6,
+//				.in_ports=0xff00, .out_ports=0xff00}, /* 3304 - all ports I/O shared*/
+				.in_ports=0xff00, .out_ports=0xff00}, /* 3304 - all ports I/O shared*/
+/* for testing only enabling all ports */
+		{.address_mode_data=-1, // No data, unknown if it is needed for 3308
+				.in_ports=0xffff, .out_ports=0xffff}, /* 3304 - all ports I/O shared*/
+		{.features=0,
+				.in_ports=0x00ff, .out_ports=0x00ff}, /* 3308 - no ports I/O shared */
+				.in_ports=0x00ff, .out_ports=0x00ff}, /* 3308 - no ports I/O shared */
+		{.features=VSC330X_FEATURE_FREEZE_UPDATES ,
+		{.address_mode_data=-1, // No data, unknown if it is needed for 3308
+				.in_ports=0xffff, .out_ports=0xffff} /* 3312 - some ports I/O shared*/
+				.in_ports=0xffff, .out_ports=0xffff} /* 3312 - some ports I/O shared*/
+};
+};
+
+static int init_device(struct i2c_client *client);
+static int read_reg(struct i2c_client *client, u8 reg);
+static int read_reg(struct i2c_client *client, u8 reg);
+static int write_reg(struct i2c_client *client, u8 reg, u8 val);
+static int write_reg(struct i2c_client *client, u8 reg, u8 val);
+static int read_page_reg(struct i2c_client *client, int page, u8 reg);
+static int write_page_reg(struct i2c_client *client, int page, u8 reg, u8 val);
+static int read_field(struct i2c_client *client, u8 reg, int ls_bit_num, int width);
+static int read_field(struct i2c_client *client, u8 reg, int ls_bit_num, int width);
+static int read_page_field(struct i2c_client *client, int page, u8 reg, int ls_bit_num, int width);
+static int read_page_field(struct i2c_client *client, int page, u8 reg, int ls_bit_num, int width);
+static int write_field(struct i2c_client *client, u8 reg, u8 val, int ls_bit_num, int width);
+static int write_field(struct i2c_client *client, u8 reg, u8 val, int ls_bit_num, int width);
+static int write_page_field(struct i2c_client *client, int page, u8 reg, u8 val, int ls_bit_num, int width);
+static int write_page_field(struct i2c_client *client, int page, u8 reg, u8 val, int ls_bit_num, int width);
+static int freeze(struct device *dev, int lock);
+static ssize_t field_show(struct device *dev, struct device_attribute *attr, char *buf,
+static ssize_t field_show(struct device *dev, struct device_attribute *attr, char *buf,
+		int page, int ls_bit_num, int width);
+		int page, int ls_bit_num, int width);
+static ssize_t field_show_reg(struct device *dev, char *buf, int page, int reg, int ls_bit_num, int width);
+static ssize_t field_show_reg(struct device *dev, char *buf, int page, int reg, int ls_bit_num, int width);
@@ -266,6 +260,7 @@ Index: git/drivers/misc/vsc330x.c
+static ssize_t soft_reset_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t soft_reset_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t address_range_show (struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t address_range_show (struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t address_range_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t address_range_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t status_combo_show (struct device *dev, struct device_attribute *attr, char *buf);
+
+
+/* Global registers - writes applied to all port registers. No sense to read (so write only), but functions preserved
+/* Global registers - writes applied to all port registers. No sense to read (so write only), but functions preserved
+ * Placed in "globals" directory */
+ * Placed in "globals" directory */
@@ -288,7 +283,7 @@ Index: git/drivers/misc/vsc330x.c
+static DEVICE_ATTR(core_config_pin_invert,SYSFS_PERMISSIONS,               core_config_pin_invert_show, core_config_pin_invert_store);
+static DEVICE_ATTR(core_config_pin_invert,SYSFS_PERMISSIONS,               core_config_pin_invert_show, core_config_pin_invert_store);
+static DEVICE_ATTR(soft_reset,        SYSFS_PERMISSIONS & SYSFS_WRITEONLY, NULL,                        soft_reset_store);
+static DEVICE_ATTR(soft_reset,        SYSFS_PERMISSIONS & SYSFS_WRITEONLY, NULL,                        soft_reset_store);
+static DEVICE_ATTR(address_range,     SYSFS_PERMISSIONS,                   address_range_show,          address_range_store);
+static DEVICE_ATTR(address_range,     SYSFS_PERMISSIONS,                   address_range_show,          address_range_store);
+static DEVICE_ATTR(status,            SYSFS_PERMISSIONS & SYSFS_READONLY,  status_show, NULL);
+static DEVICE_ATTR(status,            SYSFS_PERMISSIONS & SYSFS_READONLY,  status_combo_show, NULL);
+
+
+static struct attribute *globals_dev_attrs[] = {
+static struct attribute *globals_dev_attrs[] = {
+	&dev_attr_connection.attr,
+	&dev_attr_connection.attr,
@@ -405,6 +400,8 @@ Index: git/drivers/misc/vsc330x.c
+    	if ((retval=make_group (dev, "forward_OOB",           ports, SYSFS_PERMISSIONS, forward_OOB_show, forward_OOB_store))) return retval;
+    	if ((retval=make_group (dev, "forward_OOB",           ports, SYSFS_PERMISSIONS, forward_OOB_show, forward_OOB_store))) return retval;
+    	if ((retval=make_group (dev, "status_0_on_LOS",       ports, SYSFS_PERMISSIONS, status_0_on_LOS_show, status_0_on_LOS_store))) return retval;
+    	if ((retval=make_group (dev, "status_0_on_LOS",       ports, SYSFS_PERMISSIONS, status_0_on_LOS_show, status_0_on_LOS_store))) return retval;
+    	if ((retval=make_group (dev, "status_1_on_LOS",       ports, SYSFS_PERMISSIONS, status_1_on_LOS_show, status_1_on_LOS_store))) return retval;
+    	if ((retval=make_group (dev, "status_1_on_LOS",       ports, SYSFS_PERMISSIONS, status_1_on_LOS_show, status_1_on_LOS_store))) return retval;
+    	if ((retval=make_group (dev, "status",                ports, SYSFS_PERMISSIONS & SYSFS_READONLY, status_show, NULL))) return retval;
+
+    	if ((retval = sysfs_create_group(&dev->kobj, &dev_attr_globals_group))) return retval;
+    	if ((retval = sysfs_create_group(&dev->kobj, &dev_attr_globals_group))) return retval;
+    	if ((retval = sysfs_create_group(&dev->kobj, &dev_attr_control_group))) return retval;
+    	if ((retval = sysfs_create_group(&dev->kobj, &dev_attr_control_group))) return retval;
+    }
+    }
@@ -578,8 +575,11 @@ Index: git/drivers/misc/vsc330x.c
+	{return field_show(dev, attr, buf,         I2C_PAGE_STATUS1_CONFIGURE, 0, 1);}
+	{return field_show(dev, attr, buf,         I2C_PAGE_STATUS1_CONFIGURE, 0, 1);}
+static ssize_t status_1_on_LOS_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+static ssize_t status_1_on_LOS_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+	{return field_store(dev, attr, buf, count, I2C_PAGE_STATUS1_CONFIGURE, 0, 1);}
+	{return field_store(dev, attr, buf, count, I2C_PAGE_STATUS1_CONFIGURE, 0, 1);}
+
+/* per-port LOS status */
+static ssize_t status_show (struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t status_show (struct device *dev, struct device_attribute *attr, char *buf)
+    {return field_show(dev, attr, buf,         I2C_PAGE_STATUS_READ, 0, 1);}
+/* combined LOS status */
+static ssize_t status_combo_show (struct device *dev, struct device_attribute *attr, char *buf)
+	{return field_show_reg(dev, buf, I2C_PAGE_STATUS_READ, 0x10, 0, 2);}
+	{return field_show_reg(dev, buf, I2C_PAGE_STATUS_READ, 0x10, 0, 2);}
+
+
+
+
@@ -661,19 +661,48 @@ Index: git/drivers/misc/vsc330x.c
+	{return field_store_reg(dev, buf, count, I2C_PAGE_GLOBAL, I2C_CORE_CONFIGURATION, 0, 1);}
+	{return field_store_reg(dev, buf, count, I2C_PAGE_GLOBAL, I2C_CORE_CONFIGURATION, 0, 1);}
+
+
+static ssize_t soft_reset_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+static ssize_t soft_reset_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+	{return field_store_reg(dev, buf, count, I2C_PAGE_GLOBAL, I2C_SOFTWARE_RESET, 4, 1);}
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	int rc;
+	if (((rc=field_store_reg(dev, buf, count, I2C_PAGE_GLOBAL, I2C_SOFTWARE_RESET, 4, 1)))<0) return rc;
+	if (((rc=init_device(client)))<0) return rc;
+	return count;
+}
+
+
+static ssize_t address_range_show (struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t address_range_show (struct device *dev, struct device_attribute *attr, char *buf)
+	{return field_show_reg (dev, buf,        I2C_PAGE_GLOBAL, I2C_SOFTWARE_RESET, 0, 4);}
+	{return field_show_reg (dev, buf,        I2C_PAGE_GLOBAL, I2C_SOFTWARE_RESET, 0, 4);}
+static ssize_t address_range_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+static ssize_t address_range_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+	{return field_store_reg(dev, buf, count, I2C_PAGE_GLOBAL, I2C_SOFTWARE_RESET, 0, 4);}
+	{return field_store_reg(dev, buf, count, I2C_PAGE_GLOBAL, I2C_SOFTWARE_RESET, 0, 4);}
+
+
+/* Setup i2c, write address_mode. Needed after soft reset and power up */
+
+static int _init_device(struct i2c_client *client, int address_mode_data)
+{
+	int rc;
+    if (((rc=write_reg(client, I2C_INTERFACE_MODE, I2C_INTERFACE_MODE_DATA)))<0) return rc;
+    if (address_mode_data >=0){ /* only write if needed, 3304 needs, 3312 - does not, 3308 - ? */
+        if (((rc=write_reg(client, I2C_SOFTWARE_RESET_DATA, address_mode_data)))<0) return rc;
+    }
+    if (((rc=write_reg(client, I2C_CURRENT_PAGE, 0)))<0) return rc;
+	return 0;
+}
+
+
+static int init_device(struct i2c_client *client)
+{
+	int rc;
+	struct vsc330x_data_t *clientdata = i2c_get_clientdata(client);
+	dev_info(&client->dev,"Re-initializing %s\n",client->name);
+	if (((rc=_init_device(client, clientdata->address_mode_data)))<0) return rc;
+	clientdata->last_page=0;
+	return 0;
+}
+
+
+static int bitmask(int ls_bit_num, int width)
+static int bitmask(int ls_bit_num, int width)
+{
+{
+	return ((1 << width) -1) << ls_bit_num;
+	return ((1 << width) -1) << ls_bit_num;
+}
+}
+
+//address_mode_data
+static int read_reg(struct i2c_client *client, u8 reg)
+static int read_reg(struct i2c_client *client, u8 reg)
+{
+{
+	int val;
+	int val;
@@ -688,28 +717,6 @@ Index: git/drivers/misc/vsc330x.c
+	return i2c_smbus_write_byte_data(client, reg, val);
+	return i2c_smbus_write_byte_data(client, reg, val);
+}
+}
+
+
+static int read_page_reg(struct i2c_client *client, int page, u8 reg)
+{
+	int rc;
+	struct vsc330x_data_t *clientdata = i2c_get_clientdata(client);
+	if ((page>=0) && (clientdata->last_page!=page)) {
+		if (((rc=write_reg(client, I2C_CURRENT_PAGE, page)))<0) return rc;
+		clientdata->last_page=page;
+	}
+	return read_reg(client, reg);
+}
+
+static int write_page_reg(struct i2c_client *client, int page, u8 reg, u8 val)
+{
+	int rc;
+	struct vsc330x_data_t *clientdata = i2c_get_clientdata(client);
+	if ((page>=0) && (clientdata->last_page!=page)) {
+		if (((rc=write_reg(client, I2C_CURRENT_PAGE, page)))<0) return rc;
+		clientdata->last_page=page;
+	}
+	return write_reg(client, reg, val);
+}
+
+static int read_field(struct i2c_client *client, u8 reg, int ls_bit_num, int width)
+static int read_field(struct i2c_client *client, u8 reg, int ls_bit_num, int width)
+{
+{
+	int rc;
+	int rc;
@@ -755,32 +762,14 @@ Index: git/drivers/misc/vsc330x.c
+	return write_field(client, reg, val, ls_bit_num, width);
+	return write_field(client, reg, val, ls_bit_num, width);
+}
+}
+
+
+static int freeze(struct device *dev, int lock)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct vsc330x_data_t *clientdata = i2c_get_clientdata(client);
+	if (clientdata->features & VSC330X_FEATURE_FREEZE_UPDATES ){
+	 /*Assumes config pin grounded, otherwise swap freeze/unfreeze*/
+		return write_reg(client, I2C_CORE_CONFIGURATION, lock? I2C_CORE_CONFIGURATION_DATAF: I2C_CORE_CONFIGURATION_DATA);
+	} else {
+		dev_dbg(dev, "Update freeze is not implemented in %s\n",client->name);
+		return 0; /* just do nothing */
+	}
+
+}
+
+static int vsc330x_i2c_probe(struct i2c_client *client,
+static int vsc330x_i2c_probe(struct i2c_client *client,
+				      const struct i2c_device_id *id)
+				      const struct i2c_device_id *id)
+{
+{
+	int rc=0;
+	int rc=0;
+	struct vsc330x_data_t *clientdata = NULL;
+	struct vsc330x_data_t *clientdata = NULL;
+	/* initialize i2c mode and (if needed) address range bit field */
+
+
+/* initialize i2c mode */
+	if (((rc=_init_device(client, vsc330x_data[id->driver_data].address_mode_data)))<0) goto wr_err;
+    rc=write_reg(client, I2C_INTERFACE_MODE, I2C_INTERFACE_MODE_DATA);
+    if (rc<0) goto wr_err;
+    rc=write_reg(client, I2C_CURRENT_PAGE, 0); // initialize to page 0
+    if (rc<0) goto wr_err;
+
+	dev_info(&client->dev,
+	dev_info(&client->dev,
+		 "Chip %s found, driver version %s\n", id->name, DRV_VERSION);
+		 "Chip %s found, driver version %s\n", id->name, DRV_VERSION);
+	clientdata = devm_kzalloc(&client->dev, sizeof(*clientdata), GFP_KERNEL);
+	clientdata = devm_kzalloc(&client->dev, sizeof(*clientdata), GFP_KERNEL);
@@ -789,7 +778,7 @@ Index: git/drivers/misc/vsc330x.c
+		goto exit;
+		goto exit;
+	}
+	}
+	clientdata->last_page = 0;
+	clientdata->last_page = 0;
+	clientdata->features = vsc330x_data[id->driver_data].features;
+	clientdata->address_mode_data = vsc330x_data[id->driver_data].address_mode_data;
+	clientdata->in_ports = vsc330x_data[id->driver_data].in_ports;
+	clientdata->in_ports = vsc330x_data[id->driver_data].in_ports;
+	clientdata->out_ports = vsc330x_data[id->driver_data].out_ports;
+	clientdata->out_ports = vsc330x_data[id->driver_data].out_ports;
+	i2c_set_clientdata(client, clientdata);
+	i2c_set_clientdata(client, clientdata);