^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-or-later
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Broadcom BCM7038 PWM driver
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) * Author: Florian Fainelli
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) * Copyright (C) 2015 Broadcom Corporation
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include <linux/clk.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/export.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/init.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/io.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include <linux/of.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include <linux/platform_device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #include <linux/pwm.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #include <linux/spinlock.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) #define PWM_CTRL 0x00
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) #define CTRL_START BIT(0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) #define CTRL_OEB BIT(1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) #define CTRL_FORCE_HIGH BIT(2)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) #define CTRL_OPENDRAIN BIT(3)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) #define CTRL_CHAN_OFFS 4
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) #define PWM_CTRL2 0x04
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) #define CTRL2_OUT_SELECT BIT(0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) #define PWM_CH_SIZE 0x8
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) #define PWM_CWORD_MSB(ch) (0x08 + ((ch) * PWM_CH_SIZE))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) #define PWM_CWORD_LSB(ch) (0x0c + ((ch) * PWM_CH_SIZE))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) /* Number of bits for the CWORD value */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) #define CWORD_BIT_SIZE 16
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) * Maximum control word value allowed when variable-frequency PWM is used as a
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) * clock for the constant-frequency PMW.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) #define CONST_VAR_F_MAX 32768
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) #define CONST_VAR_F_MIN 1
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) #define PWM_ON(ch) (0x18 + ((ch) * PWM_CH_SIZE))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) #define PWM_ON_MIN 1
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) #define PWM_PERIOD(ch) (0x1c + ((ch) * PWM_CH_SIZE))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) #define PWM_PERIOD_MIN 0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) #define PWM_ON_PERIOD_MAX 0xff
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) struct brcmstb_pwm {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) void __iomem *base;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) spinlock_t lock;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) struct clk *clk;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) struct pwm_chip chip;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) static inline u32 brcmstb_pwm_readl(struct brcmstb_pwm *p,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) unsigned int offset)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) return __raw_readl(p->base + offset);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) return readl_relaxed(p->base + offset);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) static inline void brcmstb_pwm_writel(struct brcmstb_pwm *p, u32 value,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) unsigned int offset)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) __raw_writel(value, p->base + offset);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) writel_relaxed(value, p->base + offset);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) static inline struct brcmstb_pwm *to_brcmstb_pwm(struct pwm_chip *chip)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) return container_of(chip, struct brcmstb_pwm, chip);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) * Fv is derived from the variable frequency output. The variable frequency
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) * output is configured using this formula:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) * W = cword, if cword < 2 ^ 15 else 16-bit 2's complement of cword
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) * Fv = W x 2 ^ -16 x 27Mhz (reference clock)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) * The period is: (period + 1) / Fv and "on" time is on / (period + 1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) * The PWM core framework specifies that the "duty_ns" parameter is in fact the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) * "on" time, so this translates directly into our HW programming here.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) static int brcmstb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) int duty_ns, int period_ns)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) struct brcmstb_pwm *p = to_brcmstb_pwm(chip);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) unsigned long pc, dc, cword = CONST_VAR_F_MAX;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) unsigned int channel = pwm->hwpwm;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) u32 value;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) * If asking for a duty_ns equal to period_ns, we need to substract
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) * the period value by 1 to make it shorter than the "on" time and
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) * produce a flat 100% duty cycle signal, and max out the "on" time
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) if (duty_ns == period_ns) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) dc = PWM_ON_PERIOD_MAX;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) pc = PWM_ON_PERIOD_MAX - 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) goto done;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) while (1) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) u64 rate, tmp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) * Calculate the base rate from base frequency and current
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) * cword
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) rate = (u64)clk_get_rate(p->clk) * (u64)cword;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) do_div(rate, 1 << CWORD_BIT_SIZE);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) tmp = period_ns * rate;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) do_div(tmp, NSEC_PER_SEC);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) pc = tmp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) tmp = (duty_ns + 1) * rate;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) do_div(tmp, NSEC_PER_SEC);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) dc = tmp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) * We can be called with separate duty and period updates,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) * so do not reject dc == 0 right away
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) if (pc == PWM_PERIOD_MIN || (dc < PWM_ON_MIN && duty_ns))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) /* We converged on a calculation */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) if (pc <= PWM_ON_PERIOD_MAX && dc <= PWM_ON_PERIOD_MAX)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) * The cword needs to be a power of 2 for the variable
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) * frequency generator to output a 50% duty cycle variable
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) * frequency which is used as input clock to the fixed
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) * frequency generator.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) cword >>= 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) * Desired periods are too large, we do not have a divider
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) * for them
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) if (cword < CONST_VAR_F_MIN)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) done:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) * Configure the defined "cword" value to have the variable frequency
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) * generator output a base frequency for the constant frequency
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) * generator to derive from.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) spin_lock(&p->lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) brcmstb_pwm_writel(p, cword >> 8, PWM_CWORD_MSB(channel));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) brcmstb_pwm_writel(p, cword & 0xff, PWM_CWORD_LSB(channel));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) /* Select constant frequency signal output */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) value = brcmstb_pwm_readl(p, PWM_CTRL2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) value |= CTRL2_OUT_SELECT << (channel * CTRL_CHAN_OFFS);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) brcmstb_pwm_writel(p, value, PWM_CTRL2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) /* Configure on and period value */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) brcmstb_pwm_writel(p, pc, PWM_PERIOD(channel));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) brcmstb_pwm_writel(p, dc, PWM_ON(channel));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) spin_unlock(&p->lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) static inline void brcmstb_pwm_enable_set(struct brcmstb_pwm *p,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) unsigned int channel, bool enable)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) unsigned int shift = channel * CTRL_CHAN_OFFS;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) u32 value;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) spin_lock(&p->lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) value = brcmstb_pwm_readl(p, PWM_CTRL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) if (enable) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) value &= ~(CTRL_OEB << shift);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) value |= (CTRL_START | CTRL_OPENDRAIN) << shift;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) value &= ~((CTRL_START | CTRL_OPENDRAIN) << shift);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) value |= CTRL_OEB << shift;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) brcmstb_pwm_writel(p, value, PWM_CTRL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) spin_unlock(&p->lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) static int brcmstb_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) struct brcmstb_pwm *p = to_brcmstb_pwm(chip);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) brcmstb_pwm_enable_set(p, pwm->hwpwm, true);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) static void brcmstb_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) struct brcmstb_pwm *p = to_brcmstb_pwm(chip);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) brcmstb_pwm_enable_set(p, pwm->hwpwm, false);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) static const struct pwm_ops brcmstb_pwm_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) .config = brcmstb_pwm_config,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) .enable = brcmstb_pwm_enable,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) .disable = brcmstb_pwm_disable,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) .owner = THIS_MODULE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) static const struct of_device_id brcmstb_pwm_of_match[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) { .compatible = "brcm,bcm7038-pwm", },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) { /* sentinel */ }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) MODULE_DEVICE_TABLE(of, brcmstb_pwm_of_match);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) static int brcmstb_pwm_probe(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) struct brcmstb_pwm *p;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237) struct resource *res;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) if (!p)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244) spin_lock_init(&p->lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246) p->clk = devm_clk_get(&pdev->dev, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247) if (IS_ERR(p->clk)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248) dev_err(&pdev->dev, "failed to obtain clock\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) return PTR_ERR(p->clk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252) ret = clk_prepare_enable(p->clk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) if (ret < 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254) dev_err(&pdev->dev, "failed to enable clock: %d\n", ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258) platform_set_drvdata(pdev, p);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) p->chip.dev = &pdev->dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261) p->chip.ops = &brcmstb_pwm_ops;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262) p->chip.base = -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263) p->chip.npwm = 2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266) p->base = devm_ioremap_resource(&pdev->dev, res);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) if (IS_ERR(p->base)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268) ret = PTR_ERR(p->base);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269) goto out_clk;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272) ret = pwmchip_add(&p->chip);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273) if (ret) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275) goto out_clk;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280) out_clk:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281) clk_disable_unprepare(p->clk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285) static int brcmstb_pwm_remove(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287) struct brcmstb_pwm *p = platform_get_drvdata(pdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) ret = pwmchip_remove(&p->chip);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291) clk_disable_unprepare(p->clk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296) #ifdef CONFIG_PM_SLEEP
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297) static int brcmstb_pwm_suspend(struct device *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299) struct brcmstb_pwm *p = dev_get_drvdata(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301) clk_disable(p->clk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306) static int brcmstb_pwm_resume(struct device *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308) struct brcmstb_pwm *p = dev_get_drvdata(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310) clk_enable(p->clk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316) static SIMPLE_DEV_PM_OPS(brcmstb_pwm_pm_ops, brcmstb_pwm_suspend,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 317) brcmstb_pwm_resume);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 318)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 319) static struct platform_driver brcmstb_pwm_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 320) .probe = brcmstb_pwm_probe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 321) .remove = brcmstb_pwm_remove,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 322) .driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 323) .name = "pwm-brcmstb",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 324) .of_match_table = brcmstb_pwm_of_match,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 325) .pm = &brcmstb_pwm_pm_ops,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 326) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 327) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 328) module_platform_driver(brcmstb_pwm_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 329)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 330) MODULE_AUTHOR("Florian Fainelli <f.fainelli@gmail.com>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 331) MODULE_DESCRIPTION("Broadcom STB PWM driver");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 332) MODULE_ALIAS("platform:pwm-brcmstb");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 333) MODULE_LICENSE("GPL");