brintos

brintos / linux-shallow public Read only

0
0
Text · 7.3 KiB · 831aed2 Raw
287 lines · c
1/*2 * Marvell Berlin PWM driver3 *4 * Copyright (C) 2015 Marvell Technology Group Ltd.5 *6 * Author: Antoine Tenart <antoine.tenart@free-electrons.com>7 *8 * This file is licensed under the terms of the GNU General Public9 * License version 2. This program is licensed "as is" without any10 * warranty of any kind, whether express or implied.11 */12 13#include <linux/clk.h>14#include <linux/io.h>15#include <linux/kernel.h>16#include <linux/mod_devicetable.h>17#include <linux/module.h>18#include <linux/platform_device.h>19#include <linux/pwm.h>20#include <linux/slab.h>21 22#define BERLIN_PWM_EN			0x023#define  BERLIN_PWM_ENABLE		BIT(0)24#define BERLIN_PWM_CONTROL		0x425/*26 * The prescaler claims to support 8 different moduli, configured using the27 * low three bits of PWM_CONTROL. (Sequentially, they are 1, 4, 8, 16, 64,28 * 256, 1024, and 4096.)  However, the moduli from 4 to 1024 appear to be29 * implemented by internally shifting TCNT left without adding additional30 * bits. So, the max TCNT that actually works for a modulus of 4 is 0x3fff;31 * for 8, 0x1fff; and so on. This means that those moduli are entirely32 * useless, as we could just do the shift ourselves. The 4096 modulus is33 * implemented with a real prescaler, so we do use that, but we treat it34 * as a flag instead of pretending the modulus is actually configurable.35 */36#define  BERLIN_PWM_PRESCALE_4096	0x737#define  BERLIN_PWM_INVERT_POLARITY	BIT(3)38#define BERLIN_PWM_DUTY			0x839#define BERLIN_PWM_TCNT			0xc40#define  BERLIN_PWM_MAX_TCNT		6553541 42#define BERLIN_PWM_NUMPWMS		443 44struct berlin_pwm_channel {45	u32 enable;46	u32 ctrl;47	u32 duty;48	u32 tcnt;49};50 51struct berlin_pwm_chip {52	struct clk *clk;53	void __iomem *base;54	struct berlin_pwm_channel channel[BERLIN_PWM_NUMPWMS];55};56 57static inline struct berlin_pwm_chip *to_berlin_pwm_chip(struct pwm_chip *chip)58{59	return pwmchip_get_drvdata(chip);60}61 62static inline u32 berlin_pwm_readl(struct berlin_pwm_chip *bpc,63				   unsigned int channel, unsigned long offset)64{65	return readl_relaxed(bpc->base + channel * 0x10 + offset);66}67 68static inline void berlin_pwm_writel(struct berlin_pwm_chip *bpc,69				     unsigned int channel, u32 value,70				     unsigned long offset)71{72	writel_relaxed(value, bpc->base + channel * 0x10 + offset);73}74 75static int berlin_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,76			     u64 duty_ns, u64 period_ns)77{78	struct berlin_pwm_chip *bpc = to_berlin_pwm_chip(chip);79	bool prescale_4096 = false;80	u32 value, duty, period;81	u64 cycles;82 83	cycles = clk_get_rate(bpc->clk);84	cycles *= period_ns;85	do_div(cycles, NSEC_PER_SEC);86 87	if (cycles > BERLIN_PWM_MAX_TCNT) {88		prescale_4096 = true;89		cycles >>= 12; // Prescaled by 409690 91		if (cycles > BERLIN_PWM_MAX_TCNT)92			return -ERANGE;93	}94 95	period = cycles;96	cycles *= duty_ns;97	do_div(cycles, period_ns);98	duty = cycles;99 100	value = berlin_pwm_readl(bpc, pwm->hwpwm, BERLIN_PWM_CONTROL);101	if (prescale_4096)102		value |= BERLIN_PWM_PRESCALE_4096;103	else104		value &= ~BERLIN_PWM_PRESCALE_4096;105	berlin_pwm_writel(bpc, pwm->hwpwm, value, BERLIN_PWM_CONTROL);106 107	berlin_pwm_writel(bpc, pwm->hwpwm, duty, BERLIN_PWM_DUTY);108	berlin_pwm_writel(bpc, pwm->hwpwm, period, BERLIN_PWM_TCNT);109 110	return 0;111}112 113static int berlin_pwm_set_polarity(struct pwm_chip *chip,114				   struct pwm_device *pwm,115				   enum pwm_polarity polarity)116{117	struct berlin_pwm_chip *bpc = to_berlin_pwm_chip(chip);118	u32 value;119 120	value = berlin_pwm_readl(bpc, pwm->hwpwm, BERLIN_PWM_CONTROL);121 122	if (polarity == PWM_POLARITY_NORMAL)123		value &= ~BERLIN_PWM_INVERT_POLARITY;124	else125		value |= BERLIN_PWM_INVERT_POLARITY;126 127	berlin_pwm_writel(bpc, pwm->hwpwm, value, BERLIN_PWM_CONTROL);128 129	return 0;130}131 132static int berlin_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)133{134	struct berlin_pwm_chip *bpc = to_berlin_pwm_chip(chip);135	u32 value;136 137	value = berlin_pwm_readl(bpc, pwm->hwpwm, BERLIN_PWM_EN);138	value |= BERLIN_PWM_ENABLE;139	berlin_pwm_writel(bpc, pwm->hwpwm, value, BERLIN_PWM_EN);140 141	return 0;142}143 144static void berlin_pwm_disable(struct pwm_chip *chip,145			       struct pwm_device *pwm)146{147	struct berlin_pwm_chip *bpc = to_berlin_pwm_chip(chip);148	u32 value;149 150	value = berlin_pwm_readl(bpc, pwm->hwpwm, BERLIN_PWM_EN);151	value &= ~BERLIN_PWM_ENABLE;152	berlin_pwm_writel(bpc, pwm->hwpwm, value, BERLIN_PWM_EN);153}154 155static int berlin_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,156			    const struct pwm_state *state)157{158	int err;159	bool enabled = pwm->state.enabled;160 161	if (state->polarity != pwm->state.polarity) {162		if (enabled) {163			berlin_pwm_disable(chip, pwm);164			enabled = false;165		}166 167		err = berlin_pwm_set_polarity(chip, pwm, state->polarity);168		if (err)169			return err;170	}171 172	if (!state->enabled) {173		if (enabled)174			berlin_pwm_disable(chip, pwm);175		return 0;176	}177 178	err = berlin_pwm_config(chip, pwm, state->duty_cycle, state->period);179	if (err)180		return err;181 182	if (!enabled)183		return berlin_pwm_enable(chip, pwm);184 185	return 0;186}187 188static const struct pwm_ops berlin_pwm_ops = {189	.apply = berlin_pwm_apply,190};191 192static const struct of_device_id berlin_pwm_match[] = {193	{ .compatible = "marvell,berlin-pwm" },194	{ },195};196MODULE_DEVICE_TABLE(of, berlin_pwm_match);197 198static int berlin_pwm_probe(struct platform_device *pdev)199{200	struct pwm_chip *chip;201	struct berlin_pwm_chip *bpc;202	int ret;203 204	chip = devm_pwmchip_alloc(&pdev->dev, BERLIN_PWM_NUMPWMS, sizeof(*bpc));205	if (IS_ERR(chip))206		return PTR_ERR(chip);207	bpc = to_berlin_pwm_chip(chip);208 209	bpc->base = devm_platform_ioremap_resource(pdev, 0);210	if (IS_ERR(bpc->base))211		return PTR_ERR(bpc->base);212 213	bpc->clk = devm_clk_get_enabled(&pdev->dev, NULL);214	if (IS_ERR(bpc->clk))215		return PTR_ERR(bpc->clk);216 217	chip->ops = &berlin_pwm_ops;218 219	ret = devm_pwmchip_add(&pdev->dev, chip);220	if (ret < 0)221		return dev_err_probe(&pdev->dev, ret, "failed to add PWM chip\n");222 223	platform_set_drvdata(pdev, chip);224 225	return 0;226}227 228static int berlin_pwm_suspend(struct device *dev)229{230	struct pwm_chip *chip = dev_get_drvdata(dev);231	struct berlin_pwm_chip *bpc = to_berlin_pwm_chip(chip);232	unsigned int i;233 234	for (i = 0; i < chip->npwm; i++) {235		struct berlin_pwm_channel *channel = &bpc->channel[i];236 237		channel->enable = berlin_pwm_readl(bpc, i, BERLIN_PWM_ENABLE);238		channel->ctrl = berlin_pwm_readl(bpc, i, BERLIN_PWM_CONTROL);239		channel->duty = berlin_pwm_readl(bpc, i, BERLIN_PWM_DUTY);240		channel->tcnt = berlin_pwm_readl(bpc, i, BERLIN_PWM_TCNT);241	}242 243	clk_disable_unprepare(bpc->clk);244 245	return 0;246}247 248static int berlin_pwm_resume(struct device *dev)249{250	struct pwm_chip *chip = dev_get_drvdata(dev);251	struct berlin_pwm_chip *bpc = to_berlin_pwm_chip(chip);252	unsigned int i;253	int ret;254 255	ret = clk_prepare_enable(bpc->clk);256	if (ret)257		return ret;258 259	for (i = 0; i < chip->npwm; i++) {260		struct berlin_pwm_channel *channel = &bpc->channel[i];261 262		berlin_pwm_writel(bpc, i, channel->ctrl, BERLIN_PWM_CONTROL);263		berlin_pwm_writel(bpc, i, channel->duty, BERLIN_PWM_DUTY);264		berlin_pwm_writel(bpc, i, channel->tcnt, BERLIN_PWM_TCNT);265		berlin_pwm_writel(bpc, i, channel->enable, BERLIN_PWM_ENABLE);266	}267 268	return 0;269}270 271static DEFINE_SIMPLE_DEV_PM_OPS(berlin_pwm_pm_ops, berlin_pwm_suspend,272				berlin_pwm_resume);273 274static struct platform_driver berlin_pwm_driver = {275	.probe = berlin_pwm_probe,276	.driver = {277		.name = "berlin-pwm",278		.of_match_table = berlin_pwm_match,279		.pm = pm_ptr(&berlin_pwm_pm_ops),280	},281};282module_platform_driver(berlin_pwm_driver);283 284MODULE_AUTHOR("Antoine Tenart <antoine.tenart@free-electrons.com>");285MODULE_DESCRIPTION("Marvell Berlin PWM driver");286MODULE_LICENSE("GPL v2");287