146 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Ampere Computing SoC's SMpro Misc Driver4 *5 * Copyright (c) 2022, Ampere Computing LLC6 */7#include <linux/mod_devicetable.h>8#include <linux/module.h>9#include <linux/platform_device.h>10#include <linux/regmap.h>11 12/* Boot Stage/Progress Registers */13#define BOOTSTAGE 0xB014#define BOOTSTAGE_LO 0xB115#define CUR_BOOTSTAGE 0xB216#define BOOTSTAGE_HI 0xB317 18/* SOC State Registers */19#define SOC_POWER_LIMIT 0xE520 21struct smpro_misc {22 struct regmap *regmap;23};24 25static ssize_t boot_progress_show(struct device *dev, struct device_attribute *da, char *buf)26{27 struct smpro_misc *misc = dev_get_drvdata(dev);28 u16 boot_progress[3] = { 0 };29 u32 bootstage;30 u8 boot_stage;31 u8 cur_stage;32 u32 reg_lo;33 u32 reg;34 int ret;35 36 /* Read current boot stage */37 ret = regmap_read(misc->regmap, CUR_BOOTSTAGE, ®);38 if (ret)39 return ret;40 41 cur_stage = reg & 0xff;42 43 ret = regmap_read(misc->regmap, BOOTSTAGE, &bootstage);44 if (ret)45 return ret;46 47 boot_stage = (bootstage >> 8) & 0xff;48 49 if (boot_stage > cur_stage)50 return -EINVAL;51 52 ret = regmap_read(misc->regmap, BOOTSTAGE_LO, ®_lo);53 if (!ret)54 ret = regmap_read(misc->regmap, BOOTSTAGE_HI, ®);55 if (ret)56 return ret;57 58 /* Firmware to report new boot stage next time */59 if (boot_stage < cur_stage) {60 ret = regmap_write(misc->regmap, BOOTSTAGE, ((bootstage & 0xff00) | 0x1));61 if (ret)62 return ret;63 }64 65 boot_progress[0] = bootstage;66 boot_progress[1] = swab16(reg);67 boot_progress[2] = swab16(reg_lo);68 69 return sysfs_emit(buf, "%*phN\n", (int)sizeof(boot_progress), boot_progress);70}71 72static DEVICE_ATTR_RO(boot_progress);73 74static ssize_t soc_power_limit_show(struct device *dev, struct device_attribute *da, char *buf)75{76 struct smpro_misc *misc = dev_get_drvdata(dev);77 unsigned int value;78 int ret;79 80 ret = regmap_read(misc->regmap, SOC_POWER_LIMIT, &value);81 if (ret)82 return ret;83 84 return sysfs_emit(buf, "%d\n", value);85}86 87static ssize_t soc_power_limit_store(struct device *dev, struct device_attribute *da,88 const char *buf, size_t count)89{90 struct smpro_misc *misc = dev_get_drvdata(dev);91 unsigned long val;92 s32 ret;93 94 ret = kstrtoul(buf, 0, &val);95 if (ret)96 return ret;97 98 ret = regmap_write(misc->regmap, SOC_POWER_LIMIT, (unsigned int)val);99 if (ret)100 return -EPROTO;101 102 return count;103}104 105static DEVICE_ATTR_RW(soc_power_limit);106 107static struct attribute *smpro_misc_attrs[] = {108 &dev_attr_boot_progress.attr,109 &dev_attr_soc_power_limit.attr,110 NULL111};112 113ATTRIBUTE_GROUPS(smpro_misc);114 115static int smpro_misc_probe(struct platform_device *pdev)116{117 struct smpro_misc *misc;118 119 misc = devm_kzalloc(&pdev->dev, sizeof(struct smpro_misc), GFP_KERNEL);120 if (!misc)121 return -ENOMEM;122 123 platform_set_drvdata(pdev, misc);124 125 misc->regmap = dev_get_regmap(pdev->dev.parent, NULL);126 if (!misc->regmap)127 return -ENODEV;128 129 return 0;130}131 132static struct platform_driver smpro_misc_driver = {133 .probe = smpro_misc_probe,134 .driver = {135 .name = "smpro-misc",136 .dev_groups = smpro_misc_groups,137 },138};139 140module_platform_driver(smpro_misc_driver);141 142MODULE_AUTHOR("Tung Nguyen <tungnguyen@os.amperecomputing.com>");143MODULE_AUTHOR("Quan Nguyen <quan@os.amperecomputing.com>");144MODULE_DESCRIPTION("Ampere Altra SMpro Misc driver");145MODULE_LICENSE("GPL");146