blob: 1c4210b4f1991d4b5c451e3c368e19b58aa4ad1a [file] [log] [blame]
/*
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
*
* Authors:
* Shaik Ameer Basha <shaik.ameer@samsung.com>
* Raman Kumar Banka (raman.k2@samsung.com)
*
* Airbrush State Manager Control driver.
*
* 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/debugfs.h>
#include <linux/gpio/consumer.h>
#include <linux/airbrush-sm-ctrl.h>
#include "airbrush-regs.h"
#include "airbrush-ddr.h"
static int mapped_chip_state_set(void *data, u64 val)
{
struct ab_state_context *sc = (struct ab_state_context *)data;
int ret;
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;
}
static int mapped_chip_state_get(void *sc, u64 *val)
{
*val = ab_sm_get_state((struct ab_state_context *)sc);
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(fops_mapped_chip_state, mapped_chip_state_get,
mapped_chip_state_set, "%llu\n");
#if IS_ENABLED(CONFIG_AIRBRUSH_SM_PROFILE)
static int time_stamps_set(void *data, u64 val)
{
struct ab_state_context *sc = (struct ab_state_context *)data;
sc->ts_enabled = !!val;
return 0;
}
static int time_stamps_get(void *data, u64 *val)
{
struct ab_state_context *sc = (struct ab_state_context *)data;
*val = (uint64_t)sc->ts_enabled;
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(fops_time_stamps, time_stamps_get,
time_stamps_set, "%llu\n");
#endif
static int ldo5_delay_set(void *data, u64 val)
{
struct ab_state_context *sc = (struct ab_state_context *)data;
sc->ldo5_delay = val;
return 0;
}
static int ldo5_delay_get(void *data, u64 *val)
{
struct ab_state_context *sc = (struct ab_state_context *)data;
*val = sc->ldo5_delay;
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(fops_ldo5_delay, ldo5_delay_get,
ldo5_delay_set, "%llu\n");
static int s60_delay_set(void *data, u64 val)
{
struct ab_state_context *sc = (struct ab_state_context *)data;
sc->s60_delay = val;
return 0;
}
static int s60_delay_get(void *data, u64 *val)
{
struct ab_state_context *sc = (struct ab_state_context *)data;
*val = sc->s60_delay;
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(fops_s60_delay, s60_delay_get,
s60_delay_set, "%llu\n");
static int ldo4_delay_set(void *data, u64 val)
{
struct ab_state_context *sc = (struct ab_state_context *)data;
sc->ldo4_delay = val;
return 0;
}
static int ldo4_delay_get(void *data, u64 *val)
{
struct ab_state_context *sc = (struct ab_state_context *)data;
*val = sc->ldo4_delay;
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(fops_ldo4_delay, ldo4_delay_get,
ldo4_delay_set, "%llu\n");
static int smps2_delay_set(void *data, u64 val)
{
struct ab_state_context *sc = (struct ab_state_context *)data;
sc->smps2_delay = val;
return 0;
}
static int smps2_delay_get(void *data, u64 *val)
{
struct ab_state_context *sc = (struct ab_state_context *)data;
*val = sc->smps2_delay;
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(fops_smps2_delay, smps2_delay_get,
smps2_delay_set, "%llu\n");
static int alternate_boot_set(void *data, u64 val)
{
struct ab_state_context *sc = (struct ab_state_context *)data;
sc->alternate_boot = !!val;
return 0;
}
static int alternate_boot_get(void *data, u64 *val)
{
struct ab_state_context *sc = (struct ab_state_context *)data;
*val = (uint64_t)sc->alternate_boot;
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(fops_alternate_boot, alternate_boot_get,
alternate_boot_set, "%llu\n");
static int asv_version_set(void *data, u64 val)
{
struct ab_state_context *sc = data;
set_asv_version(&sc->asv_info, val);
return 0;
}
static int asv_version_get(void *data, u64 *val)
{
struct ab_state_context *sc = data;
*val = sc->asv_info.asv_version;
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(fops_asv_version_override, asv_version_get,
asv_version_set, "%llu\n");
/*
* This is added for testing error handling case when pcie link init
* is skipped.
*/
static int ab_sm_skip_pcie_link_init_set(void *data, u64 val)
{
struct ab_state_context *sc = (struct ab_state_context *)data;
sc->debug_skip_pcie_link_init = !!val;
return 0;
}
static int ab_sm_skip_pcie_link_init_get(void *data, u64 *val)
{
struct ab_state_context *sc = (struct ab_state_context *)data;
*val = sc->debug_skip_pcie_link_init;
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(ab_sm_skip_pcie_link_init_fops,
ab_sm_skip_pcie_link_init_get,
ab_sm_skip_pcie_link_init_set, "%llu\n");
static int id_get(void *blk_passed, u64 *val)
{
struct block *blk = (struct block *)blk_passed;
*val = blk->current_state->id;
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(fops_id, id_get, NULL, "%llu\n");
static int vrail_status_get(void *blk_passed, u64 *val)
{
struct block *blk = (struct block *)blk_passed;
*val = blk->current_state->rail_en;
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(fops_vrail_status,
vrail_status_get, NULL, "%llu\n");
static int logic_voltage_get(void *blk_passed, u64 *val)
{
struct block *blk = (struct block *)blk_passed;
*val = blk->current_state->logic_voltage;
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(fops_logic_voltage,
logic_voltage_get, NULL, "%llu\n");
static int clk_status_get(void *blk_passed, u64 *val)
{
struct block *blk = (struct block *)blk_passed;
*val = blk->current_state->clk_status;
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(fops_clk_status, clk_status_get, NULL, "%llu\n");
static int clk_frequency_get(void *blk_passed, u64 *val)
{
struct block *blk = (struct block *)blk_passed;
*val = blk->current_state->clk_frequency;
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(fops_clk_frequency,
clk_frequency_get, NULL, "%llu\n");
static int num_powered_cores_get(void *blk_passed, u64 *val)
{
struct block *blk = (struct block *)blk_passed;
*val = blk->current_state->num_powered_cores;
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(fops_num_powered_cores,
num_powered_cores_get, NULL, "%llu\n");
static int num_computing_cores_get(void *blk_passed, u64 *val)
{
struct block *blk = (struct block *)blk_passed;
*val = blk->current_state->num_computing_cores;
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(fops_num_computing_cores,
num_computing_cores_get, NULL, "%llu\n");
static int num_powered_tiles_get(void *blk_passed, u64 *val)
{
struct block *blk = (struct block *)blk_passed;
*val = blk->current_state->num_powered_tiles;
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(fops_num_powered_tiles,
num_powered_tiles_get, NULL, "%llu\n");
static int data_rate_get(void *blk_passed, u64 *val)
{
struct block *blk = (struct block *)blk_passed;
*val = blk->current_state->data_rate;
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(fops_data_rate, data_rate_get, NULL, "%llu\n");
static const struct file_operations chip_info_fops = {
.open = chip_info_open,
.read = seq_read,
};
static int ab_debugfs_ddr_ctrl(void *data, u64 val)
{
struct ab_state_context *sc = (struct ab_state_context *) data;
switch (val) {
case 0:
if (IS_HOST_DDR_INIT())
sc->dram_ops->init(sc->dram_ops->ctx);
break;
case 1:
sc->dram_ops->suspend(sc->dram_ops->ctx);
break;
case 2:
sc->dram_ops->resume(sc->dram_ops->ctx);
break;
case 3:
sc->dram_ops->sref_enter(sc->dram_ops->ctx);
break;
case 4:
sc->dram_ops->sref_exit(sc->dram_ops->ctx);
break;
case 5:
sc->dram_ops->setup(sc->dram_ops->ctx, sc);
break;
case 6:
sc->dram_ops->rw_test(sc->dram_ops->ctx,
DDR_TEST_PCIE_DMA_READ_WRITE(512));
break;
case 1866:
sc->dram_ops->set_freq(sc->dram_ops->ctx, DRAM_CLK_1866MHZ);
break;
case 1600:
sc->dram_ops->set_freq(sc->dram_ops->ctx, DRAM_CLK_1600MHZ);
break;
case 1200:
sc->dram_ops->set_freq(sc->dram_ops->ctx, DRAM_CLK_1200MHZ);
break;
case 933:
sc->dram_ops->set_freq(sc->dram_ops->ctx, DRAM_CLK_933MHZ);
break;
case 800:
sc->dram_ops->set_freq(sc->dram_ops->ctx, DRAM_CLK_800MHZ);
break;
default:
pr_err("ERROR!! Invalid DDR Control\n");
break;
}
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(ab_ddr_ctrl_fops, NULL, ab_debugfs_ddr_ctrl, "%lli\n");
static int ab_debugfs_ddr_test(void *data, u64 val)
{
struct ab_state_context *sc = (struct ab_state_context *)data;
return sc->dram_ops->rw_test(sc->dram_ops->ctx, (unsigned int)val);
}
DEFINE_SIMPLE_ATTRIBUTE(ab_ddr_test_fops, NULL, ab_debugfs_ddr_test, "%lli\n");
static int ab_debugfs_ddr_eye_margin(void *data, u64 val)
{
struct ab_state_context *sc = (struct ab_state_context *)data;
sc->dram_ops->eye_margin(sc->dram_ops->ctx, (unsigned int)val);
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(ab_ddr_eye_margin_fops, NULL,
ab_debugfs_ddr_eye_margin, "%lli\n");
static int ab_debugfs_ddr_eye_margin_plot(void *data, u64 val)
{
struct ab_state_context *sc = (struct ab_state_context *)data;
sc->dram_ops->eye_margin_plot(sc->dram_ops->ctx);
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(ab_ddr_eye_margin_plot_fops, NULL,
ab_debugfs_ddr_eye_margin_plot, "%lli\n");
static int ab_debugfs_clkout_sel(void *data, u64 val)
{
struct ab_state_context *sc = (struct ab_state_context *)data;
ab_clkout_sel(sc, val);
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(ab_clkout_sel_fops, NULL,
ab_debugfs_clkout_sel, "%lli\n");
static int ab_debugfs_clkout_blksel(void *data, u64 val)
{
struct ab_state_context *sc = (struct ab_state_context *)data;
ab_clkout_blksel(sc, val);
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(ab_clkout_blksel_fops, NULL,
ab_debugfs_clkout_blksel, "%lli\n");
static int ab_debugfs_clkout_clksel(void *data, u64 val)
{
struct ab_state_context *sc = (struct ab_state_context *)data;
ab_clkout_clksel(sc, val);
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(ab_clkout_clksel_fops, NULL,
ab_debugfs_clkout_clksel, "%lli\n");
static int ab_debugfs_clkout_enable(void *data, u64 val)
{
struct ab_state_context *sc = (struct ab_state_context *)data;
ab_clkout_enable(sc, val);
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(ab_clkout_enable_fops, NULL,
ab_debugfs_clkout_enable, "%lli\n");
static int ab_debugfs_clkout_freq(void *data, u64 *val)
{
struct ab_state_context *sc = (struct ab_state_context *)data;
ab_clkout_freq(sc, val);
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(ab_clkout_freq_fops, ab_debugfs_clkout_freq,
NULL, "%lli\n");
static ssize_t ab_debugfs_read_prop_table(struct file *file, char __user *ubuf,
size_t len, loff_t *ppos)
{
struct block *blk = file->private_data;
char *buf;
int pos = 0;
int i, ret;
buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
/* Keep legend in sync with struct block_property. */
pos += scnprintf(buf + pos, PAGE_SIZE - pos,
"{id, state_name, substate_name, pmu, rail_en, logic_voltage, clk_status, clk_frequency, num_powered_cores, num_computing_cores, num_powered_tiles, data_rate}\n");
for (i = 0; i < blk->nr_block_states; i++) {
pos += scnprintf(buf + pos, PAGE_SIZE - pos,
"{%d, %s, %s, %d, %d, %d, %d, %llu, %u, %u, %u, %u}\n",
blk->prop_table[i].id,
blk->prop_table[i].state_name,
blk->prop_table[i].substate_name,
blk->prop_table[i].pmu,
blk->prop_table[i].rail_en,
blk->prop_table[i].logic_voltage,
blk->prop_table[i].clk_status,
blk->prop_table[i].clk_frequency,
blk->prop_table[i].num_powered_cores,
blk->prop_table[i].num_computing_cores,
blk->prop_table[i].num_powered_tiles,
blk->prop_table[i].data_rate);
}
ret = simple_read_from_buffer(ubuf, len, ppos, buf, pos);
kfree(buf);
return ret;
}
static const struct file_operations fops_prop_table = {
.open = simple_open,
.read = ab_debugfs_read_prop_table,
};
static int ab_debugfs_ddr_ppc_event0(void *data, u64 val)
{
struct ab_state_context *sc = (struct ab_state_context *)data;
sc->dram_ops->ppc_set_event(sc->dram_ops->ctx, 0, val);
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(ab_ddr_ppc_event0_fops, NULL,
ab_debugfs_ddr_ppc_event0, "%lli\n");
static int ab_debugfs_ddr_ppc_event1(void *data, u64 val)
{
struct ab_state_context *sc = (struct ab_state_context *)data;
sc->dram_ops->ppc_set_event(sc->dram_ops->ctx, 1, val);
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(ab_ddr_ppc_event1_fops, NULL,
ab_debugfs_ddr_ppc_event1, "%lli\n");
static int ab_debugfs_ddr_ppc_event2(void *data, u64 val)
{
struct ab_state_context *sc = (struct ab_state_context *)data;
sc->dram_ops->ppc_set_event(sc->dram_ops->ctx, 2, val);
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(ab_ddr_ppc_event2_fops, NULL,
ab_debugfs_ddr_ppc_event2, "%lli\n");
static int ab_debugfs_ddr_ppc_event3(void *data, u64 val)
{
struct ab_state_context *sc = (struct ab_state_context *)data;
sc->dram_ops->ppc_set_event(sc->dram_ops->ctx, 3, val);
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(ab_ddr_ppc_event3_fops, NULL,
ab_debugfs_ddr_ppc_event3, "%lli\n");
static int ab_debugfs_ddr_ppc_ctrl(void *data, u64 val)
{
struct ab_state_context *sc = (struct ab_state_context *)data;
sc->dram_ops->ppc_ctrl(sc->dram_ops->ctx, val);
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(ab_ddr_ppc_ctrl_fops, NULL,
ab_debugfs_ddr_ppc_ctrl, "%lli\n");
void create_block_debugfs(struct dentry *parent_dir, struct block *blk)
{
debugfs_create_file("state_id", 0444, parent_dir,
blk, &fops_id);
debugfs_create_file("rail_en", 0444, parent_dir,
blk, &fops_vrail_status);
debugfs_create_file("logic_voltage", 0444, parent_dir,
blk, &fops_logic_voltage);
debugfs_create_file("clk_status", 0444, parent_dir,
blk, &fops_clk_status);
debugfs_create_file("clk_frequency", 0444, parent_dir,
blk, &fops_clk_frequency);
debugfs_create_file("num_powered_cores", 0444, parent_dir,
blk, &fops_num_powered_cores);
debugfs_create_file("num_computing_cores", 0444, parent_dir,
blk, &fops_num_computing_cores);
debugfs_create_file("num_powered_tiles", 0444, parent_dir,
blk, &fops_num_powered_tiles);
debugfs_create_file("data_rate", 0444, parent_dir,
blk, &fops_data_rate);
debugfs_create_file("prop_table", 0444, parent_dir,
blk, &fops_prop_table);
return;
}
void ab_sm_create_debugfs(struct ab_state_context *sc)
{
struct dentry *d_chip, *d_block;
sc->d_entry = debugfs_create_dir("airbrush", NULL);
debugfs_create_file("ab_ddr_ctrl", 0200, sc->d_entry, sc,
&ab_ddr_ctrl_fops);
debugfs_create_file("ab_ddr_test", 0200, sc->d_entry, sc,
&ab_ddr_test_fops);
debugfs_create_file("ddr_eye_margin", 0200, sc->d_entry, sc,
&ab_ddr_eye_margin_fops);
debugfs_create_file("ddr_eye_margin_plot", 0200, sc->d_entry, sc,
&ab_ddr_eye_margin_plot_fops);
debugfs_create_file("clkout_sel", 0200, sc->d_entry, sc,
&ab_clkout_sel_fops);
debugfs_create_file("clkout_blksel", 0200, sc->d_entry, sc,
&ab_clkout_blksel_fops);
debugfs_create_file("clkout_clksel", 0200, sc->d_entry, sc,
&ab_clkout_clksel_fops);
debugfs_create_file("clkout_enable", 0200, sc->d_entry, sc,
&ab_clkout_enable_fops);
debugfs_create_file("clkout_freq", 0400, sc->d_entry, sc,
&ab_clkout_freq_fops);
debugfs_create_file("ddr_ppc_event0", 0200, sc->d_entry, sc,
&ab_ddr_ppc_event0_fops);
debugfs_create_file("ddr_ppc_event1", 0200, sc->d_entry, sc,
&ab_ddr_ppc_event1_fops);
debugfs_create_file("ddr_ppc_event2", 0200, sc->d_entry, sc,
&ab_ddr_ppc_event2_fops);
debugfs_create_file("ddr_ppc_event3", 0200, sc->d_entry, sc,
&ab_ddr_ppc_event3_fops);
debugfs_create_file("ddr_ppc_ctrl", 0200, sc->d_entry, sc,
&ab_ddr_ppc_ctrl_fops);
debugfs_create_file("chip_info", 0444, sc->d_entry, NULL,
&chip_info_fops);
d_chip = debugfs_create_dir("airbrush_sm", sc->d_entry);
debugfs_create_file("mapped_chip_state", 0664, d_chip, sc,
&fops_mapped_chip_state);
debugfs_create_file("smps2_delay", 0664, d_chip, sc,
&fops_smps2_delay);
debugfs_create_file("ldo4_delay", 0664, d_chip, sc,
&fops_ldo4_delay);
debugfs_create_file("ldo5_delay", 0664, d_chip, sc,
&fops_ldo5_delay);
debugfs_create_file("s60_delay", 0664, d_chip, sc,
&fops_s60_delay);
#if IS_ENABLED(CONFIG_AIRBRUSH_SM_PROFILE)
debugfs_create_file("time_stamps", 0664, d_chip, sc,
&fops_time_stamps);
#endif
debugfs_create_file("asv_version", 0660, d_chip, sc,
&fops_asv_version_override);
debugfs_create_file("skip_pcie_link_init", 0660, d_chip, sc,
&ab_sm_skip_pcie_link_init_fops);
d_block = debugfs_create_dir("ipu", d_chip);
create_block_debugfs(d_block, &(sc->blocks[BLK_IPU]));
d_block = debugfs_create_dir("tpu", d_chip);
create_block_debugfs(d_block, &(sc->blocks[BLK_TPU]));
d_block = debugfs_create_dir("dram", d_chip);
create_block_debugfs(d_block, &(sc->blocks[DRAM]));
d_block = debugfs_create_dir("mif", d_chip);
create_block_debugfs(d_block, &(sc->blocks[BLK_MIF]));
d_block = debugfs_create_dir("fsys", d_chip);
create_block_debugfs(d_block, &(sc->blocks[BLK_FSYS]));
d_block = debugfs_create_dir("aon", d_chip);
create_block_debugfs(d_block, &(sc->blocks[BLK_AON]));
return;
}
void ab_sm_remove_debugfs(struct ab_state_context *sc)
{
debugfs_remove_recursive(sc->d_entry);
}