linux/drivers/mtd/devices/phram.c
<<
>>
Prefs
   1/**
   2 * Copyright (c) ????           Jochen Schäuble <psionic@psionic.de>
   3 * Copyright (c) 2003-2004      Joern Engel <joern@wh.fh-wedel.de>
   4 *
   5 * Usage:
   6 *
   7 * one commend line parameter per device, each in the form:
   8 *   phram=<name>,<start>,<len>
   9 * <name> may be up to 63 characters.
  10 * <start> and <len> can be octal, decimal or hexadecimal.  If followed
  11 * by "ki", "Mi" or "Gi", the numbers will be interpreted as kilo, mega or
  12 * gigabytes.
  13 *
  14 * Example:
  15 *      phram=swap,64Mi,128Mi phram=test,900Mi,1Mi
  16 */
  17
  18#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  19
  20#include <asm/io.h>
  21#include <linux/init.h>
  22#include <linux/kernel.h>
  23#include <linux/list.h>
  24#include <linux/module.h>
  25#include <linux/moduleparam.h>
  26#include <linux/slab.h>
  27#include <linux/mtd/mtd.h>
  28
  29struct phram_mtd_list {
  30        struct mtd_info mtd;
  31        struct list_head list;
  32};
  33
  34static LIST_HEAD(phram_list);
  35
  36
  37static int phram_erase(struct mtd_info *mtd, struct erase_info *instr)
  38{
  39        u_char *start = mtd->priv;
  40
  41        if (instr->addr + instr->len > mtd->size)
  42                return -EINVAL;
  43
  44        memset(start + instr->addr, 0xff, instr->len);
  45
  46        /* This'll catch a few races. Free the thing before returning :)
  47         * I don't feel at all ashamed. This kind of thing is possible anyway
  48         * with flash, but unlikely.
  49         */
  50
  51        instr->state = MTD_ERASE_DONE;
  52
  53        mtd_erase_callback(instr);
  54
  55        return 0;
  56}
  57
  58static int phram_point(struct mtd_info *mtd, loff_t from, size_t len,
  59                size_t *retlen, void **virt, resource_size_t *phys)
  60{
  61        if (from + len > mtd->size)
  62                return -EINVAL;
  63
  64        /* can we return a physical address with this driver? */
  65        if (phys)
  66                return -EINVAL;
  67
  68        *virt = mtd->priv + from;
  69        *retlen = len;
  70        return 0;
  71}
  72
  73static void phram_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
  74{
  75}
  76
  77static int phram_read(struct mtd_info *mtd, loff_t from, size_t len,
  78                size_t *retlen, u_char *buf)
  79{
  80        u_char *start = mtd->priv;
  81
  82        if (from >= mtd->size)
  83                return -EINVAL;
  84
  85        if (len > mtd->size - from)
  86                len = mtd->size - from;
  87
  88        memcpy(buf, start + from, len);
  89
  90        *retlen = len;
  91        return 0;
  92}
  93
  94static int phram_write(struct mtd_info *mtd, loff_t to, size_t len,
  95                size_t *retlen, const u_char *buf)
  96{
  97        u_char *start = mtd->priv;
  98
  99        if (to >= mtd->size)
 100                return -EINVAL;
 101
 102        if (len > mtd->size - to)
 103                len = mtd->size - to;
 104
 105        memcpy(start + to, buf, len);
 106
 107        *retlen = len;
 108        return 0;
 109}
 110
 111
 112
 113static void unregister_devices(void)
 114{
 115        struct phram_mtd_list *this, *safe;
 116
 117        list_for_each_entry_safe(this, safe, &phram_list, list) {
 118                mtd_device_unregister(&this->mtd);
 119                iounmap(this->mtd.priv);
 120                kfree(this->mtd.name);
 121                kfree(this);
 122        }
 123}
 124
 125static int register_device(char *name, unsigned long start, unsigned long len)
 126{
 127        struct phram_mtd_list *new;
 128        int ret = -ENOMEM;
 129
 130        new = kzalloc(sizeof(*new), GFP_KERNEL);
 131        if (!new)
 132                goto out0;
 133
 134        ret = -EIO;
 135        new->mtd.priv = ioremap(start, len);
 136        if (!new->mtd.priv) {
 137                pr_err("ioremap failed\n");
 138                goto out1;
 139        }
 140
 141
 142        new->mtd.name = name;
 143        new->mtd.size = len;
 144        new->mtd.flags = MTD_CAP_RAM;
 145        new->mtd.erase = phram_erase;
 146        new->mtd.point = phram_point;
 147        new->mtd.unpoint = phram_unpoint;
 148        new->mtd.read = phram_read;
 149        new->mtd.write = phram_write;
 150        new->mtd.owner = THIS_MODULE;
 151        new->mtd.type = MTD_RAM;
 152        new->mtd.erasesize = PAGE_SIZE;
 153        new->mtd.writesize = 1;
 154
 155        ret = -EAGAIN;
 156        if (mtd_device_register(&new->mtd, NULL, 0)) {
 157                pr_err("Failed to register new device\n");
 158                goto out2;
 159        }
 160
 161        list_add_tail(&new->list, &phram_list);
 162        return 0;
 163
 164out2:
 165        iounmap(new->mtd.priv);
 166out1:
 167        kfree(new);
 168out0:
 169        return ret;
 170}
 171
 172static int ustrtoul(const char *cp, char **endp, unsigned int base)
 173{
 174        unsigned long result = simple_strtoul(cp, endp, base);
 175
 176        switch (**endp) {
 177        case 'G':
 178                result *= 1024;
 179        case 'M':
 180                result *= 1024;
 181        case 'k':
 182                result *= 1024;
 183        /* By dwmw2 editorial decree, "ki", "Mi" or "Gi" are to be used. */
 184                if ((*endp)[1] == 'i')
 185                        (*endp) += 2;
 186        }
 187        return result;
 188}
 189
 190static int parse_num32(uint32_t *num32, const char *token)
 191{
 192        char *endp;
 193        unsigned long n;
 194
 195        n = ustrtoul(token, &endp, 0);
 196        if (*endp)
 197                return -EINVAL;
 198
 199        *num32 = n;
 200        return 0;
 201}
 202
 203static int parse_name(char **pname, const char *token)
 204{
 205        size_t len;
 206        char *name;
 207
 208        len = strlen(token) + 1;
 209        if (len > 64)
 210                return -ENOSPC;
 211
 212        name = kmalloc(len, GFP_KERNEL);
 213        if (!name)
 214                return -ENOMEM;
 215
 216        strcpy(name, token);
 217
 218        *pname = name;
 219        return 0;
 220}
 221
 222
 223static inline void kill_final_newline(char *str)
 224{
 225        char *newline = strrchr(str, '\n');
 226        if (newline && !newline[1])
 227                *newline = 0;
 228}
 229
 230
 231#define parse_err(fmt, args...) do {    \
 232        pr_err(fmt , ## args);  \
 233        return 1;               \
 234} while (0)
 235
 236static int phram_setup(const char *val, struct kernel_param *kp)
 237{
 238        char buf[64+12+12], *str = buf;
 239        char *token[3];
 240        char *name;
 241        uint32_t start;
 242        uint32_t len;
 243        int i, ret;
 244
 245        if (strnlen(val, sizeof(buf)) >= sizeof(buf))
 246                parse_err("parameter too long\n");
 247
 248        strcpy(str, val);
 249        kill_final_newline(str);
 250
 251        for (i=0; i<3; i++)
 252                token[i] = strsep(&str, ",");
 253
 254        if (str)
 255                parse_err("too many arguments\n");
 256
 257        if (!token[2])
 258                parse_err("not enough arguments\n");
 259
 260        ret = parse_name(&name, token[0]);
 261        if (ret)
 262                return ret;
 263
 264        ret = parse_num32(&start, token[1]);
 265        if (ret) {
 266                kfree(name);
 267                parse_err("illegal start address\n");
 268        }
 269
 270        ret = parse_num32(&len, token[2]);
 271        if (ret) {
 272                kfree(name);
 273                parse_err("illegal device length\n");
 274        }
 275
 276        ret = register_device(name, start, len);
 277        if (!ret)
 278                pr_info("%s device: %#x at %#x\n", name, len, start);
 279        else
 280                kfree(name);
 281
 282        return ret;
 283}
 284
 285module_param_call(phram, phram_setup, NULL, NULL, 000);
 286MODULE_PARM_DESC(phram, "Memory region to map. \"phram=<name>,<start>,<length>\"");
 287
 288
 289static int __init init_phram(void)
 290{
 291        return 0;
 292}
 293
 294static void __exit cleanup_phram(void)
 295{
 296        unregister_devices();
 297}
 298
 299module_init(init_phram);
 300module_exit(cleanup_phram);
 301
 302MODULE_LICENSE("GPL");
 303MODULE_AUTHOR("Joern Engel <joern@wh.fh-wedel.de>");
 304MODULE_DESCRIPTION("MTD driver for physical RAM");
 305