Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Power Supply for UCSI
0004  *
0005  * Copyright (C) 2020, Intel Corporation
0006  * Author: K V, Abhilash <abhilash.k.v@intel.com>
0007  * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
0008  */
0009 
0010 #include <linux/property.h>
0011 #include <linux/usb/pd.h>
0012 
0013 #include "ucsi.h"
0014 
0015 /* Power Supply access to expose source power information */
0016 enum ucsi_psy_online_states {
0017     UCSI_PSY_OFFLINE = 0,
0018     UCSI_PSY_FIXED_ONLINE,
0019     UCSI_PSY_PROG_ONLINE,
0020 };
0021 
0022 static enum power_supply_property ucsi_psy_props[] = {
0023     POWER_SUPPLY_PROP_USB_TYPE,
0024     POWER_SUPPLY_PROP_ONLINE,
0025     POWER_SUPPLY_PROP_VOLTAGE_MIN,
0026     POWER_SUPPLY_PROP_VOLTAGE_MAX,
0027     POWER_SUPPLY_PROP_VOLTAGE_NOW,
0028     POWER_SUPPLY_PROP_CURRENT_MAX,
0029     POWER_SUPPLY_PROP_CURRENT_NOW,
0030 };
0031 
0032 static int ucsi_psy_get_online(struct ucsi_connector *con,
0033                    union power_supply_propval *val)
0034 {
0035     val->intval = UCSI_PSY_OFFLINE;
0036     if (con->status.flags & UCSI_CONSTAT_CONNECTED &&
0037         (con->status.flags & UCSI_CONSTAT_PWR_DIR) == TYPEC_SINK)
0038         val->intval = UCSI_PSY_FIXED_ONLINE;
0039     return 0;
0040 }
0041 
0042 static int ucsi_psy_get_voltage_min(struct ucsi_connector *con,
0043                     union power_supply_propval *val)
0044 {
0045     u32 pdo;
0046 
0047     switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) {
0048     case UCSI_CONSTAT_PWR_OPMODE_PD:
0049         pdo = con->src_pdos[0];
0050         val->intval = pdo_fixed_voltage(pdo) * 1000;
0051         break;
0052     case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
0053     case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
0054     case UCSI_CONSTAT_PWR_OPMODE_BC:
0055     case UCSI_CONSTAT_PWR_OPMODE_DEFAULT:
0056         val->intval = UCSI_TYPEC_VSAFE5V * 1000;
0057         break;
0058     default:
0059         val->intval = 0;
0060         break;
0061     }
0062     return 0;
0063 }
0064 
0065 static int ucsi_psy_get_voltage_max(struct ucsi_connector *con,
0066                     union power_supply_propval *val)
0067 {
0068     u32 pdo;
0069 
0070     switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) {
0071     case UCSI_CONSTAT_PWR_OPMODE_PD:
0072         if (con->num_pdos > 0) {
0073             pdo = con->src_pdos[con->num_pdos - 1];
0074             val->intval = pdo_fixed_voltage(pdo) * 1000;
0075         } else {
0076             val->intval = 0;
0077         }
0078         break;
0079     case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
0080     case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
0081     case UCSI_CONSTAT_PWR_OPMODE_BC:
0082     case UCSI_CONSTAT_PWR_OPMODE_DEFAULT:
0083         val->intval = UCSI_TYPEC_VSAFE5V * 1000;
0084         break;
0085     default:
0086         val->intval = 0;
0087         break;
0088     }
0089     return 0;
0090 }
0091 
0092 static int ucsi_psy_get_voltage_now(struct ucsi_connector *con,
0093                     union power_supply_propval *val)
0094 {
0095     int index;
0096     u32 pdo;
0097 
0098     switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) {
0099     case UCSI_CONSTAT_PWR_OPMODE_PD:
0100         index = rdo_index(con->rdo);
0101         if (index > 0) {
0102             pdo = con->src_pdos[index - 1];
0103             val->intval = pdo_fixed_voltage(pdo) * 1000;
0104         } else {
0105             val->intval = 0;
0106         }
0107         break;
0108     case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
0109     case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
0110     case UCSI_CONSTAT_PWR_OPMODE_BC:
0111     case UCSI_CONSTAT_PWR_OPMODE_DEFAULT:
0112         val->intval = UCSI_TYPEC_VSAFE5V * 1000;
0113         break;
0114     default:
0115         val->intval = 0;
0116         break;
0117     }
0118     return 0;
0119 }
0120 
0121 static int ucsi_psy_get_current_max(struct ucsi_connector *con,
0122                     union power_supply_propval *val)
0123 {
0124     u32 pdo;
0125 
0126     switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) {
0127     case UCSI_CONSTAT_PWR_OPMODE_PD:
0128         if (con->num_pdos > 0) {
0129             pdo = con->src_pdos[con->num_pdos - 1];
0130             val->intval = pdo_max_current(pdo) * 1000;
0131         } else {
0132             val->intval = 0;
0133         }
0134         break;
0135     case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
0136         val->intval = UCSI_TYPEC_1_5_CURRENT * 1000;
0137         break;
0138     case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
0139         val->intval = UCSI_TYPEC_3_0_CURRENT * 1000;
0140         break;
0141     case UCSI_CONSTAT_PWR_OPMODE_BC:
0142     case UCSI_CONSTAT_PWR_OPMODE_DEFAULT:
0143     /* UCSI can't tell b/w DCP/CDP or USB2/3x1/3x2 SDP chargers */
0144     default:
0145         val->intval = 0;
0146         break;
0147     }
0148     return 0;
0149 }
0150 
0151 static int ucsi_psy_get_current_now(struct ucsi_connector *con,
0152                     union power_supply_propval *val)
0153 {
0154     u16 flags = con->status.flags;
0155 
0156     if (UCSI_CONSTAT_PWR_OPMODE(flags) == UCSI_CONSTAT_PWR_OPMODE_PD)
0157         val->intval = rdo_op_current(con->rdo) * 1000;
0158     else
0159         val->intval = 0;
0160     return 0;
0161 }
0162 
0163 static int ucsi_psy_get_usb_type(struct ucsi_connector *con,
0164                  union power_supply_propval *val)
0165 {
0166     u16 flags = con->status.flags;
0167 
0168     val->intval = POWER_SUPPLY_USB_TYPE_C;
0169     if (flags & UCSI_CONSTAT_CONNECTED &&
0170         UCSI_CONSTAT_PWR_OPMODE(flags) == UCSI_CONSTAT_PWR_OPMODE_PD)
0171         val->intval = POWER_SUPPLY_USB_TYPE_PD;
0172 
0173     return 0;
0174 }
0175 
0176 static int ucsi_psy_get_prop(struct power_supply *psy,
0177                  enum power_supply_property psp,
0178                  union power_supply_propval *val)
0179 {
0180     struct ucsi_connector *con = power_supply_get_drvdata(psy);
0181 
0182     switch (psp) {
0183     case POWER_SUPPLY_PROP_USB_TYPE:
0184         return ucsi_psy_get_usb_type(con, val);
0185     case POWER_SUPPLY_PROP_ONLINE:
0186         return ucsi_psy_get_online(con, val);
0187     case POWER_SUPPLY_PROP_VOLTAGE_MIN:
0188         return ucsi_psy_get_voltage_min(con, val);
0189     case POWER_SUPPLY_PROP_VOLTAGE_MAX:
0190         return ucsi_psy_get_voltage_max(con, val);
0191     case POWER_SUPPLY_PROP_VOLTAGE_NOW:
0192         return ucsi_psy_get_voltage_now(con, val);
0193     case POWER_SUPPLY_PROP_CURRENT_MAX:
0194         return ucsi_psy_get_current_max(con, val);
0195     case POWER_SUPPLY_PROP_CURRENT_NOW:
0196         return ucsi_psy_get_current_now(con, val);
0197     default:
0198         return -EINVAL;
0199     }
0200 }
0201 
0202 static enum power_supply_usb_type ucsi_psy_usb_types[] = {
0203     POWER_SUPPLY_USB_TYPE_C,
0204     POWER_SUPPLY_USB_TYPE_PD,
0205     POWER_SUPPLY_USB_TYPE_PD_PPS,
0206 };
0207 
0208 int ucsi_register_port_psy(struct ucsi_connector *con)
0209 {
0210     struct power_supply_config psy_cfg = {};
0211     struct device *dev = con->ucsi->dev;
0212     char *psy_name;
0213 
0214     psy_cfg.drv_data = con;
0215     psy_cfg.fwnode = dev_fwnode(dev);
0216 
0217     psy_name = devm_kasprintf(dev, GFP_KERNEL, "ucsi-source-psy-%s%d",
0218                   dev_name(dev), con->num);
0219     if (!psy_name)
0220         return -ENOMEM;
0221 
0222     con->psy_desc.name = psy_name;
0223     con->psy_desc.type = POWER_SUPPLY_TYPE_USB;
0224     con->psy_desc.usb_types = ucsi_psy_usb_types;
0225     con->psy_desc.num_usb_types = ARRAY_SIZE(ucsi_psy_usb_types);
0226     con->psy_desc.properties = ucsi_psy_props;
0227     con->psy_desc.num_properties = ARRAY_SIZE(ucsi_psy_props);
0228     con->psy_desc.get_property = ucsi_psy_get_prop;
0229 
0230     con->psy = power_supply_register(dev, &con->psy_desc, &psy_cfg);
0231 
0232     return PTR_ERR_OR_ZERO(con->psy);
0233 }
0234 
0235 void ucsi_unregister_port_psy(struct ucsi_connector *con)
0236 {
0237     if (IS_ERR_OR_NULL(con->psy))
0238         return;
0239 
0240     power_supply_unregister(con->psy);
0241     con->psy = NULL;
0242 }
0243 
0244 void ucsi_port_psy_changed(struct ucsi_connector *con)
0245 {
0246     if (IS_ERR_OR_NULL(con->psy))
0247         return;
0248 
0249     power_supply_changed(con->psy);
0250 }