[PATCH v2 2/4] ipmi: bt-i2c: added IPMI Block Transfer over I2C host side
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
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