Received: by 2002:a25:ab43:0:0:0:0:0 with SMTP id u61csp5410983ybi; Wed, 12 Jun 2019 02:00:09 -0700 (PDT) X-Google-Smtp-Source: APXvYqz0o+0hnWaz9K27XLCk1OGmwzroHImgwfmhm3TGjolcuIMY7839rAY6CV55xBm92948mVzp X-Received: by 2002:a17:902:b102:: with SMTP id q2mr71012331plr.149.1560330009128; Wed, 12 Jun 2019 02:00:09 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1560330009; cv=none; d=google.com; s=arc-20160816; b=i8i3dlitx0JwjjbmKgEih2ahgtk+OmWgme+eLxjM+gvT4GOfGy8QGip9hH2vcS1D9/ IUNdXOUAZMmTib2m2Cv/qjm90v5EOTzJtitFoC5sLrveXWtVcN/s3coSt6F82l5VhmJl ANxqfllOkMPSa0YjHCHlyu97Dw95xMfvrBX1yAjjfAh8GvwCpbbH+O6WymXOtIT4XA+r e0tst7x5gvcOTZHoticzc+oLp2cFD7/4BbEFRClpY/JTOtM790EKlNXsrM2to9WMBVOB 3jyoT0mF0EmYkNK0D7k1Z/Zla5L/YtGjTBnbi7NurxT072RL9looW8WW3hKn6681hDsq qUIw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:references :in-reply-to:message-id:date:subject:cc:to:from; bh=1+iI1rHQbKwZm75PLVQkGOFDIa5UtCFZNQxQAqast0g=; b=IWGiOURX1CHyCWffBjd1hf5T6bcAtjqbkJfopz2VbpDA65YXnLJeCHNX+CpvpLCkWf 3sZYFVUkFc2yyBthLzH5qaJOYj3xDQxK6TvrUNFk7M90Om/9LII1dIO0HjMcCEAwr3Gc 2ky+d9qtsoAM7tXhtHvHv64+ppcHQA0ybSMKzidDIA3u9fjdXNbYugqu3uORrJEg86m8 oFwjcwMzXvOde+cUtOmxiifFew3Fi5NbN5IBRanESx0A47sCcFCt+MX++sPn97qyFo3H DmULZz1f2e2KcuX9KgPRkxDjeWJR1FfCtuDHkjWSGysnkIeeJIZs1TSOym3gbtVo6ZHL LoYw== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id x204si5115581pgx.354.2019.06.12.01.59.54; Wed, 12 Jun 2019 02:00:09 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731823AbfFLIpl (ORCPT + 99 others); Wed, 12 Jun 2019 04:45:41 -0400 Received: from mail.steuer-voss.de ([85.183.69.95]:43568 "EHLO mail.steuer-voss.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726731AbfFLIpi (ORCPT ); Wed, 12 Jun 2019 04:45:38 -0400 Received: from pc-niv.weinmann.com (localhost [127.0.0.1]) by mail.steuer-voss.de (Postfix) with ESMTP id 5473B4D07F; Wed, 12 Jun 2019 10:36:38 +0200 (CEST) From: Nikolaus Voss To: "Rafael J. Wysocki" , Len Brown , Robert Moore , Erik Schmauss , Jacek Anaszewski , Pavel Machek , Dan Murphy , Thierry Reding Cc: Nikolaus Voss , linux-acpi@vger.kernel.org, devel@acpica.org, linux-leds@vger.kernel.org, linux-pwm@vger.kernel.org, linux-kernel@vger.kernel.org, nv@vosn.de Subject: [PATCH v2 2/3] PWM framework: add support referencing PWMs from ACPI Date: Wed, 12 Jun 2019 10:36:07 +0200 Message-Id: <68c51a7af257b5c6d4f7d1b95b181b920132a64d.1560327219.git.nikolaus.voss@loewensteinmedical.de> X-Mailer: git-send-email 2.17.1 In-Reply-To: References: In-Reply-To: References: Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org In analogy to referencing a GPIO using the "gpios" property from ACPI, support referencing a PWM using the "pwms" property. ACPI entries must look like Package () {"pwms", Package () { , , [, ]}} In contrast to the DT implementation, only _one_ PWM entry in the "pwms" property is supported. As a consequence "pwm-names"-property and con_id lookup aren't supported. Support for ACPI is added via the firmware-node framework which is an abstraction layer on top of ACPI/DT. To keep this patch clean, DT and ACPI paths are kept separate. The firmware-node framework could be used to unify both paths in a future patch. To support leds-pwm driver, an additional method devm_fwnode_pwm_get() which supports both ACPI and DT configuration is exported. Signed-off-by: Nikolaus Voss --- drivers/pwm/core.c | 113 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/pwm.h | 9 ++++ 2 files changed, 122 insertions(+) diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 275b5f399a1a..5fed419d0833 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -6,6 +6,7 @@ * Copyright (C) 2011-2012 Avionic Design GmbH */ +#include #include #include #include @@ -700,6 +701,76 @@ struct pwm_device *of_pwm_get(struct device_node *np, const char *con_id) } EXPORT_SYMBOL_GPL(of_pwm_get); +static struct pwm_chip *device_to_pwmchip(struct device *dev) +{ + struct pwm_chip *chip; + + mutex_lock(&pwm_lock); + + list_for_each_entry(chip, &pwm_chips, list) { + struct acpi_device *adev = ACPI_COMPANION(chip->dev); + + if ((chip->dev == dev) || (adev && &adev->dev == dev)) { + mutex_unlock(&pwm_lock); + return chip; + } + } + + mutex_unlock(&pwm_lock); + + return ERR_PTR(-EPROBE_DEFER); +} + +/** + * acpi_pwm_get() - request a PWM via parsing "pwms" property in ACPI + * @fwnode: firmware node to get the "pwm" property from + * + * Returns the PWM device parsed from the fwnode and index specified in the + * "pwms" property or a negative error-code on failure. + * Values parsed from the device tree are stored in the returned PWM device + * object. + * + * This is analogous to of_pwm_get() except con_id is not yet supported. + * ACPI entries must look like + * Package () {"pwms", Package () + * { , , [, ]}} + * + * Returns: A pointer to the requested PWM device or an ERR_PTR()-encoded + * error code on failure. + */ +static struct pwm_device *acpi_pwm_get(struct fwnode_handle *fwnode) +{ + struct fwnode_reference_args args; + struct pwm_chip *chip; + struct pwm_device *pwm = ERR_PTR(-ENODEV); + int ret; + + memset(&args, 0, sizeof(args)); + ret = __acpi_node_get_property_reference(fwnode, "pwms", 0, 3, &args); + + if (!to_acpi_device_node(args.fwnode)) + return ERR_PTR(-EINVAL); + + if (args.nargs < 2) + return ERR_PTR(-EPROTO); + + chip = device_to_pwmchip(&to_acpi_device_node(args.fwnode)->dev); + if (IS_ERR(chip)) + return ERR_CAST(chip); + + pwm = pwm_request_from_chip(chip, args.args[0], NULL); + if (IS_ERR(pwm)) + return pwm; + + pwm->args.period = args.args[1]; + pwm->args.polarity = PWM_POLARITY_NORMAL; + + if (args.nargs > 2 && args.args[2] & PWM_POLARITY_INVERTED) + pwm->args.polarity = PWM_POLARITY_INVERSED; + + return pwm; +} + /** * pwm_add_table() - register PWM device consumers * @table: array of consumers to register @@ -763,6 +834,10 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id) if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node) return of_pwm_get(dev->of_node, con_id); + /* then lookup via ACPI */ + if (dev && is_acpi_node(dev->fwnode)) + return acpi_pwm_get(dev->fwnode); + /* * We look up the provider in the static table typically provided by * board setup code. We first try to lookup the consumer device by @@ -942,6 +1017,44 @@ struct pwm_device *devm_of_pwm_get(struct device *dev, struct device_node *np, } EXPORT_SYMBOL_GPL(devm_of_pwm_get); +/** + * devm_fwnode_pwm_get() - request a resource managed PWM from firmware node + * @dev: device for PWM consumer + * @fwnode: firmware node to get the PWM from + * @con_id: consumer name + * + * Returns the PWM device parsed from the firmware node. See of_pwm_get() and + * acpi_pwm_get() for a detailed description. + * + * Returns: A pointer to the requested PWM device or an ERR_PTR()-encoded + * error code on failure. + */ +struct pwm_device *devm_fwnode_pwm_get(struct device *dev, + struct fwnode_handle *fwnode, + const char *con_id) +{ + struct pwm_device **ptr, *pwm = ERR_PTR(-ENODEV); + + ptr = devres_alloc(devm_pwm_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + if (is_of_node(fwnode)) + pwm = of_pwm_get(to_of_node(fwnode), con_id); + else if (is_acpi_node(fwnode)) + pwm = acpi_pwm_get(fwnode); + + if (!IS_ERR(pwm)) { + *ptr = pwm; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return pwm; +} +EXPORT_SYMBOL_GPL(devm_fwnode_pwm_get); + static int devm_pwm_match(struct device *dev, void *res, void *data) { struct pwm_device **p = res; diff --git a/include/linux/pwm.h b/include/linux/pwm.h index eaa5c6e3fc9f..1a635916cdfb 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -411,6 +411,9 @@ void pwm_put(struct pwm_device *pwm); struct pwm_device *devm_pwm_get(struct device *dev, const char *con_id); struct pwm_device *devm_of_pwm_get(struct device *dev, struct device_node *np, const char *con_id); +struct pwm_device *devm_fwnode_pwm_get(struct device *dev, + struct fwnode_handle *fwnode, + const char *con_id); void devm_pwm_put(struct device *dev, struct pwm_device *pwm); #else static inline struct pwm_device *pwm_request(int pwm_id, const char *label) @@ -516,6 +519,12 @@ static inline struct pwm_device *devm_of_pwm_get(struct device *dev, return ERR_PTR(-ENODEV); } +static inline struct pwm_device *devm_fwnode_pwm_get( + struct device *dev, struct fwnode_handle *fwnode, const char *con_id) +{ + return ERR_PTR(-ENODEV); +} + static inline void devm_pwm_put(struct device *dev, struct pwm_device *pwm) { } -- 2.17.1