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(map: misc->regmap, CUR_BOOTSTAGE, val: ®); |
38 | if (ret) |
39 | return ret; |
40 | |
41 | cur_stage = reg & 0xff; |
42 | |
43 | ret = regmap_read(map: misc->regmap, BOOTSTAGE, val: &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(map: misc->regmap, BOOTSTAGE_LO, val: ®_lo); |
53 | if (!ret) |
54 | ret = regmap_read(map: misc->regmap, BOOTSTAGE_HI, val: ®); |
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(map: misc->regmap, BOOTSTAGE, val: ((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, fmt: "%*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(map: misc->regmap, SOC_POWER_LIMIT, val: &value); |
81 | if (ret) |
82 | return ret; |
83 | |
84 | return sysfs_emit(buf, fmt: "%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(s: buf, base: 0, res: &val); |
95 | if (ret) |
96 | return ret; |
97 | |
98 | ret = regmap_write(map: misc->regmap, SOC_POWER_LIMIT, val: (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(dev: &pdev->dev, size: sizeof(struct smpro_misc), GFP_KERNEL); |
120 | if (!misc) |
121 | return -ENOMEM; |
122 | |
123 | platform_set_drvdata(pdev, data: misc); |
124 | |
125 | misc->regmap = dev_get_regmap(dev: 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 | |