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 21 struct smpro_misc { 22 struct regmap *regmap; 23 }; 24 25 static 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 72 static DEVICE_ATTR_RO(boot_progress); 73 74 static 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 87 static 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 105 static DEVICE_ATTR_RW(soc_power_limit); 106 107 static struct attribute *smpro_misc_attrs[] = { 108 &dev_attr_boot_progress.attr, 109 &dev_attr_soc_power_limit.attr, 110 NULL 111 }; 112 113 ATTRIBUTE_GROUPS(smpro_misc); 114 115 static 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 132 static 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 140 module_platform_driver(smpro_misc_driver); 141 142 MODULE_AUTHOR("Tung Nguyen <tungnguyen@os.amperecomputing.com>"); 143 MODULE_AUTHOR("Quan Nguyen <quan@os.amperecomputing.com>"); 144 MODULE_DESCRIPTION("Ampere Altra SMpro Misc driver"); 145 MODULE_LICENSE("GPL"); 146