summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/pwm/Kconfig10
-rw-r--r--drivers/pwm/Makefile1
-rw-r--r--drivers/pwm/pwm-iqs620a.c94
-rw-r--r--drivers/pwm/pwm-lpc18xx-sct.c2
-rw-r--r--drivers/pwm/pwm-rockchip.c32
-rw-r--r--drivers/pwm/pwm-zx.c278
6 files changed, 65 insertions, 352 deletions
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 0937e1c047ac..9a4f66ae8070 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -611,14 +611,4 @@ config PWM_VT8500
To compile this driver as a module, choose M here: the module
will be called pwm-vt8500.
-config PWM_ZX
- tristate "ZTE ZX PWM support"
- depends on ARCH_ZX || COMPILE_TEST
- depends on HAS_IOMEM
- help
- Generic PWM framework driver for ZTE ZX family SoCs.
-
- To compile this driver as a module, choose M here: the module
- will be called pwm-zx.
-
endif
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 18b89d7fd092..6374d3b1d6f3 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -57,4 +57,3 @@ obj-$(CONFIG_PWM_TIEHRPWM) += pwm-tiehrpwm.o
obj-$(CONFIG_PWM_TWL) += pwm-twl.o
obj-$(CONFIG_PWM_TWL_LED) += pwm-twl-led.o
obj-$(CONFIG_PWM_VT8500) += pwm-vt8500.o
-obj-$(CONFIG_PWM_ZX) += pwm-zx.o
diff --git a/drivers/pwm/pwm-iqs620a.c b/drivers/pwm/pwm-iqs620a.c
index 5ede8255926e..957b972c458b 100644
--- a/drivers/pwm/pwm-iqs620a.c
+++ b/drivers/pwm/pwm-iqs620a.c
@@ -37,16 +37,34 @@ struct iqs620_pwm_private {
struct pwm_chip chip;
struct notifier_block notifier;
struct mutex lock;
- bool out_en;
- u8 duty_val;
+ unsigned int duty_scale;
};
+static int iqs620_pwm_init(struct iqs620_pwm_private *iqs620_pwm,
+ unsigned int duty_scale)
+{
+ struct iqs62x_core *iqs62x = iqs620_pwm->iqs62x;
+ int ret;
+
+ if (!duty_scale)
+ return regmap_update_bits(iqs62x->regmap, IQS620_PWR_SETTINGS,
+ IQS620_PWR_SETTINGS_PWM_OUT, 0);
+
+ ret = regmap_write(iqs62x->regmap, IQS620_PWM_DUTY_CYCLE,
+ duty_scale - 1);
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(iqs62x->regmap, IQS620_PWR_SETTINGS,
+ IQS620_PWR_SETTINGS_PWM_OUT, 0xff);
+}
+
static int iqs620_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
const struct pwm_state *state)
{
struct iqs620_pwm_private *iqs620_pwm;
- struct iqs62x_core *iqs62x;
- u64 duty_scale;
+ unsigned int duty_cycle;
+ unsigned int duty_scale;
int ret;
if (state->polarity != PWM_POLARITY_NORMAL)
@@ -56,7 +74,6 @@ static int iqs620_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
return -EINVAL;
iqs620_pwm = container_of(chip, struct iqs620_pwm_private, chip);
- iqs62x = iqs620_pwm->iqs62x;
/*
* The duty cycle generated by the device is calculated as follows:
@@ -70,38 +87,18 @@ static int iqs620_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
* For lower duty cycles (e.g. 0), the PWM output is simply disabled to
* allow an external pull-down resistor to hold the GPIO3/LTX pin low.
*/
- duty_scale = div_u64(state->duty_cycle * 256, IQS620_PWM_PERIOD_NS);
-
- mutex_lock(&iqs620_pwm->lock);
-
- if (!state->enabled || !duty_scale) {
- ret = regmap_update_bits(iqs62x->regmap, IQS620_PWR_SETTINGS,
- IQS620_PWR_SETTINGS_PWM_OUT, 0);
- if (ret)
- goto err_mutex;
- }
+ duty_cycle = min_t(u64, state->duty_cycle, IQS620_PWM_PERIOD_NS);
+ duty_scale = duty_cycle * 256 / IQS620_PWM_PERIOD_NS;
- if (duty_scale) {
- u8 duty_val = min_t(u64, duty_scale - 1, 0xff);
+ if (!state->enabled)
+ duty_scale = 0;
- ret = regmap_write(iqs62x->regmap, IQS620_PWM_DUTY_CYCLE,
- duty_val);
- if (ret)
- goto err_mutex;
-
- iqs620_pwm->duty_val = duty_val;
- }
-
- if (state->enabled && duty_scale) {
- ret = regmap_update_bits(iqs62x->regmap, IQS620_PWR_SETTINGS,
- IQS620_PWR_SETTINGS_PWM_OUT, 0xff);
- if (ret)
- goto err_mutex;
- }
+ mutex_lock(&iqs620_pwm->lock);
- iqs620_pwm->out_en = state->enabled;
+ ret = iqs620_pwm_init(iqs620_pwm, duty_scale);
+ if (!ret)
+ iqs620_pwm->duty_scale = duty_scale;
-err_mutex:
mutex_unlock(&iqs620_pwm->lock);
return ret;
@@ -119,12 +116,11 @@ static void iqs620_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
/*
* Since the device cannot generate a 0% duty cycle, requests to do so
* cause subsequent calls to iqs620_pwm_get_state to report the output
- * as disabled with duty cycle equal to that which was in use prior to
- * the request. This is not ideal, but is the best compromise based on
+ * as disabled. This is not ideal, but is the best compromise based on
* the capabilities of the device.
*/
- state->enabled = iqs620_pwm->out_en;
- state->duty_cycle = DIV_ROUND_UP((iqs620_pwm->duty_val + 1) *
+ state->enabled = iqs620_pwm->duty_scale > 0;
+ state->duty_cycle = DIV_ROUND_UP(iqs620_pwm->duty_scale *
IQS620_PWM_PERIOD_NS, 256);
mutex_unlock(&iqs620_pwm->lock);
@@ -136,7 +132,6 @@ static int iqs620_pwm_notifier(struct notifier_block *notifier,
unsigned long event_flags, void *context)
{
struct iqs620_pwm_private *iqs620_pwm;
- struct iqs62x_core *iqs62x;
int ret;
if (!(event_flags & BIT(IQS62X_EVENT_SYS_RESET)))
@@ -144,7 +139,6 @@ static int iqs620_pwm_notifier(struct notifier_block *notifier,
iqs620_pwm = container_of(notifier, struct iqs620_pwm_private,
notifier);
- iqs62x = iqs620_pwm->iqs62x;
mutex_lock(&iqs620_pwm->lock);
@@ -153,16 +147,8 @@ static int iqs620_pwm_notifier(struct notifier_block *notifier,
* of a device reset, so nothing else is printed here unless there is
* an additional failure.
*/
- ret = regmap_write(iqs62x->regmap, IQS620_PWM_DUTY_CYCLE,
- iqs620_pwm->duty_val);
- if (ret)
- goto err_mutex;
+ ret = iqs620_pwm_init(iqs620_pwm, iqs620_pwm->duty_scale);
- ret = regmap_update_bits(iqs62x->regmap, IQS620_PWR_SETTINGS,
- IQS620_PWR_SETTINGS_PWM_OUT,
- iqs620_pwm->out_en ? 0xff : 0);
-
-err_mutex:
mutex_unlock(&iqs620_pwm->lock);
if (ret) {
@@ -209,12 +195,14 @@ static int iqs620_pwm_probe(struct platform_device *pdev)
ret = regmap_read(iqs62x->regmap, IQS620_PWR_SETTINGS, &val);
if (ret)
return ret;
- iqs620_pwm->out_en = val & IQS620_PWR_SETTINGS_PWM_OUT;
- ret = regmap_read(iqs62x->regmap, IQS620_PWM_DUTY_CYCLE, &val);
- if (ret)
- return ret;
- iqs620_pwm->duty_val = val;
+ if (val & IQS620_PWR_SETTINGS_PWM_OUT) {
+ ret = regmap_read(iqs62x->regmap, IQS620_PWM_DUTY_CYCLE, &val);
+ if (ret)
+ return ret;
+
+ iqs620_pwm->duty_scale = val + 1;
+ }
iqs620_pwm->chip.dev = &pdev->dev;
iqs620_pwm->chip.ops = &iqs620_pwm_ops;
diff --git a/drivers/pwm/pwm-lpc18xx-sct.c b/drivers/pwm/pwm-lpc18xx-sct.c
index dc5133bec3e7..7ef40243eb6c 100644
--- a/drivers/pwm/pwm-lpc18xx-sct.c
+++ b/drivers/pwm/pwm-lpc18xx-sct.c
@@ -289,7 +289,7 @@ static int lpc18xx_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
dev_err(lpc18xx_pwm->dev,
"maximum number of simultaneous channels reached\n");
return -EBUSY;
- };
+ }
set_bit(event, &lpc18xx_pwm->event_map);
lpc18xx_data->duty_event = event;
diff --git a/drivers/pwm/pwm-rockchip.c b/drivers/pwm/pwm-rockchip.c
index 389a5e140412..6ad7d0a50aed 100644
--- a/drivers/pwm/pwm-rockchip.c
+++ b/drivers/pwm/pwm-rockchip.c
@@ -72,6 +72,10 @@ static void rockchip_pwm_get_state(struct pwm_chip *chip,
if (ret)
return;
+ ret = clk_enable(pc->clk);
+ if (ret)
+ return;
+
clk_rate = clk_get_rate(pc->clk);
tmp = readl_relaxed(pc->base + pc->data->regs.period);
@@ -90,6 +94,7 @@ static void rockchip_pwm_get_state(struct pwm_chip *chip,
else
state->polarity = PWM_POLARITY_NORMAL;
+ clk_disable(pc->clk);
clk_disable(pc->pclk);
}
@@ -189,6 +194,10 @@ static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
if (ret)
return ret;
+ ret = clk_enable(pc->clk);
+ if (ret)
+ return ret;
+
pwm_get_state(pwm, &curstate);
enabled = curstate.enabled;
@@ -208,6 +217,7 @@ static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
}
out:
+ clk_disable(pc->clk);
clk_disable(pc->pclk);
return ret;
@@ -288,6 +298,7 @@ static int rockchip_pwm_probe(struct platform_device *pdev)
const struct of_device_id *id;
struct rockchip_pwm_chip *pc;
u32 enable_conf, ctrl;
+ bool enabled;
int ret, count;
id = of_match_device(rockchip_pwm_dt_ids, &pdev->dev);
@@ -307,7 +318,7 @@ static int rockchip_pwm_probe(struct platform_device *pdev)
pc->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(pc->clk))
return dev_err_probe(&pdev->dev, PTR_ERR(pc->clk),
- "Can't get bus clk\n");
+ "Can't get PWM clk\n");
}
count = of_count_phandle_with_args(pdev->dev.of_node,
@@ -326,13 +337,13 @@ static int rockchip_pwm_probe(struct platform_device *pdev)
ret = clk_prepare_enable(pc->clk);
if (ret) {
- dev_err(&pdev->dev, "Can't prepare enable bus clk: %d\n", ret);
+ dev_err(&pdev->dev, "Can't prepare enable PWM clk: %d\n", ret);
return ret;
}
- ret = clk_prepare(pc->pclk);
+ ret = clk_prepare_enable(pc->pclk);
if (ret) {
- dev_err(&pdev->dev, "Can't prepare APB clk: %d\n", ret);
+ dev_err(&pdev->dev, "Can't prepare enable APB clk: %d\n", ret);
goto err_clk;
}
@@ -349,23 +360,26 @@ static int rockchip_pwm_probe(struct platform_device *pdev)
pc->chip.of_pwm_n_cells = 3;
}
+ enable_conf = pc->data->enable_conf;
+ ctrl = readl_relaxed(pc->base + pc->data->regs.ctrl);
+ enabled = (ctrl & enable_conf) == enable_conf;
+
ret = pwmchip_add(&pc->chip);
if (ret < 0) {
- clk_unprepare(pc->clk);
dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
goto err_pclk;
}
/* Keep the PWM clk enabled if the PWM appears to be up and running. */
- enable_conf = pc->data->enable_conf;
- ctrl = readl_relaxed(pc->base + pc->data->regs.ctrl);
- if ((ctrl & enable_conf) != enable_conf)
+ if (!enabled)
clk_disable(pc->clk);
+ clk_disable(pc->pclk);
+
return 0;
err_pclk:
- clk_unprepare(pc->pclk);
+ clk_disable_unprepare(pc->pclk);
err_clk:
clk_disable_unprepare(pc->clk);
diff --git a/drivers/pwm/pwm-zx.c b/drivers/pwm/pwm-zx.c
deleted file mode 100644
index 34e91195ce98..000000000000
--- a/drivers/pwm/pwm-zx.c
+++ /dev/null
@@ -1,278 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (C) 2017 Sanechips Technology Co., Ltd.
- * Copyright 2017 Linaro Ltd.
- */
-
-#include <linux/clk.h>
-#include <linux/err.h>
-#include <linux/io.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/pwm.h>
-#include <linux/slab.h>
-
-#define ZX_PWM_MODE 0x0
-#define ZX_PWM_CLKDIV_SHIFT 2
-#define ZX_PWM_CLKDIV_MASK GENMASK(11, 2)
-#define ZX_PWM_CLKDIV(x) (((x) << ZX_PWM_CLKDIV_SHIFT) & \
- ZX_PWM_CLKDIV_MASK)
-#define ZX_PWM_POLAR BIT(1)
-#define ZX_PWM_EN BIT(0)
-#define ZX_PWM_PERIOD 0x4
-#define ZX_PWM_DUTY 0x8
-
-#define ZX_PWM_CLKDIV_MAX 1023
-#define ZX_PWM_PERIOD_MAX 65535
-
-struct zx_pwm_chip {
- struct pwm_chip chip;
- struct clk *pclk;
- struct clk *wclk;
- void __iomem *base;
-};
-
-static inline struct zx_pwm_chip *to_zx_pwm_chip(struct pwm_chip *chip)
-{
- return container_of(chip, struct zx_pwm_chip, chip);
-}
-
-static inline u32 zx_pwm_readl(struct zx_pwm_chip *zpc, unsigned int hwpwm,
- unsigned int offset)
-{
- return readl(zpc->base + (hwpwm + 1) * 0x10 + offset);
-}
-
-static inline void zx_pwm_writel(struct zx_pwm_chip *zpc, unsigned int hwpwm,
- unsigned int offset, u32 value)
-{
- writel(value, zpc->base + (hwpwm + 1) * 0x10 + offset);
-}
-
-static void zx_pwm_set_mask(struct zx_pwm_chip *zpc, unsigned int hwpwm,
- unsigned int offset, u32 mask, u32 value)
-{
- u32 data;
-
- data = zx_pwm_readl(zpc, hwpwm, offset);
- data &= ~mask;
- data |= value & mask;
- zx_pwm_writel(zpc, hwpwm, offset, data);
-}
-
-static void zx_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
- struct pwm_state *state)
-{
- struct zx_pwm_chip *zpc = to_zx_pwm_chip(chip);
- unsigned long rate;
- unsigned int div;
- u32 value;
- u64 tmp;
-
- value = zx_pwm_readl(zpc, pwm->hwpwm, ZX_PWM_MODE);
-
- if (value & ZX_PWM_POLAR)
- state->polarity = PWM_POLARITY_NORMAL;
- else
- state->polarity = PWM_POLARITY_INVERSED;
-
- if (value & ZX_PWM_EN)
- state->enabled = true;
- else
- state->enabled = false;
-
- div = (value & ZX_PWM_CLKDIV_MASK) >> ZX_PWM_CLKDIV_SHIFT;
- rate = clk_get_rate(zpc->wclk);
-
- tmp = zx_pwm_readl(zpc, pwm->hwpwm, ZX_PWM_PERIOD);
- tmp *= div * NSEC_PER_SEC;
- state->period = DIV_ROUND_CLOSEST_ULL(tmp, rate);
-
- tmp = zx_pwm_readl(zpc, pwm->hwpwm, ZX_PWM_DUTY);
- tmp *= div * NSEC_PER_SEC;
- state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, rate);
-}
-
-static int zx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
- unsigned int duty_ns, unsigned int period_ns)
-{
- struct zx_pwm_chip *zpc = to_zx_pwm_chip(chip);
- unsigned int period_cycles, duty_cycles;
- unsigned long long c;
- unsigned int div = 1;
- unsigned long rate;
-
- /* Find out the best divider */
- rate = clk_get_rate(zpc->wclk);
-
- while (1) {
- c = rate / div;
- c = c * period_ns;
- do_div(c, NSEC_PER_SEC);
-
- if (c < ZX_PWM_PERIOD_MAX)
- break;
-
- div++;
-
- if (div > ZX_PWM_CLKDIV_MAX)
- return -ERANGE;
- }
-
- /* Calculate duty cycles */
- period_cycles = c;
- c *= duty_ns;
- do_div(c, period_ns);
- duty_cycles = c;
-
- /*
- * If the PWM is being enabled, we have to temporarily disable it
- * before configuring the registers.
- */
- if (pwm_is_enabled(pwm))
- zx_pwm_set_mask(zpc, pwm->hwpwm, ZX_PWM_MODE, ZX_PWM_EN, 0);
-
- /* Set up registers */
- zx_pwm_set_mask(zpc, pwm->hwpwm, ZX_PWM_MODE, ZX_PWM_CLKDIV_MASK,
- ZX_PWM_CLKDIV(div));
- zx_pwm_writel(zpc, pwm->hwpwm, ZX_PWM_PERIOD, period_cycles);
- zx_pwm_writel(zpc, pwm->hwpwm, ZX_PWM_DUTY, duty_cycles);
-
- /* Re-enable the PWM if needed */
- if (pwm_is_enabled(pwm))
- zx_pwm_set_mask(zpc, pwm->hwpwm, ZX_PWM_MODE,
- ZX_PWM_EN, ZX_PWM_EN);
-
- return 0;
-}
-
-static int zx_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
- const struct pwm_state *state)
-{
- struct zx_pwm_chip *zpc = to_zx_pwm_chip(chip);
- struct pwm_state cstate;
- int ret;
-
- pwm_get_state(pwm, &cstate);
-
- if (state->polarity != cstate.polarity)
- zx_pwm_set_mask(zpc, pwm->hwpwm, ZX_PWM_MODE, ZX_PWM_POLAR,
- (state->polarity == PWM_POLARITY_INVERSED) ?
- 0 : ZX_PWM_POLAR);
-
- if (state->period != cstate.period ||
- state->duty_cycle != cstate.duty_cycle) {
- ret = zx_pwm_config(chip, pwm, state->duty_cycle,
- state->period);
- if (ret)
- return ret;
- }
-
- if (state->enabled != cstate.enabled) {
- if (state->enabled) {
- ret = clk_prepare_enable(zpc->wclk);
- if (ret)
- return ret;
-
- zx_pwm_set_mask(zpc, pwm->hwpwm, ZX_PWM_MODE,
- ZX_PWM_EN, ZX_PWM_EN);
- } else {
- zx_pwm_set_mask(zpc, pwm->hwpwm, ZX_PWM_MODE,
- ZX_PWM_EN, 0);
- clk_disable_unprepare(zpc->wclk);
- }
- }
-
- return 0;
-}
-
-static const struct pwm_ops zx_pwm_ops = {
- .apply = zx_pwm_apply,
- .get_state = zx_pwm_get_state,
- .owner = THIS_MODULE,
-};
-
-static int zx_pwm_probe(struct platform_device *pdev)
-{
- struct zx_pwm_chip *zpc;
- unsigned int i;
- int ret;
-
- zpc = devm_kzalloc(&pdev->dev, sizeof(*zpc), GFP_KERNEL);
- if (!zpc)
- return -ENOMEM;
-
- zpc->base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(zpc->base))
- return PTR_ERR(zpc->base);
-
- zpc->pclk = devm_clk_get(&pdev->dev, "pclk");
- if (IS_ERR(zpc->pclk))
- return PTR_ERR(zpc->pclk);
-
- zpc->wclk = devm_clk_get(&pdev->dev, "wclk");
- if (IS_ERR(zpc->wclk))
- return PTR_ERR(zpc->wclk);
-
- ret = clk_prepare_enable(zpc->pclk);
- if (ret)
- return ret;
-
- zpc->chip.dev = &pdev->dev;
- zpc->chip.ops = &zx_pwm_ops;
- zpc->chip.base = -1;
- zpc->chip.npwm = 4;
- zpc->chip.of_xlate = of_pwm_xlate_with_flags;
- zpc->chip.of_pwm_n_cells = 3;
-
- /*
- * PWM devices may be enabled by firmware, and let's disable all of
- * them initially to save power.
- */
- for (i = 0; i < zpc->chip.npwm; i++)
- zx_pwm_set_mask(zpc, i, ZX_PWM_MODE, ZX_PWM_EN, 0);
-
- ret = pwmchip_add(&zpc->chip);
- if (ret < 0) {
- dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret);
- clk_disable_unprepare(zpc->pclk);
- return ret;
- }
-
- platform_set_drvdata(pdev, zpc);
-
- return 0;
-}
-
-static int zx_pwm_remove(struct platform_device *pdev)
-{
- struct zx_pwm_chip *zpc = platform_get_drvdata(pdev);
- int ret;
-
- ret = pwmchip_remove(&zpc->chip);
- clk_disable_unprepare(zpc->pclk);
-
- return ret;
-}
-
-static const struct of_device_id zx_pwm_dt_ids[] = {
- { .compatible = "zte,zx296718-pwm", },
- { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, zx_pwm_dt_ids);
-
-static struct platform_driver zx_pwm_driver = {
- .driver = {
- .name = "zx-pwm",
- .of_match_table = zx_pwm_dt_ids,
- },
- .probe = zx_pwm_probe,
- .remove = zx_pwm_remove,
-};
-module_platform_driver(zx_pwm_driver);
-
-MODULE_ALIAS("platform:zx-pwm");
-MODULE_AUTHOR("Shawn Guo <shawn.guo@linaro.org>");
-MODULE_DESCRIPTION("ZTE ZX PWM Driver");
-MODULE_LICENSE("GPL v2");