Blame view
drivers/pwm/pwm-lpss.c
4.96 KB
d16a5aa9e pwm: add support ... |
1 2 3 4 5 6 7 8 |
/* * Intel Low Power Subsystem PWM controller driver * * Copyright (C) 2014, Intel Corporation * Author: Mika Westerberg <mika.westerberg@linux.intel.com> * Author: Chew Kean Ho <kean.ho.chew@intel.com> * Author: Chang Rebecca Swee Fun <rebecca.swee.fun.chang@intel.com> * Author: Chew Chiau Ee <chiau.ee.chew@intel.com> |
093e00bb3 pwm: lpss: Add su... |
9 |
* Author: Alan Cox <alan@linux.intel.com> |
d16a5aa9e pwm: add support ... |
10 11 12 13 14 |
* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ |
37670676a pwm: lpss: Rework... |
15 |
#include <linux/delay.h> |
e0c86a3b6 pwm: lpss: Fix bu... |
16 |
#include <linux/io.h> |
d16a5aa9e pwm: add support ... |
17 18 |
#include <linux/kernel.h> #include <linux/module.h> |
f080be27d pwm: lpss: Add su... |
19 |
#include <linux/pm_runtime.h> |
883e4d070 pwm: lpss: Update... |
20 |
#include <linux/time.h> |
093e00bb3 pwm: lpss: Add su... |
21 |
|
c558e39e1 pwm: lpss: Proper... |
22 |
#include "pwm-lpss.h" |
d16a5aa9e pwm: add support ... |
23 24 25 26 27 |
#define PWM 0x00000000 #define PWM_ENABLE BIT(31) #define PWM_SW_UPDATE BIT(30) #define PWM_BASE_UNIT_SHIFT 8 |
d16a5aa9e pwm: add support ... |
28 |
#define PWM_ON_TIME_DIV_MASK 0x000000ff |
d16a5aa9e pwm: add support ... |
29 |
|
4e11f5acb pwm: lpss: Add su... |
30 31 |
/* Size of each PWM register space if multiple */ #define PWM_SIZE 0x400 |
d16a5aa9e pwm: add support ... |
32 33 34 |
struct pwm_lpss_chip { struct pwm_chip chip; void __iomem *regs; |
883e4d070 pwm: lpss: Update... |
35 |
const struct pwm_lpss_boardinfo *info; |
093e00bb3 pwm: lpss: Add su... |
36 |
}; |
093e00bb3 pwm: lpss: Add su... |
37 |
/* BayTrail */ |
c558e39e1 pwm: lpss: Proper... |
38 |
const struct pwm_lpss_boardinfo pwm_lpss_byt_info = { |
4e11f5acb pwm: lpss: Add su... |
39 40 |
.clk_rate = 25000000, .npwm = 1, |
883e4d070 pwm: lpss: Update... |
41 |
.base_unit_bits = 16, |
d16a5aa9e pwm: add support ... |
42 |
}; |
c558e39e1 pwm: lpss: Proper... |
43 |
EXPORT_SYMBOL_GPL(pwm_lpss_byt_info); |
d16a5aa9e pwm: add support ... |
44 |
|
373c57829 pwm: lpss: Add AC... |
45 |
/* Braswell */ |
c558e39e1 pwm: lpss: Proper... |
46 |
const struct pwm_lpss_boardinfo pwm_lpss_bsw_info = { |
4e11f5acb pwm: lpss: Add su... |
47 48 |
.clk_rate = 19200000, .npwm = 1, |
883e4d070 pwm: lpss: Update... |
49 |
.base_unit_bits = 16, |
373c57829 pwm: lpss: Add AC... |
50 |
}; |
c558e39e1 pwm: lpss: Proper... |
51 |
EXPORT_SYMBOL_GPL(pwm_lpss_bsw_info); |
373c57829 pwm: lpss: Add AC... |
52 |
|
87219cb47 pwm: lpss: Suppor... |
53 54 55 56 |
/* Broxton */ const struct pwm_lpss_boardinfo pwm_lpss_bxt_info = { .clk_rate = 19200000, .npwm = 4, |
883e4d070 pwm: lpss: Update... |
57 |
.base_unit_bits = 22, |
87219cb47 pwm: lpss: Suppor... |
58 59 |
}; EXPORT_SYMBOL_GPL(pwm_lpss_bxt_info); |
d16a5aa9e pwm: add support ... |
60 61 62 63 |
static inline struct pwm_lpss_chip *to_lpwm(struct pwm_chip *chip) { return container_of(chip, struct pwm_lpss_chip, chip); } |
4e11f5acb pwm: lpss: Add su... |
64 65 66 67 68 69 70 71 72 73 74 75 76 |
static inline u32 pwm_lpss_read(const struct pwm_device *pwm) { struct pwm_lpss_chip *lpwm = to_lpwm(pwm->chip); return readl(lpwm->regs + pwm->hwpwm * PWM_SIZE + PWM); } static inline void pwm_lpss_write(const struct pwm_device *pwm, u32 value) { struct pwm_lpss_chip *lpwm = to_lpwm(pwm->chip); writel(value, lpwm->regs + pwm->hwpwm * PWM_SIZE + PWM); } |
37670676a pwm: lpss: Rework... |
77 78 79 80 81 82 |
static void pwm_lpss_update(struct pwm_device *pwm) { pwm_lpss_write(pwm, pwm_lpss_read(pwm) | PWM_SW_UPDATE); /* Give it some time to propagate */ usleep_range(10, 50); } |
d16a5aa9e pwm: add support ... |
83 84 85 86 |
static int pwm_lpss_config(struct pwm_chip *chip, struct pwm_device *pwm, int duty_ns, int period_ns) { struct pwm_lpss_chip *lpwm = to_lpwm(chip); |
ab248b603 pwm: lpss: Preven... |
87 |
unsigned long long on_time_div; |
d9cd4a736 pwm: lpss: Move c... |
88 |
unsigned long c = lpwm->info->clk_rate, base_unit_range; |
883e4d070 pwm: lpss: Update... |
89 |
unsigned long long base_unit, freq = NSEC_PER_SEC; |
d16a5aa9e pwm: add support ... |
90 91 92 |
u32 ctrl; do_div(freq, period_ns); |
883e4d070 pwm: lpss: Update... |
93 94 |
/* * The equation is: |
e5ca42458 pwm: lpss: Fix ba... |
95 |
* base_unit = round(base_unit_range * freq / c) |
883e4d070 pwm: lpss: Update... |
96 97 |
*/ base_unit_range = BIT(lpwm->info->base_unit_bits); |
e5ca42458 pwm: lpss: Fix ba... |
98 |
freq *= base_unit_range; |
d16a5aa9e pwm: add support ... |
99 |
|
e5ca42458 pwm: lpss: Fix ba... |
100 |
base_unit = DIV_ROUND_CLOSEST_ULL(freq, c); |
d16a5aa9e pwm: add support ... |
101 102 103 |
if (duty_ns <= 0) duty_ns = 1; |
ab248b603 pwm: lpss: Preven... |
104 105 106 |
on_time_div = 255ULL * duty_ns; do_div(on_time_div, period_ns); on_time_div = 255ULL - on_time_div; |
d16a5aa9e pwm: add support ... |
107 |
|
f080be27d pwm: lpss: Add su... |
108 |
pm_runtime_get_sync(chip->dev); |
4e11f5acb pwm: lpss: Add su... |
109 |
ctrl = pwm_lpss_read(pwm); |
883e4d070 pwm: lpss: Update... |
110 111 112 113 |
ctrl &= ~PWM_ON_TIME_DIV_MASK; ctrl &= ~((base_unit_range - 1) << PWM_BASE_UNIT_SHIFT); base_unit &= (base_unit_range - 1); ctrl |= (u32) base_unit << PWM_BASE_UNIT_SHIFT; |
d16a5aa9e pwm: add support ... |
114 |
ctrl |= on_time_div; |
4e11f5acb pwm: lpss: Add su... |
115 |
pwm_lpss_write(pwm, ctrl); |
d16a5aa9e pwm: add support ... |
116 |
|
37670676a pwm: lpss: Rework... |
117 118 119 120 121 122 |
/* * If the PWM is already enabled we need to notify the hardware * about the change by setting PWM_SW_UPDATE. */ if (pwm_is_enabled(pwm)) pwm_lpss_update(pwm); |
f080be27d pwm: lpss: Add su... |
123 |
pm_runtime_put(chip->dev); |
d16a5aa9e pwm: add support ... |
124 125 126 127 128 |
return 0; } static int pwm_lpss_enable(struct pwm_chip *chip, struct pwm_device *pwm) { |
f080be27d pwm: lpss: Add su... |
129 |
pm_runtime_get_sync(chip->dev); |
37670676a pwm: lpss: Rework... |
130 131 132 133 134 135 |
/* * Hardware must first see PWM_SW_UPDATE before the PWM can be * enabled. */ pwm_lpss_update(pwm); |
4e11f5acb pwm: lpss: Add su... |
136 |
pwm_lpss_write(pwm, pwm_lpss_read(pwm) | PWM_ENABLE); |
d16a5aa9e pwm: add support ... |
137 138 139 140 141 |
return 0; } static void pwm_lpss_disable(struct pwm_chip *chip, struct pwm_device *pwm) { |
4e11f5acb pwm: lpss: Add su... |
142 |
pwm_lpss_write(pwm, pwm_lpss_read(pwm) & ~PWM_ENABLE); |
f080be27d pwm: lpss: Add su... |
143 |
pm_runtime_put(chip->dev); |
d16a5aa9e pwm: add support ... |
144 145 146 147 148 149 150 151 |
} static const struct pwm_ops pwm_lpss_ops = { .config = pwm_lpss_config, .enable = pwm_lpss_enable, .disable = pwm_lpss_disable, .owner = THIS_MODULE, }; |
c558e39e1 pwm: lpss: Proper... |
152 153 |
struct pwm_lpss_chip *pwm_lpss_probe(struct device *dev, struct resource *r, const struct pwm_lpss_boardinfo *info) |
d16a5aa9e pwm: add support ... |
154 155 |
{ struct pwm_lpss_chip *lpwm; |
d9cd4a736 pwm: lpss: Move c... |
156 |
unsigned long c; |
d16a5aa9e pwm: add support ... |
157 |
int ret; |
093e00bb3 pwm: lpss: Add su... |
158 |
lpwm = devm_kzalloc(dev, sizeof(*lpwm), GFP_KERNEL); |
d16a5aa9e pwm: add support ... |
159 |
if (!lpwm) |
093e00bb3 pwm: lpss: Add su... |
160 |
return ERR_PTR(-ENOMEM); |
d16a5aa9e pwm: add support ... |
161 |
|
093e00bb3 pwm: lpss: Add su... |
162 |
lpwm->regs = devm_ioremap_resource(dev, r); |
d16a5aa9e pwm: add support ... |
163 |
if (IS_ERR(lpwm->regs)) |
89c0339e0 pwm: lpss: Fix co... |
164 |
return ERR_CAST(lpwm->regs); |
093e00bb3 pwm: lpss: Add su... |
165 |
|
883e4d070 pwm: lpss: Update... |
166 |
lpwm->info = info; |
d9cd4a736 pwm: lpss: Move c... |
167 168 169 170 |
c = lpwm->info->clk_rate; if (!c) return ERR_PTR(-EINVAL); |
093e00bb3 pwm: lpss: Add su... |
171 |
lpwm->chip.dev = dev; |
d16a5aa9e pwm: add support ... |
172 173 |
lpwm->chip.ops = &pwm_lpss_ops; lpwm->chip.base = -1; |
4e11f5acb pwm: lpss: Add su... |
174 |
lpwm->chip.npwm = info->npwm; |
d16a5aa9e pwm: add support ... |
175 176 177 |
ret = pwmchip_add(&lpwm->chip); if (ret) { |
093e00bb3 pwm: lpss: Add su... |
178 179 180 |
dev_err(dev, "failed to add PWM chip: %d ", ret); return ERR_PTR(ret); |
d16a5aa9e pwm: add support ... |
181 |
} |
093e00bb3 pwm: lpss: Add su... |
182 |
return lpwm; |
d16a5aa9e pwm: add support ... |
183 |
} |
c558e39e1 pwm: lpss: Proper... |
184 |
EXPORT_SYMBOL_GPL(pwm_lpss_probe); |
d16a5aa9e pwm: add support ... |
185 |
|
c558e39e1 pwm: lpss: Proper... |
186 |
int pwm_lpss_remove(struct pwm_lpss_chip *lpwm) |
d16a5aa9e pwm: add support ... |
187 |
{ |
d16a5aa9e pwm: add support ... |
188 189 |
return pwmchip_remove(&lpwm->chip); } |
c558e39e1 pwm: lpss: Proper... |
190 |
EXPORT_SYMBOL_GPL(pwm_lpss_remove); |
d16a5aa9e pwm: add support ... |
191 192 193 194 |
MODULE_DESCRIPTION("PWM driver for Intel LPSS"); MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>"); MODULE_LICENSE("GPL v2"); |