drivers: clock: Add Atmel SAM PMC driver

Add initial version of clock control for Atmel SAM SoC series. This add
support to Power Management which allows control peripherals clock.

Signed-off-by: Gerson Fernando Budke <nandojve@gmail.com>
This commit is contained in:
Gerson Fernando Budke
2023-03-05 22:03:59 +01:00
committed by Marti Bolivar
parent 2e3d68e7d5
commit 88cedcf5c5
13 changed files with 314 additions and 0 deletions

View File

@@ -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)

View File

@@ -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

View File

@@ -0,0 +1,9 @@
# Copyright (c) 2023 Gerson Fernando Budke <nandojve@gmail.com>
# 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.

View File

@@ -0,0 +1,150 @@
/*
* Copyright (c) 2023 Gerson Fernando Budke <nandojve@gmail.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT atmel_sam_pmc
#include <stdint.h>
#include <zephyr/arch/cpu.h>
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/clock_control.h>
#include <zephyr/drivers/clock_control/atmel_sam_pmc.h>
#include <zephyr/logging/log.h>
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);

View File

@@ -6,6 +6,7 @@
#include <arm/armv7-m.dtsi>
#include <zephyr/dt-bindings/i2c/i2c.h>
#include <zephyr/dt-bindings/clock/atmel_sam_pmc.h>
/ {
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>;

View File

@@ -7,6 +7,7 @@
#include <arm/armv7-m.dtsi>
#include <zephyr/dt-bindings/i2c/i2c.h>
#include <zephyr/dt-bindings/gpio/gpio.h>
#include <zephyr/dt-bindings/clock/atmel_sam_pmc.h>
/ {
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";
};

View File

@@ -7,6 +7,7 @@
#include <arm/armv7-m.dtsi>
#include <zephyr/dt-bindings/i2c/i2c.h>
#include <zephyr/dt-bindings/gpio/gpio.h>
#include <zephyr/dt-bindings/clock/atmel_sam_pmc.h>
/ {
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>;

View File

@@ -9,6 +9,7 @@
#include <zephyr/dt-bindings/i2c/i2c.h>
#include <zephyr/dt-bindings/gpio/gpio.h>
#include <zephyr/dt-bindings/pwm/pwm.h>
#include <zephyr/dt-bindings/clock/atmel_sam_pmc.h>
/ {
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";
};

View File

@@ -9,6 +9,7 @@
#include <zephyr/dt-bindings/i2c/i2c.h>
#include <zephyr/dt-bindings/gpio/gpio.h>
#include <zephyr/dt-bindings/pwm/pwm.h>
#include <zephyr/dt-bindings/clock/atmel_sam_pmc.h>
/ {
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>;

View File

@@ -0,0 +1,51 @@
# Copyright (c) 2023 Gerson Fernando Budke <nandojve@gmail.com>
# 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

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) 2023 Gerson Fernando Budke <nandojve@gmail.com
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_INCLUDE_DRIVERS_CLOCK_CONTROL_ATMEL_SAM_PMC_H_
#define ZEPHYR_INCLUDE_DRIVERS_CLOCK_CONTROL_ATMEL_SAM_PMC_H_
#include <zephyr/drivers/clock_control.h>
#include <zephyr/dt-bindings/clock/atmel_sam_pmc.h>
#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_ */

View File

@@ -0,0 +1,16 @@
/*
* Copyright (c) 2023 Gerson Fernando Budke <nandojve@gmail.com
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_INCLUDE_DT_BINDINGS_CLOCK_ATMEL_SAM_PMC_H_
#define ZEPHYR_INCLUDE_DT_BINDINGS_CLOCK_ATMEL_SAM_PMC_H_
#define PMC_TYPE_CORE 0
#define PMC_TYPE_SYSTEM 1
#define PMC_TYPE_PERIPHERAL 2
#define PMC_TYPE_GCK 3
#define PMC_TYPE_PROGRAMMABLE 4
#endif /* ZEPHYR_INCLUDE_DT_BINDINGS_CLOCK_ATMEL_SAM_PMC_H_ */

View File

@@ -10,6 +10,9 @@ if SOC_FAMILY_SAM
config SYS_CLOCK_HW_CYCLES_PER_SEC
default $(dt_node_int_prop_int,/cpus/cpu@0,clock-frequency)
config CLOCK_CONTROL
default y
config PINCTRL
default y