[PATCH v2 2/4] ipmi: bt-i2c: added IPMI Block Transfer over I2C host side

2017-08-04 Thread Brendan Higgins
The IPMI definition of the Block Transfer protocol defines the hardware
registers and behavior in addition to the message format and messaging
semantics. This implements a new protocol that uses IPMI Block Transfer
messages and semantics on top of a standard I2C interface.

Signed-off-by: Brendan Higgins 
---
Changes for v2:
  - None
---
 drivers/char/ipmi/Kconfig   |   4 +
 drivers/char/ipmi/Makefile  |   1 +
 drivers/char/ipmi/ipmi_bt_i2c.c | 452 
 3 files changed, 457 insertions(+)
 create mode 100644 drivers/char/ipmi/ipmi_bt_i2c.c

diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig
index f6fa056a52fc..a8734a369cb0 100644
--- a/drivers/char/ipmi/Kconfig
+++ b/drivers/char/ipmi/Kconfig
@@ -79,6 +79,10 @@ config IPMI_POWEROFF
  This enables a function to power off the system with IPMI if
 the IPMI management controller is capable of this.
 
+config IPMI_BT_I2C
+   select I2C
+   tristate 'BT IPMI bmc driver over I2c'
+
 endif # IPMI_HANDLER
 
 config ASPEED_BT_IPMI_BMC
diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile
index eefb0b301e83..323de0b0b8b5 100644
--- a/drivers/char/ipmi/Makefile
+++ b/drivers/char/ipmi/Makefile
@@ -12,4 +12,5 @@ obj-$(CONFIG_IPMI_SSIF) += ipmi_ssif.o
 obj-$(CONFIG_IPMI_POWERNV) += ipmi_powernv.o
 obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o
 obj-$(CONFIG_IPMI_POWEROFF) += ipmi_poweroff.o
+obj-$(CONFIG_IPMI_BT_I2C) += ipmi_bt_i2c.o
 obj-$(CONFIG_ASPEED_BT_IPMI_BMC) += bt-bmc.o
