285 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * PISMO memory driver - http://www.pismoworld.org/4 *5 * For ARM Realview and Versatile platforms6 */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 519 20struct 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 29struct pismo_eeprom {30 struct pismo_cs_block cs[PISMO_NUM_CS];31 char board[15];32 u8 sum;33} __packed;34 35struct pismo_mem {36 phys_addr_t base;37 u32 size;38 u16 access;39 u8 width;40 u8 type;41};42 43struct 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 50static 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 58static 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 66static 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 88static 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 130static 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 144static 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 155static 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 at174 * 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, ®ion);190 break;191 case 3:192 /* static RAM */193 pismo_add_sram(pismo, i, ®ion);194 break;195 }196}197 198static void 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 209static int pismo_probe(struct i2c_client *client)210{211 struct pismo_pdata *pdata = client->dev.platform_data;212 struct pismo_eeprom eeprom;213 struct pismo_data *pismo;214 int ret, i;215 216 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {217 dev_err(&client->dev, "functionality mismatch\n");218 return -EIO;219 }220 221 pismo = kzalloc(sizeof(*pismo), GFP_KERNEL);222 if (!pismo)223 return -ENOMEM;224 225 pismo->client = client;226 if (pdata) {227 pismo->vpp = pdata->set_vpp;228 pismo->vpp_data = pdata->vpp_data;229 }230 i2c_set_clientdata(client, pismo);231 232 ret = pismo_eeprom_read(client, &eeprom, 0, sizeof(eeprom));233 if (ret < 0) {234 dev_err(&client->dev, "error reading EEPROM: %d\n", ret);235 goto exit_free;236 }237 238 dev_info(&client->dev, "%.15s board found\n", eeprom.board);239 240 for (i = 0; i < ARRAY_SIZE(eeprom.cs); i++)241 if (eeprom.cs[i].type != 0xff)242 pismo_add_one(pismo, i, &eeprom.cs[i],243 pdata->cs_addrs[i]);244 245 return 0;246 247 exit_free:248 kfree(pismo);249 return ret;250}251 252static const struct i2c_device_id pismo_id[] = {253 { "pismo" },254 { },255};256MODULE_DEVICE_TABLE(i2c, pismo_id);257 258static struct i2c_driver pismo_driver = {259 .driver = {260 .name = "pismo",261 },262 .probe = pismo_probe,263 .remove = pismo_remove,264 .id_table = pismo_id,265};266 267static int __init pismo_init(void)268{269 BUILD_BUG_ON(sizeof(struct pismo_cs_block) != 48);270 BUILD_BUG_ON(sizeof(struct pismo_eeprom) != 256);271 272 return i2c_add_driver(&pismo_driver);273}274module_init(pismo_init);275 276static void __exit pismo_exit(void)277{278 i2c_del_driver(&pismo_driver);279}280module_exit(pismo_exit);281 282MODULE_AUTHOR("Russell King <linux@arm.linux.org.uk>");283MODULE_DESCRIPTION("PISMO memory driver");284MODULE_LICENSE("GPL");285