├── .gitattributes ├── USB_TCPM.cpp ├── .gitignore ├── README.md ├── LICENSE ├── examples └── FUSB302_test.ino │ └── FUSB302_test.ino ├── USB_TCPM.h ├── FUSB302.h └── FUSB302.cpp /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /USB_TCPM.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | USB_TCPM.cpp - Library for interacting with the FUSB302B chip. 3 | Copyright 2010 The Chromium OS Authors 4 | Copyright 2017 Jason Cerundolo 5 | Released under an MIT license. See LICENSE file. 6 | */ 7 | 8 | #include "USB_TCPM.h" 9 | 10 | int USB_TCPM::get_num_bytes(uint16_t header) 11 | { 12 | int rv; 13 | 14 | /* Grab the Number of Data Objects field.*/ 15 | rv = PD_HEADER_CNT(header); 16 | 17 | /* Multiply by four to go from 32-bit words -> bytes */ 18 | rv *= 4; 19 | 20 | /* Plus 2 for header */ 21 | rv += 2; 22 | 23 | return rv; 24 | } 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear in the root of a volume 35 | .DocumentRevisions-V100 36 | .fseventsd 37 | .Spotlight-V100 38 | .TemporaryItems 39 | .Trashes 40 | .VolumeIcon.icns 41 | 42 | # Directories potentially created on remote AFP share 43 | .AppleDB 44 | .AppleDesktop 45 | Network Trash Folder 46 | Temporary Items 47 | .apdisk 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Deprecation 2 | 3 | This library has been replaced by a new project that redesigns the library. It started with the [USB-C Explorer](https://github.com/ReclaimerLabs/USB-C-Explorer/tree/master/firmware/USB-C%20Explorer). Then that firmware was ported to [Arduino](https://github.com/graycatlabs/usb-c-arduino). This new version is much easier to port to new platforms, and so it recommended for new projects. 4 | 5 | # Introduction 6 | 7 | This library is a port of the [**Google Chrome EC library**](https://www.chromium.org/chromium-os/ec-development). The goal is port the code to C++ and packge it as a library that can be used with Arduino, Particle, and other embedded applications. This code is very much a work in progress. Not all of it has been tested. 8 | 9 | The FUSB302B is a USB Type-C port controller and BMC PHY. It allows configuring a USB-C port and sending USB Power Delivery messages. This library supports all the basic functionality. In addition, it supports the USB Type-C Port Manager (TCPM) interface, which should allow for this and other, similar libraries to be used with the more general [**USB Power Delivery library**](https://github.com/ReclaimerLabs/USB_PD). 10 | 11 | # Example Usage 12 | 13 | The included example shows some basic usage of this library. Without the USB PD library, any received message doesn't make much sense. An Apple USB-C laptop charger will send unsolisicated Source_Capabilities messages. These can be decoded manually by referring to the USB Power Delivery specification. 14 | 15 | # Next Development Steps 16 | 17 | The next steps are to remove the Arduino-specific references to make the code more platform agnostic. 18 | 19 | # Questions, Comments, and Contributions 20 | 21 | Pull requests are welcome. If you have questions or comments, you can email me directly at jason@reclaimerlabs.com. 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 The Chromium OS Authors. All rights reserved. 2 | Redistribution and use in source and binary forms, with or without 3 | modification, are permitted provided that the following conditions are 4 | met: 5 | * Redistributions of source code must retain the above copyright 6 | notice, this list of conditions and the following disclaimer. 7 | * Redistributions in binary form must reproduce the above 8 | copyright notice, this list of conditions and the following disclaimer 9 | in the documentation and/or other materials provided with the 10 | distribution. 11 | * Neither the name of Google Inc. nor the names of its 12 | contributors may be used to endorse or promote products derived from 13 | this software without specific prior written permission. 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 15 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 16 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 17 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 18 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 19 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 20 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | 26 | Copyright (c) 2017 Jason Cerundolo 27 | 28 | Permission is hereby granted, free of charge, to any person obtaining 29 | a copy of this hardware, software, and associated documentation files 30 | (the "Product"), to deal in the Product without restriction, including 31 | without limitation the rights to use, copy, modify, merge, publish, 32 | distribute, sublicense, and/or sell copies of the Product, and to 33 | permit persons to whom the Product is furnished to do so, subject to 34 | the following conditions: 35 | 36 | The above copyright notice and this permission notice shall be included 37 | in all copies or substantial portions of the Product. 38 | 39 | THE PRODUCT IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 40 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 41 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 42 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 43 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 44 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 45 | PRODUCT OR THE USE OR OTHER DEALINGS IN THE PRODUCT. 46 | -------------------------------------------------------------------------------- /examples/FUSB302_test.ino/FUSB302_test.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /* 4 | * Use this example with the 5 | * Reclaimer Labs USB-C Breakout Board 6 | * https://www.tindie.com/products/ReclaimerLabs/usb-type-c-power-delivery-phy-breakout-board/ 7 | * 8 | * How to connect your Arduino. 9 | * 10 | * Name on Arduino -> Name on Breakout Board 11 | * 12 | * GND -> GND 13 | * 3.3V -> VDD 14 | * IOREF -> Vpu 15 | * 16 | * The I2C lines must be connected as per the Wire library 17 | * https://www.arduino.cc/en/Reference/Wire 18 | * 19 | * For Arduino Uno 20 | * A4 -> SDA 21 | * A5 -> SCL 22 | */ 23 | 24 | FUSB302 usbc; 25 | 26 | void setup() { 27 | // put your setup code here, to run once: 28 | Serial.begin(115200); 29 | Serial.println("Welcome to USB-C!"); 30 | digitalWrite(13, HIGH); 31 | 32 | int usb_pd_message_header; 33 | uint32_t usb_pd_message_buffer[10]; 34 | 35 | usbc.init(); 36 | 37 | int chip_id; 38 | usbc.get_chip_id(&chip_id); 39 | Serial.print("FUSB302 ID = 0x"); 40 | Serial.println(chip_id, HEX); 41 | 42 | usbc.pd_reset(); 43 | 44 | int cc1_meas, cc2_meas; 45 | usbc.detect_cc_pin_sink(&cc1_meas, &cc2_meas); 46 | Serial.print("CC1 level = "); 47 | switch (cc1_meas) { 48 | case TYPEC_CC_VOLT_OPEN : 49 | Serial.println("Open"); 50 | break; 51 | case TYPEC_CC_VOLT_RA : 52 | Serial.println("Ra pull-down"); 53 | break; 54 | case TYPEC_CC_VOLT_RD : 55 | Serial.println("Rd pull-down"); 56 | break; 57 | case TYPEC_CC_VOLT_SNK_DEF : 58 | Serial.println("Connected with default power"); 59 | break; 60 | case TYPEC_CC_VOLT_SNK_1_5 : 61 | Serial.println("Connected with 1.5A at 5V"); 62 | break; 63 | case TYPEC_CC_VOLT_SNK_3_0 : 64 | Serial.println("Connected with 3.0A at 5V"); 65 | break; 66 | default : 67 | Serial.println("Unknown"); 68 | break; 69 | } 70 | 71 | Serial.print("CC2 level = "); 72 | switch (cc2_meas) { 73 | case TYPEC_CC_VOLT_OPEN : 74 | Serial.println("Open"); 75 | break; 76 | case TYPEC_CC_VOLT_RA : 77 | Serial.println("Ra pull-down"); 78 | break; 79 | case TYPEC_CC_VOLT_RD : 80 | Serial.println("Rd pull-down"); 81 | break; 82 | case TYPEC_CC_VOLT_SNK_DEF : 83 | Serial.println("Connected with default power"); 84 | break; 85 | case TYPEC_CC_VOLT_SNK_1_5 : 86 | Serial.println("Connected with 1.5A at 5V"); 87 | break; 88 | case TYPEC_CC_VOLT_SNK_3_0 : 89 | Serial.println("Connected with 3.0A at 5V"); 90 | break; 91 | default : 92 | Serial.println("Unknown"); 93 | break; 94 | } 95 | 96 | if (cc1_meas > cc2_meas) { 97 | usbc.set_polarity(0); 98 | } else { 99 | usbc.set_polarity(1); 100 | } 101 | 102 | delay(1000); 103 | 104 | usbc.get_message(usb_pd_message_buffer, &usb_pd_message_header); 105 | Serial.print("Header = 0x"); 106 | Serial.println(usb_pd_message_header, HEX); 107 | for (int i=0; i<10; i++) { 108 | Serial.print(i, DEC); 109 | Serial.print(" = 0x"); 110 | Serial.println(usb_pd_message_buffer[i], HEX); 111 | } 112 | } 113 | 114 | void loop() { 115 | // put your main code here, to run repeatedly: 116 | 117 | } 118 | -------------------------------------------------------------------------------- /USB_TCPM.h: -------------------------------------------------------------------------------- 1 | /* 2 | USB_TCPM.h - Library for interacting with the FUSB302B chip. 3 | Copyright 2010 The Chromium OS Authors 4 | Copyright 2017 Jason Cerundolo 5 | Released under an MIT license. See LICENSE file. 6 | */ 7 | 8 | #ifndef USB_TCPM_H 9 | #define USB_TCPM_H 10 | 11 | #include "Arduino.h" 12 | 13 | #define PD_HEADER_CNT(header) (((header) >> 12) & 7) 14 | #define PD_HEADER_TYPE(header) ((header) & 0xF) 15 | #define PD_HEADER_ID(header) (((header) >> 9) & 7) 16 | 17 | /* Minimum PD supply current (mA) */ 18 | #define PD_MIN_MA 500 19 | 20 | /* Minimum PD voltage (mV) */ 21 | #define PD_MIN_MV 5000 22 | 23 | /* No connect voltage threshold for sources based on Rp */ 24 | #define PD_SRC_DEF_VNC_MV 1600 25 | #define PD_SRC_1_5_VNC_MV 1600 26 | #define PD_SRC_3_0_VNC_MV 2600 27 | 28 | /* Rd voltage threshold for sources based on Rp */ 29 | #define PD_SRC_DEF_RD_THRESH_MV 200 30 | #define PD_SRC_1_5_RD_THRESH_MV 400 31 | #define PD_SRC_3_0_RD_THRESH_MV 800 32 | 33 | /* Voltage threshold to detect connection when presenting Rd */ 34 | #define PD_SNK_VA_MV 250 35 | 36 | /* Flags for i2c_xfer() */ 37 | #define I2C_XFER_START (1 << 0) /* Start smbus session from idle state */ 38 | #define I2C_XFER_STOP (1 << 1) /* Terminate smbus session with stop bit */ 39 | #define I2C_XFER_SINGLE (I2C_XFER_START | I2C_XFER_STOP) /* One transaction */ 40 | 41 | /* Default retry count for transmitting */ 42 | #define PD_RETRY_COUNT 3 43 | 44 | enum tcpc_cc_voltage_status { 45 | TYPEC_CC_VOLT_OPEN = 0, 46 | TYPEC_CC_VOLT_RA = 1, 47 | TYPEC_CC_VOLT_RD = 2, 48 | TYPEC_CC_VOLT_SNK_DEF = 5, 49 | TYPEC_CC_VOLT_SNK_1_5 = 6, 50 | TYPEC_CC_VOLT_SNK_3_0 = 7, 51 | }; 52 | 53 | enum tcpc_cc_pull { 54 | TYPEC_CC_RA = 0, 55 | TYPEC_CC_RP = 1, 56 | TYPEC_CC_RD = 2, 57 | TYPEC_CC_OPEN = 3, 58 | }; 59 | 60 | enum tcpc_rp_value { 61 | TYPEC_RP_USB = 0, 62 | TYPEC_RP_1A5 = 1, 63 | TYPEC_RP_3A0 = 2, 64 | TYPEC_RP_RESERVED = 3, 65 | }; 66 | 67 | enum tcpm_transmit_type { 68 | TCPC_TX_SOP = 0, 69 | TCPC_TX_SOP_PRIME = 1, 70 | TCPC_TX_SOP_PRIME_PRIME = 2, 71 | TCPC_TX_SOP_DEBUG_PRIME = 3, 72 | TCPC_TX_SOP_DEBUG_PRIME_PRIME = 4, 73 | TCPC_TX_HARD_RESET = 5, 74 | TCPC_TX_CABLE_RESET = 6, 75 | TCPC_TX_BIST_MODE_2 = 7 76 | }; 77 | 78 | enum tcpc_transmit_complete { 79 | TCPC_TX_COMPLETE_SUCCESS = 0, 80 | TCPC_TX_COMPLETE_DISCARDED = 1, 81 | TCPC_TX_COMPLETE_FAILED = 2, 82 | }; 83 | 84 | /* List of common error codes that can be returned */ 85 | enum ec_error_list { 86 | /* Success - no error */ 87 | EC_SUCCESS = 0, 88 | /* Unknown error */ 89 | EC_ERROR_UNKNOWN = 1, 90 | /* Function not implemented yet */ 91 | EC_ERROR_UNIMPLEMENTED = 2, 92 | /* Overflow error; too much input provided. */ 93 | EC_ERROR_OVERFLOW = 3, 94 | /* Timeout */ 95 | EC_ERROR_TIMEOUT = 4, 96 | /* Invalid argument */ 97 | EC_ERROR_INVAL = 5, 98 | /* Already in use, or not ready yet */ 99 | EC_ERROR_BUSY = 6, 100 | /* Access denied */ 101 | EC_ERROR_ACCESS_DENIED = 7, 102 | /* Failed because component does not have power */ 103 | EC_ERROR_NOT_POWERED = 8, 104 | /* Failed because component is not calibrated */ 105 | EC_ERROR_NOT_CALIBRATED = 9, 106 | /* Failed because CRC error */ 107 | EC_ERROR_CRC = 10, 108 | /* Invalid console command param (PARAMn means parameter n is bad) */ 109 | EC_ERROR_PARAM1 = 11, 110 | EC_ERROR_PARAM2 = 12, 111 | EC_ERROR_PARAM3 = 13, 112 | EC_ERROR_PARAM4 = 14, 113 | EC_ERROR_PARAM5 = 15, 114 | EC_ERROR_PARAM6 = 16, 115 | EC_ERROR_PARAM7 = 17, 116 | EC_ERROR_PARAM8 = 18, 117 | EC_ERROR_PARAM9 = 19, 118 | EC_ERROR_PARAM_COUNT = 20, /* Wrong number of params */ 119 | 120 | EC_ERROR_NOT_HANDLED = 21, /* Interrupt event not handled */ 121 | 122 | /* Module-internal error codes may use this range. */ 123 | EC_ERROR_INTERNAL_FIRST = 0x10000, 124 | EC_ERROR_INTERNAL_LAST = 0x1FFFF 125 | }; 126 | 127 | class USB_TCPM 128 | { 129 | public: 130 | USB_TCPM() {} 131 | 132 | // Common methods for TCPM implementations 133 | virtual int init(void) =0; 134 | virtual int get_cc(int *cc1, int *cc2) =0; 135 | virtual int get_vbus_level(void) =0; 136 | virtual int select_rp_value(int rp) =0; 137 | virtual int set_cc(int pull) =0; 138 | virtual int set_polarity(int polarity) =0; 139 | virtual int set_vconn(int enable) =0; 140 | virtual int set_msg_header(int power_role, int data_role) =0; 141 | virtual int set_rx_enable(int enable) =0; 142 | virtual int get_message(uint32_t *payload, int *head) =0; 143 | virtual int transmit(enum tcpm_transmit_type type, 144 | uint16_t header, const uint32_t *data) =0; 145 | //int alert(void); 146 | 147 | static int get_num_bytes(uint16_t header); 148 | }; 149 | 150 | #endif /* USB_TCPM_H */ 151 | -------------------------------------------------------------------------------- /FUSB302.h: -------------------------------------------------------------------------------- 1 | /* 2 | FUSB302.h - Library for interacting with the FUSB302B chip. 3 | Copyright 2010 The Chromium OS Authors 4 | Copyright 2017 Jason Cerundolo 5 | Released under an MIT license. See LICENSE file. 6 | */ 7 | 8 | #ifndef FUSB302_H 9 | #define FUSB302_H 10 | 11 | #include "Arduino.h" 12 | #include 13 | #include "USB_TCPM.h" 14 | 15 | /* Chip Device ID - 302A or 302B */ 16 | #define FUSB302_DEVID_302A 0x08 17 | #define FUSB302_DEVID_302B 0x09 18 | 19 | /* I2C slave address varies by part number */ 20 | /* FUSB302BUCX / FUSB302BMPX */ 21 | #define FUSB302_I2C_SLAVE_ADDR 0x22 // 7-bit address for Arduino 22 | /* FUSB302B01MPX */ 23 | #define FUSB302_I2C_SLAVE_ADDR_B01 0x23 24 | /* FUSB302B10MPX */ 25 | #define FUSB302_I2C_SLAVE_ADDR_B10 0x24 26 | /* FUSB302B11MPX */ 27 | #define FUSB302_I2C_SLAVE_ADDR_B11 0x25 28 | 29 | /* Default retry count for transmitting */ 30 | #define PD_RETRY_COUNT 3 31 | 32 | /* Time to wait for TCPC to complete transmit */ 33 | #define PD_T_TCPC_TX_TIMEOUT (100*MSEC) 34 | 35 | #define TCPC_REG_DEVICE_ID 0x01 36 | 37 | #define TCPC_REG_SWITCHES0 0x02 38 | #define TCPC_REG_SWITCHES0_CC2_PU_EN (1<<7) 39 | #define TCPC_REG_SWITCHES0_CC1_PU_EN (1<<6) 40 | #define TCPC_REG_SWITCHES0_VCONN_CC2 (1<<5) 41 | #define TCPC_REG_SWITCHES0_VCONN_CC1 (1<<4) 42 | #define TCPC_REG_SWITCHES0_MEAS_CC2 (1<<3) 43 | #define TCPC_REG_SWITCHES0_MEAS_CC1 (1<<2) 44 | #define TCPC_REG_SWITCHES0_CC2_PD_EN (1<<1) 45 | #define TCPC_REG_SWITCHES0_CC1_PD_EN (1<<0) 46 | 47 | #define TCPC_REG_SWITCHES1 0x03 48 | #define TCPC_REG_SWITCHES1_POWERROLE (1<<7) 49 | #define TCPC_REG_SWITCHES1_SPECREV1 (1<<6) 50 | #define TCPC_REG_SWITCHES1_SPECREV0 (1<<5) 51 | #define TCPC_REG_SWITCHES1_DATAROLE (1<<4) 52 | #define TCPC_REG_SWITCHES1_AUTO_GCRC (1<<2) 53 | #define TCPC_REG_SWITCHES1_TXCC2_EN (1<<1) 54 | #define TCPC_REG_SWITCHES1_TXCC1_EN (1<<0) 55 | 56 | #define TCPC_REG_MEASURE 0x04 57 | #define TCPC_REG_MEASURE_VBUS (1<<6) 58 | #define TCPC_REG_MEASURE_MDAC_MV(mv) (((mv)/42) & 0x3f) 59 | 60 | #define TCPC_REG_CONTROL0 0x06 61 | #define TCPC_REG_CONTROL0_TX_FLUSH (1<<6) 62 | #define TCPC_REG_CONTROL0_INT_MASK (1<<5) 63 | #define TCPC_REG_CONTROL0_HOST_CUR_MASK (3<<2) 64 | #define TCPC_REG_CONTROL0_HOST_CUR_3A0 (3<<2) 65 | #define TCPC_REG_CONTROL0_HOST_CUR_1A5 (2<<2) 66 | #define TCPC_REG_CONTROL0_HOST_CUR_USB (1<<2) 67 | #define TCPC_REG_CONTROL0_TX_START (1<<0) 68 | 69 | #define TCPC_REG_CONTROL1 0x07 70 | #define TCPC_REG_CONTROL1_ENSOP2DB (1<<6) 71 | #define TCPC_REG_CONTROL1_ENSOP1DB (1<<5) 72 | #define TCPC_REG_CONTROL1_BIST_MODE2 (1<<4) 73 | #define TCPC_REG_CONTROL1_RX_FLUSH (1<<2) 74 | #define TCPC_REG_CONTROL1_ENSOP2 (1<<1) 75 | #define TCPC_REG_CONTROL1_ENSOP1 (1<<0) 76 | 77 | #define TCPC_REG_CONTROL2 0x08 78 | /* two-bit field, valid values below */ 79 | #define TCPC_REG_CONTROL2_MODE (1<<1) 80 | #define TCPC_REG_CONTROL2_MODE_DFP (0x3) 81 | #define TCPC_REG_CONTROL2_MODE_UFP (0x2) 82 | #define TCPC_REG_CONTROL2_MODE_DRP (0x1) 83 | #define TCPC_REG_CONTROL2_MODE_POS (1) 84 | #define TCPC_REG_CONTROL2_TOGGLE (1<<0) 85 | 86 | #define TCPC_REG_CONTROL3 0x09 87 | #define TCPC_REG_CONTROL3_SEND_HARDRESET (1<<6) 88 | #define TCPC_REG_CONTROL3_BIST_TMODE (1<<5) /* 302B Only */ 89 | #define TCPC_REG_CONTROL3_AUTO_HARDRESET (1<<4) 90 | #define TCPC_REG_CONTROL3_AUTO_SOFTRESET (1<<3) 91 | /* two-bit field */ 92 | #define TCPC_REG_CONTROL3_N_RETRIES (1<<1) 93 | #define TCPC_REG_CONTROL3_N_RETRIES_POS (1) 94 | #define TCPC_REG_CONTROL3_N_RETRIES_SIZE (2) 95 | #define TCPC_REG_CONTROL3_AUTO_RETRY (1<<0) 96 | 97 | #define TCPC_REG_MASK 0x0A 98 | #define TCPC_REG_MASK_VBUSOK (1<<7) 99 | #define TCPC_REG_MASK_ACTIVITY (1<<6) 100 | #define TCPC_REG_MASK_COMP_CHNG (1<<5) 101 | #define TCPC_REG_MASK_CRC_CHK (1<<4) 102 | #define TCPC_REG_MASK_ALERT (1<<3) 103 | #define TCPC_REG_MASK_WAKE (1<<2) 104 | #define TCPC_REG_MASK_COLLISION (1<<1) 105 | #define TCPC_REG_MASK_BC_LVL (1<<0) 106 | 107 | #define TCPC_REG_POWER 0x0B 108 | #define TCPC_REG_POWER_PWR (1<<0) /* four-bit field */ 109 | #define TCPC_REG_POWER_PWR_LOW 0x1 /* Bandgap + Wake circuitry */ 110 | #define TCPC_REG_POWER_PWR_MEDIUM 0x3 /* LOW + Receiver + Current refs */ 111 | #define TCPC_REG_POWER_PWR_HIGH 0x7 /* MEDIUM + Measure block */ 112 | #define TCPC_REG_POWER_PWR_ALL 0xF /* HIGH + Internal Oscillator */ 113 | 114 | #define TCPC_REG_RESET 0x0C 115 | #define TCPC_REG_RESET_PD_RESET (1<<1) 116 | #define TCPC_REG_RESET_SW_RESET (1<<0) 117 | 118 | #define TCPC_REG_MASKA 0x0E 119 | #define TCPC_REG_MASKA_OCP_TEMP (1<<7) 120 | #define TCPC_REG_MASKA_TOGDONE (1<<6) 121 | #define TCPC_REG_MASKA_SOFTFAIL (1<<5) 122 | #define TCPC_REG_MASKA_RETRYFAIL (1<<4) 123 | #define TCPC_REG_MASKA_HARDSENT (1<<3) 124 | #define TCPC_REG_MASKA_TX_SUCCESS (1<<2) 125 | #define TCPC_REG_MASKA_SOFTRESET (1<<1) 126 | #define TCPC_REG_MASKA_HARDRESET (1<<0) 127 | 128 | #define TCPC_REG_MASKB 0x0F 129 | #define TCPC_REG_MASKB_GCRCSENT (1<<0) 130 | 131 | #define TCPC_REG_STATUS0A 0x3C 132 | #define TCPC_REG_STATUS0A_SOFTFAIL (1<<5) 133 | #define TCPC_REG_STATUS0A_RETRYFAIL (1<<4) 134 | #define TCPC_REG_STATUS0A_POWER (1<<2) /* two-bit field */ 135 | #define TCPC_REG_STATUS0A_RX_SOFT_RESET (1<<1) 136 | #define TCPC_REG_STATUS0A_RX_HARD_RESEt (1<<0) 137 | 138 | #define TCPC_REG_STATUS1A 0x3D 139 | /* three-bit field, valid values below */ 140 | #define TCPC_REG_STATUS1A_TOGSS (1<<3) 141 | #define TCPC_REG_STATUS1A_TOGSS_RUNNING 0x0 142 | #define TCPC_REG_STATUS1A_TOGSS_SRC1 0x1 143 | #define TCPC_REG_STATUS1A_TOGSS_SRC2 0x2 144 | #define TCPC_REG_STATUS1A_TOGSS_SNK1 0x5 145 | #define TCPC_REG_STATUS1A_TOGSS_SNK2 0x6 146 | #define TCPC_REG_STATUS1A_TOGSS_AA 0x7 147 | #define TCPC_REG_STATUS1A_TOGSS_POS (3) 148 | #define TCPC_REG_STATUS1A_TOGSS_MASK (0x7) 149 | 150 | #define TCPC_REG_STATUS1A_RXSOP2DB (1<<2) 151 | #define TCPC_REG_STATUS1A_RXSOP1DB (1<<1) 152 | #define TCPC_REG_STATUS1A_RXSOP (1<<0) 153 | 154 | #define TCPC_REG_INTERRUPTA 0x3E 155 | #define TCPC_REG_INTERRUPTA_OCP_TEMP (1<<7) 156 | #define TCPC_REG_INTERRUPTA_TOGDONE (1<<6) 157 | #define TCPC_REG_INTERRUPTA_SOFTFAIL (1<<5) 158 | #define TCPC_REG_INTERRUPTA_RETRYFAIL (1<<4) 159 | #define TCPC_REG_INTERRUPTA_HARDSENT (1<<3) 160 | #define TCPC_REG_INTERRUPTA_TX_SUCCESS (1<<2) 161 | #define TCPC_REG_INTERRUPTA_SOFTRESET (1<<1) 162 | #define TCPC_REG_INTERRUPTA_HARDRESET (1<<0) 163 | 164 | #define TCPC_REG_INTERRUPTB 0x3F 165 | #define TCPC_REG_INTERRUPTB_GCRCSENT (1<<0) 166 | 167 | #define TCPC_REG_STATUS0 0x40 168 | #define TCPC_REG_STATUS0_VBUSOK (1<<7) 169 | #define TCPC_REG_STATUS0_ACTIVITY (1<<6) 170 | #define TCPC_REG_STATUS0_COMP (1<<5) 171 | #define TCPC_REG_STATUS0_CRC_CHK (1<<4) 172 | #define TCPC_REG_STATUS0_ALERT (1<<3) 173 | #define TCPC_REG_STATUS0_WAKE (1<<2) 174 | #define TCPC_REG_STATUS0_BC_LVL1 (1<<1) /* two-bit field */ 175 | #define TCPC_REG_STATUS0_BC_LVL0 (1<<0) /* two-bit field */ 176 | 177 | #define TCPC_REG_STATUS1 0x41 178 | #define TCPC_REG_STATUS1_RXSOP2 (1<<7) 179 | #define TCPC_REG_STATUS1_RXSOP1 (1<<6) 180 | #define TCPC_REG_STATUS1_RX_EMPTY (1<<5) 181 | #define TCPC_REG_STATUS1_RX_FULL (1<<4) 182 | #define TCPC_REG_STATUS1_TX_EMPTY (1<<3) 183 | #define TCPC_REG_STATUS1_TX_FULL (1<<2) 184 | 185 | #define TCPC_REG_INTERRUPT 0x42 186 | #define TCPC_REG_INTERRUPT_VBUSOK (1<<7) 187 | #define TCPC_REG_INTERRUPT_ACTIVITY (1<<6) 188 | #define TCPC_REG_INTERRUPT_COMP_CHNG (1<<5) 189 | #define TCPC_REG_INTERRUPT_CRC_CHK (1<<4) 190 | #define TCPC_REG_INTERRUPT_ALERT (1<<3) 191 | #define TCPC_REG_INTERRUPT_WAKE (1<<2) 192 | #define TCPC_REG_INTERRUPT_COLLISION (1<<1) 193 | #define TCPC_REG_INTERRUPT_BC_LVL (1<<0) 194 | 195 | #define TCPC_REG_FIFOS 0x43 196 | 197 | /* Tokens defined for the FUSB302 TX FIFO */ 198 | enum fusb302_txfifo_tokens { 199 | FUSB302_TKN_TXON = 0xA1, 200 | FUSB302_TKN_SYNC1 = 0x12, 201 | FUSB302_TKN_SYNC2 = 0x13, 202 | FUSB302_TKN_SYNC3 = 0x1B, 203 | FUSB302_TKN_RST1 = 0x15, 204 | FUSB302_TKN_RST2 = 0x16, 205 | FUSB302_TKN_PACKSYM = 0x80, 206 | FUSB302_TKN_JAMCRC = 0xFF, 207 | FUSB302_TKN_EOP = 0x14, 208 | FUSB302_TKN_TXOFF = 0xFE, 209 | }; 210 | 211 | class FUSB302 : public USB_TCPM 212 | { 213 | public: 214 | FUSB302(); 215 | 216 | // Common methods for TCPM implementations 217 | int init(void); 218 | int get_cc(int *cc1, int *cc2); 219 | int get_vbus_level(void); 220 | int select_rp_value(int rp); 221 | int set_cc(int pull); 222 | int set_polarity(int polarity); 223 | int set_vconn(int enable); 224 | int set_msg_header(int power_role, int data_role); 225 | int set_rx_enable(int enable); 226 | int get_message(uint32_t *payload, int *head); 227 | int transmit(enum tcpm_transmit_type type, 228 | uint16_t header, const uint32_t *data); 229 | //int alert(void); 230 | 231 | // Other methods made public for convenience 232 | void pd_reset(void); 233 | void auto_goodcrc_enable(int enable); 234 | int convert_bc_lvl(int bc_lvl); 235 | void detect_cc_pin_source_manual(int *cc1_lvl, int *cc2_lvl); 236 | int measure_cc_pin_source(int cc_measure); 237 | void detect_cc_pin_sink(int *cc1, int *cc2); 238 | int send_message(uint16_t header, const uint32_t *data, 239 | uint8_t *buf, int buf_pos); 240 | void flush_rx_fifo(void); 241 | void flush_tx_fifo(void); 242 | void clear_int_pin(void); 243 | void set_bist_test_data(void); 244 | int get_chip_id(int *id); 245 | uint32_t get_interrupt_reason(void); 246 | int tcpc_write(int reg, int val); 247 | int tcpc_read(int reg, int *val); 248 | int tcpc_xfer(const uint8_t *out, 249 | int out_size, uint8_t *in, 250 | int in_size, int flags); 251 | 252 | private: 253 | 254 | int cc_polarity; 255 | int vconn_enabled; 256 | // 1 = pulling up (DFP) 0 = pulling down (UFP) 257 | int pulling_up; 258 | int rx_enable; 259 | int dfp_toggling_on; 260 | int previous_pull; 261 | int togdone_pullup_cc1; 262 | int togdone_pullup_cc2; 263 | int tx_hard_reset_req; 264 | int set_cc_lock; 265 | uint8_t mdac_vnc; 266 | uint8_t mdac_rd; 267 | }; 268 | 269 | #endif /* FUSB302_H */ 270 | 271 | -------------------------------------------------------------------------------- /FUSB302.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | FUSB302.c - Library for interacting with the FUSB302B chip. 3 | Copyright 2010 The Chromium OS Authors 4 | Copyright 2017 Jason Cerundolo 5 | Released under an MIT license. See LICENSE file. 6 | */ 7 | 8 | #include "FUSB302.h" 9 | 10 | FUSB302::FUSB302() : USB_TCPM() 11 | { 12 | Wire.begin(); 13 | this->vconn_enabled = 0; 14 | } 15 | 16 | /* bring the FUSB302 out of reset after Hard Reset signaling */ 17 | void FUSB302::pd_reset() 18 | { 19 | this->tcpc_write(TCPC_REG_RESET, TCPC_REG_RESET_PD_RESET); 20 | } 21 | 22 | void FUSB302::auto_goodcrc_enable(int enable) { 23 | int reg; 24 | 25 | tcpc_read(TCPC_REG_SWITCHES1, ®); 26 | 27 | if (enable) 28 | reg |= TCPC_REG_SWITCHES1_AUTO_GCRC; 29 | else 30 | reg &= ~TCPC_REG_SWITCHES1_AUTO_GCRC; 31 | 32 | tcpc_write(TCPC_REG_SWITCHES1, reg); 33 | } 34 | 35 | void FUSB302::flush_rx_fifo() 36 | { 37 | /* 38 | * other bits in the register _should_ be 0 39 | * until the day we support other SOP* types... 40 | * then we'll have to keep a shadow of what this register 41 | * value should be so we don't clobber it here! 42 | */ 43 | this->tcpc_write(TCPC_REG_CONTROL1, TCPC_REG_CONTROL1_RX_FLUSH); 44 | } 45 | 46 | void FUSB302::flush_tx_fifo() 47 | { 48 | int reg; 49 | 50 | this->tcpc_read(TCPC_REG_CONTROL0, ®); 51 | reg |= TCPC_REG_CONTROL0_TX_FLUSH; 52 | this->tcpc_write(TCPC_REG_CONTROL0, reg); 53 | } 54 | 55 | /* Convert BC LVL values (in FUSB302) to Type-C CC Voltage Status */ 56 | int FUSB302::convert_bc_lvl(int bc_lvl) 57 | { 58 | /* assume OPEN unless one of the following conditions is true... */ 59 | int ret = TYPEC_CC_VOLT_OPEN; 60 | 61 | if (this->pulling_up) { 62 | if (bc_lvl == 0x00) 63 | ret = TYPEC_CC_VOLT_RA; 64 | else if (bc_lvl < 0x3) 65 | ret = TYPEC_CC_VOLT_RD; 66 | } else { 67 | if (bc_lvl == 0x1) 68 | ret = TYPEC_CC_VOLT_SNK_DEF; 69 | else if (bc_lvl == 0x2) 70 | ret = TYPEC_CC_VOLT_SNK_1_5; 71 | else if (bc_lvl == 0x3) 72 | ret = TYPEC_CC_VOLT_SNK_3_0; 73 | } 74 | 75 | return ret; 76 | } 77 | 78 | int FUSB302::measure_cc_pin_source(int cc_measure) 79 | { 80 | int switches0_reg; 81 | int reg; 82 | int cc_lvl; 83 | 84 | /* Read status register */ 85 | this->tcpc_read(TCPC_REG_SWITCHES0, ®); 86 | /* Save current value */ 87 | switches0_reg = reg; 88 | /* Clear pull-up register settings and measure bits */ 89 | reg &= ~(TCPC_REG_SWITCHES0_MEAS_CC1 | TCPC_REG_SWITCHES0_MEAS_CC2); 90 | /* Set desired pullup register bit */ 91 | if (cc_measure == TCPC_REG_SWITCHES0_MEAS_CC1) 92 | reg |= TCPC_REG_SWITCHES0_CC1_PU_EN; 93 | else 94 | reg |= TCPC_REG_SWITCHES0_CC2_PU_EN; 95 | /* Set CC measure bit */ 96 | reg |= cc_measure; 97 | 98 | /* Set measurement switch */ 99 | this->tcpc_write(TCPC_REG_SWITCHES0, reg); 100 | 101 | /* Set MDAC for Open vs Rd/Ra comparison */ 102 | this->tcpc_write(TCPC_REG_MEASURE, this->mdac_vnc); 103 | 104 | /* Wait on measurement */ 105 | delayMicroseconds(250); 106 | 107 | /* Read status register */ 108 | this->tcpc_read(TCPC_REG_STATUS0, ®); 109 | 110 | /* Assume open */ 111 | cc_lvl = TYPEC_CC_VOLT_OPEN; 112 | 113 | /* CC level is below the 'no connect' threshold (vOpen) */ 114 | if ((reg & TCPC_REG_STATUS0_COMP) == 0) { 115 | /* Set MDAC for Rd vs Ra comparison */ 116 | this->tcpc_write(TCPC_REG_MEASURE, this->mdac_rd); 117 | 118 | /* Wait on measurement */ 119 | delayMicroseconds(250); 120 | 121 | /* Read status register */ 122 | this->tcpc_read(TCPC_REG_STATUS0, ®); 123 | 124 | cc_lvl = (reg & TCPC_REG_STATUS0_COMP) ? TYPEC_CC_VOLT_RD 125 | : TYPEC_CC_VOLT_RA; 126 | } 127 | 128 | /* Restore SWITCHES0 register to its value prior */ 129 | this->tcpc_write(TCPC_REG_SWITCHES0, switches0_reg); 130 | 131 | return cc_lvl; 132 | } 133 | 134 | /* Determine cc pin state for source when in manual detect mode */ 135 | void FUSB302::detect_cc_pin_source_manual(int *cc1_lvl, int *cc2_lvl) 136 | { 137 | int cc1_measure = TCPC_REG_SWITCHES0_MEAS_CC1; 138 | int cc2_measure = TCPC_REG_SWITCHES0_MEAS_CC2; 139 | 140 | if (this->vconn_enabled) { 141 | /* If VCONN enabled, measure cc_pin that matches polarity */ 142 | if (this->cc_polarity) 143 | *cc2_lvl = this->measure_cc_pin_source(cc2_measure); 144 | else 145 | *cc1_lvl = this->measure_cc_pin_source(cc1_measure); 146 | } else { 147 | /* If VCONN not enabled, measure both cc1 and cc2 */ 148 | *cc1_lvl = this->measure_cc_pin_source(cc1_measure); 149 | *cc2_lvl = this->measure_cc_pin_source(cc2_measure); 150 | } 151 | 152 | } 153 | 154 | /* Determine cc pin state for sink */ 155 | void FUSB302::detect_cc_pin_sink(int *cc1, int *cc2) 156 | { 157 | int reg; 158 | int orig_meas_cc1; 159 | int orig_meas_cc2; 160 | int bc_lvl_cc1; 161 | int bc_lvl_cc2; 162 | 163 | /* 164 | * Measure CC1 first. 165 | */ 166 | this->tcpc_read(TCPC_REG_SWITCHES0, ®); 167 | 168 | /* save original state to be returned to later... */ 169 | if (reg & TCPC_REG_SWITCHES0_MEAS_CC1) 170 | orig_meas_cc1 = 1; 171 | else 172 | orig_meas_cc1 = 0; 173 | 174 | if (reg & TCPC_REG_SWITCHES0_MEAS_CC2) 175 | orig_meas_cc2 = 1; 176 | else 177 | orig_meas_cc2 = 0; 178 | 179 | /* Disable CC2 measurement switch, enable CC1 measurement switch */ 180 | reg &= ~TCPC_REG_SWITCHES0_MEAS_CC2; 181 | reg |= TCPC_REG_SWITCHES0_MEAS_CC1; 182 | 183 | this->tcpc_write(TCPC_REG_SWITCHES0, reg); 184 | 185 | /* CC1 is now being measured by FUSB302. */ 186 | 187 | /* Wait on measurement */ 188 | delayMicroseconds(250); 189 | 190 | this->tcpc_read(TCPC_REG_STATUS0, &bc_lvl_cc1); 191 | 192 | /* mask away unwanted bits */ 193 | bc_lvl_cc1 &= (TCPC_REG_STATUS0_BC_LVL0 | TCPC_REG_STATUS0_BC_LVL1); 194 | 195 | /* 196 | * Measure CC2 next. 197 | */ 198 | 199 | this->tcpc_read(TCPC_REG_SWITCHES0, ®); 200 | 201 | /* Disable CC1 measurement switch, enable CC2 measurement switch */ 202 | reg &= ~TCPC_REG_SWITCHES0_MEAS_CC1; 203 | reg |= TCPC_REG_SWITCHES0_MEAS_CC2; 204 | 205 | this->tcpc_write(TCPC_REG_SWITCHES0, reg); 206 | 207 | /* CC2 is now being measured by FUSB302. */ 208 | 209 | /* Wait on measurement */ 210 | delayMicroseconds(250); 211 | 212 | this->tcpc_read(TCPC_REG_STATUS0, &bc_lvl_cc2); 213 | 214 | /* mask away unwanted bits */ 215 | bc_lvl_cc2 &= (TCPC_REG_STATUS0_BC_LVL0 | TCPC_REG_STATUS0_BC_LVL1); 216 | 217 | *cc1 = convert_bc_lvl(bc_lvl_cc1); 218 | *cc2 = convert_bc_lvl(bc_lvl_cc2); 219 | 220 | /* return MEAS_CC1/2 switches to original state */ 221 | this->tcpc_read(TCPC_REG_SWITCHES0, ®); 222 | if (orig_meas_cc1) 223 | reg |= TCPC_REG_SWITCHES0_MEAS_CC1; 224 | else 225 | reg &= ~TCPC_REG_SWITCHES0_MEAS_CC1; 226 | if (orig_meas_cc2) 227 | reg |= TCPC_REG_SWITCHES0_MEAS_CC2; 228 | else 229 | reg &= ~TCPC_REG_SWITCHES0_MEAS_CC2; 230 | 231 | this->tcpc_write(TCPC_REG_SWITCHES0, reg); 232 | } 233 | 234 | int FUSB302::init(void) 235 | { 236 | int reg; 237 | 238 | /* set default */ 239 | this->cc_polarity = -1; 240 | 241 | this->previous_pull = TYPEC_CC_RD; 242 | /* set the voltage threshold for no connect detection (vOpen) */ 243 | this->mdac_vnc = TCPC_REG_MEASURE_MDAC_MV(PD_SRC_DEF_VNC_MV); 244 | /* set the voltage threshold for Rd vs Ra detection */ 245 | this->mdac_rd = TCPC_REG_MEASURE_MDAC_MV(PD_SRC_DEF_RD_THRESH_MV); 246 | 247 | /* all other variables assumed to default to 0 */ 248 | 249 | /* Restore default settings */ 250 | this->tcpc_write(TCPC_REG_RESET, TCPC_REG_RESET_SW_RESET); 251 | 252 | /* Turn on retries and set number of retries */ 253 | this->tcpc_read(TCPC_REG_CONTROL3, ®); 254 | reg |= TCPC_REG_CONTROL3_AUTO_RETRY; 255 | reg |= (PD_RETRY_COUNT & 0x3) << 256 | TCPC_REG_CONTROL3_N_RETRIES_POS; 257 | this->tcpc_write(TCPC_REG_CONTROL3, reg); 258 | 259 | /* Create interrupt masks */ 260 | reg = 0xFF; 261 | /* CC level changes */ 262 | reg &= ~TCPC_REG_MASK_BC_LVL; 263 | /* collisions */ 264 | reg &= ~TCPC_REG_MASK_COLLISION; 265 | /* misc alert */ 266 | reg &= ~TCPC_REG_MASK_ALERT; 267 | this->tcpc_write(TCPC_REG_MASK, reg); 268 | 269 | reg = 0xFF; 270 | /* when all pd message retries fail... */ 271 | reg &= ~TCPC_REG_MASKA_RETRYFAIL; 272 | /* when fusb302 send a hard reset. */ 273 | reg &= ~TCPC_REG_MASKA_HARDSENT; 274 | /* when fusb302 receives GoodCRC ack for a pd message */ 275 | reg &= ~TCPC_REG_MASKA_TX_SUCCESS; 276 | /* when fusb302 receives a hard reset */ 277 | reg &= ~TCPC_REG_MASKA_HARDRESET; 278 | this->tcpc_write(TCPC_REG_MASKA, reg); 279 | 280 | reg = 0xFF; 281 | /* when fusb302 sends GoodCRC to ack a pd message */ 282 | reg &= ~TCPC_REG_MASKB_GCRCSENT; 283 | this->tcpc_write(TCPC_REG_MASKB, reg); 284 | 285 | /* Interrupt Enable */ 286 | this->tcpc_read(TCPC_REG_CONTROL0, ®); 287 | reg &= ~TCPC_REG_CONTROL0_INT_MASK; 288 | this->tcpc_write(TCPC_REG_CONTROL0, reg); 289 | 290 | /* Set VCONN switch defaults */ 291 | this->set_polarity(0); 292 | this->set_vconn(0); 293 | 294 | /* Turn on the power! */ 295 | /* TODO: Reduce power consumption */ 296 | this->tcpc_write(TCPC_REG_POWER, TCPC_REG_POWER_PWR_ALL); 297 | 298 | return 0; 299 | } 300 | 301 | /** 302 | * Set polarity 303 | * 304 | * @param polarity 0=> transmit on CC1, 1=> transmit on CC2 305 | * 306 | * @return 0 or error 307 | */ 308 | int FUSB302::set_polarity(int polarity) 309 | { 310 | /* Port polarity : 0 => CC1 is CC line, 1 => CC2 is CC line */ 311 | int reg; 312 | 313 | this->tcpc_read(TCPC_REG_SWITCHES0, ®); 314 | 315 | /* clear VCONN switch bits */ 316 | reg &= ~TCPC_REG_SWITCHES0_VCONN_CC1; 317 | reg &= ~TCPC_REG_SWITCHES0_VCONN_CC2; 318 | 319 | if (this->vconn_enabled) { 320 | /* set VCONN switch to be non-CC line */ 321 | if (polarity) 322 | reg |= TCPC_REG_SWITCHES0_VCONN_CC1; 323 | else 324 | reg |= TCPC_REG_SWITCHES0_VCONN_CC2; 325 | } 326 | 327 | /* clear meas_cc bits (RX line select) */ 328 | reg &= ~TCPC_REG_SWITCHES0_MEAS_CC1; 329 | reg &= ~TCPC_REG_SWITCHES0_MEAS_CC2; 330 | 331 | /* set rx polarity */ 332 | if (polarity) 333 | reg |= TCPC_REG_SWITCHES0_MEAS_CC2; 334 | else 335 | reg |= TCPC_REG_SWITCHES0_MEAS_CC1; 336 | 337 | this->tcpc_write(TCPC_REG_SWITCHES0, reg); 338 | 339 | this->tcpc_read(TCPC_REG_SWITCHES1, ®); 340 | 341 | /* clear tx_cc bits */ 342 | reg &= ~TCPC_REG_SWITCHES1_TXCC1_EN; 343 | reg &= ~TCPC_REG_SWITCHES1_TXCC2_EN; 344 | 345 | /* set tx polarity */ 346 | if (polarity) 347 | reg |= TCPC_REG_SWITCHES1_TXCC2_EN; 348 | else 349 | reg |= TCPC_REG_SWITCHES1_TXCC1_EN; 350 | 351 | // Enable auto GoodCRC sending 352 | reg |= TCPC_REG_SWITCHES1_AUTO_GCRC; 353 | 354 | this->tcpc_write(TCPC_REG_SWITCHES1, reg); 355 | 356 | /* Save the polarity for later */ 357 | this->cc_polarity = polarity; 358 | 359 | return 0; 360 | } 361 | 362 | int FUSB302::set_vconn(int enable) 363 | { 364 | /* 365 | * FUSB302 does not have dedicated VCONN Enable switch. 366 | * We'll get through this by disabling both of the 367 | * VCONN - CC* switches to disable, and enabling the 368 | * saved polarity when enabling. 369 | * Therefore at startup, set_polarity should be called first, 370 | * or else live with the default put into init. 371 | */ 372 | int reg; 373 | 374 | /* save enable state for later use */ 375 | this->vconn_enabled = enable; 376 | 377 | if (enable) { 378 | /* set to saved polarity */ 379 | set_polarity(this->cc_polarity); 380 | } else { 381 | 382 | this->tcpc_read(TCPC_REG_SWITCHES0, ®); 383 | 384 | /* clear VCONN switch bits */ 385 | reg &= ~TCPC_REG_SWITCHES0_VCONN_CC1; 386 | reg &= ~TCPC_REG_SWITCHES0_VCONN_CC2; 387 | 388 | this->tcpc_write(TCPC_REG_SWITCHES0, reg); 389 | } 390 | 391 | return 0; 392 | } 393 | 394 | int FUSB302::set_msg_header(int power_role, int data_role) 395 | { 396 | int reg; 397 | 398 | this->tcpc_read(TCPC_REG_SWITCHES1, ®); 399 | 400 | reg &= ~TCPC_REG_SWITCHES1_POWERROLE; 401 | reg &= ~TCPC_REG_SWITCHES1_DATAROLE; 402 | 403 | if (power_role) 404 | reg |= TCPC_REG_SWITCHES1_POWERROLE; 405 | if (data_role) 406 | reg |= TCPC_REG_SWITCHES1_DATAROLE; 407 | 408 | this->tcpc_write(TCPC_REG_SWITCHES1, reg); 409 | 410 | return 0; 411 | } 412 | 413 | int FUSB302::set_rx_enable(int enable) 414 | { 415 | int reg; 416 | 417 | this->rx_enable = enable; 418 | 419 | /* Get current switch state */ 420 | this->tcpc_read(TCPC_REG_SWITCHES0, ®); 421 | 422 | /* Clear CC1/CC2 measure bits */ 423 | reg &= ~TCPC_REG_SWITCHES0_MEAS_CC1; 424 | reg &= ~TCPC_REG_SWITCHES0_MEAS_CC2; 425 | 426 | if (enable) { 427 | switch (this->cc_polarity) { 428 | /* if CC polarity hasnt been determined, can't enable */ 429 | case -1: 430 | return EC_ERROR_UNKNOWN; 431 | case 0: 432 | reg |= TCPC_REG_SWITCHES0_MEAS_CC1; 433 | break; 434 | case 1: 435 | reg |= TCPC_REG_SWITCHES0_MEAS_CC2; 436 | break; 437 | default: 438 | /* "shouldn't get here" */ 439 | return EC_ERROR_UNKNOWN; 440 | } 441 | this->tcpc_write(TCPC_REG_SWITCHES0, reg); 442 | 443 | tcpc_read(TCPC_REG_SWITCHES1, ®); 444 | reg |= TCPC_REG_SWITCHES1_AUTO_GCRC; 445 | tcpc_write(TCPC_REG_SWITCHES1, reg); 446 | 447 | /* flush rx fifo in case messages have been coming our way */ 448 | this->flush_rx_fifo(); 449 | 450 | 451 | } else { 452 | /* 453 | * bit of a hack here. 454 | * when this function is called to disable rx (enable=0) 455 | * using it as an indication of detach (gulp!) 456 | * to reset our knowledge of where 457 | * the toggle state machine landed. 458 | */ 459 | this->togdone_pullup_cc1 = 0; 460 | this->togdone_pullup_cc2 = 0; 461 | 462 | this->set_cc(this->previous_pull); 463 | 464 | this->tcpc_write(TCPC_REG_SWITCHES0, reg); 465 | 466 | tcpc_read(TCPC_REG_SWITCHES1, ®); 467 | reg &= ~(TCPC_REG_SWITCHES1_AUTO_GCRC); 468 | tcpc_write(TCPC_REG_SWITCHES1, reg); 469 | } 470 | 471 | this->auto_goodcrc_enable(enable); 472 | 473 | return 0; 474 | } 475 | /* 476 | * Make sure to allocate enough memory for *payload. 477 | * 478 | * Maximum size is (max number of data objects + 1) * 4 479 | * The extra "+1" is for the CRC32 calculation 480 | */ 481 | int FUSB302::get_message(uint32_t *payload, int *head) 482 | { 483 | /* 484 | * this is the buffer that will get the burst-read data 485 | * from the fusb302. 486 | * 487 | * it's re-used in a couple different spots, the worst of which 488 | * is the PD packet (not header) and CRC. 489 | * maximum size necessary = 28 + 4 = 32 490 | */ 491 | uint8_t buf[32]; 492 | int rv = 0; 493 | int len; 494 | 495 | /* NOTE: Assuming enough memory has been allocated for payload. */ 496 | 497 | /* 498 | * PART 1 OF BURST READ: Write in register address. 499 | * Issue a START, no STOP. 500 | */ 501 | //tcpc_lock(port, 1); 502 | buf[0] = TCPC_REG_FIFOS; 503 | rv |= this->tcpc_xfer(buf, 1, 0, 0, I2C_XFER_START); 504 | 505 | /* 506 | * PART 2 OF BURST READ: Read up to the header. 507 | * Issue a repeated START, no STOP. 508 | * only grab three bytes so we can get the header 509 | * and determine how many more bytes we need to read. 510 | */ 511 | rv |= this->tcpc_xfer(0, 0, buf, 3, I2C_XFER_START); 512 | 513 | /* Grab the header */ 514 | *head = (buf[1] & 0xFF); 515 | *head |= ((buf[2] << 8) & 0xFF00); 516 | 517 | /* figure out packet length, subtract header bytes */ 518 | len = get_num_bytes(*head) - 2; 519 | 520 | /* 521 | * PART 3 OF BURST READ: Read everything else. 522 | * No START, but do issue a STOP at the end. 523 | * add 4 to len to read CRC out 524 | */ 525 | rv |= this->tcpc_xfer(0, 0, buf, len+4, I2C_XFER_STOP); 526 | 527 | //tcpc_lock(port, 0); 528 | 529 | /* return the data */ 530 | memcpy(payload, buf, len+4); 531 | 532 | return rv; 533 | } 534 | 535 | int FUSB302::send_message(uint16_t header, const uint32_t *data, 536 | uint8_t *buf, int buf_pos) 537 | { 538 | int rv; 539 | int reg; 540 | int len; 541 | 542 | len = get_num_bytes(header); 543 | 544 | /* 545 | * packsym tells the TXFIFO that the next X bytes are payload, 546 | * and should not be interpreted as special tokens. 547 | * The 5 LSBs represent X, the number of bytes. 548 | */ 549 | reg = FUSB302_TKN_PACKSYM; 550 | reg |= (len & 0x1F); 551 | 552 | buf[buf_pos++] = reg; 553 | 554 | /* write in the header */ 555 | reg = header; 556 | buf[buf_pos++] = reg & 0xFF; 557 | 558 | reg >>= 8; 559 | buf[buf_pos++] = reg & 0xFF; 560 | 561 | /* header is done, subtract from length to make this for-loop simpler */ 562 | len -= 2; 563 | 564 | /* write data objects, if present */ 565 | memcpy(&buf[buf_pos], data, len); 566 | buf_pos += len; 567 | 568 | /* put in the CRC */ 569 | buf[buf_pos++] = FUSB302_TKN_JAMCRC; 570 | 571 | /* put in EOP */ 572 | buf[buf_pos++] = FUSB302_TKN_EOP; 573 | 574 | /* Turn transmitter off after sending message */ 575 | buf[buf_pos++] = FUSB302_TKN_TXOFF; 576 | 577 | /* Start transmission */ 578 | reg = FUSB302_TKN_TXON; 579 | buf[buf_pos++] = FUSB302_TKN_TXON; 580 | 581 | /* burst write for speed! */ 582 | rv = tcpc_xfer(buf, buf_pos, 0, 0, I2C_XFER_SINGLE); 583 | 584 | return rv; 585 | } 586 | 587 | int FUSB302::transmit(enum tcpm_transmit_type type, 588 | uint16_t header, const uint32_t *data) 589 | { 590 | /* 591 | * this is the buffer that will be burst-written into the fusb302 592 | * maximum size necessary = 593 | * 1: FIFO register address 594 | * 4: SOP* tokens 595 | * 1: Token that signifies "next X bytes are not tokens" 596 | * 30: 2 for header and up to 7*4 = 28 for rest of message 597 | * 1: "Insert CRC" Token 598 | * 1: EOP Token 599 | * 1: "Turn transmitter off" token 600 | * 1: "Star Transmission" Command 601 | * - 602 | * 40: 40 bytes worst-case 603 | */ 604 | uint8_t buf[40]; 605 | int buf_pos = 0; 606 | 607 | int reg; 608 | 609 | /* Flush the TXFIFO */ 610 | this->flush_tx_fifo(); 611 | 612 | switch (type) { 613 | case TCPC_TX_SOP: 614 | 615 | /* put register address first for of burst tcpc write */ 616 | buf[buf_pos++] = TCPC_REG_FIFOS; 617 | 618 | /* Write the SOP Ordered Set into TX FIFO */ 619 | buf[buf_pos++] = FUSB302_TKN_SYNC1; 620 | buf[buf_pos++] = FUSB302_TKN_SYNC1; 621 | buf[buf_pos++] = FUSB302_TKN_SYNC1; 622 | buf[buf_pos++] = FUSB302_TKN_SYNC2; 623 | 624 | return this->send_message(header, data, buf, buf_pos); 625 | case TCPC_TX_HARD_RESET: 626 | this->tx_hard_reset_req = 1; 627 | 628 | /* Simply hit the SEND_HARD_RESET bit */ 629 | this->tcpc_read(TCPC_REG_CONTROL3, ®); 630 | reg |= TCPC_REG_CONTROL3_SEND_HARDRESET; 631 | this->tcpc_write(TCPC_REG_CONTROL3, reg); 632 | 633 | break; 634 | case TCPC_TX_BIST_MODE_2: 635 | /* Simply hit the BIST_MODE2 bit */ 636 | this->tcpc_read(TCPC_REG_CONTROL1, ®); 637 | reg |= TCPC_REG_CONTROL1_BIST_MODE2; 638 | this->tcpc_write(TCPC_REG_CONTROL1, reg); 639 | break; 640 | default: 641 | return EC_ERROR_UNIMPLEMENTED; 642 | } 643 | 644 | return 0; 645 | } 646 | 647 | /** 648 | * Set the value of the CC pull-up used when we are a source. 649 | * 650 | * @param rp One of enum tcpc_rp_value 651 | * 652 | * @return 0 or error 653 | */ 654 | int FUSB302::select_rp_value(int rp) { 655 | int reg; 656 | int rv; 657 | uint8_t vnc, rd; 658 | 659 | rv = tcpc_read(TCPC_REG_CONTROL0, ®); 660 | if (rv) { 661 | return rv; 662 | } 663 | 664 | /* Set the current source for Rp value */ 665 | reg &= ~TCPC_REG_CONTROL0_HOST_CUR_MASK; 666 | switch (rp) { 667 | case TYPEC_RP_1A5: 668 | reg |= TCPC_REG_CONTROL0_HOST_CUR_1A5; 669 | vnc = TCPC_REG_MEASURE_MDAC_MV(PD_SRC_1_5_VNC_MV); 670 | rd = TCPC_REG_MEASURE_MDAC_MV(PD_SRC_1_5_RD_THRESH_MV); 671 | break; 672 | case TYPEC_RP_3A0: 673 | reg |= TCPC_REG_CONTROL0_HOST_CUR_3A0; 674 | vnc = TCPC_REG_MEASURE_MDAC_MV(PD_SRC_3_0_VNC_MV); 675 | rd = TCPC_REG_MEASURE_MDAC_MV(PD_SRC_3_0_RD_THRESH_MV); 676 | break; 677 | case TYPEC_RP_USB: 678 | default: 679 | reg |= TCPC_REG_CONTROL0_HOST_CUR_USB; 680 | vnc = TCPC_REG_MEASURE_MDAC_MV(PD_SRC_DEF_VNC_MV); 681 | rd = TCPC_REG_MEASURE_MDAC_MV(PD_SRC_DEF_RD_THRESH_MV); 682 | } 683 | this->mdac_vnc = vnc; 684 | this->mdac_rd = rd; 685 | return tcpc_write(TCPC_REG_CONTROL0, reg); 686 | } 687 | 688 | int FUSB302::get_vbus_level() 689 | { 690 | int reg; 691 | 692 | /* Read status register */ 693 | this->tcpc_read(TCPC_REG_STATUS0, ®); 694 | 695 | return (reg & TCPC_REG_STATUS0_VBUSOK) ? 1 : 0; 696 | } 697 | 698 | int FUSB302::get_cc(int *cc1, int *cc2) 699 | { 700 | /* 701 | * can't measure while doing DFP toggling - 702 | * FUSB302 takes control of the switches. 703 | * During this time, tell software that CCs are open - 704 | * at least until we get the TOGDONE interrupt... 705 | * which signals that the hardware found something. 706 | */ 707 | if (this->dfp_toggling_on) { 708 | *cc1 = TYPEC_CC_VOLT_OPEN; 709 | *cc2 = TYPEC_CC_VOLT_OPEN; 710 | return 0; 711 | } 712 | 713 | if (this->pulling_up) { 714 | /* Source mode? */ 715 | this->detect_cc_pin_source_manual(cc1, cc2); 716 | } else { 717 | /* Sink mode? */ 718 | this->detect_cc_pin_sink(cc1, cc2); 719 | } 720 | 721 | return 0; 722 | } 723 | 724 | int FUSB302::set_cc(int pull) 725 | { 726 | int reg; 727 | 728 | /* 729 | * Ensure we aren't in the process of changing CC from the alert 730 | * handler, then cancel any pending toggle-triggered CC change. 731 | */ 732 | //mutex_lock(&this->set_cc_lock); 733 | this->dfp_toggling_on = 0; 734 | //mutex_unlock(&this->set_cc_lock); 735 | 736 | this->previous_pull = pull; 737 | 738 | /* NOTE: FUSB302 toggles a single pull-up between CC1 and CC2 */ 739 | /* NOTE: FUSB302 Does not support Ra. */ 740 | switch (pull) { 741 | case TYPEC_CC_RP: 742 | /* enable the pull-up we know to be necessary */ 743 | tcpc_read(TCPC_REG_SWITCHES0, ®); 744 | 745 | reg &= ~(TCPC_REG_SWITCHES0_CC2_PU_EN | 746 | TCPC_REG_SWITCHES0_CC1_PU_EN | 747 | TCPC_REG_SWITCHES0_CC1_PD_EN | 748 | TCPC_REG_SWITCHES0_CC2_PD_EN | 749 | TCPC_REG_SWITCHES0_VCONN_CC1 | 750 | TCPC_REG_SWITCHES0_VCONN_CC2); 751 | 752 | reg |= TCPC_REG_SWITCHES0_CC1_PU_EN | 753 | TCPC_REG_SWITCHES0_CC2_PU_EN; 754 | 755 | if (this->vconn_enabled) 756 | reg |= this->togdone_pullup_cc1 ? 757 | TCPC_REG_SWITCHES0_VCONN_CC2 : 758 | TCPC_REG_SWITCHES0_VCONN_CC1; 759 | 760 | tcpc_write(TCPC_REG_SWITCHES0, reg); 761 | 762 | this->pulling_up = 1; 763 | this->dfp_toggling_on = 0; 764 | break; 765 | case TYPEC_CC_RD: 766 | /* Enable UFP Mode */ 767 | 768 | /* turn off toggle */ 769 | tcpc_read(TCPC_REG_CONTROL2, ®); 770 | reg &= ~TCPC_REG_CONTROL2_TOGGLE; 771 | tcpc_write(TCPC_REG_CONTROL2, reg); 772 | 773 | /* enable pull-downs, disable pullups */ 774 | tcpc_read(TCPC_REG_SWITCHES0, ®); 775 | 776 | reg &= ~(TCPC_REG_SWITCHES0_CC2_PU_EN); 777 | reg &= ~(TCPC_REG_SWITCHES0_CC1_PU_EN); 778 | reg |= (TCPC_REG_SWITCHES0_CC1_PD_EN); 779 | reg |= (TCPC_REG_SWITCHES0_CC2_PD_EN); 780 | tcpc_write(TCPC_REG_SWITCHES0, reg); 781 | 782 | this->pulling_up = 0; 783 | this->dfp_toggling_on = 0; 784 | break; 785 | case TYPEC_CC_OPEN: 786 | /* Disable toggling */ 787 | tcpc_read(TCPC_REG_CONTROL2, ®); 788 | reg &= ~TCPC_REG_CONTROL2_TOGGLE; 789 | tcpc_write(TCPC_REG_CONTROL2, reg); 790 | 791 | /* Ensure manual switches are opened */ 792 | tcpc_read(TCPC_REG_SWITCHES0, ®); 793 | reg &= ~TCPC_REG_SWITCHES0_CC1_PU_EN; 794 | reg &= ~TCPC_REG_SWITCHES0_CC2_PU_EN; 795 | reg &= ~TCPC_REG_SWITCHES0_CC1_PD_EN; 796 | reg &= ~TCPC_REG_SWITCHES0_CC2_PD_EN; 797 | tcpc_write(TCPC_REG_SWITCHES0, reg); 798 | 799 | this->pulling_up = 0; 800 | this->dfp_toggling_on = 0; 801 | break; 802 | default: 803 | /* Unsupported... */ 804 | return EC_ERROR_UNIMPLEMENTED; 805 | } 806 | return 0; 807 | } 808 | 809 | void FUSB302::set_bist_test_data() 810 | { 811 | int reg; 812 | 813 | /* Read control3 register */ 814 | this->tcpc_read(TCPC_REG_CONTROL3, ®); 815 | 816 | /* Set the BIST_TMODE bit (Clears on Hard Reset) */ 817 | reg |= TCPC_REG_CONTROL3_BIST_TMODE; 818 | 819 | /* Write the updated value */ 820 | this->tcpc_write(TCPC_REG_CONTROL3, reg); 821 | } 822 | 823 | int FUSB302::get_chip_id(int *id) { 824 | int return_val; 825 | return_val = this->tcpc_read(TCPC_REG_DEVICE_ID, id); 826 | return return_val; 827 | } 828 | 829 | uint32_t FUSB302::get_interrupt_reason(void) { 830 | int res; 831 | uint32_t return_val; 832 | this->tcpc_read(TCPC_REG_INTERRUPTA, &res); 833 | return_val = ((res&0xFFl) << 16); 834 | this->tcpc_read(TCPC_REG_INTERRUPTB, &res); 835 | return_val |= ((res&0xFF) << 8); 836 | this->tcpc_read(TCPC_REG_INTERRUPT, &res); 837 | return_val |= (res&0xFF); 838 | 839 | return return_val; 840 | } 841 | 842 | int FUSB302::tcpc_write(int reg, int val) { 843 | Wire.beginTransmission(FUSB302_I2C_SLAVE_ADDR); 844 | Wire.write(reg & 0xFF); 845 | Wire.write(val & 0xFF); 846 | Wire.endTransmission(); 847 | 848 | return 0; 849 | } 850 | 851 | int FUSB302::tcpc_read(int reg, int *val) { 852 | Wire.beginTransmission(FUSB302_I2C_SLAVE_ADDR); 853 | Wire.write(reg & 0xFF); 854 | Wire.endTransmission(false); 855 | Wire.requestFrom(FUSB302_I2C_SLAVE_ADDR, 1, true); 856 | *val = Wire.read(); 857 | 858 | return 0; 859 | } 860 | 861 | int FUSB302::tcpc_xfer(const uint8_t *out, int out_size, 862 | uint8_t *in, int in_size, int flags) { 863 | Wire.beginTransmission(FUSB302_I2C_SLAVE_ADDR); 864 | for (; out_size>0; out_size--) { 865 | Wire.write(*out); 866 | out++; 867 | } 868 | 869 | if (in_size) { 870 | Wire.endTransmission(false); 871 | Wire.requestFrom(FUSB302_I2C_SLAVE_ADDR, in_size, (flags & I2C_XFER_STOP)); 872 | for (; in_size>0; in_size--) { 873 | *in = Wire.read(); 874 | in++; 875 | } 876 | } else { 877 | Wire.endTransmission(flags & I2C_XFER_STOP); 878 | } 879 | } 880 | 881 | 882 | void FUSB302::clear_int_pin(void) { 883 | int res; 884 | this->tcpc_read(TCPC_REG_INTERRUPTA, &res); 885 | this->tcpc_read(TCPC_REG_INTERRUPTB, &res); 886 | this->tcpc_read(TCPC_REG_INTERRUPT, &res); 887 | } --------------------------------------------------------------------------------