mtd/drivers/mtd/chips cfi_cmdset_0001.c,1.144,1.145
Nicolas Pitre
nico at infradead.org
Thu Jun 10 16:54:59 EDT 2004
Update of /home/cvs/mtd/drivers/mtd/chips
In directory phoenix.infradead.org:/tmp/cvs-serv32670/drivers/mtd/chips
Modified Files:
cfi_cmdset_0001.c
Log Message:
Second attempt at supporting the multipartition L18 Intel flash chips.
Again, the non-partitioned case should not have its logic altered so this
is not supposed to break existing users. The partition support code could
benefit from extra review though.
Index: cfi_cmdset_0001.c
===================================================================
RCS file: /home/cvs/mtd/drivers/mtd/chips/cfi_cmdset_0001.c,v
retrieving revision 1.144
retrieving revision 1.145
diff -u -r1.144 -r1.145
--- cfi_cmdset_0001.c 9 Jun 2004 19:34:04 -0000 1.144
+++ cfi_cmdset_0001.c 10 Jun 2004 20:54:55 -0000 1.145
@@ -56,6 +56,7 @@
struct mtd_info *cfi_cmdset_0001(struct map_info *, int);
static struct mtd_info *cfi_intelext_setup (struct map_info *);
+static int cfi_intelext_partition_fixup(struct map_info *, struct cfi_private **);
static int cfi_intelext_point (struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char **mtdbuf);
@@ -311,6 +312,12 @@
mtd->flags = MTD_CAP_NORFLASH;
map->fldrv = &cfi_intelext_chipdrv;
mtd->name = map->name;
+
+ /* This function has the potential to distort the reality
+ a bit and therefore should be called last. */
+ if (cfi_intelext_partition_fixup(map, &cfi) != 0)
+ goto setup_err;
+
__module_get(THIS_MODULE);
return mtd;
@@ -324,6 +331,79 @@
return NULL;
}
+static int cfi_intelext_partition_fixup(struct map_info *map,
+ struct cfi_private **pcfi)
+{
+ struct cfi_private *cfi = *pcfi;
+ struct cfi_pri_intelext *extp = cfi->cmdset_priv;
+
+ /*
+ * Probing of multi-partition flash ships.
+ *
+ * This is extremely crude at the moment and should probably be
+ * extracted entirely from the Intel extended query data instead.
+ * Right now a L18 flash is assumed if multiple operations is
+ * detected.
+ *
+ * To support multiple partitions when available, we simply arrange
+ * for each of them to have their own flchip structure even if they
+ * are on the same physical chip. This means completely recreating
+ * a new cfi_private structure right here which is a blatent code
+ * layering violation, but this is still the least intrusive
+ * arrangement at this point. This can be rearranged in the future
+ * if someone feels motivated enough. --nico
+ */
+ if (extp && extp->FeatureSupport & (1 << 9)) {
+ struct cfi_private *newcfi;
+ struct flchip *chip;
+ struct flchip_shared *shared;
+ int numparts, partshift, numvirtchips, i, j;
+
+ /*
+ * The L18 flash memory array is divided
+ * into multiple 8-Mbit partitions.
+ */
+ numparts = 1 << (cfi->cfiq->DevSize - 20);
+ partshift = 20 + __ffs(cfi->interleave);
+ numvirtchips = cfi->numchips * numparts;
+
+ newcfi = kmalloc(sizeof(struct cfi_private) + numvirtchips * sizeof(struct flchip), GFP_KERNEL);
+ if (!newcfi)
+ return -ENOMEM;
+ shared = kmalloc(sizeof(struct flchip_shared) * cfi->numchips, GFP_KERNEL);
+ if (!shared) {
+ kfree(newcfi);
+ return -ENOMEM;
+ }
+ memcpy(newcfi, cfi, sizeof(struct cfi_private));
+ newcfi->numchips = numvirtchips;
+ newcfi->chipshift = partshift;
+
+ chip = &newcfi->chips[0];
+ for (i = 0; i < cfi->numchips; i++) {
+ shared[i].writing = shared[i].erasing = NULL;
+ spin_lock_init(&shared[i].lock);
+ for (j = 0; j < numparts; j++) {
+ *chip = cfi->chips[i];
+ chip->start += j << partshift;
+ chip->priv = &shared[i];
+ /* those should be reset too since
+ they create memory references. */
+ init_waitqueue_head(&chip->wq);
+ spin_lock_init(&chip->_spinlock);
+ chip->mutex = &chip->_spinlock;
+ chip++;
+ }
+ }
+
+ map->fldrv_priv = newcfi;
+ *pcfi = newcfi;
+ kfree(cfi);
+ }
+
+ return 0;
+}
+
/*
* *********** CHIP ACCESS FUNCTIONS ***********
*/
@@ -334,11 +414,71 @@
struct cfi_private *cfi = map->fldrv_priv;
cfi_word status, status_OK = CMD(0x80);
unsigned long timeo;
- struct cfi_pri_intelext *cfip = (struct cfi_pri_intelext *)cfi->cmdset_priv;
+ struct cfi_pri_intelext *cfip = cfi->cmdset_priv;
resettime:
timeo = jiffies + HZ;
retry:
+ if (chip->priv && (mode == FL_WRITING || mode == FL_ERASING)) {
+ /*
+ * OK. We have possibility for contension on the write/erase
+ * operations which are global to the real chip and not per
+ * partition. So let's fight it over in the partition which
+ * currently has authority on the operation.
+ *
+ * The rules are as follows:
+ *
+ * - any write operation must own shared->writing.
+ *
+ * - any erase operation must own _both_ shared->writing and
+ * shared->erasing.
+ *
+ * - contension arbitration is handled in the owner's context.
+ *
+ * The 'shared' struct can be read when its lock is taken.
+ * However any writes to it can only be made when the current
+ * owner's lock is also held.
+ */
+ struct flchip_shared *shared = chip->priv;
+ struct flchip *contender;
+ spin_lock(&shared->lock);
+ contender = shared->writing;
+ if (mode == FL_ERASING && shared->erasing)
+ contender = shared->erasing;
+ if (contender && contender != chip) {
+ /*
+ * The engine to perform desired operation on this
+ * partition is already in use by someone else.
+ * Let's fight over it in the context of the chip
+ * currently using it. If it is possible to suspend,
+ * that other partition will do just that, otherwise
+ * it'll happily send us to sleep. In any case, when
+ * get_chip returns success we're clear to go ahead.
+ */
+ int ret = spin_trylock(contender->mutex);
+ spin_unlock(&shared->lock);
+ if (!ret)
+ goto retry;
+ spin_unlock(chip->mutex);
+ ret = get_chip(map, contender, contender->start, mode);
+ spin_lock(chip->mutex);
+ if (ret) {
+ spin_unlock(contender->mutex);
+ return ret;
+ }
+ timeo = jiffies + HZ;
+ spin_lock(&shared->lock);
+ }
+
+ /* We now own it */
+ shared->writing = chip;
+ if (mode == FL_ERASING)
+ shared->erasing = chip;
+ if (contender && contender != chip)
+ spin_unlock(contender->mutex);
+ spin_unlock(&shared->lock);
+ }
+
switch (chip->state) {
case FL_STATUS:
@@ -431,6 +571,24 @@
{
struct cfi_private *cfi = map->fldrv_priv;
+ if (chip->priv) {
+ struct flchip_shared *shared = chip->priv;
+ spin_lock(&shared->lock);
+ if (shared->writing == chip) {
+ /* We own the ability to write, but we're done */
+ shared->writing = shared->erasing;
+ if (shared->writing && shared->writing != chip) {
+ /* give back ownership to who we loaned it from */
+ struct flchip *loaner = shared->writing;
+ spin_lock(loaner->mutex);
+ spin_unlock(&shared->lock);
+ put_chip(map, loaner, loaner->start);
+ } else {
+ spin_unlock(&shared->lock);
+ }
+ }
+ }
+
switch(chip->oldstate) {
case FL_ERASING:
chip->state = chip->oldstate;
@@ -1697,6 +1855,7 @@
struct cfi_private *cfi = map->fldrv_priv;
kfree(cfi->cmdset_priv);
kfree(cfi->cfiq);
+ kfree(cfi->chips[0].priv);
kfree(cfi);
kfree(mtd->eraseregions);
}
More information about the linux-mtd-cvs
mailing list