diff --git a/pocket-reform-sysctl-fw/CMakeLists.txt b/pocket-reform-sysctl-fw/CMakeLists.txt new file mode 100644 index 0000000..b3dcc5c --- /dev/null +++ b/pocket-reform-sysctl-fw/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 3.13) + +# initialize the SDK based on PICO_SDK_PATH +# note: this must happen before project() +include(pico_sdk_import.cmake) + +project(my_project) + +# initialize the Raspberry Pi Pico SDK +pico_sdk_init() + +# rest of your project + +add_executable(sysctl + sysctl.c +) + +target_link_libraries(sysctl pico_stdlib hardware_i2c) + +pico_enable_stdio_usb(sysctl 1) +pico_enable_stdio_uart(sysctl 0) + +pico_add_extra_outputs(sysctl) + diff --git a/pocket-reform-sysctl-fw/build.sh b/pocket-reform-sysctl-fw/build.sh new file mode 100755 index 0000000..0ef09b7 --- /dev/null +++ b/pocket-reform-sysctl-fw/build.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +export PICO_SDK_PATH=$PWD/../../pico-sdk + +mkdir -p build +cd build +cmake .. + +make + diff --git a/pocket-reform-sysctl-fw/flash.sh b/pocket-reform-sysctl-fw/flash.sh new file mode 100755 index 0000000..83a2e10 --- /dev/null +++ b/pocket-reform-sysctl-fw/flash.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +picotool load ./build/sysctl.uf2 + diff --git a/pocket-reform-sysctl-fw/fusb302b.h b/pocket-reform-sysctl-fw/fusb302b.h new file mode 100644 index 0000000..63e43b9 --- /dev/null +++ b/pocket-reform-sysctl-fw/fusb302b.h @@ -0,0 +1,260 @@ +/* + * PD Buddy Firmware Library - USB Power Delivery for everyone + * Copyright 2017-2018 Clayton G. Hobbs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PDB_FUSB302B_H +#define PDB_FUSB302B_H + +#include + +#include "pdb_msg.h" + +/* Device ID register */ +#define FUSB_DEVICE_ID 0x01 +#define FUSB_DEVICE_ID_VERSION_ID_SHIFT 4 +#define FUSB_DEVICE_ID_VERSION_ID (0xF << FUSB_DEVICE_ID_VERSION_ID_SHIFT) +#define FUSB_DEVICE_ID_PRODUCT_ID_SHIFT 2 +#define FUSB_DEVICE_ID_PRODUCT_ID (0x3 << FUSB_DEVICE_ID_PRODUCT_ID_SHIFT) +#define FUSB_DEVICE_ID_REVISION_ID_SHIFT 0 +#define FUSB_DEVICE_ID_REVISION_ID (0x3 << FUSB_DEVICE_ID_REVISION_ID_SHIFT) + +/* Switches0 register */ +#define FUSB_SWITCHES0 0x02 +#define FUSB_SWITCHES0_PU_EN2 (1 << 7) +#define FUSB_SWITCHES0_PU_EN1 (1 << 6) +#define FUSB_SWITCHES0_VCONN_CC2 (1 << 5) +#define FUSB_SWITCHES0_VCONN_CC1 (1 << 4) +#define FUSB_SWITCHES0_MEAS_CC2 (1 << 3) +#define FUSB_SWITCHES0_MEAS_CC1 (1 << 2) +#define FUSB_SWITCHES0_PDWN_2 (1 << 1) +#define FUSB_SWITCHES0_PDWN_1 1 + +/* Switches1 register */ +#define FUSB_SWITCHES1 0x03 +#define FUSB_SWITCHES1_POWERROLE (1 << 7) +#define FUSB_SWITCHES1_SPECREV_SHIFT 5 +#define FUSB_SWITCHES1_SPECREV (0x3 << FUSB_SWITCHES1_SPECREV_SHIFT) +#define FUSB_SWITCHES1_DATAROLE (1 << 4) +#define FUSB_SWITCHES1_AUTO_CRC (1 << 2) +#define FUSB_SWITCHES1_TXCC2 (1 << 1) +#define FUSB_SWITCHES1_TXCC1 1 + +/* Measure register */ +#define FUSB_MEASURE 0x04 +#define FUSB_MEASURE_MEAS_VBUS (1 << 6) +#define FUSB_MEASURE_MDAC_SHIFT 0 +#define FUSB_MEASURE_MDAC (0x3F << FUSB_MEASURE_MDAC_SHIFT) + +/* Slice register */ +#define FUSB_SLICE 0x05 +#define FUSB_SLICE_SDAC_HYS_SHIFT 6 +#define FUSB_SLICE_SDAC_HYS (0x3 << FUSB_SLICE_SDAC_HYS_SHIFT) +#define FUSB_SLICE_SDAC_SHIFT 0 +#define FUSB_SLICE_SDAC (0x3F << FUSB_SLICE_SDAC_SHIFT) + +/* Control0 register */ +#define FUSB_CONTROL0 0x06 +#define FUSB_CONTROL0_TX_FLUSH (1 << 6) +#define FUSB_CONTROL0_INT_MASK (1 << 5) +#define FUSB_CONTROL0_HOST_CUR_SHIFT 2 +#define FUSB_CONTROL0_HOST_CUR (0x3 << FUSB_CONTROL0_HOST_CUR_SHIFT) +#define FUSB_CONTROL0_AUTO_PRE (1 << 1) +#define FUSB_CONTROL0_TX_START 1 + +/* Control1 register */ +#define FUSB_CONTROL1 0x07 +#define FUSB_CONTROL1_ENSOP2DB (1 << 6) +#define FUSB_CONTROL1_ENSOP1DB (1 << 5) +#define FUSB_CONTROL1_BIST_MODE2 (1 << 4) +#define FUSB_CONTROL1_RX_FLUSH (1 << 2) +#define FUSB_CONTROL1_ENSOP2 (1 << 1) +#define FUSB_CONTROL1_ENSOP1 1 + +/* Control2 register */ +#define FUSB_CONTROL2 0x08 +#define FUSB_CONTROL2_TOG_SAVE_PWR_SHIFT 6 +#define FUSB_CONTROL2_TOG_SAVE_PWR (0x3 << FUSB_CONTROL2_TOG_SAVE_PWR) +#define FUSB_CONTROL2_TOG_RD_ONLY (1 << 5) +#define FUSB_CONTROL2_WAKE_EN (1 << 3) +#define FUSB_CONTROL2_MODE_SHIFT 1 +#define FUSB_CONTROL2_MODE (0x3 << FUSB_CONTROL2_MODE_SHIFT) +#define FUSB_CONTROL2_TOGGLE 1 + +/* Control3 register */ +#define FUSB_CONTROL3 0x09 +#define FUSB_CONTROL3_SEND_HARD_RESET (1 << 6) +#define FUSB_CONTROL3_BIST_TMODE (1 << 5) +#define FUSB_CONTROL3_AUTO_HARDRESET (1 << 4) +#define FUSB_CONTROL3_AUTO_SOFTRESET (1 << 3) +#define FUSB_CONTROL3_N_RETRIES_SHIFT 1 +#define FUSB_CONTROL3_N_RETRIES (0x3 << FUSB_CONTROL3_N_RETRIES_SHIFT) +#define FUSB_CONTROL3_AUTO_RETRY 1 + +/* Mask1 register */ +#define FUSB_MASK1 0x0A +#define FUSB_MASK1_M_VBUSOK (1 << 7) +#define FUSB_MASK1_M_ACTIVITY (1 << 6) +#define FUSB_MASK1_M_COMP_CHNG (1 << 5) +#define FUSB_MASK1_M_CRC_CHK (1 << 4) +#define FUSB_MASK1_M_ALERT (1 << 3) +#define FUSB_MASK1_M_WAKE (1 << 2) +#define FUSB_MASK1_M_COLLISION (1 << 1) +#define FUSB_MASK1_M_BC_LVL (1 << 0) + +/* Power register */ +#define FUSB_POWER 0x0B +#define FUSB_POWER_PWR3 (1 << 3) +#define FUSB_POWER_PWR2 (1 << 2) +#define FUSB_POWER_PWR1 (1 << 1) +#define FUSB_POWER_PWR0 1 + +/* Reset register */ +#define FUSB_RESET 0x0C +#define FUSB_RESET_PD_RESET (1 << 1) +#define FUSB_RESET_SW_RES 1 + +/* OCPreg register */ +#define FUSB_OCPREG 0x0D +#define FUSB_OCPREG_OCP_RANGE (1 << 3) +#define FUSB_OCPREG_OCP_CUR_SHIFT 0 +#define FUSB_OCPREG_OCP_CUR (0x7 << FUSB_OCPREG_OCP_CUR_SHIFT) + +/* Maska register */ +#define FUSB_MASKA 0x0E +#define FUSB_MASKA_M_OCP_TEMP (1 << 7) +#define FUSB_MASKA_M_TOGDONE (1 << 6) +#define FUSB_MASKA_M_SOFTFAIL (1 << 5) +#define FUSB_MASKA_M_RETRYFAIL (1 << 4) +#define FUSB_MASKA_M_HARDSENT (1 << 3) +#define FUSB_MASKA_M_TXSENT (1 << 2) +#define FUSB_MASKA_M_SOFTRST (1 << 1) +#define FUSB_MASKA_M_HARDRST 1 + +/* Maskb register */ +#define FUSB_MASKB 0x0F +#define FUSB_MASKB_M_GCRCSENT 1 + +/* Control4 register */ +#define FUSB_CONTROL4 0x10 +#define FUSB_CONTROL4_TOG_EXIT_AUD 1 + +/* Status0a register */ +#define FUSB_STATUS0A 0x3C +#define FUSB_STATUS0A_SOFTFAIL (1 << 5) +#define FUSB_STATUS0A_RETRYFAIL (1 << 4) +#define FUSB_STATUS0A_POWER3 (1 << 3) +#define FUSB_STATUS0A_POWER2 (1 << 2) +#define FUSB_STATUS0A_SOFTRST (1 << 1) +#define FUSB_STATUS0A_HARDRST 1 + +/* Status1a register */ +#define FUSB_STATUS1A 0x3D +#define FUSB_STATUS1A_TOGSS_SHIFT 3 +#define FUSB_STATUS1A_TOGSS (0x7 << FUSB_STATUS1A_TOGSS_SHIFT) +#define FUSB_STATUS1A_RXSOP2DB (1 << 2) +#define FUSB_STATUS1A_RXSOP1DB (1 << 1) +#define FUSB_STATUS1A_RXSOP 1 + +/* Interrupta register */ +#define FUSB_INTERRUPTA 0x3E +#define FUSB_INTERRUPTA_I_OCP_TEMP (1 << 7) +#define FUSB_INTERRUPTA_I_TOGDONE (1 << 6) +#define FUSB_INTERRUPTA_I_SOFTFAIL (1 << 5) +#define FUSB_INTERRUPTA_I_RETRYFAIL (1 << 4) +#define FUSB_INTERRUPTA_I_HARDSENT (1 << 3) +#define FUSB_INTERRUPTA_I_TXSENT (1 << 2) +#define FUSB_INTERRUPTA_I_SOFTRST (1 << 1) +#define FUSB_INTERRUPTA_I_HARDRST 1 + +/* Interruptb register */ +#define FUSB_INTERRUPTB 0x3F +#define FUSB_INTERRUPTB_I_GCRCSENT 1 + +/* Status0 register */ +#define FUSB_STATUS0 0x40 +#define FUSB_STATUS0_VBUSOK (1 << 7) +#define FUSB_STATUS0_ACTIVITY (1 << 6) +#define FUSB_STATUS0_COMP (1 << 5) +#define FUSB_STATUS0_CRC_CHK (1 << 4) +#define FUSB_STATUS0_ALERT (1 << 3) +#define FUSB_STATUS0_WAKE (1 << 2) +#define FUSB_STATUS0_BC_LVL_SHIFT 0 +#define FUSB_STATUS0_BC_LVL (0x3 << FUSB_STATUS0_BC_LVL_SHIFT) + +/* Status1 register */ +#define FUSB_STATUS1 0x41 +#define FUSB_STATUS1_RXSOP2 (1 << 7) +#define FUSB_STATUS1_RXSOP1 (1 << 6) +#define FUSB_STATUS1_RX_EMPTY (1 << 5) +#define FUSB_STATUS1_RX_FULL (1 << 4) +#define FUSB_STATUS1_TX_EMPTY (1 << 3) +#define FUSB_STATUS1_TX_FULL (1 << 2) +#define FUSB_STATUS1_OVRTEMP (1 << 1) +#define FUSB_STATUS1_OCP 1 + +/* Interrupt register */ +#define FUSB_INTERRUPT 0x42 +#define FUSB_INTERRUPT_I_VBUSOK (1 << 7) +#define FUSB_INTERRUPT_I_ACTIVITY (1 << 6) +#define FUSB_INTERRUPT_I_COMP_CHNG (1 << 5) +#define FUSB_INTERRUPT_I_CRC_CHK (1 << 4) +#define FUSB_INTERRUPT_I_ALERT (1 << 3) +#define FUSB_INTERRUPT_I_WAKE (1 << 2) +#define FUSB_INTERRUPT_I_COLLISION (1 << 1) +#define FUSB_INTERRUPT_I_BC_LVL 1 + +/* FIFOs register */ +#define FUSB_FIFOS 0x43 + +#define FUSB_FIFO_TX_TXON 0xA1 +#define FUSB_FIFO_TX_SOP1 0x12 +#define FUSB_FIFO_TX_SOP2 0x13 +#define FUSB_FIFO_TX_SOP3 0x1B +#define FUSB_FIFO_TX_RESET1 0x15 +#define FUSB_FIFO_TX_RESET2 0x16 +#define FUSB_FIFO_TX_PACKSYM 0x80 +#define FUSB_FIFO_TX_JAM_CRC 0xFF +#define FUSB_FIFO_TX_EOP 0x14 +#define FUSB_FIFO_TX_TXOFF 0xFE + +#define FUSB_FIFO_RX_TOKEN_BITS 0xE0 +#define FUSB_FIFO_RX_SOP 0xE0 +#define FUSB_FIFO_RX_SOP1 0xC0 +#define FUSB_FIFO_RX_SOP2 0xA0 +#define FUSB_FIFO_RX_SOP1DB 0x80 +#define FUSB_FIFO_RX_SOP2DB 0x60 + + +/* + * FUSB status union + * + * Provides a nicer structure than just an array of uint8_t for working with + * the FUSB302B status and interrupt flags. + */ +union fusb_status { + uint8_t bytes[7]; + struct { + uint8_t status0a; + uint8_t status1a; + uint8_t interrupta; + uint8_t interruptb; + uint8_t status0; + uint8_t status1; + uint8_t interrupt; + }; +}; + +#endif /* PDB_FUSB302B_H */ diff --git a/pocket-reform-sysctl-fw/pd.h b/pocket-reform-sysctl-fw/pd.h new file mode 100644 index 0000000..4eb1259 --- /dev/null +++ b/pocket-reform-sysctl-fw/pd.h @@ -0,0 +1,394 @@ +/* + * PD Buddy Firmware Library - USB Power Delivery for everyone + * Copyright 2017-2018 Clayton G. Hobbs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PDB_PD_H +#define PDB_PD_H + +#include + +/* + * Macros for working with USB Power Delivery messages. + * + * This file is mostly written from the PD Rev. 2.0 spec, but the header is + * written from the Rev. 3.0 spec. + */ + +/* + * PD Header + */ +#define PD_HDR_MSGTYPE_SHIFT 0 +#define PD_HDR_MSGTYPE (0x1F << PD_HDR_MSGTYPE_SHIFT) +#define PD_HDR_DATAROLE_SHIFT 5 +#define PD_HDR_DATAROLE (0x1 << PD_HDR_DATAROLE_SHIFT) +#define PD_HDR_SPECREV_SHIFT 6 +#define PD_HDR_SPECREV (0x3 << PD_HDR_SPECREV_SHIFT) +#define PD_HDR_POWERROLE_SHIFT 8 +#define PD_HDR_POWERROLE (1 << PD_HDR_POWERROLE_SHIFT) +#define PD_HDR_MESSAGEID_SHIFT 9 +#define PD_HDR_MESSAGEID (0x7 << PD_HDR_MESSAGEID_SHIFT) +#define PD_HDR_NUMOBJ_SHIFT 12 +#define PD_HDR_NUMOBJ (0x7 << PD_HDR_NUMOBJ_SHIFT) +#define PD_HDR_EXT (1 << 15) + +/* Message types */ +#define PD_MSGTYPE_GET(msg) (((msg)->hdr & PD_HDR_MSGTYPE) >> PD_HDR_MSGTYPE_SHIFT) +/* Control Message */ +#define PD_MSGTYPE_GOODCRC 0x01 +#define PD_MSGTYPE_GOTOMIN 0x02 +#define PD_MSGTYPE_ACCEPT 0x03 +#define PD_MSGTYPE_REJECT 0x04 +#define PD_MSGTYPE_PING 0x05 +#define PD_MSGTYPE_PS_RDY 0x06 +#define PD_MSGTYPE_GET_SOURCE_CAP 0x07 +#define PD_MSGTYPE_GET_SINK_CAP 0x08 +#define PD_MSGTYPE_DR_SWAP 0x09 +#define PD_MSGTYPE_PR_SWAP 0x0A +#define PD_MSGTYPE_VCONN_SWAP 0x0B +#define PD_MSGTYPE_WAIT 0x0C +#define PD_MSGTYPE_SOFT_RESET 0x0D +#define PD_MSGTYPE_NOT_SUPPORTED 0x10 +#define PD_MSGTYPE_GET_SOURCE_CAP_EXTENDED 0x11 +#define PD_MSGTYPE_GET_STATUS 0x12 +#define PD_MSGTYPE_FR_SWAP 0x13 +#define PD_MSGTYPE_GET_PPS_STATUS 0x14 +#define PD_MSGTYPE_GET_COUNTRY_CODES 0x15 +/* Data Message */ +#define PD_MSGTYPE_SOURCE_CAPABILITIES 0x01 +#define PD_MSGTYPE_REQUEST 0x02 +#define PD_MSGTYPE_BIST 0x03 +#define PD_MSGTYPE_SINK_CAPABILITIES 0x04 +#define PD_MSGTYPE_BATTERY_STATUS 0x05 +#define PD_MSGTYPE_ALERT 0x06 +#define PD_MSGTYPE_GET_COUNTRY_INFO 0x07 +#define PD_MSGTYPE_VENDOR_DEFINED 0x0F +/* Extended Message */ +#define PD_MSGTYPE_SOURCE_CAPABILITIES_EXTENDED 0x01 +#define PD_MSGTYPE_STATUS 0x02 +#define PD_MSGTYPE_GET_BATTERY_CAP 0x03 +#define PD_MSGTYPE_GET_BATTERY_STATUS 0x04 +#define PD_MSGTYPE_BATTERY_CAPABILITIES 0x05 +#define PD_MSGTYPE_GET_MANUFACTURER_INFO 0x06 +#define PD_MSGTYPE_MANUFACTURER_INFO 0x07 +#define PD_MSGTYPE_SECURITY_REQUEST 0x08 +#define PD_MSGTYPE_SECURITY_RESPONSE 0x09 +#define PD_MSGTYPE_FIRMWARE_UPDATE_REQUEST 0x0A +#define PD_MSGTYPE_FIRMWARE_UPDATE_RESPONSE 0x0B +#define PD_MSGTYPE_PPS_STATUS 0x0C +#define PD_MSGTYPE_COUNTRY_INFO 0x0D +#define PD_MSGTYPE_COUNTRY_CODES 0x0E + +/* Data roles */ +#define PD_DATAROLE_UFP (0x0 << PD_HDR_DATAROLE_SHIFT) +#define PD_DATAROLE_DFP (0x1 << PD_HDR_DATAROLE_SHIFT) + +/* Specification revisions */ +#define PD_SPECREV_1_0 (0x0 << PD_HDR_SPECREV_SHIFT) +#define PD_SPECREV_2_0 (0x1 << PD_HDR_SPECREV_SHIFT) +#define PD_SPECREV_3_0 (0x2 << PD_HDR_SPECREV_SHIFT) + +/* Port power roles */ +#define PD_POWERROLE_SINK (0x0 << PD_HDR_POWERROLE_SHIFT) +#define PD_POWERROLE_SOURCE (0x1 << PD_HDR_POWERROLE_SHIFT) + +/* Message ID */ +#define PD_MESSAGEID_GET(msg) (((msg)->hdr & PD_HDR_MESSAGEID) >> PD_HDR_MESSAGEID_SHIFT) + +/* Number of data objects */ +#define PD_NUMOBJ(n) (((n) << PD_HDR_NUMOBJ_SHIFT) & PD_HDR_NUMOBJ) +#define PD_NUMOBJ_GET(msg) (((msg)->hdr & PD_HDR_NUMOBJ) >> PD_HDR_NUMOBJ_SHIFT) + + +/* + * PD Extended Message Header + */ +#define PD_EXTHDR_DATA_SIZE_SHIFT 0 +#define PD_EXTHDR_DATA_SIZE (0x1FF << PD_EXTHDR_DATA_SIZE_SHIFT) +#define PD_EXTHDR_REQUEST_CHUNK_SHIFT 10 +#define PD_EXTHDR_REQUEST_CHUNK (1 << PD_EXTHDR_REQUEST_CHUNK_SHIFT) +#define PD_EXTHDR_CHUNK_NUMBER_SHIFT 11 +#define PD_EXTHDR_CHUNK_NUMBER (0xF << PD_EXTHDR_CHUNK_NUMBER_SHIFT) +#define PD_EXTHDR_CHUNKED_SHIFT 15 +#define PD_EXTHDR_CHUNKED (1 << PD_EXTHDR_CHUNKED_SHIFT) + +/* Data size */ +#define PD_DATA_SIZE(n) (((n) << PD_EXTHDR_DATA_SIZE_SHIFT) & PD_EXTHDR_DATA_SIZE) +#define PD_DATA_SIZE_GET(msg) (((msg)->exthdr & PD_EXTHDR_DATA_SIZE) >> PD_EXTHDR_DATA_SIZE_SHIFT) + +/* Chunk number */ +#define PD_CHUNK_NUMBER(n) (((n) << PD_EXTHDR_CHUNK_NUMBER_SHIFT) & PD_EXTHDR_CHUNK_NUMBER) +#define PD_CHUNK_NUMBER_GET(msg) (((msg)->exthdr & PD_EXTHDR_CHUNK_NUMBER) >> PD_EXTHDR_CHUNK_NUMBER_SHIFT) + + +/* + * PD Power Data Object + */ +#define PD_PDO_TYPE_SHIFT 30 +#define PD_PDO_TYPE (0x3 << PD_PDO_TYPE_SHIFT) + +/* PDO types */ +#define PD_PDO_TYPE_FIXED ((unsigned) (0x0 << PD_PDO_TYPE_SHIFT)) +#define PD_PDO_TYPE_BATTERY ((unsigned) (0x1 << PD_PDO_TYPE_SHIFT)) +#define PD_PDO_TYPE_VARIABLE ((unsigned) (0x2 << PD_PDO_TYPE_SHIFT)) +#define PD_PDO_TYPE_AUGMENTED ((unsigned) (0x3 << PD_PDO_TYPE_SHIFT)) + +#define PD_APDO_TYPE_SHIFT 28 +#define PD_APDO_TYPE (0x3 << PD_APDO_TYPE_SHIFT) + +/* APDO types */ +#define PD_APDO_TYPE_PPS (0x0 << PD_APDO_TYPE_SHIFT) + +/* PD Source Fixed PDO */ +#define PD_PDO_SRC_FIXED_DUAL_ROLE_PWR_SHIFT 29 +#define PD_PDO_SRC_FIXED_DUAL_ROLE_PWR (1 << PD_PDO_SRC_FIXED_DUAL_ROLE_PWR_SHIFT) +#define PD_PDO_SRC_FIXED_USB_SUSPEND_SHIFT 28 +#define PD_PDO_SRC_FIXED_USB_SUSPEND (1 << PD_PDO_SRC_FIXED_USB_SUSPEND_SHIFT) +#define PD_PDO_SRC_FIXED_UNCONSTRAINED_SHIFT 27 +#define PD_PDO_SRC_FIXED_UNCONSTRAINED (1 << PD_PDO_SRC_FIXED_UNCONSTRAINED_SHIFT) +#define PD_PDO_SRC_FIXED_USB_COMMS_SHIFT 26 +#define PD_PDO_SRC_FIXED_USB_COMMS (1 << PD_PDO_SRC_FIXED_USB_COMMS_SHIFT) +#define PD_PDO_SRC_FIXED_DUAL_ROLE_DATA_SHIFT 25 +#define PD_PDO_SRC_FIXED_DUAL_ROLE_DATA (1 << PD_PDO_SRC_FIXED_DUAL_ROLE_DATA_SHIFT) +#define PD_PDO_SRC_FIXED_UNCHUNKED_EXT_MSG_SHIFT 24 +#define PD_PDO_SRC_FIXED_UNCHUNKED_EXT_MSG (1 << PD_PDO_SRC_FIXED_UNCHUNKED_EXT_MSG_SHIFT) +#define PD_PDO_SRC_FIXED_PEAK_CURRENT_SHIFT 20 +#define PD_PDO_SRC_FIXED_PEAK_CURRENT (0x3 << PD_PDO_SRC_FIXED_PEAK_CURRENT_SHIFT) +#define PD_PDO_SRC_FIXED_VOLTAGE_SHIFT 10 +#define PD_PDO_SRC_FIXED_VOLTAGE (0x3FF << PD_PDO_SRC_FIXED_VOLTAGE_SHIFT) +#define PD_PDO_SRC_FIXED_CURRENT_SHIFT 0 +#define PD_PDO_SRC_FIXED_CURRENT (0x3FF << PD_PDO_SRC_FIXED_CURRENT_SHIFT) + +/* PD Source Fixed PDO current */ +#define PD_PDO_SRC_FIXED_CURRENT_GET(pdo) (((pdo) & PD_PDO_SRC_FIXED_CURRENT) >> PD_PDO_SRC_FIXED_CURRENT_SHIFT) + +/* PD Source Fixed PDO voltage */ +#define PD_PDO_SRC_FIXED_VOLTAGE_GET(pdo) (((pdo) & PD_PDO_SRC_FIXED_VOLTAGE) >> PD_PDO_SRC_FIXED_VOLTAGE_SHIFT) + +/* PD Programmable Power Supply APDO */ +#define PD_APDO_PPS_MAX_VOLTAGE_SHIFT 17 +#define PD_APDO_PPS_MAX_VOLTAGE (0xFF << PD_APDO_PPS_MAX_VOLTAGE_SHIFT) +#define PD_APDO_PPS_MIN_VOLTAGE_SHIFT 8 +#define PD_APDO_PPS_MIN_VOLTAGE (0xFF << PD_APDO_PPS_MIN_VOLTAGE_SHIFT) +#define PD_APDO_PPS_CURRENT_SHIFT 0 +#define PD_APDO_PPS_CURRENT (0x7F << PD_APDO_PPS_CURRENT_SHIFT) + +/* PD Programmable Power Supply APDO voltages */ +#define PD_APDO_PPS_MAX_VOLTAGE_GET(pdo) (((pdo) & PD_APDO_PPS_MAX_VOLTAGE) >> PD_APDO_PPS_MAX_VOLTAGE_SHIFT) +#define PD_APDO_PPS_MIN_VOLTAGE_GET(pdo) (((pdo) & PD_APDO_PPS_MIN_VOLTAGE) >> PD_APDO_PPS_MIN_VOLTAGE_SHIFT) + +#define PD_APDO_PPS_MAX_VOLTAGE_SET(v) (((v) << PD_APDO_PPS_MAX_VOLTAGE_SHIFT) & PD_APDO_PPS_MAX_VOLTAGE) +#define PD_APDO_PPS_MIN_VOLTAGE_SET(v) (((v) << PD_APDO_PPS_MIN_VOLTAGE_SHIFT) & PD_APDO_PPS_MIN_VOLTAGE) + +/* PD Programmable Power Supply APDO current */ +#define PD_APDO_PPS_CURRENT_GET(pdo) ((uint8_t) (((pdo) & PD_APDO_PPS_CURRENT) >> PD_APDO_PPS_CURRENT_SHIFT)) + +#define PD_APDO_PPS_CURRENT_SET(i) (((i) << PD_APDO_PPS_CURRENT_SHIFT) & PD_APDO_PPS_CURRENT) + +/* TODO: other types of source PDO */ + +/* PD Sink Fixed PDO */ +#define PD_PDO_SNK_FIXED_DUAL_ROLE_PWR_SHIFT 29 +#define PD_PDO_SNK_FIXED_DUAL_ROLE_PWR (1 << PD_PDO_SNK_FIXED_DUAL_ROLE_PWR_SHIFT) +#define PD_PDO_SNK_FIXED_HIGHER_CAP_SHIFT 28 +#define PD_PDO_SNK_FIXED_HIGHER_CAP (1 << PD_PDO_SNK_FIXED_HIGHER_CAP_SHIFT) +#define PD_PDO_SNK_FIXED_UNCONSTRAINED_SHIFT 27 +#define PD_PDO_SNK_FIXED_UNCONSTRAINED (1 << PD_PDO_SNK_FIXED_UNCONSTRAINED_SHIFT) +#define PD_PDO_SNK_FIXED_USB_COMMS_SHIFT 26 +#define PD_PDO_SNK_FIXED_USB_COMMS (1 << PD_PDO_SNK_FIXED_USB_COMMS_SHIFT) +#define PD_PDO_SNK_FIXED_DUAL_ROLE_DATA_SHIFT 25 +#define PD_PDO_SNK_FIXED_DUAL_ROLE_DATA (1 << PD_PDO_SNK_FIXED_DUAL_ROLE_DATA_SHIFT) +#define PD_PDO_SNK_FIXED_VOLTAGE_SHIFT 10 +#define PD_PDO_SNK_FIXED_VOLTAGE (0x3FF << PD_PDO_SNK_FIXED_VOLTAGE_SHIFT) +#define PD_PDO_SNK_FIXED_CURRENT_SHIFT 0 +#define PD_PDO_SNK_FIXED_CURRENT (0x3FF << PD_PDO_SNK_FIXED_CURRENT_SHIFT) + +/* PD Sink Fixed PDO current */ +#define PD_PDO_SNK_FIXED_CURRENT_SET(i) (((i) << PD_PDO_SNK_FIXED_CURRENT_SHIFT) & PD_PDO_SNK_FIXED_CURRENT) + +/* PD Sink Fixed PDO voltage */ +#define PD_PDO_SNK_FIXED_VOLTAGE_SET(v) (((v) << PD_PDO_SNK_FIXED_VOLTAGE_SHIFT) & PD_PDO_SNK_FIXED_VOLTAGE) + +/* TODO: other types of sink PDO */ + + +/* + * PD Request Data Object + */ +#define PD_RDO_OBJPOS_SHIFT 28 +#define PD_RDO_OBJPOS (0x7 << PD_RDO_OBJPOS_SHIFT) +#define PD_RDO_GIVEBACK_SHIFT 27 +#define PD_RDO_GIVEBACK (1 << PD_RDO_GIVEBACK_SHIFT) +#define PD_RDO_CAP_MISMATCH_SHIFT 26 +#define PD_RDO_CAP_MISMATCH (1 << PD_RDO_CAP_MISMATCH_SHIFT) +#define PD_RDO_USB_COMMS_SHIFT 25 +#define PD_RDO_USB_COMMS (1 << PD_RDO_USB_COMMS_SHIFT) +#define PD_RDO_NO_USB_SUSPEND_SHIFT 24 +#define PD_RDO_NO_USB_SUSPEND (1 << PD_RDO_NO_USB_SUSPEND_SHIFT) +#define PD_RDO_UNCHUNKED_EXT_MSG_SHIFT 23 +#define PD_RDO_UNCHUNKED_EXT_MSG (1 << PD_RDO_UNCHUNKED_EXT_MSG_SHIFT) + +#define PD_RDO_OBJPOS_SET(i) (((i) << PD_RDO_OBJPOS_SHIFT) & PD_RDO_OBJPOS) +#define PD_RDO_OBJPOS_GET(msg) (((msg)->obj[0] & PD_RDO_OBJPOS) >> PD_RDO_OBJPOS_SHIFT) + +/* Fixed and Variable RDO, no GiveBack support */ +#define PD_RDO_FV_CURRENT_SHIFT 10 +#define PD_RDO_FV_CURRENT (0x3FF << PD_RDO_FV_CURRENT_SHIFT) +#define PD_RDO_FV_MAX_CURRENT_SHIFT 0 +#define PD_RDO_FV_MAX_CURRENT (0x3FF << PD_RDO_FV_MAX_CURRENT_SHIFT) + +#define PD_RDO_FV_CURRENT_SET(i) (((i) << PD_RDO_FV_CURRENT_SHIFT) & PD_RDO_FV_CURRENT) +#define PD_RDO_FV_MAX_CURRENT_SET(i) (((i) << PD_RDO_FV_MAX_CURRENT_SHIFT) & PD_RDO_FV_MAX_CURRENT) + +/* Fixed and Variable RDO with GiveBack support */ +#define PD_RDO_FV_MIN_CURRENT_SHIFT 0 +#define PD_RDO_FV_MIN_CURRENT (0x3FF << PD_RDO_FV_MIN_CURRENT_SHIFT) + +#define PD_RDO_FV_MIN_CURRENT_SET(i) (((i) << PD_RDO_FV_MIN_CURRENT_SHIFT) & PD_RDO_FV_MIN_CURRENT) + +/* TODO: Battery RDOs */ + +/* Programmable RDO */ +#define PD_RDO_PROG_VOLTAGE_SHIFT 9 +#define PD_RDO_PROG_VOLTAGE (0x7FF << PD_RDO_PROG_VOLTAGE_SHIFT) +#define PD_RDO_PROG_CURRENT_SHIFT 0 +#define PD_RDO_PROG_CURRENT (0x7F << PD_RDO_PROG_CURRENT_SHIFT) + +#define PD_RDO_PROG_VOLTAGE_SET(i) (((i) << PD_RDO_PROG_VOLTAGE_SHIFT) & PD_RDO_PROG_VOLTAGE) +#define PD_RDO_PROG_CURRENT_SET(i) (((i) << PD_RDO_PROG_CURRENT_SHIFT) & PD_RDO_PROG_CURRENT) + + +/* + * Time values + * + * Where a range is specified, the middle of the range (rounded down to the + * nearest millisecond) is used. + */ +#define PD_T_CHUNKING_NOT_SUPPORTED TIME_MS2I(45) +#define PD_T_HARD_RESET_COMPLETE TIME_MS2I(4) +#define PD_T_PS_TRANSITION TIME_MS2I(500) +#define PD_T_SENDER_RESPONSE TIME_MS2I(27) +#define PD_T_SINK_REQUEST TIME_MS2I(100) +#define PD_T_TYPEC_SINK_WAIT_CAP TIME_MS2I(465) +#define PD_T_PPS_REQUEST TIME_S2I(10) +/* This is actually from Type-C, not Power Delivery, but who cares? */ +#define PD_T_PD_DEBOUNCE TIME_MS2I(15) + + +/* + * Counter maximums + */ +#define PD_N_HARD_RESET_COUNT 2 + + +/* + * Value parameters + */ +#define PD_MAX_EXT_MSG_LEN 260 +#define PD_MAX_EXT_MSG_CHUNK_LEN 26 +#define PD_MAX_EXT_MSG_LEGACY_LEN 26 + + +/* + * Unit conversions + * + * V: volt + * CV: centivolt + * MV: millivolt + * PRV: Programmable RDO voltage unit (20 mV) + * PDV: Power Delivery voltage unit (50 mV) + * PAV: PPS APDO voltage unit (100 mV) + * + * A: ampere + * CA: centiampere + * MA: milliampere + * PDI: Power Delivery current unit (10 mA) + * PAI: PPS APDO current unit (50 mA) + * + * W: watt + * CW: centiwatt + * MW: milliwatt + * + * O: ohm + * CO: centiohm + * MO: milliohm + */ +#define PD_MV2PRV(mv) ((mv) / 20) +#define PD_MV2PDV(mv) ((mv) / 50) +#define PD_MV2PAV(mv) ((mv) / 100) +#define PD_PRV2MV(prv) ((prv) * 20) +#define PD_PDV2MV(pdv) ((pdv) * 50) +#define PD_PAV2MV(pav) ((pav) * 100) + +#define PD_MA2CA(ma) (((ma) + 10 - 1) / 10) +#define PD_MA2PDI(ma) (((ma) + 10 - 1) / 10) +#define PD_MA2PAI(ma) (((ma) + 50 - 1) / 50) +#define PD_CA2PAI(ca) (((ca) + 5 - 1) / 5) +#define PD_PDI2MA(pdi) ((pdi) * 10) +#define PD_PAI2MA(pai) ((pai) * 50) +#define PD_PAI2CA(pai) ((pai) * 5) + +#define PD_MW2CW(mw) ((mw) / 10) + +#define PD_MO2CO(mo) ((mo) / 10) + +/* Get portions of a voltage in more normal units */ +#define PD_MV_V(mv) ((mv) / 1000) +#define PD_MV_MV(mv) ((mv) % 1000) + +#define PD_PDV_V(pdv) ((pdv) / 20) +#define PD_PDV_CV(pdv) (5 * ((pdv) % 20)) + +#define PD_PAV_V(pav) ((pav) / 10) +#define PD_PAV_CV(pav) (10 * ((pav) % 10)) + +/* Get portions of a PD current in more normal units */ +#define PD_PDI_A(pdi) ((pdi) / 100) +#define PD_PDI_CA(pdi) ((pdi) % 100) + +#define PD_PAI_A(pai) ((pai) / 20) +#define PD_PAI_CA(pai) (5 * ((pai) % 20)) + +/* Get portions of a power in more normal units */ +#define PD_CW_W(cw) ((cw) / 100) +#define PD_CW_CW(cw) ((cw) % 100) + +/* Get portions of a resistance in more normal units */ +#define PD_CO_O(co) ((co) / 100) +#define PD_CO_CO(co) ((co) % 100) + +/* + * Unit constants + */ +#define PD_MV_MIN 0 +#define PD_MV_MAX 21000 +#define PD_PDV_MIN PD_MV2PDV(PD_MV_MIN) +#define PD_PDV_MAX PD_MV2PDV(PD_MV_MAX) + +#define PD_MA_MIN 0 +#define PD_MA_MAX 5000 +#define PD_CA_MIN PD_MA2CA(PD_MA_MIN) +#define PD_CA_MAX PD_MA2CA(PD_MA_MAX) +#define PD_PDI_MIN PD_MA2PDI(PD_MA_MIN) +#define PD_PDI_MAX PD_MA2PDI(PD_MA_MAX) + +#define PD_MW_MIN 0 +#define PD_MW_MAX 100000 + +#define PD_MO_MIN 500 +#define PD_MO_MAX 655350 + + +#endif /* PDB_PD_H */ diff --git a/pocket-reform-sysctl-fw/pdb_msg.h b/pocket-reform-sysctl-fw/pdb_msg.h new file mode 100644 index 0000000..ed508e1 --- /dev/null +++ b/pocket-reform-sysctl-fw/pdb_msg.h @@ -0,0 +1,52 @@ +/* + * PD Buddy Firmware Library - USB Power Delivery for everyone + * Copyright 2017-2018 Clayton G. Hobbs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PDB_MSG_H +#define PDB_MSG_H + +#include + +/* + * PD message union + * + * This can be safely read from or written to in any form without any + * transformations because everything in the system is little-endian. + * + * Two bytes of padding are required at the start to prevent problems due to + * alignment. Specifically, without the padding, &obj[0] != &bytes[2], making + * the statement in the previous paragraph invalid. + */ +union pd_msg { + struct { + uint8_t _pad1[2]; + uint8_t bytes[30]; + } __attribute__((packed)); + struct { + uint8_t _pad2[2]; + uint16_t hdr; + union { + uint32_t obj[7]; + struct { + uint16_t exthdr; + uint8_t data[26]; + }; + }; + } __attribute__((packed)); +}; + + +#endif /* PDB_MSG_H */ diff --git a/pocket-reform-sysctl-fw/pico_sdk_import.cmake b/pocket-reform-sysctl-fw/pico_sdk_import.cmake new file mode 100644 index 0000000..28efe9e --- /dev/null +++ b/pocket-reform-sysctl-fw/pico_sdk_import.cmake @@ -0,0 +1,62 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG master + ) + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + FetchContent_Populate(pico_sdk) + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/pocket-reform-sysctl-fw/sysctl.c b/pocket-reform-sysctl-fw/sysctl.c new file mode 100644 index 0000000..9ed6251 --- /dev/null +++ b/pocket-reform-sysctl-fw/sysctl.c @@ -0,0 +1,969 @@ +#include +#include "pico/stdlib.h" +#include "pico/binary_info.h" +#include "hardware/i2c.h" +#include "hardware/irq.h" +#include "fusb302b.h" +#include "pd.h" + +#define FW_REV "PREF1SYS D1 20220824" + +#define PIN_SDA 0 +#define PIN_SCL 1 + +#define PIN_DISP_RESET 2 +#define PIN_FLIGHTMODE 3 +#define PIN_KBD_UART_TX 4 +#define PIN_KBD_UART_RX 5 +#define PIN_WOWWAN 6 +#define PIN_DISP_EN 7 +#define PIN_SOM_MOSI 8 +#define PIN_SOM_SS0 9 +#define PIN_SOM_SCK 10 +#define PIN_SOM_MISO 11 +#define PIN_SOM_UART_TX 12 +#define PIN_SOM_UART_RX 13 +#define PIN_FUSB_INT 14 +#define PIN_LED_B 15 +#define PIN_LED_R 16 +#define PIN_LED_G 17 +#define PIN_SOM_WAKE 19 +#define PIN_1V1_ENABLE 23 +#define PIN_3V3_ENABLE 24 +#define PIN_5V_ENABLE 25 +#define PIN_USB_SRC_ENABLE 28 + +// FUSB302B USB-PD controller +#define FUSB_ADDR 0x22 +// MAX17320 protector/balancer +// https://datasheets.maximintegrated.com/en/ds/MAX17320.pdf +#define MAX_ADDR1 0x36 +#define MAX_ADDR2 0x0b +// MP2762A charger +// https://www.monolithicpower.com/en/documentview/productdocument/index/version/2/document_type/Datasheet/lang/en/sku/MP2762AGV/document_id/9073/ +#define MPS_ADDR 0x5c + +void i2c_scan() { + printf("\nI2C Scan\n"); + printf(" 0 1 2 3 4 5 6 7 8 9 A B C D E F\n"); + + for (int addr = 0; addr < (1 << 7); ++addr) { + if (addr % 16 == 0) { + printf("%02x ", addr); + } + + int ret; + uint8_t rxdata; + ret = i2c_read_blocking(i2c0, addr, &rxdata, 1, false); + + printf(ret < 0 ? "." : "@"); + printf(addr % 16 == 15 ? "\n" : " "); + } +} + +uint8_t max_read_byte(uint8_t addr) +{ + uint8_t buf; + i2c_write_blocking(i2c0, MAX_ADDR1, &addr, 1, true); + i2c_read_blocking(i2c0, MAX_ADDR1, &buf, 1, false); + return buf; +} + +uint16_t max_read_word(uint8_t addr) +{ + uint8_t buf[2]; + i2c_write_blocking(i2c0, MAX_ADDR1, &addr, 1, true); + i2c_read_blocking(i2c0, MAX_ADDR1, buf, 2, false); + uint16_t result = ((uint16_t)buf[1]<<8) | (uint16_t)buf[0]; + return result; +} + +uint16_t max_read_word_100(uint8_t addr) +{ + uint8_t buf[2]; + i2c_write_blocking(i2c0, MAX_ADDR2, &addr, 1, true); + i2c_read_blocking(i2c0, MAX_ADDR2, buf, 2, false); + uint16_t result = ((uint16_t)buf[1]<<8) | (uint16_t)buf[0]; + return result; +} + +void max_read_buf(uint8_t addr, uint8_t size, uint8_t *buf) +{ + i2c_write_blocking(i2c0, MAX_ADDR1, &addr, 1, true); + i2c_read_blocking(i2c0, MAX_ADDR1, buf, size, false); +} + +void max_write_byte(uint8_t addr, uint8_t byte) +{ + uint8_t buf[2] = {addr, byte}; + i2c_write_blocking(i2c0, MAX_ADDR1, buf, 2, false); +} + +void max_write_word(uint8_t addr, uint16_t word) +{ + uint8_t buf[3] = {addr, word&0xff, word>>8}; + int res = i2c_write_blocking(i2c0, MAX_ADDR1, buf, 3, false); +} + +void max_write_word_100(uint8_t addr, uint16_t word) +{ + uint8_t buf[3] = {addr, word&0xff, word>>8}; + int res = i2c_write_blocking(i2c0, MAX_ADDR2, buf, 3, false); +} + +void max_write_buf(uint8_t addr, uint8_t size, const uint8_t *buf) +{ + uint8_t txbuf[size + 1]; + txbuf[0] = addr; + for (int i = 0; i < size; i++) { + txbuf[i + 1] = buf[i]; + } + i2c_write_blocking(i2c0, MAX_ADDR1, txbuf, size + 1, false); +} + +float max_word_to_mv(uint16_t w) +{ + float result = ((float)w)*0.078125; + return result; +} + +float max_word_to_pack_mv(uint16_t w) +{ + float result = ((float)w)*0.3125; + return result; +} + +float max_word_to_ma(uint16_t w) +{ + float result = ((float)w)*0.3125; + return result; +} + +uint8_t mps_read_byte(uint8_t addr) +{ + uint8_t buf; + i2c_write_blocking(i2c0, MPS_ADDR, &addr, 1, true); + i2c_read_blocking(i2c0, MPS_ADDR, &buf, 1, false); + return buf; +} + +uint16_t mps_read_word(uint8_t addr) +{ + uint8_t buf[2]; + i2c_write_blocking(i2c0, MPS_ADDR, &addr, 1, true); + i2c_read_blocking(i2c0, MPS_ADDR, buf, 2, false); + uint16_t result = ((uint16_t)buf[1]<<8) | (uint16_t)buf[0]; + return result; +} + +float mps_word_to_ntc(uint16_t w) +{ + float result = (float)(w&0xfff)*1.6/4096.0; + return result; +} + +float mps_word_to_3200(uint16_t w) +{ + float result = (w>>10)*100 + + ((w&(1<<9))>>9)*50 + + ((w&(1<<8))>>8)*25 + + ((w&(1<<7))>>7)*12.5 + + ((w&(1<<6))>>6)*6.25; + return result; +} + +float mps_word_to_6400(uint16_t w) +{ + float result = (w>>9)*100 + + ((w&(1<<8))>>8)*50 + + ((w&(1<<7))>>7)*25 + + ((w&(1<<6))>>6)*12.5; + return result; +} + +float mps_word_to_12800(uint16_t w) +{ + float result = (w>>8)*100 + + ((w&(1<<7))>>7)*50 + + ((w&(1<<6))>>6)*25; + return result; +} + +float mps_word_to_w(uint16_t w) +{ + float result = (w>>9) + + ((w&(1<<8))>>8)*0.5 + + ((w&(1<<7))>>7)*0.25 + + ((w&(1<<6))>>6)*0.125; + return result; +} + +// tj=903-2.578*t +// tj-903=-2.578*t +// (tj-903)/-2.578=t + +float mps_word_to_temp(uint16_t w) +{ + float result = (float)(w>>6); + result = (result - 903) / -2.578; + //result = 903-2.578*result; + return result; +} + +void mps_read_buf(uint8_t addr, uint8_t size, uint8_t *buf) +{ + i2c_write_blocking(i2c0, MPS_ADDR, &addr, 1, true); + i2c_read_blocking(i2c0, MPS_ADDR, buf, size, false); +} + +void mps_write_byte(uint8_t addr, uint8_t byte) +{ + uint8_t buf[2] = {addr, byte}; + i2c_write_blocking(i2c0, MPS_ADDR, buf, 2, false); +} + +void mps_write_buf(uint8_t addr, uint8_t size, const uint8_t *buf) +{ + uint8_t txbuf[size + 1]; + txbuf[0] = addr; + for (int i = 0; i < size; i++) { + txbuf[i + 1] = buf[i]; + } + i2c_write_blocking(i2c0, MPS_ADDR, txbuf, size + 1, false); +} + +// https://git.clarahobbs.com/pd-buddy/pd-buddy-firmware/src/branch/master/lib/src/fusb302b.c + +uint8_t fusb_read_byte(uint8_t addr) +{ + uint8_t buf; + i2c_write_blocking(i2c0, FUSB_ADDR, &addr, 1, true); + i2c_read_blocking(i2c0, FUSB_ADDR, &buf, 1, false); + return buf; +} + +void fusb_read_buf(uint8_t addr, uint8_t size, uint8_t *buf) +{ + i2c_write_blocking(i2c0, FUSB_ADDR, &addr, 1, true); + i2c_read_blocking(i2c0, FUSB_ADDR, buf, size, false); +} + +void fusb_write_byte(uint8_t addr, uint8_t byte) +{ + uint8_t buf[2] = {addr, byte}; + i2c_write_blocking(i2c0, FUSB_ADDR, buf, 2, false); +} + +void fusb_write_buf(uint8_t addr, uint8_t size, const uint8_t *buf) +{ + uint8_t txbuf[size + 1]; + txbuf[0] = addr; + for (int i = 0; i < size; i++) { + txbuf[i + 1] = buf[i]; + } + i2c_write_blocking(i2c0, FUSB_ADDR, txbuf, size + 1, false); +} + +void fusb_send_message(const union pd_msg *msg) +{ + /* Token sequences for the FUSB302B */ + static uint8_t sop_seq[5] = { + FUSB_FIFO_TX_SOP1, + FUSB_FIFO_TX_SOP1, + FUSB_FIFO_TX_SOP1, + FUSB_FIFO_TX_SOP2, + FUSB_FIFO_TX_PACKSYM + }; + static uint8_t eop_seq[4] = { + FUSB_FIFO_TX_JAM_CRC, + FUSB_FIFO_TX_EOP, + FUSB_FIFO_TX_TXOFF, + FUSB_FIFO_TX_TXON + }; + + /* Get the length of the message: a two-octet header plus NUMOBJ four-octet + * data objects */ + uint8_t msg_len = 2 + 4 * PD_NUMOBJ_GET(msg); + + /* Set the number of bytes to be transmitted in the packet */ + sop_seq[4] = FUSB_FIFO_TX_PACKSYM | msg_len; + + /* Write all three parts of the message to the TX FIFO */ + fusb_write_buf(FUSB_FIFOS, 5, sop_seq); + fusb_write_buf(FUSB_FIFOS, msg_len, msg->bytes); + fusb_write_buf(FUSB_FIFOS, 4, eop_seq); +} + +uint8_t fusb_read_message(union pd_msg *msg) +{ + uint8_t garbage[4]; + uint8_t numobj; + + /* If this isn't an SOP message, return error. + * Because of our configuration, we should be able to assume this means the + * buffer is empty, and not try to read past a non-SOP message. */ + if ((fusb_read_byte(FUSB_FIFOS) & FUSB_FIFO_RX_TOKEN_BITS) + != FUSB_FIFO_RX_SOP) { + return 1; + } + /* Read the message header into msg */ + fusb_read_buf(FUSB_FIFOS, 2, msg->bytes); + /* Get the number of data objects */ + numobj = PD_NUMOBJ_GET(msg); + /* If there is at least one data object, read the data objects */ + if (numobj > 0) { + fusb_read_buf(FUSB_FIFOS, numobj * 4, msg->bytes + 2); + } + /* Throw the CRC32 in the garbage, since the PHY already checked it. */ + fusb_read_buf(FUSB_FIFOS, 4, garbage); + + return 0; +} + +// returns voltage +int print_src_fixed_pdo(uint32_t pdo) { + int tmp; + + /* Dual-role power */ + tmp = (pdo & PD_PDO_SRC_FIXED_DUAL_ROLE_PWR) >> PD_PDO_SRC_FIXED_DUAL_ROLE_PWR_SHIFT; + if (tmp) { + printf("\tdual_role_pwr: %d\n", tmp); + } + + /* USB Suspend Supported */ + tmp = (pdo & PD_PDO_SRC_FIXED_USB_SUSPEND) >> PD_PDO_SRC_FIXED_USB_SUSPEND_SHIFT; + if (tmp) { + printf("\tusb_suspend: %d\n", tmp); + } + + /* Unconstrained Power */ + tmp = (pdo & PD_PDO_SRC_FIXED_UNCONSTRAINED) >> PD_PDO_SRC_FIXED_UNCONSTRAINED_SHIFT; + if (tmp) { + printf("\tunconstrained_pwr: %d\n", tmp); + } + + /* USB Communications Capable */ + tmp = (pdo & PD_PDO_SRC_FIXED_USB_COMMS) >> PD_PDO_SRC_FIXED_USB_COMMS_SHIFT; + if (tmp) { + printf("\tusb_comms: %d\n", tmp); + } + + /* Dual-Role Data */ + tmp = (pdo & PD_PDO_SRC_FIXED_DUAL_ROLE_DATA) >> PD_PDO_SRC_FIXED_DUAL_ROLE_DATA_SHIFT; + if (tmp) { + printf("\tdual_role_data: %d\n", tmp); + } + + /* Unchunked Extended Messages Supported */ + tmp = (pdo & PD_PDO_SRC_FIXED_UNCHUNKED_EXT_MSG) >> PD_PDO_SRC_FIXED_UNCHUNKED_EXT_MSG_SHIFT; + if (tmp) { + printf("\tunchunked_ext_msg: %d\n", tmp); + } + + /* Peak Current */ + tmp = (pdo & PD_PDO_SRC_FIXED_PEAK_CURRENT) >> PD_PDO_SRC_FIXED_PEAK_CURRENT_SHIFT; + if (tmp) { + printf("\tpeak_i: %d\n", tmp); + } + + /* Voltage */ + tmp = (pdo & PD_PDO_SRC_FIXED_VOLTAGE) >> PD_PDO_SRC_FIXED_VOLTAGE_SHIFT; + printf("\tv: %d.%02d V\n", PD_PDV_V(tmp), PD_PDV_CV(tmp)); + + int voltage = (int)PD_PDV_V(tmp); + + /* Maximum Current */ + tmp = (pdo & PD_PDO_SRC_FIXED_CURRENT) >> PD_PDO_SRC_FIXED_CURRENT_SHIFT; + printf("\ti: %d.%02d A\n", PD_PDI_A(tmp), PD_PDI_CA(tmp)); + + return voltage; +} + +int charger_dump() { + uint8_t status = mps_read_byte(0x13); + uint8_t fault = mps_read_byte(0x14); + + float adc_bat_v = mps_word_to_6400(mps_read_word(0x16))/1000.0; + float adc_sys_v = mps_word_to_6400(mps_read_word(0x18))/1000.0; + float adc_charge_c = mps_word_to_6400(mps_read_word(0x1a))/1000.0; + float adc_input_v = mps_word_to_12800(mps_read_word(0x1c))/1000.0; + float adc_input_c = mps_word_to_3200(mps_read_word(0x1e))/1000.0; + float adc_temp = mps_word_to_temp(mps_read_word(0x24)); + float adc_sys_pwr = mps_word_to_w(mps_read_word(0x26)); + float adc_discharge_c = mps_word_to_6400(mps_read_word(0x28))/1000.0; + float adc_ntc_v = mps_word_to_ntc(mps_read_word(0x40))/1000.0; + + uint8_t input_c_limit = mps_read_byte(0x00); + uint8_t input_v_limit = mps_read_byte(0x01); + uint8_t charge_c = mps_read_byte(0x02); + uint8_t precharge_c = mps_read_byte(0x03); + uint8_t bat_full_v = mps_read_byte(0x04); + + printf("[charger info]\n"); + printf(" status: %x\n", status); + printf(" fault: %x\n ------------\n", fault); + + printf(" adc_bat_v: %f\n", adc_bat_v); + printf(" adc_sys_v: %f\n", adc_sys_v); + printf(" adc_charge_c: %f\n", adc_charge_c); + printf(" adc_input_v: %f\n", adc_input_v); + printf(" adc_input_c: %f\n", adc_input_c); + printf(" adc_temp: %f\n", adc_temp); + printf(" adc_sys_pwr: %f\n", adc_sys_pwr); + printf(" adc_discharge_c: %f\n", adc_discharge_c); + printf(" adc_ntc_v: %f\n ------------\n", adc_ntc_v); + + printf(" input_c_limit: %d\n", input_c_limit); + printf(" input_v_limit: %d\n", input_v_limit); + printf(" charge_c: %d\n", charge_c); + printf(" precharge_c: %d\n", precharge_c); + printf(" bat_full_v: %d\n ============\n", bat_full_v); + + if (adc_input_v < 11) { + // renegotiate PD + return 1; + } + return 0; +} + +void max_dump() { + // disable write protection (CommStat) + max_write_word(0x61, 0x0000); + max_write_word(0x61, 0x0000); + // fixme set thermistor config + max_write_word_100(0xca, 0x58ef); + // set pack cfg: 2 cells (0), 1 thermistor, 6v charge pump (?), no aoldo, btpken off (??) + max_write_word_100(0xb5, (1<11)|(0<<8)|(0<<2)|0); + + uint16_t comm_stat = max_read_word(0x61); + uint16_t status = max_read_word(0x00); + uint16_t prot_status = max_read_word(0xd9); + uint16_t prot_alert = max_read_word(0xaf); + uint16_t prot_cfg2 = max_read_word_100(0xf1); + uint16_t therm_cfg = max_read_word_100(0xca); + float vcell = max_word_to_mv(max_read_word(0x1a)); + float avg_vcell = max_word_to_mv(max_read_word(0x19)); + float cell1 = max_word_to_mv(max_read_word(0xd8)); + float cell2 = max_word_to_mv(max_read_word(0xd7)); + float cell3 = max_word_to_mv(max_read_word(0xd6)); + float cell4 = max_word_to_mv(max_read_word(0xd5)); + float vpack = max_word_to_pack_mv(max_read_word(0xda)); + + float temp = ((float)((int16_t)max_read_word(0x1b)))*(1.0/256.0); + float die_temp = ((float)((int16_t)max_read_word(0x34)))*(1.0/256.0); + float temp1 = ((float)((int16_t)max_read_word_100(0x3a)))*(1.0/256.0); + float temp2 = ((float)((int16_t)max_read_word_100(0x39)))*(1.0/256.0); + float temp3 = ((float)((int16_t)max_read_word_100(0x38)))*(1.0/256.0); + float temp4 = ((float)((int16_t)max_read_word_100(0x37)))*(1.0/256.0); + + printf("[pack info]\n"); + + printf(" comm_stat: %04x\n", comm_stat); + + printf(" status: %04x\n", status); + if (status & 0x8000) { + printf(" `-- prot alert\n"); + } + if (status & 0x0002) { + printf(" `-- POR, clearing\n"); + max_write_word(0x61, 0x0000); + max_write_word(0x61, 0x0000); + max_write_word(0x00, status & (~0x0002)); + } + printf(" prot_alert: %04x\n", prot_alert); + printf(" prot_cfg2: %04x\n", prot_cfg2); + printf(" therm_cfg: %04x\n", therm_cfg); + printf(" temp: %f\n", temp); + printf(" die temp: %f\n", die_temp); + printf(" temp1: %f\n", temp1); + printf(" temp2: %f\n", temp2); + printf(" temp3: %f\n", temp3); + printf(" temp4: %f\n", temp4); + + printf(" prot_status: %04x\n", prot_status); + if (prot_status & (1<<14)) { + printf(" `-- too hot\n"); + } + if (prot_status & (1<<13)) { + printf(" `-- full\n"); + } + if (prot_status & (1<<12)) { + printf(" `-- too cold for charge\n"); + } + if (prot_status & (1<<11)) { + printf(" `-- overvoltage\n"); + } + if (prot_status & (1<<10)) { + printf(" `-- overcharge current\n"); + } + if (prot_status & (1<<9)) { + printf(" `-- qoverflow\n"); + } + if (prot_status & (1<<8)) { + printf(" `-- prequal timeout\n"); + } + if (prot_status & (1<<7)) { + printf(" `-- imbalance\n"); + } + if (prot_status & (1<<6)) { + printf(" `-- perm fail\n"); + } + if (prot_status & (1<<5)) { + printf(" `-- die hot\n"); + } + if (prot_status & (1<<4)) { + printf(" `-- too hot for discharge\n"); + } + if (prot_status & (1<<3)) { + printf(" `-- undervoltage\n"); + } + if (prot_status & (1<<2)) { + printf(" `-- overdischarge current\n"); + } + if (prot_status & (1<<1)) { + printf(" `-- resdfault\n"); + } + if (prot_status & (1<<0)) { + printf(" `-- ship\n"); + } + + printf(" vcell: %f\n", vcell); + printf(" avg_vcell: %f\n", avg_vcell); + printf(" cell1: %f\n", cell1); + printf(" cell2: %f\n", cell2); + printf(" cell3: %f\n", cell3); + printf(" cell4: %f\n", cell4); + printf(" vpack: %f\n ============\n", vpack); +} + +void turn_som_power_on() { + gpio_put(PIN_LED_B, 1); + + printf("[turn_som_power_on]\n"); + gpio_put(PIN_1V1_ENABLE, 1); + sleep_ms(10); + gpio_put(PIN_3V3_ENABLE, 1); + sleep_ms(10); + gpio_put(PIN_5V_ENABLE, 1); + + sleep_ms(10); + gpio_put(PIN_DISP_EN, 1); + sleep_ms(10); + gpio_put(PIN_DISP_RESET, 1); // not connected? +} + +void turn_som_power_off() { + gpio_put(PIN_LED_B, 0); + + printf("[turn_som_power_off]\n"); + gpio_put(PIN_DISP_RESET, 0); + gpio_put(PIN_DISP_EN, 0); + + gpio_put(PIN_5V_ENABLE, 0); + sleep_ms(10); + gpio_put(PIN_3V3_ENABLE, 0); + sleep_ms(10); + gpio_put(PIN_1V1_ENABLE, 0); +} + +#define I2C_TIMEOUT (1000*500) + +#define UART_ID uart1 +#define BAUD_RATE 115200 +#define DATA_BITS 8 +#define STOP_BITS 1 +#define PARITY UART_PARITY_NONE + + +#define ST_EXPECT_DIGIT_0 0 +#define ST_EXPECT_DIGIT_1 1 +#define ST_EXPECT_DIGIT_2 2 +#define ST_EXPECT_DIGIT_3 3 +#define ST_EXPECT_CMD 4 +#define ST_SYNTAX_ERROR 5 +#define ST_EXPECT_RETURN 6 + +char remote_cmd = 0; +uint8_t remote_arg = 0; +unsigned char cmd_state = ST_EXPECT_DIGIT_0; +unsigned int cmd_number = 0; +int cmd_echo = 0; +char uart_buffer[255] = {0}; + +// chr: input character +void handle_commands(char chr) { + printf("rx: [%c]\n", chr); + + if (cmd_echo) { + sprintf(uart_buffer, "%c", chr); + uart_puts(UART_ID, uart_buffer); + } + + // states: + // 0-3 digits of optional command argument + // 4 command letter expected + // 5 syntax error (unexpected character) + // 6 command letter entered + + if (cmd_state>=ST_EXPECT_DIGIT_0 && cmd_state<=ST_EXPECT_DIGIT_3) { + // read number or command + if (chr >= '0' && chr <= '9') { + cmd_number*=10; + cmd_number+=(chr-'0'); + cmd_state++; + } else if ((chr >= 'a' && chr <= 'z') || (chr >= 'A' && chr <= 'Z')) { + // command entered instead of digit + remote_cmd = chr; + cmd_state = ST_EXPECT_RETURN; + } else if (chr == '\n' || chr == ' ') { + // ignore newlines or spaces + } else if (chr == '\r') { + sprintf(uart_buffer, "error:syntax\r\n"); + uart_puts(UART_ID, uart_buffer); + cmd_state = ST_EXPECT_DIGIT_0; + cmd_number = 0; + } else { + // syntax error + cmd_state = ST_SYNTAX_ERROR; + } + } + else if (cmd_state == ST_EXPECT_CMD) { + // read command + if ((chr >= 'a' && chr <= 'z') || (chr >= 'A' && chr <= 'Z')) { + remote_cmd = chr; + cmd_state = ST_EXPECT_RETURN; + } else { + cmd_state = ST_SYNTAX_ERROR; + } + } + else if (cmd_state == ST_SYNTAX_ERROR) { + // syntax error + if (chr == '\r') { + sprintf(uart_buffer, "error:syntax\r\n"); + uart_puts(UART_ID, uart_buffer); + cmd_state = ST_EXPECT_DIGIT_0; + cmd_number = 0; + } + } + else if (cmd_state == ST_EXPECT_RETURN) { + if (chr == '\n' || chr == ' ') { + // ignore newlines or spaces + } + else if (chr == '\r') { + if (cmd_echo) { + // FIXME + sprintf(uart_buffer,"\n"); + uart_puts(UART_ID, uart_buffer); + } + + // execute + if (remote_cmd == 'p') { + // toggle system power and/or reset imx + if (cmd_number == 0) { + turn_som_power_off(); + sprintf(uart_buffer,"system: off\r\n"); + uart_puts(UART_ID, uart_buffer); + } else if (cmd_number == 2) { + //reset_som(); + sprintf(uart_buffer,"system: reset\r\n"); + uart_puts(UART_ID, uart_buffer); + } else { + turn_som_power_on(); + sprintf(uart_buffer,"system: on\r\n"); + uart_puts(UART_ID, uart_buffer); + } + } + else if (remote_cmd == 'a') { + // TODO + // get system current (mA) + sprintf(uart_buffer,"%d\r\n",0); + uart_puts(UART_ID, uart_buffer); + } + else if (remote_cmd == 'v' && cmd_number>=0 && cmd_number<=0) { + // TODO + // get cell voltage + sprintf(uart_buffer,"%d\r\n",0); + uart_puts(UART_ID, uart_buffer); + } + else if (remote_cmd == 'V') { + // TODO + // get system voltage + sprintf(uart_buffer,"%d\r\n",0); + uart_puts(UART_ID, uart_buffer); + } + else if (remote_cmd == 's') { + // TODO + sprintf(uart_buffer,FW_REV"normal,%d,%d,%d\r",0,0,0); + uart_puts(UART_ID, uart_buffer); + } + else if (remote_cmd == 'u') { + // TODO + // turn reporting to i.MX on or off + } + else if (remote_cmd == 'w') { + // TODO + // wake SoC + } + else if (remote_cmd == 'c') { + // TODO + // get status of cells, current, voltage, fuel gauge + sprintf(uart_buffer, "%d\r\n", 0); + uart_puts(UART_ID, uart_buffer); + } + else if (remote_cmd == 'S') { + // TODO + // get charger system cycles in current state + sprintf(uart_buffer, "%d\r\n", 0); + uart_puts(UART_ID, uart_buffer); + } + else if (remote_cmd == 'C') { + // TODO + // get battery capacity (mAh) + sprintf(uart_buffer,"%d/%d/%d\r\n",0,0,0); + uart_puts(UART_ID, uart_buffer); + } + else if (remote_cmd == 'e') { + // toggle serial echo + cmd_echo = cmd_number?1:0; + } + else { + sprintf(uart_buffer, "error:command\r\n"); + uart_puts(UART_ID, uart_buffer); + } + + cmd_state = ST_EXPECT_DIGIT_0; + cmd_number = 0; + } else { + cmd_state = ST_SYNTAX_ERROR; + } + } +} + +void on_uart_rx() { + while (uart_is_readable(UART_ID)) { + handle_commands(uart_getc(UART_ID)); + } +} + +int main() { + stdio_init_all(); + + //sleep_ms(2000); + // UART to keyboard + uart_init(UART_ID, BAUD_RATE); + uart_set_format(UART_ID, DATA_BITS, STOP_BITS, PARITY); + uart_set_hw_flow(UART_ID, false, false); + uart_set_fifo_enabled(UART_ID, true); + gpio_set_function(PIN_KBD_UART_TX, GPIO_FUNC_UART); + gpio_set_function(PIN_KBD_UART_RX, GPIO_FUNC_UART); + int UART_IRQ = UART_ID == uart0 ? UART0_IRQ : UART1_IRQ; + //irq_set_exclusive_handler(UART_IRQ, on_uart_rx); + //irq_set_enabled(UART_IRQ, true); + //uart_set_irq_enables(UART_ID, true, false); // bool rx_has_data, bool tx_needs_data + + printf("uart setup\n"); + + gpio_set_function(PIN_SDA, GPIO_FUNC_I2C); + gpio_set_function(PIN_SCL, GPIO_FUNC_I2C); + bi_decl(bi_2pins_with_func(PIN_SDA, PIN_SCL, GPIO_FUNC_I2C)); + i2c_init(i2c0, 100 * 1000); + + gpio_init(PIN_LED_R); + gpio_init(PIN_LED_G); + gpio_init(PIN_LED_B); + gpio_set_dir(PIN_LED_R, 1); + gpio_set_dir(PIN_LED_G, 1); + gpio_set_dir(PIN_LED_B, 1); + + gpio_init(PIN_1V1_ENABLE); + gpio_init(PIN_3V3_ENABLE); + gpio_init(PIN_5V_ENABLE); + gpio_set_dir(PIN_1V1_ENABLE, 1); + gpio_set_dir(PIN_3V3_ENABLE, 1); + gpio_set_dir(PIN_5V_ENABLE, 1); + gpio_put(PIN_1V1_ENABLE, 0); + gpio_put(PIN_3V3_ENABLE, 0); + gpio_put(PIN_5V_ENABLE, 0); + + gpio_init(PIN_DISP_RESET); + gpio_init(PIN_DISP_EN); + gpio_set_dir(PIN_DISP_EN, 1); + gpio_set_dir(PIN_DISP_RESET, 1); + gpio_put(PIN_DISP_EN, 0); + gpio_put(PIN_DISP_RESET, 0); + + gpio_put(PIN_LED_R, 0); + gpio_put(PIN_LED_G, 0); + gpio_put(PIN_LED_B, 0); + + unsigned int t = 0; + + int state = 0; + int request_sent = 0; + uint8_t rxdata[2]; + + union pd_msg tx; + int tx_id_count = 0; + union pd_msg rx_msg; + + int power_objects = 0; + + // https://www.reclaimerlabs.com/blog/2017/2/1/usb-c-for-engineers-part-3 + + printf("main loop\n"); + + while (true) { + sleep_ms(1); + + while (uart_is_readable(UART_ID)) { + handle_commands(uart_getc(UART_ID)); + } + + if (state == 0) { + gpio_put(PIN_LED_R, 0); + power_objects = 0; + + //printf("[pocket-sysctl] state 0\n"); + // probe FUSB302BMPX + if (i2c_read_timeout_us(i2c0, FUSB_ADDR, rxdata, 1, false, I2C_TIMEOUT)) { + // 1. set auto GoodCRC + // AUTO_CRC in Switches1 + // Address: 03h; Reset Value: 0b0010_0000 + // TODO: figure out CC direction? + + printf("[pocket-sysctl] FUSB probed.\n"); + + fusb_write_byte(FUSB_RESET, FUSB_RESET_SW_RES); + // turn on all power + fusb_write_byte(FUSB_POWER, 0x0F); + fusb_write_byte(FUSB_CONTROL3, 0x07); + fusb_write_byte(FUSB_CONTROL1, FUSB_CONTROL1_RX_FLUSH); + + /* Measure CC1 */ + fusb_write_byte(FUSB_SWITCHES0, 0x07); + sleep_us(250); + uint8_t cc1 = fusb_read_byte(FUSB_STATUS0) & FUSB_STATUS0_BC_LVL; + + printf("[pocket-sysctl] CC1: %d\n", cc1); + + /* Measure CC2 */ + fusb_write_byte(FUSB_SWITCHES0, 0x0B); + sleep_us(250); + uint8_t cc2 = fusb_read_byte(FUSB_STATUS0) & FUSB_STATUS0_BC_LVL; + + printf("[pocket-sysctl] CC2: %d\n", cc2); + + if (cc1 > cc2) { + fusb_write_byte(FUSB_SWITCHES1, 0x25); + fusb_write_byte(FUSB_SWITCHES0, 0x07); + } else { + fusb_write_byte(FUSB_SWITCHES1, 0x26); + fusb_write_byte(FUSB_SWITCHES0, 0x0B); + } + + printf("[pocket-sysctl] switches set\n"); + + //fusb_write_byte(FUSB_RESET, FUSB_RESET_PD_RESET); + t = 0; + state = 1; + } else { + if (t > 1000) { + printf("[pocket-sysctl] state 0: fusb timeout\n"); + t = 0; + } + } + + } else if (state == 1) { + //printf("[pocket-sysctl] state 2\n"); + + if (t>2000) { + printf("[pocket-sysctl] state 2, timeout\n"); + + // issue hard reset + fusb_write_byte(FUSB_CONTROL3, (1<<6) | 0x07); + sleep_ms(1); + fusb_write_byte(FUSB_CONTROL3, 0x07); + + request_sent = 0; + state = 0; + t = 0; + } + + int res = fusb_read_message(&rx_msg); + + if (!res) { + uint8_t msgtype = PD_MSGTYPE_GET(&rx_msg); + uint8_t numobj = PD_NUMOBJ_GET(&rx_msg); + printf(" msg type: %x numobj: %d\n", msgtype, numobj); + if (msgtype == PD_MSGTYPE_SOURCE_CAPABILITIES) { + int max_voltage = 0; + for (int i=0; i max_voltage && voltage <= 20) { + power_objects = i+1; + max_voltage = voltage; + } + } else { + printf("not a fixed PDO: %08x\n", pdo); + } + } + if (!request_sent) { + state = 2; + t = 0; + } + } else if (msgtype == PD_MSGTYPE_PS_RDY) { + // power supply is ready + printf("[pocket-sysctl] power supply ready!\n"); + request_sent = 0; + t = 0; + state = 3; + } + } + } else if (state == 2) { + printf("[pocket-sysctl] state 2, requesting PO %d\n", power_objects); + + tx.hdr = PD_MSGTYPE_REQUEST | PD_NUMOBJ(1) | PD_DATAROLE_UFP | PD_POWERROLE_SINK | PD_SPECREV_2_0; + + tx.hdr &= ~PD_HDR_MESSAGEID; + tx.hdr |= (tx_id_count % 8) << PD_HDR_MESSAGEID_SHIFT; + + int current = 1; + + tx.obj[0] = PD_RDO_FV_MAX_CURRENT_SET(current) + | PD_RDO_FV_CURRENT_SET(current) + | PD_RDO_NO_USB_SUSPEND | PD_RDO_OBJPOS_SET(power_objects); // FIXME + + fusb_send_message(&tx); + + printf("[pocket-sysctl] request sent.\n"); + + tx_id_count++; + + request_sent = 1; + state = 1; + } else if (state == 3) { + gpio_put(PIN_LED_R, 1); + + // running + if (t>2000) { + printf("[pocket-sysctl] state 3\n"); + //i2c_scan(); + + int renegotiate = charger_dump(); + max_dump(); + + if (renegotiate) { + state = 0; + } + + t = 0; + } + } + + t++; + } + + return 0; +}