From patchwork Tue Jul 21 19:35:48 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: William Breathitt Gray X-Patchwork-Id: 11676549 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 1791E13B1 for ; Tue, 21 Jul 2020 19:38:11 +0000 (UTC) Received: from merlin.infradead.org (merlin.infradead.org [205.233.59.134]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id D5A52206C1 for ; Tue, 21 Jul 2020 19:38:10 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="vuqSctd/"; dkim=fail reason="signature verification failed" (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="LswHUBdq" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org D5A52206C1 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=merlin.20170209; h=Sender:Content-Transfer-Encoding: Content-Type:Cc:List-Subscribe:List-Help:List-Post:List-Archive: List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To:Message-Id:Date: Subject:To:From:Reply-To:Content-ID:Content-Description:Resent-Date: Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=VGSWl6Y4gli4VD2I34o+xmAvI0ylilz/gHfQJsfTjhA=; b=vuqSctd/XLzTY8toEwBP/8DLh e2lPDaU83Si1Bcic+pWWcSzxOi1iUE/mupHtBY035mVam3UMJ5EK7nZ3bqsmPsIfMF8nccCE73zaJ nVrnr+MBWgZhDjhROOF1ke1siPfIE6yDrpsKLLdSRDKphIxzwaF+R+TuWuLRrH7D0M7DHxBj5Aza3 TBVIn2ny65M6MK408Bj8ybpEmkwlPoDTqksIhDKQygrPm8TVjSxyyaLCirT2tGkdseX6XVCHCmCyd lpkD6S8uOpzVsq5txj8OPAje3GPBc2TrdoFZ17/VMt9BAZE1YbmSm3CgSix9O5VW18ILNuOybTNko SowTrwvYA==; Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.92.3 #3 (Red Hat Linux)) id 1jxy3p-00014W-Mt; Tue, 21 Jul 2020 19:36:21 +0000 Received: from mail-qk1-x744.google.com ([2607:f8b0:4864:20::744]) by merlin.infradead.org with esmtps (Exim 4.92.3 #3 (Red Hat Linux)) id 1jxy3h-00012R-QV for linux-arm-kernel@lists.infradead.org; Tue, 21 Jul 2020 19:36:16 +0000 Received: by mail-qk1-x744.google.com with SMTP id z15so13800762qki.10 for ; Tue, 21 Jul 2020 12:36:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=EkmSpmhEZ7Vcfm2rjya4j142Ah+gIfxqSabZ9X2+OQk=; b=LswHUBdqdFDzDoWaN5hiGPSzeG14JPwzq5oiZbcopOsfAfQMECrJZoYvZRJk3vRVS+ IYnOa4FQgrC/ATGbjm+TpIJuyS8RXAJKuHFY8Fb8+72i6psvcjb8eP7Et36GzODXUkFj G0B17T1ZD0IiV4Wuv4B2/XhDp3HB7qCi1tSm6mGmLWS/cX1ZcP33wDYxCwUGUcc0PfJT 9hMhon0UZc0himDV8vSG2Yjzu9adZg6HDeDA0jmV+RRaOJ1mw7Ek8U9ve9NogXebsMY8 k+DQoItLyq+3pisZeiVnKmDYPvBZoa/nvBhIMuX8KyjfoOsMJZ4xGXjx0W8dL9D46TFE F+sA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=EkmSpmhEZ7Vcfm2rjya4j142Ah+gIfxqSabZ9X2+OQk=; b=pX41ReFijd04oRu53gMfw2c5N7g1dcOmCdRQK//6hiXCKtkT3+xOAffuP7NnIFr7q3 oqnaOha7iGhvR7uqPxBVe1iQ/LqmIwlCJIVmHoXBQ/tqekpbQAcDMfv39AK6AlgxrEtc GYig7T94PdI7E5t6qHfWGbyS4KpBNJElwBTEOf75MDli9Bjpkce1hYUfbGGJWYsQuD34 gd9kuzZH/Gjez6FcjR1a8V+/Uw2S5YlwdujSpUO+1ogDy8SWdy/8vDm8pI407zf8Q+43 ZZ+F1hbciMUIzR4WMh20BawJmVMtETtoc+6c7yZHcpLttGBlV9E7GzTEBYduiXSt6tn2 uoXw== X-Gm-Message-State: AOAM530dOHbYeN/AZtA7ZVldXm/wdKRp7/SkNNmGTAos5/Xi/WHY4HKy IKJAUApwWc5ydY+8B4gwrRc= X-Google-Smtp-Source: ABdhPJzWz1aEsBaigm4673xyKRgmVwtNkd72PAJG1KG5LD9K3GN8e60qf7VA+P4ubw/TBm4D726Kvg== X-Received: by 2002:ae9:dcc6:: with SMTP id q189mr29931803qkf.332.1595360172014; Tue, 21 Jul 2020 12:36:12 -0700 (PDT) Received: from localhost.localdomain (072-189-064-225.res.spectrum.com. [72.189.64.225]) by smtp.gmail.com with ESMTPSA id j72sm3331351qke.20.2020.07.21.12.36.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 21 Jul 2020 12:36:11 -0700 (PDT) From: William Breathitt Gray To: jic23@kernel.org Subject: [PATCH v4 2/5] docs: counter: Update to reflect sysfs internalization Date: Tue, 21 Jul 2020 15:35:48 -0400 Message-Id: <79e05c620129b35317684e8ef4e573bce531570d.1595358237.git.vilhelm.gray@gmail.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: References: MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20200721_153614_018697_EE9CBCB0 X-CRM114-Status: GOOD ( 22.90 ) X-Spam-Score: -0.2 (/) X-Spam-Report: SpamAssassin version 3.4.4 on merlin.infradead.org summary: Content analysis details: (-0.2 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at https://www.dnswl.org/, no trust [2607:f8b0:4864:20:0:0:0:744 listed in] [list.dnswl.org] 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record 0.0 FREEMAIL_FROM Sender email is commonly abused enduser mail provider [vilhelm.gray[at]gmail.com] -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID_EF Message has a valid DKIM or DK signature from envelope-from domain X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: kamel.bouhara@bootlin.com, gwendal@chromium.org, david@lechnology.com, linux-iio@vger.kernel.org, patrick.havelange@essensium.com, alexandre.belloni@bootlin.com, linux-kernel@vger.kernel.org, mcoquelin.stm32@gmail.com, William Breathitt Gray , fabrice.gasnier@st.com, syednwaris@gmail.com, linux-stm32@st-md-mailman.stormreply.com, linux-arm-kernel@lists.infradead.org, alexandre.torgue@st.com Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org The Counter subsystem architecture and driver implementations have changed in order to handle Counter sysfs interactions in a more consistent way. This patch updates the Generic Counter interface documentation to reflect the changes. Signed-off-by: William Breathitt Gray --- Documentation/driver-api/generic-counter.rst | 216 +++++++++++++------ 1 file changed, 150 insertions(+), 66 deletions(-) diff --git a/Documentation/driver-api/generic-counter.rst b/Documentation/driver-api/generic-counter.rst index b02c52cd69d6..fa2d699d44a5 100644 --- a/Documentation/driver-api/generic-counter.rst +++ b/Documentation/driver-api/generic-counter.rst @@ -250,8 +250,8 @@ for defining a counter device. .. kernel-doc:: drivers/counter/counter.c :export: -Implementation -============== +Driver Implementation +===================== To support a counter device, a driver must first allocate the available Counter Signals via counter_signal structures. These Signals should @@ -267,25 +267,59 @@ respective counter_count structure. These counter_count structures are set to the counts array member of an allocated counter_device structure before the Counter is registered to the system. -Driver callbacks should be provided to the counter_device structure via -a constant counter_ops structure in order to communicate with the -device: to read and write various Signals and Counts, and to set and get -the "action mode" and "function mode" for various Synapses and Counts -respectively. +Driver callbacks must be provided to the counter_device structure in +order to communicate with the device: to read and write various Signals +and Counts, and to set and get the "action mode" and "function mode" for +various Synapses and Counts respectively. A defined counter_device structure may be registered to the system by passing it to the counter_register function, and unregistered by passing it to the counter_unregister function. Similarly, the -devm_counter_register and devm_counter_unregister functions may be used -if device memory-managed registration is desired. - -Extension sysfs attributes can be created for auxiliary functionality -and data by passing in defined counter_device_ext, counter_count_ext, -and counter_signal_ext structures. In these cases, the -counter_device_ext structure is used for global/miscellaneous exposure -and configuration of the respective Counter device, while the -counter_count_ext and counter_signal_ext structures allow for auxiliary -exposure and configuration of a specific Count or Signal respectively. +devm_counter_register function may be used if device memory-managed +registration is desired. + +The struct counter_data structure is used to define counter extensions +for Signals, Synapses, and Counts. + +The "type" member specifies the type of high-level data (e.g. BOOL, +COUNT_DIRECTION, etc.) handled by this extension. The "`*_read`" and +"`*_write`" members can then be set by the counter device driver with +callbacks to handle that data using native C data types (i.e. u8, u64, +etc.). + +Convenience macros such as `COUNTER_DATA_COUNT_U64` are provided for use +by driver authors. In particular, driver authors are expected to use +the provided macros for standard Counter subsystem attributes in order +to maintain a consistent interface for userspace. For example, a counter +device driver may define several standard attributes like so:: + + struct counter_data count_ext[] = { + COUNTER_DATA_DIRECTION(count_direction_read), + COUNTER_DATA_ENABLE(count_enable_read, count_enable_write), + COUNTER_DATA_CEILING(count_ceiling_read, count_ceiling_write), + }; + +This makes it simple to see, add, and modify the attributes that are +supported by this driver ("direction", "enable", and "ceiling") and to +maintain this code without getting lost in a web of struct braces. + +Callbacks must match the function type expected for the respective +component or extension. These function types are defined in the struct +counter_data structure as the "`*_read`" and "`*_write`" union members. + +The corresponding callback prototypes for the extensions mentioned in +the previous example above would be:: + + int count_direction_read(struct counter_device *counter, + struct counter_count *count, u8 *direction); + int count_enable_read(struct counter_device *counter, + struct counter_count *count, u8 *enable); + int count_enable_write(struct counter_device *counter, + struct counter_count *count, u8 enable); + int count_ceiling_read(struct counter_device *counter, + struct counter_count *count, u64 *ceiling); + int count_ceiling_write(struct counter_device *counter, + struct counter_count *count, u64 ceiling); Determining the type of extension to create is a matter of scope. @@ -313,52 +347,102 @@ Determining the type of extension to create is a matter of scope. chip overheated via a device extension called "error_overtemp": /sys/bus/counter/devices/counterX/error_overtemp -Architecture -============ - -When the Generic Counter interface counter module is loaded, the -counter_init function is called which registers a bus_type named -"counter" to the system. Subsequently, when the module is unloaded, the -counter_exit function is called which unregisters the bus_type named -"counter" from the system. - -Counter devices are registered to the system via the counter_register -function, and later removed via the counter_unregister function. The -counter_register function establishes a unique ID for the Counter -device and creates a respective sysfs directory, where X is the -mentioned unique ID: - - /sys/bus/counter/devices/counterX - -Sysfs attributes are created within the counterX directory to expose -functionality, configurations, and data relating to the Counts, Signals, -and Synapses of the Counter device, as well as options and information -for the Counter device itself. - -Each Signal has a directory created to house its relevant sysfs -attributes, where Y is the unique ID of the respective Signal: - - /sys/bus/counter/devices/counterX/signalY - -Similarly, each Count has a directory created to house its relevant -sysfs attributes, where Y is the unique ID of the respective Count: - - /sys/bus/counter/devices/counterX/countY - -For a more detailed breakdown of the available Generic Counter interface -sysfs attributes, please refer to the -Documentation/ABI/testing/sysfs-bus-counter file. - -The Signals and Counts associated with the Counter device are registered -to the system as well by the counter_register function. The -signal_read/signal_write driver callbacks are associated with their -respective Signal attributes, while the count_read/count_write and -function_get/function_set driver callbacks are associated with their -respective Count attributes; similarly, the same is true for the -action_get/action_set driver callbacks and their respective Synapse -attributes. If a driver callback is left undefined, then the respective -read/write permission is left disabled for the relevant attributes. - -Similarly, extension sysfs attributes are created for the defined -counter_device_ext, counter_count_ext, and counter_signal_ext -structures that are passed in. +Subsystem Architecture +====================== + +Counter drivers pass and take data natively (i.e. `u8`, `u64`, etc.) and +the shared counter module handles the translation between the sysfs +interface. This gurantees a standard userspace interface for all counter +drivers, and helps generalize the Generic Counter driver ABI in order to +support the Generic Counter chrdev interface without significant changes +to the existing counter drivers. + +A high-level view of how a count value is passed down from a counter +driver is exemplified by the following:: + + Count data request: + ~~~~~~~~~~~~~~~~~~~ + ---------------------- + / Counter device \ + +----------------------+ + | Count register: 0x28 | + +----------------------+ + | + ----------------- + / raw count data / + ----------------- + | + V + +----------------------------+ + | Counter device driver |----------+ + +----------------------------+ | + | Processes data from device | ------------------- + |----------------------------| / driver callbacks / + | Type: u64 | ------------------- + | Value: 42 | | + +----------------------------+ | + | | + ---------- | + / u64 / | + ---------- | + | | + | V + | +----------------------+ + | | Counter core | + | +----------------------+ + | | Routes device driver | + | | callbacks to the | + | | userspace interfaces | + | +----------------------+ + | | + | ------------------- + | / driver callbacks / + | ------------------- + | | + +-------+ | + | | + | +---------------+ + | | + V | + +--------------------+ | + | Counter sysfs |<-+ + +--------------------+ + | Translates to the | + | standard Counter | + | sysfs output | + |--------------------| + | Type: const char * | + | Value: "42" | + +--------------------+ + | + --------------- + / const char * / + --------------- + | + V + +--------------------------------------------------+ + | `/sys/bus/counter/devices/counterX/countY/count` | + +--------------------------------------------------+ + \ Count: "42" / + -------------------------------------------------- + +There are three primary components involved: + +Counter device driver +--------------------- +Communicates with the hardware device to read/write data; e.g. counter +drivers for quadrature encoders, timers, etc. + +Counter core +------------ +Registers the counter device driver to the system so that the respective +callbacks are called during userspace interaction. + +Counter sysfs +------------- +Translates counter data to the standard Counter sysfs interface format +and vice versa. + +Please refer to the `Documentation/ABI/testing/sysfs-bus-counter` file +for a detailed breakdown of the available Generic Counter interface +sysfs attributes. From patchwork Tue Jul 21 19:35:49 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: William Breathitt Gray X-Patchwork-Id: 11676551 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 8814613B1 for ; Tue, 21 Jul 2020 19:38:12 +0000 (UTC) Received: from merlin.infradead.org (merlin.infradead.org [205.233.59.134]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 51C86206C1 for ; Tue, 21 Jul 2020 19:38:12 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="DTUtPg6L"; dkim=fail reason="signature verification failed" (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="LcAncKg+" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 51C86206C1 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=merlin.20170209; h=Sender:Content-Transfer-Encoding: Content-Type:Cc:List-Subscribe:List-Help:List-Post:List-Archive: List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To:Message-Id:Date: Subject:To:From:Reply-To:Content-ID:Content-Description:Resent-Date: Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=jhBPLx0tp0WyGkg8CH3RENawjVlv+LDz4WAwdH6KHWw=; b=DTUtPg6LtS7DDXiXsJYqam0kj WAysck4/lamTQTUAkB71Uas9XHLfWSgcTMa48/838ShH6BGLJ5IdrsYQgamYki3beZ9TKWY+IeieD zLrQf+oCPqDuqh7Yb40u9tcc3AZRbRv4Pao4KFs9o46XjfNkrBWJL13oQ4B/vDzsbGRUOJ/dSXUGN MmEnrYA0CsMgFQxHs1htjK798VvmqnNH5DzExi2sTu/E3DTyhbk4HxpoPeuhBwSdxvPWj93UVPh3m 9OFa9mIUhp53jjqILy6cM5tn+N1Sb/3i/PIiK811dp3u0TS3OW80Vp1NNj6JOB0Nihe6wwZcfJYEs xANul8IMg==; Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.92.3 #3 (Red Hat Linux)) id 1jxy3x-00015u-64; Tue, 21 Jul 2020 19:36:29 +0000 Received: from mail-qv1-xf42.google.com ([2607:f8b0:4864:20::f42]) by merlin.infradead.org with esmtps (Exim 4.92.3 #3 (Red Hat Linux)) id 1jxy3j-00012o-CP for linux-arm-kernel@lists.infradead.org; Tue, 21 Jul 2020 19:36:20 +0000 Received: by mail-qv1-xf42.google.com with SMTP id m8so9878019qvk.7 for ; Tue, 21 Jul 2020 12:36:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=t9tyAwUinMCB1oJUAyOuuifwIeIOtmGEvSaPKx2dPaQ=; b=LcAncKg+OFDSpjv3wz/xQjdQ1CYHU6fWdYJbzD6XZIQRUTKIzroS/xVyFlQ97n+YYq PSn63MEkBsux3uPYghE2nx7ZbWGZcWCL32yfJ63MqrYeZFVyPAwuTWkHHp2b9B4NLtYf bhLVnFSNySsPvi/iKnNnpMMoMOykNh0Gnz8BqdTFqTwROQHgR+x+0UXL/rToaS1/2Uf8 5jYVOwCm6o05EX+Yjo7B77C1jm25y7VNiqFTFHBZfjdFCNqs+ONR/8XPbH9u/cmVnETQ PCVBMl1PmBuHF9BG7vwtPkm981yF6qx+zyEjZuoMugl5NtiPTRERbF5JJmh8JZpqUa7m q0cw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=t9tyAwUinMCB1oJUAyOuuifwIeIOtmGEvSaPKx2dPaQ=; b=e+4Oah43fx1sHTVLXtuHff5An0os19PT2n4djJFj3H4SJSW28PraeB1JLoUWPbx3od sP20gQzBBW1R4wOsmo4PnPQz9CEGzIQUuZ7fz3yexIyoMqf9hhU9+4+qNfjcxkh/onU0 i002D3EPBJmmGYcMrNsRJlgHio1t4Hk2u8EPNSXIeeKFNMeZFd/GiUQcfFmCVgPqdORf ujPaO1TKhvTvuHbrSKs4bH0hJGBYpnXj89JeCirkY47I1oPpLXKsiebk8rNGCHtk64/v Ctmucn4J4bkO2iwdGNGofjr9BJjnItsEJNoZCisLYSfi146oUnQxbaiFlXBTT8INMqEq qj/g== X-Gm-Message-State: AOAM530Z+f68M5sothz7tVA/xt6A2I74zGijQbXCC3YZcWHXt+v4gMcA Wf2npKFFl5pfbd8QxaDUbOI= X-Google-Smtp-Source: ABdhPJwVViaSfl+JWVubq5SDBxy/8we9OD74Z6Q+YmvkZ5B1zDf4IYe5yO6eSe8HCxz9f2+cIS/mHw== X-Received: by 2002:a0c:a993:: with SMTP id a19mr28465400qvb.34.1595360173586; Tue, 21 Jul 2020 12:36:13 -0700 (PDT) Received: from localhost.localdomain (072-189-064-225.res.spectrum.com. [72.189.64.225]) by smtp.gmail.com with ESMTPSA id j72sm3331351qke.20.2020.07.21.12.36.12 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 21 Jul 2020 12:36:12 -0700 (PDT) From: William Breathitt Gray To: jic23@kernel.org Subject: [PATCH v4 3/5] counter: Add character device interface Date: Tue, 21 Jul 2020 15:35:49 -0400 Message-Id: <08b3ac7349a59ba7fa5cd438bbe78360842ccd11.1595358237.git.vilhelm.gray@gmail.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: References: MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20200721_153615_590444_A27450CA X-CRM114-Status: GOOD ( 31.70 ) X-Spam-Score: -0.2 (/) X-Spam-Report: SpamAssassin version 3.4.4 on merlin.infradead.org summary: Content analysis details: (-0.2 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at https://www.dnswl.org/, no trust [2607:f8b0:4864:20:0:0:0:f42 listed in] [list.dnswl.org] 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record 0.0 FREEMAIL_FROM Sender email is commonly abused enduser mail provider [vilhelm.gray[at]gmail.com] -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID_EF Message has a valid DKIM or DK signature from envelope-from domain X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: kamel.bouhara@bootlin.com, gwendal@chromium.org, david@lechnology.com, linux-iio@vger.kernel.org, patrick.havelange@essensium.com, alexandre.belloni@bootlin.com, linux-kernel@vger.kernel.org, mcoquelin.stm32@gmail.com, William Breathitt Gray , fabrice.gasnier@st.com, syednwaris@gmail.com, linux-stm32@st-md-mailman.stormreply.com, linux-arm-kernel@lists.infradead.org, alexandre.torgue@st.com Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org This patch introduces a character device interface for the Counter subsystem. Device data is exposed through standard character device read operations. Device data is gathered when a Counter event is pushed by the respective Counter device driver. Configuration is handled via ioctl operations on the respective Counter character device node. A high-level view of how a count value is passed down from a counter driver is exemplified by the following: ---------------------- / Counter device \ +----------------------+ | Count register: 0x28 | +----------------------+ | ----------------- / raw count data / ----------------- | V +----------------------------+ | Counter device driver |----------+ +----------------------------+ | | Processes data from device | ------------------- |----------------------------| / driver callbacks / | Type: u64 | ------------------- | Value: 42 | | +----------------------------+ | | | ---------- | / u64 / | ---------- | | | | V | +----------------------+ | | Counter core | | +----------------------+ | | Routes device driver | | | callbacks to the | | | userspace interfaces | | +----------------------+ | | | ------------------- | / driver callbacks / | ------------------- | | +-------+---------------+ | | | | | +-------|-------+ | | | V | V +--------------------+ | +---------------------+ | Counter sysfs |<-+->| Counter chrdev | +--------------------+ +---------------------+ | Translates to the | | Translates to the | | standard Counter | | standard Counter | | sysfs output | | character device | |--------------------| |---------------------+ | Type: const char * | | Type: u64 | | Value: "42" | | Value: 42 | +--------------------+ +---------------------+ | | --------------- ----------------------- / const char * / / struct counter_event / --------------- ----------------------- | | | V | +-----------+ | | read | | +-----------+ | \ Count: 42 / | ----------- | V +--------------------------------------------------+ | `/sys/bus/counter/devices/counterX/countY/count` | +--------------------------------------------------+ \ Count: "42" / -------------------------------------------------- Counter character device nodes are created under the `/dev` directory as `counterX`, where `X` is the respective counter device id. Defines for the standard Counter data types are exposed via the userspace `include/uapi/linux/counter.h` file. Counter events -------------- Counter device drivers can support Counter events by utilizing the `counter_push_event` function: int counter_push_event(struct counter_device *const counter, const u8 event); The event id is specified by the `event` parameter. When this function is called, the Counter data associated with the respective event is gathered, and a `struct counter_event` is generated for each datum and pushed to userspace. Counter events can be configured by users to report various Counter data of interest. This can be conceptualized as a list of Counter component read calls to perform. For example: +------------------------+------------------------+ | Event 0 | Event 1 | +------------------------+------------------------+ | * Count 0 | * Signal 0 | | * Count 1 | * Signal 0 Extension 0 | | * Signal 3 | * Extension 4 | | * Count 4 Extension 2 | | | * Signal 5 Extension 0 | | +------------------------+------------------------+ When `counter_push_event(counter, 1)` is called for example, it will go down the list for Event 1 and execute the read callbacks for Signal 0, Signal 0 Extension 0, and Extension 4 -- the data returned for each is pushed to a kfifo as a `struct counter_event`, which userspace can retrieve via a standard read operation on the respective character device node. Userspace --------- Userspace applications can configure Counter events via ioctl operations on the Counter character device node. There following ioctl codes are supported and provided by the `linux/counter.h` userspace header file: * COUNTER_CLEAR_WATCHES_IOCTL: Clear all Counter watches from all events * COUNTER_SET_WATCH_IOCTL: Set a Counter watch on the specified event To configure events to gather Counter data, users first populate a `struct counter_watch` with the relevant event id and the information for the desired Counter component from which to read, and then pass it via the `COUNTER_SET_WATCH_IOCTL` ioctl command. Userspace applications can then execute a `read` operation (optionally calling `poll` first) on the Counter character device node to retrieve `struct counter_event` elements with the desired data. For example, the following userspace code opens `/dev/counter0`, configures Event 0 to gather Count 0 and Count 1, and prints out the data as it becomes available on the character device node: #include #include #include #include #include #include struct counter_watch watches[2] = { { .event = 0, .component.owner_type = COUNTER_OWNER_TYPE_COUNT, .component.owner_id = 0, .component.type = COUNTER_COMPONENT_TYPE_COUNT, }, { .event = 0, .component.owner_type = COUNTER_OWNER_TYPE_COUNT, .component.owner_id = 1, .component.type = COUNTER_COMPONENT_TYPE_COUNT, }, }; int main(void) { struct pollfd pfd = { .events = POLLIN }; struct counter_event event_data[2]; pfd.fd = open("/dev/counter0", O_RDWR); ioctl(pfd.fd, COUNTER_SET_WATCH_IOCTL, watches); ioctl(pfd.fd, COUNTER_SET_WATCH_IOCTL, watches + 1); for (;;) { poll(&pfd, 1, -1); read(pfd.fd, event_data, sizeof(event_data)); printf("Timestamp 0: %llu\nCount 0: %llu\n" "Timestamp 1: %llu\nCount 1: %llu\n", (unsigned long long)event_data[0].timestamp, (unsigned long long)event_data[0].value_u64, (unsigned long long)event_data[1].timestamp, (unsigned long long)event_data[1].value_u64); } return 0; } Cc: David Lechner Cc: Gwendal Grignou Signed-off-by: William Breathitt Gray --- drivers/counter/Makefile | 2 +- drivers/counter/counter-chrdev.c | 441 +++++++++++++++++++++++++++++++ drivers/counter/counter-chrdev.h | 16 ++ drivers/counter/counter-core.c | 35 ++- include/linux/counter.h | 15 ++ include/uapi/linux/counter.h | 52 ++++ 6 files changed, 558 insertions(+), 3 deletions(-) create mode 100644 drivers/counter/counter-chrdev.c create mode 100644 drivers/counter/counter-chrdev.h diff --git a/drivers/counter/Makefile b/drivers/counter/Makefile index cbe1d06af6a9..c4870eb5b1dd 100644 --- a/drivers/counter/Makefile +++ b/drivers/counter/Makefile @@ -4,7 +4,7 @@ # obj-$(CONFIG_COUNTER) += counter.o -counter-y := counter-core.o counter-sysfs.o +counter-y := counter-core.o counter-sysfs.o counter-chrdev.o obj-$(CONFIG_104_QUAD_8) += 104-quad-8.o obj-$(CONFIG_STM32_TIMER_CNT) += stm32-timer-cnt.o diff --git a/drivers/counter/counter-chrdev.c b/drivers/counter/counter-chrdev.c new file mode 100644 index 000000000000..9aa2d32e7bc9 --- /dev/null +++ b/drivers/counter/counter-chrdev.c @@ -0,0 +1,441 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Generic Counter character device interface + * Copyright (C) 2020 William Breathitt Gray + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "counter-chrdev.h" + +struct counter_data_item { + struct list_head l; + struct counter_component component; + struct counter_data data; + void *owner; +}; + +struct counter_event_item { + struct list_head l; + u8 event; + struct list_head data_list; +}; + +static ssize_t counter_chrdev_read(struct file *filp, char __user *buf, + size_t len, loff_t *f_ps) +{ + struct counter_device *const counter = filp->private_data; + int err; + unsigned long flags; + unsigned int copied; + + if (len < sizeof(struct counter_event)) + return -EINVAL; + + do { + if (kfifo_is_empty(&counter->events)) { + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + + err = wait_event_interruptible(counter->events_wait, + !kfifo_is_empty(&counter->events)); + if (err) + return err; + } + + raw_spin_lock_irqsave(&counter->events_lock, flags); + err = kfifo_to_user(&counter->events, buf, len, &copied); + raw_spin_unlock_irqrestore(&counter->events_lock, flags); + if (err) + return err; + } while (!copied); + + return copied; +} + +static __poll_t counter_chrdev_poll(struct file *filp, + struct poll_table_struct *pollt) +{ + struct counter_device *const counter = filp->private_data; + __poll_t events = 0; + + poll_wait(filp, &counter->events_wait, pollt); + + if (!kfifo_is_empty(&counter->events)) + events = EPOLLIN | EPOLLRDNORM; + + return events; +} + +static void counter_events_list_free(struct counter_device *const counter) +{ + unsigned long flags; + struct counter_event_item *p, *n; + struct counter_data_item *q, *o; + + raw_spin_lock_irqsave(&counter->events_lock, flags); + + list_for_each_entry_safe(p, n, &counter->events_list, l) { + /* Free associated data items */ + list_for_each_entry_safe(q, o, &p->data_list, l) { + list_del(&q->l); + kfree(q); + } + + /* Free event item */ + list_del(&p->l); + kfree(p); + } + + raw_spin_unlock_irqrestore(&counter->events_lock, flags); +} + +static int counter_set_event_item(struct counter_device *const counter, + const u8 event, + const struct counter_data_item *const cfg) +{ + unsigned long flags; + struct counter_event_item *event_item; + int err; + struct counter_data_item *data_item; + + raw_spin_lock_irqsave(&counter->events_lock, flags); + + /* Search for event in the list */ + list_for_each_entry(event_item, &counter->events_list, l) + if (event_item->event == event) + break; + + /* If event is not already in the list */ + if (&event_item->l == &counter->events_list) { + /* Allocate new event item */ + event_item = kmalloc(sizeof(*event_item), GFP_ATOMIC); + if (!event_item) { + err = -ENOMEM; + goto err_event_item; + } + + /* Configure event item and add to the list */ + event_item->event = event; + INIT_LIST_HEAD(&event_item->data_list); + list_add(&event_item->l, &counter->events_list); + } + + /* Search for data item in the list */ + list_for_each_entry(data_item, &event_item->data_list, l) + if (data_item->owner == cfg->owner && + data_item->data.count_u8_read == cfg->data.count_u8_read) { + err = -EINVAL; + goto err_data_item; + } + + /* Allocate data item */ + data_item = kmalloc(sizeof(*data_item), GFP_ATOMIC); + if (!data_item) { + err = -ENOMEM; + goto err_data_item; + } + *data_item = *cfg; + + /* Add data item to event item */ + list_add_tail(&data_item->l, &event_item->data_list); + + raw_spin_unlock_irqrestore(&counter->events_lock, flags); + + return 0; + +err_data_item: + if (list_empty(&event_item->data_list)) { + list_del(&event_item->l); + kfree(event_item); + } +err_event_item: + raw_spin_unlock_irqrestore(&counter->events_lock, flags); + return err; +} + +static int counter_set_watch(struct counter_device *const counter, + const unsigned long arg) +{ + void __user *const uwatch = (void __user *)arg; + struct counter_watch watch; + struct counter_data_item data_item; + size_t owner_id, id; + struct counter_data *ext; + size_t num_ext; + + if (copy_from_user(&watch, uwatch, sizeof(watch))) + return -EFAULT; + owner_id = watch.component.owner_id; + id = watch.component.id; + + /* Configure owner info for data item */ + switch (watch.component.owner_type) { + case COUNTER_OWNER_TYPE_DEVICE: + data_item.owner = NULL; + + ext = counter->ext; + num_ext = counter->num_ext; + break; + case COUNTER_OWNER_TYPE_SIGNAL: + if (counter->num_signals < owner_id + 1) + return -EINVAL; + + data_item.owner = counter->signals + owner_id; + + ext = counter->signals[owner_id].ext; + num_ext = counter->signals[owner_id].num_ext; + break; + case COUNTER_OWNER_TYPE_COUNT: + if (counter->num_counts < owner_id + 1) + return -EINVAL; + + data_item.owner = counter->counts + owner_id; + + ext = counter->counts[owner_id].ext; + num_ext = counter->counts[owner_id].num_ext; + break; + default: + return -EINVAL; + } + + /* Configure component info for data item */ + switch (watch.component.type) { + case COUNTER_COMPONENT_TYPE_SIGNAL: + if (watch.component.owner_type != COUNTER_OWNER_TYPE_SIGNAL) + return -EINVAL; + + data_item.data.type = COUNTER_DATA_TYPE_SIGNAL; + data_item.data.signal_u8_read = counter->signal_read; + break; + case COUNTER_COMPONENT_TYPE_COUNT: + if (watch.component.owner_type != COUNTER_OWNER_TYPE_COUNT) + return -EINVAL; + + data_item.data.type = COUNTER_DATA_TYPE_U64; + data_item.data.count_u64_read = counter->count_read; + break; + case COUNTER_COMPONENT_TYPE_COUNT_FUNCTION: + if (watch.component.owner_type != COUNTER_OWNER_TYPE_COUNT) + return -EINVAL; + + data_item.data.type = COUNTER_DATA_TYPE_COUNT_FUNCTION; + data_item.data.count_u8_read = counter->function_read; + break; + case COUNTER_COMPONENT_TYPE_SYNAPSE_ACTION: + if (watch.component.owner_type != COUNTER_OWNER_TYPE_COUNT) + return -EINVAL; + if (counter->counts[owner_id].num_synapses < id + 1) + return -EINVAL; + + data_item.data.type = COUNTER_DATA_TYPE_SYNAPSE_ACTION; + data_item.data.action_read = counter->action_read; + data_item.data.priv = counter->counts[owner_id].synapses + id; + break; + case COUNTER_COMPONENT_TYPE_EXTENSION: + if (num_ext < id + 1) + return -EINVAL; + + data_item.data = ext[id]; + break; + default: + return -EINVAL; + } + if (!data_item.data.count_u8_read) + return -EFAULT; + data_item.component = watch.component; + + return counter_set_event_item(counter, watch.event, &data_item); +} + +static long counter_chrdev_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct counter_device *const counter = filp->private_data; + + switch (cmd) { + case COUNTER_CLEAR_WATCHES_IOCTL: + counter_events_list_free(counter); + break; + case COUNTER_SET_WATCH_IOCTL: + return counter_set_watch(counter, arg); + default: + return -ENOIOCTLCMD; + } + + return 0; +} + +static int counter_chrdev_open(struct inode *inode, struct file *filp) +{ + struct counter_device *const counter = container_of(inode->i_cdev, + typeof(*counter), + chrdev); + + get_device(&counter->dev); + filp->private_data = counter; + + return nonseekable_open(inode, filp); +} + +static int counter_chrdev_release(struct inode *inode, struct file *filp) +{ + struct counter_device *const counter = filp->private_data; + + put_device(&counter->dev); + + counter_events_list_free(counter); + + return 0; +} + +static const struct file_operations counter_fops = { + .llseek = no_llseek, + .read = counter_chrdev_read, + .poll = counter_chrdev_poll, + .unlocked_ioctl = counter_chrdev_ioctl, + .open = counter_chrdev_open, + .release = counter_chrdev_release, +}; + +int counter_chrdev_add(struct counter_device *const counter, + const dev_t counter_devt) +{ + struct device *const dev = &counter->dev; + struct cdev *const chrdev = &counter->chrdev; + + /* Initialize Counter events list */ + INIT_LIST_HEAD(&counter->events_list); + raw_spin_lock_init(&counter->events_lock); + + /* Initialize Counter events queue */ + INIT_KFIFO(counter->events); + init_waitqueue_head(&counter->events_wait); + + /* Initialize character device */ + cdev_init(chrdev, &counter_fops); + dev->devt = MKDEV(MAJOR(counter_devt), counter->id); + cdev_set_parent(chrdev, &dev->kobj); + + return cdev_add(chrdev, dev->devt, 1); +} + +void counter_chrdev_free(struct counter_device *const counter) +{ + cdev_del(&counter->chrdev); +} + +static int counter_get_data(struct counter_device *const counter, + const struct counter_data_item *const data_item, + void *const value) +{ + const struct counter_data *const data = &data_item->data; + void *const owner = data_item->owner; + + switch (data->type) { + case COUNTER_DATA_TYPE_U8: + case COUNTER_DATA_TYPE_BOOL: + case COUNTER_DATA_TYPE_SIGNAL: + case COUNTER_DATA_TYPE_COUNT_FUNCTION: + case COUNTER_DATA_TYPE_ENUM: + case COUNTER_DATA_TYPE_COUNT_DIRECTION: + case COUNTER_DATA_TYPE_COUNT_MODE: + switch (data_item->component.owner_type) { + case COUNTER_OWNER_TYPE_DEVICE: + return data->device_u8_read(counter, value); + case COUNTER_OWNER_TYPE_SIGNAL: + return data->signal_u8_read(counter, owner, value); + case COUNTER_OWNER_TYPE_COUNT: + return data->count_u8_read(counter, owner, value); + } + break; + case COUNTER_DATA_TYPE_U64: + switch (data_item->component.owner_type) { + case COUNTER_OWNER_TYPE_DEVICE: + return data->device_u64_read(counter, value); + case COUNTER_OWNER_TYPE_SIGNAL: + return data->signal_u64_read(counter, owner, value); + case COUNTER_OWNER_TYPE_COUNT: + return data->count_u64_read(counter, owner, value); + } + break; + case COUNTER_DATA_TYPE_SYNAPSE_ACTION: + return data->action_read(counter, owner, data->priv, value); + } + + return 0; +} + +/** + * counter_push_event - queue event for userspace reading + * @counter: pointer to Counter structure + * @event: triggered event + * + * Note: If no one is watching for the respective event, it is silently + * discarded. + * + * RETURNS: + * 0 on success, negative error number on failure. + */ +int counter_push_event(struct counter_device *const counter, const u8 event) +{ + struct counter_event ev = { .watch.event = event }; + unsigned int copied = 0; + unsigned long flags; + struct counter_event_item *event_item; + struct counter_data_item *data_item; + int err; + + ev.timestamp = ktime_get_ns(); + + raw_spin_lock_irqsave(&counter->events_lock, flags); + + /* Search for event in the list */ + list_for_each_entry(event_item, &counter->events_list, l) + if (event_item->event == event) + break; + + /* If event is not in the list */ + if (&event_item->l == &counter->events_list) + goto exit_early; + + /* Read and queue relevant data for userspace */ + list_for_each_entry(data_item, &event_item->data_list, l) { + err = counter_get_data(counter, data_item, &ev.value_u8); + if (err) + goto err_counter_get_data; + + ev.watch.component = data_item->component; + + copied += kfifo_put(&counter->events, ev); + } + + if (copied) + wake_up_poll(&counter->events_wait, EPOLLIN); + +exit_early: + raw_spin_unlock_irqrestore(&counter->events_lock, flags); + + return 0; + +err_counter_get_data: + raw_spin_unlock_irqrestore(&counter->events_lock, flags); + return err; +} +EXPORT_SYMBOL_GPL(counter_push_event); diff --git a/drivers/counter/counter-chrdev.h b/drivers/counter/counter-chrdev.h new file mode 100644 index 000000000000..7ab0797d3857 --- /dev/null +++ b/drivers/counter/counter-chrdev.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Counter character device interface + * Copyright (C) 2020 William Breathitt Gray + */ +#ifndef _COUNTER_CHRDEV_H_ +#define _COUNTER_CHRDEV_H_ + +#include +#include + +int counter_chrdev_add(struct counter_device *const counter, + const dev_t counter_devt); +void counter_chrdev_free(struct counter_device *const counter); + +#endif /* _COUNTER_CHRDEV_H_ */ diff --git a/drivers/counter/counter-core.c b/drivers/counter/counter-core.c index 499664809c75..d9ae889a0a8c 100644 --- a/drivers/counter/counter-core.c +++ b/drivers/counter/counter-core.c @@ -5,12 +5,16 @@ */ #include #include +#include #include +#include #include #include #include #include +#include +#include "counter-chrdev.h" #include "counter-sysfs.h" /* Provides a unique ID for each counter device */ @@ -33,6 +37,8 @@ static struct bus_type counter_bus_type = { .name = "counter" }; +static dev_t counter_devt; + /** * counter_register - register Counter to the system * @counter: pointer to Counter to register @@ -62,10 +68,15 @@ int counter_register(struct counter_device *const counter) device_initialize(dev); dev_set_drvdata(dev, counter); + /* Add Counter character device */ + err = counter_chrdev_add(counter, counter_devt); + if (err) + goto err_free_id; + /* Add Counter sysfs attributes */ err = counter_sysfs_add(counter); if (err) - goto err_free_id; + goto err_free_chrdev; /* Add device to system */ err = device_add(dev); @@ -76,6 +87,8 @@ int counter_register(struct counter_device *const counter) err_free_sysfs: counter_sysfs_free(counter); +err_free_chrdev: + counter_chrdev_free(counter); err_free_id: ida_simple_remove(&counter_ida, counter->id); return err; @@ -93,6 +106,7 @@ void counter_unregister(struct counter_device *const counter) if (counter) { device_del(&counter->dev); counter_sysfs_free(counter); + counter_chrdev_free(counter); } } EXPORT_SYMBOL_GPL(counter_unregister); @@ -139,13 +153,30 @@ int devm_counter_register(struct device *dev, } EXPORT_SYMBOL_GPL(devm_counter_register); +#define COUNTER_DEV_MAX 256 + static int __init counter_init(void) { - return bus_register(&counter_bus_type); + int err; + + err = bus_register(&counter_bus_type); + if (err < 0) + return err; + + err = alloc_chrdev_region(&counter_devt, 0, COUNTER_DEV_MAX, "counter"); + if (err < 0) + goto err_unregister_bus; + + return 0; + +err_unregister_bus: + bus_unregister(&counter_bus_type); + return err; } static void __exit counter_exit(void) { + unregister_chrdev_region(counter_devt, COUNTER_DEV_MAX); bus_unregister(&counter_bus_type); } diff --git a/include/linux/counter.h b/include/linux/counter.h index 76657d203a26..d4f2f2463ea3 100644 --- a/include/linux/counter.h +++ b/include/linux/counter.h @@ -6,10 +6,14 @@ #ifndef _COUNTER_H_ #define _COUNTER_H_ +#include #include #include +#include #include +#include #include +#include #include struct counter_device; @@ -166,6 +170,11 @@ struct counter_attribute_group { * @priv: optional private data supplied by driver * @id: unique ID used to identify the Counter * @dev: internal device structure + * @chrdev: internal character device structure + * @events_list: list for Counter events + * @events_lock: synchronization lock for Counter events + * @events_wait: wait queue to allow blocking reads of Counter events + * @events: queue of detected Counter events * @dynamic_names_list: list for dynamically allocated names * @groups_list: attribute groups list (for Signals, Counts, and ext) * @num_groups: number of attribute groups containers @@ -204,6 +213,11 @@ struct counter_device { int id; struct device dev; + struct cdev chrdev; + struct list_head events_list; + raw_spinlock_t events_lock; + wait_queue_head_t events_wait; + DECLARE_KFIFO(events, struct counter_event, 64); struct list_head dynamic_names_list; struct counter_attribute_group *groups_list; size_t num_groups; @@ -216,6 +230,7 @@ int devm_counter_register(struct device *dev, struct counter_device *const counter); void devm_counter_unregister(struct device *dev, struct counter_device *const counter); +int counter_push_event(struct counter_device *const counter, const u8 event); #define COUNTER_DATA_DEVICE_U8(_name, _read, _write) \ { \ diff --git a/include/uapi/linux/counter.h b/include/uapi/linux/counter.h index 2ddee9fc93e0..b903d2ad9a94 100644 --- a/include/uapi/linux/counter.h +++ b/include/uapi/linux/counter.h @@ -6,10 +6,62 @@ #ifndef _UAPI_COUNTER_H_ #define _UAPI_COUNTER_H_ +#include +#include + #define COUNTER_OWNER_TYPE_DEVICE 0 #define COUNTER_OWNER_TYPE_SIGNAL 1 #define COUNTER_OWNER_TYPE_COUNT 2 +#define COUNTER_COMPONENT_TYPE_SIGNAL 0 +#define COUNTER_COMPONENT_TYPE_COUNT 1 +#define COUNTER_COMPONENT_TYPE_COUNT_FUNCTION 2 +#define COUNTER_COMPONENT_TYPE_SYNAPSE_ACTION 3 +#define COUNTER_COMPONENT_TYPE_EXTENSION 4 + +/** + * struct counter_component - Counter component identification + * @owner_type: owner type (Device, Count, or Signal) + * @owner_id: owner identification number + * @type: component type (Count, extension, etc.) + * @id: component identification number + */ +struct counter_component { + __u8 owner_type; + __u64 owner_id; + __u8 type; + __u64 id; +}; + +/** + * struct counter_watch - Counter component watch configuration + * @event: event that triggers + * @component: component to watch when event triggers + */ +struct counter_watch { + __u8 event; + struct counter_component component; +}; + +#define COUNTER_CLEAR_WATCHES_IOCTL _IO(0x3E, 0x00) +#define COUNTER_SET_WATCH_IOCTL _IOW(0x3E, 0x01, struct counter_watch) + +/** + * struct counter_event - Counter event data + * @timestamp: best estimate of time of event occurrence, in nanoseconds + * @watch: component watch configuration + * @value_u8: component value as __u8 data type + * @value_u64: component value as __u64 data type + */ +struct counter_event { + __u64 timestamp; + struct counter_watch watch; + union { + __u8 value_u8; + __u64 value_u64; + }; +}; + #define COUNTER_COUNT_DIRECTION_FORWARD 0 #define COUNTER_COUNT_DIRECTION_BACKWARD 1 From patchwork Tue Jul 21 19:35:50 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: William Breathitt Gray X-Patchwork-Id: 11676547 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id A9B80159A for ; Tue, 21 Jul 2020 19:38:10 +0000 (UTC) Received: from merlin.infradead.org (merlin.infradead.org [205.233.59.134]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 73B0A206C1 for ; Tue, 21 Jul 2020 19:38:10 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="b1er1z2e"; dkim=fail reason="signature verification failed" (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="GUtowlPk" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 73B0A206C1 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=merlin.20170209; h=Sender:Content-Transfer-Encoding: Content-Type:Cc:List-Subscribe:List-Help:List-Post:List-Archive: List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To:Message-Id:Date: Subject:To:From:Reply-To:Content-ID:Content-Description:Resent-Date: Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=PWNBSg45aL6GbQROnErtkcCgrxb7kGnMEEkV3NU3f2U=; b=b1er1z2eByyNlUiPbjCjlMpwV I1SVZj6YDPgvt+i1MEVOEV2ADGJ5gOcWIBbKZNSM119WZjXJpNPnlSYpr1/sBbpWL0nX8va8bLD37 kUqqwLMhAUjW8hby/M7AXI6NJgVyE6U1NjAuFqQye95XwOlxTdfrF0qfDerl1VUMy4FgxtM3Jd+rH YTWobHHLykJf4n6L3X+HHry2c9Ve55Qzj/2ObIEtlExrUxsiSoun4PeGS3S+rzt6CqCA1ER2JQ31c Bo4IG4Yc/9UZLDZKzY+2IlrfsMJ4exvKrzlDaUBuVzY9cxesNVTGkmfWNafIkwAv6DSkwWq4pvAXi bHZzqoZGw==; Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.92.3 #3 (Red Hat Linux)) id 1jxy3v-00015T-9J; Tue, 21 Jul 2020 19:36:27 +0000 Received: from mail-qv1-xf41.google.com ([2607:f8b0:4864:20::f41]) by merlin.infradead.org with esmtps (Exim 4.92.3 #3 (Red Hat Linux)) id 1jxy3k-000136-QJ for linux-arm-kernel@lists.infradead.org; Tue, 21 Jul 2020 19:36:20 +0000 Received: by mail-qv1-xf41.google.com with SMTP id o2so7367613qvk.6 for ; Tue, 21 Jul 2020 12:36:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=GTQTbShVF5f+c6wukNK1yKIzK7colO0Y48fVskJS4aE=; b=GUtowlPk3wJgdTQAJUt+BchNDRUG4rnXm/tlAG1ai+m26C4yvzi1h/zqN3S0UAkDBi Q1M7grYdML3d4TDrM2IpP7r2pF8LWFIMsoij2hUczGyBg1Sy3WDLEAT0XzQTUA9NToAa z6VGe0cnT7isqdZ1jWLPKzoqWqr07keyDMMTKhekMajceZucRUDUOrEgYhYG5k9Fy9dq YfDFw+01C2pg7XFsFsNZkYMAFWipbmU4j/IGa+NtZPtvzi2vr5c2XHtrbDAT1FHLFS9T 5KzghTczdaAOtowshcYdfujxcPZ0So8tf1q2qwH+h9TqYqrnOew9gDKwrB7E0n0tm0v0 CI0w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=GTQTbShVF5f+c6wukNK1yKIzK7colO0Y48fVskJS4aE=; b=fBiGNZGz2rmoU144NpJurF+6Uke7Rh2wABFwMa/MuDkW9AIF/vvSxRyYmvh92wNSQr yHFLKYO7hX75ItzaATBObKnbJvKCREc0DiX5RyR7CKYw6fhAXRsgmyM3WvxsLIHB4cfb z9/MJmzSt2O6/gEOTtKznKuOV1M/H8huJWtN9lBSGdxy0wa03hYiGep/3MYk6od7HNYg 2zj+P9CQ0qwyNFIXtpeSwYYNUaQ8Wa0xy/5rxTNq9NcSQG6XfX/zogE6pQF1LuhYRUnS EN1g3TJ+yQG+7U0JPeWmPksl4CoQzEe4pMba7HNhb62173iLaALywhIQfhd0e9PWEvKZ o4VQ== X-Gm-Message-State: AOAM532CD+8Gkbj+o8aIOPKpqVrlEMFY3gynTB3N53eXy1UmCtzPXAna d+YPlVgBN6vQVab6JuCnOtU= X-Google-Smtp-Source: ABdhPJxcaSBOI4D/8aW82KLKn9JCSjGjSsu8kPHk1DE1l/RNeaz84H0iZSUjbA309N/npHs4UTxcKQ== X-Received: by 2002:ad4:4f4b:: with SMTP id eu11mr28692269qvb.142.1595360175095; Tue, 21 Jul 2020 12:36:15 -0700 (PDT) Received: from localhost.localdomain (072-189-064-225.res.spectrum.com. [72.189.64.225]) by smtp.gmail.com with ESMTPSA id j72sm3331351qke.20.2020.07.21.12.36.13 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 21 Jul 2020 12:36:14 -0700 (PDT) From: William Breathitt Gray To: jic23@kernel.org Subject: [PATCH v4 4/5] docs: counter: Document character device interface Date: Tue, 21 Jul 2020 15:35:50 -0400 Message-Id: X-Mailer: git-send-email 2.27.0 In-Reply-To: References: MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20200721_153616_951851_6F1D47D2 X-CRM114-Status: GOOD ( 23.39 ) X-Spam-Score: -0.2 (/) X-Spam-Report: SpamAssassin version 3.4.4 on merlin.infradead.org summary: Content analysis details: (-0.2 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at https://www.dnswl.org/, no trust [2607:f8b0:4864:20:0:0:0:f41 listed in] [list.dnswl.org] 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record 0.0 FREEMAIL_FROM Sender email is commonly abused enduser mail provider [vilhelm.gray[at]gmail.com] -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID_EF Message has a valid DKIM or DK signature from envelope-from domain X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: kamel.bouhara@bootlin.com, gwendal@chromium.org, david@lechnology.com, linux-iio@vger.kernel.org, patrick.havelange@essensium.com, alexandre.belloni@bootlin.com, linux-kernel@vger.kernel.org, mcoquelin.stm32@gmail.com, William Breathitt Gray , fabrice.gasnier@st.com, syednwaris@gmail.com, linux-stm32@st-md-mailman.stormreply.com, linux-arm-kernel@lists.infradead.org, alexandre.torgue@st.com Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org This patch adds high-level documentation about the Counter subsystem character device interface. Signed-off-by: William Breathitt Gray --- Documentation/driver-api/generic-counter.rst | 191 +++++++++++++++--- .../userspace-api/ioctl/ioctl-number.rst | 1 + 2 files changed, 160 insertions(+), 32 deletions(-) diff --git a/Documentation/driver-api/generic-counter.rst b/Documentation/driver-api/generic-counter.rst index fa2d699d44a5..a5f2e8dc430c 100644 --- a/Documentation/driver-api/generic-counter.rst +++ b/Documentation/driver-api/generic-counter.rst @@ -223,19 +223,6 @@ whether an input line is differential or single-ended) and instead focus on the core idea of what the data and process represent (e.g. position as interpreted from quadrature encoding data). -Userspace Interface -=================== - -Several sysfs attributes are generated by the Generic Counter interface, -and reside under the /sys/bus/counter/devices/counterX directory, where -counterX refers to the respective counter device. Please see -Documentation/ABI/testing/sysfs-bus-counter for detailed -information on each Generic Counter interface sysfs attribute. - -Through these sysfs attributes, programs and scripts may interact with -the Generic Counter paradigm Counts, Signals, and Synapses of respective -counter devices. - Driver API ========== @@ -399,25 +386,32 @@ driver is exemplified by the following:: | / driver callbacks / | ------------------- | | - +-------+ | + +-------+---------------+ | + | | | + | +-------|-------+ + | | | + V | V + +--------------------+ | +---------------------+ + | Counter sysfs |<-+->| Counter chrdev | + +--------------------+ +---------------------+ + | Translates to the | | Translates to the | + | standard Counter | | standard Counter | + | sysfs output | | character device | + |--------------------| |---------------------+ + | Type: const char * | | Type: u64 | + | Value: "42" | | Value: 42 | + +--------------------+ +---------------------+ | | - | +---------------+ - | | - V | - +--------------------+ | - | Counter sysfs |<-+ - +--------------------+ - | Translates to the | - | standard Counter | - | sysfs output | - |--------------------| - | Type: const char * | - | Value: "42" | - +--------------------+ - | - --------------- - / const char * / - --------------- + --------------- ----------------------- + / const char * / / struct counter_event / + --------------- ----------------------- + | | + | V + | +-----------+ + | | read | + | +-----------+ + | \ Count: 42 / + | ----------- | V +--------------------------------------------------+ @@ -426,7 +420,7 @@ driver is exemplified by the following:: \ Count: "42" / -------------------------------------------------- -There are three primary components involved: +There are four primary components involved: Counter device driver --------------------- @@ -446,3 +440,136 @@ and vice versa. Please refer to the `Documentation/ABI/testing/sysfs-bus-counter` file for a detailed breakdown of the available Generic Counter interface sysfs attributes. + +Counter chrdev +-------------- +Translates counter data to the standard Counter character device; data +is transferred via standard character device read calls, while Counter +events are configured via ioctl calls. + +Sysfs Interface +=============== + +Several sysfs attributes are generated by the Generic Counter interface, +and reside under the `/sys/bus/counter/devices/counterX` directory, +where `X` is to the respective counter device id. Please see +`Documentation/ABI/testing/sysfs-bus-counter` for detailed information +on each Generic Counter interface sysfs attribute. + +Through these sysfs attributes, programs and scripts may interact with +the Generic Counter paradigm Counts, Signals, and Synapses of respective +counter devices. + +Counter Character Device +======================== + +Counter character device nodes are created under the `/dev` directory as +`counterX`, where `X` is the respective counter device id. Defines for +the standard Counter data types are exposed via the userspace +`include/uapi/linux/counter.h` file. + +Counter events +-------------- +Counter device drivers can support Counter events by utilizing the +`counter_push_event` function:: + + int counter_push_event(struct counter_device *const counter, const u8 event); + +The event id is specified by the `event` parameter. When this function +is called, the Counter data associated with the respective event is +gathered, and a `struct counter_event` is generated for each datum and +pushed to userspace. + +Counter events can be configured by users to report various Counter +data of interest. This can be conceptualized as a list of Counter +component read calls to perform. For example:: + + +------------------------+------------------------+ + | Event 0 | Event 1 | + +------------------------+------------------------+ + | * Count 0 | * Signal 0 | + | * Count 1 | * Signal 0 Extension 0 | + | * Signal 3 | * Extension 4 | + | * Count 4 Extension 2 | | + | * Signal 5 Extension 0 | | + +------------------------+------------------------+ + +When `counter_push_event(counter, 1)` is called for example, it will go +down the list for Event 1 and execute the read callbacks for Signal 0, +Signal 0 Extension 0, and Extension 4 -- the data returned for each is +pushed to a kfifo as a `struct counter_event`, which userspace can +retrieve via a standard read operation on the respective character +device node. + +Userspace +--------- +Userspace applications can configure Counter events via ioctl operations +on the Counter character device node. There following ioctl codes are +supported and provided by the `linux/counter.h` userspace header file: + +* COUNTER_CLEAR_WATCHES_IOCTL: + Clear all Counter watches from all events + +* COUNTER_SET_WATCH_IOCTL: + Set a Counter watch on the specified event + +To configure events to gather Counter data, users first populate a +`struct counter_watch` with the relevant event id and the information +for the desired Counter component from which to read, and then pass it +via the `COUNTER_SET_WATCH_IOCTL` ioctl command. + +Userspace applications can then execute a `read` operation (optionally +calling `poll` first) on the Counter character device node to retrieve +`struct counter_event` elements with the desired data. + +For example, the following userspace code opens `/dev/counter0`, +configures Event 0 to gather Count 0 and Count 1, and prints out the +data as it becomes available on the character device node:: + + #include + #include + #include + #include + #include + #include + + struct counter_watch watches[2] = { + { + .event = 0, + .component.owner_type = COUNTER_OWNER_TYPE_COUNT, + .component.owner_id = 0, + .component.type = COUNTER_COMPONENT_TYPE_COUNT, + }, + { + .event = 0, + .component.owner_type = COUNTER_OWNER_TYPE_COUNT, + .component.owner_id = 1, + .component.type = COUNTER_COMPONENT_TYPE_COUNT, + }, + }; + + int main(void) + { + struct pollfd pfd = { .events = POLLIN }; + struct counter_event event_data[2]; + + pfd.fd = open("/dev/counter0", O_RDWR); + + ioctl(pfd.fd, COUNTER_SET_WATCH_IOCTL, watches); + ioctl(pfd.fd, COUNTER_SET_WATCH_IOCTL, watches + 1); + + for (;;) { + poll(&pfd, 1, -1); + + read(pfd.fd, event_data, sizeof(event_data)); + + printf("Timestamp 0: %llu\nCount 0: %llu\n" + "Timestamp 1: %llu\nCount 1: %llu\n", + (unsigned long long)event_data[0].timestamp, + (unsigned long long)event_data[0].value_u64, + (unsigned long long)event_data[1].timestamp, + (unsigned long long)event_data[1].value_u64); + } + + return 0; + } diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst index 59472cd6a11d..63ff377561fd 100644 --- a/Documentation/userspace-api/ioctl/ioctl-number.rst +++ b/Documentation/userspace-api/ioctl/ioctl-number.rst @@ -88,6 +88,7 @@ Code Seq# Include File Comments 0x20 all drivers/cdrom/cm206.h 0x22 all scsi/sg.h +0x3E 00-0F linux/counter.h '!' 00-1F uapi/linux/seccomp.h '#' 00-3F IEEE 1394 Subsystem Block for the entire subsystem From patchwork Tue Jul 21 19:35:51 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: William Breathitt Gray X-Patchwork-Id: 11676553 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id C346813B1 for ; Tue, 21 Jul 2020 19:38:13 +0000 (UTC) Received: from merlin.infradead.org (merlin.infradead.org [205.233.59.134]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 8D38E206C1 for ; Tue, 21 Jul 2020 19:38:13 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="pcJWB/qU"; dkim=fail reason="signature verification failed" (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="BwkCv2NA" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 8D38E206C1 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=merlin.20170209; h=Sender:Content-Transfer-Encoding: Content-Type:Cc:List-Subscribe:List-Help:List-Post:List-Archive: List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To:Message-Id:Date: Subject:To:From:Reply-To:Content-ID:Content-Description:Resent-Date: Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=WlsTkocycELOCcMnz34pesRL8imO9obGiLZRyRkqFlg=; b=pcJWB/qUociKl/xthfg76wvJv LYrzlUDP4GJGbosGc6CtHFKFCm6ivCg/J4mhs28BE3OKZis8WPy/UfF43e1FOP5P4UKfx/voOM8OL RsvMUCd15TSxdNaw3sPFXy4oLx2MoMolNlmOuFO9P+8QhNmg3SlVYS+HqY9S85yaOfoI9xsHPCydP +YnXlep4Buy/qC3t1+zDcLti9f3veH7Di126B6LynGwjXx70WN2MvQjKO+JyUY3l/+v8dVWW1STni CibMDR0UR6/exRxn6m68zsmJU8th6pe3+R8BXtuIwog1gU99WybttDtuTJMliZhFeatNUze8Q3qnJ TAHYGE43A==; Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.92.3 #3 (Red Hat Linux)) id 1jxy3z-00016N-FV; Tue, 21 Jul 2020 19:36:31 +0000 Received: from mail-qk1-x744.google.com ([2607:f8b0:4864:20::744]) by merlin.infradead.org with esmtps (Exim 4.92.3 #3 (Red Hat Linux)) id 1jxy3m-00013b-6B for linux-arm-kernel@lists.infradead.org; Tue, 21 Jul 2020 19:36:24 +0000 Received: by mail-qk1-x744.google.com with SMTP id g26so10383778qka.3 for ; Tue, 21 Jul 2020 12:36:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=tpbV9uh2Ik+YDykdy9+G9J7XXK+IXKQ2w3m8jQT6aas=; b=BwkCv2NAVZaZlxQTuEL2gB2Q6CbbJ4VxdjPPn+zaamuSBWrq0R3YIYLTvfTRci4OG4 WNHOoMDX2icRoS7KHmDV8bU3s6SVkEo9iiEs+95QrN7mV0RpD42oSrGOnKEJatLzfajx TU+scHXr9yCIkX6atf5YPAhA2/xYU4wvGrMRp5uaMormlusdjunk3ZmnZDldTsB9Qu5e HurQla+2L+zxZoUYHvmpBUFKlCRPWt8lLZ8N6D1FHXPxAj0WcN1G8dc/cKsRa1LuXr0J kuiH9YdgzcJFx7g940+HEy1+Q8uK+RZzADRiHpy96joLVF+8e/3ZfLLNvPL/viMn2Cw8 jYDg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=tpbV9uh2Ik+YDykdy9+G9J7XXK+IXKQ2w3m8jQT6aas=; b=ROujvdWIHf945ObRri8TMW3sSNeNq/g6MULV5+JizXJpLtz0AgLFNuHzh8OoxWN3El njluboqrzZDdLIlwDO1A/N1Zt7nsOwMM1P7/m8JqbwS6/eLpQVejL0dS+ogdJjW/0QFw 6leBOYdmIGaqDsPA7TN3BWQY3W7Tb2Gf/ILbLjSR1NHDtPlmXilVPM5nO5uBqI7DF3+6 2ONHvw4o+/YhBGtQJ5cWPEUpmE/n3bUQtQ8uOK8WGvfprmvK2V+9DP5Hiyu8qLS7mjI9 0do5GBvypsWgr1ht1dQH0UkIOjxz7I4LyXxE2dGAspq+6whWbRTz8Mq6x0LCWZCsO43i UNPA== X-Gm-Message-State: AOAM531/9Vf9JkAYwhFMtGUfKrSb9Goy2kd518YhQcRT77yclcVM63Me oC9a2sRu9TgsGT1Xw05iWOQ= X-Google-Smtp-Source: ABdhPJyzn4js74+1vAfLlj+tb1UR4/vT5l1PjxM6rQpyFghCKrqL+Vf/vhVH1vTHNuNssE452An9EA== X-Received: by 2002:a37:bb45:: with SMTP id l66mr21185209qkf.434.1595360176624; Tue, 21 Jul 2020 12:36:16 -0700 (PDT) Received: from localhost.localdomain (072-189-064-225.res.spectrum.com. [72.189.64.225]) by smtp.gmail.com with ESMTPSA id j72sm3331351qke.20.2020.07.21.12.36.15 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 21 Jul 2020 12:36:15 -0700 (PDT) From: William Breathitt Gray To: jic23@kernel.org Subject: [PATCH v4 5/5] counter: 104-quad-8: Add IRQ support for the ACCES 104-QUAD-8 Date: Tue, 21 Jul 2020 15:35:51 -0400 Message-Id: X-Mailer: git-send-email 2.27.0 In-Reply-To: References: MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20200721_153618_609411_5A610F59 X-CRM114-Status: GOOD ( 20.59 ) X-Spam-Score: -0.2 (/) X-Spam-Report: SpamAssassin version 3.4.4 on merlin.infradead.org summary: Content analysis details: (-0.2 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at https://www.dnswl.org/, no trust [2607:f8b0:4864:20:0:0:0:744 listed in] [list.dnswl.org] 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record 0.0 FREEMAIL_FROM Sender email is commonly abused enduser mail provider [vilhelm.gray[at]gmail.com] -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID_EF Message has a valid DKIM or DK signature from envelope-from domain X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: kamel.bouhara@bootlin.com, gwendal@chromium.org, david@lechnology.com, linux-iio@vger.kernel.org, patrick.havelange@essensium.com, alexandre.belloni@bootlin.com, linux-kernel@vger.kernel.org, mcoquelin.stm32@gmail.com, William Breathitt Gray , fabrice.gasnier@st.com, syednwaris@gmail.com, linux-stm32@st-md-mailman.stormreply.com, linux-arm-kernel@lists.infradead.org, alexandre.torgue@st.com Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org The LSI/CSI LS7266R1 chip provides programmable output via the FLG pins. When interrupts are enabled on the ACCES 104-QUAD-8, they occur whenever FLG1 is active. Four functions are available for the FLG1 signal: Carry, Compare, Carry-Borrow, and Index. Carry: Interrupt generated on active low Carry signal. Carry signal toggles every time the respective channel's counter overflows. Compare: Interrupt generated on active low Compare signal. Compare signal toggles every time respective channel's preset register is equal to the respective channel's counter. Carry-Borrow: Interrupt generated on active low Carry signal and active low Borrow signal. Carry signal toggles every time the respective channel's counter overflows. Borrow signal toggles every time the respective channel's counter underflows. Index: Interrupt generated on active high Index signal. The irq_trigger Count extension is introduced to allow the selection of the desired IRQ trigger function per channel. The irq_trigger_enable Count extension is introduced to allow the enablement of interrupts for a respective channel. Interrupts push Counter events as Event X, where 'X' is the respective channel whose FLG1 activated. This patch adds IRQ support for the ACCES 104-QUAD-8. The interrupt line numbers for the devices may be configured via the irq array module parameter. Cc: Syed Nayyar Waris Signed-off-by: William Breathitt Gray Reviewed-by: Syed Nayyar Waris --- .../ABI/testing/sysfs-bus-counter-104-quad-8 | 32 ++ drivers/counter/104-quad-8.c | 283 +++++++++++++----- drivers/counter/Kconfig | 6 +- 3 files changed, 249 insertions(+), 72 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-bus-counter-104-quad-8 b/Documentation/ABI/testing/sysfs-bus-counter-104-quad-8 index eac32180c40d..718f6199c71e 100644 --- a/Documentation/ABI/testing/sysfs-bus-counter-104-quad-8 +++ b/Documentation/ABI/testing/sysfs-bus-counter-104-quad-8 @@ -1,3 +1,35 @@ +What: /sys/bus/counter/devices/counterX/countY/irq_trigger +KernelVersion: 5.9 +Contact: linux-iio@vger.kernel.org +Description: + IRQ trigger function for channel Y. Four trigger functions are + available: carry, compare, carry-borrow, and index. + + carry: + Interrupt generated on active low Carry signal. Carry + signal toggles every time channel Y counter overflows. + + compare: + Interrupt generated on active low Compare signal. + Compare signal toggles every time channel Y preset + register is equal to channel Y counter. + + carry-borrow: + Interrupt generated on active low Carry signal and + active low Borrow signal. Carry signal toggles every + time channel Y counter overflows. Borrow signal toggles + every time channel Y counter underflows. + + index: + Interrupt generated on active high Index signal. + +What: /sys/bus/counter/devices/counterX/countY/irq_trigger_enable +KernelVersion: 5.9 +Contact: linux-iio@vger.kernel.org +Description: + Whether generation of interrupts is enabled for channel Y. Valid + attribute values are boolean. + What: /sys/bus/counter/devices/counterX/signalY/cable_fault KernelVersion: 5.7 Contact: linux-iio@vger.kernel.org diff --git a/drivers/counter/104-quad-8.c b/drivers/counter/104-quad-8.c index 0f20920073d6..b43be2d5464d 100644 --- a/drivers/counter/104-quad-8.c +++ b/drivers/counter/104-quad-8.c @@ -13,23 +13,30 @@ #include #include #include +#include #include #include #include #include #include +#include #define QUAD8_EXTENT 32 static unsigned int base[max_num_isa_dev(QUAD8_EXTENT)]; static unsigned int num_quad8; -module_param_array(base, uint, &num_quad8, 0); +module_param_hw_array(base, uint, ioport, &num_quad8, 0); MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses"); +static unsigned int irq[max_num_isa_dev(QUAD8_EXTENT)]; +module_param_hw_array(irq, uint, irq, NULL, 0); +MODULE_PARM_DESC(irq, "ACCES 104-QUAD-8 interrupt line numbers"); + #define QUAD8_NUM_COUNTERS 8 /** * struct quad8_iio - IIO device private data structure + * @lock: synchronization lock to prevent I/O race conditions * @counter: instance of the counter_device * @fck_prescaler: array of filter clock prescaler configurations * @preset: array of preset values @@ -38,13 +45,14 @@ MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses"); * @quadrature_scale: array of quadrature mode scale configurations * @ab_enable: array of A and B inputs enable configurations * @preset_enable: array of set_to_preset_on_index attribute configurations + * @irq_trigger: array of interrupt trigger function configurations * @synchronous_mode: array of index function synchronous mode configurations * @index_polarity: array of index function polarity configurations * @cable_fault_enable: differential encoder cable status enable configurations * @base: base port address of the IIO device */ struct quad8_iio { - struct mutex lock; + raw_spinlock_t lock; struct counter_device counter; unsigned int fck_prescaler[QUAD8_NUM_COUNTERS]; unsigned int preset[QUAD8_NUM_COUNTERS]; @@ -53,13 +61,16 @@ struct quad8_iio { unsigned int quadrature_scale[QUAD8_NUM_COUNTERS]; unsigned int ab_enable[QUAD8_NUM_COUNTERS]; unsigned int preset_enable[QUAD8_NUM_COUNTERS]; + unsigned int irq_trigger[QUAD8_NUM_COUNTERS]; unsigned int synchronous_mode[QUAD8_NUM_COUNTERS]; unsigned int index_polarity[QUAD8_NUM_COUNTERS]; unsigned int cable_fault_enable; unsigned int base; }; +#define QUAD8_REG_INTERRUPT_STATUS 0x10 #define QUAD8_REG_CHAN_OP 0x11 +#define QUAD8_REG_INDEX_INTERRUPT 0x12 #define QUAD8_REG_INDEX_INPUT_LEVELS 0x16 #define QUAD8_DIFF_ENCODER_CABLE_STATUS 0x17 /* Borrow Toggle flip-flop */ @@ -92,8 +103,8 @@ struct quad8_iio { #define QUAD8_RLD_CNTR_OUT 0x10 /* Transfer Preset Register LSB to FCK Prescaler */ #define QUAD8_RLD_PRESET_PSC 0x18 -#define QUAD8_CHAN_OP_ENABLE_COUNTERS 0x00 #define QUAD8_CHAN_OP_RESET_COUNTERS 0x01 +#define QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC 0x04 #define QUAD8_CMR_QUADRATURE_X1 0x08 #define QUAD8_CMR_QUADRATURE_X2 0x10 #define QUAD8_CMR_QUADRATURE_X4 0x18 @@ -107,6 +118,7 @@ static int quad8_read_raw(struct iio_dev *indio_dev, unsigned int flags; unsigned int borrow; unsigned int carry; + unsigned long irqflags; int i; switch (mask) { @@ -124,7 +136,7 @@ static int quad8_read_raw(struct iio_dev *indio_dev, /* Borrow XOR Carry effectively doubles count range */ *val = (borrow ^ carry) << 24; - mutex_lock(&priv->lock); + raw_spin_lock_irqsave(&priv->lock, irqflags); /* Reset Byte Pointer; transfer Counter to Output Latch */ outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_CNTR_OUT, @@ -133,7 +145,7 @@ static int quad8_read_raw(struct iio_dev *indio_dev, for (i = 0; i < 3; i++) *val |= (unsigned int)inb(base_offset) << (8 * i); - mutex_unlock(&priv->lock); + raw_spin_unlock_irqrestore(&priv->lock, irqflags); return IIO_VAL_INT; case IIO_CHAN_INFO_ENABLE: @@ -153,6 +165,7 @@ static int quad8_write_raw(struct iio_dev *indio_dev, { struct quad8_iio *const priv = iio_priv(indio_dev); const int base_offset = priv->base + 2 * chan->channel; + unsigned long flags; int i; unsigned int ior_cfg; @@ -165,7 +178,7 @@ static int quad8_write_raw(struct iio_dev *indio_dev, if ((unsigned int)val > 0xFFFFFF) return -EINVAL; - mutex_lock(&priv->lock); + raw_spin_lock_irqsave(&priv->lock, flags); /* Reset Byte Pointer */ outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1); @@ -190,7 +203,7 @@ static int quad8_write_raw(struct iio_dev *indio_dev, /* Reset Error flag */ outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, base_offset + 1); - mutex_unlock(&priv->lock); + raw_spin_unlock_irqrestore(&priv->lock, flags); return 0; case IIO_CHAN_INFO_ENABLE: @@ -198,25 +211,26 @@ static int quad8_write_raw(struct iio_dev *indio_dev, if (val < 0 || val > 1) return -EINVAL; - mutex_lock(&priv->lock); + raw_spin_lock_irqsave(&priv->lock, flags); priv->ab_enable[chan->channel] = val; - ior_cfg = val | priv->preset_enable[chan->channel] << 1; + ior_cfg = val | priv->preset_enable[chan->channel] << 1 | + priv->irq_trigger[chan->channel] << 3; /* Load I/O control configuration */ outb(QUAD8_CTR_IOR | ior_cfg, base_offset + 1); - mutex_unlock(&priv->lock); + raw_spin_unlock_irqrestore(&priv->lock, flags); return 0; case IIO_CHAN_INFO_SCALE: - mutex_lock(&priv->lock); + raw_spin_lock_irqsave(&priv->lock, flags); /* Quadrature scaling only available in quadrature mode */ if (!priv->quadrature_mode[chan->channel] && (val2 || val != 1)) { - mutex_unlock(&priv->lock); + raw_spin_unlock_irqrestore(&priv->lock, flags); return -EINVAL; } @@ -232,15 +246,15 @@ static int quad8_write_raw(struct iio_dev *indio_dev, priv->quadrature_scale[chan->channel] = 2; break; default: - mutex_unlock(&priv->lock); + raw_spin_unlock_irqrestore(&priv->lock, flags); return -EINVAL; } else { - mutex_unlock(&priv->lock); + raw_spin_unlock_irqrestore(&priv->lock, flags); return -EINVAL; } - mutex_unlock(&priv->lock); + raw_spin_unlock_irqrestore(&priv->lock, flags); return 0; } @@ -266,6 +280,7 @@ static ssize_t quad8_write_preset(struct iio_dev *indio_dev, uintptr_t private, struct quad8_iio *const priv = iio_priv(indio_dev); const int base_offset = priv->base + 2 * chan->channel; unsigned int preset; + unsigned long irqflags; int ret; int i; @@ -277,7 +292,7 @@ static ssize_t quad8_write_preset(struct iio_dev *indio_dev, uintptr_t private, if (preset > 0xFFFFFF) return -EINVAL; - mutex_lock(&priv->lock); + raw_spin_lock_irqsave(&priv->lock, irqflags); priv->preset[chan->channel] = preset; @@ -288,7 +303,7 @@ static ssize_t quad8_write_preset(struct iio_dev *indio_dev, uintptr_t private, for (i = 0; i < 3; i++) outb(preset >> (8 * i), base_offset); - mutex_unlock(&priv->lock); + raw_spin_unlock_irqrestore(&priv->lock, irqflags); return len; } @@ -309,6 +324,7 @@ static ssize_t quad8_write_set_to_preset_on_index(struct iio_dev *indio_dev, struct quad8_iio *const priv = iio_priv(indio_dev); const int base_offset = priv->base + 2 * chan->channel + 1; bool preset_enable; + unsigned long irqflags; int ret; unsigned int ior_cfg; @@ -319,17 +335,18 @@ static ssize_t quad8_write_set_to_preset_on_index(struct iio_dev *indio_dev, /* Preset enable is active low in Input/Output Control register */ preset_enable = !preset_enable; - mutex_lock(&priv->lock); + raw_spin_lock_irqsave(&priv->lock, irqflags); priv->preset_enable[chan->channel] = preset_enable; ior_cfg = priv->ab_enable[chan->channel] | - (unsigned int)preset_enable << 1; + (unsigned int)preset_enable << 1 | + priv->irq_trigger[chan->channel] << 3; /* Load I/O control configuration to Input / Output Control Register */ outb(QUAD8_CTR_IOR | ior_cfg, base_offset); - mutex_unlock(&priv->lock); + raw_spin_unlock_irqrestore(&priv->lock, irqflags); return len; } @@ -387,8 +404,9 @@ static int quad8_set_count_mode(struct iio_dev *indio_dev, struct quad8_iio *const priv = iio_priv(indio_dev); unsigned int mode_cfg = cnt_mode << 1; const int base_offset = priv->base + 2 * chan->channel + 1; + unsigned long irqflags; - mutex_lock(&priv->lock); + raw_spin_lock_irqsave(&priv->lock, irqflags); priv->count_mode[chan->channel] = cnt_mode; @@ -399,7 +417,7 @@ static int quad8_set_count_mode(struct iio_dev *indio_dev, /* Load mode configuration to Counter Mode Register */ outb(QUAD8_CTR_CMR | mode_cfg, base_offset); - mutex_unlock(&priv->lock); + raw_spin_unlock_irqrestore(&priv->lock, irqflags); return 0; } @@ -430,14 +448,15 @@ static int quad8_set_synchronous_mode(struct iio_dev *indio_dev, struct quad8_iio *const priv = iio_priv(indio_dev); const int base_offset = priv->base + 2 * chan->channel + 1; unsigned int idr_cfg = synchronous_mode; + unsigned long irqflags; - mutex_lock(&priv->lock); + raw_spin_lock_irqsave(&priv->lock, irqflags); idr_cfg |= priv->index_polarity[chan->channel] << 1; /* Index function must be non-synchronous in non-quadrature mode */ if (synchronous_mode && !priv->quadrature_mode[chan->channel]) { - mutex_unlock(&priv->lock); + raw_spin_unlock_irqrestore(&priv->lock, irqflags); return -EINVAL; } @@ -446,7 +465,7 @@ static int quad8_set_synchronous_mode(struct iio_dev *indio_dev, /* Load Index Control configuration to Index Control Register */ outb(QUAD8_CTR_IDR | idr_cfg, base_offset); - mutex_unlock(&priv->lock); + raw_spin_unlock_irqrestore(&priv->lock, irqflags); return 0; } @@ -476,9 +495,10 @@ static int quad8_set_quadrature_mode(struct iio_dev *indio_dev, { struct quad8_iio *const priv = iio_priv(indio_dev); const int base_offset = priv->base + 2 * chan->channel + 1; + unsigned long irqflags; unsigned int mode_cfg; - mutex_lock(&priv->lock); + raw_spin_lock_irqsave(&priv->lock, irqflags); mode_cfg = priv->count_mode[chan->channel] << 1; @@ -498,7 +518,7 @@ static int quad8_set_quadrature_mode(struct iio_dev *indio_dev, /* Load mode configuration to Counter Mode Register */ outb(QUAD8_CTR_CMR | mode_cfg, base_offset); - mutex_unlock(&priv->lock); + raw_spin_unlock_irqrestore(&priv->lock, irqflags); return 0; } @@ -529,8 +549,9 @@ static int quad8_set_index_polarity(struct iio_dev *indio_dev, struct quad8_iio *const priv = iio_priv(indio_dev); const int base_offset = priv->base + 2 * chan->channel + 1; unsigned int idr_cfg = index_polarity << 1; + unsigned long irqflags; - mutex_lock(&priv->lock); + raw_spin_lock_irqsave(&priv->lock, irqflags); idr_cfg |= priv->synchronous_mode[chan->channel]; @@ -539,7 +560,7 @@ static int quad8_set_index_polarity(struct iio_dev *indio_dev, /* Load Index Control configuration to Index Control Register */ outb(QUAD8_CTR_IDR | idr_cfg, base_offset); - mutex_unlock(&priv->lock); + raw_spin_unlock_irqrestore(&priv->lock, irqflags); return 0; } @@ -646,6 +667,7 @@ static int quad8_count_read(struct counter_device *counter, unsigned int flags; unsigned int borrow; unsigned int carry; + unsigned long irqflags; int i; flags = inb(base_offset + 1); @@ -655,7 +677,7 @@ static int quad8_count_read(struct counter_device *counter, /* Borrow XOR Carry effectively doubles count range */ *val = (unsigned long)(borrow ^ carry) << 24; - mutex_lock(&priv->lock); + raw_spin_lock_irqsave(&priv->lock, irqflags); /* Reset Byte Pointer; transfer Counter to Output Latch */ outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_CNTR_OUT, @@ -664,7 +686,7 @@ static int quad8_count_read(struct counter_device *counter, for (i = 0; i < 3; i++) *val |= (unsigned long)inb(base_offset) << (8 * i); - mutex_unlock(&priv->lock); + raw_spin_unlock_irqrestore(&priv->lock, irqflags); return 0; } @@ -674,13 +696,14 @@ static int quad8_count_write(struct counter_device *counter, { struct quad8_iio *const priv = counter->priv; const int base_offset = priv->base + 2 * count->id; + unsigned long irqflags; int i; /* Only 24-bit values are supported */ if (val > 0xFFFFFF) return -EINVAL; - mutex_lock(&priv->lock); + raw_spin_lock_irqsave(&priv->lock, irqflags); /* Reset Byte Pointer */ outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1); @@ -705,7 +728,7 @@ static int quad8_count_write(struct counter_device *counter, /* Reset Error flag */ outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, base_offset + 1); - mutex_unlock(&priv->lock); + raw_spin_unlock_irqrestore(&priv->lock, irqflags); return 0; } @@ -723,8 +746,9 @@ static int quad8_function_read(struct counter_device *counter, { struct quad8_iio *const priv = counter->priv; const int id = count->id; + unsigned long irqflags; - mutex_lock(&priv->lock); + raw_spin_lock_irqsave(&priv->lock, irqflags); if (priv->quadrature_mode[id]) switch (priv->quadrature_scale[id]) { @@ -741,7 +765,7 @@ static int quad8_function_read(struct counter_device *counter, else *function = COUNTER_COUNT_FUNCTION_PULSE_DIRECTION; - mutex_unlock(&priv->lock); + raw_spin_unlock_irqrestore(&priv->lock, irqflags); return 0; } @@ -755,10 +779,11 @@ static int quad8_function_write(struct counter_device *counter, unsigned int *const scale = priv->quadrature_scale + id; unsigned int *const synchronous_mode = priv->synchronous_mode + id; const int base_offset = priv->base + 2 * id + 1; + unsigned long irqflags; unsigned int mode_cfg; unsigned int idr_cfg; - mutex_lock(&priv->lock); + raw_spin_lock_irqsave(&priv->lock, irqflags); mode_cfg = priv->count_mode[id] << 1; idr_cfg = priv->index_polarity[id] << 1; @@ -797,7 +822,7 @@ static int quad8_function_write(struct counter_device *counter, /* Load mode configuration to Counter Mode Register */ outb(QUAD8_CTR_CMR | mode_cfg, base_offset); - mutex_unlock(&priv->lock); + raw_spin_unlock_irqrestore(&priv->lock, irqflags); return 0; } @@ -906,9 +931,10 @@ static int quad8_index_polarity_set(struct counter_device *counter, struct quad8_iio *const priv = counter->priv; const size_t channel_id = signal->id - 16; const int base_offset = priv->base + 2 * channel_id + 1; + unsigned long irqflags; unsigned int idr_cfg; - mutex_lock(&priv->lock); + raw_spin_lock_irqsave(&priv->lock, irqflags); priv->index_polarity[channel_id] = index_polarity; @@ -916,7 +942,7 @@ static int quad8_index_polarity_set(struct counter_device *counter, idr_cfg = priv->synchronous_mode[channel_id] | index_polarity << 1; outb(QUAD8_CTR_IDR | idr_cfg, base_offset); - mutex_unlock(&priv->lock); + raw_spin_unlock_irqrestore(&priv->lock, irqflags); return 0; } @@ -940,13 +966,14 @@ static int quad8_synchronous_mode_set(struct counter_device *counter, struct quad8_iio *const priv = counter->priv; const size_t channel_id = signal->id - 16; const int base_offset = priv->base + 2 * channel_id + 1; + unsigned long irqflags; unsigned int idr_cfg; - mutex_lock(&priv->lock); + raw_spin_lock_irqsave(&priv->lock, irqflags); /* Index function must be non-synchronous in non-quadrature mode */ if (synchronous_mode && !priv->quadrature_mode[channel_id]) { - mutex_unlock(&priv->lock); + raw_spin_unlock_irqrestore(&priv->lock, irqflags); return -EINVAL; } @@ -956,7 +983,7 @@ static int quad8_synchronous_mode_set(struct counter_device *counter, idr_cfg = synchronous_mode | priv->index_polarity[channel_id] << 1; outb(QUAD8_CTR_IDR | idr_cfg, base_offset); - mutex_unlock(&priv->lock); + raw_spin_unlock_irqrestore(&priv->lock, irqflags); return 0; } @@ -1001,6 +1028,7 @@ static int quad8_count_mode_write(struct counter_device *counter, unsigned int count_mode; unsigned int mode_cfg; const int base_offset = priv->base + 2 * count->id + 1; + unsigned long irqflags; /* Map Generic Counter count mode to 104-QUAD-8 count mode */ switch (cnt_mode) { @@ -1018,7 +1046,7 @@ static int quad8_count_mode_write(struct counter_device *counter, break; } - mutex_lock(&priv->lock); + raw_spin_lock_irqsave(&priv->lock, irqflags); priv->count_mode[count->id] = count_mode; @@ -1032,7 +1060,7 @@ static int quad8_count_mode_write(struct counter_device *counter, /* Load mode configuration to Counter Mode Register */ outb(QUAD8_CTR_CMR | mode_cfg, base_offset); - mutex_unlock(&priv->lock); + raw_spin_unlock_irqrestore(&priv->lock, irqflags); return 0; } @@ -1052,18 +1080,20 @@ static int quad8_count_enable_write(struct counter_device *counter, { struct quad8_iio *const priv = counter->priv; const int base_offset = priv->base + 2 * count->id; + unsigned long irqflags; unsigned int ior_cfg; - mutex_lock(&priv->lock); + raw_spin_lock_irqsave(&priv->lock, irqflags); priv->ab_enable[count->id] = enable; - ior_cfg = enable | priv->preset_enable[count->id] << 1; + ior_cfg = enable | priv->preset_enable[count->id] << 1 | + priv->irq_trigger[count->id] << 3; /* Load I/O control configuration */ outb(QUAD8_CTR_IOR | ior_cfg, base_offset + 1); - mutex_unlock(&priv->lock); + raw_spin_unlock_irqrestore(&priv->lock, irqflags); return 0; } @@ -1109,16 +1139,17 @@ static int quad8_count_preset_write(struct counter_device *counter, struct counter_count *count, u64 preset) { struct quad8_iio *const priv = counter->priv; + unsigned long irqflags; /* Only 24-bit values are supported */ if (preset > 0xFFFFFF) return -EINVAL; - mutex_lock(&priv->lock); + raw_spin_lock_irqsave(&priv->lock, irqflags); quad8_preset_register_set(priv, count->id, preset); - mutex_unlock(&priv->lock); + raw_spin_unlock_irqrestore(&priv->lock, irqflags); return 0; } @@ -1127,18 +1158,19 @@ static int quad8_count_ceiling_read(struct counter_device *counter, struct counter_count *count, u64 *ceiling) { struct quad8_iio *const priv = counter->priv; + unsigned long irqflags; - mutex_lock(&priv->lock); + raw_spin_lock_irqsave(&priv->lock, irqflags); /* Range Limit and Modulo-N count modes use preset value as ceiling */ switch (priv->count_mode[count->id]) { case 1: case 3: - mutex_unlock(&priv->lock); + raw_spin_unlock_irqrestore(&priv->lock, irqflags); return quad8_count_preset_read(counter, count, ceiling); } - mutex_unlock(&priv->lock); + raw_spin_unlock_irqrestore(&priv->lock, irqflags); /* By default 0x1FFFFFF (25 bits unsigned) is maximum count */ *ceiling = 0x1FFFFFF; @@ -1150,12 +1182,13 @@ static int quad8_count_ceiling_write(struct counter_device *counter, struct counter_count *count, u64 ceiling) { struct quad8_iio *const priv = counter->priv; + unsigned long irqflags; /* Only 24-bit values are supported */ if (ceiling > 0xFFFFFF) return -EINVAL; - mutex_lock(&priv->lock); + raw_spin_lock_irqsave(&priv->lock, irqflags); /* Range Limit and Modulo-N count modes use preset value as ceiling */ switch (priv->count_mode[count->id]) { @@ -1164,7 +1197,7 @@ static int quad8_count_ceiling_write(struct counter_device *counter, return quad8_count_preset_write(counter, count, ceiling); } - mutex_unlock(&priv->lock); + raw_spin_unlock_irqrestore(&priv->lock, irqflags); return -EINVAL; } @@ -1186,21 +1219,91 @@ static int quad8_count_preset_enable_write(struct counter_device *counter, { struct quad8_iio *const priv = counter->priv; const int base_offset = priv->base + 2 * count->id + 1; + unsigned long irqflags; unsigned int ior_cfg; /* Preset enable is active low in Input/Output Control register */ preset_enable = !preset_enable; - mutex_lock(&priv->lock); + raw_spin_lock_irqsave(&priv->lock, irqflags); priv->preset_enable[count->id] = preset_enable; - ior_cfg = priv->ab_enable[count->id] | preset_enable << 1; + ior_cfg = priv->ab_enable[count->id] | preset_enable << 1 | + priv->irq_trigger[count->id] << 3; + + /* Load I/O control configuration to Input / Output Control Register */ + outb(QUAD8_CTR_IOR | ior_cfg, base_offset); + + raw_spin_unlock_irqrestore(&priv->lock, irqflags); + + return 0; +} + +static int quad8_irq_trigger_get(struct counter_device *counter, + struct counter_count *count, u8 *irq_trigger) +{ + const struct quad8_iio *const priv = counter->priv; + + *irq_trigger = priv->irq_trigger[count->id]; + + return 0; +} + +static int quad8_irq_trigger_set(struct counter_device *counter, + struct counter_count *count, u8 irq_trigger) +{ + struct quad8_iio *const priv = counter->priv; + const unsigned long base_offset = priv->base + 2 * count->id + 1; + unsigned long irqflags; + unsigned long ior_cfg; + + raw_spin_lock_irqsave(&priv->lock, irqflags); + + priv->irq_trigger[count->id] = irq_trigger; + + ior_cfg = priv->ab_enable[count->id] | + priv->preset_enable[count->id] << 1 | irq_trigger << 3; /* Load I/O control configuration to Input / Output Control Register */ outb(QUAD8_CTR_IOR | ior_cfg, base_offset); - mutex_unlock(&priv->lock); + raw_spin_unlock_irqrestore(&priv->lock, irqflags); + + return 0; +} + +static int quad8_irq_trigger_enable_read(struct counter_device *counter, + struct counter_count *count, u8 *state) +{ + const struct quad8_iio *const priv = counter->priv; + unsigned long irq_enabled; + + irq_enabled = inb(priv->base + QUAD8_REG_INDEX_INTERRUPT); + *state = !!(irq_enabled & BIT(count->id)); + + return 0; +} + +static int quad8_irq_trigger_enable_write(struct counter_device *counter, + struct counter_count *count, u8 state) +{ + struct quad8_iio *const priv = counter->priv; + unsigned long irqflags; + unsigned long irq_enabled; + + raw_spin_lock_irqsave(&priv->lock, irqflags); + + irq_enabled = inb(priv->base + QUAD8_REG_INDEX_INTERRUPT); + + if (state) + irq_enabled |= BIT(count->id); + else + irq_enabled &= ~BIT(count->id); + + outb(irq_enabled, priv->base + QUAD8_REG_INDEX_INTERRUPT); + + raw_spin_unlock_irqrestore(&priv->lock, irqflags); return 0; } @@ -1211,22 +1314,23 @@ static int quad8_signal_cable_fault_read(struct counter_device *counter, { struct quad8_iio *const priv = counter->priv; const size_t channel_id = signal->id / 2; + unsigned long irqflags; bool disabled; unsigned int status; - mutex_lock(&priv->lock); + raw_spin_lock_irqsave(&priv->lock, irqflags); disabled = !(priv->cable_fault_enable & BIT(channel_id)); if (disabled) { - mutex_unlock(&priv->lock); + raw_spin_unlock_irqrestore(&priv->lock, irqflags); return -EINVAL; } /* Logic 0 = cable fault */ status = inb(priv->base + QUAD8_DIFF_ENCODER_CABLE_STATUS); - mutex_unlock(&priv->lock); + raw_spin_unlock_irqrestore(&priv->lock, irqflags); /* Mask respective channel and invert logic */ *cable_fault = !(status & BIT(channel_id)); @@ -1252,9 +1356,10 @@ static int quad8_signal_cable_fault_enable_write(struct counter_device *counter, { struct quad8_iio *const priv = counter->priv; const size_t channel_id = signal->id / 2; + unsigned long irqflags; unsigned int cable_fault_enable; - mutex_lock(&priv->lock); + raw_spin_lock_irqsave(&priv->lock, irqflags); if (enable) priv->cable_fault_enable |= BIT(channel_id); @@ -1266,7 +1371,7 @@ static int quad8_signal_cable_fault_enable_write(struct counter_device *counter, outb(cable_fault_enable, priv->base + QUAD8_DIFF_ENCODER_CABLE_STATUS); - mutex_unlock(&priv->lock); + raw_spin_unlock_irqrestore(&priv->lock, irqflags); return 0; } @@ -1289,8 +1394,9 @@ static int quad8_signal_fck_prescaler_write(struct counter_device *counter, struct quad8_iio *const priv = counter->priv; const size_t channel_id = signal->id / 2; const int base_offset = priv->base + 2 * channel_id; + unsigned long irqflags; - mutex_lock(&priv->lock); + raw_spin_lock_irqsave(&priv->lock, irqflags); priv->fck_prescaler[channel_id] = prescaler; @@ -1302,7 +1408,7 @@ static int quad8_signal_fck_prescaler_write(struct counter_device *counter, outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_PRESET_PSC, base_offset + 1); - mutex_unlock(&priv->lock); + raw_spin_unlock_irqrestore(&priv->lock, irqflags); return 0; } @@ -1405,7 +1511,15 @@ static const u8 quad8_cnt_modes[] = { static DEFINE_COUNTER_AVAILABLE(quad8_count_mode_available, quad8_cnt_modes); +static const char *const quad8_irq_trigger_states[] = { + "carry", + "compare", + "carry-borrow", + "index", +}; + static DEFINE_COUNTER_ENUM(quad8_error_noise_enum, quad8_noise_error_states); +static DEFINE_COUNTER_ENUM(quad8_irq_trigger_enum, quad8_irq_trigger_states); static struct counter_data quad8_count_ext[] = { COUNTER_DATA_CEILING(quad8_count_ceiling_read, @@ -1420,6 +1534,11 @@ static struct counter_data quad8_count_ext[] = { COUNTER_DATA_PRESET(quad8_count_preset_read, quad8_count_preset_write), COUNTER_DATA_PRESET_ENABLE(quad8_count_preset_enable_read, quad8_count_preset_enable_write), + COUNTER_DATA_COUNT_ENUM("irq_trigger", quad8_irq_trigger_get, + quad8_irq_trigger_set, quad8_irq_trigger_enum), + COUNTER_DATA_COUNT_BOOL("irq_trigger_enable", + quad8_irq_trigger_enable_read, + quad8_irq_trigger_enable_write), }; #define QUAD8_COUNT(_id, _cntname) { \ @@ -1444,6 +1563,26 @@ static struct counter_count quad8_counts[] = { QUAD8_COUNT(7, "Channel 8 Count") }; +static irqreturn_t quad8_irq_handler(int irq, void *quad8iio) +{ + struct quad8_iio *const priv = quad8iio; + const unsigned long base = priv->base; + unsigned long irq_status; + unsigned long channel; + + irq_status = inb(base + QUAD8_REG_INTERRUPT_STATUS); + if (!irq_status) + return IRQ_NONE; + + for_each_set_bit(channel, &irq_status, QUAD8_NUM_COUNTERS) + counter_push_event(&priv->counter, channel); + + /* Clear pending interrupts on device */ + outb(QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC, base + QUAD8_REG_CHAN_OP); + + return IRQ_HANDLED; +} + static int quad8_probe(struct device *dev, unsigned int id) { struct iio_dev *indio_dev; @@ -1487,9 +1626,10 @@ static int quad8_probe(struct device *dev, unsigned int id) quad8iio->counter.priv = quad8iio; quad8iio->base = base[id]; - /* Initialize mutex */ - mutex_init(&quad8iio->lock); + raw_spin_lock_init(&quad8iio->lock); + /* Reset Index/Interrupt Register */ + outb(0x00, base[id] + QUAD8_REG_INDEX_INTERRUPT); /* Reset all counters and disable interrupt function */ outb(QUAD8_CHAN_OP_RESET_COUNTERS, base[id] + QUAD8_REG_CHAN_OP); /* Set initial configuration for all counters */ @@ -1519,8 +1659,8 @@ static int quad8_probe(struct device *dev, unsigned int id) } /* Disable Differential Encoder Cable Status for all channels */ outb(0xFF, base[id] + QUAD8_DIFF_ENCODER_CABLE_STATUS); - /* Enable all counters */ - outb(QUAD8_CHAN_OP_ENABLE_COUNTERS, base[id] + QUAD8_REG_CHAN_OP); + /* Enable all counters and enable interrupt function */ + outb(QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC, base[id] + QUAD8_REG_CHAN_OP); /* Register IIO device */ err = devm_iio_device_register(dev, indio_dev); @@ -1528,7 +1668,12 @@ static int quad8_probe(struct device *dev, unsigned int id) return err; /* Register Counter device */ - return devm_counter_register(dev, &quad8iio->counter); + err = devm_counter_register(dev, &quad8iio->counter); + if (err) + return err; + + return devm_request_irq(dev, irq[id], quad8_irq_handler, IRQF_SHARED, + quad8iio->counter.name, quad8iio); } static struct isa_driver quad8_driver = { diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig index 2de53ab0dd25..bd42df98f522 100644 --- a/drivers/counter/Kconfig +++ b/drivers/counter/Kconfig @@ -23,11 +23,11 @@ config 104_QUAD_8 A counter's respective error flag may be cleared by performing a write operation on the respective count value attribute. Although the 104-QUAD-8 counters have a 25-bit range, only the lower 24 bits may be - set, either directly or via the counter's preset attribute. Interrupts - are not supported by this driver. + set, either directly or via the counter's preset attribute. The base port addresses for the devices may be configured via the base - array module parameter. + array module parameter. The interrupt line numbers for the devices may + be configured via the irq array module parameter. config STM32_TIMER_CNT tristate "STM32 Timer encoder counter driver"