<linux/power_supply_old.h>

该博客主要展示了Linux电源供应监控类的代码。定义了电源供应的多种状态、类型、健康状况等枚举值,还定义了电源供应属性结构体及相关操作函数,如注册、注销、获取电源供应信息等,同时包含一些辅助判断函数。

/*
 *  Universal power supply monitor class
 *
 *  Copyright © 2007  Anton Vorontsov <cbou@mail.ru>
 *  Copyright © 2004  Szabolcs Gyurko
 *  Copyright © 2003  Ian Molton <spyro@f2s.com>
 *
 *  Modified: 2004, Oct     Szabolcs Gyurko
 *
 *  You may use this code as per GPL version 2
 */

#ifndef __LINUX_POWER_SUPPLY_H__
#define __LINUX_POWER_SUPPLY_H__

#include <linux/workqueue.h>
#include <linux/leds.h>
#include <linux/spinlock.h>
#include <linux/notifier.h>
#include <linux/types.h>

/*
 * All voltages, currents, charges, energies, time and temperatures in uV,
 * µA, µAh, µWh, seconds and tenths of degree Celsius unless otherwise
 * stated. It's driver's job to convert its raw values to units in which
 * this class operates.
 */

/*
 * For systems where the charger determines the maximum battery capacity
 * the min and max fields should be used to present these values to user
 * space. Unused/unknown fields will not appear in sysfs.
 */

enum {
    POWER_SUPPLY_STATUS_UNKNOWN = 0,
    POWER_SUPPLY_STATUS_CHARGING,
    POWER_SUPPLY_STATUS_DISCHARGING,
    POWER_SUPPLY_STATUS_NOT_CHARGING,
    POWER_SUPPLY_STATUS_FULL,
};

enum {
    POWER_SUPPLY_CHARGE_TYPE_UNKNOWN = 0,
    POWER_SUPPLY_CHARGE_TYPE_NONE,
    POWER_SUPPLY_CHARGE_TYPE_TRICKLE,
    POWER_SUPPLY_CHARGE_TYPE_FAST,
};

enum {
    POWER_SUPPLY_HEALTH_UNKNOWN = 0,
    POWER_SUPPLY_HEALTH_GOOD,
    POWER_SUPPLY_HEALTH_OVERHEAT,
    POWER_SUPPLY_HEALTH_DEAD,
    POWER_SUPPLY_HEALTH_OVERVOLTAGE,
    POWER_SUPPLY_HEALTH_UNSPEC_FAILURE,
    POWER_SUPPLY_HEALTH_COLD,
    POWER_SUPPLY_HEALTH_WATCHDOG_TIMER_EXPIRE,
    POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE,
};

enum {
    POWER_SUPPLY_TECHNOLOGY_UNKNOWN = 0,
    POWER_SUPPLY_TECHNOLOGY_NiMH,
    POWER_SUPPLY_TECHNOLOGY_LION,
    POWER_SUPPLY_TECHNOLOGY_LIPO,
    POWER_SUPPLY_TECHNOLOGY_LiFe,
    POWER_SUPPLY_TECHNOLOGY_NiCd,
    POWER_SUPPLY_TECHNOLOGY_LiMn,
};

enum {
    POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN = 0,
    POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL,
    POWER_SUPPLY_CAPACITY_LEVEL_LOW,
    POWER_SUPPLY_CAPACITY_LEVEL_NORMAL,
    POWER_SUPPLY_CAPACITY_LEVEL_HIGH,
    POWER_SUPPLY_CAPACITY_LEVEL_FULL,
};

enum {
    POWER_SUPPLY_SCOPE_UNKNOWN = 0,
    POWER_SUPPLY_SCOPE_SYSTEM,
    POWER_SUPPLY_SCOPE_DEVICE,
};

enum power_supply_property {
    /* Properties of type `int' */
    POWER_SUPPLY_PROP_STATUS = 0,
    POWER_SUPPLY_PROP_CHARGE_TYPE,
    POWER_SUPPLY_PROP_HEALTH,
    POWER_SUPPLY_PROP_PRESENT,
    POWER_SUPPLY_PROP_ONLINE,
    POWER_SUPPLY_PROP_AUTHENTIC,
    POWER_SUPPLY_PROP_TECHNOLOGY,
    POWER_SUPPLY_PROP_CYCLE_COUNT,
    POWER_SUPPLY_PROP_VOLTAGE_MAX,
    POWER_SUPPLY_PROP_VOLTAGE_MIN,
    POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
    POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
    POWER_SUPPLY_PROP_VOLTAGE_NOW,
    POWER_SUPPLY_PROP_VOLTAGE_AVG,
    POWER_SUPPLY_PROP_VOLTAGE_OCV,
    POWER_SUPPLY_PROP_VOLTAGE_BOOT,
    POWER_SUPPLY_PROP_CURRENT_MAX,
    POWER_SUPPLY_PROP_CURRENT_NOW,
    POWER_SUPPLY_PROP_CURRENT_AVG,
    POWER_SUPPLY_PROP_CURRENT_BOOT,
    POWER_SUPPLY_PROP_POWER_NOW,
    POWER_SUPPLY_PROP_POWER_AVG,
    POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
    POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN,
    POWER_SUPPLY_PROP_CHARGE_FULL,
    POWER_SUPPLY_PROP_CHARGE_EMPTY,
    POWER_SUPPLY_PROP_CHARGE_NOW,
    POWER_SUPPLY_PROP_CHARGE_AVG,
    POWER_SUPPLY_PROP_CHARGE_COUNTER,
    POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
    POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
    POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
    POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
    POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
    POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX,
    POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
    POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
    POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN,
    POWER_SUPPLY_PROP_ENERGY_FULL,
    POWER_SUPPLY_PROP_ENERGY_EMPTY,
    POWER_SUPPLY_PROP_ENERGY_NOW,
    POWER_SUPPLY_PROP_ENERGY_AVG,
    POWER_SUPPLY_PROP_CAPACITY, /* in percents! */
    POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN, /* in percents! */
    POWER_SUPPLY_PROP_CAPACITY_ALERT_MAX, /* in percents! */
    POWER_SUPPLY_PROP_CAPACITY_LEVEL,
    POWER_SUPPLY_PROP_TEMP,
    POWER_SUPPLY_PROP_TEMP_MAX,
    POWER_SUPPLY_PROP_TEMP_MIN,
    POWER_SUPPLY_PROP_TEMP_ALERT_MIN,
    POWER_SUPPLY_PROP_TEMP_ALERT_MAX,
    POWER_SUPPLY_PROP_TEMP_AMBIENT,
    POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN,
    POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX,
    POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
    POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
    POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
    POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
    POWER_SUPPLY_PROP_TYPE, /* use power_supply.type instead */
    POWER_SUPPLY_PROP_SCOPE,
    POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
    POWER_SUPPLY_PROP_CALIBRATE,
    /* Local extensions */
    POWER_SUPPLY_PROP_USB_HC,
    POWER_SUPPLY_PROP_USB_OTG,
    POWER_SUPPLY_PROP_CHARGE_ENABLED,
    /* Local extensions of type int64_t */
    POWER_SUPPLY_PROP_CHARGE_COUNTER_EXT,
    /* Properties of type `const char *' */
    POWER_SUPPLY_PROP_MODEL_NAME,
    POWER_SUPPLY_PROP_MANUFACTURER,
    POWER_SUPPLY_PROP_SERIAL_NUMBER,
};

