diff --git a/drivers/clock_control/CMakeLists.txt b/drivers/clock_control/CMakeLists.txt index 034db40ad6..857a62e52e 100644 --- a/drivers/clock_control/CMakeLists.txt +++ b/drivers/clock_control/CMakeLists.txt @@ -21,6 +21,7 @@ zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NRF clock_cont zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_NRF_DRIVER_CALIBRATION nrf_clock_calibration.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_RV32M1_PCC clock_control_rv32m1_pcc.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_INFINEON_CAT1 clock_control_ifx_cat1.c) +zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_SAM clock_control_sam_pmc.c) if(CONFIG_CLOCK_CONTROL_STM32_CUBE) diff --git a/drivers/clock_control/Kconfig b/drivers/clock_control/Kconfig index 7cb07258a6..1f2a2347fa 100644 --- a/drivers/clock_control/Kconfig +++ b/drivers/clock_control/Kconfig @@ -68,4 +68,6 @@ source "drivers/clock_control/Kconfig.aspeed" source "drivers/clock_control/Kconfig.gd32" +source "drivers/clock_control/Kconfig.sam" + endif # CLOCK_CONTROL diff --git a/drivers/clock_control/Kconfig.sam b/drivers/clock_control/Kconfig.sam new file mode 100644 index 0000000000..59c095abc1 --- /dev/null +++ b/drivers/clock_control/Kconfig.sam @@ -0,0 +1,9 @@ +# Copyright (c) 2023 Gerson Fernando Budke +# SPDX-License-Identifier: Apache-2.0 + +config CLOCK_CONTROL_SAM + bool "Atmel SAM clock control" + default y + depends on DT_HAS_ATMEL_SAM_PMC_ENABLED + help + Enable driver for Atmel SAM Clock Control. diff --git a/drivers/clock_control/clock_control_sam_pmc.c b/drivers/clock_control/clock_control_sam_pmc.c new file mode 100644 index 0000000000..cb37e8dcd8 --- /dev/null +++ b/drivers/clock_control/clock_control_sam_pmc.c @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2023 Gerson Fernando Budke + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT atmel_sam_pmc + +#include + +#include +#include +#include +#include +#include + +#include +LOG_MODULE_REGISTER(clock_control, CONFIG_CLOCK_CONTROL_LOG_LEVEL); + +static int atmel_sam_clock_control_on(const struct device *dev, + clock_control_subsys_t sys) +{ + ARG_UNUSED(dev); + + const struct atmel_sam_pmc_config *cfg = (const struct atmel_sam_pmc_config *)sys; + + if (cfg == NULL) { + LOG_ERR("The PMC config can not be NULL."); + return -ENXIO; + } + + LOG_DBG("Type: %x, Id: %d", cfg->clock_type, cfg->peripheral_id); + + switch (cfg->clock_type) { + case PMC_TYPE_PERIPHERAL: + soc_pmc_peripheral_enable(cfg->peripheral_id); + break; + default: + LOG_ERR("The PMC clock type is not implemented."); + return -ENODEV; + } + + return 0; +} + +static int atmel_sam_clock_control_off(const struct device *dev, + clock_control_subsys_t sys) +{ + ARG_UNUSED(dev); + + const struct atmel_sam_pmc_config *cfg = (const struct atmel_sam_pmc_config *)sys; + + if (cfg == NULL) { + LOG_ERR("The PMC config can not be NULL."); + return -ENXIO; + } + + LOG_DBG("Type: %x, Id: %d", cfg->clock_type, cfg->peripheral_id); + + switch (cfg->clock_type) { + case PMC_TYPE_PERIPHERAL: + soc_pmc_peripheral_disable(cfg->peripheral_id); + break; + default: + LOG_ERR("The PMC clock type is not implemented."); + return -ENODEV; + } + + return 0; +} + +static int atmel_sam_clock_control_get_rate(const struct device *dev, + clock_control_subsys_t sys, + uint32_t *rate) +{ + ARG_UNUSED(dev); + + const struct atmel_sam_pmc_config *cfg = (const struct atmel_sam_pmc_config *)sys; + + if (cfg == NULL) { + LOG_ERR("The PMC config can not be NULL."); + return -ENXIO; + } + + LOG_DBG("Type: %x, Id: %d", cfg->clock_type, cfg->peripheral_id); + + switch (cfg->clock_type) { + case PMC_TYPE_PERIPHERAL: + *rate = SOC_ATMEL_SAM_MCK_FREQ_HZ; + break; + default: + LOG_ERR("The PMC clock type is not implemented."); + return -ENODEV; + } + + LOG_DBG("Rate: %d", *rate); + + return 0; +} + +static enum clock_control_status +atmel_sam_clock_control_get_status(const struct device *dev, + clock_control_subsys_t sys) +{ + ARG_UNUSED(dev); + + const struct atmel_sam_pmc_config *cfg = (const struct atmel_sam_pmc_config *)sys; + enum clock_control_status status; + + if (cfg == NULL) { + LOG_ERR("The PMC config can not be NULL."); + return -ENXIO; + } + + LOG_DBG("Type: %x, Id: %d", cfg->clock_type, cfg->peripheral_id); + + switch (cfg->clock_type) { + case PMC_TYPE_PERIPHERAL: + status = soc_pmc_peripheral_is_enabled(cfg->peripheral_id) > 0 + ? CLOCK_CONTROL_STATUS_ON + : CLOCK_CONTROL_STATUS_OFF; + break; + default: + LOG_ERR("The PMC clock type is not implemented."); + return -ENODEV; + } + + return status; +} + +static struct clock_control_driver_api atmel_sam_clock_control_api = { + .on = atmel_sam_clock_control_on, + .off = atmel_sam_clock_control_off, + .get_rate = atmel_sam_clock_control_get_rate, + .get_status = atmel_sam_clock_control_get_status, +}; + +static int atmel_sam_clock_control_init(const struct device *dev) +{ + ARG_UNUSED(dev); + + return 0; +} + +DEVICE_DT_INST_DEFINE(0, atmel_sam_clock_control_init, + NULL, + NULL, + NULL, + PRE_KERNEL_1, CONFIG_CLOCK_CONTROL_INIT_PRIORITY, + &atmel_sam_clock_control_api); diff --git a/dts/arm/atmel/sam3x.dtsi b/dts/arm/atmel/sam3x.dtsi index 56299175a3..17b1b97a0b 100644 --- a/dts/arm/atmel/sam3x.dtsi +++ b/dts/arm/atmel/sam3x.dtsi @@ -6,6 +6,7 @@ #include #include +#include / { aliases { @@ -28,6 +29,14 @@ }; soc { + pmc: pmc@400e0600 { + compatible = "atmel,sam-pmc"; + reg = <0x400e0600 0x200>; + interrupts = <5 0>; + #clock-cells = <2>; + status = "okay"; + }; + sram0: memory@20070000 { compatible = "mmio-sram"; reg = <0x20070000 0x18000>; diff --git a/dts/arm/atmel/sam4e.dtsi b/dts/arm/atmel/sam4e.dtsi index 1ee52c97ae..1a766e2161 100644 --- a/dts/arm/atmel/sam4e.dtsi +++ b/dts/arm/atmel/sam4e.dtsi @@ -7,6 +7,7 @@ #include #include #include +#include / { aliases { @@ -37,6 +38,14 @@ }; soc { + pmc: pmc@400e0400 { + compatible = "atmel,sam-pmc"; + reg = <0x400e0400 0x200>; + interrupts = <5 0>; + #clock-cells = <2>; + status = "okay"; + }; + sram0: memory@20000000 { compatible = "mmio-sram"; }; diff --git a/dts/arm/atmel/sam4l.dtsi b/dts/arm/atmel/sam4l.dtsi index cb3052880f..296471a7c5 100644 --- a/dts/arm/atmel/sam4l.dtsi +++ b/dts/arm/atmel/sam4l.dtsi @@ -7,6 +7,7 @@ #include #include #include +#include / { chosen { @@ -50,6 +51,14 @@ }; soc { + pmc: pmc@400e0000 { + compatible = "atmel,sam-pmc"; + reg = <0x400e0000 0x740>; + interrupts = <22 0>; + #clock-cells = <2>; + status = "okay"; + }; + flashcalw: flash-controller@400a0000 { compatible = "atmel,sam4l-flashcalw-controller"; reg = <0x400a0000 0x400>; diff --git a/dts/arm/atmel/sam4s.dtsi b/dts/arm/atmel/sam4s.dtsi index 3d44894601..8e67afbbb3 100644 --- a/dts/arm/atmel/sam4s.dtsi +++ b/dts/arm/atmel/sam4s.dtsi @@ -9,6 +9,7 @@ #include #include #include +#include / { aliases { @@ -39,6 +40,14 @@ }; soc { + pmc: pmc@400e0400 { + compatible = "atmel,sam-pmc"; + reg = <0x400e0400 0x200>; + interrupts = <5 0>; + #clock-cells = <2>; + status = "okay"; + }; + sram0: memory@20100000 { compatible = "mmio-sram"; }; diff --git a/dts/arm/atmel/same70.dtsi b/dts/arm/atmel/same70.dtsi index b22454a7d9..6d774b38cc 100644 --- a/dts/arm/atmel/same70.dtsi +++ b/dts/arm/atmel/same70.dtsi @@ -9,6 +9,7 @@ #include #include #include +#include / { aliases { @@ -47,6 +48,14 @@ }; soc { + pmc: pmc@400e0600 { + compatible = "atmel,sam-pmc"; + reg = <0x400e0600 0x200>; + interrupts = <5 0>; + #clock-cells = <2>; + status = "okay"; + }; + eefc: flash-controller@400e0c00 { compatible = "atmel,sam-flash-controller"; reg = <0x400e0c00 0x200>; diff --git a/dts/bindings/clock/atmel,sam-pmc.yaml b/dts/bindings/clock/atmel,sam-pmc.yaml new file mode 100644 index 0000000000..5eb066979c --- /dev/null +++ b/dts/bindings/clock/atmel,sam-pmc.yaml @@ -0,0 +1,51 @@ +# Copyright (c) 2023 Gerson Fernando Budke +# SPDX-License-Identifier: Apache-2.0 + +description: | + Atmel Power Management Controller (PMC) + + The Power Management Controller (PMC) optimizes power consumption by + controlling all system and user peripheral clocks. The PMC enables/disables + the clock inputs to many of the peripherals and the processor. + + To specify the clocks in a peripheral, the standard clocks property needs + to be used, e.g.: + + uart: uart@xxx { + ... + clocks = <&pmc PMC_TYPE_PERIPHERAL p-id>; + ... + }; + + In this example the clock-type was defined as PMC_TYPE_PERIPHERAL and the + peripheral-id was defined as p-id. The p-id number should be consulted on + datasheet, usually it is available at Product Mapping figure. + + NOTE: The predefined clock type cell is defined at + include/zephyr/drivers/clock_clontrol/atmel_sam_pmc.h header file. + + The clock-type constants are: + PMC_TYPE_CORE + PMC_TYPE_SYSTEM + PMC_TYPE_PERIPHERAL + PMC_TYPE_GCK + PMC_TYPE_PROGRAMMABLE + +compatible: "atmel,sam-pmc" + +include: [clock-controller.yaml, base.yaml] + +properties: + reg: + required: true + + "#clock-cells": + const: 2 + description: | + from common clock binding; shall be set to 2. The first entry is the type + of the clock (core, system, peripheral or generated) and the second entry + it's the peripheral identification index as provided by the datasheet. + +clock-cells: + - clock-type + - peripheral-id diff --git a/include/zephyr/drivers/clock_control/atmel_sam_pmc.h b/include/zephyr/drivers/clock_control/atmel_sam_pmc.h new file mode 100644 index 0000000000..3a57a4ce01 --- /dev/null +++ b/include/zephyr/drivers/clock_control/atmel_sam_pmc.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2023 Gerson Fernando Budke +#include + +#define SAM_DT_PMC_CONTROLLER DEVICE_DT_GET(DT_NODELABEL(pmc)) + +struct atmel_sam_pmc_config { + uint32_t clock_type; + uint32_t peripheral_id; +}; + +#define SAM_DT_CLOCK_PMC_CFG(clock_id, node_id) \ + { \ + .clock_type = DT_CLOCKS_CELL_BY_IDX(node_id, clock_id, clock_type), \ + .peripheral_id = DT_CLOCKS_CELL_BY_IDX(node_id, clock_id, peripheral_id)\ + } + +#define SAM_DT_INST_CLOCK_PMC_CFG(inst) SAM_DT_CLOCK_PMC_CFG(0, DT_DRV_INST(inst)) + +#define SAM_DT_CLOCKS_PMC_CFG(node_id) \ + { \ + LISTIFY(DT_NUM_CLOCKS(node_id), \ + SAM_DT_CLOCK_PMC_CFG, (,), node_id) \ + } + +#define SAM_DT_INST_CLOCKS_PMC_CFG(inst) \ + SAM_DT_CLOCKS_PMC_CFG(DT_DRV_INST(inst)) + +#endif /* ZEPHYR_INCLUDE_DRIVERS_CLOCK_CONTROL_ATMEL_SAM_PMC_H_ */ diff --git a/include/zephyr/dt-bindings/clock/atmel_sam_pmc.h b/include/zephyr/dt-bindings/clock/atmel_sam_pmc.h new file mode 100644 index 0000000000..aa7b068001 --- /dev/null +++ b/include/zephyr/dt-bindings/clock/atmel_sam_pmc.h @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2023 Gerson Fernando Budke