Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 //
0003 // Copyright (c) 2015 Samsung Electronics Co., Ltd.
0004 //        http://www.samsung.com/
0005 //
0006 // Exynos - SROM Controller support
0007 // Author: Pankaj Dubey <pankaj.dubey@samsung.com>
0008 
0009 #include <linux/io.h>
0010 #include <linux/init.h>
0011 #include <linux/of.h>
0012 #include <linux/of_address.h>
0013 #include <linux/of_platform.h>
0014 #include <linux/platform_device.h>
0015 #include <linux/slab.h>
0016 
0017 #include "exynos-srom.h"
0018 
0019 static const unsigned long exynos_srom_offsets[] = {
0020     /* SROM side */
0021     EXYNOS_SROM_BW,
0022     EXYNOS_SROM_BC0,
0023     EXYNOS_SROM_BC1,
0024     EXYNOS_SROM_BC2,
0025     EXYNOS_SROM_BC3,
0026 };
0027 
0028 /**
0029  * struct exynos_srom_reg_dump: register dump of SROM Controller registers.
0030  * @offset: srom register offset from the controller base address.
0031  * @value: the value of register under the offset.
0032  */
0033 struct exynos_srom_reg_dump {
0034     u32     offset;
0035     u32     value;
0036 };
0037 
0038 /**
0039  * struct exynos_srom: platform data for exynos srom controller driver.
0040  * @dev: platform device pointer
0041  * @reg_base: srom base address
0042  * @reg_offset: exynos_srom_reg_dump pointer to hold offset and its value.
0043  */
0044 struct exynos_srom {
0045     struct device *dev;
0046     void __iomem *reg_base;
0047     struct exynos_srom_reg_dump *reg_offset;
0048 };
0049 
0050 static struct exynos_srom_reg_dump *
0051 exynos_srom_alloc_reg_dump(const unsigned long *rdump,
0052                unsigned long nr_rdump)
0053 {
0054     struct exynos_srom_reg_dump *rd;
0055     unsigned int i;
0056 
0057     rd = kcalloc(nr_rdump, sizeof(*rd), GFP_KERNEL);
0058     if (!rd)
0059         return NULL;
0060 
0061     for (i = 0; i < nr_rdump; ++i)
0062         rd[i].offset = rdump[i];
0063 
0064     return rd;
0065 }
0066 
0067 static int exynos_srom_configure_bank(struct exynos_srom *srom,
0068                       struct device_node *np)
0069 {
0070     u32 bank, width, pmc = 0;
0071     u32 timing[6];
0072     u32 cs, bw;
0073 
0074     if (of_property_read_u32(np, "reg", &bank))
0075         return -EINVAL;
0076     if (of_property_read_u32(np, "reg-io-width", &width))
0077         width = 1;
0078     if (of_property_read_bool(np, "samsung,srom-page-mode"))
0079         pmc = 1 << EXYNOS_SROM_BCX__PMC__SHIFT;
0080     if (of_property_read_u32_array(np, "samsung,srom-timing", timing,
0081                        ARRAY_SIZE(timing)))
0082         return -EINVAL;
0083 
0084     bank *= 4; /* Convert bank into shift/offset */
0085 
0086     cs = 1 << EXYNOS_SROM_BW__BYTEENABLE__SHIFT;
0087     if (width == 2)
0088         cs |= 1 << EXYNOS_SROM_BW__DATAWIDTH__SHIFT;
0089 
0090     bw = readl_relaxed(srom->reg_base + EXYNOS_SROM_BW);
0091     bw = (bw & ~(EXYNOS_SROM_BW__CS_MASK << bank)) | (cs << bank);
0092     writel_relaxed(bw, srom->reg_base + EXYNOS_SROM_BW);
0093 
0094     writel_relaxed(pmc | (timing[0] << EXYNOS_SROM_BCX__TACP__SHIFT) |
0095                (timing[1] << EXYNOS_SROM_BCX__TCAH__SHIFT) |
0096                (timing[2] << EXYNOS_SROM_BCX__TCOH__SHIFT) |
0097                (timing[3] << EXYNOS_SROM_BCX__TACC__SHIFT) |
0098                (timing[4] << EXYNOS_SROM_BCX__TCOS__SHIFT) |
0099                (timing[5] << EXYNOS_SROM_BCX__TACS__SHIFT),
0100                srom->reg_base + EXYNOS_SROM_BC0 + bank);
0101 
0102     return 0;
0103 }
0104 
0105 static int exynos_srom_probe(struct platform_device *pdev)
0106 {
0107     struct device_node *np, *child;
0108     struct exynos_srom *srom;
0109     struct device *dev = &pdev->dev;
0110     bool bad_bank_config = false;
0111 
0112     np = dev->of_node;
0113     if (!np) {
0114         dev_err(&pdev->dev, "could not find device info\n");
0115         return -EINVAL;
0116     }
0117 
0118     srom = devm_kzalloc(&pdev->dev,
0119                 sizeof(struct exynos_srom), GFP_KERNEL);
0120     if (!srom)
0121         return -ENOMEM;
0122 
0123     srom->dev = dev;
0124     srom->reg_base = of_iomap(np, 0);
0125     if (!srom->reg_base) {
0126         dev_err(&pdev->dev, "iomap of exynos srom controller failed\n");
0127         return -ENOMEM;
0128     }
0129 
0130     platform_set_drvdata(pdev, srom);
0131 
0132     srom->reg_offset = exynos_srom_alloc_reg_dump(exynos_srom_offsets,
0133                               ARRAY_SIZE(exynos_srom_offsets));
0134     if (!srom->reg_offset) {
0135         iounmap(srom->reg_base);
0136         return -ENOMEM;
0137     }
0138 
0139     for_each_child_of_node(np, child) {
0140         if (exynos_srom_configure_bank(srom, child)) {
0141             dev_err(dev,
0142                 "Could not decode bank configuration for %pOFn\n",
0143                 child);
0144             bad_bank_config = true;
0145         }
0146     }
0147 
0148     /*
0149      * If any bank failed to configure, we still provide suspend/resume,
0150      * but do not probe child devices
0151      */
0152     if (bad_bank_config)
0153         return 0;
0154 
0155     return of_platform_populate(np, NULL, NULL, dev);
0156 }
0157 
0158 #ifdef CONFIG_PM_SLEEP
0159 static void exynos_srom_save(void __iomem *base,
0160                  struct exynos_srom_reg_dump *rd,
0161                  unsigned int num_regs)
0162 {
0163     for (; num_regs > 0; --num_regs, ++rd)
0164         rd->value = readl(base + rd->offset);
0165 }
0166 
0167 static void exynos_srom_restore(void __iomem *base,
0168                 const struct exynos_srom_reg_dump *rd,
0169                 unsigned int num_regs)
0170 {
0171     for (; num_regs > 0; --num_regs, ++rd)
0172         writel(rd->value, base + rd->offset);
0173 }
0174 
0175 static int exynos_srom_suspend(struct device *dev)
0176 {
0177     struct exynos_srom *srom = dev_get_drvdata(dev);
0178 
0179     exynos_srom_save(srom->reg_base, srom->reg_offset,
0180              ARRAY_SIZE(exynos_srom_offsets));
0181     return 0;
0182 }
0183 
0184 static int exynos_srom_resume(struct device *dev)
0185 {
0186     struct exynos_srom *srom = dev_get_drvdata(dev);
0187 
0188     exynos_srom_restore(srom->reg_base, srom->reg_offset,
0189                 ARRAY_SIZE(exynos_srom_offsets));
0190     return 0;
0191 }
0192 #endif
0193 
0194 static const struct of_device_id of_exynos_srom_ids[] = {
0195     {
0196         .compatible = "samsung,exynos4210-srom",
0197     },
0198     {},
0199 };
0200 
0201 static SIMPLE_DEV_PM_OPS(exynos_srom_pm_ops, exynos_srom_suspend, exynos_srom_resume);
0202 
0203 static struct platform_driver exynos_srom_driver = {
0204     .probe = exynos_srom_probe,
0205     .driver = {
0206         .name = "exynos-srom",
0207         .of_match_table = of_exynos_srom_ids,
0208         .pm = &exynos_srom_pm_ops,
0209         .suppress_bind_attrs = true,
0210     },
0211 };
0212 builtin_platform_driver(exynos_srom_driver);