1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Ampere Computing SoC's SMpro Misc Driver 4 * 5 * Copyright (c) 2022, Ampere Computing LLC 6 */ 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 0xB0 14#define BOOTSTAGE_LO 0xB1 15#define CUR_BOOTSTAGE 0xB2 16#define BOOTSTAGE_HI 0xB3 17 18/* SOC State Registers */ 19#define SOC_POWER_LIMIT 0xE5 20 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 NULL 111}; 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