pwm: Changes for v4.13-rc1
This release cycle's changes include mostly updates and cleanups to existing drivers along with a few cleanups to the core, documentation and device tree bindings. -----BEGIN PGP SIGNATURE----- iQJNBAABCAA3FiEEiOrDCAFJzPfAjcif3SOs138+s6EFAllnboQZHHRoaWVycnku cmVkaW5nQGdtYWlsLmNvbQAKCRDdI6zXfz6zocCFD/9kUnHzwyM3epnGQruCwgLI xwcZmk2vAImbLoMN0NwtLpKyxIxehDCjvgsRxAm5ggE7Whs2Wzs1tzdML863umdl DcfF46T+zHlUXNZMgES+Znp787G6Bqnjj35PNVqNneptukmrZakPMQ4nZkL0UidD dZnXs/KhK97G4ys00+uVSfS/x6Ck1oD9XUtJttSGx4f7wIhWcsiwUpJvi772jcG+ bVmVkbJsOnui4UdvUs1uCb5yOppzOJqwsuNCAyX9a3NKR6u3/RtETjo4DQHhqXrv mXoHEIZkljDOgni2Zu/kcUDjooQ/GJVLjwku2iq1UGmJTu6FOJKqSLBKGQzyCXs+ BN0JM7pwEwwGKFzKbNn/ZxpJD06Hk0ABWcjeObIgsZaUOGmUdiDzwpFmLtKK22TC 9zNEODo+OjuaNYKADa4RHZrDbujiPMCL9ezonO3Xb1AS9UQfR8wXsVFqR44EofQA 0x+q+yIS5GVMab6s/jN51G/RVW2Ao+SdT+0/F8QF8N3YlCFsFCwt+X96383oDPAj STiHxqIRzEkn44Ua9CFg9utTrQeUVtNB3tw0wnD+zgsMFH1YRzcU2p714viqN8tQ N0yJUi+VwHXxnfNiVZNdrgidRH7ETWXyJY/OFRt2BCq9VuZ8V3E/Dr41pgFxZ0e6 WP7awOH/n25e4L09CjRtng== =ioFX -----END PGP SIGNATURE----- Merge tag 'pwm/for-4.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm Pull pwm updates from Thierry Reding: "This release cycle's changes include mostly updates and cleanups to existing drivers along with a few cleanups to the core, documentation and device tree bindings" * tag 'pwm/for-4.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm: pwm: cros-ec: Fix transposed param settings pwm: meson: Improve PWM calculation precision dt-bindings: pwm: meson: Add compatible for gxbb ao PWMs pwm: meson: Add compatible for the gxbb ao PWMs pwm: sun4i: Drop legacy callbacks pwm: sun4i: Switch to atomic PWM pwm: sun4i: Improve hardware read out pwm: hibvt: Constify hibvt_pwm_ops pwm: Silently error out on EPROBE_DEFER pwm: Standardize document format pwm: bfin: Remove unneeded error message dt-bindings: pwm: Update STM32 timers clock names dt-bindings: pwm: Add R-Car M3-W device tree bindings pwm: tegra: Set maximum pwm clock source per SoC tapeout
This commit is contained in:
		
						commit
						38f7d2da4e
					
				| @ -2,7 +2,9 @@ Amlogic Meson PWM Controller | ||||
| ============================ | ||||
| 
 | ||||
| Required properties: | ||||
| - compatible: Shall contain "amlogic,meson8b-pwm" or "amlogic,meson-gxbb-pwm". | ||||
| - compatible: Shall contain "amlogic,meson8b-pwm" | ||||
|                          or "amlogic,meson-gxbb-pwm" | ||||
|                          or "amlogic,meson-gxbb-ao-pwm" | ||||
| - #pwm-cells: Should be 3. See pwm.txt in this directory for a description of | ||||
|   the cells format. | ||||
| 
 | ||||
|  | ||||
| @ -24,7 +24,7 @@ Example: | ||||
| 		compatible = "st,stm32-timers"; | ||||
| 		reg = <0x40010000 0x400>; | ||||
| 		clocks = <&rcc 0 160>; | ||||
| 		clock-names = "clk_int"; | ||||
| 		clock-names = "int"; | ||||
| 
 | ||||
