brintos

brintos / linux-shallow public Read only

0
0
Text · 10.6 KiB · 7154295 Raw
426 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Imagination Technologies Pulse Width Modulator driver4 *5 * Copyright (c) 2014-2015, Imagination Technologies6 *7 * Based on drivers/pwm/pwm-tegra.c, Copyright (c) 2010, NVIDIA Corporation8 */9 10#include <linux/clk.h>11#include <linux/err.h>12#include <linux/io.h>13#include <linux/mfd/syscon.h>14#include <linux/module.h>15#include <linux/of.h>16#include <linux/platform_device.h>17#include <linux/pm_runtime.h>18#include <linux/property.h>19#include <linux/pwm.h>20#include <linux/regmap.h>21#include <linux/slab.h>22 23/* PWM registers */24#define PWM_CTRL_CFG				0x000025#define PWM_CTRL_CFG_NO_SUB_DIV			026#define PWM_CTRL_CFG_SUB_DIV0			127#define PWM_CTRL_CFG_SUB_DIV1			228#define PWM_CTRL_CFG_SUB_DIV0_DIV1		329#define PWM_CTRL_CFG_DIV_SHIFT(ch)		((ch) * 2 + 4)30#define PWM_CTRL_CFG_DIV_MASK			0x331 32#define PWM_CH_CFG(ch)				(0x4 + (ch) * 4)33#define PWM_CH_CFG_TMBASE_SHIFT			034#define PWM_CH_CFG_DUTY_SHIFT			1635 36#define PERIP_PWM_PDM_CONTROL			0x014037#define PERIP_PWM_PDM_CONTROL_CH_MASK		0x138#define PERIP_PWM_PDM_CONTROL_CH_SHIFT(ch)	((ch) * 4)39 40#define IMG_PWM_PM_TIMEOUT			1000 /* ms */41 42/*43 * PWM period is specified with a timebase register,44 * in number of step periods. The PWM duty cycle is also45 * specified in step periods, in the [0, $timebase] range.46 * In other words, the timebase imposes the duty cycle47 * resolution. Therefore, let's constraint the timebase to48 * a minimum value to allow a sane range of duty cycle values.49 * Imposing a minimum timebase, will impose a maximum PWM frequency.50 *51 * The value chosen is completely arbitrary.52 */53#define MIN_TMBASE_STEPS			1654 55#define IMG_PWM_NPWM				456 57struct img_pwm_soc_data {58	u32 max_timebase;59};60 61struct img_pwm_chip {62	struct clk	*pwm_clk;63	struct clk	*sys_clk;64	void __iomem	*base;65	struct regmap	*periph_regs;66	int		max_period_ns;67	int		min_period_ns;68	const struct img_pwm_soc_data   *data;69	u32		suspend_ctrl_cfg;70	u32		suspend_ch_cfg[IMG_PWM_NPWM];71};72 73static inline struct img_pwm_chip *to_img_pwm_chip(struct pwm_chip *chip)74{75	return pwmchip_get_drvdata(chip);76}77 78static inline void img_pwm_writel(struct img_pwm_chip *imgchip,79				  u32 reg, u32 val)80{81	writel(val, imgchip->base + reg);82}83 84static inline u32 img_pwm_readl(struct img_pwm_chip *imgchip, u32 reg)85{86	return readl(imgchip->base + reg);87}88 89static int img_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,90			  int duty_ns, int period_ns)91{92	u32 val, div, duty, timebase;93	unsigned long mul, output_clk_hz, input_clk_hz;94	struct img_pwm_chip *imgchip = to_img_pwm_chip(chip);95	unsigned int max_timebase = imgchip->data->max_timebase;96	int ret;97 98	if (period_ns < imgchip->min_period_ns ||99	    period_ns > imgchip->max_period_ns) {100		dev_err(pwmchip_parent(chip), "configured period not in range\n");101		return -ERANGE;102	}103 104	input_clk_hz = clk_get_rate(imgchip->pwm_clk);105	output_clk_hz = DIV_ROUND_UP(NSEC_PER_SEC, period_ns);106 107	mul = DIV_ROUND_UP(input_clk_hz, output_clk_hz);108	if (mul <= max_timebase) {109		div = PWM_CTRL_CFG_NO_SUB_DIV;110		timebase = DIV_ROUND_UP(mul, 1);111	} else if (mul <= max_timebase * 8) {112		div = PWM_CTRL_CFG_SUB_DIV0;113		timebase = DIV_ROUND_UP(mul, 8);114	} else if (mul <= max_timebase * 64) {115		div = PWM_CTRL_CFG_SUB_DIV1;116		timebase = DIV_ROUND_UP(mul, 64);117	} else if (mul <= max_timebase * 512) {118		div = PWM_CTRL_CFG_SUB_DIV0_DIV1;119		timebase = DIV_ROUND_UP(mul, 512);120	} else {121		dev_err(pwmchip_parent(chip),122			"failed to configure timebase steps/divider value\n");123		return -EINVAL;124	}125 126	duty = DIV_ROUND_UP(timebase * duty_ns, period_ns);127 128	ret = pm_runtime_resume_and_get(pwmchip_parent(chip));129	if (ret < 0)130		return ret;131 132	val = img_pwm_readl(imgchip, PWM_CTRL_CFG);133	val &= ~(PWM_CTRL_CFG_DIV_MASK << PWM_CTRL_CFG_DIV_SHIFT(pwm->hwpwm));134	val |= (div & PWM_CTRL_CFG_DIV_MASK) <<135		PWM_CTRL_CFG_DIV_SHIFT(pwm->hwpwm);136	img_pwm_writel(imgchip, PWM_CTRL_CFG, val);137 138	val = (duty << PWM_CH_CFG_DUTY_SHIFT) |139	      (timebase << PWM_CH_CFG_TMBASE_SHIFT);140	img_pwm_writel(imgchip, PWM_CH_CFG(pwm->hwpwm), val);141 142	pm_runtime_mark_last_busy(pwmchip_parent(chip));143	pm_runtime_put_autosuspend(pwmchip_parent(chip));144 145	return 0;146}147 148static int img_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)149{150	u32 val;151	struct img_pwm_chip *imgchip = to_img_pwm_chip(chip);152	int ret;153 154	ret = pm_runtime_resume_and_get(pwmchip_parent(chip));155	if (ret < 0)156		return ret;157 158	val = img_pwm_readl(imgchip, PWM_CTRL_CFG);159	val |= BIT(pwm->hwpwm);160	img_pwm_writel(imgchip, PWM_CTRL_CFG, val);161 162	regmap_clear_bits(imgchip->periph_regs, PERIP_PWM_PDM_CONTROL,163			  PERIP_PWM_PDM_CONTROL_CH_MASK <<164			  PERIP_PWM_PDM_CONTROL_CH_SHIFT(pwm->hwpwm));165 166	return 0;167}168 169static void img_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)170{171	u32 val;172	struct img_pwm_chip *imgchip = to_img_pwm_chip(chip);173 174	val = img_pwm_readl(imgchip, PWM_CTRL_CFG);175	val &= ~BIT(pwm->hwpwm);176	img_pwm_writel(imgchip, PWM_CTRL_CFG, val);177 178	pm_runtime_mark_last_busy(pwmchip_parent(chip));179	pm_runtime_put_autosuspend(pwmchip_parent(chip));180}181 182static int img_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,183			 const struct pwm_state *state)184{185	int err;186 187	if (state->polarity != PWM_POLARITY_NORMAL)188		return -EINVAL;189 190	if (!state->enabled) {191		if (pwm->state.enabled)192			img_pwm_disable(chip, pwm);193 194		return 0;195	}196 197	err = img_pwm_config(chip, pwm, state->duty_cycle, state->period);198	if (err)199		return err;200 201	if (!pwm->state.enabled)202		err = img_pwm_enable(chip, pwm);203 204	return err;205}206 207static const struct pwm_ops img_pwm_ops = {208	.apply = img_pwm_apply,209};210 211static const struct img_pwm_soc_data pistachio_pwm = {212	.max_timebase = 255,213};214 215static const struct of_device_id img_pwm_of_match[] = {216	{217		.compatible = "img,pistachio-pwm",218		.data = &pistachio_pwm,219	},220	{ }221};222MODULE_DEVICE_TABLE(of, img_pwm_of_match);223 224static int img_pwm_runtime_suspend(struct device *dev)225{226	struct pwm_chip *chip = dev_get_drvdata(dev);227	struct img_pwm_chip *imgchip = to_img_pwm_chip(chip);228 229	clk_disable_unprepare(imgchip->pwm_clk);230	clk_disable_unprepare(imgchip->sys_clk);231 232	return 0;233}234 235static int img_pwm_runtime_resume(struct device *dev)236{237	struct pwm_chip *chip = dev_get_drvdata(dev);238	struct img_pwm_chip *imgchip = to_img_pwm_chip(chip);239	int ret;240 241	ret = clk_prepare_enable(imgchip->sys_clk);242	if (ret < 0) {243		dev_err(dev, "could not prepare or enable sys clock\n");244		return ret;245	}246 247	ret = clk_prepare_enable(imgchip->pwm_clk);248	if (ret < 0) {249		dev_err(dev, "could not prepare or enable pwm clock\n");250		clk_disable_unprepare(imgchip->sys_clk);251		return ret;252	}253 254	return 0;255}256 257static int img_pwm_probe(struct platform_device *pdev)258{259	int ret;260	u64 val;261	unsigned long clk_rate;262	struct pwm_chip *chip;263	struct img_pwm_chip *imgchip;264 265	chip = devm_pwmchip_alloc(&pdev->dev, IMG_PWM_NPWM, sizeof(*imgchip));266	if (IS_ERR(chip))267		return PTR_ERR(chip);268	imgchip = to_img_pwm_chip(chip);269 270	imgchip->base = devm_platform_ioremap_resource(pdev, 0);271	if (IS_ERR(imgchip->base))272		return PTR_ERR(imgchip->base);273 274	imgchip->data = device_get_match_data(&pdev->dev);275 276	imgchip->periph_regs = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,277							       "img,cr-periph");278	if (IS_ERR(imgchip->periph_regs))279		return PTR_ERR(imgchip->periph_regs);280 281	imgchip->sys_clk = devm_clk_get(&pdev->dev, "sys");282	if (IS_ERR(imgchip->sys_clk)) {283		dev_err(&pdev->dev, "failed to get system clock\n");284		return PTR_ERR(imgchip->sys_clk);285	}286 287	imgchip->pwm_clk = devm_clk_get(&pdev->dev, "pwm");288	if (IS_ERR(imgchip->pwm_clk)) {289		dev_err(&pdev->dev, "failed to get pwm clock\n");290		return PTR_ERR(imgchip->pwm_clk);291	}292 293	platform_set_drvdata(pdev, chip);294 295	pm_runtime_set_autosuspend_delay(&pdev->dev, IMG_PWM_PM_TIMEOUT);296	pm_runtime_use_autosuspend(&pdev->dev);297	pm_runtime_enable(&pdev->dev);298	if (!pm_runtime_enabled(&pdev->dev)) {299		ret = img_pwm_runtime_resume(&pdev->dev);300		if (ret)301			goto err_pm_disable;302	}303 304	clk_rate = clk_get_rate(imgchip->pwm_clk);305	if (!clk_rate) {306		dev_err(&pdev->dev, "imgchip clock has no frequency\n");307		ret = -EINVAL;308		goto err_suspend;309	}310 311	/* The maximum input clock divider is 512 */312	val = (u64)NSEC_PER_SEC * 512 * imgchip->data->max_timebase;313	do_div(val, clk_rate);314	imgchip->max_period_ns = val;315 316	val = (u64)NSEC_PER_SEC * MIN_TMBASE_STEPS;317	do_div(val, clk_rate);318	imgchip->min_period_ns = val;319 320	chip->ops = &img_pwm_ops;321 322	ret = pwmchip_add(chip);323	if (ret < 0) {324		dev_err(&pdev->dev, "pwmchip_add failed: %d\n", ret);325		goto err_suspend;326	}327 328	return 0;329 330err_suspend:331	if (!pm_runtime_enabled(&pdev->dev))332		img_pwm_runtime_suspend(&pdev->dev);333err_pm_disable:334	pm_runtime_disable(&pdev->dev);335	pm_runtime_dont_use_autosuspend(&pdev->dev);336	return ret;337}338 339static void img_pwm_remove(struct platform_device *pdev)340{341	struct pwm_chip *chip = platform_get_drvdata(pdev);342 343	pm_runtime_disable(&pdev->dev);344	if (!pm_runtime_status_suspended(&pdev->dev))345		img_pwm_runtime_suspend(&pdev->dev);346 347	pwmchip_remove(chip);348}349 350#ifdef CONFIG_PM_SLEEP351static int img_pwm_suspend(struct device *dev)352{353	struct pwm_chip *chip = dev_get_drvdata(dev);354	struct img_pwm_chip *imgchip = to_img_pwm_chip(chip);355	int i, ret;356 357	if (pm_runtime_status_suspended(dev)) {358		ret = img_pwm_runtime_resume(dev);359		if (ret)360			return ret;361	}362 363	for (i = 0; i < chip->npwm; i++)364		imgchip->suspend_ch_cfg[i] = img_pwm_readl(imgchip,365							   PWM_CH_CFG(i));366 367	imgchip->suspend_ctrl_cfg = img_pwm_readl(imgchip, PWM_CTRL_CFG);368 369	img_pwm_runtime_suspend(dev);370 371	return 0;372}373 374static int img_pwm_resume(struct device *dev)375{376	struct pwm_chip *chip = dev_get_drvdata(dev);377	struct img_pwm_chip *imgchip = to_img_pwm_chip(chip);378	int ret;379	int i;380 381	ret = img_pwm_runtime_resume(dev);382	if (ret)383		return ret;384 385	for (i = 0; i < chip->npwm; i++)386		img_pwm_writel(imgchip, PWM_CH_CFG(i),387			       imgchip->suspend_ch_cfg[i]);388 389	img_pwm_writel(imgchip, PWM_CTRL_CFG, imgchip->suspend_ctrl_cfg);390 391	for (i = 0; i < chip->npwm; i++)392		if (imgchip->suspend_ctrl_cfg & BIT(i))393			regmap_clear_bits(imgchip->periph_regs,394					  PERIP_PWM_PDM_CONTROL,395					  PERIP_PWM_PDM_CONTROL_CH_MASK <<396					  PERIP_PWM_PDM_CONTROL_CH_SHIFT(i));397 398	if (pm_runtime_status_suspended(dev))399		img_pwm_runtime_suspend(dev);400 401	return 0;402}403#endif /* CONFIG_PM */404 405static const struct dev_pm_ops img_pwm_pm_ops = {406	SET_RUNTIME_PM_OPS(img_pwm_runtime_suspend,407			   img_pwm_runtime_resume,408			   NULL)409	SET_SYSTEM_SLEEP_PM_OPS(img_pwm_suspend, img_pwm_resume)410};411 412static struct platform_driver img_pwm_driver = {413	.driver = {414		.name = "img-pwm",415		.pm = &img_pwm_pm_ops,416		.of_match_table = img_pwm_of_match,417	},418	.probe = img_pwm_probe,419	.remove = img_pwm_remove,420};421module_platform_driver(img_pwm_driver);422 423MODULE_AUTHOR("Sai Masarapu <Sai.Masarapu@imgtec.com>");424MODULE_DESCRIPTION("Imagination Technologies PWM DAC driver");425MODULE_LICENSE("GPL v2");426