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