pismo.c (5978B)
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 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 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, ®ion); 190 break; 191 case 3: 192 /* static RAM */ 193 pismo_add_sram(pismo, i, ®ion); 194 break; 195 } 196} 197 198static 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 211static 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 255static const struct i2c_device_id pismo_id[] = { 256 { "pismo" }, 257 { }, 258}; 259MODULE_DEVICE_TABLE(i2c, pismo_id); 260 261static 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 270static 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} 277module_init(pismo_init); 278 279static void __exit pismo_exit(void) 280{ 281 i2c_del_driver(&pismo_driver); 282} 283module_exit(pismo_exit); 284 285MODULE_AUTHOR("Russell King <linux@arm.linux.org.uk>"); 286MODULE_DESCRIPTION("PISMO memory driver"); 287MODULE_LICENSE("GPL");