diff options
Diffstat (limited to 'drivers/pwm/pwm-mediatek.c')
| -rw-r--r-- | drivers/pwm/pwm-mediatek.c | 231 | 
1 files changed, 119 insertions, 112 deletions
diff --git a/drivers/pwm/pwm-mediatek.c b/drivers/pwm/pwm-mediatek.c index eb6674ce995f..b94e0d09c300 100644 --- a/drivers/pwm/pwm-mediatek.c +++ b/drivers/pwm/pwm-mediatek.c @@ -1,12 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0  /* - * Mediatek Pulse Width Modulator driver + * MediaTek Pulse Width Modulator driver   *   * Copyright (C) 2015 John Crispin <[email protected]>   * Copyright (C) 2017 Zhi Mao <[email protected]>   * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied.   */  #include <linux/err.h> @@ -35,125 +33,107 @@  #define PWM_CLK_DIV_MAX		7 -enum { -	MTK_CLK_MAIN = 0, -	MTK_CLK_TOP, -	MTK_CLK_PWM1, -	MTK_CLK_PWM2, -	MTK_CLK_PWM3, -	MTK_CLK_PWM4, -	MTK_CLK_PWM5, -	MTK_CLK_PWM6, -	MTK_CLK_PWM7, -	MTK_CLK_PWM8, -	MTK_CLK_MAX, -}; - -static const char * const mtk_pwm_clk_name[MTK_CLK_MAX] = { -	"main", "top", "pwm1", "pwm2", "pwm3", "pwm4", "pwm5", "pwm6", "pwm7", -	"pwm8" -}; - -struct mtk_pwm_platform_data { +struct pwm_mediatek_of_data {  	unsigned int num_pwms;  	bool pwm45_fixup; -	bool has_clks;  };  /** - * struct mtk_pwm_chip - struct representing PWM chip + * struct pwm_mediatek_chip - struct representing PWM chip   * @chip: linux PWM chip representation   * @regs: base address of PWM chip - * @clks: list of clocks + * @clk_top: the top clock generator + * @clk_main: the clock used by PWM core + * @clk_pwms: the clock used by each PWM channel + * @clk_freq: the fix clock frequency of legacy MIPS SoC   */ -struct mtk_pwm_chip { +struct pwm_mediatek_chip {  	struct pwm_chip chip;  	void __iomem *regs; -	struct clk *clks[MTK_CLK_MAX]; -	const struct mtk_pwm_platform_data *soc; +	struct clk *clk_top; +	struct clk *clk_main; +	struct clk **clk_pwms; +	const struct pwm_mediatek_of_data *soc;  }; -static const unsigned int mtk_pwm_reg_offset[] = { +static const unsigned int pwm_mediatek_reg_offset[] = {  	0x0010, 0x0050, 0x0090, 0x00d0, 0x0110, 0x0150, 0x0190, 0x0220  }; -static inline struct mtk_pwm_chip *to_mtk_pwm_chip(struct pwm_chip *chip) +static inline struct pwm_mediatek_chip * +to_pwm_mediatek_chip(struct pwm_chip *chip)  { -	return container_of(chip, struct mtk_pwm_chip, chip); +	return container_of(chip, struct pwm_mediatek_chip, chip);  } -static int mtk_pwm_clk_enable(struct pwm_chip *chip, struct pwm_device *pwm) +static int pwm_mediatek_clk_enable(struct pwm_chip *chip, +				   struct pwm_device *pwm)  { -	struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip); +	struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip);  	int ret; -	if (!pc->soc->has_clks) -		return 0; - -	ret = clk_prepare_enable(pc->clks[MTK_CLK_TOP]); +	ret = clk_prepare_enable(pc->clk_top);  	if (ret < 0)  		return ret; -	ret = clk_prepare_enable(pc->clks[MTK_CLK_MAIN]); +	ret = clk_prepare_enable(pc->clk_main);  	if (ret < 0)  		goto disable_clk_top; -	ret = clk_prepare_enable(pc->clks[MTK_CLK_PWM1 + pwm->hwpwm]); +	ret = clk_prepare_enable(pc->clk_pwms[pwm->hwpwm]);  	if (ret < 0)  		goto disable_clk_main;  	return 0;  disable_clk_main: -	clk_disable_unprepare(pc->clks[MTK_CLK_MAIN]); +	clk_disable_unprepare(pc->clk_main);  disable_clk_top: -	clk_disable_unprepare(pc->clks[MTK_CLK_TOP]); +	clk_disable_unprepare(pc->clk_top);  	return ret;  } -static void mtk_pwm_clk_disable(struct pwm_chip *chip, struct pwm_device *pwm) +static void pwm_mediatek_clk_disable(struct pwm_chip *chip, +				     struct pwm_device *pwm)  { -	struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip); - -	if (!pc->soc->has_clks) -		return; +	struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip); -	clk_disable_unprepare(pc->clks[MTK_CLK_PWM1 + pwm->hwpwm]); -	clk_disable_unprepare(pc->clks[MTK_CLK_MAIN]); -	clk_disable_unprepare(pc->clks[MTK_CLK_TOP]); +	clk_disable_unprepare(pc->clk_pwms[pwm->hwpwm]); +	clk_disable_unprepare(pc->clk_main); +	clk_disable_unprepare(pc->clk_top);  } -static inline u32 mtk_pwm_readl(struct mtk_pwm_chip *chip, unsigned int num, -				unsigned int offset) +static inline u32 pwm_mediatek_readl(struct pwm_mediatek_chip *chip, +				     unsigned int num, unsigned int offset)  { -	return readl(chip->regs + mtk_pwm_reg_offset[num] + offset); +	return readl(chip->regs + pwm_mediatek_reg_offset[num] + offset);  } -static inline void mtk_pwm_writel(struct mtk_pwm_chip *chip, -				  unsigned int num, unsigned int offset, -				  u32 value) +static inline void pwm_mediatek_writel(struct pwm_mediatek_chip *chip, +				       unsigned int num, unsigned int offset, +				       u32 value)  { -	writel(value, chip->regs + mtk_pwm_reg_offset[num] + offset); +	writel(value, chip->regs + pwm_mediatek_reg_offset[num] + offset);  } -static int mtk_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, -			  int duty_ns, int period_ns) +static int pwm_mediatek_config(struct pwm_chip *chip, struct pwm_device *pwm, +			       int duty_ns, int period_ns)  { -	struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip); -	struct clk *clk = pc->clks[MTK_CLK_PWM1 + pwm->hwpwm]; +	struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip);  	u32 clkdiv = 0, cnt_period, cnt_duty, reg_width = PWMDWIDTH,  	    reg_thres = PWMTHRES;  	u64 resolution;  	int ret; -	ret = mtk_pwm_clk_enable(chip, pwm); +	ret = pwm_mediatek_clk_enable(chip, pwm); +  	if (ret < 0)  		return ret;  	/* Using resolution in picosecond gets accuracy higher */  	resolution = (u64)NSEC_PER_SEC * 1000; -	do_div(resolution, clk_get_rate(clk)); +	do_div(resolution, clk_get_rate(pc->clk_pwms[pwm->hwpwm]));  	cnt_period = DIV_ROUND_CLOSEST_ULL((u64)period_ns * 1000, resolution);  	while (cnt_period > 8191) { @@ -164,7 +144,7 @@ static int mtk_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,  	}  	if (clkdiv > PWM_CLK_DIV_MAX) { -		mtk_pwm_clk_disable(chip, pwm); +		pwm_mediatek_clk_disable(chip, pwm);  		dev_err(chip->dev, "period %d not supported\n", period_ns);  		return -EINVAL;  	} @@ -179,22 +159,22 @@ static int mtk_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,  	}  	cnt_duty = DIV_ROUND_CLOSEST_ULL((u64)duty_ns * 1000, resolution); -	mtk_pwm_writel(pc, pwm->hwpwm, PWMCON, BIT(15) | clkdiv); -	mtk_pwm_writel(pc, pwm->hwpwm, reg_width, cnt_period); -	mtk_pwm_writel(pc, pwm->hwpwm, reg_thres, cnt_duty); +	pwm_mediatek_writel(pc, pwm->hwpwm, PWMCON, BIT(15) | clkdiv); +	pwm_mediatek_writel(pc, pwm->hwpwm, reg_width, cnt_period); +	pwm_mediatek_writel(pc, pwm->hwpwm, reg_thres, cnt_duty); -	mtk_pwm_clk_disable(chip, pwm); +	pwm_mediatek_clk_disable(chip, pwm);  	return 0;  } -static int mtk_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +static int pwm_mediatek_enable(struct pwm_chip *chip, struct pwm_device *pwm)  { -	struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip); +	struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip);  	u32 value;  	int ret; -	ret = mtk_pwm_clk_enable(chip, pwm); +	ret = pwm_mediatek_clk_enable(chip, pwm);  	if (ret < 0)  		return ret; @@ -205,29 +185,28 @@ static int mtk_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)  	return 0;  } -static void mtk_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +static void pwm_mediatek_disable(struct pwm_chip *chip, struct pwm_device *pwm)  { -	struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip); +	struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip);  	u32 value;  	value = readl(pc->regs);  	value &= ~BIT(pwm->hwpwm);  	writel(value, pc->regs); -	mtk_pwm_clk_disable(chip, pwm); +	pwm_mediatek_clk_disable(chip, pwm);  } -static const struct pwm_ops mtk_pwm_ops = { -	.config = mtk_pwm_config, -	.enable = mtk_pwm_enable, -	.disable = mtk_pwm_disable, +static const struct pwm_ops pwm_mediatek_ops = { +	.config = pwm_mediatek_config, +	.enable = pwm_mediatek_enable, +	.disable = pwm_mediatek_disable,  	.owner = THIS_MODULE,  }; -static int mtk_pwm_probe(struct platform_device *pdev) +static int pwm_mediatek_probe(struct platform_device *pdev)  { -	const struct mtk_pwm_platform_data *data; -	struct mtk_pwm_chip *pc; +	struct pwm_mediatek_chip *pc;  	struct resource *res;  	unsigned int i;  	int ret; @@ -236,31 +215,51 @@ static int mtk_pwm_probe(struct platform_device *pdev)  	if (!pc)  		return -ENOMEM; -	data = of_device_get_match_data(&pdev->dev); -	if (data == NULL) -		return -EINVAL; -	pc->soc = data; +	pc->soc = of_device_get_match_data(&pdev->dev);  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  	pc->regs = devm_ioremap_resource(&pdev->dev, res);  	if (IS_ERR(pc->regs))  		return PTR_ERR(pc->regs); -	for (i = 0; i < data->num_pwms + 2 && pc->soc->has_clks; i++) { -		pc->clks[i] = devm_clk_get(&pdev->dev, mtk_pwm_clk_name[i]); -		if (IS_ERR(pc->clks[i])) { +	pc->clk_pwms = devm_kcalloc(&pdev->dev, pc->soc->num_pwms, +				    sizeof(*pc->clk_pwms), GFP_KERNEL); +	if (!pc->clk_pwms) +		return -ENOMEM; + +	pc->clk_top = devm_clk_get(&pdev->dev, "top"); +	if (IS_ERR(pc->clk_top)) { +		dev_err(&pdev->dev, "clock: top fail: %ld\n", +			PTR_ERR(pc->clk_top)); +		return PTR_ERR(pc->clk_top); +	} + +	pc->clk_main = devm_clk_get(&pdev->dev, "main"); +	if (IS_ERR(pc->clk_main)) { +		dev_err(&pdev->dev, "clock: main fail: %ld\n", +			PTR_ERR(pc->clk_main)); +		return PTR_ERR(pc->clk_main); +	} + +	for (i = 0; i < pc->soc->num_pwms; i++) { +		char name[8]; + +		snprintf(name, sizeof(name), "pwm%d", i + 1); + +		pc->clk_pwms[i] = devm_clk_get(&pdev->dev, name); +		if (IS_ERR(pc->clk_pwms[i])) {  			dev_err(&pdev->dev, "clock: %s fail: %ld\n", -				mtk_pwm_clk_name[i], PTR_ERR(pc->clks[i])); -			return PTR_ERR(pc->clks[i]); +				name, PTR_ERR(pc->clk_pwms[i])); +			return PTR_ERR(pc->clk_pwms[i]);  		}  	}  	platform_set_drvdata(pdev, pc);  	pc->chip.dev = &pdev->dev; -	pc->chip.ops = &mtk_pwm_ops; +	pc->chip.ops = &pwm_mediatek_ops;  	pc->chip.base = -1; -	pc->chip.npwm = data->num_pwms; +	pc->chip.npwm = pc->soc->num_pwms;  	ret = pwmchip_add(&pc->chip);  	if (ret < 0) { @@ -271,55 +270,63 @@ static int mtk_pwm_probe(struct platform_device *pdev)  	return 0;  } -static int mtk_pwm_remove(struct platform_device *pdev) +static int pwm_mediatek_remove(struct platform_device *pdev)  { -	struct mtk_pwm_chip *pc = platform_get_drvdata(pdev); +	struct pwm_mediatek_chip *pc = platform_get_drvdata(pdev);  	return pwmchip_remove(&pc->chip);  } -static const struct mtk_pwm_platform_data mt2712_pwm_data = { +static const struct pwm_mediatek_of_data mt2712_pwm_data = {  	.num_pwms = 8,  	.pwm45_fixup = false, -	.has_clks = true,  }; -static const struct mtk_pwm_platform_data mt7622_pwm_data = { +static const struct pwm_mediatek_of_data mt7622_pwm_data = {  	.num_pwms = 6,  	.pwm45_fixup = false, -	.has_clks = true,  }; -static const struct mtk_pwm_platform_data mt7623_pwm_data = { +static const struct pwm_mediatek_of_data mt7623_pwm_data = {  	.num_pwms = 5,  	.pwm45_fixup = true, -	.has_clks = true,  }; -static const struct mtk_pwm_platform_data mt7628_pwm_data = { +static const struct pwm_mediatek_of_data mt7628_pwm_data = {  	.num_pwms = 4,  	.pwm45_fixup = true, -	.has_clks = false,  }; -static const struct of_device_id mtk_pwm_of_match[] = { +static const struct pwm_mediatek_of_data mt7629_pwm_data = { +	.num_pwms = 1, +	.pwm45_fixup = false, +}; + +static const struct pwm_mediatek_of_data mt8516_pwm_data = { +	.num_pwms = 5, +	.pwm45_fixup = false, +}; + +static const struct of_device_id pwm_mediatek_of_match[] = {  	{ .compatible = "mediatek,mt2712-pwm", .data = &mt2712_pwm_data },  	{ .compatible = "mediatek,mt7622-pwm", .data = &mt7622_pwm_data },  	{ .compatible = "mediatek,mt7623-pwm", .data = &mt7623_pwm_data },  	{ .compatible = "mediatek,mt7628-pwm", .data = &mt7628_pwm_data }, +	{ .compatible = "mediatek,mt7629-pwm", .data = &mt7629_pwm_data }, +	{ .compatible = "mediatek,mt8516-pwm", .data = &mt8516_pwm_data },  	{ },  }; -MODULE_DEVICE_TABLE(of, mtk_pwm_of_match); +MODULE_DEVICE_TABLE(of, pwm_mediatek_of_match); -static struct platform_driver mtk_pwm_driver = { +static struct platform_driver pwm_mediatek_driver = {  	.driver = { -		.name = "mtk-pwm", -		.of_match_table = mtk_pwm_of_match, +		.name = "pwm-mediatek", +		.of_match_table = pwm_mediatek_of_match,  	}, -	.probe = mtk_pwm_probe, -	.remove = mtk_pwm_remove, +	.probe = pwm_mediatek_probe, +	.remove = pwm_mediatek_remove,  }; -module_platform_driver(mtk_pwm_driver); +module_platform_driver(pwm_mediatek_driver);  MODULE_AUTHOR("John Crispin <[email protected]>"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2");  |