/* * Copyright (c) 2017 Vitor Massaru Iha * * SPDX-License-Identifier: Apache-2.0 */ /* Include esp-idf headers first to avoid redefining BIT() macro */ #include #include #include #include #include #include #include #include #include #include #include #define PWM_ESP32_HSCH_HPOINT(i) (LEDC_HSCH0_HPOINT_REG + (0x14 * i)) #define PWM_ESP32_HSCH_DUTY(i) (LEDC_HSCH0_DUTY_REG + (0x14 * i)) #define PWM_ESP32_HSCH_CONF0(i) (LEDC_HSCH0_CONF0_REG + (0x14 * i)) #define PWM_ESP32_HSCH_CONF1(i) (LEDC_HSCH0_CONF1_REG + (0x14 * i)) #define PWM_ESP32_HSTIMER(i) (LEDC_HSTIMER0_CONF_REG + (0x8 * i)) #define PWM_ESP32_LSCH_HPOINT(i) (LEDC_LSCH0_HPOINT_REG + (0x14 * i)) #define PWM_ESP32_LSCH_DUTY(i) (LEDC_LSCH0_DUTY_REG + (0x14 * i)) #define PWM_ESP32_LSCH_CONF0(i) (LEDC_LSCH0_CONF0_REG + (0x14 * i)) #define PWM_ESP32_LSCH_CONF1(i) (LEDC_LSCH0_CONF1_REG + (0x14 * i)) #define PWM_ESP32_LSTIMER(i) (LEDC_LSTIMER0_CONF_REG + (0x8 * i)) enum { PWM_LED_ESP32_REF_TICK_FREQ, PWM_LED_ESP32_APB_CLK_FREQ, }; enum { PWM_LED_ESP32_HIGH_SPEED, PWM_LED_ESP32_LOW_SPEED }; struct pwm_led_esp32_timer { int freq; u8_t bit_num; } __attribute__ ((__packed__)); struct pwm_led_esp32_channel { u8_t timer : 2; u8_t gpio : 6; }; union pwm_led_esp32_duty { struct { u32_t start: 1; u32_t direction: 1; u32_t num: 3; u32_t cycle: 3; u32_t scale: 3; }; u32_t val; }; struct pwm_led_esp32_config { /* Speed mode * 0: High speed mode * 1: Low speed mode * * Timers * 0 - 3: 4 timers */ struct pwm_led_esp32_channel ch_cfg[16]; struct pwm_led_esp32_timer timer_cfg[2][4]; }; /* TODO: Remove these functions after this PR: * https://github.com/zephyrproject-rtos/zephyr/pull/5113 */ static inline void set_mask32(u32_t v, u32_t mem_addr) { sys_write32(sys_read32(mem_addr) | v, mem_addr); } static inline void clear_mask32(u32_t v, u32_t mem_addr) { sys_write32(sys_read32(mem_addr) & ~v, mem_addr); } static const char *esp32_get_gpio_for_pin(int pin) { if (pin < 32) { #if defined(CONFIG_GPIO_ESP32_0) return DT_INST_0_ESPRESSIF_ESP32_GPIO_LABEL; #else return NULL; #endif /* CONFIG_GPIO_ESP32_0 */ } #if defined(CONFIG_GPIO_ESP32_1) return DT_INST_1_ESPRESSIF_ESP32_GPIO_LABEL; #else return NULL; #endif /* CONFIG_GPIO_ESP32_1 */ } /* end Remove after PR 5113 */ static u8_t pwm_led_esp32_get_gpio_config(u8_t pin, const struct pwm_led_esp32_channel *ch_cfg) { u8_t i; for (i = 0U; i < 16; i++) { if (ch_cfg[i].gpio == pin) { return i; } } return -EINVAL; } static void pwm_led_esp32_low_speed_update(int speed_mode, int channel) { u32_t reg_addr; if (speed_mode == PWM_LED_ESP32_LOW_SPEED) { reg_addr = PWM_ESP32_LSCH_CONF0(channel); sys_set_bit(reg_addr, LEDC_PARA_UP_LSCH0_S); } } static void pwm_led_esp32_update_duty(int speed_mode, int channel) { u32_t conf0_addr; u32_t conf1_addr; if (speed_mode == PWM_LED_ESP32_HIGH_SPEED) { conf0_addr = PWM_ESP32_HSCH_CONF0(channel); conf1_addr = PWM_ESP32_HSCH_CONF1(channel); } else { conf0_addr = PWM_ESP32_LSCH_CONF0(channel); conf1_addr = PWM_ESP32_LSCH_CONF1(channel); } sys_set_bit(conf0_addr, LEDC_SIG_OUT_EN_LSCH0_S); sys_set_bit(conf1_addr, LEDC_DUTY_START_LSCH0_S); pwm_led_esp32_low_speed_update(speed_mode, channel); } static void pwm_led_esp32_duty_config(int speed_mode, int channel, int duty_val, union pwm_led_esp32_duty duty) { volatile u32_t hpoint_addr; volatile u32_t duty_addr; volatile u32_t conf1_addr; volatile u32_t conf1_val; if (speed_mode == PWM_LED_ESP32_HIGH_SPEED) { hpoint_addr = PWM_ESP32_HSCH_HPOINT(channel); duty_addr = PWM_ESP32_HSCH_DUTY(channel); conf1_addr = PWM_ESP32_HSCH_CONF1(channel); } else { hpoint_addr = PWM_ESP32_LSCH_HPOINT(channel); duty_addr = PWM_ESP32_LSCH_DUTY(channel); conf1_addr = PWM_ESP32_LSCH_CONF1(channel); } sys_write32(0, hpoint_addr); sys_write32(duty_val, duty_addr); sys_write32(duty.val, conf1_addr); pwm_led_esp32_low_speed_update(speed_mode, channel); } static void pwm_led_esp32_duty_set(int speed_mode, int channel, int duty_val) { union pwm_led_esp32_duty duty; duty.start = 0U; duty.direction = 1U; duty.cycle = 1U; duty.scale = 0U; pwm_led_esp32_duty_config(speed_mode, channel, duty_val << 4, duty); } static void pwm_led_esp32_bind_channel_timer(int speed_mode, int channel, int timer) { volatile u32_t timer_addr; if (speed_mode == PWM_LED_ESP32_HIGH_SPEED) { timer_addr = PWM_ESP32_HSCH_CONF0(channel); } else { timer_addr = PWM_ESP32_LSCH_CONF0(channel); } set_mask32(timer, timer_addr); pwm_led_esp32_low_speed_update(speed_mode, channel); } static int pwm_led_esp32_channel_set(int pin, bool speed_mode, int channel, int duty, int timer) { const int pin_mode = GPIO_DIR_OUT; const char *device_name; struct device *gpio; int ret; u32_t sig_out_idx; /* Set duty cycle */ pwm_led_esp32_duty_set(speed_mode, channel, duty); pwm_led_esp32_update_duty(speed_mode, channel); pwm_led_esp32_bind_channel_timer(speed_mode, channel, timer); /* Set pin */ device_name = esp32_get_gpio_for_pin(pin); if (!device_name) { return -EINVAL; } gpio = device_get_binding(device_name); if (!gpio) { return -EINVAL; } ret = gpio_pin_configure(gpio, pin, pin_mode); if (ret < 0) { return ret; } if (speed_mode == PWM_LED_ESP32_HIGH_SPEED) { sig_out_idx = LEDC_HS_SIG_OUT0_IDX + channel; } else { sig_out_idx = LEDC_LS_SIG_OUT0_IDX + channel; } esp32_rom_gpio_matrix_out(pin, sig_out_idx, 0, 0); return 0; } static int pwm_led_esp32_timer_set(int speed_mode, int timer, int bit_num, int frequency) { u32_t timer_addr; u64_t div_num; int tick_sel = PWM_LED_ESP32_APB_CLK_FREQ; u32_t precision = (0x1 << bit_num); assert(frequency > 0); /* This expression comes from ESP32 Espressif's Technical Reference * Manual chapter 13.2.2 Timers. * div_num is a fixed point value (Q10.8). */ div_num = ((u64_t) APB_CLK_FREQ << 8) / frequency / precision; if (div_num < 0x100) { /* Since Q10.8 is a fixed point value, then div_num < 0x100 * means divisor is too low. */ return -EINVAL; } if (div_num > 0x3FFFF) { /* Since Q10.8 is a fixed point value, then div_num > 0x3FFFF * means divisor is too high. We can try to use the REF_TICK. */ div_num = ((u64_t) 1000000 << 8) / frequency / precision; if (div_num < 0x100 || div_num > 0x3FFFF) { return -EINVAL; } tick_sel = PWM_LED_ESP32_REF_TICK_FREQ; } else { if (speed_mode) { sys_set_bit(LEDC_CONF_REG, LEDC_APB_CLK_SEL_S); } } if (speed_mode == PWM_LED_ESP32_HIGH_SPEED) { timer_addr = PWM_ESP32_HSTIMER(timer); } else { timer_addr = PWM_ESP32_LSTIMER(timer); } set_mask32(div_num << LEDC_DIV_NUM_LSTIMER0_S, timer_addr); set_mask32(tick_sel << LEDC_TICK_SEL_LSTIMER0_S, timer_addr); set_mask32(bit_num & LEDC_LSTIMER0_LIM_M, timer_addr); if (speed_mode) { /* update div_num and bit_num */ sys_set_bit(timer_addr, LEDC_LSTIMER0_PARA_UP_S); } /* reset low speed timer */ sys_set_bit(timer_addr, LEDC_LSTIMER0_RST_S); sys_clear_bit(timer_addr, LEDC_LSTIMER0_RST_S); return 0; } /* period_cycles is not used, set frequency on menuconfig instead. */ static int pwm_led_esp32_pin_set_cycles(struct device *dev, u32_t pwm, u32_t period_cycles, u32_t pulse_cycles) { int speed_mode; int channel; int timer; int ret; const struct pwm_led_esp32_config * const config = (struct pwm_led_esp32_config *) dev->config->config_info; ARG_UNUSED(period_cycles); channel = pwm_led_esp32_get_gpio_config(pwm, config->ch_cfg); if (channel < 0) { return -EINVAL; } speed_mode = channel < 8 ? PWM_LED_ESP32_HIGH_SPEED : PWM_LED_ESP32_LOW_SPEED; timer = config->ch_cfg[channel].timer; /* Now we know which speed_mode and timer is set, then we will convert * the channel number from (0 - 15) to (0 - 7). */ channel %= 8; /* Enable peripheral */ set_mask32(DPORT_LEDC_CLK_EN, DPORT_PERIP_CLK_EN_REG); clear_mask32(DPORT_LEDC_RST, DPORT_PERIP_RST_EN_REG); /* Set timer */ ret = pwm_led_esp32_timer_set(speed_mode, timer, config->timer_cfg[speed_mode][timer].bit_num, config->timer_cfg[speed_mode][timer].freq); if (ret < 0) { return ret; } /* Set channel */ ret = pwm_led_esp32_channel_set(pwm, speed_mode, channel, 0, timer); if (ret < 0) { return ret; } pwm_led_esp32_duty_set(speed_mode, channel, pulse_cycles); pwm_led_esp32_update_duty(speed_mode, channel); return ret; } static int pwm_led_esp32_get_cycles_per_sec(struct device *dev, u32_t pwm, u64_t *cycles) { const struct pwm_led_esp32_config *config; int channel; int timer; int speed_mode; config = (struct pwm_led_esp32_config *) dev->config->config_info; channel = pwm_led_esp32_get_gpio_config(pwm, config->ch_cfg); if (channel < 0) { return -EINVAL; } speed_mode = channel < 8 ? PWM_LED_ESP32_HIGH_SPEED : PWM_LED_ESP32_LOW_SPEED; timer = config->ch_cfg[channel].timer; *cycles = config->timer_cfg[speed_mode][timer].freq; return 0; } static const struct pwm_driver_api pwm_led_esp32_api = { .pin_set = pwm_led_esp32_pin_set_cycles, .get_cycles_per_sec = pwm_led_esp32_get_cycles_per_sec, }; int pwm_led_esp32_init(struct device *dev) { return 0; } /* Initialization for PWM_LED_ESP32 */ #include #include DEVICE_DECLARE(pwm_led_esp32_0); #define CH_HS_TIMER(i) ((CONFIG_PWM_LED_ESP32_HS_CH ## i ## _TIMER) & 0x2) #define CH_HS_GPIO(i) ((CONFIG_PWM_LED_ESP32_HS_CH ## i ## _GPIO) & 0xfff) #define CH_LS_TIMER(i) ((CONFIG_PWM_LED_ESP32_LS_CH ## i ## _TIMER) & 0x2) #define CH_LS_GPIO(i) ((CONFIG_PWM_LED_ESP32_LS_CH ## i ## _GPIO) & 0xfff) #define TIMER_HS_FREQ(i) (CONFIG_PWM_LED_ESP32_HS_TIMER ## i ## _FREQ) #define TIMER_LS_FREQ(i) (CONFIG_PWM_LED_ESP32_LS_TIMER ## i ## _FREQ) #define TIMER_HS_BIT_NUM(i) (CONFIG_PWM_LED_ESP32_HS_TIMER ## i ## _BIT_NUM) #define TIMER_LS_BIT_NUM(i) (CONFIG_PWM_LED_ESP32_LS_TIMER ## i ## _BIT_NUM) const static struct pwm_led_esp32_config pwm_led_esp32_config = { .timer_cfg[PWM_LED_ESP32_HIGH_SPEED][0] = { .bit_num = TIMER_HS_BIT_NUM(0), .freq = TIMER_HS_FREQ(0), }, .timer_cfg[PWM_LED_ESP32_HIGH_SPEED][1] = { .bit_num = TIMER_HS_BIT_NUM(1), .freq = TIMER_HS_FREQ(1), }, .timer_cfg[PWM_LED_ESP32_HIGH_SPEED][2] = { .bit_num = TIMER_HS_BIT_NUM(2), .freq = TIMER_HS_FREQ(2), }, .timer_cfg[PWM_LED_ESP32_HIGH_SPEED][2] = { .bit_num = TIMER_HS_BIT_NUM(2), .freq = TIMER_HS_FREQ(2), }, .timer_cfg[PWM_LED_ESP32_HIGH_SPEED][3] = { .bit_num = TIMER_HS_BIT_NUM(3), .freq = TIMER_HS_FREQ(3), }, .timer_cfg[PWM_LED_ESP32_LOW_SPEED][0] = { .bit_num = TIMER_LS_BIT_NUM(0), .freq = TIMER_LS_FREQ(0), }, .timer_cfg[PWM_LED_ESP32_LOW_SPEED][1] = { .bit_num = TIMER_LS_BIT_NUM(1), .freq = TIMER_LS_FREQ(1), }, .timer_cfg[PWM_LED_ESP32_LOW_SPEED][2] = { .bit_num = TIMER_LS_BIT_NUM(2), .freq = TIMER_LS_FREQ(2), }, .timer_cfg[PWM_LED_ESP32_LOW_SPEED][3] = { .bit_num = TIMER_LS_BIT_NUM(3), .freq = TIMER_LS_FREQ(3), }, .ch_cfg[0] = { .timer = CH_HS_TIMER(0), .gpio = CH_HS_GPIO(0), }, .ch_cfg[1] = { .timer = CH_HS_TIMER(1), .gpio = CH_HS_GPIO(1), }, .ch_cfg[2] = { .timer = CH_HS_TIMER(2), .gpio = CH_HS_GPIO(2), }, .ch_cfg[3] = { .timer = CH_HS_TIMER(3), .gpio = CH_HS_GPIO(3), }, .ch_cfg[4] = { .timer = CH_HS_TIMER(4), .gpio = CH_HS_GPIO(4), }, .ch_cfg[5] = { .timer = CH_HS_TIMER(5), .gpio = CH_HS_GPIO(5), }, .ch_cfg[6] = { .timer = CH_HS_TIMER(6), .gpio = CH_HS_GPIO(6), }, .ch_cfg[7] = { .timer = CH_HS_TIMER(7), .gpio = CH_HS_GPIO(7), }, .ch_cfg[8] = { .timer = CH_LS_TIMER(0), .gpio = CH_LS_GPIO(0), }, .ch_cfg[9] = { .timer = CH_LS_TIMER(1), .gpio = CH_LS_GPIO(1), }, .ch_cfg[10] = { .timer = CH_LS_TIMER(2), .gpio = CH_LS_GPIO(2), }, .ch_cfg[11] = { .timer = CH_LS_TIMER(3), .gpio = CH_LS_GPIO(3), }, .ch_cfg[12] = { .timer = CH_LS_TIMER(4), .gpio = CH_LS_GPIO(4), }, .ch_cfg[13] = { .timer = CH_LS_TIMER(5), .gpio = CH_LS_GPIO(5), }, .ch_cfg[14] = { .timer = CH_LS_TIMER(6), .gpio = CH_LS_GPIO(6), }, .ch_cfg[15] = { .timer = CH_LS_TIMER(7), .gpio = CH_LS_GPIO(7), }, }; DEVICE_AND_API_INIT(pwm_led_esp32_0, CONFIG_PWM_LED_ESP32_DEV_NAME_0, pwm_led_esp32_init, NULL, &pwm_led_esp32_config, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &pwm_led_esp32_api);