blob: a5c236b31cd40121a67d495cbf89d933f98a0baf [file] [log] [blame]
/*
* Authors:
* Shaun Kelsey <shaunkelsey@google.com>
*
* Airbrush State Manager Sysfs Entries
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*/
#include <linux/airbrush-sm-ctrl.h>
#include <linux/stat.h>
#include <linux/sysfs.h>
#include "airbrush-regs.h"
static ssize_t mapped_chip_state_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
u32 val;
struct ab_state_context *sc =
(struct ab_state_context *)dev_get_drvdata(dev);
val = ab_sm_get_state((struct ab_state_context *)sc);
return scnprintf(buf, PAGE_SIZE, "%u\n", val);
}
static ssize_t mapped_chip_state_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
int ret;
int val;
struct ab_state_context *sc =
(struct ab_state_context *)dev_get_drvdata(dev);
if (kstrtoint(buf, 10, &val) < 0) {
dev_err(sc->dev, "Bad input format\n");
return -EIO;
}
ret = ab_sm_set_state(sc, val);
if (ret < 0) {
dev_err(sc->dev, "%s: State change failed, ret %d\n",
__func__, ret);
return ret;
}
return count;
}
static ssize_t version_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
enum ab_chip_id chip_id;
struct ab_state_context *sc =
(struct ab_state_context *)dev_get_drvdata(dev);
chip_id = ab_get_chip_id(sc);
switch (chip_id) {
case CHIP_ID_A0:
return scnprintf(buf, PAGE_SIZE, "A0\n");
case CHIP_ID_B0:
return scnprintf(buf, PAGE_SIZE, "B0\n");
default:
return scnprintf(buf, PAGE_SIZE, "Unknown\n");
}
return 0;
}
static ssize_t state_stats_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
/* keep contents of states array in sync with stat_state enum */
const char * const states[STAT_STATE_SIZE] = {"ACTIVE", "SLEEP",
"DEEP SLEEP", "SUSPEND", "OFF", "UNKNOWN"};
u32 pos = 0;
u32 i;
struct ab_state_context *sc =
(struct ab_state_context *)dev_get_drvdata(dev);
pos += scnprintf(buf + pos, PAGE_SIZE - pos,
"Pixel Visual Core Subsystem Power Stats\n");
mutex_lock(&sc->state_transitioning_lock);
for (i = 0; i < STAT_STATE_SIZE; i++) {
ktime_t adjusted_duration = sc->state_stats[i].duration;
ktime_t last_entry = sc->state_stats[i].last_entry;
ktime_t last_exit = sc->state_stats[i].last_exit;
/* adjust duration for current state */
if (ab_chip_state_to_stat_state(sc->curr_chip_substate_id) == i
&& ktime_after(last_entry, last_exit)) {
ktime_t partial_duration = ktime_sub(
ktime_get_boottime(), last_entry);
adjusted_duration = ktime_add(adjusted_duration,
partial_duration);
}
pos += scnprintf(buf + pos, PAGE_SIZE - pos, "%s\n"
"\tCumulative count: %llu\n"
"\tCumulative duration msec: %llu\n"
"\tLast entry timestamp msec: %llu\n"
"\tLast exit timestamp msec: %llu\n",
states[i], sc->state_stats[i].counter,
ktime_to_ms(adjusted_duration),
ktime_to_ms(last_entry),
ktime_to_ms(last_exit));
}
mutex_unlock(&sc->state_transitioning_lock);
return pos;
}
/* Trigger an error event for testing purpose; input is ignored */
static ssize_t gen_error_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
dev_warn(dev, "Triggering an error event for testing.\n");
sysfs_notify(&dev->kobj, NULL, "error_event");
return count;
}
/* Supposed to be polled by userspace daemon. */
static ssize_t error_event_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
/* Return fixed string "1" */
return scnprintf(buf, PAGE_SIZE, "%d\n", 1);
}
static ssize_t clk_expect_frequency_show(struct device *dev,
char *buf, enum block_name blk_id)
{
u64 clk_frequency_expt;
struct ab_state_context *sc =
(struct ab_state_context *)dev_get_drvdata(dev);
mutex_lock(&sc->state_transitioning_lock);
clk_frequency_expt = sc->blocks[blk_id].current_state->clk_frequency;
mutex_unlock(&sc->state_transitioning_lock);
return scnprintf(buf, PAGE_SIZE, "%llu\n", clk_frequency_expt);
}
static ssize_t ipu_clk_expect_frequency_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return clk_expect_frequency_show(dev, buf, BLK_IPU);
}
static ssize_t tpu_clk_expect_frequency_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return clk_expect_frequency_show(dev, buf, BLK_TPU);
}
static ssize_t aon_clk_expect_frequency_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return clk_expect_frequency_show(dev, buf, BLK_AON);
}
static ssize_t mif_clk_expect_frequency_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return clk_expect_frequency_show(dev, buf, BLK_MIF);
}
static ssize_t clk_meas_frequency_show(struct device *dev,
char *buf, enum block_name blk_id)
{
unsigned int clkout_idx, clkout_blk_idx, clkout_clk_idx;
u64 val;
int retval;
struct ab_state_context *sc =
(struct ab_state_context *)dev_get_drvdata(dev);
clkout_idx = 0;
switch (blk_id) {
case BLK_IPU:
clkout_blk_idx = 1;
clkout_clk_idx = 2;
break;
case BLK_TPU:
clkout_blk_idx = 2;
clkout_clk_idx = 2;
break;
case BLK_AON:
clkout_blk_idx = 0;
clkout_clk_idx = 4;
break;
case BLK_MIF:
clkout_blk_idx = 4;
clkout_clk_idx = 11;
break;
default:
return scnprintf(buf, PAGE_SIZE, "UnKnown\n");
}
ab_clkout_sel(sc, clkout_idx);
ab_clkout_blksel(sc, clkout_blk_idx);
ab_clkout_clksel(sc, clkout_clk_idx);
retval = ab_clkout_freq(sc, &val);
return scnprintf(buf, PAGE_SIZE, "%llu\n", val);
}
static ssize_t ipu_clk_meas_frequency_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return clk_meas_frequency_show(dev, buf, BLK_IPU);
}
static ssize_t tpu_clk_meas_frequency_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return clk_meas_frequency_show(dev, buf, BLK_TPU);
}
static ssize_t aon_clk_meas_frequency_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return clk_meas_frequency_show(dev, buf, BLK_AON);
}
static ssize_t mif_clk_meas_frequency_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return clk_meas_frequency_show(dev, buf, BLK_MIF);
}
static struct device_attribute ab_sm_attrs[] = {
__ATTR_RW(mapped_chip_state),
__ATTR_RO(version),
__ATTR_RO(state_stats),
__ATTR_RO(error_event),
__ATTR_WO(gen_error),
__ATTR_RO(ipu_clk_expect_frequency),
__ATTR_RO(tpu_clk_expect_frequency),
__ATTR_RO(aon_clk_expect_frequency),
__ATTR_RO(mif_clk_expect_frequency),
__ATTR_RO(ipu_clk_meas_frequency),
__ATTR_RO(tpu_clk_meas_frequency),
__ATTR_RO(aon_clk_meas_frequency),
__ATTR_RO(mif_clk_meas_frequency)
};
void ab_sm_create_sysfs(struct ab_state_context *sc)
{
int i;
for (i = 0; i < ARRAY_SIZE(ab_sm_attrs); i++)
device_create_file(sc->dev, &ab_sm_attrs[i]);
}
void ab_sm_remove_sysfs(struct ab_state_context *sc)
{
int i;
for (i = 0; i < ARRAY_SIZE(ab_sm_attrs); i++)
device_remove_file(sc->dev, &ab_sm_attrs[i]);
}