1 // SPDX-License-Identifier: GPL-2.0 2 // 3 // Copyright (c) 2015 Samsung Electronics Co., Ltd. 4 // http://www.samsung.com/ 5 // 6 // Exynos - SROM Controller support 7 // Author: Pankaj Dubey <pankaj.dubey@samsung.com> 8 9 #include <linux/io.h> 10 #include <linux/init.h> 11 #include <linux/of.h> 12 #include <linux/of_address.h> 13 #include <linux/of_platform.h> 14 #include <linux/platform_device.h> 15 #include <linux/slab.h> 16 17 #include "exynos-srom.h" 18 19 static const unsigned long exynos_srom_offsets[] = { 20 /* SROM side */ 21 EXYNOS_SROM_BW, 22 EXYNOS_SROM_BC0, 23 EXYNOS_SROM_BC1, 24 EXYNOS_SROM_BC2, 25 EXYNOS_SROM_BC3, 26 }; 27 28 /** 29 * struct exynos_srom_reg_dump: register dump of SROM Controller registers. 30 * @offset: srom register offset from the controller base address. 31 * @value: the value of register under the offset. 32 */ 33 struct exynos_srom_reg_dump { 34 u32 offset; 35 u32 value; 36 }; 37 38 /** 39 * struct exynos_srom: platform data for exynos srom controller driver. 40 * @dev: platform device pointer 41 * @reg_base: srom base address 42 * @reg_offset: exynos_srom_reg_dump pointer to hold offset and its value. 43 */ 44 struct exynos_srom { 45 struct device *dev; 46 void __iomem *reg_base; 47 struct exynos_srom_reg_dump *reg_offset; 48 }; 49 50 static struct exynos_srom_reg_dump * 51 exynos_srom_alloc_reg_dump(const unsigned long *rdump, 52 unsigned long nr_rdump) 53 { 54 struct exynos_srom_reg_dump *rd; 55 unsigned int i; 56 57 rd = kcalloc(nr_rdump, sizeof(*rd), GFP_KERNEL); 58 if (!rd) 59 return NULL; 60 61 for (i = 0; i < nr_rdump; ++i) 62 rd[i].offset = rdump[i]; 63 64 return rd; 65 } 66 67 static int exynos_srom_configure_bank(struct exynos_srom *srom, 68 struct device_node *np) 69 { 70 u32 bank, width, pmc = 0; 71 u32 timing[6]; 72 u32 cs, bw; 73 74 if (of_property_read_u32(np, "reg", &bank)) 75 return -EINVAL; 76 if (of_property_read_u32(np, "reg-io-width", &width)) 77 width = 1; 78 if (of_property_read_bool(np, "samsung,srom-page-mode")) 79 pmc = 1 << EXYNOS_SROM_BCX__PMC__SHIFT; 80 if (of_property_read_u32_array(np, "samsung,srom-timing", timing, 81 ARRAY_SIZE(timing))) 82 return -EINVAL; 83 84 bank *= 4; /* Convert bank into shift/offset */ 85 86 cs = 1 << EXYNOS_SROM_BW__BYTEENABLE__SHIFT; 87 if (width == 2) 88 cs |= 1 << EXYNOS_SROM_BW__DATAWIDTH__SHIFT; 89 90 bw = readl_relaxed(srom->reg_base + EXYNOS_SROM_BW); 91 bw = (bw & ~(EXYNOS_SROM_BW__CS_MASK << bank)) | (cs << bank); 92 writel_relaxed(bw, srom->reg_base + EXYNOS_SROM_BW); 93 94 writel_relaxed(pmc | (timing[0] << EXYNOS_SROM_BCX__TACP__SHIFT) | 95 (timing[1] << EXYNOS_SROM_BCX__TCAH__SHIFT) | 96 (timing[2] << EXYNOS_SROM_BCX__TCOH__SHIFT) | 97 (timing[3] << EXYNOS_SROM_BCX__TACC__SHIFT) | 98 (timing[4] << EXYNOS_SROM_BCX__TCOS__SHIFT) | 99 (timing[5] << EXYNOS_SROM_BCX__TACS__SHIFT), 100 srom->reg_base + EXYNOS_SROM_BC0 + bank); 101 102 return 0; 103 } 104 105 static int exynos_srom_probe(struct platform_device *pdev) 106 { 107 struct device_node *np, *child; 108 struct exynos_srom *srom; 109 struct device *dev = &pdev->dev; 110 bool bad_bank_config = false; 111 112 np = dev->of_node; 113 if (!np) { 114 dev_err(&pdev->dev, "could not find device info\n"); 115 return -EINVAL; 116 } 117 118 srom = devm_kzalloc(&pdev->dev, 119 sizeof(struct exynos_srom), GFP_KERNEL); 120 if (!srom) 121 return -ENOMEM; 122 123 srom->dev = dev; 124 srom->reg_base = devm_platform_ioremap_resource(pdev, 0); 125 if (IS_ERR(srom->reg_base)) { 126 dev_err(&pdev->dev, "iomap of exynos srom controller failed\n"); 127 return PTR_ERR(srom->reg_base); 128 } 129 130 platform_set_drvdata(pdev, srom); 131 132 srom->reg_offset = exynos_srom_alloc_reg_dump(exynos_srom_offsets, 133 ARRAY_SIZE(exynos_srom_offsets)); 134 if (!srom->reg_offset) 135 return -ENOMEM; 136 137 for_each_child_of_node(np, child) { 138 if (exynos_srom_configure_bank(srom, child)) { 139 dev_err(dev, 140 "Could not decode bank configuration for %pOFn\n", 141 child); 142 bad_bank_config = true; 143 } 144 } 145 146 /* 147 * If any bank failed to configure, we still provide suspend/resume, 148 * but do not probe child devices 149 */ 150 if (bad_bank_config) 151 return 0; 152 153 return of_platform_populate(np, NULL, NULL, dev); 154 } 155 156 #ifdef CONFIG_PM_SLEEP 157 static void exynos_srom_save(void __iomem *base, 158 struct exynos_srom_reg_dump *rd, 159 unsigned int num_regs) 160 { 161 for (; num_regs > 0; --num_regs, ++rd) 162 rd->value = readl(base + rd->offset); 163 } 164 165 static void exynos_srom_restore(void __iomem *base, 166 const struct exynos_srom_reg_dump *rd, 167 unsigned int num_regs) 168 { 169 for (; num_regs > 0; --num_regs, ++rd) 170 writel(rd->value, base + rd->offset); 171 } 172 173 static int exynos_srom_suspend(struct device *dev) 174 { 175 struct exynos_srom *srom = dev_get_drvdata(dev); 176 177 exynos_srom_save(srom->reg_base, srom->reg_offset, 178 ARRAY_SIZE(exynos_srom_offsets)); 179 return 0; 180 } 181 182 static int exynos_srom_resume(struct device *dev) 183 { 184 struct exynos_srom *srom = dev_get_drvdata(dev); 185 186 exynos_srom_restore(srom->reg_base, srom->reg_offset, 187 ARRAY_SIZE(exynos_srom_offsets)); 188 return 0; 189 } 190 #endif 191 192 static const struct of_device_id of_exynos_srom_ids[] = { 193 { 194 .compatible = "samsung,exynos4210-srom", 195 }, 196 {}, 197 }; 198 199 static SIMPLE_DEV_PM_OPS(exynos_srom_pm_ops, exynos_srom_suspend, exynos_srom_resume); 200 201 static struct platform_driver exynos_srom_driver = { 202 .probe = exynos_srom_probe, 203 .driver = { 204 .name = "exynos-srom", 205 .of_match_table = of_exynos_srom_ids, 206 .pm = &exynos_srom_pm_ops, 207 .suppress_bind_attrs = true, 208 }, 209 }; 210 builtin_platform_driver(exynos_srom_driver); 211