From patchwork Fri Mar 17 14:41:26 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Matti Vaittinen X-Patchwork-Id: 71327 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a5d:604a:0:0:0:0:0 with SMTP id j10csp399700wrt; Fri, 17 Mar 2023 08:20:08 -0700 (PDT) X-Google-Smtp-Source: AK7set+FpECPJYp8ZKIhFJp9Cb6Lcod4llYskKK1TgvYQZKQkLNU9LCCivyk465/ydZvm63BBltr X-Received: by 2002:a05:6a21:32a2:b0:cc:32a8:69ad with SMTP id yt34-20020a056a2132a200b000cc32a869admr9319588pzb.1.1679066407873; Fri, 17 Mar 2023 08:20:07 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1679066407; cv=none; d=google.com; s=arc-20160816; b=ePhFeD9ne4OHG6IIs4TK0FjRdMA+wTy5f9QcDhRt67UK47MM3WEp/xli6EMo5mRME7 mFdz4AQZto3JcpicEnlkCynzBViJMEsJ1m9tLYlFsiKzWsBgYv3RY/7/E+1p4Fly9wZA 6PA8mxuvt+3bUqQg2Qt3vv2FbSRsIOyRIAnmWoOUkRiOKw9XV8SNgeFtBBuEv5SNAYPD tJYzuqTR88eLWUSLfrpb9cbBuVbpkr2RkIaIjXVVAVXcA1qYcS8d0uZ31njx0BHistew WN6smdpgnOq2UU29fEGasPwqaXZjCRd8XzNdRYJxLbJdhy4wUcJBSr0WaIi11HVWS0td nCRA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:in-reply-to:content-disposition:mime-version :references:message-id:subject:cc:to:from:date:dkim-signature; bh=voAKQ3WGCeTkXYK2HgOGiBs7zBasJeDeL3Ew5RR80Fo=; b=cNnsOo6p5AOVg62iiFeDYRIIcrFqVcs9wirLUtV/o46mGlODVcXUJcz7b+FLbGIGlF mBC3uiUixCbF7bnDUAZZ6p7osYbcgvySSpKOj/CZgfOyOHpJGbDpPh9uBY+PukpOLw7V Yl6VwQvh5I35eriPO5cpX+2bmKD/go3QXUEYH5Sb7XLevbYx8oNVVYdq7eyDO29eEmE/ Ai3xg/3Vl+er9Mv5J84fpoORTwgYnozYSahsEaGPaMh431gbIOmef0/Tx+zp5oVR1zIs uq/3hYyZGK1CArdq4ZGaq6FyFf5YaoXosgSdsG1PrX8qVIsCgAUHwxih5ImI7aQUCODA 5WTw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gmail.com header.s=20210112 header.b=dmHVDP7L; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id y1-20020a056a001c8100b006223c3afa63si2500715pfw.323.2023.03.17.08.19.53; Fri, 17 Mar 2023 08:20:07 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@gmail.com header.s=20210112 header.b=dmHVDP7L; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230212AbjCQOlt (ORCPT + 99 others); Fri, 17 Mar 2023 10:41:49 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:41550 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229601AbjCQOlr (ORCPT ); Fri, 17 Mar 2023 10:41:47 -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 006702D162 for ; Fri, 17 Mar 2023 07:41:44 -0700 (PDT) Received: by mail-lf1-x12e.google.com with SMTP id y15so6759090lfa.7 for ; Fri, 17 Mar 2023 07:41:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; t=1679064103; h=in-reply-to:content-disposition:mime-version:references:message-id :subject:cc:to:from:date:from:to:cc:subject:date:message-id:reply-to; bh=voAKQ3WGCeTkXYK2HgOGiBs7zBasJeDeL3Ew5RR80Fo=; b=dmHVDP7L4ERSg8EX1AnfG1RtU7LykXB8+MYj1A6GJvnhbd6nlvGI12KndfuPvM96Vm 6IndSgb/TPB+NWJYVOsQygy7QOrEpOPCKXY4CXzRuRqbbP5/1vm9u51hop11TG6LwKjY W+KGpRLXdB7aCIgBJ8NAonMgCsYU2sEHiPrR8OZwPVeK2okMl9PE8oMfZaX88ajUUDIJ jMXyGWqP0qB/af+LIWtwcu2evJLu2cs9K8tyHyTuwy5KN1VvyfMZAAltPF2zvC2PJE7v GicRTK1Wjs31qQ0XiEhjtd9KLM0aPdh48Yn4a/kHnvLN3l9k/C04Ysg0okJzCRg2Z+ml UMjQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1679064103; 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 :message-id:reply-to; bh=voAKQ3WGCeTkXYK2HgOGiBs7zBasJeDeL3Ew5RR80Fo=; b=tahGmWk8BaIF3RFlzW2XiA+isvwKoKvIiFBpbl5SeW685nD6qUe396rehXBkDwVioX H3bS5SeZ/XblBWg9Yuvv+DbU5r2kkMIifpsTYWlwRtenfBjp04PAl9vcXQOvJiU5Ifqm umFqyCPcAEOTUSy+Bm4twhOjP2D2lKdALwU2ZlM4kjJZyr/PX0eiCUvwufcZYaCfxXwR qfc1rhO++Hdddyo+4rYwKA9imXT/aUPGd6tAKvLDZGjK3NlcRlGE5SNyYdCIA7hoW8ve eX0ppciQDhO4FsXyVUQtrAegwux/HX799IR/d4l2j5bBIAp+g3Mu91eHag1nz7EJvvgS yj6Q== X-Gm-Message-State: AO0yUKXdQ9Km8xSZTPFj1iPNAkpzxM1LOrsWlYiXU9NUfAIeycDPFZ7l NuI9unxkgHAF2gJLubG3p3s= X-Received: by 2002:ac2:446f:0:b0:4b0:1305:6e02 with SMTP id y15-20020ac2446f000000b004b013056e02mr4026129lfl.8.1679064103061; Fri, 17 Mar 2023 07:41:43 -0700 (PDT) Received: from fedora ([213.255.186.46]) by smtp.gmail.com with ESMTPSA id b6-20020a056512218600b004e8452972a2sm391958lft.247.2023.03.17.07.41.42 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 17 Mar 2023 07:41:42 -0700 (PDT) Date: Fri, 17 Mar 2023 16:41:26 +0200 From: Matti Vaittinen To: Matti Vaittinen , Matti Vaittinen Cc: David Airlie , Daniel Vetter , Emma Anholt , Maxime Ripard , Maarten Lankhorst , Thomas Zimmermann , =?iso-8859-1?q?Ma=EDra?= Canal , Noralf =?iso-8859-1?q?T?= =?iso-8859-1?q?r=F8nnes?= , Javier Martinez Canillas , Matti Vaittinen , dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, Stephen Boyd Subject: [PATCH v4 1/8] drm/tests: helpers: rename generic helpers Message-ID: <15d07e799f7c7fddc91030b16266d4a8bbaf1cc1.1679062529.git.mazziesaccount@gmail.com> References: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM, RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1760628737536164048?= X-GMAIL-MSGID: =?utf-8?q?1760628737536164048?= The creation of a dummy device in order to test managed interfaces is a generally useful test feature. The drm test helpers drm_kunit_helper_alloc_device() and drm_kunit_helper_free_device() are doing exactly this. It makes no sense that each and every component which intends to be testing managed interfaces will create similar helpers so rename these to more generic format. Signed-off-by: Matti Vaittinen --- drivers/gpu/drm/tests/drm_client_modeset_test.c | 4 ++-- drivers/gpu/drm/tests/drm_kunit_helpers.c | 16 ++++++++-------- drivers/gpu/drm/tests/drm_managed_test.c | 4 ++-- drivers/gpu/drm/tests/drm_modes_test.c | 4 ++-- drivers/gpu/drm/tests/drm_probe_helper_test.c | 4 ++-- drivers/gpu/drm/vc4/tests/vc4_mock.c | 2 +- drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c | 8 ++++---- include/drm/drm_kunit_helpers.h | 8 ++++---- 8 files changed, 25 insertions(+), 25 deletions(-) diff --git a/drivers/gpu/drm/tests/drm_client_modeset_test.c b/drivers/gpu/drm/tests/drm_client_modeset_test.c index 416a279b6dae..27ab03d1c518 100644 --- a/drivers/gpu/drm/tests/drm_client_modeset_test.c +++ b/drivers/gpu/drm/tests/drm_client_modeset_test.c @@ -60,7 +60,7 @@ static int drm_client_modeset_test_init(struct kunit *test) test->priv = priv; - priv->dev = drm_kunit_helper_alloc_device(test); + priv->dev = test_kunit_helper_alloc_device(test); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->dev); priv->drm = __drm_kunit_helper_alloc_drm_device(test, priv->dev, @@ -86,7 +86,7 @@ static void drm_client_modeset_test_exit(struct kunit *test) { struct drm_client_modeset_test_priv *priv = test->priv; - drm_kunit_helper_free_device(test, priv->dev); + test_kunit_helper_free_device(test, priv->dev); } static void drm_test_pick_cmdline_res_1920_1080_60(struct kunit *test) diff --git a/drivers/gpu/drm/tests/drm_kunit_helpers.c b/drivers/gpu/drm/tests/drm_kunit_helpers.c index e98b4150f556..ec93b0300f03 100644 --- a/drivers/gpu/drm/tests/drm_kunit_helpers.c +++ b/drivers/gpu/drm/tests/drm_kunit_helpers.c @@ -33,7 +33,7 @@ static struct platform_driver fake_platform_driver = { }; /** - * drm_kunit_helper_alloc_device - Allocate a mock device for a KUnit test + * test_kunit_helper_alloc_device - Allocate a mock device for a KUnit test * @test: The test context object * * This allocates a fake struct &device to create a mock for a KUnit @@ -41,13 +41,13 @@ static struct platform_driver fake_platform_driver = { * able to leverage the usual infrastructure and most notably the * device-managed resources just like a "real" device. * - * Callers need to make sure drm_kunit_helper_free_device() on the + * Callers need to make sure test_kunit_helper_free_device() on the * device when done. * * Returns: * A pointer to the new device, or an ERR_PTR() otherwise. */ -struct device *drm_kunit_helper_alloc_device(struct kunit *test) +struct device *test_kunit_helper_alloc_device(struct kunit *test) { struct platform_device *pdev; int ret; @@ -63,23 +63,23 @@ struct device *drm_kunit_helper_alloc_device(struct kunit *test) return &pdev->dev; } -EXPORT_SYMBOL_GPL(drm_kunit_helper_alloc_device); +EXPORT_SYMBOL_GPL(test_kunit_helper_alloc_device); /** - * drm_kunit_helper_free_device - Frees a mock device + * test_kunit_helper_free_device - Frees a mock device * @test: The test context object * @dev: The device to free * - * Frees a device allocated with drm_kunit_helper_alloc_device(). + * Frees a device allocated with test_kunit_helper_alloc_device(). */ -void drm_kunit_helper_free_device(struct kunit *test, struct device *dev) +void test_kunit_helper_free_device(struct kunit *test, struct device *dev) { struct platform_device *pdev = to_platform_device(dev); platform_device_unregister(pdev); platform_driver_unregister(&fake_platform_driver); } -EXPORT_SYMBOL_GPL(drm_kunit_helper_free_device); +EXPORT_SYMBOL_GPL(test_kunit_helper_free_device); struct drm_device * __drm_kunit_helper_alloc_drm_device_with_driver(struct kunit *test, diff --git a/drivers/gpu/drm/tests/drm_managed_test.c b/drivers/gpu/drm/tests/drm_managed_test.c index 1652dca11d30..53f870493577 100644 --- a/drivers/gpu/drm/tests/drm_managed_test.c +++ b/drivers/gpu/drm/tests/drm_managed_test.c @@ -35,7 +35,7 @@ static void drm_test_managed_run_action(struct kunit *test) KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv); init_waitqueue_head(&priv->action_wq); - dev = drm_kunit_helper_alloc_device(test); + dev = test_kunit_helper_alloc_device(test); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); drm = __drm_kunit_helper_alloc_drm_device(test, dev, sizeof(*drm), 0, DRIVER_MODESET); @@ -48,7 +48,7 @@ static void drm_test_managed_run_action(struct kunit *test) KUNIT_ASSERT_EQ(test, ret, 0); drm_dev_unregister(drm); - drm_kunit_helper_free_device(test, dev); + test_kunit_helper_free_device(test, dev); ret = wait_event_interruptible_timeout(priv->action_wq, priv->action_done, msecs_to_jiffies(TEST_TIMEOUT_MS)); diff --git a/drivers/gpu/drm/tests/drm_modes_test.c b/drivers/gpu/drm/tests/drm_modes_test.c index bc4aa2ce78be..1bd8540086e9 100644 --- a/drivers/gpu/drm/tests/drm_modes_test.c +++ b/drivers/gpu/drm/tests/drm_modes_test.c @@ -23,7 +23,7 @@ static int drm_test_modes_init(struct kunit *test) priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, priv); - priv->dev = drm_kunit_helper_alloc_device(test); + priv->dev = test_kunit_helper_alloc_device(test); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->dev); priv->drm = __drm_kunit_helper_alloc_drm_device(test, priv->dev, @@ -40,7 +40,7 @@ static void drm_test_modes_exit(struct kunit *test) { struct drm_test_modes_priv *priv = test->priv; - drm_kunit_helper_free_device(test, priv->dev); + test_kunit_helper_free_device(test, priv->dev); } static void drm_test_modes_analog_tv_ntsc_480i(struct kunit *test) diff --git a/drivers/gpu/drm/tests/drm_probe_helper_test.c b/drivers/gpu/drm/tests/drm_probe_helper_test.c index 0ee65828623e..bc4b271bec09 100644 --- a/drivers/gpu/drm/tests/drm_probe_helper_test.c +++ b/drivers/gpu/drm/tests/drm_probe_helper_test.c @@ -40,7 +40,7 @@ static int drm_probe_helper_test_init(struct kunit *test) KUNIT_ASSERT_NOT_NULL(test, priv); test->priv = priv; - priv->dev = drm_kunit_helper_alloc_device(test); + priv->dev = test_kunit_helper_alloc_device(test); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->dev); priv->drm = __drm_kunit_helper_alloc_drm_device(test, priv->dev, @@ -64,7 +64,7 @@ static void drm_probe_helper_test_exit(struct kunit *test) { struct drm_probe_helper_test_priv *priv = test->priv; - drm_kunit_helper_free_device(test, priv->dev); + test_kunit_helper_free_device(test, priv->dev); } typedef struct drm_display_mode *(*expected_mode_func_t)(struct drm_device *); diff --git a/drivers/gpu/drm/vc4/tests/vc4_mock.c b/drivers/gpu/drm/vc4/tests/vc4_mock.c index a4bed26af32f..5bb1fa828a3f 100644 --- a/drivers/gpu/drm/vc4/tests/vc4_mock.c +++ b/drivers/gpu/drm/vc4/tests/vc4_mock.c @@ -162,7 +162,7 @@ static struct vc4_dev *__mock_device(struct kunit *test, bool is_vc5) struct device *dev; int ret; - dev = drm_kunit_helper_alloc_device(test); + dev = test_kunit_helper_alloc_device(test); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); vc4 = drm_kunit_helper_alloc_drm_device_with_driver(test, dev, diff --git a/drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c b/drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c index ae0bd0f81698..8b373fa76d6f 100644 --- a/drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c +++ b/drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c @@ -762,7 +762,7 @@ static void vc4_pv_muxing_test_exit(struct kunit *test) drm_modeset_drop_locks(&priv->ctx); drm_modeset_acquire_fini(&priv->ctx); drm_dev_unregister(drm); - drm_kunit_helper_free_device(test, vc4->dev); + test_kunit_helper_free_device(test, vc4->dev); } static struct kunit_case vc4_pv_muxing_tests[] = { @@ -873,7 +873,7 @@ static void drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable(struct kunit *tes drm_modeset_drop_locks(&ctx); drm_modeset_acquire_fini(&ctx); drm_dev_unregister(drm); - drm_kunit_helper_free_device(test, vc4->dev); + test_kunit_helper_free_device(test, vc4->dev); } static void drm_test_vc5_pv_muxing_bugs_stable_fifo(struct kunit *test) @@ -963,7 +963,7 @@ static void drm_test_vc5_pv_muxing_bugs_stable_fifo(struct kunit *test) drm_modeset_drop_locks(&ctx); drm_modeset_acquire_fini(&ctx); drm_dev_unregister(drm); - drm_kunit_helper_free_device(test, vc4->dev); + test_kunit_helper_free_device(test, vc4->dev); } static void @@ -1017,7 +1017,7 @@ drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable_too_many_crtc_state(struct ku drm_modeset_drop_locks(&ctx); drm_modeset_acquire_fini(&ctx); drm_dev_unregister(drm); - drm_kunit_helper_free_device(test, vc4->dev); + test_kunit_helper_free_device(test, vc4->dev); } static struct kunit_case vc5_pv_muxing_bugs_tests[] = { diff --git a/include/drm/drm_kunit_helpers.h b/include/drm/drm_kunit_helpers.h index ed013fdcc1ff..8e3aae6a5ed5 100644 --- a/include/drm/drm_kunit_helpers.h +++ b/include/drm/drm_kunit_helpers.h @@ -8,8 +8,8 @@ struct drm_device; struct kunit; -struct device *drm_kunit_helper_alloc_device(struct kunit *test); -void drm_kunit_helper_free_device(struct kunit *test, struct device *dev); +struct device *test_kunit_helper_alloc_device(struct kunit *test); +void test_kunit_helper_free_device(struct kunit *test, struct device *dev); struct drm_device * __drm_kunit_helper_alloc_drm_device_with_driver(struct kunit *test, @@ -27,7 +27,7 @@ __drm_kunit_helper_alloc_drm_device_with_driver(struct kunit *test, * * This function creates a struct &drm_device from @_dev and @_drv. * - * @_dev should be allocated using drm_kunit_helper_alloc_device(). + * @_dev should be allocated using test_kunit_helper_alloc_device(). * * The driver is tied to the @_test context and will get cleaned at the * end of the test. The drm_device is allocated through @@ -72,7 +72,7 @@ __drm_kunit_helper_alloc_drm_device(struct kunit *test, * This function creates a struct &drm_driver and will create a struct * &drm_device from @_dev and that driver. * - * @_dev should be allocated using drm_kunit_helper_alloc_device(). + * @_dev should be allocated using test_kunit_helper_alloc_device(). * * The driver is tied to the @_test context and will get cleaned at the * end of the test. The drm_device is allocated through From patchwork Fri Mar 17 14:42:25 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Matti Vaittinen X-Patchwork-Id: 71304 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a5d:604a:0:0:0:0:0 with SMTP id j10csp385353wrt; Fri, 17 Mar 2023 07:57:20 -0700 (PDT) X-Google-Smtp-Source: AK7set8ZPfkXqf2U0H7TqEpXn0EImgSCIi2oKoE/KCo12NIHGPr6JpTlJJ4uYiZ8p+DsWuiClANy X-Received: by 2002:a17:902:c40c:b0:19c:f005:92de with SMTP id k12-20020a170902c40c00b0019cf00592demr7031834plk.4.1679065039666; Fri, 17 Mar 2023 07:57:19 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1679065039; cv=none; d=google.com; s=arc-20160816; b=JNnsHA3m+2+FmTrx0hiXgMQBsY5UM7yyVUqYdYCxakqJ8tMJOCFAbTxr/tpb2aAhMY k02/s39MUcXo9eipVbtdLBMjn2ejT/oIe7RpUB5DxOUQbVb18Vvbyob22hUhXevUO76T h7WwLRgG7rQJCry8aVAWLDvkBsSa+lnx+TQyDMjwzkLHXxjXivTHCMRmn2vcP8adnCuR G/Uv2mPrrDZxHyDK8F/QheE4VVNlE497GunQ2Vdbfc40sOg3sw/+8vRsW/wWdZRx2U13 rxZA1fao6EwTWdUzteMx9+/Dq56/a1fNlCmvDAKvgDg1NMwDaOs9ml7pebwSo1r9ZAOE JlMg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:in-reply-to:content-disposition:mime-version :references:message-id:subject:cc:to:from:date:dkim-signature; bh=UGy7oNS94ufYKednhkXhlJ35f2xeVGrjXtmL/TmdAmU=; b=hArn3ZlytqB6K6tAlJmvUfC25tSxMwYNtjZPoJR3XpnryTx3AKa2rEr1mP42Rv6wAg M5vKsHJVt+MmPYnPqNzmlS/Rbp2IXJ2M0JYTE3iQmLEaaskJ6EOaN13ZzXqBeJr+lPqL aCswGWiTw9+3gq++XBgnMAYgozASsTdEIZJCqZypPrJDT7coUmfpZ4y0XluyFY3TC88D 1z1dqmIzLQJsSMuwjtD0jp7CXJPYA5+jhplOLHrYcInTDCv+i6eSV1sGo4f+U/eAaYI+ dRrbGC3hkEI7Nczu+wxaq6zjpExGA4I3SkCAw6JGEC+drwsWYFbdEG08txj36D5simTs md+Q== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gmail.com header.s=20210112 header.b=UGEIY1Tr; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id l13-20020a170902f68d00b0019cbce63a6esi2749461plg.553.2023.03.17.07.57.06; Fri, 17 Mar 2023 07:57:19 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@gmail.com header.s=20210112 header.b=UGEIY1Tr; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229601AbjCQOmo (ORCPT + 99 others); Fri, 17 Mar 2023 10:42:44 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42456 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230173AbjCQOmm (ORCPT ); Fri, 17 Mar 2023 10:42:42 -0400 Received: from mail-lj1-x22b.google.com (mail-lj1-x22b.google.com [IPv6:2a00:1450:4864:20::22b]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 4413F2A9BC for ; Fri, 17 Mar 2023 07:42:40 -0700 (PDT) Received: by mail-lj1-x22b.google.com with SMTP id z5so5315430ljc.8 for ; Fri, 17 Mar 2023 07:42:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; t=1679064158; h=in-reply-to:content-disposition:mime-version:references:message-id :subject:cc:to:from:date:from:to:cc:subject:date:message-id:reply-to; bh=UGy7oNS94ufYKednhkXhlJ35f2xeVGrjXtmL/TmdAmU=; b=UGEIY1Truue6z82ReZqcgi14hxPvGrZO5KlhxsHlIX6C1wTBXMm2obt2RCu4mK5BiD V9YDocoX8kwS0N2Ob6lozTqfoKLxuBSvOhP9AnvnVwq1k+AwqvK2Q+zYfP5KdXKVgC8w q5FSfwkdW8zPo3Owai1Voz1u/gDO8sJ2PZOxe53V3b+9uNVgxo/xggHe1XmqUfxHq+vF fQ26riHH/IX5JnZAHKJMq/SlqRAG/pvv1Yjp8KK8FVTmo348bo0N6burD62msiM6qUXO fg5hRWjVAJg3xPElpIIXmoiVG7XtaUMq/NXoeyqjBlu6X4N4Q46nxiJnwNprHezKlw2n SBDw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1679064158; 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 :message-id:reply-to; bh=UGy7oNS94ufYKednhkXhlJ35f2xeVGrjXtmL/TmdAmU=; b=YgxCHs4aMNslLJtUdUmebaSIpa+C4HE5NhYRa010+hSVWfZww6YD7gTKWXKshwiw/C fsPgY9ZfoNlpLeGaG9QtH4CzY7R4rrwmna3OPve15zR1NnJVWOkEn0PPL79fiXYOB+6+ qCIoG55O613kb78l8jWOQQHVM8bN2AdYGFEPXT9nGbbwY0jicW0740/5SLLbQ2ZqAmfJ IHOm4QLtezDnQOGRszz7+bfOFTKWKoN3KCyqP4sNKC4RTOdNnh344sF1+VDQ8FT6L3e0 RxmUXLyakc1gtOMtelXHtUHlxp0ozFFzhPu8hqYS0aIbwncAybLR7vJBfhPCct451ynh YSSg== X-Gm-Message-State: AO0yUKVfaddZMQlaorGQhs44e5kLsufpXZxjTzNs0VwGf4wp3lg9jMhN nokdUmFXFxLaOBqqFlp+dQM= X-Received: by 2002:a05:651c:19a7:b0:298:a124:dd8c with SMTP id bx39-20020a05651c19a700b00298a124dd8cmr4534321ljb.38.1679064158295; Fri, 17 Mar 2023 07:42:38 -0700 (PDT) Received: from fedora ([213.255.186.46]) by smtp.gmail.com with ESMTPSA id c14-20020a2e680e000000b00295735991edsm438209lja.38.2023.03.17.07.42.37 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 17 Mar 2023 07:42:37 -0700 (PDT) Date: Fri, 17 Mar 2023 16:42:25 +0200 From: Matti Vaittinen To: Matti Vaittinen , Matti Vaittinen Cc: Greg Kroah-Hartman , "Rafael J. Wysocki" , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , David Airlie , Daniel Vetter , Emma Anholt , Andy Shevchenko , Matti Vaittinen , Heikki Krogerus , =?iso-8859-1?q?Ma=EDra?= Canal , Javier Martinez Canillas , Noralf =?iso-8859-1?q?Tr=F8n?= =?iso-8859-1?q?nes?= , linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, Stephen Boyd Subject: [PATCH v4 2/8] kunit: drm/tests: move generic helpers Message-ID: <1abd47784b08939ff08ff03d3d4f60449e87625f.1679062529.git.mazziesaccount@gmail.com> References: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM, RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1760627303156939297?= X-GMAIL-MSGID: =?utf-8?q?1760627303156939297?= The creation of a dummy device in order to test managed interfaces is a generally useful test feature. The drm test helpers test_kunit_helper_alloc_device() and test_kunit_helper_free_device() are doing exactly this. It makes no sense that each and every component which intends to be testing managed interfaces will create similar helpers. Move these functions to place where it is more obvious they can be used also by other subsystems but drm. Signed-off-by: Matti Vaittinen --- Please note that there's something similat ongoing in the CCF: https://lore.kernel.org/all/20230302013822.1808711-1-sboyd@kernel.org/ I do like the simplicity of these DRM-originated helpers so I think they're worth. I do equally like the Stephen's idea of having the "dummy platform device" related helpers under drivers/base and the header being in include/kunit/platform_device.h which is similar to real platform device under include/linux/platform_device.h - so, in the end of the day I hope Stephen's changes as well as the changes this patch introduces to end up in those files. This, however, will require some co-operation to avoid conflicts. --- drivers/base/test/Kconfig | 5 ++ drivers/base/test/Makefile | 2 + drivers/base/test/test_kunit_device.c | 83 +++++++++++++++++++ drivers/gpu/drm/Kconfig | 2 + .../gpu/drm/tests/drm_client_modeset_test.c | 1 + drivers/gpu/drm/tests/drm_kunit_helpers.c | 69 --------------- drivers/gpu/drm/tests/drm_managed_test.c | 1 + drivers/gpu/drm/tests/drm_modes_test.c | 1 + drivers/gpu/drm/tests/drm_probe_helper_test.c | 1 + drivers/gpu/drm/vc4/Kconfig | 1 + drivers/gpu/drm/vc4/tests/vc4_mock.c | 1 + .../gpu/drm/vc4/tests/vc4_test_pv_muxing.c | 1 + include/drm/drm_kunit_helpers.h | 3 - 13 files changed, 99 insertions(+), 72 deletions(-) create mode 100644 drivers/base/test/test_kunit_device.c diff --git a/drivers/base/test/Kconfig b/drivers/base/test/Kconfig index 610a1ba7a467..642b5b183c10 100644 --- a/drivers/base/test/Kconfig +++ b/drivers/base/test/Kconfig @@ -1,4 +1,9 @@ # SPDX-License-Identifier: GPL-2.0 + +config TEST_KUNIT_DEVICE_HELPERS + depends on KUNIT + tristate + config TEST_ASYNC_DRIVER_PROBE tristate "Build kernel module to test asynchronous driver probing" depends on m diff --git a/drivers/base/test/Makefile b/drivers/base/test/Makefile index 7f76fee6f989..49926524ec6f 100644 --- a/drivers/base/test/Makefile +++ b/drivers/base/test/Makefile @@ -1,5 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_TEST_ASYNC_DRIVER_PROBE) += test_async_driver_probe.o +obj-$(CONFIG_TEST_KUNIT_DEVICE_HELPERS) += test_kunit_device.o + obj-$(CONFIG_DRIVER_PE_KUNIT_TEST) += property-entry-test.o CFLAGS_property-entry-test.o += $(DISABLE_STRUCTLEAK_PLUGIN) diff --git a/drivers/base/test/test_kunit_device.c b/drivers/base/test/test_kunit_device.c new file mode 100644 index 000000000000..75790e15b85c --- /dev/null +++ b/drivers/base/test/test_kunit_device.c @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * These helpers have been extracted from drm test code at + * drm_kunit_helpers.c which was authored by + * Maxime Ripard + */ + +#include +#include + +#include + +#define KUNIT_DEVICE_NAME "test-kunit-mock-device" + +static int fake_probe(struct platform_device *pdev) +{ + return 0; +} + +static int fake_remove(struct platform_device *pdev) +{ + return 0; +} + +static struct platform_driver fake_platform_driver = { + .probe = fake_probe, + .remove = fake_remove, + .driver = { + .name = KUNIT_DEVICE_NAME, + }, +}; + +/** + * test_kunit_helper_alloc_device - Allocate a mock device for a KUnit test + * @test: The test context object + * + * This allocates a fake struct &device to create a mock for a KUnit + * test. The device will also be bound to a fake driver. It will thus be + * able to leverage the usual infrastructure and most notably the + * device-managed resources just like a "real" device. + * + * Callers need to make sure test_kunit_helper_free_device() on the + * device when done. + * + * Returns: + * A pointer to the new device, or an ERR_PTR() otherwise. + */ +struct device *test_kunit_helper_alloc_device(struct kunit *test) +{ + struct platform_device *pdev; + int ret; + + ret = platform_driver_register(&fake_platform_driver); + KUNIT_ASSERT_EQ(test, ret, 0); + + pdev = platform_device_alloc(KUNIT_DEVICE_NAME, PLATFORM_DEVID_NONE); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); + + ret = platform_device_add(pdev); + KUNIT_ASSERT_EQ(test, ret, 0); + + return &pdev->dev; +} +EXPORT_SYMBOL_GPL(test_kunit_helper_alloc_device); + +/** + * test_kunit_helper_free_device - Frees a mock device + * @test: The test context object + * @dev: The device to free + * + * Frees a device allocated with test_kunit_helper_alloc_device(). + */ +void test_kunit_helper_free_device(struct kunit *test, struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + + platform_device_unregister(pdev); + platform_driver_unregister(&fake_platform_driver); +} +EXPORT_SYMBOL_GPL(test_kunit_helper_free_device); + +MODULE_AUTHOR("Matti Vaittinen "); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index dc0f94f02a82..0ee8ebe64f57 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -66,6 +66,7 @@ config DRM_USE_DYNAMIC_DEBUG config DRM_KUNIT_TEST_HELPERS tristate depends on DRM && KUNIT + select TEST_KUNIT_DEVICE_HELPERS help KUnit Helpers for KMS drivers. @@ -80,6 +81,7 @@ config DRM_KUNIT_TEST select DRM_BUDDY select DRM_EXPORT_FOR_TESTS if m select DRM_KUNIT_TEST_HELPERS + select TEST_KUNIT_DEVICE_HELPERS default KUNIT_ALL_TESTS help This builds unit tests for DRM. This option is not useful for diff --git a/drivers/gpu/drm/tests/drm_client_modeset_test.c b/drivers/gpu/drm/tests/drm_client_modeset_test.c index 27ab03d1c518..d7eaa0938eb4 100644 --- a/drivers/gpu/drm/tests/drm_client_modeset_test.c +++ b/drivers/gpu/drm/tests/drm_client_modeset_test.c @@ -3,6 +3,7 @@ * Copyright (c) 2022 Maxime Ripard */ +#include #include #include diff --git a/drivers/gpu/drm/tests/drm_kunit_helpers.c b/drivers/gpu/drm/tests/drm_kunit_helpers.c index ec93b0300f03..ae84d0ed8744 100644 --- a/drivers/gpu/drm/tests/drm_kunit_helpers.c +++ b/drivers/gpu/drm/tests/drm_kunit_helpers.c @@ -9,78 +9,9 @@ #include #include -#define KUNIT_DEVICE_NAME "drm-kunit-mock-device" - static const struct drm_mode_config_funcs drm_mode_config_funcs = { }; -static int fake_probe(struct platform_device *pdev) -{ - return 0; -} - -static int fake_remove(struct platform_device *pdev) -{ - return 0; -} - -static struct platform_driver fake_platform_driver = { - .probe = fake_probe, - .remove = fake_remove, - .driver = { - .name = KUNIT_DEVICE_NAME, - }, -}; - -/** - * test_kunit_helper_alloc_device - Allocate a mock device for a KUnit test - * @test: The test context object - * - * This allocates a fake struct &device to create a mock for a KUnit - * test. The device will also be bound to a fake driver. It will thus be - * able to leverage the usual infrastructure and most notably the - * device-managed resources just like a "real" device. - * - * Callers need to make sure test_kunit_helper_free_device() on the - * device when done. - * - * Returns: - * A pointer to the new device, or an ERR_PTR() otherwise. - */ -struct device *test_kunit_helper_alloc_device(struct kunit *test) -{ - struct platform_device *pdev; - int ret; - - ret = platform_driver_register(&fake_platform_driver); - KUNIT_ASSERT_EQ(test, ret, 0); - - pdev = platform_device_alloc(KUNIT_DEVICE_NAME, PLATFORM_DEVID_NONE); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); - - ret = platform_device_add(pdev); - KUNIT_ASSERT_EQ(test, ret, 0); - - return &pdev->dev; -} -EXPORT_SYMBOL_GPL(test_kunit_helper_alloc_device); - -/** - * test_kunit_helper_free_device - Frees a mock device - * @test: The test context object - * @dev: The device to free - * - * Frees a device allocated with test_kunit_helper_alloc_device(). - */ -void test_kunit_helper_free_device(struct kunit *test, struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - - platform_device_unregister(pdev); - platform_driver_unregister(&fake_platform_driver); -} -EXPORT_SYMBOL_GPL(test_kunit_helper_free_device); - struct drm_device * __drm_kunit_helper_alloc_drm_device_with_driver(struct kunit *test, struct device *dev, diff --git a/drivers/gpu/drm/tests/drm_managed_test.c b/drivers/gpu/drm/tests/drm_managed_test.c index 53f870493577..6b39d2cde164 100644 --- a/drivers/gpu/drm/tests/drm_managed_test.c +++ b/drivers/gpu/drm/tests/drm_managed_test.c @@ -4,6 +4,7 @@ #include #include +#include #include #include diff --git a/drivers/gpu/drm/tests/drm_modes_test.c b/drivers/gpu/drm/tests/drm_modes_test.c index 1bd8540086e9..addc4d923a26 100644 --- a/drivers/gpu/drm/tests/drm_modes_test.c +++ b/drivers/gpu/drm/tests/drm_modes_test.c @@ -7,6 +7,7 @@ #include #include +#include #include #include diff --git a/drivers/gpu/drm/tests/drm_probe_helper_test.c b/drivers/gpu/drm/tests/drm_probe_helper_test.c index bc4b271bec09..f23213464d34 100644 --- a/drivers/gpu/drm/tests/drm_probe_helper_test.c +++ b/drivers/gpu/drm/tests/drm_probe_helper_test.c @@ -13,6 +13,7 @@ #include #include +#include #include struct drm_probe_helper_test_priv { diff --git a/drivers/gpu/drm/vc4/Kconfig b/drivers/gpu/drm/vc4/Kconfig index 91dcf8d174d6..a4bd96445315 100644 --- a/drivers/gpu/drm/vc4/Kconfig +++ b/drivers/gpu/drm/vc4/Kconfig @@ -39,6 +39,7 @@ config DRM_VC4_KUNIT_TEST tristate "KUnit tests for VC4" if !KUNIT_ALL_TESTS depends on DRM_VC4 && KUNIT select DRM_KUNIT_TEST_HELPERS + select TEST_KUNIT_DEVICE_HELPERS default KUNIT_ALL_TESTS help This builds unit tests for the VC4 DRM/KMS driver. This option is diff --git a/drivers/gpu/drm/vc4/tests/vc4_mock.c b/drivers/gpu/drm/vc4/tests/vc4_mock.c index 5bb1fa828a3f..29eb045b0db1 100644 --- a/drivers/gpu/drm/vc4/tests/vc4_mock.c +++ b/drivers/gpu/drm/vc4/tests/vc4_mock.c @@ -3,6 +3,7 @@ #include #include +#include #include #include "vc4_mock.h" diff --git a/drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c b/drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c index 8b373fa76d6f..64b90e2e3706 100644 --- a/drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c +++ b/drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c @@ -12,6 +12,7 @@ #include #include +#include #include #include "../vc4_drv.h" diff --git a/include/drm/drm_kunit_helpers.h b/include/drm/drm_kunit_helpers.h index 8e3aae6a5ed5..ab438d97aed3 100644 --- a/include/drm/drm_kunit_helpers.h +++ b/include/drm/drm_kunit_helpers.h @@ -8,9 +8,6 @@ struct drm_device; struct kunit; -struct device *test_kunit_helper_alloc_device(struct kunit *test); -void test_kunit_helper_free_device(struct kunit *test, struct device *dev); - struct drm_device * __drm_kunit_helper_alloc_drm_device_with_driver(struct kunit *test, struct device *dev, From patchwork Fri Mar 17 14:42:57 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Matti Vaittinen X-Patchwork-Id: 71318 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a5d:604a:0:0:0:0:0 with SMTP id j10csp395913wrt; Fri, 17 Mar 2023 08:13:39 -0700 (PDT) X-Google-Smtp-Source: AK7set//wJavDrW/mENAIbWX6VPNpqgxICcAQCcva4Qp5rKBtKE/uwib7UkfYqRfXzKQC/Y6PRVK X-Received: by 2002:a05:6a00:2d3:b0:619:49fe:3be5 with SMTP id b19-20020a056a0002d300b0061949fe3be5mr7040405pft.1.1679066019276; Fri, 17 Mar 2023 08:13:39 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1679066019; cv=none; d=google.com; s=arc-20160816; b=kWUwk4K2GOVARmqBcbLv3g1cu8Or8n93Ts62Vlvc1cMSl7Glg2nA/Yo5qy1WIdVYse B3Wn/zrIE+3iIUwvd0pwj40Q1xrhTOqwAe+zWP5gsYzIlQ8CxPltrN/eq9rhjCTNKlI7 DCJpWVkHySq9EmGkEpdg3a3vUxu1rGnzQ3MbJVUpMiOTjUgjfS9IEDstUoPBEBNP4DpL fnMvN0cxe+gcoL+3oqawEDblR/XQz4w2MVzpNIHiaD3zSQyZsnGZGjl4eK86evII+OtG 9ZerPbdVSyoTvAq6qGlr+WW5Dq0ZDy8v8OK7/DJD1b+WC45SkcUDI3m/cOOqmSOb2GPe Z14A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:in-reply-to:content-disposition:mime-version :references:message-id:subject:cc:to:from:date:dkim-signature; bh=ghmMfinFDDdTN92ApK4Yf07wpTrmYSX8BQBPIjw/BlY=; b=03vL2dylPWBb9F56rpUR2zsusnKEm2sirJjBqVOKTdLu4Qnzq4vZULqt/q5xW6K5nO YzTp2KJof9nv91NJfkuD1Jc53MkNYthoJewtH7V6bt0hwRngsZp4psXDBpJ8f2N6ezQs mfXy+0gJTXCgtqtRcGxI/g6nKw1lEOid2ifIgD8OWrydejkHgRZhs043/W8rsNCWLcfm TK8CNR4n7QnduomfJiqku1unJtYzCIDJVDETu6SIDpTOLyIw5vXlUDeggQvgKX0QGBBh gmrcuoBdyPvKnUW3r507Pt84UdxAhDWAqEInY+0KYIdIuNSNPh6pPpQAE3UXg75PfpSc 6uXA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gmail.com header.s=20210112 header.b=Rw4Z7Oon; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id x71-20020a63864a000000b0050bfb509db6si2630130pgd.636.2023.03.17.08.13.23; Fri, 17 Mar 2023 08:13:39 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@gmail.com header.s=20210112 header.b=Rw4Z7Oon; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230401AbjCQOnP (ORCPT + 99 others); Fri, 17 Mar 2023 10:43:15 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:43162 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230479AbjCQOnM (ORCPT ); Fri, 17 Mar 2023 10:43:12 -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 B56D8367C5; Fri, 17 Mar 2023 07:43:03 -0700 (PDT) Received: by mail-lj1-x232.google.com with SMTP id f16so5309222ljq.10; Fri, 17 Mar 2023 07:43:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; t=1679064182; h=in-reply-to:content-disposition:mime-version:references:message-id :subject:cc:to:from:date:from:to:cc:subject:date:message-id:reply-to; bh=ghmMfinFDDdTN92ApK4Yf07wpTrmYSX8BQBPIjw/BlY=; b=Rw4Z7OonWOm+/1aLHL0yAopg91Yj6Nk75pGuBl73xJuErQZp8AbYTMvPICA+G4NftH OV51IDbVPMV86jBf3nDDWS/YjPuxNkZ4ZaqCF8zsX7pv+Ysc49qLovWAo1xLaVIkqQqc ff6t8axPIboXtt2vusX5meHj9gfujmrHRaAvDERvLsSIb2i9ElpeCGffHxoDX0jrowZI 5i017j+KCmdD+3qSyLGnVgKNwycaZ3XvTAa+243HYUHSepLg2rNvi+K/TkWcikqYentU 7sGJlFnQ0PXPZi7qf741otX10tD8TzR4Q88lY/SDsoRp0e5M78AK77lwRGLDhGs9kLh7 sXeA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1679064182; 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 :message-id:reply-to; bh=ghmMfinFDDdTN92ApK4Yf07wpTrmYSX8BQBPIjw/BlY=; b=s8WbZF7W7q9Rk+iWsHzPkfnUc3OF85JIEV99O3jcWAAkziws/rej7ZgCVEWfuaXzDh fSg17mx7n3dWHMxd2TrmYLT++Y191JEfLO7/UKGin9Jqu6SFKcs0y6EqEf/OjQxNKvW6 r2RoocG3pBbTuBDezki29QhTRq30IjxidVBfVJt6fElMQqsQkSEIp9YN+MXcFfIzSTwM DTyE+5I0WWI3B9yV26880ULRJHeqh2609D9I5X0NJBe5aQ73VgnXiq477gxnEl32Cckv 7fYW4QimLoKOzPkXBYhslVbpvgX3rs1lmK8dUn5mkHqNtYoKpmGlY8ojDKaOXegvGRFz NUqA== X-Gm-Message-State: AO0yUKVdflT+RCd8bCfFqM5p+mdts/VFcdZR2foZALbH1sa99jGSkM8I zBfyG/ZgSvRdBEjDjAEEHq8= X-Received: by 2002:a05:651c:150a:b0:298:a164:590f with SMTP id e10-20020a05651c150a00b00298a164590fmr4426010ljf.45.1679064182065; Fri, 17 Mar 2023 07:43:02 -0700 (PDT) Received: from fedora ([213.255.186.46]) by smtp.gmail.com with ESMTPSA id s9-20020a2e2c09000000b00293d7c95df1sm437851ljs.78.2023.03.17.07.43.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 17 Mar 2023 07:43:01 -0700 (PDT) Date: Fri, 17 Mar 2023 16:42:57 +0200 From: Matti Vaittinen To: Matti Vaittinen , Matti Vaittinen Cc: Jonathan Cameron , Lars-Peter Clausen , Rob Herring , Krzysztof Kozlowski , Matti Vaittinen , linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v4 3/8] dt-bindings: iio: light: Support ROHM BU27034 Message-ID: References: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM, RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1760628329833298626?= X-GMAIL-MSGID: =?utf-8?q?1760628329833298626?= ROHM BU27034 is an ambient light sesnor with 3 channels and 3 photo diodes capable of detecting a very wide range of illuminance. Typical application is adjusting LCD and backlight power of TVs and mobile phones. Add dt-bindings. Signed-off-by: Matti Vaittinen Reviewed-by: Krzysztof Kozlowski --- v2 => - No changes Changes since RFCv1 => v2 - Fix binding file name and id by using comma instead of a hyphen to separate the vendor and part names. --- .../bindings/iio/light/rohm,bu27034.yaml | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/light/rohm,bu27034.yaml diff --git a/Documentation/devicetree/bindings/iio/light/rohm,bu27034.yaml b/Documentation/devicetree/bindings/iio/light/rohm,bu27034.yaml new file mode 100644 index 000000000000..30a109a1bf3b --- /dev/null +++ b/Documentation/devicetree/bindings/iio/light/rohm,bu27034.yaml @@ -0,0 +1,46 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/light/rohm,bu27034.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ROHM BU27034 ambient light sensor + +maintainers: + - Matti Vaittinen + +description: | + ROHM BU27034 is an ambient light sesnor with 3 channels and 3 photo diodes + capable of detecting a very wide range of illuminance. Typical application + is adjusting LCD and backlight power of TVs and mobile phones. + https://fscdn.rohm.com/en/products/databook/datasheet/ic/sensor/light/bu27034nuc-e.pdf + +properties: + compatible: + const: rohm,bu27034 + + reg: + maxItems: 1 + + vdd-supply: true + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + light-sensor@38 { + compatible = "rohm,bu27034"; + reg = <0x38>; + vdd-supply = <&vdd>; + }; + }; + +... From patchwork Fri Mar 17 14:43:23 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Matti Vaittinen X-Patchwork-Id: 71300 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a5d:604a:0:0:0:0:0 with SMTP id j10csp383572wrt; Fri, 17 Mar 2023 07:53:01 -0700 (PDT) X-Google-Smtp-Source: AK7set+cGw9OkCScooRrj8DNuK4LpMLT2B5jP1t+6q5i1WTpZiZwShTfmT8mtx1WLf4+IV9oq14X X-Received: by 2002:a05:6a21:32a1:b0:c7:af88:4199 with SMTP id yt33-20020a056a2132a100b000c7af884199mr9294372pzb.6.1679064780584; Fri, 17 Mar 2023 07:53:00 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1679064780; cv=none; d=google.com; s=arc-20160816; b=CHDEMNhArI7dYEWNkmlzfyxaycmbP6YrSaAYpeMkFwxZD6vqGagChsmmw7lZxtLut9 a8ZD9OwF86J+YRN2KRIoiILT0WXtsR3XFbJ4VDIU7h/zOErvMXM9QncXY3HcXEXk/zsR N1CZBZnFkJ7OprI7EMlrG+KhVVUMdfqyihxOAoMC3B/IcIYJPwSm1N+BunjwyrJddZO7 c1qT/OyDGj7QIivHNRk9TwRq4LWASGjWe951XCFWLA3iss+Dt63PNvtVGb4TnbomCx7K tJC5LH9RVBlF/DaRPLQhzs5L4A9TSOrzM+LUk+ImeBRxs/ijuW93fbjJ9SGahMJY1BOD GlrA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:in-reply-to:content-disposition:mime-version :references:message-id:subject:cc:to:from:date:dkim-signature; bh=iF8in9l1T6zbpnXyIS54Ym53qHWHsCbph9y5DNXEN8c=; b=TR3YFw8JxkjCHw0F9X4IdEaUFEyCAMHWUYY91bg8X/0FmSinFz4pYYWjE71rsJlbVi 8ut9rncP84QAyP/sn1qXgxRVzLgsCkub+/rgldZXEbG2Pvdc0YjTuiBaeA30Ej1QwJJW QnS9LCeuolLyMvNHcNKfNxdj/OZXQ/WN6TtmlgSJcdtu5N+BHgPNQcU8RhhMz6lV7ES2 CysvFKPnsLe5NtUU+K89hIaPmIMuYIkQZqRDaqZA7wD0FBh5yNhVoMsAkiHrHYwFtqL4 lxia/MlLdB/LhfC0YkxxDv/PYpukWzNReAFIypflgm87ybL5CpGB8HimbCTrF4ckv37j ZfbQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gmail.com header.s=20210112 header.b=qxABR3SG; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id n127-20020a632785000000b00507681e2613si2635329pgn.569.2023.03.17.07.52.45; Fri, 17 Mar 2023 07:53:00 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@gmail.com header.s=20210112 header.b=qxABR3SG; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230016AbjCQOnw (ORCPT + 99 others); Fri, 17 Mar 2023 10:43:52 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44366 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230453AbjCQOnt (ORCPT ); Fri, 17 Mar 2023 10:43:49 -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 CB039367CD; Fri, 17 Mar 2023 07:43:29 -0700 (PDT) Received: by mail-lf1-x12e.google.com with SMTP id bi9so6736536lfb.12; Fri, 17 Mar 2023 07:43:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; t=1679064208; h=in-reply-to:content-disposition:mime-version:references:message-id :subject:cc:to:from:date:from:to:cc:subject:date:message-id:reply-to; bh=iF8in9l1T6zbpnXyIS54Ym53qHWHsCbph9y5DNXEN8c=; b=qxABR3SGhH3RVHhzgnhmd2WCB5tb4G80tbxpoCg9afuVktDALoT+YUb/2NTJzh+Vjm vfuMweZ0xp9yptFXUFSZfKbJlWpwglh2LObh8wnftnmHDbHjUQQpyuar+r1mZP6g5dE4 jiyuSum7HK9/cq2b7p7v4qJuFBln0EWQYehaZD/AyCdmJNWAERxwLNC8b50yChG83ICu LwFFzbKSKoIGNaxdlg9/d+EaN2xjDYkiAXRxYyKut7mORyeaH6sRAzFbSl8gFWYhrz6w BwVQbetmQrQX+4uvRVzlWzLb0lLQguYcLES8yRtiMipgdCYf50jq3AAakVyoKRpskMnP HKfw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1679064208; 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 :message-id:reply-to; bh=iF8in9l1T6zbpnXyIS54Ym53qHWHsCbph9y5DNXEN8c=; b=JMzxY3vIS8TF22x8M71IPsMO1W4Ada700ugguE4cCyiwujkSRAbamC1dLoI4kp7sgt IM9jSXjjgxVxkqCx3JyOWKvuum+VT8SO/AxdXYfzEmSp+mVqZBO5rXeequQQNtFFC5iE r01yCwy5H7oBLIVD/0WWUk/fKmkxmpmUYqubNmFL/Al3apSg3sjND0hC/VJI7zCgQd8a Z/D1ndrSlux7CwXWRs5xP7mnCRV4IxZgvd9R6Ju/jffUqZKbLZQ4KZysLa7G5ghgE0RM N5nlkOzeSSM/0notZlrT8h4KUW/V9OH+r/EqO3PRt8Ik9IY4zY4sPC5UyOjqdVyl3ETK 3aKA== X-Gm-Message-State: AO0yUKXuoS6AAZ0PoxO4LsqRSWAlWSlBGlPD0P5t4Z31lmV02tB1fWTV hL1B/OhKIsKg0zCJm/8rpiN+9bqTLh0= X-Received: by 2002:ac2:5975:0:b0:4e2:1440:b392 with SMTP id h21-20020ac25975000000b004e21440b392mr4029863lfp.56.1679064207645; Fri, 17 Mar 2023 07:43:27 -0700 (PDT) Received: from fedora ([213.255.186.46]) by smtp.gmail.com with ESMTPSA id l17-20020ac25551000000b004cc9042c9cfsm393993lfk.158.2023.03.17.07.43.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 17 Mar 2023 07:43:27 -0700 (PDT) Date: Fri, 17 Mar 2023 16:43:23 +0200 From: Matti Vaittinen To: Matti Vaittinen , Matti Vaittinen Cc: Jonathan Cameron , Lars-Peter Clausen , linux-kernel@vger.kernel.org, linux-iio@vger.kernel.org Subject: [PATCH v4 4/8] iio: light: Add gain-time-scale helpers Message-ID: References: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM, RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1760627031178992298?= X-GMAIL-MSGID: =?utf-8?q?1760627031178992298?= Some light sensors can adjust both the HW-gain and integration time. There are cases where adjusting the integration time has similar impact to the scale of the reported values as gain setting has. IIO users do typically expect to handle scale by a single writable 'scale' entry. Driver should then adjust the gain/time accordingly. It however is difficult for a driver to know whether it should change gain or integration time to meet the requested scale. Usually it is preferred to have longer integration time which usually improves accuracy, but there may be use-cases where long measurement times can be an issue. Thus it can be preferable to allow also changing the integration time - but mitigate the scale impact by also changing the gain underneath. Eg, if integration time change doubles the measured values, the driver can reduce the HW-gain to half. The theory of the computations of gain-time-scale is simple. However, some people (undersigned) got that implemented wrong for more than once. Add some gain-time-scale helpers in order to not dublicate errors in all drivers needing these computations. Signed-off-by: Matti Vaittinen --- Currently it is only BU27034 using these in this series. I am however working with drivers for RGB sensors BU27008 and BU27010 which have similar [gain - integration time - scale] - relation. I hope sending those follows soon after the BU27034 is done. Changes: v3 => v4: - doc styling - use memset to zero the helper struct at init - drop unnecessary min calculation at iio_find_closest_gain_low() - use namespace to all exports - many minor stylings - make available outside iio/light (move code to drivers/iio and move the header under include - rename to look like other files under drivers/iio (s/iio/industrialio) - drop unused functions - don't export only internally used functions and make them static Note, I decided to keep iio_gts_total_gain_to_scale() exported as it is currently needed by the tests outside the helpers. v2 => v3: (mostly fixes based on review by Andy) - Fix typos - Styling fixes - Use namespace for exported symbols - Protect allocs against argument overflow - Fix include protection name - add types.h inclusion and struct device forward declaration RFCv1 => v2: - fix include guardian - Improve kernel doc for iio_init_iio_gts. - Add iio_gts_scale_to_total_gain - Add iio_gts_total_gain_to_scale - Fix review comments from Jonathan - add documentation to few functions - replace 0xffffffffffffffffLLU by U64_MAX - some styling fixes - drop unnecessary NULL checks - order function arguments by in / out purpose - drop GAIN_SCALE_ITIME_MS() - Add helpers for available scales and times - Rename to iio-gts-helpers --- drivers/iio/Kconfig | 3 + drivers/iio/Makefile | 1 + drivers/iio/industrialio-gts-helper.c | 990 ++++++++++++++++++++++++++ include/linux/iio/iio-gts-helper.h | 113 +++ 4 files changed, 1107 insertions(+) create mode 100644 drivers/iio/industrialio-gts-helper.c create mode 100644 include/linux/iio/iio-gts-helper.h diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig index b190846c3dc2..52eb46ef84c1 100644 --- a/drivers/iio/Kconfig +++ b/drivers/iio/Kconfig @@ -30,6 +30,9 @@ config IIO_CONFIGFS (e.g. software triggers). For more info see Documentation/iio/iio_configfs.rst. +config IIO_GTS_HELPER + tristate + config IIO_TRIGGER bool "Enable triggered sampling support" help diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile index 3be08cdadd7e..9622347a1c1b 100644 --- a/drivers/iio/Makefile +++ b/drivers/iio/Makefile @@ -9,6 +9,7 @@ industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o obj-$(CONFIG_IIO_CONFIGFS) += industrialio-configfs.o +obj-$(CONFIG_IIO_GTS_HELPER) += industrialio-gts-helper.o obj-$(CONFIG_IIO_SW_DEVICE) += industrialio-sw-device.o obj-$(CONFIG_IIO_SW_TRIGGER) += industrialio-sw-trigger.o obj-$(CONFIG_IIO_TRIGGERED_EVENT) += industrialio-triggered-event.o diff --git a/drivers/iio/industrialio-gts-helper.c b/drivers/iio/industrialio-gts-helper.c new file mode 100644 index 000000000000..9494ea7cdbcf --- /dev/null +++ b/drivers/iio/industrialio-gts-helper.c @@ -0,0 +1,990 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* gain-time-scale conversion helpers for IIO light sensors + * + * Copyright (c) 2023 Matti Vaittinen + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define DEFAULT_SYMBOL_NAMESPACE IIO_GTS_HELPER + +/** + * iio_gts_get_gain - Convert scale to total gain + * + * Internal helper for converting scale to total gain. + * + * @max: Maximum linearized scale. As an example, when scale is created + * in magnitude of NANOs and max scale is 64.1 - The linearized + * scale is 64 100 000 000. + * @scale: Linearized scale to compte the gain for. + * + * Return: (floored) gain corresponding to the scale. -EINVAL if scale + * is invalid. + */ +static int iio_gts_get_gain(const u64 max, const u64 scale) +{ + u64 full = max; + int tmp = 1; + + if (scale > full || !scale) + return -EINVAL; + + if (U64_MAX - full < scale) { + /* Risk of overflow */ + if (full - scale < scale) + return 1; + + full -= scale; + tmp++; + } + + while (full > scale * (u64)tmp) + tmp++; + + return tmp; +} + +/** + * gain_get_scale_fraction - get the gain or time based on scale and known one + * + * @max: Maximum linearized scale. As an example, when scale is created + * in magnitude of NANOs and max scale is 64.1 - The linearized + * scale is 64 100 000 000. + * @scale: Linearized scale to compute the gain/time for. + * @known: Either integration time or gain depending on which one is known + * @unknown: Pointer to variable where the computed gain/time is stored + * + * Internal helper for computing unknown fraction of total gain. + * Compute either gain or time based on scale and either the gain or time + * depending on which one is known. + * + * Return: 0 on success. + */ +static int gain_get_scale_fraction(const u64 max, u64 scale, int known, + int *unknown) +{ + int tot_gain; + + tot_gain = iio_gts_get_gain(max, scale); + if (tot_gain < 0) + return tot_gain; + + *unknown = tot_gain / known; + + /* We require total gain to be exact multiple of known * unknown */ + if (!*unknown || *unknown * known != tot_gain) + return -EINVAL; + + return 0; +} + +static const struct iio_itime_sel_mul * +iio_gts_find_itime_by_time(struct iio_gts *gts, int time) +{ + int i; + + if (!gts->num_itime) + return NULL; + + for (i = 0; i < gts->num_itime; i++) + if (gts->itime_table[i].time_us == time) + return >s->itime_table[i]; + + return NULL; +} + +static const struct iio_itime_sel_mul * +iio_gts_find_itime_by_sel(struct iio_gts *gts, int sel) +{ + int i; + + for (i = 0; i < gts->num_itime; i++) + if (gts->itime_table[i].sel == sel) + return >s->itime_table[i]; + + return NULL; +} + +static int iio_gts_delinearize(u64 lin_scale, unsigned long scaler, + int *scale_whole, int *scale_nano) +{ + int frac; + + if (scaler > NANO) + return -EOVERFLOW; + + if (!scaler) + return -EINVAL; + + frac = do_div(lin_scale, scaler); + + *scale_whole = lin_scale; + *scale_nano = frac * (NANO / scaler); + + return 0; +} + +static int iio_gts_linearize(int scale_whole, int scale_nano, + unsigned long scaler, u64 *lin_scale) +{ + /* + * Expect scale to be (mostly) NANO or MICRO. Divide divider instead of + * multiplication followed by division to avoid overflow. + */ + if (scaler > NANO || !scaler) + return -EINVAL; + + *lin_scale = (u64)scale_whole * (u64)scaler + + (u64)(scale_nano / (NANO / scaler)); + + return 0; +} + +/** + * iio_gts_total_gain_to_scale - convert gain to scale + * @gts: Gain time scale descriptor + * @total_gain: the gain to be converted + * @scale_int: Pointer to integral part of the scale (typically val1) + * @scale_nano: Pointer to fractional part of the scale (nano or ppb) + * + * Convert the total gain value to scale. NOTE: This does not separate gain + * generated by hwgain or integration time. It is up to caller to decide what + * part of the total gain is due to integration time and what due to hw-gain. + * + * Return: 0 on success. Negative errno on failure. + */ +int iio_gts_total_gain_to_scale(struct iio_gts *gts, int total_gain, + int *scale_int, int *scale_nano) +{ + u64 tmp; + + tmp = gts->max_scale; + + do_div(tmp, total_gain); + + return iio_gts_delinearize(tmp, NANO, scale_int, scale_nano); +} +EXPORT_SYMBOL_GPL(iio_gts_total_gain_to_scale); + +/** + * iio_init_iio_gts - Initialize the gain-time-scale helper + * @max_scale_int: integer part of the maximum scale value + * @max_scale_nano: fraction part of the maximum scale value + * @gain_tbl: table describing supported gains + * @num_gain: number of gains in the gaintable + * @tim_tbl: table describing supported integration times. Provide + * the integration time table sorted so that the preferred + * integration time is in the first array index. The search + * functions like the + * iio_gts_find_time_and_gain_sel_for_scale() start search + * from first provided time. + * @num_times: number of times in the time table + * @gts: pointer to the helper struct + * + * Initialize the gain-time-scale helper for use. + * + * Return: 0 on success. + */ +int iio_init_iio_gts(int max_scale_int, int max_scale_nano, + const struct iio_gain_sel_pair *gain_tbl, int num_gain, + const struct iio_itime_sel_mul *tim_tbl, int num_times, + struct iio_gts *gts) +{ + int ret; + + memset(gts, 0, sizeof(*gts)); + + ret = iio_gts_linearize(max_scale_int, max_scale_nano, NANO, + >s->max_scale); + if (ret) + return ret; + + gts->hwgain_table = gain_tbl; + gts->num_hwgain = num_gain; + gts->itime_table = tim_tbl; + gts->num_itime = num_times; + + return 0; +} +EXPORT_SYMBOL_GPL(iio_init_iio_gts); + +/** + * iio_gts_purge_avail_scale_table - free-up the available scale tables + * @gts: Gain time scale descriptor + * + * Free the space reserved by iio_gts_build_avail_scale_table(). Please note + * that the helpers for getting available scales like the + * iio_gts_all_avail_scales() are not usable after this call. Thus, this should + * be only called after these helpers can no longer be called (Eg. after + * the iio-device has been deregistered). + */ +static void iio_gts_purge_avail_scale_table(struct iio_gts *gts) +{ + int i; + + if (gts->per_time_avail_scale_tables) { + for (i = 0; i < gts->num_itime; i++) + kfree(gts->per_time_avail_scale_tables[i]); + + kfree(gts->per_time_avail_scale_tables); + gts->per_time_avail_scale_tables = NULL; + } + + kfree(gts->avail_all_scales_table); + gts->avail_all_scales_table = NULL; + + gts->num_avail_all_scales = 0; +} + +static int iio_gts_gain_cmp(const void *a, const void *b) +{ + return *(int *)a - *(int *)b; +} + +static int gain_to_scaletables(struct iio_gts *gts, int **gains, int **scales) +{ + int ret, i, j, new_idx, time_idx; + int *all_gains; + size_t gain_bytes; + + for (i = 0; i < gts->num_itime; i++) { + /* + * Sort the tables for nice output and for easier finding of + * unique values. + */ + sort(gains[i], gts->num_hwgain, sizeof(int), iio_gts_gain_cmp, + NULL); + + /* Convert gains to scales */ + for (j = 0; j < gts->num_hwgain; j++) { + ret = iio_gts_total_gain_to_scale(gts, gains[i][j], + &scales[i][2 * j], + &scales[i][2 * j + 1]); + if (ret) + return ret; + } + } + + gain_bytes = array_size(gts->num_hwgain, sizeof(int)); + all_gains = kcalloc(gts->num_itime, gain_bytes, GFP_KERNEL); + if (!all_gains) + return -ENOMEM; + + /* + * We assume all the gains for same integration time were unique. + * It is likely the first time table had greatest time multiplier as + * the times are in the order of preference and greater times are + * usually preferred. Hence we start from the last table which is likely + * to have the smallest total gains. + */ + time_idx = gts->num_itime - 1; + memcpy(all_gains, gains[time_idx], gain_bytes); + new_idx = gts->num_hwgain; + + while (time_idx--) { + for (j = 0; j < gts->num_hwgain; j++) { + int candidate = gains[time_idx][j]; + int chk; + + if (candidate > all_gains[new_idx - 1]) { + all_gains[new_idx] = candidate; + new_idx++; + + continue; + } + for (chk = 0; chk < new_idx; chk++) + if (candidate <= all_gains[chk]) + break; + + if (candidate == all_gains[chk]) + continue; + + memmove(&all_gains[chk + 1], &all_gains[chk], + (new_idx - chk) * sizeof(int)); + all_gains[chk] = candidate; + new_idx++; + } + } + + gts->num_avail_all_scales = new_idx; + gts->avail_all_scales_table = kcalloc(gts->num_avail_all_scales, + 2 * sizeof(int), GFP_KERNEL); + if (!gts->avail_all_scales_table) + ret = -ENOMEM; + else + for (i = 0; !ret && i < gts->num_avail_all_scales; i++) + ret = iio_gts_total_gain_to_scale(gts, all_gains[i], + >s->avail_all_scales_table[i * 2], + >s->avail_all_scales_table[i * 2 + 1]); + + kfree(all_gains); + if (ret) + kfree(gts->avail_all_scales_table); + + return ret; +} + +/** + * iio_gts_build_avail_scale_table - create tables of available scales + * @gts: Gain time scale descriptor + * + * Build the tables which can represent the available scales based on the + * originally given gain and time tables. When both time and gain tables are + * given this results: + * 1. A set of tables representing available scales for each supported + * integration time. + * 2. A single table listing all the unique scales that any combination of + * supported gains and times can provide. + * + * NOTE: Space allocated for the tables must be freed using + * iio_gts_purge_avail_scale_table() when the tables are no longer needed. + * + * Return: 0 on success. + */ +static int iio_gts_build_avail_scale_table(struct iio_gts *gts) +{ + int **per_time_gains, **per_time_scales, i, j, ret = -ENOMEM; + + per_time_gains = kcalloc(gts->num_itime, sizeof(int *), GFP_KERNEL); + if (!per_time_gains) + return ret; + + per_time_scales = kcalloc(gts->num_itime, sizeof(int *), GFP_KERNEL); + if (!per_time_scales) + goto free_gains; + + for (i = 0; i < gts->num_itime; i++) { + per_time_scales[i] = kcalloc(gts->num_hwgain, 2 * sizeof(int), + GFP_KERNEL); + if (!per_time_scales[i]) + goto err_free_out; + + per_time_gains[i] = kcalloc(gts->num_hwgain, sizeof(int), + GFP_KERNEL); + if (!per_time_gains[i]) + goto err_free_scale_out; + + + for (j = 0; j < gts->num_hwgain; j++) + per_time_gains[i][j] = gts->hwgain_table[j].gain * + gts->itime_table[i].mul; + } + + ret = gain_to_scaletables(gts, per_time_gains, per_time_scales); + if (ret) + goto err_free_out; + + kfree(per_time_gains); + gts->per_time_avail_scale_tables = per_time_scales; + + return 0; + +err_free_scale_out: + kfree(per_time_scales[i]); +err_free_out: + for (i--; i; i--) { + kfree(per_time_scales[i]); + kfree(per_time_gains[i]); + } + kfree(per_time_scales); +free_gains: + kfree(per_time_gains); + + return ret; +} + +/** + * iio_gts_build_avail_time_table - build table of available integration times + * @gts: Gain time scale descriptor + * + * Build the table which can represent the available times to be returned + * to users using the read_avail-callback. + * + * NOTE: Space allocated for the tables must be freed using + * iio_gts_purge_avail_time_table() when the tables are no longer needed. + * + * Return: 0 on success. + */ +static int iio_gts_build_avail_time_table(struct iio_gts *gts) +{ + int *times, i, j, idx = 0; + + if (!gts->num_itime) + return 0; + + times = kcalloc(gts->num_itime, sizeof(int), GFP_KERNEL); + if (!times) + return -ENOMEM; + + for (i = gts->num_itime - 1; i >= 0; i--) { + int new = gts->itime_table[i].time_us; + + if (times[idx] < new) { + times[idx++] = new; + continue; + } + + for (j = 0; j <= idx; j++) { + if (times[j] > new) { + memmove(×[j + 1], ×[j], + (idx - j) * sizeof(int)); + times[j] = new; + idx++; + } + } + } + gts->avail_time_tables = times; + /* + * This is just to survive a unlikely corner-case where times in the + * given time table were not unique. Else we could just trust the + * gts->num_itime. + */ + gts->num_avail_time_tables = idx; + + return 0; +} + +/** + * iio_gts_purge_avail_time_table - free-up the available integration time table + * @gts: Gain time scale descriptor + * + * Free the space reserved by iio_gts_build_avail_time_table(). Please note + * that the helpers for getting available integration times like the + * iio_gts_avail_times() are not usable after this call. Thus, this should + * be only called after these helpers can no longer be called (Eg. after + * the iio-device has been deregistered). + */ +static void iio_gts_purge_avail_time_table(struct iio_gts *gts) +{ + if (gts->num_avail_time_tables) { + kfree(gts->avail_time_tables); + gts->avail_time_tables = NULL; + gts->num_avail_time_tables = 0; + } +} + +/** + * iio_gts_build_avail_tables - create tables of available scales and int times + * @gts: Gain time scale descriptor + * + * Build the tables which can represent the available scales and available + * integration times. Availability tables are built based on the originally + * given gain and given time tables. + * + * When both time and gain tables are + * given this results: + * 1. A set of sorted tables representing available scales for each supported + * integration time. + * 2. A single sorted table listing all the unique scales that any combination + * of supported gains and times can provide. + * 3. A sorted table of supported integration times + * + * After these tables are built one can use the iio_gts_all_avail_scales(), + * iio_gts_avail_scales_for_time() and iio_gts_avail_times() helpers to + * implement the read_avail opeations. + * + * NOTE: Space allocated for the tables must be freed using + * iio_gts_purge_avail_tables() when the tables are no longer needed. + * + * Return: 0 on success. + */ +static int iio_gts_build_avail_tables(struct iio_gts *gts) +{ + int ret; + + ret = iio_gts_build_avail_scale_table(gts); + if (ret) + return ret; + + ret = iio_gts_build_avail_time_table(gts); + if (ret) + iio_gts_purge_avail_scale_table(gts); + + return ret; +} + +/** + * iio_gts_purge_avail_tables - free-up the availability tables + * @gts: Gain time scale descriptor + * + * Free the space reserved by iio_gts_build_avail_tables(). Frees both the + * integration time and scale tables. + * + * Note that the helpers for getting available integration times or scales + * like the iio_gts_avail_times() are not usable after this call. Thus, this + * should be only called after these helpers can no longer be called (Eg. + * after the iio-device has been deregistered). + */ +static void iio_gts_purge_avail_tables(struct iio_gts *gts) +{ + iio_gts_purge_avail_time_table(gts); + iio_gts_purge_avail_scale_table(gts); +} + +static void devm_iio_gts_avail_all_drop(void *res) +{ + iio_gts_purge_avail_tables(res); +} + +/** + * devm_iio_gts_build_avail_tables - manged add availability tables + * @dev: Pointer to the device whose lifetime tables are bound + * @gts: Gain time scale descriptor + * + * Build the tables which can represent the available scales and available + * integration times. Availability tables are built based on the originally + * given gain and given time tables. + * + * When both time and gain tables are + * given this results: + * 1. A set of sorted tables representing available scales for each supported + * integration time. + * 2. A single sorted table listing all the unique scales that any combination + * of supported gains and times can provide. + * 3. A sorted table of supported integration times + * + * After these tables are built one can use the iio_gts_all_avail_scales(), + * iio_gts_avail_scales_for_time() and iio_gts_avail_times() helpers to + * implement the read_avail opeations. + * + * The tables are automatically released upon device detach. + * + * NOTE: after the tables have been purged, the helpers for getting + * available scales / integration times are no longer usable. Care must be + * taken that unwinding is done in correct order (iio device is deregistered + * prior purging the tables). + * + * Return: 0 on success. + */ +int devm_iio_gts_build_avail_tables(struct device *dev, struct iio_gts *gts) +{ + int ret; + + ret = iio_gts_build_avail_tables(gts); + if (ret) + return ret; + + return devm_add_action_or_reset(dev, devm_iio_gts_avail_all_drop, gts); +} +EXPORT_SYMBOL_GPL(devm_iio_gts_build_avail_tables); + +/** + * iio_gts_all_avail_scales - helper for listing all available scales + * @gts: Gain time scale descriptor + * @vals: Returned array of supported scales + * @type: Type of returned scale values + * @length: Amount of returned values in array + * + * Return: a value suitable to be returned from read_avail or a negative error. + */ +int iio_gts_all_avail_scales(struct iio_gts *gts, const int **vals, int *type, + int *length) +{ + if (!gts->num_avail_all_scales) + return -EINVAL; + + *vals = gts->avail_all_scales_table; + *type = IIO_VAL_INT_PLUS_NANO; + *length = gts->num_avail_all_scales * 2; + + return IIO_AVAIL_LIST; +} +EXPORT_SYMBOL_GPL(iio_gts_all_avail_scales); + +/** + * iio_gts_avail_scales_for_time - list scales for integration time + * @gts: Gain time scale descriptor + * @time: Integration time for which the scales are listed + * @vals: Returned array of supported scales + * @type: Type of returned scale values + * @length: Amount of returned values in array + * + * Drivers which do not allow scale setting to change integration time can + * use this helper to list only the scales whic are valid for given integration + * time. + * + * Return: a value suitable to be returned from read_avail or a negative error. + */ +int iio_gts_avail_scales_for_time(struct iio_gts *gts, int time, + const int **vals, int *type, int *length) +{ + int i; + + for (i = 0; i < gts->num_itime; i++) + if (gts->itime_table[i].time_us == time) + break; + + if (i == gts->num_itime) + return -EINVAL; + + *vals = gts->per_time_avail_scale_tables[i]; + *type = IIO_VAL_INT_PLUS_NANO; + *length = gts->num_hwgain * 2; + + return IIO_AVAIL_LIST; +} +EXPORT_SYMBOL_GPL(iio_gts_avail_scales_for_time); + +/** + * iio_gts_avail_times - helper for listing available integration times + * @gts: Gain time scale descriptor + * @vals: Returned array of supported timees + * @type: Type of returned scale values + * @length: Amount of returned values in array + * + * Return: a value suitable to be returned from read_avail or a negative error. + */ +int iio_gts_avail_times(struct iio_gts *gts, const int **vals, int *type, + int *length) +{ + if (!gts->num_avail_time_tables) + return -EINVAL; + + *vals = gts->avail_time_tables; + *type = IIO_VAL_INT; + *length = gts->num_avail_time_tables; + + return IIO_AVAIL_LIST; +} +EXPORT_SYMBOL_GPL(iio_gts_avail_times); + +/** + * iio_gts_valid_time - check if given integration time is valid + * @gts: Gain time scale descriptor + * @time_us: Integration time to check + * + * Return: True if given time is supported by device. False if not. + */ +bool iio_gts_valid_time(struct iio_gts *gts, int time_us) +{ + return iio_gts_find_itime_by_time(gts, time_us); +} +EXPORT_SYMBOL_GPL(iio_gts_valid_time); + +int iio_gts_find_sel_by_gain(struct iio_gts *gts, int gain) +{ + int i; + + for (i = 0; i < gts->num_hwgain; i++) + if (gts->hwgain_table[i].gain == gain) + return gts->hwgain_table[i].sel; + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(iio_gts_find_sel_by_gain); + +bool iio_gts_valid_gain(struct iio_gts *gts, int gain) +{ + return iio_gts_find_sel_by_gain(gts, gain) >= 0; +} +EXPORT_SYMBOL_GPL(iio_gts_valid_gain); + +int iio_gts_find_gain_by_sel(struct iio_gts *gts, int sel) +{ + int i; + + for (i = 0; i < gts->num_hwgain; i++) + if (gts->hwgain_table[i].sel == sel) + return gts->hwgain_table[i].gain; + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(iio_gts_find_gain_by_sel); + +int iio_gts_get_min_gain(struct iio_gts *gts) +{ + int i, min = -EINVAL; + + for (i = 0; i < gts->num_hwgain; i++) { + int gain = gts->hwgain_table[i].gain; + + if (min == -EINVAL) + min = gain; + else + min = min(min, gain); + } + + return min; +} +EXPORT_SYMBOL_GPL(iio_gts_get_min_gain); + +/** + * iio_find_closest_gain_low - Find the closest lower matching gain + * @gts: Gain time scale descriptor + * @gain: reference gain for which the closest match is searched + * @in_range: indicate if the reference gain was actually in the range of + * supported gains. + * + * Search for closest supported gain that is lower than or equal to the + * gain given as a parameter. This is usable for drivers which do not require + * user to request exact matching gain but rather fo rounding to a supported + * gain value which is equal or lower (setting lower gain is typical for + * avoiding saturation) + * + * Return: The closest matching supported gain or -EINVAL is reference + * gain was smaller than the smallest supported gain. + */ +int iio_find_closest_gain_low(struct iio_gts *gts, int gain, bool *in_range) +{ + int i, diff = 0; + int best = -1; + + *in_range = false; + + for (i = 0; i < gts->num_hwgain; i++) { + /* + * It is not expected this function is called for an exactly + * matching gain. + */ + if (unlikely(gain == gts->hwgain_table[i].gain)) { + *in_range = true; + return gain; + } + + if (gain > gts->hwgain_table[i].gain) { + if (!diff) { + diff = gain - gts->hwgain_table[i].gain; + best = i; + } else { + int tmp = gain - gts->hwgain_table[i].gain; + + if (tmp < diff) { + diff = tmp; + best = i; + } + } + } else { + /* + * We found valid hwgain which is greater than + * reference. So, unless we return a failure below we + * will have found an in-range gain + */ + *in_range = true; + } + } + /* The requested gain was smaller than anything we support */ + if (!diff) { + *in_range = false; + + return -EINVAL; + } + + return gts->hwgain_table[best].gain; +} +EXPORT_SYMBOL_GPL(iio_find_closest_gain_low); + +static int iio_gts_get_int_time_gain_multiplier_by_sel(struct iio_gts *gts, + int sel) +{ + const struct iio_itime_sel_mul *time; + + time = iio_gts_find_itime_by_sel(gts, sel); + if (!time) + return -EINVAL; + + return time->mul; +} + +/** + * iio_gts_find_gain_for_scale_using_time - Find gain by time and scale + * @gts: Gain time scale descriptor + * @time_sel: Integration time selector correspondig to the time gain is + * searhed for + * @scale_int: Integral part of the scale (typically val1) + * @scale_nano: Fractional part of the scale (nano or ppb) + * @gain: Pointer to value where gain is stored. + * + * In some cases the light sensors may want to find a gain setting which + * corresponds given scale and integration time. Sensors which fill the + * gain and time tables may use this helper to retrieve the gain. + * + * Return: 0 on success. -EINVAL if gain matching the parameters is not + * found. + */ +static int iio_gts_find_gain_for_scale_using_time(struct iio_gts *gts, int time_sel, + int scale_int, int scale_nano, + int *gain) +{ + u64 scale_linear; + int ret, mul; + + ret = iio_gts_linearize(scale_int, scale_nano, NANO, &scale_linear); + if (ret) + return ret; + + ret = iio_gts_get_int_time_gain_multiplier_by_sel(gts, time_sel); + if (ret < 0) + return ret; + + mul = ret; + + ret = gain_get_scale_fraction(gts->max_scale, scale_linear, mul, gain); + if (ret) + return ret; + + if (!iio_gts_valid_gain(gts, *gain)) + return -EINVAL; + + return 0; +} + +/* + * iio_gts_find_gain_sel_for_scale_using_time - Fetch gain selector. + * See iio_gts_find_gain_for_scale_using_time() for more information + */ +int iio_gts_find_gain_sel_for_scale_using_time(struct iio_gts *gts, int time_sel, + int scale_int, int scale_nano, + int *gain_sel) +{ + int gain, ret; + + ret = iio_gts_find_gain_for_scale_using_time(gts, time_sel, scale_int, + scale_nano, &gain); + if (ret) + return ret; + + ret = iio_gts_find_sel_by_gain(gts, gain); + if (ret < 0) + return ret; + + *gain_sel = ret; + + return 0; +} +EXPORT_SYMBOL_GPL(iio_gts_find_gain_sel_for_scale_using_time); + +int iio_gts_find_int_time_by_sel(struct iio_gts *gts, int sel) +{ + const struct iio_itime_sel_mul *itime; + + itime = iio_gts_find_itime_by_sel(gts, sel); + if (!itime) + return -EINVAL; + + return itime->time_us; +} +EXPORT_SYMBOL_GPL(iio_gts_find_int_time_by_sel); + +int iio_gts_find_sel_by_int_time(struct iio_gts *gts, int time) +{ + const struct iio_itime_sel_mul *itime; + + itime = iio_gts_find_itime_by_time(gts, time); + if (!itime) + return -EINVAL; + + return itime->sel; +} +EXPORT_SYMBOL_GPL(iio_gts_find_sel_by_int_time); + +static int iio_gts_get_total_gain(struct iio_gts *gts, int gain, int time) +{ + const struct iio_itime_sel_mul *itime; + + if (!iio_gts_valid_gain(gts, gain)) + return -EINVAL; + + if (!gts->num_itime) + return gain; + + itime = iio_gts_find_itime_by_time(gts, time); + if (!itime) + return -EINVAL; + + return gain * itime->mul; +} + +static int iio_gts_get_scale_linear(struct iio_gts *gts, int gain, int time, + u64 *scale) +{ + int total_gain; + u64 tmp; + + total_gain = iio_gts_get_total_gain(gts, gain, time); + if (total_gain < 0) + return total_gain; + + tmp = gts->max_scale; + + do_div(tmp, total_gain); + + *scale = tmp; + + return 0; +} + +int iio_gts_get_scale(struct iio_gts *gts, int gain, int time, int *scale_int, + int *scale_nano) +{ + u64 lin_scale; + int ret; + + ret = iio_gts_get_scale_linear(gts, gain, time, &lin_scale); + if (ret) + return ret; + + return iio_gts_delinearize(lin_scale, NANO, scale_int, scale_nano); +} +EXPORT_SYMBOL_GPL(iio_gts_get_scale); + +/** + * iio_gts_find_new_gain_sel_by_old_gain_time - compensate time change + * @gts: Gain time scale descriptor + * @old_gain: Previously set gain + * @old_time_sel: Selector corresponding previously set time + * @new_time_sel: Selector corresponding new time to be set + * @new_gain: Pointer to value where new gain is to be written + * + * We may want to mitigate the scale change caused by setting a new integration + * time (for a light sensor) by also updating the (HW)gain. This helper computes + * new gain value to maintain the scale with new integration time. + * + * Return: 0 on success. -EINVAL if gain matching the new time is not found. + */ +int iio_gts_find_new_gain_sel_by_old_gain_time(struct iio_gts *gts, + int old_gain, int old_time_sel, + int new_time_sel, int *new_gain) +{ + const struct iio_itime_sel_mul *itime_old, *itime_new; + u64 scale; + int ret; + + itime_old = iio_gts_find_itime_by_sel(gts, old_time_sel); + if (!itime_old) + return -EINVAL; + + itime_new = iio_gts_find_itime_by_sel(gts, new_time_sel); + if (!itime_new) + return -EINVAL; + + ret = iio_gts_get_scale_linear(gts, old_gain, itime_old->time_us, + &scale); + if (ret) + return ret; + + ret = gain_get_scale_fraction(gts->max_scale, scale, itime_new->mul, + new_gain); + if (ret) + return -EINVAL; + + if (!iio_gts_valid_gain(gts, *new_gain)) + return -EINVAL; + + return 0; +} +EXPORT_SYMBOL_GPL(iio_gts_find_new_gain_sel_by_old_gain_time); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Matti Vaittinen "); +MODULE_DESCRIPTION("IIO light sensor gain-time-scale helpers"); diff --git a/include/linux/iio/iio-gts-helper.h b/include/linux/iio/iio-gts-helper.h new file mode 100644 index 000000000000..95c712007962 --- /dev/null +++ b/include/linux/iio/iio-gts-helper.h @@ -0,0 +1,113 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* gain-time-scale conversion helpers for IIO light sensors + * + * Copyright (c) 2023 Matti Vaittinen + */ + +#ifndef __IIO_GTS_HELPER__ +#define __IIO_GTS_HELPER__ + +#include + +struct device; + +/** + * struct iio_gain_sel_pair - gain - selector values + * + * In many cases devices like light sensors allow setting signal amplification + * (gain) using a register interface. This structure describes amplification + * and corresponding selector (register value) + * + * @gain: Gain (multiplication) value. + * @sel: Selector (usually register value) used to indicate this gain + */ +struct iio_gain_sel_pair { + int gain; + int sel; +}; + +/** + * struct iio_itime_sel_mul - integration time description + * + * In many cases devices like light sensors allow setting the duration of + * collecting data. Typically this duration has also an impact to the magnitude + * of measured values (gain). This structure describes the relation of + * integration time and amplification as well as corresponding selector + * (register value). + * + * An example could be a sensor allowing 50, 100, 200 and 400 mS times. The + * respective multiplication values could be 50 mS => 1, 100 mS => 2, + * 200 mS => 4 and 400 mS => 8 assuming the impact of integration time would be + * linear in a way that when collecting data for 50 mS caused value X, doubling + * the data collection time caused value 2X etc.. + * + * @time_us: Integration time in microseconds. + * @sel: Selector (usually register value) used to indicate this time + * @mul: Multiplication to the values caused by this time. + */ +struct iio_itime_sel_mul { + int time_us; + int sel; + int mul; +}; + +struct iio_gts { + u64 max_scale; + const struct iio_gain_sel_pair *hwgain_table; + int num_hwgain; + const struct iio_itime_sel_mul *itime_table; + int num_itime; + int **per_time_avail_scale_tables; + int *avail_all_scales_table; + int num_avail_all_scales; + int *avail_time_tables; + int num_avail_time_tables; +}; + +#define GAIN_SCALE_GAIN(_gain, _sel) \ +{ \ + .gain = (_gain), \ + .sel = (_sel), \ +} + +#define GAIN_SCALE_ITIME_US(_itime, _sel, _mul) \ +{ \ + .time_us = (_itime), \ + .sel = (_sel), \ + .mul = (_mul), \ +} + +int iio_init_iio_gts(int max_scale_int, int max_scale_nano, + const struct iio_gain_sel_pair *gain_tbl, int num_gain, + const struct iio_itime_sel_mul *tim_tbl, int num_times, + struct iio_gts *gts); + +bool iio_gts_valid_gain(struct iio_gts *gts, int gain); +bool iio_gts_valid_time(struct iio_gts *gts, int time_us); + +int iio_find_closest_gain_low(struct iio_gts *gts, int gain, bool *in_range); +int iio_gts_find_gain_by_sel(struct iio_gts *gts, int sel); +int iio_gts_find_sel_by_gain(struct iio_gts *gts, int gain); +int iio_gts_get_min_gain(struct iio_gts *gts); +int iio_gts_find_int_time_by_sel(struct iio_gts *gts, int sel); +int iio_gts_find_sel_by_int_time(struct iio_gts *gts, int time); + +int iio_gts_total_gain_to_scale(struct iio_gts *gts, int total_gain, + int *scale_int, int *scale_nano); +int iio_gts_find_gain_sel_for_scale_using_time(struct iio_gts *gts, int time_sel, + int scale_int, int scale_nano, + int *gain_sel); +int iio_gts_get_scale(struct iio_gts *gts, int gain, int time, int *scale_int, + int *scale_nano); +int iio_gts_find_new_gain_sel_by_old_gain_time(struct iio_gts *gts, + int old_gain, int old_time_sel, + int new_time_sel, int *new_gain); +int devm_iio_gts_build_avail_tables(struct device *dev, struct iio_gts *gts); +int iio_gts_avail_times(struct iio_gts *gts, const int **vals, int *type, + int *length); +int iio_gts_all_avail_scales(struct iio_gts *gts, const int **vals, int *type, + int *length); +int iio_gts_avail_scales_for_time(struct iio_gts *gts, int time, + const int **vals, int *type, int *length); + +#endif From patchwork Fri Mar 17 14:43:49 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Matti Vaittinen X-Patchwork-Id: 71332 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a5d:604a:0:0:0:0:0 with SMTP id j10csp403556wrt; Fri, 17 Mar 2023 08:27:30 -0700 (PDT) X-Google-Smtp-Source: AK7set/AJ1Thkx61edEdYQ0nTSdpo2RPTAFguUNsNLJWtpPH3ekhiG7xj84QEqIDG5o4mIXDoPGv X-Received: by 2002:a17:903:2786:b0:19a:7217:32af with SMTP id jw6-20020a170903278600b0019a721732afmr7178636plb.5.1679066849976; Fri, 17 Mar 2023 08:27:29 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1679066849; cv=none; d=google.com; s=arc-20160816; b=FdSG1F/vnAkbIkUZ5Nv8SFisBLsztlJ5vMbA7vPleU80Xw3SUBbCWTKVaFWtfcSDuG rDVFXqPPcANqFeCP6hxbeS49oxp39wWAleGa7VwJkYlA+riMAyIVyCZHgpuSb7A8TLIN 6Mv8RGi0+bG8yBotyW50V54CoJpNzY6zafKAVg3LHQnOGt397tTiWzD3dvzT/WNTDCq0 qvpNKVDMEh85PXOG+q3eD9SjjgJcYpB5aKk9LUERPr6WLijrMHgoc+f2I22HOAf2HRLk vM4ohVwihCEDsoLz8yuhlX410sUgJM0o8EsMrxmbRB9QIX8oVwVaG0v6n7nRAKhu/OuF 7LDA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:in-reply-to:content-disposition:mime-version :references:message-id:subject:cc:to:from:date:dkim-signature; bh=5gNnLKDM3tcCGbdN/rtth+qkmLgZk8AsENLlpVCrPag=; b=UU8QaVej9d1lMX1hfvxhhqzAdykPrFCbRWRGj3GffKpP4xUW6TNUR2CtBeeljfho9c Zk23gX7CkStL5pHKgNPVIVyGFQYc8iayHcRz/JAlbpPfnn4+1dbypgx3X5jjhpw+QkN+ V7xf2vA7XIymODX0Xuk13RRCPL9T3nsdctQqOtnLDOtLkSmbxnyhz7IMai5+ppeQDbTU fmN/V8BefsWM6tnL2OO4CqaKCfBnxEecbfcfwSG5hGIBjvon18I25iY0p29kizvN4W+r GBXsjHaTgCeijqe/531ySGCeS+QDXaTcLFvOgw6p9IVefCMtIQMMU3GTSOdKdHVkvI/N gnSA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gmail.com header.s=20210112 header.b=AxvtPXBc; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id m13-20020a170902db0d00b0019e6a5cf724si3092705plx.203.2023.03.17.08.27.14; Fri, 17 Mar 2023 08:27:29 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@gmail.com header.s=20210112 header.b=AxvtPXBc; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231133AbjCQOoJ (ORCPT + 99 others); Fri, 17 Mar 2023 10:44:09 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44750 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231192AbjCQOoA (ORCPT ); Fri, 17 Mar 2023 10:44:00 -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 47D7B1735; Fri, 17 Mar 2023 07:43:56 -0700 (PDT) Received: by mail-lf1-x12c.google.com with SMTP id j11so6731295lfg.13; Fri, 17 Mar 2023 07:43:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; t=1679064234; h=in-reply-to:content-disposition:mime-version:references:message-id :subject:cc:to:from:date:from:to:cc:subject:date:message-id:reply-to; bh=5gNnLKDM3tcCGbdN/rtth+qkmLgZk8AsENLlpVCrPag=; b=AxvtPXBcrDva1XeTvsk2NA6D/z7NZ/uPCU20sV0DevlSZSwmkU8yFWRZmkTijPjRzU D1Nk+F8mKrtBIe9UUZ6zych8/l2LQynVLP5sa2IWOYKqneWiK/Eflx99yHwG1+qqFqyv p6EVLBw/oY6qDDo9HlDgLfysy4qqzv5vrmFxgt3UNv/nLWrVXiXsgTTdGFw5q1spFj0v K9qWrYLjtkgBy0N1acENy5NQ/MDgKkIt5UrepxuD14BhAaUEik05UBHsBmJpYBL7o11+ 55/Q/7e7zkzzumFIzCVzUbyRG3bZ9sMXYjxStbFgla3y8LDeLh71yYb/42EP2WM1hFbD JL0A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1679064234; 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 :message-id:reply-to; bh=5gNnLKDM3tcCGbdN/rtth+qkmLgZk8AsENLlpVCrPag=; b=0KWrg/mKnle/V9aKHey53YL/rUvFFddVcjbpsK5GIPXcWsQRMOmYZmfs8G3bYCrkUc 1onSwdgtCohvvJPzmh0rGEEZfttiSaTcp8e8cq9ye9SRC3SCUcQhHhbLNgLLDe4b3G05 zq+k1EadAD0gpKknmIPqKW7DosTLgFuDmAWjWUVCSgAhbYrYWDWfhAGDDzqdMulpOAZo 7IGaWuWjAhLBJJJZ4K77t0SQAEoFsOm/MrEsgOwSYdVEDCOsk+sCSypikD1DLoFY+ebe //MTx7OgG5r/oDKBXjpep+HYOq89cCVJwE1NI/ivOSjDJZc8fyitB3PGv9G9gCZc8Mrk FSkw== X-Gm-Message-State: AO0yUKVoCNBH8xeALXPCYfTgm+twuL1hb1PC8OvWtoMO8GGyset5leOk jAM5yVBgicfR3L6RIbiOfAM= X-Received: by 2002:ac2:5685:0:b0:4d4:fcdb:6454 with SMTP id 5-20020ac25685000000b004d4fcdb6454mr4443305lfr.17.1679064234397; Fri, 17 Mar 2023 07:43:54 -0700 (PDT) Received: from fedora ([213.255.186.46]) by smtp.gmail.com with ESMTPSA id d8-20020ac25448000000b004d093d60f50sm394431lfn.215.2023.03.17.07.43.53 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 17 Mar 2023 07:43:53 -0700 (PDT) Date: Fri, 17 Mar 2023 16:43:49 +0200 From: Matti Vaittinen To: Matti Vaittinen , Matti Vaittinen Cc: Jonathan Cameron , Lars-Peter Clausen , Matti Vaittinen , Liam Beguin , Randy Dunlap , Masahiro Yamada , linux-kernel@vger.kernel.org, linux-iio@vger.kernel.org Subject: [PATCH v4 5/8] iio: test: test gain-time-scale helpers Message-ID: <31cf5765078b2d808d9e66eb623cde70ee6478ac.1679062529.git.mazziesaccount@gmail.com> References: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM, RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1760629201206032185?= X-GMAIL-MSGID: =?utf-8?q?1760629201206032185?= Some light sensors can adjust both the HW-gain and integration time. There are cases where adjusting the integration time has similar impact to the scale of the reported values as gain setting has. IIO users do typically expect to handle scale by a single writable 'scale' entry. Driver should then adjust the gain/time accordingly. It however is difficult for a driver to know whether it should change gain or integration time to meet the requested scale. Usually it is preferred to have longer integration time which usually improves accuracy, but there may be use-cases where long measurement times can be an issue. Thus it can be preferable to allow also changing the integration time - but mitigate the scale impact by also changing the gain underneath. Eg, if integration time change doubles the measured values, the driver can reduce the HW-gain to half. The theory of the computations of gain-time-scale is simple. However, some people (undersigned) got that implemented wrong for more than once. Hence some gain-time-scale helpers were introduced. Add some simple tests to verify the most hairy functions. Signed-off-by: Matti Vaittinen --- Changes: v3 => v4: - use dummy device to test devm interfaces - adapt to the new header location - drop tests for dropped interfaces v2 => v3: - Use namespace for iio-gts-helpers RFCv1 => v2: - add tests for available scales/times helpers - adapt to renamed iio-gts-helpers.h header --- drivers/iio/test/Kconfig | 16 ++ drivers/iio/test/Makefile | 1 + drivers/iio/test/iio-test-gts.c | 461 ++++++++++++++++++++++++++++++++ 3 files changed, 478 insertions(+) create mode 100644 drivers/iio/test/iio-test-gts.c diff --git a/drivers/iio/test/Kconfig b/drivers/iio/test/Kconfig index 0b6e4e278a2f..4d5cfb9fe60b 100644 --- a/drivers/iio/test/Kconfig +++ b/drivers/iio/test/Kconfig @@ -4,6 +4,20 @@ # # Keep in alphabetical order +config IIO_GTS_KUNIT_TEST + tristate "Test IIO formatting functions" if !KUNIT_ALL_TESTS + depends on KUNIT + select IIO_GTS_HELPER + select TEST_KUNIT_DEVICE_HELPERS + default KUNIT_ALL_TESTS + help + build unit tests for the IIO light sensor gain-time-scale helpers. + + For more information on KUnit and unit tests in general, please refer + to the KUnit documentation in Documentation/dev-tools/kunit/. + + If unsure, say N. Keep in alphabetical order + config IIO_RESCALE_KUNIT_TEST tristate "Test IIO rescale conversion functions" if !KUNIT_ALL_TESTS depends on KUNIT && IIO_RESCALE @@ -27,3 +41,5 @@ config IIO_FORMAT_KUNIT_TEST to the KUnit documentation in Documentation/dev-tools/kunit/. If unsure, say N. + + diff --git a/drivers/iio/test/Makefile b/drivers/iio/test/Makefile index d76eaf36da82..e9a4cf1ff57f 100644 --- a/drivers/iio/test/Makefile +++ b/drivers/iio/test/Makefile @@ -6,4 +6,5 @@ # Keep in alphabetical order obj-$(CONFIG_IIO_RESCALE_KUNIT_TEST) += iio-test-rescale.o obj-$(CONFIG_IIO_FORMAT_KUNIT_TEST) += iio-test-format.o +obj-$(CONFIG_IIO_GTS_KUNIT_TEST) += iio-test-gts.o CFLAGS_iio-test-format.o += $(DISABLE_STRUCTLEAK_PLUGIN) diff --git a/drivers/iio/test/iio-test-gts.c b/drivers/iio/test/iio-test-gts.c new file mode 100644 index 000000000000..ff9311acd0bb --- /dev/null +++ b/drivers/iio/test/iio-test-gts.c @@ -0,0 +1,461 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Unit tests for IIO light sensor gain-time-scale helpers + * + * Copyright (c) 2023 Matti Vaittinen + */ + +#include +#include +#include +#include + +/* + * Please, read the "rant" from the top of the lib/test_linear_ranges.c if + * you see a line of helper code which is not being tested. + * + * Then, please look at the line which is not being tested. Is this line + * somehow unusually complex? If answer is "no", then chances are that the + * "development inertia" caused by adding a test exceeds the benefits. + * + * If yes, then adding a test is probably a good idea but please stop for a + * moment and consider the effort of changing all the tests when code gets + * refactored. Eventually it neeeds to be. + */ + +#define TEST_TSEL_50 1 +#define TEST_TSEL_X_MIN TEST_TSEL_50 +#define TEST_TSEL_100 0 +#define TEST_TSEL_200 2 +#define TEST_TSEL_400 4 +#define TEST_TSEL_X_MAX TEST_TSEL_400 + +#define TEST_GSEL_1 0x00 +#define TEST_GSEL_X_MIN TEST_GSEL_1 +#define TEST_GSEL_4 0x08 +#define TEST_GSEL_16 0x0a +#define TEST_GSEL_32 0x0b +#define TEST_GSEL_64 0x0c +#define TEST_GSEL_256 0x18 +#define TEST_GSEL_512 0x19 +#define TEST_GSEL_1024 0x1a +#define TEST_GSEL_2048 0x1b +#define TEST_GSEL_4096 0x1c +#define TEST_GSEL_X_MAX TEST_GSEL_4096 + +#define TEST_SCALE_1X 64 +#define TEST_SCALE_MIN_X TEST_SCALE_1X +#define TEST_SCALE_2X 32 +#define TEST_SCALE_4X 16 +#define TEST_SCALE_8X 8 +#define TEST_SCALE_16X 4 +#define TEST_SCALE_32X 2 +#define TEST_SCALE_64X 1 + +#define TEST_SCALE_NANO_128X 500000000 +#define TEST_SCALE_NANO_256X 250000000 +#define TEST_SCALE_NANO_512X 125000000 +#define TEST_SCALE_NANO_1024X 62500000 +#define TEST_SCALE_NANO_2048X 31250000 +#define TEST_SCALE_NANO_4096X 15625000 +#define TEST_SCALE_NANO_4096X2 7812500 +#define TEST_SCALE_NANO_4096X4 3906250 +#define TEST_SCALE_NANO_4096X8 1953125 + +#define TEST_SCALE_NANO_MAX_X TEST_SCALE_NANO_4096X8 + +static const struct iio_gain_sel_pair gts_test_gains[] = { + GAIN_SCALE_GAIN(1, TEST_GSEL_1), + GAIN_SCALE_GAIN(4, TEST_GSEL_4), + GAIN_SCALE_GAIN(16, TEST_GSEL_16), + GAIN_SCALE_GAIN(32, TEST_GSEL_32), + GAIN_SCALE_GAIN(64, TEST_GSEL_64), + GAIN_SCALE_GAIN(256, TEST_GSEL_256), + GAIN_SCALE_GAIN(512, TEST_GSEL_512), + GAIN_SCALE_GAIN(1024, TEST_GSEL_1024), + GAIN_SCALE_GAIN(2048, TEST_GSEL_2048), + GAIN_SCALE_GAIN(4096, TEST_GSEL_4096), +#define HWGAIN_MAX 4096 +}; + +static const struct iio_itime_sel_mul gts_test_itimes[] = { + GAIN_SCALE_ITIME_US(400 * 1000, TEST_TSEL_400, 8), + GAIN_SCALE_ITIME_US(200 * 1000, TEST_TSEL_200, 4), + GAIN_SCALE_ITIME_US(100 * 1000, TEST_TSEL_100, 2), + GAIN_SCALE_ITIME_US(50 * 1000, TEST_TSEL_50, 1), +#define TIMEGAIN_MAX 8 +}; +#define TOTAL_GAIN_MAX (HWGAIN_MAX * TIMEGAIN_MAX) + +static int test_init_iio_gain_scale(struct iio_gts *gts, int max_scale_int, + int max_scale_nano) +{ + int ret; + + ret = iio_init_iio_gts(max_scale_int, max_scale_nano, gts_test_gains, + ARRAY_SIZE(gts_test_gains), gts_test_itimes, + ARRAY_SIZE(gts_test_itimes), gts); + + return ret; +} + +static void test_iio_gts_find_gain_for_scale_using_time(struct kunit *test) +{ + struct iio_gts gts; + int ret, gain_sel; + + ret = test_init_iio_gain_scale(>s, TEST_SCALE_1X, 0); + KUNIT_EXPECT_EQ(test, 0, ret); + if (ret) + return; + + ret = iio_gts_find_gain_sel_for_scale_using_time(>s, TEST_TSEL_100, + TEST_SCALE_8X, 0, &gain_sel); + /* + * Meas time 100 => gain by time 2x + * TEST_SCALE_8X matches total gain 8x + * => required HWGAIN 4x + */ + KUNIT_EXPECT_EQ(test, 0, ret); + KUNIT_EXPECT_EQ(test, TEST_GSEL_4, gain_sel); + + ret = iio_gts_find_gain_sel_for_scale_using_time(>s, TEST_TSEL_200, 0, + TEST_SCALE_NANO_256X, &gain_sel); + /* + * Meas time 200 => gain by time 4x + * TEST_SCALE_256X matches total gain 256x + * => required HWGAIN 256/4 => 64x + */ + KUNIT_EXPECT_EQ(test, 0, ret); + KUNIT_EXPECT_EQ(test, TEST_GSEL_64, gain_sel); + + /* Min time, Min gain */ + ret = iio_gts_find_gain_sel_for_scale_using_time(>s, TEST_TSEL_X_MIN, + TEST_SCALE_MIN_X, 0, &gain_sel); + KUNIT_EXPECT_EQ(test, 0, ret); + KUNIT_EXPECT_EQ(test, TEST_GSEL_1, gain_sel); + + /* Max time, Max gain */ + ret = iio_gts_find_gain_sel_for_scale_using_time(>s, TEST_TSEL_X_MAX, + 0, TEST_SCALE_NANO_MAX_X, &gain_sel); + KUNIT_EXPECT_EQ(test, 0, ret); + KUNIT_EXPECT_EQ(test, TEST_GSEL_4096, gain_sel); + + ret = iio_gts_find_gain_sel_for_scale_using_time(>s, TEST_TSEL_100, 0, + TEST_SCALE_NANO_256X, &gain_sel); + /* + * Meas time 100 => gain by time 2x + * TEST_SCALE_256X matches total gain 256x + * => required HWGAIN 256/2 => 128x (not in gain-table - unsupported) + */ + KUNIT_EXPECT_NE(test, 0, ret); + + ret = iio_gts_find_gain_sel_for_scale_using_time(>s, TEST_TSEL_200, 0, + TEST_SCALE_NANO_MAX_X, &gain_sel); + /* We can't reach the max gain with integration time smaller than MAX */ + KUNIT_EXPECT_NE(test, 0, ret); + + ret = iio_gts_find_gain_sel_for_scale_using_time(>s, TEST_TSEL_50, 0, + TEST_SCALE_NANO_MAX_X, &gain_sel); + /* We can't reach the max gain with integration time smaller than MAX */ + KUNIT_EXPECT_NE(test, 0, ret); +} + +static void test_iio_gts_find_new_gain_sel_by_old_gain_time(struct kunit *test) +{ + struct iio_gts gts; + int ret, old_gain, new_gain, old_time_sel, new_time_sel; + + ret = test_init_iio_gain_scale(>s, TEST_SCALE_1X, 0); + KUNIT_EXPECT_EQ(test, 0, ret); + if (ret) + return; + + old_gain = 32; + old_time_sel = TEST_TSEL_200; + new_time_sel = TEST_TSEL_400; + + ret = iio_gts_find_new_gain_sel_by_old_gain_time(>s, old_gain, + old_time_sel, new_time_sel, &new_gain); + KUNIT_EXPECT_EQ(test, 0, ret); + /* + * Doubling the integration time doubles the total gain - so old + * (hw)gain must be divided by two to compensate. => 32 / 2 => 16 + */ + KUNIT_EXPECT_EQ(test, 16, new_gain); + + old_gain = 4; + old_time_sel = TEST_TSEL_50; + new_time_sel = TEST_TSEL_200; + ret = iio_gts_find_new_gain_sel_by_old_gain_time(>s, old_gain, + old_time_sel, new_time_sel, &new_gain); + KUNIT_EXPECT_EQ(test, 0, ret); + /* + * gain by time 1x => 4x - (hw)gain 4x => 1x + */ + KUNIT_EXPECT_EQ(test, 1, new_gain); + + old_gain = 512; + old_time_sel = TEST_TSEL_400; + new_time_sel = TEST_TSEL_50; + ret = iio_gts_find_new_gain_sel_by_old_gain_time(>s, old_gain, + old_time_sel, new_time_sel, &new_gain); + KUNIT_EXPECT_EQ(test, 0, ret); + /* + * gain by time 8x => 1x - (hw)gain 512x => 4096x) + */ + KUNIT_EXPECT_EQ(test, 4096, new_gain); + + /* Unsupported gain 2x */ + old_gain = 4; + old_time_sel = TEST_TSEL_200; + new_time_sel = TEST_TSEL_400; + ret = iio_gts_find_new_gain_sel_by_old_gain_time(>s, old_gain, + old_time_sel, new_time_sel, &new_gain); + KUNIT_EXPECT_NE(test, 0, ret); + + /* Too small gain */ + old_gain = 4; + old_time_sel = TEST_TSEL_50; + new_time_sel = TEST_TSEL_400; + ret = iio_gts_find_new_gain_sel_by_old_gain_time(>s, old_gain, + old_time_sel, new_time_sel, &new_gain); + KUNIT_EXPECT_NE(test, 0, ret); + + /* Too big gain */ + old_gain = 1024; + old_time_sel = TEST_TSEL_400; + new_time_sel = TEST_TSEL_50; + ret = iio_gts_find_new_gain_sel_by_old_gain_time(>s, old_gain, + old_time_sel, new_time_sel, &new_gain); + KUNIT_EXPECT_NE(test, 0, ret); +} + +static void test_iio_find_closest_gain_low(struct kunit *test) +{ + struct iio_gts gts; + bool in_range; + int ret; + + const struct iio_gain_sel_pair gts_test_gains_gain_low[] = { + GAIN_SCALE_GAIN(4, TEST_GSEL_4), + GAIN_SCALE_GAIN(16, TEST_GSEL_16), + GAIN_SCALE_GAIN(32, TEST_GSEL_32), + }; + + ret = test_init_iio_gain_scale(>s, TEST_SCALE_1X, 0); + KUNIT_EXPECT_EQ(test, 0, ret); + if (ret) + return; + + ret = iio_find_closest_gain_low(>s, 2, &in_range); + KUNIT_EXPECT_EQ(test, 1, ret); + KUNIT_EXPECT_EQ(test, true, in_range); + + ret = iio_find_closest_gain_low(>s, 1, &in_range); + KUNIT_EXPECT_EQ(test, 1, ret); + KUNIT_EXPECT_EQ(test, true, in_range); + + ret = iio_find_closest_gain_low(>s, 4095, &in_range); + KUNIT_EXPECT_EQ(test, 2048, ret); + KUNIT_EXPECT_EQ(test, true, in_range); + + ret = iio_find_closest_gain_low(>s, 4097, &in_range); + KUNIT_EXPECT_EQ(test, 4096, ret); + KUNIT_EXPECT_EQ(test, false, in_range); + + ret = iio_init_iio_gts(TEST_SCALE_1X, 0, gts_test_gains_gain_low, + ARRAY_SIZE(gts_test_gains_gain_low), + gts_test_itimes, ARRAY_SIZE(gts_test_itimes), + >s); + KUNIT_EXPECT_EQ(test, 0, ret); + if (ret) + return; + + ret = iio_find_closest_gain_low(>s, 3, &in_range); + KUNIT_EXPECT_EQ(test, -EINVAL, ret); + KUNIT_EXPECT_EQ(test, false, in_range); +} + +static void test_iio_gts_total_gain_to_scale(struct kunit *test) +{ + struct iio_gts gts; + int ret, scale_int, scale_nano; + + ret = test_init_iio_gain_scale(>s, TEST_SCALE_1X, 0); + KUNIT_EXPECT_EQ(test, 0, ret); + if (ret) + return; + + ret = iio_gts_total_gain_to_scale(>s, 1, &scale_int, &scale_nano); + KUNIT_EXPECT_EQ(test, 0, ret); + KUNIT_EXPECT_EQ(test, TEST_SCALE_1X, scale_int); + KUNIT_EXPECT_EQ(test, 0, scale_nano); + + ret = iio_gts_total_gain_to_scale(>s, 1, &scale_int, &scale_nano); + KUNIT_EXPECT_EQ(test, 0, ret); + KUNIT_EXPECT_EQ(test, TEST_SCALE_1X, scale_int); + KUNIT_EXPECT_EQ(test, 0, scale_nano); + + ret = iio_gts_total_gain_to_scale(>s, 4096 * 8, &scale_int, + &scale_nano); + KUNIT_EXPECT_EQ(test, 0, ret); + KUNIT_EXPECT_EQ(test, 0, scale_int); + KUNIT_EXPECT_EQ(test, TEST_SCALE_NANO_4096X8, scale_nano); +} + +static void test_iio_gts_chk_times(struct kunit *test, const int *vals) +{ + static const int expected[] = {50000, 100000, 200000, 400000}; + int i; + + for (i = 0; i < ARRAY_SIZE(expected); i++) + KUNIT_EXPECT_EQ(test, expected[i], vals[i]); +} + +static void test_iio_gts_chk_scales_all(struct kunit *test, struct iio_gts *gts, + const int *vals, int len) +{ + static const int gains[] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, + 1024, 2048, 4096, 4096 * 2, 4096 * 4, + 4096 * 8}; + + int expected[ARRAY_SIZE(gains) * 2]; + int i, ret; + int exp_len = ARRAY_SIZE(gains) * 2; + + KUNIT_EXPECT_EQ(test, exp_len, len); + if (len != exp_len) + return; + + for (i = 0; i < ARRAY_SIZE(gains); i++) { + ret = iio_gts_total_gain_to_scale(gts, gains[i], + &expected[2 * i], + &expected[2 * i + 1]); + KUNIT_EXPECT_EQ(test, 0, ret); + if (ret) + return; + } + + for (i = 0; i < ARRAY_SIZE(expected); i++) + KUNIT_EXPECT_EQ(test, expected[i], vals[i]); +} + +static void test_iio_gts_chk_scales_t200(struct kunit *test, struct iio_gts *gts, + const int *vals, int len) +{ + /* The gain caused by time 200 is 4x */ + static const int gains[] = { + 1 * 4, + 4 * 4, + 16 * 4, + 32 * 4, + 64 * 4, + 256 * 4, + 512 * 4, + 1024 * 4, + 2048 * 4, + 4096 * 4 + }; + int expected[ARRAY_SIZE(gains) * 2]; + int i, ret; + + KUNIT_EXPECT_EQ(test, 2 * ARRAY_SIZE(gains), len); + if (len < 2 * ARRAY_SIZE(gains)) + return; + + for (i = 0; i < ARRAY_SIZE(gains); i++) { + ret = iio_gts_total_gain_to_scale(gts, gains[i], + &expected[2 * i], + &expected[2 * i + 1]); + KUNIT_EXPECT_EQ(test, 0, ret); + if (ret) + return; + } + + for (i = 0; i < ARRAY_SIZE(expected); i++) + KUNIT_EXPECT_EQ(test, expected[i], vals[i]); +} + +static void test_iio_gts_avail_test(struct kunit *test) +{ + struct iio_gts gts; + int ret; + int type, len; + const int *vals; + struct device *dev; + + ret = test_init_iio_gain_scale(>s, TEST_SCALE_1X, 0); + KUNIT_EXPECT_EQ(test, 0, ret); + if (ret) + return; + + dev = test_kunit_helper_alloc_device(test); + KUNIT_EXPECT_NOT_ERR_OR_NULL(test, dev); + if (!dev) + return; + + ret = devm_iio_gts_build_avail_tables(dev, >s); + KUNIT_EXPECT_EQ(test, 0, ret); + if (ret) + goto drop_testdev; + + /* test table building for times and iio_gts_avail_times() */ + ret = iio_gts_avail_times(>s, &vals, &type, &len); + KUNIT_EXPECT_EQ(test, IIO_AVAIL_LIST, ret); + if (ret) + goto drop_testdev; + + KUNIT_EXPECT_EQ(test, IIO_VAL_INT, type); + KUNIT_EXPECT_EQ(test, 4, len); + if (len < 4) + goto drop_testdev; + + test_iio_gts_chk_times(test, vals); + + /* Test table building for all scales and iio_gts_all_avail_scales() */ + ret = iio_gts_all_avail_scales(>s, &vals, &type, &len); + KUNIT_EXPECT_EQ(test, IIO_AVAIL_LIST, ret); + if (ret) + goto drop_testdev; + + KUNIT_EXPECT_EQ(test, IIO_VAL_INT_PLUS_NANO, type); + + test_iio_gts_chk_scales_all(test, >s, vals, len); + + /* + * Test table building for scales/time and + * iio_gts_avail_scales_for_time() + */ + ret = iio_gts_avail_scales_for_time(>s, 200000, &vals, &type, &len); + KUNIT_EXPECT_EQ(test, IIO_AVAIL_LIST, ret); + if (ret) + goto drop_testdev; + + KUNIT_EXPECT_EQ(test, IIO_VAL_INT_PLUS_NANO, type); + test_iio_gts_chk_scales_t200(test, >s, vals, len); + +drop_testdev: + test_kunit_helper_free_device(test, dev); +} + +static struct kunit_case iio_gts_test_cases[] = { + KUNIT_CASE(test_iio_gts_find_gain_for_scale_using_time), + KUNIT_CASE(test_iio_gts_find_new_gain_sel_by_old_gain_time), + KUNIT_CASE(test_iio_find_closest_gain_low), + KUNIT_CASE(test_iio_gts_total_gain_to_scale), + KUNIT_CASE(test_iio_gts_avail_test), + {} +}; + +static struct kunit_suite iio_gts_test_suite = { + .name = "iio-gain-time-scale", + .test_cases = iio_gts_test_cases, +}; + +kunit_test_suite(iio_gts_test_suite); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Matti Vaittinen "); +MODULE_DESCRIPTION("Test IIO light sensor gain-time-scale helpers"); +MODULE_IMPORT_NS(IIO_GTS_HELPER); + From patchwork Fri Mar 17 14:44:11 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Matti Vaittinen X-Patchwork-Id: 71329 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a5d:604a:0:0:0:0:0 with SMTP id j10csp401956wrt; Fri, 17 Mar 2023 08:24:26 -0700 (PDT) X-Google-Smtp-Source: AK7set/5hSAknONlikd3Ex97Sz6uZxXO6ZhNfSR0NGHuagQ1MSZRCglERa3DIAA7nnxX8SJy471o X-Received: by 2002:a17:90a:9c5:b0:23f:ebc:6c51 with SMTP id 63-20020a17090a09c500b0023f0ebc6c51mr6779780pjo.0.1679066665534; Fri, 17 Mar 2023 08:24:25 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1679066665; cv=none; d=google.com; s=arc-20160816; b=fGbJUeHdTfXrHGigAOIWf1FhFyZQ8fi+TE5ukCdDcRAj93FRECYRU2E9080y85iuZW SDzjZRjDyRgcognt2ndO/QgF/FVbrQkX57xLdb4lUqGH1/EOGQoHrANL5dnu86Uiug1E AmAOsU19An5fhLzwQT5ynZVglx0bWZ0jHtTRDghSCvtTR56OOjERxQMgfIIEpgSuOTsx J3WQoM0uiLP4U1VAwkPgkky5ZtEQ3AlWi45lM13o7IKWoYrP8Y8LUKIwvRZRajZDzHEk b+cywiOSWJYbZj+GnC6xs0PHSVDIOshbxpugIDBhkA5AxSfxA40y3x0S3rYxsWceY/dl YodA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:in-reply-to:content-disposition:mime-version :references:message-id:subject:cc:to:from:date:dkim-signature; bh=F4vsu7qDZdO3SeamPBvE2xBcvyuEaU+xg4d8g5k76Do=; b=ToVPwLlKjjJX/mH1WxuDFeto5U/2/LPaJbMdwPyMD9ERKtgs8GpHFAzBfLw/ei9drI esHGN2aop1S0/ua/xhN/FA96LY79EkrQ0f0YWR+/bJNbHgTOn7b23ZfHoJPslwpTDm/C +fRIZBgUZGG99duqrTWmXUR0+RBenw3oiECne4BJ/qhmgg5Yy8NzjWD5DTr/BCKnlaCK RR9V4HnTjkaGRYFO932jpEG9buKWXgd+zkG8CJXO6jrgdAIIPekO7SKJV3SMm18SvCmU 7qAqSlfiydH8iXCbpx/4YkQYeeCkh19WcM9oZ+hx0qdkk9o9BIH5A6JgnKuQaFge5QaU kNzQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gmail.com header.s=20210112 header.b=EBNUn5qP; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id q14-20020a631f4e000000b00502f0d2abc5si2469780pgm.725.2023.03.17.08.24.12; Fri, 17 Mar 2023 08:24:25 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@gmail.com header.s=20210112 header.b=EBNUn5qP; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229533AbjCQOo3 (ORCPT + 99 others); Fri, 17 Mar 2023 10:44:29 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:45708 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230509AbjCQOo0 (ORCPT ); Fri, 17 Mar 2023 10:44:26 -0400 Received: from mail-lj1-x22a.google.com (mail-lj1-x22a.google.com [IPv6:2a00:1450:4864:20::22a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 4327310EABC for ; Fri, 17 Mar 2023 07:44:17 -0700 (PDT) Received: by mail-lj1-x22a.google.com with SMTP id t14so5333567ljd.5 for ; Fri, 17 Mar 2023 07:44:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; t=1679064255; h=in-reply-to:content-disposition:mime-version:references:message-id :subject:cc:to:from:date:from:to:cc:subject:date:message-id:reply-to; bh=F4vsu7qDZdO3SeamPBvE2xBcvyuEaU+xg4d8g5k76Do=; b=EBNUn5qPmRgzgClHOzimEzjFNVsW6K3Kxfof44nz7BuqCgupFZrmBQeQL30mDc1YvK ulVhBdQLNeQqYapns6bsrHSBRxXdyKWjWyps03eQjsv/2CU79tbe3ZI9osH2ydef1sLI AhZ/IxnQOAukqgg3/wPEBQw9p83H18o8bR3qf9T6BgbtUBTBlTVm0t281ZIoBlOBMVUi 82O9XUWBhH1yAdiisDupkqxEy7q0AcgrXzGdsoYDYh6HyhPHZo7bHQpipFqK4KN8rYM4 wpD0+fvKYXtrDdq0JUNll4fFd3FIsJtrWx4Lr+DYhiT7Z3wrc7VYzPZ571jC3keVzZ9M elmA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1679064255; 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 :message-id:reply-to; bh=F4vsu7qDZdO3SeamPBvE2xBcvyuEaU+xg4d8g5k76Do=; b=ApnVQRb6yPIYAvYhOXn5X4V6FLrVK65OemG39N2AUUiHJo0tIGIkGvD5otrbgqKw5a Rlgb3Y238vTB3XfCdwLPIqTTIngJsK8vDrKbJryJeC93mAkFdVYNgC6+65QfPlSSf2lu OCeoVD1XTaLV9TOyV6cnpziVu1IH08UvNt55iYGC4p2cGjvS+YWqamfZc8EFjLRk0MWd 6DYW52ahGXvpz68Z6LRc3rtj3TXytpfFF0nKEnumCRC6WM/qTMdPbC+IKiKsgUI0gbxM rag/a/1HOdpjasFdFg+hqIYJ8/XxlEtw8oNRzQ8k0HalxA26iFRHKDpnbLqysB68q1dm wmsA== X-Gm-Message-State: AO0yUKV5m/aTKUXbEZwxAt9zGPDafZOwx8QiWq3Lv1E5jiLrJu/GupiP Jt4tYDe2RIvW6zIwfH1TL0A= X-Received: by 2002:a05:651c:30f:b0:28b:ad5b:578f with SMTP id a15-20020a05651c030f00b0028bad5b578fmr3147018ljp.31.1679064255378; Fri, 17 Mar 2023 07:44:15 -0700 (PDT) Received: from fedora ([213.255.186.46]) by smtp.gmail.com with ESMTPSA id f22-20020a2e9516000000b00295da33c42dsm443886ljh.15.2023.03.17.07.44.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 17 Mar 2023 07:44:14 -0700 (PDT) Date: Fri, 17 Mar 2023 16:44:11 +0200 From: Matti Vaittinen To: Matti Vaittinen , Matti Vaittinen Cc: linux-kernel@vger.kernel.org Subject: [PATCH v4 6/8] MAINTAINERS: Add IIO gain-time-scale helpers Message-ID: <692652c6021608aa854774f1bf21755a36a1bc51.1679062529.git.mazziesaccount@gmail.com> References: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM, RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1760629008005671699?= X-GMAIL-MSGID: =?utf-8?q?1760629008005671699?= Add myself as a maintainer for IIO light sensor helpers (helpers for maintaining the scale while adjusting intergration time or gain) and related Kunit tests. Signed-off-by: Matti Vaittinen --- RFCv1 => - No changes --- MAINTAINERS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index ec57c42ed544..6ec9326f4ce9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9938,6 +9938,14 @@ F: Documentation/ABI/testing/sysfs-bus-iio-adc-envelope-detector F: Documentation/devicetree/bindings/iio/adc/envelope-detector.yaml F: drivers/iio/adc/envelope-detector.c +IIO LIGHT SENSOR GAIN-TIME-SCALE HELPERS +M: Matti Vaittinen +L: linux-iio@vger.kernel.org +S: Maintained +F: drivers/iio/light/gain-time-scale-helper.c +F: drivers/iio/light/gain-time-scale-helper.h +F: drivers/iio/test/iio-test-gts.c + IIO MULTIPLEXER M: Peter Rosin L: linux-iio@vger.kernel.org From patchwork Fri Mar 17 14:44:40 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Matti Vaittinen X-Patchwork-Id: 71305 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a5d:604a:0:0:0:0:0 with SMTP id j10csp387172wrt; Fri, 17 Mar 2023 08:01:00 -0700 (PDT) X-Google-Smtp-Source: AK7set+U7tkJDy91e0rmxNFjqAzuf42AzY/DHy3DqMNJt5vF/ZhqnvaVU+O1BIWEUzreKU9IawNI X-Received: by 2002:a17:902:ef8d:b0:19d:1e21:7f59 with SMTP id iz13-20020a170902ef8d00b0019d1e217f59mr10417204plb.0.1679065259646; Fri, 17 Mar 2023 08:00:59 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1679065259; cv=none; d=google.com; s=arc-20160816; b=TuKBLcDLywNuk4qLb43clJHBIAOkzJ1N8iCdowaawu6j+y4LloTZIc0Ih0mltVEVtp jCZuDCyU5tEGXkfxt6Al/QKfQVxTemkL2NnSWrfKGme/9wnxClsQtBV8wrMTI6tqyExp AKy0uZREMUIGVNjePtjDVNwZB+OnVB6oampkt6tgGvok37ydJt5JDZyRAPJOnFWJs0PO fssACjst7YXsuZfHlvGvpvKME3KePR1em41tSHx03/i5Kkha+BD+F135cfWFp/idBfuG tuKxnHuF8Mj/WqcH2E0CuCn5t0d4FzNRjlHWvAYBE2lH6MWAogKFHoLuFE/nWkUp7F6z MaiA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:in-reply-to:content-disposition:mime-version :references:message-id:subject:cc:to:from:date:dkim-signature; bh=H2+l0SmeReqGQu3Fo8ZgLf4MJYBCKBEhsX/K99zzlH0=; b=v9Wtrvk9uhibf0xOxpLryT01fVDTIG4I0YLdTKeGn6HjpKpP+7glccBuufEiWH+tuP VMgmhl824XeEycKrW+BVJU+6H9Qh61YGCy4d57UHdbma4n33jDmUD/sFYkSwzBO+j26z bkDYnbfS4F6jVvDd4taKmzobcNh9Aik/Wb7F/e7AP2Ze2oEqfEJd3QC8Ns3gr2z+L6I8 vkze+yOHZXwjJRt7hwytVk5JZumnMeUQy0VV0qmp94XThgZA98oxr3+AhhvlUSZuk119 aEi/j0BD07UhqNkH9tpnTkXue6LM+t0eGRgKiNKZ9uL6gsRaN459UDQ5pQB/tq8QCHLu KdzQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gmail.com header.s=20210112 header.b=a2jQt+nC; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id r15-20020a17090a560f00b0023747b030e7si2356264pjf.105.2023.03.17.08.00.39; Fri, 17 Mar 2023 08:00:59 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@gmail.com header.s=20210112 header.b=a2jQt+nC; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230520AbjCQOpT (ORCPT + 99 others); Fri, 17 Mar 2023 10:45:19 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47302 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231190AbjCQOpQ (ORCPT ); Fri, 17 Mar 2023 10:45:16 -0400 Received: from mail-lj1-x236.google.com (mail-lj1-x236.google.com [IPv6:2a00:1450:4864:20::236]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D5CA010EA99; Fri, 17 Mar 2023 07:44:46 -0700 (PDT) Received: by mail-lj1-x236.google.com with SMTP id b10so5378102ljr.0; Fri, 17 Mar 2023 07:44:46 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; t=1679064285; h=in-reply-to:content-disposition:mime-version:references:message-id :subject:cc:to:from:date:from:to:cc:subject:date:message-id:reply-to; bh=H2+l0SmeReqGQu3Fo8ZgLf4MJYBCKBEhsX/K99zzlH0=; b=a2jQt+nCgypB2XT+m2FUJJSrZiK5y+M5YviLbkt7Bs9XqeMmpuTJXa7aADAi1+U2Zc JKFsSCuVG4ArOVmkToaPDhGUD+9k01W3bSStxLhoECltb1A8NMWQ18coVFppIMNBMNSA T7EuUMlis3X7w/O+ldm8lrPOmOQiOtvmv0cIsk5oKGLg6rfLz+443ag1mwm2VcJDHpuc 1wF4dJLKPoQafg0uI+9F4xPoHYv8TI3m/+Y8UVTB15b0Jq7BhneQkDyhmTv5tZWEkr9d BPRtTIXHBeyor/xH+v/NgSGhMMwrBg+/cIIRU091wsBoLWfshbPHUf2xQ8/dxIJX10Ta hU2g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1679064285; 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 :message-id:reply-to; bh=H2+l0SmeReqGQu3Fo8ZgLf4MJYBCKBEhsX/K99zzlH0=; b=UEPPtXlS6RnUTzHXTs5q9DGETcdzYcvpd5v3IqgW3r7DWqz8HA3x1SDn9AkdfLVhEW CgHV9Akm8X21iCXJDTiOo/Ksr9Huc17d/PEToeWIbyIWd97sx+PP3+GqYgU7jM4E06rw 25YfUXGGiMHGljJkDgEA+UkgMQEkC47DoniMizb6PFYQ2weLnQO7yyjHJAyyoSkSf8Re /ySmLZ/9sQpyqAEeEv+GKBiIQQPcXIIwkTIOUwpRQ8/Ma9fol3kBBj9RWBkG52KIPUEl Qh/QgBBV3GRSyhoWV15EAn3NEp7qaY5F0+t50fvuFFjzwcPCHF1Lvke7hG+XPVwRtpH3 Ut9A== X-Gm-Message-State: AO0yUKWGYFnk+8dEbC4wYGNVRe3+N4emHAkeTEIh98H9LT69PTsmvrC4 iiwrqKZAb9VDtczl0otdDuqdv4OgSiA= X-Received: by 2002:a2e:8ec6:0:b0:295:acea:5885 with SMTP id e6-20020a2e8ec6000000b00295acea5885mr1198450ljl.14.1679064284788; Fri, 17 Mar 2023 07:44:44 -0700 (PDT) Received: from fedora ([213.255.186.46]) by smtp.gmail.com with ESMTPSA id s9-20020a2e98c9000000b00293534d9757sm441872ljj.81.2023.03.17.07.44.43 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 17 Mar 2023 07:44:44 -0700 (PDT) Date: Fri, 17 Mar 2023 16:44:40 +0200 From: Matti Vaittinen To: Matti Vaittinen , Matti Vaittinen Cc: Jonathan Cameron , Lars-Peter Clausen , Matti Vaittinen , Andy Shevchenko , Paul Gazzillo , Zhigang Shi , Shreeya Patel , Dmitry Osipenko , linux-kernel@vger.kernel.org, linux-iio@vger.kernel.org Subject: [PATCH v4 7/8] iio: light: ROHM BU27034 Ambient Light Sensor Message-ID: References: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM, RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1760627533798202678?= X-GMAIL-MSGID: =?utf-8?q?1760627533798202678?= ROHM BU27034 is an ambient light sensor with 3 channels and 3 photo diodes capable of detecting a very wide range of illuminance. Typical application is adjusting LCD and backlight power of TVs and mobile phones. Add initial support for the ROHM BU27034 ambient light sensor. NOTE: - Driver exposes 4 channels. One IIO_LIGHT channel providing the calculated lux values based on measured data from diodes #0 and #1. In addition, 3 IIO_INTENSITY channels are emitting the raw register data from all diodes for more intense user-space computations. - Sensor has GAIN values that can be adjusted from 1x to 4096x. - Sensor has adjustible measurement times of 5, 55, 100, 200 and 400 mS. Driver does not support 5 mS which has special limitations. - Driver exposes standard 'scale' adjustment which is implemented by: 1) Trying to adjust only the GAIN 2) If GAIN adjustment alone can't provide requested scale, adjusting both the time and the gain is attempted. - Driver exposes writable INT_TIME property that can be used for adjusting the measurement time. Time adjustment will also cause the driver to try to adjust the GAIN so that the overall scale is kept as close to the original as possible. Signed-off-by: Matti Vaittinen --- Changes v3 => v4: - use min_t() for division by zero check - adapt to new GTS helper header location - calculate luxes not milli luxes - drop scale for PROCESSED channel - comment improvements - do not allow changing gain (scale) for channel 2. - 'tie' channel 2 scale to channel 0 scale This is because channel 0 and channel 2 GAIN settings share part of the bits in the register. This means that setting one will also impact the other. The v3 of the patches attempted to work-around this by only disallowing the channel 2 gain setting to set the bits which were shared with channel 0 gain. This does not work because setting channel 0 gain (which was allowed to set also the shared bits) could result unsupported bit combinations for channel 2 gain. Thus it is safest to always set also the channel 2 gain to same value as channel 0 gain. - Use the correct integration time (55 mS) in the gain table as the calcuations can be done based on the time multiplier. - styling v2 => v3: - commit message update and typofixes - switch warning messages to dbg - drop incorrect comment about unchanged scales - return 'no new data' if valid bit read failed - shorten the 'div by zero' checks - don't use u32 pointer when int * is epected in lux calculation - add a comment clarifying why it is safe to return int from lux calculation - simplify read_raw() by refactoring the measurement start / stop in another function and dropping the goto based unlocking. - Styling fixes - select IIO_BUFFER and IIO_KFIFO_BUF - Alphabetical order of header includes - Split multipication w/ overflow check to own function - Do not hang in read_raw() if sensor does not return valid sample - Spelling fix - Do not require fwnode - Use namespace for gts helpers RFCv1 => v2: - (really) protect read-only registers - fix get and set gain - buffered mode - Protect the whole sequences including meas_en/meas_dis to avoid messing up the enable / disable order - typofixes / doc improvements - change dropped GAIN_SCALE_ITIME_MS() to GAIN_SCALE_ITIME_US() - use more accurate scale for lux channel (milli lux) - provide available scales / integration times (using helpers). - adapt to renamed iio-gts-helpers.h file - bu27034 - longer lines in Kconfig - Drop bu27034_meas_en and bu27034_meas_dis wrappers. - Change device-name from bu27034-als to bu27034 --- drivers/iio/light/Kconfig | 14 + drivers/iio/light/Makefile | 1 + drivers/iio/light/rohm-bu27034.c | 1491 ++++++++++++++++++++++++++++++ 3 files changed, 1506 insertions(+) create mode 100644 drivers/iio/light/rohm-bu27034.c diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index 0d4447df7200..6fa31fcd71a1 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -289,6 +289,20 @@ config JSA1212 To compile this driver as a module, choose M here: the module will be called jsa1212. +config ROHM_BU27034 + tristate "ROHM BU27034 ambient light sensor" + depends on I2C + select REGMAP_I2C + select IIO_GTS_HELPER + select IIO_BUFFER + select IIO_KFIFO_BUF + help + Enable support for the ROHM BU27034 ambient light sensor. ROHM BU27034 + is an ambient light sesnor with 3 channels and 3 photo diodes capable + of detecting a very wide range of illuminance. + Typical application is adjusting LCD and backlight power of TVs and + mobile phones. + config RPR0521 tristate "ROHM RPR0521 ALS and proximity sensor driver" depends on I2C diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index d74d2b5ff14c..985f6feaccd4 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -38,6 +38,7 @@ obj-$(CONFIG_MAX44009) += max44009.o obj-$(CONFIG_NOA1305) += noa1305.o obj-$(CONFIG_OPT3001) += opt3001.o obj-$(CONFIG_PA12203001) += pa12203001.o +obj-$(CONFIG_ROHM_BU27034) += rohm-bu27034.o obj-$(CONFIG_RPR0521) += rpr0521.o obj-$(CONFIG_SI1133) += si1133.o obj-$(CONFIG_SI1145) += si1145.o diff --git a/drivers/iio/light/rohm-bu27034.c b/drivers/iio/light/rohm-bu27034.c new file mode 100644 index 000000000000..db1bbb0c0d35 --- /dev/null +++ b/drivers/iio/light/rohm-bu27034.c @@ -0,0 +1,1491 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * BU27034 ROHM Ambient Light Sensor + * + * Copyright (c) 2023, ROHM Semiconductor. + * https://fscdn.rohm.com/en/products/databook/datasheet/ic/sensor/light/bu27034nuc-e.pdf + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + +#define BU27034_REG_SYSTEM_CONTROL 0x40 +#define BU27034_MASK_SW_RESET BIT(7) +#define BU27034_MASK_PART_ID GENMASK(5, 0) +#define BU27034_ID 0x19 +#define BU27034_REG_MODE_CONTROL1 0x41 +#define BU27034_MASK_MEAS_MODE GENMASK(2, 0) + +#define BU27034_REG_MODE_CONTROL2 0x42 +#define BU27034_MASK_D01_GAIN GENMASK(7, 3) +#define BU27034_SHIFT_D01_GAIN 3 +#define BU27034_MASK_D2_GAIN_HI GENMASK(7, 6) +#define BU27034_MASK_D2_GAIN_LO GENMASK(2, 0) + +#define BU27034_REG_MODE_CONTROL3 0x43 +#define BU27034_REG_MODE_CONTROL4 0x44 +#define BU27034_MASK_MEAS_EN BIT(0) +#define BU27034_MASK_VALID BIT(7) +#define BU27034_REG_DATA0_LO 0x50 +#define BU27034_REG_DATA1_LO 0x52 +#define BU27034_REG_DATA2_LO 0x54 +#define BU27034_REG_DATA2_HI 0x55 +#define BU27034_REG_MANUFACTURER_ID 0x92 +#define BU27034_REG_MAX BU27034_REG_MANUFACTURER_ID + +/* + * The BU27034 does not have interrupt to trigger the data read when a + * measurement has finished. Hence we poll the VALID bit in a thread. We will + * try to wake the thread BU27034_MEAS_WAIT_PREMATURE_MS milliseconds before + * the expected sampling time to prevent the drifting. + * + * If we constantly wake up a bit too late we would eventually skip a sample. + * And because the sleep can't wake up _exactly_ at given time this would be + * inevitable even if the sensor clock would be perfectly phase-locked to CPU + * clock - which we can't say is the case. + * + * This is still fragile. No matter how big advance do we have, we will still + * risk of losing a sample because things can in a rainy-day skenario be + * delayed a lot. Yet, more we reserve the time for polling, more we also lose + * the performance by spending cycles polling the register. So, selecting this + * value is a balancing dance between severity of wasting CPU time and severity + * of losing samples. + * + * In most cases losing the samples is not _that_ crucial because light levels + * tend to change slowly. + * + * Other option that was pointed to me would be always sleeping 1/2 of the + * measurement time, checking the VALID bit and just sleeping again if the bit + * was not set. That should be pretty tolerant against missing samples due to + * the scheduling delays while also not wasting much of cycles for polling. + * Downside is that the time-stamps would be very inaccurate as the wake-up + * would not really be tied to the senso toggling the valid bit. This would also + * result 'jumps' in the time-stamps when the delay drifted so that wake-up was + * performed during teo consecutive wake-ups (Or, when sensor and CPU clocks + * were very different and scheduling the wake-ups was very close to given + * timeout - and when the time-outs were very close to the actual sensor + * sampling, Eg. once in a blue moon, two consequtive time-outs would occur + * without having a sample ready). + */ +#define BU27034_MEAS_WAIT_PREMATURE_MS 5 +#define BU27034_DATA_WAIT_TIME_US 1000 +#define BU27034_TOTAL_DATA_WAIT_TIME_US (BU27034_MEAS_WAIT_PREMATURE_MS * 1000) + +#define BU27034_RETRY_LIMIT 18 + +enum { + BU27034_CHAN_ALS, + BU27034_CHAN_DATA0, + BU27034_CHAN_DATA1, + BU27034_CHAN_DATA2, + BU27034_NUM_CHANS +}; + +static const unsigned long bu27034_scan_masks[] = { + BIT(BU27034_CHAN_ALS) | BIT(BU27034_CHAN_DATA0) | + BIT(BU27034_CHAN_DATA1) | BIT(BU27034_CHAN_DATA2), 0 +}; + +/* + * Available scales with gain 1x - 4096x, timings 55, 100, 200, 400 mS + * Time impacts to gain: 1x, 2x, 4x, 8x. + * + * => Max total gain is HWGAIN * gain by integration time (8 * 4096) = 32768 + * + * Using NANO precision for scale we must use scale 64x corresponding gain 1x + * to avoid precision loss. (32x would result scale 976 562.5(nanos). + */ +#define BU27034_SCALE_1X 64 + +/* See the data sheet for the "Gain Setting" table */ +#define BU27034_GSEL_1X 0x00 /* 00000 */ +#define BU27034_GSEL_4X 0x08 /* 01000 */ +#define BU27034_GSEL_16X 0x0a /* 01010 */ +#define BU27034_GSEL_32X 0x0b /* 01011 */ +#define BU27034_GSEL_64X 0x0c /* 01100 */ +#define BU27034_GSEL_256X 0x18 /* 11000 */ +#define BU27034_GSEL_512X 0x19 /* 11001 */ +#define BU27034_GSEL_1024X 0x1a /* 11010 */ +#define BU27034_GSEL_2048X 0x1b /* 11011 */ +#define BU27034_GSEL_4096X 0x1c /* 11100 */ + +/* Available gain settings */ +static const struct iio_gain_sel_pair bu27034_gains[] = { + GAIN_SCALE_GAIN(1, BU27034_GSEL_1X), + GAIN_SCALE_GAIN(4, BU27034_GSEL_4X), + GAIN_SCALE_GAIN(16, BU27034_GSEL_16X), + GAIN_SCALE_GAIN(32, BU27034_GSEL_32X), + GAIN_SCALE_GAIN(64, BU27034_GSEL_64X), + GAIN_SCALE_GAIN(256, BU27034_GSEL_256X), + GAIN_SCALE_GAIN(512, BU27034_GSEL_512X), + GAIN_SCALE_GAIN(1024, BU27034_GSEL_1024X), + GAIN_SCALE_GAIN(2048, BU27034_GSEL_2048X), + GAIN_SCALE_GAIN(4096, BU27034_GSEL_4096X), +}; + +/* + * The IC has 5 modes for sampling time. 5 mS mode is exceptional as it limits + * the data collection to data0-channel only and cuts the supported range to + * 10 bit. It is not supported by the driver. + * + * "normal" modes are 55, 100, 200 and 400 mS modes - which do have direct + * multiplying impact to the register values (similar to gain). + * + * This means that if meas-mode is changed for example from 400 => 200, + * the scale is doubled. Eg, time impact to total gain is x1, x2, x4, x8. + */ +#define BU27034_MEAS_MODE_100MS 0 +#define BU27034_MEAS_MODE_55MS 1 +#define BU27034_MEAS_MODE_200MS 2 +#define BU27034_MEAS_MODE_400MS 4 + +static const struct iio_itime_sel_mul bu27034_itimes[] = { + GAIN_SCALE_ITIME_US(400000, BU27034_MEAS_MODE_400MS, 8), + GAIN_SCALE_ITIME_US(200000, BU27034_MEAS_MODE_200MS, 4), + GAIN_SCALE_ITIME_US(100000, BU27034_MEAS_MODE_100MS, 2), + GAIN_SCALE_ITIME_US(55000, BU27034_MEAS_MODE_55MS, 1), +}; + +#define BU27034_CHAN_DATA(_name, _ch2) \ +{ \ + .type = IIO_INTENSITY, \ + .channel = BU27034_CHAN_##_name, \ + .channel2 = (_ch2), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), \ + .info_mask_shared_by_all_available = \ + BIT(IIO_CHAN_INFO_INT_TIME), \ + .address = BU27034_REG_##_name##_LO, \ + .scan_index = BU27034_CHAN_##_name, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_LE, \ + }, \ + .indexed = 1, \ +} + +static const struct iio_chan_spec bu27034_channels[] = { + { + .type = IIO_LIGHT, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + .channel = BU27034_CHAN_ALS, + .scan_index = BU27034_CHAN_ALS, + .scan_type = { + .sign = 'u', + .realbits = 32, + .storagebits = 32, + .endianness = IIO_CPU, + }, + }, + /* + * The BU27034 DATA0 and DATA1 channels are both on the visible light + * area (mostly). The data0 sensitivity peaks at 500nm, DATA1 at 600nm. + * These wave lengths are pretty much on the border of colours making + * these a poor candidates for R/G/B standardization. Hence they're both + * marked as clear channels + */ + BU27034_CHAN_DATA(DATA0, IIO_MOD_LIGHT_CLEAR), + BU27034_CHAN_DATA(DATA1, IIO_MOD_LIGHT_CLEAR), + BU27034_CHAN_DATA(DATA2, IIO_MOD_LIGHT_IR), + IIO_CHAN_SOFT_TIMESTAMP(4), +}; + +struct bu27034_data { + struct regmap *regmap; + struct device *dev; + /* + * Protect gain and time during scale adjustment and data reading. + * Protect measurement enabling/disabling. + */ + struct mutex mutex; + struct iio_gts gts; + struct task_struct *task; + __le16 raw[3]; + struct { + u32 lux; + __le16 channels[3]; + s64 ts __aligned(8); + } scan; +}; + +struct bu27034_result { + u16 ch0; + u16 ch1; + u16 ch2; +}; + +static const struct regmap_range bu27034_volatile_ranges[] = { + { + .range_min = BU27034_REG_MODE_CONTROL4, + .range_max = BU27034_REG_MODE_CONTROL4, + }, { + .range_min = BU27034_REG_DATA0_LO, + .range_max = BU27034_REG_DATA2_HI, + }, +}; + +static const struct regmap_access_table bu27034_volatile_regs = { + .yes_ranges = &bu27034_volatile_ranges[0], + .n_yes_ranges = ARRAY_SIZE(bu27034_volatile_ranges), +}; + +static const struct regmap_range bu27034_read_only_ranges[] = { + { + .range_min = BU27034_REG_DATA0_LO, + .range_max = BU27034_REG_DATA2_HI, + }, { + .range_min = BU27034_REG_MANUFACTURER_ID, + .range_max = BU27034_REG_MANUFACTURER_ID, + } +}; + +static const struct regmap_access_table bu27034_ro_regs = { + .no_ranges = &bu27034_read_only_ranges[0], + .n_no_ranges = ARRAY_SIZE(bu27034_read_only_ranges), +}; + +static const struct regmap_config bu27034_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = BU27034_REG_MAX, + .cache_type = REGCACHE_RBTREE, + .volatile_table = &bu27034_volatile_regs, + .wr_table = &bu27034_ro_regs, +}; + +struct bu27034_gain_check { + int old_gain; + int new_gain; + int chan; +}; + +static int bu27034_get_gain_sel(struct bu27034_data *data, int chan) +{ + int ret, val; + + switch (chan) { + case BU27034_CHAN_DATA0: + case BU27034_CHAN_DATA1: + { + int reg[] = { + [BU27034_CHAN_DATA0] = BU27034_REG_MODE_CONTROL2, + [BU27034_CHAN_DATA1] = BU27034_REG_MODE_CONTROL3, + }; + ret = regmap_read(data->regmap, reg[chan], &val); + if (ret) + return ret; + + return FIELD_GET(BU27034_MASK_D01_GAIN, val); + } + case BU27034_CHAN_DATA2: + { + int d2_lo_bits = fls(BU27034_MASK_D2_GAIN_LO); + + ret = regmap_read(data->regmap, BU27034_REG_MODE_CONTROL2, &val); + if (ret) + return ret; + + /* + * The data2 channel gain is composed by 5 non continuous bits + * [7:6], [2:0]. Thus when we combine the 5-bit 'selector' + * from register value we must right shift the high bits by 3. + */ + return FIELD_GET(BU27034_MASK_D2_GAIN_HI, val) << d2_lo_bits | + FIELD_GET(BU27034_MASK_D2_GAIN_LO, val); + } + default: + return -EINVAL; + } +} + +static int bu27034_get_gain(struct bu27034_data *data, int chan, int *gain) +{ + int ret, sel; + + ret = bu27034_get_gain_sel(data, chan); + if (ret < 0) + return ret; + + sel = ret; + + ret = iio_gts_find_gain_by_sel(&data->gts, sel); + if (ret < 0) { + dev_err(data->dev, "chan %u: unknown gain value 0x%x\n", chan, + sel); + + return ret; + } + + *gain = ret; + + return 0; +} + +static int bu27034_get_int_time(struct bu27034_data *data) +{ + int ret, sel; + + ret = regmap_read(data->regmap, BU27034_REG_MODE_CONTROL1, &sel); + if (ret) + return ret; + + return iio_gts_find_int_time_by_sel(&data->gts, + sel & BU27034_MASK_MEAS_MODE); +} + +static int _bu27034_get_scale(struct bu27034_data *data, int channel, int *val, + int *val2) +{ + int gain, ret; + + ret = bu27034_get_gain(data, channel, &gain); + if (ret) + return ret; + + ret = bu27034_get_int_time(data); + if (ret < 0) + return ret; + + return iio_gts_get_scale(&data->gts, gain, ret, val, val2); +} + +static int bu27034_get_scale(struct bu27034_data *data, int channel, int *val, + int *val2) +{ + int ret; + + mutex_lock(&data->mutex); + ret = _bu27034_get_scale(data, channel, val, val2); + mutex_unlock(&data->mutex); + if (ret) + return ret; + + return IIO_VAL_INT_PLUS_NANO; +} + +/* Caller should hold the lock to protect lux reading */ +static int bu27034_write_gain_sel(struct bu27034_data *data, int chan, int sel) +{ + static const int reg[] = { + [BU27034_CHAN_DATA0] = BU27034_REG_MODE_CONTROL2, + [BU27034_CHAN_DATA1] = BU27034_REG_MODE_CONTROL3, + }; + int mask, val; + + if (chan != BU27034_CHAN_DATA0 && chan != BU27034_CHAN_DATA1) + return -EINVAL; + + val = sel << BU27034_SHIFT_D01_GAIN; + mask = BU27034_MASK_D01_GAIN; + + if (chan == BU27034_CHAN_DATA0) { + /* + * We keep the same gain for channel 2 as we set for channel 0 + * We can't allow them to be individually controlled because + * setting one will impact also the other. Also, if we don't + * always update both gains we may result unsupported bit + * combinations. + * + * This is not nice but this is yet another place where the + * user space must be prepared to surprizes. Namely, see chan 2 + * gain changed when chan 0 gain is changed. + * + * This is not fatal for most users though. I don't expect the + * channel 2 to be used in any generic cases - the intensity + * values provided by the sensor for IR area are not openly + * documented. Also, channel 2 is not used for visible light. + * + * So, if there is application which is written to utilize the + * channel 2 - then it is probably specifically targeted to this + * sensor and knows how to utilize those values. It is safe to + * hope such user can also cope with the gain changes. + */ + mask |= BU27034_MASK_D2_GAIN_LO; + + /* + * The D2 gain bits are directly the lowest bits of selector. + * Just do add those bits to the value + */ + val |= sel & BU27034_MASK_D2_GAIN_LO; + } + + return regmap_update_bits(data->regmap, reg[chan], mask, val); +} + +static int bu27034_set_gain(struct bu27034_data *data, int chan, int gain) +{ + int ret; + + /* + * We don't allow setting channel 2 gain as it messes up the + * gain for channel 0 - which shares the high bits + */ + if (chan != BU27034_CHAN_DATA0 && chan != BU27034_CHAN_DATA1) + return -EINVAL; + + ret = iio_gts_find_sel_by_gain(&data->gts, gain); + if (ret < 0) + return ret; + + return bu27034_write_gain_sel(data, chan, ret); +} + +/* Caller should hold the lock to protect data->int_time */ +static int bu27034_set_int_time(struct bu27034_data *data, int time) +{ + int ret; + + ret = iio_gts_find_sel_by_int_time(&data->gts, time); + if (ret < 0) + return ret; + + return regmap_update_bits(data->regmap, BU27034_REG_MODE_CONTROL1, + BU27034_MASK_MEAS_MODE, ret); +} + +/* + * We try to change the time in such way that the scale is maintained for + * given channels by adjusting gain so that it compensates the time change. + */ +static int bu27034_try_set_int_time(struct bu27034_data *data, int time_us) +{ + struct bu27034_gain_check gains[] = { + { .chan = BU27034_CHAN_DATA0 }, + { .chan = BU27034_CHAN_DATA1 }, + }; + int numg = ARRAY_SIZE(gains); + int ret, int_time_old, i; + + mutex_lock(&data->mutex); + ret = bu27034_get_int_time(data); + if (ret < 0) + goto unlock_out; + + int_time_old = ret; + + if (!iio_gts_valid_time(&data->gts, time_us)) { + dev_err(data->dev, "Unsupported integration time %u\n", + time_us); + ret = -EINVAL; + + goto unlock_out; + } + + if (time_us == int_time_old) { + ret = 0; + goto unlock_out; + } + + for (i = 0; i < numg; i++) { + ret = bu27034_get_gain(data, gains[i].chan, &gains[i].old_gain); + if (ret) + goto unlock_out; + + gains[i].new_gain = gains[i].old_gain * int_time_old / time_us; + + if (!iio_gts_valid_gain(&data->gts, gains[i].new_gain)) { + int scale1, scale2; + bool ok; + + _bu27034_get_scale(data, gains[i].chan, &scale1, &scale2); + dev_dbg(data->dev, + "chan %u, can't support time %u with scale %u %u\n", + gains[i].chan, time_us, scale1, scale2); + + /* + * If caller requests for integration time change and we + * can't support the scale - then the caller should be + * prepared to 'pick up the pieces and deal with the + * fact that the scale changed'. + */ + ret = iio_find_closest_gain_low(&data->gts, + gains[i].new_gain, &ok); + + if (!ok) { + dev_dbg(data->dev, + "optimal gain out of range for chan %u\n", + gains[i].chan); + } + if (ret < 0) { + dev_dbg(data->dev, + "Total gain increase. Risk of saturation"); + ret = iio_gts_get_min_gain(&data->gts); + if (ret < 0) + goto unlock_out; + } + dev_dbg(data->dev, "chan %u scale changed\n", + gains[i].chan); + gains[i].new_gain = ret; + dev_dbg(data->dev, "chan %u new gain %u\n", + gains[i].chan, gains[i].new_gain); + } + } + + for (i = 0; i < numg; i++) { + ret = bu27034_set_gain(data, gains[i].chan, gains[i].new_gain); + if (ret) + goto unlock_out; + } + + ret = bu27034_set_int_time(data, time_us); + +unlock_out: + mutex_unlock(&data->mutex); + + return ret; +} + +static int bu27034_set_scale(struct bu27034_data *data, int chan, + int val, int val2) +{ + int ret, time_sel, gain_sel, i; + bool found = false; + + if (chan == BU27034_CHAN_DATA2) + return -EINVAL; + + mutex_lock(&data->mutex); + ret = regmap_read(data->regmap, BU27034_REG_MODE_CONTROL1, &time_sel); + if (ret) + goto unlock_out; + + ret = iio_gts_find_gain_sel_for_scale_using_time(&data->gts, time_sel, + val, val2 * 1000, &gain_sel); + if (ret) { + /* + * Could not support scale with given time. Need to change time. + * We still want to maintain the scale for all channels + */ + struct bu27034_gain_check gain; + int new_time_sel; + + /* + * Populate information for the other channel which should also + * maintain the scale. (Due to the HW limitations the chan2 + * gets the same gain as chan0, so we only need to explicitly + * set the chan 0 and 1). + */ + if (chan == BU27034_CHAN_DATA0) + gain.chan = BU27034_CHAN_DATA1; + else if (chan == BU27034_CHAN_DATA1) + gain.chan = BU27034_CHAN_DATA0; + + ret = bu27034_get_gain(data, gain.chan, &gain.old_gain); + if (ret) + goto unlock_out; + + /* + * Iterate through all the times to see if we find one which + * can support requested scale for requested channel, while + * maintaining the scale for other channels + */ + for (i = 0; i < data->gts.num_itime; i++) { + new_time_sel = data->gts.itime_table[i].sel; + + if (new_time_sel == time_sel) + continue; + + /* Can we provide requested scale with this time? */ + ret = iio_gts_find_gain_sel_for_scale_using_time( + &data->gts, new_time_sel, val, val2 * 1000, + &gain_sel); + if (ret) + continue; + + /* Can the othe channel(s) maintain scale? */ + ret = iio_gts_find_new_gain_sel_by_old_gain_time( + &data->gts, gain.old_gain, time_sel, + new_time_sel, &gain.new_gain); + if (!ret) { + /* Yes - we found suitable time */ + found = true; + break; + } + } + if (!found) { + dev_dbg(data->dev, + "Can't set scale maintaining other channels\n"); + ret = -EINVAL; + + goto unlock_out; + } + + for (i = 0; i < 2; i++) { + ret = bu27034_set_gain(data, gain.chan, + gain.new_gain); + if (ret) + goto unlock_out; + } + + ret = regmap_update_bits(data->regmap, BU27034_REG_MODE_CONTROL1, + BU27034_MASK_MEAS_MODE, new_time_sel); + if (ret) + goto unlock_out; + } + + ret = bu27034_write_gain_sel(data, chan, gain_sel); +unlock_out: + mutex_unlock(&data->mutex); + + return ret; +} + +/* + * for (D1/D0 < 0.87): + * lx = 0.004521097 * D1 - 0.002663996 * D0 + + * 0.00012213 * D1 * D1 / D0 + * + * => 115.7400832 * ch1 / gain1 / mt - + * 68.1982976 * ch0 / gain0 / mt + + * 0.00012213 * 25600 * (ch1 / gain1 / mt) * 25600 * + * (ch1 /gain1 / mt) / (25600 * ch0 / gain0 / mt) + * + * A = 0.00012213 * 25600 * (ch1 /gain1 / mt) * 25600 * + * (ch1 /gain1 / mt) / (25600 * ch0 / gain0 / mt) + * => 0.00012213 * 25600 * (ch1 /gain1 / mt) * + * (ch1 /gain1 / mt) / (ch0 / gain0 / mt) + * => 0.00012213 * 25600 * (ch1 / gain1) * (ch1 /gain1 / mt) / + * (ch0 / gain0) + * => 0.00012213 * 25600 * (ch1 / gain1) * (ch1 /gain1 / mt) * + * gain0 / ch0 + * => 3.126528 * ch1 * ch1 * gain0 / gain1 / gain1 / mt /ch0 + * + * lx = (115.7400832 * ch1 / gain1 - 68.1982976 * ch0 / gain0) / + * mt + A + * => (115.7400832 * ch1 / gain1 - 68.1982976 * ch0 / gain0) / + * mt + 3.126528 * ch1 * ch1 * gain0 / gain1 / gain1 / mt / + * ch0 + * + * => (115.7400832 * ch1 / gain1 - 68.1982976 * ch0 / gain0 + + * 3.126528 * ch1 * ch1 * gain0 / gain1 / gain1 / ch0) / + * mt + * + * For (0.87 <= D1/D0 < 1.00) + * lx = (0.001331* D0 + 0.0000354 * D1) * ((D1/D0 – 0.87) * (0.385) + 1) + * => (0.001331 * 256 * 100 * ch0 / gain0 / mt + 0.0000354 * 256 * + * 100 * ch1 / gain1 / mt) * ((D1/D0 - 0.87) * (0.385) + 1) + * => (34.0736 * ch0 / gain0 / mt + 0.90624 * ch1 / gain1 / mt) * + * ((D1/D0 - 0.87) * (0.385) + 1) + * => (34.0736 * ch0 / gain0 / mt + 0.90624 * ch1 / gain1 / mt) * + * (0.385 * D1/D0 - 0.66505) + * => (34.0736 * ch0 / gain0 / mt + 0.90624 * ch1 / gain1 / mt) * + * (0.385 * 256 * 100 * ch1 / gain1 / mt / (256 * 100 * ch0 / gain0 / mt) - 0.66505) + * => (34.0736 * ch0 / gain0 / mt + 0.90624 * ch1 / gain1 / mt) * + * (9856 * ch1 / gain1 / mt / (25600 * ch0 / gain0 / mt) + 0.66505) + * => 13.118336 * ch1 / (gain1 * mt) + * + 22.66064768 * ch0 / (gain0 * mt) + * + 8931.90144 * ch1 * ch1 * gain0 / + * (25600 * ch0 * gain1 * gain1 * mt) + * + 0.602694912 * ch1 / (gain1 * mt) + * + * => [0.3489024 * ch1 * ch1 * gain0 / (ch0 * gain1 * gain1) + * + 22.66064768 * ch0 / gain0 + * + 13.721030912 * ch1 / gain1 + * ] / mt + * + * For (D1/D0 >= 1.00) + * + * lx = (0.001331* D0 + 0.0000354 * D1) * ((D1/D0 – 2.0) * (-0.05) + 1) + * => (0.001331* D0 + 0.0000354 * D1) * (-0.05D1/D0 + 1.1) + * => (0.001331 * 256 * 100 * ch0 / gain0 / mt + 0.0000354 * 256 * + * 100 * ch1 / gain1 / mt) * (-0.05D1/D0 + 1.1) + * => (34.0736 * ch0 / gain0 / mt + 0.90624 * ch1 / gain1 / mt) * + * (-0.05 * 256 * 100 * ch1 / gain1 / mt / (256 * 100 * ch0 / gain0 / mt) + 1.1) + * => (34.0736 * ch0 / gain0 / mt + 0.90624 * ch1 / gain1 / mt) * + * (-1280 * ch1 / (gain1 * mt * 25600 * ch0 / gain0 / mt) + 1.1) + * => (34.0736 * ch0 * -1280 * ch1 * gain0 * mt /( gain0 * mt * gain1 * mt * 25600 * ch0) + * + 34.0736 * 1.1 * ch0 / (gain0 * mt) + * + 0.90624 * ch1 * -1280 * ch1 *gain0 * mt / (gain1 * mt *gain1 * mt * 25600 * ch0) + * + 1.1 * 0.90624 * ch1 / (gain1 * mt) + * => -43614.208 * ch1 / (gain1 * mt * 25600) + * + 37.48096 ch0 / (gain0 * mt) + * - 1159.9872 * ch1 * ch1 * gain0 / (gain1 * gain1 * mt * 25600 * ch0) + * + 0.996864 ch1 / (gain1 * mt) + * => [ + * - 0.045312 * ch1 * ch1 * gain0 / (gain1 * gain1 * ch0) + * - 0.706816 * ch1 / gain1 + * + 37.48096 ch0 /gain0 + * ] * mt + * + * + * So, the first case (D1/D0 < 0.87) can be computed to a form: + * + * lx = (3.126528 * ch1 * ch1 * gain0 / (ch0 * gain1 * gain1) + + * 115.7400832 * ch1 / gain1 + + * -68.1982976 * ch0 / gain0 + * / mt + * + * Second case (0.87 <= D1/D0 < 1.00) goes to form: + * + * => [0.3489024 * ch1 * ch1 * gain0 / (ch0 * gain1 * gain1) + + * 13.721030912 * ch1 / gain1 + + * 22.66064768 * ch0 / gain0 + * ] / mt + * + * Third case (D1/D0 >= 1.00) goes to form: + * => [-0.045312 * ch1 * ch1 * gain0 / (ch0 * gain1 * gain1) + + * -0.706816 * ch1 / gain1 + + * 37.48096 ch0 /(gain0 + * ] / mt + * + * This can be unified to format: + * lx = [ + * A * ch1 * ch1 * gain0 / (ch0 * gain1 * gain1) + + * B * ch1 / gain1 + + * C * ch0 / gain0 + * ] / mt + * + * For case 1: + * A = 3.126528, + * B = 115.7400832 + * C = -68.1982976 + * + * For case 2: + * A = 0.3489024 + * B = 13.721030912 + * C = 22.66064768 + * + * For case 3: + * A = -0.045312 + * B = -0.706816 + * C = 37.48096 + */ + +struct bu27034_lx_coeff { + unsigned int A; + unsigned int B; + unsigned int C; + /* Indicate which of the coefficients above are negative */ + bool is_neg[3]; +}; + +static inline u64 gain_mul_div_helper(u64 val, unsigned int gain, + unsigned int div) +{ + /* + * Max gain for a channel is 4096. The max u64 (0xffffffffffffffffULL) + * divided by 4096 is 0xFFFFFFFFFFFFF (GENMASK_ULL(51, 0)) (floored). + * Thus, the 0xFFFFFFFFFFFFF is the largest value we can safely multiply + * with the gain, no matter what gain is set. + * + * So, multiplication with max gain may overflow if val is greater than + * 0xFFFFFFFFFFFFF (52 bits set).. + * + * If this is the case we divide first. + */ + if (val < GENMASK_ULL(51, 0)) { + val *= gain; + do_div(val, div); + } else { + do_div(val, div); + val *= gain; + } + + return val; +} + +static u64 bu27034_fixp_calc_t1(unsigned int coeff, unsigned int ch0, + unsigned int ch1, unsigned int gain0, + unsigned int gain1) +{ + unsigned int helper, tmp; + u64 helper64; + + /* + * Here we could overflow even the 64bit value. Hence we + * multiply with gain0 only after the divisions - even though + * it may result loss of accuracy + */ + helper64 = (u64)coeff * (u64)ch1 * (u64)ch1; + helper = coeff * ch1 * ch1; + tmp = helper * gain0; + + if (helper == helper64 && (tmp / gain0 == helper)) + return tmp / (gain1 * gain1) / ch0; + + helper = gain1 * gain1; + if (helper > ch0) { + do_div(helper64, helper); + + return gain_mul_div_helper(helper64, gain0, ch0); + } + + do_div(helper64, ch0); + + return gain_mul_div_helper(helper64, gain0, helper); +} + +static u64 bu27034_fixp_calc_t23(unsigned int coeff, unsigned int ch, + unsigned int gain) +{ + unsigned int helper; + u64 helper64; + + helper64 = (u64)coeff * (u64)ch; + helper = coeff * ch; + + if (helper == helper64) + return helper / gain; + + do_div(helper64, gain); + + return helper64; +} + +static int bu27034_fixp_calc_lx(unsigned int ch0, unsigned int ch1, + unsigned int gain0, unsigned int gain1, + unsigned int meastime, int coeff_idx) +{ + static const struct bu27034_lx_coeff coeff[] = { + { + .A = 31265280, /* 3.126528 */ + .B = 1157400832, /*115.7400832 */ + .C = 681982976, /* -68.1982976 */ + .is_neg = {false, false, true}, + }, { + .A = 3489024, /* 0.3489024 */ + .B = 137210309, /* 13.721030912 */ + .C = 226606476, /* 22.66064768 */ + /* All terms positive */ + }, { + .A = 453120, /* -0.045312 */ + .B = 7068160, /* -0.706816 */ + .C = 374809600, /* 37.48096 */ + .is_neg = {true, true, false}, + } + }; + const struct bu27034_lx_coeff *c = &coeff[coeff_idx]; + u64 res = 0, terms[3]; + int i; + + if (coeff_idx >= ARRAY_SIZE(coeff)) + return -EINVAL; + + terms[0] = bu27034_fixp_calc_t1(c->A, ch0, ch1, gain0, gain1); + terms[1] = bu27034_fixp_calc_t23(c->B, ch1, gain1); + terms[2] = bu27034_fixp_calc_t23(c->C, ch0, gain0); + + /* First, add positive terms */ + for (i = 0; i < 3; i++) + if (!c->is_neg[i]) + res += terms[i]; + + /* No positive term => zero lux */ + if (!res) + return 0; + + /* Then, subtract negative terms (if any) */ + for (i = 0; i < 3; i++) + if (c->is_neg[i]) { + /* + * If the negative term is greater than positive - then + * the darknes has taken over and we are all doomed! Eh, + * I mean, then we can just return 0 lx and go out + */ + if (terms[i] >= res) + return 0; + + res -= terms[i]; + } + + meastime *= 10000; + do_div(res, meastime); + + return (int) res; +} + +static bool bu27034_has_valid_sample(struct bu27034_data *data) +{ + int ret, val; + + ret = regmap_read(data->regmap, BU27034_REG_MODE_CONTROL4, &val); + if (ret) { + dev_err(data->dev, "Read failed %d\n", ret); + + return false; + } + + return val & BU27034_MASK_VALID; +} + +/* + * Reading the register where VALID bit is clears this bit. (So does changing + * any gain / integration time configuration registers) The bit gets + * set when we have acquired new data. We use this bit to indicate data + * validity. + */ +static void bu27034_invalidate_read_data(struct bu27034_data *data) +{ + bu27034_has_valid_sample(data); +} + +static int bu27034_read_result(struct bu27034_data *data, int chan, int *res) +{ + int reg[] = { + [BU27034_CHAN_DATA0] = BU27034_REG_DATA0_LO, + [BU27034_CHAN_DATA1] = BU27034_REG_DATA1_LO, + [BU27034_CHAN_DATA2] = BU27034_REG_DATA2_LO, + }; + int valid, ret; + __le16 val; + + ret = regmap_read_poll_timeout(data->regmap, BU27034_REG_MODE_CONTROL4, + valid, (valid & BU27034_MASK_VALID), + BU27034_DATA_WAIT_TIME_US, 0); + if (ret) + return ret; + + ret = regmap_bulk_read(data->regmap, reg[chan], &val, sizeof(val)); + if (ret) + return ret; + + *res = le16_to_cpu(val); + + return 0; +} + +static int bu27034_get_result_unlocked(struct bu27034_data *data, __le16 *res, + int size) +{ + int ret = 0, retry_cnt = 0; + +retry: + /* Get new value from sensor if data is ready */ + if (bu27034_has_valid_sample(data)) { + ret = regmap_bulk_read(data->regmap, BU27034_REG_DATA0_LO, + res, size); + if (ret) + return ret; + + bu27034_invalidate_read_data(data); + } else { + retry_cnt++; + + /* No new data in sensor. Wait and retry */ + msleep(25); + + if (retry_cnt > BU27034_RETRY_LIMIT) { + dev_err(data->dev, "No data from sensor\n"); + + return -ETIMEDOUT; + } + + goto retry; + } + + return ret; +} + +static int bu27034_meas_set(struct bu27034_data *data, bool en) +{ + if (en) + return regmap_set_bits(data->regmap, BU27034_REG_MODE_CONTROL4, + BU27034_MASK_MEAS_EN); + + return regmap_clear_bits(data->regmap, BU27034_REG_MODE_CONTROL4, + BU27034_MASK_MEAS_EN); +} + +static int bu27034_get_single_result(struct bu27034_data *data, int chan, + int *val) +{ + int ret; + + ret = bu27034_meas_set(data, true); + if (ret) + return ret; + + ret = bu27034_get_int_time(data); + if (ret < 0) + return ret; + + msleep(ret / 1000); + + return bu27034_read_result(data, chan, val); +} + +/* + * The formula given by vendor for computing luxes out of data0 and data1 + * (in open air) is as follows: + * + * Let's mark: + * D0 = data0/ch0_gain/meas_time_ms * 25600 + * D1 = data1/ch1_gain/meas_time_ms * 25600 + * + * Then: + * if (D1/D0 < 0.87) + * lx = (0.001331 * D0 + 0.0000354 * D1) * ((D1 / D0 - 0.87) * 3.45 + 1) + * else if (D1/D0 < 1) + * lx = (0.001331 * D0 + 0.0000354 * D1) * ((D1 / D0 - 0.87) * 0.385 + 1) + * else + * lx = (0.001331 * D0 + 0.0000354 * D1) * ((D1 / D0 - 2) * -0.05 + 1) + * + * We use it here. Users who have for example some colored lens + * need to modify the calculation but I hope this gives a starting point for + * those working with such devices. + */ + +static int bu27034_calc_lux(struct bu27034_data *data, __le16 *res, int *val) +{ + unsigned int gain0, gain1, meastime; + unsigned int d1_d0_ratio_scaled; + u16 ch0, ch1; + u64 helper64; + int ret; + + /* + * We return 0 luxes if calculation fails. This should be reasonably + * easy to spot from the buffers especially if raw-data channels show + * valid values + */ + *val = 0; + + ch0 = max_t(u16, 1, le16_to_cpu(res[0])); + ch1 = max_t(u16, 1, le16_to_cpu(res[1])); + + ret = bu27034_get_gain(data, BU27034_CHAN_DATA0, &gain0); + if (ret) + return ret; + + ret = bu27034_get_gain(data, BU27034_CHAN_DATA1, &gain1); + if (ret) + return ret; + + ret = bu27034_get_int_time(data); + if (ret < 0) + return ret; + + meastime = ret; + + d1_d0_ratio_scaled = (unsigned int)ch1 * (unsigned int)gain0 * 100; + helper64 = (u64)ch1 * (u64)gain0 * 100LLU; + + if (helper64 != d1_d0_ratio_scaled) { + unsigned int div = (unsigned int)ch0 * gain1; + + do_div(helper64, div); + d1_d0_ratio_scaled = helper64; + } else { + d1_d0_ratio_scaled /= ch0 * gain1; + } + + if (d1_d0_ratio_scaled < 87) + ret = bu27034_fixp_calc_lx(ch0, ch1, gain0, gain1, meastime, 0); + else if (d1_d0_ratio_scaled < 100) + ret = bu27034_fixp_calc_lx(ch0, ch1, gain0, gain1, meastime, 1); + else + ret = bu27034_fixp_calc_lx(ch0, ch1, gain0, gain1, meastime, 2); + + if (ret < 0) + return ret; + + *val = ret; + + return 0; + +} + +static int bu27034_get_lux(struct bu27034_data *data, int *val) +{ + __le16 res[3]; + int ret; + + ret = bu27034_meas_set(data, true); + if (ret) + return ret; + + ret = bu27034_get_result_unlocked(data, &res[0], sizeof(res)); + if (ret) + return ret; + + ret = bu27034_calc_lux(data, res, val); + if (ret) + return ret; + + ret = bu27034_meas_set(data, false); + if (ret) + dev_err(data->dev, "failed to disable measurement\n"); + + return 0; +} + +static int bu27034_read_raw(struct iio_dev *idev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct bu27034_data *data = iio_priv(idev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_INT_TIME: + *val = bu27034_get_int_time(data); + if (*val < 0) + return *val; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + return bu27034_get_scale(data, chan->channel, val, val2); + + case IIO_CHAN_INFO_RAW: + { + if (chan->type != IIO_INTENSITY) + return -EINVAL; + + if (chan->channel < BU27034_CHAN_DATA0 || + chan->channel > BU27034_CHAN_DATA2) + return -EINVAL; + + /* Don't mess with measurement enabling while buffering */ + ret = iio_device_claim_direct_mode(idev); + if (ret) + return ret; + + mutex_lock(&data->mutex); + /* + * Reading one channel at a time is ineffiecient but we don't + * care here. Buffered version should be used if performance is + * an issue. + */ + ret = bu27034_get_single_result(data, chan->channel, val); + + mutex_unlock(&data->mutex); + iio_device_release_direct_mode(idev); + + if (ret) + return ret; + + return IIO_VAL_INT; + } + + case IIO_CHAN_INFO_PROCESSED: + if (chan->type != IIO_LIGHT) + return -EINVAL; + + /* Don't mess with measurement enabling while buffering */ + ret = iio_device_claim_direct_mode(idev); + if (ret) + return ret; + + mutex_lock(&data->mutex); + + ret = bu27034_get_lux(data, val); + + mutex_unlock(&data->mutex); + iio_device_release_direct_mode(idev); + + if (ret) + return ret; + + return IIO_VAL_INT; + + default: + return -EINVAL; + + } +} + +static int bu27034_write_raw(struct iio_dev *idev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct bu27034_data *data = iio_priv(idev); + int ret; + + ret = iio_device_claim_direct_mode(idev); + if (ret) + return ret; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + ret = bu27034_set_scale(data, chan->channel, val, val2); + break; + case IIO_CHAN_INFO_INT_TIME: + ret = bu27034_try_set_int_time(data, val); + break; + default: + ret = -EINVAL; + break; + } + + iio_device_release_direct_mode(idev); + + return ret; +} + +static int bu27034_read_avail(struct iio_dev *idev, + struct iio_chan_spec const *chan, const int **vals, + int *type, int *length, long mask) +{ + struct bu27034_data *data = iio_priv(idev); + + switch (mask) { + case IIO_CHAN_INFO_INT_TIME: + return iio_gts_avail_times(&data->gts, vals, type, length); + case IIO_CHAN_INFO_SCALE: + return iio_gts_all_avail_scales(&data->gts, vals, type, length); + default: + return -EINVAL; + } +} + +static const struct iio_info bu27034_info = { + .read_raw = &bu27034_read_raw, + .write_raw = &bu27034_write_raw, + .read_avail = &bu27034_read_avail, +}; + +static int bu27034_chip_init(struct bu27034_data *data) +{ + int ret, sel; + + /* Reset */ + ret = regmap_update_bits(data->regmap, BU27034_REG_SYSTEM_CONTROL, + BU27034_MASK_SW_RESET, BU27034_MASK_SW_RESET); + if (ret) + return dev_err_probe(data->dev, ret, "Sensor reset failed\n"); + + msleep(1); + /* + * Read integration time here to ensure it is in regmap cache. We do + * this to speed-up the int-time acquisition in the start of the buffer + * handling thread where longer delays could make it more likely we end + * up skipping a sample, and where the longer delays make timestamps + * less accurate. + */ + ret = regmap_read(data->regmap, BU27034_REG_MODE_CONTROL1, &sel); + if (ret) + dev_err(data->dev, "reading integration time failed\n"); + + return 0; +} + +static int bu27034_wait_for_data(struct bu27034_data *data) +{ + int ret, val; + + ret = regmap_read_poll_timeout(data->regmap, BU27034_REG_MODE_CONTROL4, + val, val & BU27034_MASK_VALID, + BU27034_DATA_WAIT_TIME_US, + BU27034_TOTAL_DATA_WAIT_TIME_US); + if (ret) { + dev_err(data->dev, "data polling %s\n", + !(val & BU27034_MASK_VALID) ? "timeout" : "fail"); + + return ret; + } + + ret = regmap_bulk_read(data->regmap, BU27034_REG_DATA0_LO, + &data->scan.channels[0], + sizeof(data->scan.channels)); + if (ret) + return ret; + + bu27034_invalidate_read_data(data); + + return 0; +} + +static int bu27034_buffer_thread(void *arg) +{ + struct iio_dev *idev = arg; + struct bu27034_data *data; + int wait_ms; + + data = iio_priv(idev); + + wait_ms = bu27034_get_int_time(data); + wait_ms /= 1000; + + wait_ms -= BU27034_MEAS_WAIT_PREMATURE_MS; + + while (!kthread_should_stop()) { + int ret; + int64_t tstamp; + + msleep(wait_ms); + ret = bu27034_wait_for_data(data); + if (ret) + continue; + + tstamp = iio_get_time_ns(idev); + + if (test_bit(BU27034_CHAN_ALS, idev->active_scan_mask)) { + int lux; + + ret = bu27034_calc_lux(data, &data->scan.channels[0], + &lux); + if (ret) + dev_err(data->dev, "failed to calculate lux\n"); + + /* + * The maximum milli lux value we get with gain 1x time + * 55mS data ch0 = 0xffff ch1 = 0xffff fits in 26 bits + * so there should be no problem returning int from + * computations and casting it to u32 + */ + data->scan.lux = (u32)lux; + } + iio_push_to_buffers_with_timestamp(idev, &data->scan, tstamp); + } + + return 0; +} + +static int bu27034_buffer_enable(struct iio_dev *idev) +{ + struct bu27034_data *data = iio_priv(idev); + struct task_struct *task; + int ret; + + mutex_lock(&data->mutex); + ret = bu27034_meas_set(data, true); + if (ret) + goto unlock_out; + + task = kthread_run(bu27034_buffer_thread, idev, + "bu27034-buffering-%u", + iio_device_id(idev)); + if (IS_ERR(task)) { + ret = PTR_ERR(task); + goto unlock_out; + } + + data->task = task; + +unlock_out: + mutex_unlock(&data->mutex); + + return ret; +} + +static int bu27034_buffer_disable(struct iio_dev *idev) +{ + struct bu27034_data *data = iio_priv(idev); + int ret; + + mutex_lock(&data->mutex); + if (data->task) { + kthread_stop(data->task); + data->task = NULL; + } + + ret = bu27034_meas_set(data, false); + mutex_unlock(&data->mutex); + + return ret; +} + +static const struct iio_buffer_setup_ops bu27034_buffer_ops = { + .postenable = &bu27034_buffer_enable, + .predisable = &bu27034_buffer_disable, +}; + +static int bu27034_probe(struct i2c_client *i2c) +{ + struct device *dev = &i2c->dev; + struct bu27034_data *data; + struct regmap *regmap; + struct iio_dev *idev; + unsigned int part_id, reg; + int ret; + + regmap = devm_regmap_init_i2c(i2c, &bu27034_regmap); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), + "Failed to initialize Regmap\n"); + + idev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!idev) + return -ENOMEM; + + ret = devm_regulator_get_enable(dev, "vdd"); + if (ret) + return dev_err_probe(dev, ret, "Failed to get regulator\n"); + + data = iio_priv(idev); + + ret = regmap_read(regmap, BU27034_REG_SYSTEM_CONTROL, ®); + if (ret) + return dev_err_probe(dev, ret, "Failed to access sensor\n"); + + part_id = FIELD_GET(BU27034_MASK_PART_ID, reg); + + if (part_id != BU27034_ID) + dev_warn(dev, "unknown device 0x%x\n", part_id); + + ret = iio_init_iio_gts(BU27034_SCALE_1X, 0, bu27034_gains, + ARRAY_SIZE(bu27034_gains), bu27034_itimes, + ARRAY_SIZE(bu27034_itimes), &data->gts); + if (ret) + return ret; + + ret = devm_iio_gts_build_avail_tables(dev, &data->gts); + if (ret) + return ret; + + mutex_init(&data->mutex); + data->regmap = regmap; + data->dev = dev; + + idev->channels = bu27034_channels; + idev->num_channels = ARRAY_SIZE(bu27034_channels); + idev->name = "bu27034"; + idev->info = &bu27034_info; + + idev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; + idev->available_scan_masks = bu27034_scan_masks; + + ret = bu27034_chip_init(data); + if (ret) + return ret; + + ret = devm_iio_kfifo_buffer_setup(dev, idev, &bu27034_buffer_ops); + if (ret) + return dev_err_probe(dev, ret, "buffer setup failed\n"); + + ret = devm_iio_device_register(dev, idev); + if (ret < 0) + return dev_err_probe(dev, ret, + "Unable to register iio device\n"); + + return ret; +} + +static const struct of_device_id bu27034_of_match[] = { + { .compatible = "rohm,bu27034" }, + { } +}; +MODULE_DEVICE_TABLE(of, bu27034_of_match); + +static struct i2c_driver bu27034_i2c_driver = { + .driver = { + .name = "bu27034-als", + .of_match_table = bu27034_of_match, + }, + .probe_new = bu27034_probe, +}; +module_i2c_driver(bu27034_i2c_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Matti Vaittinen "); +MODULE_DESCRIPTION("ROHM BU27034 ambient light sensor driver"); +MODULE_IMPORT_NS(IIO_GTS_HELPER); From patchwork Fri Mar 17 14:45:00 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Matti Vaittinen X-Patchwork-Id: 71321 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a5d:604a:0:0:0:0:0 with SMTP id j10csp397902wrt; Fri, 17 Mar 2023 08:16:53 -0700 (PDT) X-Google-Smtp-Source: AK7set9iC2bkFdffSKQp4aOGL2MHRv38ZnnyWoM5YNQs/EcEhMaUdWD95M2MGZaT9zguhj6mp7RX X-Received: by 2002:a05:6a00:1a56:b0:625:5aa6:3b37 with SMTP id h22-20020a056a001a5600b006255aa63b37mr7024814pfv.3.1679066212974; Fri, 17 Mar 2023 08:16:52 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1679066212; cv=none; d=google.com; s=arc-20160816; b=VkyI7HHVvchp/h6VVywhH7q8YGfKFMVxsM2X8eT5By08F/BCtFZ1M/UShpLQSVLE2S 7JC/Wvw1I/S5mgm75Bd9Y+L1PBpLEH7/9KaCd6EozRIc1LolHxjHAIDxBOjjMhFLlqzn xmDB03G9xhfY3p7XFX4lI7xA8xm7PU711RM4NM1vgk7LLlEOzgzOq66qRkopl+hI6If1 CwfWrl3XH83taq2n5fcchbZn/vYFhj4W6jy+NpRMgwfRrH63tMlIk5MnUmVAs+xjwl6b K9JUaWma88q+PR7xsU4IVA8Ce0GfGLEoDKwFwuL9BFkX9m0HurKNGKiqwBL3ph7iWheP 0ZLw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:in-reply-to:content-disposition:mime-version :references:message-id:subject:cc:to:from:date:dkim-signature; bh=hGTIDqMndMNqbxALauKCkD2N7wb/KyvKcaFqWGLKpW8=; b=YyUpYziPeB1zZ9NnI12HSI0Ls62od6Qq5XxpXJi24/LKzL2RfSJ9xwoh7od6lAZm2a bMJisEGkCm8pDFCP2ACZXCK0iVuOzJw7tOjpN7zL2i5QO2IPGwAN1Ts8zn7+MVX8CGBf +s71ICH1x15c8VsDZCk6haYxy5RYy5hBO8GGnt5hBgk8zW3NAa1YVMTklRcb+YEDK12g iXOIZFOsn0lqJZNIpJEJwhOUYShwIPFPUnIUopgso9OsN95voqylFkkAP4ObZestRj/7 dg4DO+kMXSmwovj/1ayeudvoMj3e1Tbh1+EBBpE6gEwqv36kKGvH70n2+IzQKhEWDshW AsRw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gmail.com header.s=20210112 header.b=RYpgpmM6; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id d134-20020a621d8c000000b006257a8b0d69si2470272pfd.100.2023.03.17.08.16.39; Fri, 17 Mar 2023 08:16:52 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@gmail.com header.s=20210112 header.b=RYpgpmM6; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231264AbjCQOpb (ORCPT + 99 others); Fri, 17 Mar 2023 10:45:31 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47916 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231239AbjCQOp2 (ORCPT ); Fri, 17 Mar 2023 10:45:28 -0400 Received: from mail-lf1-x135.google.com (mail-lf1-x135.google.com [IPv6:2a00:1450:4864:20::135]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 6E5953019F for ; Fri, 17 Mar 2023 07:45:09 -0700 (PDT) Received: by mail-lf1-x135.google.com with SMTP id b10so5584464lfb.9 for ; Fri, 17 Mar 2023 07:45:09 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; t=1679064307; h=in-reply-to:content-disposition:mime-version:references:message-id :subject:cc:to:from:date:from:to:cc:subject:date:message-id:reply-to; bh=hGTIDqMndMNqbxALauKCkD2N7wb/KyvKcaFqWGLKpW8=; b=RYpgpmM6QAuOiJ/vpIPP6iIIZSZ7EBhol/R5PpAVAiq3qVDNzvccBReap+7h7xjaI7 KVFnEdrR1lICbHIOKXf6P0lD32XGoqwVtPaBrUpzrGRapz5JOxE3Jtbce7S2zqWRZjJ8 DDE6k8eS5plAw5JiI0E9FBjD6ioxZatquD3B5IAU5UDQcosBu2vDdbgSaOCn1MGJ8M45 J1euPdaq0GMUs8u+ooFI5RKTmMZrxFyF+TyOrG/BjGe2jn8J7KC6xCjumhhSNyeIcFVC R5Q4I78gmH475K6O4XU01Jm6Y/MzDB+QV4MJXOyi+qoCc4w5c5y9yOHgnRCnPHmE5kpQ 1KmQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1679064307; 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 :message-id:reply-to; bh=hGTIDqMndMNqbxALauKCkD2N7wb/KyvKcaFqWGLKpW8=; b=u6l7nLx4WfOAZ3hw+ofNwBZJYKhEakBJX6cidCcguzOQoHzDIQX+MfKRPm2Mfti2GB XHnOchDfQtT6FUZRPfXnRws+C9K83DN218LFSOzMuQ6Cej6tFNKPVehBki9Sqqh4zbIy zw6qoy+xi/TQLaehPgP4+riR/lFEOCJsJlH6MlahilB4YKXjjYnmf+6OrH+oTIb+H8lq H6i1YbqqG9GhoUTKI2n4scYNiMMdE9txfLSJ5B4WTyS820wg3XgC2eP/s2nMgspQehDX pEC6Cjn37W/jGJPiAnFkHTspyleZWWmuO4uZfNKU+OaccHJdjPyj0tVVq2NcEYM//C1A upxQ== X-Gm-Message-State: AO0yUKVJwPPCEd6/M4Y4Dpjq9msRqCQ5XTDHIl1DC2VJ3KJUgROm7Egs XrCwwrFMxs+O/yww1Obc9gIW9EIlHm4= X-Received: by 2002:ac2:52ad:0:b0:4e8:3f1f:3646 with SMTP id r13-20020ac252ad000000b004e83f1f3646mr4315195lfm.55.1679064307416; Fri, 17 Mar 2023 07:45:07 -0700 (PDT) Received: from fedora ([213.255.186.46]) by smtp.gmail.com with ESMTPSA id x25-20020ac25dd9000000b004d576298c89sm398427lfq.141.2023.03.17.07.45.06 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 17 Mar 2023 07:45:06 -0700 (PDT) Date: Fri, 17 Mar 2023 16:45:00 +0200 From: Matti Vaittinen To: Matti Vaittinen , Matti Vaittinen Cc: linux-kernel@vger.kernel.org Subject: [PATCH v4 8/8] MAINTAINERS: Add ROHM BU27034 Message-ID: References: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM, RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1760628533554764820?= X-GMAIL-MSGID: =?utf-8?q?1760628533554764820?= Add myself as a maintainer for ROHM BU27034 ALS driver. Signed-off-by: Matti Vaittinen --- Changes v2 => - No changes sRFCv1 => v2: - Add iio-list --- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 6ec9326f4ce9..3f13466e50fd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18100,6 +18100,12 @@ S: Maintained F: Documentation/devicetree/bindings/iio/light/bh1750.yaml F: drivers/iio/light/bh1750.c +ROHM BU27034 AMBIENT LIGHT SENSOR DRIVER +M: Matti Vaittinen +L: linux-iio@vger.kernel.org +S: Supported +F: drivers/iio/light/rohm-bu27034.c + ROHM MULTIFUNCTION BD9571MWV-M PMIC DEVICE DRIVERS M: Marek Vasut L: linux-kernel@vger.kernel.org