xref: /linux/drivers/hwmon/aspeed-g6-pwm-tach.c (revision 36ec807b627b4c0a0a382f0ae48eac7187d14b2b)
17e1449cdSBilly Tsai // SPDX-License-Identifier: GPL-2.0-or-later
27e1449cdSBilly Tsai /*
37e1449cdSBilly Tsai  * Copyright (C) 2021 Aspeed Technology Inc.
47e1449cdSBilly Tsai  *
57e1449cdSBilly Tsai  * PWM/TACH controller driver for Aspeed ast2600 SoCs.
67e1449cdSBilly Tsai  * This drivers doesn't support earlier version of the IP.
77e1449cdSBilly Tsai  *
87e1449cdSBilly Tsai  * The hardware operates in time quantities of length
97e1449cdSBilly Tsai  * Q := (DIV_L + 1) << DIV_H / input-clk
107e1449cdSBilly Tsai  * The length of a PWM period is (DUTY_CYCLE_PERIOD + 1) * Q.
117e1449cdSBilly Tsai  * The maximal value for DUTY_CYCLE_PERIOD is used here to provide
127e1449cdSBilly Tsai  * a fine grained selection for the duty cycle.
137e1449cdSBilly Tsai  *
147e1449cdSBilly Tsai  * This driver uses DUTY_CYCLE_RISING_POINT = 0, so from the start of a
157e1449cdSBilly Tsai  * period the output is active until DUTY_CYCLE_FALLING_POINT * Q. Note
167e1449cdSBilly Tsai  * that if DUTY_CYCLE_RISING_POINT = DUTY_CYCLE_FALLING_POINT the output is
177e1449cdSBilly Tsai  * always active.
187e1449cdSBilly Tsai  *
197e1449cdSBilly Tsai  * Register usage:
207e1449cdSBilly Tsai  * PIN_ENABLE: When it is unset the pwm controller will emit inactive level to the external.
217e1449cdSBilly Tsai  * Use to determine whether the PWM channel is enabled or disabled
227e1449cdSBilly Tsai  * CLK_ENABLE: When it is unset the pwm controller will assert the duty counter reset and
237e1449cdSBilly Tsai  * emit inactive level to the PIN_ENABLE mux after that the driver can still change the pwm period
247e1449cdSBilly Tsai  * and duty and the value will apply when CLK_ENABLE be set again.
257e1449cdSBilly Tsai  * Use to determine whether duty_cycle bigger than 0.
267e1449cdSBilly Tsai  * PWM_ASPEED_CTRL_INVERSE: When it is toggled the output value will inverse immediately.
277e1449cdSBilly Tsai  * PWM_ASPEED_DUTY_CYCLE_FALLING_POINT/PWM_ASPEED_DUTY_CYCLE_RISING_POINT: When these two
287e1449cdSBilly Tsai  * values are equal it means the duty cycle = 100%.
297e1449cdSBilly Tsai  *
307e1449cdSBilly Tsai  * The glitch may generate at:
317e1449cdSBilly Tsai  * - Enabled changing when the duty_cycle bigger than 0% and less than 100%.
327e1449cdSBilly Tsai  * - Polarity changing when the duty_cycle bigger than 0% and less than 100%.
337e1449cdSBilly Tsai  *
347e1449cdSBilly Tsai  * Limitations:
357e1449cdSBilly Tsai  * - When changing both duty cycle and period, we cannot prevent in
367e1449cdSBilly Tsai  *   software that the output might produce a period with mixed
377e1449cdSBilly Tsai  *   settings.
387e1449cdSBilly Tsai  * - Disabling the PWM doesn't complete the current period.
397e1449cdSBilly Tsai  *
407e1449cdSBilly Tsai  * Improvements:
417e1449cdSBilly Tsai  * - When only changing one of duty cycle or period, our pwm controller will not
427e1449cdSBilly Tsai  *   generate the glitch, the configure will change at next cycle of pwm.
437e1449cdSBilly Tsai  *   This improvement can disable/enable through PWM_ASPEED_CTRL_DUTY_SYNC_DISABLE.
447e1449cdSBilly Tsai  */
457e1449cdSBilly Tsai 
467e1449cdSBilly Tsai #include <linux/bitfield.h>
477e1449cdSBilly Tsai #include <linux/clk.h>
487e1449cdSBilly Tsai #include <linux/delay.h>
497e1449cdSBilly Tsai #include <linux/errno.h>
507e1449cdSBilly Tsai #include <linux/hwmon.h>
517e1449cdSBilly Tsai #include <linux/io.h>
527e1449cdSBilly Tsai #include <linux/kernel.h>
537e1449cdSBilly Tsai #include <linux/math64.h>
547e1449cdSBilly Tsai #include <linux/module.h>
557e1449cdSBilly Tsai #include <linux/of_device.h>
567e1449cdSBilly Tsai #include <linux/of_platform.h>
577e1449cdSBilly Tsai #include <linux/platform_device.h>
587e1449cdSBilly Tsai #include <linux/pwm.h>
597e1449cdSBilly Tsai #include <linux/reset.h>
607e1449cdSBilly Tsai #include <linux/sysfs.h>
617e1449cdSBilly Tsai 
627e1449cdSBilly Tsai /* The channel number of Aspeed pwm controller */
637e1449cdSBilly Tsai #define PWM_ASPEED_NR_PWMS			16
647e1449cdSBilly Tsai /* PWM Control Register */
657e1449cdSBilly Tsai #define PWM_ASPEED_CTRL(ch)			((ch) * 0x10 + 0x00)
667e1449cdSBilly Tsai #define PWM_ASPEED_CTRL_LOAD_SEL_RISING_AS_WDT	BIT(19)
677e1449cdSBilly Tsai #define PWM_ASPEED_CTRL_DUTY_LOAD_AS_WDT_ENABLE	BIT(18)
687e1449cdSBilly Tsai #define PWM_ASPEED_CTRL_DUTY_SYNC_DISABLE	BIT(17)
697e1449cdSBilly Tsai #define PWM_ASPEED_CTRL_CLK_ENABLE		BIT(16)
707e1449cdSBilly Tsai #define PWM_ASPEED_CTRL_LEVEL_OUTPUT		BIT(15)
717e1449cdSBilly Tsai #define PWM_ASPEED_CTRL_INVERSE			BIT(14)
727e1449cdSBilly Tsai #define PWM_ASPEED_CTRL_OPEN_DRAIN_ENABLE	BIT(13)
737e1449cdSBilly Tsai #define PWM_ASPEED_CTRL_PIN_ENABLE		BIT(12)
747e1449cdSBilly Tsai #define PWM_ASPEED_CTRL_CLK_DIV_H		GENMASK(11, 8)
757e1449cdSBilly Tsai #define PWM_ASPEED_CTRL_CLK_DIV_L		GENMASK(7, 0)
767e1449cdSBilly Tsai 
777e1449cdSBilly Tsai /* PWM Duty Cycle Register */
787e1449cdSBilly Tsai #define PWM_ASPEED_DUTY_CYCLE(ch)		((ch) * 0x10 + 0x04)
797e1449cdSBilly Tsai #define PWM_ASPEED_DUTY_CYCLE_PERIOD		GENMASK(31, 24)
807e1449cdSBilly Tsai #define PWM_ASPEED_DUTY_CYCLE_POINT_AS_WDT	GENMASK(23, 16)
817e1449cdSBilly Tsai #define PWM_ASPEED_DUTY_CYCLE_FALLING_POINT	GENMASK(15, 8)
827e1449cdSBilly Tsai #define PWM_ASPEED_DUTY_CYCLE_RISING_POINT	GENMASK(7, 0)
837e1449cdSBilly Tsai 
847e1449cdSBilly Tsai /* PWM fixed value */
857e1449cdSBilly Tsai #define PWM_ASPEED_FIXED_PERIOD			FIELD_MAX(PWM_ASPEED_DUTY_CYCLE_PERIOD)
867e1449cdSBilly Tsai 
877e1449cdSBilly Tsai /* The channel number of Aspeed tach controller */
887e1449cdSBilly Tsai #define TACH_ASPEED_NR_TACHS		16
897e1449cdSBilly Tsai /* TACH Control Register */
907e1449cdSBilly Tsai #define TACH_ASPEED_CTRL(ch)		(((ch) * 0x10) + 0x08)
917e1449cdSBilly Tsai #define TACH_ASPEED_IER			BIT(31)
927e1449cdSBilly Tsai #define TACH_ASPEED_INVERS_LIMIT	BIT(30)
937e1449cdSBilly Tsai #define TACH_ASPEED_LOOPBACK		BIT(29)
947e1449cdSBilly Tsai #define TACH_ASPEED_ENABLE		BIT(28)
957e1449cdSBilly Tsai #define TACH_ASPEED_DEBOUNCE_MASK	GENMASK(27, 26)
967e1449cdSBilly Tsai #define TACH_ASPEED_DEBOUNCE_BIT	26
977e1449cdSBilly Tsai #define TACH_ASPEED_IO_EDGE_MASK	GENMASK(25, 24)
987e1449cdSBilly Tsai #define TACH_ASPEED_IO_EDGE_BIT		24
997e1449cdSBilly Tsai #define TACH_ASPEED_CLK_DIV_T_MASK	GENMASK(23, 20)
1007e1449cdSBilly Tsai #define TACH_ASPEED_CLK_DIV_BIT		20
1017e1449cdSBilly Tsai #define TACH_ASPEED_THRESHOLD_MASK	GENMASK(19, 0)
1027e1449cdSBilly Tsai /* [27:26] */
1037e1449cdSBilly Tsai #define DEBOUNCE_3_CLK			0x00
1047e1449cdSBilly Tsai #define DEBOUNCE_2_CLK			0x01
1057e1449cdSBilly Tsai #define DEBOUNCE_1_CLK			0x02
1067e1449cdSBilly Tsai #define DEBOUNCE_0_CLK			0x03
1077e1449cdSBilly Tsai /* [25:24] */
1087e1449cdSBilly Tsai #define F2F_EDGES			0x00
1097e1449cdSBilly Tsai #define R2R_EDGES			0x01
1107e1449cdSBilly Tsai #define BOTH_EDGES			0x02
1117e1449cdSBilly Tsai /* [23:20] */
1127e1449cdSBilly Tsai /* divisor = 4 to the nth power, n = register value */
1137e1449cdSBilly Tsai #define DEFAULT_TACH_DIV		1024
1147e1449cdSBilly Tsai #define DIV_TO_REG(divisor)		(ilog2(divisor) >> 1)
1157e1449cdSBilly Tsai 
1167e1449cdSBilly Tsai /* TACH Status Register */
1177e1449cdSBilly Tsai #define TACH_ASPEED_STS(ch)		(((ch) * 0x10) + 0x0C)
1187e1449cdSBilly Tsai 
1197e1449cdSBilly Tsai /*PWM_TACH_STS */
1207e1449cdSBilly Tsai #define TACH_ASPEED_ISR			BIT(31)
1217e1449cdSBilly Tsai #define TACH_ASPEED_PWM_OUT		BIT(25)
1227e1449cdSBilly Tsai #define TACH_ASPEED_PWM_OEN		BIT(24)
1237e1449cdSBilly Tsai #define TACH_ASPEED_DEB_INPUT		BIT(23)
1247e1449cdSBilly Tsai #define TACH_ASPEED_RAW_INPUT		BIT(22)
1257e1449cdSBilly Tsai #define TACH_ASPEED_VALUE_UPDATE	BIT(21)
1267e1449cdSBilly Tsai #define TACH_ASPEED_FULL_MEASUREMENT	BIT(20)
1277e1449cdSBilly Tsai #define TACH_ASPEED_VALUE_MASK		GENMASK(19, 0)
1287e1449cdSBilly Tsai /**********************************************************
1297e1449cdSBilly Tsai  * Software setting
1307e1449cdSBilly Tsai  *********************************************************/
1317e1449cdSBilly Tsai #define DEFAULT_FAN_PULSE_PR		2
1327e1449cdSBilly Tsai 
1337e1449cdSBilly Tsai struct aspeed_pwm_tach_data {
1347e1449cdSBilly Tsai 	struct device *dev;
1357e1449cdSBilly Tsai 	void __iomem *base;
1367e1449cdSBilly Tsai 	struct clk *clk;
1377e1449cdSBilly Tsai 	struct reset_control *reset;
1387e1449cdSBilly Tsai 	unsigned long clk_rate;
1397e1449cdSBilly Tsai 	bool tach_present[TACH_ASPEED_NR_TACHS];
1407e1449cdSBilly Tsai 	u32 tach_divisor;
1417e1449cdSBilly Tsai };
1427e1449cdSBilly Tsai 
1437e1449cdSBilly Tsai static inline struct aspeed_pwm_tach_data *
1447e1449cdSBilly Tsai aspeed_pwm_chip_to_data(struct pwm_chip *chip)
1457e1449cdSBilly Tsai {
14679dedfadSUwe Kleine-König 	return pwmchip_get_drvdata(chip);
1477e1449cdSBilly Tsai }
1487e1449cdSBilly Tsai 
1497e1449cdSBilly Tsai static int aspeed_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
1507e1449cdSBilly Tsai 				struct pwm_state *state)
1517e1449cdSBilly Tsai {
1527e1449cdSBilly Tsai 	struct aspeed_pwm_tach_data *priv = aspeed_pwm_chip_to_data(chip);
1537e1449cdSBilly Tsai 	u32 hwpwm = pwm->hwpwm;
1547e1449cdSBilly Tsai 	bool polarity, pin_en, clk_en;
1557e1449cdSBilly Tsai 	u32 duty_pt, val;
1567e1449cdSBilly Tsai 	u64 div_h, div_l, duty_cycle_period, dividend;
1577e1449cdSBilly Tsai 
1587e1449cdSBilly Tsai 	val = readl(priv->base + PWM_ASPEED_CTRL(hwpwm));
1597e1449cdSBilly Tsai 	polarity = FIELD_GET(PWM_ASPEED_CTRL_INVERSE, val);
1607e1449cdSBilly Tsai 	pin_en = FIELD_GET(PWM_ASPEED_CTRL_PIN_ENABLE, val);
1617e1449cdSBilly Tsai 	clk_en = FIELD_GET(PWM_ASPEED_CTRL_CLK_ENABLE, val);
1627e1449cdSBilly Tsai 	div_h = FIELD_GET(PWM_ASPEED_CTRL_CLK_DIV_H, val);
1637e1449cdSBilly Tsai 	div_l = FIELD_GET(PWM_ASPEED_CTRL_CLK_DIV_L, val);
1647e1449cdSBilly Tsai 	val = readl(priv->base + PWM_ASPEED_DUTY_CYCLE(hwpwm));
1657e1449cdSBilly Tsai 	duty_pt = FIELD_GET(PWM_ASPEED_DUTY_CYCLE_FALLING_POINT, val);
1667e1449cdSBilly Tsai 	duty_cycle_period = FIELD_GET(PWM_ASPEED_DUTY_CYCLE_PERIOD, val);
1677e1449cdSBilly Tsai 	/*
1687e1449cdSBilly Tsai 	 * This multiplication doesn't overflow, the upper bound is
1697e1449cdSBilly Tsai 	 * 1000000000 * 256 * 256 << 15 = 0x1dcd650000000000
1707e1449cdSBilly Tsai 	 */
1717e1449cdSBilly Tsai 	dividend = (u64)NSEC_PER_SEC * (div_l + 1) * (duty_cycle_period + 1)
1727e1449cdSBilly Tsai 		       << div_h;
1737e1449cdSBilly Tsai 	state->period = DIV_ROUND_UP_ULL(dividend, priv->clk_rate);
1747e1449cdSBilly Tsai 
1757e1449cdSBilly Tsai 	if (clk_en && duty_pt) {
1767e1449cdSBilly Tsai 		dividend = (u64)NSEC_PER_SEC * (div_l + 1) * duty_pt
1777e1449cdSBilly Tsai 				 << div_h;
1787e1449cdSBilly Tsai 		state->duty_cycle = DIV_ROUND_UP_ULL(dividend, priv->clk_rate);
1797e1449cdSBilly Tsai 	} else {
1807e1449cdSBilly Tsai 		state->duty_cycle = clk_en ? state->period : 0;
1817e1449cdSBilly Tsai 	}
1827e1449cdSBilly Tsai 	state->polarity = polarity ? PWM_POLARITY_INVERSED : PWM_POLARITY_NORMAL;
1837e1449cdSBilly Tsai 	state->enabled = pin_en;
1847e1449cdSBilly Tsai 	return 0;
1857e1449cdSBilly Tsai }
1867e1449cdSBilly Tsai 
1877e1449cdSBilly Tsai static int aspeed_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
1887e1449cdSBilly Tsai 			    const struct pwm_state *state)
1897e1449cdSBilly Tsai {
1907e1449cdSBilly Tsai 	struct aspeed_pwm_tach_data *priv = aspeed_pwm_chip_to_data(chip);
1917e1449cdSBilly Tsai 	u32 hwpwm = pwm->hwpwm, duty_pt, val;
1927e1449cdSBilly Tsai 	u64 div_h, div_l, divisor, expect_period;
1937e1449cdSBilly Tsai 	bool clk_en;
1947e1449cdSBilly Tsai 
1957e1449cdSBilly Tsai 	expect_period = div64_u64(ULLONG_MAX, (u64)priv->clk_rate);
1967e1449cdSBilly Tsai 	expect_period = min(expect_period, state->period);
197940052bcSUwe Kleine-König 	dev_dbg(pwmchip_parent(chip), "expect period: %lldns, duty_cycle: %lldns",
1987e1449cdSBilly Tsai 		expect_period, state->duty_cycle);
1997e1449cdSBilly Tsai 	/*
2007e1449cdSBilly Tsai 	 * Pick the smallest value for div_h so that div_l can be the biggest
2017e1449cdSBilly Tsai 	 * which results in a finer resolution near the target period value.
2027e1449cdSBilly Tsai 	 */
2037e1449cdSBilly Tsai 	divisor = (u64)NSEC_PER_SEC * (PWM_ASPEED_FIXED_PERIOD + 1) *
2047e1449cdSBilly Tsai 		  (FIELD_MAX(PWM_ASPEED_CTRL_CLK_DIV_L) + 1);
2057e1449cdSBilly Tsai 	div_h = order_base_2(DIV64_U64_ROUND_UP(priv->clk_rate * expect_period, divisor));
2067e1449cdSBilly Tsai 	if (div_h > 0xf)
2077e1449cdSBilly Tsai 		div_h = 0xf;
2087e1449cdSBilly Tsai 
2097e1449cdSBilly Tsai 	divisor = ((u64)NSEC_PER_SEC * (PWM_ASPEED_FIXED_PERIOD + 1)) << div_h;
2107e1449cdSBilly Tsai 	div_l = div64_u64(priv->clk_rate * expect_period, divisor);
2117e1449cdSBilly Tsai 
2127e1449cdSBilly Tsai 	if (div_l == 0)
2137e1449cdSBilly Tsai 		return -ERANGE;
2147e1449cdSBilly Tsai 
2157e1449cdSBilly Tsai 	div_l -= 1;
2167e1449cdSBilly Tsai 
2177e1449cdSBilly Tsai 	if (div_l > 255)
2187e1449cdSBilly Tsai 		div_l = 255;
2197e1449cdSBilly Tsai 
220940052bcSUwe Kleine-König 	dev_dbg(pwmchip_parent(chip), "clk source: %ld div_h %lld, div_l : %lld\n",
2217e1449cdSBilly Tsai 		priv->clk_rate, div_h, div_l);
2227e1449cdSBilly Tsai 	/* duty_pt = duty_cycle * (PERIOD + 1) / period */
2237e1449cdSBilly Tsai 	duty_pt = div64_u64(state->duty_cycle * priv->clk_rate,
2247e1449cdSBilly Tsai 			    (u64)NSEC_PER_SEC * (div_l + 1) << div_h);
225940052bcSUwe Kleine-König 	dev_dbg(pwmchip_parent(chip), "duty_cycle = %lld, duty_pt = %d\n",
2267e1449cdSBilly Tsai 		state->duty_cycle, duty_pt);
2277e1449cdSBilly Tsai 
2287e1449cdSBilly Tsai 	/*
2297e1449cdSBilly Tsai 	 * Fixed DUTY_CYCLE_PERIOD to its max value to get a
2307e1449cdSBilly Tsai 	 * fine-grained resolution for duty_cycle at the expense of a
2317e1449cdSBilly Tsai 	 * coarser period resolution.
2327e1449cdSBilly Tsai 	 */
2337e1449cdSBilly Tsai 	val = readl(priv->base + PWM_ASPEED_DUTY_CYCLE(hwpwm));
2347e1449cdSBilly Tsai 	val &= ~PWM_ASPEED_DUTY_CYCLE_PERIOD;
2357e1449cdSBilly Tsai 	val |= FIELD_PREP(PWM_ASPEED_DUTY_CYCLE_PERIOD,
2367e1449cdSBilly Tsai 			  PWM_ASPEED_FIXED_PERIOD);
2377e1449cdSBilly Tsai 	writel(val, priv->base + PWM_ASPEED_DUTY_CYCLE(hwpwm));
2387e1449cdSBilly Tsai 
2397e1449cdSBilly Tsai 	if (duty_pt == 0) {
2407e1449cdSBilly Tsai 		/* emit inactive level and assert the duty counter reset */
2417e1449cdSBilly Tsai 		clk_en = 0;
2427e1449cdSBilly Tsai 	} else {
2437e1449cdSBilly Tsai 		clk_en = 1;
2447e1449cdSBilly Tsai 		if (duty_pt >= (PWM_ASPEED_FIXED_PERIOD + 1))
2457e1449cdSBilly Tsai 			duty_pt = 0;
2467e1449cdSBilly Tsai 		val = readl(priv->base + PWM_ASPEED_DUTY_CYCLE(hwpwm));
2477e1449cdSBilly Tsai 		val &= ~(PWM_ASPEED_DUTY_CYCLE_RISING_POINT |
2487e1449cdSBilly Tsai 			 PWM_ASPEED_DUTY_CYCLE_FALLING_POINT);
2497e1449cdSBilly Tsai 		val |= FIELD_PREP(PWM_ASPEED_DUTY_CYCLE_FALLING_POINT, duty_pt);
2507e1449cdSBilly Tsai 		writel(val, priv->base + PWM_ASPEED_DUTY_CYCLE(hwpwm));
2517e1449cdSBilly Tsai 	}
2527e1449cdSBilly Tsai 
2537e1449cdSBilly Tsai 	val = readl(priv->base + PWM_ASPEED_CTRL(hwpwm));
2547e1449cdSBilly Tsai 	val &= ~(PWM_ASPEED_CTRL_CLK_DIV_H | PWM_ASPEED_CTRL_CLK_DIV_L |
2557e1449cdSBilly Tsai 		 PWM_ASPEED_CTRL_PIN_ENABLE | PWM_ASPEED_CTRL_CLK_ENABLE |
2567e1449cdSBilly Tsai 		 PWM_ASPEED_CTRL_INVERSE);
2577e1449cdSBilly Tsai 	val |= FIELD_PREP(PWM_ASPEED_CTRL_CLK_DIV_H, div_h) |
2587e1449cdSBilly Tsai 	       FIELD_PREP(PWM_ASPEED_CTRL_CLK_DIV_L, div_l) |
2597e1449cdSBilly Tsai 	       FIELD_PREP(PWM_ASPEED_CTRL_PIN_ENABLE, state->enabled) |
2607e1449cdSBilly Tsai 	       FIELD_PREP(PWM_ASPEED_CTRL_CLK_ENABLE, clk_en) |
2617e1449cdSBilly Tsai 	       FIELD_PREP(PWM_ASPEED_CTRL_INVERSE, state->polarity);
2627e1449cdSBilly Tsai 	writel(val, priv->base + PWM_ASPEED_CTRL(hwpwm));
2637e1449cdSBilly Tsai 
2647e1449cdSBilly Tsai 	return 0;
2657e1449cdSBilly Tsai }
2667e1449cdSBilly Tsai 
2677e1449cdSBilly Tsai static const struct pwm_ops aspeed_pwm_ops = {
2687e1449cdSBilly Tsai 	.apply = aspeed_pwm_apply,
2697e1449cdSBilly Tsai 	.get_state = aspeed_pwm_get_state,
2707e1449cdSBilly Tsai };
2717e1449cdSBilly Tsai 
2727e1449cdSBilly Tsai static void aspeed_tach_ch_enable(struct aspeed_pwm_tach_data *priv, u8 tach_ch,
2737e1449cdSBilly Tsai 				  bool enable)
2747e1449cdSBilly Tsai {
2757e1449cdSBilly Tsai 	if (enable)
2767e1449cdSBilly Tsai 		writel(readl(priv->base + TACH_ASPEED_CTRL(tach_ch)) |
2777e1449cdSBilly Tsai 			       TACH_ASPEED_ENABLE,
2787e1449cdSBilly Tsai 		       priv->base + TACH_ASPEED_CTRL(tach_ch));
2797e1449cdSBilly Tsai 	else
2807e1449cdSBilly Tsai 		writel(readl(priv->base + TACH_ASPEED_CTRL(tach_ch)) &
2817e1449cdSBilly Tsai 			       ~TACH_ASPEED_ENABLE,
2827e1449cdSBilly Tsai 		       priv->base + TACH_ASPEED_CTRL(tach_ch));
2837e1449cdSBilly Tsai }
2847e1449cdSBilly Tsai 
2857e1449cdSBilly Tsai static int aspeed_tach_val_to_rpm(struct aspeed_pwm_tach_data *priv, u32 tach_val)
2867e1449cdSBilly Tsai {
2877e1449cdSBilly Tsai 	u64 rpm;
2887e1449cdSBilly Tsai 	u32 tach_div;
2897e1449cdSBilly Tsai 
2907e1449cdSBilly Tsai 	tach_div = tach_val * priv->tach_divisor * DEFAULT_FAN_PULSE_PR;
2917e1449cdSBilly Tsai 
2927e1449cdSBilly Tsai 	dev_dbg(priv->dev, "clk %ld, tach_val %d , tach_div %d\n",
2937e1449cdSBilly Tsai 		priv->clk_rate, tach_val, tach_div);
2947e1449cdSBilly Tsai 
2957e1449cdSBilly Tsai 	rpm = (u64)priv->clk_rate * 60;
2967e1449cdSBilly Tsai 	do_div(rpm, tach_div);
2977e1449cdSBilly Tsai 
2987e1449cdSBilly Tsai 	return (int)rpm;
2997e1449cdSBilly Tsai }
3007e1449cdSBilly Tsai 
3017e1449cdSBilly Tsai static int aspeed_get_fan_tach_ch_rpm(struct aspeed_pwm_tach_data *priv,
3027e1449cdSBilly Tsai 				      u8 fan_tach_ch)
3037e1449cdSBilly Tsai {
3047e1449cdSBilly Tsai 	u32 val;
3057e1449cdSBilly Tsai 
3067e1449cdSBilly Tsai 	val = readl(priv->base + TACH_ASPEED_STS(fan_tach_ch));
3077e1449cdSBilly Tsai 
3087e1449cdSBilly Tsai 	if (!(val & TACH_ASPEED_FULL_MEASUREMENT))
3097e1449cdSBilly Tsai 		return 0;
3107e1449cdSBilly Tsai 	val = FIELD_GET(TACH_ASPEED_VALUE_MASK, val);
3117e1449cdSBilly Tsai 	return aspeed_tach_val_to_rpm(priv, val);
3127e1449cdSBilly Tsai }
3137e1449cdSBilly Tsai 
3147e1449cdSBilly Tsai static int aspeed_tach_hwmon_read(struct device *dev,
3157e1449cdSBilly Tsai 				  enum hwmon_sensor_types type, u32 attr,
3167e1449cdSBilly Tsai 				  int channel, long *val)
3177e1449cdSBilly Tsai {
3187e1449cdSBilly Tsai 	struct aspeed_pwm_tach_data *priv = dev_get_drvdata(dev);
3197e1449cdSBilly Tsai 	u32 reg_val;
3207e1449cdSBilly Tsai 
3217e1449cdSBilly Tsai 	switch (attr) {
3227e1449cdSBilly Tsai 	case hwmon_fan_input:
3237e1449cdSBilly Tsai 		*val = aspeed_get_fan_tach_ch_rpm(priv, channel);
3247e1449cdSBilly Tsai 		break;
3257e1449cdSBilly Tsai 	case hwmon_fan_div:
3267e1449cdSBilly Tsai 		reg_val = readl(priv->base + TACH_ASPEED_CTRL(channel));
3277e1449cdSBilly Tsai 		reg_val = FIELD_GET(TACH_ASPEED_CLK_DIV_T_MASK, reg_val);
3287e1449cdSBilly Tsai 		*val = BIT(reg_val << 1);
3297e1449cdSBilly Tsai 		break;
3307e1449cdSBilly Tsai 	default:
3317e1449cdSBilly Tsai 		return -EOPNOTSUPP;
3327e1449cdSBilly Tsai 	}
3337e1449cdSBilly Tsai 	return 0;
3347e1449cdSBilly Tsai }
3357e1449cdSBilly Tsai 
3367e1449cdSBilly Tsai static int aspeed_tach_hwmon_write(struct device *dev,
3377e1449cdSBilly Tsai 				   enum hwmon_sensor_types type, u32 attr,
3387e1449cdSBilly Tsai 				   int channel, long val)
3397e1449cdSBilly Tsai {
3407e1449cdSBilly Tsai 	struct aspeed_pwm_tach_data *priv = dev_get_drvdata(dev);
3417e1449cdSBilly Tsai 	u32 reg_val;
3427e1449cdSBilly Tsai 
3437e1449cdSBilly Tsai 	switch (attr) {
3447e1449cdSBilly Tsai 	case hwmon_fan_div:
3457e1449cdSBilly Tsai 		if (!is_power_of_2(val) || (ilog2(val) % 2) ||
3467e1449cdSBilly Tsai 		    DIV_TO_REG(val) > 0xb)
3477e1449cdSBilly Tsai 			return -EINVAL;
3487e1449cdSBilly Tsai 		priv->tach_divisor = val;
3497e1449cdSBilly Tsai 		reg_val = readl(priv->base + TACH_ASPEED_CTRL(channel));
3507e1449cdSBilly Tsai 		reg_val &= ~TACH_ASPEED_CLK_DIV_T_MASK;
3517e1449cdSBilly Tsai 		reg_val |= FIELD_PREP(TACH_ASPEED_CLK_DIV_T_MASK,
3527e1449cdSBilly Tsai 				      DIV_TO_REG(priv->tach_divisor));
3537e1449cdSBilly Tsai 		writel(reg_val, priv->base + TACH_ASPEED_CTRL(channel));
3547e1449cdSBilly Tsai 		break;
3557e1449cdSBilly Tsai 	default:
3567e1449cdSBilly Tsai 		return -EOPNOTSUPP;
3577e1449cdSBilly Tsai 	}
3587e1449cdSBilly Tsai 
3597e1449cdSBilly Tsai 	return 0;
3607e1449cdSBilly Tsai }
3617e1449cdSBilly Tsai 
3627e1449cdSBilly Tsai static umode_t aspeed_tach_dev_is_visible(const void *drvdata,
3637e1449cdSBilly Tsai 					  enum hwmon_sensor_types type,
3647e1449cdSBilly Tsai 					  u32 attr, int channel)
3657e1449cdSBilly Tsai {
3667e1449cdSBilly Tsai 	const struct aspeed_pwm_tach_data *priv = drvdata;
3677e1449cdSBilly Tsai 
3687e1449cdSBilly Tsai 	if (!priv->tach_present[channel])
3697e1449cdSBilly Tsai 		return 0;
3707e1449cdSBilly Tsai 	switch (attr) {
3717e1449cdSBilly Tsai 	case hwmon_fan_input:
3727e1449cdSBilly Tsai 		return 0444;
3737e1449cdSBilly Tsai 	case hwmon_fan_div:
3747e1449cdSBilly Tsai 		return 0644;
3757e1449cdSBilly Tsai 	}
3767e1449cdSBilly Tsai 	return 0;
3777e1449cdSBilly Tsai }
3787e1449cdSBilly Tsai 
3797e1449cdSBilly Tsai static const struct hwmon_ops aspeed_tach_ops = {
3807e1449cdSBilly Tsai 	.is_visible = aspeed_tach_dev_is_visible,
3817e1449cdSBilly Tsai 	.read = aspeed_tach_hwmon_read,
3827e1449cdSBilly Tsai 	.write = aspeed_tach_hwmon_write,
3837e1449cdSBilly Tsai };
3847e1449cdSBilly Tsai 
3857e1449cdSBilly Tsai static const struct hwmon_channel_info *aspeed_tach_info[] = {
3867e1449cdSBilly Tsai 	HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_DIV, HWMON_F_INPUT | HWMON_F_DIV,
3877e1449cdSBilly Tsai 			   HWMON_F_INPUT | HWMON_F_DIV, HWMON_F_INPUT | HWMON_F_DIV,
3887e1449cdSBilly Tsai 			   HWMON_F_INPUT | HWMON_F_DIV, HWMON_F_INPUT | HWMON_F_DIV,
3897e1449cdSBilly Tsai 			   HWMON_F_INPUT | HWMON_F_DIV, HWMON_F_INPUT | HWMON_F_DIV,
3907e1449cdSBilly Tsai 			   HWMON_F_INPUT | HWMON_F_DIV, HWMON_F_INPUT | HWMON_F_DIV,
3917e1449cdSBilly Tsai 			   HWMON_F_INPUT | HWMON_F_DIV, HWMON_F_INPUT | HWMON_F_DIV,
3927e1449cdSBilly Tsai 			   HWMON_F_INPUT | HWMON_F_DIV, HWMON_F_INPUT | HWMON_F_DIV,
3937e1449cdSBilly Tsai 			   HWMON_F_INPUT | HWMON_F_DIV, HWMON_F_INPUT | HWMON_F_DIV),
3947e1449cdSBilly Tsai 	NULL
3957e1449cdSBilly Tsai };
3967e1449cdSBilly Tsai 
3977e1449cdSBilly Tsai static const struct hwmon_chip_info aspeed_tach_chip_info = {
3987e1449cdSBilly Tsai 	.ops = &aspeed_tach_ops,
3997e1449cdSBilly Tsai 	.info = aspeed_tach_info,
4007e1449cdSBilly Tsai };
4017e1449cdSBilly Tsai 
4027e1449cdSBilly Tsai static void aspeed_present_fan_tach(struct aspeed_pwm_tach_data *priv, u8 *tach_ch, int count)
4037e1449cdSBilly Tsai {
4047e1449cdSBilly Tsai 	u8 ch, index;
4057e1449cdSBilly Tsai 	u32 val;
4067e1449cdSBilly Tsai 
4077e1449cdSBilly Tsai 	for (index = 0; index < count; index++) {
4087e1449cdSBilly Tsai 		ch = tach_ch[index];
4097e1449cdSBilly Tsai 		priv->tach_present[ch] = true;
4107e1449cdSBilly Tsai 		priv->tach_divisor = DEFAULT_TACH_DIV;
4117e1449cdSBilly Tsai 
4127e1449cdSBilly Tsai 		val = readl(priv->base + TACH_ASPEED_CTRL(ch));
4137e1449cdSBilly Tsai 		val &= ~(TACH_ASPEED_INVERS_LIMIT | TACH_ASPEED_DEBOUNCE_MASK |
4147e1449cdSBilly Tsai 			 TACH_ASPEED_IO_EDGE_MASK | TACH_ASPEED_CLK_DIV_T_MASK |
4157e1449cdSBilly Tsai 			 TACH_ASPEED_THRESHOLD_MASK);
4167e1449cdSBilly Tsai 		val |= (DEBOUNCE_3_CLK << TACH_ASPEED_DEBOUNCE_BIT) |
4177e1449cdSBilly Tsai 		       F2F_EDGES |
4187e1449cdSBilly Tsai 		       FIELD_PREP(TACH_ASPEED_CLK_DIV_T_MASK,
4197e1449cdSBilly Tsai 				  DIV_TO_REG(priv->tach_divisor));
4207e1449cdSBilly Tsai 		writel(val, priv->base + TACH_ASPEED_CTRL(ch));
4217e1449cdSBilly Tsai 
4227e1449cdSBilly Tsai 		aspeed_tach_ch_enable(priv, ch, true);
4237e1449cdSBilly Tsai 	}
4247e1449cdSBilly Tsai }
4257e1449cdSBilly Tsai 
4267e1449cdSBilly Tsai static int aspeed_create_fan_monitor(struct device *dev,
4277e1449cdSBilly Tsai 				     struct device_node *child,
4287e1449cdSBilly Tsai 				     struct aspeed_pwm_tach_data *priv)
4297e1449cdSBilly Tsai {
4307e1449cdSBilly Tsai 	int ret, count;
4317e1449cdSBilly Tsai 	u8 *tach_ch;
4327e1449cdSBilly Tsai 
4337e1449cdSBilly Tsai 	count = of_property_count_u8_elems(child, "tach-ch");
4347e1449cdSBilly Tsai 	if (count < 1)
4357e1449cdSBilly Tsai 		return -EINVAL;
4367e1449cdSBilly Tsai 	tach_ch = devm_kcalloc(dev, count, sizeof(*tach_ch), GFP_KERNEL);
4377e1449cdSBilly Tsai 	if (!tach_ch)
4387e1449cdSBilly Tsai 		return -ENOMEM;
4397e1449cdSBilly Tsai 	ret = of_property_read_u8_array(child, "tach-ch", tach_ch, count);
4407e1449cdSBilly Tsai 	if (ret)
4417e1449cdSBilly Tsai 		return ret;
4427e1449cdSBilly Tsai 
4437e1449cdSBilly Tsai 	aspeed_present_fan_tach(priv, tach_ch, count);
4447e1449cdSBilly Tsai 
4457e1449cdSBilly Tsai 	return 0;
4467e1449cdSBilly Tsai }
4477e1449cdSBilly Tsai 
4487e1449cdSBilly Tsai static void aspeed_pwm_tach_reset_assert(void *data)
4497e1449cdSBilly Tsai {
4507e1449cdSBilly Tsai 	struct reset_control *rst = data;
4517e1449cdSBilly Tsai 
4527e1449cdSBilly Tsai 	reset_control_assert(rst);
4537e1449cdSBilly Tsai }
4547e1449cdSBilly Tsai 
4557e1449cdSBilly Tsai static int aspeed_pwm_tach_probe(struct platform_device *pdev)
4567e1449cdSBilly Tsai {
4577e1449cdSBilly Tsai 	struct device *dev = &pdev->dev, *hwmon;
4587e1449cdSBilly Tsai 	int ret;
4597e1449cdSBilly Tsai 	struct device_node *child;
4607e1449cdSBilly Tsai 	struct aspeed_pwm_tach_data *priv;
46179dedfadSUwe Kleine-König 	struct pwm_chip *chip;
4627e1449cdSBilly Tsai 
4637e1449cdSBilly Tsai 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
4647e1449cdSBilly Tsai 	if (!priv)
4657e1449cdSBilly Tsai 		return -ENOMEM;
4667e1449cdSBilly Tsai 	priv->dev = dev;
4677e1449cdSBilly Tsai 	priv->base = devm_platform_ioremap_resource(pdev, 0);
4687e1449cdSBilly Tsai 	if (IS_ERR(priv->base))
4697e1449cdSBilly Tsai 		return PTR_ERR(priv->base);
4707e1449cdSBilly Tsai 
4717e1449cdSBilly Tsai 	priv->clk = devm_clk_get_enabled(dev, NULL);
4727e1449cdSBilly Tsai 	if (IS_ERR(priv->clk))
4737e1449cdSBilly Tsai 		return dev_err_probe(dev, PTR_ERR(priv->clk),
4747e1449cdSBilly Tsai 				     "Couldn't get clock\n");
4757e1449cdSBilly Tsai 	priv->clk_rate = clk_get_rate(priv->clk);
4767e1449cdSBilly Tsai 	priv->reset = devm_reset_control_get_exclusive(dev, NULL);
4777e1449cdSBilly Tsai 	if (IS_ERR(priv->reset))
4787e1449cdSBilly Tsai 		return dev_err_probe(dev, PTR_ERR(priv->reset),
4797e1449cdSBilly Tsai 				     "Couldn't get reset control\n");
4807e1449cdSBilly Tsai 
4817e1449cdSBilly Tsai 	ret = reset_control_deassert(priv->reset);
4827e1449cdSBilly Tsai 	if (ret)
4837e1449cdSBilly Tsai 		return dev_err_probe(dev, ret,
4847e1449cdSBilly Tsai 				     "Couldn't deassert reset control\n");
4857e1449cdSBilly Tsai 	ret = devm_add_action_or_reset(dev, aspeed_pwm_tach_reset_assert,
4867e1449cdSBilly Tsai 				       priv->reset);
4877e1449cdSBilly Tsai 	if (ret)
4887e1449cdSBilly Tsai 		return ret;
4897e1449cdSBilly Tsai 
49079dedfadSUwe Kleine-König 	chip = devm_pwmchip_alloc(dev, PWM_ASPEED_NR_PWMS, 0);
49179dedfadSUwe Kleine-König 	if (IS_ERR(chip))
49279dedfadSUwe Kleine-König 		return PTR_ERR(chip);
4937e1449cdSBilly Tsai 
49479dedfadSUwe Kleine-König 	pwmchip_set_drvdata(chip, priv);
49579dedfadSUwe Kleine-König 	chip->ops = &aspeed_pwm_ops;
49679dedfadSUwe Kleine-König 
49779dedfadSUwe Kleine-König 	ret = devm_pwmchip_add(dev, chip);
4987e1449cdSBilly Tsai 	if (ret)
4997e1449cdSBilly Tsai 		return dev_err_probe(dev, ret, "Failed to add PWM chip\n");
5007e1449cdSBilly Tsai 
5017e1449cdSBilly Tsai 	for_each_child_of_node(dev->of_node, child) {
5027e1449cdSBilly Tsai 		ret = aspeed_create_fan_monitor(dev, child, priv);
5037e1449cdSBilly Tsai 		if (ret) {
5047e1449cdSBilly Tsai 			of_node_put(child);
5057e1449cdSBilly Tsai 			dev_warn(dev, "Failed to create fan %d", ret);
5067e1449cdSBilly Tsai 			return 0;
5077e1449cdSBilly Tsai 		}
5087e1449cdSBilly Tsai 	}
5097e1449cdSBilly Tsai 
5107e1449cdSBilly Tsai 	hwmon = devm_hwmon_device_register_with_info(dev, "aspeed_tach", priv,
5117e1449cdSBilly Tsai 						     &aspeed_tach_chip_info, NULL);
5127e1449cdSBilly Tsai 	ret = PTR_ERR_OR_ZERO(hwmon);
5137e1449cdSBilly Tsai 	if (ret)
5147e1449cdSBilly Tsai 		return dev_err_probe(dev, ret,
5157e1449cdSBilly Tsai 				     "Failed to register hwmon device\n");
5167e1449cdSBilly Tsai 
5177e1449cdSBilly Tsai 	of_platform_populate(dev->of_node, NULL, NULL, dev);
5187e1449cdSBilly Tsai 
5197e1449cdSBilly Tsai 	return 0;
5207e1449cdSBilly Tsai }
5217e1449cdSBilly Tsai 
522*e27cbb60SUwe Kleine-König static void aspeed_pwm_tach_remove(struct platform_device *pdev)
5237e1449cdSBilly Tsai {
5247e1449cdSBilly Tsai 	struct aspeed_pwm_tach_data *priv = platform_get_drvdata(pdev);
5257e1449cdSBilly Tsai 
5267e1449cdSBilly Tsai 	reset_control_assert(priv->reset);
5277e1449cdSBilly Tsai }
5287e1449cdSBilly Tsai 
5297e1449cdSBilly Tsai static const struct of_device_id aspeed_pwm_tach_match[] = {
5307e1449cdSBilly Tsai 	{
5317e1449cdSBilly Tsai 		.compatible = "aspeed,ast2600-pwm-tach",
5327e1449cdSBilly Tsai 	},
5337e1449cdSBilly Tsai 	{},
5347e1449cdSBilly Tsai };
5357e1449cdSBilly Tsai MODULE_DEVICE_TABLE(of, aspeed_pwm_tach_match);
5367e1449cdSBilly Tsai 
5377e1449cdSBilly Tsai static struct platform_driver aspeed_pwm_tach_driver = {
5387e1449cdSBilly Tsai 	.probe = aspeed_pwm_tach_probe,
539*e27cbb60SUwe Kleine-König 	.remove_new = aspeed_pwm_tach_remove,
5407e1449cdSBilly Tsai 	.driver	= {
5417e1449cdSBilly Tsai 		.name = "aspeed-g6-pwm-tach",
5427e1449cdSBilly Tsai 		.of_match_table = aspeed_pwm_tach_match,
5437e1449cdSBilly Tsai 	},
5447e1449cdSBilly Tsai };
5457e1449cdSBilly Tsai 
5467e1449cdSBilly Tsai module_platform_driver(aspeed_pwm_tach_driver);
5477e1449cdSBilly Tsai 
5487e1449cdSBilly Tsai MODULE_AUTHOR("Billy Tsai <billy_tsai@aspeedtech.com>");
5497e1449cdSBilly Tsai MODULE_DESCRIPTION("Aspeed ast2600 PWM and Fan Tach device driver");
5507e1449cdSBilly Tsai MODULE_LICENSE("GPL");
551