[go: nahoru, domu]

1d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg/*
2d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg * Intel Low Power Subsystem PWM controller driver
3d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg *
4d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg * Copyright (C) 2014, Intel Corporation
5d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg * Author: Mika Westerberg <mika.westerberg@linux.intel.com>
6d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg * Author: Chew Kean Ho <kean.ho.chew@intel.com>
7d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg * Author: Chang Rebecca Swee Fun <rebecca.swee.fun.chang@intel.com>
8d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg * Author: Chew Chiau Ee <chiau.ee.chew@intel.com>
9093e00bb3f82f3c67e2d1682e316fc012bcd0d92Alan Cox * Author: Alan Cox <alan@linux.intel.com>
10d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg *
11d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg * This program is free software; you can redistribute it and/or modify
12d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg * it under the terms of the GNU General Public License version 2 as
13d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg * published by the Free Software Foundation.
14d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg */
15d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg
16e0c86a3b63e948e51a47d17382c7cd8711d19750Thierry Reding#include <linux/io.h>
17d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg#include <linux/kernel.h>
18d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg#include <linux/module.h>
19093e00bb3f82f3c67e2d1682e316fc012bcd0d92Alan Cox
20c558e39e14c2372394f49e07fbe94e9708b615cbAndy Shevchenko#include "pwm-lpss.h"
21d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg
22d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg#define PWM				0x00000000
23d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg#define PWM_ENABLE			BIT(31)
24d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg#define PWM_SW_UPDATE			BIT(30)
25d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg#define PWM_BASE_UNIT_SHIFT		8
26d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg#define PWM_BASE_UNIT_MASK		0x00ffff00
27d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg#define PWM_ON_TIME_DIV_MASK		0x000000ff
28d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg#define PWM_DIVISION_CORRECTION		0x2
29d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg#define PWM_LIMIT			(0x8000 + PWM_DIVISION_CORRECTION)
30d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg#define NSECS_PER_SEC			1000000000UL
31d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg
32d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerbergstruct pwm_lpss_chip {
33d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	struct pwm_chip chip;
34d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	void __iomem *regs;
35093e00bb3f82f3c67e2d1682e316fc012bcd0d92Alan Cox	unsigned long clk_rate;
36093e00bb3f82f3c67e2d1682e316fc012bcd0d92Alan Cox};
37093e00bb3f82f3c67e2d1682e316fc012bcd0d92Alan Cox
38093e00bb3f82f3c67e2d1682e316fc012bcd0d92Alan Cox/* BayTrail */
39c558e39e14c2372394f49e07fbe94e9708b615cbAndy Shevchenkoconst struct pwm_lpss_boardinfo pwm_lpss_byt_info = {
40b2b7adeb21745266326d453b95e5d0b1b9cb1d4eJulia Lawall	.clk_rate = 25000000
41d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg};
42c558e39e14c2372394f49e07fbe94e9708b615cbAndy ShevchenkoEXPORT_SYMBOL_GPL(pwm_lpss_byt_info);
43d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg
44373c57829a3f9da1405b1fbd3d17e50f8e1f476eAlan Cox/* Braswell */
45c558e39e14c2372394f49e07fbe94e9708b615cbAndy Shevchenkoconst struct pwm_lpss_boardinfo pwm_lpss_bsw_info = {
46b2b7adeb21745266326d453b95e5d0b1b9cb1d4eJulia Lawall	.clk_rate = 19200000
47373c57829a3f9da1405b1fbd3d17e50f8e1f476eAlan Cox};
48c558e39e14c2372394f49e07fbe94e9708b615cbAndy ShevchenkoEXPORT_SYMBOL_GPL(pwm_lpss_bsw_info);
49373c57829a3f9da1405b1fbd3d17e50f8e1f476eAlan Cox
50d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerbergstatic inline struct pwm_lpss_chip *to_lpwm(struct pwm_chip *chip)
51d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg{
52d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	return container_of(chip, struct pwm_lpss_chip, chip);
53d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg}
54d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg
55d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerbergstatic int pwm_lpss_config(struct pwm_chip *chip, struct pwm_device *pwm,
56d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg			   int duty_ns, int period_ns)
57d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg{
58d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	struct pwm_lpss_chip *lpwm = to_lpwm(chip);
59d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	u8 on_time_div;
60d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	unsigned long c;
61d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	unsigned long long base_unit, freq = NSECS_PER_SEC;
62d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	u32 ctrl;
63d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg
64d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	do_div(freq, period_ns);
65d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg
66d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	/* The equation is: base_unit = ((freq / c) * 65536) + correction */
67d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	base_unit = freq * 65536;
68d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg
69093e00bb3f82f3c67e2d1682e316fc012bcd0d92Alan Cox	c = lpwm->clk_rate;
70d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	if (!c)
71d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg		return -EINVAL;
72d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg
73d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	do_div(base_unit, c);
74d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	base_unit += PWM_DIVISION_CORRECTION;
75d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	if (base_unit > PWM_LIMIT)
76d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg		return -EINVAL;
77d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg
78d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	if (duty_ns <= 0)
79d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg		duty_ns = 1;
80d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	on_time_div = 255 - (255 * duty_ns / period_ns);
81d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg
82d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	ctrl = readl(lpwm->regs + PWM);
83d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	ctrl &= ~(PWM_BASE_UNIT_MASK | PWM_ON_TIME_DIV_MASK);
84d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	ctrl |= (u16) base_unit << PWM_BASE_UNIT_SHIFT;
85d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	ctrl |= on_time_div;
86d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	/* request PWM to update on next cycle */
87d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	ctrl |= PWM_SW_UPDATE;
88d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	writel(ctrl, lpwm->regs + PWM);
89d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg
90d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	return 0;
91d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg}
92d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg
93d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerbergstatic int pwm_lpss_enable(struct pwm_chip *chip, struct pwm_device *pwm)
94d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg{
95d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	struct pwm_lpss_chip *lpwm = to_lpwm(chip);
96d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	u32 ctrl;
97d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg
98d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	ctrl = readl(lpwm->regs + PWM);
99d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	writel(ctrl | PWM_ENABLE, lpwm->regs + PWM);
100d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg
101d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	return 0;
102d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg}
103d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg
104d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerbergstatic void pwm_lpss_disable(struct pwm_chip *chip, struct pwm_device *pwm)
105d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg{
106d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	struct pwm_lpss_chip *lpwm = to_lpwm(chip);
107d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	u32 ctrl;
108d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg
109d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	ctrl = readl(lpwm->regs + PWM);
110d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	writel(ctrl & ~PWM_ENABLE, lpwm->regs + PWM);
111d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg}
112d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg
113d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerbergstatic const struct pwm_ops pwm_lpss_ops = {
114d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	.config = pwm_lpss_config,
115d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	.enable = pwm_lpss_enable,
116d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	.disable = pwm_lpss_disable,
117d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	.owner = THIS_MODULE,
118d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg};
119d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg
120c558e39e14c2372394f49e07fbe94e9708b615cbAndy Shevchenkostruct pwm_lpss_chip *pwm_lpss_probe(struct device *dev, struct resource *r,
121c558e39e14c2372394f49e07fbe94e9708b615cbAndy Shevchenko				     const struct pwm_lpss_boardinfo *info)
122d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg{
123d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	struct pwm_lpss_chip *lpwm;
124d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	int ret;
125d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg
126093e00bb3f82f3c67e2d1682e316fc012bcd0d92Alan Cox	lpwm = devm_kzalloc(dev, sizeof(*lpwm), GFP_KERNEL);
127d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	if (!lpwm)
128093e00bb3f82f3c67e2d1682e316fc012bcd0d92Alan Cox		return ERR_PTR(-ENOMEM);
129d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg
130093e00bb3f82f3c67e2d1682e316fc012bcd0d92Alan Cox	lpwm->regs = devm_ioremap_resource(dev, r);
131d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	if (IS_ERR(lpwm->regs))
13289c0339e0aa097384b3efed894b23820814c21d3Thierry Reding		return ERR_CAST(lpwm->regs);
133093e00bb3f82f3c67e2d1682e316fc012bcd0d92Alan Cox
13465accd87381ed96bf8893124b149bae08edd2740Heikki Krogerus	lpwm->clk_rate = info->clk_rate;
135093e00bb3f82f3c67e2d1682e316fc012bcd0d92Alan Cox	lpwm->chip.dev = dev;
136d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	lpwm->chip.ops = &pwm_lpss_ops;
137d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	lpwm->chip.base = -1;
138d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	lpwm->chip.npwm = 1;
139d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg
140d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	ret = pwmchip_add(&lpwm->chip);
141d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	if (ret) {
142093e00bb3f82f3c67e2d1682e316fc012bcd0d92Alan Cox		dev_err(dev, "failed to add PWM chip: %d\n", ret);
143093e00bb3f82f3c67e2d1682e316fc012bcd0d92Alan Cox		return ERR_PTR(ret);
144d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	}
145d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg
146093e00bb3f82f3c67e2d1682e316fc012bcd0d92Alan Cox	return lpwm;
147d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg}
148c558e39e14c2372394f49e07fbe94e9708b615cbAndy ShevchenkoEXPORT_SYMBOL_GPL(pwm_lpss_probe);
149d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg
150c558e39e14c2372394f49e07fbe94e9708b615cbAndy Shevchenkoint pwm_lpss_remove(struct pwm_lpss_chip *lpwm)
151d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg{
152d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	u32 ctrl;
153d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg
154d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	ctrl = readl(lpwm->regs + PWM);
155d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	writel(ctrl & ~PWM_ENABLE, lpwm->regs + PWM);
156d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg
157d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg	return pwmchip_remove(&lpwm->chip);
158d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg}
159c558e39e14c2372394f49e07fbe94e9708b615cbAndy ShevchenkoEXPORT_SYMBOL_GPL(pwm_lpss_remove);
160d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika Westerberg
161d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika WesterbergMODULE_DESCRIPTION("PWM driver for Intel LPSS");
162d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika WesterbergMODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
163d16a5aa9e821633a3095d7a88cd1d2cd108bf966Mika WesterbergMODULE_LICENSE("GPL v2");
164