1 /*
2 * pwm_hi35xx_linux.c
3 *
4 * pwm driver of hi35xx
5 *
6 * Copyright (c) 2020-2021 Huawei Device Co., Ltd.
7 *
8 * This software is licensed under the terms of the GNU General Public
9 * License version 2, as published by the Free Software Foundation, and
10 * may be copied, distributed, and modified under those terms.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 */
18
19 #include "pwm_hi35xx.h"
20 #include <linux/clk.h>
21 #include <linux/module.h>
22 #include <linux/of_platform.h>
23 #include <linux/pwm.h>
24 #include <linux/version.h>
25 #include "hdf_log.h"
26
27 #define HDF_LOG_TAG pwm_hi35xx_linux
28 #define PWM_ENABLE_MASK 0x1
29 #define PWM_HI35XX_N_CELLS 2
30
31 struct Hi35xxPwmChip {
32 struct pwm_chip chip;
33 struct HiPwmRegs *reg;
34 void __iomem *base;
35 struct clk *clk;
36 };
37
38 #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,6,0)
Hi35xxPwmApply(struct pwm_chip * chip,struct pwm_device * pwm,const struct pwm_state * state)39 static int Hi35xxPwmApply(struct pwm_chip *chip, struct pwm_device *pwm, const struct pwm_state *state)
40 #else
41 static int Hi35xxPwmApply(struct pwm_chip *chip, struct pwm_device *pwm, struct pwm_state *state)
42 #endif
43 {
44 struct HiPwmRegs *reg = NULL;
45 struct Hi35xxPwmChip *hi35xxChip = (struct Hi35xxPwmChip *)chip;
46
47 if (hi35xxChip == NULL || pwm == NULL || state == NULL) {
48 HDF_LOGE("%s: parameter is null\n", __func__);
49 return -EINVAL;
50 }
51 reg = (struct HiPwmRegs *)hi35xxChip->base;
52 if (state->polarity != PWM_POLARITY_NORMAL && state->polarity != PWM_POLARITY_INVERSED) {
53 HDF_LOGE("%s: polarity %u is invalid", __func__, state->polarity);
54 return -EINVAL;
55 }
56
57 if (state->period < PWM_MIN_PERIOD) {
58 HDF_LOGE("%s: period %llu is not support, min period %d", __func__, state->period, PWM_MIN_PERIOD);
59 return -EINVAL;
60 }
61 if (state->duty_cycle < 1 || state->duty_cycle > state->period) {
62 HDF_LOGE("%s: duty %llu is not support, duty must in [1, period = %llu].",
63 __func__, state->duty_cycle , state->period);
64 return -EINVAL;
65 }
66
67 HiPwmDisable(reg);
68 HDF_LOGI("%s: [HiPwmDisable] done.", __func__);
69 if (pwm->state.polarity != state->polarity) {
70 HiPwmSetPolarity(reg, state->polarity);
71 HDF_LOGI("%s: [HiPwmSetPolarity] done, polarity: %u -> %u.", __func__, pwm->state.polarity, state->polarity);
72 }
73 if (pwm->state.period != state->period) {
74 HiPwmSetPeriod(reg, state->period);
75 HDF_LOGI("%s: [HiPwmSetPeriod] done, period: %llu -> %llu.", __func__, pwm->state.period, state->period);
76 }
77 if (pwm->state.duty_cycle != state->duty_cycle) {
78 HiPwmSetDuty(reg, state->duty_cycle);
79 HDF_LOGI("%s: [HiPwmSetDuty] done, duty: %llu -> %llu.", __func__, pwm->state.duty_cycle, state->duty_cycle);
80 }
81 if (state->enabled) {
82 HiPwmAlwaysOutput(reg);
83 HDF_LOGI("%s: [HiPwmAlwaysOutput] done, then enable.", __func__);
84 }
85
86 HDF_LOGI("%s: set PwmConfig done: number none, period %llu, duty %llu, polarity %u, enable %u.",
87 __func__, state->period, state->duty_cycle, state->polarity, state->enabled);
88 HDF_LOGI("%s: success.", __func__);
89
90 return 0;
91 }
92
Hi35xxGetState(struct pwm_chip * chip,struct pwm_device * pwm,struct pwm_state * state)93 static void Hi35xxGetState(struct pwm_chip *chip, struct pwm_device *pwm, struct pwm_state *state)
94 {
95 struct HiPwmRegs *reg = NULL;
96 struct Hi35xxPwmChip *hi35xxChip = (struct Hi35xxPwmChip *)chip;
97
98 if (hi35xxChip == NULL || pwm == NULL || state == NULL) {
99 pr_err("%s: parameter is null\n", __func__);
100 return;
101 }
102 reg = (struct HiPwmRegs *)hi35xxChip->base;
103 state->period = reg->cfg0 * PWM_CLK_PERIOD;
104 state->duty_cycle = reg->cfg1 * PWM_CLK_PERIOD;
105 state->polarity = (reg->ctrl & (1 << PWM_INV_OFFSET)) >> PWM_INV_OFFSET;
106 state->enabled = reg->ctrl & PWM_ENABLE_MASK;
107
108 HDF_LOGI("%s: get PwmConfig: number none, period %llu, duty %llu, polarity %u, enable %u.",
109 __func__, state->period, state->duty_cycle, state->polarity, state->enabled);
110 }
111
112 static struct pwm_ops Hi35xxPwmOps = {
113 .apply = Hi35xxPwmApply,
114 .get_state = Hi35xxGetState,
115 .owner = THIS_MODULE,
116 };
117
PwmProbe(struct platform_device * pdev)118 static int PwmProbe(struct platform_device *pdev)
119 {
120 int ret;
121 struct resource *r = NULL;
122 struct Hi35xxPwmChip *chip = NULL;
123 struct device_node *np = pdev->dev.of_node;
124
125 if (!np) {
126 dev_err(&pdev->dev, "invalid devicetree node\n");
127 return -EINVAL;
128 }
129
130 chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
131 if (chip == NULL) {
132 return -ENOMEM;
133 }
134 chip->chip.dev = &pdev->dev;
135 chip->chip.ops = &Hi35xxPwmOps;
136 chip->chip.of_xlate = NULL;
137 chip->chip.of_pwm_n_cells = PWM_HI35XX_N_CELLS;
138 chip->chip.base = -1;
139 chip->chip.npwm = 1;
140 r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
141 chip->base = devm_ioremap_resource(&pdev->dev, r);
142 if (IS_ERR(chip->base)) {
143 return PTR_ERR(chip->base);
144 }
145 chip->reg = (struct HiPwmRegs *)chip->base;
146 chip->clk = devm_clk_get(&pdev->dev, NULL);
147 if (IS_ERR(chip->clk)) {
148 dev_err(&pdev->dev, "failed to get clock\n");
149 return PTR_ERR(chip->clk);
150 }
151 ret = clk_prepare_enable(chip->clk);
152 if (ret < 0) {
153 dev_err(&pdev->dev, "failed to enable clock\n");
154 return ret;
155 }
156 ret = pwmchip_add(&chip->chip);
157 if (ret < 0) {
158 dev_err(&pdev->dev, "failed to add PWM chip\n");
159 return ret;
160 }
161
162 platform_set_drvdata(pdev, chip);
163 return ret;
164 }
165
PwmRemove(struct platform_device * pdev)166 static int PwmRemove(struct platform_device *pdev)
167 {
168 int ret;
169 struct Hi35xxPwmChip *chip = NULL;
170
171 chip = platform_get_drvdata(pdev);
172 if (chip == NULL) {
173 return -ENODEV;
174 }
175 ret = pwmchip_remove(&chip->chip);
176 if (ret < 0) {
177 return ret;
178 }
179 clk_disable_unprepare(chip->clk);
180 return 0;
181 }
182
183 static const struct of_device_id g_pwmMatch[] = {
184 { .compatible = "hisilicon,pwm" },
185 {},
186 };
187
188 static struct platform_driver g_pwmDriver = {
189 .probe = PwmProbe,
190 .remove = PwmRemove,
191 .driver = {
192 .name = "pwm",
193 .of_match_table = g_pwmMatch,
194 }
195 };
196 module_platform_driver(g_pwmDriver);
197 MODULE_LICENSE("GPL");
198