From 01573e231f18eb2d99162747186f59511f56b64d Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Fri, 08 Dec 2023 10:40:48 +0000
Subject: [PATCH] 移去rt

---
 kernel/drivers/pwm/pwm-rockchip.c |  265 +++++++++++++++++++++++++++++++++++++++++-----------
 1 files changed, 207 insertions(+), 58 deletions(-)

diff --git a/kernel/drivers/pwm/pwm-rockchip.c b/kernel/drivers/pwm/pwm-rockchip.c
index e505d58..50e9195 100644
--- a/kernel/drivers/pwm/pwm-rockchip.c
+++ b/kernel/drivers/pwm/pwm-rockchip.c
@@ -1,16 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * PWM driver for Rockchip SoCs
  *
  * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com>
  * Copyright (C) 2014 ROCKCHIP, Inc.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
  */
 
 #include <linux/clk.h>
+#include <linux/interrupt.h>
 #include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/irq.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
@@ -18,12 +18,19 @@
 #include <linux/platform_device.h>
 #include <linux/pwm.h>
 #include <linux/time.h>
+#include "pwm-rockchip.h"
+
+#define PWM_MAX_CHANNEL_NUM	4
 
 #define PWM_CTRL_TIMER_EN	(1 << 0)
 #define PWM_CTRL_OUTPUT_EN	(1 << 3)
 
 #define PWM_ENABLE		(1 << 0)
-#define PWM_CONTINUOUS		(1 << 1)
+#define PWM_MODE_SHIFT		1
+#define PWM_MODE_MASK		(0x3 << PWM_MODE_SHIFT)
+#define PWM_ONESHOT		(0 << PWM_MODE_SHIFT)
+#define PWM_CONTINUOUS		(1 << PWM_MODE_SHIFT)
+#define PWM_CAPTURE		(2 << PWM_MODE_SHIFT)
 #define PWM_DUTY_POSITIVE	(1 << 3)
 #define PWM_DUTY_NEGATIVE	(0 << 3)
 #define PWM_INACTIVE_NEGATIVE	(0 << 4)
@@ -33,9 +40,23 @@
 #define PWM_OUTPUT_CENTER	(1 << 5)
 #define PWM_LOCK_EN		(1 << 6)
 #define PWM_LP_DISABLE		(0 << 8)
+#define PWM_CLK_SEL_SHIFT	9
+#define PWM_CLK_SEL_MASK	(1 << PWM_CLK_SEL_SHIFT)
+#define PWM_SEL_NO_SCALED_CLOCK	(0 << PWM_CLK_SEL_SHIFT)
+#define PWM_SEL_SCALED_CLOCK	(1 << PWM_CLK_SEL_SHIFT)
+#define PWM_PRESCELE_SHIFT	12
+#define PWM_PRESCALE_MASK	(0x3 << PWM_PRESCELE_SHIFT)
+#define PWM_SCALE_SHIFT		16
+#define PWM_SCALE_MASK		(0xff << PWM_SCALE_SHIFT)
 
 #define PWM_ONESHOT_COUNT_SHIFT	24
+#define PWM_ONESHOT_COUNT_MASK	(0xff << PWM_ONESHOT_COUNT_SHIFT)
 #define PWM_ONESHOT_COUNT_MAX	256
