Merge tag '6.9-rc5-cifs-fixes-part2' of git://git.samba.org/sfrench/cifs-2.6
[sfrench/cifs-2.6.git] / drivers / memory / brcmstb_memc.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * DDR Self-Refresh Power Down (SRPD) support for Broadcom STB SoCs
4  *
5  */
6
7 #include <linux/init.h>
8 #include <linux/io.h>
9 #include <linux/kernel.h>
10 #include <linux/module.h>
11 #include <linux/of.h>
12 #include <linux/platform_device.h>
13 #include <linux/property.h>
14
15 #define REG_MEMC_CNTRLR_CONFIG          0x00
16 #define  CNTRLR_CONFIG_LPDDR4_SHIFT     5
17 #define  CNTRLR_CONFIG_MASK             0xf
18 #define REG_MEMC_SRPD_CFG_21            0x20
19 #define REG_MEMC_SRPD_CFG_20            0x34
20 #define REG_MEMC_SRPD_CFG_1x            0x3c
21 #define INACT_COUNT_SHIFT               0
22 #define INACT_COUNT_MASK                0xffff
23 #define SRPD_EN_SHIFT                   16
24
25 struct brcmstb_memc_data {
26         u32 srpd_offset;
27 };
28
29 struct brcmstb_memc {
30         struct device *dev;
31         void __iomem *ddr_ctrl;
32         unsigned int timeout_cycles;
33         u32 frequency;
34         u32 srpd_offset;
35 };
36
37 static int brcmstb_memc_uses_lpddr4(struct brcmstb_memc *memc)
38 {
39         void __iomem *config = memc->ddr_ctrl + REG_MEMC_CNTRLR_CONFIG;
40         u32 reg;
41
42         reg = readl_relaxed(config) & CNTRLR_CONFIG_MASK;
43
44         return reg == CNTRLR_CONFIG_LPDDR4_SHIFT;
45 }
46
47 static int brcmstb_memc_srpd_config(struct brcmstb_memc *memc,
48                                     unsigned int cycles)
49 {
50         void __iomem *cfg = memc->ddr_ctrl + memc->srpd_offset;
51         u32 val;
52
53         /* Max timeout supported in HW */
54         if (cycles > INACT_COUNT_MASK)
55                 return -EINVAL;
56
57         memc->timeout_cycles = cycles;
58
59         val = (cycles << INACT_COUNT_SHIFT) & INACT_COUNT_MASK;
60         if (cycles)
61                 val |= BIT(SRPD_EN_SHIFT);
62
63         writel_relaxed(val, cfg);
64         /* Ensure the write is committed to the controller */
65         (void)readl_relaxed(cfg);
66
67         return 0;
68 }
69
70 static ssize_t frequency_show(struct device *dev,
71                               struct device_attribute *attr, char *buf)
72 {
73         struct brcmstb_memc *memc = dev_get_drvdata(dev);
74
75         return sprintf(buf, "%d\n", memc->frequency);
76 }
77
78 static ssize_t srpd_show(struct device *dev,
79                          struct device_attribute *attr, char *buf)
80 {
81         struct brcmstb_memc *memc = dev_get_drvdata(dev);
82
83         return sprintf(buf, "%d\n", memc->timeout_cycles);
84 }
85
86 static ssize_t srpd_store(struct device *dev, struct device_attribute *attr,
87                           const char *buf, size_t count)
88 {
89         struct brcmstb_memc *memc = dev_get_drvdata(dev);
90         unsigned int val;
91         int ret;
92
93         /*
94          * Cannot change the inactivity timeout on LPDDR4 chips because the
95          * dynamic tuning process will also get affected by the inactivity
96          * timeout, thus making it non functional.
97          */
98         if (brcmstb_memc_uses_lpddr4(memc))
99                 return -EOPNOTSUPP;
100
101         ret = kstrtouint(buf, 10, &val);
102         if (ret < 0)
103                 return ret;
104
105         ret = brcmstb_memc_srpd_config(memc, val);
106         if (ret)
107                 return ret;
108
109         return count;
110 }
111
112 static DEVICE_ATTR_RO(frequency);
113 static DEVICE_ATTR_RW(srpd);
114
115 static struct attribute *dev_attrs[] = {
116         &dev_attr_frequency.attr,
117         &dev_attr_srpd.attr,
118         NULL,
119 };
120
121 static struct attribute_group dev_attr_group = {
122         .attrs = dev_attrs,
123 };
124
125 static int brcmstb_memc_probe(struct platform_device *pdev)
126 {
127         const struct brcmstb_memc_data *memc_data;
128         struct device *dev = &pdev->dev;
129         struct brcmstb_memc *memc;
130         int ret;
131
132         memc = devm_kzalloc(dev, sizeof(*memc), GFP_KERNEL);
133         if (!memc)
134                 return -ENOMEM;
135
136         dev_set_drvdata(dev, memc);
137
138         memc_data = device_get_match_data(dev);
139         memc->srpd_offset = memc_data->srpd_offset;
140
141         memc->ddr_ctrl = devm_platform_ioremap_resource(pdev, 0);
142         if (IS_ERR(memc->ddr_ctrl))
143                 return PTR_ERR(memc->ddr_ctrl);
144
145         of_property_read_u32(pdev->dev.of_node, "clock-frequency",
146                              &memc->frequency);
147
148         ret = sysfs_create_group(&dev->kobj, &dev_attr_group);
149         if (ret)
150                 return ret;
151
152         return 0;
153 }
154
155 static void brcmstb_memc_remove(struct platform_device *pdev)
156 {
157         struct device *dev = &pdev->dev;
158
159         sysfs_remove_group(&dev->kobj, &dev_attr_group);
160 }
161
162 enum brcmstb_memc_hwtype {
163         BRCMSTB_MEMC_V21,
164         BRCMSTB_MEMC_V20,
165         BRCMSTB_MEMC_V1X,
166 };
167
168 static const struct brcmstb_memc_data brcmstb_memc_versions[] = {
169         { .srpd_offset = REG_MEMC_SRPD_CFG_21 },
170         { .srpd_offset = REG_MEMC_SRPD_CFG_20 },
171         { .srpd_offset = REG_MEMC_SRPD_CFG_1x },
172 };
173
174 static const struct of_device_id brcmstb_memc_of_match[] = {
175         {
176                 .compatible = "brcm,brcmstb-memc-ddr-rev-b.1.x",
177                 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V1X]
178         },
179         {
180                 .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.0",
181                 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V20]
182         },
183         {
184                 .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.1",
185                 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21]
186         },
187         {
188                 .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.2",
189                 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21]
190         },
191         {
192                 .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.3",
193                 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21]
194         },
195         {
196                 .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.5",
197                 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21]
198         },
199         {
200                 .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.6",
201                 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21]
202         },
203         {
204                 .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.7",
205                 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21]
206         },
207         {
208                 .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.8",
209                 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21]
210         },
211         {
212                 .compatible = "brcm,brcmstb-memc-ddr-rev-b.3.0",
213                 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21]
214         },
215         {
216                 .compatible = "brcm,brcmstb-memc-ddr-rev-b.3.1",
217                 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21]
218         },
219         {
220                 .compatible = "brcm,brcmstb-memc-ddr-rev-c.1.0",
221                 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21]
222         },
223         {
224                 .compatible = "brcm,brcmstb-memc-ddr-rev-c.1.1",
225                 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21]
226         },
227         {
228                 .compatible = "brcm,brcmstb-memc-ddr-rev-c.1.2",
229                 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21]
230         },
231         {
232                 .compatible = "brcm,brcmstb-memc-ddr-rev-c.1.3",
233                 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21]
234         },
235         {
236                 .compatible = "brcm,brcmstb-memc-ddr-rev-c.1.4",
237                 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21]
238         },
239         /* default to the original offset */
240         {
241                 .compatible = "brcm,brcmstb-memc-ddr",
242                 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V1X]
243         },
244         {}
245 };
246
247 static int brcmstb_memc_suspend(struct device *dev)
248 {
249         struct brcmstb_memc *memc = dev_get_drvdata(dev);
250         void __iomem *cfg = memc->ddr_ctrl + memc->srpd_offset;
251         u32 val;
252
253         if (memc->timeout_cycles == 0)
254                 return 0;
255
256         /*
257          * Disable SRPD prior to suspending the system since that can
258          * cause issues with other memory clients managed by the ARM
259          * trusted firmware to access memory.
260          */
261         val = readl_relaxed(cfg);
262         val &= ~BIT(SRPD_EN_SHIFT);
263         writel_relaxed(val, cfg);
264         /* Ensure the write is committed to the controller */
265         (void)readl_relaxed(cfg);
266
267         return 0;
268 }
269
270 static int brcmstb_memc_resume(struct device *dev)
271 {
272         struct brcmstb_memc *memc = dev_get_drvdata(dev);
273
274         if (memc->timeout_cycles == 0)
275                 return 0;
276
277         return brcmstb_memc_srpd_config(memc, memc->timeout_cycles);
278 }
279
280 static DEFINE_SIMPLE_DEV_PM_OPS(brcmstb_memc_pm_ops, brcmstb_memc_suspend,
281                                 brcmstb_memc_resume);
282
283 static struct platform_driver brcmstb_memc_driver = {
284         .probe = brcmstb_memc_probe,
285         .remove_new = brcmstb_memc_remove,
286         .driver = {
287                 .name           = "brcmstb_memc",
288                 .of_match_table = brcmstb_memc_of_match,
289                 .pm             = pm_ptr(&brcmstb_memc_pm_ops),
290         },
291 };
292 module_platform_driver(brcmstb_memc_driver);
293
294 MODULE_LICENSE("GPL");
295 MODULE_AUTHOR("Broadcom");
296 MODULE_DESCRIPTION("DDR SRPD driver for Broadcom STB chips");