Commit | Line | Data |
---|---|---|
763dc90e QN |
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"); |