1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * i.MX IIM driver |
4 | * |
5 | * Copyright (c) 2017 Pengutronix, Michael Grzeschik <m.grzeschik@pengutronix.de> |
6 | * |
7 | * Based on the barebox iim driver, |
8 | * Copyright (c) 2010 Baruch Siach <baruch@tkos.co.il>, |
9 | * Orex Computed Radiography |
10 | */ |
11 | |
12 | #include <linux/device.h> |
13 | #include <linux/io.h> |
14 | #include <linux/module.h> |
15 | #include <linux/nvmem-provider.h> |
16 | #include <linux/of.h> |
17 | #include <linux/platform_device.h> |
18 | #include <linux/slab.h> |
19 | #include <linux/clk.h> |
20 | |
21 | #define IIM_BANK_BASE(n) (0x800 + 0x400 * (n)) |
22 | |
23 | struct imx_iim_drvdata { |
24 | unsigned int nregs; |
25 | }; |
26 | |
27 | struct iim_priv { |
28 | void __iomem *base; |
29 | struct clk *clk; |
30 | }; |
31 | |
32 | static int imx_iim_read(void *context, unsigned int offset, |
33 | void *buf, size_t bytes) |
34 | { |
35 | struct iim_priv *iim = context; |
36 | int i, ret; |
37 | u8 *buf8 = buf; |
38 | |
39 | ret = clk_prepare_enable(clk: iim->clk); |
40 | if (ret) |
41 | return ret; |
42 | |
43 | for (i = offset; i < offset + bytes; i++) { |
44 | int bank = i >> 5; |
45 | int reg = i & 0x1f; |
46 | |
47 | *buf8++ = readl(addr: iim->base + IIM_BANK_BASE(bank) + reg * 4); |
48 | } |
49 | |
50 | clk_disable_unprepare(clk: iim->clk); |
51 | |
52 | return 0; |
53 | } |
54 | |
55 | static struct imx_iim_drvdata imx27_drvdata = { |
56 | .nregs = 2 * 32, |
57 | }; |
58 | |
59 | static struct imx_iim_drvdata imx25_imx31_imx35_drvdata = { |
60 | .nregs = 3 * 32, |
61 | }; |
62 | |
63 | static struct imx_iim_drvdata imx51_drvdata = { |
64 | .nregs = 4 * 32, |
65 | }; |
66 | |
67 | static struct imx_iim_drvdata imx53_drvdata = { |
68 | .nregs = 4 * 32 + 16, |
69 | }; |
70 | |
71 | static const struct of_device_id imx_iim_dt_ids[] = { |
72 | { |
73 | .compatible = "fsl,imx25-iim" , |
74 | .data = &imx25_imx31_imx35_drvdata, |
75 | }, { |
76 | .compatible = "fsl,imx27-iim" , |
77 | .data = &imx27_drvdata, |
78 | }, { |
79 | .compatible = "fsl,imx31-iim" , |
80 | .data = &imx25_imx31_imx35_drvdata, |
81 | }, { |
82 | .compatible = "fsl,imx35-iim" , |
83 | .data = &imx25_imx31_imx35_drvdata, |
84 | }, { |
85 | .compatible = "fsl,imx51-iim" , |
86 | .data = &imx51_drvdata, |
87 | }, { |
88 | .compatible = "fsl,imx53-iim" , |
89 | .data = &imx53_drvdata, |
90 | }, { |
91 | /* sentinel */ |
92 | }, |
93 | }; |
94 | MODULE_DEVICE_TABLE(of, imx_iim_dt_ids); |
95 | |
96 | static int imx_iim_probe(struct platform_device *pdev) |
97 | { |
98 | struct device *dev = &pdev->dev; |
99 | struct iim_priv *iim; |
100 | struct nvmem_device *nvmem; |
101 | struct nvmem_config cfg = {}; |
102 | const struct imx_iim_drvdata *drvdata = NULL; |
103 | |
104 | iim = devm_kzalloc(dev, size: sizeof(*iim), GFP_KERNEL); |
105 | if (!iim) |
106 | return -ENOMEM; |
107 | |
108 | iim->base = devm_platform_ioremap_resource(pdev, index: 0); |
109 | if (IS_ERR(ptr: iim->base)) |
110 | return PTR_ERR(ptr: iim->base); |
111 | |
112 | drvdata = of_device_get_match_data(dev: &pdev->dev); |
113 | |
114 | iim->clk = devm_clk_get(dev, NULL); |
115 | if (IS_ERR(ptr: iim->clk)) |
116 | return PTR_ERR(ptr: iim->clk); |
117 | |
118 | cfg.name = "imx-iim" , |
119 | cfg.read_only = true, |
120 | cfg.word_size = 1, |
121 | cfg.stride = 1, |
122 | cfg.reg_read = imx_iim_read, |
123 | cfg.dev = dev; |
124 | cfg.size = drvdata->nregs; |
125 | cfg.priv = iim; |
126 | |
127 | nvmem = devm_nvmem_register(dev, cfg: &cfg); |
128 | |
129 | return PTR_ERR_OR_ZERO(ptr: nvmem); |
130 | } |
131 | |
132 | static struct platform_driver imx_iim_driver = { |
133 | .probe = imx_iim_probe, |
134 | .driver = { |
135 | .name = "imx-iim" , |
136 | .of_match_table = imx_iim_dt_ids, |
137 | }, |
138 | }; |
139 | module_platform_driver(imx_iim_driver); |
140 | |
141 | MODULE_AUTHOR("Michael Grzeschik <m.grzeschik@pengutronix.de>" ); |
142 | MODULE_DESCRIPTION("i.MX IIM driver" ); |
143 | MODULE_LICENSE("GPL v2" ); |
144 | |