Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux
[sfrench/cifs-2.6.git] / drivers / nvmem / imx-iim.c
1 /*
2  * i.MX IIM driver
3  *
4  * Copyright (c) 2017 Pengutronix, Michael Grzeschik <m.grzeschik@pengutronix.de>
5  *
6  * Based on the barebox iim driver,
7  * Copyright (c) 2010 Baruch Siach <baruch@tkos.co.il>,
8  *      Orex Computed Radiography
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License version 2
12  * as published by the Free Software Foundation.
13  *
14  * http://www.opensource.org/licenses/gpl-license.html
15  * http://www.gnu.org/copyleft/gpl.html
16  */
17
18 #include <linux/device.h>
19 #include <linux/io.h>
20 #include <linux/module.h>
21 #include <linux/nvmem-provider.h>
22 #include <linux/of.h>
23 #include <linux/of_device.h>
24 #include <linux/platform_device.h>
25 #include <linux/slab.h>
26 #include <linux/clk.h>
27
28 #define IIM_BANK_BASE(n)        (0x800 + 0x400 * (n))
29
30 struct imx_iim_drvdata {
31         unsigned int nregs;
32 };
33
34 struct iim_priv {
35         void __iomem *base;
36         struct clk *clk;
37         struct nvmem_config nvmem;
38 };
39
40 static int imx_iim_read(void *context, unsigned int offset,
41                           void *buf, size_t bytes)
42 {
43         struct iim_priv *iim = context;
44         int i, ret;
45         u8 *buf8 = buf;
46
47         ret = clk_prepare_enable(iim->clk);
48         if (ret)
49                 return ret;
50
51         for (i = offset; i < offset + bytes; i++) {
52                 int bank = i >> 5;
53                 int reg = i & 0x1f;
54
55                 *buf8++ = readl(iim->base + IIM_BANK_BASE(bank) + reg * 4);
56         }
57
58         clk_disable_unprepare(iim->clk);
59
60         return 0;
61 }
62
63 static struct imx_iim_drvdata imx27_drvdata = {
64         .nregs = 2 * 32,
65 };
66
67 static struct imx_iim_drvdata imx25_imx31_imx35_drvdata = {
68         .nregs = 3 * 32,
69 };
70
71 static struct imx_iim_drvdata imx51_drvdata = {
72         .nregs = 4 * 32,
73 };
74
75 static struct imx_iim_drvdata imx53_drvdata = {
76         .nregs = 4 * 32 + 16,
77 };
78
79 static const struct of_device_id imx_iim_dt_ids[] = {
80         {
81                 .compatible = "fsl,imx25-iim",
82                 .data = &imx25_imx31_imx35_drvdata,
83         }, {
84                 .compatible = "fsl,imx27-iim",
85                 .data = &imx27_drvdata,
86         }, {
87                 .compatible = "fsl,imx31-iim",
88                 .data = &imx25_imx31_imx35_drvdata,
89         }, {
90                 .compatible = "fsl,imx35-iim",
91                 .data = &imx25_imx31_imx35_drvdata,
92         }, {
93                 .compatible = "fsl,imx51-iim",
94                 .data = &imx51_drvdata,
95         }, {
96                 .compatible = "fsl,imx53-iim",
97                 .data = &imx53_drvdata,
98         }, {
99                 /* sentinel */
100         },
101 };
102 MODULE_DEVICE_TABLE(of, imx_iim_dt_ids);
103
104 static int imx_iim_probe(struct platform_device *pdev)
105 {
106         const struct of_device_id *of_id;
107         struct device *dev = &pdev->dev;
108         struct resource *res;
109         struct iim_priv *iim;
110         struct nvmem_device *nvmem;
111         struct nvmem_config *cfg;
112         const struct imx_iim_drvdata *drvdata = NULL;
113
114         iim = devm_kzalloc(dev, sizeof(*iim), GFP_KERNEL);
115         if (!iim)
116                 return -ENOMEM;
117
118         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
119         iim->base = devm_ioremap_resource(dev, res);
120         if (IS_ERR(iim->base))
121                 return PTR_ERR(iim->base);
122
123         of_id = of_match_device(imx_iim_dt_ids, dev);
124         if (!of_id)
125                 return -ENODEV;
126
127         drvdata = of_id->data;
128
129         iim->clk = devm_clk_get(&pdev->dev, NULL);
130         if (IS_ERR(iim->clk))
131                 return PTR_ERR(iim->clk);
132
133         cfg = &iim->nvmem;
134
135         cfg->name = "imx-iim",
136         cfg->read_only = true,
137         cfg->word_size = 1,
138         cfg->stride = 1,
139         cfg->owner = THIS_MODULE,
140         cfg->reg_read = imx_iim_read,
141         cfg->dev = dev;
142         cfg->size = drvdata->nregs;
143         cfg->priv = iim;
144
145         nvmem = nvmem_register(cfg);
146         if (IS_ERR(nvmem))
147                 return PTR_ERR(nvmem);
148
149         platform_set_drvdata(pdev, nvmem);
150
151         return 0;
152 }
153
154 static int imx_iim_remove(struct platform_device *pdev)
155 {
156         struct nvmem_device *nvmem = platform_get_drvdata(pdev);
157
158         return nvmem_unregister(nvmem);
159 }
160
161 static struct platform_driver imx_iim_driver = {
162         .probe  = imx_iim_probe,
163         .remove = imx_iim_remove,
164         .driver = {
165                 .name   = "imx-iim",
166                 .of_match_table = imx_iim_dt_ids,
167         },
168 };
169 module_platform_driver(imx_iim_driver);
170
171 MODULE_AUTHOR("Michael Grzeschik <m.grzeschik@pengutronix.de>");
172 MODULE_DESCRIPTION("i.MX IIM driver");
173 MODULE_LICENSE("GPL v2");