diff options
author | Kevin Cernekee <cernekee@gmail.com> | 2010-04-29 13:26:56 -0400 |
---|---|---|
committer | David Woodhouse <David.Woodhouse@intel.com> | 2010-05-10 09:25:12 -0400 |
commit | eafe1311aa3cdb13efa25c60251bce12e60ae38a (patch) | |
tree | ee8fa168dd311fda3cb1ae067f346a2906e73a7f /drivers | |
parent | bff3c10d369440bc87ba612b45ba2777d2bf017f (diff) |
mtd: cfi_cmdset_0002: Add reboot notifier for AMD flashes
Ensure that the flash device is in a quiescent state before rebooting.
The implementation is closely modeled after the cfi_cmdset_0001 reboot
notifier, commit 963a6fb0a0d336d0513083b7e4b5c3ff9d6d2061 .
Signed-off-by: Kevin Cernekee <cernekee@gmail.com>
Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mtd/chips/cfi_cmdset_0002.c | 56 |
1 files changed, 56 insertions, 0 deletions
diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index c93e47d21ce0..c16b8cecc3a8 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c | |||
@@ -32,6 +32,7 @@ | |||
32 | #include <linux/slab.h> | 32 | #include <linux/slab.h> |
33 | #include <linux/delay.h> | 33 | #include <linux/delay.h> |
34 | #include <linux/interrupt.h> | 34 | #include <linux/interrupt.h> |
35 | #include <linux/reboot.h> | ||
35 | #include <linux/mtd/compatmac.h> | 36 | #include <linux/mtd/compatmac.h> |
36 | #include <linux/mtd/map.h> | 37 | #include <linux/mtd/map.h> |
37 | #include <linux/mtd/mtd.h> | 38 | #include <linux/mtd/mtd.h> |
@@ -56,6 +57,7 @@ static int cfi_amdstd_erase_varsize(struct mtd_info *, struct erase_info *); | |||
56 | static void cfi_amdstd_sync (struct mtd_info *); | 57 | static void cfi_amdstd_sync (struct mtd_info *); |
57 | static int cfi_amdstd_suspend (struct mtd_info *); | 58 | static int cfi_amdstd_suspend (struct mtd_info *); |
58 | static void cfi_amdstd_resume (struct mtd_info *); | 59 | static void cfi_amdstd_resume (struct mtd_info *); |
60 | static int cfi_amdstd_reboot(struct notifier_block *, unsigned long, void *); | ||
59 | static int cfi_amdstd_secsi_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); | 61 | static int cfi_amdstd_secsi_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); |
60 | 62 | ||
61 | static void cfi_amdstd_destroy(struct mtd_info *); | 63 | static void cfi_amdstd_destroy(struct mtd_info *); |
@@ -351,6 +353,8 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) | |||
351 | mtd->name = map->name; | 353 | mtd->name = map->name; |
352 | mtd->writesize = 1; | 354 | mtd->writesize = 1; |
353 | 355 | ||
356 | mtd->reboot_notifier.notifier_call = cfi_amdstd_reboot; | ||
357 | |||
354 | if (cfi->cfi_mode==CFI_MODE_CFI){ | 358 | if (cfi->cfi_mode==CFI_MODE_CFI){ |
355 | unsigned char bootloc; | 359 | unsigned char bootloc; |
356 | /* | 360 | /* |
@@ -487,6 +491,7 @@ static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd) | |||
487 | #endif | 491 | #endif |
488 | 492 | ||
489 | __module_get(THIS_MODULE); | 493 | __module_get(THIS_MODULE); |
494 | register_reboot_notifier(&mtd->reboot_notifier); | ||
490 | return mtd; | 495 | return mtd; |
491 | 496 | ||
492 | setup_err: | 497 | setup_err: |
@@ -628,6 +633,10 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr | |||
628 | chip->state = FL_READY; | 633 | chip->state = FL_READY; |
629 | return 0; | 634 | return 0; |
630 | 635 | ||
636 | case FL_SHUTDOWN: | ||
637 | /* The machine is rebooting */ | ||
638 | return -EIO; | ||
639 | |||
631 | case FL_POINT: | 640 | case FL_POINT: |
632 | /* Only if there's no operation suspended... */ | 641 | /* Only if there's no operation suspended... */ |
633 | if (mode == FL_READY && chip->oldstate == FL_READY) | 642 | if (mode == FL_READY && chip->oldstate == FL_READY) |
@@ -1918,11 +1927,58 @@ static void cfi_amdstd_resume(struct mtd_info *mtd) | |||
1918 | } | 1927 | } |
1919 | } | 1928 | } |
1920 | 1929 | ||
1930 | |||
1931 | /* | ||
1932 | * Ensure that the flash device is put back into read array mode before | ||
1933 | * unloading the driver or rebooting. On some systems, rebooting while | ||
1934 | * the flash is in query/program/erase mode will prevent the CPU from | ||
1935 | * fetching the bootloader code, requiring a hard reset or power cycle. | ||
1936 | */ | ||
1937 | static int cfi_amdstd_reset(struct mtd_info *mtd) | ||
1938 | { | ||
1939 | struct map_info *map = mtd->priv; | ||
1940 | struct cfi_private *cfi = map->fldrv_priv; | ||
1941 | int i, ret; | ||
1942 | struct flchip *chip; | ||
1943 | |||
1944 | for (i = 0; i < cfi->numchips; i++) { | ||
1945 | |||
1946 | chip = &cfi->chips[i]; | ||
1947 | |||
1948 | mutex_lock(&chip->mutex); | ||
1949 | |||
1950 | ret = get_chip(map, chip, chip->start, FL_SHUTDOWN); | ||
1951 | if (!ret) { | ||
1952 | map_write(map, CMD(0xF0), chip->start); | ||
1953 | chip->state = FL_SHUTDOWN; | ||
1954 | put_chip(map, chip, chip->start); | ||
1955 | } | ||
1956 | |||
1957 | mutex_unlock(&chip->mutex); | ||
1958 | } | ||
1959 | |||
1960 | return 0; | ||
1961 | } | ||
1962 | |||
1963 | |||
1964 | static int cfi_amdstd_reboot(struct notifier_block *nb, unsigned long val, | ||
1965 | void *v) | ||
1966 | { | ||
1967 | struct mtd_info *mtd; | ||
1968 | |||
1969 | mtd = container_of(nb, struct mtd_info, reboot_notifier); | ||
1970 | cfi_amdstd_reset(mtd); | ||
1971 | return NOTIFY_DONE; | ||
1972 | } | ||
1973 | |||
1974 | |||
1921 | static void cfi_amdstd_destroy(struct mtd_info *mtd) | 1975 | static void cfi_amdstd_destroy(struct mtd_info *mtd) |
1922 | { | 1976 | { |
1923 | struct map_info *map = mtd->priv; | 1977 | struct map_info *map = mtd->priv; |
1924 | struct cfi_private *cfi = map->fldrv_priv; | 1978 | struct cfi_private *cfi = map->fldrv_priv; |
1925 | 1979 | ||
1980 | cfi_amdstd_reset(mtd); | ||
1981 | unregister_reboot_notifier(&mtd->reboot_notifier); | ||
1926 | kfree(cfi->cmdset_priv); | 1982 | kfree(cfi->cmdset_priv); |
1927 | kfree(cfi->cfiq); | 1983 | kfree(cfi->cfiq); |
1928 | kfree(cfi); | 1984 | kfree(cfi); |