From patchwork Tue Dec 6 12:45:28 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Edmund Berenson X-Patchwork-Id: 30269 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:adf:f944:0:0:0:0:0 with SMTP id q4csp2801216wrr; Tue, 6 Dec 2022 04:53:35 -0800 (PST) X-Google-Smtp-Source: AA0mqf4ilFbXksM2xQ9dA6kiacpOJSQ51PRllaqqNMt6e6jk+YCP4EMdIB5fmlo1aofGXtcHyxyZ X-Received: by 2002:a17:906:a983:b0:7b8:31b1:b23f with SMTP id jr3-20020a170906a98300b007b831b1b23fmr56272318ejb.591.1670331215183; Tue, 06 Dec 2022 04:53:35 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1670331215; cv=none; d=google.com; s=arc-20160816; b=YKZiNoHmYGvi0mviKD8JfqgkZhPd/gHv/YGGEMe13B4oq/QwNRhihhu+4g6kkSV48a sM/g65CVAaI327HgcaIB474QoMYY0vjm+UWaaf3nsISK9y8dhbhnNRKDgwhQpIDwS3fl euGuHtN3Aw0dCJ0T/yfGa9iNGTsKnP3w/vZ7GGOmApEeY6erSprid/gS0Dpp/ViRsnN2 GRdNuo+M9SSzvtv4C28LzKfpH1w51tG+6tUwvnu7ddbbaZXPHTUHb+MA+JpdoaiFpx2J LB28E19z94lTS8jiBvFDdAGLS1x6Ed0qqN+BX1aSrX/loCMmbWvqKximLZwWDNoCpt5D Kbow== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:to:content-transfer-encoding:mime-version :message-id:date:subject:cc:from; bh=81boVyoOQ6fo2301HPdn+zc+iBKKxHM9qHhJax+aNxg=; b=NGbQBSQ47o5R3sAtsK76aAI2C4P6ydkoAyMTAVRR8RqEzR1qIL/oGLa1Nb4nIiJnxA nwEYakX2SYlT78b8oY5Zb0eXMvxxf+348jzRGiJV/lS2MumUBGvtzsvuSX7NNKMY1mI6 j6+L2O2psFj85tUTaMUmU/Suaavl5AKAadh3IOMqoq/BPPqj+Tq0EjZeQCqdHMnJge5C 45UlpMmOYN0IQlRk+lbNuZ7rPMao0ALVqbq/WCZMpBotlkSm/F+v/hHq/b24YGWHxylD 8iHaCKt0locvrz/+bI2d7wdhNPEXGnGngjTPhenyYxS5CPhGDeVMvHA43aDS1gzoglMP FvHA== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id i16-20020a1709064fd000b007b29ccd79c0si2576320ejw.590.2022.12.06.04.53.10; Tue, 06 Dec 2022 04:53:35 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233978AbiLFMwa (ORCPT + 99 others); Tue, 6 Dec 2022 07:52:30 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55950 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234131AbiLFMw1 (ORCPT ); Tue, 6 Dec 2022 07:52:27 -0500 Received: from mx1.emlix.com (mx1.emlix.com [136.243.223.33]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 9FCC215A29; Tue, 6 Dec 2022 04:52:21 -0800 (PST) Received: from mailer.emlix.com (p5098be52.dip0.t-ipconnect.de [80.152.190.82]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.emlix.com (Postfix) with ESMTPS id 78B0A60730; Tue, 6 Dec 2022 13:45:37 +0100 (CET) From: Edmund Berenson Cc: Edmund Berenson , Lukasz Zemla , Linus Walleij , Bartosz Golaszewski , linux-kernel@vger.kernel.org, linux-gpio@vger.kernel.org Subject: [PATCH 2/2] gpio: max7317: Add gpio expander driver Date: Tue, 6 Dec 2022 13:45:28 +0100 Message-Id: <20221206124530.5431-1-edmund.berenson@emlix.com> X-Mailer: git-send-email 2.37.4 MIME-Version: 1.0 X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,SPF_HELO_NONE, SPF_PASS autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net To: unlisted-recipients:; (no To-header on input) Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1751469224225252131?= X-GMAIL-MSGID: =?utf-8?q?1751469224225252131?= Add driver for maxim MAX7317 SPI-Interfaced 10 Port GPIO Expander. Co-developed-by: Lukasz Zemla Signed-off-by: Lukasz Zemla Signed-off-by: Edmund Berenson --- drivers/gpio/Kconfig | 8 ++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-max7317.c | 221 ++++++++++++++++++++++++++++++++++++ 3 files changed, 230 insertions(+) create mode 100644 drivers/gpio/gpio-max7317.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index a01af1180616..24ed968a2b7f 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1622,6 +1622,14 @@ config GPIO_MAX7301 help GPIO driver for Maxim MAX7301 SPI-based GPIO expander. +config GPIO_MAX7317 + tristate "Maxim MAX7317 GPIO expander" + depends on SPI + help + GPIO driver for Maxim MAX7317 SPI-based GPIO expander. + The MAX7317 is a serial-interfaced gpio extender, with + 10 ports. + config GPIO_MC33880 tristate "Freescale MC33880 high-side/low-side switch" help diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 29e3beb6548c..edbfb3d918ce 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -89,6 +89,7 @@ obj-$(CONFIG_GPIO_MAX7300) += gpio-max7300.o obj-$(CONFIG_GPIO_MAX7301) += gpio-max7301.o obj-$(CONFIG_GPIO_MAX730X) += gpio-max730x.o obj-$(CONFIG_GPIO_MAX732X) += gpio-max732x.o +obj-$(CONFIG_GPIO_MAX7317) += gpio-max7317.o obj-$(CONFIG_GPIO_MAX77620) += gpio-max77620.o obj-$(CONFIG_GPIO_MAX77650) += gpio-max77650.o obj-$(CONFIG_GPIO_MB86S7X) += gpio-mb86s7x.o diff --git a/drivers/gpio/gpio-max7317.c b/drivers/gpio/gpio-max7317.c new file mode 100644 index 000000000000..58b66f763beb --- /dev/null +++ b/drivers/gpio/gpio-max7317.c @@ -0,0 +1,221 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2021, Lukasz Zemla, Woodward Inc. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* MAX7317 has 10 pins, controlled by 10 internal registers, + * addressed as 0x00 - 0x09. + */ +#define PIN_NUMBER 10 + +/* Register code to read inputs from 7 to 0 */ +#define REG_CODE_READ_PORTS_7_TO_0 ((u8)0x0E) +/* Register code to read inputs from 9 to 8 */ +#define REG_CODE_READ_PORTS_9_TO_8 ((u8)0x0F) + +struct max7317 { + struct mutex lock; + struct gpio_chip chip; + struct device *dev; +}; + +struct max7317_platform_data { + /* number assigned to the first GPIO */ + unsigned int gpio_base; +}; + +/* A write to the MAX7317 means one message with one transfer */ +static int max7317_spi_write(struct device *dev, unsigned int reg, + unsigned int val) +{ + struct spi_device *spi = to_spi_device(dev); + u16 word = ((reg & 0x7F) << 8) | (val & 0xFF); + + return spi_write_then_read(spi, &word, sizeof(word), NULL, 0); +} + +/* A read from the MAX7317 means two transfers, with chip select going high between them. */ +static int max7317_spi_read(struct device *dev, unsigned int reg) +{ + int ret; + u16 word; + struct spi_device *spi = to_spi_device(dev); + + word = 0x8000 | (reg << 8); + + /* First transfer: ask to read register */ + ret = spi_write(spi, &word, sizeof(word)); + if (ret) + return ret; + + /* Second transfer: read register value */ + ret = spi_read(spi, &word, sizeof(word)); + if (ret) + return ret; + + return word & 0xff; +} + +static void max7317_set(struct gpio_chip *chip, unsigned int offset, int value) +{ + struct max7317 *ts = gpiochip_get_data(chip); + + mutex_lock(&ts->lock); + max7317_spi_write(ts->dev, offset, value); + mutex_unlock(&ts->lock); +} + +static int max7317_get(struct gpio_chip *chip, unsigned int offset) +{ + struct max7317 *ts = gpiochip_get_data(chip); + unsigned int reg, val, mask, shift; + int inputs; + + if (offset < 8) { + reg = REG_CODE_READ_PORTS_7_TO_0; + mask = 1u << offset; + shift = offset; + } else { + reg = REG_CODE_READ_PORTS_9_TO_8; + mask = 1u << (offset - 8); + shift = offset - 8; + } + + mutex_lock(&ts->lock); + inputs = max7317_spi_read(ts->dev, reg); + mutex_unlock(&ts->lock); + + val = ((unsigned int)inputs & mask) >> shift; + + return (int)val; +} + +static int max7317_direction_input(struct gpio_chip *chip, unsigned int offset) +{ + /* Per documentation: + * 'Configure a port as an input by setting its output register to 0x01, + * which sets the port output high impedance (Table 4). + */ + max7317_set(chip, offset, 1); + return 0; +} + +static int max7301_direction_output(struct gpio_chip *chip, unsigned int offset, + int value) +{ + max7317_set(chip, offset, value); + return 0; +} + +static int max7317_probe(struct spi_device *spi) +{ + struct max7317 *ts; + struct device *dev; + struct max7317_platform_data *pdata; + int ret; + + /* bits_per_word cannot be configured in platform data */ + spi->bits_per_word = 16; + ret = spi_setup(spi); + if (ret < 0) + return ret; + + ts = devm_kzalloc(&spi->dev, sizeof(struct max7317), GFP_KERNEL); + if (!ts) + return -ENOMEM; + + ts->dev = &spi->dev; + + dev = ts->dev; + + pdata = dev_get_platdata(dev); + + mutex_init(&ts->lock); + dev_set_drvdata(dev, ts); + + if (pdata) + ts->chip.base = pdata->gpio_base; + else + ts->chip.base = -1; + + ts->chip.label = dev->driver->name; + + ts->chip.direction_input = max7317_direction_input; + ts->chip.get = max7317_get; + ts->chip.direction_output = max7301_direction_output; + ts->chip.set = max7317_set; + + ts->chip.ngpio = PIN_NUMBER; + ts->chip.can_sleep = true; + ts->chip.parent = dev; + ts->chip.owner = THIS_MODULE; + + ret = gpiochip_add_data(&ts->chip, ts); + if (!ret) + return ret; + + mutex_destroy(&ts->lock); + return ret; +} + +static void max7317_remove(struct spi_device *spi) +{ + struct max7317 *ts = dev_get_drvdata(&spi->dev); + + if (!ts) + return; + + gpiochip_remove(&ts->chip); + mutex_destroy(&ts->lock); +} + +static const struct spi_device_id max7317_id[] = { + { "max7317", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, max7317_id); + +static const struct of_device_id max7317_of_table[] = { + { .compatible = "maxim,max7317" }, + { } +}; +MODULE_DEVICE_TABLE(of, max7317_of_table); + +static struct spi_driver max7317_driver = { + .driver = { + .name = "max7317", + .of_match_table = of_match_ptr(max7317_of_table), + }, + .probe = max7317_probe, + .remove = max7317_remove, + .id_table = max7317_id, +}; + +static int __init max7317_init(void) +{ + return spi_register_driver(&max7317_driver); +} +/* register after spi postcore initcall and before + * subsys initcalls that may rely on these GPIOs + */ +subsys_initcall(max7317_init); + +static void __exit max7317_exit(void) +{ + spi_unregister_driver(&max7317_driver); +} +module_exit(max7317_exit); + +MODULE_AUTHOR("Lukasz Zemla"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("MAX7317 GPIO-Expander");