forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathpwm-msc313e.c
201 lines (166 loc) · 5.17 KB
/
pwm-msc313e.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
/*
*
* There are channels at
* 0x1f003400
* 0x1f003480
* 0x1f003500
* 0x1f003580
* 0x1f003600
* 0x1f003680
* ..
*
* reset value writable bits
0x0 - 0 0xFFFF
0x4 - 0 0x3
0x8 - 0 0xFFFF -- duty
0xc - 0 0x3
0x10 - 0 0xFFFF -- period
0x14 - 0 0x3
0x18 - 0 0xFFFF -- clk div
0x1c - 0 0x1F
4 |
polarity |
*/
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/clk.h>
#include <linux/pwm.h>
#define DRIVER_NAME "msc313e-pwm"
struct msc313e_pwm {
struct clk *clk;
struct regmap *regmap;
struct pwm_chip pwmchip;
struct regmap_field* clkdiv;
struct regmap_field* polarity;
};
#define REG_UNKNOWN 0x0
#define REG_DUTY 0x8
#define REG_PERIOD 0x10
#define REG_DIV 0x18
static struct reg_field div_clkdiv_field = REG_FIELD(REG_DIV, 0, 7);
#define REG_CTRL 0x1c
static struct reg_field ctrl_polarity_field = REG_FIELD(REG_CTRL, 4, 4);
#define to_msc313e_pwm(ptr) container_of(ptr, struct msc313e_pwm, pwmchip)
static const struct regmap_config msc313e_pwm_regmap_config = {
.name = "msc313e-pwm",
.reg_bits = 16,
.val_bits = 16,
.reg_stride = 4,
};
static const struct of_device_id msc313e_pwm_dt_ids[] = {
{ .compatible = "mstar,msc313e-pwm" },
{},
};
MODULE_DEVICE_TABLE(of, msc313e_pwm_dt_ids);
static void msc313e_pwm_writecounter(struct msc313e_pwm *pwm, u8 reg, u32 value){
regmap_update_bits(pwm->regmap, reg, 0xffff, value);
regmap_update_bits(pwm->regmap, reg + 4, 0x3, value >> 16);
}
static int msc313e_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
int duty_ns, int period_ns)
{
struct msc313e_pwm *mpwm = to_msc313e_pwm(chip);
unsigned long long nspertick = DIV_ROUND_CLOSEST_ULL(NSEC_PER_SEC, clk_get_rate(mpwm->clk));
unsigned long long div = 1;
// fit the period into the period register by prescaling the clk
while(DIV_ROUND_CLOSEST_ULL(period_ns, (nspertick = DIV_ROUND_CLOSEST_ULL(nspertick, div))) > 0x3ffff){
div++;
if(div > (0xffff + 1)){
printk("can't fit period into period register\n");
return -EINVAL;
}
}
regmap_field_write(mpwm->clkdiv, div - 1);
msc313e_pwm_writecounter(mpwm, REG_UNKNOWN, 0);
msc313e_pwm_writecounter(mpwm, REG_DUTY, DIV_ROUND_CLOSEST_ULL(duty_ns, nspertick));
msc313e_pwm_writecounter(mpwm, REG_PERIOD, DIV_ROUND_CLOSEST_ULL(period_ns, nspertick));
return 0;
};
static int msc313e_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *device,
enum pwm_polarity polarity)
{
struct msc313e_pwm *pwm = to_msc313e_pwm(chip);
unsigned pol = 0;
if(polarity == PWM_POLARITY_INVERSED)
pol = 1;
regmap_field_update_bits(pwm->polarity, 1, pol);
return 0;
}
static int msc313e_pwm_enable(struct pwm_chip *chip, struct pwm_device *device)
{
struct msc313e_pwm *pwm = to_msc313e_pwm(chip);
return clk_prepare_enable(pwm->clk);
}
static void msc313e_pwm_disable(struct pwm_chip *chip, struct pwm_device *device){
struct msc313e_pwm *pwm = to_msc313e_pwm(chip);
clk_disable(pwm->clk);
}
static int msc313e_apply(struct pwm_chip *chip, struct pwm_device *pwm,
struct pwm_state *state)
{
msc313e_pwm_set_polarity(chip, pwm, state->polarity);
msc313e_pwm_config(chip, pwm, state->duty_cycle, state->period);
return 0;
}
static void msc313e_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
struct pwm_state *state){
printk("pwm get state\n");
}
static const struct pwm_ops msc313e_pwm_ops = {
.config = msc313e_pwm_config,
.set_polarity = msc313e_pwm_set_polarity,
.enable = msc313e_pwm_enable,
.disable = msc313e_pwm_disable,
.apply = msc313e_apply,
.get_state = msc313e_get_state,
.owner = THIS_MODULE
};
static int msc313e_pwm_probe(struct platform_device *pdev)
{
int ret;
struct msc313e_pwm *pwm;
struct resource *res;
__iomem void* base;
pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL);
if (!pwm)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
pwm->regmap = devm_regmap_init_mmio(&pdev->dev, base,
&msc313e_pwm_regmap_config);
if(IS_ERR(pwm->regmap))
return PTR_ERR(pwm->regmap);
pwm->clkdiv = devm_regmap_field_alloc(&pdev->dev, pwm->regmap, div_clkdiv_field);
pwm->polarity = devm_regmap_field_alloc(&pdev->dev, pwm->regmap, ctrl_polarity_field);
pwm->clk = of_clk_get(pdev->dev.of_node, 0);
pwm->pwmchip.dev = &pdev->dev;
pwm->pwmchip.ops = &msc313e_pwm_ops;
pwm->pwmchip.base = -1;
pwm->pwmchip.npwm = 1;
pwm->pwmchip.of_xlate = of_pwm_xlate_with_flags;
pwm->pwmchip.of_pwm_n_cells = 3;
ret = pwmchip_add(&pwm->pwmchip);
if(ret)
dev_err(&pdev->dev, "failed to register pwm chip");
return ret;
}
static int msc313e_pwm_remove(struct platform_device *pdev)
{
return 0;
}
static struct platform_driver msc313e_pwm_driver = {
.probe = msc313e_pwm_probe,
.remove = msc313e_pwm_remove,
.driver = {
.name = DRIVER_NAME,
.of_match_table = msc313e_pwm_dt_ids,
},
};
module_platform_driver(msc313e_pwm_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Mstar MSC313e PWM driver");
MODULE_AUTHOR("Daniel Palmer <daniel@0x0f.com>");