+
+#define PWM_REG_INTSTS(n)	((3 - (n)) * 0x10 + 0x10)
+#define PWM_REG_INT_EN(n)	((3 - (n)) * 0x10 + 0x14)
+
+#define PWM_CH_INT(n)		BIT(n)
 
 struct rockchip_pwm_chip {
 	struct pwm_chip chip;
@@ -48,7 +69,9 @@
 	unsigned long clk_rate;
 	bool vop_pwm_en; /* indicate voppwm mirror register state */
 	bool center_aligned;
-	bool oneshot;
+	bool oneshot_en;
+	int channel_id;
+	int irq;
 };
 
 struct rockchip_pwm_regs {
@@ -81,44 +104,81 @@
 	u32 enable_conf = pc->data->enable_conf;
 	u64 tmp;
 	u32 val;
+	u32 dclk_div;
 	int ret;
 
-	ret = clk_enable(pc->pclk);
-	if (ret)
-		return;
+	if (!pc->oneshot_en) {
+		ret = clk_enable(pc->pclk);
+		if (ret)
+			return;
+	}
+
+	dclk_div = pc->oneshot_en ? 2 : 1;
 
 	tmp = readl_relaxed(pc->base + pc->data->regs.period);
-	tmp *= pc->data->prescaler * NSEC_PER_SEC;
+	tmp *= dclk_div * pc->data->prescaler * NSEC_PER_SEC;
 	state->period = DIV_ROUND_CLOSEST_ULL(tmp, pc->clk_rate);
 
 	tmp = readl_relaxed(pc->base + pc->data->regs.duty);
-	tmp *= pc->data->prescaler * NSEC_PER_SEC;
+	tmp *= dclk_div * pc->data->prescaler * NSEC_PER_SEC;
 	state->duty_cycle =  DIV_ROUND_CLOSEST_ULL(tmp, pc->clk_rate);
 
 	val = readl_relaxed(pc->base + pc->data->regs.ctrl);
-	if (pc->data->supports_polarity)
-		state->enabled = ((val & enable_conf) != enable_conf) ?
-				 false : true;
+	if (pc->oneshot_en)
+		enable_conf &= ~PWM_CONTINUOUS;
+	state->enabled = (val & enable_conf) == enable_conf;
+
+	if (pc->data->supports_polarity && !(val & PWM_DUTY_POSITIVE))
+		state->polarity = PWM_POLARITY_INVERSED;
 	else
-		state->enabled = ((val & enable_conf) == enable_conf) ?
-				 true : false;
+		state->polarity = PWM_POLARITY_NORMAL;
 
-	if (pc->data->supports_polarity) {
-		if (!(val & PWM_DUTY_POSITIVE))
-			state->polarity = PWM_POLARITY_INVERSED;
-	}
+	if (!pc->oneshot_en)
+		clk_disable(pc->pclk);
+}
 
-	clk_disable(pc->pclk);
+static irqreturn_t rockchip_pwm_oneshot_irq(int irq, void *data)
+{
+	struct rockchip_pwm_chip *pc = data;
+	struct pwm_state state;
+	unsigned int id = pc->channel_id;
+	int val;
+
+	if (id > 3)
+		return IRQ_NONE;
+	val = readl_relaxed(pc->base + PWM_REG_INTSTS(id));
+
+	if ((val & PWM_CH_INT(id)) == 0)
+		return IRQ_NONE;
+
+	writel_relaxed(PWM_CH_INT(id), pc->base + PWM_REG_INTSTS(id));
+
+	/*
+	 * Set pwm state to disabled when the oneshot mode finished.
+	 */
+	pwm_get_state(&pc->chip.pwms[0], &state);
+	state.enabled = false;
+	pwm_apply_state(&pc->chip.pwms[0], &state);
+
+	rockchip_pwm_oneshot_callback(&pc->chip.pwms[0], &state);
+
+	return IRQ_HANDLED;
 }
 
 static void rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
