diff --git a/drivers/pwm/pwm-loongson.c b/drivers/pwm/pwm-loongson.c index 1c7c926fa..502157a13 100644 --- a/drivers/pwm/pwm-loongson.c +++ b/drivers/pwm/pwm-loongson.c @@ -22,12 +22,16 @@ #include #include #include +#include #include #include #include #include #include #include +#include +#include +#include /* 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);