drivers: add Atmel SAM PWM driver
This patch adds basic support for the PWM devices available on the Atmel SAM family. Beside enabling the driver, everything is selected through the device tree, including enabling the PWM0 and PWM1 devices. Thus CONFIG_PWM_0 and CONFIG_PWM_1 are ignored. Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
This commit is contained in:
parent
580de0f386
commit
2168d80987
7 changed files with 217 additions and 0 deletions
|
@ -9,5 +9,6 @@ zephyr_library_sources_ifdef(CONFIG_PWM_NRFX pwm_nrfx.c)
|
|||
zephyr_library_sources_ifdef(CONFIG_PWM_MCUX_FTM pwm_mcux_ftm.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_PWM_IMX pwm_imx.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_PWM_LED_ESP32 pwm_led_esp32.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_PWM_SAM pwm_sam.c)
|
||||
|
||||
zephyr_library_sources_ifdef(CONFIG_USERSPACE pwm_handlers.c)
|
||||
|
|
|
@ -50,4 +50,6 @@ source "drivers/pwm/Kconfig.imx"
|
|||
|
||||
source "drivers/pwm/Kconfig.esp32"
|
||||
|
||||
source "drivers/pwm/Kconfig.sam"
|
||||
|
||||
endif # PWM
|
||||
|
|
12
drivers/pwm/Kconfig.sam
Normal file
12
drivers/pwm/Kconfig.sam
Normal file
|
@ -0,0 +1,12 @@
|
|||
# Kconfig.sam - Atmel SAM TRNG configuration
|
||||
#
|
||||
# Copyright (c) 2019 Aurelien Jarno
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
menuconfig PWM_SAM
|
||||
bool "Atmel SAM MCU Family PWM Driver"
|
||||
depends on SOC_FAMILY_SAM
|
||||
depends on PWM
|
||||
help
|
||||
Enable PWM driver for Atmel SAM MCUs.
|
119
drivers/pwm/pwm_sam.c
Normal file
119
drivers/pwm/pwm_sam.c
Normal file
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Aurelien Jarno
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <device.h>
|
||||
#include <errno.h>
|
||||
#include <pwm.h>
|
||||
#include <soc.h>
|
||||
|
||||
#define LOG_LEVEL CONFIG_PWM_LOG_LEVEL
|
||||
#include <logging/log.h>
|
||||
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 == 0 || 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 */
|
|
@ -254,6 +254,30 @@
|
|||
#gpio-cells = <2>;
|
||||
};
|
||||
|
||||
pwm0: pwm0@40020000 {
|
||||
compatible = "atmel,sam-pwm";
|
||||
reg = <0x40020000 0x4000>;
|
||||
interrupts = <31 0>;
|
||||
peripheral-id = <31>;
|
||||
status = "disabled";
|
||||
label = "PWM_0";
|
||||
prescaler = <10>;
|
||||
divider = <1>;
|
||||
#pwm-cells = <2>;
|
||||
};
|
||||
|
||||
pwm1: pwm1@4005c000 {
|
||||
compatible = "atmel,sam-pwm";
|
||||
reg = <0x4005c000 0x4000>;
|
||||
interrupts = <60 0>;
|
||||
peripheral-id = <60>;
|
||||
status = "disabled";
|
||||
label = "PWM_1";
|
||||
prescaler = <10>;
|
||||
divider = <1>;
|
||||
#pwm-cells = <2>;
|
||||
};
|
||||
|
||||
usbhs: usbd@40038000 {
|
||||
compatible = "atmel,sam-usbhs";
|
||||
#address-cells = <1>;
|
||||
|
|
54
dts/bindings/pwm/atmel,sam-pwm.yaml
Normal file
54
dts/bindings/pwm/atmel,sam-pwm.yaml
Normal file
|
@ -0,0 +1,54 @@
|
|||
#
|
||||
# Copyright (c) 2019, Aurelien Jarno
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
---
|
||||
title: Atmel SAM PWM
|
||||
version: 0.1
|
||||
|
||||
description: >
|
||||
This binding gives a base representation of the Atmel SAM PWM
|
||||
|
||||
inherits:
|
||||
!include pwm.yaml
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
constraint: "atmel,sam-pwm"
|
||||
|
||||
reg:
|
||||
type: array
|
||||
description: mmio register space
|
||||
generation: define
|
||||
category: required
|
||||
|
||||
interrupts:
|
||||
type: array
|
||||
category: required
|
||||
description: required interrupts
|
||||
generation: define
|
||||
|
||||
peripheral-id:
|
||||
type: int
|
||||
description: peripheral ID
|
||||
generation: define
|
||||
category: required
|
||||
|
||||
prescaler:
|
||||
type: int
|
||||
category: required
|
||||
description: Clock prescaler at the input of the PWM (0 to 10)
|
||||
generation: define
|
||||
|
||||
divider:
|
||||
type: int
|
||||
category: required
|
||||
description: Clock divider at the input of the PWM (1 to 255)
|
||||
generation: define
|
||||
|
||||
"#cells":
|
||||
- channel
|
||||
# period in terms of nanoseconds
|
||||
- period
|
||||
...
|
|
@ -83,4 +83,9 @@ config SOC_FLASH_SAM
|
|||
default y
|
||||
endif # FLASH
|
||||
|
||||
if PWM
|
||||
config PWM_SAM
|
||||
default y
|
||||
endif # PWM
|
||||
|
||||
endif # SOC_SERIES_SAME70
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue