-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
6c7d5e9
commit db7fe3f
Showing
3 changed files
with
697 additions
and
128 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,346 @@ | ||
diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c | ||
index adc768f908f1..a2bd264ee92c 100644 | ||
--- a/drivers/gpio/gpio-mvebu.c | ||
+++ b/drivers/gpio/gpio-mvebu.c | ||
@@ -92,20 +92,41 @@ | ||
|
||
#define MVEBU_MAX_GPIO_PER_BANK 32 | ||
|
||
-struct mvebu_pwm { | ||
+enum mvebu_pwm_ctrl { | ||
+ MVEBU_PWM_CTRL_SET_A = 0, | ||
+ MVEBU_PWM_CTRL_SET_B, | ||
+ MVEBU_PWM_CTRL_MAX | ||
+}; | ||
+ | ||
+struct mvebu_pwmchip { | ||
void __iomem *membase; | ||
unsigned long clk_rate; | ||
+ spinlock_t lock; | ||
+ bool in_use; | ||
+ | ||
+ /* Used to preserve GPIO/PWM registers across suspend/resume */ | ||
+ u32 blink_on_duration; | ||
+ u32 blink_off_duration; | ||
+}; | ||
+ | ||
+struct mvebu_pwm_chip_drv { | ||
+ enum mvebu_pwm_ctrl ctrl; | ||
struct gpio_desc *gpiod; | ||
+ bool master; | ||
+}; | ||
+ | ||
+struct mvebu_pwm { | ||
struct pwm_chip chip; | ||
- spinlock_t lock; | ||
struct mvebu_gpio_chip *mvchip; | ||
+ struct mvebu_pwmchip controller; | ||
+ enum mvebu_pwm_ctrl default_counter; | ||
|
||
/* Used to preserve GPIO/PWM registers across suspend/resume */ | ||
u32 blink_select; | ||
- u32 blink_on_duration; | ||
- u32 blink_off_duration; | ||
}; | ||
|
||
+static struct mvebu_pwmchip *mvebu_pwm_list[MVEBU_PWM_CTRL_MAX]; | ||
+ | ||
struct mvebu_gpio_chip { | ||
struct gpio_chip chip; | ||
struct regmap *regs; | ||
@@ -282,12 +303,12 @@ mvebu_gpio_write_level_mask(struct mvebu_gpio_chip *mvchip, u32 val) | ||
* Functions returning addresses of individual registers for a given | ||
* PWM controller. | ||
*/ | ||
-static void __iomem *mvebu_pwmreg_blink_on_duration(struct mvebu_pwm *mvpwm) | ||
+static void __iomem *mvebu_pwmreg_blink_on_duration(struct mvebu_pwmchip *mvpwm) | ||
{ | ||
return mvpwm->membase + PWM_BLINK_ON_DURATION_OFF; | ||
} | ||
|
||
-static void __iomem *mvebu_pwmreg_blink_off_duration(struct mvebu_pwm *mvpwm) | ||
+static void __iomem *mvebu_pwmreg_blink_off_duration(struct mvebu_pwmchip *mvpwm) | ||
{ | ||
return mvpwm->membase + PWM_BLINK_OFF_DURATION_OFF; | ||
} | ||
@@ -599,43 +620,76 @@ 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; | ||
+ enum mvebu_pwm_ctrl id; | ||
unsigned long flags; | ||
int ret = 0; | ||
+ struct mvebu_pwm_chip_drv *chip_data; | ||
|
||
- spin_lock_irqsave(&mvpwm->lock, flags); | ||
+ spin_lock_irqsave(&mvpwm->controller.lock, flags); | ||
|
||
- if (mvpwm->gpiod) { | ||
- ret = -EBUSY; | ||
- } else { | ||
- desc = gpiochip_request_own_desc(&mvchip->chip, | ||
- pwm->hwpwm, "mvebu-pwm", 0); | ||
- if (IS_ERR(desc)) { | ||
- ret = PTR_ERR(desc); | ||
- goto out; | ||
- } | ||
+ regmap_read(mvchip->regs, GPIO_BLINK_EN_OFF + mvchip->offset, | ||
+ &mvchip->blink_en_reg); | ||
+ if (pwm->chip_data || (mvchip->blink_en_reg & BIT(pwm->hwpwm))) | ||
+ return -EBUSY; | ||
|
||
- ret = gpiod_direction_output(desc, 0); | ||
- if (ret) { | ||
- gpiochip_free_own_desc(desc); | ||
- goto out; | ||
- } | ||
+ desc = gpiochip_request_own_desc(&mvchip->chip, pwm->hwpwm, "mvebu-pwm", 0); | ||
+ if (IS_ERR(desc)) { | ||
+ ret = PTR_ERR(desc); | ||
+ goto out; | ||
+ } | ||
+ | ||
+ ret = gpiod_direction_output(desc, 0); | ||
+ if (ret) { | ||
+ gpiochip_free_own_desc(desc); | ||
+ goto out; | ||
+ } | ||
+ | ||
+ chip_data = kzalloc(sizeof(struct mvebu_pwm_chip_drv), GFP_KERNEL); | ||
+ if (!chip_data) { | ||
+ gpiochip_free_own_desc(desc); | ||
+ ret = -ENOMEM; | ||
+ goto out; | ||
+ } | ||
|
||
- mvpwm->gpiod = desc; | ||
+ for (id = MVEBU_PWM_CTRL_SET_A;id < MVEBU_PWM_CTRL_MAX; id++) { | ||
+ if (!mvebu_pwm_list[id]->in_use) { | ||
+ chip_data->ctrl = id; | ||
+ chip_data->master = true; | ||
+ mvebu_pwm_list[id]->in_use = true; | ||
+ break; | ||
+ } | ||
} | ||
+ | ||
+ if (!chip_data->master) | ||
+ chip_data->ctrl = mvpwm->default_counter; | ||
+ | ||
+ regmap_update_bits(mvchip->regs, GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset, | ||
+ BIT(pwm->hwpwm), chip_data->ctrl ? BIT(pwm->hwpwm) : 0); | ||
+ | ||
+ chip_data->gpiod = desc; | ||
+ pwm->chip_data = chip_data; | ||
+ | ||
+ regmap_read(mvchip->regs, GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset, | ||
+ &mvpwm->blink_select); | ||
out: | ||
- spin_unlock_irqrestore(&mvpwm->lock, flags); | ||
+ spin_unlock_irqrestore(&mvpwm->controller.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); | ||
+ struct mvebu_pwm_chip_drv *chip_data = (struct mvebu_pwm_chip_drv*) pwm->chip_data; | ||
unsigned long flags; | ||
|
||
- spin_lock_irqsave(&mvpwm->lock, flags); | ||
- gpiochip_free_own_desc(mvpwm->gpiod); | ||
- mvpwm->gpiod = NULL; | ||
- spin_unlock_irqrestore(&mvpwm->lock, flags); | ||
+ spin_lock_irqsave(&mvpwm->controller.lock, flags); | ||
+ if (chip_data->master) | ||
+ mvebu_pwm_list[chip_data->ctrl]->in_use = false; | ||
+ | ||
+ gpiochip_free_own_desc(chip_data->gpiod); | ||
+ kfree(chip_data); | ||
+ pwm->chip_data = NULL; | ||
+ spin_unlock_irqrestore(&mvpwm->controller.lock, flags); | ||
} | ||
|
||
static void mvebu_pwm_get_state(struct pwm_chip *chip, | ||
@@ -643,17 +697,24 @@ static void mvebu_pwm_get_state(struct pwm_chip *chip, | ||
struct pwm_state *state) { | ||
|
||
struct mvebu_pwm *mvpwm = to_mvebu_pwm(chip); | ||
+ struct mvebu_pwm_chip_drv *chip_data = (struct mvebu_pwm_chip_drv*) pwm->chip_data; | ||
+ struct mvebu_pwmchip *controller; | ||
struct mvebu_gpio_chip *mvchip = mvpwm->mvchip; | ||
unsigned long long val; | ||
unsigned long flags; | ||
u32 u; | ||
|
||
- spin_lock_irqsave(&mvpwm->lock, flags); | ||
+ if (chip_data) | ||
+ controller = mvebu_pwm_list[chip_data->ctrl]; | ||
+ else | ||
+ controller = &mvpwm->controller; | ||
+ | ||
+ spin_lock_irqsave(&controller->lock, flags); | ||
|
||
val = (unsigned long long) | ||
- readl_relaxed(mvebu_pwmreg_blink_on_duration(mvpwm)); | ||
+ readl_relaxed(mvebu_pwmreg_blink_on_duration(controller)); | ||
val *= NSEC_PER_SEC; | ||
- do_div(val, mvpwm->clk_rate); | ||
+ do_div(val, controller->clk_rate); | ||
if (val > UINT_MAX) | ||
state->duty_cycle = UINT_MAX; | ||
else if (val) | ||
@@ -662,9 +723,9 @@ static void mvebu_pwm_get_state(struct pwm_chip *chip, | ||
state->duty_cycle = 1; | ||
|
||
val = (unsigned long long) | ||
- readl_relaxed(mvebu_pwmreg_blink_off_duration(mvpwm)); | ||
+ readl_relaxed(mvebu_pwmreg_blink_off_duration(controller)); | ||
val *= NSEC_PER_SEC; | ||
- do_div(val, mvpwm->clk_rate); | ||
+ do_div(val, controller->clk_rate); | ||
if (val < state->duty_cycle) { | ||
state->period = 1; | ||
} else { | ||
@@ -683,19 +744,21 @@ static void mvebu_pwm_get_state(struct pwm_chip *chip, | ||
else | ||
state->enabled = false; | ||
|
||
- spin_unlock_irqrestore(&mvpwm->lock, flags); | ||
+ spin_unlock_irqrestore(&controller->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_pwm_chip_drv *chip_data = (struct mvebu_pwm_chip_drv*) pwm->chip_data; | ||
+ struct mvebu_pwmchip *controller = mvebu_pwm_list[chip_data->ctrl]; | ||
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; | ||
+ val = (unsigned long long) controller->clk_rate * state->duty_cycle; | ||
do_div(val, NSEC_PER_SEC); | ||
if (val > UINT_MAX) | ||
return -EINVAL; | ||
@@ -704,7 +767,7 @@ static int mvebu_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, | ||
else | ||
on = 1; | ||
|
||
- val = (unsigned long long) mvpwm->clk_rate * | ||
+ val = (unsigned long long) controller->clk_rate * | ||
(state->period - state->duty_cycle); | ||
do_div(val, NSEC_PER_SEC); | ||
if (val > UINT_MAX) | ||
@@ -714,16 +777,16 @@ static int mvebu_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, | ||
else | ||
off = 1; | ||
|
||
- spin_lock_irqsave(&mvpwm->lock, flags); | ||
+ spin_lock_irqsave(&controller->lock, flags); | ||
|
||
- writel_relaxed(on, mvebu_pwmreg_blink_on_duration(mvpwm)); | ||
- writel_relaxed(off, mvebu_pwmreg_blink_off_duration(mvpwm)); | ||
+ writel_relaxed(on, mvebu_pwmreg_blink_on_duration(controller)); | ||
+ writel_relaxed(off, mvebu_pwmreg_blink_off_duration(controller)); | ||
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); | ||
+ spin_unlock_irqrestore(&controller->lock, flags); | ||
|
||
return 0; | ||
} | ||
@@ -742,10 +805,10 @@ static void __maybe_unused mvebu_pwm_suspend(struct mvebu_gpio_chip *mvchip) | ||
|
||
regmap_read(mvchip->regs, GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset, | ||
&mvpwm->blink_select); | ||
- mvpwm->blink_on_duration = | ||
- readl_relaxed(mvebu_pwmreg_blink_on_duration(mvpwm)); | ||
- mvpwm->blink_off_duration = | ||
- readl_relaxed(mvebu_pwmreg_blink_off_duration(mvpwm)); | ||
+ mvpwm->controller.blink_on_duration = | ||
+ readl_relaxed(mvebu_pwmreg_blink_on_duration(&mvpwm->controller)); | ||
+ mvpwm->controller.blink_off_duration = | ||
+ readl_relaxed(mvebu_pwmreg_blink_off_duration(&mvpwm->controller)); | ||
} | ||
|
||
static void __maybe_unused mvebu_pwm_resume(struct mvebu_gpio_chip *mvchip) | ||
@@ -754,10 +817,10 @@ static void __maybe_unused mvebu_pwm_resume(struct mvebu_gpio_chip *mvchip) | ||
|
||
regmap_write(mvchip->regs, GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset, | ||
mvpwm->blink_select); | ||
- writel_relaxed(mvpwm->blink_on_duration, | ||
- mvebu_pwmreg_blink_on_duration(mvpwm)); | ||
- writel_relaxed(mvpwm->blink_off_duration, | ||
- mvebu_pwmreg_blink_off_duration(mvpwm)); | ||
+ writel_relaxed(mvpwm->controller.blink_on_duration, | ||
+ mvebu_pwmreg_blink_on_duration(&mvpwm->controller)); | ||
+ writel_relaxed(mvpwm->controller.blink_off_duration, | ||
+ mvebu_pwmreg_blink_off_duration(&mvpwm->controller)); | ||
} | ||
|
||
static int mvebu_pwm_probe(struct platform_device *pdev, | ||
@@ -768,6 +831,7 @@ static int mvebu_pwm_probe(struct platform_device *pdev, | ||
struct mvebu_pwm *mvpwm; | ||
struct resource *res; | ||
u32 set; | ||
+ enum mvebu_pwm_ctrl ctrl_set; | ||
|
||
if (!of_device_is_compatible(mvchip->chip.of_node, | ||
"marvell,armada-370-gpio")) | ||
@@ -790,12 +854,15 @@ static int mvebu_pwm_probe(struct platform_device *pdev, | ||
* 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) | ||
+ if (id == 0) { | ||
set = 0; | ||
- else if (id == 1) | ||
+ ctrl_set = MVEBU_PWM_CTRL_SET_A; | ||
+ } else if (id == 1) { | ||
set = U32_MAX; | ||
- else | ||
+ ctrl_set = MVEBU_PWM_CTRL_SET_B; | ||
+ } else { | ||
return -EINVAL; | ||
+ } | ||
regmap_write(mvchip->regs, | ||
GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset, set); | ||
|
||
@@ -805,15 +872,13 @@ static int mvebu_pwm_probe(struct platform_device *pdev, | ||
mvchip->mvpwm = mvpwm; | ||
mvpwm->mvchip = mvchip; | ||
|
||
- mvpwm->membase = devm_ioremap_resource(dev, res); | ||
- if (IS_ERR(mvpwm->membase)) | ||
- return PTR_ERR(mvpwm->membase); | ||
+ mvpwm->controller.membase = devm_ioremap_resource(dev, res); | ||
+ if (IS_ERR(mvpwm->controller.membase)) | ||
+ return PTR_ERR(mvpwm->controller.membase); | ||
|
||
- mvpwm->clk_rate = clk_get_rate(mvchip->clk); | ||
- if (!mvpwm->clk_rate) { | ||
- dev_err(dev, "failed to get clock rate\n"); | ||
+ mvpwm->controller.clk_rate = clk_get_rate(mvchip->clk); | ||
+ if (!mvpwm->controller.clk_rate) | ||
return -EINVAL; | ||
- } | ||
|
||
mvpwm->chip.dev = dev; | ||
mvpwm->chip.ops = &mvebu_pwm_ops; | ||
@@ -826,7 +891,9 @@ static int mvebu_pwm_probe(struct platform_device *pdev, | ||
*/ | ||
mvpwm->chip.base = -1; | ||
|
||
- spin_lock_init(&mvpwm->lock); | ||
+ spin_lock_init(&mvpwm->controller.lock); | ||
+ mvpwm->default_counter = ctrl_set; | ||
+ mvebu_pwm_list[ctrl_set] = &mvpwm->controller; | ||
|
||
return pwmchip_add(&mvpwm->chip); | ||
} |
Oops, something went wrong.