enum power_supply_type {
    POWER_SUPPLY_TYPE_UNKNOWN = 0,
    POWER_SUPPLY_TYPE_BATTERY,
    POWER_SUPPLY_TYPE_UPS,
    POWER_SUPPLY_TYPE_MAINS,
    POWER_SUPPLY_TYPE_USB,        /* Standard Downstream Port */
    POWER_SUPPLY_TYPE_USB_DCP,    /* Dedicated Charging Port */
    POWER_SUPPLY_TYPE_USB_CDP,    /* Charging Downstream Port */
    POWER_SUPPLY_TYPE_USB_ACA,    /* Accessory Charger Adapters */
};

enum power_supply_notifier_events {
    PSY_EVENT_PROP_CHANGED,
};

union power_supply_propval {
    int intval;
    const char *strval;
    int64_t int64val;
};

struct device;
struct device_node;

struct power_supply {
    const char *name;
    enum power_supply_type type;
    enum power_supply_property *properties;
    size_t num_properties;

    char **supplied_to;
    size_t num_supplicants;

    char **supplied_from;
    size_t num_supplies;
    struct device_node *of_node;

    int (*get_property)(struct power_supply *psy,
                enum power_supply_property psp,
                union power_supply_propval *val);
    int (*set_property)(struct power_supply *psy,
                enum power_supply_property psp,
                const union power_supply_propval *val);
    int (*property_is_writeable)(struct power_supply *psy,
                     enum power_supply_property psp);
    void (*external_power_changed)(struct power_supply *psy);
    void (*set_charged)(struct power_supply *psy);

    /*
     * Set if thermal zone should not be created for this power supply.
     * For example for virtual supplies forwarding calls to actual
     * sensors or other supplies.
     */
    bool no_thermal;
    /* For APM emulation, think legacy userspace. */
    int use_for_apm;

    /* private */
    struct device *dev;
    struct work_struct changed_work;
    spinlock_t changed_lock;
    bool changed;
#ifdef CONFIG_THERMAL
    struct thermal_zone_device *tzd;
    struct thermal_cooling_device *tcd;
#endif

#ifdef CONFIG_LEDS_TRIGGERS
    struct led_trigger *charging_full_trig;
    char *charging_full_trig_name;
    struct led_trigger *charging_trig;
    char *charging_trig_name;
    struct led_trigger *full_trig;
    char *full_trig_name;
    struct led_trigger *online_trig;
    char *online_trig_name;
    struct led_trigger *charging_blink_full_solid_trig;
    char *charging_blink_full_solid_trig_name;
#endif
};

/*
 * This is recommended structure to specify static power supply parameters.
 * Generic one, parametrizable for different power supplies. Power supply
 * class itself does not use it, but that's what implementing most platform
 * drivers, should try reuse for consistency.
 */

struct power_supply_info {
    const char *name;
    int technology;
    int voltage_max_design;
    int voltage_min_design;
    int charge_full_design;
    int charge_empty_design;
    int energy_full_design;
    int energy_empty_design;
    int use_for_apm;
};

extern struct atomic_notifier_head power_supply_notifier;
extern int power_supply_reg_notifier(struct notifier_block *nb);
extern void power_supply_unreg_notifier(struct notifier_block *nb);
extern struct power_supply *power_supply_get_by_name(const char *name);
#ifdef CONFIG_OF
extern struct power_supply *power_supply_get_by_phandle(struct device_node *np,
                            const char *property);
#else /* !CONFIG_OF */
static inline struct power_supply *
power_supply_get_by_phandle(struct device_node *np, const char *property)
{ return NULL; }
#endif /* CONFIG_OF */
extern void power_supply_changed(struct power_supply *psy);
extern int power_supply_am_i_supplied(struct power_supply *psy);
extern int power_supply_set_battery_charged(struct power_supply *psy);

#ifdef CONFIG_POWER_SUPPLY
extern int power_supply_is_system_supplied(void);
#else
static inline int power_supply_is_system_supplied(void) { return -ENOSYS; }
#endif

extern int power_supply_register(struct device *parent,
                 struct power_supply *psy);
extern int power_supply_register_no_ws(struct device *parent,
                 struct power_supply *psy);
extern void power_supply_unregister(struct power_supply *psy);
extern int power_supply_powers(struct power_supply *psy, struct device *dev);

/* For APM emulation, think legacy userspace. */
extern struct class *power_supply_class;

static inline bool power_supply_is_amp_property(enum power_supply_property psp)
{
    switch (psp) {
    case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
    case POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN:
    case POWER_SUPPLY_PROP_CHARGE_FULL:
    case POWER_SUPPLY_PROP_CHARGE_EMPTY:
    case POWER_SUPPLY_PROP_CHARGE_NOW:
    case POWER_SUPPLY_PROP_CHARGE_AVG:
    case POWER_SUPPLY_PROP_CHARGE_COUNTER:
    case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
    case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
    case POWER_SUPPLY_PROP_CURRENT_MAX:
    case POWER_SUPPLY_PROP_CURRENT_NOW:
    case POWER_SUPPLY_PROP_CURRENT_AVG:
    case POWER_SUPPLY_PROP_CURRENT_BOOT:
        return 1;
    default:
        break;
    }

    return 0;
}

static inline bool power_supply_is_watt_property(enum power_supply_property psp)
{
    switch (psp) {
    case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
    case POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN:
    case POWER_SUPPLY_PROP_ENERGY_FULL:
    case POWER_SUPPLY_PROP_ENERGY_EMPTY:
    case POWER_SUPPLY_PROP_ENERGY_NOW:
    case POWER_SUPPLY_PROP_ENERGY_AVG:
    case POWER_SUPPLY_PROP_VOLTAGE_MAX:
    case POWER_SUPPLY_PROP_VOLTAGE_MIN:
    case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
    case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
    case POWER_SUPPLY_PROP_VOLTAGE_NOW:
    case POWER_SUPPLY_PROP_VOLTAGE_AVG:
    case POWER_SUPPLY_PROP_VOLTAGE_OCV:
    case POWER_SUPPLY_PROP_VOLTAGE_BOOT:
    case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
    case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
    case POWER_SUPPLY_PROP_POWER_NOW:
        return 1;
    default:
        break;
    }

    return 0;
}

