18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// Copyright (c) 2015 Samsung Electronics Co., Ltd. 48c2ecf20Sopenharmony_ci// http://www.samsung.com/ 58c2ecf20Sopenharmony_ci// 68c2ecf20Sopenharmony_ci// Exynos - SROM Controller support 78c2ecf20Sopenharmony_ci// Author: Pankaj Dubey <pankaj.dubey@samsung.com> 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/io.h> 108c2ecf20Sopenharmony_ci#include <linux/init.h> 118c2ecf20Sopenharmony_ci#include <linux/of.h> 128c2ecf20Sopenharmony_ci#include <linux/of_address.h> 138c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 148c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include "exynos-srom.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistatic const unsigned long exynos_srom_offsets[] = { 208c2ecf20Sopenharmony_ci /* SROM side */ 218c2ecf20Sopenharmony_ci EXYNOS_SROM_BW, 228c2ecf20Sopenharmony_ci EXYNOS_SROM_BC0, 238c2ecf20Sopenharmony_ci EXYNOS_SROM_BC1, 248c2ecf20Sopenharmony_ci EXYNOS_SROM_BC2, 258c2ecf20Sopenharmony_ci EXYNOS_SROM_BC3, 268c2ecf20Sopenharmony_ci}; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/** 298c2ecf20Sopenharmony_ci * struct exynos_srom_reg_dump: register dump of SROM Controller registers. 308c2ecf20Sopenharmony_ci * @offset: srom register offset from the controller base address. 318c2ecf20Sopenharmony_ci * @value: the value of register under the offset. 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_cistruct exynos_srom_reg_dump { 348c2ecf20Sopenharmony_ci u32 offset; 358c2ecf20Sopenharmony_ci u32 value; 368c2ecf20Sopenharmony_ci}; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/** 398c2ecf20Sopenharmony_ci * struct exynos_srom: platform data for exynos srom controller driver. 408c2ecf20Sopenharmony_ci * @dev: platform device pointer 418c2ecf20Sopenharmony_ci * @reg_base: srom base address 428c2ecf20Sopenharmony_ci * @reg_offset: exynos_srom_reg_dump pointer to hold offset and its value. 438c2ecf20Sopenharmony_ci */ 448c2ecf20Sopenharmony_cistruct exynos_srom { 458c2ecf20Sopenharmony_ci struct device *dev; 468c2ecf20Sopenharmony_ci void __iomem *reg_base; 478c2ecf20Sopenharmony_ci struct exynos_srom_reg_dump *reg_offset; 488c2ecf20Sopenharmony_ci}; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic struct exynos_srom_reg_dump * 518c2ecf20Sopenharmony_ciexynos_srom_alloc_reg_dump(const unsigned long *rdump, 528c2ecf20Sopenharmony_ci unsigned long nr_rdump) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci struct exynos_srom_reg_dump *rd; 558c2ecf20Sopenharmony_ci unsigned int i; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci rd = kcalloc(nr_rdump, sizeof(*rd), GFP_KERNEL); 588c2ecf20Sopenharmony_ci if (!rd) 598c2ecf20Sopenharmony_ci return NULL; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci for (i = 0; i < nr_rdump; ++i) 628c2ecf20Sopenharmony_ci rd[i].offset = rdump[i]; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci return rd; 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic int exynos_srom_configure_bank(struct exynos_srom *srom, 688c2ecf20Sopenharmony_ci struct device_node *np) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci u32 bank, width, pmc = 0; 718c2ecf20Sopenharmony_ci u32 timing[6]; 728c2ecf20Sopenharmony_ci u32 cs, bw; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci if (of_property_read_u32(np, "reg", &bank)) 758c2ecf20Sopenharmony_ci return -EINVAL; 768c2ecf20Sopenharmony_ci if (of_property_read_u32(np, "reg-io-width", &width)) 778c2ecf20Sopenharmony_ci width = 1; 788c2ecf20Sopenharmony_ci if (of_property_read_bool(np, "samsung,srom-page-mode")) 798c2ecf20Sopenharmony_ci pmc = 1 << EXYNOS_SROM_BCX__PMC__SHIFT; 808c2ecf20Sopenharmony_ci if (of_property_read_u32_array(np, "samsung,srom-timing", timing, 818c2ecf20Sopenharmony_ci ARRAY_SIZE(timing))) 828c2ecf20Sopenharmony_ci return -EINVAL; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci bank *= 4; /* Convert bank into shift/offset */ 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci cs = 1 << EXYNOS_SROM_BW__BYTEENABLE__SHIFT; 878c2ecf20Sopenharmony_ci if (width == 2) 888c2ecf20Sopenharmony_ci cs |= 1 << EXYNOS_SROM_BW__DATAWIDTH__SHIFT; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci bw = readl_relaxed(srom->reg_base + EXYNOS_SROM_BW); 918c2ecf20Sopenharmony_ci bw = (bw & ~(EXYNOS_SROM_BW__CS_MASK << bank)) | (cs << bank); 928c2ecf20Sopenharmony_ci writel_relaxed(bw, srom->reg_base + EXYNOS_SROM_BW); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci writel_relaxed(pmc | (timing[0] << EXYNOS_SROM_BCX__TACP__SHIFT) | 958c2ecf20Sopenharmony_ci (timing[1] << EXYNOS_SROM_BCX__TCAH__SHIFT) | 968c2ecf20Sopenharmony_ci (timing[2] << EXYNOS_SROM_BCX__TCOH__SHIFT) | 978c2ecf20Sopenharmony_ci (timing[3] << EXYNOS_SROM_BCX__TACC__SHIFT) | 988c2ecf20Sopenharmony_ci (timing[4] << EXYNOS_SROM_BCX__TCOS__SHIFT) | 998c2ecf20Sopenharmony_ci (timing[5] << EXYNOS_SROM_BCX__TACS__SHIFT), 1008c2ecf20Sopenharmony_ci srom->reg_base + EXYNOS_SROM_BC0 + bank); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci return 0; 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic int exynos_srom_probe(struct platform_device *pdev) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci struct device_node *np, *child; 1088c2ecf20Sopenharmony_ci struct exynos_srom *srom; 1098c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 1108c2ecf20Sopenharmony_ci bool bad_bank_config = false; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci np = dev->of_node; 1138c2ecf20Sopenharmony_ci if (!np) { 1148c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "could not find device info\n"); 1158c2ecf20Sopenharmony_ci return -EINVAL; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci srom = devm_kzalloc(&pdev->dev, 1198c2ecf20Sopenharmony_ci sizeof(struct exynos_srom), GFP_KERNEL); 1208c2ecf20Sopenharmony_ci if (!srom) 1218c2ecf20Sopenharmony_ci return -ENOMEM; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci srom->dev = dev; 1248c2ecf20Sopenharmony_ci srom->reg_base = of_iomap(np, 0); 1258c2ecf20Sopenharmony_ci if (!srom->reg_base) { 1268c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "iomap of exynos srom controller failed\n"); 1278c2ecf20Sopenharmony_ci return -ENOMEM; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, srom); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci srom->reg_offset = exynos_srom_alloc_reg_dump(exynos_srom_offsets, 1338c2ecf20Sopenharmony_ci ARRAY_SIZE(exynos_srom_offsets)); 1348c2ecf20Sopenharmony_ci if (!srom->reg_offset) { 1358c2ecf20Sopenharmony_ci iounmap(srom->reg_base); 1368c2ecf20Sopenharmony_ci return -ENOMEM; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci for_each_child_of_node(np, child) { 1408c2ecf20Sopenharmony_ci if (exynos_srom_configure_bank(srom, child)) { 1418c2ecf20Sopenharmony_ci dev_err(dev, 1428c2ecf20Sopenharmony_ci "Could not decode bank configuration for %pOFn\n", 1438c2ecf20Sopenharmony_ci child); 1448c2ecf20Sopenharmony_ci bad_bank_config = true; 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci /* 1498c2ecf20Sopenharmony_ci * If any bank failed to configure, we still provide suspend/resume, 1508c2ecf20Sopenharmony_ci * but do not probe child devices 1518c2ecf20Sopenharmony_ci */ 1528c2ecf20Sopenharmony_ci if (bad_bank_config) 1538c2ecf20Sopenharmony_ci return 0; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci return of_platform_populate(np, NULL, NULL, dev); 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 1598c2ecf20Sopenharmony_cistatic void exynos_srom_save(void __iomem *base, 1608c2ecf20Sopenharmony_ci struct exynos_srom_reg_dump *rd, 1618c2ecf20Sopenharmony_ci unsigned int num_regs) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci for (; num_regs > 0; --num_regs, ++rd) 1648c2ecf20Sopenharmony_ci rd->value = readl(base + rd->offset); 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic void exynos_srom_restore(void __iomem *base, 1688c2ecf20Sopenharmony_ci const struct exynos_srom_reg_dump *rd, 1698c2ecf20Sopenharmony_ci unsigned int num_regs) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci for (; num_regs > 0; --num_regs, ++rd) 1728c2ecf20Sopenharmony_ci writel(rd->value, base + rd->offset); 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic int exynos_srom_suspend(struct device *dev) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci struct exynos_srom *srom = dev_get_drvdata(dev); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci exynos_srom_save(srom->reg_base, srom->reg_offset, 1808c2ecf20Sopenharmony_ci ARRAY_SIZE(exynos_srom_offsets)); 1818c2ecf20Sopenharmony_ci return 0; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic int exynos_srom_resume(struct device *dev) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci struct exynos_srom *srom = dev_get_drvdata(dev); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci exynos_srom_restore(srom->reg_base, srom->reg_offset, 1898c2ecf20Sopenharmony_ci ARRAY_SIZE(exynos_srom_offsets)); 1908c2ecf20Sopenharmony_ci return 0; 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci#endif 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic const struct of_device_id of_exynos_srom_ids[] = { 1958c2ecf20Sopenharmony_ci { 1968c2ecf20Sopenharmony_ci .compatible = "samsung,exynos4210-srom", 1978c2ecf20Sopenharmony_ci }, 1988c2ecf20Sopenharmony_ci {}, 1998c2ecf20Sopenharmony_ci}; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(exynos_srom_pm_ops, exynos_srom_suspend, exynos_srom_resume); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic struct platform_driver exynos_srom_driver = { 2048c2ecf20Sopenharmony_ci .probe = exynos_srom_probe, 2058c2ecf20Sopenharmony_ci .driver = { 2068c2ecf20Sopenharmony_ci .name = "exynos-srom", 2078c2ecf20Sopenharmony_ci .of_match_table = of_exynos_srom_ids, 2088c2ecf20Sopenharmony_ci .pm = &exynos_srom_pm_ops, 2098c2ecf20Sopenharmony_ci .suppress_bind_attrs = true, 2108c2ecf20Sopenharmony_ci }, 2118c2ecf20Sopenharmony_ci}; 2128c2ecf20Sopenharmony_cibuiltin_platform_driver(exynos_srom_driver); 213