cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

psy.c (6304B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Power Supply for UCSI
      4 *
      5 * Copyright (C) 2020, Intel Corporation
      6 * Author: K V, Abhilash <abhilash.k.v@intel.com>
      7 * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
      8 */
      9
     10#include <linux/property.h>
     11#include <linux/usb/pd.h>
     12
     13#include "ucsi.h"
     14
     15/* Power Supply access to expose source power information */
     16enum ucsi_psy_online_states {
     17	UCSI_PSY_OFFLINE = 0,
     18	UCSI_PSY_FIXED_ONLINE,
     19	UCSI_PSY_PROG_ONLINE,
     20};
     21
     22static enum power_supply_property ucsi_psy_props[] = {
     23	POWER_SUPPLY_PROP_USB_TYPE,
     24	POWER_SUPPLY_PROP_ONLINE,
     25	POWER_SUPPLY_PROP_VOLTAGE_MIN,
     26	POWER_SUPPLY_PROP_VOLTAGE_MAX,
     27	POWER_SUPPLY_PROP_VOLTAGE_NOW,
     28	POWER_SUPPLY_PROP_CURRENT_MAX,
     29	POWER_SUPPLY_PROP_CURRENT_NOW,
     30};
     31
     32static int ucsi_psy_get_online(struct ucsi_connector *con,
     33			       union power_supply_propval *val)
     34{
     35	val->intval = UCSI_PSY_OFFLINE;
     36	if (con->status.flags & UCSI_CONSTAT_CONNECTED &&
     37	    (con->status.flags & UCSI_CONSTAT_PWR_DIR) == TYPEC_SINK)
     38		val->intval = UCSI_PSY_FIXED_ONLINE;
     39	return 0;
     40}
     41
     42static int ucsi_psy_get_voltage_min(struct ucsi_connector *con,
     43				    union power_supply_propval *val)
     44{
     45	u32 pdo;
     46
     47	switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) {
     48	case UCSI_CONSTAT_PWR_OPMODE_PD:
     49		pdo = con->src_pdos[0];
     50		val->intval = pdo_fixed_voltage(pdo) * 1000;
     51		break;
     52	case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
     53	case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
     54	case UCSI_CONSTAT_PWR_OPMODE_BC:
     55	case UCSI_CONSTAT_PWR_OPMODE_DEFAULT:
     56		val->intval = UCSI_TYPEC_VSAFE5V * 1000;
     57		break;
     58	default:
     59		val->intval = 0;
     60		break;
     61	}
     62	return 0;
     63}
     64
     65static int ucsi_psy_get_voltage_max(struct ucsi_connector *con,
     66				    union power_supply_propval *val)
     67{
     68	u32 pdo;
     69
     70	switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) {
     71	case UCSI_CONSTAT_PWR_OPMODE_PD:
     72		if (con->num_pdos > 0) {
     73			pdo = con->src_pdos[con->num_pdos - 1];
     74			val->intval = pdo_fixed_voltage(pdo) * 1000;
     75		} else {
     76			val->intval = 0;
     77		}
     78		break;
     79	case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
     80	case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
     81	case UCSI_CONSTAT_PWR_OPMODE_BC:
     82	case UCSI_CONSTAT_PWR_OPMODE_DEFAULT:
     83		val->intval = UCSI_TYPEC_VSAFE5V * 1000;
     84		break;
     85	default:
     86		val->intval = 0;
     87		break;
     88	}
     89	return 0;
     90}
     91
     92static int ucsi_psy_get_voltage_now(struct ucsi_connector *con,
     93				    union power_supply_propval *val)
     94{
     95	int index;
     96	u32 pdo;
     97
     98	switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) {
     99	case UCSI_CONSTAT_PWR_OPMODE_PD:
    100		index = rdo_index(con->rdo);
    101		if (index > 0) {
    102			pdo = con->src_pdos[index - 1];
    103			val->intval = pdo_fixed_voltage(pdo) * 1000;
    104		} else {
    105			val->intval = 0;
    106		}
    107		break;
    108	case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
    109	case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
    110	case UCSI_CONSTAT_PWR_OPMODE_BC:
    111	case UCSI_CONSTAT_PWR_OPMODE_DEFAULT:
    112		val->intval = UCSI_TYPEC_VSAFE5V * 1000;
    113		break;
    114	default:
    115		val->intval = 0;
    116		break;
    117	}
    118	return 0;
    119}
    120
    121static int ucsi_psy_get_current_max(struct ucsi_connector *con,
    122				    union power_supply_propval *val)
    123{
    124	u32 pdo;
    125
    126	switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) {
    127	case UCSI_CONSTAT_PWR_OPMODE_PD:
    128		if (con->num_pdos > 0) {
    129			pdo = con->src_pdos[con->num_pdos - 1];
    130			val->intval = pdo_max_current(pdo) * 1000;
    131		} else {
    132			val->intval = 0;
    133		}
    134		break;
    135	case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
    136		val->intval = UCSI_TYPEC_1_5_CURRENT * 1000;
    137		break;
    138	case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
    139		val->intval = UCSI_TYPEC_3_0_CURRENT * 1000;
    140		break;
    141	case UCSI_CONSTAT_PWR_OPMODE_BC:
    142	case UCSI_CONSTAT_PWR_OPMODE_DEFAULT:
    143	/* UCSI can't tell b/w DCP/CDP or USB2/3x1/3x2 SDP chargers */
    144	default:
    145		val->intval = 0;
    146		break;
    147	}
    148	return 0;
    149}
    150
    151static int ucsi_psy_get_current_now(struct ucsi_connector *con,
    152				    union power_supply_propval *val)
    153{
    154	u16 flags = con->status.flags;
    155
    156	if (UCSI_CONSTAT_PWR_OPMODE(flags) == UCSI_CONSTAT_PWR_OPMODE_PD)
    157		val->intval = rdo_op_current(con->rdo) * 1000;
    158	else
    159		val->intval = 0;
    160	return 0;
    161}
    162
    163static int ucsi_psy_get_usb_type(struct ucsi_connector *con,
    164				 union power_supply_propval *val)
    165{
    166	u16 flags = con->status.flags;
    167
    168	val->intval = POWER_SUPPLY_USB_TYPE_C;
    169	if (flags & UCSI_CONSTAT_CONNECTED &&
    170	    UCSI_CONSTAT_PWR_OPMODE(flags) == UCSI_CONSTAT_PWR_OPMODE_PD)
    171		val->intval = POWER_SUPPLY_USB_TYPE_PD;
    172
    173	return 0;
    174}
    175
    176static int ucsi_psy_get_prop(struct power_supply *psy,
    177			     enum power_supply_property psp,
    178			     union power_supply_propval *val)
    179{
    180	struct ucsi_connector *con = power_supply_get_drvdata(psy);
    181
    182	switch (psp) {
    183	case POWER_SUPPLY_PROP_USB_TYPE:
    184		return ucsi_psy_get_usb_type(con, val);
    185	case POWER_SUPPLY_PROP_ONLINE:
    186		return ucsi_psy_get_online(con, val);
    187	case POWER_SUPPLY_PROP_VOLTAGE_MIN:
    188		return ucsi_psy_get_voltage_min(con, val);
    189	case POWER_SUPPLY_PROP_VOLTAGE_MAX:
    190		return ucsi_psy_get_voltage_max(con, val);
    191	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
    192		return ucsi_psy_get_voltage_now(con, val);
    193	case POWER_SUPPLY_PROP_CURRENT_MAX:
    194		return ucsi_psy_get_current_max(con, val);
    195	case POWER_SUPPLY_PROP_CURRENT_NOW:
    196		return ucsi_psy_get_current_now(con, val);
    197	default:
    198		return -EINVAL;
    199	}
    200}
    201
    202static enum power_supply_usb_type ucsi_psy_usb_types[] = {
    203	POWER_SUPPLY_USB_TYPE_C,
    204	POWER_SUPPLY_USB_TYPE_PD,
    205	POWER_SUPPLY_USB_TYPE_PD_PPS,
    206};
    207
    208int ucsi_register_port_psy(struct ucsi_connector *con)
    209{
    210	struct power_supply_config psy_cfg = {};
    211	struct device *dev = con->ucsi->dev;
    212	char *psy_name;
    213
    214	psy_cfg.drv_data = con;
    215	psy_cfg.fwnode = dev_fwnode(dev);
    216
    217	psy_name = devm_kasprintf(dev, GFP_KERNEL, "ucsi-source-psy-%s%d",
    218				  dev_name(dev), con->num);
    219	if (!psy_name)
    220		return -ENOMEM;
    221
    222	con->psy_desc.name = psy_name;
    223	con->psy_desc.type = POWER_SUPPLY_TYPE_USB;
    224	con->psy_desc.usb_types = ucsi_psy_usb_types;
    225	con->psy_desc.num_usb_types = ARRAY_SIZE(ucsi_psy_usb_types);
    226	con->psy_desc.properties = ucsi_psy_props;
    227	con->psy_desc.num_properties = ARRAY_SIZE(ucsi_psy_props);
    228	con->psy_desc.get_property = ucsi_psy_get_prop;
    229
    230	con->psy = power_supply_register(dev, &con->psy_desc, &psy_cfg);
    231
    232	return PTR_ERR_OR_ZERO(con->psy);
    233}
    234
    235void ucsi_unregister_port_psy(struct ucsi_connector *con)
    236{
    237	if (IS_ERR_OR_NULL(con->psy))
    238		return;
    239
    240	power_supply_unregister(con->psy);
    241	con->psy = NULL;
    242}
    243
    244void ucsi_port_psy_changed(struct ucsi_connector *con)
    245{
    246	if (IS_ERR_OR_NULL(con->psy))
    247		return;
    248
    249	power_supply_changed(con->psy);
    250}