/* * Copyright (c) 2022 IoT.bzh * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT renesas_pwm_rcar #include #include #include #include #include #include #include #define LOG_LEVEL CONFIG_PWM_LOG_LEVEL #include LOG_MODULE_REGISTER(pwm_rcar); /* PWM Controller capabilities */ #define RCAR_PWM_MAX_CYCLE 1023U #define RCAR_PWM_MAX_DIV 24U #define RCAR_PWM_MAX_CHANNEL 6 /* Registers */ #define RCAR_PWM_REG_SHIFT 0x1000 #define RCAR_PWM_CR(channel) \ ((uint32_t)((channel * RCAR_PWM_REG_SHIFT)) + 0x00) /* PWM Control Register */ #define RCAR_PWM_CNT(channel) \ ((uint32_t)((channel * RCAR_PWM_REG_SHIFT)) + 0x04) /* PWM Count Register */ /* PWMCR (PWM Control Register) */ #define RCAR_PWM_CR_CC_MASK 0x000f0000 /* Clock Control */ #define RCAR_PWM_CR_CC_SHIFT 16 #define RCAR_PWM_CR_CCMD BIT(15) /* Frequency Division Mode */ #define RCAR_PWM_CR_SYNC BIT(11) #define RCAR_PWM_CR_SS BIT(4) /* Single Pulse Output */ #define RCAR_PWM_CR_EN BIT(0) /* Channel Enable */ /* PWM Diviser is on 5 bits (CC combined with CCMD) */ #define RCAR_PWM_DIVISER_MASK (RCAR_PWM_CR_CC_MASK | RCAR_PWM_CR_CCMD) #define RCAR_PWM_DIVISER_SHIFT 15 /* PWMCNT (PWM Count Register) */ #define RCAR_PWM_CNT_CYC_MASK 0x03ff0000 /* PWM Cycle */ #define RCAR_PWM_CNT_CYC_SHIFT 16 #define RCAR_PWM_CNT_PH_MASK 0x000003ff /* PWM High-Level Period */ #define RCAR_PWM_CNT_PH_SHIFT 0 struct pwm_rcar_cfg { uint32_t reg_addr; const struct device *clock_dev; struct rcar_cpg_clk core_clk; struct rcar_cpg_clk mod_clk; const struct pinctrl_dev_config *pcfg; }; struct pwm_rcar_data { uint32_t clk_rate; }; static uint32_t pwm_rcar_read(const struct pwm_rcar_cfg *config, uint32_t offs) { return sys_read32(config->reg_addr + offs); } static void pwm_rcar_write(const struct pwm_rcar_cfg *config, uint32_t offs, uint32_t value) { sys_write32(value, config->reg_addr + offs); } static void pwm_rcar_write_bit(const struct pwm_rcar_cfg *config, uint32_t offs, uint32_t bits, bool value) { uint32_t reg_val = pwm_rcar_read(config, offs); if (value) { reg_val |= bits; } else { reg_val &= ~(bits); } pwm_rcar_write(config, offs, reg_val); } static int pwm_rcar_update_clk(const struct pwm_rcar_cfg *config, uint32_t channel, uint32_t *period_cycles, uint32_t *pulse_cycles) { uint32_t reg_val, power, diviser; power = pwm_rcar_read(config, RCAR_PWM_CR(channel)) & RCAR_PWM_DIVISER_MASK; power = power >> RCAR_PWM_DIVISER_SHIFT; diviser = 1 << power; LOG_DBG("Found old diviser : 2^%d=%d", power, diviser); /* Looking for the best possible clock diviser */ if (*period_cycles > RCAR_PWM_MAX_CYCLE) { /* Reducing clock speed */ while (*period_cycles > RCAR_PWM_MAX_CYCLE) { diviser *= 2; *period_cycles /= 2; *pulse_cycles /= 2; power++; if (power > RCAR_PWM_MAX_DIV) { return -ENOTSUP; } } } else { /* Increasing clock speed */ while (*period_cycles < (RCAR_PWM_MAX_CYCLE / 2)) { if (power == 0) { return -ENOTSUP; } diviser /= 2; *period_cycles *= 2; *pulse_cycles *= 2; power--; } } LOG_DBG("Found new diviser : 2^%d=%d\n", power, diviser); /* Set new clock Diviser */ reg_val = pwm_rcar_read(config, RCAR_PWM_CR(channel)); reg_val &= ~RCAR_PWM_DIVISER_MASK; reg_val |= (power << RCAR_PWM_DIVISER_SHIFT); pwm_rcar_write(config, RCAR_PWM_CR(channel), reg_val); return 0; } static int pwm_rcar_set_cycles(const struct device *dev, uint32_t channel, uint32_t period_cycles, uint32_t pulse_cycles, pwm_flags_t flags) { const struct pwm_rcar_cfg *config = dev->config; uint32_t reg_val; int ret = 0; if (channel > RCAR_PWM_MAX_CHANNEL) { return -ENOTSUP; } if (flags != PWM_POLARITY_NORMAL) { return -ENOTSUP; } /* Prohibited values */ if (period_cycles == 0U || pulse_cycles == 0U || pulse_cycles > period_cycles) { return -EINVAL; } LOG_DBG("base_reg=0x%x, pulse_cycles=%d, period_cycles=%d," " duty_cycle=%d", config->reg_addr, pulse_cycles, period_cycles, (pulse_cycles * 100U / period_cycles)); /* Disable PWM */ pwm_rcar_write_bit(config, RCAR_PWM_CR(channel), RCAR_PWM_CR_EN, false); /* Set continuous mode */ pwm_rcar_write_bit(config, RCAR_PWM_CR(channel), RCAR_PWM_CR_SS, false); /* Enable SYNC mode */ pwm_rcar_write_bit(config, RCAR_PWM_CR(channel), RCAR_PWM_CR_SYNC, true); /* * Set clock counter according to the requested period_cycles * if period_cycles is less than half of the counter, then the * clock diviser could be updated as the diviser is a modulo 2. */ if (period_cycles > RCAR_PWM_MAX_CYCLE || period_cycles < (RCAR_PWM_MAX_CYCLE / 2)) { LOG_DBG("Adapting frequency diviser..."); ret = pwm_rcar_update_clk(config, channel, &period_cycles, &pulse_cycles); if (ret != 0) { return ret; } } /* Set total period cycle */ reg_val = pwm_rcar_read(config, RCAR_PWM_CNT(channel)); reg_val &= ~(RCAR_PWM_CNT_CYC_MASK); reg_val |= (period_cycles << RCAR_PWM_CNT_CYC_SHIFT); pwm_rcar_write(config, RCAR_PWM_CNT(channel), reg_val); /* Set high level period cycle */ reg_val = pwm_rcar_read(config, RCAR_PWM_CNT(channel)); reg_val &= ~(RCAR_PWM_CNT_PH_MASK); reg_val |= (pulse_cycles << RCAR_PWM_CNT_PH_SHIFT); pwm_rcar_write(config, RCAR_PWM_CNT(channel), reg_val); /* Enable PWM */ pwm_rcar_write_bit(config, RCAR_PWM_CR(channel), RCAR_PWM_CR_EN, true); return ret; } static int pwm_rcar_get_cycles_per_sec(const struct device *dev, uint32_t channel, uint64_t *cycles) { const struct pwm_rcar_cfg *config = dev->config; struct pwm_rcar_data *data = dev->data; uint32_t diviser; if (channel > RCAR_PWM_MAX_CHANNEL) { return -ENOTSUP; } diviser = pwm_rcar_read(config, RCAR_PWM_CR(channel)) & RCAR_PWM_DIVISER_MASK; diviser = diviser >> RCAR_PWM_DIVISER_SHIFT; *cycles = data->clk_rate >> diviser; LOG_DBG("Actual division: %d and Frequency: %d Hz", diviser, (uint32_t)*cycles); return 0; } static int pwm_rcar_init(const struct device *dev) { const struct pwm_rcar_cfg *config = dev->config; struct pwm_rcar_data *data = dev->data; int ret; /* Configure dt provided device signals when available */ ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT); if (ret < 0) { return ret; } ret = clock_control_on(config->clock_dev, (clock_control_subsys_t)&config->mod_clk); if (ret < 0) { return ret; } ret = clock_control_get_rate(config->clock_dev, (clock_control_subsys_t)&config->core_clk, &data->clk_rate); if (ret < 0) { return ret; } return 0; } static const struct pwm_driver_api pwm_rcar_driver_api = { .set_cycles = pwm_rcar_set_cycles, .get_cycles_per_sec = pwm_rcar_get_cycles_per_sec, }; /* Device Instantiation */ #define PWM_DEVICE_RCAR_INIT(n) \ PINCTRL_DT_INST_DEFINE(n); \ static const struct pwm_rcar_cfg pwm_rcar_cfg_##n = { \ .reg_addr = DT_INST_REG_ADDR(n), \ .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \ .mod_clk.module = DT_INST_CLOCKS_CELL_BY_IDX(n, 0, module), \ .mod_clk.domain = DT_INST_CLOCKS_CELL_BY_IDX(n, 0, domain), \ .core_clk.module = DT_INST_CLOCKS_CELL_BY_IDX(n, 1, module), \ .core_clk.domain = DT_INST_CLOCKS_CELL_BY_IDX(n, 1, domain), \ }; \ static struct pwm_rcar_data pwm_rcar_data_##n; \ DEVICE_DT_INST_DEFINE(n, pwm_rcar_init, NULL, &pwm_rcar_data_##n, &pwm_rcar_cfg_##n, \ POST_KERNEL, CONFIG_PWM_INIT_PRIORITY, \ &pwm_rcar_driver_api); DT_INST_FOREACH_STATUS_OKAY(PWM_DEVICE_RCAR_INIT)