From patchwork Wed Oct 5 14:57:43 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Martin_Za=C5=A5ovi=C4=8D?= X-Patchwork-Id: 613091 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 B2482C4321E for ; Wed, 5 Oct 2022 14:58:49 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230268AbiJEO6s (ORCPT ); Wed, 5 Oct 2022 10:58:48 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:50542 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230214AbiJEO6d (ORCPT ); Wed, 5 Oct 2022 10:58:33 -0400 Received: from mail-ej1-x629.google.com (mail-ej1-x629.google.com [IPv6:2a00:1450:4864:20::629]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id DC6217D1E7; Wed, 5 Oct 2022 07:58:02 -0700 (PDT) Received: by mail-ej1-x629.google.com with SMTP id 13so36061707ejn.3; Wed, 05 Oct 2022 07:58:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date; bh=ImmpV6nBR3axrQ+CsoksQDBmfqCSG7z7E6j/x4o0FG0=; b=nPWqxTArnPtfpiAmk4Eb6+XVVapsXuO5q+8bmGaUKhDR5EPdr0gNGhfB1zxngaLaGr DjADGn7XP2uqDx8OQ1DYP3Y3a2g5UjSQXpb0nAn34GU0xijMOGElKiYNnJXNw46FLEPx U2xNqPCFKMuDc6dxw+VMaq1Q9Ii8xrBDmcFUrF4Sxl2GvqGJkO52Ab2t8eGxtdpuJmsG bTtDPIq4jG6Ge4wB91DlhIzPCzxzlteGu+GgkdacK0CgLN904hZAKJhGa9v9T0l1r/ey 9XZ+LRiHY9sSSYsc6cjUCFp5ct4mECU5Us2bBEgolfUoZ4Hl2t4f2hTay7gCfTygLhQd cOww== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-message-state:from:to:cc:subject:date; bh=ImmpV6nBR3axrQ+CsoksQDBmfqCSG7z7E6j/x4o0FG0=; b=5Dq5iOSjUQj94GxOBhaewKgHTFV3HozClj0++imGOJMoH4D1Ilv0czF1DFiF5Zk+Zr jhTBp0hwahBjqmVPX1GMSruSXsepBK3NBzMsRdC0h1GCpk0jonXUJyussUnSLvH0gOlD 8FCIz1DtNjmWyjKIsBRIfB2qRWidlpL+yj2jDrlkqpOnVOHLRQRokaBlL+qZ/vH8ujvF scu76lakhYaF389o6JtWcY9DTocfAbMOyUJ/k+9jRk8RfJKqv+ao9M6Vc2URmrxKsNhE jyykPTjcdaQ54pd1ZyUXqNzLRk/b5iZWPYclqmD7nUni0n7p/6sSvgeGoOYqAzduYJDj 4log== X-Gm-Message-State: ACrzQf3PkHfNd4q1utgP7seUlqgVWr3Qh+mxETw6Ew+Kfl1Ug6VMQp9D Ra56qHPdimbgmD7ksteiGxE= X-Google-Smtp-Source: AMsMyM4eEyb/PxSl+suE8e3QB/xio6KselddNr/nODPfHDQELLaOI2roSQegyfiTMwwa/Q0FpMq6rg== X-Received: by 2002:a17:907:988c:b0:78c:e54b:9030 with SMTP id ja12-20020a170907988c00b0078ce54b9030mr3878ejc.149.1664981881135; Wed, 05 Oct 2022 07:58:01 -0700 (PDT) Received: from fedora.local.tbs-biometrics.cz (176-74-132-138.netdatacomm.cz. [176.74.132.138]) by smtp.gmail.com with ESMTPSA id m5-20020a50cc05000000b004588ef795easm4023941edi.34.2022.10.05.07.58.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 05 Oct 2022 07:58:00 -0700 (PDT) From: =?utf-8?q?Martin_Za=C5=A5ovi=C4=8D?= To: robh+dt@kernel.org, krzysztof.kozlowski+dt@linaro.org, linus.walleij@linaro.org, brgl@bgdev.pl, gregkh@linuxfoundation.org, jeffrey.l.hugo@gmail.com, andersson@kernel.org, Michael.Srba@seznam.cz, saravanak@google.com, mani@kernel.org, hemantk@codeaurora.org, linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, linux-gpio@vger.kernel.org Cc: =?utf-8?q?Martin_Za=C5=A5ovi=C4=8D?= Subject: [RFCv2 PATCH 1/4] dt-bindings: bus: add Wiegand bus dt documentation Date: Wed, 5 Oct 2022 16:57:43 +0200 Message-Id: <20221005145746.172138-1-m.zatovic1@gmail.com> X-Mailer: git-send-email 2.37.3 MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org This patch documents the devicetree entry for enabling Wiegand bus driver. The drivers that will use Wiegand bus driver shall create a sub-node of the documented node. Signed-off-by: Martin Zaťovič --- Hello again, this is the second round of RFC patches in an attempt to add Wiegand driver to linux kernel. Thank you for all the issues you have pointed out in the first round. I have tried to fix all of them and I have also implemented a Wiegand bus driver, that is now used by the GPIO driver itself - as suggested by Linus. Any advice you have for me regarding the patches will be appreciated! With regards, Martin Zaťovič --- .../devicetree/bindings/bus/wiegand.yaml | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 Documentation/devicetree/bindings/bus/wiegand.yaml diff --git a/Documentation/devicetree/bindings/bus/wiegand.yaml b/Documentation/devicetree/bindings/bus/wiegand.yaml new file mode 100644 index 000000000000..1ed863ab925c --- /dev/null +++ b/Documentation/devicetree/bindings/bus/wiegand.yaml @@ -0,0 +1,50 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/bus/wiegand.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Wiegand Bus + +maintainers: + - Martin Zaťovič + +description: | + Wiegand interface is a wiring standard popularized in the 1980s. To this day + many card readers, fingerprint readers, sensors, etc. use Wiegand interface + particularly for access control applications. It utilizes two wires to + transmit the data - D0 and D1. + + Both data lines are initially pulled up. To send a bit of value 1, the D1 + line is set low. Similarly to send a bit of value 0, the D0 line is set low. + +properties: + $nodename: + pattern: "^wiegand(@[0-9a-f]+)?$" + + compatible: + contains: + const: wiegand + +required: + - compatible + +additionalProperties: false + +examples: + - | + #include + + wiegand { + compatible = "wiegand"; + + wiegand-gpio { + compatible = "wiegand,wiegand-gpio"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart2_wiegand>; + data-hi-gpios = <&gpio 7 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>; + data-lo-gpios = <&gpio 6 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>; + }; + }; + +... From patchwork Wed Oct 5 14:57:44 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Martin_Za=C5=A5ovi=C4=8D?= X-Patchwork-Id: 612637 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 AF923C433F5 for ; Wed, 5 Oct 2022 14:58:48 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230204AbiJEO6r (ORCPT ); Wed, 5 Oct 2022 10:58:47 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:50586 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230326AbiJEO6f (ORCPT ); Wed, 5 Oct 2022 10:58:35 -0400 Received: from mail-ej1-x632.google.com (mail-ej1-x632.google.com [IPv6:2a00:1450:4864:20::632]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 4E9CE7D1F1; Wed, 5 Oct 2022 07:58:05 -0700 (PDT) Received: by mail-ej1-x632.google.com with SMTP id a2so15927472ejx.10; Wed, 05 Oct 2022 07:58:05 -0700 (PDT) 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; bh=etL2ksIlrrnJJ1KEHn1go3jBhV9FQX3PGBEX2c5GfSI=; b=jQ4mkneT5oTogfVdED0WyJA99zvHPfgOnvlVgW6LrBQEprGwjLEKV5HyxtPdnElIdw kO6uh3qQ67NgeCOJKOLkOo1qx5OEneDYHZM2xK7cVwNFXqTdzohwxD4QQJb3O/kyvw/A aw9rWiqPtsErwNAoMyS8FzbSmUS311WysO3f22ovd2L//NS8Qv6DfnuYlyIfpJXTK6Te BANP7TcQUNkWsv+XtXKx3KseghqkLMA1ABDBIaeGGpMWuk6MIyIh+Vbij53KnamK2TUU gVP9GReqiw+JOuQVEkB9ugZf1jBJwZxB06JFWZKfHOIA4WEAHzE4JM1XgstkBmt6k5Lf g7Kw== 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; bh=etL2ksIlrrnJJ1KEHn1go3jBhV9FQX3PGBEX2c5GfSI=; b=jZs0zNPR+ua6CSHw87FiKFEVu0n5A/y4f9HdXMunjjG5NYNuLYSYx8U2Vmssh7XUVN er39cRmFVNbgcz1lLG0EHwHszVm3dx+kaqNxbWHG24HhUWEw8Vz0NiuXo0ZTjFsJ5DMA EvmBjnsO3zTaJSQPFc8PfU2PLbKgSNWPVkJSuz2QXxSMgrEQuZbdtpQdPYdmuW4hj/bi V8CsghLDxH8trlr5DB1yK3w9GF4SAjf68DWuejdPeIKnrfWDM3Gvu7yMRS4CyVQeOMna 5sarxEWD43NeqAaS3lPrisHH3rPfgAxzXUvyxyfbNkhp+TsCyImZ1lvh7QqDy0a31Urr /M4w== X-Gm-Message-State: ACrzQf2ZsEGFRq3gAKJ4L5OJs+bNq6q8+EuJK7U6anHs9EGjcSI7+bZi NAyenAw/X7N/BsGzpconnow= X-Google-Smtp-Source: AMsMyM40zfdZshyq3QUevHuMa7d9iPfVJBOZ1r9LcGWYVA/9Mri+D/cvHNV2VMINfmdO1uyKWjDoYw== X-Received: by 2002:a17:907:1c9d:b0:782:fdeb:a9fc with SMTP id nb29-20020a1709071c9d00b00782fdeba9fcmr28929ejc.308.1664981883594; Wed, 05 Oct 2022 07:58:03 -0700 (PDT) Received: from fedora.local.tbs-biometrics.cz (176-74-132-138.netdatacomm.cz. [176.74.132.138]) by smtp.gmail.com with ESMTPSA id m5-20020a50cc05000000b004588ef795easm4023941edi.34.2022.10.05.07.58.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 05 Oct 2022 07:58:03 -0700 (PDT) From: =?utf-8?q?Martin_Za=C5=A5ovi=C4=8D?= To: robh+dt@kernel.org, krzysztof.kozlowski+dt@linaro.org, linus.walleij@linaro.org, brgl@bgdev.pl, gregkh@linuxfoundation.org, jeffrey.l.hugo@gmail.com, andersson@kernel.org, Michael.Srba@seznam.cz, saravanak@google.com, mani@kernel.org, hemantk@codeaurora.org, linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, linux-gpio@vger.kernel.org Cc: =?utf-8?q?Martin_Za=C5=A5ovi=C4=8D?= Subject: [RFCv2 PATCH 2/4] bus: add Wiegand bus driver Date: Wed, 5 Oct 2022 16:57:44 +0200 Message-Id: <20221005145746.172138-2-m.zatovic1@gmail.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221005145746.172138-1-m.zatovic1@gmail.com> References: <20221005145746.172138-1-m.zatovic1@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org The Wiegand bus driver spawns devices and matches them with drivers. Signed-off-by: Martin Zaťovič --- The bus driver currently assumes that any new Wiegand driver will have a matching entry in the devicetree. It is currently sufficient as I will only be implementing the GPIO driver. If someone implements a Wiegand driver that will not use devicetree, he will also have to edit this bus driver, in order to match properly. Is that a correct approach? --- drivers/bus/Kconfig | 6 + drivers/bus/Makefile | 2 + drivers/bus/wiegand.c | 339 ++++++++++++++++++++++++++++++++++++++++ include/linux/wiegand.h | 110 +++++++++++++ 4 files changed, 457 insertions(+) create mode 100644 drivers/bus/wiegand.c create mode 100644 include/linux/wiegand.h diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index 7bfe998f3514..9675f5a13ffb 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig @@ -241,6 +241,12 @@ config VEXPRESS_CONFIG Platform configuration infrastructure for the ARM Ltd. Versatile Express. +config WIEGAND + tristate "Wiegand bus" + help + Wiegand Protocol is a low level 2-wire serial protocol. This + enables the support of the bus. + config DA8XX_MSTPRI bool "TI da8xx master peripheral priority driver" depends on ARCH_DAVINCI_DA8XX diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile index d90eed189a65..4a9fa6314e54 100644 --- a/drivers/bus/Makefile +++ b/drivers/bus/Makefile @@ -36,6 +36,8 @@ obj-$(CONFIG_TI_SYSC) += ti-sysc.o obj-$(CONFIG_TS_NBUS) += ts-nbus.o obj-$(CONFIG_UNIPHIER_SYSTEM_BUS) += uniphier-system-bus.o obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o +obj-$(CONFIG_WIEGAND) += wiegand.o + obj-$(CONFIG_DA8XX_MSTPRI) += da8xx-mstpri.o diff --git a/drivers/bus/wiegand.c b/drivers/bus/wiegand.c new file mode 100644 index 000000000000..c76026d0c3b1 --- /dev/null +++ b/drivers/bus/wiegand.c @@ -0,0 +1,339 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include + +static const struct { + const char *name; + const char *desc; +} wiegand_module_table[] = { + { NULL, NULL }, + { "wiegand-gpio", "Wiegand GPIO write-only master" }, +}; + +int wiegand_gpio_calc_parity8(u8 v) +{ + v = (v >> 4) ^ (v & ((1 << 4)-1)); + v = (v >> 2) ^ (v & ((1 << 2)-1)); + v = (v >> 1) ^ (v & ((1 << 1)-1)); + return v; +} +EXPORT_SYMBOL_GPL(wiegand_gpio_calc_parity8); + +void wiegand_gpio_add_parity_to_data(unsigned char *tmp, u8 *data, + enum wiegand_format fmt) +{ + switch (fmt) { + case WIEGAND_V26: + data[0] = (tmp[0] >> 1) | (wiegand_gpio_calc_parity8( + tmp[0] ^ (tmp[1] & 0xf0)) << 7); + data[1] = (tmp[0] << 7) | (tmp[1] >> 1); + data[2] = (tmp[1] << 7) | (tmp[2] >> 1); + data[3] = (tmp[2] << 7) | (!wiegand_gpio_calc_parity8( + (tmp[1] & 0x0f) ^ tmp[2]) << 6); + break; + case WIEGAND_V36: + tmp[4] &= 0xc0; + + data[0] = (tmp[0] >> 1) | (wiegand_gpio_calc_parity8( + tmp[0] ^ tmp[1] ^ (tmp[2] & 0x80)) << 7); + data[1] = (tmp[0] << 7) | (tmp[1] >> 1); + data[2] = (tmp[1] << 7) | (tmp[2] >> 1); + data[3] = (tmp[2] << 7) | (tmp[3] >> 1); + data[4] = (tmp[3] << 7) | (tmp[4] >> 1) | + (!wiegand_gpio_calc_parity8( + (tmp[2] & 0x7f) ^ tmp[3] ^ tmp[4]) << 4); + break; + case WIEGAND_V37: + tmp[4] &= 0xe0; + + data[0] = (tmp[0] >> 1) | (wiegand_gpio_calc_parity8( + tmp[0] ^ tmp[1] ^ (tmp[2] & 0xc0)) << 7); + data[1] = (tmp[0] << 7) | (tmp[1] >> 1); + data[2] = (tmp[1] << 7) | (tmp[2] >> 1); + data[3] = (tmp[2] << 7) | (tmp[3] >> 1); + data[4] = (tmp[3] << 7) | (tmp[4] >> 1) | + (!wiegand_gpio_calc_parity8( + (tmp[2] & 0x7f) ^ tmp[3] ^ tmp[4]) << 3); + break; + default: + WARN_ON(fmt != WIEGAND_V37 && + fmt != WIEGAND_V36 && + fmt != WIEGAND_V26); + } +} +EXPORT_SYMBOL_GPL(wiegand_gpio_add_parity_to_data); + +static inline bool wiegand_module_known(unsigned int id) +{ + return id >= WIEGAND_MODULE_FIRST && id <= WIEGAND_MODULE_LAST; +} + +static inline const char *wiegand_module_name(unsigned int id) +{ + if (wiegand_module_known(id)) + return wiegand_module_table[id].name; + else + return "unknown"; +} + +static int wiegand_match(struct device *dev, struct device_driver *drv) +{ + struct wiegand_device *wdev = to_wiegand_device(dev); + struct wiegand_driver *wdrv = to_wiegand_driver(drv); + const enum wiegand_module_id *t; + + if (of_driver_match_device(dev, drv)) + return 1; + + if (!wdrv->id_table) + return 0; + + for (t = wdrv->id_table; *t; ++t) + if (*t == wdev->id) + return 1; + + return 0; +} + +static struct bus_type wiegand_bus_type = { + .name = "wiegand", + .match = wiegand_match, +}; + +int __wiegand_register_driver(struct module *owner, + struct wiegand_driver *wdrv) +{ + wdrv->driver.owner = owner; + wdrv->driver.bus = &wiegand_bus_type; + return driver_register(&wdrv->driver); +} +EXPORT_SYMBOL_GPL(__wiegand_register_driver); + +static int wiegand_dev_check(struct device *dev, void *data) +{ + struct wiegand_device *wdev = to_wiegand_device(dev); + struct wiegand_device *new_dev = data; + + if (wdev->wiegand == new_dev->wiegand && wdev->id == new_dev->id && + wdev->idx == new_dev->idx) + return -EBUSY; + return 0; +} + +static void wiegand_dev_release(struct device *dev) +{ + struct wiegand_device *wdev = to_wiegand_device(dev); + + put_device(wdev->wiegand->dev); + kfree(wdev); +} + +static struct wiegand_device * +wiegand_alloc_device(struct wiegand *wiegand) +{ + struct wiegand_device *dev; + + if (!get_device(wiegand->dev)) + return NULL; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + put_device(wiegand->dev); + return NULL; + } + + dev->wiegand = wiegand; + dev->dev.parent = wiegand->dev; + dev->dev.bus = &wiegand_bus_type; + dev->dev.release = wiegand_dev_release; + + device_initialize(&dev->dev); + + return dev; +} + +static int wiegand_add_device(struct wiegand_device *dev) +{ + static DEFINE_MUTEX(add_mutex); + int ret; + + if (dev->idx >= WIEGAND_MAX_MODULES) + return -EINVAL; + + dev_set_name(&dev->dev, "wiegand-%s.%u", wiegand_module_name(dev->id), + dev->idx); + + mutex_lock(&add_mutex); + + ret = bus_for_each_dev(&wiegand_bus_type, NULL, dev, + wiegand_dev_check); + if (ret) + goto done; + + ret = device_add(&dev->dev); + if (ret < 0) + dev_err(dev->wiegand->dev, "Can't add %s, status %d\n", + dev_name(dev->wiegand->dev), ret); + +done: + mutex_unlock(&add_mutex); + return ret; +} + +static int __unregister(struct device *dev, void *null) +{ + if (dev->of_node) + of_node_put(dev->of_node); + + device_unregister(dev); + + return 0; +} + +static struct wiegand_device * +of_register_wiegand_device(struct wiegand *wiegand, struct device_node *nc) +{ + struct wiegand_device *dev; + const char *val; + int ret; + + dev = wiegand_alloc_device(wiegand); + if (!dev) { + dev_err(wiegand->dev, + "Wiegand device alloc error for %pOF\n", nc); + return ERR_PTR(-ENOMEM); + } + + ret = of_property_read_string(nc, "compatible", &val); + if (ret) { + dev_err(wiegand->dev, "%pOF has no valid 'compatible' property (%d)\n", + nc, ret); + goto err_put; + } + + if (strcmp(val, "wiegand,wiegand-gpio") == 0) { + dev->idx = 0; + dev->id = WIEGAND_MODULE_GPIO; + wiegand->modules[0] = dev->id; + wiegand->count++; + } + + of_node_get(nc); + dev->dev.of_node = nc; + + ret = wiegand_add_device(dev); + if (ret) { + dev_err(wiegand->dev, + "Wiegand device register error for %pOF\n", nc); + of_node_put(nc); + goto err_put; + } + + return dev; + +err_put: + put_device(&dev->dev); + return ERR_PTR(ret); +} + +static void of_register_wiegand_devices(struct wiegand *wiegand) +{ + struct wiegand_device *dev; + struct device_node *nc; + + if (!wiegand->dev->of_node) + return; + + for_each_available_child_of_node(wiegand->dev->of_node, nc) { + dev = of_register_wiegand_device(wiegand, nc); + if (IS_ERR(dev)) { + dev_warn(wiegand->dev, + "Failed to create Wiegand device for %pOF\n", + nc); + } + } +} + +static int wiegand_probe(struct platform_device *pdev) +{ + struct wiegand *wiegand; + + wiegand = devm_kzalloc(&pdev->dev, sizeof(struct wiegand), + GFP_KERNEL); + if (!wiegand) + return -ENOMEM; + + wiegand->dev = &pdev->dev; + dev_set_drvdata(&pdev->dev, wiegand); + + mutex_init(&wiegand->lock); + + of_register_wiegand_devices(wiegand); + + return 0; +} + +static int wiegand_remove(struct platform_device *pdev) +{ + struct wiegand *wiegand = dev_get_drvdata(&pdev->dev); + + device_for_each_child(&pdev->dev, NULL, __unregister); + + mutex_destroy(&wiegand->lock); + + return 0; +} + +static const struct of_device_id wiegand_dt_ids[] = { + { .compatible = "wiegand" }, + {}, +}; +MODULE_DEVICE_TABLE(of, wiegand_dt_ids); + +static struct platform_driver wiegand_driver = { + .driver = { + .name = "wiegand", + .of_match_table = wiegand_dt_ids, + }, + .probe = wiegand_probe, + .remove = wiegand_remove, +}; + +static int __init wiegand_init(void) +{ + int ret; + + ret = bus_register(&wiegand_bus_type); + if (ret < 0) { + pr_err("Wiegand bus registration failed: %d\n", ret); + goto error; + } + + ret = platform_driver_register(&wiegand_driver); + if (ret < 0) { + pr_err("Wiegand driver registration failed: %d\n", ret); + goto error_bus; + } + + return 0; + +error_bus: + bus_unregister(&wiegand_bus_type); +error: + return ret; +} +postcore_initcall_sync(wiegand_init); + +static void __exit wiegand_exit(void) +{ + platform_driver_unregister(&wiegand_driver); + bus_unregister(&wiegand_bus_type); +} +module_exit(wiegand_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Wiegand bus driver"); +MODULE_AUTHOR("Martin Zaťovič "); diff --git a/include/linux/wiegand.h b/include/linux/wiegand.h new file mode 100644 index 000000000000..98d8671f042e --- /dev/null +++ b/include/linux/wiegand.h @@ -0,0 +1,110 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef H_LINUX_INCLUDE_LINUX_WIEGAND_H +#define H_LINUX_INCLUDE_LINUX_WIEGAND_H + +#include +#include + +#define WIEGAND_MAX_PAYLEN_BYTES 256 +#define WIEGAND_MAX_MODULES 1 + +/* The used wiegand format; when data does not end at octet boundaries, the + * lower bits of the last octet will be ignored and only the upper ones will be + * used. + */ +enum wiegand_format { + WIEGAND_V26 = 26, + WIEGAND_V36 = 36, + WIEGAND_V37 = 37, + WIEGAND_CUSTOM = 0, +}; + +enum wiegand_module_id { + WIEGAND_MODULE_FIRST = 0x01, + WIEGAND_MODULE_GPIO = 0x01, + WIEGAND_MODULE_LAST = 0x01, +}; + +enum wiegand_paylen { + WIEGAND_V26_PAYLEN = 24, + WIEGAND_V36_PAYLEN = 34, + WIEGAND_V37_PAYLEN = 35, +}; + +extern struct bus_type wiegand_type; + +struct wiegand { + struct device *dev; + struct mutex lock; + int count; + u8 modules[WIEGAND_MAX_MODULES]; +}; + +struct wiegand_driver { + const enum wiegand_module_id *id_table; + struct device_driver driver; +}; + +struct wiegand_device { + struct device dev; + struct wiegand *wiegand; + enum wiegand_module_id id; + unsigned int idx; +}; + +extern int __wiegand_register_driver(struct module *owner, + struct wiegand_driver *mdrv); + +static inline void wiegand_unregister_driver(struct wiegand_driver *mdrv) +{ + if (mdrv) + driver_unregister(&mdrv->driver); +} + +#define wiegand_register_driver(driver) \ + __wiegand_register_driver(THIS_MODULE, driver) + +#define module_wiegand_driver(__wiegand_driver) \ + module_driver(__wiegand_driver, wiegand_register_driver, \ + wiegand_unregister_driver) + +static inline struct wiegand_driver * +to_wiegand_driver(struct device_driver *drv) +{ + if (!drv) + return NULL; + return container_of(drv, struct wiegand_driver, driver); +} + +static inline struct wiegand_device * +to_wiegand_device(struct device *dev) +{ + if (!dev) + return NULL; + return container_of(dev, struct wiegand_device, dev); +} + +/** + * struct wiegand_setup - Wiegand communication attributes + * @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 high + * phase) in usec; defaults to interval_len + * @format: the used wiegand format + * @payload_len: payload of wiegand message in bits + */ +struct wiegand_setup { + unsigned long pulse_len; + unsigned long interval_len; + unsigned long frame_gap; + enum wiegand_format format; + unsigned long payload_len; +}; + +extern int wiegand_gpio_calc_parity8(u8 v); +extern void wiegand_gpio_add_parity_to_data(unsigned char *tmp, u8 *data, + enum wiegand_format fmt); + +#endif /* H_LINUX_INCLUDE_LINUX_WIEGAND_H */ From patchwork Wed Oct 5 14:57:45 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Martin_Za=C5=A5ovi=C4=8D?= X-Patchwork-Id: 612636 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 8CDC3C4167B for ; Wed, 5 Oct 2022 14:58:50 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230286AbiJEO6t (ORCPT ); Wed, 5 Oct 2022 10:58:49 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:50456 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230339AbiJEO6g (ORCPT ); Wed, 5 Oct 2022 10:58:36 -0400 Received: from mail-ej1-x631.google.com (mail-ej1-x631.google.com [IPv6:2a00:1450:4864:20::631]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 64B167D1FB; Wed, 5 Oct 2022 07:58:07 -0700 (PDT) Received: by mail-ej1-x631.google.com with SMTP id z23so19291360ejw.12; Wed, 05 Oct 2022 07:58:07 -0700 (PDT) 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; bh=QxTd2irbE3fT1lcVlkhKiLQzr/UYrot8baYcYKlC0xc=; b=SmMrgmFXsJM2cd5WiBDwjLrzDEm4ZQpclFl5fQJqvWfhdjtLagDqU4Iq0k4ymXKw4W UA8Ntnz52Q+u1QUVHvf9lLHsKg1zZmKD9VWhchClrjHbVDjisX96M5X6ON/hO/UvPBV9 H8bd1MApY9HHNFklIcM/I+XGG/mx261pYzVw9+BIriNShF3Tyto2IRpikKn3JzPV5Oxc TSYQr4hW1KIpxGsG1S7aqa56qRr5eJk11cVC6Hg7EjgaDt9qnl6pBV333fpuX3B2D7jt q3CP/zSih1C/64EZwAJ7Nb8dmWxbHAKDRtcmB6HnqkZ/WHqrro6AtoqakCjNTuJkQVqr +dwg== 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; bh=QxTd2irbE3fT1lcVlkhKiLQzr/UYrot8baYcYKlC0xc=; b=qURHhKNOxPwdUUsr1wIS5CzV2hebGt09Vl1GU4PSMC0rv7pTsp1E+jkOmACXMpEmdV 1c5Bshj7ufkeWUiXu6KBbvAoi1+OnrjWRkxonUcmZyetlUDxYShAzcl/pT6bfu9y9fYB 8rjW7u4mI1FDFM748P2IUFrGOGPSBgpZCIm4G8OoWmkHYBgRO9DNp8dDEmzG45C/iG8b 9dHa/1VA13zyzWN0xpiNeEi/vy1IPVRVL8UhetGu47gkVJIEDTZI37ZhGtbOr4sW1Rju yxfbiLLoJl0arzDtOn+I87IvKRBj6TwQG1ILesTvuCBBvqcoarV8mt/rA9rGSezZzVtM EklQ== X-Gm-Message-State: ACrzQf2uAcvkDi4NOxy2HcDMZn9591zxyY3iXZh90k7yS1SY2olNvjQr YEsYTDOTKDKgd/Elfzbaqmo= X-Google-Smtp-Source: AMsMyM7R7aXCBc2h7atYt9hgjEDpMN/slyKxkpdhU9K6YUVfX8YP2xBy+dG0upo2i4CB2pdB5NJZuQ== X-Received: by 2002:a17:906:eec9:b0:783:e662:2513 with SMTP id wu9-20020a170906eec900b00783e6622513mr13897ejb.656.1664981885264; Wed, 05 Oct 2022 07:58:05 -0700 (PDT) Received: from fedora.local.tbs-biometrics.cz (176-74-132-138.netdatacomm.cz. [176.74.132.138]) by smtp.gmail.com with ESMTPSA id m5-20020a50cc05000000b004588ef795easm4023941edi.34.2022.10.05.07.58.04 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 05 Oct 2022 07:58:04 -0700 (PDT) From: =?utf-8?q?Martin_Za=C5=A5ovi=C4=8D?= To: robh+dt@kernel.org, krzysztof.kozlowski+dt@linaro.org, linus.walleij@linaro.org, brgl@bgdev.pl, gregkh@linuxfoundation.org, jeffrey.l.hugo@gmail.com, andersson@kernel.org, Michael.Srba@seznam.cz, saravanak@google.com, mani@kernel.org, hemantk@codeaurora.org, linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, linux-gpio@vger.kernel.org Cc: =?utf-8?q?Martin_Za=C5=A5ovi=C4=8D?= Subject: [RFCv2 PATCH 3/4] dt-bindings: gpio: add Wiegand GPIO driver dt documentation Date: Wed, 5 Oct 2022 16:57:45 +0200 Message-Id: <20221005145746.172138-3-m.zatovic1@gmail.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221005145746.172138-1-m.zatovic1@gmail.com> References: <20221005145746.172138-1-m.zatovic1@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org The Wiegand GPIO driver uses two GPIO lines to transmit data - data-hi and data-lo. These lines need to be defined in the devicetree, otherwise the driver will not probe successfully. Signed-off-by: Martin Zaťovič --- .../bindings/gpio/gpio-wiegand.yaml | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 Documentation/devicetree/bindings/gpio/gpio-wiegand.yaml diff --git a/Documentation/devicetree/bindings/gpio/gpio-wiegand.yaml b/Documentation/devicetree/bindings/gpio/gpio-wiegand.yaml new file mode 100644 index 000000000000..3b235667ae17 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/gpio-wiegand.yaml @@ -0,0 +1,53 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/gpio-wiegand.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Wiegand GPIO controller + +description: | + Wiegand GPIO controller running under Wiegand bus. + +maintainers: + - Martin Zaťovič + +properties: + $nodename: + pattern: "^wiegand-gpio@[0-9a-f]+$" + + compatible: + const: wiegand,wiegand-gpio + + data-hi-gpios: + description: GPIO spec for data-hi line to use + maxItems: 1 + + data-lo-gpios: + description: GPIO spec for data-lo line to use + maxItems: 1 + +required: + - compatible + - data-hi-gpios + - data-lo-gpios + +additionalProperties: false + +examples: + - | + #include + + wiegand { + compatible = "wiegand"; + + wiegand-gpio { + compatible = "wiegand,wiegand-gpio"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart2_wiegand>; + data-hi-gpios = <&gpio2 7 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>; + data-lo-gpios = <&gpio2 6 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>; + }; + }; + +... From patchwork Wed Oct 5 14:57:46 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Martin_Za=C5=A5ovi=C4=8D?= X-Patchwork-Id: 613090 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 AA168C04A95 for ; Wed, 5 Oct 2022 14:58:51 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230294AbiJEO6u (ORCPT ); Wed, 5 Oct 2022 10:58:50 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:50626 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230352AbiJEO6g (ORCPT ); Wed, 5 Oct 2022 10:58:36 -0400 Received: from mail-ed1-x52d.google.com (mail-ed1-x52d.google.com [IPv6:2a00:1450:4864:20::52d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 04F27644A; Wed, 5 Oct 2022 07:58:09 -0700 (PDT) Received: by mail-ed1-x52d.google.com with SMTP id u21so19175300edi.9; Wed, 05 Oct 2022 07:58:08 -0700 (PDT) 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; bh=cz2NKqcOvUsyVj8cGKDamLS3SuGdjUPEtX355TEPN1k=; b=X3rOdxmdmiUYZpe+mBlNzfdQSE86NeC+jDe4l/9zEot1Yre72e8Yn0a/ga1nmRgDih ak75COSLS7Qo0Cksvoj8eieU2wEC4kNdUgffdl+16Xwftu6A2NvQ2lDyKy/rE9sGnMZm K6AtXd4lxb/kHfB2muiqtNUnjNLWu0uAgKxq5BpXIMmxcILBp/GMQ/fx/NtZCfCspv77 ZMRd6Td2UZGkpKAwG505+nGq5SSaaXPXL7uAZ8eiNehf3moEzav1FVmVEsOZfXkxmRDa cXF5AockY3+rBRdZVsC1zVRnYOiWES2+0I/EJy85VjVOVbyLV6JLVy2ITUlDg+Q1g7AX WqDg== 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; bh=cz2NKqcOvUsyVj8cGKDamLS3SuGdjUPEtX355TEPN1k=; b=q+U03sfK2/11ZnCiCftJH7LvhcjDXy7wqvN23OinQ/wUrr/13EXSWfZkOp2R+GirZA yUh/XhdqH0fBwv2XOh7rUkys+jM8LpHsFLUxHm4AgAJqnFFkiZhxTlw/KoSQRJcUtcaY DGzt6oLLLRKeqEdyMliAUfJ7NunfF2yUbDKf1H4tGEJfIuE/nEz11xKRwiHr38Nxc61H jf4mJk/tPF/tJX+RlGAMuCnpu4qnZpG6bMK1sho2KJoXAsYk8sz8ptElN8AbErBcdzyA LOfoiiBJF69VTlws0CVMcuEXCiY8MR/IPhswp5vx3/IOj+Iz6O/ureRe76EJoIH9Csdb 3B2g== X-Gm-Message-State: ACrzQf2yA7i7MawWorxFPToWVQa6PtRTT1I1p2Zd2BmnxcGrFENvwgbU LEAweUeeHi0gcWW0CKQmpc8= X-Google-Smtp-Source: AMsMyM6NsazvFVOAPupGuNu7YfrB5l7/4cBdGM8m/PTeKFizIBYnhEDTzSyWNpc1X7pd6kd8cZBLKQ== X-Received: by 2002:a05:6402:1a33:b0:458:a00a:80a with SMTP id be19-20020a0564021a3300b00458a00a080amr160740edb.378.1664981887103; Wed, 05 Oct 2022 07:58:07 -0700 (PDT) Received: from fedora.local.tbs-biometrics.cz (176-74-132-138.netdatacomm.cz. [176.74.132.138]) by smtp.gmail.com with ESMTPSA id m5-20020a50cc05000000b004588ef795easm4023941edi.34.2022.10.05.07.58.06 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 05 Oct 2022 07:58:06 -0700 (PDT) From: =?utf-8?q?Martin_Za=C5=A5ovi=C4=8D?= To: robh+dt@kernel.org, krzysztof.kozlowski+dt@linaro.org, linus.walleij@linaro.org, brgl@bgdev.pl, gregkh@linuxfoundation.org, jeffrey.l.hugo@gmail.com, andersson@kernel.org, Michael.Srba@seznam.cz, saravanak@google.com, mani@kernel.org, hemantk@codeaurora.org, linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, linux-gpio@vger.kernel.org Cc: =?utf-8?q?Martin_Za=C5=A5ovi=C4=8D?= Subject: [RFCv2 PATCH 4/4] gpio: add Wiegand GPIO driver Date: Wed, 5 Oct 2022 16:57:46 +0200 Message-Id: <20221005145746.172138-4-m.zatovic1@gmail.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221005145746.172138-1-m.zatovic1@gmail.com> References: <20221005145746.172138-1-m.zatovic1@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org Wiegand GPIO driver uses GPIO lines defined in the devicetree to transmit data following the Wiegand protocol. Signed-off-by: Martin Zaťovič --- .../ABI/testing/sysfs-driver-gpio-wiegand | 40 ++ MAINTAINERS | 9 + drivers/gpio/Kconfig | 7 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio-wiegand.c | 492 ++++++++++++++++++ 5 files changed, 549 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-driver-gpio-wiegand create mode 100644 drivers/gpio/gpio-wiegand.c diff --git a/Documentation/ABI/testing/sysfs-driver-gpio-wiegand b/Documentation/ABI/testing/sysfs-driver-gpio-wiegand new file mode 100644 index 000000000000..efcce06968ef --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-gpio-wiegand @@ -0,0 +1,40 @@ +What: /sys/devices/platform/.../wiegand-gpio-attributes/format +Date: October 2022 +Contact: Martin Zaťovič +Description: Sets a format of Wiegand communication. Currently supported + formats are 26, 36 and 37 bits. These formats automatically add + checksums to the first and last bits of a message. To set a custom + format, 0 needs to be written here. Custom format writes an amount of + bits defined by payload_len(see below) and it does not add checksums so + the user is responsible for that. +Users: any user space application which wants to communicate using Wiegand + +What: /sys/devices/platform/.../wiegand-gpio-attributes/payload_len +Date: October 2022 +Contact: Martin Zaťovič +Description: Depends on format attribute - using one of the standard formats + the payload_len attribute file becomes read-only and it contains the + number of bits of a message without the checksum bits(e.g. 24 for + 26-bit format). Using a custom format makes this file writable for + configuring the Wiegand message length in bits. +Users: any user space application which wants to communicate using Wiegand + +What: /sys/devices/platform/.../wiegand-gpio-attributes/pulse_len +Date: October 2022 +Contact: Martin Zaťovič +Description: Length of the low pulse in usecs; defaults to 50us. +Users: any user space application which wants to communicate using Wiegand + +What: /sys/devices/platform/.../wiegand-gpio-attributes/interval_len +Date: October 2022 +Contact: Martin Zaťovič +Description: Length of the whole bit(both the pulse and the high phase) in + usecs; defaults to 2000us +Users: any user space application which wants to communicate using Wiegand + +What: /sys/devices/platform/.../wiegand-gpio-attributes/frame_gap +Date: October 2022 +Contact: Martin Zaťovič +Description: Length of the last bit of a frame(both the pulse and the high + phase) in usecs; defaults to interval_len +Users: any user space application which wants to communicate using Wiegand diff --git a/MAINTAINERS b/MAINTAINERS index 8656ab794786..43b21bd5333a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21960,6 +21960,15 @@ L: linux-input@vger.kernel.org S: Maintained F: drivers/hid/hid-wiimote* +WIEGAND DRIVER +M: Martin Zaťovič +S: Maintained +F: Documentation/devicetree/bindings/bus/wiegand.yaml +F: Documentation/devicetree/bindings/gpio/gpio-wiegand.yaml +F: drivers/bus/wiegand.c +F: drivers/gpio/gpio-wiegand.c +F: include/linux/wiegand.h + WILOCITY WIL6210 WIRELESS DRIVER L: linux-wireless@vger.kernel.org S: Orphan diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 0642f579196f..3cd57e28df19 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -703,6 +703,13 @@ config GPIO_VX855 Additional drivers must be enabled in order to use the functionality of the device. +config GPIO_WIEGAND + tristate "Wiegand GPIO support" + depends on WIEGAND + help + This enables the GPIO module for Wiegand protocol communication. + The Wiegand bus driver has to be enabled first. + config GPIO_WCD934X tristate "Qualcomm Technologies Inc WCD9340/WCD9341 GPIO controller driver" depends on MFD_WCD934X && OF_GPIO diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index a0985d30f51b..09024e291cc4 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -173,6 +173,7 @@ obj-$(CONFIG_GPIO_VISCONTI) += gpio-visconti.o obj-$(CONFIG_GPIO_VX855) += gpio-vx855.o obj-$(CONFIG_GPIO_WCD934X) += gpio-wcd934x.o obj-$(CONFIG_GPIO_WHISKEY_COVE) += gpio-wcove.o +obj-$(CONFIG_GPIO_WIEGAND) += gpio-wiegand.o obj-$(CONFIG_GPIO_WINBOND) += gpio-winbond.o obj-$(CONFIG_GPIO_WM831X) += gpio-wm831x.o obj-$(CONFIG_GPIO_WM8350) += gpio-wm8350.o diff --git a/drivers/gpio/gpio-wiegand.c b/drivers/gpio/gpio-wiegand.c new file mode 100644 index 000000000000..b45e39010fd6 --- /dev/null +++ b/drivers/gpio/gpio-wiegand.c @@ -0,0 +1,492 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include + +struct wiegand_gpio_device *wiegand_gpio_glob; + +struct wiegand_gpio_device { + struct device *dev; + struct miscdevice *misc_dev; + struct mutex mutex; + struct gpio_desc *gpio_data_hi; + struct gpio_desc *gpio_data_lo; + struct wiegand_setup setup; + u8 data[WIEGAND_MAX_PAYLEN_BYTES]; +}; + +struct wiegand_gpio_instance { + struct wiegand_gpio_device *dev; + unsigned long flags; +}; + +static const struct wiegand_setup WIEGAND_SETUP = { + .pulse_len = 50, + .interval_len = 2000, + .frame_gap = 2000, + .format = WIEGAND_V26, + .payload_len = 24, +}; + +/* + * 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 ar the same time. + */ +void wiegand_gpio_send_bit(struct wiegand_gpio_device *wiegand_gpio, + bool value, bool last) +{ + struct wiegand_setup *setup = &wiegand_gpio->setup; + struct gpio_desc *gpio = value ? wiegand_gpio->gpio_data_hi + : wiegand_gpio->gpio_data_lo; + + gpiod_set_value_cansleep(gpio, 0); + udelay(setup->pulse_len); + gpiod_set_value_cansleep(gpio, 1); + + if (last) + udelay(setup->frame_gap - setup->pulse_len); + else + udelay(setup->interval_len - setup->pulse_len); +} + +/* This function is used for custom format */ +static int wiegand_gpio_write_by_bits(struct wiegand_gpio_device *wiegand_gpio, + size_t len) +{ + size_t i, bitlen; + bool bit_value, is_last_bit; + struct wiegand_setup *setup = &wiegand_gpio->setup; + + bitlen = setup->format ? setup->payload_len + 2 : setup->payload_len; + + for (i = 0; i < bitlen; i++) { + bit_value = ((wiegand_gpio->data[i / 8] >> (7 - (i % 8))) + & 0x01); + is_last_bit = (i + 1) == bitlen; + + wiegand_gpio_send_bit(wiegand_gpio, (bool)bit_value, + is_last_bit); + } + + return 0; +} + +static unsigned int wiegand_gpio_calc_bytes(unsigned int bits) +{ + if (bits % 8 != 0) + return (bits / 8) + 1; + else + return bits / 8; +} + +static unsigned int wiegand_gpio_get_payload_size( + unsigned long payload_len_bits, + enum wiegand_format fmt) +{ + unsigned int ret; + + if (fmt == WIEGAND_CUSTOM) + ret = wiegand_gpio_calc_bytes(payload_len_bits); + else + /* add 2 for parity bits */ + ret = wiegand_gpio_calc_bytes(payload_len_bits + 2); + + return ret; +} + +static ssize_t wiegand_gpio_get_user_data( + struct wiegand_gpio_device *wiegand_gpio, + char __user const *buf, size_t len) +{ + size_t rc; + size_t num_copy; + unsigned char tmp[WIEGAND_MAX_PAYLEN_BYTES]; + struct wiegand_setup *setup = &wiegand_gpio->setup; + + num_copy = wiegand_gpio_get_payload_size(setup->payload_len, + setup->format); + + if (setup->format == WIEGAND_CUSTOM) { + rc = copy_from_user(&wiegand_gpio->data[0], buf, num_copy); + if (rc < 0) + return rc; + } else { + rc = copy_from_user(tmp, buf, num_copy); + if (rc < 0) + return rc; + wiegand_gpio_add_parity_to_data(tmp, wiegand_gpio->data, + setup->format); + } + return num_copy; +} + +static int wiegand_gpio_release(struct inode *ino, struct file *filp) +{ + struct wiegand_gpio_instance *info = filp->private_data; + struct wiegand_gpio_device *wiegand_gpio = info->dev; + + mutex_lock(&wiegand_gpio->mutex); + mutex_unlock(&wiegand_gpio->mutex); + + kfree(info); + + return 0; +} + +static ssize_t wiegand_gpio_write(struct file *filp, + char __user const *buf, size_t len, loff_t *offset) +{ + struct wiegand_gpio_instance *info = filp->private_data; + struct wiegand_gpio_device *wiegand_gpio = info->dev; + int rc; + + if (len * 8 < wiegand_gpio->setup.payload_len) + return -EBADMSG; + if (buf == NULL || len == 0) + return -EINVAL; + + wiegand_gpio_get_user_data(wiegand_gpio, buf, len); + rc = wiegand_gpio_write_by_bits(wiegand_gpio, len); + + return len; +} + +static int wiegand_gpio_open(struct inode *ino, struct file *filp) +{ + struct wiegand_gpio_device *wiegand_gpio; + struct wiegand_gpio_instance *info; + int rc; + + wiegand_gpio = wiegand_gpio_glob; + + mutex_lock(&wiegand_gpio->mutex); + + if ((filp->f_flags & O_ACCMODE) == O_RDONLY || + (filp->f_flags & O_ACCMODE) == O_RDWR) { + dev_err(wiegand_gpio->dev, "Device is write only\n"); + rc = -EIO; + goto err; + } + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + rc = -ENOMEM; + goto err; + } + + gpiod_direction_output(wiegand_gpio->gpio_data_hi, 1); + gpiod_direction_output(wiegand_gpio->gpio_data_lo, 1); + + info->dev = wiegand_gpio; + info->flags = filp->f_flags; + + mutex_unlock(&wiegand_gpio->mutex); + + filp->private_data = info; + + return 0; +err: + mutex_unlock(&wiegand_gpio->mutex); + + return rc; +} + +const struct file_operations wiegand_gpio_fops = { + .owner = THIS_MODULE, + .open = wiegand_gpio_open, + .release = wiegand_gpio_release, + .write = wiegand_gpio_write, +}; + +static struct miscdevice wiegand_misc_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "wiegand-gpio", + .fops = &wiegand_gpio_fops, +}; + +static ssize_t store_ulong(unsigned long *val, const char *buf, + size_t size, unsigned long max) +{ + int ret; + unsigned long new; + + ret = kstrtoul(buf, 0, &new); + if (ret) + return ret; + if (max != 0 && new > max) + return -EINVAL; + + *val = new; + return size; +} + +ssize_t pulse_len_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct wiegand_gpio_device *device = dev_get_drvdata(dev); + + if (!device) + return -ENODEV; + + return sysfs_emit(buf, "%lu\n", device->setup.pulse_len); +} + +ssize_t pulse_len_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct wiegand_gpio_device *device = dev_get_drvdata(dev); + + if (!device) + return -ENODEV; + + return store_ulong(&(device->setup.pulse_len), buf, count, 0); +} + +ssize_t interval_len_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct wiegand_gpio_device *device = dev_get_drvdata(dev); + + if (!device) + return -ENODEV; + + return sysfs_emit(buf, "%lu\n", device->setup.pulse_len); +} + +ssize_t interval_len_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct wiegand_gpio_device *device = dev_get_drvdata(dev); + + if (!device) + return -ENODEV; + + return store_ulong(&(device->setup.interval_len), buf, count, 0); +} + +ssize_t frame_gap_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct wiegand_gpio_device *device = dev_get_drvdata(dev); + + if (!device) + return -ENODEV; + + return sysfs_emit(buf, "%lu\n", device->setup.frame_gap); +} + +ssize_t frame_gap_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct wiegand_gpio_device *device = dev_get_drvdata(dev); + + if (!device) + return -ENODEV; + + return store_ulong(&(device->setup.frame_gap), buf, count, 0); +} + +ssize_t format_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct wiegand_gpio_device *device = dev_get_drvdata(dev); + + if (!device) + return -ENODEV; + + return sysfs_emit(buf, "%u\n", device->setup.format); +} + +/* + * To set a particular format, the number of bits the driver is supposed to + * transmit is written to the format sysfs file. For standard formats, the + * allowed inputs are 26, 36 and 37. To set a custom format, 0 is passed. + */ +ssize_t format_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + unsigned long new; + enum wiegand_format *val; + struct wiegand_gpio_device *device = dev_get_drvdata(dev); + + if (!device) + return -ENODEV; + + val = &(device->setup.format); + + ret = kstrtoul(buf, 0, &new); + if (ret) + return ret; + + switch (new) { + case 0: + *val = WIEGAND_CUSTOM; + break; + case 26: + *val = WIEGAND_V26; + device->setup.payload_len = WIEGAND_V26_PAYLEN; + break; + case 36: + *val = WIEGAND_V36; + device->setup.payload_len = WIEGAND_V36_PAYLEN; + break; + case 37: + *val = WIEGAND_V37; + device->setup.payload_len = WIEGAND_V37_PAYLEN; + break; + default: + return -EINVAL; + } + + return count; +} + +/* + * Using a custom format, the payload_len sysfs file configures the size of + * messages payload in bits. When one of the standard formats is used, this + * file is read-only and contains the size of the message in bits without the + * parity bits. + */ +ssize_t payload_len_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct wiegand_gpio_device *device = dev_get_drvdata(dev); + + if (!device) + return -ENODEV; + + return sysfs_emit(buf, "%lu\n", device->setup.pulse_len); +} + +ssize_t payload_len_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct wiegand_gpio_device *device = dev_get_drvdata(dev); + + if (!device) + return -ENODEV; + + /* standard formats use fixed payload size */ + if (device->setup.format != WIEGAND_CUSTOM) + return -EPERM; + + return store_ulong(&(device->setup.payload_len), buf, count, + WIEGAND_MAX_PAYLEN_BYTES * 8); +} + +DEVICE_ATTR_RW(pulse_len); +DEVICE_ATTR_RW(interval_len); +DEVICE_ATTR_RW(frame_gap); +DEVICE_ATTR_RW(format); +DEVICE_ATTR_RW(payload_len); + +static struct attribute *wiegand_attrs[] = { + &dev_attr_pulse_len.attr, + &dev_attr_interval_len.attr, + &dev_attr_frame_gap.attr, + &dev_attr_format.attr, + &dev_attr_payload_len.attr, + NULL, +}; + +static struct attribute_group wiegand_group = { + .name = "wiegand-gpio-attributes", + .attrs = wiegand_attrs, +}; + +static int wiegand_gpio_dev_probe(struct device *device) +{ + int rc; + struct wiegand_gpio_device *wiegand_gpio; + + wiegand_gpio = devm_kzalloc(device, sizeof(*wiegand_gpio), GFP_KERNEL); + if (!wiegand_gpio) + return -ENOMEM; + + wiegand_gpio->dev = device; + wiegand_gpio->misc_dev = &wiegand_misc_device; + + wiegand_gpio_glob = wiegand_gpio; + + /* Get GPIO lines using device tree bindings. */ + wiegand_gpio->gpio_data_lo = devm_gpiod_get(wiegand_gpio->dev, + "data-lo", GPIOD_OUT_HIGH); + if (IS_ERR(wiegand_gpio->gpio_data_lo)) { + return dev_err_probe(wiegand_gpio->dev, + PTR_ERR(wiegand_gpio->gpio_data_lo), + "Can't get data-lo line."); + } + wiegand_gpio->gpio_data_hi = devm_gpiod_get(wiegand_gpio->dev, + "data-hi", GPIOD_OUT_HIGH); + if (IS_ERR(wiegand_gpio->gpio_data_hi)) { + return dev_err_probe(wiegand_gpio->dev, + PTR_ERR(wiegand_gpio->gpio_data_hi), + "Can't get data-hi line."); + } + + rc = sysfs_create_group(&wiegand_gpio->dev->kobj, &wiegand_group); + if (rc < 0) { + dev_err(wiegand_gpio->dev, "Couldn't register sysfs group\n"); + return rc; + } + + mutex_init(&wiegand_gpio->mutex); + + misc_register(wiegand_gpio->misc_dev); + wiegand_gpio->misc_dev->parent = wiegand_gpio->dev; + + memcpy(&wiegand_gpio->setup, &WIEGAND_SETUP, + sizeof(struct wiegand_setup)); + + dev_set_drvdata(device, wiegand_gpio); + + return 0; +} + +static int wiegand_gpio_dev_remove(struct device *device) +{ + struct wiegand_gpio_device *wiegand_gpio = dev_get_drvdata(device); + + sysfs_remove_group(&wiegand_gpio->dev->kobj, &wiegand_group); + + misc_deregister(&wiegand_misc_device); + kfree(wiegand_gpio); + + return 0; +} + +static const struct of_device_id wiegand_gpio_dt_idtable[] = { + { .compatible = "wiegand,wiegand-gpio", }, + {}, +}; +MODULE_DEVICE_TABLE(of, wiegand_gpio_dt_idtable); + +static const enum wiegand_module_id wiegand_gpio_module_table[] = { + WIEGAND_MODULE_GPIO, + 0, +}; + +static struct wiegand_driver wiegand_gpio_driver = { + .driver = { + .name = "wiegand-gpio", + .probe = wiegand_gpio_dev_probe, + .remove = wiegand_gpio_dev_remove, + .of_match_table = wiegand_gpio_dt_idtable, + }, + .id_table = wiegand_gpio_module_table, +}; + +module_wiegand_driver(wiegand_gpio_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Wiegand write-only driver realized by GPIOs"); +MODULE_AUTHOR("Martin Zaťovič ");