-				struct pwm_state *state)
+			       const struct pwm_state *state)
 {
 	struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
 	unsigned long period, duty;
 	unsigned long flags;
 	u64 div;
 	u32 ctrl;
+	u8 dclk_div = 1;
+
+#ifdef CONFIG_PWM_ROCKCHIP_ONESHOT
+	if (state->oneshot_count > 0 && state->oneshot_count <= PWM_ONESHOT_COUNT_MAX)
+		dclk_div = 2;
+#endif
 
 	/*
 	 * Since period and duty cycle registers have a width of 32
@@ -126,11 +186,10 @@
 	 * default prescaler value for all practical clock rate values.
 	 */
 	div = (u64)pc->clk_rate * state->period;
-	period = DIV_ROUND_CLOSEST_ULL(div,
-				       pc->data->prescaler * NSEC_PER_SEC);
+	period = DIV_ROUND_CLOSEST_ULL(div, dclk_div * pc->data->prescaler * NSEC_PER_SEC);
 
 	div = (u64)pc->clk_rate * state->duty_cycle;
-	duty = DIV_ROUND_CLOSEST_ULL(div, pc->data->prescaler * NSEC_PER_SEC);
+	duty = DIV_ROUND_CLOSEST_ULL(div, dclk_div * pc->data->prescaler * NSEC_PER_SEC);
 
 	local_irq_save(flags);
 	/*
@@ -146,15 +205,51 @@
 	}
 
 #ifdef CONFIG_PWM_ROCKCHIP_ONESHOT
-	if (state->oneshot_count > PWM_ONESHOT_COUNT_MAX) {
-		pc->oneshot = false;
-		dev_err(chip->dev, "Oneshot_count value overflow.\n");
-	} else if (state->oneshot_count > 0) {
-		pc->oneshot = true;
+	if (state->oneshot_count > 0 && state->oneshot_count <= PWM_ONESHOT_COUNT_MAX) {
+		u32 int_ctrl;
+
+		/*
+		 * This is a workaround, an uncertain waveform will be
+		 * generated after oneshot ends. It is needed to enable
+		 * the dclk scale function to resolve it. It doesn't
+		 * matter what the scale factor is, just make sure the
+		 * scale function is turned on, for which we set scale
+		 * factor to 2.
+		 */
+		ctrl &= ~PWM_SCALE_MASK;
+		ctrl |= (dclk_div / 2) << PWM_SCALE_SHIFT;
+		ctrl &= ~PWM_CLK_SEL_MASK;
+		ctrl |= PWM_SEL_SCALED_CLOCK;
+
+		pc->oneshot_en = true;
+		ctrl &= ~PWM_MODE_MASK;
+		ctrl |= PWM_ONESHOT;
+
+		ctrl &= ~PWM_ONESHOT_COUNT_MASK;
 		ctrl |= (state->oneshot_count - 1) << PWM_ONESHOT_COUNT_SHIFT;
+
+		int_ctrl = readl_relaxed(pc->base + PWM_REG_INT_EN(pc->channel_id));
+		int_ctrl |= PWM_CH_INT(pc->channel_id);
+		writel_relaxed(int_ctrl, pc->base + PWM_REG_INT_EN(pc->channel_id));
 	} else {
-		pc->oneshot = false;
+		u32 int_ctrl;
+
+		ctrl &= ~PWM_SCALE_MASK;
+		ctrl &= ~PWM_CLK_SEL_MASK;
+		ctrl |= PWM_SEL_NO_SCALED_CLOCK;
+
+		if (state->oneshot_count)
+			dev_err(chip->dev, "Oneshot_count must be between 1 and 256.\n");
+
+		pc->oneshot_en = false;
+		ctrl &= ~PWM_MODE_MASK;
 		ctrl |= PWM_CONTINUOUS;
+
+		ctrl &= ~PWM_ONESHOT_COUNT_MASK;
+
+		int_ctrl = readl_relaxed(pc->base + PWM_REG_INT_EN(pc->channel_id));
+		int_ctrl &= ~PWM_CH_INT(pc->channel_id);
+		writel_relaxed(int_ctrl, pc->base + PWM_REG_INT_EN(pc->channel_id));
 	}
 #endif
 
@@ -211,7 +306,7 @@
 
 	if (enable) {
 		val |= enable_conf;
-		if (pc->oneshot)
+		if (pc->oneshot_en)
 			val &= ~PWM_CONTINUOUS;
 	} else {
 		val &= ~enable_conf;
@@ -228,16 +323,18 @@
 }
 
 static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
-			      struct pwm_state *state)
+			      const struct pwm_state *state)
 {
 	struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
 	struct pwm_state curstate;
 	bool enabled;
 	int ret = 0;
 
-	ret = clk_enable(pc->pclk);
-	if (ret)
-		return ret;
+	if (!pc->oneshot_en) {
+		ret = clk_enable(pc->pclk);
+		if (ret)
+			return ret;
+	}
 
 	pwm_get_state(pwm, &curstate);
 	enabled = curstate.enabled;
@@ -257,16 +354,11 @@
 			goto out;
 	}
 
-	/*
-	 * Update the state with the real hardware, which can differ a bit
-	 * because of period/duty_cycle approximation.
-	 */
-	rockchip_pwm_get_state(chip, pwm, state);
-
-	if (state->enabled || pc->oneshot)
+	if (state->enabled)
 		ret = pinctrl_select_state(pc->pinctrl, pc->active_state);
 out:
-	clk_disable(pc->pclk);
+	if (!pc->oneshot_en)
+		clk_disable(pc->pclk);
 
 	return ret;
 }
@@ -349,11 +441,20 @@
 };
 MODULE_DEVICE_TABLE(of, rockchip_pwm_dt_ids);
 
