diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-07-09 18:57:45 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-07-09 18:57:45 +0300 |
commit | 6e2bbb688aa6d05073dd1dd0b836d9becec195c1 (patch) | |
tree | b267f5f593c397aa4c5eababc8d78ebc69ea3e7b /drivers/pwm/pwm-meson.c | |
parent | 5ad18b2e60b75c7297a998dea702451d33a052ed (diff) | |
parent | 3d25025ce9c2f364ea4ee76f1461c8714b9c0b6d (diff) | |
download | linux-6e2bbb688aa6d05073dd1dd0b836d9becec195c1.tar.xz |
Merge tag 'pwm/for-5.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm
Pull pwm updates from Thierry Reding:
"This set of changes contains a new driver for SiFive SoCs as well as
enhancements to the core (device links are used to track dependencies
between PWM providers and consumers, support for PWM controllers via
ACPI, sysfs will now suspend/resume PWMs that it has claimed) and
various existing drivers"
* tag 'pwm/for-5.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm: (37 commits)
pwm: fsl-ftm: Make sure to unlock mutex on failure
pwm: fsl-ftm: Use write protection for prescaler & polarity
pwm: fsl-ftm: More relaxed permissions for updating period
pwm: atmel-hlcdc: Add compatible for SAM9X60 HLCDC's PWM
pwm: bcm2835: Improve precision of PWM
leds: pwm: Support ACPI via firmware-node framework
pwm: Add support referencing PWMs from ACPI
pwm: rcar: Remove suspend/resume support
pwm: sysfs: Add suspend/resume support
pwm: Add power management descriptions
pwm: meson: Add documentation to the driver
pwm: meson: Add support PWM_POLARITY_INVERSED when disabling
pwm: meson: Don't cache struct pwm_state internally
pwm: meson: Read the full hardware state in meson_pwm_get_state()
pwm: meson: Simplify the calculation of the pre-divider and count
pwm: meson: Move pwm_set_chip_data() to meson_pwm_request()
pwm: meson: Add the per-channel register offsets and bits in a struct
pwm: meson: Add the meson_pwm_channel data to struct meson_pwm
pwm: meson: Pass struct pwm_device to meson_pwm_calc()
pwm: meson: Don't duplicate the polarity internally
...
Diffstat (limited to 'drivers/pwm/pwm-meson.c')
-rw-r--r-- | drivers/pwm/pwm-meson.c | 386 |
1 files changed, 182 insertions, 204 deletions
diff --git a/drivers/pwm/pwm-meson.c b/drivers/pwm/pwm-meson.c index fb5a369b1a8d..3cbff5cbb789 100644 --- a/drivers/pwm/pwm-meson.c +++ b/drivers/pwm/pwm-meson.c @@ -1,65 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. + * PWM controller driver for Amlogic Meson SoCs. * - * GPL LICENSE SUMMARY + * This PWM is only a set of Gates, Dividers and Counters: + * PWM output is achieved by calculating a clock that permits calculating + * two periods (low and high). The counter then has to be set to switch after + * N cycles for the first half period. + * The hardware has no "polarity" setting. This driver reverses the period + * cycles (the low length is inverted with the high length) for + * PWM_POLARITY_INVERSED. This means that .get_state cannot read the polarity + * from the hardware. + * Setting the duty cycle will disable and re-enable the PWM output. + * Disabling the PWM stops the output immediately (without waiting for the + * current period to complete first). * - * Copyright (c) 2016 BayLibre, SAS. - * Author: Neil Armstrong <narmstrong@baylibre.com> - * Copyright (C) 2014 Amlogic, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * BSD LICENSE + * The public S912 (GXM) datasheet contains some documentation for this PWM + * controller starting on page 543: + * https://dl.khadas.com/Hardware/VIM2/Datasheet/S912_Datasheet_V0.220170314publicversion-Wesion.pdf + * An updated version of this IP block is found in S922X (G12B) SoCs. The + * datasheet contains the description for this IP block revision starting at + * page 1084: + * https://dn.odroid.com/S922X/ODROID-N2/Datasheet/S922X_Public_Datasheet_V0.2.pdf * * Copyright (c) 2016 BayLibre, SAS. * Author: Neil Armstrong <narmstrong@baylibre.com> * Copyright (C) 2014 Amlogic, Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include <linux/bitfield.h> +#include <linux/bits.h> #include <linux/clk.h> #include <linux/clk-provider.h> #include <linux/err.h> #include <linux/io.h> #include <linux/kernel.h> +#include <linux/math64.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_device.h> @@ -70,7 +45,8 @@ #define REG_PWM_A 0x0 #define REG_PWM_B 0x4 -#define PWM_HIGH_SHIFT 16 +#define PWM_LOW_MASK GENMASK(15, 0) +#define PWM_HIGH_MASK GENMASK(31, 16) #define REG_MISC_AB 0x8 #define MISC_B_CLK_EN BIT(23) @@ -80,13 +56,33 @@ #define MISC_A_CLK_DIV_SHIFT 8 #define MISC_B_CLK_SEL_SHIFT 6 #define MISC_A_CLK_SEL_SHIFT 4 -#define MISC_CLK_SEL_WIDTH 2 +#define MISC_CLK_SEL_MASK 0x3 #define MISC_B_EN BIT(1) #define MISC_A_EN BIT(0) -static const unsigned int mux_reg_shifts[] = { - MISC_A_CLK_SEL_SHIFT, - MISC_B_CLK_SEL_SHIFT +#define MESON_NUM_PWMS 2 + +static struct meson_pwm_channel_data { + u8 reg_offset; + u8 clk_sel_shift; + u8 clk_div_shift; + u32 clk_en_mask; + u32 pwm_en_mask; +} meson_pwm_per_channel_data[MESON_NUM_PWMS] = { + { + .reg_offset = REG_PWM_A, + .clk_sel_shift = MISC_A_CLK_SEL_SHIFT, + .clk_div_shift = MISC_A_CLK_DIV_SHIFT, + .clk_en_mask = MISC_A_CLK_EN, + .pwm_en_mask = MISC_A_EN, + }, + { + .reg_offset = REG_PWM_B, + .clk_sel_shift = MISC_B_CLK_SEL_SHIFT, + .clk_div_shift = MISC_B_CLK_DIV_SHIFT, + .clk_en_mask = MISC_B_CLK_EN, + .pwm_en_mask = MISC_B_EN, + } }; struct meson_pwm_channel { @@ -94,8 +90,6 @@ struct meson_pwm_channel { unsigned int lo; u8 pre_div; - struct pwm_state state; - struct clk *clk_parent; struct clk_mux mux; struct clk *clk; @@ -109,8 +103,8 @@ struct meson_pwm_data { struct meson_pwm { struct pwm_chip chip; const struct meson_pwm_data *data; + struct meson_pwm_channel channels[MESON_NUM_PWMS]; void __iomem *base; - u8 inverter_mask; /* * Protects register (write) access to the REG_MISC_AB register * that is shared between the two PWMs. @@ -125,12 +119,16 @@ static inline struct meson_pwm *to_meson_pwm(struct pwm_chip *chip) static int meson_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) { - struct meson_pwm_channel *channel = pwm_get_chip_data(pwm); + struct meson_pwm *meson = to_meson_pwm(chip); + struct meson_pwm_channel *channel; struct device *dev = chip->dev; int err; - if (!channel) - return -ENODEV; + channel = pwm_get_chip_data(pwm); + if (channel) + return 0; + + channel = &meson->channels[pwm->hwpwm]; if (channel->clk_parent) { err = clk_set_parent(channel->clk, channel->clk_parent); @@ -149,9 +147,7 @@ static int meson_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) return err; } - chip->ops->get_state(chip, pwm, &channel->state); - - return 0; + return pwm_set_chip_data(pwm, channel); } static void meson_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) @@ -162,20 +158,18 @@ static void meson_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) clk_disable_unprepare(channel->clk); } -static int meson_pwm_calc(struct meson_pwm *meson, - struct meson_pwm_channel *channel, unsigned int id, - unsigned int duty, unsigned int period) +static int meson_pwm_calc(struct meson_pwm *meson, struct pwm_device *pwm, + struct pwm_state *state) { - unsigned int pre_div, cnt, duty_cnt; + struct meson_pwm_channel *channel = pwm_get_chip_data(pwm); + unsigned int duty, period, pre_div, cnt, duty_cnt; unsigned long fin_freq = -1; - u64 fin_ps; - if (~(meson->inverter_mask >> id) & 0x1) - duty = period - duty; + duty = state->duty_cycle; + period = state->period; - if (period == channel->state.period && - duty == channel->state.duty_cycle) - return 0; + if (state->polarity == PWM_POLARITY_INVERSED) + duty = period - duty; fin_freq = clk_get_rate(channel->clk); if (fin_freq == 0) { @@ -184,24 +178,19 @@ static int meson_pwm_calc(struct meson_pwm *meson, } dev_dbg(meson->chip.dev, "fin_freq: %lu Hz\n", fin_freq); - fin_ps = (u64)NSEC_PER_SEC * 1000; - do_div(fin_ps, fin_freq); - - /* Calc pre_div with the period */ - for (pre_div = 0; pre_div <= MISC_CLK_DIV_MASK; pre_div++) { - cnt = DIV_ROUND_CLOSEST_ULL((u64)period * 1000, - fin_ps * (pre_div + 1)); - dev_dbg(meson->chip.dev, "fin_ps=%llu pre_div=%u cnt=%u\n", - fin_ps, pre_div, cnt); - if (cnt <= 0xffff) - break; - } + pre_div = div64_u64(fin_freq * (u64)period, NSEC_PER_SEC * 0xffffLL); if (pre_div > MISC_CLK_DIV_MASK) { dev_err(meson->chip.dev, "unable to get period pre_div\n"); return -EINVAL; } + cnt = div64_u64(fin_freq * (u64)period, NSEC_PER_SEC * (pre_div + 1)); + if (cnt > 0xffff) { + dev_err(meson->chip.dev, "unable to get period cnt\n"); + return -EINVAL; + } + dev_dbg(meson->chip.dev, "period=%u pre_div=%u cnt=%u\n", period, pre_div, cnt); @@ -215,8 +204,8 @@ static int meson_pwm_calc(struct meson_pwm *meson, channel->lo = cnt; } else { /* Then check is we can have the duty with the same pre_div */ - duty_cnt = DIV_ROUND_CLOSEST_ULL((u64)duty * 1000, - fin_ps * (pre_div + 1)); + duty_cnt = div64_u64(fin_freq * (u64)duty, + NSEC_PER_SEC * (pre_div + 1)); if (duty_cnt > 0xffff) { dev_err(meson->chip.dev, "unable to get duty cycle\n"); return -EINVAL; @@ -233,73 +222,43 @@ static int meson_pwm_calc(struct meson_pwm *meson, return 0; } -static void meson_pwm_enable(struct meson_pwm *meson, - struct meson_pwm_channel *channel, - unsigned int id) +static void meson_pwm_enable(struct meson_pwm *meson, struct pwm_device *pwm) { - u32 value, clk_shift, clk_enable, enable; - unsigned int offset; + struct meson_pwm_channel *channel = pwm_get_chip_data(pwm); + struct meson_pwm_channel_data *channel_data; unsigned long flags; + u32 value; - switch (id) { - case 0: - clk_shift = MISC_A_CLK_DIV_SHIFT; - clk_enable = MISC_A_CLK_EN; - enable = MISC_A_EN; - offset = REG_PWM_A; - break; - - case 1: - clk_shift = MISC_B_CLK_DIV_SHIFT; - clk_enable = MISC_B_CLK_EN; - enable = MISC_B_EN; - offset = REG_PWM_B; - break; - - default: - return; - } + channel_data = &meson_pwm_per_channel_data[pwm->hwpwm]; spin_lock_irqsave(&meson->lock, flags); value = readl(meson->base + REG_MISC_AB); - value &= ~(MISC_CLK_DIV_MASK << clk_shift); - value |= channel->pre_div << clk_shift; - value |= clk_enable; + value &= ~(MISC_CLK_DIV_MASK << channel_data->clk_div_shift); + value |= channel->pre_div << channel_data->clk_div_shift; + value |= channel_data->clk_en_mask; writel(value, meson->base + REG_MISC_AB); - value = (channel->hi << PWM_HIGH_SHIFT) | channel->lo; - writel(value, meson->base + offset); + value = FIELD_PREP(PWM_HIGH_MASK, channel->hi) | + FIELD_PREP(PWM_LOW_MASK, channel->lo); + writel(value, meson->base + channel_data->reg_offset); value = readl(meson->base + REG_MISC_AB); - value |= enable; + value |= channel_data->pwm_en_mask; writel(value, meson->base + REG_MISC_AB); spin_unlock_irqrestore(&meson->lock, flags); } -static void meson_pwm_disable(struct meson_pwm *meson, unsigned int id) +static void meson_pwm_disable(struct meson_pwm *meson, struct pwm_device *pwm) { - u32 value, enable; unsigned long flags; - - switch (id) { - case 0: - enable = MISC_A_EN; - break; - - case 1: - enable = MISC_B_EN; - break; - - default: - return; - } + u32 value; spin_lock_irqsave(&meson->lock, flags); value = readl(meson->base + REG_MISC_AB); - value &= ~enable; + value &= ~meson_pwm_per_channel_data[pwm->hwpwm].pwm_en_mask; writel(value, meson->base + REG_MISC_AB); spin_unlock_irqrestore(&meson->lock, flags); @@ -316,64 +275,97 @@ static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, return -EINVAL; if (!state->enabled) { - meson_pwm_disable(meson, pwm->hwpwm); - channel->state.enabled = false; - - return 0; - } - - if (state->period != channel->state.period || - state->duty_cycle != channel->state.duty_cycle || - state->polarity != channel->state.polarity) { - if (state->polarity != channel->state.polarity) { - if (state->polarity == PWM_POLARITY_NORMAL) - meson->inverter_mask |= BIT(pwm->hwpwm); - else - meson->inverter_mask &= ~BIT(pwm->hwpwm); + if (state->polarity == PWM_POLARITY_INVERSED) { + /* + * This IP block revision doesn't have an "always high" + * setting which we can use for "inverted disabled". + * Instead we achieve this using the same settings + * that we use a pre_div of 0 (to get the shortest + * possible duration for one "count") and + * "period == duty_cycle". This results in a signal + * which is LOW for one "count", while being HIGH for + * the rest of the (so the signal is HIGH for slightly + * less than 100% of the period, but this is the best + * we can achieve). + */ + channel->pre_div = 0; + channel->hi = ~0; + channel->lo = 0; + + meson_pwm_enable(meson, pwm); + } else { + meson_pwm_disable(meson, pwm); } - - err = meson_pwm_calc(meson, channel, pwm->hwpwm, - state->duty_cycle, state->period); + } else { + err = meson_pwm_calc(meson, pwm, state); if (err < 0) return err; - channel->state.polarity = state->polarity; - channel->state.period = state->period; - channel->state.duty_cycle = state->duty_cycle; - } - - if (state->enabled && !channel->state.enabled) { - meson_pwm_enable(meson, channel, pwm->hwpwm); - channel->state.enabled = true; + meson_pwm_enable(meson, pwm); } return 0; } +static unsigned int meson_pwm_cnt_to_ns(struct pwm_chip *chip, + struct pwm_device *pwm, u32 cnt) +{ + struct meson_pwm *meson = to_meson_pwm(chip); + struct meson_pwm_channel *channel; + unsigned long fin_freq; + u32 fin_ns; + + /* to_meson_pwm() can only be used after .get_state() is called */ + channel = &meson->channels[pwm->hwpwm]; + + fin_freq = clk_get_rate(channel->clk); + if (fin_freq == 0) + return 0; + + fin_ns = div_u64(NSEC_PER_SEC, fin_freq); + + return cnt * fin_ns * (channel->pre_div + 1); +} + static void meson_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, struct pwm_state *state) { struct meson_pwm *meson = to_meson_pwm(chip); - u32 value, mask; + struct meson_pwm_channel_data *channel_data; + struct meson_pwm_channel *channel; + u32 value, tmp; if (!state) return; - switch (pwm->hwpwm) { - case 0: - mask = MISC_A_EN; - break; + channel = &meson->channels[pwm->hwpwm]; + channel_data = &meson_pwm_per_channel_data[pwm->hwpwm]; - case 1: - mask = MISC_B_EN; - break; + value = readl(meson->base + REG_MISC_AB); - default: - return; - } + tmp = channel_data->pwm_en_mask | channel_data->clk_en_mask; + state->enabled = (value & tmp) == tmp; - value = readl(meson->base + REG_MISC_AB); - state->enabled = (value & mask) != 0; + tmp = value >> channel_data->clk_div_shift; + channel->pre_div = FIELD_GET(MISC_CLK_DIV_MASK, tmp); + + value = readl(meson->base + channel_data->reg_offset); + + channel->lo = FIELD_GET(PWM_LOW_MASK, value); + channel->hi = FIELD_GET(PWM_HIGH_MASK, value); + + if (channel->lo == 0) { + state->period = meson_pwm_cnt_to_ns(chip, pwm, channel->hi); + state->duty_cycle = state->period; + } else if (channel->lo >= channel->hi) { + state->period = meson_pwm_cnt_to_ns(chip, pwm, + channel->lo + channel->hi); + state->duty_cycle = meson_pwm_cnt_to_ns(chip, pwm, + channel->hi); + } else { + state->period = 0; + state->duty_cycle = 0; + } } static const struct pwm_ops meson_pwm_ops = { @@ -433,8 +425,17 @@ static const struct meson_pwm_data pwm_axg_ao_data = { .num_parents = ARRAY_SIZE(pwm_axg_ao_parent_names), }; +static const char * const pwm_g12a_ao_ab_parent_names[] = { + "xtal", "aoclk81", "fclk_div4", "fclk_div5" +}; + +static const struct meson_pwm_data pwm_g12a_ao_ab_data = { + .parent_names = pwm_g12a_ao_ab_parent_names, + .num_parents = ARRAY_SIZE(pwm_g12a_ao_ab_parent_names), +}; + static const char * const pwm_g12a_ao_cd_parent_names[] = { - "aoclk81", "xtal", + "xtal", "aoclk81", }; static const struct meson_pwm_data pwm_g12a_ao_cd_data = { @@ -478,7 +479,7 @@ static const struct of_device_id meson_pwm_matches[] = { }, { .compatible = "amlogic,meson-g12a-ao-pwm-ab", - .data = &pwm_axg_ao_data + .data = &pwm_g12a_ao_ab_data }, { .compatible = "amlogic,meson-g12a-ao-pwm-cd", @@ -488,8 +489,7 @@ static const struct of_device_id meson_pwm_matches[] = { }; MODULE_DEVICE_TABLE(of, meson_pwm_matches); -static int meson_pwm_init_channels(struct meson_pwm *meson, - struct meson_pwm_channel *channels) +static int meson_pwm_init_channels(struct meson_pwm *meson) { struct device *dev = meson->chip.dev; struct clk_init_data init; @@ -498,7 +498,7 @@ static int meson_pwm_init_channels(struct meson_pwm *meson, int err; for (i = 0; i < meson->chip.npwm; i++) { - struct meson_pwm_channel *channel = &channels[i]; + struct meson_pwm_channel *channel = &meson->channels[i]; snprintf(name, sizeof(name), "%s#mux%u", dev_name(dev), i); @@ -509,8 +509,9 @@ static int meson_pwm_init_channels(struct meson_pwm *meson, init.num_parents = meson->data->num_parents; channel->mux.reg = meson->base + REG_MISC_AB; - channel->mux.shift = mux_reg_shifts[i]; - channel->mux.mask = BIT(MISC_CLK_SEL_WIDTH) - 1; + channel->mux.shift = + meson_pwm_per_channel_data[i].clk_sel_shift; + channel->mux.mask = MISC_CLK_SEL_MASK; channel->mux.flags = 0; channel->mux.lock = &meson->lock; channel->mux.table = NULL; @@ -525,31 +526,16 @@ static int meson_pwm_init_channels(struct meson_pwm *meson, snprintf(name, sizeof(name), "clkin%u", i); - channel->clk_parent = devm_clk_get(dev, name); - if (IS_ERR(channel->clk_parent)) { - err = PTR_ERR(channel->clk_parent); - if (err == -EPROBE_DEFER) - return err; - - channel->clk_parent = NULL; - } + channel->clk_parent = devm_clk_get_optional(dev, name); + if (IS_ERR(channel->clk_parent)) + return PTR_ERR(channel->clk_parent); } return 0; } -static void meson_pwm_add_channels(struct meson_pwm *meson, - struct meson_pwm_channel *channels) -{ - unsigned int i; - - for (i = 0; i < meson->chip.npwm; i++) - pwm_set_chip_data(&meson->chip.pwms[i], &channels[i]); -} - static int meson_pwm_probe(struct platform_device *pdev) { - struct meson_pwm_channel *channels; struct meson_pwm *meson; struct resource *regs; int err; @@ -567,19 +553,13 @@ static int meson_pwm_probe(struct platform_device *pdev) meson->chip.dev = &pdev->dev; meson->chip.ops = &meson_pwm_ops; meson->chip.base = -1; - meson->chip.npwm = 2; + meson->chip.npwm = MESON_NUM_PWMS; meson->chip.of_xlate = of_pwm_xlate_with_flags; meson->chip.of_pwm_n_cells = 3; meson->data = of_device_get_match_data(&pdev->dev); - meson->inverter_mask = BIT(meson->chip.npwm) - 1; - channels = devm_kcalloc(&pdev->dev, meson->chip.npwm, - sizeof(*channels), GFP_KERNEL); - if (!channels) - return -ENOMEM; - - err = meson_pwm_init_channels(meson, channels); + err = meson_pwm_init_channels(meson); if (err < 0) return err; @@ -589,8 +569,6 @@ static int meson_pwm_probe(struct platform_device *pdev) return err; } - meson_pwm_add_channels(meson, channels); - platform_set_drvdata(pdev, meson); return 0; |