blob: d5687b9225c958bf01f51597aaecb779d1af4388 [file] [edit]
/* Copyright 2023 The ChromiumOS Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include <stdio.h>
#include <string.h>
#include <zephyr/kernel.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/sys/ring_buffer.h>
#include <stm32g0xx_ll_bus.h>
#include <stm32g0xx_ll_system.h>
#include <stm32g0xx_ll_dma.h>
#include <stm32g0xx_ll_rcc.h>
#include <stm32g0xx_ll_gpio.h>
#include <stm32g0xx_ll_ucpd.h>
#include "meas.h"
#include "controls.h"
#include "view.h"
#include "model.h"
#include "ll_ucpd_patch.h"
/* STM32 interrupt registers */
#define UCPD_IRQ 8
#define DMA1_CHANNEL1_IRQ 9
#define DMA1_CHANNEL1_PRIO 2
#define UCPD_PRIO 2
/* snooper model thread parameters */
#define MODEL_THREAD_STACK_SIZE 500
#define MODEL_THREAD_PRIORITY 5
/* STM32 UCPD parameters */
static LL_UCPD_InitTypeDef ucpd_params;
/* Byte size of various portions of the packet */
#define MOD_BUFFERS 40
#define PACKET_HEADER_LEN 20
#define PD_SAMPLES 492
#define PACKET_BYTE_SIZE (PACKET_HEADER_LEN + PD_SAMPLES)
#define MAX_PACKET_XFER_SIZE 64
/* container for information of the type of packet to be sent */
struct packet_type_t {
uint16_t unused1: 4;
uint16_t polarity: 2;
uint16_t lost: 1;
uint16_t partial: 1;
uint16_t version: 4;
uint16_t type: 4;
};
/* container for header of the packet */
struct header_t {
uint32_t sequence;
uint16_t cc1_voltage;
uint16_t cc2_voltage;
uint16_t vcon_current;
uint16_t vbus_voltage;
uint16_t vbus_current;
struct packet_type_t packet_type;
uint16_t data_len;
uint16_t unused;
};
/* container for the entire packet */
struct packet_t {
struct header_t header;
uint8_t data[PD_SAMPLES];
};
/* storage for all the information of the current state of the snooper */
static struct model_t {
const struct device *dev;
struct packet_t packet;
uint8_t dma_buffer[PD_SAMPLES];
k_tid_t tid;
bool start;
bool empty_print;
bool slow_print;
bool auto_stop;
uint8_t mod_buff[MOD_BUFFERS][PD_SAMPLES];
uint16_t mod_size[MOD_BUFFERS];
struct packet_type_t sop[MOD_BUFFERS];
uint8_t mw;
uint8_t mr;
uint32_t sleep_time;
} model;
K_THREAD_STACK_DEFINE(model_stack_area, MODEL_THREAD_STACK_SIZE);
static struct k_thread model_thread_data;
static int pd_line;
void start_snooper(bool s)
{
model.start = s;
if (s) {
model.packet.header.sequence = 0;
view_set_snoop(CC1_CHANNEL_BIT | CC2_CHANNEL_BIT);
} else {
view_set_snoop(0);
}
}
void reset_snooper()
{
memset(&model.mod_buff, 0, MOD_BUFFERS * PD_SAMPLES);
memset(&model.mod_size, 0, MOD_BUFFERS * sizeof(uint16_t));
memset(&model.sop, 0, MOD_BUFFERS * sizeof(uint16_t));
model.mr = 0;
model.mw = 0;
model.packet.header.sequence = 0;
}
void set_role(snooper_mask_t role_mask)
{
if (role_mask & SINK_BIT) {
LL_UCPD_SetccEnable(UCPD1, LL_UCPD_CCENABLE_CC1CC2);
LL_UCPD_SetSNKRole(UCPD1);
return;
}
if (role_mask & CC1_CHANNEL_BIT) {
LL_UCPD_SetccEnable(UCPD1, LL_UCPD_CCENABLE_CC1);
} else if (role_mask & CC2_CHANNEL_BIT) {
LL_UCPD_SetccEnable(UCPD1, LL_UCPD_CCENABLE_CC2);
}
LL_UCPD_SetSRCRole(UCPD1);
uint8_t Rp = role_mask & PULL_RESISTOR_BITS;
switch (Rp) {
case 0:
LL_UCPD_SetRpResistor(UCPD1, LL_UCPD_RESISTOR_NONE);
break;
case 1:
LL_UCPD_SetRpResistor(UCPD1, LL_UCPD_RESISTOR_DEFAULT);
break;
case 2:
LL_UCPD_SetRpResistor(UCPD1, LL_UCPD_RESISTOR_1_5A);
break;
case 3:
LL_UCPD_SetRpResistor(UCPD1, LL_UCPD_RESISTOR_3_0A);
}
}
void set_empty_print(bool e)
{
model.empty_print = e;
}
void set_sleep_time(uint32_t st)
{
model.sleep_time = st;
}
void set_auto_stop(bool s)
{
model.auto_stop = s;
}
void print_status(char *ret, int i)
{
switch (i) {
case 0:
sprintf(ret, "Twinkie status: %s", model.start ? "on" : "off");
break;
case 1:
sprintf(ret, "Current packet number: %i", model.packet.header.sequence);
break;
}
}
#define CC_VOLTAGE_LOW 500
#define CC_VOLTAGE_HIGH 2000
static void model_thread(void *arg1, void *arg2, void *arg3)
{
int32_t vbus_v, vbus_c;
int32_t vcon_c;
int32_t cc1_v, cc2_v;
struct model_t *sm = (struct model_t *)arg1;
while (1) {
if (sm->start) {
sm->packet.header.sequence++;
if (sm->packet.header.sequence % 10 == 0) {
meas_vbus_v(&vbus_v);
meas_vbus_c(&vbus_c);
meas_cc1_v(&cc1_v);
meas_cc2_v(&cc2_v);
meas_vcon_c(&vcon_c);
/* because the twinkie itself is a port, detecting a
* valid connection through the UCPD line will cause
* false positives. e.g. if the line being snooped is a
* source to source connection and the twinkie is set as
* a sink, the twinkie UCPD will incorrectly detect a
* valid connection. So the connection has to be
* detected using the ADC pins.
*/
if ((cc1_v < CC_VOLTAGE_HIGH) && (cc1_v > CC_VOLTAGE_LOW)) {
/*connect to non-active line if the active line is not set
* to view*/
if (get_view_snoop() & CC1_CHANNEL_BIT) {
LL_UCPD_SetCCPin(UCPD1, LL_UCPD_CCPIN_CC1);
} else {
LL_UCPD_SetCCPin(UCPD1, LL_UCPD_CCPIN_CC2);
}
view_set_connection(CC1_CHANNEL_BIT);
pd_line = 1;
} else if ((cc2_v < CC_VOLTAGE_HIGH) && (cc2_v > CC_VOLTAGE_LOW)) {
/*connect to non-active line if the active line is not set
* to view*/
if (get_view_snoop() & CC2_CHANNEL_BIT) {
LL_UCPD_SetCCPin(UCPD1, LL_UCPD_CCPIN_CC2);
} else {
LL_UCPD_SetCCPin(UCPD1, LL_UCPD_CCPIN_CC1);
}
view_set_connection(CC2_CHANNEL_BIT);
pd_line = 2;
} else {
view_set_connection(0);
}
}
if (sm->packet.header.sequence % 10 < 8) {
sm->packet.header.vbus_voltage = vbus_v;
sm->packet.header.vbus_current = vbus_c;
sm->packet.header.cc1_voltage = cc1_v;
sm->packet.header.cc2_voltage = cc2_v;
sm->packet.header.vcon_current = vcon_c;
/* put pd message in the packet if any are stored */
if (sm->mw != sm->mr) {
sm->packet.header.packet_type = sm->sop[sm->mr];
sm->packet.header.data_len = sm->mod_size[sm->mr];
memcpy(sm->packet.data, sm->mod_buff[sm->mr],
sm->mod_size[sm->mr]);
memset((uint8_t *)&sm->sop[sm->mr], 0, sizeof(uint16_t));
sm->mod_size[sm->mr] = 0;
memset(sm->mod_buff[sm->mr], 0, PD_SAMPLES);
sm->mr++;
if (sm->mr == MOD_BUFFERS) {
sm->mr = 0;
}
}
if (sm->empty_print || sm->packet.header.data_len != 0) {
int stop_timer = 0;
for (int i = 0, w = 0; i < PACKET_BYTE_SIZE; i += w) {
w = uart_fifo_fill(sm->dev,
(const uint8_t *)&sm->packet + i,
PACKET_BYTE_SIZE - i);
if (w <= 0) {
stop_timer++;
k_usleep(sm->sleep_time);
if (stop_timer > 100 && sm->auto_stop) {
start_snooper(false);
break;
}
}
}
}
memset(sm->packet.data, 0, PD_SAMPLES);
sm->packet.header.data_len = 0;
}
}
if (sm->mw == sm->mr) {
k_usleep(sm->sleep_time);
}
}
}
#define MY_DEV_IRQ 12
#define MY_DEV_PRIO 2
#define MY_ISR_ARG NULL
#define MY_IRQ_FLAGS 0
void ucpd_isr(void)
{
struct model_t *sm = &model;
/* TypeCEvent flag currently not used */
if (LL_UCPD_IsActiveFlag_TypeCEventCC1(UCPD1) ||
LL_UCPD_IsActiveFlag_TypeCEventCC2(UCPD1)) {
LL_UCPD_ClearFlag_TypeCEventCC1(UCPD1);
LL_UCPD_ClearFlag_TypeCEventCC2(UCPD1);
}
if (LL_UCPD_IsActiveFlag_RxErr(UCPD1)) {
LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1);
LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, PD_SAMPLES);
memcpy(sm->mod_buff[sm->mw], sm->dma_buffer, PD_SAMPLES);
memset(sm->dma_buffer, 0, PD_SAMPLES);
sm->mod_size[sm->mw] = LL_UCPD_ReadRxPaySize(UCPD1);
sm->sop[sm->mr].type = LL_UCPD_ReadRxOrderSet(UCPD1);
sm->sop[sm->mw].polarity = pd_line;
sm->sop[sm->mw].partial = true;
sm->mw++;
if (sm->mw == MOD_BUFFERS) {
sm->mw = 0;
}
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1);
k_wakeup(sm->tid);
}
if (LL_UCPD_IsActiveFlag_RxMsgEnd(UCPD1)) {
LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1);
LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, PD_SAMPLES);
memcpy(sm->mod_buff[sm->mw], sm->dma_buffer, PD_SAMPLES);
memset(sm->dma_buffer, 0, PD_SAMPLES);
sm->mod_size[sm->mw] = LL_UCPD_ReadRxPaySize(UCPD1);
sm->sop[sm->mr].type = LL_UCPD_ReadRxOrderSet(UCPD1);
sm->sop[sm->mw].polarity = pd_line;
sm->sop[sm->mw].partial = false;
sm->mw++;
if (sm->mw == MOD_BUFFERS) {
sm->mw = 0;
}
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1);
k_wakeup(sm->tid);
LL_UCPD_ClearFlag_RxMsgEnd(UCPD1);
}
}
static void update_stm32g0x_cc_line(void)
{
SYSCFG->CFGR1 |= SYSCFG_CFGR1_UCPD1_STROBE_Msk;
}
enum pd_cc_t {
PD_OFF,
PD_BOTH,
PD_CC1,
PD_CC2
};
static void pd_on_cc(enum pd_cc_t p)
{
switch (p) {
case PD_OFF:
LL_UCPD_TypeCDetectionCC1Disable(UCPD1);
LL_UCPD_TypeCDetectionCC2Disable(UCPD1);
break;
case PD_BOTH:
LL_UCPD_TypeCDetectionCC1Enable(UCPD1);
LL_UCPD_TypeCDetectionCC2Enable(UCPD1);
break;
case PD_CC1:
LL_UCPD_TypeCDetectionCC2Disable(UCPD1);
LL_UCPD_TypeCDetectionCC1Enable(UCPD1);
break;
case PD_CC2:
LL_UCPD_TypeCDetectionCC1Disable(UCPD1);
LL_UCPD_TypeCDetectionCC2Enable(UCPD1);
break;
}
}
int model_init(const struct device *dev)
{
int ret;
model.dev = dev;
model.mw = 0;
model.mr = 0;
update_stm32g0x_cc_line();
/** Configure CC pins */
/* Set PIN A8 as analog */
LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_8, LL_GPIO_MODE_ANALOG);
/* Set PIN B15 as analog */
LL_GPIO_SetPinMode(GPIOB, LL_GPIO_PIN_15, LL_GPIO_MODE_ANALOG);
/** Configure DMA */
/* DMA CLOCK */
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1);
LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1);
LL_DMA_ConfigTransfer(DMA1, LL_DMA_CHANNEL_1, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
/* DMA from UCPD RXDR register */
LL_DMA_ConfigAddresses(DMA1, LL_DMA_CHANNEL_1, (uint32_t)&UCPD1->RXDR,
(uint32_t)model.dma_buffer, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MODE_NORMAL);
LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PERIPH_NOINCREMENT);
LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MEMORY_INCREMENT);
LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PDATAALIGN_BYTE);
LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MDATAALIGN_BYTE);
LL_DMA_SetChannelPriorityLevel(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PRIORITY_VERYHIGH);
LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, PD_SAMPLES);
LL_DMA_SetPeriphRequest(DMA1, LL_DMA_CHANNEL_1, LL_DMAMUX_REQ_UCPD1_RX);
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1);
model.packet.header.sequence = 0;
/** Configure UCPD */
ucpd_params.psc_ucpdclk = 0;
ucpd_params.transwin = 7;
ucpd_params.IfrGap = 16;
ucpd_params.HbitClockDiv = 26;
/*
* The UCPD port is disabled in the LL_UCPD_Init function
*
* NOTE: For proper Power Management operation, this function
* should not be used because it circumvents the zephyr
* clock API. Instead, DTS clock settings and the zephyr
* clock API should be used to enable clocks.
*/
ret = LL_UCPD_Init(UCPD1, &ucpd_params);
if (ret == SUCCESS) {
/* ORDSET */
LL_UCPD_SetRxOrderSet(UCPD1, LL_UCPD_ORDERSET_SOP | LL_UCPD_ORDERSET_SOP1 |
LL_UCPD_ORDERSET_SOP2 |
LL_UCPD_ORDERSET_HARDRST |
LL_UCPD_ORDERSET_CABLERST);
/* ENABLE DMA */
LL_UCPD_RxDMAEnable(UCPD1);
/* Enable UCPD port */
LL_UCPD_Enable(UCPD1);
start_snooper(false);
pd_on_cc(PD_BOTH);
update_stm32g0x_cc_line();
LL_UCPD_EnableIT_RxNE(UCPD1);
LL_UCPD_EnableIT_TypeCEventCC1(UCPD1);
LL_UCPD_EnableIT_TypeCEventCC2(UCPD1);
LL_UCPD_ClearFlag_TypeCEventCC1(UCPD1);
LL_UCPD_ClearFlag_TypeCEventCC2(UCPD1);
LL_UCPD_SetccEnable(UCPD1, LL_UCPD_CCENABLE_CC1CC2);
LL_UCPD_EnableIT_RxMsgEnd(UCPD1);
LL_UCPD_ClearFlag_RxMsgEnd(UCPD1);
LL_UCPD_RxEnable(UCPD1);
} else {
return -EIO;
}
LL_UCPD_SetSNKRole(UCPD1);
en_cc1(true);
en_cc2(true);
set_auto_stop(true);
set_empty_print(true);
set_sleep_time(500);
model.tid = k_thread_create(&model_thread_data, model_stack_area,
K_THREAD_STACK_SIZEOF(model_stack_area), model_thread, &model,
NULL, NULL, MODEL_THREAD_PRIORITY, 0, K_NO_WAIT);
return 0;
}