]> rtime.felk.cvut.cz Git - zynq/linux.git/blob - drivers/nvmem/zynqmp_nvmem.c
nvmem: zynqmp: Added support to access efuse
[zynq/linux.git] / drivers / nvmem / zynqmp_nvmem.c
1 /*
2  * Copyright (C) 2017 Xilinx, Inc.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  */
14
15 #include <linux/dma-mapping.h>
16 #include <linux/module.h>
17 #include <linux/nvmem-provider.h>
18 #include <linux/of.h>
19 #include <linux/platform_device.h>
20 #include <linux/firmware/xilinx/zynqmp/firmware.h>
21
22 #define SILICON_REVISION_MASK 0xF
23 #define WORD_INBYTES            (4)
24 #define SOC_VER_SIZE            (0x4)
25 #define EFUSE_MEMORY_SIZE       (0xF4)
26 #define UNUSED_SPACE            (0x8)
27 #define ZYNQMP_NVMEM_SIZE       (SOC_VER_SIZE + UNUSED_SPACE + \
28                                  EFUSE_MEMORY_SIZE)
29 #define SOC_VERSION_OFFSET      (0x0)
30 #define EFUSE_START_OFFSET      (0xC)
31 #define EFUSE_END_OFFSET        (0xFC)
32 #define EFUSE_NOT_ENABLED       (29)
33 #define EFUSE_READ              (0)
34 #define EFUSE_WRITE             (1)
35
36 /**
37  * struct xilinx_efuse - the basic structure
38  * @src:        address of the buffer to store the data to be write/read
39  * @size:       no of words to be read/write
40  * @offset:     offset to be read/write`
41  * @flag:       0 - represents efuse read and 1- represents efuse write
42  *
43  * this structure stores all the required details to
44  * read/write efuse memory.
45  */
46 struct xilinx_efuse {
47         u64 src;
48         u32 size;
49         u32 offset;
50         u32 flag;
51         u32 fullmap;
52 };
53
54 static int zynqmp_efuse_access(void *context, unsigned int offset,
55                                void *val, size_t bytes, unsigned int flag)
56 {
57         const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
58         size_t words = bytes / WORD_INBYTES;
59         struct device *dev = context;
60         dma_addr_t dma_addr, dma_buf;
61         struct xilinx_efuse *efuse;
62         char *data;
63         int ret;
64
65         if (!eemi_ops || !eemi_ops->efuse_access)
66                 return -ENXIO;
67
68         if (bytes % WORD_INBYTES != 0) {
69                 dev_err(dev,
70                         "ERROR: Bytes requested should be word aligned\n\r");
71                 return -ENOTSUPP;
72         }
73         if (offset % WORD_INBYTES != 0) {
74                 dev_err(dev,
75                         "ERROR: offset requested should be word aligned\n\r");
76                 return -ENOTSUPP;
77         }
78
79         efuse = dma_alloc_coherent(dev, sizeof(struct xilinx_efuse),
80                                    &dma_addr, GFP_KERNEL);
81         if (!efuse)
82                 return -ENOMEM;
83
84         data = dma_alloc_coherent(dev, sizeof(bytes),
85                                   &dma_buf, GFP_KERNEL);
86         if (!data) {
87                 dma_free_coherent(dev, sizeof(struct xilinx_efuse),
88                                   efuse, dma_addr);
89                 return -ENOMEM;
90         }
91
92         if (flag == EFUSE_WRITE) {
93                 memcpy(data, val, bytes);
94                 efuse->flag = EFUSE_WRITE;
95         } else {
96                 efuse->flag = EFUSE_READ;
97         }
98
99         efuse->src = dma_buf;
100         efuse->size = words;
101         efuse->offset = offset;
102
103         eemi_ops->efuse_access(dma_addr, &ret);
104         if (ret != 0) {
105                 if (ret == EFUSE_NOT_ENABLED) {
106                         dev_err(dev, "ERROR: efuse access is not enabled\n\r");
107                         ret = -ENOTSUPP;
108                         goto END;
109                 }
110                 dev_err(dev, "ERROR: in efuse read %x\n\r", ret);
111                 ret = -EPERM;
112                 goto END;
113         }
114
115         if (flag == EFUSE_READ)
116                 memcpy(val, data, bytes);
117 END:
118
119         dma_free_coherent(dev, sizeof(struct xilinx_efuse),
120                           efuse, dma_addr);
121         dma_free_coherent(dev, sizeof(bytes),
122                           data, dma_buf);
123
124         return ret;
125 }
126
127 static int zynqmp_nvmem_read(void *context, unsigned int offset,
128                                         void *val, size_t bytes)
129 {
130         int ret;
131         int idcode, version;
132         const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
133
134         if (!eemi_ops || !eemi_ops->get_chipid)
135                 return -ENXIO;
136
137         switch (offset) {
138         /* Soc version offset is zero */
139         case SOC_VERSION_OFFSET:
140                 if (bytes != SOC_VER_SIZE)
141                         return -ENOTSUPP;
142
143                 ret = eemi_ops->get_chipid(&idcode, &version);
144                 if (ret < 0)
145                         return ret;
146
147                 pr_debug("Read chipid val %x %x\n", idcode, version);
148                 *(int *)val = version & SILICON_REVISION_MASK;
149                 break;
150         /* Efuse offset starts from 0xc */
151         case EFUSE_START_OFFSET ... EFUSE_END_OFFSET:
152                 ret = zynqmp_efuse_access(context, offset, val,
153                                           bytes, EFUSE_READ);
154                 break;
155         default:
156                 *(u32 *)val = 0xDEADBEEF;
157                 ret = 0;
158                 break;
159         }
160
161         return ret;
162 }
163
164 static int zynqmp_nvmem_write(void *context,
165                               unsigned int offset, void *val, size_t bytes)
166 {
167         /* Efuse offset starts from 0xc */
168         if (offset < EFUSE_START_OFFSET)
169                 return -ENOTSUPP;
170
171         return(zynqmp_efuse_access(context, offset,
172                                    val, bytes, EFUSE_WRITE));
173 }
174
175 static struct nvmem_config econfig = {
176         .name = "zynqmp-nvmem",
177         .owner = THIS_MODULE,
178         .word_size = 1,
179         .size = ZYNQMP_NVMEM_SIZE,
180 };
181
182 static const struct of_device_id zynqmp_nvmem_match[] = {
183         { .compatible = "xlnx,zynqmp-nvmem-fw", },
184         { /* sentinel */ },
185 };
186 MODULE_DEVICE_TABLE(of, zynqmp_nvmem_match);
187
188 static int zynqmp_nvmem_probe(struct platform_device *pdev)
189 {
190         struct nvmem_device *nvmem;
191
192         econfig.dev = &pdev->dev;
193         econfig.priv = &pdev->dev;
194         econfig.reg_read = zynqmp_nvmem_read;
195         econfig.reg_write = zynqmp_nvmem_write;
196
197         nvmem = nvmem_register(&econfig);
198         if (IS_ERR(nvmem))
199                 return PTR_ERR(nvmem);
200
201         platform_set_drvdata(pdev, nvmem);
202
203         return 0;
204 }
205
206 static int zynqmp_nvmem_remove(struct platform_device *pdev)
207 {
208         struct nvmem_device *nvmem = platform_get_drvdata(pdev);
209
210         return nvmem_unregister(nvmem);
211 }
212
213 static struct platform_driver zynqmp_nvmem_driver = {
214         .probe = zynqmp_nvmem_probe,
215         .remove = zynqmp_nvmem_remove,
216         .driver = {
217                 .name = "zynqmp-nvmem",
218                 .of_match_table = zynqmp_nvmem_match,
219         },
220 };
221
222 module_platform_driver(zynqmp_nvmem_driver);
223
224 MODULE_AUTHOR("Michal Simek <michal.simek@xilinx.com>, Nava kishore Manne <navam@xilinx.com>");
225 MODULE_DESCRIPTION("ZynqMP NVMEM driver");
226 MODULE_LICENSE("GPL");