From: Andreas Noever <andreas.noever@gmail.com>
To: linux-kernel@vger.kernel.org,
Matthew Garrett <matthew.garrett@nebula.com>,
Greg KH <greg@kroah.com>, Bjorn Helgaas <bhelgaas@google.com>,
linux-pci@vger.kernel.org
Cc: Andreas Noever <andreas.noever@gmail.com>
Subject: [PATCH v5 14/15] thunderbolt: Read switch uid from EEPROM
Date: Tue, 3 Jun 2014 22:04:11 +0200 [thread overview]
Message-ID: <1401825852-4745-15-git-send-email-andreas.noever@gmail.com> (raw)
In-Reply-To: <1401825852-4745-1-git-send-email-andreas.noever@gmail.com>
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.noever@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 0000000..f28e402
--- /dev/null
+++ b/drivers/thunderbolt/eeprom.c
@@ -0,0 +1,189 @@
+/*
+ * Thunderbolt Cactus Ridge driver - eeprom access
+ *
+ * Copyright (c) 2014 Andreas Noever <andreas.noever@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;
+ res = tb_eeprom_out(sw, offset);
+ if (res)
+ return res;
+ for (i = 0; i < count; i++) {
+ res = tb_eeprom_in(sw, val + i);
+ if (res)
+ return res;
+ }
+ return tb_eeprom_active(sw, false);
+}
+
+int tb_eeprom_read_uid(struct tb_switch *sw, u64 *uid)
+{
+ u8 data[9];
+ struct tb_cap_plug_events cap;
+ int res;
+ if (!sw->cap_plug_events) {
+ tb_sw_warn(sw, "no TB_CAP_PLUG_EVENTS, cannot read eeprom\n");
+ return -ENOSYS;
+ }
+ res = tb_sw_read(sw, &cap, TB_CFG_SWITCH, sw->cap_plug_events,
+ sizeof(cap) / 4);
+ if (res)
+ return res;
+ if (!cap.eeprom_ctl.present || cap.eeprom_ctl.not_present) {
+ tb_sw_warn(sw, "no NVM\n");
+ return -ENOSYS;
+ }
+
+ if (cap.drom_offset > 0xffff) {
+ tb_sw_warn(sw, "drom offset is larger than 0xffff: %#x\n",
+ cap.drom_offset);
+ return -ENXIO;
+ }
+
+ /* read uid */
+ res = tb_eeprom_read_n(sw, cap.drom_offset, data, 9);
+ if (res)
+ return res;
+ /* TODO: check checksum in data[0] */
+ *uid = *(u64 *)(data+1);
+ return 0;
+}
+
+
+
diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index 667413f..aeb5c30 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -376,6 +376,11 @@ struct tb_switch *tb_switch_alloc(struct tb *tb, u64 route)
}
sw->cap_plug_events = cap;
+ if (tb_eeprom_read_uid(sw, &sw->uid))
+ tb_sw_warn(sw, "could not read uid from eeprom\n");
+ else
+ tb_sw_info(sw, "uid: %#llx\n", sw->uid);
+
if (tb_plug_events_active(sw, true))
goto err;
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index 508abc4..a89087f 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -19,6 +19,7 @@ struct tb_switch {
struct tb_regs_switch_header config;
struct tb_port *ports;
struct tb *tb;
+ u64 uid;
int cap_plug_events; /* offset, zero if not found */
bool is_unplugged; /* unplugged, will go away */
};
@@ -231,6 +232,8 @@ int tb_path_activate(struct tb_path *path);
void tb_path_deactivate(struct tb_path *path);
bool tb_path_is_invalid(struct tb_path *path);
+int tb_eeprom_read_uid(struct tb_switch *sw, u64 *uid);
+
static inline int tb_route_length(u64 route)
{
--
2.0.0
next prev parent reply other threads:[~2014-06-03 20:05 UTC|newest]
Thread overview: 23+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-06-03 20:03 [PATCH v5 00/15] Thunderbolt driver for Apple MacBooks Andreas Noever
2014-06-03 20:03 ` [PATCH v5 01/15] thunderbolt: Add initial cactus ridge NHI support Andreas Noever
2014-06-20 7:41 ` Paul Bolle
2014-06-20 16:34 ` Greg KH
2014-06-20 19:45 ` Andreas Noever
2014-06-20 20:28 ` Paul Bolle
2014-06-03 20:03 ` [PATCH v5 02/15] thunderbolt: Add control channel interface Andreas Noever
2014-06-03 20:04 ` [PATCH v5 03/15] thunderbolt: Setup control channel Andreas Noever
2014-06-03 20:04 ` [PATCH v5 04/15] thunderbolt: Add tb_regs.h Andreas Noever
2014-06-03 20:04 ` [PATCH v5 05/15] thunderbolt: Initialize root switch and ports Andreas Noever
2014-06-03 20:04 ` [PATCH v5 06/15] thunderbolt: Add thunderbolt capability handling Andreas Noever
2014-06-03 20:04 ` [PATCH v5 07/15] thunderbolt: Enable plug events Andreas Noever
2014-06-03 20:04 ` [PATCH v5 08/15] thunderbolt: Scan for downstream switches Andreas Noever
2014-06-03 20:04 ` [PATCH v5 09/15] thunderbolt: Handle hotplug events Andreas Noever
2014-06-03 20:04 ` [PATCH v5 10/15] thunderbolt: Add path setup code Andreas Noever
2014-06-03 20:04 ` [PATCH v5 11/15] thunderbolt: Add support for simple pci tunnels Andreas Noever
2014-06-03 20:04 ` [PATCH v5 12/15] PCI: Add pci_fixup_suspend_late quirk pass Andreas Noever
2014-06-03 20:04 ` [PATCH v5 13/15] PCI: Suspend/resume quirks for Apple thunderbolt Andreas Noever
2014-06-03 20:04 ` Andreas Noever [this message]
2014-06-03 20:04 ` [PATCH v5 15/15] thunderbolt: Add suspend/hibernate support Andreas Noever
2014-06-03 20:33 ` [PATCH v5 00/15] Thunderbolt driver for Apple MacBooks Greg KH
2014-06-19 21:14 ` Greg KH
2014-06-30 9:17 ` Andreas Noever
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1401825852-4745-15-git-send-email-andreas.noever@gmail.com \
--to=andreas.noever@gmail.com \
--cc=bhelgaas@google.com \
--cc=greg@kroah.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pci@vger.kernel.org \
--cc=matthew.garrett@nebula.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.