Merge tag 'ti-driver-soc-for-v6.7' of https://git.kernel.org/pub/scm/linux/kernel...
[linux-2.6-microblaze.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 int 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         return 0;
162 }
163
164 enum brcmstb_memc_hwtype {
165         BRCMSTB_MEMC_V21,
166         BRCMSTB_MEMC_V20,
167         BRCMSTB_MEMC_V1X,
168 };
169
170 static const struct brcmstb_memc_data brcmstb_memc_versions[] = {
171         { .srpd_offset = REG_MEMC_SRPD_CFG_21 },
172         { .srpd_offset = REG_MEMC_SRPD_CFG_20 },
173         { .srpd_offset = REG_MEMC_SRPD_CFG_1x },
174 };
175
176 static const struct of_device_id brcmstb_memc_of_match[] = {
177         {
178                 .compatible = "brcm,brcmstb-memc-ddr-rev-b.1.x",
179                 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V1X]
180         },
181         {
182                 .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.0",
183                 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V20]
184         },
185         {
186                 .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.1",
187                 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21]
188         },
189         {
190                 .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.2",
191                 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21]
192         },
193         {
194                 .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.3",
195                 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21]
196         },
197         {
198                 .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.5",
199                 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21]
200         },
201         {
202                 .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.6",
203                 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21]
204         },
205         {
206                 .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.7",
207                 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21]
208         },
209         {
210                 .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.8",
211                 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21]
212         },
213         {
214                 .compatible = "brcm,brcmstb-memc-ddr-rev-b.3.0",
215                 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21]
216         },
217         {
218                 .compatible = "brcm,brcmstb-memc-ddr-rev-b.3.1",
219                 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21]
220         },
221         {
222                 .compatible = "brcm,brcmstb-memc-ddr-rev-c.1.0",
223                 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21]
224         },
225         {
226                 .compatible = "brcm,brcmstb-memc-ddr-rev-c.1.1",
227                 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21]
228         },
229         {
230                 .compatible = "brcm,brcmstb-memc-ddr-rev-c.1.2",
231                 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21]
232         },
233         {
234                 .compatible = "brcm,brcmstb-memc-ddr-rev-c.1.3",
235                 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21]
236         },
237         {
238                 .compatible = "brcm,brcmstb-memc-ddr-rev-c.1.4",
239                 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21]
240         },
241         /* default to the original offset */
242         {
243                 .compatible = "brcm,brcmstb-memc-ddr",
244                 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V1X]
245         },
246         {}
247 };
248
249 static int brcmstb_memc_suspend(struct device *dev)
250 {
251         struct brcmstb_memc *memc = dev_get_drvdata(dev);
252         void __iomem *cfg = memc->ddr_ctrl + memc->srpd_offset;
253         u32 val;
254
255         if (memc->timeout_cycles == 0)
256                 return 0;
257
258         /*
259          * Disable SRPD prior to suspending the system since that can
260          * cause issues with other memory clients managed by the ARM
261          * trusted firmware to access memory.
262          */
263         val = readl_relaxed(cfg);
264         val &= ~BIT(SRPD_EN_SHIFT);
265         writel_relaxed(val, cfg);
266         /* Ensure the write is committed to the controller */
267         (void)readl_relaxed(cfg);
268
269         return 0;
270 }
271
272 static int brcmstb_memc_resume(struct device *dev)
273 {
274         struct brcmstb_memc *memc = dev_get_drvdata(dev);
275
276         if (memc->timeout_cycles == 0)
277                 return 0;
278
279         return brcmstb_memc_srpd_config(memc, memc->timeout_cycles);
280 }
281
282 static DEFINE_SIMPLE_DEV_PM_OPS(brcmstb_memc_pm_ops, brcmstb_memc_suspend,
283                                 brcmstb_memc_resume);
284
285 static struct platform_driver brcmstb_memc_driver = {
286         .probe = brcmstb_memc_probe,
287         .remove = brcmstb_memc_remove,
288         .driver = {
289                 .name           = "brcmstb_memc",
290                 .of_match_table = brcmstb_memc_of_match,
291                 .pm             = pm_ptr(&brcmstb_memc_pm_ops),
292         },
293 };
294 module_platform_driver(brcmstb_memc_driver);
295
296 MODULE_LICENSE("GPL");
297 MODULE_AUTHOR("Broadcom");
298 MODULE_DESCRIPTION("DDR SRPD driver for Broadcom STB chips");