exynos-srom.c (5317B)
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 19static 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 */ 33struct 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 */ 44struct exynos_srom { 45 struct device *dev; 46 void __iomem *reg_base; 47 struct exynos_srom_reg_dump *reg_offset; 48}; 49 50static struct exynos_srom_reg_dump * 51exynos_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 67static 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 105static 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 = of_iomap(np, 0); 125 if (!srom->reg_base) { 126 dev_err(&pdev->dev, "iomap of exynos srom controller failed\n"); 127 return -ENOMEM; 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 iounmap(srom->reg_base); 136 return -ENOMEM; 137 } 138 139 for_each_child_of_node(np, child) { 140 if (exynos_srom_configure_bank(srom, child)) { 141 dev_err(dev, 142 "Could not decode bank configuration for %pOFn\n", 143 child); 144 bad_bank_config = true; 145 } 146 } 147 148 /* 149 * If any bank failed to configure, we still provide suspend/resume, 150 * but do not probe child devices 151 */ 152 if (bad_bank_config) 153 return 0; 154 155 return of_platform_populate(np, NULL, NULL, dev); 156} 157 158#ifdef CONFIG_PM_SLEEP 159static void exynos_srom_save(void __iomem *base, 160 struct exynos_srom_reg_dump *rd, 161 unsigned int num_regs) 162{ 163 for (; num_regs > 0; --num_regs, ++rd) 164 rd->value = readl(base + rd->offset); 165} 166 167static void exynos_srom_restore(void __iomem *base, 168 const struct exynos_srom_reg_dump *rd, 169 unsigned int num_regs) 170{ 171 for (; num_regs > 0; --num_regs, ++rd) 172 writel(rd->value, base + rd->offset); 173} 174 175static int exynos_srom_suspend(struct device *dev) 176{ 177 struct exynos_srom *srom = dev_get_drvdata(dev); 178 179 exynos_srom_save(srom->reg_base, srom->reg_offset, 180 ARRAY_SIZE(exynos_srom_offsets)); 181 return 0; 182} 183 184static int exynos_srom_resume(struct device *dev) 185{ 186 struct exynos_srom *srom = dev_get_drvdata(dev); 187 188 exynos_srom_restore(srom->reg_base, srom->reg_offset, 189 ARRAY_SIZE(exynos_srom_offsets)); 190 return 0; 191} 192#endif 193 194static const struct of_device_id of_exynos_srom_ids[] = { 195 { 196 .compatible = "samsung,exynos4210-srom", 197 }, 198 {}, 199}; 200 201static SIMPLE_DEV_PM_OPS(exynos_srom_pm_ops, exynos_srom_suspend, exynos_srom_resume); 202 203static struct platform_driver exynos_srom_driver = { 204 .probe = exynos_srom_probe, 205 .driver = { 206 .name = "exynos-srom", 207 .of_match_table = of_exynos_srom_ids, 208 .pm = &exynos_srom_pm_ops, 209 .suppress_bind_attrs = true, 210 }, 211}; 212builtin_platform_driver(exynos_srom_driver);