diff options
50 files changed, 710 insertions, 642 deletions
diff --git a/Documentation/devicetree/bindings/pwm/pwm-tiecap.txt b/Documentation/devicetree/bindings/pwm/pwm-tiecap.txt deleted file mode 100644 index c7c4347a769a..000000000000 --- a/Documentation/devicetree/bindings/pwm/pwm-tiecap.txt +++ /dev/null @@ -1,51 +0,0 @@ -TI SOC ECAP based APWM controller - -Required properties: -- compatible: Must be "ti,<soc>-ecap". - for am33xx - compatible = "ti,am3352-ecap", "ti,am33xx-ecap"; - for am4372 - compatible = "ti,am4372-ecap", "ti,am3352-ecap", "ti,am33xx-ecap"; - for da850 - compatible = "ti,da850-ecap", "ti,am3352-ecap", "ti,am33xx-ecap"; - for dra746 - compatible = "ti,dra746-ecap", "ti,am3352-ecap"; - for 66ak2g - compatible = "ti,k2g-ecap", "ti,am3352-ecap"; - for am654 - compatible = "ti,am654-ecap", "ti,am3352-ecap"; -- #pwm-cells: should be 3. See pwm.yaml in this directory for a description of - the cells format. The PWM channel index ranges from 0 to 4. The only third - cell flag supported by this binding is PWM_POLARITY_INVERTED. -- reg: physical base address and size of the registers map. - -Optional properties: -- clocks: Handle to the ECAP's functional clock. -- clock-names: Must be set to "fck". - -Example: - -ecap0: ecap@48300100 { /* ECAP on am33xx */ - compatible = "ti,am3352-ecap", "ti,am33xx-ecap"; - #pwm-cells = <3>; - reg = <0x48300100 0x80>; - clocks = <&l4ls_gclk>; - clock-names = "fck"; -}; - -ecap0: ecap@48300100 { /* ECAP on am4372 */ - compatible = "ti,am4372-ecap", "ti,am3352-ecap", "ti,am33xx-ecap"; - #pwm-cells = <3>; - reg = <0x48300100 0x80>; - ti,hwmods = "ecap0"; - clocks = <&l4ls_gclk>; - clock-names = "fck"; -}; - -ecap0: ecap@1f06000 { /* ECAP on da850 */ - compatible = "ti,da850-ecap", "ti,am3352-ecap", "ti,am33xx-ecap"; - #pwm-cells = <3>; - reg = <0x1f06000 0x80>; -}; - -ecap0: ecap@4843e100 { - compatible = "ti,dra746-ecap", "ti,am3352-ecap"; - #pwm-cells = <3>; - reg = <0x4843e100 0x80>; - clocks = <&l4_root_clk_div>; - clock-names = "fck"; -}; diff --git a/Documentation/devicetree/bindings/pwm/pwm-tiecap.yaml b/Documentation/devicetree/bindings/pwm/pwm-tiecap.yaml new file mode 100644 index 000000000000..ed35b6cc48d5 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/pwm-tiecap.yaml @@ -0,0 +1,64 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pwm/pwm-tiecap.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TI SOC ECAP based APWM controller + +maintainers: + - Vignesh R <vigneshr@ti.com> + +allOf: + - $ref: pwm.yaml# + +properties: + compatible: + oneOf: + - const: ti,am3352-ecap + - items: + - enum: + - ti,da850-ecap + - ti,am4372-ecap + - ti,dra746-ecap + - ti,k2g-ecap + - ti,am654-ecap + - ti,am64-ecap + - const: ti,am3352-ecap + + reg: + maxItems: 1 + + "#pwm-cells": + const: 3 + description: | + See pwm.yaml in this directory for a description of the cells format. + The only third cell flag supported by this binding is PWM_POLARITY_INVERTED. + + clock-names: + const: fck + + clocks: + maxItems: 1 + + power-domains: + maxItems: 1 + +required: + - compatible + - reg + - "#pwm-cells" + - clocks + - clock-names + +additionalProperties: false + +examples: + - | + ecap0: pwm@48300100 { /* ECAP on am33xx */ + compatible = "ti,am3352-ecap"; + #pwm-cells = <3>; + reg = <0x48300100 0x80>; + clocks = <&l4ls_gclk>; + clock-names = "fck"; + }; diff --git a/Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.txt b/Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.txt deleted file mode 100644 index c7e28f6d28be..000000000000 --- a/Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.txt +++ /dev/null @@ -1,50 +0,0 @@ -TI SOC EHRPWM based PWM controller - -Required properties: -- compatible: Must be "ti,<soc>-ehrpwm". - for am33xx - compatible = "ti,am3352-ehrpwm", "ti,am33xx-ehrpwm"; - for am4372 - compatible = "ti,am4372-ehrpwm", "ti-am3352-ehrpwm", "ti,am33xx-ehrpwm"; - for am654 - compatible = "ti,am654-ehrpwm", "ti-am3352-ehrpwm"; - for da850 - compatible = "ti,da850-ehrpwm", "ti-am3352-ehrpwm", "ti,am33xx-ehrpwm"; - for dra746 - compatible = "ti,dra746-ehrpwm", "ti-am3352-ehrpwm"; -- #pwm-cells: should be 3. See pwm.yaml in this directory for a description of - the cells format. The only third cell flag supported by this binding is - PWM_POLARITY_INVERTED. -- reg: physical base address and size of the registers map. - -Optional properties: -- clocks: Handle to the PWM's time-base and functional clock. -- clock-names: Must be set to "tbclk" and "fck". - -Example: - -ehrpwm0: pwm@48300200 { /* EHRPWM on am33xx */ - compatible = "ti,am3352-ehrpwm", "ti,am33xx-ehrpwm"; - #pwm-cells = <3>; - reg = <0x48300200 0x100>; - clocks = <&ehrpwm0_tbclk>, <&l4ls_gclk>; - clock-names = "tbclk", "fck"; -}; - -ehrpwm0: pwm@48300200 { /* EHRPWM on am4372 */ - compatible = "ti,am4372-ehrpwm", "ti,am3352-ehrpwm", "ti,am33xx-ehrpwm"; - #pwm-cells = <3>; - reg = <0x48300200 0x80>; - clocks = <&ehrpwm0_tbclk>, <&l4ls_gclk>; - clock-names = "tbclk", "fck"; - ti,hwmods = "ehrpwm0"; -}; - -ehrpwm0: pwm@1f00000 { /* EHRPWM on da850 */ - compatible = "ti,da850-ehrpwm", "ti,am3352-ehrpwm", "ti,am33xx-ehrpwm"; - #pwm-cells = <3>; - reg = <0x1f00000 0x2000>; -}; - -ehrpwm0: pwm@4843e200 { /* EHRPWM on dra746 */ - compatible = "ti,dra746-ehrpwm", "ti,am3352-ehrpwm"; - #pwm-cells = <3>; - reg = <0x4843e200 0x80>; - clocks = <&ehrpwm0_tbclk>, <&l4_root_clk_div>; - clock-names = "tbclk", "fck"; -}; diff --git a/Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.yaml b/Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.yaml new file mode 100644 index 000000000000..ee312cb210e6 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.yaml @@ -0,0 +1,65 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pwm/pwm-tiehrpwm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TI SOC EHRPWM based PWM controller + +maintainers: + - Vignesh R <vigneshr@ti.com> + +allOf: + - $ref: pwm.yaml# + +properties: + compatible: + oneOf: + - const: ti,am3352-ehrpwm + - items: + - enum: + - ti,da850-ehrpwm + - ti,am4372-ehrpwm + - ti,dra746-ehrpwm + - ti,am654-ehrpwm + - ti,am64-epwm + - const: ti,am3352-ehrpwm + + reg: + maxItems: 1 + + "#pwm-cells": + const: 3 + description: | + See pwm.yaml in this directory for a description of the cells format. + The only third cell flag supported by this binding is PWM_POLARITY_INVERTED. + + clock-names: + items: + - const: tbclk + - const: fck + + clocks: + maxItems: 2 + + power-domains: + maxItems: 1 + +required: + - compatible + - reg + - "#pwm-cells" + - clocks + - clock-names + +additionalProperties: false + +examples: + - | + ehrpwm0: pwm@48300200 { /* EHRPWM on am33xx */ + compatible = "ti,am3352-ehrpwm"; + #pwm-cells = <3>; + reg = <0x48300200 0x100>; + clocks = <&ehrpwm0_tbclk>, <&l4ls_gclk>; + clock-names = "tbclk", "fck"; + }; diff --git a/Documentation/driver-api/driver-model/devres.rst b/Documentation/driver-api/driver-model/devres.rst index 0fe1fffa295e..650096523f4f 100644 --- a/Documentation/driver-api/driver-model/devres.rst +++ b/Documentation/driver-api/driver-model/devres.rst @@ -400,7 +400,8 @@ POWER PWM devm_pwm_get() - devm_pwm_put() + devm_of_pwm_get() + devm_fwnode_pwm_get() REGULATOR devm_regulator_bulk_get() diff --git a/Documentation/driver-api/pwm.rst b/Documentation/driver-api/pwm.rst index a7ca4f58305a..ccb06e485756 100644 --- a/Documentation/driver-api/pwm.rst +++ b/Documentation/driver-api/pwm.rst @@ -40,7 +40,8 @@ after usage with pwm_free(). New users should use the pwm_get() function and pass to it the consumer device or a consumer name. pwm_put() is used to free the PWM device. Managed -variants of these functions, devm_pwm_get() and devm_pwm_put(), also exist. +variants of the getter, devm_pwm_get(), devm_of_pwm_get(), +devm_fwnode_pwm_get(), also exist. After being requested, a PWM has to be configured using:: @@ -48,6 +49,10 @@ After being requested, a PWM has to be configured using:: This API controls both the PWM period/duty_cycle config and the enable/disable state. +There is also a usage_power setting: If set, the PWM driver is only required to +maintain the power output but has more freedom regarding signal form. +If supported by the driver, the signal can be optimized, for example to improve +EMI by phase shifting the individual channels of a chip. The pwm_config(), pwm_enable() and pwm_disable() functions are just wrappers around pwm_apply_state() and should not be used if the user wants to change diff --git a/Documentation/firmware-guide/acpi/enumeration.rst b/Documentation/firmware-guide/acpi/enumeration.rst index 18074eb71860..74b830b2fd59 100644 --- a/Documentation/firmware-guide/acpi/enumeration.rst +++ b/Documentation/firmware-guide/acpi/enumeration.rst @@ -258,6 +258,38 @@ input driver:: .id_table = mpu3050_ids, }; +Reference to PWM device +======================= + +Sometimes a device can be a consumer of PWM channel. Obviously OS would like +to know which one. To provide this mapping the special property has been +introduced, i.e.:: + + Device (DEV) + { + Name (_DSD, Package () + { + ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), + Package () { + Package () { "compatible", Package () { "pwm-leds" } }, + Package () { "label", "alarm-led" }, + Package () { "pwms", + Package () { + "\\_SB.PCI0.PWM", // <PWM device reference> + 0, // <PWM index> + 600000000, // <PWM period> + 0, // <PWM flags> + } + } + } + + }) + ... + +In the above example the PWM-based LED driver references to the PWM channel 0 +of \_SB.PCI0.PWM device with initial period setting equal to 600 ms (note that +value is given in nanoseconds). + GPIO support ============ diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index c4d5c0667137..35e894f4a379 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -126,8 +126,7 @@ of_pwm_xlate_with_flags(struct pwm_chip *pc, const struct of_phandle_args *args) { struct pwm_device *pwm; - /* check, whether the driver supports a third cell for flags */ - if (pc->of_pwm_n_cells < 3) + if (pc->of_pwm_n_cells < 2) return ERR_PTR(-EINVAL); /* flags in the third cell are optional */ @@ -144,46 +143,29 @@ of_pwm_xlate_with_flags(struct pwm_chip *pc, const struct of_phandle_args *args) pwm->args.period = args->args[1]; pwm->args.polarity = PWM_POLARITY_NORMAL; - if (args->args_count > 2 && args->args[2] & PWM_POLARITY_INVERTED) - pwm->args.polarity = PWM_POLARITY_INVERSED; + if (pc->of_pwm_n_cells >= 3) { + if (args->args_count > 2 && args->args[2] & PWM_POLARITY_INVERTED) + pwm->args.polarity = PWM_POLARITY_INVERSED; + } return pwm; } EXPORT_SYMBOL_GPL(of_pwm_xlate_with_flags); -static struct pwm_device * -of_pwm_simple_xlate(struct pwm_chip *pc, const struct of_phandle_args *args) -{ - struct pwm_device *pwm; - - /* sanity check driver support */ - if (pc->of_pwm_n_cells < 2) - return ERR_PTR(-EINVAL); - - /* all cells are required */ - if (args->args_count != pc->of_pwm_n_cells) - return ERR_PTR(-EINVAL); - - if (args->args[0] >= pc->npwm) - return ERR_PTR(-EINVAL); - - pwm = pwm_request_from_chip(pc, args->args[0], NULL); - if (IS_ERR(pwm)) - return pwm; - - pwm->args.period = args->args[1]; - - return pwm; -} - static void of_pwmchip_add(struct pwm_chip *chip) { if (!chip->dev || !chip->dev->of_node) return; if (!chip->of_xlate) { - chip->of_xlate = of_pwm_simple_xlate; - chip->of_pwm_n_cells = 2; + u32 pwm_cells; + + if (of_property_read_u32(chip->dev->of_node, "#pwm-cells", + &pwm_cells)) + pwm_cells = 2; + + chip->of_xlate = of_pwm_xlate_with_flags; + chip->of_pwm_n_cells = pwm_cells; } of_node_get(chip->dev->of_node); @@ -324,22 +306,10 @@ EXPORT_SYMBOL_GPL(pwmchip_add); */ int pwmchip_remove(struct pwm_chip *chip) { - unsigned int i; - int ret = 0; - pwmchip_sysfs_unexport(chip); mutex_lock(&pwm_lock); - for (i = 0; i < chip->npwm; i++) { - struct pwm_device *pwm = &chip->pwms[i]; - - if (test_bit(PWMF_REQUESTED, &pwm->flags)) { - ret = -EBUSY; - goto out; - } - } - list_del_init(&chip->list); if (IS_ENABLED(CONFIG_OF)) @@ -347,12 +317,31 @@ int pwmchip_remove(struct pwm_chip *chip) free_pwms(chip); -out: mutex_unlock(&pwm_lock); - return ret; + + return 0; } EXPORT_SYMBOL_GPL(pwmchip_remove); +static void devm_pwmchip_remove(void *data) +{ + struct pwm_chip *chip = data; + + pwmchip_remove(chip); +} + +int devm_pwmchip_add(struct device *dev, struct pwm_chip *chip) +{ + int ret; + + ret = pwmchip_add(chip); + if (ret) + return ret; + + return devm_add_action_or_reset(dev, devm_pwmchip_remove, chip); +} +EXPORT_SYMBOL_GPL(devm_pwmchip_add); + /** * pwm_request() - request a PWM device * @pwm: global PWM device index @@ -554,7 +543,8 @@ int pwm_apply_state(struct pwm_device *pwm, const struct pwm_state *state) if (state->period == pwm->state.period && state->duty_cycle == pwm->state.duty_cycle && state->polarity == pwm->state.polarity && - state->enabled == pwm->state.enabled) + state->enabled == pwm->state.enabled && + state->usage_power == pwm->state.usage_power) return 0; if (chip->ops->apply) { @@ -709,14 +699,14 @@ int pwm_adjust_config(struct pwm_device *pwm) } EXPORT_SYMBOL_GPL(pwm_adjust_config); -static struct pwm_chip *of_node_to_pwmchip(struct device_node *np) +static struct pwm_chip *fwnode_to_pwmchip(struct fwnode_handle *fwnode) { struct pwm_chip *chip; mutex_lock(&pwm_lock); list_for_each_entry(chip, &pwm_chips, list) - if (chip->dev && chip->dev->of_node == np) { + if (chip->dev && dev_fwnode(chip->dev) == fwnode) { mutex_unlock(&pwm_lock); return chip; } @@ -795,7 +785,7 @@ struct pwm_device *of_pwm_get(struct device *dev, struct device_node *np, return ERR_PTR(err); } - pc = of_node_to_pwmchip(args.np); + pc = fwnode_to_pwmchip(of_fwnode_handle(args.np)); if (IS_ERR(pc)) { if (PTR_ERR(pc) != -EPROBE_DEFER) pr_err("%s(): PWM chip not found\n", __func__); @@ -837,31 +827,9 @@ put: } EXPORT_SYMBOL_GPL(of_pwm_get); -#if IS_ENABLED(CONFIG_ACPI) -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); -} -#endif - /** * acpi_pwm_get() - request a PWM via parsing "pwms" property in ACPI - * @fwnode: firmware node to get the "pwm" property from + * @fwnode: firmware node to get the "pwms" property from * * Returns the PWM device parsed from the fwnode and index specified in the * "pwms" property or a negative error-code on failure. @@ -876,12 +844,10 @@ static struct pwm_chip *device_to_pwmchip(struct device *dev) * 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) +static struct pwm_device *acpi_pwm_get(const struct fwnode_handle *fwnode) { - struct pwm_device *pwm = ERR_PTR(-ENODEV); -#if IS_ENABLED(CONFIG_ACPI) + struct pwm_device *pwm; struct fwnode_reference_args args; - struct acpi_device *acpi; struct pwm_chip *chip; int ret; @@ -891,14 +857,10 @@ static struct pwm_device *acpi_pwm_get(struct fwnode_handle *fwnode) if (ret < 0) return ERR_PTR(ret); - acpi = to_acpi_device_node(args.fwnode); - if (!acpi) - return ERR_PTR(-EINVAL); - if (args.nargs < 2) return ERR_PTR(-EPROTO); - chip = device_to_pwmchip(&acpi->dev); + chip = fwnode_to_pwmchip(args.fwnode); if (IS_ERR(chip)) return ERR_CAST(chip); @@ -911,7 +873,6 @@ static struct pwm_device *acpi_pwm_get(struct fwnode_handle *fwnode) if (args.nargs > 2 && args.args[2] & PWM_POLARITY_INVERTED) pwm->args.polarity = PWM_POLARITY_INVERSED; -#endif return pwm; } @@ -967,6 +928,7 @@ void pwm_remove_table(struct pwm_lookup *table, size_t num) */ struct pwm_device *pwm_get(struct device *dev, const char *con_id) { + const struct fwnode_handle *fwnode = dev ? dev_fwnode(dev) : NULL; const char *dev_id = dev ? dev_name(dev) : NULL; struct pwm_device *pwm; struct pwm_chip *chip; @@ -977,12 +939,12 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id) int err; /* look up via DT first */ - if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node) - return of_pwm_get(dev, dev->of_node, con_id); + if (is_of_node(fwnode)) + return of_pwm_get(dev, to_of_node(fwnode), con_id); /* then lookup via ACPI */ - if (dev && is_acpi_node(dev->fwnode)) { - pwm = acpi_pwm_get(dev->fwnode); + if (is_acpi_node(fwnode)) { + pwm = acpi_pwm_get(fwnode); if (!IS_ERR(pwm) || PTR_ERR(pwm) != -ENOENT) return pwm; } @@ -1103,9 +1065,9 @@ out: } EXPORT_SYMBOL_GPL(pwm_put); -static void devm_pwm_release(struct device *dev, void *res) +static void devm_pwm_release(void *pwm) { - pwm_put(*(struct pwm_device **)res); + pwm_put(pwm); } /** @@ -1121,19 +1083,16 @@ static void devm_pwm_release(struct device *dev, void *res) */ struct pwm_device *devm_pwm_get(struct device *dev, const char *con_id) { - struct pwm_device **ptr, *pwm; - - ptr = devres_alloc(devm_pwm_release, sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return ERR_PTR(-ENOMEM); + struct pwm_device *pwm; + int ret; pwm = pwm_get(dev, con_id); - if (!IS_ERR(pwm)) { - *ptr = pwm; - devres_add(dev, ptr); - } else { - devres_free(ptr); - } + if (IS_ERR(pwm)) + return pwm; + + ret = devm_add_action_or_reset(dev, devm_pwm_release, pwm); + if (ret) + return ERR_PTR(ret); return pwm; } @@ -1154,19 +1113,16 @@ EXPORT_SYMBOL_GPL(devm_pwm_get); struct pwm_device *devm_of_pwm_get(struct device *dev, struct device_node *np, const char *con_id) { - struct pwm_device **ptr, *pwm; - - ptr = devres_alloc(devm_pwm_release, sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return ERR_PTR(-ENOMEM); + struct pwm_device *pwm; + int ret; pwm = of_pwm_get(dev, np, con_id); - if (!IS_ERR(pwm)) { - *ptr = pwm; - devres_add(dev, ptr); - } else { - devres_free(ptr); - } + if (IS_ERR(pwm)) + return pwm; + + ret = devm_add_action_or_reset(dev, devm_pwm_release, pwm); + if (ret) + return ERR_PTR(ret); return pwm; } @@ -1188,53 +1144,24 @@ 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); + struct pwm_device *pwm = ERR_PTR(-ENODEV); + int ret; if (is_of_node(fwnode)) pwm = of_pwm_get(dev, to_of_node(fwnode), con_id); else if (is_acpi_node(fwnode)) pwm = acpi_pwm_get(fwnode); + if (IS_ERR(pwm)) + return pwm; - if (!IS_ERR(pwm)) { - *ptr = pwm; - devres_add(dev, ptr); - } else { - devres_free(ptr); - } + ret = devm_add_action_or_reset(dev, devm_pwm_release, pwm); + if (ret) + return ERR_PTR(ret); return pwm; } EXPORT_SYMBOL_GPL(devm_fwnode_pwm_get); -static int devm_pwm_match(struct devic |
