mirror of
https://gitee.com/open-loongarch/linux-6.12.git
synced 2026-10-25 20:47:11 +08:00
!3 feat(pwm): support pwm capture
Merge pull request !3 from Ilikara/feat/pwm-capture
This commit is contained in:
@@ -22,12 +22,16 @@
|
||||
#include <linux/device.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pwm.h>
|
||||
#include <linux/units.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
/* Loongson PWM registers */
|
||||
#define LOONGSON_PWM_REG_DUTY 0x4 /* Low Pulse Buffer Register */
|
||||
@@ -57,8 +61,11 @@ struct pwm_loongson_suspend_store {
|
||||
struct pwm_loongson_ddata {
|
||||
struct clk *clk;
|
||||
void __iomem *base;
|
||||
u32 irq;
|
||||
u32 int_count;
|
||||
u64 clk_rate;
|
||||
struct pwm_loongson_suspend_store lss;
|
||||
struct wait_queue_head capture_wait_queue;
|
||||
};
|
||||
|
||||
static inline struct pwm_loongson_ddata *to_pwm_loongson_ddata(struct pwm_chip *chip)
|
||||
@@ -77,6 +84,64 @@ static inline void pwm_loongson_writel(struct pwm_loongson_ddata *ddata,
|
||||
writel(val, ddata->base + offset);
|
||||
}
|
||||
|
||||
static irqreturn_t pwm_loongson_isr(int irq, void *dev)
|
||||
{
|
||||
u32 val;
|
||||
struct pwm_chip *chip = dev_get_drvdata(dev);
|
||||
struct pwm_loongson_ddata *ddata = to_pwm_loongson_ddata(chip);
|
||||
|
||||
val = pwm_loongson_readl(ddata, LOONGSON_PWM_REG_CTRL);
|
||||
if ((val & LOONGSON_PWM_CTRL_INT) == 0) {
|
||||
return IRQ_NONE;
|
||||
}
|
||||
val |= LOONGSON_PWM_CTRL_INT;
|
||||
pwm_loongson_writel(ddata, val, LOONGSON_PWM_REG_CTRL);
|
||||
|
||||
ddata->int_count++;
|
||||
|
||||
dev_info(dev, "pwm_loongson_isr count %u\n", ddata->int_count);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int pwm_loongson_capture(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
struct pwm_capture *result,
|
||||
unsigned long timeout)
|
||||
{
|
||||
u32 val;
|
||||
struct device *dev = pwmchip_parent(chip);
|
||||
struct pwm_loongson_ddata *ddata = to_pwm_loongson_ddata(chip);
|
||||
int ret;
|
||||
|
||||
pwm_loongson_writel(ddata, 0, LOONGSON_PWM_REG_PERIOD);
|
||||
|
||||
val = pwm_loongson_readl(ddata, LOONGSON_PWM_REG_CTRL);
|
||||
val |= LOONGSON_PWM_CTRL_EN | LOONGSON_PWM_CTRL_CAPTE |
|
||||
LOONGSON_PWM_CTRL_OE;
|
||||
pwm_loongson_writel(ddata, val, LOONGSON_PWM_REG_CTRL);
|
||||
ret = readl_poll_timeout(ddata->base + LOONGSON_PWM_REG_PERIOD, val,
|
||||
val, 10, timeout * 1000);
|
||||
if (ret < 0)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
dev_dbg(dev, "get first period: %u\n", val);
|
||||
|
||||
usleep_range(val * 4 / 100 + 1, val * 4 / 100 + 2);
|
||||
|
||||
val = pwm_loongson_readl(ddata, LOONGSON_PWM_REG_PERIOD);
|
||||
result->period = DIV64_U64_ROUND_UP((u64)val * NSEC_PER_SEC, ddata->clk_rate);
|
||||
dev_dbg(dev, "get second period: %u\n", val);
|
||||
val = pwm_loongson_readl(ddata, LOONGSON_PWM_REG_DUTY);
|
||||
result->duty_cycle = DIV64_U64_ROUND_UP((u64)val * NSEC_PER_SEC, ddata->clk_rate);
|
||||
|
||||
val = pwm_loongson_readl(ddata, LOONGSON_PWM_REG_CTRL);
|
||||
val &= ~(LOONGSON_PWM_CTRL_EN | LOONGSON_PWM_CTRL_CAPTE |
|
||||
LOONGSON_PWM_CTRL_OE);
|
||||
pwm_loongson_writel(ddata, val, LOONGSON_PWM_REG_CTRL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pwm_loongson_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
enum pwm_polarity polarity)
|
||||
{
|
||||
@@ -207,6 +272,7 @@ static int pwm_loongson_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
}
|
||||
|
||||
static const struct pwm_ops pwm_loongson_ops = {
|
||||
.capture = pwm_loongson_capture,
|
||||
.apply = pwm_loongson_apply,
|
||||
.get_state = pwm_loongson_get_state,
|
||||
};
|
||||
@@ -219,12 +285,23 @@ static int pwm_loongson_probe(struct platform_device *pdev)
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
u32 of_clk_freq = 0;
|
||||
u32 irq;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
dev_info(dev, "irq get:%u\n", irq);
|
||||
if (irq <= 0) {
|
||||
dev_err(&pdev->dev, "no irq resource?\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
chip = devm_pwmchip_alloc(dev, 1, sizeof(*ddata));
|
||||
if (IS_ERR(chip))
|
||||
return PTR_ERR(chip);
|
||||
ddata = to_pwm_loongson_ddata(chip);
|
||||
|
||||
// 初始化等待队列
|
||||
init_waitqueue_head(&ddata->capture_wait_queue);
|
||||
|
||||
ddata->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(ddata->base))
|
||||
return PTR_ERR(ddata->base);
|
||||
@@ -247,6 +324,14 @@ static int pwm_loongson_probe(struct platform_device *pdev)
|
||||
#endif
|
||||
}
|
||||
|
||||
ddata->irq = irq;
|
||||
|
||||
ret = devm_request_irq(dev, ddata->irq, pwm_loongson_isr, IRQF_SHARED,
|
||||
dev_name(dev), dev);
|
||||
|
||||
if (ret)
|
||||
dev_err(dev, "failure requesting irq %d\n", ret);
|
||||
|
||||
/* Explicitly initialize the CTRL register */
|
||||
pwm_loongson_writel(ddata, 0, LOONGSON_PWM_REG_CTRL);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user