181 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Power-source driver for Bay Trail Crystal Cove PMIC4 *5 * Copyright (c) 2023 Hans de Goede <hdegoede@redhat.com>6 *7 * Based on intel_crystalcove_pwrsrc.c from Android kernel sources, which is:8 * Copyright (C) 2013 Intel Corporation9 */10 11#include <linux/debugfs.h>12#include <linux/mfd/intel_soc_pmic.h>13#include <linux/module.h>14#include <linux/platform_device.h>15#include <linux/regmap.h>16 17#define CRYSTALCOVE_SPWRSRC_REG 0x1E18#define CRYSTALCOVE_RESETSRC0_REG 0x2019#define CRYSTALCOVE_RESETSRC1_REG 0x2120#define CRYSTALCOVE_WAKESRC_REG 0x2221 22struct crc_pwrsrc_data {23 struct regmap *regmap;24 struct dentry *debug_dentry;25 unsigned int resetsrc0;26 unsigned int resetsrc1;27 unsigned int wakesrc;28};29 30static const char * const pwrsrc_pwrsrc_info[] = {31 /* bit 0 */ "USB",32 /* bit 1 */ "DC in",33 /* bit 2 */ "Battery",34 NULL,35};36 37static const char * const pwrsrc_resetsrc0_info[] = {38 /* bit 0 */ "SOC reporting a thermal event",39 /* bit 1 */ "critical PMIC temperature",40 /* bit 2 */ "critical system temperature",41 /* bit 3 */ "critical battery temperature",42 /* bit 4 */ "VSYS under voltage",43 /* bit 5 */ "VSYS over voltage",44 /* bit 6 */ "battery removal",45 NULL,46};47 48static const char * const pwrsrc_resetsrc1_info[] = {49 /* bit 0 */ "VCRIT threshold",50 /* bit 1 */ "BATID reporting battery removal",51 /* bit 2 */ "user pressing the power button",52 NULL,53};54 55static const char * const pwrsrc_wakesrc_info[] = {56 /* bit 0 */ "user pressing the power button",57 /* bit 1 */ "a battery insertion",58 /* bit 2 */ "a USB charger insertion",59 /* bit 3 */ "an adapter insertion",60 NULL,61};62 63static void crc_pwrsrc_log(struct seq_file *seq, const char *prefix,64 const char * const *info, unsigned int reg_val)65{66 int i;67 68 for (i = 0; info[i]; i++) {69 if (reg_val & BIT(i))70 seq_printf(seq, "%s by %s\n", prefix, info[i]);71 }72}73 74static int pwrsrc_show(struct seq_file *seq, void *unused)75{76 struct crc_pwrsrc_data *data = seq->private;77 unsigned int reg_val;78 int ret;79 80 ret = regmap_read(data->regmap, CRYSTALCOVE_SPWRSRC_REG, ®_val);81 if (ret)82 return ret;83 84 crc_pwrsrc_log(seq, "System powered", pwrsrc_pwrsrc_info, reg_val);85 return 0;86}87 88static int resetsrc_show(struct seq_file *seq, void *unused)89{90 struct crc_pwrsrc_data *data = seq->private;91 92 crc_pwrsrc_log(seq, "Last shutdown caused", pwrsrc_resetsrc0_info, data->resetsrc0);93 crc_pwrsrc_log(seq, "Last shutdown caused", pwrsrc_resetsrc1_info, data->resetsrc1);94 return 0;95}96 97static int wakesrc_show(struct seq_file *seq, void *unused)98{99 struct crc_pwrsrc_data *data = seq->private;100 101 crc_pwrsrc_log(seq, "Last wake caused", pwrsrc_wakesrc_info, data->wakesrc);102 return 0;103}104 105DEFINE_SHOW_ATTRIBUTE(pwrsrc);106DEFINE_SHOW_ATTRIBUTE(resetsrc);107DEFINE_SHOW_ATTRIBUTE(wakesrc);108 109static int crc_pwrsrc_read_and_clear(struct crc_pwrsrc_data *data,110 unsigned int reg, unsigned int *val)111{112 int ret;113 114 ret = regmap_read(data->regmap, reg, val);115 if (ret)116 return ret;117 118 return regmap_write(data->regmap, reg, *val);119}120 121static int crc_pwrsrc_probe(struct platform_device *pdev)122{123 struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent);124 struct crc_pwrsrc_data *data;125 int ret;126 127 data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);128 if (!data)129 return -ENOMEM;130 131 data->regmap = pmic->regmap;132 133 /*134 * Read + clear resetsrc0/1 and wakesrc now, so that they get135 * cleared even if the debugfs interface is never used.136 *137 * Properly clearing the wakesrc is important, leaving bit 0 of it138 * set turns reboot into poweroff on some tablets.139 */140 ret = crc_pwrsrc_read_and_clear(data, CRYSTALCOVE_RESETSRC0_REG, &data->resetsrc0);141 if (ret)142 return ret;143 144 ret = crc_pwrsrc_read_and_clear(data, CRYSTALCOVE_RESETSRC1_REG, &data->resetsrc1);145 if (ret)146 return ret;147 148 ret = crc_pwrsrc_read_and_clear(data, CRYSTALCOVE_WAKESRC_REG, &data->wakesrc);149 if (ret)150 return ret;151 152 data->debug_dentry = debugfs_create_dir(KBUILD_MODNAME, NULL);153 debugfs_create_file("pwrsrc", 0444, data->debug_dentry, data, &pwrsrc_fops);154 debugfs_create_file("resetsrc", 0444, data->debug_dentry, data, &resetsrc_fops);155 debugfs_create_file("wakesrc", 0444, data->debug_dentry, data, &wakesrc_fops);156 157 platform_set_drvdata(pdev, data);158 return 0;159}160 161static void crc_pwrsrc_remove(struct platform_device *pdev)162{163 struct crc_pwrsrc_data *data = platform_get_drvdata(pdev);164 165 debugfs_remove_recursive(data->debug_dentry);166}167 168static struct platform_driver crc_pwrsrc_driver = {169 .probe = crc_pwrsrc_probe,170 .remove_new = crc_pwrsrc_remove,171 .driver = {172 .name = "crystal_cove_pwrsrc",173 },174};175module_platform_driver(crc_pwrsrc_driver);176 177MODULE_ALIAS("platform:crystal_cove_pwrsrc");178MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");179MODULE_DESCRIPTION("Power-source driver for Bay Trail Crystal Cove PMIC");180MODULE_LICENSE("GPL");181