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