+static int rockchip_pwm_get_channel_id(const char *name)
+{
+	int len = strlen(name);
+
+	return name[len - 2] - '0';
+}
+
 static int rockchip_pwm_probe(struct platform_device *pdev)
 {
 	const struct of_device_id *id;
 	struct rockchip_pwm_chip *pc;
 	struct resource *r;
+	u32 enable_conf, ctrl;
+	bool enabled;
 	int ret, count;
 
 	id = of_match_device(rockchip_pwm_dt_ids, &pdev->dev);
@@ -373,13 +474,9 @@
 	pc->clk = devm_clk_get(&pdev->dev, "pwm");
 	if (IS_ERR(pc->clk)) {
 		pc->clk = devm_clk_get(&pdev->dev, NULL);
-		if (IS_ERR(pc->clk)) {
-			ret = PTR_ERR(pc->clk);
-			if (ret != -EPROBE_DEFER)
-				dev_err(&pdev->dev, "Can't get bus clk: %d\n",
-					ret);
-			return ret;
-		}
+		if (IS_ERR(pc->clk))
+			return dev_err_probe(&pdev->dev, PTR_ERR(pc->clk),
+					     "Can't get bus clk\n");
 	}
 
 	count = of_count_phandle_with_args(pdev->dev.of_node,
@@ -402,22 +499,48 @@
 		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;
+	}
+
+	pc->channel_id = rockchip_pwm_get_channel_id(pdev->dev.of_node->full_name);
+	if (pc->channel_id < 0 || pc->channel_id >= PWM_MAX_CHANNEL_NUM) {
+		dev_err(&pdev->dev, "Channel id is out of range: %d\n", pc->channel_id);
+		ret = -EINVAL;
+		goto err_pclk;
+	}
+
+	if (IS_ENABLED(CONFIG_PWM_ROCKCHIP_ONESHOT)) {
+		pc->irq = platform_get_irq(pdev, 0);
+		if (pc->irq < 0) {
+			dev_err(&pdev->dev, "Get oneshot mode irq failed\n");
+			ret = pc->irq;
+			goto err_pclk;
+		}
+
+		ret = devm_request_irq(&pdev->dev, pc->irq, rockchip_pwm_oneshot_irq,
+				       IRQF_NO_SUSPEND | IRQF_SHARED,
+				       "rk_pwm_oneshot_irq", pc);
+		if (ret) {
+			dev_err(&pdev->dev, "Claim oneshot IRQ failed\n");
+			goto err_pclk;
+		}
 	}
 
 	pc->pinctrl = devm_pinctrl_get(&pdev->dev);
 	if (IS_ERR(pc->pinctrl)) {
 		dev_err(&pdev->dev, "Get pinctrl failed!\n");
-		return PTR_ERR(pc->pinctrl);
+		ret = PTR_ERR(pc->pinctrl);
+		goto err_pclk;
 	}
 
 	pc->active_state = pinctrl_lookup_state(pc->pinctrl, "active");
 	if (IS_ERR(pc->active_state)) {
 		dev_err(&pdev->dev, "No active pinctrl state\n");
-		return PTR_ERR(pc->active_state);
+		ret = PTR_ERR(pc->active_state);
+		goto err_pclk;
 	}
 
 	platform_set_drvdata(pdev, pc);
@@ -425,7 +548,7 @@
 	pc->data = id->data;
 	pc->chip.dev = &pdev->dev;
 	pc->chip.ops = &rockchip_pwm_ops;
-	pc->chip.base = -1;
+	pc->chip.base = of_alias_get_id(pdev->dev.of_node, "pwm");
 	pc->chip.npwm = 1;
 	pc->clk_rate = clk_get_rate(pc->clk);
 
@@ -433,6 +556,10 @@
 		pc->chip.of_xlate = of_pwm_xlate_with_flags;
 		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;
 
 	pc->center_aligned =
 		device_property_read_bool(&pdev->dev, "center-aligned");
@@ -444,13 +571,15 @@
 	}
 
 	/* Keep the PWM clk enabled if the PWM appears to be up and running. */
-	if (!pwm_is_enabled(pc->chip.pwms))
+	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);
 
@@ -460,7 +589,27 @@
 static int rockchip_pwm_remove(struct platform_device *pdev)
 {
 	struct rockchip_pwm_chip *pc = platform_get_drvdata(pdev);
+	struct pwm_state state;
+	u32 val;
 
+	/*
+	 * For oneshot mode, it is needed to wait for bit PWM_ENABLE
+	 * to 0, which is automatic if all periods have been sent.
+	 */
+	pwm_get_state(&pc->chip.pwms[0], &state);
+	if (state.enabled) {
+		if (pc->oneshot_en) {
+			if (readl_poll_timeout(pc->base + pc->data->regs.ctrl,
+					       val, !(val & PWM_ENABLE), 1000, 10 * 1000))
+				dev_err(&pdev->dev, "Wait for oneshot to complete failed\n");
+		} else {
+			state.enabled = false;
+			pwm_apply_state(&pc->chip.pwms[0], &state);
+		}
+	}
+
+	if (pc->oneshot_en)
+		clk_disable(pc->pclk);
 	clk_unprepare(pc->pclk);
 	clk_unprepare(pc->clk);
 

--
Gitblit v1.6.2