|
@@ -42,29 +42,44 @@
|
|
|
#include <linux/io.h>
|
|
|
#include <linux/of_irq.h>
|
|
|
#include <linux/of_device.h>
|
|
|
+#include <linux/pwm.h>
|
|
|
#include <linux/clk.h>
|
|
|
#include <linux/pinctrl/consumer.h>
|
|
|
#include <linux/irqchip/chained_irq.h>
|
|
|
+#include <linux/platform_device.h>
|
|
|
+#include <linux/bitops.h>
|
|
|
+
|
|
|
+#include "gpiolib.h"
|
|
|
|
|
|
/*
|
|
|
* GPIO unit register offsets.
|
|
|
*/
|
|
|
-#define GPIO_OUT_OFF 0x0000
|
|
|
-#define GPIO_IO_CONF_OFF 0x0004
|
|
|
-#define GPIO_BLINK_EN_OFF 0x0008
|
|
|
-#define GPIO_IN_POL_OFF 0x000c
|
|
|
-#define GPIO_DATA_IN_OFF 0x0010
|
|
|
-#define GPIO_EDGE_CAUSE_OFF 0x0014
|
|
|
-#define GPIO_EDGE_MASK_OFF 0x0018
|
|
|
-#define GPIO_LEVEL_MASK_OFF 0x001c
|
|
|
+#define GPIO_OUT_OFF 0x0000
|
|
|
+#define GPIO_IO_CONF_OFF 0x0004
|
|
|
+#define GPIO_BLINK_EN_OFF 0x0008
|
|
|
+#define GPIO_IN_POL_OFF 0x000c
|
|
|
+#define GPIO_DATA_IN_OFF 0x0010
|
|
|
+#define GPIO_EDGE_CAUSE_OFF 0x0014
|
|
|
+#define GPIO_EDGE_MASK_OFF 0x0018
|
|
|
+#define GPIO_LEVEL_MASK_OFF 0x001c
|
|
|
+#define GPIO_BLINK_CNT_SELECT_OFF 0x0020
|
|
|
+
|
|
|
+/*
|
|
|
+ * PWM register offsets.
|
|
|
+ */
|
|
|
+#define PWM_BLINK_ON_DURATION_OFF 0x0
|
|
|
+#define PWM_BLINK_OFF_DURATION_OFF 0x4
|
|
|
+
|
|
|
|
|
|
/* The MV78200 has per-CPU registers for edge mask and level mask */
|
|
|
#define GPIO_EDGE_MASK_MV78200_OFF(cpu) ((cpu) ? 0x30 : 0x18)
|
|
|
#define GPIO_LEVEL_MASK_MV78200_OFF(cpu) ((cpu) ? 0x34 : 0x1C)
|
|
|
|
|
|
-/* The Armada XP has per-CPU registers for interrupt cause, interrupt
|
|
|
+/*
|
|
|
+ * The Armada XP has per-CPU registers for interrupt cause, interrupt
|
|
|
* mask and interrupt level mask. Those are relative to the
|
|
|
- * percpu_membase. */
|
|
|
+ * percpu_membase.
|
|
|
+ */
|
|
|
#define GPIO_EDGE_CAUSE_ARMADAXP_OFF(cpu) ((cpu) * 0x4)
|
|
|
#define GPIO_EDGE_MASK_ARMADAXP_OFF(cpu) (0x10 + (cpu) * 0x4)
|
|
|
#define GPIO_LEVEL_MASK_ARMADAXP_OFF(cpu) (0x20 + (cpu) * 0x4)
|
|
@@ -75,6 +90,20 @@
|
|
|
|
|
|
#define MVEBU_MAX_GPIO_PER_BANK 32
|
|
|
|
|
|
+struct mvebu_pwm {
|
|
|
+ void __iomem *membase;
|
|
|
+ unsigned long clk_rate;
|
|
|
+ struct gpio_desc *gpiod;
|
|
|
+ struct pwm_chip chip;
|
|
|
+ spinlock_t lock;
|
|
|
+ struct mvebu_gpio_chip *mvchip;
|
|
|
+
|
|
|
+ /* Used to preserve GPIO/PWM registers across suspend/resume */
|
|
|
+ u32 blink_select;
|
|
|
+ u32 blink_on_duration;
|
|
|
+ u32 blink_off_duration;
|
|
|
+};
|
|
|
+
|
|
|
struct mvebu_gpio_chip {
|
|
|
struct gpio_chip chip;
|
|
|
spinlock_t lock;
|
|
@@ -84,48 +113,55 @@ struct mvebu_gpio_chip {
|
|
|
struct irq_domain *domain;
|
|
|
int soc_variant;
|
|
|
|
|
|
+ /* Used for PWM support */
|
|
|
+ struct clk *clk;
|
|
|
+ struct mvebu_pwm *mvpwm;
|
|
|
+
|
|
|
/* Used to preserve GPIO registers across suspend/resume */
|
|
|
- u32 out_reg;
|
|
|
- u32 io_conf_reg;
|
|
|
- u32 blink_en_reg;
|
|
|
- u32 in_pol_reg;
|
|
|
- u32 edge_mask_regs[4];
|
|
|
- u32 level_mask_regs[4];
|
|
|
+ u32 out_reg;
|
|
|
+ u32 io_conf_reg;
|
|
|
+ u32 blink_en_reg;
|
|
|
+ u32 in_pol_reg;
|
|
|
+ u32 edge_mask_regs[4];
|
|
|
+ u32 level_mask_regs[4];
|
|
|
};
|
|
|
|
|
|
/*
|
|
|
* Functions returning addresses of individual registers for a given
|
|
|
* GPIO controller.
|
|
|
*/
|
|
|
-static inline void __iomem *mvebu_gpioreg_out(struct mvebu_gpio_chip *mvchip)
|
|
|
+static void __iomem *mvebu_gpioreg_out(struct mvebu_gpio_chip *mvchip)
|
|
|
{
|
|
|
return mvchip->membase + GPIO_OUT_OFF;
|
|
|
}
|
|
|
|
|
|
-static inline void __iomem *mvebu_gpioreg_blink(struct mvebu_gpio_chip *mvchip)
|
|
|
+static void __iomem *mvebu_gpioreg_blink(struct mvebu_gpio_chip *mvchip)
|
|
|
{
|
|
|
return mvchip->membase + GPIO_BLINK_EN_OFF;
|
|
|
}
|
|
|
|
|
|
-static inline void __iomem *
|
|
|
-mvebu_gpioreg_io_conf(struct mvebu_gpio_chip *mvchip)
|
|
|
+static void __iomem *mvebu_gpioreg_blink_counter_select(struct mvebu_gpio_chip
|
|
|
+ *mvchip)
|
|
|
+{
|
|
|
+ return mvchip->membase + GPIO_BLINK_CNT_SELECT_OFF;
|
|
|
+}
|
|
|
+
|
|
|
+static void __iomem *mvebu_gpioreg_io_conf(struct mvebu_gpio_chip *mvchip)
|
|
|
{
|
|
|
return mvchip->membase + GPIO_IO_CONF_OFF;
|
|
|
}
|
|
|
|
|
|
-static inline void __iomem *mvebu_gpioreg_in_pol(struct mvebu_gpio_chip *mvchip)
|
|
|
+static void __iomem *mvebu_gpioreg_in_pol(struct mvebu_gpio_chip *mvchip)
|
|
|
{
|
|
|
return mvchip->membase + GPIO_IN_POL_OFF;
|
|
|
}
|
|
|
|
|
|
-static inline void __iomem *
|
|
|
-mvebu_gpioreg_data_in(struct mvebu_gpio_chip *mvchip)
|
|
|
+static void __iomem *mvebu_gpioreg_data_in(struct mvebu_gpio_chip *mvchip)
|
|
|
{
|
|
|
return mvchip->membase + GPIO_DATA_IN_OFF;
|
|
|
}
|
|
|
|
|
|
-static inline void __iomem *
|
|
|
-mvebu_gpioreg_edge_cause(struct mvebu_gpio_chip *mvchip)
|
|
|
+static void __iomem *mvebu_gpioreg_edge_cause(struct mvebu_gpio_chip *mvchip)
|
|
|
{
|
|
|
int cpu;
|
|
|
|
|
@@ -142,8 +178,7 @@ mvebu_gpioreg_edge_cause(struct mvebu_gpio_chip *mvchip)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-static inline void __iomem *
|
|
|
-mvebu_gpioreg_edge_mask(struct mvebu_gpio_chip *mvchip)
|
|
|
+static void __iomem *mvebu_gpioreg_edge_mask(struct mvebu_gpio_chip *mvchip)
|
|
|
{
|
|
|
int cpu;
|
|
|
|
|
@@ -182,10 +217,23 @@ static void __iomem *mvebu_gpioreg_level_mask(struct mvebu_gpio_chip *mvchip)
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
- * Functions implementing the gpio_chip methods
|
|
|
+ * Functions returning addresses of individual registers for a given
|
|
|
+ * PWM controller.
|
|
|
*/
|
|
|
+static void __iomem *mvebu_pwmreg_blink_on_duration(struct mvebu_pwm *mvpwm)
|
|
|
+{
|
|
|
+ return mvpwm->membase + PWM_BLINK_ON_DURATION_OFF;
|
|
|
+}
|
|
|
+
|
|
|
+static void __iomem *mvebu_pwmreg_blink_off_duration(struct mvebu_pwm *mvpwm)
|
|
|
+{
|
|
|
+ return mvpwm->membase + PWM_BLINK_OFF_DURATION_OFF;
|
|
|
+}
|
|
|
|
|
|
-static void mvebu_gpio_set(struct gpio_chip *chip, unsigned pin, int value)
|
|
|
+/*
|
|
|
+ * Functions implementing the gpio_chip methods
|
|
|
+ */
|
|
|
+static void mvebu_gpio_set(struct gpio_chip *chip, unsigned int pin, int value)
|
|
|
{
|
|
|
struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip);
|
|
|
unsigned long flags;
|
|
@@ -194,19 +242,19 @@ static void mvebu_gpio_set(struct gpio_chip *chip, unsigned pin, int value)
|
|
|
spin_lock_irqsave(&mvchip->lock, flags);
|
|
|
u = readl_relaxed(mvebu_gpioreg_out(mvchip));
|
|
|
if (value)
|
|
|
- u |= 1 << pin;
|
|
|
+ u |= BIT(pin);
|
|
|
else
|
|
|
- u &= ~(1 << pin);
|
|
|
+ u &= ~BIT(pin);
|
|
|
writel_relaxed(u, mvebu_gpioreg_out(mvchip));
|
|
|
spin_unlock_irqrestore(&mvchip->lock, flags);
|
|
|
}
|
|
|
|
|
|
-static int mvebu_gpio_get(struct gpio_chip *chip, unsigned pin)
|
|
|
+static int mvebu_gpio_get(struct gpio_chip *chip, unsigned int pin)
|
|
|
{
|
|
|
struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip);
|
|
|
u32 u;
|
|
|
|
|
|
- if (readl_relaxed(mvebu_gpioreg_io_conf(mvchip)) & (1 << pin)) {
|
|
|
+ if (readl_relaxed(mvebu_gpioreg_io_conf(mvchip)) & BIT(pin)) {
|
|
|
u = readl_relaxed(mvebu_gpioreg_data_in(mvchip)) ^
|
|
|
readl_relaxed(mvebu_gpioreg_in_pol(mvchip));
|
|
|
} else {
|
|
@@ -216,7 +264,8 @@ static int mvebu_gpio_get(struct gpio_chip *chip, unsigned pin)
|
|
|
return (u >> pin) & 1;
|
|
|
}
|
|
|
|
|
|
-static void mvebu_gpio_blink(struct gpio_chip *chip, unsigned pin, int value)
|
|
|
+static void mvebu_gpio_blink(struct gpio_chip *chip, unsigned int pin,
|
|
|
+ int value)
|
|
|
{
|
|
|
struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip);
|
|
|
unsigned long flags;
|
|
@@ -225,36 +274,38 @@ static void mvebu_gpio_blink(struct gpio_chip *chip, unsigned pin, int value)
|
|
|
spin_lock_irqsave(&mvchip->lock, flags);
|
|
|
u = readl_relaxed(mvebu_gpioreg_blink(mvchip));
|
|
|
if (value)
|
|
|
- u |= 1 << pin;
|
|
|
+ u |= BIT(pin);
|
|
|
else
|
|
|
- u &= ~(1 << pin);
|
|
|
+ u &= ~BIT(pin);
|
|
|
writel_relaxed(u, mvebu_gpioreg_blink(mvchip));
|
|
|
spin_unlock_irqrestore(&mvchip->lock, flags);
|
|
|
}
|
|
|
|
|
|
-static int mvebu_gpio_direction_input(struct gpio_chip *chip, unsigned pin)
|
|
|
+static int mvebu_gpio_direction_input(struct gpio_chip *chip, unsigned int pin)
|
|
|
{
|
|
|
struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip);
|
|
|
unsigned long flags;
|
|
|
int ret;
|
|
|
u32 u;
|
|
|
|
|
|
- /* Check with the pinctrl driver whether this pin is usable as
|
|
|
- * an input GPIO */
|
|
|
+ /*
|
|
|
+ * Check with the pinctrl driver whether this pin is usable as
|
|
|
+ * an input GPIO
|
|
|
+ */
|
|
|
ret = pinctrl_gpio_direction_input(chip->base + pin);
|
|
|
if (ret)
|
|
|
return ret;
|
|
|
|
|
|
spin_lock_irqsave(&mvchip->lock, flags);
|
|
|
u = readl_relaxed(mvebu_gpioreg_io_conf(mvchip));
|
|
|
- u |= 1 << pin;
|
|
|
+ u |= BIT(pin);
|
|
|
writel_relaxed(u, mvebu_gpioreg_io_conf(mvchip));
|
|
|
spin_unlock_irqrestore(&mvchip->lock, flags);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int mvebu_gpio_direction_output(struct gpio_chip *chip, unsigned pin,
|
|
|
+static int mvebu_gpio_direction_output(struct gpio_chip *chip, unsigned int pin,
|
|
|
int value)
|
|
|
{
|
|
|
struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip);
|
|
@@ -262,8 +313,10 @@ static int mvebu_gpio_direction_output(struct gpio_chip *chip, unsigned pin,
|
|
|
int ret;
|
|
|
u32 u;
|
|
|
|
|
|
- /* Check with the pinctrl driver whether this pin is usable as
|
|
|
- * an output GPIO */
|
|
|
+ /*
|
|
|
+ * Check with the pinctrl driver whether this pin is usable as
|
|
|
+ * an output GPIO
|
|
|
+ */
|
|
|
ret = pinctrl_gpio_direction_output(chip->base + pin);
|
|
|
if (ret)
|
|
|
return ret;
|
|
@@ -273,16 +326,17 @@ static int mvebu_gpio_direction_output(struct gpio_chip *chip, unsigned pin,
|
|
|
|
|
|
spin_lock_irqsave(&mvchip->lock, flags);
|
|
|
u = readl_relaxed(mvebu_gpioreg_io_conf(mvchip));
|
|
|
- u &= ~(1 << pin);
|
|
|
+ u &= ~BIT(pin);
|
|
|
writel_relaxed(u, mvebu_gpioreg_io_conf(mvchip));
|
|
|
spin_unlock_irqrestore(&mvchip->lock, flags);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int mvebu_gpio_to_irq(struct gpio_chip *chip, unsigned pin)
|
|
|
+static int mvebu_gpio_to_irq(struct gpio_chip *chip, unsigned int pin)
|
|
|
{
|
|
|
struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip);
|
|
|
+
|
|
|
return irq_create_mapping(mvchip->domain, pin);
|
|
|
}
|
|
|
|
|
@@ -389,7 +443,7 @@ static int mvebu_gpio_irq_set_type(struct irq_data *d, unsigned int type)
|
|
|
|
|
|
pin = d->hwirq;
|
|
|
|
|
|
- u = readl_relaxed(mvebu_gpioreg_io_conf(mvchip)) & (1 << pin);
|
|
|
+ u = readl_relaxed(mvebu_gpioreg_io_conf(mvchip)) & BIT(pin);
|
|
|
if (!u)
|
|
|
return -EINVAL;
|
|
|
|
|
@@ -409,13 +463,13 @@ static int mvebu_gpio_irq_set_type(struct irq_data *d, unsigned int type)
|
|
|
case IRQ_TYPE_EDGE_RISING:
|
|
|
case IRQ_TYPE_LEVEL_HIGH:
|
|
|
u = readl_relaxed(mvebu_gpioreg_in_pol(mvchip));
|
|
|
- u &= ~(1 << pin);
|
|
|
+ u &= ~BIT(pin);
|
|
|
writel_relaxed(u, mvebu_gpioreg_in_pol(mvchip));
|
|
|
break;
|
|
|
case IRQ_TYPE_EDGE_FALLING:
|
|
|
case IRQ_TYPE_LEVEL_LOW:
|
|
|
u = readl_relaxed(mvebu_gpioreg_in_pol(mvchip));
|
|
|
- u |= 1 << pin;
|
|
|
+ u |= BIT(pin);
|
|
|
writel_relaxed(u, mvebu_gpioreg_in_pol(mvchip));
|
|
|
break;
|
|
|
case IRQ_TYPE_EDGE_BOTH: {
|
|
@@ -428,10 +482,10 @@ static int mvebu_gpio_irq_set_type(struct irq_data *d, unsigned int type)
|
|
|
* set initial polarity based on current input level
|
|
|
*/
|
|
|
u = readl_relaxed(mvebu_gpioreg_in_pol(mvchip));
|
|
|
- if (v & (1 << pin))
|
|
|
- u |= 1 << pin; /* falling */
|
|
|
+ if (v & BIT(pin))
|
|
|
+ u |= BIT(pin); /* falling */
|
|
|
else
|
|
|
- u &= ~(1 << pin); /* rising */
|
|
|
+ u &= ~BIT(pin); /* rising */
|
|
|
writel_relaxed(u, mvebu_gpioreg_in_pol(mvchip));
|
|
|
break;
|
|
|
}
|
|
@@ -461,7 +515,7 @@ static void mvebu_gpio_irq_handler(struct irq_desc *desc)
|
|
|
|
|
|
irq = irq_find_mapping(mvchip->domain, i);
|
|
|
|
|
|
- if (!(cause & (1 << i)))
|
|
|
+ if (!(cause & BIT(i)))
|
|
|
continue;
|
|
|
|
|
|
type = irq_get_trigger_type(irq);
|
|
@@ -470,7 +524,7 @@ static void mvebu_gpio_irq_handler(struct irq_desc *desc)
|
|
|
u32 polarity;
|
|
|
|
|
|
polarity = readl_relaxed(mvebu_gpioreg_in_pol(mvchip));
|
|
|
- polarity ^= 1 << i;
|
|
|
+ polarity ^= BIT(i);
|
|
|
writel_relaxed(polarity, mvebu_gpioreg_in_pol(mvchip));
|
|
|
}
|
|
|
|
|
@@ -480,6 +534,246 @@ static void mvebu_gpio_irq_handler(struct irq_desc *desc)
|
|
|
chained_irq_exit(chip, desc);
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * Functions implementing the pwm_chip methods
|
|
|
+ */
|
|
|
+static struct mvebu_pwm *to_mvebu_pwm(struct pwm_chip *chip)
|
|
|
+{
|
|
|
+ return container_of(chip, struct mvebu_pwm, chip);
|
|
|
+}
|
|
|
+
|
|
|
+static int mvebu_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
|
|
|
+{
|
|
|
+ struct mvebu_pwm *mvpwm = to_mvebu_pwm(chip);
|
|
|
+ struct mvebu_gpio_chip *mvchip = mvpwm->mvchip;
|
|
|
+ struct gpio_desc *desc;
|
|
|
+ unsigned long flags;
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&mvpwm->lock, flags);
|
|
|
+
|
|
|
+ if (mvpwm->gpiod) {
|
|
|
+ ret = -EBUSY;
|
|
|
+ } else {
|
|
|
+ desc = gpio_to_desc(mvchip->chip.base + pwm->hwpwm);
|
|
|
+ if (!desc) {
|
|
|
+ ret = -ENODEV;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = gpiod_request(desc, "mvebu-pwm");
|
|
|
+ if (ret)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ ret = gpiod_direction_output(desc, 0);
|
|
|
+ if (ret) {
|
|
|
+ gpiod_free(desc);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ mvpwm->gpiod = desc;
|
|
|
+ }
|
|
|
+out:
|
|
|
+ spin_unlock_irqrestore(&mvpwm->lock, flags);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static void mvebu_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
|
|
|
+{
|
|
|
+ struct mvebu_pwm *mvpwm = to_mvebu_pwm(chip);
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&mvpwm->lock, flags);
|
|
|
+ gpiod_free(mvpwm->gpiod);
|
|
|
+ mvpwm->gpiod = NULL;
|
|
|
+ spin_unlock_irqrestore(&mvpwm->lock, flags);
|
|
|
+}
|
|
|
+
|
|
|
+static void mvebu_pwm_get_state(struct pwm_chip *chip,
|
|
|
+ struct pwm_device *pwm,
|
|
|
+ struct pwm_state *state) {
|
|
|
+
|
|
|
+ struct mvebu_pwm *mvpwm = to_mvebu_pwm(chip);
|
|
|
+ struct mvebu_gpio_chip *mvchip = mvpwm->mvchip;
|
|
|
+ unsigned long long val;
|
|
|
+ unsigned long flags;
|
|
|
+ u32 u;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&mvpwm->lock, flags);
|
|
|
+
|
|
|
+ val = (unsigned long long)
|
|
|
+ readl_relaxed(mvebu_pwmreg_blink_on_duration(mvpwm));
|
|
|
+ val *= NSEC_PER_SEC;
|
|
|
+ do_div(val, mvpwm->clk_rate);
|
|
|
+ if (val > UINT_MAX)
|
|
|
+ state->duty_cycle = UINT_MAX;
|
|
|
+ else if (val)
|
|
|
+ state->duty_cycle = val;
|
|
|
+ else
|
|
|
+ state->duty_cycle = 1;
|
|
|
+
|
|
|
+ val = (unsigned long long)
|
|
|
+ readl_relaxed(mvebu_pwmreg_blink_off_duration(mvpwm));
|
|
|
+ val *= NSEC_PER_SEC;
|
|
|
+ do_div(val, mvpwm->clk_rate);
|
|
|
+ if (val < state->duty_cycle) {
|
|
|
+ state->period = 1;
|
|
|
+ } else {
|
|
|
+ val -= state->duty_cycle;
|
|
|
+ if (val > UINT_MAX)
|
|
|
+ state->period = UINT_MAX;
|
|
|
+ else if (val)
|
|
|
+ state->period = val;
|
|
|
+ else
|
|
|
+ state->period = 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ u = readl_relaxed(mvebu_gpioreg_blink(mvchip));
|
|
|
+ if (u)
|
|
|
+ state->enabled = true;
|
|
|
+ else
|
|
|
+ state->enabled = false;
|
|
|
+
|
|
|
+ spin_unlock_irqrestore(&mvpwm->lock, flags);
|
|
|
+}
|
|
|
+
|
|
|
+static int mvebu_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
|
|
+ struct pwm_state *state)
|
|
|
+{
|
|
|
+ struct mvebu_pwm *mvpwm = to_mvebu_pwm(chip);
|
|
|
+ struct mvebu_gpio_chip *mvchip = mvpwm->mvchip;
|
|
|
+ unsigned long long val;
|
|
|
+ unsigned long flags;
|
|
|
+ unsigned int on, off;
|
|
|
+
|
|
|
+ val = (unsigned long long) mvpwm->clk_rate * state->duty_cycle;
|
|
|
+ do_div(val, NSEC_PER_SEC);
|
|
|
+ if (val > UINT_MAX)
|
|
|
+ return -EINVAL;
|
|
|
+ if (val)
|
|
|
+ on = val;
|
|
|
+ else
|
|
|
+ on = 1;
|
|
|
+
|
|
|
+ val = (unsigned long long) mvpwm->clk_rate *
|
|
|
+ (state->period - state->duty_cycle);
|
|
|
+ do_div(val, NSEC_PER_SEC);
|
|
|
+ if (val > UINT_MAX)
|
|
|
+ return -EINVAL;
|
|
|
+ if (val)
|
|
|
+ off = val;
|
|
|
+ else
|
|
|
+ off = 1;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&mvpwm->lock, flags);
|
|
|
+
|
|
|
+ writel_relaxed(on, mvebu_pwmreg_blink_on_duration(mvpwm));
|
|
|
+ writel_relaxed(off, mvebu_pwmreg_blink_off_duration(mvpwm));
|
|
|
+ if (state->enabled)
|
|
|
+ mvebu_gpio_blink(&mvchip->chip, pwm->hwpwm, 1);
|
|
|
+ else
|
|
|
+ mvebu_gpio_blink(&mvchip->chip, pwm->hwpwm, 0);
|
|
|
+
|
|
|
+ spin_unlock_irqrestore(&mvpwm->lock, flags);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static const struct pwm_ops mvebu_pwm_ops = {
|
|
|
+ .request = mvebu_pwm_request,
|
|
|
+ .free = mvebu_pwm_free,
|
|
|
+ .get_state = mvebu_pwm_get_state,
|
|
|
+ .apply = mvebu_pwm_apply,
|
|
|
+ .owner = THIS_MODULE,
|
|
|
+};
|
|
|
+
|
|
|
+static void __maybe_unused mvebu_pwm_suspend(struct mvebu_gpio_chip *mvchip)
|
|
|
+{
|
|
|
+ struct mvebu_pwm *mvpwm = mvchip->mvpwm;
|
|
|
+
|
|
|
+ mvpwm->blink_select =
|
|
|
+ readl_relaxed(mvebu_gpioreg_blink_counter_select(mvchip));
|
|
|
+ mvpwm->blink_on_duration =
|
|
|
+ readl_relaxed(mvebu_pwmreg_blink_on_duration(mvpwm));
|
|
|
+ mvpwm->blink_off_duration =
|
|
|
+ readl_relaxed(mvebu_pwmreg_blink_off_duration(mvpwm));
|
|
|
+}
|
|
|
+
|
|
|
+static void __maybe_unused mvebu_pwm_resume(struct mvebu_gpio_chip *mvchip)
|
|
|
+{
|
|
|
+ struct mvebu_pwm *mvpwm = mvchip->mvpwm;
|
|
|
+
|
|
|
+ writel_relaxed(mvpwm->blink_select,
|
|
|
+ mvebu_gpioreg_blink_counter_select(mvchip));
|
|
|
+ writel_relaxed(mvpwm->blink_on_duration,
|
|
|
+ mvebu_pwmreg_blink_on_duration(mvpwm));
|
|
|
+ writel_relaxed(mvpwm->blink_off_duration,
|
|
|
+ mvebu_pwmreg_blink_off_duration(mvpwm));
|
|
|
+}
|
|
|
+
|
|
|
+static int mvebu_pwm_probe(struct platform_device *pdev,
|
|
|
+ struct mvebu_gpio_chip *mvchip,
|
|
|
+ int id)
|
|
|
+{
|
|
|
+ struct device *dev = &pdev->dev;
|
|
|
+ struct mvebu_pwm *mvpwm;
|
|
|
+ struct resource *res;
|
|
|
+ u32 set;
|
|
|
+
|
|
|
+ if (!of_device_is_compatible(mvchip->chip.of_node,
|
|
|
+ "marvell,armada-370-xp-gpio"))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ if (IS_ERR(mvchip->clk))
|
|
|
+ return PTR_ERR(mvchip->clk);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * There are only two sets of PWM configuration registers for
|
|
|
+ * all the GPIO lines on those SoCs which this driver reserves
|
|
|
+ * for the first two GPIO chips. So if the resource is missing
|
|
|
+ * we can't treat it as an error.
|
|
|
+ */
|
|
|
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pwm");
|
|
|
+ if (!res)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Use set A for lines of GPIO chip with id 0, B for GPIO chip
|
|
|
+ * with id 1. Don't allow further GPIO chips to be used for PWM.
|
|
|
+ */
|
|
|
+ if (id == 0)
|
|
|
+ set = 0;
|
|
|
+ else if (id == 1)
|
|
|
+ set = U32_MAX;
|
|
|
+ else
|
|
|
+ return -EINVAL;
|
|
|
+ writel_relaxed(0, mvebu_gpioreg_blink_counter_select(mvchip));
|
|
|
+
|
|
|
+ mvpwm = devm_kzalloc(dev, sizeof(struct mvebu_pwm), GFP_KERNEL);
|
|
|
+ if (!mvpwm)
|
|
|
+ return -ENOMEM;
|
|
|
+ mvchip->mvpwm = mvpwm;
|
|
|
+ mvpwm->mvchip = mvchip;
|
|
|
+
|
|
|
+ mvpwm->membase = devm_ioremap_resource(dev, res);
|
|
|
+ if (IS_ERR(mvpwm->membase))
|
|
|
+ return PTR_ERR(mvpwm->membase);
|
|
|
+
|
|
|
+ mvpwm->clk_rate = clk_get_rate(mvchip->clk);
|
|
|
+ if (!mvpwm->clk_rate) {
|
|
|
+ dev_err(dev, "failed to get clock rate\n");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ mvpwm->chip.dev = dev;
|
|
|
+ mvpwm->chip.ops = &mvebu_pwm_ops;
|
|
|
+ mvpwm->chip.npwm = mvchip->chip.ngpio;
|
|
|
+
|
|
|
+ spin_lock_init(&mvpwm->lock);
|
|
|
+
|
|
|
+ return pwmchip_add(&mvpwm->chip);
|
|
|
+}
|
|
|
+
|
|
|
#ifdef CONFIG_DEBUG_FS
|
|
|
#include <linux/seq_file.h>
|
|
|
|
|
@@ -507,7 +801,7 @@ static void mvebu_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
|
|
|
if (!label)
|
|
|
continue;
|
|
|
|
|
|
- msk = 1 << i;
|
|
|
+ msk = BIT(i);
|
|
|
is_out = !(io_conf & msk);
|
|
|
|
|
|
seq_printf(s, " gpio-%-3d (%-20.20s)", chip->base + i, label);
|
|
@@ -550,6 +844,10 @@ static const struct of_device_id mvebu_gpio_of_match[] = {
|
|
|
.compatible = "marvell,armadaxp-gpio",
|
|
|
.data = (void *) MVEBU_GPIO_SOC_VARIANT_ARMADAXP,
|
|
|
},
|
|
|
+ {
|
|
|
+ .compatible = "marvell,armada-370-xp-gpio",
|
|
|
+ .data = (void *) MVEBU_GPIO_SOC_VARIANT_ORION,
|
|
|
+ },
|
|
|
{
|
|
|
/* sentinel */
|
|
|
},
|
|
@@ -596,6 +894,9 @@ static int mvebu_gpio_suspend(struct platform_device *pdev, pm_message_t state)
|
|
|
BUG();
|
|
|
}
|
|
|
|
|
|
+ if (IS_ENABLED(CONFIG_PWM))
|
|
|
+ mvebu_pwm_suspend(mvchip);
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -639,6 +940,9 @@ static int mvebu_gpio_resume(struct platform_device *pdev)
|
|
|
BUG();
|
|
|
}
|
|
|
|
|
|
+ if (IS_ENABLED(CONFIG_PWM))
|
|
|
+ mvebu_pwm_resume(mvchip);
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -650,7 +954,6 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
|
|
|
struct resource *res;
|
|
|
struct irq_chip_generic *gc;
|
|
|
struct irq_chip_type *ct;
|
|
|
- struct clk *clk;
|
|
|
unsigned int ngpios;
|
|
|
bool have_irqs;
|
|
|
int soc_variant;
|
|
@@ -684,10 +987,10 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
|
|
|
return id;
|
|
|
}
|
|
|
|
|
|
- clk = devm_clk_get(&pdev->dev, NULL);
|
|
|
+ mvchip->clk = devm_clk_get(&pdev->dev, NULL);
|
|
|
/* Not all SoCs require a clock.*/
|
|
|
- if (!IS_ERR(clk))
|
|
|
- clk_prepare_enable(clk);
|
|
|
+ if (!IS_ERR(mvchip->clk))
|
|
|
+ clk_prepare_enable(mvchip->clk);
|
|
|
|
|
|
mvchip->soc_variant = soc_variant;
|
|
|
mvchip->chip.label = dev_name(&pdev->dev);
|
|
@@ -712,8 +1015,10 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
|
|
|
if (IS_ERR(mvchip->membase))
|
|
|
return PTR_ERR(mvchip->membase);
|
|
|
|
|
|
- /* The Armada XP has a second range of registers for the
|
|
|
- * per-CPU registers */
|
|
|
+ /*
|
|
|
+ * The Armada XP has a second range of registers for the
|
|
|
+ * per-CPU registers
|
|
|
+ */
|
|
|
if (soc_variant == MVEBU_GPIO_SOC_VARIANT_ARMADAXP) {
|
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
|
|
mvchip->percpu_membase = devm_ioremap_resource(&pdev->dev,
|
|
@@ -780,7 +1085,8 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
|
|
|
goto err_domain;
|
|
|
}
|
|
|
|
|
|
- /* NOTE: The common accessors cannot be used because of the percpu
|
|
|
+ /*
|
|
|
+ * NOTE: The common accessors cannot be used because of the percpu
|
|
|
* access to the mask registers
|
|
|
*/
|
|
|
gc = irq_get_domain_generic_chip(mvchip->domain, 0);
|
|
@@ -801,7 +1107,8 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
|
|
|
ct->handler = handle_edge_irq;
|
|
|
ct->chip.name = mvchip->chip.label;
|
|
|
|
|
|
- /* Setup the interrupt handlers. Each chip can have up to 4
|
|
|
+ /*
|
|
|
+ * Setup the interrupt handlers. Each chip can have up to 4
|
|
|
* interrupt handlers, with each handler dealing with 8 GPIO
|
|
|
* pins.
|
|
|
*/
|
|
@@ -814,6 +1121,10 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
|
|
|
mvchip);
|
|
|
}
|
|
|
|
|
|
+ /* Armada 370/XP has simple PWM support for GPIO lines */
|
|
|
+ if (IS_ENABLED(CONFIG_PWM))
|
|
|
+ return mvebu_pwm_probe(pdev, mvchip, id);
|
|
|
+
|
|
|
return 0;
|
|
|
|
|
|
err_domain:
|