1 /*
2 * Copyright (c) 2024 ITE Corporation. All Rights Reserved.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT ite_it8801_pwm
8
9 #include <zephyr/device.h>
10 #include <zephyr/drivers/i2c.h>
11 #include <zephyr/drivers/mfd/mfd_ite_it8801.h>
12 #include <zephyr/drivers/pwm.h>
13
14 #include <zephyr/logging/log.h>
15 LOG_MODULE_REGISTER(pwm_ite_it8801, CONFIG_PWM_LOG_LEVEL);
16
17 struct it8801_pwm_map {
18 uint8_t pushpull_en;
19 };
20 const static struct it8801_pwm_map it8801_pwm_gpio_map[] = {
21 [1] = {.pushpull_en = BIT(0)}, [2] = {.pushpull_en = BIT(1)}, [3] = {.pushpull_en = BIT(2)},
22 [4] = {.pushpull_en = BIT(3)}, [7] = {.pushpull_en = BIT(4)}, [8] = {.pushpull_en = BIT(5)},
23 [9] = {.pushpull_en = BIT(6)},
24 };
25
26 struct it8801_mfd_pwm_altctrl_cfg {
27 /* GPIO control device structure */
28 const struct device *gpiocr;
29 /* GPIO control pin */
30 uint8_t pin;
31 /* GPIO function select */
32 uint8_t alt_func;
33 };
34
35 struct pwm_it8801_config {
36 /* IT8801 controller dev */
37 const struct device *mfd;
38 /* I2C device for the MFD parent */
39 const struct i2c_dt_spec i2c_dev;
40 /* PWM alternate configuration */
41 const struct it8801_mfd_pwm_altctrl_cfg *altctrl;
42 int mfdctrl_len;
43 int channel;
44 /* PWM mode control register */
45 uint8_t reg_mcr;
46 /* PWM duty cycle register */
47 uint8_t reg_dcr;
48 /* PWM prescale LSB register */
49 uint8_t reg_prslr;
50 /* PWM prescale MSB register */
51 uint8_t reg_prsmr;
52 };
53
pwm_enable(const struct device * dev,int enabled)54 static void pwm_enable(const struct device *dev, int enabled)
55 {
56 const struct pwm_it8801_config *config = dev->config;
57 int ret;
58
59 if (enabled) {
60 ret = i2c_reg_update_byte_dt(&config->i2c_dev, config->reg_mcr,
61 IT8801_PWMMCR_MCR_MASK, IT8801_PWMMCR_MCR_BLINKING);
62 } else {
63 ret = i2c_reg_update_byte_dt(&config->i2c_dev, config->reg_mcr,
64 IT8801_PWMMCR_MCR_MASK, 0);
65 }
66 if (ret != 0) {
67 LOG_ERR("Failed to enable pwm (ret %d)", ret);
68 return;
69 }
70 }
71
pwm_it8801_get_cycles_per_sec(const struct device * dev,uint32_t channel,uint64_t * cycles)72 static int pwm_it8801_get_cycles_per_sec(const struct device *dev, uint32_t channel,
73 uint64_t *cycles)
74 {
75 ARG_UNUSED(dev);
76 ARG_UNUSED(channel);
77
78 *cycles = (uint64_t)PWM_IT8801_FREQ;
79
80 return 0;
81 }
82
pwm_it8801_set_cycles(const struct device * dev,uint32_t channel,uint32_t period_cycles,uint32_t pulse_cycles,pwm_flags_t flags)83 static int pwm_it8801_set_cycles(const struct device *dev, uint32_t channel, uint32_t period_cycles,
84 uint32_t pulse_cycles, pwm_flags_t flags)
85 {
86 ARG_UNUSED(channel);
87
88 const struct pwm_it8801_config *config = dev->config;
89 int ch = config->channel, ret;
90 uint8_t duty, mask;
91
92 /* Enable PWM output push-pull */
93 if (flags & PWM_IT8801_PUSH_PULL) {
94 mask = it8801_pwm_gpio_map[ch].pushpull_en;
95
96 ret = i2c_reg_update_byte_dt(&config->i2c_dev, IT8801_REG_PWMODDSR, mask, mask);
97 if (ret != 0) {
98 LOG_ERR("Failed to set push-pull (ret %d)", ret);
99 return ret;
100 }
101 }
102
103 /* Set PWM channel duty cycle */
104 if (pulse_cycles == 0) {
105 duty = 0;
106 } else {
107 duty = pulse_cycles * 256 / period_cycles - 1;
108 }
109 LOG_DBG("IT8801 pwm duty cycles = %d", duty);
110
111 ret = i2c_reg_write_byte_dt(&config->i2c_dev, config->reg_dcr, duty);
112 if (ret != 0) {
113 LOG_ERR("Failed to set cycles (ret %d)", ret);
114 return ret;
115 }
116
117 /* PWM channel clock source not gating */
118 pwm_enable(dev, 1);
119
120 return 0;
121 }
122
pwm_it8801_init(const struct device * dev)123 static int pwm_it8801_init(const struct device *dev)
124 {
125 const struct pwm_it8801_config *config = dev->config;
126 int ret;
127
128 /* Verify multi-function parent is ready */
129 if (!device_is_ready(config->mfd)) {
130 LOG_ERR("(pwm)%s is not ready", config->mfd->name);
131 return -ENODEV;
132 }
133
134 /* PWM channel clock source gating before configuring */
135 pwm_enable(dev, 0);
136
137 for (int i = 0; i < config->mfdctrl_len; i++) {
138 /* Switching the pin to PWM alternate function */
139 ret = mfd_it8801_configure_pins(&config->i2c_dev, config->altctrl[i].gpiocr,
140 config->altctrl[i].pin,
141 config->altctrl[i].alt_func);
142 if (ret != 0) {
143 LOG_ERR("Failed to configure pins (ret %d)", ret);
144 return ret;
145 }
146 }
147
148 return 0;
149 }
150
151 static DEVICE_API(pwm, pwm_it8801_api) = {
152 .set_cycles = pwm_it8801_set_cycles,
153 .get_cycles_per_sec = pwm_it8801_get_cycles_per_sec,
154 };
155
156 #define PWM_IT8801_INIT(inst) \
157 static const struct it8801_mfd_pwm_altctrl_cfg \
158 it8801_pwm_altctrl##inst[IT8801_DT_INST_MFDCTRL_LEN(inst)] = \
159 IT8801_DT_MFD_ITEMS_LIST(inst); \
160 static const struct pwm_it8801_config pwm_it8801_cfg_##inst = { \
161 .mfd = DEVICE_DT_GET(DT_INST_PARENT(inst)), \
162 .i2c_dev = I2C_DT_SPEC_GET(DT_INST_PARENT(inst)), \
163 .altctrl = it8801_pwm_altctrl##inst, \
164 .mfdctrl_len = IT8801_DT_INST_MFDCTRL_LEN(inst), \
165 .channel = DT_INST_PROP(inst, channel), \
166 .reg_mcr = DT_INST_REG_ADDR_BY_IDX(inst, 0), \
167 .reg_dcr = DT_INST_REG_ADDR_BY_IDX(inst, 1), \
168 .reg_prslr = DT_INST_REG_ADDR_BY_IDX(inst, 2), \
169 .reg_prsmr = DT_INST_REG_ADDR_BY_IDX(inst, 3), \
170 }; \
171 DEVICE_DT_INST_DEFINE(inst, &pwm_it8801_init, NULL, NULL, &pwm_it8801_cfg_##inst, \
172 POST_KERNEL, CONFIG_MFD_INIT_PRIORITY, &pwm_it8801_api);
173
174 DT_INST_FOREACH_STATUS_OKAY(PWM_IT8801_INIT)
175