From patchwork Fri May 29 13:05:02 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexandru Tachici X-Patchwork-Id: 199766 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.7 required=3.0 tests=HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 62F44C433E3 for ; Fri, 29 May 2020 13:06:30 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 4C3A4206B6 for ; Fri, 29 May 2020 13:06:30 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727121AbgE2NG3 (ORCPT ); Fri, 29 May 2020 09:06:29 -0400 Received: from mx0a-00128a01.pphosted.com ([148.163.135.77]:36840 "EHLO mx0a-00128a01.pphosted.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727089AbgE2NG1 (ORCPT ); Fri, 29 May 2020 09:06:27 -0400 Received: from pps.filterd (m0167088.ppops.net [127.0.0.1]) by mx0a-00128a01.pphosted.com (8.16.0.42/8.16.0.42) with SMTP id 04TCxZSr028194; Fri, 29 May 2020 09:06:01 -0400 Received: from nwd2mta4.analog.com ([137.71.173.58]) by mx0a-00128a01.pphosted.com with ESMTP id 316wp8c20n-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Fri, 29 May 2020 09:06:00 -0400 Received: from SCSQMBX11.ad.analog.com (scsqmbx11.ad.analog.com [10.77.17.10]) by nwd2mta4.analog.com (8.14.7/8.14.7) with ESMTP id 04TD5xxv003727 (version=TLSv1/SSLv3 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128 verify=FAIL); Fri, 29 May 2020 09:05:59 -0400 Received: from SCSQMBX11.ad.analog.com (10.77.17.10) by SCSQMBX11.ad.analog.com (10.77.17.10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.1779.2; Fri, 29 May 2020 06:05:57 -0700 Received: from zeus.spd.analog.com (10.64.82.11) by SCSQMBX11.ad.analog.com (10.77.17.10) with Microsoft SMTP Server id 15.1.1779.2 via Frontend Transport; Fri, 29 May 2020 06:05:57 -0700 Received: from localhost.localdomain ([10.48.65.12]) by zeus.spd.analog.com (8.15.1/8.15.1) with ESMTP id 04TD5pKO026894; Fri, 29 May 2020 09:05:54 -0400 From: To: , , CC: , , Alexandru Tachici Subject: [PATCH v3 2/6] hwmon: (pmbus/core) Add Block WR Date: Fri, 29 May 2020 16:05:02 +0300 Message-ID: <20200529130506.73511-3-alexandru.tachici@analog.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200529130506.73511-1-alexandru.tachici@analog.com> References: <20200529130506.73511-1-alexandru.tachici@analog.com> MIME-Version: 1.0 X-ADIRoutedOnPrem: True X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10434:6.0.216, 18.0.687 definitions=2020-05-29_07:2020-05-28,2020-05-29 signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 suspectscore=0 bulkscore=0 phishscore=0 adultscore=0 mlxlogscore=999 impostorscore=0 clxscore=1015 malwarescore=0 mlxscore=0 lowpriorityscore=0 cotscore=-2147483648 spamscore=0 priorityscore=1501 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-2004280000 definitions=main-2005290104 Sender: devicetree-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org From: Alexandru Tachici PmBus devices support Block Write-Block Read Process Call described in SMBus specification v 2.0 with the exception that Block writes and reads are permitted to have up 255 data bytes instead of max 32 bytes (SMBus). This patch adds Block WR process call support for PMBus. Signed-off-by: Alexandru Tachici --- drivers/hwmon/pmbus/Kconfig | 2 +- drivers/hwmon/pmbus/pmbus.h | 4 ++ drivers/hwmon/pmbus/pmbus_core.c | 88 ++++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index 6949483aa732..f11712fdcea8 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -5,7 +5,7 @@ menuconfig PMBUS tristate "PMBus support" - depends on I2C + depends on I2C && CRC8 help Say yes here if you want to enable PMBus support. diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h index 18e06fc6c53f..ae4e15c5dff2 100644 --- a/drivers/hwmon/pmbus/pmbus.h +++ b/drivers/hwmon/pmbus/pmbus.h @@ -392,6 +392,8 @@ enum pmbus_sensor_classes { #define PMBUS_PHASE_VIRTUAL BIT(30) /* Phases on this page are virtual */ #define PMBUS_PAGE_VIRTUAL BIT(31) /* Page is virtual */ +#define PMBUS_BLOCK_MAX 255 + enum pmbus_data_format { linear = 0, direct, vid }; enum vrm_version { vr11 = 0, vr12, vr13, imvp9, amd625mv }; @@ -467,6 +469,8 @@ int pmbus_read_word_data(struct i2c_client *client, int page, int phase, u8 reg); int pmbus_write_word_data(struct i2c_client *client, int page, u8 reg, u16 word); +int pmbus_block_wr(struct i2c_client *client, u8 cmd, u8 w_len, u8 *data_w, + u8 *data_r); int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg); int pmbus_write_byte(struct i2c_client *client, int page, u8 value); int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg, diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index 8d321bf7d15b..ef63468da3b5 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -6,6 +6,7 @@ * Copyright (c) 2012 Guenter Roeck */ +#include #include #include #include @@ -44,6 +45,8 @@ #define PMBUS_NAME_SIZE 24 +DECLARE_CRC8_TABLE(pmbus_crc_table); + struct pmbus_sensor { struct pmbus_sensor *next; char name[PMBUS_NAME_SIZE]; /* sysfs sensor name */ @@ -184,6 +187,89 @@ int pmbus_set_page(struct i2c_client *client, int page, int phase) } EXPORT_SYMBOL_GPL(pmbus_set_page); +/* Block Write/Read command. + * @client: Handle to slave device + * @cmd: Byte interpreted by slave + * @w_len: Size of write data block; PMBus allows at most 255 bytes + * @data_w: byte array which will be written. + * @data_r: Byte array into which data will be read; big enough to hold + * the data returned by the slave. PMBus allows at most 255 bytes. + * + * 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. + * + * Returns number of bytes read or negative errno. + */ +int pmbus_block_wr(struct i2c_client *client, u8 cmd, u8 w_len, + u8 *data_w, u8 *data_r) +{ + u8 write_buf[PMBUS_BLOCK_MAX + 1]; + 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 = PMBUS_BLOCK_MAX, + } + }; + 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) { + dev_err(&client->dev, "I2C transfer error."); + 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; +} +EXPORT_SYMBOL_GPL(pmbus_block_wr); + int pmbus_write_byte(struct i2c_client *client, int page, u8 value) { int rv; @@ -2522,6 +2608,8 @@ int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id, if (!data->groups) return -ENOMEM; + crc8_populate_msb(pmbus_crc_table, 0x7); + i2c_set_clientdata(client, data); mutex_init(&data->update_lock); data->dev = dev; From patchwork Fri May 29 13:05:04 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexandru Tachici X-Patchwork-Id: 199764 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.7 required=3.0 tests=HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id D162EC433DF for ; Fri, 29 May 2020 13:06:40 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id BA8DE206B6 for ; Fri, 29 May 2020 13:06:40 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727091AbgE2NGY (ORCPT ); Fri, 29 May 2020 09:06:24 -0400 Received: from mx0a-00128a01.pphosted.com ([148.163.135.77]:28620 "EHLO mx0a-00128a01.pphosted.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726687AbgE2NGX (ORCPT ); Fri, 29 May 2020 09:06:23 -0400 Received: from pps.filterd (m0167088.ppops.net [127.0.0.1]) by mx0a-00128a01.pphosted.com (8.16.0.42/8.16.0.42) with SMTP id 04TCxknv028510; Fri, 29 May 2020 09:06:04 -0400 Received: from nwd2mta3.analog.com ([137.71.173.56]) by mx0a-00128a01.pphosted.com with ESMTP id 316wp8c20y-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Fri, 29 May 2020 09:06:03 -0400 Received: from SCSQMBX10.ad.analog.com (scsqmbx10.ad.analog.com [10.77.17.5]) by nwd2mta3.analog.com (8.14.7/8.14.7) with ESMTP id 04TD62HD004107 (version=TLSv1/SSLv3 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128 verify=FAIL); Fri, 29 May 2020 09:06:02 -0400 Received: from SCSQMBX11.ad.analog.com (10.77.17.10) by SCSQMBX10.ad.analog.com (10.77.17.5) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.1779.2; Fri, 29 May 2020 06:06:00 -0700 Received: from zeus.spd.analog.com (10.64.82.11) by SCSQMBX11.ad.analog.com (10.77.17.10) with Microsoft SMTP Server id 15.1.1779.2 via Frontend Transport; Fri, 29 May 2020 06:06:00 -0700 Received: from localhost.localdomain ([10.48.65.12]) by zeus.spd.analog.com (8.15.1/8.15.1) with ESMTP id 04TD5pKQ026894; Fri, 29 May 2020 09:05:57 -0400 From: To: , , CC: , , Alexandru Tachici Subject: [PATCH v3 4/6] hwmon: pmbus: adm1266: add debugfs attr for states Date: Fri, 29 May 2020 16:05:04 +0300 Message-ID: <20200529130506.73511-5-alexandru.tachici@analog.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200529130506.73511-1-alexandru.tachici@analog.com> References: <20200529130506.73511-1-alexandru.tachici@analog.com> MIME-Version: 1.0 X-ADIRoutedOnPrem: True X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10434:6.0.216, 18.0.687 definitions=2020-05-29_07:2020-05-28,2020-05-29 signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 suspectscore=0 bulkscore=0 phishscore=0 adultscore=0 mlxlogscore=999 impostorscore=0 clxscore=1015 malwarescore=0 mlxscore=0 lowpriorityscore=0 cotscore=-2147483648 spamscore=0 priorityscore=1501 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-2004280000 definitions=main-2005290104 Sender: devicetree-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org From: Alexandru Tachici Add debugfs files for go_command and read_state. Signed-off-by: Alexandru Tachici --- drivers/hwmon/pmbus/adm1266.c | 47 +++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c index 190170300ef1..85d6795b79d3 100644 --- a/drivers/hwmon/pmbus/adm1266.c +++ b/drivers/hwmon/pmbus/adm1266.c @@ -19,6 +19,8 @@ #include "pmbus.h" #define ADM1266_PDIO_CONFIG 0xD4 +#define ADM1266_GO_COMMAND 0xD8 +#define ADM1266_READ_STATE 0xD9 #define ADM1266_GPIO_CONFIG 0xE1 #define ADM1266_PDIO_STATUS 0xE9 #define ADM1266_GPIO_STATUS 0xEA @@ -41,6 +43,7 @@ struct adm1266_data { struct gpio_chip gc; const char *gpio_names[ADM1266_GPIO_NR + ADM1266_PDIO_NR]; struct i2c_client *client; + struct dentry *debugfs_dir; }; #if IS_ENABLED(CONFIG_GPIOLIB) @@ -234,6 +237,48 @@ static inline int adm1266_config_gpio(struct adm1266_data *data) } #endif +static int adm1266_get_state_op(void *pdata, u64 *state) +{ + struct adm1266_data *data = pdata; + int ret; + + ret = i2c_smbus_read_word_data(data->client, ADM1266_READ_STATE); + if (ret < 0) + return ret; + + *state = ret; + + return 0; +} + +static int adm1266_set_go_command_op(void *pdata, u64 val) +{ + struct adm1266_data *data = pdata; + u8 reg; + + reg = FIELD_GET(GENMASK(4, 0), val); + + return i2c_smbus_write_word_data(data->client, ADM1266_GO_COMMAND, reg); +} + +DEFINE_DEBUGFS_ATTRIBUTE(go_command_fops, NULL, adm1266_set_go_command_op, + "%llu\n"); +DEFINE_DEBUGFS_ATTRIBUTE(read_state_fops, adm1266_get_state_op, NULL, "%llu\n"); + +static void adm1266_debug_init(struct adm1266_data *data) +{ + struct dentry *root; + char dir_name[30]; + + sprintf(dir_name, "adm1266-%x_debugfs", data->client->addr); + root = debugfs_create_dir(dir_name, NULL); + data->debugfs_dir = root; + debugfs_create_file_unsafe("go_command", 0200, root, data, + &go_command_fops); + debugfs_create_file_unsafe("read_state", 0400, root, data, + &read_state_fops); +} + static int adm1266_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -254,6 +299,8 @@ static int adm1266_probe(struct i2c_client *client, if (ret < 0) return ret; + adm1266_debug_init(data); + info = &data->info; info->pages = 17; info->format[PSC_VOLTAGE_OUT] = linear; From patchwork Fri May 29 13:05:05 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexandru Tachici X-Patchwork-Id: 199765 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.7 required=3.0 tests=HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 42787C433E0 for ; Fri, 29 May 2020 13:06:40 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 24C0A208B8 for ; Fri, 29 May 2020 13:06:40 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727799AbgE2NGd (ORCPT ); Fri, 29 May 2020 09:06:33 -0400 Received: from mx0a-00128a01.pphosted.com ([148.163.135.77]:40478 "EHLO mx0a-00128a01.pphosted.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727771AbgE2NG3 (ORCPT ); Fri, 29 May 2020 09:06:29 -0400 Received: from pps.filterd (m0167089.ppops.net [127.0.0.1]) by mx0a-00128a01.pphosted.com (8.16.0.42/8.16.0.42) with SMTP id 04TD2BKO006308; Fri, 29 May 2020 09:06:08 -0400 Received: from nwd2mta4.analog.com ([137.71.173.58]) by mx0a-00128a01.pphosted.com with ESMTP id 31ax1pgt35-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Fri, 29 May 2020 09:06:08 -0400 Received: from ASHBMBX9.ad.analog.com (ashbmbx9.ad.analog.com [10.64.17.10]) by nwd2mta4.analog.com (8.14.7/8.14.7) with ESMTP id 04TD63BE003739 (version=TLSv1/SSLv3 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128 verify=FAIL); Fri, 29 May 2020 09:06:03 -0400 Received: from ASHBMBX9.ad.analog.com (10.64.17.10) by ASHBMBX9.ad.analog.com (10.64.17.10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.1779.2; Fri, 29 May 2020 09:06:02 -0400 Received: from zeus.spd.analog.com (10.64.82.11) by ASHBMBX9.ad.analog.com (10.64.17.10) with Microsoft SMTP Server id 15.1.1779.2 via Frontend Transport; Fri, 29 May 2020 09:06:02 -0400 Received: from localhost.localdomain ([10.48.65.12]) by zeus.spd.analog.com (8.15.1/8.15.1) with ESMTP id 04TD5pKR026894; Fri, 29 May 2020 09:05:59 -0400 From: To: , , CC: , , Alexandru Tachici Subject: [PATCH v3 5/6] hwmon: pmbus: adm1266: read blackbox Date: Fri, 29 May 2020 16:05:05 +0300 Message-ID: <20200529130506.73511-6-alexandru.tachici@analog.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200529130506.73511-1-alexandru.tachici@analog.com> References: <20200529130506.73511-1-alexandru.tachici@analog.com> MIME-Version: 1.0 X-ADIRoutedOnPrem: True X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10434:6.0.216, 18.0.687 definitions=2020-05-29_07:2020-05-28,2020-05-29 signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 bulkscore=0 mlxlogscore=999 adultscore=0 suspectscore=0 phishscore=0 mlxscore=0 clxscore=1015 priorityscore=1501 impostorscore=0 lowpriorityscore=0 malwarescore=0 cotscore=-2147483648 spamscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-2004280000 definitions=main-2005290104 Sender: devicetree-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org From: Alexandru Tachici Use the nvmem kernel api to expose the black box chip functionality to userspace. Signed-off-by: Alexandru Tachici --- drivers/hwmon/pmbus/adm1266.c | 160 ++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c index 85d6795b79d3..831156004087 100644 --- a/drivers/hwmon/pmbus/adm1266.c +++ b/drivers/hwmon/pmbus/adm1266.c @@ -14,14 +14,19 @@ #include #include #include +#include +#include #include #include "pmbus.h" +#define ADM1266_BLACKBOX_CONFIG 0xD3 #define ADM1266_PDIO_CONFIG 0xD4 #define ADM1266_GO_COMMAND 0xD8 #define ADM1266_READ_STATE 0xD9 +#define ADM1266_READ_BLACKBOX 0xDE #define ADM1266_GPIO_CONFIG 0xE1 +#define ADM1266_BLACKBOX_INFO 0xE6 #define ADM1266_PDIO_STATUS 0xE9 #define ADM1266_GPIO_STATUS 0xEA @@ -38,12 +43,26 @@ #define ADM1266_PDIO_GLITCH_FILT(x) FIELD_GET(GENMASK(12, 9), x) #define ADM1266_PDIO_OUT_CFG(x) FIELD_GET(GENMASK(2, 0), x) +#define ADM1266_BLACKBOX_OFFSET 0x7F700 +#define ADM1266_BLACKBOX_SIZE 64 + struct adm1266_data { struct pmbus_driver_info info; struct gpio_chip gc; const char *gpio_names[ADM1266_GPIO_NR + ADM1266_PDIO_NR]; struct i2c_client *client; struct dentry *debugfs_dir; + struct nvmem_config nvmem_config; + struct nvmem_device *nvmem; + u8 *dev_mem; +}; + +static const struct nvmem_cell_info adm1266_nvmem_cells[] = { + { + .name = "blackbox", + .offset = ADM1266_BLACKBOX_OFFSET, + .bytes = 2048, + }, }; #if IS_ENABLED(CONFIG_GPIOLIB) @@ -261,6 +280,28 @@ static int adm1266_set_go_command_op(void *pdata, u64 val) return i2c_smbus_write_word_data(data->client, ADM1266_GO_COMMAND, reg); } +static int adm1266_blackbox_information_read(struct seq_file *s, void *pdata) +{ + struct device *dev = s->private; + struct i2c_client *client = to_i2c_client(dev); + u8 read_buf[PMBUS_BLOCK_MAX + 1]; + unsigned int latest_id; + int ret; + + ret = i2c_smbus_read_block_data(client, ADM1266_BLACKBOX_INFO, + read_buf); + if (ret < 0) + return ret; + + seq_puts(s, "BLACKBOX_INFORMATION:\n"); + latest_id = read_buf[0] + (read_buf[1] << 8); + seq_printf(s, "Black box ID: %x\n", latest_id); + seq_printf(s, "Logic index: %x\n", read_buf[2]); + seq_printf(s, "Record count: %x\n", read_buf[3]); + + return 0; +} + DEFINE_DEBUGFS_ATTRIBUTE(go_command_fops, NULL, adm1266_set_go_command_op, "%llu\n"); DEFINE_DEBUGFS_ATTRIBUTE(read_state_fops, adm1266_get_state_op, NULL, "%llu\n"); @@ -277,6 +318,121 @@ static void adm1266_debug_init(struct adm1266_data *data) &go_command_fops); debugfs_create_file_unsafe("read_state", 0400, root, data, &read_state_fops); + debugfs_create_devm_seqfile(&data->client->dev, "blackbox_information", + root, adm1266_blackbox_information_read); +} + +static int adm1266_nvmem_read_blackbox(struct adm1266_data *data, u8 *buf) +{ + u8 write_buf[PMBUS_BLOCK_MAX + 1]; + u8 read_buf[PMBUS_BLOCK_MAX + 1]; + int record_count; + int ret; + int i; + + ret = i2c_smbus_read_block_data(data->client, ADM1266_BLACKBOX_INFO, + read_buf); + if (ret < 0) + return ret; + + record_count = read_buf[3]; + + for (i = 0; i < record_count; i++) { + write_buf[0] = i; + ret = pmbus_block_wr(data->client, ADM1266_READ_BLACKBOX, 1, + write_buf, buf); + if (ret < 0) + return ret; + + buf += ADM1266_BLACKBOX_SIZE; + } + + return 0; +} + +static bool adm1266_cell_is_accessed(const struct nvmem_cell_info *mem_cell, + unsigned int offset, size_t bytes) +{ + unsigned int start_addr = offset; + unsigned int end_addr = offset + bytes; + unsigned int cell_start = mem_cell->offset; + unsigned int cell_end = mem_cell->offset + mem_cell->bytes; + + if (start_addr <= cell_end && cell_start <= end_addr) + return true; + + return false; +} + +static int adm1266_read_mem_cell(struct adm1266_data *data, + const struct nvmem_cell_info *mem_cell) +{ + u8 *mem_offset; + int ret; + + switch (mem_cell->offset) { + case ADM1266_BLACKBOX_OFFSET: + mem_offset = data->dev_mem + mem_cell->offset; + ret = adm1266_nvmem_read_blackbox(data, mem_offset); + if (ret) + dev_err(&data->client->dev, "Could not read blackbox!"); + return ret; + default: + return -EINVAL; + } +} + +static int adm1266_nvmem_read(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + const struct nvmem_cell_info *mem_cell; + struct adm1266_data *data = priv; + int ret; + int i; + + for (i = 0; i < data->nvmem_config.ncells; i++) { + mem_cell = &adm1266_nvmem_cells[i]; + if (!adm1266_cell_is_accessed(mem_cell, offset, bytes)) + continue; + + ret = adm1266_read_mem_cell(data, mem_cell); + if (ret < 0) + return ret; + } + + memcpy(val, data->dev_mem + offset, bytes); + + return 0; +} + +static int adm1266_config_nvmem(struct adm1266_data *data) +{ + data->nvmem_config.name = dev_name(&data->client->dev); + data->nvmem_config.dev = &data->client->dev; + data->nvmem_config.root_only = true; + data->nvmem_config.read_only = true; + data->nvmem_config.owner = THIS_MODULE; + data->nvmem_config.reg_read = adm1266_nvmem_read; + data->nvmem_config.cells = adm1266_nvmem_cells; + data->nvmem_config.ncells = ARRAY_SIZE(adm1266_nvmem_cells); + data->nvmem_config.priv = data; + data->nvmem_config.stride = 1; + data->nvmem_config.word_size = 1; + data->nvmem_config.size = 0x80000; + + data->nvmem = nvmem_register(&data->nvmem_config); + if (IS_ERR(data->nvmem)) { + dev_err(&data->client->dev, "Could not register nvmem!"); + return PTR_ERR(data->nvmem); + } + + data->dev_mem = devm_kzalloc(&data->client->dev, + data->nvmem_config.size, + GFP_KERNEL); + if (!data->dev_mem) + return -ENOMEM; + + return 0; } static int adm1266_probe(struct i2c_client *client, @@ -299,6 +455,10 @@ static int adm1266_probe(struct i2c_client *client, if (ret < 0) return ret; + ret = adm1266_config_nvmem(data); + if (ret < 0) + return ret; + adm1266_debug_init(data); info = &data->info;