Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * PISMO memory driver - http://www.pismoworld.org/
0004  *
0005  * For ARM Realview and Versatile platforms
0006  */
0007 #include <linux/init.h>
0008 #include <linux/module.h>
0009 #include <linux/i2c.h>
0010 #include <linux/slab.h>
0011 #include <linux/platform_device.h>
0012 #include <linux/spinlock.h>
0013 #include <linux/mutex.h>
0014 #include <linux/mtd/physmap.h>
0015 #include <linux/mtd/plat-ram.h>
0016 #include <linux/mtd/pismo.h>
0017 
0018 #define PISMO_NUM_CS    5
0019 
0020 struct pismo_cs_block {
0021     u8  type;
0022     u8  width;
0023     __le16  access;
0024     __le32  size;
0025     u32 reserved[2];
0026     char    device[32];
0027 } __packed;
0028 
0029 struct pismo_eeprom {
0030     struct pismo_cs_block cs[PISMO_NUM_CS];
0031     char    board[15];
0032     u8  sum;
0033 } __packed;
0034 
0035 struct pismo_mem {
0036     phys_addr_t base;
0037     u32 size;
0038     u16 access;
0039     u8  width;
0040     u8  type;
0041 };
0042 
0043 struct pismo_data {
0044     struct i2c_client   *client;
0045     void            (*vpp)(void *, int);
0046     void            *vpp_data;
0047     struct platform_device  *dev[PISMO_NUM_CS];
0048 };
0049 
0050 static void pismo_set_vpp(struct platform_device *pdev, int on)
0051 {
0052     struct i2c_client *client = to_i2c_client(pdev->dev.parent);
0053     struct pismo_data *pismo = i2c_get_clientdata(client);
0054 
0055     pismo->vpp(pismo->vpp_data, on);
0056 }
0057 
0058 static unsigned int pismo_width_to_bytes(unsigned int width)
0059 {
0060     width &= 15;
0061     if (width > 2)
0062         return 0;
0063     return 1 << width;
0064 }
0065 
0066 static int pismo_eeprom_read(struct i2c_client *client, void *buf, u8 addr,
0067                  size_t size)
0068 {
0069     int ret;
0070     struct i2c_msg msg[] = {
0071         {
0072             .addr = client->addr,
0073             .len = sizeof(addr),
0074             .buf = &addr,
0075         }, {
0076             .addr = client->addr,
0077             .flags = I2C_M_RD,
0078             .len = size,
0079             .buf = buf,
0080         },
0081     };
0082 
0083     ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
0084 
0085     return ret == ARRAY_SIZE(msg) ? size : -EIO;
0086 }
0087 
0088 static int pismo_add_device(struct pismo_data *pismo, int i,
0089                 struct pismo_mem *region, const char *name,
0090                 void *pdata, size_t psize)
0091 {
0092     struct platform_device *dev;
0093     struct resource res = { };
0094     phys_addr_t base = region->base;
0095     int ret;
0096 
0097     if (base == ~0)
0098         return -ENXIO;
0099 
0100     res.start = base;
0101     res.end = base + region->size - 1;
0102     res.flags = IORESOURCE_MEM;
0103 
0104     dev = platform_device_alloc(name, i);
0105     if (!dev)
0106         return -ENOMEM;
0107     dev->dev.parent = &pismo->client->dev;
0108 
0109     do {
0110         ret = platform_device_add_resources(dev, &res, 1);
0111         if (ret)
0112             break;
0113 
0114         ret = platform_device_add_data(dev, pdata, psize);
0115         if (ret)
0116             break;
0117 
0118         ret = platform_device_add(dev);
0119         if (ret)
0120             break;
0121 
0122         pismo->dev[i] = dev;
0123         return 0;
0124     } while (0);
0125 
0126     platform_device_put(dev);
0127     return ret;
0128 }
0129 
0130 static int pismo_add_nor(struct pismo_data *pismo, int i,
0131              struct pismo_mem *region)
0132 {
0133     struct physmap_flash_data data = {
0134         .width = region->width,
0135     };
0136 
0137     if (pismo->vpp)
0138         data.set_vpp = pismo_set_vpp;
0139 
0140     return pismo_add_device(pismo, i, region, "physmap-flash",
0141         &data, sizeof(data));
0142 }
0143 
0144 static int pismo_add_sram(struct pismo_data *pismo, int i,
0145               struct pismo_mem *region)
0146 {
0147     struct platdata_mtd_ram data = {
0148         .bankwidth = region->width,
0149     };
0150 
0151     return pismo_add_device(pismo, i, region, "mtd-ram",
0152         &data, sizeof(data));
0153 }
0154 
0155 static void pismo_add_one(struct pismo_data *pismo, int i,
0156               const struct pismo_cs_block *cs, phys_addr_t base)
0157 {
0158     struct device *dev = &pismo->client->dev;
0159     struct pismo_mem region;
0160 
0161     region.base = base;
0162     region.type = cs->type;
0163     region.width = pismo_width_to_bytes(cs->width);
0164     region.access = le16_to_cpu(cs->access);
0165     region.size = le32_to_cpu(cs->size);
0166 
0167     if (region.width == 0) {
0168         dev_err(dev, "cs%u: bad width: %02x, ignoring\n", i, cs->width);
0169         return;
0170     }
0171 
0172     /*
0173      * FIXME: may need to the platforms memory controller here, but at
0174      * the moment we assume that it has already been correctly setup.
0175      * The memory controller can also tell us the base address as well.
0176      */
0177 
0178     dev_info(dev, "cs%u: %.32s: type %02x access %u00ps size %uK\n",
0179         i, cs->device, region.type, region.access, region.size / 1024);
0180 
0181     switch (region.type) {
0182     case 0:
0183         break;
0184     case 1:
0185         /* static DOC */
0186         break;
0187     case 2:
0188         /* static NOR */
0189         pismo_add_nor(pismo, i, &region);
0190         break;
0191     case 3:
0192         /* static RAM */
0193         pismo_add_sram(pismo, i, &region);
0194         break;
0195     }
0196 }
0197 
0198 static int pismo_remove(struct i2c_client *client)
0199 {
0200     struct pismo_data *pismo = i2c_get_clientdata(client);
0201     int i;
0202 
0203     for (i = 0; i < ARRAY_SIZE(pismo->dev); i++)
0204         platform_device_unregister(pismo->dev[i]);
0205 
0206     kfree(pismo);
0207 
0208     return 0;
0209 }
0210 
0211 static int pismo_probe(struct i2c_client *client,
0212                const struct i2c_device_id *id)
0213 {
0214     struct pismo_pdata *pdata = client->dev.platform_data;
0215     struct pismo_eeprom eeprom;
0216     struct pismo_data *pismo;
0217     int ret, i;
0218 
0219     if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
0220         dev_err(&client->dev, "functionality mismatch\n");
0221         return -EIO;
0222     }
0223 
0224     pismo = kzalloc(sizeof(*pismo), GFP_KERNEL);
0225     if (!pismo)
0226         return -ENOMEM;
0227 
0228     pismo->client = client;
0229     if (pdata) {
0230         pismo->vpp = pdata->set_vpp;
0231         pismo->vpp_data = pdata->vpp_data;
0232     }
0233     i2c_set_clientdata(client, pismo);
0234 
0235     ret = pismo_eeprom_read(client, &eeprom, 0, sizeof(eeprom));
0236     if (ret < 0) {
0237         dev_err(&client->dev, "error reading EEPROM: %d\n", ret);
0238         goto exit_free;
0239     }
0240 
0241     dev_info(&client->dev, "%.15s board found\n", eeprom.board);
0242 
0243     for (i = 0; i < ARRAY_SIZE(eeprom.cs); i++)
0244         if (eeprom.cs[i].type != 0xff)
0245             pismo_add_one(pismo, i, &eeprom.cs[i],
0246                       pdata->cs_addrs[i]);
0247 
0248     return 0;
0249 
0250  exit_free:
0251     kfree(pismo);
0252     return ret;
0253 }
0254 
0255 static const struct i2c_device_id pismo_id[] = {
0256     { "pismo" },
0257     { },
0258 };
0259 MODULE_DEVICE_TABLE(i2c, pismo_id);
0260 
0261 static struct i2c_driver pismo_driver = {
0262     .driver = {
0263         .name   = "pismo",
0264     },
0265     .probe      = pismo_probe,
0266     .remove     = pismo_remove,
0267     .id_table   = pismo_id,
0268 };
0269 
0270 static int __init pismo_init(void)
0271 {
0272     BUILD_BUG_ON(sizeof(struct pismo_cs_block) != 48);
0273     BUILD_BUG_ON(sizeof(struct pismo_eeprom) != 256);
0274 
0275     return i2c_add_driver(&pismo_driver);
0276 }
0277 module_init(pismo_init);
0278 
0279 static void __exit pismo_exit(void)
0280 {
0281     i2c_del_driver(&pismo_driver);
0282 }
0283 module_exit(pismo_exit);
0284 
0285 MODULE_AUTHOR("Russell King <linux@arm.linux.org.uk>");
0286 MODULE_DESCRIPTION("PISMO memory driver");
0287 MODULE_LICENSE("GPL");