xref: /openbmc/linux/drivers/pwm/pwm-lpss.c (revision 33ac9dba)
1 /*
2  * Intel Low Power Subsystem PWM controller driver
3  *
4  * Copyright (C) 2014, Intel Corporation
5  * Author: Mika Westerberg <mika.westerberg@linux.intel.com>
6  * Author: Chew Kean Ho <kean.ho.chew@intel.com>
7  * Author: Chang Rebecca Swee Fun <rebecca.swee.fun.chang@intel.com>
8  * Author: Chew Chiau Ee <chiau.ee.chew@intel.com>
9  * Author: Alan Cox <alan@linux.intel.com>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License version 2 as
13  * published by the Free Software Foundation.
14  */
15 
16 #include <linux/acpi.h>
17 #include <linux/device.h>
18 #include <linux/kernel.h>
19 #include <linux/module.h>
20 #include <linux/pwm.h>
21 #include <linux/platform_device.h>
22 #include <linux/pci.h>
23 
24 static int pci_drv, plat_drv;	/* So we know which drivers registered */
25 
26 #define PWM				0x00000000
27 #define PWM_ENABLE			BIT(31)
28 #define PWM_SW_UPDATE			BIT(30)
29 #define PWM_BASE_UNIT_SHIFT		8
30 #define PWM_BASE_UNIT_MASK		0x00ffff00
31 #define PWM_ON_TIME_DIV_MASK		0x000000ff
32 #define PWM_DIVISION_CORRECTION		0x2
33 #define PWM_LIMIT			(0x8000 + PWM_DIVISION_CORRECTION)
34 #define NSECS_PER_SEC			1000000000UL
35 
36 struct pwm_lpss_chip {
37 	struct pwm_chip chip;
38 	void __iomem *regs;
39 	unsigned long clk_rate;
40 };
41 
42 struct pwm_lpss_boardinfo {
43 	unsigned long clk_rate;
44 };
45 
46 /* BayTrail */
47 static const struct pwm_lpss_boardinfo byt_info = {
48 	25000000
49 };
50 
51 static inline struct pwm_lpss_chip *to_lpwm(struct pwm_chip *chip)
52 {
53 	return container_of(chip, struct pwm_lpss_chip, chip);
54 }
55 
56 static int pwm_lpss_config(struct pwm_chip *chip, struct pwm_device *pwm,
57 			   int duty_ns, int period_ns)
58 {
59 	struct pwm_lpss_chip *lpwm = to_lpwm(chip);
60 	u8 on_time_div;
61 	unsigned long c;
62 	unsigned long long base_unit, freq = NSECS_PER_SEC;
63 	u32 ctrl;
64 
65 	do_div(freq, period_ns);
66 
67 	/* The equation is: base_unit = ((freq / c) * 65536) + correction */
68 	base_unit = freq * 65536;
69 
70 	c = lpwm->clk_rate;
71 	if (!c)
72 		return -EINVAL;
73 
74 	do_div(base_unit, c);
75 	base_unit += PWM_DIVISION_CORRECTION;
76 	if (base_unit > PWM_LIMIT)
77 		return -EINVAL;
78 
79 	if (duty_ns <= 0)
80 		duty_ns = 1;
81 	on_time_div = 255 - (255 * duty_ns / period_ns);
82 
83 	ctrl = readl(lpwm->regs + PWM);
84 	ctrl &= ~(PWM_BASE_UNIT_MASK | PWM_ON_TIME_DIV_MASK);
85 	ctrl |= (u16) base_unit << PWM_BASE_UNIT_SHIFT;
86 	ctrl |= on_time_div;
87 	/* request PWM to update on next cycle */
88 	ctrl |= PWM_SW_UPDATE;
89 	writel(ctrl, lpwm->regs + PWM);
90 
91 	return 0;
92 }
93 
94 static int pwm_lpss_enable(struct pwm_chip *chip, struct pwm_device *pwm)
95 {
96 	struct pwm_lpss_chip *lpwm = to_lpwm(chip);
97 	u32 ctrl;
98 
99 	ctrl = readl(lpwm->regs + PWM);
100 	writel(ctrl | PWM_ENABLE, lpwm->regs + PWM);
101 
102 	return 0;
103 }
104 
105 static void pwm_lpss_disable(struct pwm_chip *chip, struct pwm_device *pwm)
106 {
107 	struct pwm_lpss_chip *lpwm = to_lpwm(chip);
108 	u32 ctrl;
109 
110 	ctrl = readl(lpwm->regs + PWM);
111 	writel(ctrl & ~PWM_ENABLE, lpwm->regs + PWM);
112 }
113 
114 static const struct pwm_ops pwm_lpss_ops = {
115 	.config = pwm_lpss_config,
116 	.enable = pwm_lpss_enable,
117 	.disable = pwm_lpss_disable,
118 	.owner = THIS_MODULE,
119 };
120 
121 static struct pwm_lpss_chip *pwm_lpss_probe(struct device *dev,
122 					    struct resource *r,
123 					    const struct pwm_lpss_boardinfo *info)
124 {
125 	struct pwm_lpss_chip *lpwm;
126 	int ret;
127 
128 	lpwm = devm_kzalloc(dev, sizeof(*lpwm), GFP_KERNEL);
129 	if (!lpwm)
130 		return ERR_PTR(-ENOMEM);
131 
132 	lpwm->regs = devm_ioremap_resource(dev, r);
133 	if (IS_ERR(lpwm->regs))
134 		return ERR_CAST(lpwm->regs);
135 
136 	lpwm->clk_rate = info->clk_rate;
137 	lpwm->chip.dev = dev;
138 	lpwm->chip.ops = &pwm_lpss_ops;
139 	lpwm->chip.base = -1;
140 	lpwm->chip.npwm = 1;
141 
142 	ret = pwmchip_add(&lpwm->chip);
143 	if (ret) {
144 		dev_err(dev, "failed to add PWM chip: %d\n", ret);
145 		return ERR_PTR(ret);
146 	}
147 
148 	return lpwm;
149 }
150 
151 static int pwm_lpss_remove(struct pwm_lpss_chip *lpwm)
152 {
153 	u32 ctrl;
154 
155 	ctrl = readl(lpwm->regs + PWM);
156 	writel(ctrl & ~PWM_ENABLE, lpwm->regs + PWM);
157 
158 	return pwmchip_remove(&lpwm->chip);
159 }
160 
161 static int pwm_lpss_probe_pci(struct pci_dev *pdev,
162 			      const struct pci_device_id *id)
163 {
164 	const struct pwm_lpss_boardinfo *info;
165 	struct pwm_lpss_chip *lpwm;
166 	int err;
167 
168 	err = pci_enable_device(pdev);
169 	if (err < 0)
170 		return err;
171 
172 	info = (struct pwm_lpss_boardinfo *)id->driver_data;
173 	lpwm = pwm_lpss_probe(&pdev->dev, &pdev->resource[0], info);
174 	if (IS_ERR(lpwm))
175 		return PTR_ERR(lpwm);
176 
177 	pci_set_drvdata(pdev, lpwm);
178 	return 0;
179 }
180 
181 static void pwm_lpss_remove_pci(struct pci_dev *pdev)
182 {
183 	struct pwm_lpss_chip *lpwm = pci_get_drvdata(pdev);
184 
185 	pwm_lpss_remove(lpwm);
186 	pci_disable_device(pdev);
187 }
188 
189 static struct pci_device_id pwm_lpss_pci_ids[] = {
190 	{ PCI_VDEVICE(INTEL, 0x0f08), (unsigned long)&byt_info},
191 	{ PCI_VDEVICE(INTEL, 0x0f09), (unsigned long)&byt_info},
192 	{ },
193 };
194 MODULE_DEVICE_TABLE(pci, pwm_lpss_pci_ids);
195 
196 static struct pci_driver pwm_lpss_driver_pci = {
197 	.name = "pwm-lpss",
198 	.id_table = pwm_lpss_pci_ids,
199 	.probe = pwm_lpss_probe_pci,
200 	.remove = pwm_lpss_remove_pci,
201 };
202 
203 static int pwm_lpss_probe_platform(struct platform_device *pdev)
204 {
205 	const struct pwm_lpss_boardinfo *info;
206 	const struct acpi_device_id *id;
207 	struct pwm_lpss_chip *lpwm;
208 	struct resource *r;
209 
210 	id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev);
211 	if (!id)
212 		return -ENODEV;
213 
214 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
215 
216 	info = (struct pwm_lpss_boardinfo *)id->driver_data;
217 	lpwm = pwm_lpss_probe(&pdev->dev, r, info);
218 	if (IS_ERR(lpwm))
219 		return PTR_ERR(lpwm);
220 
221 	platform_set_drvdata(pdev, lpwm);
222 	return 0;
223 }
224 
225 static int pwm_lpss_remove_platform(struct platform_device *pdev)
226 {
227 	struct pwm_lpss_chip *lpwm = platform_get_drvdata(pdev);
228 
229 	return pwm_lpss_remove(lpwm);
230 }
231 
232 static const struct acpi_device_id pwm_lpss_acpi_match[] = {
233 	{ "80860F09", (unsigned long)&byt_info },
234 	{ },
235 };
236 MODULE_DEVICE_TABLE(acpi, pwm_lpss_acpi_match);
237 
238 static struct platform_driver pwm_lpss_driver_platform = {
239 	.driver = {
240 		.name = "pwm-lpss",
241 		.acpi_match_table = pwm_lpss_acpi_match,
242 	},
243 	.probe = pwm_lpss_probe_platform,
244 	.remove = pwm_lpss_remove_platform,
245 };
246 
247 static int __init pwm_init(void)
248 {
249 	pci_drv = pci_register_driver(&pwm_lpss_driver_pci);
250 	plat_drv = platform_driver_register(&pwm_lpss_driver_platform);
251 	if (pci_drv && plat_drv)
252 		return pci_drv;
253 
254 	return 0;
255 }
256 module_init(pwm_init);
257 
258 static void __exit pwm_exit(void)
259 {
260 	if (!pci_drv)
261 		pci_unregister_driver(&pwm_lpss_driver_pci);
262 	if (!plat_drv)
263 		platform_driver_unregister(&pwm_lpss_driver_platform);
264 }
265 module_exit(pwm_exit);
266 
267 MODULE_DESCRIPTION("PWM driver for Intel LPSS");
268 MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
269 MODULE_LICENSE("GPL v2");
270 MODULE_ALIAS("platform:pwm-lpss");
271