Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
linux
Commits
b7aa48be
Commit
b7aa48be
authored
May 09, 2007
by
David Woodhouse
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[MTD] [CHIPS] Remove MTD_OBSOLETE_CHIPS (jedec, amd_flash, sharp)
Signed-off-by:
David Woodhouse
<
dwmw2@infradead.org
>
parent
42f209d3
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
0 additions
and
2976 deletions
+0
-2976
drivers/mtd/chips/Kconfig
drivers/mtd/chips/Kconfig
+0
-40
drivers/mtd/chips/Makefile
drivers/mtd/chips/Makefile
+0
-4
drivers/mtd/chips/amd_flash.c
drivers/mtd/chips/amd_flash.c
+0
-1396
drivers/mtd/chips/jedec.c
drivers/mtd/chips/jedec.c
+0
-935
drivers/mtd/chips/sharp.c
drivers/mtd/chips/sharp.c
+0
-601
No files found.
drivers/mtd/chips/Kconfig
View file @
b7aa48be
# drivers/mtd/chips/Kconfig
# drivers/mtd/chips/Kconfig
# $Id: Kconfig,v 1.18 2005/11/07 11:14:22 gleixner Exp $
menu "RAM/ROM/Flash chip drivers"
menu "RAM/ROM/Flash chip drivers"
depends on MTD!=n
depends on MTD!=n
...
@@ -231,45 +230,6 @@ config MTD_ABSENT
...
@@ -231,45 +230,6 @@ config MTD_ABSENT
the system regardless of media presence. Device nodes created
the system regardless of media presence. Device nodes created
with this driver will return -ENODEV upon access.
with this driver will return -ENODEV upon access.
config MTD_OBSOLETE_CHIPS
bool "Older (theoretically obsoleted now) drivers for non-CFI chips"
help
This option does not enable any code directly, but will allow you to
select some other chip drivers which are now considered obsolete,
because the generic CONFIG_JEDECPROBE code above should now detect
the chips which are supported by these drivers, and allow the generic
CFI-compatible drivers to drive the chips. Say 'N' here unless you have
already tried the CONFIG_JEDECPROBE method and reported its failure
to the MTD mailing list at <linux-mtd@lists.infradead.org>
config MTD_AMDSTD
tristate "AMD compatible flash chip support (non-CFI)"
depends on MTD_OBSOLETE_CHIPS && BROKEN
help
This option enables support for flash chips using AMD-compatible
commands, including some which are not CFI-compatible and hence
cannot be used with the CONFIG_MTD_CFI_AMDSTD option.
It also works on AMD compatible chips that do conform to CFI.
config MTD_SHARP
tristate "pre-CFI Sharp chip support"
depends on MTD_OBSOLETE_CHIPS
help
This option enables support for flash chips using Sharp-compatible
commands, including some which are not CFI-compatible and hence
cannot be used with the CONFIG_MTD_CFI_INTELxxx options.
config MTD_JEDEC
tristate "JEDEC device support"
depends on MTD_OBSOLETE_CHIPS && BROKEN
help
Enable older JEDEC flash interface devices for self
programming flash. It is commonly used in older AMD chips. It is
only called JEDEC because the JEDEC association
<http://www.jedec.org/> distributes the identification codes for the
chips.
config MTD_XIP
config MTD_XIP
bool "XIP aware MTD support"
bool "XIP aware MTD support"
depends on !SMP && (MTD_CFI_INTELEXT || MTD_CFI_AMDSTD) && EXPERIMENTAL && ARCH_MTD_XIP
depends on !SMP && (MTD_CFI_INTELEXT || MTD_CFI_AMDSTD) && EXPERIMENTAL && ARCH_MTD_XIP
...
...
drivers/mtd/chips/Makefile
View file @
b7aa48be
#
#
# linux/drivers/chips/Makefile
# linux/drivers/chips/Makefile
#
#
# $Id: Makefile.common,v 1.5 2005/11/07 11:14:22 gleixner Exp $
obj-$(CONFIG_MTD)
+=
chipreg.o
obj-$(CONFIG_MTD)
+=
chipreg.o
obj-$(CONFIG_MTD_AMDSTD)
+=
amd_flash.o
obj-$(CONFIG_MTD_CFI)
+=
cfi_probe.o
obj-$(CONFIG_MTD_CFI)
+=
cfi_probe.o
obj-$(CONFIG_MTD_CFI_UTIL)
+=
cfi_util.o
obj-$(CONFIG_MTD_CFI_UTIL)
+=
cfi_util.o
obj-$(CONFIG_MTD_CFI_STAA)
+=
cfi_cmdset_0020.o
obj-$(CONFIG_MTD_CFI_STAA)
+=
cfi_cmdset_0020.o
obj-$(CONFIG_MTD_CFI_AMDSTD)
+=
cfi_cmdset_0002.o
obj-$(CONFIG_MTD_CFI_AMDSTD)
+=
cfi_cmdset_0002.o
obj-$(CONFIG_MTD_CFI_INTELEXT)
+=
cfi_cmdset_0001.o
obj-$(CONFIG_MTD_CFI_INTELEXT)
+=
cfi_cmdset_0001.o
obj-$(CONFIG_MTD_GEN_PROBE)
+=
gen_probe.o
obj-$(CONFIG_MTD_GEN_PROBE)
+=
gen_probe.o
obj-$(CONFIG_MTD_JEDEC)
+=
jedec.o
obj-$(CONFIG_MTD_JEDECPROBE)
+=
jedec_probe.o
obj-$(CONFIG_MTD_JEDECPROBE)
+=
jedec_probe.o
obj-$(CONFIG_MTD_RAM)
+=
map_ram.o
obj-$(CONFIG_MTD_RAM)
+=
map_ram.o
obj-$(CONFIG_MTD_ROM)
+=
map_rom.o
obj-$(CONFIG_MTD_ROM)
+=
map_rom.o
obj-$(CONFIG_MTD_SHARP)
+=
sharp.o
obj-$(CONFIG_MTD_ABSENT)
+=
map_absent.o
obj-$(CONFIG_MTD_ABSENT)
+=
map_absent.o
drivers/mtd/chips/amd_flash.c
deleted
100644 → 0
View file @
42f209d3
/*
* MTD map driver for AMD compatible flash chips (non-CFI)
*
* Author: Jonas Holmberg <jonas.holmberg@axis.com>
*
* $Id: amd_flash.c,v 1.28 2005/11/07 11:14:22 gleixner Exp $
*
* Copyright (c) 2001 Axis Communications AB
*
* This file is under GPL.
*
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/mtd/map.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/flashchip.h>
/* There's no limit. It exists only to avoid realloc. */
#define MAX_AMD_CHIPS 8
#define DEVICE_TYPE_X8 (8 / 8)
#define DEVICE_TYPE_X16 (16 / 8)
#define DEVICE_TYPE_X32 (32 / 8)
/* Addresses */
#define ADDR_MANUFACTURER 0x0000
#define ADDR_DEVICE_ID 0x0001
#define ADDR_SECTOR_LOCK 0x0002
#define ADDR_HANDSHAKE 0x0003
#define ADDR_UNLOCK_1 0x0555
#define ADDR_UNLOCK_2 0x02AA
/* Commands */
#define CMD_UNLOCK_DATA_1 0x00AA
#define CMD_UNLOCK_DATA_2 0x0055
#define CMD_MANUFACTURER_UNLOCK_DATA 0x0090
#define CMD_UNLOCK_BYPASS_MODE 0x0020
#define CMD_PROGRAM_UNLOCK_DATA 0x00A0
#define CMD_RESET_DATA 0x00F0
#define CMD_SECTOR_ERASE_UNLOCK_DATA 0x0080
#define CMD_SECTOR_ERASE_UNLOCK_DATA_2 0x0030
#define CMD_UNLOCK_SECTOR 0x0060
/* Manufacturers */
#define MANUFACTURER_AMD 0x0001
#define MANUFACTURER_ATMEL 0x001F
#define MANUFACTURER_FUJITSU 0x0004
#define MANUFACTURER_ST 0x0020
#define MANUFACTURER_SST 0x00BF
#define MANUFACTURER_TOSHIBA 0x0098
/* AMD */
#define AM29F800BB 0x2258
#define AM29F800BT 0x22D6
#define AM29LV800BB 0x225B
#define AM29LV800BT 0x22DA
#define AM29LV160DT 0x22C4
#define AM29LV160DB 0x2249
#define AM29BDS323D 0x22D1
/* Atmel */
#define AT49xV16x 0x00C0
#define AT49xV16xT 0x00C2
/* Fujitsu */
#define MBM29LV160TE 0x22C4
#define MBM29LV160BE 0x2249
#define MBM29LV800BB 0x225B
/* ST - www.st.com */
#define M29W800T 0x00D7
#define M29W160DT 0x22C4
#define M29W160DB 0x2249
/* SST */
#define SST39LF800 0x2781
#define SST39LF160 0x2782
/* Toshiba */
#define TC58FVT160 0x00C2
#define TC58FVB160 0x0043
#define D6_MASK 0x40
struct
amd_flash_private
{
int
device_type
;
int
interleave
;
int
numchips
;
unsigned
long
chipshift
;
struct
flchip
chips
[
0
];
};
struct
amd_flash_info
{
const
__u16
mfr_id
;
const
__u16
dev_id
;
const
char
*
name
;
const
u_long
size
;
const
int
numeraseregions
;
const
struct
mtd_erase_region_info
regions
[
4
];
};
static
int
amd_flash_read
(
struct
mtd_info
*
,
loff_t
,
size_t
,
size_t
*
,
u_char
*
);
static
int
amd_flash_write
(
struct
mtd_info
*
,
loff_t
,
size_t
,
size_t
*
,
const
u_char
*
);
static
int
amd_flash_erase
(
struct
mtd_info
*
,
struct
erase_info
*
);
static
void
amd_flash_sync
(
struct
mtd_info
*
);
static
int
amd_flash_suspend
(
struct
mtd_info
*
);
static
void
amd_flash_resume
(
struct
mtd_info
*
);
static
void
amd_flash_destroy
(
struct
mtd_info
*
);
static
struct
mtd_info
*
amd_flash_probe
(
struct
map_info
*
map
);
static
struct
mtd_chip_driver
amd_flash_chipdrv
=
{
.
probe
=
amd_flash_probe
,
.
destroy
=
amd_flash_destroy
,
.
name
=
"amd_flash"
,
.
module
=
THIS_MODULE
};
static
inline
__u32
wide_read
(
struct
map_info
*
map
,
__u32
addr
)
{
if
(
map
->
buswidth
==
1
)
{
return
map_read8
(
map
,
addr
);
}
else
if
(
map
->
buswidth
==
2
)
{
return
map_read16
(
map
,
addr
);
}
else
if
(
map
->
buswidth
==
4
)
{
return
map_read32
(
map
,
addr
);
}
return
0
;
}
static
inline
void
wide_write
(
struct
map_info
*
map
,
__u32
val
,
__u32
addr
)
{
if
(
map
->
buswidth
==
1
)
{
map_write8
(
map
,
val
,
addr
);
}
else
if
(
map
->
buswidth
==
2
)
{
map_write16
(
map
,
val
,
addr
);
}
else
if
(
map
->
buswidth
==
4
)
{
map_write32
(
map
,
val
,
addr
);
}
}
static
inline
__u32
make_cmd
(
struct
map_info
*
map
,
__u32
cmd
)
{
const
struct
amd_flash_private
*
private
=
map
->
fldrv_priv
;
if
((
private
->
interleave
==
2
)
&&
(
private
->
device_type
==
DEVICE_TYPE_X16
))
{
cmd
|=
(
cmd
<<
16
);
}
return
cmd
;
}
static
inline
void
send_unlock
(
struct
map_info
*
map
,
unsigned
long
base
)
{
wide_write
(
map
,
(
CMD_UNLOCK_DATA_1
<<
16
)
|
CMD_UNLOCK_DATA_1
,
base
+
(
map
->
buswidth
*
ADDR_UNLOCK_1
));
wide_write
(
map
,
(
CMD_UNLOCK_DATA_2
<<
16
)
|
CMD_UNLOCK_DATA_2
,
base
+
(
map
->
buswidth
*
ADDR_UNLOCK_2
));
}
static
inline
void
send_cmd
(
struct
map_info
*
map
,
unsigned
long
base
,
__u32
cmd
)
{
send_unlock
(
map
,
base
);
wide_write
(
map
,
make_cmd
(
map
,
cmd
),
base
+
(
map
->
buswidth
*
ADDR_UNLOCK_1
));
}
static
inline
void
send_cmd_to_addr
(
struct
map_info
*
map
,
unsigned
long
base
,
__u32
cmd
,
unsigned
long
addr
)
{
send_unlock
(
map
,
base
);
wide_write
(
map
,
make_cmd
(
map
,
cmd
),
addr
);
}
static
inline
int
flash_is_busy
(
struct
map_info
*
map
,
unsigned
long
addr
,
int
interleave
)
{
if
((
interleave
==
2
)
&&
(
map
->
buswidth
==
4
))
{
__u32
read1
,
read2
;
read1
=
wide_read
(
map
,
addr
);
read2
=
wide_read
(
map
,
addr
);
return
(((
read1
>>
16
)
&
D6_MASK
)
!=
((
read2
>>
16
)
&
D6_MASK
))
||
(((
read1
&
0xffff
)
&
D6_MASK
)
!=
((
read2
&
0xffff
)
&
D6_MASK
));
}
return
((
wide_read
(
map
,
addr
)
&
D6_MASK
)
!=
(
wide_read
(
map
,
addr
)
&
D6_MASK
));
}
static
inline
void
unlock_sector
(
struct
map_info
*
map
,
unsigned
long
sect_addr
,
int
unlock
)
{
/* Sector lock address. A6 = 1 for unlock, A6 = 0 for lock */
int
SLA
=
unlock
?
(
sect_addr
|
(
0x40
*
map
->
buswidth
))
:
(
sect_addr
&
~
(
0x40
*
map
->
buswidth
))
;
__u32
cmd
=
make_cmd
(
map
,
CMD_UNLOCK_SECTOR
);
wide_write
(
map
,
make_cmd
(
map
,
CMD_RESET_DATA
),
0
);
wide_write
(
map
,
cmd
,
SLA
);
/* 1st cycle: write cmd to any address */
wide_write
(
map
,
cmd
,
SLA
);
/* 2nd cycle: write cmd to any address */
wide_write
(
map
,
cmd
,
SLA
);
/* 3rd cycle: write cmd to SLA */
}
static
inline
int
is_sector_locked
(
struct
map_info
*
map
,
unsigned
long
sect_addr
)
{
int
status
;
wide_write
(
map
,
CMD_RESET_DATA
,
0
);
send_cmd
(
map
,
sect_addr
,
CMD_MANUFACTURER_UNLOCK_DATA
);
/* status is 0x0000 for unlocked and 0x0001 for locked */
status
=
wide_read
(
map
,
sect_addr
+
(
map
->
buswidth
*
ADDR_SECTOR_LOCK
));
wide_write
(
map
,
CMD_RESET_DATA
,
0
);
return
status
;
}
static
int
amd_flash_do_unlock
(
struct
mtd_info
*
mtd
,
loff_t
ofs
,
size_t
len
,
int
is_unlock
)
{
struct
map_info
*
map
;
struct
mtd_erase_region_info
*
merip
;
int
eraseoffset
,
erasesize
,
eraseblocks
;
int
i
;
int
retval
=
0
;
int
lock_status
;
map
=
mtd
->
priv
;
/* Pass the whole chip through sector by sector and check for each
sector if the sector and the given interval overlap */
for
(
i
=
0
;
i
<
mtd
->
numeraseregions
;
i
++
)
{
merip
=
&
mtd
->
eraseregions
[
i
];
eraseoffset
=
merip
->
offset
;
erasesize
=
merip
->
erasesize
;
eraseblocks
=
merip
->
numblocks
;
if
(
ofs
>
eraseoffset
+
erasesize
)
continue
;
while
(
eraseblocks
>
0
)
{
if
(
ofs
<
eraseoffset
+
erasesize
&&
ofs
+
len
>
eraseoffset
)
{
unlock_sector
(
map
,
eraseoffset
,
is_unlock
);
lock_status
=
is_sector_locked
(
map
,
eraseoffset
);
if
(
is_unlock
&&
lock_status
)
{
printk
(
"Cannot unlock sector at address %x length %xx
\n
"
,
eraseoffset
,
merip
->
erasesize
);
retval
=
-
1
;
}
else
if
(
!
is_unlock
&&
!
lock_status
)
{
printk
(
"Cannot lock sector at address %x length %x
\n
"
,
eraseoffset
,
merip
->
erasesize
);
retval
=
-
1
;
}
}
eraseoffset
+=
erasesize
;
eraseblocks
--
;
}
}
return
retval
;
}
static
int
amd_flash_unlock
(
struct
mtd_info
*
mtd
,
loff_t
ofs
,
size_t
len
)
{
return
amd_flash_do_unlock
(
mtd
,
ofs
,
len
,
1
);
}
static
int
amd_flash_lock
(
struct
mtd_info
*
mtd
,
loff_t
ofs
,
size_t
len
)
{
return
amd_flash_do_unlock
(
mtd
,
ofs
,
len
,
0
);
}
/*
* Reads JEDEC manufacturer ID and device ID and returns the index of the first
* matching table entry (-1 if not found or alias for already found chip).
*/
static
int
probe_new_chip
(
struct
mtd_info
*
mtd
,
__u32
base
,
struct
flchip
*
chips
,
struct
amd_flash_private
*
private
,
const
struct
amd_flash_info
*
table
,
int
table_size
)
{
__u32
mfr_id
;
__u32
dev_id
;
struct
map_info
*
map
=
mtd
->
priv
;
struct
amd_flash_private
temp
;
int
i
;
temp
.
device_type
=
DEVICE_TYPE_X16
;
// Assume X16 (FIXME)
temp
.
interleave
=
2
;
map
->
fldrv_priv
=
&
temp
;
/* Enter autoselect mode. */
send_cmd
(
map
,
base
,
CMD_RESET_DATA
);
send_cmd
(
map
,
base
,
CMD_MANUFACTURER_UNLOCK_DATA
);
mfr_id
=
wide_read
(
map
,
base
+
(
map
->
buswidth
*
ADDR_MANUFACTURER
));
dev_id
=
wide_read
(
map
,
base
+
(
map
->
buswidth
*
ADDR_DEVICE_ID
));
if
((
map
->
buswidth
==
4
)
&&
((
mfr_id
>>
16
)
==
(
mfr_id
&
0xffff
))
&&
((
dev_id
>>
16
)
==
(
dev_id
&
0xffff
)))
{
mfr_id
&=
0xffff
;
dev_id
&=
0xffff
;
}
else
{
temp
.
interleave
=
1
;
}
for
(
i
=
0
;
i
<
table_size
;
i
++
)
{
if
((
mfr_id
==
table
[
i
].
mfr_id
)
&&
(
dev_id
==
table
[
i
].
dev_id
))
{
if
(
chips
)
{
int
j
;
/* Is this an alias for an already found chip?
* In that case that chip should be in
* autoselect mode now.
*/
for
(
j
=
0
;
j
<
private
->
numchips
;
j
++
)
{
__u32
mfr_id_other
;
__u32
dev_id_other
;
mfr_id_other
=
wide_read
(
map
,
chips
[
j
].
start
+
(
map
->
buswidth
*
ADDR_MANUFACTURER
));
dev_id_other
=
wide_read
(
map
,
chips
[
j
].
start
+
(
map
->
buswidth
*
ADDR_DEVICE_ID
));
if
(
temp
.
interleave
==
2
)
{
mfr_id_other
&=
0xffff
;
dev_id_other
&=
0xffff
;
}
if
((
mfr_id_other
==
mfr_id
)
&&
(
dev_id_other
==
dev_id
))
{
/* Exit autoselect mode. */
send_cmd
(
map
,
base
,
CMD_RESET_DATA
);
return
-
1
;
}
}
if
(
private
->
numchips
==
MAX_AMD_CHIPS
)
{
printk
(
KERN_WARNING
"%s: Too many flash chips "
"detected. Increase "
"MAX_AMD_CHIPS from %d.
\n
"
,
map
->
name
,
MAX_AMD_CHIPS
);
return
-
1
;
}
chips
[
private
->
numchips
].
start
=
base
;
chips
[
private
->
numchips
].
state
=
FL_READY
;
chips
[
private
->
numchips
].
mutex
=
&
chips
[
private
->
numchips
].
_spinlock
;
private
->
numchips
++
;
}
printk
(
"%s: Found %d x %ldMiB %s at 0x%x
\n
"
,
map
->
name
,
temp
.
interleave
,
(
table
[
i
].
size
)
/
(
1024
*
1024
),
table
[
i
].
name
,
base
);
mtd
->
size
+=
table
[
i
].
size
*
temp
.
interleave
;
mtd
->
numeraseregions
+=
table
[
i
].
numeraseregions
;
break
;
}
}
/* Exit autoselect mode. */
send_cmd
(
map
,
base
,
CMD_RESET_DATA
);
if
(
i
==
table_size
)
{
printk
(
KERN_DEBUG
"%s: unknown flash device at 0x%x, "
"mfr id 0x%x, dev id 0x%x
\n
"
,
map
->
name
,
base
,
mfr_id
,
dev_id
);
map
->
fldrv_priv
=
NULL
;
return
-
1
;
}
private
->
device_type
=
temp
.
device_type
;
private
->
interleave
=
temp
.
interleave
;
return
i
;
}
static
struct
mtd_info
*
amd_flash_probe
(
struct
map_info
*
map
)
{
static
const
struct
amd_flash_info
table
[]
=
{
{
.
mfr_id
=
MANUFACTURER_AMD
,
.
dev_id
=
AM29LV160DT
,
.
name
=
"AMD AM29LV160DT"
,
.
size
=
0x00200000
,
.
numeraseregions
=
4
,
.
regions
=
{
{
.
offset
=
0x000000
,
.
erasesize
=
0x10000
,
.
numblocks
=
31
},
{
.
offset
=
0x1F0000
,
.
erasesize
=
0x08000
,
.
numblocks
=
1
},
{
.
offset
=
0x1F8000
,
.
erasesize
=
0x02000
,
.
numblocks
=
2
},
{
.
offset
=
0x1FC000
,
.
erasesize
=
0x04000
,
.
numblocks
=
1
}
}
},
{
.
mfr_id
=
MANUFACTURER_AMD
,
.
dev_id
=
AM29LV160DB
,
.
name
=
"AMD AM29LV160DB"
,
.
size
=
0x00200000
,
.
numeraseregions
=
4
,
.
regions
=
{
{
.
offset
=
0x000000
,
.
erasesize
=
0x04000
,
.
numblocks
=
1
},
{
.
offset
=
0x004000
,
.
erasesize
=
0x02000
,
.
numblocks
=
2
},
{
.
offset
=
0x008000
,
.
erasesize
=
0x08000
,
.
numblocks
=
1
},
{
.
offset
=
0x010000
,
.
erasesize
=
0x10000
,
.
numblocks
=
31
}
}
},
{
.
mfr_id
=
MANUFACTURER_TOSHIBA
,
.
dev_id
=
TC58FVT160
,
.
name
=
"Toshiba TC58FVT160"
,
.
size
=
0x00200000
,
.
numeraseregions
=
4
,
.
regions
=
{
{
.
offset
=
0x000000
,
.
erasesize
=
0x10000
,
.
numblocks
=
31
},
{
.
offset
=
0x1F0000
,
.
erasesize
=
0x08000
,
.
numblocks
=
1
},
{
.
offset
=
0x1F8000
,
.
erasesize
=
0x02000
,
.
numblocks
=
2
},
{
.
offset
=
0x1FC000
,
.
erasesize
=
0x04000
,
.
numblocks
=
1
}
}
},
{
.
mfr_id
=
MANUFACTURER_FUJITSU
,
.
dev_id
=
MBM29LV160TE
,
.
name
=
"Fujitsu MBM29LV160TE"
,
.
size
=
0x00200000
,
.
numeraseregions
=
4
,
.
regions
=
{
{
.
offset
=
0x000000
,
.
erasesize
=
0x10000
,
.
numblocks
=
31
},
{
.
offset
=
0x1F0000
,
.
erasesize
=
0x08000
,
.
numblocks
=
1
},
{
.
offset
=
0x1F8000
,
.
erasesize
=
0x02000
,
.
numblocks
=
2
},
{
.
offset
=
0x1FC000
,
.
erasesize
=
0x04000
,
.
numblocks
=
1
}
}
},
{
.
mfr_id
=
MANUFACTURER_TOSHIBA
,
.
dev_id
=
TC58FVB160
,
.
name
=
"Toshiba TC58FVB160"
,
.
size
=
0x00200000
,
.
numeraseregions
=
4
,
.
regions
=
{
{
.
offset
=
0x000000
,
.
erasesize
=
0x04000
,
.
numblocks
=
1
},
{
.
offset
=
0x004000
,
.
erasesize
=
0x02000
,
.
numblocks
=
2
},
{
.
offset
=
0x008000
,
.
erasesize
=
0x08000
,
.
numblocks
=
1
},
{
.
offset
=
0x010000
,
.
erasesize
=
0x10000
,
.
numblocks
=
31
}
}
},
{
.
mfr_id
=
MANUFACTURER_FUJITSU
,
.
dev_id
=
MBM29LV160BE
,
.
name
=
"Fujitsu MBM29LV160BE"
,
.
size
=
0x00200000
,
.
numeraseregions
=
4
,
.
regions
=
{
{
.
offset
=
0x000000
,
.
erasesize
=
0x04000
,
.
numblocks
=
1
},
{
.
offset
=
0x004000
,
.
erasesize
=
0x02000
,
.
numblocks
=
2
},
{
.
offset
=
0x008000
,
.
erasesize
=
0x08000
,
.
numblocks
=
1
},
{
.
offset
=
0x010000
,
.
erasesize
=
0x10000
,
.
numblocks
=
31
}
}
},
{
.
mfr_id
=
MANUFACTURER_AMD
,
.
dev_id
=
AM29LV800BB
,
.
name
=
"AMD AM29LV800BB"
,
.
size
=
0x00100000
,
.
numeraseregions
=
4
,
.
regions
=
{
{
.
offset
=
0x000000
,
.
erasesize
=
0x04000
,
.
numblocks
=
1
},
{
.
offset
=
0x004000
,
.
erasesize
=
0x02000
,
.
numblocks
=
2
},
{
.
offset
=
0x008000
,
.
erasesize
=
0x08000
,
.
numblocks
=
1
},
{
.
offset
=
0x010000
,
.
erasesize
=
0x10000
,
.
numblocks
=
15
}
}
},
{
.
mfr_id
=
MANUFACTURER_AMD
,
.
dev_id
=
AM29F800BB
,
.
name
=
"AMD AM29F800BB"
,
.
size
=
0x00100000
,
.
numeraseregions
=
4
,
.
regions
=
{
{
.
offset
=
0x000000
,
.
erasesize
=
0x04000
,
.
numblocks
=
1
},
{
.
offset
=
0x004000
,
.
erasesize
=
0x02000
,
.
numblocks
=
2
},
{
.
offset
=
0x008000
,
.
erasesize
=
0x08000
,
.
numblocks
=
1
},
{
.
offset
=
0x010000
,
.
erasesize
=
0x10000
,
.
numblocks
=
15
}
}
},
{
.
mfr_id
=
MANUFACTURER_AMD
,
.
dev_id
=
AM29LV800BT
,
.
name
=
"AMD AM29LV800BT"
,
.
size
=
0x00100000
,
.
numeraseregions
=
4
,
.
regions
=
{
{
.
offset
=
0x000000
,
.
erasesize
=
0x10000
,
.
numblocks
=
15
},
{
.
offset
=
0x0F0000
,
.
erasesize
=
0x08000
,
.
numblocks
=
1
},
{
.
offset
=
0x0F8000
,
.
erasesize
=
0x02000
,
.
numblocks
=
2
},
{
.
offset
=
0x0FC000
,
.
erasesize
=
0x04000
,
.
numblocks
=
1
}
}
},
{
.
mfr_id
=
MANUFACTURER_AMD
,
.
dev_id
=
AM29F800BT
,
.
name
=
"AMD AM29F800BT"
,
.
size
=
0x00100000
,
.
numeraseregions
=
4
,
.
regions
=
{
{
.
offset
=
0x000000
,
.
erasesize
=
0x10000
,
.
numblocks
=
15
},
{
.
offset
=
0x0F0000
,
.
erasesize
=
0x08000
,
.
numblocks
=
1
},
{
.
offset
=
0x0F8000
,
.
erasesize
=
0x02000
,
.
numblocks
=
2
},
{
.
offset
=
0x0FC000
,
.
erasesize
=
0x04000
,
.
numblocks
=
1
}
}
},
{
.
mfr_id
=
MANUFACTURER_AMD
,
.
dev_id
=
AM29LV800BB
,
.
name
=
"AMD AM29LV800BB"
,
.
size
=
0x00100000
,
.
numeraseregions
=
4
,
.
regions
=
{
{
.
offset
=
0x000000
,
.
erasesize
=
0x10000
,
.
numblocks
=
15
},
{
.
offset
=
0x0F0000
,
.
erasesize
=
0x08000
,
.
numblocks
=
1
},
{
.
offset
=
0x0F8000
,
.
erasesize
=
0x02000
,
.
numblocks
=
2
},
{
.
offset
=
0x0FC000
,
.
erasesize
=
0x04000
,
.
numblocks
=
1
}
}
},
{
.
mfr_id
=
MANUFACTURER_FUJITSU
,
.
dev_id
=
MBM29LV800BB
,
.
name
=
"Fujitsu MBM29LV800BB"
,
.
size
=
0x00100000
,
.
numeraseregions
=
4
,
.
regions
=
{
{
.
offset
=
0x000000
,
.
erasesize
=
0x04000
,
.
numblocks
=
1
},
{
.
offset
=
0x004000
,
.
erasesize
=
0x02000
,
.
numblocks
=
2
},
{
.
offset
=
0x008000
,
.
erasesize
=
0x08000
,
.
numblocks
=
1
},
{
.
offset
=
0x010000
,
.
erasesize
=
0x10000
,
.
numblocks
=
15
}
}
},
{
.
mfr_id
=
MANUFACTURER_ST
,
.
dev_id
=
M29W800T
,
.
name
=
"ST M29W800T"
,
.
size
=
0x00100000
,
.
numeraseregions
=
4
,
.
regions
=
{
{
.
offset
=
0x000000
,
.
erasesize
=
0x10000
,
.
numblocks
=
15
},
{
.
offset
=
0x0F0000
,
.
erasesize
=
0x08000
,
.
numblocks
=
1
},
{
.
offset
=
0x0F8000
,
.
erasesize
=
0x02000
,
.
numblocks
=
2
},
{
.
offset
=
0x0FC000
,
.
erasesize
=
0x04000
,
.
numblocks
=
1
}
}
},
{
.
mfr_id
=
MANUFACTURER_ST
,
.
dev_id
=
M29W160DT
,
.
name
=
"ST M29W160DT"
,
.
size
=
0x00200000
,
.
numeraseregions
=
4
,
.
regions
=
{
{
.
offset
=
0x000000
,
.
erasesize
=
0x10000
,
.
numblocks
=
31
},
{
.
offset
=
0x1F0000
,
.
erasesize
=
0x08000
,
.
numblocks
=
1
},
{
.
offset
=
0x1F8000
,
.
erasesize
=
0x02000
,
.
numblocks
=
2
},
{
.
offset
=
0x1FC000
,
.
erasesize
=
0x04000
,
.
numblocks
=
1
}
}
},
{
.
mfr_id
=
MANUFACTURER_ST
,
.
dev_id
=
M29W160DB
,
.
name
=
"ST M29W160DB"
,
.
size
=
0x00200000
,
.
numeraseregions
=
4
,
.
regions
=
{
{
.
offset
=
0x000000
,
.
erasesize
=
0x04000
,
.
numblocks
=
1
},
{
.
offset
=
0x004000
,
.
erasesize
=
0x02000
,
.
numblocks
=
2
},
{
.
offset
=
0x008000
,
.
erasesize
=
0x08000
,
.
numblocks
=
1
},
{
.
offset
=
0x010000
,
.
erasesize
=
0x10000
,
.
numblocks
=
31
}
}
},
{
.
mfr_id
=
MANUFACTURER_AMD
,
.
dev_id
=
AM29BDS323D
,
.
name
=
"AMD AM29BDS323D"
,
.
size
=
0x00400000
,
.
numeraseregions
=
3
,
.
regions
=
{
{
.
offset
=
0x000000
,
.
erasesize
=
0x10000
,
.
numblocks
=
48
},
{
.
offset
=
0x300000
,
.
erasesize
=
0x10000
,
.
numblocks
=
15
},
{
.
offset
=
0x3f0000
,
.
erasesize
=
0x02000
,
.
numblocks
=
8
},
}
},
{
.
mfr_id
=
MANUFACTURER_ATMEL
,
.
dev_id
=
AT49xV16x
,
.
name
=
"Atmel AT49xV16x"
,
.
size
=
0x00200000
,
.
numeraseregions
=
2
,
.
regions
=
{
{
.
offset
=
0x000000
,
.
erasesize
=
0x02000
,
.
numblocks
=
8
},
{
.
offset
=
0x010000
,
.
erasesize
=
0x10000
,
.
numblocks
=
31
}
}
},
{
.
mfr_id
=
MANUFACTURER_ATMEL
,
.
dev_id
=
AT49xV16xT
,
.
name
=
"Atmel AT49xV16xT"
,
.
size
=
0x00200000
,
.
numeraseregions
=
2
,
.
regions
=
{
{
.
offset
=
0x000000
,
.
erasesize
=
0x10000
,
.
numblocks
=
31
},
{
.
offset
=
0x1F0000
,
.
erasesize
=
0x02000
,
.
numblocks
=
8
}
}
}
};
struct
mtd_info
*
mtd
;
struct
flchip
chips
[
MAX_AMD_CHIPS
];
int
table_pos
[
MAX_AMD_CHIPS
];
struct
amd_flash_private
temp
;
struct
amd_flash_private
*
private
;
u_long
size
;
unsigned
long
base
;
int
i
;
int
reg_idx
;
int
offset
;
mtd
=
kzalloc
(
sizeof
(
*
mtd
),
GFP_KERNEL
);
if
(
!
mtd
)
{
printk
(
KERN_WARNING
"%s: kmalloc failed for info structure
\n
"
,
map
->
name
);
return
NULL
;
}
mtd
->
priv
=
map
;
memset
(
&
temp
,
0
,
sizeof
(
temp
));
printk
(
"%s: Probing for AMD compatible flash...
\n
"
,
map
->
name
);
if
((
table_pos
[
0
]
=
probe_new_chip
(
mtd
,
0
,
NULL
,
&
temp
,
table
,
ARRAY_SIZE
(
table
)))
==
-
1
)
{
printk
(
KERN_WARNING
"%s: Found no AMD compatible device at location zero
\n
"
,
map
->
name
);
kfree
(
mtd
);
return
NULL
;
}
chips
[
0
].
start
=
0
;
chips
[
0
].
state
=
FL_READY
;
chips
[
0
].
mutex
=
&
chips
[
0
].
_spinlock
;
temp
.
numchips
=
1
;
for
(
size
=
mtd
->
size
;
size
>
1
;
size
>>=
1
)
{
temp
.
chipshift
++
;
}
switch
(
temp
.
interleave
)
{
case
2
:
temp
.
chipshift
+=
1
;
break
;
case
4
:
temp
.
chipshift
+=
2
;
break
;
}
/* Find out if there are any more chips in the map. */
for
(
base
=
(
1
<<
temp
.
chipshift
);
base
<
map
->
size
;
base
+=
(
1
<<
temp
.
chipshift
))
{
int
numchips
=
temp
.
numchips
;
table_pos
[
numchips
]
=
probe_new_chip
(
mtd
,
base
,
chips
,
&
temp
,
table
,
ARRAY_SIZE
(
table
));
}
mtd
->
eraseregions
=
kmalloc
(
sizeof
(
struct
mtd_erase_region_info
)
*
mtd
->
numeraseregions
,
GFP_KERNEL
);
if
(
!
mtd
->
eraseregions
)
{
printk
(
KERN_WARNING
"%s: Failed to allocate "
"memory for MTD erase region info
\n
"
,
map
->
name
);
kfree
(
mtd
);
map
->
fldrv_priv
=
NULL
;
return
NULL
;
}
reg_idx
=
0
;
offset
=
0
;
for
(
i
=
0
;
i
<
temp
.
numchips
;
i
++
)
{
int
dev_size
;
int
j
;
dev_size
=
0
;
for
(
j
=
0
;
j
<
table
[
table_pos
[
i
]].
numeraseregions
;
j
++
)
{
mtd
->
eraseregions
[
reg_idx
].
offset
=
offset
+
(
table
[
table_pos
[
i
]].
regions
[
j
].
offset
*
temp
.
interleave
);
mtd
->
eraseregions
[
reg_idx
].
erasesize
=
table
[
table_pos
[
i
]].
regions
[
j
].
erasesize
*
temp
.
interleave
;
mtd
->
eraseregions
[
reg_idx
].
numblocks
=
table
[
table_pos
[
i
]].
regions
[
j
].
numblocks
;
if
(
mtd
->
erasesize
<
mtd
->
eraseregions
[
reg_idx
].
erasesize
)
{
mtd
->
erasesize
=
mtd
->
eraseregions
[
reg_idx
].
erasesize
;
}
dev_size
+=
mtd
->
eraseregions
[
reg_idx
].
erasesize
*
mtd
->
eraseregions
[
reg_idx
].
numblocks
;
reg_idx
++
;
}
offset
+=
dev_size
;
}
mtd
->
type
=
MTD_NORFLASH
;
mtd
->
writesize
=
1
;
mtd
->
flags
=
MTD_CAP_NORFLASH
;
mtd
->
name
=
map
->
name
;
mtd
->
erase
=
amd_flash_erase
;
mtd
->
read
=
amd_flash_read
;
mtd
->
write
=
amd_flash_write
;
mtd
->
sync
=
amd_flash_sync
;
mtd
->
suspend
=
amd_flash_suspend
;
mtd
->
resume
=
amd_flash_resume
;
mtd
->
lock
=
amd_flash_lock
;
mtd
->
unlock
=
amd_flash_unlock
;
private
=
kmalloc
(
sizeof
(
*
private
)
+
(
sizeof
(
struct
flchip
)
*
temp
.
numchips
),
GFP_KERNEL
);
if
(
!
private
)
{
printk
(
KERN_WARNING
"%s: kmalloc failed for private structure
\n
"
,
map
->
name
);
kfree
(
mtd
);
map
->
fldrv_priv
=
NULL
;
return
NULL
;
}
memcpy
(
private
,
&
temp
,
sizeof
(
temp
));
memcpy
(
private
->
chips
,
chips
,
sizeof
(
struct
flchip
)
*
private
->
numchips
);
for
(
i
=
0
;
i
<
private
->
numchips
;
i
++
)
{
init_waitqueue_head
(
&
private
->
chips
[
i
].
wq
);
spin_lock_init
(
&
private
->
chips
[
i
].
_spinlock
);
}
map
->
fldrv_priv
=
private
;
map
->
fldrv
=
&
amd_flash_chipdrv
;
__module_get
(
THIS_MODULE
);
return
mtd
;
}
static
inline
int
read_one_chip
(
struct
map_info
*
map
,
struct
flchip
*
chip
,
loff_t
adr
,
size_t
len
,
u_char
*
buf
)
{
DECLARE_WAITQUEUE
(
wait
,
current
);
unsigned
long
timeo
=
jiffies
+
HZ
;
retry:
spin_lock_bh
(
chip
->
mutex
);
if
(
chip
->
state
!=
FL_READY
){
printk
(
KERN_INFO
"%s: waiting for chip to read, state = %d
\n
"
,
map
->
name
,
chip
->
state
);
set_current_state
(
TASK_UNINTERRUPTIBLE
);
add_wait_queue
(
&
chip
->
wq
,
&
wait
);
spin_unlock_bh
(
chip
->
mutex
);
schedule
();
remove_wait_queue
(
&
chip
->
wq
,
&
wait
);
if
(
signal_pending
(
current
))
{
return
-
EINTR
;
}
timeo
=
jiffies
+
HZ
;
goto
retry
;
}
adr
+=
chip
->
start
;
chip
->
state
=
FL_READY
;
map_copy_from
(
map
,
buf
,
adr
,
len
);
wake_up
(
&
chip
->
wq
);
spin_unlock_bh
(
chip
->
mutex
);
return
0
;
}
static
int
amd_flash_read
(
struct
mtd_info
*
mtd
,
loff_t
from
,
size_t
len
,
size_t
*
retlen
,
u_char
*
buf
)
{
struct
map_info
*
map
=
mtd
->
priv
;
struct
amd_flash_private
*
private
=
map
->
fldrv_priv
;
unsigned
long
ofs
;
int
chipnum
;
int
ret
=
0
;
if
((
from
+
len
)
>
mtd
->
size
)
{
printk
(
KERN_WARNING
"%s: read request past end of device "
"(0x%lx)
\n
"
,
map
->
name
,
(
unsigned
long
)
from
+
len
);
return
-
EINVAL
;
}
/* Offset within the first chip that the first read should start. */
chipnum
=
(
from
>>
private
->
chipshift
);
ofs
=
from
-
(
chipnum
<<
private
->
chipshift
);
*
retlen
=
0
;
while
(
len
)
{
unsigned
long
this_len
;
if
(
chipnum
>=
private
->
numchips
)
{
break
;
}
if
((
len
+
ofs
-
1
)
>>
private
->
chipshift
)
{
this_len
=
(
1
<<
private
->
chipshift
)
-
ofs
;
}
else
{
this_len
=
len
;
}
ret
=
read_one_chip
(
map
,
&
private
->
chips
[
chipnum
],
ofs
,
this_len
,
buf
);
if
(
ret
)
{
break
;
}
*
retlen
+=
this_len
;
len
-=
this_len
;
buf
+=
this_len
;
ofs
=
0
;
chipnum
++
;
}
return
ret
;
}
static
int
write_one_word
(
struct
map_info
*
map
,
struct
flchip
*
chip
,
unsigned
long
adr
,
__u32
datum
)
{
unsigned
long
timeo
=
jiffies
+
HZ
;
struct
amd_flash_private
*
private
=
map
->
fldrv_priv
;
DECLARE_WAITQUEUE
(
wait
,
current
);
int
ret
=
0
;
int
times_left
;
retry:
spin_lock_bh
(
chip
->
mutex
);
if
(
chip
->
state
!=
FL_READY
){
printk
(
"%s: waiting for chip to write, state = %d
\n
"
,
map
->
name
,
chip
->
state
);
set_current_state
(
TASK_UNINTERRUPTIBLE
);
add_wait_queue
(
&
chip
->
wq
,
&
wait
);
spin_unlock_bh
(
chip
->
mutex
);
schedule
();
remove_wait_queue
(
&
chip
->
wq
,
&
wait
);
printk
(
KERN_INFO
"%s: woke up to write
\n
"
,
map
->
name
);
if
(
signal_pending
(
current
))
return
-
EINTR
;
timeo
=
jiffies
+
HZ
;
goto
retry
;
}
chip
->
state
=
FL_WRITING
;
adr
+=
chip
->
start
;
ENABLE_VPP
(
map
);
send_cmd
(
map
,
chip
->
start
,
CMD_PROGRAM_UNLOCK_DATA
);
wide_write
(
map
,
datum
,
adr
);
times_left
=
500000
;
while
(
times_left
--
&&
flash_is_busy
(
map
,
adr
,
private
->
interleave
))
{
if
(
need_resched
())
{
spin_unlock_bh
(
chip
->
mutex
);
schedule
();
spin_lock_bh
(
chip
->
mutex
);
}
}
if
(
!
times_left
)
{
printk
(
KERN_WARNING
"%s: write to 0x%lx timed out!
\n
"
,
map
->
name
,
adr
);
ret
=
-
EIO
;
}
else
{
__u32
verify
;
if
((
verify
=
wide_read
(
map
,
adr
))
!=
datum
)
{
printk
(
KERN_WARNING
"%s: write to 0x%lx failed. "
"datum = %x, verify = %x
\n
"
,
map
->
name
,
adr
,
datum
,
verify
);
ret
=
-
EIO
;
}
}
DISABLE_VPP
(
map
);
chip
->
state
=
FL_READY
;
wake_up
(
&
chip
->
wq
);
spin_unlock_bh
(
chip
->
mutex
);
return
ret
;
}
static
int
amd_flash_write
(
struct
mtd_info
*
mtd
,
loff_t
to
,
size_t
len
,
size_t
*
retlen
,
const
u_char
*
buf
)
{
struct
map_info
*
map
=
mtd
->
priv
;
struct
amd_flash_private
*
private
=
map
->
fldrv_priv
;
int
ret
=
0
;
int
chipnum
;
unsigned
long
ofs
;
unsigned
long
chipstart
;
*
retlen
=
0
;
if
(
!
len
)
{
return
0
;
}
chipnum
=
to
>>
private
->
chipshift
;
ofs
=
to
-
(
chipnum
<<
private
->
chipshift
);
chipstart
=
private
->
chips
[
chipnum
].
start
;
/* If it's not bus-aligned, do the first byte write. */
if
(
ofs
&
(
map
->
buswidth
-
1
))
{
unsigned
long
bus_ofs
=
ofs
&
~
(
map
->
buswidth
-
1
);
int
i
=
ofs
-
bus_ofs
;
int
n
=
0
;
u_char
tmp_buf
[
4
];
__u32
datum
;
map_copy_from
(
map
,
tmp_buf
,
bus_ofs
+
private
->
chips
[
chipnum
].
start
,
map
->
buswidth
);
while
(
len
&&
i
<
map
->
buswidth
)
tmp_buf
[
i
++
]
=
buf
[
n
++
],
len
--
;
if
(
map
->
buswidth
==
2
)
{
datum
=
*
(
__u16
*
)
tmp_buf
;
}
else
if
(
map
->
buswidth
==
4
)
{
datum
=
*
(
__u32
*
)
tmp_buf
;
}
else
{
return
-
EINVAL
;
/* should never happen, but be safe */
}
ret
=
write_one_word
(
map
,
&
private
->
chips
[
chipnum
],
bus_ofs
,
datum
);
if
(
ret
)
{
return
ret
;
}
ofs
+=
n
;
buf
+=
n
;
(
*
retlen
)
+=
n
;
if
(
ofs
>>
private
->
chipshift
)
{
chipnum
++
;
ofs
=
0
;
if
(
chipnum
==
private
->
numchips
)
{
return
0
;
}
}
}
/* We are now aligned, write as much as possible. */
while
(
len
>=
map
->
buswidth
)
{
__u32
datum
;
if
(
map
->
buswidth
==
1
)
{
datum
=
*
(
__u8
*
)
buf
;
}
else
if
(
map
->
buswidth
==
2
)
{
datum
=
*
(
__u16
*
)
buf
;
}
else
if
(
map
->
buswidth
==
4
)
{
datum
=
*
(
__u32
*
)
buf
;
}
else
{
return
-
EINVAL
;
}
ret
=
write_one_word
(
map
,
&
private
->
chips
[
chipnum
],
ofs
,
datum
);
if
(
ret
)
{
return
ret
;
}
ofs
+=
map
->
buswidth
;
buf
+=
map
->
buswidth
;
(
*
retlen
)
+=
map
->
buswidth
;
len
-=
map
->
buswidth
;
if
(
ofs
>>
private
->
chipshift
)
{
chipnum
++
;
ofs
=
0
;
if
(
chipnum
==
private
->
numchips
)
{
return
0
;
}
chipstart
=
private
->
chips
[
chipnum
].
start
;
}
}
if
(
len
&
(
map
->
buswidth
-
1
))
{
int
i
=
0
,
n
=
0
;
u_char
tmp_buf
[
2
];
__u32
datum
;
map_copy_from
(
map
,
tmp_buf
,
ofs
+
private
->
chips
[
chipnum
].
start
,
map
->
buswidth
);
while
(
len
--
)
{
tmp_buf
[
i
++
]
=
buf
[
n
++
];
}
if
(
map
->
buswidth
==
2
)
{
datum
=
*
(
__u16
*
)
tmp_buf
;
}
else
if
(
map
->
buswidth
==
4
)
{
datum
=
*
(
__u32
*
)
tmp_buf
;
}
else
{
return
-
EINVAL
;
/* should never happen, but be safe */
}
ret
=
write_one_word
(
map
,
&
private
->
chips
[
chipnum
],
ofs
,
datum
);
if
(
ret
)
{
return
ret
;
}
(
*
retlen
)
+=
n
;
}
return
0
;
}
static
inline
int
erase_one_block
(
struct
map_info
*
map
,
struct
flchip
*
chip
,
unsigned
long
adr
,
u_long
size
)
{
unsigned
long
timeo
=
jiffies
+
HZ
;
struct
amd_flash_private
*
private
=
map
->
fldrv_priv
;
DECLARE_WAITQUEUE
(
wait
,
current
);
retry:
spin_lock_bh
(
chip
->
mutex
);
if
(
chip
->
state
!=
FL_READY
){
set_current_state
(
TASK_UNINTERRUPTIBLE
);
add_wait_queue
(
&
chip
->
wq
,
&
wait
);
spin_unlock_bh
(
chip
->
mutex
);
schedule
();
remove_wait_queue
(
&
chip
->
wq
,
&
wait
);
if
(
signal_pending
(
current
))
{
return
-
EINTR
;
}
timeo
=
jiffies
+
HZ
;
goto
retry
;
}
chip
->
state
=
FL_ERASING
;
adr
+=
chip
->
start
;
ENABLE_VPP
(
map
);
send_cmd
(
map
,
chip
->
start
,
CMD_SECTOR_ERASE_UNLOCK_DATA
);
send_cmd_to_addr
(
map
,
chip
->
start
,
CMD_SECTOR_ERASE_UNLOCK_DATA_2
,
adr
);
timeo
=
jiffies
+
(
HZ
*
20
);
spin_unlock_bh
(
chip
->
mutex
);
msleep
(
1000
);
spin_lock_bh
(
chip
->
mutex
);
while
(
flash_is_busy
(
map
,
adr
,
private
->
interleave
))
{
if
(
chip
->
state
!=
FL_ERASING
)
{
/* Someone's suspended the erase. Sleep */
set_current_state
(
TASK_UNINTERRUPTIBLE
);
add_wait_queue
(
&
chip
->
wq
,
&
wait
);
spin_unlock_bh
(
chip
->
mutex
);
printk
(
KERN_INFO
"%s: erase suspended. Sleeping
\n
"
,
map
->
name
);
schedule
();
remove_wait_queue
(
&
chip
->
wq
,
&
wait
);
if
(
signal_pending
(
current
))
{
return
-
EINTR
;
}
timeo
=
jiffies
+
(
HZ
*
2
);
/* FIXME */
spin_lock_bh
(
chip
->
mutex
);
continue
;
}
/* OK Still waiting */
if
(
time_after
(
jiffies
,
timeo
))
{
chip
->
state
=
FL_READY
;
spin_unlock_bh
(
chip
->
mutex
);
printk
(
KERN_WARNING
"%s: waiting for erase to complete "
"timed out.
\n
"
,
map
->
name
);
DISABLE_VPP
(
map
);
return
-
EIO
;
}
/* Latency issues. Drop the lock, wait a while and retry */
spin_unlock_bh
(
chip
->
mutex
);
if
(
need_resched
())
schedule
();
else
udelay
(
1
);
spin_lock_bh
(
chip
->
mutex
);
}
/* Verify every single word */
{
int
address
;
int
error
=
0
;
__u8
verify
;
for
(
address
=
adr
;
address
<
(
adr
+
size
);
address
++
)
{
if
((
verify
=
map_read8
(
map
,
address
))
!=
0xFF
)
{
error
=
1
;
break
;
}
}
if
(
error
)
{
chip
->
state
=
FL_READY
;
spin_unlock_bh
(
chip
->
mutex
);
printk
(
KERN_WARNING
"%s: verify error at 0x%x, size %ld.
\n
"
,
map
->
name
,
address
,
size
);
DISABLE_VPP
(
map
);
return
-
EIO
;
}
}
DISABLE_VPP
(
map
);
chip
->
state
=
FL_READY
;
wake_up
(
&
chip
->
wq
);
spin_unlock_bh
(
chip
->
mutex
);
return
0
;
}
static
int
amd_flash_erase
(
struct
mtd_info
*
mtd
,
struct
erase_info
*
instr
)
{
struct
map_info
*
map
=
mtd
->
priv
;
struct
amd_flash_private
*
private
=
map
->
fldrv_priv
;
unsigned
long
adr
,
len
;
int
chipnum
;
int
ret
=
0
;
int
i
;
int
first
;
struct
mtd_erase_region_info
*
regions
=
mtd
->
eraseregions
;
if
(
instr
->
addr
>
mtd
->
size
)
{
return
-
EINVAL
;
}
if
((
instr
->
len
+
instr
->
addr
)
>
mtd
->
size
)
{
return
-
EINVAL
;
}
/* Check that both start and end of the requested erase are
* aligned with the erasesize at the appropriate addresses.
*/
i
=
0
;
/* Skip all erase regions which are ended before the start of
the requested erase. Actually, to save on the calculations,
we skip to the first erase region which starts after the
start of the requested erase, and then go back one.
*/
while
((
i
<
mtd
->
numeraseregions
)
&&
(
instr
->
addr
>=
regions
[
i
].
offset
))
{
i
++
;
}
i
--
;
/* OK, now i is pointing at the erase region in which this
* erase request starts. Check the start of the requested
* erase range is aligned with the erase size which is in
* effect here.
*/
if
(
instr
->
addr
&
(
regions
[
i
].
erasesize
-
1
))
{
return
-
EINVAL
;
}
/* Remember the erase region we start on. */
first
=
i
;
/* Next, check that the end of the requested erase is aligned
* with the erase region at that address.
*/
while
((
i
<
mtd
->
numeraseregions
)
&&
((
instr
->
addr
+
instr
->
len
)
>=
regions
[
i
].
offset
))
{
i
++
;
}
/* As before, drop back one to point at the region in which
* the address actually falls.
*/
i
--
;
if
((
instr
->
addr
+
instr
->
len
)
&
(
regions
[
i
].
erasesize
-
1
))
{
return
-
EINVAL
;
}
chipnum
=
instr
->
addr
>>
private
->
chipshift
;
adr
=
instr
->
addr
-
(
chipnum
<<
private
->
chipshift
);
len
=
instr
->
len
;
i
=
first
;
while
(
len
)
{
ret
=
erase_one_block
(
map
,
&
private
->
chips
[
chipnum
],
adr
,
regions
[
i
].
erasesize
);
if
(
ret
)
{
return
ret
;
}
adr
+=
regions
[
i
].
erasesize
;
len
-=
regions
[
i
].
erasesize
;
if
((
adr
%
(
1
<<
private
->
chipshift
))
==
((
regions
[
i
].
offset
+
(
regions
[
i
].
erasesize
*
regions
[
i
].
numblocks
))
%
(
1
<<
private
->
chipshift
)))
{
i
++
;
}
if
(
adr
>>
private
->
chipshift
)
{
adr
=
0
;
chipnum
++
;
if
(
chipnum
>=
private
->
numchips
)
{
break
;
}
}
}
instr
->
state
=
MTD_ERASE_DONE
;
mtd_erase_callback
(
instr
);
return
0
;
}
static
void
amd_flash_sync
(
struct
mtd_info
*
mtd
)
{
struct
map_info
*
map
=
mtd
->
priv
;
struct
amd_flash_private
*
private
=
map
->
fldrv_priv
;
int
i
;
struct
flchip
*
chip
;
int
ret
=
0
;
DECLARE_WAITQUEUE
(
wait
,
current
);
for
(
i
=
0
;
!
ret
&&
(
i
<
private
->
numchips
);
i
++
)
{
chip
=
&
private
->
chips
[
i
];
retry:
spin_lock_bh
(
chip
->
mutex
);
switch
(
chip
->
state
)
{
case
FL_READY
:
case
FL_STATUS
:
case
FL_CFI_QUERY
:
case
FL_JEDEC_QUERY
:
chip
->
oldstate
=
chip
->
state
;
chip
->
state
=
FL_SYNCING
;
/* No need to wake_up() on this state change -
* as the whole point is that nobody can do anything
* with the chip now anyway.
*/
case
FL_SYNCING
:
spin_unlock_bh
(
chip
->
mutex
);
break
;
default:
/* Not an idle state */
add_wait_queue
(
&
chip
->
wq
,
&
wait
);
spin_unlock_bh
(
chip
->
mutex
);
schedule
();
remove_wait_queue
(
&
chip
->
wq
,
&
wait
);
goto
retry
;
}
}
/* Unlock the chips again */
for
(
i
--
;
i
>=
0
;
i
--
)
{
chip
=
&
private
->
chips
[
i
];
spin_lock_bh
(
chip
->
mutex
);
if
(
chip
->
state
==
FL_SYNCING
)
{
chip
->
state
=
chip
->
oldstate
;
wake_up
(
&
chip
->
wq
);
}
spin_unlock_bh
(
chip
->
mutex
);
}
}
static
int
amd_flash_suspend
(
struct
mtd_info
*
mtd
)
{
printk
(
"amd_flash_suspend(): not implemented!
\n
"
);
return
-
EINVAL
;
}
static
void
amd_flash_resume
(
struct
mtd_info
*
mtd
)
{
printk
(
"amd_flash_resume(): not implemented!
\n
"
);
}
static
void
amd_flash_destroy
(
struct
mtd_info
*
mtd
)
{
struct
map_info
*
map
=
mtd
->
priv
;
struct
amd_flash_private
*
private
=
map
->
fldrv_priv
;
kfree
(
private
);
}
int
__init
amd_flash_init
(
void
)
{
register_mtd_chip_driver
(
&
amd_flash_chipdrv
);
return
0
;
}
void
__exit
amd_flash_exit
(
void
)
{
unregister_mtd_chip_driver
(
&
amd_flash_chipdrv
);
}
module_init
(
amd_flash_init
);
module_exit
(
amd_flash_exit
);
MODULE_LICENSE
(
"GPL"
);
MODULE_AUTHOR
(
"Jonas Holmberg <jonas.holmberg@axis.com>"
);
MODULE_DESCRIPTION
(
"Old MTD chip driver for AMD flash chips"
);
drivers/mtd/chips/jedec.c
deleted
100644 → 0
View file @
42f209d3
/* JEDEC Flash Interface.
* This is an older type of interface for self programming flash. It is
* commonly use in older AMD chips and is obsolete compared with CFI.
* It is called JEDEC because the JEDEC association distributes the ID codes
* for the chips.
*
* See the AMD flash databook for information on how to operate the interface.
*
* This code does not support anything wider than 8 bit flash chips, I am
* not going to guess how to send commands to them, plus I expect they will
* all speak CFI..
*
* $Id: jedec.c,v 1.22 2005/01/05 18:05:11 dwmw2 Exp $
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/mtd/jedec.h>
#include <linux/mtd/map.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/compatmac.h>
static
struct
mtd_info
*
jedec_probe
(
struct
map_info
*
);
static
int
jedec_probe8
(
struct
map_info
*
map
,
unsigned
long
base
,
struct
jedec_private
*
priv
);
static
int
jedec_probe16
(
struct
map_info
*
map
,
unsigned
long
base
,
struct
jedec_private
*
priv
);
static
int
jedec_probe32
(
struct
map_info
*
map
,
unsigned
long
base
,
struct
jedec_private
*
priv
);
static
void
jedec_flash_chip_scan
(
struct
jedec_private
*
priv
,
unsigned
long
start
,
unsigned
long
len
);
static
int
flash_erase
(
struct
mtd_info
*
mtd
,
struct
erase_info
*
instr
);
static
int
flash_write
(
struct
mtd_info
*
mtd
,
loff_t
start
,
size_t
len
,
size_t
*
retlen
,
const
u_char
*
buf
);
static
unsigned
long
my_bank_size
;
/* Listing of parts and sizes. We need this table to learn the sector
size of the chip and the total length */
static
const
struct
JEDECTable
JEDEC_table
[]
=
{
{
.
jedec
=
0x013D
,
.
name
=
"AMD Am29F017D"
,
.
size
=
2
*
1024
*
1024
,
.
sectorsize
=
64
*
1024
,
.
capabilities
=
MTD_CAP_NORFLASH
},
{
.
jedec
=
0x01AD
,
.
name
=
"AMD Am29F016"
,
.
size
=
2
*
1024
*
1024
,
.
sectorsize
=
64
*
1024
,
.
capabilities
=
MTD_CAP_NORFLASH
},
{
.
jedec
=
0x01D5
,
.
name
=
"AMD Am29F080"
,
.
size
=
1
*
1024
*
1024
,
.
sectorsize
=
64
*
1024
,
.
capabilities
=
MTD_CAP_NORFLASH
},
{
.
jedec
=
0x01A4
,
.
name
=
"AMD Am29F040"
,
.
size
=
512
*
1024
,
.
sectorsize
=
64
*
1024
,
.
capabilities
=
MTD_CAP_NORFLASH
},
{
.
jedec
=
0x20E3
,
.
name
=
"AMD Am29W040B"
,
.
size
=
512
*
1024
,
.
sectorsize
=
64
*
1024
,
.
capabilities
=
MTD_CAP_NORFLASH
},
{
.
jedec
=
0xC2AD
,
.
name
=
"Macronix MX29F016"
,
.
size
=
2
*
1024
*
1024
,
.
sectorsize
=
64
*
1024
,
.
capabilities
=
MTD_CAP_NORFLASH
},
{
.
jedec
=
0x0
}
};
static
const
struct
JEDECTable
*
jedec_idtoinf
(
__u8
mfr
,
__u8
id
);
static
void
jedec_sync
(
struct
mtd_info
*
mtd
)
{};
static
int
jedec_read
(
struct
mtd_info
*
mtd
,
loff_t
from
,
size_t
len
,
size_t
*
retlen
,
u_char
*
buf
);
static
int
jedec_read_banked
(
struct
mtd_info
*
mtd
,
loff_t
from
,
size_t
len
,
size_t
*
retlen
,
u_char
*
buf
);
static
struct
mtd_info
*
jedec_probe
(
struct
map_info
*
map
);
static
struct
mtd_chip_driver
jedec_chipdrv
=
{
.
probe
=
jedec_probe
,
.
name
=
"jedec"
,
.
module
=
THIS_MODULE
};
/* Probe entry point */
static
struct
mtd_info
*
jedec_probe
(
struct
map_info
*
map
)
{
struct
mtd_info
*
MTD
;
struct
jedec_private
*
priv
;
unsigned
long
Base
;
unsigned
long
SectorSize
;
unsigned
count
;
unsigned
I
,
Uniq
;
char
Part
[
200
];
memset
(
&
priv
,
0
,
sizeof
(
priv
));
MTD
=
kzalloc
(
sizeof
(
struct
mtd_info
)
+
sizeof
(
struct
jedec_private
),
GFP_KERNEL
);
if
(
!
MTD
)
return
NULL
;
priv
=
(
struct
jedec_private
*
)
&
MTD
[
1
];
my_bank_size
=
map
->
size
;
if
(
map
->
size
/
my_bank_size
>
MAX_JEDEC_CHIPS
)
{
printk
(
"mtd: Increase MAX_JEDEC_CHIPS, too many banks.
\n
"
);
kfree
(
MTD
);
return
NULL
;
}
for
(
Base
=
0
;
Base
<
map
->
size
;
Base
+=
my_bank_size
)
{
// Perhaps zero could designate all tests?
if
(
map
->
buswidth
==
0
)
map
->
buswidth
=
1
;
if
(
map
->
buswidth
==
1
){
if
(
jedec_probe8
(
map
,
Base
,
priv
)
==
0
)
{
printk
(
"did recognize jedec chip
\n
"
);
kfree
(
MTD
);
return
NULL
;
}
}
if
(
map
->
buswidth
==
2
)
jedec_probe16
(
map
,
Base
,
priv
);
if
(
map
->
buswidth
==
4
)
jedec_probe32
(
map
,
Base
,
priv
);
}
// Get the biggest sector size
SectorSize
=
0
;
for
(
I
=
0
;
priv
->
chips
[
I
].
jedec
!=
0
&&
I
<
MAX_JEDEC_CHIPS
;
I
++
)
{
// printk("priv->chips[%d].jedec is %x\n",I,priv->chips[I].jedec);
// printk("priv->chips[%d].sectorsize is %lx\n",I,priv->chips[I].sectorsize);
if
(
priv
->
chips
[
I
].
sectorsize
>
SectorSize
)
SectorSize
=
priv
->
chips
[
I
].
sectorsize
;
}
// Quickly ensure that the other sector sizes are factors of the largest
for
(
I
=
0
;
priv
->
chips
[
I
].
jedec
!=
0
&&
I
<
MAX_JEDEC_CHIPS
;
I
++
)
{
if
((
SectorSize
/
priv
->
chips
[
I
].
sectorsize
)
*
priv
->
chips
[
I
].
sectorsize
!=
SectorSize
)
{
printk
(
"mtd: Failed. Device has incompatible mixed sector sizes
\n
"
);
kfree
(
MTD
);
return
NULL
;
}
}
/* Generate a part name that includes the number of different chips and
other configuration information */
count
=
1
;
strlcpy
(
Part
,
map
->
name
,
sizeof
(
Part
)
-
10
);
strcat
(
Part
,
" "
);
Uniq
=
0
;
for
(
I
=
0
;
priv
->
chips
[
I
].
jedec
!=
0
&&
I
<
MAX_JEDEC_CHIPS
;
I
++
)
{
const
struct
JEDECTable
*
JEDEC
;
if
(
priv
->
chips
[
I
+
1
].
jedec
==
priv
->
chips
[
I
].
jedec
)
{
count
++
;
continue
;
}
// Locate the chip in the jedec table
JEDEC
=
jedec_idtoinf
(
priv
->
chips
[
I
].
jedec
>>
8
,
priv
->
chips
[
I
].
jedec
);
if
(
JEDEC
==
0
)
{
printk
(
"mtd: Internal Error, JEDEC not set
\n
"
);
kfree
(
MTD
);
return
NULL
;
}
if
(
Uniq
!=
0
)
strcat
(
Part
,
","
);
Uniq
++
;
if
(
count
!=
1
)
sprintf
(
Part
+
strlen
(
Part
),
"%x*[%s]"
,
count
,
JEDEC
->
name
);
else
sprintf
(
Part
+
strlen
(
Part
),
"%s"
,
JEDEC
->
name
);
if
(
strlen
(
Part
)
>
sizeof
(
Part
)
*
2
/
3
)
break
;
count
=
1
;
}
/* Determine if the chips are organized in a linear fashion, or if there
are empty banks. Note, the last bank does not count here, only the
first banks are important. Holes on non-bank boundaries can not exist
due to the way the detection algorithm works. */
if
(
priv
->
size
<
my_bank_size
)
my_bank_size
=
priv
->
size
;
priv
->
is_banked
=
0
;
//printk("priv->size is %x, my_bank_size is %x\n",priv->size,my_bank_size);
//printk("priv->bank_fill[0] is %x\n",priv->bank_fill[0]);
if
(
!
priv
->
size
)
{
printk
(
"priv->size is zero
\n
"
);
kfree
(
MTD
);
return
NULL
;
}
if
(
priv
->
size
/
my_bank_size
)
{
if
(
priv
->
size
/
my_bank_size
==
1
)
{
priv
->
size
=
my_bank_size
;
}
else
{
for
(
I
=
0
;
I
!=
priv
->
size
/
my_bank_size
-
1
;
I
++
)
{
if
(
priv
->
bank_fill
[
I
]
!=
my_bank_size
)
priv
->
is_banked
=
1
;
/* This even could be eliminated, but new de-optimized read/write
functions have to be written */
printk
(
"priv->bank_fill[%d] is %lx, priv->bank_fill[0] is %lx
\n
"
,
I
,
priv
->
bank_fill
[
I
],
priv
->
bank_fill
[
0
]);
if
(
priv
->
bank_fill
[
I
]
!=
priv
->
bank_fill
[
0
])
{
printk
(
"mtd: Failed. Cannot handle unsymmetric banking
\n
"
);
kfree
(
MTD
);
return
NULL
;
}
}
}
}
if
(
priv
->
is_banked
==
1
)
strcat
(
Part
,
", banked"
);
// printk("Part: '%s'\n",Part);
memset
(
MTD
,
0
,
sizeof
(
*
MTD
));
// strlcpy(MTD->name,Part,sizeof(MTD->name));
MTD
->
name
=
map
->
name
;
MTD
->
type
=
MTD_NORFLASH
;
MTD
->
flags
=
MTD_CAP_NORFLASH
;
MTD
->
writesize
=
1
;
MTD
->
erasesize
=
SectorSize
*
(
map
->
buswidth
);
// printk("MTD->erasesize is %x\n",(unsigned int)MTD->erasesize);
MTD
->
size
=
priv
->
size
;
// printk("MTD->size is %x\n",(unsigned int)MTD->size);
//MTD->module = THIS_MODULE; // ? Maybe this should be the low level module?
MTD
->
erase
=
flash_erase
;
if
(
priv
->
is_banked
==
1
)
MTD
->
read
=
jedec_read_banked
;
else
MTD
->
read
=
jedec_read
;
MTD
->
write
=
flash_write
;
MTD
->
sync
=
jedec_sync
;
MTD
->
priv
=
map
;
map
->
fldrv_priv
=
priv
;
map
->
fldrv
=
&
jedec_chipdrv
;
__module_get
(
THIS_MODULE
);
return
MTD
;
}
/* Helper for the JEDEC function, JEDEC numbers all have odd parity */
static
int
checkparity
(
u_char
C
)
{
u_char
parity
=
0
;
while
(
C
!=
0
)
{
parity
^=
C
&
1
;
C
>>=
1
;
}
return
parity
==
1
;
}
/* Take an array of JEDEC numbers that represent interleved flash chips
and process them. Check to make sure they are good JEDEC numbers, look
them up and then add them to the chip list */
static
int
handle_jedecs
(
struct
map_info
*
map
,
__u8
*
Mfg
,
__u8
*
Id
,
unsigned
Count
,
unsigned
long
base
,
struct
jedec_private
*
priv
)
{
unsigned
I
,
J
;
unsigned
long
Size
;
unsigned
long
SectorSize
;
const
struct
JEDECTable
*
JEDEC
;
// Test #2 JEDEC numbers exhibit odd parity
for
(
I
=
0
;
I
!=
Count
;
I
++
)
{
if
(
checkparity
(
Mfg
[
I
])
==
0
||
checkparity
(
Id
[
I
])
==
0
)
return
0
;
}
// Finally, just make sure all the chip sizes are the same
JEDEC
=
jedec_idtoinf
(
Mfg
[
0
],
Id
[
0
]);
if
(
JEDEC
==
0
)
{
printk
(
"mtd: Found JEDEC flash chip, but do not have a table entry for %x:%x
\n
"
,
Mfg
[
0
],
Mfg
[
1
]);
return
0
;
}
Size
=
JEDEC
->
size
;
SectorSize
=
JEDEC
->
sectorsize
;
for
(
I
=
0
;
I
!=
Count
;
I
++
)
{
JEDEC
=
jedec_idtoinf
(
Mfg
[
0
],
Id
[
0
]);
if
(
JEDEC
==
0
)
{
printk
(
"mtd: Found JEDEC flash chip, but do not have a table entry for %x:%x
\n
"
,
Mfg
[
0
],
Mfg
[
1
]);
return
0
;
}
if
(
Size
!=
JEDEC
->
size
||
SectorSize
!=
JEDEC
->
sectorsize
)
{
printk
(
"mtd: Failed. Interleved flash does not have matching characteristics
\n
"
);
return
0
;
}
}
// Load the Chips
for
(
I
=
0
;
I
!=
MAX_JEDEC_CHIPS
;
I
++
)
{
if
(
priv
->
chips
[
I
].
jedec
==
0
)
break
;
}
if
(
I
+
Count
>
MAX_JEDEC_CHIPS
)
{
printk
(
"mtd: Device has too many chips. Increase MAX_JEDEC_CHIPS
\n
"
);
return
0
;
}
// Add them to the table
for
(
J
=
0
;
J
!=
Count
;
J
++
)
{
unsigned
long
Bank
;
JEDEC
=
jedec_idtoinf
(
Mfg
[
J
],
Id
[
J
]);
priv
->
chips
[
I
].
jedec
=
(
Mfg
[
J
]
<<
8
)
|
Id
[
J
];
priv
->
chips
[
I
].
size
=
JEDEC
->
size
;
priv
->
chips
[
I
].
sectorsize
=
JEDEC
->
sectorsize
;
priv
->
chips
[
I
].
base
=
base
+
J
;
priv
->
chips
[
I
].
datashift
=
J
*
8
;
priv
->
chips
[
I
].
capabilities
=
JEDEC
->
capabilities
;
priv
->
chips
[
I
].
offset
=
priv
->
size
+
J
;
// log2 n :|
priv
->
chips
[
I
].
addrshift
=
0
;
for
(
Bank
=
Count
;
Bank
!=
1
;
Bank
>>=
1
,
priv
->
chips
[
I
].
addrshift
++
);
// Determine how filled this bank is.
Bank
=
base
&
(
~
(
my_bank_size
-
1
));
if
(
priv
->
bank_fill
[
Bank
/
my_bank_size
]
<
base
+
(
JEDEC
->
size
<<
priv
->
chips
[
I
].
addrshift
)
-
Bank
)
priv
->
bank_fill
[
Bank
/
my_bank_size
]
=
base
+
(
JEDEC
->
size
<<
priv
->
chips
[
I
].
addrshift
)
-
Bank
;
I
++
;
}
priv
->
size
+=
priv
->
chips
[
I
-
1
].
size
*
Count
;
return
priv
->
chips
[
I
-
1
].
size
;
}
/* Lookup the chip information from the JEDEC ID table. */
static
const
struct
JEDECTable
*
jedec_idtoinf
(
__u8
mfr
,
__u8
id
)
{
__u16
Id
=
(
mfr
<<
8
)
|
id
;
unsigned
long
I
=
0
;
for
(
I
=
0
;
JEDEC_table
[
I
].
jedec
!=
0
;
I
++
)
if
(
JEDEC_table
[
I
].
jedec
==
Id
)
return
JEDEC_table
+
I
;
return
NULL
;
}
// Look for flash using an 8 bit bus interface
static
int
jedec_probe8
(
struct
map_info
*
map
,
unsigned
long
base
,
struct
jedec_private
*
priv
)
{
#define flread(x) map_read8(map,base+x)
#define flwrite(v,x) map_write8(map,v,base+x)
const
unsigned
long
AutoSel1
=
0xAA
;
const
unsigned
long
AutoSel2
=
0x55
;
const
unsigned
long
AutoSel3
=
0x90
;
const
unsigned
long
Reset
=
0xF0
;
__u32
OldVal
;
__u8
Mfg
[
1
];
__u8
Id
[
1
];
unsigned
I
;
unsigned
long
Size
;
// Wait for any write/erase operation to settle
OldVal
=
flread
(
base
);
for
(
I
=
0
;
OldVal
!=
flread
(
base
)
&&
I
<
10000
;
I
++
)
OldVal
=
flread
(
base
);
// Reset the chip
flwrite
(
Reset
,
0x555
);
// Send the sequence
flwrite
(
AutoSel1
,
0x555
);
flwrite
(
AutoSel2
,
0x2AA
);
flwrite
(
AutoSel3
,
0x555
);
// Get the JEDEC numbers
Mfg
[
0
]
=
flread
(
0
);
Id
[
0
]
=
flread
(
1
);
// printk("Mfg is %x, Id is %x\n",Mfg[0],Id[0]);
Size
=
handle_jedecs
(
map
,
Mfg
,
Id
,
1
,
base
,
priv
);
// printk("handle_jedecs Size is %x\n",(unsigned int)Size);
if
(
Size
==
0
)
{
flwrite
(
Reset
,
0x555
);
return
0
;
}
// Reset.
flwrite
(
Reset
,
0x555
);
return
1
;
#undef flread
#undef flwrite
}
// Look for flash using a 16 bit bus interface (ie 2 8-bit chips)
static
int
jedec_probe16
(
struct
map_info
*
map
,
unsigned
long
base
,
struct
jedec_private
*
priv
)
{
return
0
;
}
// Look for flash using a 32 bit bus interface (ie 4 8-bit chips)
static
int
jedec_probe32
(
struct
map_info
*
map
,
unsigned
long
base
,
struct
jedec_private
*
priv
)
{
#define flread(x) map_read32(map,base+((x)<<2))
#define flwrite(v,x) map_write32(map,v,base+((x)<<2))
const
unsigned
long
AutoSel1
=
0xAAAAAAAA
;
const
unsigned
long
AutoSel2
=
0x55555555
;
const
unsigned
long
AutoSel3
=
0x90909090
;
const
unsigned
long
Reset
=
0xF0F0F0F0
;
__u32
OldVal
;
__u8
Mfg
[
4
];
__u8
Id
[
4
];
unsigned
I
;
unsigned
long
Size
;
// Wait for any write/erase operation to settle
OldVal
=
flread
(
base
);
for
(
I
=
0
;
OldVal
!=
flread
(
base
)
&&
I
<
10000
;
I
++
)
OldVal
=
flread
(
base
);
// Reset the chip
flwrite
(
Reset
,
0x555
);
// Send the sequence
flwrite
(
AutoSel1
,
0x555
);
flwrite
(
AutoSel2
,
0x2AA
);
flwrite
(
AutoSel3
,
0x555
);
// Test #1, JEDEC numbers are readable from 0x??00/0x??01
if
(
flread
(
0
)
!=
flread
(
0x100
)
||
flread
(
1
)
!=
flread
(
0x101
))
{
flwrite
(
Reset
,
0x555
);
return
0
;
}
// Split up the JEDEC numbers
OldVal
=
flread
(
0
);
for
(
I
=
0
;
I
!=
4
;
I
++
)
Mfg
[
I
]
=
(
OldVal
>>
(
I
*
8
));
OldVal
=
flread
(
1
);
for
(
I
=
0
;
I
!=
4
;
I
++
)
Id
[
I
]
=
(
OldVal
>>
(
I
*
8
));
Size
=
handle_jedecs
(
map
,
Mfg
,
Id
,
4
,
base
,
priv
);
if
(
Size
==
0
)
{
flwrite
(
Reset
,
0x555
);
return
0
;
}
/* Check if there is address wrap around within a single bank, if this
returns JEDEC numbers then we assume that it is wrap around. Notice
we call this routine with the JEDEC return still enabled, if two or
more flashes have a truncated address space the probe test will still
work */
if
(
base
+
(
Size
<<
2
)
+
0x555
<
map
->
size
&&
base
+
(
Size
<<
2
)
+
0x555
<
(
base
&
(
~
(
my_bank_size
-
1
)))
+
my_bank_size
)
{
if
(
flread
(
base
+
Size
)
!=
flread
(
base
+
Size
+
0x100
)
||
flread
(
base
+
Size
+
1
)
!=
flread
(
base
+
Size
+
0x101
))
{
jedec_probe32
(
map
,
base
+
Size
,
priv
);
}
}
// Reset.
flwrite
(
0xF0F0F0F0
,
0x555
);
return
1
;
#undef flread
#undef flwrite
}
/* Linear read. */
static
int
jedec_read
(
struct
mtd_info
*
mtd
,
loff_t
from
,
size_t
len
,
size_t
*
retlen
,
u_char
*
buf
)
{
struct
map_info
*
map
=
mtd
->
priv
;
map_copy_from
(
map
,
buf
,
from
,
len
);
*
retlen
=
len
;
return
0
;
}
/* Banked read. Take special care to jump past the holes in the bank
mapping. This version assumes symetry in the holes.. */
static
int
jedec_read_banked
(
struct
mtd_info
*
mtd
,
loff_t
from
,
size_t
len
,
size_t
*
retlen
,
u_char
*
buf
)
{
struct
map_info
*
map
=
mtd
->
priv
;
struct
jedec_private
*
priv
=
map
->
fldrv_priv
;
*
retlen
=
0
;
while
(
len
>
0
)
{
// Determine what bank and offset into that bank the first byte is
unsigned
long
bank
=
from
&
(
~
(
priv
->
bank_fill
[
0
]
-
1
));
unsigned
long
offset
=
from
&
(
priv
->
bank_fill
[
0
]
-
1
);
unsigned
long
get
=
len
;
if
(
priv
->
bank_fill
[
0
]
-
offset
<
len
)
get
=
priv
->
bank_fill
[
0
]
-
offset
;
bank
/=
priv
->
bank_fill
[
0
];
map_copy_from
(
map
,
buf
+
*
retlen
,
bank
*
my_bank_size
+
offset
,
get
);
len
-=
get
;
*
retlen
+=
get
;
from
+=
get
;
}
return
0
;
}
/* Pass the flags value that the flash return before it re-entered read
mode. */
static
void
jedec_flash_failed
(
unsigned
char
code
)
{
/* Bit 5 being high indicates that there was an internal device
failure, erasure time limits exceeded or something */
if
((
code
&
(
1
<<
5
))
!=
0
)
{
printk
(
"mtd: Internal Flash failure
\n
"
);
return
;
}
printk
(
"mtd: Programming didn't take
\n
"
);
}
/* This uses the erasure function described in the AMD Flash Handbook,
it will work for flashes with a fixed sector size only. Flashes with
a selection of sector sizes (ie the AMD Am29F800B) will need a different
routine. This routine tries to parallize erasing multiple chips/sectors
where possible */
static
int
flash_erase
(
struct
mtd_info
*
mtd
,
struct
erase_info
*
instr
)
{
// Does IO to the currently selected chip
#define flread(x) map_read8(map,chip->base+((x)<<chip->addrshift))
#define flwrite(v,x) map_write8(map,v,chip->base+((x)<<chip->addrshift))
unsigned
long
Time
=
0
;
unsigned
long
NoTime
=
0
;
unsigned
long
start
=
instr
->
addr
,
len
=
instr
->
len
;
unsigned
int
I
;
struct
map_info
*
map
=
mtd
->
priv
;
struct
jedec_private
*
priv
=
map
->
fldrv_priv
;
// Verify the arguments..
if
(
start
+
len
>
mtd
->
size
||
(
start
%
mtd
->
erasesize
)
!=
0
||
(
len
%
mtd
->
erasesize
)
!=
0
||
(
len
/
mtd
->
erasesize
)
==
0
)
return
-
EINVAL
;
jedec_flash_chip_scan
(
priv
,
start
,
len
);
// Start the erase sequence on each chip
for
(
I
=
0
;
priv
->
chips
[
I
].
jedec
!=
0
&&
I
<
MAX_JEDEC_CHIPS
;
I
++
)
{
unsigned
long
off
;
struct
jedec_flash_chip
*
chip
=
priv
->
chips
+
I
;
if
(
chip
->
length
==
0
)
continue
;
if
(
chip
->
start
+
chip
->
length
>
chip
->
size
)
{
printk
(
"DIE
\n
"
);
return
-
EIO
;
}
flwrite
(
0xF0
,
chip
->
start
+
0x555
);
flwrite
(
0xAA
,
chip
->
start
+
0x555
);
flwrite
(
0x55
,
chip
->
start
+
0x2AA
);
flwrite
(
0x80
,
chip
->
start
+
0x555
);
flwrite
(
0xAA
,
chip
->
start
+
0x555
);
flwrite
(
0x55
,
chip
->
start
+
0x2AA
);
/* Once we start selecting the erase sectors the delay between each
command must not exceed 50us or it will immediately start erasing
and ignore the other sectors */
for
(
off
=
0
;
off
<
len
;
off
+=
chip
->
sectorsize
)
{
// Check to make sure we didn't timeout
flwrite
(
0x30
,
chip
->
start
+
off
);
if
(
off
==
0
)
continue
;
if
((
flread
(
chip
->
start
+
off
)
&
(
1
<<
3
))
!=
0
)
{
printk
(
"mtd: Ack! We timed out the erase timer!
\n
"
);
return
-
EIO
;
}
}
}
/* We could split this into a timer routine and return early, performing
background erasure.. Maybe later if the need warrents */
/* Poll the flash for erasure completion, specs say this can take as long
as 480 seconds to do all the sectors (for a 2 meg flash).
Erasure time is dependent on chip age, temp and wear.. */
/* This being a generic routine assumes a 32 bit bus. It does read32s
and bundles interleved chips into the same grouping. This will work
for all bus widths */
Time
=
0
;
NoTime
=
0
;
for
(
I
=
0
;
priv
->
chips
[
I
].
jedec
!=
0
&&
I
<
MAX_JEDEC_CHIPS
;
I
++
)
{
struct
jedec_flash_chip
*
chip
=
priv
->
chips
+
I
;
unsigned
long
off
=
0
;
unsigned
todo
[
4
]
=
{
0
,
0
,
0
,
0
};
unsigned
todo_left
=
0
;
unsigned
J
;
if
(
chip
->
length
==
0
)
continue
;
/* Find all chips in this data line, realistically this is all
or nothing up to the interleve count */
for
(
J
=
0
;
priv
->
chips
[
J
].
jedec
!=
0
&&
J
<
MAX_JEDEC_CHIPS
;
J
++
)
{
if
((
priv
->
chips
[
J
].
base
&
(
~
((
1
<<
chip
->
addrshift
)
-
1
)))
==
(
chip
->
base
&
(
~
((
1
<<
chip
->
addrshift
)
-
1
))))
{
todo_left
++
;
todo
[
priv
->
chips
[
J
].
base
&
((
1
<<
chip
->
addrshift
)
-
1
)]
=
1
;
}
}
/* printk("todo: %x %x %x %x\n",(short)todo[0],(short)todo[1],
(short)todo[2],(short)todo[3]);
*/
while
(
1
)
{
__u32
Last
[
4
];
unsigned
long
Count
=
0
;
/* During erase bit 7 is held low and bit 6 toggles, we watch this,
should it stop toggling or go high then the erase is completed,
or this is not really flash ;> */
switch
(
map
->
buswidth
)
{
case
1
:
Last
[
0
]
=
map_read8
(
map
,(
chip
->
base
>>
chip
->
addrshift
)
+
chip
->
start
+
off
);
Last
[
1
]
=
map_read8
(
map
,(
chip
->
base
>>
chip
->
addrshift
)
+
chip
->
start
+
off
);
Last
[
2
]
=
map_read8
(
map
,(
chip
->
base
>>
chip
->
addrshift
)
+
chip
->
start
+
off
);
break
;
case
2
:
Last
[
0
]
=
map_read16
(
map
,(
chip
->
base
>>
chip
->
addrshift
)
+
chip
->
start
+
off
);
Last
[
1
]
=
map_read16
(
map
,(
chip
->
base
>>
chip
->
addrshift
)
+
chip
->
start
+
off
);
Last
[
2
]
=
map_read16
(
map
,(
chip
->
base
>>
chip
->
addrshift
)
+
chip
->
start
+
off
);
break
;
case
3
:
Last
[
0
]
=
map_read32
(
map
,(
chip
->
base
>>
chip
->
addrshift
)
+
chip
->
start
+
off
);
Last
[
1
]
=
map_read32
(
map
,(
chip
->
base
>>
chip
->
addrshift
)
+
chip
->
start
+
off
);
Last
[
2
]
=
map_read32
(
map
,(
chip
->
base
>>
chip
->
addrshift
)
+
chip
->
start
+
off
);
break
;
}
Count
=
3
;
while
(
todo_left
!=
0
)
{
for
(
J
=
0
;
J
!=
4
;
J
++
)
{
__u8
Byte1
=
(
Last
[(
Count
-
1
)
%
4
]
>>
(
J
*
8
))
&
0xFF
;
__u8
Byte2
=
(
Last
[(
Count
-
2
)
%
4
]
>>
(
J
*
8
))
&
0xFF
;
__u8
Byte3
=
(
Last
[(
Count
-
3
)
%
4
]
>>
(
J
*
8
))
&
0xFF
;
if
(
todo
[
J
]
==
0
)
continue
;
if
((
Byte1
&
(
1
<<
7
))
==
0
&&
Byte1
!=
Byte2
)
{
// printk("Check %x %x %x\n",(short)J,(short)Byte1,(short)Byte2);
continue
;
}
if
(
Byte1
==
Byte2
)
{
jedec_flash_failed
(
Byte3
);
return
-
EIO
;
}
todo
[
J
]
=
0
;
todo_left
--
;
}
/* if (NoTime == 0)
Time += HZ/10 - schedule_timeout(HZ/10);*/
NoTime
=
0
;
switch
(
map
->
buswidth
)
{
case
1
:
Last
[
Count
%
4
]
=
map_read8
(
map
,(
chip
->
base
>>
chip
->
addrshift
)
+
chip
->
start
+
off
);
break
;
case
2
:
Last
[
Count
%
4
]
=
map_read16
(
map
,(
chip
->
base
>>
chip
->
addrshift
)
+
chip
->
start
+
off
);
break
;
case
4
:
Last
[
Count
%
4
]
=
map_read32
(
map
,(
chip
->
base
>>
chip
->
addrshift
)
+
chip
->
start
+
off
);
break
;
}
Count
++
;
/* // Count time, max of 15s per sector (according to AMD)
if (Time > 15*len/mtd->erasesize*HZ)
{
printk("mtd: Flash Erase Timed out\n");
return -EIO;
} */
}
// Skip to the next chip if we used chip erase
if
(
chip
->
length
==
chip
->
size
)
off
=
chip
->
size
;
else
off
+=
chip
->
sectorsize
;
if
(
off
>=
chip
->
length
)
break
;
NoTime
=
1
;
}
for
(
J
=
0
;
priv
->
chips
[
J
].
jedec
!=
0
&&
J
<
MAX_JEDEC_CHIPS
;
J
++
)
{
if
((
priv
->
chips
[
J
].
base
&
(
~
((
1
<<
chip
->
addrshift
)
-
1
)))
==
(
chip
->
base
&
(
~
((
1
<<
chip
->
addrshift
)
-
1
))))
priv
->
chips
[
J
].
length
=
0
;
}
}
//printk("done\n");
instr
->
state
=
MTD_ERASE_DONE
;
mtd_erase_callback
(
instr
);
return
0
;
#undef flread
#undef flwrite
}
/* This is the simple flash writing function. It writes to every byte, in
sequence. It takes care of how to properly address the flash if
the flash is interleved. It can only be used if all the chips in the
array are identical!*/
static
int
flash_write
(
struct
mtd_info
*
mtd
,
loff_t
start
,
size_t
len
,
size_t
*
retlen
,
const
u_char
*
buf
)
{
/* Does IO to the currently selected chip. It takes the bank addressing
base (which is divisible by the chip size) adds the necessary lower bits
of addrshift (interleave index) and then adds the control register index. */
#define flread(x) map_read8(map,base+(off&((1<<chip->addrshift)-1))+((x)<<chip->addrshift))
#define flwrite(v,x) map_write8(map,v,base+(off&((1<<chip->addrshift)-1))+((x)<<chip->addrshift))
struct
map_info
*
map
=
mtd
->
priv
;
struct
jedec_private
*
priv
=
map
->
fldrv_priv
;
unsigned
long
base
;
unsigned
long
off
;
size_t
save_len
=
len
;
if
(
start
+
len
>
mtd
->
size
)
return
-
EIO
;
//printk("Here");
//printk("flash_write: start is %x, len is %x\n",start,(unsigned long)len);
while
(
len
!=
0
)
{
struct
jedec_flash_chip
*
chip
=
priv
->
chips
;
unsigned
long
bank
;
unsigned
long
boffset
;
// Compute the base of the flash.
off
=
((
unsigned
long
)
start
)
%
(
chip
->
size
<<
chip
->
addrshift
);
base
=
start
-
off
;
// Perform banked addressing translation.
bank
=
base
&
(
~
(
priv
->
bank_fill
[
0
]
-
1
));
boffset
=
base
&
(
priv
->
bank_fill
[
0
]
-
1
);
bank
=
(
bank
/
priv
->
bank_fill
[
0
])
*
my_bank_size
;
base
=
bank
+
boffset
;
// printk("Flasing %X %X %X\n",base,chip->size,len);
// printk("off is %x, compare with %x\n",off,chip->size << chip->addrshift);
// Loop over this page
for
(;
off
!=
(
chip
->
size
<<
chip
->
addrshift
)
&&
len
!=
0
;
start
++
,
len
--
,
off
++
,
buf
++
)
{
unsigned
char
oldbyte
=
map_read8
(
map
,
base
+
off
);
unsigned
char
Last
[
4
];
unsigned
long
Count
=
0
;
if
(
oldbyte
==
*
buf
)
{
// printk("oldbyte and *buf is %x,len is %x\n",oldbyte,len);
continue
;
}
if
(((
~
oldbyte
)
&
*
buf
)
!=
0
)
printk
(
"mtd: warn: Trying to set a 0 to a 1
\n
"
);
// Write
flwrite
(
0xAA
,
0x555
);
flwrite
(
0x55
,
0x2AA
);
flwrite
(
0xA0
,
0x555
);
map_write8
(
map
,
*
buf
,
base
+
off
);
Last
[
0
]
=
map_read8
(
map
,
base
+
off
);
Last
[
1
]
=
map_read8
(
map
,
base
+
off
);
Last
[
2
]
=
map_read8
(
map
,
base
+
off
);
/* Wait for the flash to finish the operation. We store the last 4
status bytes that have been retrieved so we can determine why
it failed. The toggle bits keep toggling when there is a
failure */
for
(
Count
=
3
;
Last
[(
Count
-
1
)
%
4
]
!=
Last
[(
Count
-
2
)
%
4
]
&&
Count
<
10000
;
Count
++
)
Last
[
Count
%
4
]
=
map_read8
(
map
,
base
+
off
);
if
(
Last
[(
Count
-
1
)
%
4
]
!=
*
buf
)
{
jedec_flash_failed
(
Last
[(
Count
-
3
)
%
4
]);
return
-
EIO
;
}
}
}
*
retlen
=
save_len
;
return
0
;
}
/* This is used to enhance the speed of the erase routine,
when things are being done to multiple chips it is possible to
parallize the operations, particularly full memory erases of multi
chip memories benifit */
static
void
jedec_flash_chip_scan
(
struct
jedec_private
*
priv
,
unsigned
long
start
,
unsigned
long
len
)
{
unsigned
int
I
;
// Zero the records
for
(
I
=
0
;
priv
->
chips
[
I
].
jedec
!=
0
&&
I
<
MAX_JEDEC_CHIPS
;
I
++
)
priv
->
chips
[
I
].
start
=
priv
->
chips
[
I
].
length
=
0
;
// Intersect the region with each chip
for
(
I
=
0
;
priv
->
chips
[
I
].
jedec
!=
0
&&
I
<
MAX_JEDEC_CHIPS
;
I
++
)
{
struct
jedec_flash_chip
*
chip
=
priv
->
chips
+
I
;
unsigned
long
ByteStart
;
unsigned
long
ChipEndByte
=
chip
->
offset
+
(
chip
->
size
<<
chip
->
addrshift
);
// End is before this chip or the start is after it
if
(
start
+
len
<
chip
->
offset
||
ChipEndByte
-
(
1
<<
chip
->
addrshift
)
<
start
)
continue
;
if
(
start
<
chip
->
offset
)
{
ByteStart
=
chip
->
offset
;
chip
->
start
=
0
;
}
else
{
chip
->
start
=
(
start
-
chip
->
offset
+
(
1
<<
chip
->
addrshift
)
-
1
)
>>
chip
->
addrshift
;
ByteStart
=
start
;
}
if
(
start
+
len
>=
ChipEndByte
)
chip
->
length
=
(
ChipEndByte
-
ByteStart
)
>>
chip
->
addrshift
;
else
chip
->
length
=
(
start
+
len
-
ByteStart
+
(
1
<<
chip
->
addrshift
)
-
1
)
>>
chip
->
addrshift
;
}
}
int
__init
jedec_init
(
void
)
{
register_mtd_chip_driver
(
&
jedec_chipdrv
);
return
0
;
}
static
void
__exit
jedec_exit
(
void
)
{
unregister_mtd_chip_driver
(
&
jedec_chipdrv
);
}
module_init
(
jedec_init
);
module_exit
(
jedec_exit
);
MODULE_LICENSE
(
"GPL"
);
MODULE_AUTHOR
(
"Jason Gunthorpe <jgg@deltatee.com> et al."
);
MODULE_DESCRIPTION
(
"Old MTD chip driver for JEDEC-compliant flash chips"
);
drivers/mtd/chips/sharp.c
deleted
100644 → 0
View file @
42f209d3
/*
* MTD chip driver for pre-CFI Sharp flash chips
*
* Copyright 2000,2001 David A. Schleef <ds@schleef.org>
* 2000,2001 Lineo, Inc.
*
* $Id: sharp.c,v 1.17 2005/11/29 14:28:28 gleixner Exp $
*
* Devices supported:
* LH28F016SCT Symmetrical block flash memory, 2Mx8
* LH28F008SCT Symmetrical block flash memory, 1Mx8
*
* Documentation:
* http://www.sharpmeg.com/datasheets/memic/flashcmp/
* http://www.sharpmeg.com/datasheets/memic/flashcmp/01symf/16m/016sctl9.pdf
* 016sctl9.pdf
*
* Limitations:
* This driver only supports 4x1 arrangement of chips.
* Not tested on anything but PowerPC.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/mtd/map.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/cfi.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/slab.h>
#define CMD_RESET 0xffffffff
#define CMD_READ_ID 0x90909090
#define CMD_READ_STATUS 0x70707070
#define CMD_CLEAR_STATUS 0x50505050
#define CMD_BLOCK_ERASE_1 0x20202020
#define CMD_BLOCK_ERASE_2 0xd0d0d0d0
#define CMD_BYTE_WRITE 0x40404040
#define CMD_SUSPEND 0xb0b0b0b0
#define CMD_RESUME 0xd0d0d0d0
#define CMD_SET_BLOCK_LOCK_1 0x60606060
#define CMD_SET_BLOCK_LOCK_2 0x01010101
#define CMD_SET_MASTER_LOCK_1 0x60606060
#define CMD_SET_MASTER_LOCK_2 0xf1f1f1f1
#define CMD_CLEAR_BLOCK_LOCKS_1 0x60606060
#define CMD_CLEAR_BLOCK_LOCKS_2 0xd0d0d0d0
#define SR_READY 0x80808080 // 1 = ready
#define SR_ERASE_SUSPEND 0x40404040 // 1 = block erase suspended
#define SR_ERROR_ERASE 0x20202020 // 1 = error in block erase or clear lock bits
#define SR_ERROR_WRITE 0x10101010 // 1 = error in byte write or set lock bit
#define SR_VPP 0x08080808 // 1 = Vpp is low
#define SR_WRITE_SUSPEND 0x04040404 // 1 = byte write suspended
#define SR_PROTECT 0x02020202 // 1 = lock bit set
#define SR_RESERVED 0x01010101
#define SR_ERRORS (SR_ERROR_ERASE|SR_ERROR_WRITE|SR_VPP|SR_PROTECT)
/* Configuration options */
#undef AUTOUNLOCK
/* automatically unlocks blocks before erasing */
static
struct
mtd_info
*
sharp_probe
(
struct
map_info
*
);
static
int
sharp_probe_map
(
struct
map_info
*
map
,
struct
mtd_info
*
mtd
);
static
int
sharp_read
(
struct
mtd_info
*
mtd
,
loff_t
from
,
size_t
len
,
size_t
*
retlen
,
u_char
*
buf
);
static
int
sharp_write
(
struct
mtd_info
*
mtd
,
loff_t
from
,
size_t
len
,
size_t
*
retlen
,
const
u_char
*
buf
);
static
int
sharp_erase
(
struct
mtd_info
*
mtd
,
struct
erase_info
*
instr
);
static
void
sharp_sync
(
struct
mtd_info
*
mtd
);
static
int
sharp_suspend
(
struct
mtd_info
*
mtd
);
static
void
sharp_resume
(
struct
mtd_info
*
mtd
);
static
void
sharp_destroy
(
struct
mtd_info
*
mtd
);
static
int
sharp_write_oneword
(
struct
map_info
*
map
,
struct
flchip
*
chip
,
unsigned
long
adr
,
__u32
datum
);
static
int
sharp_erase_oneblock
(
struct
map_info
*
map
,
struct
flchip
*
chip
,
unsigned
long
adr
);
#ifdef AUTOUNLOCK
static
void
sharp_unlock_oneblock
(
struct
map_info
*
map
,
struct
flchip
*
chip
,
unsigned
long
adr
);
#endif
struct
sharp_info
{
struct
flchip
*
chip
;
int
bogus
;
int
chipshift
;
int
numchips
;
struct
flchip
chips
[
1
];
};
static
void
sharp_destroy
(
struct
mtd_info
*
mtd
);
static
struct
mtd_chip_driver
sharp_chipdrv
=
{
.
probe
=
sharp_probe
,
.
destroy
=
sharp_destroy
,
.
name
=
"sharp"
,
.
module
=
THIS_MODULE
};
static
struct
mtd_info
*
sharp_probe
(
struct
map_info
*
map
)
{
struct
mtd_info
*
mtd
=
NULL
;
struct
sharp_info
*
sharp
=
NULL
;
int
width
;
mtd
=
kzalloc
(
sizeof
(
*
mtd
),
GFP_KERNEL
);
if
(
!
mtd
)
return
NULL
;
sharp
=
kzalloc
(
sizeof
(
*
sharp
),
GFP_KERNEL
);
if
(
!
sharp
)
{
kfree
(
mtd
);
return
NULL
;
}
width
=
sharp_probe_map
(
map
,
mtd
);
if
(
!
width
){
kfree
(
mtd
);
kfree
(
sharp
);
return
NULL
;
}
mtd
->
priv
=
map
;
mtd
->
type
=
MTD_NORFLASH
;
mtd
->
erase
=
sharp_erase
;
mtd
->
read
=
sharp_read
;
mtd
->
write
=
sharp_write
;
mtd
->
sync
=
sharp_sync
;
mtd
->
suspend
=
sharp_suspend
;
mtd
->
resume
=
sharp_resume
;
mtd
->
flags
=
MTD_CAP_NORFLASH
;
mtd
->
writesize
=
1
;
mtd
->
name
=
map
->
name
;
sharp
->
chipshift
=
23
;
sharp
->
numchips
=
1
;
sharp
->
chips
[
0
].
start
=
0
;
sharp
->
chips
[
0
].
state
=
FL_READY
;
sharp
->
chips
[
0
].
mutex
=
&
sharp
->
chips
[
0
].
_spinlock
;
sharp
->
chips
[
0
].
word_write_time
=
0
;
init_waitqueue_head
(
&
sharp
->
chips
[
0
].
wq
);
spin_lock_init
(
&
sharp
->
chips
[
0
].
_spinlock
);
map
->
fldrv
=
&
sharp_chipdrv
;
map
->
fldrv_priv
=
sharp
;
__module_get
(
THIS_MODULE
);
return
mtd
;
}
static
inline
void
sharp_send_cmd
(
struct
map_info
*
map
,
unsigned
long
cmd
,
unsigned
long
adr
)
{
map_word
map_cmd
;
map_cmd
.
x
[
0
]
=
cmd
;
map_write
(
map
,
map_cmd
,
adr
);
}
static
int
sharp_probe_map
(
struct
map_info
*
map
,
struct
mtd_info
*
mtd
)
{
map_word
tmp
,
read0
,
read4
;
unsigned
long
base
=
0
;
int
width
=
4
;
tmp
=
map_read
(
map
,
base
+
0
);
sharp_send_cmd
(
map
,
CMD_READ_ID
,
base
+
0
);
read0
=
map_read
(
map
,
base
+
0
);
read4
=
map_read
(
map
,
base
+
4
);
if
(
read0
.
x
[
0
]
==
0x89898989
){
printk
(
"Looks like sharp flash
\n
"
);
switch
(
read4
.
x
[
0
]){
case
0xaaaaaaaa
:
case
0xa0a0a0a0
:
/* aa - LH28F016SCT-L95 2Mx8, 32 64k blocks*/
/* a0 - LH28F016SCT-Z4 2Mx8, 32 64k blocks*/
mtd
->
erasesize
=
0x10000
*
width
;
mtd
->
size
=
0x200000
*
width
;
return
width
;
case
0xa6a6a6a6
:
/* a6 - LH28F008SCT-L12 1Mx8, 16 64k blocks*/
/* a6 - LH28F008SCR-L85 1Mx8, 16 64k blocks*/
mtd
->
erasesize
=
0x10000
*
width
;
mtd
->
size
=
0x100000
*
width
;
return
width
;
#if 0
case 0x00000000: /* unknown */
/* XX - LH28F004SCT 512kx8, 8 64k blocks*/
mtd->erasesize = 0x10000 * width;
mtd->size = 0x80000 * width;
return width;
#endif
default:
printk
(
"Sort-of looks like sharp flash, 0x%08lx 0x%08lx
\n
"
,
read0
.
x
[
0
],
read4
.
x
[
0
]);
}
}
else
if
((
map_read
(
map
,
base
+
0
).
x
[
0
]
==
CMD_READ_ID
)){
/* RAM, probably */
printk
(
"Looks like RAM
\n
"
);
map_write
(
map
,
tmp
,
base
+
0
);
}
else
{
printk
(
"Doesn't look like sharp flash, 0x%08lx 0x%08lx
\n
"
,
read0
.
x
[
0
],
read4
.
x
[
0
]);
}
return
0
;
}
/* This function returns with the chip->mutex lock held. */
static
int
sharp_wait
(
struct
map_info
*
map
,
struct
flchip
*
chip
)
{
int
i
;
map_word
status
;
unsigned
long
timeo
=
jiffies
+
HZ
;
DECLARE_WAITQUEUE
(
wait
,
current
);
int
adr
=
0
;
retry:
spin_lock_bh
(
chip
->
mutex
);
switch
(
chip
->
state
){
case
FL_READY
:
sharp_send_cmd
(
map
,
CMD_READ_STATUS
,
adr
);
chip
->
state
=
FL_STATUS
;
case
FL_STATUS
:
for
(
i
=
0
;
i
<
100
;
i
++
){
status
=
map_read
(
map
,
adr
);
if
((
status
.
x
[
0
]
&
SR_READY
)
==
SR_READY
)
break
;
udelay
(
1
);
}
break
;
default:
printk
(
"Waiting for chip
\n
"
);
set_current_state
(
TASK_INTERRUPTIBLE
);
add_wait_queue
(
&
chip
->
wq
,
&
wait
);
spin_unlock_bh
(
chip
->
mutex
);
schedule
();
remove_wait_queue
(
&
chip
->
wq
,
&
wait
);
if
(
signal_pending
(
current
))
return
-
EINTR
;
timeo
=
jiffies
+
HZ
;
goto
retry
;
}
sharp_send_cmd
(
map
,
CMD_RESET
,
adr
);
chip
->
state
=
FL_READY
;
return
0
;
}
static
void
sharp_release
(
struct
flchip
*
chip
)
{
wake_up
(
&
chip
->
wq
);
spin_unlock_bh
(
chip
->
mutex
);
}
static
int
sharp_read
(
struct
mtd_info
*
mtd
,
loff_t
from
,
size_t
len
,
size_t
*
retlen
,
u_char
*
buf
)
{
struct
map_info
*
map
=
mtd
->
priv
;
struct
sharp_info
*
sharp
=
map
->
fldrv_priv
;
int
chipnum
;
int
ret
=
0
;
int
ofs
=
0
;
chipnum
=
(
from
>>
sharp
->
chipshift
);
ofs
=
from
&
((
1
<<
sharp
->
chipshift
)
-
1
);
*
retlen
=
0
;
while
(
len
){
unsigned
long
thislen
;
if
(
chipnum
>=
sharp
->
numchips
)
break
;
thislen
=
len
;
if
(
ofs
+
thislen
>=
(
1
<<
sharp
->
chipshift
))
thislen
=
(
1
<<
sharp
->
chipshift
)
-
ofs
;
ret
=
sharp_wait
(
map
,
&
sharp
->
chips
[
chipnum
]);
if
(
ret
<
0
)
break
;
map_copy_from
(
map
,
buf
,
ofs
,
thislen
);
sharp_release
(
&
sharp
->
chips
[
chipnum
]);
*
retlen
+=
thislen
;
len
-=
thislen
;
buf
+=
thislen
;
ofs
=
0
;
chipnum
++
;
}
return
ret
;
}
static
int
sharp_write
(
struct
mtd_info
*
mtd
,
loff_t
to
,
size_t
len
,
size_t
*
retlen
,
const
u_char
*
buf
)
{
struct
map_info
*
map
=
mtd
->
priv
;
struct
sharp_info
*
sharp
=
map
->
fldrv_priv
;
int
ret
=
0
;
int
i
,
j
;
int
chipnum
;
unsigned
long
ofs
;
union
{
u32
l
;
unsigned
char
uc
[
4
];
}
tbuf
;
*
retlen
=
0
;
while
(
len
){
tbuf
.
l
=
0xffffffff
;
chipnum
=
to
>>
sharp
->
chipshift
;
ofs
=
to
&
((
1
<<
sharp
->
chipshift
)
-
1
);
j
=
0
;
for
(
i
=
ofs
&
3
;
i
<
4
&&
len
;
i
++
){
tbuf
.
uc
[
i
]
=
*
buf
;
buf
++
;
to
++
;
len
--
;
j
++
;
}
sharp_write_oneword
(
map
,
&
sharp
->
chips
[
chipnum
],
ofs
&~
3
,
tbuf
.
l
);
if
(
ret
<
0
)
return
ret
;
(
*
retlen
)
+=
j
;
}
return
0
;
}
static
int
sharp_write_oneword
(
struct
map_info
*
map
,
struct
flchip
*
chip
,
unsigned
long
adr
,
__u32
datum
)
{
int
ret
;
int
timeo
;
int
try
;
int
i
;
map_word
data
,
status
;
status
.
x
[
0
]
=
0
;
ret
=
sharp_wait
(
map
,
chip
);
for
(
try
=
0
;
try
<
10
;
try
++
){
sharp_send_cmd
(
map
,
CMD_BYTE_WRITE
,
adr
);
/* cpu_to_le32 -> hack to fix the writel be->le conversion */
data
.
x
[
0
]
=
cpu_to_le32
(
datum
);
map_write
(
map
,
data
,
adr
);
chip
->
state
=
FL_WRITING
;
timeo
=
jiffies
+
(
HZ
/
2
);
sharp_send_cmd
(
map
,
CMD_READ_STATUS
,
adr
);
for
(
i
=
0
;
i
<
100
;
i
++
){
status
=
map_read
(
map
,
adr
);
if
((
status
.
x
[
0
]
&
SR_READY
)
==
SR_READY
)
break
;
}
if
(
i
==
100
){
printk
(
"sharp: timed out writing
\n
"
);
}
if
(
!
(
status
.
x
[
0
]
&
SR_ERRORS
))
break
;
printk
(
"sharp: error writing byte at addr=%08lx status=%08lx
\n
"
,
adr
,
status
.
x
[
0
]);
sharp_send_cmd
(
map
,
CMD_CLEAR_STATUS
,
adr
);
}
sharp_send_cmd
(
map
,
CMD_RESET
,
adr
);
chip
->
state
=
FL_READY
;
wake_up
(
&
chip
->
wq
);
spin_unlock_bh
(
chip
->
mutex
);
return
0
;
}
static
int
sharp_erase
(
struct
mtd_info
*
mtd
,
struct
erase_info
*
instr
)
{
struct
map_info
*
map
=
mtd
->
priv
;
struct
sharp_info
*
sharp
=
map
->
fldrv_priv
;
unsigned
long
adr
,
len
;
int
chipnum
,
ret
=
0
;
//printk("sharp_erase()\n");
if
(
instr
->
addr
&
(
mtd
->
erasesize
-
1
))
return
-
EINVAL
;
if
(
instr
->
len
&
(
mtd
->
erasesize
-
1
))
return
-
EINVAL
;
if
(
instr
->
len
+
instr
->
addr
>
mtd
->
size
)
return
-
EINVAL
;
chipnum
=
instr
->
addr
>>
sharp
->
chipshift
;
adr
=
instr
->
addr
&
((
1
<<
sharp
->
chipshift
)
-
1
);
len
=
instr
->
len
;
while
(
len
){
ret
=
sharp_erase_oneblock
(
map
,
&
sharp
->
chips
[
chipnum
],
adr
);
if
(
ret
)
return
ret
;
adr
+=
mtd
->
erasesize
;
len
-=
mtd
->
erasesize
;
if
(
adr
>>
sharp
->
chipshift
){
adr
=
0
;
chipnum
++
;
if
(
chipnum
>=
sharp
->
numchips
)
break
;
}
}
instr
->
state
=
MTD_ERASE_DONE
;
mtd_erase_callback
(
instr
);
return
0
;
}
static
int
sharp_do_wait_for_ready
(
struct
map_info
*
map
,
struct
flchip
*
chip
,
unsigned
long
adr
)
{
int
ret
;
unsigned
long
timeo
;
map_word
status
;
DECLARE_WAITQUEUE
(
wait
,
current
);
sharp_send_cmd
(
map
,
CMD_READ_STATUS
,
adr
);
status
=
map_read
(
map
,
adr
);
timeo
=
jiffies
+
HZ
;
while
(
time_before
(
jiffies
,
timeo
)){
sharp_send_cmd
(
map
,
CMD_READ_STATUS
,
adr
);
status
=
map_read
(
map
,
adr
);
if
((
status
.
x
[
0
]
&
SR_READY
)
==
SR_READY
){
ret
=
0
;
goto
out
;
}
set_current_state
(
TASK_INTERRUPTIBLE
);
add_wait_queue
(
&
chip
->
wq
,
&
wait
);
//spin_unlock_bh(chip->mutex);
schedule_timeout
(
1
);
schedule
();
remove_wait_queue
(
&
chip
->
wq
,
&
wait
);
//spin_lock_bh(chip->mutex);
if
(
signal_pending
(
current
)){
ret
=
-
EINTR
;
goto
out
;
}
}
ret
=
-
ETIME
;
out:
return
ret
;
}
static
int
sharp_erase_oneblock
(
struct
map_info
*
map
,
struct
flchip
*
chip
,
unsigned
long
adr
)
{
int
ret
;
//int timeo;
map_word
status
;
//int i;
//printk("sharp_erase_oneblock()\n");
#ifdef AUTOUNLOCK
/* This seems like a good place to do an unlock */
sharp_unlock_oneblock
(
map
,
chip
,
adr
);
#endif
sharp_send_cmd
(
map
,
CMD_BLOCK_ERASE_1
,
adr
);
sharp_send_cmd
(
map
,
CMD_BLOCK_ERASE_2
,
adr
);
chip
->
state
=
FL_ERASING
;
ret
=
sharp_do_wait_for_ready
(
map
,
chip
,
adr
);
if
(
ret
<
0
)
return
ret
;
sharp_send_cmd
(
map
,
CMD_READ_STATUS
,
adr
);
status
=
map_read
(
map
,
adr
);
if
(
!
(
status
.
x
[
0
]
&
SR_ERRORS
)){
sharp_send_cmd
(
map
,
CMD_RESET
,
adr
);
chip
->
state
=
FL_READY
;
//spin_unlock_bh(chip->mutex);
return
0
;
}
printk
(
"sharp: error erasing block at addr=%08lx status=%08lx
\n
"
,
adr
,
status
.
x
[
0
]);
sharp_send_cmd
(
map
,
CMD_CLEAR_STATUS
,
adr
);
//spin_unlock_bh(chip->mutex);
return
-
EIO
;
}
#ifdef AUTOUNLOCK
static
void
sharp_unlock_oneblock
(
struct
map_info
*
map
,
struct
flchip
*
chip
,
unsigned
long
adr
)
{
int
i
;
map_word
status
;
sharp_send_cmd
(
map
,
CMD_CLEAR_BLOCK_LOCKS_1
,
adr
);
sharp_send_cmd
(
map
,
CMD_CLEAR_BLOCK_LOCKS_2
,
adr
);
udelay
(
100
);
status
=
map_read
(
map
,
adr
);
printk
(
"status=%08lx
\n
"
,
status
.
x
[
0
]);
for
(
i
=
0
;
i
<
1000
;
i
++
){
//sharp_send_cmd(map, CMD_READ_STATUS, adr);
status
=
map_read
(
map
,
adr
);
if
((
status
.
x
[
0
]
&
SR_READY
)
==
SR_READY
)
break
;
udelay
(
100
);
}
if
(
i
==
1000
){
printk
(
"sharp: timed out unlocking block
\n
"
);
}
if
(
!
(
status
.
x
[
0
]
&
SR_ERRORS
)){
sharp_send_cmd
(
map
,
CMD_RESET
,
adr
);
chip
->
state
=
FL_READY
;
return
;
}
printk
(
"sharp: error unlocking block at addr=%08lx status=%08lx
\n
"
,
adr
,
status
.
x
[
0
]);
sharp_send_cmd
(
map
,
CMD_CLEAR_STATUS
,
adr
);
}
#endif
static
void
sharp_sync
(
struct
mtd_info
*
mtd
)
{
//printk("sharp_sync()\n");
}
static
int
sharp_suspend
(
struct
mtd_info
*
mtd
)
{
printk
(
"sharp_suspend()
\n
"
);
return
-
EINVAL
;
}
static
void
sharp_resume
(
struct
mtd_info
*
mtd
)
{
printk
(
"sharp_resume()
\n
"
);
}
static
void
sharp_destroy
(
struct
mtd_info
*
mtd
)
{
printk
(
"sharp_destroy()
\n
"
);
}
static
int
__init
sharp_probe_init
(
void
)
{
printk
(
"MTD Sharp chip driver <ds@lineo.com>
\n
"
);
register_mtd_chip_driver
(
&
sharp_chipdrv
);
return
0
;
}
static
void
__exit
sharp_probe_exit
(
void
)
{
unregister_mtd_chip_driver
(
&
sharp_chipdrv
);
}
module_init
(
sharp_probe_init
);
module_exit
(
sharp_probe_exit
);
MODULE_LICENSE
(
"GPL"
);
MODULE_AUTHOR
(
"David Schleef <ds@schleef.org>"
);
MODULE_DESCRIPTION
(
"Old MTD chip driver for pre-CFI Sharp flash chips"
);
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment