Merge tag 'kvmarm-fixes-5.14-1' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-microblaze.git] / drivers / mtd / maps / pismo.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * PISMO memory driver - http://www.pismoworld.org/
4  *
5  * For ARM Realview and Versatile platforms
6  */
7 #include <linux/init.h>
8 #include <linux/module.h>
9 #include <linux/i2c.h>
10 #include <linux/slab.h>
11 #include <linux/platform_device.h>
12 #include <linux/spinlock.h>
13 #include <linux/mutex.h>
14 #include <linux/mtd/physmap.h>
15 #include <linux/mtd/plat-ram.h>
16 #include <linux/mtd/pismo.h>
17
18 #define PISMO_NUM_CS    5
19
20 struct pismo_cs_block {
21         u8      type;
22         u8      width;
23         __le16  access;
24         __le32  size;
25         u32     reserved[2];
26         char    device[32];
27 } __packed;
28
29 struct pismo_eeprom {
30         struct pismo_cs_block cs[PISMO_NUM_CS];
31         char    board[15];
32         u8      sum;
33 } __packed;
34
35 struct pismo_mem {
36         phys_addr_t base;
37         u32     size;
38         u16     access;
39         u8      width;
40         u8      type;
41 };
42
43 struct pismo_data {
44         struct i2c_client       *client;
45         void                    (*vpp)(void *, int);
46         void                    *vpp_data;
47         struct platform_device  *dev[PISMO_NUM_CS];
48 };
49
50 static void pismo_set_vpp(struct platform_device *pdev, int on)
51 {
52         struct i2c_client *client = to_i2c_client(pdev->dev.parent);
53         struct pismo_data *pismo = i2c_get_clientdata(client);
54
55         pismo->vpp(pismo->vpp_data, on);
56 }
57
58 static unsigned int pismo_width_to_bytes(unsigned int width)
59 {
60         width &= 15;
61         if (width > 2)
62                 return 0;
63         return 1 << width;
64 }
65
66 static int pismo_eeprom_read(struct i2c_client *client, void *buf, u8 addr,
67                              size_t size)
68 {
69         int ret;
70         struct i2c_msg msg[] = {
71                 {
72                         .addr = client->addr,
73                         .len = sizeof(addr),
74                         .buf = &addr,
75                 }, {
76                         .addr = client->addr,
77                         .flags = I2C_M_RD,
78                         .len = size,
79                         .buf = buf,
80                 },
81         };
82
83         ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
84
85         return ret == ARRAY_SIZE(msg) ? size : -EIO;
86 }
87
88 static int pismo_add_device(struct pismo_data *pismo, int i,
89                             struct pismo_mem *region, const char *name,
90                             void *pdata, size_t psize)
91 {
92         struct platform_device *dev;
93         struct resource res = { };
94         phys_addr_t base = region->base;
95         int ret;
96
97         if (base == ~0)
98                 return -ENXIO;
99
100         res.start = base;
101         res.end = base + region->size - 1;
102         res.flags = IORESOURCE_MEM;
103
104         dev = platform_device_alloc(name, i);
105         if (!dev)
106                 return -ENOMEM;
107         dev->dev.parent = &pismo->client->dev;
108
109         do {
110                 ret = platform_device_add_resources(dev, &res, 1);
111                 if (ret)
112                         break;
113
114                 ret = platform_device_add_data(dev, pdata, psize);
115                 if (ret)
116                         break;
117
118                 ret = platform_device_add(dev);
119                 if (ret)
120                         break;
121
122                 pismo->dev[i] = dev;
123                 return 0;
124         } while (0);
125
126         platform_device_put(dev);
127         return ret;
128 }
129
130 static int pismo_add_nor(struct pismo_data *pismo, int i,
131                          struct pismo_mem *region)
132 {
133         struct physmap_flash_data data = {
134                 .width = region->width,
135         };
136
137         if (pismo->vpp)
138                 data.set_vpp = pismo_set_vpp;
139
140         return pismo_add_device(pismo, i, region, "physmap-flash",
141                 &data, sizeof(data));
142 }
143
144 static int pismo_add_sram(struct pismo_data *pismo, int i,
145                           struct pismo_mem *region)
146 {
147         struct platdata_mtd_ram data = {
148                 .bankwidth = region->width,
149         };
150
151         return pismo_add_device(pismo, i, region, "mtd-ram",
152                 &data, sizeof(data));
153 }
154
155 static void pismo_add_one(struct pismo_data *pismo, int i,
156                           const struct pismo_cs_block *cs, phys_addr_t base)
157 {
158         struct device *dev = &pismo->client->dev;
159         struct pismo_mem region;
160
161         region.base = base;
162         region.type = cs->type;
163         region.width = pismo_width_to_bytes(cs->width);
164         region.access = le16_to_cpu(cs->access);
165         region.size = le32_to_cpu(cs->size);
166
167         if (region.width == 0) {
168                 dev_err(dev, "cs%u: bad width: %02x, ignoring\n", i, cs->width);
169                 return;
170         }
171
172         /*
173          * FIXME: may need to the platforms memory controller here, but at
174          * the moment we assume that it has already been correctly setup.
175          * The memory controller can also tell us the base address as well.
176          */
177
178         dev_info(dev, "cs%u: %.32s: type %02x access %u00ps size %uK\n",
179                 i, cs->device, region.type, region.access, region.size / 1024);
180
181         switch (region.type) {
182         case 0:
183                 break;
184         case 1:
185                 /* static DOC */
186                 break;
187         case 2:
188                 /* static NOR */
189                 pismo_add_nor(pismo, i, &region);
190                 break;
191         case 3:
192                 /* static RAM */
193                 pismo_add_sram(pismo, i, &region);
194                 break;
195         }
196 }
197
198 static int pismo_remove(struct i2c_client *client)
199 {
200         struct pismo_data *pismo = i2c_get_clientdata(client);
201         int i;
202
203         for (i = 0; i < ARRAY_SIZE(pismo->dev); i++)
204                 platform_device_unregister(pismo->dev[i]);
205
206         kfree(pismo);
207
208         return 0;
209 }
210
211 static int pismo_probe(struct i2c_client *client,
212                        const struct i2c_device_id *id)
213 {
214         struct pismo_pdata *pdata = client->dev.platform_data;
215         struct pismo_eeprom eeprom;
216         struct pismo_data *pismo;
217         int ret, i;
218
219         if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
220                 dev_err(&client->dev, "functionality mismatch\n");
221                 return -EIO;
222         }
223
224         pismo = kzalloc(sizeof(*pismo), GFP_KERNEL);
225         if (!pismo)
226                 return -ENOMEM;
227
228         pismo->client = client;
229         if (pdata) {
230                 pismo->vpp = pdata->set_vpp;
231                 pismo->vpp_data = pdata->vpp_data;
232         }
233         i2c_set_clientdata(client, pismo);
234
235         ret = pismo_eeprom_read(client, &eeprom, 0, sizeof(eeprom));
236         if (ret < 0) {
237                 dev_err(&client->dev, "error reading EEPROM: %d\n", ret);
238                 goto exit_free;
239         }
240
241         dev_info(&client->dev, "%.15s board found\n", eeprom.board);
242
243         for (i = 0; i < ARRAY_SIZE(eeprom.cs); i++)
244                 if (eeprom.cs[i].type != 0xff)
245                         pismo_add_one(pismo, i, &eeprom.cs[i],
246                                       pdata->cs_addrs[i]);
247
248         return 0;
249
250  exit_free:
251         kfree(pismo);
252         return ret;
253 }
254
255 static const struct i2c_device_id pismo_id[] = {
256         { "pismo" },
257         { },
258 };
259 MODULE_DEVICE_TABLE(i2c, pismo_id);
260
261 static struct i2c_driver pismo_driver = {
262         .driver = {
263                 .name   = "pismo",
264         },
265         .probe          = pismo_probe,
266         .remove         = pismo_remove,
267         .id_table       = pismo_id,
268 };
269
270 static int __init pismo_init(void)
271 {
272         BUILD_BUG_ON(sizeof(struct pismo_cs_block) != 48);
273         BUILD_BUG_ON(sizeof(struct pismo_eeprom) != 256);
274
275         return i2c_add_driver(&pismo_driver);
276 }
277 module_init(pismo_init);
278
279 static void __exit pismo_exit(void)
280 {
281         i2c_del_driver(&pismo_driver);
282 }
283 module_exit(pismo_exit);
284
285 MODULE_AUTHOR("Russell King <linux@arm.linux.org.uk>");
286 MODULE_DESCRIPTION("PISMO memory driver");
287 MODULE_LICENSE("GPL");