Commit 33a80b1e authored by Stephen Rothwell's avatar Stephen Rothwell

Merge remote-tracking branch 'pwm/for-next'

parents f86330d2 7ca17b20
......@@ -192,14 +192,23 @@ config PWM_IMG
To compile this driver as a module, choose M here: the module
will be called pwm-img
config PWM_IMX
tristate "i.MX PWM support"
config PWM_IMX1
tristate "i.MX1 PWM support"
depends on ARCH_MXC
help
Generic PWM framework driver for i.MX.
Generic PWM framework driver for i.MX1 and i.MX21
To compile this driver as a module, choose M here: the module
will be called pwm-imx.
will be called pwm-imx1.
config PWM_IMX27
tristate "i.MX27 PWM support"
depends on ARCH_MXC
help
Generic PWM framework driver for i.MX27 and later i.MX SoCs.
To compile this driver as a module, choose M here: the module
will be called pwm-imx27.
config PWM_JZ4740
tristate "Ingenic JZ47xx PWM support"
......
......@@ -17,7 +17,8 @@ obj-$(CONFIG_PWM_EP93XX) += pwm-ep93xx.o
obj-$(CONFIG_PWM_FSL_FTM) += pwm-fsl-ftm.o
obj-$(CONFIG_PWM_HIBVT) += pwm-hibvt.o
obj-$(CONFIG_PWM_IMG) += pwm-img.o
obj-$(CONFIG_PWM_IMX) += pwm-imx.o
obj-$(CONFIG_PWM_IMX1) += pwm-imx1.o
obj-$(CONFIG_PWM_IMX27) += pwm-imx27.o
obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o
obj-$(CONFIG_PWM_LP3943) += pwm-lp3943.o
obj-$(CONFIG_PWM_LPC18XX_SCT) += pwm-lpc18xx-sct.o
......
......@@ -472,7 +472,10 @@ int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state)
state->duty_cycle > state->period)
return -EINVAL;
if (!memcmp(state, &pwm->state, sizeof(*state)))
if (state->period == pwm->state.period &&
state->duty_cycle == pwm->state.duty_cycle &&
state->polarity == pwm->state.polarity &&
state->enabled == pwm->state.enabled)
return 0;
if (pwm->chip->ops->apply) {
......@@ -1033,10 +1036,7 @@ static int pwm_seq_show(struct seq_file *s, void *v)
dev_name(chip->dev), chip->npwm,
(chip->npwm != 1) ? "s" : "");
if (chip->ops->dbg_show)
chip->ops->dbg_show(chip, s);
else
pwm_dbg_show(chip, s);
pwm_dbg_show(chip, s);
return 0;
}
......
// SPDX-License-Identifier: GPL-2.0
/*
* simple driver for PWM (Pulse Width Modulator) controller
*
* Derived from pxa PWM driver by eric miao <eric.miao@marvell.com>
*/
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pwm.h>
#include <linux/slab.h>
#define MX1_PWMC 0x00 /* PWM Control Register */
#define MX1_PWMS 0x04 /* PWM Sample Register */
#define MX1_PWMP 0x08 /* PWM Period Register */
#define MX1_PWMC_EN BIT(4)
struct pwm_imx1_chip {
struct clk *clk_ipg;
struct clk *clk_per;
void __iomem *mmio_base;
struct pwm_chip chip;
};
#define to_pwm_imx1_chip(chip) container_of(chip, struct pwm_imx1_chip, chip)
static int pwm_imx1_clk_prepare_enable(struct pwm_chip *chip)
{
struct pwm_imx1_chip *imx = to_pwm_imx1_chip(chip);
int ret;
ret = clk_prepare_enable(imx->clk_ipg);
if (ret)
return ret;
ret = clk_prepare_enable(imx->clk_per);
if (ret) {
clk_disable_unprepare(imx->clk_ipg);
return ret;
}
return 0;
}
static void pwm_imx1_clk_disable_unprepare(struct pwm_chip *chip)
{
struct pwm_imx1_chip *imx = to_pwm_imx1_chip(chip);
clk_disable_unprepare(imx->clk_per);
clk_disable_unprepare(imx->clk_ipg);
}
static int pwm_imx1_config(struct pwm_chip *chip,
struct pwm_device *pwm, int duty_ns, int period_ns)
{
struct pwm_imx1_chip *imx = to_pwm_imx1_chip(chip);
u32 max, p;
/*
* The PWM subsystem allows for exact frequencies. However,
* I cannot connect a scope on my device to the PWM line and
* thus cannot provide the program the PWM controller
* exactly. Instead, I'm relying on the fact that the
* Bootloader (u-boot or WinCE+haret) has programmed the PWM
* function group already. So I'll just modify the PWM sample
* register to follow the ratio of duty_ns vs. period_ns
* accordingly.
*
* This is good enough for programming the brightness of
* the LCD backlight.
*
* The real implementation would divide PERCLK[0] first by
* both the prescaler (/1 .. /128) and then by CLKSEL
* (/2 .. /16).
*/
max = readl(imx->mmio_base + MX1_PWMP);
p = max * duty_ns / period_ns;
writel(max - p, imx->mmio_base + MX1_PWMS);
return 0;
}
static int pwm_imx1_enable(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct pwm_imx1_chip *imx = to_pwm_imx1_chip(chip);
u32 value;
int ret;
ret = pwm_imx1_clk_prepare_enable(chip);
if (ret < 0)
return ret;
value = readl(imx->mmio_base + MX1_PWMC);
value |= MX1_PWMC_EN;
writel(value, imx->mmio_base + MX1_PWMC);
return 0;
}
static void pwm_imx1_disable(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct pwm_imx1_chip *imx = to_pwm_imx1_chip(chip);
u32 value;
value = readl(imx->mmio_base + MX1_PWMC);
value &= ~MX1_PWMC_EN;
writel(value, imx->mmio_base + MX1_PWMC);
pwm_imx1_clk_disable_unprepare(chip);
}
static const struct pwm_ops pwm_imx1_ops = {
.enable = pwm_imx1_enable,
.disable = pwm_imx1_disable,
.config = pwm_imx1_config,
.owner = THIS_MODULE,
};
static const struct of_device_id pwm_imx1_dt_ids[] = {
{ .compatible = "fsl,imx1-pwm", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, pwm_imx1_dt_ids);
static int pwm_imx1_probe(struct platform_device *pdev)
{
struct pwm_imx1_chip *imx;
struct resource *r;
imx = devm_kzalloc(&pdev->dev, sizeof(*imx), GFP_KERNEL);
if (!imx)
return -ENOMEM;
platform_set_drvdata(pdev, imx);
imx->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
if (IS_ERR(imx->clk_ipg)) {
dev_err(&pdev->dev, "getting ipg clock failed with %ld\n",
PTR_ERR(imx->clk_ipg));
return PTR_ERR(imx->clk_ipg);
}
imx->clk_per = devm_clk_get(&pdev->dev, "per");
if (IS_ERR(imx->clk_per)) {
int ret = PTR_ERR(imx->clk_per);
if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev,
"failed to get peripheral clock: %d\n",
ret);
return ret;
}
imx->chip.ops = &pwm_imx1_ops;
imx->chip.dev = &pdev->dev;
imx->chip.base = -1;
imx->chip.npwm = 1;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
imx->mmio_base = devm_ioremap_resource(&pdev->dev, r);
if (IS_ERR(imx->mmio_base))
return PTR_ERR(imx->mmio_base);
return pwmchip_add(&imx->chip);
}
static int pwm_imx1_remove(struct platform_device *pdev)
{
struct pwm_imx1_chip *imx = platform_get_drvdata(pdev);
pwm_imx1_clk_disable_unprepare(&imx->chip);
return pwmchip_remove(&imx->chip);
}
static struct platform_driver pwm_imx1_driver = {
.driver = {
.name = "pwm-imx1",
.of_match_table = pwm_imx1_dt_ids,
},
.probe = pwm_imx1_probe,
.remove = pwm_imx1_remove,
};
module_platform_driver(pwm_imx1_driver);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
......@@ -19,16 +19,6 @@
#include <linux/pwm.h>
#include <linux/slab.h>
/* i.MX1 and i.MX21 share the same PWM function block: */
#define MX1_PWMC 0x00 /* PWM Control Register */
#define MX1_PWMS 0x04 /* PWM Sample Register */
#define MX1_PWMP 0x08 /* PWM Period Register */
#define MX1_PWMC_EN BIT(4)
/* i.MX27, i.MX31, i.MX35 share the same PWM function block: */
#define MX3_PWMCR 0x00 /* PWM Control Register */
#define MX3_PWMSR 0x04 /* PWM Status Register */
#define MX3_PWMSAR 0x0C /* PWM Sample Register */
......@@ -86,21 +76,18 @@
/* PWMPR register value of 0xffff has the same effect as 0xfffe */
#define MX3_PWMPR_MAX 0xfffe
struct imx_chip {
struct pwm_imx27_chip {
struct clk *clk_ipg;
struct clk *clk_per;
void __iomem *mmio_base;
struct pwm_chip chip;
};
#define to_imx_chip(chip) container_of(chip, struct imx_chip, chip)
#define to_pwm_imx27_chip(chip) container_of(chip, struct pwm_imx27_chip, chip)
static int imx_pwm_clk_prepare_enable(struct pwm_chip *chip)
static int pwm_imx27_clk_prepare_enable(struct pwm_chip *chip)
{
struct imx_chip *imx = to_imx_chip(chip);
struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip);
int ret;
ret = clk_prepare_enable(imx->clk_ipg);
......@@ -116,22 +103,23 @@ static int imx_pwm_clk_prepare_enable(struct pwm_chip *chip)
return 0;
}
static void imx_pwm_clk_disable_unprepare(struct pwm_chip *chip)
static void pwm_imx27_clk_disable_unprepare(struct pwm_chip *chip)
{
struct imx_chip *imx = to_imx_chip(chip);
struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip);
clk_disable_unprepare(imx->clk_per);
clk_disable_unprepare(imx->clk_ipg);
}
static void imx_pwm_get_state(struct pwm_chip *chip,
struct pwm_device *pwm, struct pwm_state *state)
static void pwm_imx27_get_state(struct pwm_chip *chip,
struct pwm_device *pwm, struct pwm_state *state)
{
struct imx_chip *imx = to_imx_chip(chip);
u32 period, prescaler, pwm_clk, ret, val;
struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip);
u32 period, prescaler, pwm_clk, val;
u64 tmp;
int ret;
ret = imx_pwm_clk_prepare_enable(chip);
ret = pwm_imx27_clk_prepare_enable(chip);
if (ret < 0)
return;
......@@ -139,7 +127,7 @@ static void imx_pwm_get_state(struct pwm_chip *chip,
if (val & MX3_PWMCR_EN) {
state->enabled = true;
ret = imx_pwm_clk_prepare_enable(chip);
ret = pwm_imx27_clk_prepare_enable(chip);
if (ret)
return;
} else {
......@@ -176,70 +164,12 @@ static void imx_pwm_get_state(struct pwm_chip *chip,
state->duty_cycle = 0;
}
imx_pwm_clk_disable_unprepare(chip);
}
static int imx_pwm_config_v1(struct pwm_chip *chip,
struct pwm_device *pwm, int duty_ns, int period_ns)
{
struct imx_chip *imx = to_imx_chip(chip);
/*
* The PWM subsystem allows for exact frequencies. However,
* I cannot connect a scope on my device to the PWM line and
* thus cannot provide the program the PWM controller
* exactly. Instead, I'm relying on the fact that the
* Bootloader (u-boot or WinCE+haret) has programmed the PWM
* function group already. So I'll just modify the PWM sample
* register to follow the ratio of duty_ns vs. period_ns
* accordingly.
*
* This is good enough for programming the brightness of
* the LCD backlight.
*
* The real implementation would divide PERCLK[0] first by
* both the prescaler (/1 .. /128) and then by CLKSEL
* (/2 .. /16).
*/
u32 max = readl(imx->mmio_base + MX1_PWMP);
u32 p = max * duty_ns / period_ns;
writel(max - p, imx->mmio_base + MX1_PWMS);
return 0;
}
static int imx_pwm_enable_v1(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct imx_chip *imx = to_imx_chip(chip);
u32 val;
int ret;
ret = imx_pwm_clk_prepare_enable(chip);
if (ret < 0)
return ret;
val = readl(imx->mmio_base + MX1_PWMC);
val |= MX1_PWMC_EN;
writel(val, imx->mmio_base + MX1_PWMC);
return 0;
}
static void imx_pwm_disable_v1(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct imx_chip *imx = to_imx_chip(chip);
u32 val;
val = readl(imx->mmio_base + MX1_PWMC);
val &= ~MX1_PWMC_EN;
writel(val, imx->mmio_base + MX1_PWMC);
imx_pwm_clk_disable_unprepare(chip);
pwm_imx27_clk_disable_unprepare(chip);
}
static void imx_pwm_sw_reset(struct pwm_chip *chip)
static void pwm_imx27_sw_reset(struct pwm_chip *chip)
{
struct imx_chip *imx = to_imx_chip(chip);
struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip);
struct device *dev = chip->dev;
int wait_count = 0;
u32 cr;
......@@ -255,10 +185,10 @@ static void imx_pwm_sw_reset(struct pwm_chip *chip)
dev_warn(dev, "software reset timeout\n");
}
static void imx_pwm_wait_fifo_slot(struct pwm_chip *chip,
struct pwm_device *pwm)
static void pwm_imx27_wait_fifo_slot(struct pwm_chip *chip,
struct pwm_device *pwm)
{
struct imx_chip *imx = to_imx_chip(chip);
struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip);
struct device *dev = chip->dev;
unsigned int period_ms;
int fifoav;
......@@ -277,11 +207,11 @@ static void imx_pwm_wait_fifo_slot(struct pwm_chip *chip,
}
}
static int imx_pwm_apply_v2(struct pwm_chip *chip, struct pwm_device *pwm,
struct pwm_state *state)
static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm,
struct pwm_state *state)
{
unsigned long period_cycles, duty_cycles, prescale;
struct imx_chip *imx = to_imx_chip(chip);
struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip);
struct pwm_state cstate;
unsigned long long c;
int ret;
......@@ -318,13 +248,13 @@ static int imx_pwm_apply_v2(struct pwm_chip *chip, struct pwm_device *pwm,
* enabled.
*/
if (cstate.enabled) {
imx_pwm_wait_fifo_slot(chip, pwm);
pwm_imx27_wait_fifo_slot(chip, pwm);
} else {
ret = imx_pwm_clk_prepare_enable(chip);
ret = pwm_imx27_clk_prepare_enable(chip);
if (ret)
return ret;
imx_pwm_sw_reset(chip);
pwm_imx27_sw_reset(chip);
}
writel(duty_cycles, imx->mmio_base + MX3_PWMSAR);
......@@ -343,64 +273,35 @@ static int imx_pwm_apply_v2(struct pwm_chip *chip, struct pwm_device *pwm,
} else if (cstate.enabled) {
writel(0, imx->mmio_base + MX3_PWMCR);
imx_pwm_clk_disable_unprepare(chip);
pwm_imx27_clk_disable_unprepare(chip);
}
return 0;
}
static const struct pwm_ops imx_pwm_ops_v1 = {
.enable = imx_pwm_enable_v1,
.disable = imx_pwm_disable_v1,
.config = imx_pwm_config_v1,
static const struct pwm_ops pwm_imx27_ops = {
.apply = pwm_imx27_apply,
.get_state = pwm_imx27_get_state,
.owner = THIS_MODULE,
};
static const struct pwm_ops imx_pwm_ops_v2 = {
.apply = imx_pwm_apply_v2,
.get_state = imx_pwm_get_state,
.owner = THIS_MODULE,
};
struct imx_pwm_data {
bool polarity_supported;
const struct pwm_ops *ops;
};
static struct imx_pwm_data imx_pwm_data_v1 = {
.ops = &imx_pwm_ops_v1,
};
static struct imx_pwm_data imx_pwm_data_v2 = {
.polarity_supported = true,
.ops = &imx_pwm_ops_v2,
};
static const struct of_device_id imx_pwm_dt_ids[] = {
{ .compatible = "fsl,imx1-pwm", .data = &imx_pwm_data_v1, },
{ .compatible = "fsl,imx27-pwm", .data = &imx_pwm_data_v2, },
static const struct of_device_id pwm_imx27_dt_ids[] = {
{ .compatible = "fsl,imx27-pwm", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, imx_pwm_dt_ids);
MODULE_DEVICE_TABLE(of, pwm_imx27_dt_ids);
static int imx_pwm_probe(struct platform_device *pdev)
static int pwm_imx27_probe(struct platform_device *pdev)
{
const struct of_device_id *of_id =
of_match_device(imx_pwm_dt_ids, &pdev->dev);
const struct imx_pwm_data *data;
struct imx_chip *imx;
struct pwm_imx27_chip *imx;
struct resource *r;
int ret = 0;
if (!of_id)
return -ENODEV;
data = of_id->data;
imx = devm_kzalloc(&pdev->dev, sizeof(*imx), GFP_KERNEL);
if (imx == NULL)
return -ENOMEM;
platform_set_drvdata(pdev, imx);
imx->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
if (IS_ERR(imx->clk_ipg)) {
dev_err(&pdev->dev, "getting ipg clock failed with %ld\n",
......@@ -410,57 +311,51 @@ static int imx_pwm_probe(struct platform_device *pdev)
imx->clk_per = devm_clk_get(&pdev->dev, "per");
if (IS_ERR(imx->clk_per)) {
dev_err(&pdev->dev, "getting per clock failed with %ld\n",
PTR_ERR(imx->clk_per));
return PTR_ERR(imx->clk_per);
int ret = PTR_ERR(imx->clk_per);
if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev,
"failed to get peripheral clock: %d\n",
ret);
return ret;
}
imx->chip.ops = data->ops;
imx->chip.ops = &pwm_imx27_ops;
imx->chip.dev = &pdev->dev;
imx->chip.base = -1;
imx->chip.npwm = 1;
if (data->polarity_supported) {
dev_dbg(&pdev->dev, "PWM supports output inversion\n");
imx->chip.of_xlate = of_pwm_xlate_with_flags;
imx->chip.of_pwm_n_cells = 3;
}
imx->chip.of_xlate = of_pwm_xlate_with_flags;
imx->chip.of_pwm_n_cells = 3;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
imx->mmio_base = devm_ioremap_resource(&pdev->dev, r);
if (IS_ERR(imx->mmio_base))
return PTR_ERR(imx->mmio_base);
ret = pwmchip_add(&imx->chip);
if (ret < 0)
return ret;
platform_set_drvdata(pdev, imx);
return 0;
return pwmchip_add(&imx->chip);
}
static int imx_pwm_remove(struct platform_device *pdev)
static int pwm_imx27_remove(struct platform_device *pdev)
{
struct imx_chip *imx;
struct pwm_imx27_chip *imx;
imx = platform_get_drvdata(pdev);
if (imx == NULL)
return -ENODEV;
imx_pwm_clk_disable_unprepare(&imx->chip);
pwm_imx27_clk_disable_unprepare(&imx->chip);
return pwmchip_remove(&imx->chip);
}
static struct platform_driver imx_pwm_driver = {
.driver = {
.name = "imx-pwm",
.of_match_table = imx_pwm_dt_ids,
.driver = {
.name = "pwm-imx27",
.of_match_table = pwm_imx27_dt_ids,
},
.probe = imx_pwm_probe,
.remove = imx_pwm_remove,
.probe = pwm_imx27_probe,
.remove = pwm_imx27_remove,
};
module_platform_driver(imx_pwm_driver);
MODULE_LICENSE("GPL v2");
......
......@@ -242,11 +242,7 @@ pwm_set_relative_duty_cycle(struct pwm_state *state, unsigned int duty_cycle,
* struct pwm_ops - PWM controller operations
* @request: optional hook for requesting a PWM
* @free: optional hook for freeing a PWM
* @config: configure duty cycles and period length for this PWM
* @set_polarity: configure the polarity of this PWM
* @capture: capture and report PWM signal
* @enable: enable PWM output toggling
* @disable: disable PWM output toggling
* @apply: atomically apply a new PWM config. The state argument
* should be adjusted with the real hardware config (if the
* approximate the period or duty_cycle value, state should
......@@ -254,53 +250,56 @@ pwm_set_relative_duty_cycle(struct pwm_state *state, unsigned int duty_cycle,
* @get_state: get the current PWM state. This function is only
* called once per PWM device when the PWM chip is
* registered.
* @dbg_show: optional routine to show contents in debugfs
* @owner: helps prevent removal of modules exporting active PWMs
* @config: configure duty cycles and period length for this PWM
* @set_polarity: configure the polarity of this PWM
* @enable: enable PWM output toggling
* @disable: disable PWM output toggling
*/
struct pwm_ops {
int (*request)(struct pwm_chip *chip, struct pwm_device *pwm);
void (*free)(struct pwm_chip *chip, struct pwm_device *pwm);
int (*config)(struct pwm_chip *chip, struct pwm_device *pwm,
int duty_ns, int period_ns);
int (*set_polarity)(struct pwm_chip *chip, struct pwm_device *pwm,
enum pwm_polarity polarity);
int (*capture)(struct pwm_chip *chip, struct pwm_device *pwm,
struct pwm_capture *result, unsigned long timeout);
int (*enable)(struct pwm_chip *chip, struct pwm_device *pwm);
void (*disable)(struct pwm_chip *chip, struct pwm_device *pwm);
int (*apply)(struct pwm_chip *chip, struct pwm_device *pwm,
struct pwm_state *state);
void (*get_state)(struct pwm_chip *chip, struct pwm_device *pwm,
struct pwm_state *state);
#ifdef CONFIG_DEBUG_FS
void (*dbg_show)(struct pwm_chip *chip, struct seq_file *s);
#endif
struct module *owner;
/* Only used by legacy drivers */
int (*config)(struct pwm_chip *chip, struct pwm_device *pwm,
int duty_ns, int period_ns);
int (*set_polarity)(struct pwm_chip *chip, struct pwm_device *pwm,
enum pwm_polarity polarity);
int (*enable)(struct pwm_chip *chip, struct pwm_device *pwm);
void (*disable)(struct pwm_chip *chip, struct pwm_device *pwm);
};
/**
* struct pwm_chip - abstract a PWM controller
* @dev: device providing the PWMs
* @list: list node for internal use
* @ops: callbacks for this PWM controller
* @base: number of first PWM controlled by this chip
* @npwm: number of PWMs controlled by this chip
* @pwms: array of PWM devices allocated by the framework
* @of_xlate: request a PWM device given a device tree PWM specifier
* @of_pwm_n_cells: number of cells expected in the device tree PWM specifier
* @list: list node for internal use
* @pwms: array of PWM devices allocated by the framework
*/
struct pwm_chip {
struct device *dev;
struct list_head list;
const struct pwm_ops *ops;
int base;
unsigned int npwm;
struct pwm_device *pwms;
struct pwm_device * (*of_xlate)(struct pwm_chip *pc,
const struct of_phandle_args *args);
unsigned int of_pwm_n_cells;
/* only used internally by the PWM framework */
struct list_head list;
struct pwm_device *pwms;
};
/**
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment