/* * Copyright (c) 2019 Aurelien Jarno * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #define LOG_LEVEL CONFIG_PWM_LOG_LEVEL #include LOG_MODULE_REGISTER(pwm_sam); struct sam_pwm_config { Pwm *regs; u32_t id; u8_t prescaler; u8_t divider; }; #define DEV_CFG(dev) \ ((const struct sam_pwm_config * const)(dev)->config->config_info) static int sam_pwm_get_cycles_per_sec(struct device *dev, u32_t pwm, u64_t *cycles) { u8_t prescaler = DEV_CFG(dev)->prescaler; u8_t divider = DEV_CFG(dev)->divider; *cycles = SOC_ATMEL_SAM_MCK_FREQ_HZ / ((1 << prescaler) * divider); return 0; } static int sam_pwm_pin_set(struct device *dev, u32_t ch, u32_t period_cycles, u32_t pulse_cycles) { Pwm *const pwm = DEV_CFG(dev)->regs; if (ch >= PWMCHNUM_NUMBER) { return -EINVAL; } if (period_cycles == 0U || pulse_cycles > period_cycles) { return -EINVAL; } if (period_cycles > 0xffff) { return -ENOTSUP; } /* Select clock A */ pwm->PWM_CH_NUM[ch].PWM_CMR = PWM_CMR_CPRE_CLKA_Val; /* Update period and pulse using the update registers, so that the * change is triggered at the next PWM period. */ pwm->PWM_CH_NUM[ch].PWM_CPRDUPD = period_cycles; pwm->PWM_CH_NUM[ch].PWM_CDTYUPD = pulse_cycles; /* Enable the output */ pwm->PWM_ENA = 1 << ch; return 0; } static int sam_pwm_init(struct device *dev) { Pwm *const pwm = DEV_CFG(dev)->regs; u32_t id = DEV_CFG(dev)->id; u8_t prescaler = DEV_CFG(dev)->prescaler; u8_t divider = DEV_CFG(dev)->divider; /* FIXME: way to validate prescaler & divider */ /* Enable the PWM peripheral */ soc_pmc_peripheral_enable(id); /* Configure the clock A that will be used by all 4 channels */ pwm->PWM_CLK = PWM_CLK_PREA(prescaler) | PWM_CLK_DIVA(divider); return 0; } static const struct pwm_driver_api sam_pwm_driver_api = { .pin_set = sam_pwm_pin_set, .get_cycles_per_sec = sam_pwm_get_cycles_per_sec, }; #ifdef DT_ATMEL_SAM_PWM_0 static const struct sam_pwm_config sam_pwm_config_0 = { .regs = (Pwm *)DT_ATMEL_SAM_PWM_0_BASE_ADDRESS, .id = DT_ATMEL_SAM_PWM_0_PERIPHERAL_ID, .prescaler = DT_ATMEL_SAM_PWM_0_PRESCALER, .divider = DT_ATMEL_SAM_PWM_0_DIVIDER, }; DEVICE_AND_API_INIT(sam_pwm_0, DT_ATMEL_SAM_PWM_0_LABEL, &sam_pwm_init, NULL, &sam_pwm_config_0, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &sam_pwm_driver_api); #endif /* DT_ATMEL_SAM_PWM_0 */ #ifdef DT_ATMEL_SAM_PWM_1 static const struct sam_pwm_config sam_pwm_config_1 = { .regs = (Pwm *)DT_ATMEL_SAM_PWM_1_BASE_ADDRESS, .id = DT_ATMEL_SAM_PWM_1_PERIPHERAL_ID, .prescaler = DT_ATMEL_SAM_PWM_1_PRESCALER, .divider = DT_ATMEL_SAM_PWM_1_DIVIDER, }; DEVICE_AND_API_INIT(sam_pwm_1, DT_ATMEL_SAM_PWM_1_LABEL, &sam_pwm_init, NULL, &sam_pwm_config_1, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &sam_pwm_driver_api); #endif /* DT_ATMEL_SAM_PWM_1 */