diff --git a/drivers/char/ipmi/ipmi_bt_i2c.c b/drivers/char/ipmi/ipmi_bt_i2c.c
new file mode 100644
index ..94b5c11d23cd
--- /dev/null
+++ b/drivers/char/ipmi/ipmi_bt_i2c.c
@@ -0,0 +1,452 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt)"ipmi-bt-i2c: " fmt
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define IPMI_BT_I2C_TIMEOUT (msecs_to_jiffies(1000))
+
+/* If we don't have netfn_lun, seq, and cmd, we might as well have nothing. */
+#define IPMI_BT_I2C_LEN_MIN 3
+/* We need at least netfn_lun, seq, cmd, and completion. */
+#define IPMI_BT_I2C_RESPONSE_LEN_MIN 4
+#define IPMI_BT_I2C_MSG_MAX_PAYLOAD_SIZE 252
+
+struct ipmi_bt_i2c_msg {
+   u8 len;
+   u8 netfn_lun;
+   u8 seq;
+   u8 cmd;
+   u8 payload[IPMI_BT_I2C_MSG_MAX_PAYLOAD_SIZE];
+} __packed;
+
+#define IPMI_BT_I2C_MAX_SMI_SIZE 254 /* Need extra byte for seq. */
+#define IPMI_BT_I2C_SMI_MSG_HEADER_SIZE 2
+
+struct ipmi_bt_i2c_smi_msg {
+   u8 netfn_lun;
+   u8 cmd;
+   u8 payload[IPMI_MAX_MSG_LENGTH - 2];
+} __packed;
+
+static inline u32 bt_msg_len(struct ipmi_bt_i2c_msg *bt_request)
+{
+   return bt_request->len + 1;
+}
+
+#define IPMI_BT_I2C_SEQ_MAX 256
+
+struct ipmi_bt_i2c_seq_entry {
+   struct ipmi_smi_msg *msg;
+   unsigned long   send_time;
+};
+
+struct ipmi_bt_i2c_master {
+   struct ipmi_device_id   ipmi_id;
+   struct i2c_client   *client;
+   ipmi_smi_t  intf;
+   spinlock_t  lock;
+   struct ipmi_bt_i2c_seq_entryseq_msg_map[IPMI_BT_I2C_SEQ_MAX];
+   struct work_struct  ipmi_bt_i2c_recv_work;
+   struct work_struct  ipmi_bt_i2c_send_work;
+   struct ipmi_smi_msg *msg_to_send;
+};
+
+static const unsigned long write_timeout = 25;
+
+static int ipmi_bt_i2c_send_request(struct ipmi_bt_i2c_master *master,
+   struct ipmi_bt_i2c_msg *request)
+{
+   struct i2c_client *client = master->client;
+   unsigned long timeout, read_time;
+   u8 *buf = (u8 *) request;
+   int ret;
+
+   timeout = jiffies + msecs_to_jiffies(write_timeout);
+   do {
+   read_time = jiffies;
+   ret = i2c_master_send(client, buf, bt_msg_len(request));
+   if (ret >= 0)
+   return 0;
+   usleep_range(1000, 1500);
+   } while (time_before(read_time, timeout));
+   return ret;
+}
+
+static int ipmi_bt_i2c_receive_response(struct ipmi_bt_i2c_master *master,
+   struct ipmi_bt_i2c_msg *response)
+{
+   struct i2c_client *client = master->client;
+   unsigned long timeout, read_time;
+   u8 *buf = (u8 *) response;
+   u8 len = 0;
+   int ret;
+
+   /*
+* Slave may not NACK when 

[PATCH v2 2/4] ipmi: bt-i2c: added IPMI Block Transfer over I2C host side

2017-08-04 Thread Brendan Higgins
The IPMI definition of the Block Transfer protocol defines the hardware
registers and behavior in addition to the message format and messaging
semantics. This implements a new protocol that uses IPMI Block Transfer
messages and semantics on top of a standard I2C interface.

Signed-off-by: Brendan Higgins 
---
Changes for v2:
  - None
---
 drivers/char/ipmi/Kconfig   |   4 +
 drivers/char/ipmi/Makefile  |   1 +
 drivers/char/ipmi/ipmi_bt_i2c.c | 452 
 3 files changed, 457 insertions(+)
 create mode 100644 drivers/char/ipmi/ipmi_bt_i2c.c

diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig
index f6fa056a52fc..a8734a369cb0 100644
--- a/drivers/char/ipmi/Kconfig
+++ b/drivers/char/ipmi/Kconfig
@@ -79,6 +79,10 @@ config IPMI_POWEROFF
  This enables a function to power off the system with IPMI if
 the IPMI management controller is capable of this.
 
+config IPMI_BT_I2C
+   select I2C
+   tristate 'BT IPMI bmc driver over I2c'
+
 endif # IPMI_HANDLER
 
 config ASPEED_BT_IPMI_BMC
diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile
index eefb0b301e83..323de0b0b8b5 100644
--- a/drivers/char/ipmi/Makefile
+++ b/drivers/char/ipmi/Makefile
@@ -12,4 +12,5 @@ obj-$(CONFIG_IPMI_SSIF) += ipmi_ssif.o
 obj-$(CONFIG_IPMI_POWERNV) += ipmi_powernv.o
 obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o
 obj-$(CONFIG_IPMI_POWEROFF) += ipmi_poweroff.o
+obj-$(CONFIG_IPMI_BT_I2C) += ipmi_bt_i2c.o
 obj-$(CONFIG_ASPEED_BT_IPMI_BMC) += bt-bmc.o
diff --git a/drivers/char/ipmi/ipmi_bt_i2c.c b/drivers/char/ipmi/ipmi_bt_i2c.c
new file mode 100644
index ..94b5c11d23cd
--- /dev/null
+++ b/drivers/char/ipmi/ipmi_bt_i2c.c
@@ -0,0 +1,452 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt)"ipmi-bt-i2c: " fmt
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define IPMI_BT_I2C_TIMEOUT (msecs_to_jiffies(1000))
+
+/* If we don't have netfn_lun, seq, and cmd, we might as well have nothing. */
+#define IPMI_BT_I2C_LEN_MIN 3
+/* We need at least netfn_lun, seq, cmd, and completion. */
+#define IPMI_BT_I2C_RESPONSE_LEN_MIN 4
+#define IPMI_BT_I2C_MSG_MAX_PAYLOAD_SIZE 252
+
+struct ipmi_bt_i2c_msg {
+   u8 len;
+   u8 netfn_lun;
+   u8 seq;
+   u8 cmd;
+   u8 payload[IPMI_BT_I2C_MSG_MAX_PAYLOAD_SIZE];
+} __packed;
+
+#define IPMI_BT_I2C_MAX_SMI_SIZE 254 /* Need extra byte for seq. */
+#define IPMI_BT_I2C_SMI_MSG_HEADER_SIZE 2
+
+struct ipmi_bt_i2c_smi_msg {
+   u8 netfn_lun;
+   u8 cmd;
+   u8 payload[IPMI_MAX_MSG_LENGTH - 2];
+} __packed;
+
+static inline u32 bt_msg_len(struct ipmi_bt_i2c_msg *bt_request)
+{
+   return bt_request->len + 1;
+}
+
+#define IPMI_BT_I2C_SEQ_MAX 256
+
+struct ipmi_bt_i2c_seq_entry {
+   struct ipmi_smi_msg *msg;
+   unsigned long   send_time;
+};
+
+struct ipmi_bt_i2c_master {
+   struct ipmi_device_id   ipmi_id;
+   struct i2c_client   *client;
+   ipmi_smi_t  intf;
+   spinlock_t  lock;
+   struct ipmi_bt_i2c_seq_entryseq_msg_map[IPMI_BT_I2C_SEQ_MAX];
+   struct work_struct  ipmi_bt_i2c_recv_work;
+   struct work_struct  ipmi_bt_i2c_send_work;
+   struct ipmi_smi_msg *msg_to_send;
+};
+
+static const unsigned long write_timeout = 25;
+
+static int ipmi_bt_i2c_send_request(struct ipmi_bt_i2c_master *master,
+   struct ipmi_bt_i2c_msg *request)
+{
+   struct i2c_client *client = master->client;
+   unsigned long timeout, read_time;
+   u8 *buf = (u8 *) request;
+   int ret;
+
+   timeout = jiffies + msecs_to_jiffies(write_timeout);
+   do {
+   read_time = jiffies;
+   ret = i2c_master_send(client, buf, bt_msg_len(request));
+   if (ret >= 0)
+   return 0;
+   usleep_range(1000, 1500);
+   } while (time_before(read_time, timeout));
+   return ret;
+}
+
+static int ipmi_bt_i2c_receive_response(struct ipmi_bt_i2c_master *master,
+   struct ipmi_bt_i2c_msg *response)
+{
+   struct i2c_client *client = master->client;
+   unsigned long timeout, read_time;
+   u8 *buf = (u8 *) response;
+   u8 len = 0;
+   int ret;
+
+   /*
+* Slave may not NACK when not ready, so we peek at the