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