From nobody Tue Sep 9 21:31:47 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5AFA8C6FA9D for ; Wed, 1 Mar 2023 14:28:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230195AbjCAO24 (ORCPT ); Wed, 1 Mar 2023 09:28:56 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53446 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229806AbjCAO2x (ORCPT ); Wed, 1 Mar 2023 09:28:53 -0500 Received: from mail-ed1-x533.google.com (mail-ed1-x533.google.com [IPv6:2a00:1450:4864:20::533]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id EA289199C5; Wed, 1 Mar 2023 06:28:51 -0800 (PST) Received: by mail-ed1-x533.google.com with SMTP id i34so54489760eda.7; Wed, 01 Mar 2023 06:28:51 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=Qraxm/9HXdh2HNOxnclG1G7KZkQPcMY4/tQTySpYdVE=; b=D3i5prvBSoDAcYNiuUu7+Ssu1m/BFGlOVvRWt7XGwrnAZMMjAKCR6v4T/PI3GB76Qs Obgw7A4jpZ7PipFKe7tAATehnUfTGpylcNMFettfQpwC13grefuOYknBfcjxCnG3HAeh KpSesNJxfIhJhMR+JdvQIfpGA73k3WMIP7a2yw7pgQWxxD79hk9vsGCggOL2cWaSWelE LFEBwCSJAXogAJe8o1Jj8/mf2tv0J9KKXchTQ4Z/7US4mrIvauapmIPknTjZYJNUMBp2 /DLrjfk+FsuD9teHeFW+Y4bP2hf8IG+0gStMWxCl/cIbO7yjcxj3WtsvaYpmb6jVr1Ud hNVw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=Qraxm/9HXdh2HNOxnclG1G7KZkQPcMY4/tQTySpYdVE=; b=SaOVOUxLOQzyrAm/Mid7xuNIssdbnc73fdwW0ioZPTr0EAKnbyIBrG0Qjd8LHGoz9U eOEbLXmhPIamRgk7mpu9DOz/tgpFcNlBCiseDI4DUApurvRZk2UE5Ik4kH1mdCcnIRGj VKeif3Ew/SENsJ5n8Zzn8BeKHEIEjzQaW+mKkTuFzmT79VCZ0VpKsGAJ/LBLpWGkjssh 8kBfcEQ4t/gdX854x7scQLz6o2u+K/nvYReWmb01+D0quk3pdeqxmQ8GQsKWOtD/oczy 4TB8uu3SF5bkpenXEQpSNySf4UtY4zF87tc4SVLjLulg/8uejLjlZpoZnhHCCGHCXP47 JWWQ== X-Gm-Message-State: AO0yUKV2HvDoGRYdfzyF3DCz9M80R/NB3ImHIagVj37WkcvoQZqYSfrf URcb8TfDUQKToJWP9/WjvxSSppfYugvR+w== X-Google-Smtp-Source: AK7set9NnnMxq37N5F3qFRyecE7QdE72X972I3zYtHUWUturBwQILj5tB2qap0fhHdzlqow3aMiIow== X-Received: by 2002:a05:6402:416:b0:4ad:7c30:2599 with SMTP id q22-20020a056402041600b004ad7c302599mr8180696edv.13.1677680930210; Wed, 01 Mar 2023 06:28:50 -0800 (PST) Received: from fedora.local.tbs-biometrics.cz (176-74-132-138.netdatacomm.cz. [176.74.132.138]) by smtp.gmail.com with ESMTPSA id t8-20020a170906178800b008dcf89a72d7sm5972282eje.147.2023.03.01.06.28.48 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 01 Mar 2023 06:28:49 -0800 (PST) From: =?UTF-8?q?Martin=20Za=C5=A5ovi=C4=8D?= To: linux-kernel@vger.kernel.org Cc: robh+dt@kernel.org, krzysztof.kozlowski+dt@linaro.org, gregkh@linuxfoundation.org, airlied@redhat.com, dipenp@nvidia.com, treding@nvidia.com, mwen@igalia.com, fmdefrancesco@gmail.com, arnd@arndb.de, bvanassche@acm.org, ogabbay@kernel.org, axboe@kernel.dk, mathieu.poirier@linaro.org, linux@zary.sk, masahiroy@kernel.org, yangyicong@hisilicon.com, dan.j.williams@intel.com, jacek.lawrynowicz@linux.intel.com, benjamin.tissoires@redhat.com, devicetree@vger.kernel.org, furong.zhou@linux.intel.com, andriy.shevchenko@intel.com, linus.walleij@linaro.org, =?UTF-8?q?Martin=20Za=C5=A5ovi=C4=8D?= Subject: [PATCHv3 1/4] dt-bindings: wiegand: add Wiegand controller common properties Date: Wed, 1 Mar 2023 15:28:32 +0100 Message-Id: <20230301142835.19614-2-m.zatovic1@gmail.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20230301142835.19614-1-m.zatovic1@gmail.com> References: <20230301142835.19614-1-m.zatovic1@gmail.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Wiegand bus is defined by a Wiegand controller node. This node can contain one or more device nodes for devices attached to the controller(it is advised to only connect one device as Wiegand is a point-to-point bus). Wiegand controller needs to specify several attributes such as the pulse length in order to function properly. These attributes are documented here. Acked-by: Linus Walleij Signed-off-by: Martin Za=C5=A5ovi=C4=8D Reviewed-by: Rob Herring --- .../bindings/wiegand/wiegand-controller.yaml | 39 +++++++++++++++++++ MAINTAINERS | 5 +++ 2 files changed, 44 insertions(+) create mode 100644 Documentation/devicetree/bindings/wiegand/wiegand-contr= oller.yaml diff --git a/Documentation/devicetree/bindings/wiegand/wiegand-controller.y= aml b/Documentation/devicetree/bindings/wiegand/wiegand-controller.yaml new file mode 100644 index 000000000000..df985cb3045a --- /dev/null +++ b/Documentation/devicetree/bindings/wiegand/wiegand-controller.yaml @@ -0,0 +1,39 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/wiegand/wiegand-controller.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Wiegand Generic Controller Common Properties + +maintainers: + - Martin Za=C5=A5ovi=C4=8D + +description: + Wiegand busses can be described with a node for the Wiegand controller d= evice + and a set of child nodes for each SPI slave on the bus. + +properties: + $nodename: + pattern: "^wiegand(@.*|-[0-9a-f])?$" + + pulse-len-us: + description: | + Length of the low pulse in microseconds. + + interval-len-us: + description: | + Length of a whole bit (both the pulse and the high phase) in microse= conds. + + frame-gap-us: + description: | + Length of the last bit of a frame (both the pulse and the high phase= ) in + microseconds. + +required: + - compatible + - pulse-len-us + - interval-len-us + - frame-gap-us + +additionalProperties: true diff --git a/MAINTAINERS b/MAINTAINERS index b0db911207ba..1f6f6d236f0c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22512,6 +22512,11 @@ L: linux-rtc@vger.kernel.org S: Maintained F: drivers/rtc/rtc-sd3078.c =20 +WIEGAND BUS DRIVER +M: Martin Za=C5=A5ovi=C4=8D +S: Maintained +F: Documentation/devicetree/bindings/wiegand/wiegand-controller.yaml + WIIMOTE HID DRIVER M: David Rheinsberg L: linux-input@vger.kernel.org --=20 2.39.2 From nobody Tue Sep 9 21:31:47 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id B2F7CC6FA9D for ; Wed, 1 Mar 2023 14:29:01 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230207AbjCAO27 (ORCPT ); Wed, 1 Mar 2023 09:28:59 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53442 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230170AbjCAO24 (ORCPT ); Wed, 1 Mar 2023 09:28:56 -0500 Received: from mail-ed1-x530.google.com (mail-ed1-x530.google.com [IPv6:2a00:1450:4864:20::530]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 3C16D4696; Wed, 1 Mar 2023 06:28:54 -0800 (PST) Received: by mail-ed1-x530.google.com with SMTP id da10so54616924edb.3; Wed, 01 Mar 2023 06:28:54 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=U0yVA58GaC3n6H36VuEv9LWqoXqWPCxUn0npZ2+wTRI=; b=pY40YaTk+Xcb+lMTso2R8DYrEwMbMLZqIM4d9UjwlFdAxOFRRRV0BmmxXKORPm+a87 Hn8CyiD57ZqUMyHc0DSdGC9uW1qRtA6US7NvuOhjKf0XErNh1C9MZX5z92cFvtmzhHRT L9IOHb6pzo4i21mhZZHmj+WtLA+Tn3rcR6zOYjxJqmlH9sZsY28oRDHXq8HMb7b011R8 +puSw7V3jZRESK/f6N3vyj2bShNOvDAsljIv9QXfFYqEHCpNrMPbjxBUQEU/ICEzOhqx 7iE9ynY0BLt+XTZUbhshXakz/Zb12TMHXn35Ws/tyvYdKrxTgRFd2Czeubl/oiZTAwRi OpMA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=U0yVA58GaC3n6H36VuEv9LWqoXqWPCxUn0npZ2+wTRI=; b=sbM/5q7XpS8iqp0y1oI/+oEYZq9JsoGCahtrhy708ADw3ywOguEdALlc+A0bPG8njs hyvQGTfxT2piudTjd3pOzUkDZ0dmJe+85OFifz34k41q6oPTj0Djv/s73oWuTBpBdGjw TuIRufL/vtoNoNXqiUB+b+mHewvreLgA2iVd4eNTodx3CjwvBvGzXH8TX3jqrxnFg0lX be/ulttXSyAZpF7FYtNjd/zTJU07d5X5YZpOxS2Gkhb4YoQep5Wz3FcLODHTQpK0P0Ng yEg1kh4ZwtoOL3AtCXWlLQ/ymErb+MK+Z/tFA21Ike1OBz0nUkyAtR1IuIRrkSkqd/gK aUXA== X-Gm-Message-State: AO0yUKVYC/GVad4xel3c9K6kX5/JidrsFqAUC9DWrhN3I2/7gC3COuPL sjDuKwuN4mSMiJhNo7yqlK26C6jd7IgMDA== X-Google-Smtp-Source: AK7set+RFKTftp5tNZG0AI2Vu2F6X/6l+ZziCwaMtTe/VkcfXLjCXFsXDTughXLYSeGbRWjqFeF/iw== X-Received: by 2002:a05:6402:1b06:b0:4af:7f6e:297b with SMTP id by6-20020a0564021b0600b004af7f6e297bmr7767856edb.35.1677680932565; Wed, 01 Mar 2023 06:28:52 -0800 (PST) Received: from fedora.local.tbs-biometrics.cz (176-74-132-138.netdatacomm.cz. [176.74.132.138]) by smtp.gmail.com with ESMTPSA id t8-20020a170906178800b008dcf89a72d7sm5972282eje.147.2023.03.01.06.28.51 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 01 Mar 2023 06:28:52 -0800 (PST) From: =?UTF-8?q?Martin=20Za=C5=A5ovi=C4=8D?= To: linux-kernel@vger.kernel.org Cc: robh+dt@kernel.org, krzysztof.kozlowski+dt@linaro.org, gregkh@linuxfoundation.org, airlied@redhat.com, dipenp@nvidia.com, treding@nvidia.com, mwen@igalia.com, fmdefrancesco@gmail.com, arnd@arndb.de, bvanassche@acm.org, ogabbay@kernel.org, axboe@kernel.dk, mathieu.poirier@linaro.org, linux@zary.sk, masahiroy@kernel.org, yangyicong@hisilicon.com, dan.j.williams@intel.com, jacek.lawrynowicz@linux.intel.com, benjamin.tissoires@redhat.com, devicetree@vger.kernel.org, furong.zhou@linux.intel.com, andriy.shevchenko@intel.com, linus.walleij@linaro.org, =?UTF-8?q?Martin=20Za=C5=A5ovi=C4=8D?= Subject: [PATCHv3 2/4] wiegand: add Wiegand bus driver Date: Wed, 1 Mar 2023 15:28:33 +0100 Message-Id: <20230301142835.19614-3-m.zatovic1@gmail.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20230301142835.19614-1-m.zatovic1@gmail.com> References: <20230301142835.19614-1-m.zatovic1@gmail.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Add a bus driver for Wiegand protocol. The bus driver handles Wiegand controller and Wiegand device managemement and driver matching. The bus driver defines the structures for Wiegand controllers and Wiegand devices. Wiegand controller structure represents a master and contains attributes such as the payload_len for configuring the size of a single Wiegand message in bits. It also stores the controller attributes defined in the devicetree. Each Wiegand controller should be associated with one Wiegand device, as Wiegand is typically a point-to-point bus. Acked-by: Linus Walleij Signed-off-by: Martin Za=C5=A5ovi=C4=8D --- MAINTAINERS | 2 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/wiegand/Kconfig | 20 ++ drivers/wiegand/Makefile | 1 + drivers/wiegand/wiegand.c | 500 ++++++++++++++++++++++++++++++++++++++ include/linux/wiegand.h | 155 ++++++++++++ 7 files changed, 681 insertions(+) create mode 100644 drivers/wiegand/Kconfig create mode 100644 drivers/wiegand/Makefile create mode 100644 drivers/wiegand/wiegand.c create mode 100644 include/linux/wiegand.h diff --git a/MAINTAINERS b/MAINTAINERS index 1f6f6d236f0c..23a67b32f095 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22516,6 +22516,8 @@ WIEGAND BUS DRIVER M: Martin Za=C5=A5ovi=C4=8D S: Maintained F: Documentation/devicetree/bindings/wiegand/wiegand-controller.yaml +F: drivers/wiegand/wiegand.c +F: include/linux/wiegand.h =20 WIIMOTE HID DRIVER M: David Rheinsberg diff --git a/drivers/Kconfig b/drivers/Kconfig index 968bd0a6fd78..bedc5a9fecba 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -67,6 +67,8 @@ source "drivers/spi/Kconfig" =20 source "drivers/spmi/Kconfig" =20 +source "drivers/wiegand/Kconfig" + source "drivers/hsi/Kconfig" =20 source "drivers/pps/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 20b118dca999..ef96e937eacc 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -150,6 +150,7 @@ obj-$(CONFIG_VHOST_RING) +=3D vhost/ obj-$(CONFIG_VHOST_IOTLB) +=3D vhost/ obj-$(CONFIG_VHOST) +=3D vhost/ obj-$(CONFIG_VLYNQ) +=3D vlynq/ +obj-$(CONFIG_WIEGAND) +=3D wiegand/ obj-$(CONFIG_GREYBUS) +=3D greybus/ obj-$(CONFIG_COMEDI) +=3D comedi/ obj-$(CONFIG_STAGING) +=3D staging/ diff --git a/drivers/wiegand/Kconfig b/drivers/wiegand/Kconfig new file mode 100644 index 000000000000..d6b63250e80b --- /dev/null +++ b/drivers/wiegand/Kconfig @@ -0,0 +1,20 @@ +config WIEGAND + tristate "Wiegand Bus driver" + help + The "Wiegand Interface" is an asynchronous low-level protocol + or wiring standard. It is typically used for point-to-point + communication. The data length of Wiegand messages is not defined, + so the devices usually default to 26, 36 or 37 bits per message. + The throughput of Wiegand depends on the selected pulse length and + the intervals between pulses, in comparison to other busses it + is generally rather slow. + + Despite its higher age, Wiegand remains widely used in access + control systems to connect a card swipe mechanism. Such mechanisms + utilize the Wiegand effect to transfer data from the card to + the reader. + + Wiegand uses two wires to transmit the data D0 and D1. Both lines + are initially pulled up. When a bit of value 0 is being transmitted, + the D0 line is pulled down. Similarly, when a bit of value 1 is being + transmitted, the D1 line is pulled down. diff --git a/drivers/wiegand/Makefile b/drivers/wiegand/Makefile new file mode 100644 index 000000000000..d17ecb722c6e --- /dev/null +++ b/drivers/wiegand/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_WIEGAND) +=3D wiegand.o diff --git a/drivers/wiegand/wiegand.c b/drivers/wiegand/wiegand.c new file mode 100644 index 000000000000..ebec2e3e4cd6 --- /dev/null +++ b/drivers/wiegand/wiegand.c @@ -0,0 +1,500 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct bus_type wiegand_bus_type; +static DEFINE_IDR(wiegand_controller_idr); +static DEFINE_MUTEX(board_lock); + +static void devm_wiegand_release_controller(struct device *dev, void *ctlr) +{ + wiegand_controller_put(*(struct wiegand_controller **)ctlr); +} + +struct wiegand_controller *wiegand_alloc_controller(struct device *dev, un= signed int size, + bool slave) +{ + struct wiegand_controller *ctlr; + size_t ctlr_size =3D ALIGN(sizeof(*ctlr), dma_get_cache_alignment()); + + if (!dev) + return NULL; + + ctlr =3D kzalloc(size + ctlr_size, GFP_KERNEL); + if (!ctlr) + return NULL; + + device_initialize(&ctlr->dev); + ctlr->bus_num =3D -1; + ctlr->slave =3D slave; + ctlr->dev.parent =3D dev; + wiegand_controller_set_devdata(ctlr, (void *)ctlr + ctlr_size); + + return ctlr; +} +EXPORT_SYMBOL_GPL(wiegand_alloc_controller); + +struct wiegand_controller *devm_wiegand_alloc_controller(struct device *de= v, unsigned int size, + bool slave) +{ + struct wiegand_controller **ptr, *ctlr; + + ptr =3D devres_alloc(devm_wiegand_release_controller, sizeof(*ptr), GFP_K= ERNEL); + if (!ptr) + return NULL; + + ctlr =3D wiegand_alloc_controller(dev, size, slave); + if (ctlr) { + ctlr->devm_allocated =3D true; + *ptr =3D ctlr; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return ctlr; +} +EXPORT_SYMBOL_GPL(devm_wiegand_alloc_controller); + +/** + * wiegand_controller_check_ops - checks whether message transfer function= was defined for a + * controller + * @ctlr: controller structure to check + */ +static int wiegand_controller_check_ops(struct wiegand_controller *ctlr) +{ + if (!ctlr->transfer_message) + return -EINVAL; + return 0; +} + +/** + * of_register_wiegand_device - allocates and registers a new Wiegand devi= ce based on devicetree + * node + * @ctlr: controller structure to attach device to + * @nc: devicetree node for the device + */ +static struct wiegand_device *of_register_wiegand_device(struct wiegand_co= ntroller *ctlr, + struct device_node *nc) +{ + struct wiegand_device *wiegand; + int rc; + + wiegand =3D wiegand_alloc_device(ctlr); + if (!wiegand) { + dev_err(&ctlr->dev, "wiegad_device alloc error for %pOF\n", nc); + rc =3D -ENOMEM; + goto err_out; + } + + of_node_get(nc); + wiegand->dev.of_node =3D nc; + wiegand->dev.fwnode =3D of_fwnode_handle(nc); + + rc =3D wiegand_add_device(wiegand); + if (rc) { + dev_err(&ctlr->dev, "wiegand_device register error %pOF\n", nc); + goto err_of_node_put; + } + + /* check if more devices are connected to the bus */ + if (ctlr->device_count > 1) + dev_warn(&ctlr->dev, "Wiegand is a point-to-point bus, it is advised to = only connect one device per Wiegand bus. The devices may not communicate us= ing the same pulse length, format or else.\n"); + + return wiegand; + +err_of_node_put: + of_node_put(nc); +err_out: + wiegand_dev_put(wiegand); + return ERR_PTR(rc); +} + +/** + * of_register_wiegand_devices - creates a wiegand device for all children= of a controller + * devicetree node + * @ctlr: controller structure to check + */ +static void of_register_wiegand_devices(struct wiegand_controller *ctlr) +{ + struct wiegand_device *wiegand; + struct device_node *nc; + + if (!ctlr->dev.of_node) + return; + + for_each_available_child_of_node(ctlr->dev.of_node, nc) { + if (of_node_test_and_set_flag(nc, OF_POPULATED)) + continue; + wiegand =3D of_register_wiegand_device(ctlr, nc); + if (IS_ERR(wiegand)) { + dev_warn(&ctlr->dev, "Failed to create wiegand device for %pOF\n", nc); + of_node_clear_flag(nc, OF_POPULATED); + } + } +} + +/** + * wiegand_register_controller - registers controller structure within bus + * @ctlr: controller structure to register + * + * Function checks that the message transfer functions is defined for pass= ed controller structure, + * gets the devicetree defined attributes and checks whether they have all= been initialized and + * finally adds the controller device and registers the controller on the = bus. + */ +int wiegand_register_controller(struct wiegand_controller *ctlr) +{ + struct device *dev =3D ctlr->dev.parent; + int status, id, first_dynamic; + + if (!dev) + return -ENODEV; + + status =3D wiegand_controller_check_ops(ctlr); + if (status) + return status; + + if (ctlr->dev.of_node) { + id =3D of_alias_get_id(ctlr->dev.of_node, "wiegand"); + if (id > 0) { + ctlr->bus_num =3D id; + mutex_lock(&board_lock); + id =3D idr_alloc(&wiegand_controller_idr, ctlr, ctlr->bus_num, + ctlr->bus_num + 1, GFP_KERNEL); + mutex_unlock(&board_lock); + if (WARN(id < 0, "couldn't get idr")) + return id =3D=3D -ENOSPC ? -EBUSY : id; + } + device_property_read_u32(&ctlr->dev, "pulse-len-us", &ctlr->pulse_len); + device_property_read_u32(&ctlr->dev, "interval-len-us", &ctlr->interval_= len); + device_property_read_u32(&ctlr->dev, "frame-gap-us", &ctlr->frame_gap); + } + if (ctlr->bus_num < 0) { + first_dynamic =3D of_alias_get_highest_id("wiegand"); + if (first_dynamic < 0) + first_dynamic =3D 0; + else + first_dynamic++; + + mutex_lock(&board_lock); + id =3D idr_alloc(&wiegand_controller_idr, ctlr, first_dynamic, + 0, GFP_KERNEL); + mutex_unlock(&board_lock); + if (WARN(id < 0, "couldn't get idr\n")) + return id; + ctlr->bus_num =3D id; + } + + if (ctlr->pulse_len =3D=3D 0) { + dev_warn(&ctlr->dev, "pulse_len is not initialized, setting the default = value 50us\n"); + ctlr->pulse_len =3D 50; + } + if (ctlr->interval_len =3D=3D 0) { + dev_warn(&ctlr->dev, "interval_len is not initialized, setting the defau= lt value 2000us\n"); + ctlr->interval_len =3D 2000; + } + if (ctlr->frame_gap =3D=3D 0) { + dev_warn(&ctlr->dev, "frame_gap is not initialized, setting the default = value 2000us\n"); + ctlr->frame_gap =3D 2000; + } + + dev_set_name(&ctlr->dev, "wiegand%u", ctlr->bus_num); + ctlr->device_count =3D 0; + + status =3D device_add(&ctlr->dev); + if (status < 0) + goto free_bus_id; + + of_register_wiegand_devices(ctlr); + + return status; + +free_bus_id: + mutex_lock(&board_lock); + idr_remove(&wiegand_controller_idr, ctlr->bus_num); + mutex_unlock(&board_lock); + return status; +} + +static int __unregister(struct device *dev, void *null) +{ + wiegand_unregister_device(to_wiegand_device(dev)); + return 0; +} + +void wiegand_unregister_controller(struct wiegand_controller *ctlr) +{ + struct wiegand_controller *found; + int id =3D ctlr->bus_num; + + device_for_each_child(&ctlr->dev, NULL, __unregister); + found =3D idr_find(&wiegand_controller_idr, id); + device_del(&ctlr->dev); + + mutex_lock(&board_lock); + if (found =3D=3D ctlr) + idr_remove(&wiegand_controller_idr, id); + mutex_unlock(&board_lock); + + if (!ctlr->devm_allocated) + put_device(&ctlr->dev); +} +EXPORT_SYMBOL_GPL(wiegand_unregister_controller); + +static void devm_wiegand_unregister(struct device *dev, void *res) +{ + wiegand_unregister_controller(*(struct wiegand_controller **)res); +} + +int devm_wiegand_register_controller(struct device *dev, struct wiegand_co= ntroller *ctlr) +{ + struct wiegand_controller **ptr; + int ret; + + ptr =3D devres_alloc(devm_wiegand_unregister, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + ret =3D wiegand_register_controller(ctlr); + if (!ret) { + *ptr =3D ctlr; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return ret; +} +EXPORT_SYMBOL_GPL(devm_wiegand_register_controller); + +/* Device section */ + +static void wieganddev_release(struct device *dev) +{ + struct wiegand_device *wiegand =3D to_wiegand_device(dev); + + wiegand_controller_put(wiegand->controller); + kfree(wiegand); +} + +struct wiegand_device *wiegand_alloc_device(struct wiegand_controller *ctl= r) +{ + struct wiegand_device *wiegand; + + if (!wiegand_controller_get(ctlr)) + return NULL; + + wiegand =3D kzalloc(sizeof(*wiegand), GFP_KERNEL); + if (!wiegand) { + wiegand_controller_put(ctlr); + return NULL; + } + + wiegand->controller =3D ctlr; + wiegand->dev.parent =3D &ctlr->dev; + wiegand->dev.bus =3D &wiegand_bus_type; + wiegand->dev.release =3D wieganddev_release; + + device_initialize(&wiegand->dev); + return wiegand; +} +EXPORT_SYMBOL_GPL(wiegand_alloc_device); + +static void wiegand_cleanup(struct wiegand_device *wiegand) +{ + if (wiegand->controller->cleanup) + wiegand->controller->cleanup(wiegand); +} + +static int __wiegand_add_device(struct wiegand_device *wiegand) +{ + struct wiegand_controller *ctlr =3D wiegand->controller; + struct device *dev =3D ctlr->dev.parent; + int status; + + status =3D wiegand_setup(wiegand); + if (status < 0) { + dev_err(dev, "can't setup %s, status %d\n", + dev_name(&wiegand->dev), status); + return status; + } + + status =3D device_add(&wiegand->dev); + if (status < 0) { + dev_err(dev, "can't add %s, status %d\n", dev_name(&wiegand->dev), statu= s); + wiegand_cleanup(wiegand); + } else { + dev_dbg(dev, "registered child %s\n", dev_name(&wiegand->dev)); + } + + return status; +} + +static void wiegand_dev_set_name(struct wiegand_device *wiegand, u8 id) +{ + dev_set_name(&wiegand->dev, "%s.%u", dev_name(&wiegand->controller->dev),= id); +} + +int wiegand_add_device(struct wiegand_device *wiegand) +{ + struct wiegand_controller *ctlr =3D wiegand->controller; + int status; + + wiegand_dev_set_name(wiegand, ctlr->device_count); + + status =3D __wiegand_add_device(wiegand); + if (!status) { + ctlr->device_count++; + wiegand->id =3D wiegand->controller->device_count; + } + + return status; +} + +int wiegand_setup(struct wiegand_device *wiegand) +{ + int status =3D 0; + + if (wiegand->controller->setup) { + status =3D wiegand->controller->setup(wiegand); + if (status) { + dev_err(&wiegand->controller->dev, "Failed to setup device: %d\n", stat= us); + return status; + } + } + + return status; +} +EXPORT_SYMBOL_GPL(wiegand_setup); + +void wiegand_unregister_device(struct wiegand_device *wiegand) +{ + if (!wiegand) + return; + + if (wiegand->dev.of_node) { + of_node_clear_flag(wiegand->dev.of_node, OF_POPULATED); + of_node_put(wiegand->dev.of_node); + } + device_del(&wiegand->dev); + wiegand_cleanup(wiegand); + put_device(&wiegand->dev); +} +EXPORT_SYMBOL_GPL(wiegand_unregister_device); + +int wiegand_send_message(struct wiegand_device *wiegand, u8 *message, u8 b= itlen) +{ + struct wiegand_master *master =3D wiegand->controller; + + if (message =3D=3D NULL || message =3D=3D 0) + return -EINVAL; + + if (master->transfer_message) + master->transfer_message(wiegand, message, bitlen); + + return 0; +} +EXPORT_SYMBOL_GPL(wiegand_send_message); + +static int wiegand_match_device(struct device *dev, struct device_driver *= drv) +{ + if (of_driver_match_device(dev, drv)) + return 1; + + return 0; +} + +static int wiegand_probe(struct device *dev) +{ + struct wiegand_device *wiegand =3D to_wiegand_device(dev); + const struct wiegand_driver *wdrv =3D to_wiegand_driver(dev->driver); + int ret =3D 0; + + if (wdrv->probe) + ret =3D wdrv->probe(wiegand); + + return ret; +} + +static int wiegand_remove(struct device *dev) +{ + const struct wiegand_driver *wdrv =3D to_wiegand_driver(dev->driver); + + if (wdrv->remove) + wdrv->remove(to_wiegand_device(dev)); + + return 0; +} + +static struct bus_type wiegand_bus_type =3D { + .name =3D "wiegand", + .match =3D wiegand_match_device, + .probe =3D wiegand_probe, + .remove =3D wiegand_remove, +}; + +int __wiegand_register_driver(struct module *owner, struct wiegand_driver = *wdrv) +{ + wdrv->driver.owner =3D owner; + wdrv->driver.bus =3D &wiegand_bus_type; + + if (wdrv->driver.of_match_table) { + const struct of_device_id *of_id; + + for (of_id =3D wdrv->driver.of_match_table; of_id->compatible[0]; + of_id++) { + const char *of_name; + + /* remove vendor prefix */ + of_name =3D strnchr(of_id->compatible, + sizeof(of_id->compatible), ','); + if (of_name) + of_name++; + else + of_name =3D of_id->compatible; + + if (wdrv->driver.name) { + if (strcmp(wdrv->driver.name, of_name) =3D=3D 0) + continue; + } + + pr_warn("Wiegand driver %s has no device_id for %s\n", + wdrv->driver.name, of_id->compatible); + } + } + + return driver_register(&wdrv->driver); +} +EXPORT_SYMBOL_GPL(__wiegand_register_driver); + +static int __init wiegand_init(void) +{ + int ret; + + ret =3D bus_register(&wiegand_bus_type); + if (ret < 0) { + pr_err("Wiegand bus registration failed: %d\n", ret); + return ret; + } + + return 0; +} + +static void __exit wiegand_exit(void) +{ + bus_unregister(&wiegand_bus_type); +} +postcore_initcall_sync(wiegand_init); +module_exit(wiegand_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Wiegand bus driver"); +MODULE_AUTHOR("Martin Za=C5=A5ovi=C4=8D "); diff --git a/include/linux/wiegand.h b/include/linux/wiegand.h new file mode 100644 index 000000000000..9dc683d91b3d --- /dev/null +++ b/include/linux/wiegand.h @@ -0,0 +1,155 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef H_LINUX_INCLUDE_LINUX_WIEGAND_H +#define H_LINUX_INCLUDE_LINUX_WIEGAND_H + +#include +#include +#include + +#define WIEGAND_NAME_SIZE 32 + +extern struct bus_type wiegand_type; + +/** + * struct wiegand_device - Wiegand listener device + * @dev - drivers structure of the device + * @id - unique device id + * @controller - Wiegand controller associated with the device + * @modalias - Name of the driver to use with this device, or its alias. + */ +struct wiegand_device { + struct device dev; + u8 id; + struct wiegand_controller *controller; + char modalias[WIEGAND_NAME_SIZE]; +}; + +/** + * struct wiegand_controller - Wiegand master or slave interface + * @dev - Device interface of the controller + * @list - Link with the global wiegand_controller list + * @bus_num - Board-specific identifier for Wiegand controller + * @pulse_len: length of the low pulse in usec; defaults to 50us + * @interval_len: length of a whole bit (both the pulse and the high phase= ) in usec; + * defaults to 2000us + * @frame_gap: length of the last bit of a frame (both the pulse and the h= igh phase) in usec; + * defaults to interval_len + * device_count - Counter of devices connected to the same Wiegand bus(con= troller). + * devm_allocated - Whether the allocation of this struct is devres-managed + * slave - Whether the controller is a slave(receives data). + * transfer_message - Send a message on the bus. + * setup - Setup a device. + * cleanup - Cleanup after a device. + */ +struct wiegand_controller { + struct device dev; + + s16 bus_num; + + bool slave; + bool devm_allocated; + + u32 pulse_len; + u32 interval_len; + u32 frame_gap; + u32 payload_len; + u8 device_count; + + int (*transfer_message)(struct wiegand_device *dev, u8 *message, u8 bitle= n); + int (*setup)(struct wiegand_device *wiegand); + void (*cleanup)(struct wiegand_device *wiegand); +}; + +struct wiegand_driver { + const struct wiegand_device_id *id_table; + int (*probe)(struct wiegand_device *wiegand); + void (*remove)(struct wiegand_device *wiegand); + struct device_driver driver; +}; + +/* Wiegand controller section */ + +#define wiegand_master wiegand_controller +struct wiegand_controller *wiegand_alloc_controller(struct device *host, u= nsigned int size, + bool slave); + +struct wiegand_controller *devm_wiegand_alloc_controller(struct device *de= v, unsigned int size, + bool slave); +static inline struct wiegand_controller *devm_wiegand_alloc_master(struct = device *dev, + unsigned int size) +{ + return devm_wiegand_alloc_controller(dev, size, false); +} + +extern int wiegand_register_controller(struct wiegand_controller *ctlr); +extern int devm_wiegand_register_controller(struct device *dev, struct wie= gand_controller *ctlr); +#define wiegand_register_master(_ctlr) wiegand_register_controller(_ctlr) +#define devm_wiegand_register_master(_dev, _ctlr)devm_wiegand_register_con= troller(_dev, _ctlr) +extern void wiegand_unregister_controller(struct wiegand_controller *ctlr); +#define wiegand_unregister_master(_ctlr) wiegand_unregister_controller(_ct= lr) +extern struct wiegand_master *wiegand_busnum_to_master(u16 bus_num); + +static inline void *wiegand_controller_get_devdata(struct wiegand_controll= er *ctlr) +{ + return dev_get_drvdata(&ctlr->dev); +} + +static inline void wiegand_controller_set_devdata(struct wiegand_controlle= r *ctlr, void *data) +{ + dev_set_drvdata(&ctlr->dev, data); +} + +#define wiegand_master_get_devdata(_ctlr) wiegand_controller_get_devdata(_= ctlr) +#define wiegand_master_set_devdata(_ctlr, _data) wiegand_controller_set_de= vdata(_ctlr, _data) + +static inline struct wiegand_controller *wiegand_controller_get(struct wie= gand_controller *ctlr) +{ + if (!ctlr || !get_device(&ctlr->dev)) + return NULL; + return ctlr; +} + +static inline void wiegand_controller_put(struct wiegand_controller *ctlr) +{ + if (ctlr) + put_device(&ctlr->dev); +} + +/* Wiegand device section */ + +extern struct wiegand_device *wiegand_alloc_device(struct wiegand_controll= er *ctlr); +extern int wiegand_add_device(struct wiegand_device *wiegand); +extern int wiegand_setup(struct wiegand_device *wiegand); +extern void wiegand_unregister_device(struct wiegand_device *wiegand); + +extern int wiegand_send_message(struct wiegand_device *wiegand, u8 *messag= e, u8 bitlen); + +static inline struct wiegand_device *to_wiegand_device(struct device *dev) +{ + return dev ? container_of(dev, struct wiegand_device, dev) : NULL; +} + +static inline void wiegand_dev_put(struct wiegand_device *wiegand) +{ + if (wiegand) + put_device(&wiegand->dev); +} + +/* Wiegand driver section */ + +extern int __wiegand_register_driver(struct module *owner, struct wiegand_= driver *wdrv); +#define wiegand_register_driver(driver) __wiegand_register_driver(THIS_MOD= ULE, driver) + +static inline void wiegand_unregister_driver(struct wiegand_driver *wdrv) +{ + if (wdrv) + driver_unregister(&wdrv->driver); +} + +static inline struct wiegand_driver *to_wiegand_driver(struct device_drive= r *drv) +{ + return drv ? container_of(drv, struct wiegand_driver, driver) : NULL; +} + +#endif /* H_LINUX_INCLUDE_LINUX_WIEGAND_H */ --=20 2.39.2 From nobody Tue Sep 9 21:31:47 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 38C3DC6FA9D for ; Wed, 1 Mar 2023 14:29:05 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230215AbjCAO3D (ORCPT ); Wed, 1 Mar 2023 09:29:03 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53558 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229806AbjCAO25 (ORCPT ); Wed, 1 Mar 2023 09:28:57 -0500 Received: from mail-ed1-x535.google.com (mail-ed1-x535.google.com [IPv6:2a00:1450:4864:20::535]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 4C1E21B33E; Wed, 1 Mar 2023 06:28:56 -0800 (PST) Received: by mail-ed1-x535.google.com with SMTP id cq23so54647277edb.1; Wed, 01 Mar 2023 06:28:56 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=LIK1zlcDtzGzZUoPfFsY1qnnB3Dv1hWrxQ0eoBZLglg=; b=MlmlpzzsYNljnIJffqf1a7CYsFtxeYOiLIphYeOpbxlbZdzzaEBgGcqiuqV7xjX0sb Yxq5Gn6LiUFD5pw5Je+lMZgNrfIJZhdLrkuPwDSR2wZaJ4DS2L+CBJP/QU15pMb/Eve8 H0+iuAzm8LNjJZ6YpxA/UutoOrRN/4KnTqh+4XBBEBabB8E4sW+LIKh7e/AapaMKh2Be PlTECaDUk00J5e37Z8k3ZesourLmdpxCaYKulZ2xAGUC5en8sHYpc3ypnEfVV+JK3zGM oJnZIhWVmueGQoPqFyBq+dMz/6jTukEXb8OaYrl7O97KhhxIRfjr2mX14RyRaZ30QGAF 4nMQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=LIK1zlcDtzGzZUoPfFsY1qnnB3Dv1hWrxQ0eoBZLglg=; b=N/i6T7Rx57bpLLYCsP1ubf8wBImLhSrzbL94I2wWz0Tqjy9eFQo0Y+kUO2yPwczQCF hea4oQG0kzQAcIrHAuxKUjAQ0JAOvcqE07kZKfx11bGOsvvUJrRgwkQQi+BbOyX4SXPB aorXkPdVSR5etbSj9F4UL16gzm9RJaESyUoMi+m0cU0Zgaaw6fBhVDjNdSf10FBzb7uo mDEMVJwhEiBwgxuFxC+tcwAgpMm7cQ3Cuyzk1eLn4UgEpMl+did6ZekgTox0u5M/SOup PamszRe2dfHnhrucEoASLeVvaED1EFA5agitWCDRYhkkXrgS1w3h4IFFw2yjO7zGIEtK aS8Q== X-Gm-Message-State: AO0yUKXfADGsKaJPGulhzz848/Gd0R0POpiFlw+PLBuV8j38qXt7RXTB C9vZ+OnBfkM9j/cfojnVMfhBu0SBXe8jJQ== X-Google-Smtp-Source: AK7set/vm6zpt1cqrr+wCkp29vG2fzKR2+JDt3v9fK6v15rlEFtpTsQ84efvhCy4ehXfZI41Y/s4ZA== X-Received: by 2002:aa7:c7cc:0:b0:4ac:bbc7:aa8e with SMTP id o12-20020aa7c7cc000000b004acbbc7aa8emr6134099eds.41.1677680934606; Wed, 01 Mar 2023 06:28:54 -0800 (PST) Received: from fedora.local.tbs-biometrics.cz (176-74-132-138.netdatacomm.cz. [176.74.132.138]) by smtp.gmail.com with ESMTPSA id t8-20020a170906178800b008dcf89a72d7sm5972282eje.147.2023.03.01.06.28.53 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 01 Mar 2023 06:28:54 -0800 (PST) From: =?UTF-8?q?Martin=20Za=C5=A5ovi=C4=8D?= To: linux-kernel@vger.kernel.org Cc: robh+dt@kernel.org, krzysztof.kozlowski+dt@linaro.org, gregkh@linuxfoundation.org, airlied@redhat.com, dipenp@nvidia.com, treding@nvidia.com, mwen@igalia.com, fmdefrancesco@gmail.com, arnd@arndb.de, bvanassche@acm.org, ogabbay@kernel.org, axboe@kernel.dk, mathieu.poirier@linaro.org, linux@zary.sk, masahiroy@kernel.org, yangyicong@hisilicon.com, dan.j.williams@intel.com, jacek.lawrynowicz@linux.intel.com, benjamin.tissoires@redhat.com, devicetree@vger.kernel.org, furong.zhou@linux.intel.com, andriy.shevchenko@intel.com, linus.walleij@linaro.org, =?UTF-8?q?Martin=20Za=C5=A5ovi=C4=8D?= Subject: [PATCHv3 3/4] dt-bindings: wiegand: add GPIO bitbanged Wiegand controller Date: Wed, 1 Mar 2023 15:28:34 +0100 Message-Id: <20230301142835.19614-4-m.zatovic1@gmail.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20230301142835.19614-1-m.zatovic1@gmail.com> References: <20230301142835.19614-1-m.zatovic1@gmail.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org GPIO bitbanged Wiegand controller requires definitions of GPIO lines to be used on top of the common Wiegand properties. Wiegand utilizes two such lines - D0(low data line) and D1(high data line). Acked-by: Linus Walleij Signed-off-by: Martin Za=C5=A5ovi=C4=8D --- .../bindings/wiegand/wiegand-gpio.yaml | 51 +++++++++++++++++++ MAINTAINERS | 5 ++ 2 files changed, 56 insertions(+) create mode 100644 Documentation/devicetree/bindings/wiegand/wiegand-gpio.= yaml diff --git a/Documentation/devicetree/bindings/wiegand/wiegand-gpio.yaml b/= Documentation/devicetree/bindings/wiegand/wiegand-gpio.yaml new file mode 100644 index 000000000000..df28929f6dae --- /dev/null +++ b/Documentation/devicetree/bindings/wiegand/wiegand-gpio.yaml @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/wiegand/wiegand-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: GPIO bitbanged Wiegand interface devicetree bindings + +maintainers: + - Martin Za=C5=A5ovi=C4=8D + +description: + This represents the GPIO lines used for bit-banged Wiegand on dedicated = GPIO + lines. + +allOf: + - $ref: /schemas/wiegand/wiegand-controller.yaml# + +properties: + compatible: + const: wiegand-gpio + + data-hi-gpios: + description: GPIO used as Wiegands data-hi line. + maxItems: 1 + + data-lo-gpios: + description: GPIO used as Wiegands data-lo line. + maxItems: 1 + +required: + - compatible + - data-hi-gpios + - data-lo-gpios + +unevaluatedProperties: false + +examples: + - | + #include + + wiegand@f00 { + compatible =3D "wiegand-gpio"; + pulse-len-us =3D <50>; + interval-len-us =3D <2000>; + frame-gap-us =3D <2000>; + data-lo-gpios =3D <&gpio2 6 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>; + data-hi-gpios =3D <&gpio2 7 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>; + + /* devices */ + }; diff --git a/MAINTAINERS b/MAINTAINERS index 23a67b32f095..91e573466d6b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22519,6 +22519,11 @@ F: Documentation/devicetree/bindings/wiegand/wiega= nd-controller.yaml F: drivers/wiegand/wiegand.c F: include/linux/wiegand.h =20 +WIEGAND GPIO BITBANG DRIVER +M: Martin Za=C5=A5ovi=C4=8D +S: Maintained +F: Documentation/devicetree/bindings/wiegand/wiegand-gpio.yaml + WIIMOTE HID DRIVER M: David Rheinsberg L: linux-input@vger.kernel.org --=20 2.39.2 From nobody Tue Sep 9 21:31:47 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9AE69C6FA9D for ; Wed, 1 Mar 2023 14:29:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230269AbjCAO3N (ORCPT ); Wed, 1 Mar 2023 09:29:13 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53442 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230227AbjCAO3J (ORCPT ); Wed, 1 Mar 2023 09:29:09 -0500 Received: from mail-ed1-x535.google.com (mail-ed1-x535.google.com [IPv6:2a00:1450:4864:20::535]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1AB3131E05; Wed, 1 Mar 2023 06:28:58 -0800 (PST) Received: by mail-ed1-x535.google.com with SMTP id cq23so54647812edb.1; Wed, 01 Mar 2023 06:28:57 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=iclZ11jzipPQaEkBiD57/nQz5zRbGvKcq2FeaTkbORY=; b=hcJL0QuLGnZvgbZUg2pFOTY53ESxCcsMUM4jqH2xVo5e4xsS0GiEqF14yDcVJ9m3+3 CFXONbFVzu4KYljfzI5KTuBipYqGWv862l8I7slP8U/wROqXODWVahTG2r3boJTnGYDO FjcR90jTO85s91cVBRfd9Pr2AAQH5ZrRNkVKsZtjJYn2BTlmllDdwqSJ1G5ixfCPdQfb n5vYvzzjl4jR7fNMCrVLVX4J8lUU0mN1NknG4Oke3hLm6irPikNwfPQ6u4tXqcqlSyl1 GKqd4IRXCzvpm2SKwWUU/7PNSNAzUPPU7TAYDwHvl82U5qw8C4sq7F176I0uSmaJ8T7a F9xw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=iclZ11jzipPQaEkBiD57/nQz5zRbGvKcq2FeaTkbORY=; b=HG9BGt8pEE0Scx/Vo/SLv/kNDDvVt+u/QBgyCUbocLPFGBWSBJsgoCbrT6BmxErbgU rOYCV3sOmsYrZOkqwsLNMWV1lBF6D3h4gfM2Ba8z+2KCPWPRmT8hiFyDspYI3H0zJA3I Ih7ktWQUvdgVu7N9P5G3zxvNgPTaO9/00L2KQ9/o9Vz+bCkshPWMmEsX7g0BoPTqMoRI elTRarb8spgWy8mCzBZbbR2uQFlAnAPNfzJRxY5yPV5Z5fAp4Ql4OFv+TxQkzpO9jUwh 7Xy9W8l2iqx1jVF+yKFqUvIy2tgk8E2FNtIWMSWvsvWE0Ie4PIg7KRFH56sWt4N0yMy4 wGJA== X-Gm-Message-State: AO0yUKVZsK9vwlFqGKKRh9v7iUzwF1rrnWXBBMo2gZbeHjPwuPqpaWJR 8VLezTZJB7ilpm66MMTONARtdnhDKMYEsg== X-Google-Smtp-Source: AK7set/H5R9uxXFwI65qSUTG2cHQHmYoG3na8yi7CSagttYmAjKZJoZJnGJwymfgUAnCWHd/zJ6+PA== X-Received: by 2002:a17:907:25c2:b0:887:dea8:b029 with SMTP id ae2-20020a17090725c200b00887dea8b029mr7046524ejc.1.1677680937429; Wed, 01 Mar 2023 06:28:57 -0800 (PST) Received: from fedora.local.tbs-biometrics.cz (176-74-132-138.netdatacomm.cz. [176.74.132.138]) by smtp.gmail.com with ESMTPSA id t8-20020a170906178800b008dcf89a72d7sm5972282eje.147.2023.03.01.06.28.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 01 Mar 2023 06:28:56 -0800 (PST) From: =?UTF-8?q?Martin=20Za=C5=A5ovi=C4=8D?= To: linux-kernel@vger.kernel.org Cc: robh+dt@kernel.org, krzysztof.kozlowski+dt@linaro.org, gregkh@linuxfoundation.org, airlied@redhat.com, dipenp@nvidia.com, treding@nvidia.com, mwen@igalia.com, fmdefrancesco@gmail.com, arnd@arndb.de, bvanassche@acm.org, ogabbay@kernel.org, axboe@kernel.dk, mathieu.poirier@linaro.org, linux@zary.sk, masahiroy@kernel.org, yangyicong@hisilicon.com, dan.j.williams@intel.com, jacek.lawrynowicz@linux.intel.com, benjamin.tissoires@redhat.com, devicetree@vger.kernel.org, furong.zhou@linux.intel.com, andriy.shevchenko@intel.com, linus.walleij@linaro.org, =?UTF-8?q?Martin=20Za=C5=A5ovi=C4=8D?= Subject: [PATCHv3 4/4] wiegand: add Wiegand GPIO bitbanged controller driver Date: Wed, 1 Mar 2023 15:28:35 +0100 Message-Id: <20230301142835.19614-5-m.zatovic1@gmail.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20230301142835.19614-1-m.zatovic1@gmail.com> References: <20230301142835.19614-1-m.zatovic1@gmail.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org This controller formats the data to a Wiegand format and bit-bangs the message on devicetree defined GPIO lines. Several attributes need to be defined in the devicetree in order for this driver to work, namely the data-hi-gpios, data-lo-gpios, pulse-len, frame-gap and interval-len. These attributes are documented in the devicetree bindings documentation files. The driver creates a dev file for writing messages on the bus. It also creates a sysfs file to control the payload length of messages(in bits). If a message is shorter than the set payload length, it will be discarded. On the other hand, if a message is longer, the additional bits will be stripped off. Acked-by: Linus Walleij Signed-off-by: Martin Za=C5=A5ovi=C4=8D --- .../ABI/testing/sysfs-driver-wiegand-gpio | 9 + MAINTAINERS | 2 + drivers/wiegand/Kconfig | 8 + drivers/wiegand/Makefile | 1 + drivers/wiegand/wiegand-gpio.c | 316 ++++++++++++++++++ 5 files changed, 336 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-driver-wiegand-gpio create mode 100644 drivers/wiegand/wiegand-gpio.c diff --git a/Documentation/ABI/testing/sysfs-driver-wiegand-gpio b/Document= ation/ABI/testing/sysfs-driver-wiegand-gpio new file mode 100644 index 000000000000..be2246880a83 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-wiegand-gpio @@ -0,0 +1,9 @@ +What: /sys/devices/platform/.../wiegand-gpio-attributes/payload_len +Date: January 2023 +Contact: Martin Za=C5=A5ovi=C4=8D +Description: + Read/set the payload length of messages sent by Wiegand GPIO + bit-banging controller in bits. The default value is 26, as + that is the most widely-used length of Wiegand messages. + Controller will only send messages of at least the set length + and it will strip off bits of longer messages. diff --git a/MAINTAINERS b/MAINTAINERS index 91e573466d6b..eeeb343ee91c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22522,7 +22522,9 @@ F: include/linux/wiegand.h WIEGAND GPIO BITBANG DRIVER M: Martin Za=C5=A5ovi=C4=8D S: Maintained +F: Documentation/ABI/testing/sysfs-driver-wiegand-gpio F: Documentation/devicetree/bindings/wiegand/wiegand-gpio.yaml +F: drivers/wiegand/wiegand-gpio.c =20 WIIMOTE HID DRIVER M: David Rheinsberg diff --git a/drivers/wiegand/Kconfig b/drivers/wiegand/Kconfig index d6b63250e80b..d3a6c773c767 100644 --- a/drivers/wiegand/Kconfig +++ b/drivers/wiegand/Kconfig @@ -18,3 +18,11 @@ config WIEGAND are initially pulled up. When a bit of value 0 is being transmitted, the D0 line is pulled down. Similarly, when a bit of value 1 is being transmitted, the D1 line is pulled down. + +config WIEGAND_GPIO + tristate "GPIO-based wiegand master (write only)" + depends on WIEGAND + help + This GPIO bitbanging Wiegand controller uses the libgpiod library to + utilize GPIO lines for sending Wiegand data. Userspace may access + the Wiegand GPIO interface via a dev entry. diff --git a/drivers/wiegand/Makefile b/drivers/wiegand/Makefile index d17ecb722c6e..ddf697e21088 100644 --- a/drivers/wiegand/Makefile +++ b/drivers/wiegand/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_WIEGAND) +=3D wiegand.o +obj-$(CONFIG_WIEGAND_GPIO) +=3D wiegand-gpio.o diff --git a/drivers/wiegand/wiegand-gpio.c b/drivers/wiegand/wiegand-gpio.c new file mode 100644 index 000000000000..e67a30a1c5ae --- /dev/null +++ b/drivers/wiegand/wiegand-gpio.c @@ -0,0 +1,316 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define WIEGAND_MAX_PAYLEN_BYTES 256 + +struct wiegand_gpio { + struct device *dev; + struct wiegand_controller *ctlr; + struct miscdevice misc_dev; + struct mutex mutex; + struct gpio_desc *gpio_data_hi; + struct gpio_desc *gpio_data_lo; + struct file_operations fops; + u8 data[WIEGAND_MAX_PAYLEN_BYTES]; +}; + +struct wiegand_gpio_instance { + struct wiegand_gpio *dev; + unsigned long flags; +}; + +static ssize_t store_ulong(u32 *val, const char *buf, size_t size, unsigne= d long max) +{ + int rc; + u32 new; + + rc =3D kstrtou32(buf, 0, &new); + if (rc) + return rc; + + if (max !=3D 0 && new > max) + return -EINVAL; + + *val =3D new; + return size; +} + +/* + * Attribute file for setting payload length of Wiegand messages. + */ +ssize_t payload_len_show(struct device *dev, struct device_attribute *attr= , char *buf) +{ + struct wiegand_gpio *wiegand_gpio =3D dev_get_drvdata(dev); + struct wiegand_controller *ctlr =3D wiegand_gpio->ctlr; + + return sysfs_emit(buf, "%u\n", ctlr->payload_len); +} + +ssize_t payload_len_store(struct device *dev, struct device_attribute *att= r, const char *buf, + size_t count) +{ + struct wiegand_gpio *wiegand_gpio =3D dev_get_drvdata(dev); + struct wiegand_controller *ctlr =3D wiegand_gpio->ctlr; + + return store_ulong(&(ctlr->payload_len), buf, count, WIEGAND_MAX_PAYLEN_B= YTES * 8); +} +DEVICE_ATTR_RW(payload_len); + +static struct attribute *wiegand_gpio_attrs[] =3D { + &dev_attr_payload_len.attr, + NULL, +}; + +ATTRIBUTE_GROUPS(wiegand_gpio); + +/* + * To send a bit of value 1 following the wiegand protocol, one must set + * the wiegand_data_hi to low for the duration of pulse. Similarly to send + * a bit of value 0, the wiegand_data_lo is set to low for pulse duration. + * This way the two lines are never low at the same time. + */ +void wiegand_gpio_send_bit(struct wiegand_gpio *wiegand_gpio, bool value, = bool last) +{ + u32 pulse_len =3D wiegand_gpio->ctlr->pulse_len; + u32 interval_len =3D wiegand_gpio->ctlr->interval_len; + u32 frame_gap =3D wiegand_gpio->ctlr->frame_gap; + struct gpio_desc *gpio =3D value ? wiegand_gpio->gpio_data_hi : wiegand_g= pio->gpio_data_lo; + + gpiod_set_value_cansleep(gpio, 0); + udelay(pulse_len); + gpiod_set_value_cansleep(gpio, 1); + + if (last) + udelay(frame_gap - pulse_len); + else + udelay(interval_len - pulse_len); +} + +/* This function is used for writing from file in dev directory */ +static int wiegand_gpio_write_by_bits(struct wiegand_gpio *wiegand_gpio, u= 16 bitlen) +{ + size_t i; + bool bit_value, is_last_bit; + + for (i =3D 0; i < bitlen; i++) { + bit_value =3D ((wiegand_gpio->data[i / 8] >> (7 - (i % 8))) & 0x01); + is_last_bit =3D (i + 1) =3D=3D bitlen; + wiegand_gpio_send_bit(wiegand_gpio, bit_value, is_last_bit); + } + + return 0; +} + +static ssize_t wiegand_gpio_get_user_data(struct wiegand_gpio *wiegand_gpi= o, char __user const *buf, + size_t len) +{ + size_t rc; + + if (len > WIEGAND_MAX_PAYLEN_BYTES) + return -EBADMSG; + + rc =3D copy_from_user(&wiegand_gpio->data[0], buf, WIEGAND_MAX_PAYLEN_BYT= ES); + if (rc < 0) + return rc; + + return len; +} + +static int wiegand_gpio_frelease(struct inode *ino, struct file *filp) +{ + struct wiegand_gpio_instance *info =3D filp->private_data; + struct wiegand_gpio *wiegand_gpio =3D info->dev; + + mutex_lock(&wiegand_gpio->mutex); + info->flags =3D 0; + mutex_unlock(&wiegand_gpio->mutex); + + kfree(info); + + return 0; +} + +static ssize_t wiegand_gpio_fwrite(struct file *filp, char __user const *b= uf, size_t len, + loff_t *offset) +{ + struct wiegand_gpio_instance *info =3D filp->private_data; + struct wiegand_gpio *wiegand_gpio =3D info->dev; + u32 msg_length =3D wiegand_gpio->ctlr->payload_len; + int rc; + + if (buf =3D=3D NULL || len =3D=3D 0 || len * 8 < msg_length) + return -EINVAL; + + rc =3D wiegand_gpio_get_user_data(wiegand_gpio, buf, len); + if (rc < 0) + return rc; + + wiegand_gpio_write_by_bits(wiegand_gpio, msg_length); + + return len; +} + +static int wiegand_gpio_fopen(struct inode *ino, struct file *filp) +{ + int rc; + struct wiegand_gpio_instance *info; + struct wiegand_gpio *wiegand_gpio =3D container_of(filp->f_op, struct wie= gand_gpio, fops); + + mutex_lock(&wiegand_gpio->mutex); + + if ((filp->f_flags & O_ACCMODE) =3D=3D O_RDONLY || (filp->f_flags & O_ACC= MODE) =3D=3D O_RDWR) { + dev_err(wiegand_gpio->dev, "Device is write only\n"); + rc =3D -EIO; + goto err; + } + + info =3D kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + rc =3D -ENOMEM; + goto err; + } + + info->dev =3D wiegand_gpio; + info->flags =3D filp->f_flags; + mutex_unlock(&wiegand_gpio->mutex); + + filp->private_data =3D info; + + return 0; +err: + mutex_unlock(&wiegand_gpio->mutex); + return rc; +} + +/* This function is used by device drivers */ +int wiegand_gpio_transfer_message(struct wiegand_device *dev, u8 *message,= u8 msg_bitlen) +{ + struct wiegand_controller *ctlr =3D dev->controller; + struct wiegand_gpio *wiegand_gpio =3D wiegand_master_get_devdata(ctlr); + u8 msg_bytelength =3D (msg_bitlen % 8) ? (msg_bitlen / 8) + 1 : (msg_bitl= en / 8); + + memcpy(wiegand_gpio->data, message, msg_bytelength); + wiegand_gpio_write_by_bits(wiegand_gpio, msg_bitlen); + + return 0; +} + +static int wiegand_gpio_request(struct device *dev, struct wiegand_gpio *w= iegand_gpio) +{ + /* Get GPIO lines using device tree bindings. */ + wiegand_gpio->gpio_data_lo =3D devm_gpiod_get(dev, "data-lo", GPIOD_OUT_H= IGH); + if (IS_ERR(wiegand_gpio->gpio_data_lo)) + return PTR_ERR(wiegand_gpio->gpio_data_lo); + + wiegand_gpio->gpio_data_hi =3D devm_gpiod_get(dev, "data-hi", GPIOD_OUT_H= IGH); + return PTR_ERR_OR_ZERO(wiegand_gpio->gpio_data_hi); +} + +static int wiegand_gpio_probe(struct platform_device *device) +{ + int status; + struct wiegand_controller *master; + struct wiegand_gpio *wiegand_gpio; + struct device *dev =3D &device->dev; + + master =3D devm_wiegand_alloc_master(dev, sizeof(*wiegand_gpio)); + if (!master) + return -ENOMEM; + + if (dev->of_node) + master->dev.of_node =3D device->dev.of_node; + + if (status) + return status; + + master->transfer_message =3D &wiegand_gpio_transfer_message; + master->payload_len =3D 26; /* set standard 26-bit format */ + + wiegand_gpio =3D wiegand_master_get_devdata(master); + wiegand_gpio->ctlr =3D master; + wiegand_gpio->fops.owner =3D THIS_MODULE; + wiegand_gpio->fops.open =3D wiegand_gpio_fopen; + wiegand_gpio->fops.release =3D wiegand_gpio_frelease; + wiegand_gpio->fops.write =3D wiegand_gpio_fwrite; + + platform_set_drvdata(device, wiegand_gpio); + + master->bus_num =3D device->id; + wiegand_gpio->dev =3D dev; + + mutex_init(&wiegand_gpio->mutex); + + status =3D wiegand_gpio_request(dev, wiegand_gpio); + if (status) { + dev_err(wiegand_gpio->dev, "failed at requesting GPIOs\n"); + return status; + } + + status =3D gpiod_direction_output(wiegand_gpio->gpio_data_hi, 1); + status |=3D gpiod_direction_output(wiegand_gpio->gpio_data_lo, 1); + if (status) { + dev_err(wiegand_gpio->dev, "failed to set GPIOs direction\n"); + return status; + } + + status =3D devm_wiegand_register_master(dev, master); + if (status) { + dev_err(wiegand_gpio->dev, "failed to register master\n"); + return status; + } + + wiegand_gpio->misc_dev.name =3D kasprintf(GFP_KERNEL, "wiegand-gpio%u", m= aster->bus_num); + wiegand_gpio->misc_dev.minor =3D MISC_DYNAMIC_MINOR; + wiegand_gpio->misc_dev.fops =3D &wiegand_gpio->fops; + + status =3D misc_register(&wiegand_gpio->misc_dev); + if (status) { + dev_err(wiegand_gpio->dev, "couldn't register misc device\n"); + return status; + } + wiegand_gpio->misc_dev.parent =3D wiegand_gpio->dev; + + device_create_file(dev, &dev_attr_payload_len); + dev->groups =3D wiegand_gpio_groups; + + return status; +} + +static int wiegand_gpio_remove(struct platform_device *device) +{ + struct wiegand_gpio *wiegand_gpio =3D platform_get_drvdata(device); + + misc_deregister(&wiegand_gpio->misc_dev); + + return 0; +} + +static const struct of_device_id wiegand_gpio_dt_idtable[] =3D { + { .compatible =3D "wiegand-gpio", }, + {}, +}; +MODULE_DEVICE_TABLE(of, wiegand_gpio_dt_idtable); + +static struct platform_driver wiegand_gpio_driver =3D { + .driver =3D { + .name =3D "wiegand-gpio", + .of_match_table =3D wiegand_gpio_dt_idtable, + }, + .probe =3D wiegand_gpio_probe, + .remove =3D wiegand_gpio_remove, +}; + +module_platform_driver(wiegand_gpio_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Wiegand write-only driver realized by GPIOs"); +MODULE_AUTHOR("Martin Za=C5=A5ovi=C4=8D "); --=20 2.39.2