#endif /* __LINUX_POWER_SUPPLY_H__ */

root@rk3588-buildroot:/# cat /sys/kernel/debug/usb/tcpm-5-0022 [ 0.829932] Setting usb_comm capable false [ 0.834548] Setting voltage/current limit 0 mV 0 mA [ 0.834552] polarity 0 [ 0.834567] Requesting mux state 0, usb-role 0, orientation 0 [ 0.835583] state change INVALID_STATE -> SNK_UNATTACHED [rev1 NONE_AMS] [ 0.835597] CC1: 0 -> 0, CC2: 0 -> 0 [state SNK_UNATTACHED, polarity 0, disconnected] [ 0.835603] Setting usb_comm capable false [ 0.835602] 5-0022: registered [ 0.840216] Setting voltage/current limit 0 mV 0 mA [ 0.840219] polarity 0 [ 0.840220] Requesting mux state 0, usb-role 0, orientation 0 [ 0.841007] cc:=2 [ 0.846366] pending state change PORT_RESET -> PORT_RESET_WAIT_OFF @ 100 ms [rev1 NONE_AMS] [ 0.846369] state change PORT_RESET -> PORT_RESET_WAIT_OFF [delayed 100 ms] [ 0.846372] state change PORT_RESET_WAIT_OFF -> SNK_UNATTACHED [rev1 NONE_AMS] [ 0.846375] Start toggling [ 24.957302] CC1: 0 -> 1, CC2: 0 -> 2 [state TOGGLING, polarity 0, connected] [ 24.957313] state change TOGGLING -> SRC_ATTACH_WAIT [rev1 NONE_AMS] [ 24.957321] pending state change SRC_ATTACH_WAIT -> SNK_TRY @ 200 ms [rev1 NONE_AMS] [ 25.157493] state change SRC_ATTACH_WAIT -> SNK_TRY [delayed 200 ms] [ 25.157504] cc:=2 [ 25.165426] pending state change SNK_TRY -> SNK_TRY_WAIT @ 100 ms [rev1 NONE_AMS] [ 25.265599] state change SNK_TRY -> SNK_TRY_WAIT [delayed 100 ms] [ 25.265610] state change SNK_TRY_WAIT -> SRC_TRYWAIT [rev1 NONE_AMS] [ 25.265616] cc:=5 [ 25.274990] pending state change SRC_TRYWAIT -> SRC_TRYWAIT_UNATTACHED @ 100 ms [rev1 NONE_AMS] [ 25.375162] state change SRC_TRYWAIT -> SRC_TRYWAIT_UNATTACHED [delayed 100 ms] [ 25.375173] state change SRC_TRYWAIT_UNATTACHED -> SNK_UNATTACHED [rev1 NONE_AMS] [ 25.375181] Start toggling [ 25.382564] state change SNK_UNATTACHED -> TOGGLING [rev1 NONE_AMS] [ 25.453193] CC1: 1 -> 1, CC2: 2 -> 2 [state TOGGLING, polarity 0, connected] [ 25.453202] state change TOGGLING -> SRC_ATTACH_WAIT [rev1 NONE_AMS] [ 25.453211] pending state change SRC_ATTACH_WAIT -> SRC_ATTACHED @ 200 ms [rev1 NONE_AMS] [ 25.653381] state change SRC_ATTACH_WAIT -> SRC_ATTACHED [delayed 200 ms] [ 25.653393] polarity 1 [ 25.653398] Requesting mux state 1, usb-role 1, orientation 2 [ 25.659863] vconn:=1 [ 25.660926] vbus:=1 charge=0 [ 25.661789] pending state change SRC_ATTACHED -> SRC_UNATTACHED @ 480 ms [rev1 NONE_AMS] [ 25.665368] VBUS on [ 25.665370] state change SRC_ATTACHED -> SRC_STARTUP [rev1 NONE_AMS] [ 25.665391] AMS POWER_NEGOTIATION start [ 25.665392] cc:=4 [ 25.671259] pending state change SRC_STARTUP -> AMS_START @ 16 ms [rev3 POWER_NEGOTIATION] [ 25.687347] state change SRC_STARTUP -> AMS_START [delayed 16 ms] [ 25.687351] state change AMS_START -> SRC_SEND_CAPABILITIES [rev3 POWER_NEGOTIATION] [ 25.687354] PD TX, header: 0x11a1 [ 25.698704] PD TX complete, status: 2 [ 25.698802] pending state change SRC_SEND_CAPABILITIES -> SRC_SEND_CAPABILITIES @ 150 ms [rev3 POWER_NEGOTIATION] [ 25.848977] state change SRC_SEND_CAPABILITIES -> SRC_SEND_CAPABILITIES [delayed 150 ms] [ 25.848990] PD TX, header: 0x11a1 [ 25.861437] PD TX complete, status: 2 [ 25.861579] pending state change SRC_SEND_CAPABILITIES -> SRC_SEND_CAPABILITIES @ 150 ms [rev3 POWER_NEGOTIATION] [ 26.011749] state change SRC_SEND_CAPABILITIES -> SRC_SEND_CAPABILITIES [delayed 150 ms] [ 26.011761] PD TX, header: 0x11a1 [ 26.024040] PD TX complete, status: 2 [ 26.024184] pending state change SRC_SEND_CAPABILITIES -> SRC_SEND_CAPABILITIES @ 150 ms [rev3 POWER_NEGOTIATION] [ 26.174355] state change SRC_SEND_CAPABILITIES -> SRC_SEND_CAPABILITIES [delayed 150 ms] [ 26.174366] PD TX, header: 0x11a1 [ 26.180966] PD TX complete, status: 0 [ 26.181117] pending state change SRC_SEND_CAPABILITIES -> SRC_SEND_CAPABILITIES_TIMEOUT @ 150 ms [rev3 POWER_NEGOTIATION] [ 26.186601] PD TX complete, status: 0 [ 26.190583] PD RX, header: 0x1082 [1] [ 26.190595] state change SRC_SEND_CAPABILITIES -> SRC_NEGOTIATE_CAPABILITIES [rev3 POWER_NEGOTIATION] [ 26.190605] Requested 5000 mV, 3000 mA for 3000 / 3000 mA [ 26.190611] PD TX, header: 0x3a3 [ 26.197753] PD TX complete, status: 0 [ 26.197906] Setting usb_comm capable true [ 26.197914] pending state change SRC_NEGOTIATE_CAPABILITIES -> SRC_TRANSITION_SUPPLY @ 35 ms [rev3 POWER_NEGOTIATION] [ 26.233064] state change SRC_NEGOTIATE_CAPABILITIES -> SRC_TRANSITION_SUPPLY [delayed 35 ms] [ 26.233074] PD TX, header: 0x5a6 [ 26.238665] PD TX complete, status: 0 [ 26.238881] state change SRC_TRANSITION_SUPPLY -> SRC_READY [rev3 POWER_NEGOTIATION] [ 26.238978] AMS POWER_NEGOTIATION finished [ 26.238982] cc:=5 [ 26.246622] AMS DISCOVER_IDENTITY start [ 26.246630] cc:=4 [ 26.270073] PD TX, header: 0x17af [ 26.277108] PD TX complete, status: 0 [ 26.283219] PD TX complete, status: 0 [ 26.286231] PD TX complete, status: 0 [ 26.289060] PD TX complete, status: 0 [ 26.294781] PD RX, header: 0x528f [1] [ 26.294794] Rx VDM cmd 0xff00a041 type 1 cmd 1 len 5 [ 26.294802] AMS DISCOVER_IDENTITY finished [ 26.294805] cc:=5 [ 26.303109] Idheader: 0x1c8f [1] [ 26.543195] Rx VDM cmd 0xff01a151 type 1 cmd 17 len 1 [ 26.543203] AMS STRUCTURED_VDMS finished [ 26.543206] cc:=5 详细解析log
09-09
#include <linux/module.h> #include <linux/device.h> #include <linux/err.h> #include <linux/slab.h> #include <linux/kdev_t.h> #include <linux/idr.h> #include <linux/thermal.h> #include <linux/reboot.h> #include <linux/string.h> #include <linux/of.h> #include <net/netlink.h> #include <net/genetlink.h> #include <linux/suspend.h> #include <linux/cpu_cooling.h> #include <linux/soc/qcom/panel_event_notifier.h> #include <linux/pm_qos.h> #include <linux/cpufreq.h> #include <linux/kobject.h> #include <linux/kernfs.h> #include <linux/workqueue.h> #include <linux/power_supply.h> #include "../base/base.h" #include "thermal_core.h" #if defined(CONFIG_DRM_PANEL) static struct drm_panel *prim_panel; #endif #define BOOST_BUFFER_SIZE 128 #define BOARD__BUFFER_SIZE 128 #define TEMP_AWARE_MAX 10 #define TEMP_AWARE_MIN 0 static struct class *power_debug_class; static uint8_t temp_aware_mode = TEMP_AWARE_MAX; struct mi_thermal_device { struct device *dev; struct class *class; struct attribute_group attrs; }; struct freq_table { u32 frequency; }; struct cpufreq_device { int id; unsigned int cpufreq_state; unsigned int max_level; struct freq_table *freq_table; /* In descending order */ struct cpufreq_policy *policy; struct list_head node; struct freq_qos_request *qos_req; }; #ifdef CONFIG_MI_THERMAL_MULTI_CHARGE struct usb_monitor { struct notifier_block psy_nb; struct work_struct usb_state_work; int usb_online; }; static struct usb_monitor usb_state; static atomic_t charger_mode = ATOMIC_INIT(-1); #endif static struct mi_thermal_device mi_thermal_dev; static int screen_state = 0; static int screen_light = 0; #if IS_ENABLED(CONFIG_HAVE_MULTI_SCREEN) static void *cookie_sec = NULL; static struct drm_panel *sec_panel; static int retry_count_sec = 10; #endif static int screen_last_status = 0; static void *cookie = NULL; static atomic_t temp_state = ATOMIC_INIT(0); static atomic_t switch_mode = ATOMIC_INIT(-1); static atomic_t balance_mode = ATOMIC_INIT(0); static atomic_t modem_limit = ATOMIC_INIT(0); static atomic_t market_download_limit = ATOMIC_INIT(0); static atomic_t board_sensor_temp_comp_default = ATOMIC_INIT(0); static atomic_t cpu_nolimit_temp_default = ATOMIC_INIT(0); static atomic_t poor_modem_limit= ATOMIC_INIT(0); static atomic_t wifi_limit = ATOMIC_INIT(0); static atomic_t flash_state = ATOMIC_INIT(0); static atomic_t modem_rate = ATOMIC_INIT(0); static atomic_t modem_level = ATOMIC_INIT(0); static atomic_t thermal_max_brightness = ATOMIC_INIT(0); static char boost_buf[128]; const char *board_sensor; static char board_sensor_temp[128]; static char board_sensor_second_temp[128]; static char board_sensor_other_temp[128]; static char ambient_sensor_temp[128]; const char *ambient_sensor; #ifdef CONFIG_HAVE_CHARGE_TEMP static char board_sensor_charge_temp[128]; #endif static LIST_HEAD(cpufreq_dev_list); static DEFINE_MUTEX(cpufreq_list_lock); static DEFINE_PER_CPU(struct freq_qos_request, qos_req); static struct workqueue_struct *screen_state_wq; static struct delayed_work screen_state_dw; #if IS_ENABLED(CONFIG_XIAOMI_SMART_CHG) /*mi_thermal_notifier*/ SRCU_NOTIFIER_HEAD(mi_thermal_notifier); EXPORT_SYMBOL_GPL(mi_thermal_notifier); int mi_thermal_reg_notifier(struct notifier_block *nb) { return srcu_notifier_chain_register(&mi_thermal_notifier, nb); } EXPORT_SYMBOL_GPL(mi_thermal_reg_notifier); int mi_thermal_unreg_notifier(struct notifier_block *nb) { return srcu_notifier_chain_unregister(&mi_thermal_notifier, nb); } EXPORT_SYMBOL_GPL(mi_thermal_unreg_notifier); int mi_thermal_notifier_call_chain(unsigned long event, int val) { return srcu_notifier_call_chain(&mi_thermal_notifier, event, &val); } EXPORT_SYMBOL_GPL(mi_thermal_notifier_call_chain); #endif static int cpufreq_set_level(struct cpufreq_device *cdev, unsigned long state) { /* Request state should be less than max_level */ if (WARN_ON(state > cdev->max_level)) return -EINVAL; /* Check if the old cooling action is same as new cooling action */ if (cdev->cpufreq_state == state) return 0; cdev->cpufreq_state = state; return freq_qos_update_request(cdev->qos_req,cdev->freq_table[state].frequency); } void cpu_limits_set_level(unsigned int cpu, unsigned int max_freq) { struct cpufreq_device *cpufreq_dev; unsigned int level = 0; list_for_each_entry(cpufreq_dev, &cpufreq_dev_list, node) { if (cpufreq_dev->id == cpu) { for (level = 0; level <= cpufreq_dev->max_level; level++) { int target_freq = cpufreq_dev->freq_table[level].frequency; if (max_freq >= target_freq) { cpufreq_set_level(cpufreq_dev, level); break; } } break; } } } static unsigned int find_next_max(struct cpufreq_frequency_table *table, unsigned int prev_max) { struct cpufreq_frequency_table *pos; unsigned int max = 0; cpufreq_for_each_valid_entry(pos, table) { if (pos->frequency > max && pos->frequency < prev_max) max = pos->frequency; } return max; } static int cpu_thermal_init(void) { int cpu, ret; struct cpufreq_policy *policy; struct freq_qos_request *req; for_each_possible_cpu(cpu) { unsigned int i; unsigned int freq; struct cpufreq_device *cpufreq_dev; req = &per_cpu(qos_req, cpu); policy = cpufreq_cpu_get(cpu); if (!policy) { pr_err("%s: cpufreq policy not found for cpu%d\n", __func__, cpu); return -ESRCH; } printk(KERN_ERR "%s cpu=%d\n", __func__, cpu); i = cpufreq_table_count_valid_entries(policy); if (!i) { pr_debug("%s: CPUFreq table not found or has no valid entries\n", __func__); return -ENODEV; } cpufreq_dev = kzalloc(sizeof(*cpufreq_dev), GFP_KERNEL); if (!cpufreq_dev) return -ENOMEM; cpufreq_dev->policy = policy; cpufreq_dev->qos_req = req; /* max_level is an index, not a counter */ cpufreq_dev->max_level = i - 1; cpufreq_dev->id = policy->cpu; cpufreq_dev->freq_table = kmalloc_array(i, sizeof(*cpufreq_dev->freq_table), GFP_KERNEL); if (!cpufreq_dev->freq_table) return -ENOMEM; /* Fill freq-table in descending order of frequencies */ for (i = 0, freq = -1; i <= cpufreq_dev->max_level; i++) { freq = find_next_max(policy->freq_table, freq); cpufreq_dev->freq_table[i].frequency = freq; /* Warn for duplicate entries */ if (!freq) pr_warn("%s: table has duplicate entries\n", __func__); else pr_debug("%s: freq:%u KHz\n", __func__, freq); } ret = freq_qos_add_request(&policy->constraints, cpufreq_dev->qos_req, FREQ_QOS_MAX, cpufreq_dev->freq_table[0].frequency); if (ret < 0) { pr_err("%s: Failed to add freq constraint (%d)\n", __func__, ret); return ret; } mutex_lock(&cpufreq_list_lock); list_add(&cpufreq_dev->node, &cpufreq_dev_list); mutex_unlock(&cpufreq_list_lock); } return ret; } static void destory_thermal_cpu(void){ struct cpufreq_device *priv, *tmp; list_for_each_entry_safe(priv, tmp, &cpufreq_dev_list, node) { freq_qos_remove_request(priv->qos_req); list_del(&priv->node); kfree(priv->freq_table); kfree(priv); } } static ssize_t thermal_temp_state_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&temp_state)); } static ssize_t thermal_temp_state_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { int val = -1; val = simple_strtol(buf, NULL, 10); atomic_set(&temp_state, val); return len; } static DEVICE_ATTR(temp_state, 0664, thermal_temp_state_show, thermal_temp_state_store); static ssize_t thermal_max_brightness_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&thermal_max_brightness)); } static ssize_t thermal_max_brightness_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { int val = -1; val = simple_strtol(buf, NULL, 10); atomic_set(&thermal_max_brightness, val); return len; } static DEVICE_ATTR(thermal_max_brightness, 0664, thermal_max_brightness_show, thermal_max_brightness_store); static ssize_t cpu_limits_show(struct device *dev, struct device_attribute *attr, char *buf) { return 0; } static ssize_t cpu_limits_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { unsigned int cpu; unsigned int max; if (sscanf(buf, "cpu%u %u", &cpu, &max) != 2) { pr_err("input param error, can not prase param\n"); return -EINVAL; } cpu_limits_set_level(cpu, max); return len; } static DEVICE_ATTR(cpu_limits, 0664, cpu_limits_show, cpu_limits_store); static ssize_t thermal_screen_state_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "%d\n", screen_state); } static DEVICE_ATTR(screen_state, 0664, thermal_screen_state_show, NULL); static ssize_t thermal_sconfig_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&switch_mode)); } static ssize_t thermal_sconfig_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { int val = -1; #if IS_ENABLED(CONFIG_XIAOMI_SMART_CHG) int ret = 0; #endif val = simple_strtol(buf, NULL, 10); atomic_set(&switch_mode, val); #if IS_ENABLED(CONFIG_XIAOMI_SMART_CHG) ret = mi_thermal_notifier_call_chain(THERMAL_SCENE, atomic_read(&switch_mode)); if (ret) { pr_err("%s: mi_thermal_notifier_call_chain error:%d\n", __func__, ret); } #endif return len; } static ssize_t thermal_boost_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, boost_buf); } static ssize_t thermal_boost_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { int ret; ret = snprintf(boost_buf, BOOST_BUFFER_SIZE, buf); return len; } static ssize_t thermal_balance_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&balance_mode)); } static ssize_t thermal_balance_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { int val = -1; val = simple_strtol(buf, NULL, 10); atomic_set(&balance_mode, val); return len; } static ssize_t thermal_market_download_limit_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&market_download_limit)); } static ssize_t thermal_market_download_limit_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { int val = -1; val = simple_strtol(buf, NULL, 10); atomic_set(&market_download_limit, val); return len; } static DEVICE_ATTR(boost, 0644, thermal_boost_show, thermal_boost_store); static DEVICE_ATTR(sconfig, 0664, thermal_sconfig_show, thermal_sconfig_store); static DEVICE_ATTR(balance_mode, 0664, thermal_balance_mode_show, thermal_balance_mode_store); static DEVICE_ATTR(market_download_limit, 0664, thermal_market_download_limit_show, thermal_market_download_limit_store); static ssize_t thermal_board_sensor_show(struct device *dev, struct device_attribute *attr, char *buf) { if (!board_sensor) board_sensor = "invalid"; return snprintf(buf, PAGE_SIZE, "%s", board_sensor); } static DEVICE_ATTR(board_sensor, 0664, thermal_board_sensor_show, NULL); static ssize_t thermal_board_sensor_temp_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, board_sensor_temp); } static ssize_t thermal_board_sensor_temp_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { #if IS_ENABLED(CONFIG_XIAOMI_SMART_CHG) int ret = 0; int val = -1; #endif snprintf(board_sensor_temp, BOARD__BUFFER_SIZE, buf); #if IS_ENABLED(CONFIG_XIAOMI_SMART_CHG) val = simple_strtol(buf, NULL, 10); ret = mi_thermal_notifier_call_chain(THERMAL_BOARD_TEMP, val/100); if (ret) { pr_err("%s: mi_thermal_notifier_call_chain error:%d\n", __func__, ret); } #endif return len; } static DEVICE_ATTR(board_sensor_temp, 0664, thermal_board_sensor_temp_show, thermal_board_sensor_temp_store); static ssize_t thermal_modem_rate_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&modem_rate)); } static ssize_t thermal_modem_rate_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { int val = -1; val = simple_strtol(buf, NULL, 10); atomic_set(&modem_rate, val); return len; } static DEVICE_ATTR(modem_rate, 0664, thermal_modem_rate_show, thermal_modem_rate_store); static ssize_t thermal_modem_level_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&modem_level)); } static ssize_t thermal_modem_level_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { int val = -1; val = simple_strtol(buf, NULL, 10); atomic_set(&modem_level, val); return len; } static DEVICE_ATTR(modem_level, 0664, thermal_modem_level_show, thermal_modem_level_store); static ssize_t thermal_board_sensor_second_temp_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, board_sensor_second_temp); } static ssize_t thermal_board_sensor_second_temp_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { snprintf(board_sensor_second_temp, BOARD__BUFFER_SIZE, buf); return len; } static DEVICE_ATTR(board_sensor_second_temp, 0664, thermal_board_sensor_second_temp_show, thermal_board_sensor_second_temp_store); static ssize_t thermal_board_sensor_other_temp_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, board_sensor_other_temp); } static ssize_t thermal_board_sensor_other_temp_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { snprintf(board_sensor_other_temp, BOARD__BUFFER_SIZE, buf); return len; } static DEVICE_ATTR(board_sensor_other_temp, 0664, thermal_board_sensor_other_temp_show, thermal_board_sensor_other_temp_store); static ssize_t thermal_ambient_sensor_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "%s", "ABT-SENSOR"); } static DEVICE_ATTR(ambient_sensor, 0664, thermal_ambient_sensor_show, NULL); static ssize_t thermal_ambient_sensor_temp_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, ambient_sensor_temp); } static ssize_t thermal_ambient_sensor_temp_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { snprintf(ambient_sensor_temp, BOARD__BUFFER_SIZE, buf); return len; } static DEVICE_ATTR(ambient_sensor_temp, 0664, thermal_ambient_sensor_temp_show, thermal_ambient_sensor_temp_store); #ifdef CONFIG_HAVE_CHARGE_TEMP static ssize_t thermal_board_sensor_charge_temp_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, board_sensor_charge_temp); } static ssize_t thermal_board_sensor_charge_temp_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { snprintf(board_sensor_charge_temp, BOARD__BUFFER_SIZE, buf); return len; } static DEVICE_ATTR(board_sensor_charge_temp, 0664, thermal_board_sensor_charge_temp_show, thermal_board_sensor_charge_temp_store); #endif static ssize_t thermal_modem_limit_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&modem_limit)); } static ssize_t thermal_modem_limit_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { int val = -1; val = simple_strtol(buf, NULL, 10); atomic_set(&modem_limit, val); return len; } static DEVICE_ATTR(modem_limit, 0664, thermal_modem_limit_show, thermal_modem_limit_store); static ssize_t thermal_wifi_limit_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&wifi_limit)); } static ssize_t thermal_wifi_limit_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { int val = -1; val = simple_strtol(buf, NULL, 10); atomic_set(&wifi_limit, val); return len; } static DEVICE_ATTR(wifi_limit, 0664, thermal_wifi_limit_show, thermal_wifi_limit_store); static ssize_t thermal_flash_state_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&flash_state)); } static ssize_t thermal_flash_state_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { int val = -1; val = simple_strtol(buf, NULL, 10); atomic_set(&flash_state, val); return len; } static DEVICE_ATTR(flash_state, 0664, thermal_flash_state_show, thermal_flash_state_store); static ssize_t thermal_board_sensor_temp_comp_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&board_sensor_temp_comp_default)); } static ssize_t thermal_board_sensor_temp_comp_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { int val = -1; val = simple_strtol(buf, NULL, 10); atomic_set(&board_sensor_temp_comp_default, val); return len; } static DEVICE_ATTR(board_sensor_temp_comp, 0664, thermal_board_sensor_temp_comp_show, thermal_board_sensor_temp_comp_store); static ssize_t thermal_poor_modem_limit_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&poor_modem_limit)); } static ssize_t thermal_poor_modem_limit_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { int val = -1; val = simple_strtol(buf, NULL, 10); atomic_set(&poor_modem_limit, val); return len; } static DEVICE_ATTR(poor_modem_limit, 0664, thermal_poor_modem_limit_show, thermal_poor_modem_limit_store); static ssize_t thermal_cpu_nolimit_temp_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&cpu_nolimit_temp_default)); } static ssize_t thermal_cpu_nolimit_temp_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { int val = -1; val = simple_strtol(buf, NULL, 10); atomic_set(&cpu_nolimit_temp_default, val); return len; } static ssize_t temp_aware_show (struct class *cls, struct class_attribute *attr, char *buf) { int mode = 0; mode = scnprintf(buf, PAGE_SIZE, "%d\n", temp_aware_mode); return mode; } static ssize_t temp_aware_store(struct class *cls, struct class_attribute *attr, const char *buf, size_t count) { unsigned long val; if (kstrtoul(buf, 10, &val)) return -EINVAL; if (TEMP_AWARE_MIN <= val && val < TEMP_AWARE_MAX) temp_aware_mode = val; return count; } static DEVICE_ATTR(cpu_nolimit_temp, 0664, thermal_cpu_nolimit_temp_show, thermal_cpu_nolimit_temp_store); #ifdef CONFIG_MI_THERMAL_MULTI_CHARGE static ssize_t thermal_charger_temp_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&charger_mode)); } static ssize_t thermal_charger_temp_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { int val = -1; val = simple_strtol(buf, NULL, 10); atomic_set(&charger_mode, val); return len; } static DEVICE_ATTR(charger_temp, 0664, thermal_charger_temp_show, thermal_charger_temp_store); static ssize_t thermal_usb_online_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "%d\n", usb_state.usb_online); } static DEVICE_ATTR(usb_online, 0664, thermal_usb_online_show, NULL); static int usb_online_callback(struct notifier_block *nb, unsigned long val, void *data) { struct power_supply *psy = data; if (strcmp(psy->desc->name, "usb")) return NOTIFY_OK; schedule_work(&usb_state.usb_state_work); return NOTIFY_OK; } static void usb_online_work(struct work_struct *work) { static struct power_supply *usb_psy; union power_supply_propval ret = {0,}; int err = 0; if (!usb_psy) usb_psy = power_supply_get_by_name("usb"); if (usb_psy) { err = power_supply_get_property(usb_psy, POWER_SUPPLY_PROP_ONLINE, &ret); if (err) { pr_err("usb online read error:%d\n",err); return; } usb_state.usb_online = ret.intval; if (mi_thermal_dev.dev) sysfs_notify(&mi_thermal_dev.dev->kobj, NULL, "usb_online"); } } #endif static int of_parse_thermal_message(void) { struct device_node *np; np = of_find_node_by_name(NULL, "thermal-message"); if (!np) return -EINVAL; if (of_property_read_string(np, "board-sensor", &board_sensor)) return -EINVAL; pr_info("%s board sensor: %s\n", __func__, board_sensor); return 0; } static struct class_attribute power_debug_attrs[] = { __ATTR(temp_aware, 0664, temp_aware_show, temp_aware_store), __ATTR_NULL }; static struct attribute *mi_thermal_dev_attr_group[] = { &dev_attr_temp_state.attr, &dev_attr_cpu_limits.attr, &dev_attr_sconfig.attr, &dev_attr_screen_state.attr, &dev_attr_boost.attr, &dev_attr_board_sensor.attr, &dev_attr_board_sensor_temp.attr, &dev_attr_board_sensor_second_temp.attr, &dev_attr_board_sensor_other_temp.attr, &dev_attr_balance_mode.attr, &dev_attr_modem_limit.attr, &dev_attr_wifi_limit.attr, &dev_attr_flash_state.attr, &dev_attr_thermal_max_brightness.attr, &dev_attr_market_download_limit.attr, &dev_attr_board_sensor_temp_comp.attr, &dev_attr_poor_modem_limit.attr, &dev_attr_cpu_nolimit_temp.attr, &dev_attr_modem_rate.attr, &dev_attr_modem_level.attr, &dev_attr_ambient_sensor.attr, &dev_attr_ambient_sensor_temp.attr, #ifdef CONFIG_HAVE_CHARGE_TEMP &dev_attr_board_sensor_charge_temp.attr, #endif #ifdef CONFIG_MI_THERMAL_MULTI_CHARGE &dev_attr_charger_temp.attr, &dev_attr_usb_online.attr, #endif NULL, }; static const char *get_screen_state_name(int mode) { switch (mode) { case DRM_PANEL_EVENT_UNBLANK: return "On"; case DRM_PANEL_EVENT_BLANK_LP: return "Doze"; case DRM_PANEL_EVENT_BLANK: return "Off"; default: return "Unknown"; } } static void screen_state_for_thermal_callback(enum panel_event_notifier_tag tag, struct panel_event_notification *notification, void *client_data) { if (!notification) { printk(KERN_ERR "%s:Invalid notification\n", __func__); return; } if(notification->notif_data.early_trigger) { return; } if(tag == PANEL_EVENT_NOTIFICATION_PRIMARY){ switch (notification->notif_type) { case DRM_PANEL_EVENT_UNBLANK: screen_light = screen_light | 0x1; break; case DRM_PANEL_EVENT_BLANK: case DRM_PANEL_EVENT_BLANK_LP: screen_light = screen_light & 0x2; break; case DRM_PANEL_EVENT_FPS_CHANGE: return; default: return; } printk(KERN_ERR "%s: %s, screen_light = %d, %s\n", __func__, get_screen_state_name(notification->notif_type), screen_light, board_sensor_temp); } #if IS_ENABLED(CONFIG_HAVE_MULTI_SCREEN) else if (tag == PANEL_EVENT_NOTIFICATION_SECONDARY) { switch (notification->notif_type) { case DRM_PANEL_EVENT_UNBLANK: screen_light = screen_light | 0x2; break; case DRM_PANEL_EVENT_BLANK: case DRM_PANEL_EVENT_BLANK_LP: screen_light = screen_light & 0x1; break; case DRM_PANEL_EVENT_FPS_CHANGE: return; default: return; } printk(KERN_ERR "%s: %s, sencondery_screen_light = %d, %s\n", __func__, get_screen_state_name(notification->notif_type), screen_light, board_sensor_temp); } #endif if (screen_light) { screen_state = 1; printk(KERN_ERR "%s: screen_light = %d, so screen_state = %d, %s\n", __func__, screen_light, screen_state, board_sensor_temp); } else { screen_state = 0; printk(KERN_ERR "%s: screen_light = %d, so screen_state = %d, %s\n", __func__, screen_light, screen_state, board_sensor_temp); } if (screen_last_status != screen_state) { sysfs_notify(&mi_thermal_dev.dev->kobj, NULL, "screen_state"); screen_last_status = screen_state; } } static int thermal_check_panel(struct device_node *np) { int i; int count; struct device_node *node; struct drm_panel *panel; count = of_count_phandle_with_args(np, "panel", NULL); printk(KERN_ERR "%s: count of panel in node is: %d\n",__func__ ,count); if (count <= 0){ #if IS_ENABLED(CONFIG_HAVE_MULTI_SCREEN) goto find_sec_panel; #endif goto out; } for (i = 0; i < count; i++) { node = of_parse_phandle(np, "panel", i); printk(KERN_ERR "%s: try to add of node panel: %s\n",__func__ ,node); panel = of_drm_find_panel(node); of_node_put(node); if (!IS_ERR(panel)) { prim_panel = panel; break; }else{ prim_panel = NULL; } } if (PTR_ERR(prim_panel) == -EPROBE_DEFER) { pr_err("%s ERROR: Cannot fine prim_panel of node!", __func__); } printk(KERN_ERR "%s: count of panel in node PTR_ERR_prim_panel is: %d\n",__func__ , PTR_ERR(prim_panel)); #if IS_ENABLED(CONFIG_HAVE_MULTI_SCREEN) find_sec_panel: count = of_count_phandle_with_args(np, "panel1", NULL); printk(KERN_ERR "%s: count of panel1 in node is: %d\n",__func__ , count); if (count <= 0){ goto out; } for (i = 0; i < count; i++) { node = of_parse_phandle(np, "panel1", i); printk(KERN_ERR "%s: try to add of node panel1: %s\n",__func__ , node); panel = of_drm_find_panel(node); of_node_put(node); if (!IS_ERR(panel)) { sec_panel = panel; break; } } if (PTR_ERR(sec_panel) == -EPROBE_DEFER) { pr_err("%s ERROR: Cannot fine sec_panel of node!", __func__); } printk(KERN_ERR "%s: count of panel1 in node PTR_ERR_sec_panel is: %d\n",__func__ , PTR_ERR(sec_panel)); #endif out: return 0; } static void create_thermal_message_node(void) { int ret = 0; struct kernfs_node *sysfs_sd = NULL; struct kernfs_node *thermal_sd = NULL; struct kernfs_node *class_sd = NULL; struct class *cls = NULL; struct subsys_private *cp = NULL; struct kobject *kobj_tmp = NULL; sysfs_sd = kernel_kobj->sd->parent; if (!sysfs_sd) { printk(KERN_ERR "%s: sysfs_sd is NULL\n", __func__); } else { class_sd = kernfs_find_and_get(sysfs_sd, "class"); if (!class_sd) { printk(KERN_ERR "%s:can not find class_sd\n", __func__); } else { thermal_sd = kernfs_find_and_get(class_sd, "thermal"); if (thermal_sd) { kobj_tmp = (struct kobject *)thermal_sd->priv; if (kobj_tmp) { cp = to_subsys_private(kobj_tmp); cls = cp->class; } else { printk(KERN_ERR "%s:can not find thermal kobj\n", __func__); } } else { printk(KERN_ERR "%s:can not find thermal_sd\n", __func__); } } } if (!mi_thermal_dev.class && cls) { mi_thermal_dev.class = cls; mi_thermal_dev.dev = device_create(mi_thermal_dev.class, NULL, 'H', NULL, "thermal_message"); if (!mi_thermal_dev.dev) { pr_err("%s create device dev err\n", __func__); return; } mi_thermal_dev.attrs.attrs = mi_thermal_dev_attr_group; ret = sysfs_create_group(&mi_thermal_dev.dev->kobj, &mi_thermal_dev.attrs); if (ret) { pr_err("%s ERROR: Cannot create sysfs structure!:%d\n", __func__, ret); return; } } } static int __init power_debug_init(void) { int ret; int i; power_debug_class = class_create(THIS_MODULE, "power_debug"); if (IS_ERR(power_debug_class)) { ret = PTR_ERR(power_debug_class); pr_err("Failed to create power_debug class: %d\n", ret); return ret; } for (i = 0; power_debug_attrs[i].attr.name!= NULL; i++){ ret = class_create_file(power_debug_class, &power_debug_attrs[i]); if (ret != 0){ pr_err("Failed to create temp_aware attribute: %d\n", ret); class_destroy(power_debug_class); } } pr_info("power_debug module loaded\n"); return 0; } static void __exit power_debug_exit(void) { int i; for (i = 0; power_debug_attrs[i].attr.name!= NULL; i++){ class_remove_file(power_debug_class, &power_debug_attrs[i]); } class_destroy(power_debug_class); pr_info("power_debug module unloaded\n"); } static void destroy_thermal_message_node(void) { printk(KERN_ERR "%s:destroy_thermal_message_node", __func__); sysfs_remove_group(&mi_thermal_dev.dev->kobj, &mi_thermal_dev.attrs); if (NULL != mi_thermal_dev.class){ device_destroy(mi_thermal_dev.class,'H'); mi_thermal_dev.class = NULL; } } static void screen_state_check(struct work_struct *work) { #if defined(CONFIG_OF) && defined(CONFIG_DRM_PANEL) struct device_node *node; void *pvt_data = NULL; int error = 0; static int retry_count = 10; cpu_thermal_init(); node = of_find_node_by_name(NULL, "thermal-screen"); if (!node) { pr_err("%s ERROR: Cannot find node with panel!", __func__); return; } error = thermal_check_panel(node); if (prim_panel) { if (!cookie) { cookie = panel_event_notifier_register(PANEL_EVENT_NOTIFICATION_PRIMARY, PANEL_EVENT_NOTIFIER_CLIENT_THERMAL, prim_panel, screen_state_for_thermal_callback, pvt_data); if (IS_ERR(cookie)) printk(KERN_ERR "%s:Failed to register for prim_panel events\n", __func__); else printk(KERN_ERR "%s:prim_panel_event_notifier_register register succeed\n", __func__); } } else if (retry_count > 0) { printk(KERN_ERR "%s:prim_panel is NULL Failed to register for panel events\n", __func__); retry_count--; queue_delayed_work(screen_state_wq, &screen_state_dw, 5 * HZ); } #if IS_ENABLED(CONFIG_HAVE_MULTI_SCREEN) if (sec_panel) { if (!cookie_sec) { cookie_sec = panel_event_notifier_register(PANEL_EVENT_NOTIFICATION_SECONDARY, PANEL_EVENT_NOTIFIER_CLIENT_THERMAL_SECOND, sec_panel, screen_state_for_thermal_callback, pvt_data); if (IS_ERR(cookie_sec)) printk(KERN_ERR "%s:Failed to register for sec_panel events\n", __func__); else printk(KERN_ERR "%s:sec_panel_event_notifier_register register succeed\n", __func__); } } else if (retry_count_sec > 0) { printk(KERN_ERR "%s:sec_panel is NULL Failed to register for panel events\n", __func__); retry_count_sec--; queue_delayed_work(screen_state_wq, &screen_state_dw, 5 * HZ); } #endif #endif } static int __init mi_thermal_interface_init(void) { int result; #if defined(CONFIG_OF) && defined(CONFIG_DRM_PANEL) screen_state_wq = create_singlethread_workqueue("screen_state_wq"); if (screen_state_wq) { INIT_DELAYED_WORK(&screen_state_dw, screen_state_check); queue_delayed_work(screen_state_wq, &screen_state_dw, 5 * HZ); } #endif result = of_parse_thermal_message(); if (result) printk(KERN_ERR "%s:Thermal: Can not parse thermal message node, return %d\n", __func__,result); create_thermal_message_node(); #ifdef CONFIG_MI_THERMAL_MULTI_CHARGE INIT_WORK(&usb_state.usb_state_work, usb_online_work); usb_state.psy_nb.notifier_call=usb_online_callback; result = power_supply_reg_notifier(&usb_state.psy_nb); if (result < 0) { pr_err("usb online notifier registration error. return: %d\n", result); } #endif power_debug_init(); return 0; } static void __exit mi_thermal_interface_exit(void) { #if defined(CONFIG_OF) && defined(CONFIG_DRM_PANEL) if (screen_state_wq) { cancel_delayed_work_sync(&screen_state_dw); destroy_workqueue(screen_state_wq); } if (prim_panel && !IS_ERR(cookie)) { panel_event_notifier_unregister(cookie); } else { printk(KERN_ERR "%s:prim_panel_event_notifier_unregister falt\n", __func__); } #if IS_ENABLED(CONFIG_HAVE_MULTI_SCREEN) if (sec_panel && !IS_ERR(cookie_sec)) { panel_event_notifier_unregister(cookie_sec); } else { printk(KERN_ERR "%s:sec_panel_event_notifier_unregister falt\n", __func__); } #endif #endif destroy_thermal_message_node(); power_debug_exit(); destory_thermal_cpu(); } module_init(mi_thermal_interface_init); module_exit(mi_thermal_interface_exit); MODULE_AUTHOR("Xiaomi thermal team"); MODULE_DESCRIPTION("Xiaomi thermal control interface"); MODULE_LICENSE("GPL v2");
最新发布
12-11
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值