Compare commits

...

2 commits

Author SHA1 Message Date
ce9da01428 drivers: adc: add a WCH CH32V00x ADC driver
Signed-off-by: Michael Hope <mlhx@google.com>
2024-10-06 17:58:09 +00:00
bd58007d73 drivers: console: add a QingKeV2 Debug console driver
Signed-off-by: Michael Hope <mlhx@google.com>
2024-10-06 17:33:07 +00:00
14 changed files with 400 additions and 6 deletions

View file

@ -15,7 +15,7 @@
chosen {
zephyr,sram = &sram0;
zephyr,flash = &flash0;
zephyr,console = &usart1;
zephyr,console = &debug0;
zephyr,shell-uart = &usart1;
};
@ -32,6 +32,10 @@
};
};
&debug0 {
status = "okay";
};
&gpiod {
status = "okay";
};

View file

@ -54,3 +54,4 @@ zephyr_library_sources_ifdef(CONFIG_ADC_MCUX_GAU adc_mcux_gau_adc.c)
zephyr_library_sources_ifdef(CONFIG_ADC_AMBIQ adc_ambiq.c)
zephyr_library_sources_ifdef(CONFIG_ADC_RENESAS_RA adc_renesas_ra.c)
zephyr_library_sources_ifdef(CONFIG_ADC_MAX32 adc_max32.c)
zephyr_library_sources_ifdef(CONFIG_ADC_CH32V00X adc_ch32v00x.c)

View file

@ -134,4 +134,6 @@ source "drivers/adc/Kconfig.renesas_ra"
source "drivers/adc/Kconfig.max32"
source "drivers/adc/Kconfig.ch32v00x"
endif # ADC

View file

@ -0,0 +1,9 @@
# Copyright (c) 2024 Google LLC.
# SPDX-License-Identifier: Apache-2.0
config ADC_CH32V00X
bool "WCH CH32V00x ADC Driver"
default y
depends on DT_HAS_WCH_ADC_ENABLED
help
Enable the WCH CH32V00x family Analog-to-Digital Converter (ADC) driver.

148
drivers/adc/adc_ch32v00x.c Normal file
View file

