From patchwork Thu Oct 6 14:36:52 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Matti Vaittinen X-Patchwork-Id: 13000448 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 79631C433F5 for ; Thu, 6 Oct 2022 14:37:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229675AbiJFOhE (ORCPT ); Thu, 6 Oct 2022 10:37:04 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60512 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229637AbiJFOhD (ORCPT ); Thu, 6 Oct 2022 10:37:03 -0400 Received: from mail-lf1-x12d.google.com (mail-lf1-x12d.google.com [IPv6:2a00:1450:4864:20::12d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 4D9328321A; Thu, 6 Oct 2022 07:37:02 -0700 (PDT) Received: by mail-lf1-x12d.google.com with SMTP id a29so3039135lfo.1; Thu, 06 Oct 2022 07:37:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=in-reply-to:content-disposition:mime-version:references:message-id :subject:cc:to:from:date:from:to:cc:subject:date; bh=00zNPjSV0NBnL/fqot4cVHiAuUteLJu0Hqble2AqPlI=; b=jLkxnaE6g2QlVfw6r4SfnRsGhqANVLQCmbqVqAPddbzc2Hlt6vumlPhAwOUTFRAYs9 d/KNkdWYMbp3SCC545Q7PKeHBlOva9VHeiqBqhKaj/jcCSRnMXB3mZDDWHY2wnuwF2VV 9fcWy6y2YU08w3rVhwz3xeO9T8CGSToNhEIQalwMjNy64BrAb6mDOcXgAWK/HIV8On1T eK3st/VoChGvUE3qlTL5tfrT5ePveGYZ8AEjiaZFkYfdLnG5WGBDJxxTIx6FBNMK4268 6pfVhNEZJG1tc/8l2dl0M/F+l3JjUiJZUEzyRuITdriiM21mX+mQ+Y3qYMxM/DYwBWAo BBQw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=in-reply-to:content-disposition:mime-version:references:message-id :subject:cc:to:from:date:x-gm-message-state:from:to:cc:subject:date; bh=00zNPjSV0NBnL/fqot4cVHiAuUteLJu0Hqble2AqPlI=; b=Wrwoum/GQhH1AVMDPB2FOYgOuLSSSqOlQh8nhxmLZRVwovPae5WUBu9LsDpe3Uk0LW LO6TGj3RxZIeOCdy60S6sFSmsOVctgimkTiaiTwqiowb03ixENb3BbU+FsdHE8qtjc8Y hbyxL5zKjJrG3itnjoGzC2Ccq5dNOOgg7erLrgUkAoNQy01VSRexXQlW3XZP1Jqla5qg +oOWvxeXl2i6ciYStqZfGycdQqkWcosFww6YunlEdQG64csuEif8moikdP/eaYTQ1OOZ i2A80HF+Dw1MKQvq79RyYSfpewRBw6fY/OiGH1oQLNjlyBkEjFPldLjYmAA4mHqNkuMI /Dsw== X-Gm-Message-State: ACrzQf2BONKnkwxMqEv5jWQdEe4JIdnT7zvKo+LALd/9tYsrtY68xi4V p/zU07lo4cibD1a99Ic8M0c= X-Google-Smtp-Source: AMsMyM4ar/eKhA4YXpbfMC3LzHoOECJ4YMFrhXTEqAwQpgNZ3C3rSJftHJk85dr/oOKd4qPibTs4Zg== X-Received: by 2002:a19:9202:0:b0:49d:7310:742f with SMTP id u2-20020a199202000000b0049d7310742fmr115161lfd.312.1665067020638; Thu, 06 Oct 2022 07:37:00 -0700 (PDT) Received: from dc75zzyyyyyyyyyyyyyyt-3.rev.dnainternet.fi (dc75zzyyyyyyyyyyyyyyt-3.rev.dnainternet.fi. [2001:14ba:16f3:4a00::1]) by smtp.gmail.com with ESMTPSA id h4-20020a05651c124400b0026be1de1500sm1925140ljh.79.2022.10.06.07.36.58 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 06 Oct 2022 07:36:59 -0700 (PDT) Date: Thu, 6 Oct 2022 17:36:52 +0300 From: Matti Vaittinen To: Matti Vaittinen , Matti Vaittinen Cc: Jonathan Cameron , Lars-Peter Clausen , Rob Herring , Krzysztof Kozlowski , Matti Vaittinen , Andy Shevchenko , Jagath Jog J , Nikita Yushchenko , Cosmin Tanislav , linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [RFC PATCH v2 1/5] regulator: Add devm helpers for get and enable Message-ID: References: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Precedence: bulk List-ID: X-Mailing-List: linux-iio@vger.kernel.org A few regulator consumer drivers seem to be just getting a regulator, enabling it and registering a devm-action to disable the regulator at the driver detach and then forget about it. We can simplify this a bit by adding a devm-helper for this pattern. Add devm_regulator_get_enable() and devm_regulator_get_enable_optional() Signed-off-by: Matti Vaittinen (cherry picked from commit b6058e052b842a19c8bb639798d8692cd0e7589f) --- Already in Mark's regulator tree. Not to be merged. Included just for the sake of the completeness. Will be dropped when series is rebased on top of the 6.1-rc1 --- drivers/regulator/devres.c | 59 ++++++++++++++++++++++++++++++ include/linux/regulator/consumer.h | 13 +++++++ 2 files changed, 72 insertions(+) diff --git a/drivers/regulator/devres.c b/drivers/regulator/devres.c index 32823a87fd40..3cb3eb49ad8a 100644 --- a/drivers/regulator/devres.c +++ b/drivers/regulator/devres.c @@ -70,6 +70,65 @@ struct regulator *devm_regulator_get_exclusive(struct device *dev, } EXPORT_SYMBOL_GPL(devm_regulator_get_exclusive); +static void regulator_action_disable(void *d) +{ + struct regulator *r = (struct regulator *)d; + + regulator_disable(r); +} + +static int _devm_regulator_get_enable(struct device *dev, const char *id, + int get_type) +{ + struct regulator *r; + int ret; + + r = _devm_regulator_get(dev, id, get_type); + if (IS_ERR(r)) + return PTR_ERR(r); + + ret = regulator_enable(r); + if (!ret) + ret = devm_add_action_or_reset(dev, ®ulator_action_disable, r); + + if (ret) + devm_regulator_put(r); + + return ret; +} + +/** + * devm_regulator_get_enable_optional - Resource managed regulator get and enable + * @dev: device to supply + * @id: supply name or regulator ID. + * + * Get and enable regulator for duration of the device life-time. + * regulator_disable() and regulator_put() are automatically called on driver + * detach. See regulator_get_optional() and regulator_enable() for more + * information. + */ +int devm_regulator_get_enable_optional(struct device *dev, const char *id) +{ + return _devm_regulator_get_enable(dev, id, OPTIONAL_GET); +} +EXPORT_SYMBOL_GPL(devm_regulator_get_enable_optional); + +/** + * devm_regulator_get_enable - Resource managed regulator get and enable + * @dev: device to supply + * @id: supply name or regulator ID. + * + * Get and enable regulator for duration of the device life-time. + * regulator_disable() and regulator_put() are automatically called on driver + * detach. See regulator_get() and regulator_enable() for more + * information. + */ +int devm_regulator_get_enable(struct device *dev, const char *id) +{ + return _devm_regulator_get_enable(dev, id, NORMAL_GET); +} +EXPORT_SYMBOL_GPL(devm_regulator_get_enable); + /** * devm_regulator_get_optional - Resource managed regulator_get_optional() * @dev: device to supply diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h index bc6cda706d1f..8e6cf65851b0 100644 --- a/include/linux/regulator/consumer.h +++ b/include/linux/regulator/consumer.h @@ -207,6 +207,8 @@ struct regulator *__must_check regulator_get_optional(struct device *dev, const char *id); struct regulator *__must_check devm_regulator_get_optional(struct device *dev, const char *id); +int devm_regulator_get_enable(struct device *dev, const char *id); +int devm_regulator_get_enable_optional(struct device *dev, const char *id); void regulator_put(struct regulator *regulator); void devm_regulator_put(struct regulator *regulator); @@ -354,6 +356,17 @@ devm_regulator_get_exclusive(struct device *dev, const char *id) return ERR_PTR(-ENODEV); } +static inline int devm_regulator_get_enable(struct device *dev, const char *id) +{ + return -ENODEV; +} + +static inline int devm_regulator_get_enable_optional(struct device *dev, + const char *id) +{ + return -ENODEV; +} + static inline struct regulator *__must_check regulator_get_optional(struct device *dev, const char *id) { From patchwork Thu Oct 6 14:37:14 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Matti Vaittinen X-Patchwork-Id: 13000449 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id AFE1DC433FE for ; Thu, 6 Oct 2022 14:37:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231403AbiJFOhY (ORCPT ); Thu, 6 Oct 2022 10:37:24 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60840 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229637AbiJFOhX (ORCPT ); Thu, 6 Oct 2022 10:37:23 -0400 Received: from mail-lf1-x12c.google.com (mail-lf1-x12c.google.com [IPv6:2a00:1450:4864:20::12c]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7C79983F19; Thu, 6 Oct 2022 07:37:22 -0700 (PDT) Received: by mail-lf1-x12c.google.com with SMTP id f37so2993362lfv.8; Thu, 06 Oct 2022 07:37:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=in-reply-to:content-disposition:mime-version:references:message-id :subject:cc:to:from:date:from:to:cc:subject:date; bh=TIOQsYCwUfvmYBpnckXs7J461Nfe7jNypQ8rjuxui5M=; b=UoOejhVb8uhTDW4mIIC6+sC9g9rgk8Ejo2ExD/mTw8tLBlk0QwgT7FkBe0F2W1Nu6P RuCkElQO/d1dBdWp13c3VtBT0kcLkWysZjhSUgcSUh54uV2ODwPvWWbFDnN3lP34BN33 9pFrTQ50vxXqozR9T2K7SwEnNHDY0Vul5kgWiUs5yALvYDL+PJKF8SjOaRmeCHhzJZlJ BIwUfel++pEr1+2+YpbVvJlChpmcpruT+18cdtkAujjH9rrZ/+NpqUuc20s7WK5Iklbg SIXUAZdjLSfkTFgQduy46dB/AHVdRGFHmHIc+8AgSJ6HzhYEPxCvpa4uGyRlhHdejOgA RrhA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=in-reply-to:content-disposition:mime-version:references:message-id :subject:cc:to:from:date:x-gm-message-state:from:to:cc:subject:date; bh=TIOQsYCwUfvmYBpnckXs7J461Nfe7jNypQ8rjuxui5M=; b=Dzxavq1WSnYPy87ocf14AT+P0zHp4N7LpWGryPyTtGQ4m4rf5OC67Z7yOCNqvAz0wd GVHcJH8z00WFxSaHxPehJxARmDnObWfEdNv0MORMeJzLyA+uyzv7s6+EGHaWJ18CQmyx xzohrPcOUYQV1CAZyBgk8MDVAJQjFSssTSsav5xhaoxYQiaBxgZtaKik2R+WIDqkz8wa za3f4PjegZWVKE2jIUBx7Y27q2PUameYZeF/WMWEw2dDmtSa8xunJT4EUILG7af9cH4z z813zheiet29B8F0WK9V8OYc6A7b+Mt5cZDuHRlGLM2mJ5p+tKV44V6gPmM5LEbA40TK AkZw== X-Gm-Message-State: ACrzQf14+2zGmVh9cdC2XpJTtFElILJKOKfjbclzcjrKGSAKWaQHoMOD 027z1dChz7dul4OJ7z04sHQ= X-Google-Smtp-Source: AMsMyM68nVJo1+16nF5jOCHFbbaBlMhSus02odyFoAUULTd4wWsn3gXhAfBwB1ceKOQnEsd60Q513A== X-Received: by 2002:ac2:4acf:0:b0:4a2:6c60:935 with SMTP id m15-20020ac24acf000000b004a26c600935mr91402lfp.397.1665067040824; Thu, 06 Oct 2022 07:37:20 -0700 (PDT) Received: from dc75zzyyyyyyyyyyyyyyt-3.rev.dnainternet.fi (dc75zzyyyyyyyyyyyyyyt-3.rev.dnainternet.fi. [2001:14ba:16f3:4a00::1]) by smtp.gmail.com with ESMTPSA id o20-20020a056512231400b0049f9c732858sm2713975lfu.254.2022.10.06.07.37.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 06 Oct 2022 07:37:20 -0700 (PDT) Date: Thu, 6 Oct 2022 17:37:14 +0300 From: Matti Vaittinen To: Matti Vaittinen , Matti Vaittinen Cc: Jonathan Cameron , Lars-Peter Clausen , Rob Herring , Krzysztof Kozlowski , Matti Vaittinen , Andy Shevchenko , Jagath Jog J , Nikita Yushchenko , Cosmin Tanislav , linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [RFC PATCH v2 2/5] regulator: Add devm helpers for get and enable Message-ID: References: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Precedence: bulk List-ID: X-Mailing-List: linux-iio@vger.kernel.org A few regulator consumer drivers seem to be just getting a regulator, enabling it and registering a devm-action to disable the regulator at the driver detach and then forget about it. We can simplify this a bit by adding a devm-helper for this pattern. Add devm_regulator_get_enable() and devm_regulator_get_enable_optional() Signed-off-by: Matti Vaittinen Link: https://lore.kernel.org/r/ed7b8841193bb9749d426f3cb3b199c9460794cd.1660292316.git.mazziesaccount@gmail.com Signed-off-by: Mark Brown --- Already in Mark's regulator tree. Not to be merged. Included just for the sake of the completeness. Will be dropped when series is rebased on top of the 6.1-rc1 --- drivers/regulator/devres.c | 105 +++++++++++++++++++++++++++++ include/linux/regulator/consumer.h | 14 ++++ 2 files changed, 119 insertions(+) diff --git a/drivers/regulator/devres.c b/drivers/regulator/devres.c index 3cb3eb49ad8a..3265e75e97ab 100644 --- a/drivers/regulator/devres.c +++ b/drivers/regulator/devres.c @@ -253,6 +253,111 @@ int devm_regulator_bulk_get_const(struct device *dev, int num_consumers, } EXPORT_SYMBOL_GPL(devm_regulator_bulk_get_const); +static int devm_regulator_bulk_match(struct device *dev, void *res, + void *data) +{ + struct regulator_bulk_devres *match = res; + struct regulator_bulk_data *target = data; + + /* + * We check the put uses same consumer list as the get did. + * We _could_ scan all entries in consumer array and check the + * regulators match but ATM I don't see the need. We can change this + * later if needed. + */ + return match->consumers == target; +} + +/** + * devm_regulator_bulk_put - Resource managed regulator_bulk_put() + * @consumers: consumers to free + * + * Deallocate regulators allocated with devm_regulator_bulk_get(). Normally + * this function will not need to be called and the resource management + * code will ensure that the resource is freed. + */ +void devm_regulator_bulk_put(struct regulator_bulk_data *consumers) +{ + int rc; + struct regulator *regulator = consumers[0].consumer; + + rc = devres_release(regulator->dev, devm_regulator_bulk_release, + devm_regulator_bulk_match, consumers); + if (rc != 0) + WARN_ON(rc); +} +EXPORT_SYMBOL_GPL(devm_regulator_bulk_put); + +static void devm_regulator_bulk_disable(void *res) +{ + struct regulator_bulk_devres *devres = res; + int i; + + for (i = 0; i < devres->num_consumers; i++) + regulator_disable(devres->consumers[i].consumer); +} + +/** + * devm_regulator_bulk_get_enable - managed get'n enable multiple regulators + * + * @dev: device to supply + * @num_consumers: number of consumers to register + * @id: list of supply names or regulator IDs + * + * @return 0 on success, an errno on failure. + * + * This helper function allows drivers to get several regulator + * consumers in one operation with management, the regulators will + * automatically be freed when the device is unbound. If any of the + * regulators cannot be acquired then any regulators that were + * allocated will be freed before returning to the caller. + */ +int devm_regulator_bulk_get_enable(struct device *dev, int num_consumers, + const char * const *id) +{ + struct regulator_bulk_devres *devres; + struct regulator_bulk_data *consumers; + int i, ret; + + devres = devm_kmalloc(dev, sizeof(*devres), GFP_KERNEL); + if (!devres) + return -ENOMEM; + + devres->consumers = devm_kcalloc(dev, num_consumers, sizeof(*consumers), + GFP_KERNEL); + consumers = devres->consumers; + if (!consumers) + return -ENOMEM; + + devres->num_consumers = num_consumers; + + for (i = 0; i < num_consumers; i++) + consumers[i].supply = id[i]; + + ret = devm_regulator_bulk_get(dev, num_consumers, consumers); + if (ret) + return ret; + + for (i = 0; i < num_consumers; i++) { + ret = regulator_enable(consumers[i].consumer); + if (ret) + goto unwind; + } + + ret = devm_add_action(dev, devm_regulator_bulk_disable, devres); + if (!ret) + return 0; + +unwind: + while (--i >= 0) + regulator_disable(consumers[i].consumer); + + devm_regulator_bulk_put(consumers); + + return ret; +} +EXPORT_SYMBOL_GPL(devm_regulator_bulk_get_enable); + static void devm_rdev_release(struct device *dev, void *res) { regulator_unregister(*(struct regulator_dev **)res); diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h index 8e6cf65851b0..ee3b4a014611 100644 --- a/include/linux/regulator/consumer.h +++ b/include/linux/regulator/consumer.h @@ -246,12 +246,15 @@ int __must_check regulator_bulk_get(struct device *dev, int num_consumers, struct regulator_bulk_data *consumers); int __must_check devm_regulator_bulk_get(struct device *dev, int num_consumers, struct regulator_bulk_data *consumers); +void devm_regulator_bulk_put(struct regulator_bulk_data *consumers); int __must_check devm_regulator_bulk_get_const( struct device *dev, int num_consumers, const struct regulator_bulk_data *in_consumers, struct regulator_bulk_data **out_consumers); int __must_check regulator_bulk_enable(int num_consumers, struct regulator_bulk_data *consumers); +int devm_regulator_bulk_get_enable(struct device *dev, int num_consumers, + const char * const *id); int regulator_bulk_disable(int num_consumers, struct regulator_bulk_data *consumers); int regulator_bulk_force_disable(int num_consumers, @@ -388,6 +391,10 @@ static inline void devm_regulator_put(struct regulator *regulator) { } +static inline void devm_regulator_bulk_put(struct regulator_bulk_data *consumers) +{ +} + static inline int regulator_register_supply_alias(struct device *dev, const char *id, struct device *alias_dev, @@ -478,6 +485,13 @@ static inline int regulator_bulk_enable(int num_consumers, return 0; } +static inline int devm_regulator_bulk_get_enable(struct device *dev, + int num_consumers, + const char * const *id) +{ + return 0; +} + static inline int regulator_bulk_disable(int num_consumers, struct regulator_bulk_data *consumers) { From patchwork Thu Oct 6 14:37:39 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Matti Vaittinen X-Patchwork-Id: 13000450 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id AA053C4332F for ; Thu, 6 Oct 2022 14:37:52 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230267AbiJFOhv (ORCPT ); Thu, 6 Oct 2022 10:37:51 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33106 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229755AbiJFOht (ORCPT ); Thu, 6 Oct 2022 10:37:49 -0400 Received: from mail-lf1-x133.google.com (mail-lf1-x133.google.com [IPv6:2a00:1450:4864:20::133]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A41B2A7AAB; Thu, 6 Oct 2022 07:37:48 -0700 (PDT) Received: by mail-lf1-x133.google.com with SMTP id b2so3007870lfp.6; Thu, 06 Oct 2022 07:37:48 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=in-reply-to:content-disposition:mime-version:references:message-id :subject:cc:to:from:date:from:to:cc:subject:date; bh=nSpHlKnMonBGCDJ4nQk2Ue66OCa5K5+vPeWv3/hFJOE=; b=IXpggGVZYtF/+damtbWsP1tHFm88rn7TRHulZROeeO126QYxYrSpVxYfP5Xk0t9vg8 hlxOdp1w85VXaT5Z93hs7p8IuHZDsr5oapeSmWB8p+ZerkI3wgO1Niyqxldqgcdpt1nL 4/fSwClk5SdJsdE9xar3jCf0QhsDbgqknVKjh1y29PQZZb/KjQRUFaTuAflodbSB2n+o SOH4ah8qAUgqmOsLgvkPJfeP8JStelWxG2g80XN/bJg/ynKvV8hdU9Ohn6AlGCuXLsbA FA46pzNKSaMvAzW2F/XJoLcUkZwhQVjNei0xLc9dGp09rdqE83gbG2525zpUSghPKBMV JW3Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=in-reply-to:content-disposition:mime-version:references:message-id :subject:cc:to:from:date:x-gm-message-state:from:to:cc:subject:date; bh=nSpHlKnMonBGCDJ4nQk2Ue66OCa5K5+vPeWv3/hFJOE=; b=H/HwZ4I32zwKUb61chg7Jxm2nbdDgSpTOhzEmFARuldNPSca4GAJ2VorwtnKyJnlJu OrRI25Z36yEMKLqt3x/LJZYCZDkhk5bLCTHg70S4v7AFY9Cu3YIGGV3GHAGdu65sd22a fQjLx0Xx6RpYM6vmPz/QDn6O4ccdSaaR9CNPJge41jIaWoeD7lxGCGJooBj3K2YD1DIp pXkbn+osRgsul/vZt1qFGd4W/mhY20e/rY1qby9gVUKrxy7lQOR1JfDtLbIvI18QhreV g6vvUShz+1nVuJWmO3NkJKnu//7n9clIsnYQ14TbwNulbIBTh4600ZnIXBI/6ED5strK 4+Rg== X-Gm-Message-State: ACrzQf3HRIXh1ibaKdwkMixjq/dkZTW90B/89CcTo6SX7htY1Xg8rL1M xvk/LPZ9DUOpmHveNMoYRhY= X-Google-Smtp-Source: AMsMyM7kX49lKVt1xUXI2pYaAZ5BxYNYmf78+4OvUymQl4C3q2pE9zJ73jw/SIe98bBUePxmcVVEnQ== X-Received: by 2002:a05:6512:32ab:b0:4a2:2e49:94c with SMTP id q11-20020a05651232ab00b004a22e49094cmr97808lfe.351.1665067066826; Thu, 06 Oct 2022 07:37:46 -0700 (PDT) Received: from dc75zzyyyyyyyyyyyyyyt-3.rev.dnainternet.fi (dc75zzyyyyyyyyyyyyyyt-3.rev.dnainternet.fi. [2001:14ba:16f3:4a00::1]) by smtp.gmail.com with ESMTPSA id v11-20020a2ea60b000000b0026c36023a9asm1195007ljp.131.2022.10.06.07.37.45 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 06 Oct 2022 07:37:45 -0700 (PDT) Date: Thu, 6 Oct 2022 17:37:39 +0300 From: Matti Vaittinen To: Matti Vaittinen , Matti Vaittinen Cc: Jonathan Cameron , Lars-Peter Clausen , Rob Herring , Krzysztof Kozlowski , Matti Vaittinen , Andy Shevchenko , Jagath Jog J , Nikita Yushchenko , Cosmin Tanislav , linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [RFC PATCH v2 3/5] dt-bindings: iio: Add KX022A accelerometer Message-ID: <80fa42040f385eb47f4f3c71b9b02f643a643e38.1665066397.git.mazziesaccount@gmail.com> References: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Precedence: bulk List-ID: X-Mailing-List: linux-iio@vger.kernel.org KX022A is a 3-axis Accelerometer from ROHM/Kionix. The sensor features include variable ODRs, I2C and SPI control, FIFO/LIFO with watermark IRQ, tap/motion detection, wake-up & back-to-sleep events, four acceleration ranges (2, 4, 8 and 16g) and probably some other cool features. Add the basic device tree description for the accelerometer. Only basic accelerometer features are considered as of now - new properties may or may not be needed in the future when rest of the features are supported. Signed-off-by: Matti Vaittinen --- RFCv1 => v2: Based on a review by Krzysztof: - fix a typo from commit message - const compatible - drop unnecessary descriptions/words - io_vdd-supply => io-vdd-supply - fix the binding example indentiation Also, - change my email address - support both INT pins --- .../bindings/iio/accel/kionix,kx022a.yaml | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/accel/kionix,kx022a.yaml diff --git a/Documentation/devicetree/bindings/iio/accel/kionix,kx022a.yaml b/Documentation/devicetree/bindings/iio/accel/kionix,kx022a.yaml new file mode 100644 index 000000000000..2919c436e46f --- /dev/null +++ b/Documentation/devicetree/bindings/iio/accel/kionix,kx022a.yaml @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/accel/kionix,kx022a.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ROHM/Kionix KX022A Accelerometer + +maintainers: + - Matti Vaittinen + +description: | + KX022A is a 3-axis accelerometer supporting +/- 2G, 4G, 8G and 16G ranges, + output data-rates from 0.78Hz to 1600Hz and a hardware-fifo buffering. + KX022A can be accessed either via I2C or SPI. + +properties: + compatible: + const: kionix,kx022a + + reg: + maxItems: 1 + + interrupts: + minItems: 1 + maxItems: 2 + + interrupt-names: + minItems: 1 + maxItems: 2 + items: + enum: + - INT1 + - INT2 + + vdd-supply: true + io-vdd-supply: true + + mount-matrix: + description: | + an optional 3x3 mounting rotation matrix. + +required: + - compatible + - reg + - interrupts + +additionalProperties: false + +examples: + - | + #include + i2c { + #address-cells = <1>; + #size-cells = <0>; + accel@1f { + compatible = "kionix,kx022a"; + reg = <0x1f>; + + interrupt-parent = <&gpio1>; + interrupts = <29 IRQ_TYPE_LEVEL_LOW>; + interrupt-names = "INT1"; + + io-vdd-supply = <&iovdd>; + vdd-supply = <&vdd>; + }; + }; From patchwork Thu Oct 6 14:38:14 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Matti Vaittinen X-Patchwork-Id: 13000451 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id EDAE1C433F5 for ; Thu, 6 Oct 2022 14:38:29 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230317AbiJFOi2 (ORCPT ); Thu, 6 Oct 2022 10:38:28 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33604 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229540AbiJFOi0 (ORCPT ); Thu, 6 Oct 2022 10:38:26 -0400 Received: from mail-lj1-x229.google.com (mail-lj1-x229.google.com [IPv6:2a00:1450:4864:20::229]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 79286A7AAB; Thu, 6 Oct 2022 07:38:23 -0700 (PDT) Received: by mail-lj1-x229.google.com with SMTP id t16so2484030ljh.3; Thu, 06 Oct 2022 07:38:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=in-reply-to:content-disposition:mime-version:references:message-id :subject:cc:to:from:date:from:to:cc:subject:date; bh=eco4ZiARK5LR0wbC4L4/RZiSnshqEV9jDY+kWkowvJs=; b=HCsM99Ppc/mBtAyEEvyzCKmlxylFmwQaAqtsEnTXd3NGZLHztFhqNsKs+DMbS1ovJr vpLv9TuarfdvOa7n+55mWNDZ9V8Y99PB57xx3ZporNBl7f8EJEsIGMW3bSrv6d8b6ENd KfXlWw/fhWtsQlpar+QWwhE56Yh6KsaB0WOTm6QtoVox0BXKFFyiWTBwNXvQvCvxv7sU Z0SzO/yIMor3I2krCMwzIcMu1HebxQu40lEmI7kmGub8rERdTYlkIMRs17ZOhvpnajFe n3Q1XnIE4tEN6AWMmxIb9KFi7kCSyznuQXH6Yf7E+/qGxJzBsLKQVjNKj7ssx5rTlrLM c/pw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=in-reply-to:content-disposition:mime-version:references:message-id :subject:cc:to:from:date:x-gm-message-state:from:to:cc:subject:date; bh=eco4ZiARK5LR0wbC4L4/RZiSnshqEV9jDY+kWkowvJs=; b=Pyl+z2RWHZt+FVh0CLuyEm1rtWRoR1twbedETw0IkbwueKpNLJ6P/0Z9xSVYhCrEyJ 8EjEBn2ci1R5uSZpt8W3CYFwsTvw2RyBRLvKraw9AY2oOnlkb+b62RcBJzuwGAfnPlQK F1d+0JhHxXTpsXr8QH/cr1Mi57G8XhVpR+IXhONR2+GZYhPP9YCO3e/3ryVxVDqSUIiY NZp/uVUonitcOux0vWIIFG2niobT7Fwz7UnpCFNO9VYriNnE2xZto0qN7uwxHFxdFIgF 9WYpgCGYjRXJtE1Pm8DPQwYUJLA8dXOWRhNE/Myh70bXYYbzMFuigFsIzAgB0hf4TMAR i/JA== X-Gm-Message-State: ACrzQf0MCEZ8nShccLeVgWLM7k8fFkruGLs0QFEXJcdMaSuyLnW90bru P39VPwcbzkeQJ4ITaBqt36U= X-Google-Smtp-Source: AMsMyM4LjiQyfCK/zvSzWyomrXody66hUuB2ZeCPgsqfKSbXxpU04lUBupIDG6r53pEz773gPxi9Vw== X-Received: by 2002:a2e:8e70:0:b0:26c:549b:dd0d with SMTP id t16-20020a2e8e70000000b0026c549bdd0dmr1913ljk.172.1665067101423; Thu, 06 Oct 2022 07:38:21 -0700 (PDT) Received: from dc75zzyyyyyyyyyyyyyyt-3.rev.dnainternet.fi (dc75zzyyyyyyyyyyyyyyt-3.rev.dnainternet.fi. [2001:14ba:16f3:4a00::1]) by smtp.gmail.com with ESMTPSA id p20-20020ac24ed4000000b004a2588520f5sm274273lfr.166.2022.10.06.07.38.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 06 Oct 2022 07:38:20 -0700 (PDT) Date: Thu, 6 Oct 2022 17:38:14 +0300 From: Matti Vaittinen To: Matti Vaittinen , Matti Vaittinen Cc: Jonathan Cameron , Lars-Peter Clausen , Rob Herring , Krzysztof Kozlowski , Matti Vaittinen , Andy Shevchenko , Jagath Jog J , Nikita Yushchenko , Cosmin Tanislav , linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [RFC PATCH v2 4/5] iio: accel: Support Kionix/ROHM KX022A accelerometer Message-ID: <88e24b01da9f44ebf5fcd8344ded0b75ff742fbf.1665066397.git.mazziesaccount@gmail.com> References: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Precedence: bulk List-ID: X-Mailing-List: linux-iio@vger.kernel.org KX022A is a 3-axis accelerometer from ROHM/Kionix. The senosr features include variable ODRs, I2C and SPI control, FIFO/LIFO with watermark IRQ, tap/motion detection, wake-up & back-to-sleep events, four acceleration ranges (2, 4, 8 and 16g) and probably some other cool features. Add support for the basic accelerometer features such as getting the acceleration data via IIO. (raw reads, triggered buffer [data-ready] or using the WMI IRQ). Important things to be added include the double-tap, motion detection and wake-up as well as the runtime power management. NOTE: Filling-up the hardware FIFO should be avoided. During my testing I noticed that filling up the hardware FIFO might mess-up the sample count. My sensor ended up in a state where the amount of data in FIFO was reported to be 0xff bytes, which equals to 42,5 samples. Specification says the FIFO can hold a maximum of 41 samples in HiRes mode. Also, at least once the FIFO was stuck in a state where reading data from hardware FIFO did not decrease the amount of data reported to be in the FIFO - eg. FIFO was "stuck". The code has now an error count and 10 reads with invalid FIFO data count will cause the fifo contents to be dropped. Signed-off-by: Matti Vaittinen --- RFCv1 => v2 (mostly based on feedback from Jonathan): - Fix bunch of typos from the commit message. - Add missing break; to the kx022a_write_raw() - Fix SPI driver to use of_match_table - Fix indentiation in I2C driver - Drop struct kx022a_trigger - Drop cross references from Kconfig - Use /* */ also in file header comments - Misc minor styling - Do sensor-reset at probe - Support both IRQ pins - Implement read_avail callback - Use dma aligned buffers for bulk-reads - Use iio_trigger_poll_chained() - Use devm consistently - Drop inclusion of device.h - Add SPI device ID for module loading - Add module param for hw fifo / watermark IRQ usage - Fix io-vdd-supply name to match one in the bindings --- drivers/iio/accel/Kconfig | 21 + drivers/iio/accel/Makefile | 3 + drivers/iio/accel/kionix-kx022a-i2c.c | 53 ++ drivers/iio/accel/kionix-kx022a-spi.c | 59 ++ drivers/iio/accel/kionix-kx022a.c | 1138 +++++++++++++++++++++++++ drivers/iio/accel/kionix-kx022a.h | 81 ++ 6 files changed, 1355 insertions(+) create mode 100644 drivers/iio/accel/kionix-kx022a-i2c.c create mode 100644 drivers/iio/accel/kionix-kx022a-spi.c create mode 100644 drivers/iio/accel/kionix-kx022a.c create mode 100644 drivers/iio/accel/kionix-kx022a.h diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index 35798712f811..43f1090e8c59 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -409,6 +409,27 @@ config IIO_ST_ACCEL_SPI_3AXIS To compile this driver as a module, choose M here. The module will be called st_accel_spi. +config IIO_KX022A + tristate + +config IIO_KX022A_SPI + tristate "Kionix KX022A tri-axis digital accelerometer" + depends on I2C + select IIO_KX022A + select REGMAP_SPI + help + Say Y here to enable support for the Kionix KX022A digital tri-axis + accelerometer connected to I2C interface. + +config IIO_KX022A_I2C + tristate "Kionix KX022A tri-axis digital accelerometer" + depends on I2C + select IIO_KX022A + select REGMAP_I2C + help + Say Y here to enable support for the Kionix KX022A digital tri-axis + accelerometer connected to I2C interface. + config KXSD9 tristate "Kionix KXSD9 Accelerometer Driver" select IIO_BUFFER diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile index 4d8792668838..7bd654b74f42 100644 --- a/drivers/iio/accel/Makefile +++ b/drivers/iio/accel/Makefile @@ -40,6 +40,9 @@ obj-$(CONFIG_FXLS8962AF) += fxls8962af-core.o obj-$(CONFIG_FXLS8962AF_I2C) += fxls8962af-i2c.o obj-$(CONFIG_FXLS8962AF_SPI) += fxls8962af-spi.o obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o +obj-$(CONFIG_IIO_KX022A) += kionix-kx022a.o +obj-$(CONFIG_IIO_KX022A_I2C) += kionix-kx022a-i2c.o +obj-$(CONFIG_IIO_KX022A_SPI) += kionix-kx022a-spi.o obj-$(CONFIG_KXCJK1013) += kxcjk-1013.o obj-$(CONFIG_KXSD9) += kxsd9.o obj-$(CONFIG_KXSD9_SPI) += kxsd9-spi.o diff --git a/drivers/iio/accel/kionix-kx022a-i2c.c b/drivers/iio/accel/kionix-kx022a-i2c.c new file mode 100644 index 000000000000..6dd5b4a56401 --- /dev/null +++ b/drivers/iio/accel/kionix-kx022a-i2c.c @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2022 ROHM Semiconductors + * + * ROHM/KIONIX KX022A accelerometer driver + */ + +#include +#include +#include +#include + +#include "kionix-kx022a.h" + +static int kx022a_i2c_probe(struct i2c_client *i2c) +{ + struct regmap *regmap; + struct device *dev = &i2c->dev; + + if (!i2c->irq) { + dev_err(dev, "No IRQ configured\n"); + return -EINVAL; + } + + regmap = devm_regmap_init_i2c(i2c, &kx022a_regmap); + if (IS_ERR(regmap)) { + dev_err(dev, "Failed to initialize Regmap\n"); + + return PTR_ERR(regmap); + } + + return kx022a_probe_internal(dev); +} + +static const struct of_device_id kx022a_of_match[] = { + { .compatible = "kionix,kx022a", }, + { } +}; +MODULE_DEVICE_TABLE(of, kx022a_of_match); + +static struct i2c_driver kx022a_i2c_driver = { + .driver = { + .name = "kx022a-i2c", + .of_match_table = kx022a_of_match, + }, + .probe_new = kx022a_i2c_probe, +}; + +module_i2c_driver(kx022a_i2c_driver); + +MODULE_DESCRIPTION("ROHM/Kionix KX022A accelerometer driver"); +MODULE_AUTHOR("Matti Vaittinen "); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/accel/kionix-kx022a-spi.c b/drivers/iio/accel/kionix-kx022a-spi.c new file mode 100644 index 000000000000..59944b7b49b3 --- /dev/null +++ b/drivers/iio/accel/kionix-kx022a-spi.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2022 ROHM Semiconductors + * + * ROHM/KIONIX KX022A accelerometer driver + */ + +#include +#include +#include +#include + +#include "kionix-kx022a.h" + +static int kx022a_spi_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct regmap *regmap; + + if (!spi->irq) { + dev_err(dev, "No IRQ configured\n"); + return -EINVAL; + } + + regmap = devm_regmap_init_spi(spi, &kx022a_regmap); + if (IS_ERR(regmap)) { + dev_err(dev, "Failed to initialize Regmap\n"); + + return PTR_ERR(regmap); + } + return kx022a_probe_internal(dev); +} + +static const struct spi_device_id kx022a_id[] = { + { "kx022a" }, + { } +}; +MODULE_DEVICE_TABLE(spi, kx022a_id); + +static const struct of_device_id kx022a_of_match[] = { + { .compatible = "kionix,kx022a", }, + { } +}; +MODULE_DEVICE_TABLE(of, kx022a_of_match); + +static struct spi_driver kx022a_spi_driver = { + .driver = { + .name = "kx022a-spi", + .of_match_table = kx022a_of_match, + }, + .probe = kx022a_spi_probe, + .id_table = kx022a_id, +}; + +module_spi_driver(kx022a_spi_driver); + +MODULE_DESCRIPTION("ROHM/Kionix kx022A accelerometer driver"); +MODULE_AUTHOR("Matti Vaittinen "); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/accel/kionix-kx022a.c b/drivers/iio/accel/kionix-kx022a.c new file mode 100644 index 000000000000..37d47def5927 --- /dev/null +++ b/drivers/iio/accel/kionix-kx022a.c @@ -0,0 +1,1138 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2022 ROHM Semiconductors + * + * ROHM/KIONIX KX022A accelerometer driver + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kionix-kx022a.h" + +/* + * Threshold for deciding our HW fifo is unrecoverably corrupt and should be + * cleared + */ +#define KXO22A_FIFO_ERR_THRESHOLD 10 +#define KX022A_FIFO_LENGTH 41 +/* 3 axis, 2 bytes of data for each of the axis */ +#define KX022A_FIFO_SAMPLES_SIZE_BYTES 6 +#define KX022A_FIFO_MAX_BYTES (KX022A_FIFO_LENGTH * \ + KX022A_FIFO_SAMPLES_SIZE_BYTES) + +#define KX022A_SOFT_RESET_WAIT_TIME_US (5 * KILO) +#define KX022A_SOFT_RESET_TOTAL_WAIT_TIME_US (500 * KILO) + +static bool g_kx022a_use_buffer; + +enum { + KX022A_STATE_SAMPLE, + KX022A_STATE_FIFO, +}; + +/* Regmap configs */ +static const struct regmap_range kx022a_volatile_ranges[] = { + { + .range_min = KX022A_REG_XHP_L, + .range_max = KX022A_REG_COTR, + }, { + .range_min = KX022A_REG_TSCP, + .range_max = KX022A_REG_INT_REL, + }, { + /* The reset bit will be cleared by sensor */ + .range_min = KX022A_REG_CNTL2, + .range_max = KX022A_REG_CNTL2, + }, { + .range_min = KX022A_REG_BUF_STATUS_1, + .range_max = KX022A_REG_BUF_READ, + }, +}; + +static const struct regmap_access_table kx022a_volatile_regs = { + .yes_ranges = &kx022a_volatile_ranges[0], + .n_yes_ranges = ARRAY_SIZE(kx022a_volatile_ranges), +}; + +static const struct regmap_range kx022a_precious_ranges[] = { + { + .range_min = KX022A_REG_INT_REL, + .range_max = KX022A_REG_INT_REL, + }, +}; + +static const struct regmap_access_table kx022a_precious_regs = { + .yes_ranges = &kx022a_precious_ranges[0], + .n_yes_ranges = ARRAY_SIZE(kx022a_precious_ranges), +}; + +/* + * The HW does not set WHO_AM_I reg as read-only but we don't want to write it + * so we still include it in the read-only ranges. + */ +static const struct regmap_range kx022a_read_only_ranges[] = { + { + .range_min = KX022A_REG_XHP_L, + .range_max = KX022A_REG_INT_REL, + }, { + .range_min = KX022A_REG_BUF_STATUS_1, + .range_max = KX022A_REG_BUF_STATUS_2, + }, { + .range_min = KX022A_REG_BUF_READ, + .range_max = KX022A_REG_BUF_READ, + }, +}; + +static const struct regmap_access_table kx022a_ro_regs = { + .no_ranges = &kx022a_read_only_ranges[0], + .n_no_ranges = ARRAY_SIZE(kx022a_read_only_ranges), +}; + +static const struct regmap_range kx022a_write_only_ranges[] = { + { + .range_min = KX022A_REG_BTS_WUF_TH, + .range_max = KX022A_REG_BTS_WUF_TH, + }, { + .range_min = KX022A_REG_MAN_WAKE, + .range_max = KX022A_REG_MAN_WAKE, + }, { + .range_min = KX022A_REG_SELF_TEST, + .range_max = KX022A_REG_SELF_TEST, + }, { + .range_min = KX022A_REG_BUF_CLEAR, + .range_max = KX022A_REG_BUF_CLEAR, + }, +}; + +static const struct regmap_access_table kx022a_wo_regs = { + .no_ranges = &kx022a_write_only_ranges[0], + .n_no_ranges = ARRAY_SIZE(kx022a_write_only_ranges), +}; + +static const struct regmap_range kx022a_noinc_read_ranges[] = { + { + .range_min = KX022A_REG_BUF_READ, + .range_max = KX022A_REG_BUF_READ, + }, +}; + +static const struct regmap_access_table kx022a_nir_regs = { + .yes_ranges = &kx022a_noinc_read_ranges[0], + .n_yes_ranges = ARRAY_SIZE(kx022a_noinc_read_ranges), +}; + +const struct regmap_config kx022a_regmap = { + .reg_bits = 8, + .val_bits = 8, + .volatile_table = &kx022a_volatile_regs, + .rd_table = &kx022a_wo_regs, + .wr_table = &kx022a_ro_regs, + .rd_noinc_table = &kx022a_nir_regs, + .precious_table = &kx022a_precious_regs, + .max_register = KX022A_MAX_REGISTER, + .cache_type = REGCACHE_RBTREE, +}; +EXPORT_SYMBOL_GPL(kx022a_regmap); + +struct kx022a_data; + +struct kx022a_data { + int irq; + int inc_reg; + int ien_reg; + struct regmap *regmap; + struct iio_trigger *trig; + bool trigger_enabled; + + struct device *dev; + unsigned int g_range; + struct mutex mutex; + unsigned int state; + + int64_t timestamp, old_timestamp; + unsigned int odr_ns; + + struct iio_mount_matrix orientation; + u8 watermark; + /* 3 x 16bit accel data + timestamp */ + s16 buffer[8] __aligned(IIO_DMA_MINALIGN); + struct { + __le16 channels[3]; + s64 ts __aligned(8); + } scan; +}; + +static const struct iio_mount_matrix * +kx022a_get_mount_matrix(const struct iio_dev *idev, + const struct iio_chan_spec *chan) +{ + struct kx022a_data *data = iio_priv(idev); + + return &data->orientation; +} + +enum { + AXIS_X, + AXIS_Y, + AXIS_Z, + AXIS_MAX, +}; + +static const unsigned long kx022a_scan_masks[] = { + BIT(AXIS_X) | BIT(AXIS_Y) | BIT(AXIS_Z), + 0}; + +static const struct iio_chan_spec_ext_info kx022a_ext_info[] = { + IIO_MOUNT_MATRIX(IIO_SHARED_BY_TYPE, kx022a_get_mount_matrix), + { }, +}; + +#define KX022A_ACCEL_CHAN(axis, index) \ + { \ + .type = IIO_ACCEL, \ + .modified = 1, \ + .channel2 = IIO_MOD_##axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_type_available = \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .ext_info = kx022a_ext_info, \ + .address = KX022A_REG_##axis##OUT_L, \ + .scan_index = index, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_LE, \ + }, \ + } + +static const struct iio_chan_spec kx022a_channels[] = { + KX022A_ACCEL_CHAN(X, 0), + KX022A_ACCEL_CHAN(Y, 1), + KX022A_ACCEL_CHAN(Z, 2), + IIO_CHAN_SOFT_TIMESTAMP(3), +}; + +/* + * The sensor HW can support ODR up to 1600 Hz - which is beyond what most of + * Linux CPUs can handle w/o dropping samples. Also, the low power mode is not + * available for higher sample rates. Thus the driver only supports 200 Hz and + * slower ODRs. Slowest being 0.78 Hz + */ +static const int kx022a_accel_samp_freq_table[][2] = { + [0] = { 0, 780000 }, + [1] = { 1, 563000 }, + [2] = { 3, 125000 }, + [3] = { 6, 250000 }, + [4] = { 12, 500000 }, + [5] = { 25, 0 }, + [6] = { 50, 0 }, + [7] = { 100, 0 }, + [8] = { 200, 0 } +}; + +static const unsigned int kx022a_odrs[] = { 1282051282, 639795266, 320 * MEGA, + 160 * MEGA, 80 * MEGA, 40 * MEGA, 20 * MEGA, 10 * MEGA, 5 * MEGA }; + +/* + * range is typically +-2G/4G/8G/16G, distributed over the amount of bits. + * The scale table can be calculated using + * (range / 2^bits) * g = (range / 2^bits) * 9.80665 m/s^2 + * => KX022A uses 16 bit (HiRes mode - assume the low 8 bits are zeroed + * in low-power mode(?) ) + * => +/-2G => 4 / 2^16 * 9,80665 * 10^6 (to scale to micro) + * => +/-2G - 598.550415 + * +/-4G - 1197.10083 + * +/-8G - 2394.20166 + * +/-16G - 4788.40332 + */ +static const int kx022a_scale_table[][2] = { + [0] = { 598, 550415 }, + [1] = { 1197, 100830 }, + [2] = { 2394, 201660 }, + [3] = { 4788, 403320 } +}; + +static int kx022a_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = (const int *)kx022a_accel_samp_freq_table; + *length = ARRAY_SIZE(kx022a_accel_samp_freq_table) * 2; + *type = IIO_VAL_INT_PLUS_MICRO; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SCALE: + *vals = (const int *)kx022a_scale_table; + *length = ARRAY_SIZE(kx022a_scale_table) * 2; + *type = IIO_VAL_INT_PLUS_MICRO; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +#define KX022A_DEFAULT_PERIOD_NS (20 * MEGA) + +static void kx022a_reg2freq(unsigned int val, int *val1, int *val2) +{ + *val1 = kx022a_accel_samp_freq_table[val & KX022A_MASK_ODR][0]; + *val2 = kx022a_accel_samp_freq_table[val & KX022A_MASK_ODR][1]; +} + +static void kx022a_reg2scale(unsigned int val, unsigned int *val1, + unsigned int *val2) +{ + val &= KX022A_MASK_GSEL; + val >>= KX022A_GSEL_SHIFT; + + *val1 = kx022a_scale_table[val][0]; + *val2 = kx022a_scale_table[val][1]; +} + +static int __kx022a_turn_on_unlocked(struct kx022a_data *data) +{ + int ret; + + ret = regmap_set_bits(data->regmap, KX022A_REG_CNTL, KX022A_MASK_PC1); + if (ret) + dev_err(data->dev, "Turn ON fail %d\n", ret); + + return ret; +} + +static int __kx022a_turn_off_unlocked(struct kx022a_data *data) +{ + int ret; + + ret = regmap_clear_bits(data->regmap, KX022A_REG_CNTL, KX022A_MASK_PC1); + if (ret) + dev_err(data->dev, "Turn OFF fail %d\n", ret); + + return ret; +} + +static int kx022a_turn_off_lock(struct kx022a_data *data) +{ + int ret; + + mutex_lock(&data->mutex); + ret = __kx022a_turn_off_unlocked(data); + if (ret) + mutex_unlock(&data->mutex); + + return ret; +} + +static int kx022a_turn_on_unlock(struct kx022a_data *data) +{ + int ret; + + ret = __kx022a_turn_on_unlocked(data); + mutex_unlock(&data->mutex); + + return ret; +} + +static int kx022a_write_raw(struct iio_dev *idev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct kx022a_data *data = iio_priv(idev); + int ret; + + /* + * We should not allow changing scale or frequency when FIFO is running + * as it will mess the timestamp/scale for samples existing in the + * buffer. If this turns out to be an issue we can later change logic + * to internally flush the fifo before reconfiguring so the samples in + * fifo keep matching the freq/scale settings. (Such setup could cause + * issues if users trust the watermark to be reached within known + * time-limit). + */ + ret = iio_device_claim_direct_mode(idev); + if (ret) + return ret; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + { + int n = ARRAY_SIZE(kx022a_accel_samp_freq_table); + + while (n-- > 0) + if (val == kx022a_accel_samp_freq_table[n][0] && + kx022a_accel_samp_freq_table[n][1] == val2) + break; + if (n < 0) { + ret = -EINVAL; + goto unlock_out; + } + ret = kx022a_turn_off_lock(data); + if (ret) + break; + + ret = regmap_update_bits(data->regmap, + KX022A_REG_ODCNTL, + KX022A_MASK_ODR, n); + data->odr_ns = kx022a_odrs[n]; + kx022a_turn_on_unlock(data); + break; + } + case IIO_CHAN_INFO_SCALE: + { + int n = ARRAY_SIZE(kx022a_scale_table); + + while (n-- > 0) + if (val == kx022a_scale_table[n][0] && + kx022a_scale_table[n][1] == val2) + break; + if (n < 0) { + ret = -EINVAL; + goto unlock_out; + } + + ret = kx022a_turn_off_lock(data); + if (ret) + break; + + ret = regmap_update_bits(data->regmap, KX022A_REG_CNTL, + KX022A_MASK_GSEL, + n << KX022A_GSEL_SHIFT); + kx022a_turn_on_unlock(data); + break; + } + default: + ret = -EINVAL; + } + +unlock_out: + iio_device_release_direct_mode(idev); + + return ret; +} + +static int kx022a_fifo_set_wmi(struct kx022a_data *data) +{ + u8 threshold; + + threshold = data->watermark; + + return regmap_update_bits(data->regmap, KX022A_REG_BUF_CNTL1, + KX022A_MASK_WM_TH, threshold); +} + +static int kx022a_get_axis(struct kx022a_data *data, + struct iio_chan_spec const *chan, + int *val) +{ + int ret; + + ret = regmap_bulk_read(data->regmap, chan->address, &data->buffer, + sizeof(s16)); + if (ret) + return ret; + + *val = data->buffer[0]; + + return IIO_VAL_INT; +} + +static int kx022a_read_raw(struct iio_dev *idev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct kx022a_data *data = iio_priv(idev); + unsigned int regval; + int ret = -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = iio_device_claim_direct_mode(idev); + if (ret) + return ret; + + mutex_lock(&data->mutex); + ret = kx022a_get_axis(data, chan, val); + mutex_unlock(&data->mutex); + + iio_device_release_direct_mode(idev); + break; + + case IIO_CHAN_INFO_SAMP_FREQ: + ret = regmap_read(data->regmap, KX022A_REG_ODCNTL, ®val); + if (ret) + return ret; + + if ((regval & KX022A_MASK_ODR) > + ARRAY_SIZE(kx022a_accel_samp_freq_table)) { + dev_err(data->dev, "Invalid ODR\n"); + return -EINVAL; + } + + kx022a_reg2freq(regval, val, val2); + ret = IIO_VAL_INT_PLUS_MICRO; + + break; + + case IIO_CHAN_INFO_SCALE: + ret = regmap_read(data->regmap, KX022A_REG_CNTL, ®val); + if (ret < 0) + return ret; + + kx022a_reg2scale(regval, val, val2); + + ret = IIO_VAL_INT_PLUS_MICRO; + break; + } + + return ret; +}; + +static int kx022a_validate_trigger(struct iio_dev *idev, + struct iio_trigger *trig) +{ + struct kx022a_data *data = iio_priv(idev); + + if (data->trig == trig) + return 0; + + return -EINVAL; +} + +static int kx022a_set_watermark(struct iio_dev *idev, unsigned int val) +{ + struct kx022a_data *data = iio_priv(idev); + + if (val > KX022A_FIFO_LENGTH) + val = KX022A_FIFO_LENGTH; + + mutex_lock(&data->mutex); + data->watermark = val; + mutex_unlock(&data->mutex); + + return 0; +} + +static ssize_t kx022a_get_fifo_state(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *idev = dev_to_iio_dev(dev); + struct kx022a_data *data = iio_priv(idev); + bool state; + + mutex_lock(&data->mutex); + state = data->state; + mutex_unlock(&data->mutex); + + return sprintf(buf, "%d\n", state); +} + +static ssize_t kx022a_get_fifo_watermark(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *idev = dev_to_iio_dev(dev); + struct kx022a_data *data = iio_priv(idev); + int wm; + + mutex_lock(&data->mutex); + wm = data->watermark; + mutex_unlock(&data->mutex); + + return sprintf(buf, "%d\n", wm); +} + +static IIO_DEVICE_ATTR(hwfifo_enabled, 0444, + kx022a_get_fifo_state, NULL, 0); +static IIO_DEVICE_ATTR(hwfifo_watermark, 0444, + kx022a_get_fifo_watermark, NULL, 0); + +static const struct attribute *kx022a_fifo_attributes[] = { + &iio_dev_attr_hwfifo_watermark.dev_attr.attr, + &iio_dev_attr_hwfifo_enabled.dev_attr.attr, + NULL, +}; + +static int kx022a_drop_fifo_contents(struct kx022a_data *data) +{ + /* + * We must clear the old time-stamp to avoid computing the timestamps + * based on samples acquired when buffer was last enabled + */ + data->timestamp = 0; + + return regmap_write(data->regmap, KX022A_REG_BUF_CLEAR, 0xff); +} + +static int __kx022a_fifo_flush(struct iio_dev *idev, unsigned int samples, + bool irq) +{ + struct kx022a_data *data = iio_priv(idev); + struct device *dev = regmap_get_device(data->regmap); + int ret, i; + int count, fifo_bytes; + u16 buffer[KX022A_FIFO_LENGTH * 3]; + int64_t tstamp; + uint64_t sample_period; + static int err_ctr; + + ret = regmap_read(data->regmap, KX022A_REG_BUF_STATUS_1, &fifo_bytes); + if (ret < 0) { + dev_err(dev, "Error reading buffer status\n"); + return ret; + } + + /* Let's not overflow if we for some reason get bogus value from i2c */ + if (fifo_bytes > KX022A_FIFO_MAX_BYTES) { + /* + * I've observed a strange behaviour where FIFO may get stuck if + * samples are not read out fast enough. By 'stuck' I mean + * situation where amount of data adverticed by the STATUS_1 + * reg is 255 - which equals to 42,5 (sic!) samples and by + * my experimenting there are situations where reading the + * FIFO buffer does not decrease the data count but the same + * fifo sample level (255 bytes of data) is reported + */ + err_ctr++; + dev_warn(data->dev, "Bad amount of data %u\n", fifo_bytes); + fifo_bytes = KX022A_FIFO_MAX_BYTES; + } else if (fifo_bytes % KX022A_FIFO_SAMPLES_SIZE_BYTES) { + err_ctr++; + dev_err(data->dev, "Bad FIFO alignment. Data may be corrupt\n"); + } else { + err_ctr = 0; + } + + if (err_ctr > KXO22A_FIFO_ERR_THRESHOLD) { + __kx022a_turn_off_unlocked(data); + kx022a_drop_fifo_contents(data); + __kx022a_turn_on_unlocked(data); + + err_ctr = 0; + + return -EINVAL; + } + + count = fifo_bytes / KX022A_FIFO_SAMPLES_SIZE_BYTES; + if (!count) + return 0; + + /* + * If we are being called from IRQ handler we know the stored timestamp + * is fairly accurate for the last stored sample. Otherwise, if we are + * called as a result of a read operation from userspace and hence + * before the watermark interrupt was triggered, take a timestamp + * now. We can fall anywhere in between two samples so the error in this + * case is at most one sample period. + */ + if (!irq) { + data->old_timestamp = data->timestamp; + data->timestamp = iio_get_time_ns(idev); + } + + /* + * Approximate timestamps for each of the sample based on the sampling + * frequency, timestamp for last sample and number of samples. + * + * We'd better not use the current bandwidth settings to compute the + * sample period. The real sample rate varies with the device and + * small variation adds when we store a large number of samples. + * + * To avoid this issue we compute the actual sample period ourselves + * based on the timestamp delta between the last two flush operations. + */ + if (data->old_timestamp) { + sample_period = (data->timestamp - data->old_timestamp); + do_div(sample_period, count); + } else { + sample_period = data->odr_ns; + } + tstamp = data->timestamp - (count - 1) * sample_period; + + if (samples && count > samples) { + /* + * Here we leave some old samples to the buffer. We need to + * adjust the timestamp to match the first sample in the buffer + * or we will miscalculate the sample_period at next round. + */ + data->timestamp -= (count - samples) * sample_period; + count = samples; + } + + fifo_bytes = count * KX022A_FIFO_SAMPLES_SIZE_BYTES; + ret = regmap_noinc_read(data->regmap, KX022A_REG_BUF_READ, + buffer, fifo_bytes); + if (ret) + return ret; + + for (i = 0; i < count; i++) { + int bit; + u16 *samples = &buffer[i * 3]; + + for_each_set_bit(bit, idev->active_scan_mask, AXIS_MAX) + memcpy(&data->scan.channels[bit], &samples[bit], + sizeof(data->scan.channels[0])); + + iio_push_to_buffers_with_timestamp(idev, &data->scan, tstamp); + + tstamp += sample_period; + } + + return count; +} + +static int kx022a_fifo_flush(struct iio_dev *idev, unsigned int samples) +{ + struct kx022a_data *data = iio_priv(idev); + int ret; + + mutex_lock(&data->mutex); + ret = __kx022a_fifo_flush(idev, samples, false); + mutex_unlock(&data->mutex); + + return ret; +} + +static const struct iio_info kx022a_info = { + .read_raw = &kx022a_read_raw, + .write_raw = &kx022a_write_raw, + .read_avail = &kx022a_read_avail, + + .validate_trigger = kx022a_validate_trigger, + .hwfifo_set_watermark = kx022a_set_watermark, + .hwfifo_flush_to_buffer = kx022a_fifo_flush, +}; + +static int kx022a_set_drdy_irq(struct kx022a_data *data, bool en) +{ + if (en) + return regmap_set_bits(data->regmap, KX022A_REG_CNTL, + KX022A_MASK_DRDY); + + return regmap_clear_bits(data->regmap, KX022A_REG_CNTL, + KX022A_MASK_DRDY); +} + +static int kx022a_prepare_irq_pin(struct kx022a_data *data) +{ + /* Enable IRQ1 pin. Set polarity to active low */ + int mask = KX022A_MASK_IEN | KX022A_MASK_IPOL | + KX022A_MASK_ITYP; + int val = KX022A_MASK_IEN | KX022A_IPOL_LOW | + KX022A_ITYP_LEVEL; + int ret; + + ret = regmap_update_bits(data->regmap, data->inc_reg, mask, val); + if (ret) + return ret; + + /* We enable WMI to IRQ pin only at buffer_enable */ + mask = KX022A_MASK_INS2_DRDY /*| KX122_MASK_INS2_WMI */; + + return regmap_set_bits(data->regmap, data->ien_reg, mask); +} + +static int kx022a_fifo_disable(struct kx022a_data *data) +{ + int ret = 0; + + ret = kx022a_turn_off_lock(data); + if (ret) + return ret; + + ret = regmap_clear_bits(data->regmap, data->ien_reg, + KX022A_MASK_WMI); + if (ret) + goto unlock_out; + + ret = regmap_clear_bits(data->regmap, KX022A_REG_BUF_CNTL2, + KX022A_MASK_BUF_EN); + if (ret) + goto unlock_out; + + data->state &= (~KX022A_STATE_FIFO); + + kx022a_drop_fifo_contents(data); + + return kx022a_turn_on_unlock(data); + +unlock_out: + mutex_unlock(&data->mutex); + + return ret; +} + +static int kx022a_buffer_predisable(struct iio_dev *idev) +{ + struct kx022a_data *data = iio_priv(idev); + + if (iio_device_get_current_mode(idev) == INDIO_BUFFER_TRIGGERED) + return 0; + + return kx022a_fifo_disable(data); +} + +static int kx022a_fifo_enable(struct kx022a_data *data) +{ + int ret = 0; + + ret = kx022a_turn_off_lock(data); + if (ret) + return ret; + + /* Update watermark to HW */ + ret = kx022a_fifo_set_wmi(data); + if (ret) + goto unlock_out; + + /* Enable buffer */ + ret = regmap_set_bits(data->regmap, KX022A_REG_BUF_CNTL2, + KX022A_MASK_BUF_EN); + if (ret) + goto unlock_out; + + data->state |= KX022A_STATE_FIFO; + ret = regmap_set_bits(data->regmap, data->ien_reg, + KX022A_MASK_WMI); + if (ret) + goto unlock_out; + + return kx022a_turn_on_unlock(data); + +unlock_out: + mutex_unlock(&data->mutex); + + return ret; +} + +static int kx022a_buffer_postenable(struct iio_dev *idev) +{ + struct kx022a_data *data = iio_priv(idev); + + /* + * If we use data-ready trigger, then the IRQ masks should be handled by + * trigger enable and the hardware buffer is not used but we just update + * results to the IIO fifo when data-ready triggers. + */ + if (iio_device_get_current_mode(idev) == INDIO_BUFFER_TRIGGERED) + return 0; + + /* + * Filling up the HW-FIFO can cause nasty problems. Thus we do not + * enable the fifo unless it is explicitly requested by a module param. + * If you are _sure_ your system can serve the interrupts in time you + * can enable the HW fifo. I do not recommend it for sample frequencies + * higher than 2 Hz - and even in that case I would set the watermark + * somewhere near 20 samples (HI-RES) to have magnitude of 10 sec + * safety-margin. + */ + if (!g_kx022a_use_buffer) { + dev_err(data->dev, "Neither trigger set nor hw-fifo enabled\n"); + + return -EINVAL; + } + return kx022a_fifo_enable(data); +} + +static const struct iio_buffer_setup_ops kx022a_buffer_ops = { + .postenable = kx022a_buffer_postenable, + .predisable = kx022a_buffer_predisable, +}; + +static irqreturn_t kx022a_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *idev = pf->indio_dev; + struct kx022a_data *data = iio_priv(idev); + int ret; + + ret = regmap_bulk_read(data->regmap, KX022A_REG_XOUT_L, data->buffer, + KX022A_FIFO_SAMPLES_SIZE_BYTES); + if (ret < 0) + goto err_read; + + iio_push_to_buffers_with_timestamp(idev, data->buffer, pf->timestamp); +err_read: + iio_trigger_notify_done(idev->trig); + + return IRQ_HANDLED; +} + +/* Get timestamps and wake the thread if we need to read data */ +static irqreturn_t kx022a_irq_handler(int irq, void *private) +{ + struct iio_dev *idev = private; + struct kx022a_data *data = iio_priv(idev); + + data->old_timestamp = data->timestamp; + data->timestamp = iio_get_time_ns(idev); + + if (data->state & KX022A_STATE_FIFO || data->trigger_enabled) + return IRQ_WAKE_THREAD; + + return IRQ_NONE; +} + +/* + * WMI and data-ready IRQs are acked when results are read. If we add + * TILT/WAKE or other IRQs - then we may need to implement the acking + * (which is racy). + */ +static irqreturn_t kx022a_irq_thread_handler(int irq, void *private) +{ + struct iio_dev *idev = private; + struct kx022a_data *data = iio_priv(idev); + int ret = IRQ_NONE; + + mutex_lock(&data->mutex); + + if (data->trigger_enabled) { + iio_trigger_poll_chained(data->trig); + ret = IRQ_HANDLED; + } + + if (data->state & KX022A_STATE_FIFO) { + ret = __kx022a_fifo_flush(idev, KX022A_FIFO_LENGTH, true); + if (ret > 0) + ret = IRQ_HANDLED; + } + + mutex_unlock(&data->mutex); + + return ret; +} + +static int kx022a_trigger_set_state(struct iio_trigger *trig, + bool state) +{ + struct kx022a_data *data = iio_trigger_get_drvdata(trig); + int ret = 0; + + mutex_lock(&data->mutex); + + if (data->trigger_enabled == state) + goto unlock_out; + + ret = __kx022a_turn_off_unlocked(data); + if (ret) + goto unlock_out; + + data->trigger_enabled = state; + ret = kx022a_set_drdy_irq(data, state); + if (ret) + goto unlock_out; + + ret = __kx022a_turn_on_unlocked(data); + +unlock_out: + mutex_unlock(&data->mutex); + + return ret; +} + +static const struct iio_trigger_ops kx022a_trigger_ops = { + .set_trigger_state = kx022a_trigger_set_state, +}; + +static int kx022a_chip_init(struct kx022a_data *data) +{ + int ret, val; + + /* Reset the senor */ + ret = regmap_write(data->regmap, KX022A_REG_CNTL2, KX022A_MASK_SRST); + if (ret) + return ret; + + /* + * I've seen I2C read failures if we poll too fast after the sensor + * reset. Slight delay gives I2C block the time to recover. + */ + msleep(1); + + ret = regmap_read_poll_timeout(data->regmap, KX022A_REG_CNTL2, val, + !(val & KX022A_MASK_SRST), + KX022A_SOFT_RESET_WAIT_TIME_US, + KX022A_SOFT_RESET_TOTAL_WAIT_TIME_US); + if (ret) { + dev_err(data->dev, "Sensor reset %s\n", + val & KX022A_MASK_SRST ? "timeout" : "fail#"); + return ret; + } + + ret = regmap_reinit_cache(data->regmap, &kx022a_regmap); + if (ret) { + dev_err(data->dev, "Failed to reinit reg cache\n"); + return ret; + } + + /* set data res 16bit */ + ret = regmap_set_bits(data->regmap, KX022A_REG_BUF_CNTL2, + KX022A_MASK_BRES16); + if (ret) { + dev_err(data->dev, "Failed to set data resolution\n"); + return ret; + } + + return kx022a_prepare_irq_pin(data); +} + +int kx022a_probe_internal(struct device *dev) +{ + static const char * const regulator_names[] = {"io-vdd", "vdd"}; + struct iio_trigger *indio_trig; + struct kx022a_data *data; + struct regmap *regmap; + unsigned int chip_id; + struct iio_dev *idev; + int ret, irq; + + if (WARN_ON(!dev)) + return -ENODEV; + + regmap = dev_get_regmap(dev, NULL); + if (!regmap) { + dev_err(dev, "no regmap\n"); + + return -EINVAL; + } + + idev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!idev) + return -ENOMEM; + + data = iio_priv(idev); + + /* + * VDD is the analog and digital domain voltage supply + * IO_VDD is the digital I/O voltage supply + */ + ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(regulator_names), + regulator_names); + if (ret && ret != -ENODEV) + return dev_err_probe(dev, ret, "failed to enable regulator\n"); + + ret = regmap_read(regmap, KX022A_REG_WHO, &chip_id); + if (ret) { + dev_err_probe(dev, ret, "Failed to access sensor\n"); + return ret; + } + + if (chip_id != KX022A_ID) { + dev_err(dev, "unsupported device 0x%x\n", chip_id); + return -EINVAL; + } + + irq = fwnode_irq_get_byname(dev_fwnode(dev), "INT1"); + if (irq > 0) { + data->inc_reg = KX022A_REG_INC1; + data->ien_reg = KX022A_REG_INC4; + } else { + irq = fwnode_irq_get_byname(dev_fwnode(dev), "INT2"); + if (irq < 0) + return dev_err_probe(dev, irq, "No suitable IRQ\n"); + + data->inc_reg = KX022A_REG_INC5; + data->ien_reg = KX022A_REG_INC6; + } + + data->regmap = regmap; + data->dev = dev; + data->irq = irq; + data->odr_ns = KX022A_DEFAULT_PERIOD_NS; + mutex_init(&data->mutex); + + idev->channels = kx022a_channels; + idev->num_channels = ARRAY_SIZE(kx022a_channels); + idev->name = "kx022-accel"; + idev->info = &kx022a_info; + idev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; + idev->available_scan_masks = kx022a_scan_masks; + + /* Read the mounting matrix, if present */ + ret = iio_read_mount_matrix(dev, &data->orientation); + if (ret) + return ret; + + /* The sensor must be turned off for configuration */ + ret = kx022a_turn_off_lock(data); + if (ret) + return ret; + + ret = kx022a_chip_init(data); + if (ret) { + mutex_unlock(&data->mutex); + return ret; + } + + ret = kx022a_turn_on_unlock(data); + if (ret) + return ret; + + udelay(100); + ret = devm_iio_triggered_buffer_setup_ext(dev, idev, + &iio_pollfunc_store_time, + kx022a_trigger_handler, + IIO_BUFFER_DIRECTION_IN, + &kx022a_buffer_ops, + kx022a_fifo_attributes); + + if (ret) + return dev_err_probe(data->dev, ret, + "iio_triggered_buffer_setup_ext FAIL %d\n", + ret); + indio_trig = devm_iio_trigger_alloc(dev, "%sdata-rdy-dev%d", idev->name, + iio_device_id(idev)); + if (!indio_trig) + return -ENOMEM; + + data->trig = indio_trig; + + indio_trig->ops = &kx022a_trigger_ops; + iio_trigger_set_drvdata(indio_trig, data); + + ret = devm_iio_trigger_register(dev, indio_trig); + if (ret) + return dev_err_probe(data->dev, ret, + "Trigger registration failed\n"); + + ret = devm_iio_device_register(data->dev, idev); + if (ret < 0) + return dev_err_probe(dev, ret, + "Unable to register iio device\n"); + + ret = devm_request_threaded_irq(data->dev, irq, kx022a_irq_handler, + &kx022a_irq_thread_handler, + IRQF_ONESHOT, "kx022", idev); + if (ret) + return dev_err_probe(data->dev, ret, "Could not request IRQ\n"); + + return ret; +} +EXPORT_SYMBOL_GPL(kx022a_probe_internal); + +module_param(g_kx022a_use_buffer, bool, 0); +MODULE_PARM_DESC(g_kx022a_use_buffer, + "Buffer samples. Use at own risk. Fifo must not overflow"); + +MODULE_DESCRIPTION("ROHM/Kionix KX022A accelerometer driver"); +MODULE_AUTHOR("Matti Vaittinen "); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/accel/kionix-kx022a.h b/drivers/iio/accel/kionix-kx022a.h new file mode 100644 index 000000000000..0899fd52fa55 --- /dev/null +++ b/drivers/iio/accel/kionix-kx022a.h @@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2022 ROHM Semiconductors + * + * ROHM/KIONIX KX022A accelerometer driver + */ + +#ifndef _KX022A_H_ +#define _KX022A_H_ + +#include + +#define KX022A_REG_WHO 0x0f +#define KX022A_ID 0xc8 + +#define KX022A_REG_CNTL2 0x19 +#define KX022A_MASK_SRST BIT(7) +#define KX022A_REG_CNTL 0x18 +#define KX022A_MASK_PC1 BIT(7) +#define KX022A_MASK_RES BIT(6) +#define KX022A_MASK_DRDY BIT(5) +#define KX022A_MASK_GSEL GENMASK(4, 3) +#define KX022A_GSEL_SHIFT 3 +#define KX022A_GSEL_2 0x0 +#define KX022A_GSEL_4 BIT(3) +#define KX022A_GSEL_8 BIT(4) +#define KX022A_GSEL_16 GENMASK(4, 3) + +#define KX022A_REG_INS2 0x13 +#define KX022A_MASK_INS2_DRDY BIT(4) +#define KX122_MASK_INS2_WMI BIT(5) + +#define KX022A_REG_XHP_L 0x0 +#define KX022A_REG_XOUT_L 0x06 +#define KX022A_REG_YOUT_L 0x08 +#define KX022A_REG_ZOUT_L 0x0a +#define KX022A_REG_COTR 0x0c +#define KX022A_REG_TSCP 0x10 +#define KX022A_REG_INT_REL 0x17 + +#define KX022A_REG_ODCNTL 0x1b + +#define KX022A_REG_BTS_WUF_TH 0x31 +#define KX022A_REG_MAN_WAKE 0x2c + +#define KX022A_REG_BUF_CNTL1 0x3a +#define KX022A_MASK_WM_TH GENMASK(6, 0) +#define KX022A_REG_BUF_CNTL2 0x3b +#define KX022A_MASK_BUF_EN BIT(7) +#define KX022A_MASK_BRES16 BIT(6) +#define KX022A_REG_BUF_STATUS_1 0x3c +#define KX022A_REG_BUF_STATUS_2 0x3d +#define KX022A_REG_BUF_CLEAR 0x3e +#define KX022A_REG_BUF_READ 0x3f +#define KX022A_MASK_ODR GENMASK(3, 0) +#define KX022A_ODR_SHIFT 3 +#define KX022A_FIFO_MAX_WMI_TH 41 + +#define KX022A_REG_INC1 0x1c +#define KX022A_REG_INC5 0x20 +#define KX022A_REG_INC6 0x21 +#define KX022A_MASK_IEN BIT(5) +#define KX022A_MASK_IPOL BIT(4) +#define KX022A_IPOL_LOW 0 +#define KX022A_IPOL_HIGH KX022A_MASK_IPOL1 +#define KX022A_MASK_ITYP BIT(3) +#define KX022A_ITYP_PULSE KX022A_MASK_ITYP +#define KX022A_ITYP_LEVEL 0 + +#define KX022A_REG_INC4 0x1f +#define KX022A_MASK_WMI BIT(5) + +#define KX022A_REG_SELF_TEST 0x60 +#define KX022A_MAX_REGISTER 0x60 + +struct device; + +int kx022a_probe_internal(struct device *dev); +extern const struct regmap_config kx022a_regmap; + +#endif From patchwork Thu Oct 6 14:38:34 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Matti Vaittinen X-Patchwork-Id: 13000452 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id E3633C433F5 for ; Thu, 6 Oct 2022 14:38:51 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230336AbiJFOit (ORCPT ); Thu, 6 Oct 2022 10:38:49 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33880 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231591AbiJFOis (ORCPT ); Thu, 6 Oct 2022 10:38:48 -0400 Received: from mail-lj1-x229.google.com (mail-lj1-x229.google.com [IPv6:2a00:1450:4864:20::229]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 8FACEABF20; Thu, 6 Oct 2022 07:38:42 -0700 (PDT) Received: by mail-lj1-x229.google.com with SMTP id q17so2447367lji.11; Thu, 06 Oct 2022 07:38:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=in-reply-to:content-disposition:mime-version:references:message-id :subject:cc:to:from:date:from:to:cc:subject:date; bh=4Sm8s8c+2l2jCb6IDRMALO+KLZhQiZAZcnY2ahLVTf8=; b=YXp15G7EtvmIjMpScFLm6MWIauhQnkHAPtqZv/KXDgWRzVLm+4p8FBNLu7HAgh41qr n5dozBqCScAo4+qdmZvlGbT/zmsrsJwYlwyYLektvgNz4b1pqPqIv017YSSNyps1x2CB JSGPPUs5wlOaG4HDzrumj6CGEII/FDdZduN//tzX9vMXQrFptw5HPc/UYI3cb3XJSutR sC0x5uFIE5AGErtQ+OrpugGqcPCId+I7y9BLxxHNJIiEj+wNABLTKZQ9mApEcsaOqInS 3jjmHmm6wwL5S0cz7tMY7z+0pxNHSSNQcIklYnOdotu84Y+cRzdPTaynJIk1Y/a7XcQv z5Yw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=in-reply-to:content-disposition:mime-version:references:message-id :subject:cc:to:from:date:x-gm-message-state:from:to:cc:subject:date; bh=4Sm8s8c+2l2jCb6IDRMALO+KLZhQiZAZcnY2ahLVTf8=; b=zmCufkuMXm5vLHwqpJIiqAff8enDkrLaPLqnA5lXdSxuhsSUUrtXglSDhN+6BZX4Zg 42yK/BrRIoxaTpw+GXxcgAyIqSgUfekdns99uwESGClOIGlSyaCKhayBdx++bEypnHlv yDv005FQbU7y8UZ9taSxnjS7U+olGkYHlvJIeUcxj8KnNfXLsasaJIkVwoLDvs4pKjjv lNOWdaCnuW18z+jqOOA/S3UnjZYTYPXVEgWZ806Ff3Ltf8WeWXpKeSbz0/YAlfTlLMR9 Cil7b8iIjMI6jyTZ1rZNxd1mhKMbCYwaHT5qjXtBfAZYX+WNXYBj1MyuO1fKHCiAo7ns sjuw== X-Gm-Message-State: ACrzQf2mEdYm2K9ihUhwgKBkECKJwQfoea+taFSdVd0waCajncAKxwgy WhO7RRbPEU4CfTXF3ln09oA= X-Google-Smtp-Source: AMsMyM5hZErXB7J0DZe0eFjXNqQDe51vaUZChbt0mrmZ3z+HbZkgDRfCkw0KEj5GA9ainxWqELlvrw== X-Received: by 2002:a05:651c:1949:b0:26d:cf6d:54a6 with SMTP id bs9-20020a05651c194900b0026dcf6d54a6mr1924384ljb.3.1665067120934; Thu, 06 Oct 2022 07:38:40 -0700 (PDT) Received: from dc75zzyyyyyyyyyyyyyyt-3.rev.dnainternet.fi (dc75zzyyyyyyyyyyyyyyt-3.rev.dnainternet.fi. [2001:14ba:16f3:4a00::1]) by smtp.gmail.com with ESMTPSA id g23-20020a2eb5d7000000b0026e059a3455sm81670ljn.51.2022.10.06.07.38.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 06 Oct 2022 07:38:40 -0700 (PDT) Date: Thu, 6 Oct 2022 17:38:34 +0300 From: Matti Vaittinen To: Matti Vaittinen , Matti Vaittinen Cc: Jonathan Cameron , Lars-Peter Clausen , Rob Herring , Krzysztof Kozlowski , Matti Vaittinen , Andy Shevchenko , Jagath Jog J , Nikita Yushchenko , Cosmin Tanislav , linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [RFC PATCH v2 5/5] MAINTAINERS: Add KX022A maintainer entry Message-ID: <08ccdc318b448eb69c82efc82adcd044536df4af.1665066397.git.mazziesaccount@gmail.com> References: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Precedence: bulk List-ID: X-Mailing-List: linux-iio@vger.kernel.org Add maintainer entry for ROHM/Kionix KX022A accelerometer senor driver. Signed-off-by: Matti Vaittinen --- I can also add myself as a maintainer instead of a reviewer if it better suits iio maintainer. I however don't plan setting up my own public repository and hope the further patches will be merged via IIO tree. So, as Geert once explained to me - In that case the difference between me as a maintainer vs. a reviewer would be only really relevant to the subsystem (in this case IIO) maintainer. The subsystem maintainer who merges patches is allowed to take in changes acked by downstream maintainer w/o obligation to do thorough review. (Downstream maintainer is to be blamed if things explode :]). If ack is given by a reviewer, then the subsystem maintainer has the full responsibility and should always do the review. Or - this is how I remember our discussion went - feel free to correct me if I am wrong :] In any case - please let me know if you'd rather see M: not R: in front of my name for the kx022a. --- MAINTAINERS | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index f5ca4aefd184..641b4fc2e5e2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11356,6 +11356,11 @@ F: drivers/mfd/khadas-mcu.c F: include/linux/mfd/khadas-mcu.h F: drivers/thermal/khadas_mcu_fan.c +KIONIX/ROHM KX022A ACCELEROMETER +R: Matti Vaittinen +S: Supported +F: drivers/iio/accel/kionix-kx022a* + KMEMLEAK M: Catalin Marinas S: Maintained