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