@@ -28,6 +28,7 @@ config SENSORS_PMBUS
config SENSORS_ADM1266
tristate "Analog Devices ADM1266 Sequencer"
+ select CRC8
help
If you say yes here you get hardware monitoring support for Analog
Devices ADM1266 Cascadable Super Sequencer.
@@ -6,6 +6,7 @@
* Copyright 2020 Analog Devices Inc.
*/
+#include <linux/crc8.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/kernel.h>
@@ -14,6 +15,82 @@
#include "pmbus.h"
+#define ADM1266_PMBUS_BLOCK_MAX 255
+
+DECLARE_CRC8_TABLE(pmbus_crc_table);
+
+/* Different from Block Read as it sends data and waits for the slave to
+ * return a value dependent on that data. The protocol is simply a Write Block
+ * followed by a Read Block without the Read-Block command field and the
+ * Write-Block STOP bit.
+ */
+int pmbus_block_xfer(struct i2c_client *client, u8 cmd, u8 w_len,
+ u8 *data_w, u8 *data_r)
+{
+ u8 write_buf[ADM1266_PMBUS_BLOCK_MAX + 2];
+ struct i2c_msg msgs[2] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .buf = write_buf,
+ .len = w_len + 2,
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = ADM1266_PMBUS_BLOCK_MAX + 2,
+ }
+ };
+ u8 addr = 0;
+ u8 crc = 0;
+ int ret;
+
+ msgs[0].buf[0] = cmd;
+ msgs[0].buf[1] = w_len;
+ memcpy(&msgs[0].buf[2], data_w, w_len);
+
+ msgs[0].buf = i2c_get_dma_safe_msg_buf(&msgs[0], 1);
+ if (!msgs[0].buf)
+ return -ENOMEM;
+
+ msgs[1].buf = i2c_get_dma_safe_msg_buf(&msgs[1], 1);
+ if (!msgs[1].buf) {
+ i2c_put_dma_safe_msg_buf(msgs[0].buf, &msgs[0], false);
+ return -ENOMEM;
+ }
+
+ ret = i2c_transfer(client->adapter, msgs, 2);
+ if (ret != 2) {
+ ret = -EPROTO;
+ goto cleanup;
+ }
+
+ if (client->flags & I2C_CLIENT_PEC) {
+ addr = i2c_8bit_addr_from_msg(&msgs[0]);
+ crc = crc8(pmbus_crc_table, &addr, 1, crc);
+ crc = crc8(pmbus_crc_table, msgs[0].buf, msgs[0].len, crc);
+
+ addr = i2c_8bit_addr_from_msg(&msgs[1]);
+ crc = crc8(pmbus_crc_table, &addr, 1, crc);
+ crc = crc8(pmbus_crc_table, msgs[1].buf, msgs[1].buf[0] + 1,
+ crc);
+
+ if (crc != msgs[1].buf[msgs[1].buf[0] + 1]) {
+ ret = -EBADMSG;
+ goto cleanup;
+ }
+ }
+
+ memcpy(data_r, &msgs[1].buf[1], msgs[1].buf[0]);
+ ret = msgs[1].buf[0];
+
+cleanup:
+ i2c_put_dma_safe_msg_buf(msgs[0].buf, &msgs[0], true);
+ i2c_put_dma_safe_msg_buf(msgs[1].buf, &msgs[1], true);
+
+ return ret;
+}
+
static int adm1266_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -24,6 +101,8 @@ static int adm1266_probe(struct i2c_client *client,
info = devm_kzalloc(&client->dev, sizeof(struct pmbus_driver_info),
GFP_KERNEL);
+ crc8_populate_msb(pmbus_crc_table, 0x7);
+
info->pages = 17;
info->format[PSC_VOLTAGE_OUT] = linear;
funcs = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;