linux/drivers/mtd/maps/pismo.c
<<
>>
Prefs
   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, &region);
 190                break;
 191        case 3:
 192                /* static RAM */
 193                pismo_add_sram(pismo, i, &region);
 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");
 288