| 		pwm { | ||||
| 			compatible = "st,stm32-pwm"; | ||||
|  | ||||
| @ -8,6 +8,7 @@ Required Properties: | ||||
|  - "renesas,pwm-r8a7791": for R-Car M2-W | ||||
|  - "renesas,pwm-r8a7794": for R-Car E2 | ||||
|  - "renesas,pwm-r8a7795": for R-Car H3 | ||||
|  - "renesas,pwm-r8a7796": for R-Car M3-W | ||||
| - reg: base address and length of the registers block for the PWM. | ||||
| - #pwm-cells: should be 2. See pwm.txt in this directory for a description of | ||||
|   the cells format. | ||||
|  | ||||
| @ -1,4 +1,6 @@ | ||||
| ====================================== | ||||
| Pulse Width Modulation (PWM) interface | ||||
| ====================================== | ||||
| 
 | ||||
| This provides an overview about the Linux PWM interface | ||||
| 
 | ||||
| @ -16,7 +18,7 @@ Users of the legacy PWM API use unique IDs to refer to PWM devices. | ||||
| 
 | ||||
| Instead of referring to a PWM device via its unique ID, board setup code | ||||
| should instead register a static mapping that can be used to match PWM | ||||
| consumers to providers, as given in the following example: | ||||
| consumers to providers, as given in the following example:: | ||||
| 
 | ||||
| 	static struct pwm_lookup board_pwm_lookup[] = { | ||||
| 		PWM_LOOKUP("tegra-pwm", 0, "pwm-backlight", NULL, | ||||
| @ -40,9 +42,9 @@ New users should use the pwm_get() function and pass to it the consumer | ||||
| device or a consumer name. pwm_put() is used to free the PWM device. Managed | ||||
| variants of these functions, devm_pwm_get() and devm_pwm_put(), also exist. | ||||
| 
 | ||||
| After being requested, a PWM has to be configured using: | ||||
| After being requested, a PWM has to be configured using:: | ||||
| 
 | ||||
| int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state); | ||||
| 	int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state); | ||||
| 
 | ||||
| This API controls both the PWM period/duty_cycle config and the | ||||
| enable/disable state. | ||||
| @ -72,11 +74,14 @@ interface is provided to use the PWMs from userspace. It is exposed at | ||||
| pwmchipN, where N is the base of the PWM chip. Inside the directory you | ||||
| will find: | ||||
| 
 | ||||
| npwm - The number of PWM channels this chip supports (read-only). | ||||
|   npwm | ||||
|     The number of PWM channels this chip supports (read-only). | ||||
| 
 | ||||
| export - Exports a PWM channel for use with sysfs (write-only). | ||||
|   export | ||||
|     Exports a PWM channel for use with sysfs (write-only). | ||||
| 
 | ||||
| unexport - Unexports a PWM channel from sysfs (write-only). | ||||
|   unexport | ||||
|    Unexports a PWM channel from sysfs (write-only). | ||||
| 
 | ||||
| The PWM channels are numbered using a per-chip index from 0 to npwm-1. | ||||
| 
 | ||||
| @ -84,21 +89,26 @@ When a PWM channel is exported a pwmX directory will be created in the | ||||
| pwmchipN directory it is associated with, where X is the number of the | ||||
| channel that was exported. The following properties will then be available: | ||||
| 
 | ||||
| period - The total period of the PWM signal (read/write). | ||||
| 	Value is in nanoseconds and is the sum of the active and inactive | ||||
| 	time of the PWM. | ||||
|   period | ||||
|     The total period of the PWM signal (read/write). | ||||
|     Value is in nanoseconds and is the sum of the active and inactive | ||||
|     time of the PWM. | ||||
| 
 | ||||
| duty_cycle - The active time of the PWM signal (read/write). | ||||
| 	Value is in nanoseconds and must be less than the period. | ||||
|   duty_cycle | ||||
|     The active time of the PWM signal (read/write). | ||||
|     Value is in nanoseconds and must be less than the period. | ||||
| 
 | ||||
| polarity - Changes the polarity of the PWM signal (read/write). | ||||
| 	Writes to this property only work if the PWM chip supports changing | ||||
| 	the polarity. The polarity can only be changed if the PWM is not | ||||
| 	enabled. Value is the string "normal" or "inversed". | ||||
|   polarity | ||||
|     Changes the polarity of the PWM signal (read/write). | ||||
|     Writes to this property only work if the PWM chip supports changing | ||||
|     the polarity. The polarity can only be changed if the PWM is not | ||||
|     enabled. Value is the string "normal" or "inversed". | ||||
| 
 | ||||
| enable - Enable/disable the PWM signal (read/write). | ||||
| 	0 - disabled | ||||
| 	1 - enabled | ||||
|   enable | ||||
|     Enable/disable the PWM signal (read/write). | ||||
| 
 | ||||
| 	- 0 - disabled | ||||
| 	- 1 - enabled | ||||
| 
 | ||||
| Implementing a PWM driver | ||||
| ------------------------- | ||||
|  | ||||
| @ -678,7 +678,9 @@ struct pwm_device *of_pwm_get(struct device_node *np, const char *con_id) | ||||
| 
 | ||||
| 	pc = of_node_to_pwmchip(args.np); | ||||
| 	if (IS_ERR(pc)) { | ||||
| 		pr_err("%s(): PWM chip not found\n", __func__); | ||||
| 		if (PTR_ERR(pc) != -EPROBE_DEFER) | ||||
| 			pr_err("%s(): PWM chip not found\n", __func__); | ||||
| 
 | ||||
| 		pwm = ERR_CAST(pc); | ||||
| 		goto put; | ||||
| 	} | ||||
|  | ||||
| @ -118,10 +118,8 @@ static int bfin_pwm_probe(struct platform_device *pdev) | ||||
| 	int ret; | ||||
| 
 | ||||
| 	pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL); | ||||
| 	if (!pwm) { | ||||
| 		dev_err(&pdev->dev, "failed to allocate memory\n"); | ||||
| 	if (!pwm) | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 
 | ||||
| 	platform_set_drvdata(pdev, pwm); | ||||
| 
 | ||||
|  | ||||
| @ -75,8 +75,8 @@ static int __cros_ec_pwm_get_duty(struct cros_ec_device *ec, u8 index, | ||||
| 
 | ||||
| 	msg->version = 0; | ||||
| 	msg->command = EC_CMD_PWM_GET_DUTY; | ||||
| 	msg->insize = sizeof(*params); | ||||
| 	msg->outsize = sizeof(*resp); | ||||
| 	msg->insize = sizeof(*resp); | ||||
| 	msg->outsize = sizeof(*params); | ||||
| 
 | ||||
| 	params->pwm_type = EC_PWM_TYPE_GENERIC; | ||||
| 	params->index = index; | ||||
|  | ||||
| @ -165,7 +165,7 @@ static int hibvt_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static struct pwm_ops hibvt_pwm_ops = { | ||||
| static const struct pwm_ops hibvt_pwm_ops = { | ||||
| 	.get_state = hibvt_pwm_get_state, | ||||
| 	.apply = hibvt_pwm_apply, | ||||
| 
 | ||||
|  | ||||
| @ -103,6 +103,7 @@ struct meson_pwm_channel { | ||||
| 
 | ||||
| struct meson_pwm_data { | ||||
| 	const char * const *parent_names; | ||||
| 	unsigned int num_parents; | ||||
| }; | ||||
| 
 | ||||
| struct meson_pwm { | ||||
| @ -162,7 +163,8 @@ static int meson_pwm_calc(struct meson_pwm *meson, | ||||
| 			  unsigned int duty, unsigned int period) | ||||
| { | ||||
| 	unsigned int pre_div, cnt, duty_cnt; | ||||
| 	unsigned long fin_freq = -1, fin_ns; | ||||
| 	unsigned long fin_freq = -1; | ||||
| 	u64 fin_ps; | ||||
| 
 | ||||
| 	if (~(meson->inverter_mask >> id) & 0x1) | ||||
| 		duty = period - duty; | ||||
| @ -178,13 +180,15 @@ static int meson_pwm_calc(struct meson_pwm *meson, | ||||
| 	} | ||||
| 
 | ||||
| 	dev_dbg(meson->chip.dev, "fin_freq: %lu Hz\n", fin_freq); | ||||
| 	fin_ns = NSEC_PER_SEC / fin_freq; | ||||
| 	fin_ps = (u64)NSEC_PER_SEC * 1000; | ||||
| 	do_div(fin_ps, fin_freq); | ||||
| 
 | ||||
| 	/* Calc pre_div with the period */ | ||||
| 	for (pre_div = 0; pre_div < MISC_CLK_DIV_MASK; pre_div++) { | ||||
| 		cnt = DIV_ROUND_CLOSEST(period, fin_ns * (pre_div + 1)); | ||||
| 		dev_dbg(meson->chip.dev, "fin_ns=%lu pre_div=%u cnt=%u\n", | ||||
| 			fin_ns, pre_div, cnt); | ||||
| 		cnt = DIV_ROUND_CLOSEST_ULL((u64)period * 1000, | ||||
| 					    fin_ps * (pre_div + 1)); | ||||
| 		dev_dbg(meson->chip.dev, "fin_ps=%llu pre_div=%u cnt=%u\n", | ||||
| 			fin_ps, pre_div, cnt); | ||||
| 		if (cnt <= 0xffff) | ||||
| 			break; | ||||
| 	} | ||||
| @ -207,7 +211,8 @@ static int meson_pwm_calc(struct meson_pwm *meson, | ||||
| 		channel->lo = cnt; | ||||
| 	} else { | ||||
| 		/* Then check is we can have the duty with the same pre_div */ | ||||
| 		duty_cnt = DIV_ROUND_CLOSEST(duty, fin_ns * (pre_div + 1)); | ||||
| 		duty_cnt = DIV_ROUND_CLOSEST_ULL((u64)duty * 1000, | ||||
| 						 fin_ps * (pre_div + 1)); | ||||
| 		if (duty_cnt > 0xffff) { | ||||
| 			dev_err(meson->chip.dev, "unable to get duty cycle\n"); | ||||
| 			return -EINVAL; | ||||
| @ -381,6 +386,7 @@ static const char * const pwm_meson8b_parent_names[] = { | ||||
| 
 | ||||
| static const struct meson_pwm_data pwm_meson8b_data = { | ||||
| 	.parent_names = pwm_meson8b_parent_names, | ||||
| 	.num_parents = ARRAY_SIZE(pwm_meson8b_parent_names), | ||||
| }; | ||||
| 
 | ||||
| static const char * const pwm_gxbb_parent_names[] = { | ||||
| @ -389,11 +395,35 @@ static const char * const pwm_gxbb_parent_names[] = { | ||||
| 
 | ||||
| static const struct meson_pwm_data pwm_gxbb_data = { | ||||
| 	.parent_names = pwm_gxbb_parent_names, | ||||
| 	.num_parents = ARRAY_SIZE(pwm_gxbb_parent_names), | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
|  * Only the 2 first inputs of the GXBB AO PWMs are valid | ||||
|  * The last 2 are grounded | ||||
|  */ | ||||
| static const char * const pwm_gxbb_ao_parent_names[] = { | ||||
| 	"xtal", "clk81" | ||||
| }; | ||||
| 
 | ||||
| static const struct meson_pwm_data pwm_gxbb_ao_data = { | ||||
| 	.parent_names = pwm_gxbb_ao_parent_names, | ||||
| 	.num_parents = ARRAY_SIZE(pwm_gxbb_ao_parent_names), | ||||
| }; | ||||
| 
 | ||||
| static const struct of_device_id meson_pwm_matches[] = { | ||||
| 	{ .compatible = "amlogic,meson8b-pwm", .data = &pwm_meson8b_data }, | ||||
| 	{ .compatible = "amlogic,meson-gxbb-pwm", .data = &pwm_gxbb_data }, | ||||
| 	{ | ||||
| 		.compatible = "amlogic,meson8b-pwm", | ||||
| 		.data = &pwm_meson8b_data | ||||
| 	}, | ||||
| 	{ | ||||
| 		.compatible = "amlogic,meson-gxbb-pwm", | ||||
| 		.data = &pwm_gxbb_data | ||||
| 	}, | ||||
| 	{ | ||||
| 		.compatible = "amlogic,meson-gxbb-ao-pwm", | ||||
| 		.data = &pwm_gxbb_ao_data | ||||
| 	}, | ||||
| 	{}, | ||||
| }; | ||||
| MODULE_DEVICE_TABLE(of, meson_pwm_matches); | ||||
| @ -417,7 +447,7 @@ static int meson_pwm_init_channels(struct meson_pwm *meson, | ||||
| 		init.ops = &clk_mux_ops; | ||||
| 		init.flags = CLK_IS_BASIC; | ||||
| 		init.parent_names = meson->data->parent_names; | ||||
| 		init.num_parents = 1 << MISC_CLK_SEL_WIDTH; | ||||
| 		init.num_parents = meson->data->num_parents; | ||||
| 
 | ||||
| 		channel->mux.reg = meson->base + REG_MISC_AB; | ||||
| 		channel->mux.shift = mux_reg_shifts[i]; | ||||
|  | ||||
| @ -8,8 +8,10 @@ | ||||
| 
 | ||||
| #include <linux/bitops.h> | ||||
| #include <linux/clk.h> | ||||
| #include <linux/delay.h> | ||||
| #include <linux/err.h> | ||||
| #include <linux/io.h> | ||||
| #include <linux/jiffies.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/of.h> | ||||
| #include <linux/of_device.h> | ||||
| @ -44,6 +46,10 @@ | ||||
| 
 | ||||
| #define PWM_DTY_MASK		GENMASK(15, 0) | ||||
| 
 | ||||
| #define PWM_REG_PRD(reg)	((((reg) >> 16) & PWM_PRD_MASK) + 1) | ||||
| #define PWM_REG_DTY(reg)	((reg) & PWM_DTY_MASK) | ||||
| #define PWM_REG_PRESCAL(reg, chan)	(((reg) >> ((chan) * PWMCH_OFFSET)) & PWM_PRESCAL_MASK) | ||||
| 
 | ||||
| #define BIT_CH(bit, chan)	((bit) << ((chan) * PWMCH_OFFSET)) | ||||
| 
 | ||||
| static const u32 prescaler_table[] = { | ||||
| @ -77,6 +83,8 @@ struct sun4i_pwm_chip { | ||||
| 	void __iomem *base; | ||||
| 	spinlock_t ctrl_lock; | ||||
| 	const struct sun4i_pwm_data *data; | ||||
| 	unsigned long next_period[2]; | ||||
| 	bool needs_delay[2]; | ||||
| }; | ||||
| 
 | ||||
| static inline struct sun4i_pwm_chip *to_sun4i_pwm_chip(struct pwm_chip *chip) | ||||
| @ -96,26 +104,65 @@ static inline void sun4i_pwm_writel(struct sun4i_pwm_chip *chip, | ||||
| 	writel(val, chip->base + offset); | ||||
| } | ||||
| 
 | ||||
| static int sun4i_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | ||||
| 			    int duty_ns, int period_ns) | ||||
| static void sun4i_pwm_get_state(struct pwm_chip *chip, | ||||
| 				struct pwm_device *pwm, | ||||
| 				struct pwm_state *state) | ||||
| { | ||||
| 	struct sun4i_pwm_chip *sun4i_pwm = to_sun4i_pwm_chip(chip); | ||||
| 	u32 prd, dty, val, clk_gate; | ||||
| 	u64 clk_rate, tmp; | ||||
| 	u32 val; | ||||
| 	unsigned int prescaler; | ||||
| 
 | ||||
| 	clk_rate = clk_get_rate(sun4i_pwm->clk); | ||||
| 
 | ||||
| 	val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG); | ||||
| 
 | ||||
| 	if ((val == PWM_PRESCAL_MASK) && sun4i_pwm->data->has_prescaler_bypass) | ||||
| 		prescaler = 1; | ||||
| 	else | ||||
| 		prescaler = prescaler_table[PWM_REG_PRESCAL(val, pwm->hwpwm)]; | ||||
| 
 | ||||
| 	if (prescaler == 0) | ||||
| 		return; | ||||
| 
 | ||||
| 	if (val & BIT_CH(PWM_ACT_STATE, pwm->hwpwm)) | ||||
| 		state->polarity = PWM_POLARITY_NORMAL; | ||||
| 	else | ||||
| 		state->polarity = PWM_POLARITY_INVERSED; | ||||
| 
 | ||||
| 	if (val & BIT_CH(PWM_CLK_GATING | PWM_EN, pwm->hwpwm)) | ||||
| 		state->enabled = true; | ||||
| 	else | ||||
| 		state->enabled = false; | ||||
| 
 | ||||
| 	val = sun4i_pwm_readl(sun4i_pwm, PWM_CH_PRD(pwm->hwpwm)); | ||||
| 
 | ||||
| 	tmp = prescaler * NSEC_PER_SEC * PWM_REG_DTY(val); | ||||
| 	state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, clk_rate); | ||||
| 
 | ||||
| 	tmp = prescaler * NSEC_PER_SEC * PWM_REG_PRD(val); | ||||
| 	state->period = DIV_ROUND_CLOSEST_ULL(tmp, clk_rate); | ||||
| } | ||||
| 
 | ||||
| static int sun4i_pwm_calculate(struct sun4i_pwm_chip *sun4i_pwm, | ||||
| 			       struct pwm_state *state, | ||||
| 			       u32 *dty, u32 *prd, unsigned int *prsclr) | ||||
| { | ||||
| 	u64 clk_rate, div = 0; | ||||
| 	unsigned int prescaler = 0; | ||||
| 	int err; | ||||
| 	unsigned int pval, prescaler = 0; | ||||
| 
 | ||||
| 	clk_rate = clk_get_rate(sun4i_pwm->clk); | ||||
| 
 | ||||
| 	if (sun4i_pwm->data->has_prescaler_bypass) { | ||||
| 		/* First, test without any prescaler when available */ | ||||
| 		prescaler = PWM_PRESCAL_MASK; | ||||
| 		pval = 1; | ||||
| 		/*
 | ||||
| 		 * When not using any prescaler, the clock period in nanoseconds | ||||
| 		 * is not an integer so round it half up instead of | ||||
| 		 * truncating to get less surprising values. | ||||
| 		 */ | ||||
| 		div = clk_rate * period_ns + NSEC_PER_SEC / 2; | ||||
| 		div = clk_rate * state->period + NSEC_PER_SEC / 2; | ||||
| 		do_div(div, NSEC_PER_SEC); | ||||
| 		if (div - 1 > PWM_PRD_MASK) | ||||
| 			prescaler = 0; | ||||
| @ -126,137 +173,141 @@ static int sun4i_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | ||||
| 		for (prescaler = 0; prescaler < PWM_PRESCAL_MASK; prescaler++) { | ||||
| 			if (!prescaler_table[prescaler]) | ||||
| 				continue; | ||||
| 			pval = prescaler_table[prescaler]; | ||||
| 			div = clk_rate; | ||||
| 			do_div(div, prescaler_table[prescaler]); | ||||
| 			div = div * period_ns; | ||||
| 			do_div(div, pval); | ||||
| 			div = div * state->period; | ||||
| 			do_div(div, NSEC_PER_SEC); | ||||
| 			if (div - 1 <= PWM_PRD_MASK) | ||||
| 				break; | ||||
| 		} | ||||
| 
 | ||||
| 		if (div - 1 > PWM_PRD_MASK) { | ||||
| 			dev_err(chip->dev, "period exceeds the maximum value\n"); | ||||
| 		if (div - 1 > PWM_PRD_MASK) | ||||
| 			return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	*prd = div; | ||||
| 	div *= state->duty_cycle; | ||||
| 	do_div(div, state->period); | ||||
| 	*dty = div; | ||||
| 	*prsclr = prescaler; | ||||
| 
 | ||||
| 	div = (u64)pval * NSEC_PER_SEC * *prd; | ||||
| 	state->period = DIV_ROUND_CLOSEST_ULL(div, clk_rate); | ||||
| 
 | ||||
| 	div = (u64)pval * NSEC_PER_SEC * *dty; | ||||
| 	state->duty_cycle = DIV_ROUND_CLOSEST_ULL(div, clk_rate); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, | ||||
| 			   struct pwm_state *state) | ||||
| { | ||||
| 	struct sun4i_pwm_chip *sun4i_pwm = to_sun4i_pwm_chip(chip); | ||||
| 	struct pwm_state cstate; | ||||
| 	u32 ctrl; | ||||
| 	int ret; | ||||
| 	unsigned int delay_us; | ||||
| 	unsigned long now; | ||||
| 
 | ||||
| 	pwm_get_state(pwm, &cstate); | ||||
| 
 | ||||
| 	if (!cstate.enabled) { | ||||
| 		ret = clk_prepare_enable(sun4i_pwm->clk); | ||||
| 		if (ret) { | ||||
| 			dev_err(chip->dev, "failed to enable PWM clock\n"); | ||||
| 			return ret; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	prd = div; | ||||
| 	div *= duty_ns; | ||||
| 	do_div(div, period_ns); | ||||
| 	dty = div; | ||||
| 
 | ||||
| 	err = clk_prepare_enable(sun4i_pwm->clk); | ||||
| 	if (err) { | ||||
| 		dev_err(chip->dev, "failed to enable PWM clock\n"); | ||||
| 		return err; | ||||
| 	} | ||||
| 
 | ||||
| 	spin_lock(&sun4i_pwm->ctrl_lock); | ||||
| 	val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG); | ||||
| 	ctrl = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG); | ||||
| 
 | ||||
| 	if (sun4i_pwm->data->has_rdy && (val & PWM_RDY(pwm->hwpwm))) { | ||||
| 		spin_unlock(&sun4i_pwm->ctrl_lock); | ||||
| 		clk_disable_unprepare(sun4i_pwm->clk); | ||||
| 		return -EBUSY; | ||||
| 	if ((cstate.period != state->period) || | ||||
| 	    (cstate.duty_cycle != state->duty_cycle)) { | ||||
| 		u32 period, duty, val; | ||||
| 		unsigned int prescaler; | ||||
| 
 | ||||
| 		ret = sun4i_pwm_calculate(sun4i_pwm, state, | ||||
| 					  &duty, &period, &prescaler); | ||||
| 		if (ret) { | ||||
| 			dev_err(chip->dev, "period exceeds the maximum value\n"); | ||||
| 			spin_unlock(&sun4i_pwm->ctrl_lock); | ||||
| 			if (!cstate.enabled) | ||||
| 				clk_disable_unprepare(sun4i_pwm->clk); | ||||
| 			return ret; | ||||
| 		} | ||||
| 
 | ||||
| 		if (PWM_REG_PRESCAL(ctrl, pwm->hwpwm) != prescaler) { | ||||
| 			/* Prescaler changed, the clock has to be gated */ | ||||
| 			ctrl &= ~BIT_CH(PWM_CLK_GATING, pwm->hwpwm); | ||||
| 			sun4i_pwm_writel(sun4i_pwm, ctrl, PWM_CTRL_REG); | ||||
| 
 | ||||
| 			ctrl &= ~BIT_CH(PWM_PRESCAL_MASK, pwm->hwpwm); | ||||
| 			ctrl |= BIT_CH(prescaler, pwm->hwpwm); | ||||
| 		} | ||||
| 
 | ||||
| 		val = (duty & PWM_DTY_MASK) | PWM_PRD(period); | ||||
| 		sun4i_pwm_writel(sun4i_pwm, val, PWM_CH_PRD(pwm->hwpwm)); | ||||
| 		sun4i_pwm->next_period[pwm->hwpwm] = jiffies + | ||||
| 			usecs_to_jiffies(cstate.period / 1000 + 1); | ||||
| 		sun4i_pwm->needs_delay[pwm->hwpwm] = true; | ||||
| 	} | ||||
| 
 | ||||
| 	clk_gate = val & BIT_CH(PWM_CLK_GATING, pwm->hwpwm); | ||||
| 	if (clk_gate) { | ||||
| 		val &= ~BIT_CH(PWM_CLK_GATING, pwm->hwpwm); | ||||
| 		sun4i_pwm_writel(sun4i_pwm, val, PWM_CTRL_REG); | ||||
| 	} | ||||
| 
 | ||||
| 	val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG); | ||||
| 	val &= ~BIT_CH(PWM_PRESCAL_MASK, pwm->hwpwm); | ||||
| 	val |= BIT_CH(prescaler, pwm->hwpwm); | ||||
| 	sun4i_pwm_writel(sun4i_pwm, val, PWM_CTRL_REG); | ||||
| 
 | ||||
| 	val = (dty & PWM_DTY_MASK) | PWM_PRD(prd); | ||||
| 	sun4i_pwm_writel(sun4i_pwm, val, PWM_CH_PRD(pwm->hwpwm)); | ||||
| 
 | ||||
| 	if (clk_gate) { | ||||
| 		val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG); | ||||
| 		val |= clk_gate; | ||||
| 		sun4i_pwm_writel(sun4i_pwm, val, PWM_CTRL_REG); | ||||
| 	} | ||||
| 
 | ||||
| 	spin_unlock(&sun4i_pwm->ctrl_lock); | ||||
| 	clk_disable_unprepare(sun4i_pwm->clk); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int sun4i_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm, | ||||
| 				  enum pwm_polarity polarity) | ||||
| { | ||||
| 	struct sun4i_pwm_chip *sun4i_pwm = to_sun4i_pwm_chip(chip); | ||||
| 	u32 val; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = clk_prepare_enable(sun4i_pwm->clk); | ||||
| 	if (ret) { | ||||
| 		dev_err(chip->dev, "failed to enable PWM clock\n"); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	spin_lock(&sun4i_pwm->ctrl_lock); | ||||
| 	val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG); | ||||
| 
 | ||||
| 	if (polarity != PWM_POLARITY_NORMAL) | ||||
| 		val &= ~BIT_CH(PWM_ACT_STATE, pwm->hwpwm); | ||||
| 	if (state->polarity != PWM_POLARITY_NORMAL) | ||||
| 		ctrl &= ~BIT_CH(PWM_ACT_STATE, pwm->hwpwm); | ||||
| 	else | ||||
| 		val |= BIT_CH(PWM_ACT_STATE, pwm->hwpwm); | ||||
| 		ctrl |= BIT_CH(PWM_ACT_STATE, pwm->hwpwm); | ||||
| 
 | ||||
| 	sun4i_pwm_writel(sun4i_pwm, val, PWM_CTRL_REG); | ||||
| 
 | ||||
| 	spin_unlock(&sun4i_pwm->ctrl_lock); | ||||
| 	clk_disable_unprepare(sun4i_pwm->clk); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int sun4i_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) | ||||
| { | ||||
| 	struct sun4i_pwm_chip *sun4i_pwm = to_sun4i_pwm_chip(chip); | ||||
| 	u32 val; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = clk_prepare_enable(sun4i_pwm->clk); | ||||
| 	if (ret) { | ||||
| 		dev_err(chip->dev, "failed to enable PWM clock\n"); | ||||
| 		return ret; | ||||
| 	ctrl |= BIT_CH(PWM_CLK_GATING, pwm->hwpwm); | ||||
| 	if (state->enabled) { | ||||
| 		ctrl |= BIT_CH(PWM_EN, pwm->hwpwm); | ||||
| 	} else if (!sun4i_pwm->needs_delay[pwm->hwpwm]) { | ||||
| 		ctrl &= ~BIT_CH(PWM_EN, pwm->hwpwm); | ||||
| 		ctrl &= ~BIT_CH(PWM_CLK_GATING, pwm->hwpwm); | ||||
| 	} | ||||
| 
 | ||||
| 	spin_lock(&sun4i_pwm->ctrl_lock); | ||||
| 	val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG); | ||||
| 	val |= BIT_CH(PWM_EN, pwm->hwpwm); | ||||
| 	val |= BIT_CH(PWM_CLK_GATING, pwm->hwpwm); | ||||
| 	sun4i_pwm_writel(sun4i_pwm, val, PWM_CTRL_REG); | ||||
| 	sun4i_pwm_writel(sun4i_pwm, ctrl, PWM_CTRL_REG); | ||||
| 
 | ||||
| 	spin_unlock(&sun4i_pwm->ctrl_lock); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 	if (state->enabled) | ||||
| 		return 0; | ||||
| 
 | ||||
| static void sun4i_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) | ||||
| { | ||||
| 	struct sun4i_pwm_chip *sun4i_pwm = to_sun4i_pwm_chip(chip); | ||||
| 	u32 val; | ||||
| 	if (!sun4i_pwm->needs_delay[pwm->hwpwm]) { | ||||
| 		clk_disable_unprepare(sun4i_pwm->clk); | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	/* We need a full period to elapse before disabling the channel. */ | ||||
| 	now = jiffies; | ||||
| 	if (sun4i_pwm->needs_delay[pwm->hwpwm] && | ||||
| 	    time_before(now, sun4i_pwm->next_period[pwm->hwpwm])) { | ||||
| 		delay_us = jiffies_to_usecs(sun4i_pwm->next_period[pwm->hwpwm] - | ||||
| 					   now); | ||||
| 		if ((delay_us / 500) > MAX_UDELAY_MS) | ||||
| 			msleep(delay_us / 1000 + 1); | ||||
| 		else | ||||
| 			usleep_range(delay_us, delay_us * 2); | ||||
| 	} | ||||
| 	sun4i_pwm->needs_delay[pwm->hwpwm] = false; | ||||
| 
 | ||||
| 	spin_lock(&sun4i_pwm->ctrl_lock); | ||||
| 	val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG); | ||||
| 	val &= ~BIT_CH(PWM_EN, pwm->hwpwm); | ||||
| 	val &= ~BIT_CH(PWM_CLK_GATING, pwm->hwpwm); | ||||
| 	sun4i_pwm_writel(sun4i_pwm, val, PWM_CTRL_REG); | ||||
| 	ctrl = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG); | ||||
| 	ctrl &= ~BIT_CH(PWM_CLK_GATING, pwm->hwpwm); | ||||
| 	ctrl &= ~BIT_CH(PWM_EN, pwm->hwpwm); | ||||
| 	sun4i_pwm_writel(sun4i_pwm, ctrl, PWM_CTRL_REG); | ||||
| 	spin_unlock(&sun4i_pwm->ctrl_lock); | ||||
| 
 | ||||
| 	clk_disable_unprepare(sun4i_pwm->clk); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static const struct pwm_ops sun4i_pwm_ops = { | ||||
| 	.config = sun4i_pwm_config, | ||||
| 	.set_polarity = sun4i_pwm_set_polarity, | ||||
| 	.enable = sun4i_pwm_enable, | ||||
| 	.disable = sun4i_pwm_disable, | ||||
| 	.apply = sun4i_pwm_apply, | ||||
| 	.get_state = sun4i_pwm_get_state, | ||||
| 	.owner = THIS_MODULE, | ||||
| }; | ||||
| 
 | ||||
| @ -316,8 +367,7 @@ static int sun4i_pwm_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	struct sun4i_pwm_chip *pwm; | ||||
| 	struct resource *res; | ||||
| 	u32 val; | ||||
| 	int i, ret; | ||||
| 	int ret; | ||||
| 	const struct of_device_id *match; | ||||
| 
 | ||||
| 	match = of_match_device(sun4i_pwm_dt_ids, &pdev->dev); | ||||
| @ -353,24 +403,7 @@ static int sun4i_pwm_probe(struct platform_device *pdev) | ||||
| 
 | ||||
| 	platform_set_drvdata(pdev, pwm); | ||||
| 
 | ||||
| 	ret = clk_prepare_enable(pwm->clk); | ||||
| 	if (ret) { | ||||
| 		dev_err(&pdev->dev, "failed to enable PWM clock\n"); | ||||
| 		goto clk_error; | ||||
| 	} | ||||
| 
 | ||||
| 	val = sun4i_pwm_readl(pwm, PWM_CTRL_REG); | ||||
| 	for (i = 0; i < pwm->chip.npwm; i++) | ||||
| 		if (!(val & BIT_CH(PWM_ACT_STATE, i))) | ||||
| 			pwm_set_polarity(&pwm->chip.pwms[i], | ||||
| 					 PWM_POLARITY_INVERSED); | ||||
| 	clk_disable_unprepare(pwm->clk); | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| clk_error: | ||||
| 	pwmchip_remove(&pwm->chip); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int sun4i_pwm_remove(struct platform_device *pdev) | ||||
|  | ||||
| @ -41,6 +41,9 @@ | ||||
| 
 | ||||
| struct tegra_pwm_soc { | ||||
| 	unsigned int num_channels; | ||||
| 
 | ||||
| 	/* Maximum IP frequency for given SoCs */ | ||||
| 	unsigned long max_frequency; | ||||
| }; | ||||
| 
 | ||||
| struct tegra_pwm_chip { | ||||
| @ -201,7 +204,18 @@ static int tegra_pwm_probe(struct platform_device *pdev) | ||||
| 	if (IS_ERR(pwm->clk)) | ||||
| 		return PTR_ERR(pwm->clk); | ||||
| 
 | ||||
| 	/* Read PWM clock rate from source */ | ||||
| 	/* Set maximum frequency of the IP */ | ||||
| 	ret = clk_set_rate(pwm->clk, pwm->soc->max_frequency); | ||||
| 	if (ret < 0) { | ||||
| 		dev_err(&pdev->dev, "Failed to set max frequency: %d\n", ret); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * The requested and configured frequency may differ due to | ||||
| 	 * clock register resolutions. Get the configured frequency | ||||
| 	 * so that PWM period can be calculated more accurately. | ||||
| 	 */ | ||||
| 	pwm->clk_rate = clk_get_rate(pwm->clk); | ||||
| 
 | ||||
| 	pwm->rst = devm_reset_control_get(&pdev->dev, "pwm"); | ||||
| @ -273,10 +287,12 @@ static int tegra_pwm_resume(struct device *dev) | ||||
| 
 | ||||
| static const struct tegra_pwm_soc tegra20_pwm_soc = { | ||||
| 	.num_channels = 4, | ||||
| 	.max_frequency = 48000000UL, | ||||
| }; | ||||
| 
 | ||||
| static const struct tegra_pwm_soc tegra186_pwm_soc = { | ||||
| 	.num_channels = 1, | ||||
| 	.max_frequency = 102000000UL, | ||||
| }; | ||||
| 
 | ||||
| static const struct of_device_id tegra_pwm_of_match[] = { | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user