@ -0,0 +1,148 @@
/*
* Copyright (c) 2024 Google LLC.
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT wch_adc
#include <soc.h>
#include <zephyr/drivers/adc.h>
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/drivers/clock_control.h>
#include <zephyr/logging/log.h>
#include <zephyr/irq.h>
LOG_MODULE_REGISTER(adc_ch32v00x, CONFIG_ADC_LOG_LEVEL);
#include <ch32_adc.h>
struct adc_ch32v00x_data {
};
struct adc_ch32v00x_config {
ADC_TypeDef *regs;
const struct device *clock_dev;
uint8_t clock_id;
};
static int adc_ch32v00x_channel_setup(const struct device *dev,
const struct adc_channel_cfg *channel_cfg)
{
if (channel_cfg->gain != ADC_GAIN_1) {
return -EINVAL;
}
if (channel_cfg->reference != ADC_REF_INTERNAL) {
return -EINVAL;
}
if (channel_cfg->acquisition_time != ADC_ACQ_TIME_DEFAULT) {
return -EINVAL;
}
if (channel_cfg->differential) {
return -EINVAL;
}
if (channel_cfg->channel_id >= 10) {
return -EINVAL;
}
return 0;
}
static int adc_ch32v00x_read(const struct device *dev, const struct adc_sequence *sequence)
{
const struct adc_ch32v00x_config *config = dev->config;
ADC_TypeDef *regs = config->regs;
uint32_t channels = sequence->channels;
int rsqr = 2;
int sequence_id = 0;
int total_channels = 0;
int i;
uint16_t *samples = sequence->buffer;
if (sequence->options != NULL) {
return -ENOTSUP;
}
if (sequence->resolution != 10) {
return -EINVAL;
}
if (sequence->oversampling != 0) {
return -ENOTSUP;
}
if (sequence->channels >= (1 << 10)) {
return -EINVAL;
}
if (sequence->calibrate) {
regs->CTLR2 |= ADC_RSTCAL;
while ((regs->CTLR2 & ADC_RSTCAL) != 0) {
}
regs->CTLR2 |= ADC_CAL;
while ((ADC1->CTLR2 & ADC_CAL) != 0) {
}
}
regs->RSQR1 = 0;
regs->RSQR2 = 0;
regs->RSQR3 = 0;
for (i = 0; channels != 0; i++, channels >>= 1) {
if ((channels & 1) != 0) {
total_channels++;
(&regs->RSQR1)[rsqr] |= i << sequence_id;
/* The channel IDs are packed 5 bits at a time into RSQR3 down to RSQR1.
*/
sequence_id += ADC_SQ2_0;
if (sequence_id >= 32) {
sequence_id = 0;
rsqr--;
}
}
}
if (total_channels == 0) {
return 0;
}
if (sequence->buffer_size < total_channels * sizeof(*samples)) {
return -ENOMEM;
}
/* Set the number of channels to read. Note that '0' means 'one channel'. */
regs->RSQR1 |= (total_channels - 1) * ADC_L_0;
regs->CTLR2 |= ADC_SWSTART;
for (i = 0; i < total_channels; i++) {
while ((regs->STATR & ADC_EOC) == 0) {
}
*samples++ = regs->RDATAR;
}
return 0;
}
static int adc_ch32v00x_init(const struct device *dev)
{
const struct adc_ch32v00x_config *config = dev->config;
ADC_TypeDef *regs = config->regs;
clock_control_on(config->clock_dev, (clock_control_subsys_t *)(uintptr_t)config->clock_id);
regs->CTLR2 = ADC_ADON | ADC_EXTSEL;
return 0;
}
#define ADC_CH32V00X_DEVICE(n) \
static const struct adc_driver_api adc_ch32v00x_api_##n = { \
.channel_setup = adc_ch32v00x_channel_setup, \
.read = adc_ch32v00x_read, \
.ref_internal = DT_INST_PROP(n, vref_mv), \
}; \
\
static const struct adc_ch32v00x_config adc_ch32v00x_config_##n = { \
.regs = (ADC_TypeDef *)DT_INST_REG_ADDR(n), \
.clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \
.clock_id = DT_INST_CLOCKS_CELL(n, id), \
}; \
\
DEVICE_DT_INST_DEFINE(n, adc_ch32v00x_init, NULL, NULL, &adc_ch32v00x_config_##n, \
POST_KERNEL, CONFIG_ADC_INIT_PRIORITY, &adc_ch32v00x_api_##n);
DT_INST_FOREACH_STATUS_OKAY(ADC_CH32V00X_DEVICE)

View file

@ -15,3 +15,4 @@ zephyr_library_sources_ifdef(CONFIG_UART_MCUMGR uart_mcumgr.c)
zephyr_library_sources_ifdef(CONFIG_XTENSA_SIM_CONSOLE xtensa_sim_console.c)
zephyr_library_sources_ifdef(CONFIG_EFI_CONSOLE efi_console.c)
zephyr_library_sources_ifdef(CONFIG_WINSTREAM_CONSOLE winstream_console.c)
zephyr_library_sources_ifdef(CONFIG_QINGKEV2_DEBUG_CONSOLE qingkev2_debug_console.c)

View file

@ -1,10 +1,10 @@
# Console driver configuration options
#Console driver configuration options
# Copyright (c) 2014-2015 Wind River Systems, Inc.
# Copyright (c) 2016 Cadence Design Systems, Inc.
# SPDX-License-Identifier: Apache-2.0
#Copyright(c) 2014 - 2015 Wind River Systems, Inc.
#Copyright(c) 2016 Cadence Design Systems, Inc.
#SPDX - License - Identifier : Apache - 2.0
# Setting shared by different subsystems
#Setting shared by different subsystems
menuconfig CONSOLE
bool "Console drivers"
@ -284,4 +284,13 @@ config WINSTREAM_CONSOLE
See the WINSTREAM Kconfig help for more information.
config QINGKEV2_DEBUG_CONSOLE
bool "Use the QingKeV2 debug module as a console"
depends on DT_HAS_WCH_QINGKEV2_DEBUG_ENABLED
select CONSOLE_HAS_DRIVER
help
Emit console messages to the host via the debug unit which can be displayed
using `minichlink`. Text is buffered and, in the worst case, flushed when a
space or ASCII control character is printed.
endif # CONSOLE

View file

@ -0,0 +1,114 @@
/*
* Copyright (c) 2024 Google LLC.
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT wch_qingkev2_debug
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/init.h>
#include <zephyr/kernel.h>
#include <zephyr/sys/winstream.h>
#define TX_TIMEOUT 1000000
#define TX_FULL BIT(7)
#define TX_VALID BIT(2)
#define TX_SIZE_MASK 0x03
/*
* Tracks if the host has been detected. Used to prevent spinning when the console is enabled but
* the host is not connected.
*/
enum qingkev2_debug_state {
QINGKEV2_DEBUG_STATE_INITIAL,
/* The first buffer has been written to the host. */
QINGKEV2_DEBUG_STATE_FIRST,
/* The first buffer was acknowledged by the host. */
QINGKEV2_DEBUG_STATE_ESTABLISHED,
/* Timeout while trying to send the second buffer to the host. */
QINGKEV2_DEBUG_STATE_MISSING,
};
struct qingkeyv2_debug_regs {
uint32_t unused;
uint32_t data0;
uint32_t data1;
};
struct qingkeyv2_debug_data {
/* Encoded text to be written to the host. */
uint32_t buffer;
enum qingkev2_debug_state state;
};
static struct qingkeyv2_debug_data qingkeyv2_debug_data_0;
#if defined(CONFIG_STDOUT_CONSOLE)
extern void __stdout_hook_install(int (*hook)(int));
#endif
#if defined(CONFIG_PRINTK)
extern void __printk_hook_install(int (*fn)(int));
#endif
static int qingkev2_debug_console_putc(int ch)
{
/*
* `minichlink` encodes the incoming and outgoing characters into `data0`. The least
* significant byte is used for control and length, while the upper bytes are used for
* up to three characters. The `TX_FULL` bit is used to pass ownership back and forth
* between the host and device.
*/
volatile struct qingkeyv2_debug_regs *regs =
(volatile struct qingkeyv2_debug_regs *)DT_INST_REG_ADDR(0);
struct qingkeyv2_debug_data *data = &qingkeyv2_debug_data_0;
int count = data->buffer & TX_SIZE_MASK;
int timeout;
data->buffer |= (ch << (++count * 8));
data->buffer++;
/*
* Try to send if the buffer is full, or the character is a space or a control character, or
* the host is ready.
*/
if (count == 3 || ch <= ' ' || (regs->data0 & TX_FULL) == 0) {
if (data->state != QINGKEV2_DEBUG_STATE_MISSING) {
for (timeout = 0; timeout != TX_TIMEOUT && (regs->data0 & TX_FULL) != 0;
timeout++) {
}
}
if ((regs->data0 & TX_FULL) == 0) {
regs->data0 = data->buffer | TX_FULL | TX_VALID;
if (data->state < QINGKEV2_DEBUG_STATE_ESTABLISHED) {
/* Transitions from INITIAL -> FIRST -> ESTABLISHED */
data->state++;
} else if (data->state == QINGKEV2_DEBUG_STATE_MISSING) {
/* The host has caught up. */
data->state = QINGKEV2_DEBUG_STATE_ESTABLISHED;
}
} else {
if (data->state == QINGKEV2_DEBUG_STATE_FIRST) {
data->state = QINGKEV2_DEBUG_STATE_MISSING;
}
}
data->buffer = 0;
}
return 1;
}
static int qingkev2_debug_console_init(void)
{
#if defined(CONFIG_STDOUT_CONSOLE)
__stdout_hook_install(qingkev2_debug_console_putc);
#endif
#if defined(CONFIG_PRINTK)
__printk_hook_install(qingkev2_debug_console_putc);
#endif
return 0;
}
SYS_INIT(qingkev2_debug_console_init, PRE_KERNEL_1, CONFIG_CONSOLE_INIT_PRIORITY);

View file

@ -0,0 +1,30 @@
# Copyright (c) 2024 Google LLC.
# SPDX-License-Identifier: Apache-2.0
description: WCH CH32V00x Analog-to-digital Converter (ADC)
compatible: "wch,adc"
include: adc-controller.yaml
properties:
reg:
required: true
interrupts:
required: true
clocks:
required: true
vref-mv:
type: int
default: 3300
description: |
Reference voltage of in mV. This is typically VDD.
"#io-channel-cells":
const: 1
io-channel-cells:
- input

View file

@ -0,0 +1,8 @@
# Copyright (c) 2024 Google LLC.
# SPDX-License-Identifier: Apache-2.0
description: WCH QingKeV2 Debug
compatible: "wch,qingkev2-debug"
include: base.yaml

View file

@ -30,6 +30,11 @@
compatible = "simple-bus";
ranges;
debug0: debug@e00000f0 {
compatible = "wch,qingkev2-debug";
reg = <0xe00000f0 0x40>;
};
sram0: memory@20000000 {
compatible = "mmio-sram";
reg = <0x20000000 0x800>;
@ -105,6 +110,15 @@
};
};
adc1: adc@40012400 {
compatible = "wch,adc";
reg = <0x40012400 16>;
clocks = <&rcc CH32V00X_CLOCK_ADC1>;
interrupt-parent = <&pfic>;
interrupts = <29>;
#io-channel-cells = <1>;
};
usart1: uart@40013800 {
compatible = "wch,usart";
reg = <0x40013800 16>;

View file

@ -0,0 +1,16 @@
/*
* Copyright (c) 2024 Google LLC.
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _CH32_ADC_H
#define _CH32_ADC_H
#ifdef CONFIG_SOC_CH32V003
#include <ch32v003fun.h>
#else
#error "SoC not supported!"
#endif
#endif

View file

@ -0,0 +1,37 @@
/*
* Copyright (c) 2024 Google LLC.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/dt-bindings/adc/adc.h>
/ {
zephyr,user {
/* Vcal (~1.20 V) and 1/2 Vref (~1.65 V). */
io-channels = <&adc1 8 &adc1 9>;
};
};
&adc1 {
status = "okay";
#address-cells = <1>;
#size-cells = <0>;
vcal@8 {
reg = <8>;
zephyr,gain = "ADC_GAIN_1";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
zephyr,resolution = <10>;
};
vref_div_2@9 {
reg = <9>;
zephyr,gain = "ADC_GAIN_1";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
zephyr,resolution = <10>;
};
};

View file

@ -35,6 +35,7 @@ tests:
- frdm_mcxn947/mcxn947/cpu0
- frdm_mcxc242
- ucans32k1sic
- wch_ch32v003evt
integration_platforms:
- nucleo_l073rz
- nrf52840dk/nrf52840