Merge tag 'for-6.2-rc2-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave...
[platform/kernel/linux-starfive.git] / drivers / misc / smpro-misc.c
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, &reg);
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, &reg_lo);
53         if (!ret)
54                 ret = regmap_read(misc->regmap, BOOTSTAGE_HI, &reg);
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");