Received: by 2002:a25:ef43:0:0:0:0:0 with SMTP id w3csp526898ybm; Fri, 29 May 2020 06:09:51 -0700 (PDT) X-Google-Smtp-Source: ABdhPJy+M/M9DBgtFL6z2E1A3qdcsmvt+UfrjVsdwig2RX9uPwimi2txRGT41pdNVyo65JDr7iRS X-Received: by 2002:a05:6402:31b1:: with SMTP id dj17mr8302937edb.142.1590757791696; Fri, 29 May 2020 06:09:51 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1590757791; cv=none; d=google.com; s=arc-20160816; b=qE9Ut/9qXlNptkuUeKwiSLCFrpfts+Y3b6WcvkXhUpctG6C/AMZdA8TXIn55n4Agdb U04nTRxhWSS8o8BiweGa8G5hmH64bLKquDjfktUIYjiCM2wkp3QCZkhOuGoUsbWQiUcP JQY/5bx2FWmAG76mVaNe42+kuEPS5HeYnQnQPzyZOC5kChBgVR3ouhCSsHzMxdEl5j3G N7DYclKKFu7SjADpofC48GmLCJB3YI0/RzUACfeIjZPE5xKadeXGrBlMgzXxhPFNnlol gnAnzNBS/kFlBY9CyiAbKDJUWpT1PRNgctNqX0vI6LD2qkQMvqw5TfasYa9CbPjpaPdi DCvQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from; bh=xKdi5jJ/x7btjxGLNYsejIaX4MrOKLWYwFQZl664ytM=; b=WTAoTOrB2rTcF/Vxa6kGorw3UFAFhQZZbzLspeLQaqVsjlFVM2CWQSbI/0XdXYiwXa cXie4YaFh7RxpvBZTkDZiMhbpcLHJYKShTBbWvIQSYp02yhzdD44Wgp20BifRixXAqlG 9ECRCXHoq63fN6uOWItVocEsesDUQp1N0xKS7AjowkNZrV8mqqS7mQK/TaNFap+zzikR grfSoaO/IfHr7UFWNJwN+1coLIH1tdcQzlkrVUbcn1HL1YmPcSMC7/DF/QaOa+5tN8tL c0gpBERoBoflma6BvdINNKcfFq8umO813Y4WAVV547jiovUahn83KdUbHdV69zMPvoDg ro0g== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id i4si4752124edn.492.2020.05.29.06.09.28; Fri, 29 May 2020 06:09:51 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727842AbgE2NGm (ORCPT + 99 others); Fri, 29 May 2020 09:06:42 -0400 Received: from mx0a-00128a01.pphosted.com ([148.163.135.77]:29960 "EHLO mx0a-00128a01.pphosted.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726845AbgE2NGY (ORCPT ); Fri, 29 May 2020 09:06:24 -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 04TD2Cf5006393; Fri, 29 May 2020 09:06:00 -0400 Received: from nwd2mta3.analog.com ([137.71.173.56]) by mx0a-00128a01.pphosted.com with ESMTP id 31ax1pgt2y-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Fri, 29 May 2020 09:06:00 -0400 Received: from ASHBMBX8.ad.analog.com (ashbmbx8.ad.analog.com [10.64.17.5]) by nwd2mta3.analog.com (8.14.7/8.14.7) with ESMTP id 04TD5xFW004090 (version=TLSv1/SSLv3 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128 verify=FAIL); Fri, 29 May 2020 09:05:59 -0400 Received: from ASHBCASHYB5.ad.analog.com (10.64.17.133) by ASHBMBX8.ad.analog.com (10.64.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 09:05:58 -0400 Received: from ASHBMBX9.ad.analog.com (10.64.17.10) by ASHBCASHYB5.ad.analog.com (10.64.17.133) 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:05:58 -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:05:58 -0400 Received: from localhost.localdomain ([10.48.65.12]) by zeus.spd.analog.com (8.15.1/8.15.1) with ESMTP id 04TD5pKP026894; Fri, 29 May 2020 09:05:56 -0400 From: To: , , CC: , , Alexandru Tachici Subject: [PATCH v3 3/6] hwmon: pmbus: adm1266: Add support for GPIOs Date: Fri, 29 May 2020 16:05:03 +0300 Message-ID: <20200529130506.73511-4-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 Content-Type: text/plain 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: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Alexandru Tachici Adm1266 exposes 9 GPIOs and 16 PDIOs which are currently read-only. They are controlled by the internal sequencing engine. This patch makes adm1266 driver expose GPIOs and PDIOs to user-space using GPIO provider kernel api. Signed-off-by: Alexandru Tachici --- drivers/hwmon/pmbus/adm1266.c | 233 +++++++++++++++++++++++++++++++++- 1 file changed, 232 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c index a7ef048a9a5c..190170300ef1 100644 --- a/drivers/hwmon/pmbus/adm1266.c +++ b/drivers/hwmon/pmbus/adm1266.c @@ -6,7 +6,11 @@ * Copyright 2020 Analog Devices Inc. */ +#include +#include +#include #include +#include #include #include #include @@ -14,16 +18,243 @@ #include "pmbus.h" +#define ADM1266_PDIO_CONFIG 0xD4 +#define ADM1266_GPIO_CONFIG 0xE1 +#define ADM1266_PDIO_STATUS 0xE9 +#define ADM1266_GPIO_STATUS 0xEA + +/* ADM1266 GPIO defines */ +#define ADM1266_GPIO_NR 9 +#define ADM1266_GPIO_FUNCTIONS(x) FIELD_GET(BIT(0), x) +#define ADM1266_GPIO_INPUT_EN(x) FIELD_GET(BIT(2), x) +#define ADM1266_GPIO_OUTPUT_EN(x) FIELD_GET(BIT(3), x) +#define ADM1266_GPIO_OPEN_DRAIN(x) FIELD_GET(BIT(4), x) + +/* ADM1266 PDIO defines */ +#define ADM1266_PDIO_NR 16 +#define ADM1266_PDIO_PIN_CFG(x) FIELD_GET(GENMASK(15, 13), x) +#define ADM1266_PDIO_GLITCH_FILT(x) FIELD_GET(GENMASK(12, 9), x) +#define ADM1266_PDIO_OUT_CFG(x) FIELD_GET(GENMASK(2, 0), x) + +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; +}; + +#if IS_ENABLED(CONFIG_GPIOLIB) +static const unsigned int adm1266_gpio_mapping[ADM1266_GPIO_NR][2] = { + {1, 0}, + {2, 1}, + {3, 2}, + {4, 8}, + {5, 9}, + {6, 10}, + {7, 11}, + {8, 6}, + {9, 7}, +}; + +static const char *adm1266_names[ADM1266_GPIO_NR + ADM1266_PDIO_NR] = { + "GPIO1", "GPIO2", "GPIO3", "GPIO4", "GPIO5", "GPIO6", "GPIO7", "GPIO8", + "GPIO9", "PDIO1", "PDIO2", "PDIO3", "PDIO4", "PDIO5", "PDIO6", + "PDIO7", "PDIO8", "PDIO9", "PDIO10", "PDIO11", "PDIO12", "PDIO13", + "PDIO14", "PDIO15", "PDIO16", +}; + +static int adm1266_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct adm1266_data *data = gpiochip_get_data(chip); + u8 read_buf[PMBUS_BLOCK_MAX + 1]; + unsigned long pins_status; + unsigned int pmbus_cmd; + int ret; + + if (offset < ADM1266_GPIO_NR) + pmbus_cmd = ADM1266_GPIO_STATUS; + else + pmbus_cmd = ADM1266_PDIO_STATUS; + + ret = i2c_smbus_read_block_data(data->client, pmbus_cmd, + read_buf); + if (ret < 0) + return ret; + + pins_status = read_buf[0] + (read_buf[1] << 8); + if (offset < ADM1266_GPIO_NR) + return test_bit(adm1266_gpio_mapping[offset][1], &pins_status); + else + return test_bit(offset - ADM1266_GPIO_NR, &pins_status); +} + +static int adm1266_gpio_get_multiple(struct gpio_chip *chip, + unsigned long *mask, + unsigned long *bits) +{ + struct adm1266_data *data = gpiochip_get_data(chip); + u8 gpio_data[PMBUS_BLOCK_MAX + 1]; + u8 pdio_data[PMBUS_BLOCK_MAX + 1]; + unsigned long gpio_status; + unsigned long pdio_status; + unsigned int gpio_nr; + int ret; + + ret = i2c_smbus_read_block_data(data->client, ADM1266_GPIO_STATUS, + gpio_data); + if (ret < 0) + return ret; + + ret = i2c_smbus_read_block_data(data->client, ADM1266_PDIO_STATUS, + pdio_data); + if (ret < 0) + return ret; + + gpio_status = gpio_data[0] + (gpio_data[1] << 8); + pdio_status = pdio_data[0] + (pdio_data[1] << 8); + *bits = 0; + for_each_set_bit(gpio_nr, mask, ADM1266_GPIO_NR) { + if (test_bit(adm1266_gpio_mapping[gpio_nr][1], &gpio_status)) + set_bit(gpio_nr, bits); + } + + for_each_set_bit_from(gpio_nr, mask, + ADM1266_GPIO_NR + ADM1266_PDIO_STATUS) { + if (test_bit(gpio_nr - ADM1266_GPIO_NR, &pdio_status)) + set_bit(gpio_nr, bits); + } + + return 0; +} + +static void adm1266_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) +{ + struct adm1266_data *data = gpiochip_get_data(chip); + u8 write_buf[PMBUS_BLOCK_MAX + 1]; + u8 read_buf[PMBUS_BLOCK_MAX + 1]; + unsigned long gpio_config; + unsigned long pdio_config; + unsigned long pin_cfg; + int ret; + int i; + + for (i = 0; i < ADM1266_GPIO_NR; i++) { + write_buf[0] = adm1266_gpio_mapping[i][1]; + ret = pmbus_block_wr(data->client, ADM1266_GPIO_CONFIG, 1, + write_buf, read_buf); + if (ret < 0) + dev_err(&data->client->dev, "GPIOs scan failed(%d).\n", + ret); + + gpio_config = read_buf[0]; + seq_puts(s, adm1266_names[i]); + + seq_puts(s, " ( "); + if (!ADM1266_GPIO_FUNCTIONS(gpio_config)) { + seq_puts(s, "high-Z )\n"); + continue; + } + if (ADM1266_GPIO_INPUT_EN(gpio_config)) + seq_puts(s, "input "); + if (ADM1266_GPIO_OUTPUT_EN(gpio_config)) + seq_puts(s, "output "); + if (ADM1266_GPIO_OPEN_DRAIN(gpio_config)) + seq_puts(s, "open-drain )\n"); + else + seq_puts(s, "push-pull )\n"); + } + + write_buf[0] = 0xFF; + ret = pmbus_block_wr(data->client, ADM1266_PDIO_CONFIG, 1, write_buf, + read_buf); + if (ret < 0) + dev_err(&data->client->dev, "PDIOs scan failed(%d).\n", ret); + + for (i = 0; i < ADM1266_PDIO_NR; i++) { + seq_puts(s, adm1266_names[ADM1266_GPIO_NR + i]); + + pdio_config = read_buf[2 * i]; + pdio_config += (read_buf[2 * i + 1] << 8); + pin_cfg = ADM1266_PDIO_PIN_CFG(pdio_config); + + seq_puts(s, " ( "); + if (!pin_cfg || pin_cfg > 5) { + seq_puts(s, "high-Z )\n"); + continue; + } + + if (pin_cfg & BIT(0)) + seq_puts(s, "output "); + + if (pin_cfg & BIT(1)) + seq_puts(s, "input "); + + seq_puts(s, ")\n"); + } +} + +static int adm1266_config_gpio(struct adm1266_data *data) +{ + const char *name = dev_name(&data->client->dev); + char *gpio_name; + int ret; + int i; + + for (i = 0; i < ARRAY_SIZE(data->gpio_names); i++) { + gpio_name = devm_kasprintf(&data->client->dev, GFP_KERNEL, + "adm1266-%x-%s", data->client->addr, + adm1266_names[i]); + if (!gpio_name) + return -ENOMEM; + + data->gpio_names[i] = gpio_name; + } + + data->gc.label = name; + data->gc.parent = &data->client->dev; + data->gc.owner = THIS_MODULE; + data->gc.base = -1; + data->gc.names = data->gpio_names; + data->gc.ngpio = ARRAY_SIZE(data->gpio_names); + data->gc.get = adm1266_gpio_get; + data->gc.get_multiple = adm1266_gpio_get_multiple; + data->gc.dbg_show = adm1266_gpio_dbg_show; + + ret = devm_gpiochip_add_data(&data->client->dev, &data->gc, data); + if (ret) + dev_err(&data->client->dev, "GPIO registering failed (%d)\n", + ret); + + return ret; +} +#else +static inline int adm1266_config_gpio(struct adm1266_data *data) +{ + return 0; +} +#endif + static int adm1266_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct pmbus_driver_info *info; + struct adm1266_data *data; u32 funcs; + int ret; int i; - info = devm_kzalloc(&client->dev, sizeof(struct pmbus_driver_info), + data = devm_kzalloc(&client->dev, sizeof(struct adm1266_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->client = client; + + ret = adm1266_config_gpio(data); + if (ret < 0) + return ret; + info = &data->info; info->pages = 17; info->format[PSC_VOLTAGE_OUT] = linear; funcs = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT; -- 2.20.1