// SPDX-License-Identifier: GPL-2.0-only/* * Ampere Computing SoC's SMpro Misc Driver * * Copyright (c) 2022, Ampere Computing LLC */#include <linux/module.h>#include <linux/platform_device.h>#include <linux/regmap.h> /* Boot Stage/Progress Registers */#define BOOTSTAGE 0xB0#define BOOTSTAGE_LO 0xB1#define CUR_BOOTSTAGE 0xB2#define BOOTSTAGE_HI 0xB3 /* SOC State Registers */#define SOC_POWER_LIMIT 0xE5 struct smpro_misc { struct regmap *regmap;}; static ssize_t boot_progress_show(struct device *dev, struct device_attribute *da, char *buf){ struct smpro_misc *misc = dev_get_drvdata(dev); u16 boot_progress[3] = { 0 }; u32 bootstage; u8 boot_stage; u8 cur_stage; u32 reg_lo; u32 reg; int ret; /* Read current boot stage */ ret = regmap_read(misc->regmap, CUR_BOOTSTAGE, ®); if (ret) return ret; cur_stage = reg & 0xff; ret = regmap_read(misc->regmap, BOOTSTAGE, &bootstage); if (ret) return ret; boot_stage = (bootstage >> 8) & 0xff; if (boot_stage > cur_stage) return -EINVAL; ret = regmap_read(misc->regmap, BOOTSTAGE_LO, ®_lo); if (!ret) ret = regmap_read(misc->regmap, BOOTSTAGE_HI, ®); if (ret) return ret; /* Firmware to report new boot stage next time */ if (boot_stage < cur_stage) { ret = regmap_write(misc->regmap, BOOTSTAGE, ((bootstage & 0xff00) | 0x1)); if (ret) return ret; } boot_progress[0] = bootstage; boot_progress[1] = swab16(reg); boot_progress[2] = swab16(reg_lo); return sysfs_emit(buf, "%*phN\n", (int)sizeof(boot_progress), boot_progress);} static DEVICE_ATTR_RO(boot_progress); static ssize_t soc_power_limit_show(struct device *dev, struct device_attribute *da, char *buf){ struct smpro_misc *misc = dev_get_drvdata(dev); unsigned int value; int ret; ret = regmap_read(misc->regmap, SOC_POWER_LIMIT, &value); if (ret) return ret; return sysfs_emit(buf, "%d\n", value);} static ssize_t soc_power_limit_store(struct device *dev, struct device_attribute *da, const char *buf, size_t count){ struct smpro_misc *misc = dev_get_drvdata(dev); unsigned long val; s32 ret; ret = kstrtoul(buf, 0, &val); if (ret) return ret; ret = regmap_write(misc->regmap, SOC_POWER_LIMIT, (unsigned int)val); if (ret) return -EPROTO; return count;} static DEVICE_ATTR_RW(soc_power_limit); static struct attribute *smpro_misc_attrs[] = { &dev_attr_boot_progress.attr, &dev_attr_soc_power_limit.attr, NULL}; ATTRIBUTE_GROUPS(smpro_misc); static int smpro_misc_probe(struct platform_device *pdev){ struct smpro_misc *misc; misc = devm_kzalloc(&pdev->dev, sizeof(struct smpro_misc), GFP_KERNEL); if (!misc) return -ENOMEM; platform_set_drvdata(pdev, misc); misc->regmap = dev_get_regmap(pdev->dev.parent, NULL); if (!misc->regmap) return -ENODEV; return 0;} static struct platform_driver smpro_misc_driver = { .probe = smpro_misc_probe, .driver = { .name = "smpro-misc", .dev_groups = smpro_misc_groups, },}; module_platform_driver(smpro_misc_driver); MODULE_AUTHOR("Tung Nguyen <tungnguyen@os.amperecomputing.com>");MODULE_AUTHOR("Quan Nguyen <quan@os.amperecomputing.com>");MODULE_DESCRIPTION("Ampere Altra SMpro Misc driver");MODULE_LICENSE("GPL");
// SPDX-License-Identifier: GPL-2.0-only
/*
* Ampere Computing SoC's SMpro Misc Driver
*
* Copyright (c) 2022, Ampere Computing LLC
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
/* Boot Stage/Progress Registers */
#define BOOTSTAGE 0xB0
#define BOOTSTAGE_LO 0xB1
#define CUR_BOOTSTAGE 0xB2
#define BOOTSTAGE_HI 0xB3
/* SOC State Registers */
#define SOC_POWER_LIMIT 0xE5
struct smpro_misc {
struct regmap *regmap;
};
static ssize_t boot_progress_show(struct device *dev, struct device_attribute *da, char *buf)
{
struct smpro_misc *misc = dev_get_drvdata(dev);
u16 boot_progress[3] = { 0 };
u32 bootstage;
u8 boot_stage;
u8 cur_stage;
u32 reg_lo;
u32 reg;
int ret;
/* Read current boot stage */
ret = regmap_read(misc->regmap, CUR_BOOTSTAGE, ®);
if (ret)
return ret;
cur_stage = reg & 0xff;
ret = regmap_read(misc->regmap, BOOTSTAGE, &bootstage);
if (ret)
return ret;
boot_stage = (bootstage >> 8) & 0xff;
if (boot_stage > cur_stage)
return -EINVAL;
ret = regmap_read(misc->regmap, BOOTSTAGE_LO, ®_lo);
if (!ret)
ret = regmap_read(misc->regmap, BOOTSTAGE_HI, ®);
if (ret)
return ret;
/* Firmware to report new boot stage next time */
if (boot_stage < cur_stage) {
ret = regmap_write(misc->regmap, BOOTSTAGE, ((bootstage & 0xff00) | 0x1));
if (ret)
return ret;
}
boot_progress[0] = bootstage;
boot_progress[1] = swab16(reg);
boot_progress[2] = swab16(reg_lo);
return sysfs_emit(buf, "%*phN\n", (int)sizeof(boot_progress), boot_progress);
}
static DEVICE_ATTR_RO(boot_progress);
static ssize_t soc_power_limit_show(struct device *dev, struct device_attribute *da, char *buf)
{
struct smpro_misc *misc = dev_get_drvdata(dev);
unsigned int value;
int ret;
ret = regmap_read(misc->regmap, SOC_POWER_LIMIT, &value);
if (ret)
return ret;
return sysfs_emit(buf, "%d\n", value);
}
static ssize_t soc_power_limit_store(struct device *dev, struct device_attribute *da,
const char *buf, size_t count)
{
struct smpro_misc *misc = dev_get_drvdata(dev);
unsigned long val;
s32 ret;
ret = kstrtoul(buf, 0, &val);
if (ret)
return ret;
ret = regmap_write(misc->regmap, SOC_POWER_LIMIT, (unsigned int)val);
if (ret)
return -EPROTO;
return count;
}
static DEVICE_ATTR_RW(soc_power_limit);
static struct attribute *smpro_misc_attrs[] = {
&dev_attr_boot_progress.attr,
&dev_attr_soc_power_limit.attr,
NULL
};
ATTRIBUTE_GROUPS(smpro_misc);
static int smpro_misc_probe(struct platform_device *pdev)
{
struct smpro_misc *misc;
misc = devm_kzalloc(&pdev->dev, sizeof(struct smpro_misc), GFP_KERNEL);
if (!misc)
return -ENOMEM;
platform_set_drvdata(pdev, misc);
misc->regmap = dev_get_regmap(pdev->dev.parent, NULL);
if (!misc->regmap)
return -ENODEV;
return 0;
}
static struct platform_driver smpro_misc_driver = {
.probe = smpro_misc_probe,
.driver = {
.name = "smpro-misc",
.dev_groups = smpro_misc_groups,
},
};
module_platform_driver(smpro_misc_driver);
MODULE_AUTHOR("Tung Nguyen <tungnguyen@os.amperecomputing.com>");
MODULE_AUTHOR("Quan Nguyen <quan@os.amperecomputing.com>");
MODULE_DESCRIPTION("Ampere Altra SMpro Misc driver");
MODULE_LICENSE("GPL");