[PATCH v2 13/14] thunderbolt: Read switch uid from EEPROM

2014-04-10 Thread Andreas Noever
Add eeprom access code and read the uid during switch initialization.
The UID will be used to check device identity after suspend.

Signed-off-by: Andreas Noever 
---
 drivers/thunderbolt/Makefile |   2 +-
 drivers/thunderbolt/eeprom.c | 189 +++
 drivers/thunderbolt/switch.c |   5 ++
 drivers/thunderbolt/tb.h |   3 +
 4 files changed, 198 insertions(+), 1 deletion(-)
 create mode 100644 drivers/thunderbolt/eeprom.c

diff --git a/drivers/thunderbolt/Makefile b/drivers/thunderbolt/Makefile
index 0122ca6..5d1053c 100644
--- a/drivers/thunderbolt/Makefile
+++ b/drivers/thunderbolt/Makefile
@@ -1,3 +1,3 @@
 obj-${CONFIG_THUNDERBOLT} := thunderbolt.o
-thunderbolt-objs := nhi.o ctl.o tb.o switch.o cap.o path.o tunnel_pci.o
+thunderbolt-objs := nhi.o ctl.o tb.o switch.o cap.o path.o tunnel_pci.o 
eeprom.o
 
diff --git a/drivers/thunderbolt/eeprom.c b/drivers/thunderbolt/eeprom.c
new file mode 100644
index 000..d1ae1e4
--- /dev/null
+++ b/drivers/thunderbolt/eeprom.c
@@ -0,0 +1,189 @@
+/*
+ * Thunderbolt Cactus Ridge driver - eeprom access
+ *
+ * Copyright (c) 2014 Andreas Noever 
+ */
+
+#include "tb.h"
+
+/**
+ * tb_eeprom_ctl_write() - write control word
+ */
+static int tb_eeprom_ctl_write(struct tb_switch *sw, struct tb_eeprom_ctl *ctl)
+{
+   return tb_sw_write(sw, ctl, TB_CFG_SWITCH, sw->cap_plug_events + 4, 1);
+}
+
+/**
+ * tb_eeprom_ctl_write() - read control word
+ */
+static int tb_eeprom_ctl_read(struct tb_switch *sw, struct tb_eeprom_ctl *ctl)
+{
+   return tb_sw_read(sw, ctl, TB_CFG_SWITCH, sw->cap_plug_events + 4, 1);
+}
+
+enum tb_eeprom_transfer {
+   TB_EEPROM_IN,
+   TB_EEPROM_OUT,
+};
+
+/**
+ * tb_eeprom_active - enable rom access
+ *
+ * WARNING: Always disable access after usage. Otherwise the controller will
+ * fail to reprobe.
+ */
+static int tb_eeprom_active(struct tb_switch *sw, bool enable)
+{
+   struct tb_eeprom_ctl ctl;
+   int res = tb_eeprom_ctl_read(sw, );
+   if (res)
+   return res;
+   if (enable) {
+   ctl.access_high = 1;
+   res = tb_eeprom_ctl_write(sw, );
+   if (res)
+   return res;
+   ctl.access_low = 0;
+   return tb_eeprom_ctl_write(sw, );
+   } else {
+   ctl.access_low = 1;
+   res = tb_eeprom_ctl_write(sw, );
+   if (res)
+   return res;
+   ctl.access_high = 0;
+   return tb_eeprom_ctl_write(sw, );
+   }
+}
+
+/**
+ * tb_eeprom_transfer - transfer one bit
+ *
+ * If TB_EEPROM_IN is passed, then the bit can be retrieved from ctl->data_in.
+ * IF TB_EEPROM_OUT is passed, then ctl->data_out will be written.
+ */
+static int tb_eeprom_transfer(struct tb_switch *sw, struct tb_eeprom_ctl *ctl,
+ enum tb_eeprom_transfer direction)
+{
+   int res;
+   if (direction == TB_EEPROM_OUT) {
+   res = tb_eeprom_ctl_write(sw, ctl);
+   if (res)
+   return res;
+   }
+   ctl->clock = 1;
+   res = tb_eeprom_ctl_write(sw, ctl);
+   if (res)
+   return res;
+   if (direction == TB_EEPROM_IN) {
+   res = tb_eeprom_ctl_read(sw, ctl);
+   if (res)
+   return res;
+   }
+   ctl->clock = 0;
+   return tb_eeprom_ctl_write(sw, ctl);
+}
+
+/**
+ * tb_eeprom_out - write one byte to the bus
+ */
+static int tb_eeprom_out(struct tb_switch *sw, u8 val)
+{
+   struct tb_eeprom_ctl ctl;
+   int i;
+   int res = tb_eeprom_ctl_read(sw, );
+   if (res)
+   return res;
+   for (i = 0; i < 8; i++) {
+   ctl.data_out = val & 0x80;
+   res = tb_eeprom_transfer(sw, , TB_EEPROM_OUT);
+   if (res)
+   return res;
+   val <<= 1;
+   }
+   return 0;
+}
+
+/**
+ * tb_eeprom_in - read one byte from the bus
+ */
+static int tb_eeprom_in(struct tb_switch *sw, u8 *val)
+{
+   struct tb_eeprom_ctl ctl;
+   int i;
+   int res = tb_eeprom_ctl_read(sw, );
+   if (res)
+   return res;
+   *val = 0;
+   for (i = 0; i < 8; i++) {
+   *val <<= 1;
+   res = tb_eeprom_transfer(sw, , TB_EEPROM_IN);
+   if (res)
+   return res;
+   *val |= ctl.data_in;
+   }
+   return 0;
+}
+
+/**
+ * tb_eeprom_read_n - read count bytes from offset into val
+ */
+static int tb_eeprom_read_n(struct tb_switch *sw, u16 offset, u8 *val,
+   size_t count)
+{
+   int i, res;
+   res = tb_eeprom_active(sw, true);
+   if (res)
+   return res;
+   res = tb_eeprom_out(sw, 3);
+   if (res)
+   return res;
+   res = tb_eeprom_out(sw, offset >> 8);
+   if (res)
+   return res;
+   res = tb_eeprom_out(sw, offset);
+   if (res)
+  

[PATCH v2 13/14] thunderbolt: Read switch uid from EEPROM

2014-04-10 Thread Andreas Noever
Add eeprom access code and read the uid during switch initialization.
The UID will be used to check device identity after suspend.

Signed-off-by: Andreas Noever andreas.noe...@gmail.com
---
 drivers/thunderbolt/Makefile |   2 +-
 drivers/thunderbolt/eeprom.c | 189 +++
 drivers/thunderbolt/switch.c |   5 ++
 drivers/thunderbolt/tb.h |   3 +
 4 files changed, 198 insertions(+), 1 deletion(-)
 create mode 100644 drivers/thunderbolt/eeprom.c

diff --git a/drivers/thunderbolt/Makefile b/drivers/thunderbolt/Makefile
index 0122ca6..5d1053c 100644
--- a/drivers/thunderbolt/Makefile
+++ b/drivers/thunderbolt/Makefile
@@ -1,3 +1,3 @@
 obj-${CONFIG_THUNDERBOLT} := thunderbolt.o
-thunderbolt-objs := nhi.o ctl.o tb.o switch.o cap.o path.o tunnel_pci.o
+thunderbolt-objs := nhi.o ctl.o tb.o switch.o cap.o path.o tunnel_pci.o 
eeprom.o
 
diff --git a/drivers/thunderbolt/eeprom.c b/drivers/thunderbolt/eeprom.c
new file mode 100644
index 000..d1ae1e4
--- /dev/null
+++ b/drivers/thunderbolt/eeprom.c
@@ -0,0 +1,189 @@
+/*
+ * Thunderbolt Cactus Ridge driver - eeprom access
+ *
+ * Copyright (c) 2014 Andreas Noever andreas.noe...@gmail.com
+ */
+
+#include tb.h
+
+/**
+ * tb_eeprom_ctl_write() - write control word
+ */
+static int tb_eeprom_ctl_write(struct tb_switch *sw, struct tb_eeprom_ctl *ctl)
+{
+   return tb_sw_write(sw, ctl, TB_CFG_SWITCH, sw-cap_plug_events + 4, 1);
+}
+
+/**
+ * tb_eeprom_ctl_write() - read control word
+ */
+static int tb_eeprom_ctl_read(struct tb_switch *sw, struct tb_eeprom_ctl *ctl)
+{
+   return tb_sw_read(sw, ctl, TB_CFG_SWITCH, sw-cap_plug_events + 4, 1);
+}
+
+enum tb_eeprom_transfer {
+   TB_EEPROM_IN,
+   TB_EEPROM_OUT,
+};
+
+/**
+ * tb_eeprom_active - enable rom access
+ *
+ * WARNING: Always disable access after usage. Otherwise the controller will
+ * fail to reprobe.
+ */
+static int tb_eeprom_active(struct tb_switch *sw, bool enable)
+{
+   struct tb_eeprom_ctl ctl;
+   int res = tb_eeprom_ctl_read(sw, ctl);
+   if (res)
+   return res;
+   if (enable) {
+   ctl.access_high = 1;
+   res = tb_eeprom_ctl_write(sw, ctl);
+   if (res)
+   return res;
+   ctl.access_low = 0;
+   return tb_eeprom_ctl_write(sw, ctl);
+   } else {
+   ctl.access_low = 1;
+   res = tb_eeprom_ctl_write(sw, ctl);
+   if (res)
+   return res;
+   ctl.access_high = 0;
+   return tb_eeprom_ctl_write(sw, ctl);
+   }
+}
+
+/**
+ * tb_eeprom_transfer - transfer one bit
+ *
+ * If TB_EEPROM_IN is passed, then the bit can be retrieved from ctl-data_in.
+ * IF TB_EEPROM_OUT is passed, then ctl-data_out will be written.
+ */
+static int tb_eeprom_transfer(struct tb_switch *sw, struct tb_eeprom_ctl *ctl,
+ enum tb_eeprom_transfer direction)
+{
+   int res;
+   if (direction == TB_EEPROM_OUT) {
+   res = tb_eeprom_ctl_write(sw, ctl);
+   if (res)
+   return res;
+   }
+   ctl-clock = 1;
+   res = tb_eeprom_ctl_write(sw, ctl);
+   if (res)
+   return res;
+   if (direction == TB_EEPROM_IN) {
+   res = tb_eeprom_ctl_read(sw, ctl);
+   if (res)
+   return res;
+   }
+   ctl-clock = 0;
+   return tb_eeprom_ctl_write(sw, ctl);
+}
+
+/**
+ * tb_eeprom_out - write one byte to the bus
+ */
+static int tb_eeprom_out(struct tb_switch *sw, u8 val)
+{
+   struct tb_eeprom_ctl ctl;
+   int i;
+   int res = tb_eeprom_ctl_read(sw, ctl);
+   if (res)
+   return res;
+   for (i = 0; i  8; i++) {
+   ctl.data_out = val  0x80;
+   res = tb_eeprom_transfer(sw, ctl, TB_EEPROM_OUT);
+   if (res)
+   return res;
+   val = 1;
+   }
+   return 0;
+}
+
+/**
+ * tb_eeprom_in - read one byte from the bus
+ */
+static int tb_eeprom_in(struct tb_switch *sw, u8 *val)
+{
+   struct tb_eeprom_ctl ctl;
+   int i;
+   int res = tb_eeprom_ctl_read(sw, ctl);
+   if (res)
+   return res;
+   *val = 0;
+   for (i = 0; i  8; i++) {
+   *val = 1;
+   res = tb_eeprom_transfer(sw, ctl, TB_EEPROM_IN);
+   if (res)
+   return res;
+   *val |= ctl.data_in;
+   }
+   return 0;
+}
+
+/**
+ * tb_eeprom_read_n - read count bytes from offset into val
+ */
+static int tb_eeprom_read_n(struct tb_switch *sw, u16 offset, u8 *val,
+   size_t count)
+{
+   int i, res;
+   res = tb_eeprom_active(sw, true);
+   if (res)
+   return res;
+   res = tb_eeprom_out(sw, 3);
+   if (res)
+   return res;
+   res = tb_eeprom_out(sw, offset  8);
+   if (res)
+   return res;
+