xref: /linux/drivers/misc/smpro-misc.c (revision 995832b2cebe6969d1b42635db698803ee31294d)
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, &reg);
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, &reg_lo);
52 	if (!ret)
53 		ret = regmap_read(misc->regmap, BOOTSTAGE_HI, &reg);
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