From patchwork Wed Sep 21 11:42:29 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Matti Vaittinen X-Patchwork-Id: 12983623 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 53A40C6FA8E for ; Wed, 21 Sep 2022 11:42:43 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229785AbiIULmm (ORCPT ); Wed, 21 Sep 2022 07:42:42 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53078 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229437AbiIULml (ORCPT ); Wed, 21 Sep 2022 07:42:41 -0400 Received: from mail-lj1-x232.google.com (mail-lj1-x232.google.com [IPv6:2a00:1450:4864:20::232]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D44CB8E98B; Wed, 21 Sep 2022 04:42:39 -0700 (PDT) Received: by mail-lj1-x232.google.com with SMTP id b6so6616654ljr.10; Wed, 21 Sep 2022 04:42:39 -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=C/v5U/CH8UoPmKrRTrmN93JfcftB0DZK+X0SeEjsOJU=; b=FjDC6VC8Vfr90AsitmdUCUriNQqGtcpcGGsZoZGswt0qn0vL0jsTE8qbiZ6+AAZfzt Ig0fb3QcylFmXP4i5zhQrzwQmldDocnymgCwpMbSMLRZd0rNUBpOjDya4ihrYS6AHhIl f5o2YJ6lvnGFDDPDVLZq93/Fpo4boQ0TgSafx+4p+4LFpz136bTixndvOx1CdA3HLYt8 DYoYmWuu+yHa2Ok38oFwEA+LWKWJfbv2sJuLudWqK6EyezFjMcu2NlR3fBUW578wIoB/ Cb6vNYC8otbyKT3aduEYSFx2xVqXqBY0n0oupDNriteXdVSM8CDXFJR+p8FdqVyYXois 2GQQ== 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=C/v5U/CH8UoPmKrRTrmN93JfcftB0DZK+X0SeEjsOJU=; b=rNuN9cGYH9U7k8K3cSTmVq1huUcJi+kSrvkuMCtAphgWbHZGumj3TPuD7nYbeljfQx mzn87vCDNP97m9Lj9+qT1sHrJUfOVVkGlWSWZuykrYN2KTe9CYuktlaRNFTQeKf3lc0b peq++7nn9Myydhjg+yKPzhcq0P7WW4TqWvrsLJXnVkbZKm5srSHjSAMZ1+PxCbiS7ah/ 00rCkjg6g/xtmv95stoprvzD5hhvGEF2Gci+UK9KOeStAqUvWzWaKACLyJhFDIrrIjHJ 4zp2mj9zIY93kP3I7uwCb2l8XELAMRZWnIKEmjx6W8fosXK9qKBrmQ1o5E4/gV+HryoX b9uw== X-Gm-Message-State: ACrzQf3H/d2eXaKjfVDNBWUHJOH7Pc40VyyT+4PE23B9T4iCxAJXqTXR 9qMmlLERXc383T8ZA3TTTnI= X-Google-Smtp-Source: AMsMyM4duOwh8VtqRs8Ei1L3d8dgW9TrrSDI7hgpG5kVPEO9AfhhZs6bmPrRry9pFQMa4jai5zxneg== X-Received: by 2002:a2e:924e:0:b0:26c:5dbc:2d80 with SMTP id v14-20020a2e924e000000b0026c5dbc2d80mr2498293ljg.290.1663760558152; Wed, 21 Sep 2022 04:42:38 -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 q14-20020ac2528e000000b0049480c69d98sm386790lfm.239.2022.09.21.04.42.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 21 Sep 2022 04:42:37 -0700 (PDT) Date: Wed, 21 Sep 2022 14:42:29 +0300 From: Matti Vaittinen To: Matti Vaittinen , Matti Vaittinen Cc: Jonathan Cameron , Lars-Peter Clausen , Rob Herring , Krzysztof Kozlowski , Matti Vaittinen , Andy Shevchenko , Nikita Yushchenko , Cosmin Tanislav , Jagath Jog J , linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [RFC PATCH 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) --- Please note - this patch is already in Mark's tree. It's here just to allow compilation of the series on top of v6.0-rc4 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 Wed Sep 21 11:43:49 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Matti Vaittinen X-Patchwork-Id: 12983624 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 DF43FECAAD8 for ; Wed, 21 Sep 2022 11:44:02 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229921AbiIULoA (ORCPT ); Wed, 21 Sep 2022 07:44:00 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53978 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229891AbiIULn6 (ORCPT ); Wed, 21 Sep 2022 07:43:58 -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 A746C901A9; Wed, 21 Sep 2022 04:43:56 -0700 (PDT) Received: by mail-lf1-x12c.google.com with SMTP id x27so8835573lfu.0; Wed, 21 Sep 2022 04:43:56 -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=nYLJNjXG8obWRA4UkgeigyjnB3GICD0oi50GKkfipRA=; b=gw2EksPevUhToB1Hp9odD6rDpnl5vToFCK4nV/Rjs+qZyVKxJCMfippRWva9eatKAB TQy0LkENf1UE5HKKNloOmx9a/FkiaZYRPa3dxpZPEnbp05elQ1oL8PlD8p85IXt23e7W aHYDrLp3wDechKe74JPvheXL7jez5w9EyBoD1+EQDyBo2OwZy6SAFLuKnzAK/9C5reAz 3sIKi+z8izQhFI8FM5kupq0/UwH9/uhifdvgbDDTBu3xuWBgb7tXe62Fbu0mXBezTpIt dd15j5CkUkV3Y0aegVF0FrvYcu1FSxKaB0ixy9g0lclagRjqiLlb1ubI41aveC3bxO0H ddxg== 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=nYLJNjXG8obWRA4UkgeigyjnB3GICD0oi50GKkfipRA=; b=43UU8D4QfJbYFiHApIpUnqaKk9IVl+Q2cy+xGedZW/toTI8RsoMbdqBaUquEXldgbT OA2DfG7veON+uulLKkTiuhdF63Ag9mpU5xnywdsIiAKboRwQ4Ln3fwLowM7q1R9lidZp OclG860mQfiYJ/S5cDL8SeTIrcTXReSH+mnoZVRet73EA49nN03tEim9zAz5JYE25UhH NKZhyJUiCf3VezsiCdzyisG/CQZLjPHq3F1Nx8SCXmj1AcIJmwQwoaAvF4CDkIyxDwez +A9CCf4HN7lZr60oybfwXrckaMN/ilNecql+bmU6N9ERfToboSe/3/sva1vaXQUZ0QTO mI/g== X-Gm-Message-State: ACrzQf29DXcBD6Q0EDjvc3ZFwd0zPaLW7ueYW0wG7I0kAPNDFWyPheLa e5PhTGrdn1B6cvCxw3qLfMg= X-Google-Smtp-Source: AMsMyM7lFMQgCSqon4MHjcAjgGY1jmrE5V96sfsTMn1HNMP+atJv3Epu3t+kQI+3VDmj4/thEUe5eg== X-Received: by 2002:ac2:4bc1:0:b0:49f:298f:8fed with SMTP id o1-20020ac24bc1000000b0049f298f8fedmr10543585lfq.212.1663760634831; Wed, 21 Sep 2022 04:43:54 -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 q4-20020ac25fc4000000b0049936272173sm390247lfg.204.2022.09.21.04.43.52 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 21 Sep 2022 04:43:53 -0700 (PDT) Date: Wed, 21 Sep 2022 14:43:49 +0300 From: Matti Vaittinen To: Matti Vaittinen , Matti Vaittinen Cc: Jonathan Cameron , Lars-Peter Clausen , Rob Herring , Krzysztof Kozlowski , Matti Vaittinen , Andy Shevchenko , Nikita Yushchenko , Cosmin Tanislav , Jagath Jog J , linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [RFC PATCH 2/5] regulator: Add devm helpers for get and enable Message-ID: <41d2c0a9af344b7fa4eafe8d63e3c3458059fa84.1663760018.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 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 --- Please note - this patch is already in Mark's tree. It's here just to allow compilation of the series on top of v6.0-rc4 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 Wed Sep 21 11:45:06 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Matti Vaittinen X-Patchwork-Id: 12983625 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 24564C6FA8E for ; Wed, 21 Sep 2022 11:45:23 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229891AbiIULpV (ORCPT ); Wed, 21 Sep 2022 07:45:21 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:56908 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229932AbiIULpR (ORCPT ); Wed, 21 Sep 2022 07:45:17 -0400 Received: from mail-lj1-x234.google.com (mail-lj1-x234.google.com [IPv6:2a00:1450:4864:20::234]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 0FDD6883EE; Wed, 21 Sep 2022 04:45:15 -0700 (PDT) Received: by mail-lj1-x234.google.com with SMTP id j24so6149670lja.4; Wed, 21 Sep 2022 04:45:14 -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=sX0VGnOv15V/mUkTpC6ThB5wqXUnNiQX+K/+Fn8NYpU=; b=fbGo9iAGa8xYTMTxsMpjRU3Uyt7r68gnZOtk8Uf3A8j3VogQDK8ujH1FdOZfpHICdX pka517jO91fWsth9u9H7FMl0EeLKkKjQt62/s3W/tt86wwTcHRNx6tj8dq8qo+4lk4Xw MhJkNOa0E/SOayM6O2bQjsH5qkZIfLY71XBoppBK6w0PgLSpKp2w6v+PFkxqAgzdSYaI i6fDO1tNsw8UJyzGZRfiPGIQ3LS51TPZM1umo2x11R1dRLrt5EyKt2TV/1CHll9Kn/yn /Q5unIy5OhZSV6o45NZ1kWqAir6wBt+kXoxr+MnnOMxMVhJv+lXKMlsISQ8tPRnPfjXG EqBA== 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=sX0VGnOv15V/mUkTpC6ThB5wqXUnNiQX+K/+Fn8NYpU=; b=dRD4dG0j+Qe08Uk0OXLmBcmGHFCOozawVTOhlkuFWJRO3KYPxKlHVKkhxFvwUcUDkG 5nVre3hkZQnzczHCwTblZd7YMVBY2FU8ihOn6JoqKlb8MzIG8ykbpM1AaQPRU7lCQHXS TI+n1LNnlaeIoX3J3rB2n/01AMvGojdqeo6LZbIDOiYGvr8nSPBQSnU/uKIZKRo7Phs9 w+w5ZAnyNsaMv1JTr+3aAegSii0qSlyZ0fiDjP1cDPoMEojYs5yx+HR1XVbMLpk2LgZu wX+cHyoJIiTQ2RKznIN/EgVeQPvnll8sCV72SaKaYUipDm4uM1uIcdvhKDC6eUsT1t7d H1ZQ== X-Gm-Message-State: ACrzQf0jbMIhUEQyaG2cIF4gotXdW8A2d5u6Ji7QUiMH5P9yU3vVpajC pgcyhfzlCs7Apl3f308Svks= X-Google-Smtp-Source: AMsMyM6qW906SLuKhOavxtb0aSlKqzjhc/qtM3Yv4souFhWHPfMfckzX2QfJL3MKHZfk6C/o7YxZPQ== X-Received: by 2002:a05:651c:222c:b0:25f:e654:36e3 with SMTP id y44-20020a05651c222c00b0025fe65436e3mr8445192ljq.20.1663760713051; Wed, 21 Sep 2022 04:45:13 -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 o17-20020ac24e91000000b0049964f68457sm388161lfr.262.2022.09.21.04.45.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 21 Sep 2022 04:45:12 -0700 (PDT) Date: Wed, 21 Sep 2022 14:45:06 +0300 From: Matti Vaittinen To: Matti Vaittinen , Matti Vaittinen Cc: Jonathan Cameron , Lars-Peter Clausen , Rob Herring , Krzysztof Kozlowski , Matti Vaittinen , Andy Shevchenko , Nikita Yushchenko , Cosmin Tanislav , Jagath Jog J , linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [RFC PATCH 3/5] dt-bindings: iio: Add KX022A accelerometer 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 KX022A is a 3-axis Accelerometer from ROHM/Kionix. The senor 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 fatures. 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 if rest of the features are to be supported. Signed-off-by: Matti Vaittinen --- .../bindings/iio/accel/kionix,kx022a.yaml | 58 +++++++++++++++++++ 1 file changed, 58 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..62a0c7991a62 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/accel/kionix,kx022a.yaml @@ -0,0 +1,58 @@ +# 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 bindings + +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: kionix,kx022a + + reg: + description: + I2C slave address or SPI chip-select. + maxItems: 1 + + interrupts: + maxItems: 1 + + 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>; + + io_vdd-supply = <&iovdd>; + vdd-supply = <&vdd>; + }; + }; From patchwork Wed Sep 21 11:45:35 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Matti Vaittinen X-Patchwork-Id: 12983626 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 5930DECAAD8 for ; Wed, 21 Sep 2022 11:45:51 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229751AbiIULpt (ORCPT ); Wed, 21 Sep 2022 07:45:49 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58306 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229596AbiIULps (ORCPT ); Wed, 21 Sep 2022 07:45:48 -0400 Received: from mail-lf1-x129.google.com (mail-lf1-x129.google.com [IPv6:2a00:1450:4864:20::129]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id ACD0188DC2; Wed, 21 Sep 2022 04:45:43 -0700 (PDT) Received: by mail-lf1-x129.google.com with SMTP id j16so8766287lfg.1; Wed, 21 Sep 2022 04:45:43 -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=vE9tjMqEQ2e8hncasbyM91Clo6RABGo2iHZ34XvX7kg=; b=oWAt8UVKA84oxHOICYMThXQ/rCX9AvtAEj/Adag5nNxlb5UAKyZiGqz5XPbj+2qZmo A8rEcU1Tus7QV4CGHQ8wl7D8GFy5Y3DqdewdHyLt3yB2tx5lnw+EBbLMkNVJDx+2vKE+ lhxELyk+YO8jz3kKYcBT6qsKUZkEah32LHYY+hRPpKDKuEJKuKFwVB6/vZ98kX45qFzJ LCpzvp43MWsp/29I00pRHYhTS8yCCpYdBe8hR9ExYhjLp7x33g7cIyLceBVu0YtoXEc5 74Y+VjqkWtBMN6PRe+kuBTMxlKKAIURt88v9BOT2zd7Gttk7/Qa1L2z5JCj5ofYPKXFA 6xZQ== 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=vE9tjMqEQ2e8hncasbyM91Clo6RABGo2iHZ34XvX7kg=; b=evsSJaHdUC14rjTdT4PW32w+jAAWzX/EYVkqbsoUwRfrBDD1WJqkdQqiOgt+xaZ/CB EQr78TEygohmvrI3AZf7KmkPTnGFiHt4BtHhclE4cM13PdbiTnzvsdOQFbNIjL8/RlF4 Rz0Q1x1UqP+fGS9ozUfzK15t7HF08OX0wFwLtfyIepmkVcnEdxRW14/LYoE84oS6rcGe nLZtfshuWpVrItOws0ywpu2n6y/PbU0K1TMfup+nNA1pI7HfL5URTsNhu59kXHcwE6JY wQDXJcMIVD4mKVupGHHA4VGb4b5WVyv3HU32ZwBlBaGUNA6ayf8VfyjqV/HRmH4vjraY xdjw== X-Gm-Message-State: ACrzQf0WK7wacV6CuQ40G+X8uyriOwl7QV8EXF0X060vkzZIXayOQSAI Fw4XI1Q7dljvn0wegpNvKnc= X-Google-Smtp-Source: AMsMyM5ucf479N2qxdPdDxHz8GvYaGfIvO6611X3eSs5tOz5kkOFGYjwv4uetGtwth5cUHlgVF7/vQ== X-Received: by 2002:a05:6512:3e14:b0:499:1f71:1680 with SMTP id i20-20020a0565123e1400b004991f711680mr9873594lfv.114.1663760741589; Wed, 21 Sep 2022 04:45:41 -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 o7-20020ac25e27000000b00497a8f04905sm389363lfg.251.2022.09.21.04.45.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 21 Sep 2022 04:45:40 -0700 (PDT) Date: Wed, 21 Sep 2022 14:45:35 +0300 From: Matti Vaittinen To: Matti Vaittinen , Matti Vaittinen Cc: Jonathan Cameron , Lars-Peter Clausen , Rob Herring , Krzysztof Kozlowski , Matti Vaittinen , Andy Shevchenko , Nikita Yushchenko , Cosmin Tanislav , Jagath Jog J , linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [RFC PATCH 4/5] iio: accel: Support Kionix/ROHM KX022A accelerometer 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 KX022A is a 3-axis Accelerometer from ROHM/Kionix. The senor 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 fatures. 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 amount of data in FIFO was reported to be 0xff bytes, which equals to 42,5 samples. Specification says the FIFO can hold maximum of 41 samples in HiRes mode. Also, at least once the FIFO was stuck in a state where reading data from hwardware 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 --- drivers/iio/accel/Kconfig | 23 + drivers/iio/accel/Makefile | 3 + drivers/iio/accel/kionix-kx022a-i2c.c | 52 ++ drivers/iio/accel/kionix-kx022a-spi.c | 50 ++ drivers/iio/accel/kionix-kx022a.c | 1149 +++++++++++++++++++++++++ drivers/iio/accel/kionix-kx022a.h | 76 ++ 6 files changed, 1353 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..12ad6bcb2c76 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -409,6 +409,29 @@ 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. See IIO_KX022A_I2C if + you want to enable support for the KX022A connected via I2C. + +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. See IIO_KX022A_SPI if + you want to enable support for the KX022A connected via SPI. + 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..2dbf8a33b84b --- /dev/null +++ b/drivers/iio/accel/kionix-kx022a-i2c.c @@ -0,0 +1,52 @@ +// 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, i2c->irq); +} + +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..007f4b726d8f --- /dev/null +++ b/drivers/iio/accel/kionix-kx022a-spi.c @@ -0,0 +1,50 @@ +// 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, spi->irq); +} + +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", + }, + .probe = kx022a_spi_probe, +}; + +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..9f9e0d7efc09 --- /dev/null +++ b/drivers/iio/accel/kionix-kx022a.c @@ -0,0 +1,1149 @@ +// 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 MIN_ODR_INTERVAL_MS 5 +#define MAX_ODR_INTERVAL_MS 1280 +#define NUM_SUPPORTED_ODR 9 + +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, + }, { + .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_trigger { + struct kx022a_data *data; + struct iio_trigger *indio_trig; + bool enabled; + const char *name; +}; + +struct kx022a_data { + int irq; + struct regmap *regmap; + struct kx022a_trigger trigger; + 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]; + 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), \ + .ext_info = kx022a_ext_info, \ + .address = KX022A_REG_##axis##OUT_L, \ + .scan_index = index, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .shift = 0, \ + .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 IIO_CONST_ATTR_SAMP_FREQ_AVAIL("0.78 1.563 3.125 6.25 12.5 25 50 100 200"); +static IIO_CONST_ATTR(scale_available, + "598.550415 1197.10083 2394.20166 4788.40332"); + +static struct attribute *kx022a_attributes[] = { + &iio_const_attr_sampling_frequency_available.dev_attr.attr, + &iio_const_attr_scale_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group kx022a_attrs_group = { + .attrs = kx022a_attributes, +}; + +struct kx022a_tuple { + int val; + int val2; +}; + +/* + * 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 struct kx022a_tuple kx022a_scale_table[] = { + { 598, 550415 }, { 1197, 100830 }, { 2394, 201660 }, { 4788, 403320 } +}; + +/* + * ODR (output data rate) table. First value represents the integer portion of + * frequency (Hz), and the second value is the decimal part (uHz). + * Eg, 0.78 Hz, 1.563 Hz, ... + */ +#define KX922A_DEFAULT_PERIOD 20000000 +static const struct kx022a_tuple kx022a_accel_samp_freq_table[] = { + { 0, 780000 }, { 1, 563000 }, { 3, 125000 }, { 6, 250000 }, + { 12, 500000 }, { 25, 0 }, { 50, 0 }, { 100, 0 }, { 200, 0 } +}; +static const unsigned int kx022a_odrs[] = { 1282051282, 639795266, 320000000, + 160000000, 80000000, 40000000, 20000000, 10000000, 5000000 }; + +/* Find index of tuple matching the given values */ +static int kx022a_find_tuple_index(const struct kx022a_tuple *tuples, int n, + int val, int val2) +{ + while (n-- > 0) + if (val == tuples[n].val && tuples[n].val2 == val2) + return n; + + return -EINVAL; +} + +static void kx022a_reg2freq(unsigned int val, int *val1, int *val2) +{ + *val1 = kx022a_accel_samp_freq_table[val & KX022A_MASK_ODR].val; + *val2 = kx022a_accel_samp_freq_table[val & KX022A_MASK_ODR].val2; +} + +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].val; + *val2 = kx022a_scale_table[val].val2; +} + +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). + */ + mutex_lock(&data->mutex); + if (iio_buffer_enabled(idev)) { + ret = -EBUSY; + goto unlock_out; + } + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + ret = kx022a_find_tuple_index(&kx022a_accel_samp_freq_table[0], + ARRAY_SIZE(kx022a_accel_samp_freq_table), + val, val2); + /* Configure if we found valid ODR */ + if (ret >= 0) { + int odr = ret; + + ret = __kx022a_turn_off_unlocked(data); + if (ret) + goto unlock_out; + + ret = regmap_update_bits(data->regmap, KX022A_REG_ODCNTL, + KX022A_MASK_ODR, odr); + data->odr_ns = kx022a_odrs[odr]; + __kx022a_turn_on_unlocked(data); + } + break; + case IIO_CHAN_INFO_SCALE: + ret = kx022a_find_tuple_index(&kx022a_scale_table[0], + ARRAY_SIZE(kx022a_scale_table), + val, val2); + /* Configure if we found valid scale */ + if (ret >= 0) { + ret = __kx022a_turn_off_unlocked(data); + if (ret) + goto unlock_out; + + ret = regmap_update_bits(data->regmap, KX022A_REG_CNTL, + KX022A_MASK_GSEL, + ret << KX022A_GSEL_SHIFT); + __kx022a_turn_on_unlocked(data); + } + default: + ret = -EINVAL; + } + +unlock_out: + mutex_unlock(&data->mutex); + + 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_fifo_report_data(struct kx022a_data *data, void *buffer, + int samples) +{ + int ret, fifo_bytes; + + fifo_bytes = samples * KX022A_FIFO_SAMPLES_SIZE_BYTES; + ret = regmap_noinc_read(data->regmap, KX022A_REG_BUF_READ, + buffer, fifo_bytes); + if (ret) + dev_err(data->dev, "FIFO read failed %d\n", ret); + + return ret; +} + +static int kx022a_get_axis(struct kx022a_data *data, + struct iio_chan_spec const *chan, + int *val) +{ + u16 raw_val; + int ret; + + ret = regmap_bulk_read(data->regmap, chan->address, &raw_val, + sizeof(raw_val)); + if (ret) + return ret; + *val = raw_val; + + 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; + + mutex_lock(&data->mutex); + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (iio_buffer_enabled(idev)) { + ret = -EBUSY; + goto error_ret; + } + + ret = kx022a_get_axis(data, chan, val); + break; + case IIO_CHAN_INFO_SAMP_FREQ: + { + ret = regmap_read(data->regmap, KX022A_REG_ODCNTL, ®val); + if (ret) + goto error_ret; + + if ((regval & KX022A_MASK_ODR) > + ARRAY_SIZE(kx022a_accel_samp_freq_table)) { + dev_err(data->dev, "Invalid ODR\n"); + ret = -EINVAL; + goto error_ret; + } + + 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) + goto error_ret; + + kx022a_reg2scale(regval, val, val2); + + ret = IIO_VAL_INT_PLUS_MICRO; + break; + } + +error_ret: + mutex_unlock(&data->mutex); + + return ret; +}; + +static int kx022a_validate_trigger(struct iio_dev *idev, + struct iio_trigger *trig) +{ + struct kx022a_data *data = iio_priv(idev); + + if (data->trigger.indio_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 owerflow 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; + } + + ret = kx022a_fifo_report_data(data, buffer, count); + 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, + .attrs = &kx022a_attrs_group, + + .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_IEN1 | KX022A_MASK_IPOL1 | + KX022A_MASK_ITYP; + int val = KX022A_MASK_IEN1 | KX022A_IPOL_LOW | + KX022A_ITYP_LEVEL; + int ret; + + ret = regmap_update_bits(data->regmap, KX022A_REG_INC1, mask, val); + if (ret) + return ret; + + mask = KX022A_MASK_INS2_DRDY | KX122_MASK_INS2_WMI; + + return regmap_set_bits(data->regmap, KX022A_REG_INC4, mask); +} + +static int kx022a_fifo_disable(struct kx022a_data *data) +{ + int ret = 0; + + /* PC1 to 0 */ + ret = kx022a_turn_off_lock(data); + if (ret) + return ret; + + ret = regmap_clear_bits(data->regmap, KX022A_REG_INC4, + KX022A_MASK_WMI1); + if (ret) + goto unlock_out; + + /* disable buffer */ + 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; + + /* Write WMI 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, KX022A_REG_INC4, + KX022A_MASK_WMI1); + 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 triggers, then the IRQs should be handled by trigger + * enable and buffer is not used but we just add results to buffer + * when data-ready triggers. + */ + if (iio_device_get_current_mode(idev) == INDIO_BUFFER_TRIGGERED) + return 0; + + 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; + + mutex_lock(&data->mutex); + 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: + mutex_unlock(&data->mutex); + 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); + bool ack = false; + + data->old_timestamp = data->timestamp; + data->timestamp = iio_get_time_ns(idev); + + if (data->trigger.enabled) { + iio_trigger_poll(data->trigger.indio_trig); + ack = true; + } + + if (data->state & KX022A_STATE_FIFO) + return IRQ_WAKE_THREAD; + + if (ack) { + /* + * The IRQ is not acked until data is read. We need to disable + * the IRQ in order to schedule the trigger thread. Enabling + * is done in reenable. + * + * It would be possible to set the IRQ to 50uS pulse. If we are + * losing data due to the disabled IRQ we can evaluate the + * option of using edge triggered IRQs with the pulse mode. + */ + disable_irq_nosync(irq); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +/* Read the data from the fifo and fill IIO buffers */ +static irqreturn_t kx022a_irq_thread_handler(int irq, void *private) +{ + struct iio_dev *idev = private; + struct kx022a_data *data = iio_priv(idev); + bool ack = false; + int ret; + + mutex_lock(&data->mutex); + + if (data->state & KX022A_STATE_FIFO) { + ret = __kx022a_fifo_flush(idev, KX022A_FIFO_LENGTH, true); + if (ret > 0) + ack = true; + } + /* + * 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). + */ + if (ack) + ret = IRQ_HANDLED; + else + ret = IRQ_NONE; + + mutex_unlock(&data->mutex); + + return ret; +} + +static int kx022a_trigger_set_state(struct iio_trigger *trig, + bool state) +{ + struct kx022a_trigger *t = iio_trigger_get_drvdata(trig); + struct kx022a_data *data = t->data; + int ret = 0; + + mutex_lock(&data->mutex); + + if (t->enabled == state) + goto unlock_out; + + ret = __kx022a_turn_off_unlocked(data); + if (ret) + goto unlock_out; + + t->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 void kx022a_trig_reen(struct iio_trigger *trig) +{ + struct kx022a_trigger *t = iio_trigger_get_drvdata(trig); + struct kx022a_data *data = t->data; + + enable_irq(data->irq); +} + +static const struct iio_trigger_ops kx022a_trigger_ops = { + .set_trigger_state = kx022a_trigger_set_state, + .reenable = kx022a_trig_reen, +}; + +static int kx022a_chip_init(struct kx022a_data *data) +{ + int ret, dummy; + + /* + * Disable IRQs because if the IRQs are left on (for example by + * a shutdown which did not deactivate the accelerometer) we do + * most probably end up flooding the system with unhandled IRQs + * and get the line disabled from SOC side. + */ + ret = regmap_write(data->regmap, KX022A_REG_INC4, 0); + if (ret) { + dev_err(data->dev, "Failed to init IRQ states\n"); + return ret; + } + + ret = kx022a_set_drdy_irq(data, false); + if (ret) { + dev_err(data->dev, "Failed to init DRDY\n"); + return ret; + } + + /* Clear any pending IRQs */ + ret = regmap_read(data->regmap, KX022A_REG_INT_REL, &dummy); + if (ret) { + dev_err(data->dev, "Failed to ACK IRQs\n"); + return ret; + } + /* set data res 16bit */ + ret = regmap_set_bits(data->regmap, KX022A_REG_BUF_CNTL2, + KX022A_MASK_BRES); + if (ret) { + dev_err(data->dev, "Failed to set data resolution\n"); + return ret; + } + + ret = kx022a_prepare_irq_pin(data); + if (ret) { + dev_err(data->dev, "Failed to configure IRQ pin\n"); + return ret; + } + + /* disable buffer */ + ret = regmap_clear_bits(data->regmap, KX022A_REG_BUF_CNTL2, + KX022A_MASK_BUF_EN); + if (ret) + return ret; + + return kx022a_drop_fifo_contents(data); +} + +int kx022a_probe_internal(struct device *dev, int irq) +{ + 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; + + 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(dev, "Failed to access sensor\n"); + return ret; + } + + if (chip_id != KX022A_ID) { + dev_err(dev, "unsupported device 0x%x\n", chip_id); + return -EINVAL; + } + + data->regmap = regmap; + data->dev = dev; + data->irq = irq; + data->odr_ns = KX922A_DEFAULT_PERIOD; + 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) + return ret; + + ret = kx022a_turn_on_unlock(data); + if (ret) + return ret; + + udelay(100); + + ret = iio_triggered_buffer_setup_ext(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->trigger.indio_trig = indio_trig; + + indio_trig->ops = &kx022a_trigger_ops; + data->trigger.data = data; + iio_trigger_set_drvdata(indio_trig, &data->trigger); + + ret = iio_trigger_register(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_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..ec89e46c6ca8 --- /dev/null +++ b/drivers/iio/accel/kionix-kx022a.h @@ -0,0 +1,76 @@ +/* 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 +#include + +#define KX022A_REG_WHO 0x0f +#define KX022A_ID 0xc8 + +#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_BRES 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_MASK_IEN1 BIT(5) +#define KX022A_MASK_IPOL1 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_WMI1 BIT(5) + +#define KX022A_REG_SELF_TEST 0x60 +#define KX022A_MAX_REGISTER 0x60 + +int kx022a_probe_internal(struct device *dev, int irq); +extern const struct regmap_config kx022a_regmap; + +#endif From patchwork Wed Sep 21 11:46:18 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Matti Vaittinen X-Patchwork-Id: 12983627 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 86322ECAAD8 for ; Wed, 21 Sep 2022 11:46:29 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229805AbiIULq2 (ORCPT ); Wed, 21 Sep 2022 07:46:28 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58964 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229774AbiIULq0 (ORCPT ); Wed, 21 Sep 2022 07:46:26 -0400 Received: from mail-lf1-x12e.google.com (mail-lf1-x12e.google.com [IPv6:2a00:1450:4864:20::12e]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id B56D88274E; Wed, 21 Sep 2022 04:46:25 -0700 (PDT) Received: by mail-lf1-x12e.google.com with SMTP id j16so8769395lfg.1; Wed, 21 Sep 2022 04:46:25 -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=cGOSHWHf030IkSak76+na5Q86ksMR3Kog0w1HGe6B7w=; b=YvwQVCEcOX+91V2ztGsfXdJEhHHJWkRHmJM6klWRQDY+2Dn3XwragRB47ZO1bQbgxr vi8J9f7TWxX3e7SEHx0osYHkk52Cyf1x8ASWd4RcAeIbktWq0d6fqlu/9Hrv1NmWHtjk 8tEYkFiAlYn+cbltH5A5dS2DrqzSB04pm7QJhJyBmZlj1OamQMFiJBsD0LHygCgO4Q+A vwMxQ4qb/76oXmepA/ZQU4QjNjNqk4tqGTW6q0prF5aH9Kkc3UZx+K1QaEkljXnZ7YTR xyXHgADYfq1fmiGpls5UeSjyFtJsFdNq4wBlQ1upnUmfDQQfv+BEEi8v9nX7qjTTv3DL rXFA== 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=cGOSHWHf030IkSak76+na5Q86ksMR3Kog0w1HGe6B7w=; b=yij/uo7qtgZWLZcGUXDO00wQ8X/MH5ydr77o37ToKoK2wp46xEq1oZnD/6A7HGCgHQ qT6hKvi5XFiw9UE9k5qNDhjoGzbW8WYOTJLumJMk3RG5UWSKcYU5AvlKmaWXMMdR3MRW uSx5gwzGweN6nMCmNeakWaKg7KJ6hE2nPSWvSYtYhKEt+rHKCVITOB/9za6ypNDv/NuT t+3f7Kh/V5oWXx8mPDpJakh6y4NBMmXwoK/s3H1z+3DC2wHRJByL1S/nMM25DuoHuce5 pwTLpGWQLnJtmpgk4MmTxgTuss5PsIOW8avgUgpPLJ/P2A1Ia+JAews2THF9UW0riGqN 6DmA== X-Gm-Message-State: ACrzQf0XtKHgRN2r1CwgxXvAMdxxDHy9LzDHTkNRNA338h+2TZpITmvB jPtNhRjcCVLOTOgc2KN8oSE= X-Google-Smtp-Source: AMsMyM7t/VO2lT3aSQgvK3Lr8FxWhe6n1kmdTneSbtTNXnbiiAq2oAiNTphzCbeeAnDpoCp63DKeww== X-Received: by 2002:a05:6512:3295:b0:497:a156:795a with SMTP id p21-20020a056512329500b00497a156795amr9532093lfe.345.1663760783934; Wed, 21 Sep 2022 04:46:23 -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 y18-20020a197512000000b004946aef1814sm396307lfe.137.2022.09.21.04.46.22 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 21 Sep 2022 04:46:23 -0700 (PDT) Date: Wed, 21 Sep 2022 14:46:18 +0300 From: Matti Vaittinen To: Matti Vaittinen , Matti Vaittinen Cc: Jonathan Cameron , Lars-Peter Clausen , Rob Herring , Krzysztof Kozlowski , Matti Vaittinen , Andy Shevchenko , Nikita Yushchenko , Cosmin Tanislav , Jagath Jog J , linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [RFC PATCH 5/5] MAINTAINERS: Add KX022A maintainer entry Message-ID: <68ac8d4bb3f8bd1239bd22db43b5bf64f149fd79.1663760018.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 --- MAINTAINERS | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index d30f26e07cd3..74f1c75775b5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11355,6 +11355,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