blob: e1ea3f42ddc518a034d0b247486b2188fb79a9dc [file] [log] [blame]
#include "../include/phynand.h"
extern int amlnand_init(unsigned char flag);
extern void amlchip_dumpinfo(struct amlnand_chip *aml_chip);
extern void amldev_dumpinfo(struct amlnand_phydev *phydev);
//static int plane_mode = 0;
//struct aml_nftl_dev * nftl_device;
/* just like memset function but the paraments' type is little different */
void *memset_nand_test(void *s, u32 c, size_t count)
{
u32 *sl = (u32 *) s;
u32 cl = 0;
//char *s8;
int i;
/* do it one word at a time (32 bits or 64 bits) while possible */
if ( ((ulong)s & (sizeof(*sl) - 1)) == 0) {
for (i = 0; i < sizeof(*sl); i++) {
cl <<= 8;
cl |= c & 0xff;
c =c >>8;
}
while (count >= sizeof(*sl)) {
*sl++ = cl;
count -= sizeof(*sl);
}
}
/* fill 8 bits at a time */
/*s8 = (char *)sl; //this can write one byte
while (count--)
*s8++ = c;*/
return s;
}
static int nand_erase_ops_test(struct amlnand_phydev *phydev, uint64_t off, uint64_t len)
{
//struct amlnand_chip *aml_chip = phydev->priv;
struct phydev_ops *devops = &(phydev->ops);
//struct hw_controller *controller = &(aml_chip->controller);
//struct chip_operation *operation = &(aml_chip->operation);
//struct chip_ops_para *ops_para = &(aml_chip->ops_para);
uint64_t erase_addr, erase_len, erase_off;
int ret = 0;
erase_addr = erase_off = off;
erase_len = len ;
if ((erase_off+erase_len) > phydev->size ) {
aml_nand_msg("nand write size is out of space");
ret= -NAND_ERASE_FAILED;
goto exit_error;
}
for (; erase_addr < erase_off + erase_len; erase_addr += phydev->erasesize) {
memset(devops, 0x0, sizeof(struct phydev_ops));
devops->addr = erase_addr;
devops->len = phydev->erasesize;
devops->mode = NAND_HW_ECC;
ret = phydev->block_isbad(phydev);
if (ret > 0) {
aml_nand_msg("Skipping bad block at 0x%08llx", erase_addr);
continue;
} else if (ret < 0) {
aml_nand_msg("nand get bad block failed: ret=%d at addr=%llx",ret, erase_addr);
ret = -NAND_ERASE_FAILED;
}
extern int nand_erase(struct amlnand_phydev *phydev);
ret = nand_erase(phydev);
if (ret < 0) {
aml_nand_msg("nand Erase failure: %d %llx", ret, erase_addr);
ret = -NAND_ERASE_FAILED;
}
}
exit_error:
return ret;
}
static int nand_read_ops_test(struct amlnand_phydev *phydev,uint64_t off , uint64_t len , unsigned char * dat_buf)
{
//struct amlnand_chip *aml_chip =phydev->priv;
struct phydev_ops *devops = &(phydev->ops);
uint64_t offset , write_len;
unsigned char * buffer = NULL;
int ret = 0;
offset = off;
write_len = len;
buffer = aml_nand_malloc(2 * phydev->writesize);
if (!buffer) {
aml_nand_msg("nand read test malloc failed");
ret = -NAND_READ_FAILED;
goto exit_error;
}
if (!dat_buf) {
aml_nand_msg("nand read no buf");
return -NAND_READ_FAILED;
}
if ((offset & (phydev->writesize - 1)) != 0 ||(write_len & (phydev->writesize - 1)) != 0) {
aml_nand_msg ("Attempt to read non page aligned data");
return -NAND_READ_FAILED;
}
if ((offset + write_len) > phydev->size) {
aml_nand_msg("Attemp to read out side the dev area");
return -NAND_READ_FAILED;
}
memset(devops, 0x0, sizeof(struct phydev_ops));
devops->addr = offset;
devops->len = phydev->writesize;
devops->datbuf = buffer;
devops->oobbuf = NULL;
devops->mode = NAND_HW_ECC;
aml_nand_dbg("phydev->writesize= %x",phydev->writesize);
do {
if ((devops->addr % phydev->erasesize) == 0 ) {
ret = phydev->block_isbad(phydev);
if (ret > 0) {
aml_nand_msg("Skipping bad block at %llx", devops->addr);
devops->addr += phydev->erasesize;
continue;
} else if (ret < 0) {
aml_nand_msg("AMLNAND get bad block failed: ret=%d at addr=%llx",ret, devops->addr);
return -1;
}
}
memset(buffer,0x0,(2 * phydev->writesize));
ret = phydev->read(phydev);
if ((ret)) {
aml_nand_msg("nand read failed at %llx",devops->addr);
}
if (memcmp(buffer,dat_buf,phydev->writesize)) {
aml_nand_msg("nand read test verify failed");
break;
}
devops->addr += phydev->writesize;
}while(devops->addr < (offset + write_len));
exit_error:
if (buffer) {
aml_nand_free(buffer);
}
return ret;
}
#if 0
static int nand_read_oob_ops_test(struct amlnand_phydev *phydev,uint64_t off , uint64_t len , unsigned char * dat_buf)
{
//struct amlnand_chip *aml_chip =phydev->priv;
struct phydev_ops *devops = &(phydev->ops);
uint64_t offset , write_len;
unsigned char * buffer = NULL;
int ret = 0;
offset = off;
write_len = len;
buffer = aml_nand_malloc(2 * phydev->writesize);
if (!buffer) {
aml_nand_msg("nand read oob test malloc failed");
ret = -NAND_READ_FAILED;
goto exit_error;
}
if (!dat_buf) {
aml_nand_msg("nand read oob no buf");
return -NAND_READ_FAILED;
}
if ((offset & (phydev->writesize - 1)) != 0 ||(write_len & (phydev->writesize - 1)) != 0) {
aml_nand_msg ("Attempt to read non page aligned data");
return -NAND_READ_FAILED;
}
if ((offset + write_len) > phydev->size) {
aml_nand_msg("Attemp to read out side the dev area");
return -NAND_READ_FAILED;
}
memset(devops, 0x0, sizeof(struct phydev_ops));
devops->addr = offset;
devops->len = phydev->writesize;
devops->datbuf = NULL;
devops->oobbuf = buffer;
devops->mode = NAND_HW_ECC;
aml_nand_dbg("phydev->writesize= %x",phydev->writesize);
do {
if ((devops->addr % phydev->erasesize) == 0 ) {
ret = phydev->block_isbad(phydev);
if (ret > 0) {
aml_nand_msg("Skipping bad block at %llx", devops->addr);
devops->addr += phydev->erasesize;
continue;
} else if (ret < 0) {
aml_nand_msg("AMLNAND get bad block failed: ret=%d at addr=%llx",ret, devops->addr);
return -1;
}
}
memset(buffer,0x0,(2 * phydev->writesize));
ret = phydev->read_oob(phydev);
if ((ret)) {
aml_nand_msg("nand read oob failed at %llx",devops->addr);
}
if (memcmp(buffer,dat_buf,phydev->writesize)) {
aml_nand_msg("nand read oob test verify failed");
break;
}
devops->addr += phydev->writesize;
}while(devops->addr < (offset + write_len));
exit_error:
if (buffer) {
aml_nand_free(buffer);
}
return ret;
}
#endif
static int nand_write_ops_test(struct amlnand_phydev *phydev , uint64_t off, uint64_t len, unsigned char * dat_buf, int flag)
{
//struct amlnand_chip *aml_chip = phydev->priv;
struct phydev_ops *devops = &(phydev->ops);
unsigned char * verify_buf =NULL;
unsigned char * buffer = NULL;
uint64_t offset , write_len;
int ret = 0;
offset = off;
write_len = len;
buffer = dat_buf;
flag = 1; /* fixme, verify flag! */
if (!buffer) {
aml_nand_msg("nand write no buf");
return -NAND_WRITE_FAILED;
}
if ((offset & (phydev->writesize - 1)) != 0 ||(write_len & (phydev->writesize - 1)) != 0) {
aml_nand_msg ("Attempt to write non page aligned data");
return -NAND_WRITE_FAILED;
}
if ((offset + write_len) > phydev->size) {
aml_nand_msg("Attemp to write out side the dev area : %llx, %llx",(offset + write_len),phydev->size);
return -NAND_WRITE_FAILED;
}
if (flag == 1) {
verify_buf = aml_nand_malloc(2*phydev->writesize);
if (!verify_buf) {
aml_nand_msg("Attemp to write out side the dev area");
ret = -NAND_WRITE_FAILED;
goto exit_error;
}
}
memset(devops, 0x0, sizeof(struct phydev_ops));
devops->addr = offset;
devops->len = phydev->writesize;
devops->datbuf = buffer;
devops->oobbuf = NULL;
devops->mode = NAND_HW_ECC;
aml_nand_dbg("phydev->writesize= %x",phydev->writesize);
do {
if ((devops->addr % phydev->erasesize) == 0 ) {
ret = phydev->block_isbad(phydev);
if (ret > 0) {
aml_nand_msg("Skipping bad block at %llx", devops->addr);
devops->addr += phydev->erasesize;
continue;
} else if (ret < 0) {
aml_nand_msg("AMLNAND get bad block failed: ret=%d at addr=%llx",ret, devops->addr);
return -1;
}
}
ret = phydev->write(phydev);
if (ret < 0) {
aml_nand_msg("nand write failed at %llx",devops->addr);
ret = -NAND_WRITE_FAILED;
goto exit_error;
}
if (flag == 1) { // verify data
memset(verify_buf, 0x0, (2*phydev->writesize));
devops->datbuf = verify_buf;
ret = phydev->read(phydev);
if (ret) {
aml_nand_msg("nand test read data failed at %llx",devops->addr);
ret = -NAND_WRITE_FAILED;
goto exit_error;
}
if (memcmp(verify_buf, buffer, phydev->writesize)) {
aml_nand_msg("nand test verify data failed at %llx",devops->addr);
ret = -NAND_WRITE_FAILED;
goto exit_error;
}
devops->datbuf = buffer;
}
devops->addr += phydev->writesize;
if ((((phydev->offset + devops->addr) % phydev->erasesize) == 0))
aml_nand_msg("aml nand write devops->addr %llx OK",devops->addr);
}while(devops->addr < (offset + write_len));
exit_error:
if (flag == 1) {
if (verify_buf) {
aml_nand_free(verify_buf);
}
}
return ret;
}
//nand_test 4 write and read every block test
static int amlnand_test4(void)
{
struct amlnand_phydev *phydev = NULL;
//struct amlnand_chip *aml_chip;
//struct phydev_ops *devops;
int ret =0, j = 0, verify_lag =0;
unsigned char * data_buf = NULL;
uint64_t offset = 0 , write_len = 0,read_len=0;
uint64_t erase_len, erase_off;
list_for_each_entry(phydev,&nphy_dev_list,list){
data_buf = aml_nand_malloc( phydev->writesize);
if (!data_buf) {
aml_nand_msg("malloc failed");
goto exit_0;
}
memset(data_buf, 0xa5, phydev->writesize);
if (strncmp(phydev->name,NAND_BOOT_NAME,strlen((const char*)NAND_BOOT_NAME))) {
aml_nand_msg("nand test 4 : phydev->name %s",phydev->name);
//aml_chip = (struct amlnand_chip *)phydev->priv;
//devops = &phydev->ops;
//erase
erase_off = 0;
erase_len = phydev->size ;
ret = nand_erase_ops_test(phydev, erase_off, erase_len);
if (ret < 0) {
aml_nand_msg("nand test 4 : erase failed ");
ret= -NAND_ERASE_FAILED;
goto exit_0;
}
aml_nand_msg("nand test 4 : erase %d times OK ",j);
//write
offset = 0;
write_len = phydev->size;
verify_lag = 0; // 1 indicate verify data when write
ret = nand_write_ops_test(phydev, offset, write_len,data_buf,verify_lag);
if (ret < 0 ) {
aml_nand_msg("nand test 4 : write failed ");
ret= -NAND_WRITE_FAILED;
goto exit_0;
}
aml_nand_msg("nand test 4 : write %d times OK ",j);
//read disturb
offset = 0;
read_len = phydev->size;
ret = nand_read_ops_test(phydev, offset, read_len,data_buf);
if (ret < 0 ) {
aml_nand_msg("nand test 4 : read failed ");
ret= -NAND_READ_FAILED;
goto exit_0;
}
aml_nand_msg("nand test 4 : read %d times OK ",j);
}
if (data_buf)
{
kfree(data_buf);
data_buf = NULL;
}
}
exit_0:
if (data_buf) {
kfree(data_buf);
data_buf = NULL;
}
return ret;
}
static int amlnand_test3(void)
{
struct amlnand_phydev *phydev = NULL;
//struct amlnand_chip *aml_chip;
//struct phydev_ops *devops;
int ret =0, verify_lag =0;
unsigned char * data_buf = NULL;
uint64_t offset = 0 , write_len = 0,read_len=0, i = 0;
uint64_t erase_len, erase_off;
list_for_each_entry(phydev,&nphy_dev_list,list){
data_buf = aml_nand_malloc( phydev->writesize);
if (!data_buf) {
aml_nand_msg("malloc failed");
goto exit_0;
}
memset(data_buf, 0xa5, phydev->writesize);
if (strncmp(phydev->name,NAND_BOOT_NAME,strlen((const char*)NAND_BOOT_NAME))) {
aml_nand_msg("nand test 3 : phydev->name %s",phydev->name);
//aml_chip = (struct amlnand_chip *)phydev->priv;
//devops = &phydev->ops;
aml_nand_msg("nand test 3 : phydev->size=%llx ",phydev->size);
//erase
erase_off = 0;
erase_len = phydev->size ;
ret = nand_erase_ops_test(phydev, erase_off, erase_len);
if (ret < 0) {
aml_nand_msg("nand test 3 : erase failed ");
ret= -NAND_ERASE_FAILED;
goto exit_0;
}
aml_nand_msg("nand test 3 : erase OK ");
for (i=0;i<(phydev->size>>phydev->erasesize_shift)-1;i++) {
//write
offset = i<<phydev->erasesize_shift;
write_len = (i&0xff)*phydev->writesize;
verify_lag = 1; // 1 indicate verify data when write
aml_nand_msg("nand test 3 : poffset=%llx ",offset);
ret = nand_write_ops_test(phydev, offset, write_len,data_buf,verify_lag);
if (ret < 0 ) {
aml_nand_msg("nand test 3 : write failed ");
ret= -NAND_WRITE_FAILED;
goto exit_0;
}
aml_nand_msg("nand test 3 : write OK ");
//read disturb
offset = i<<phydev->erasesize_shift;
read_len = (i&0xff)*phydev->writesize;
ret = nand_read_ops_test(phydev, offset, read_len,data_buf);
if (ret < 0 ) {
aml_nand_msg("nand test 3 : read failed ");
ret= -NAND_READ_FAILED;
goto exit_0;
}
aml_nand_msg("nand test 3 : read OK ");
}
}
if (data_buf)
{
kfree(data_buf);
data_buf = NULL;
}
}
exit_0:
if (data_buf) {
kfree(data_buf);
data_buf = NULL;
}
return ret;
}
//nand_test 2 Endurance test
static int amlnand_test2(void)
{
struct amlnand_phydev *phydev = NULL;
//struct amlnand_chip *aml_chip;
//struct phydev_ops *devops;
int ret =0, j = 0, verify_lag =0;
unsigned char * data_buf = NULL;
uint64_t offset = 0 , write_len = 0,read_len=0;
uint64_t erase_len, erase_off;
//int read_times = 1000;
list_for_each_entry(phydev,&nphy_dev_list,list){
data_buf = aml_nand_malloc( phydev->writesize);
if (!data_buf) {
aml_nand_msg("malloc failed");
goto exit_0;
}
for (j=1;j<3000;j++)
{
memset(data_buf, 0xa5, phydev->writesize);
if (strncmp(phydev->name,NAND_BOOT_NAME,strlen((const char*)NAND_BOOT_NAME))) {
aml_nand_msg("nand test 2 : phydev->name %s",phydev->name);
//aml_chip = (struct amlnand_chip *)phydev->priv;
//devops = &phydev->ops;
//erase
erase_off = 0;
erase_len = phydev->size ;
ret = nand_erase_ops_test(phydev, erase_off, erase_len);
if (ret < 0) {
aml_nand_msg("nand test 2 : erase failed ");
ret= -NAND_ERASE_FAILED;
goto exit_0;
}
aml_nand_msg("nand test 2 : erase %d times OK ",j);
//write
offset = 0;
write_len = phydev->size;
verify_lag = 0; // 1 indicate verify data when write
ret = nand_write_ops_test(phydev, offset, write_len,data_buf,verify_lag);
if (ret < 0 ) {
aml_nand_msg("nand test 2 : write failed ");
ret= -NAND_WRITE_FAILED;
goto exit_0;
}
aml_nand_msg("nand test 2 : write %d times OK ",j);
//read disturb
offset = 0;
read_len = phydev->size;
ret = nand_read_ops_test(phydev, offset, read_len,data_buf);
if (ret < 0 ) {
aml_nand_msg("nand test 2 : read failed ");
ret= -NAND_READ_FAILED;
goto exit_0;
}
aml_nand_msg("nand test 2 : read %d times OK ",j);
}
}
if (data_buf)
{
kfree(data_buf);
data_buf = NULL;
}
}
exit_0:
if (data_buf) {
kfree(data_buf);
data_buf = NULL;
}
return ret;
}
//nand_test 1 Read Distub test
static int amlnand_test1(void)
{
struct amlnand_phydev *phydev = NULL;
//struct amlnand_chip *aml_chip;
//struct phydev_ops *devops;
int ret =0, i = 0, verify_lag =0;
unsigned char * data_buf = NULL;
uint64_t offset = 0 , write_len = 0,read_len=0;
uint64_t erase_len, erase_off;
int read_times = 10000;
list_for_each_entry(phydev,&nphy_dev_list,list){
data_buf = aml_nand_malloc( phydev->writesize);
if (!data_buf) {
aml_nand_msg("malloc failed");
goto exit_0;
}
memset(data_buf, 0xa5, phydev->writesize);
if (strncmp(phydev->name,NAND_BOOT_NAME,strlen((const char*)NAND_BOOT_NAME))) {
aml_nand_msg("nand test 1 : phydev->name %s",phydev->name);
// aml_chip = (struct amlnand_chip *)phydev->priv;
// devops = &phydev->ops;
//erase
erase_off = 0;
erase_len = phydev->size ;
ret = nand_erase_ops_test(phydev, erase_off, erase_len);
if (ret < 0) {
aml_nand_msg("nand test 1 : erase failed ");
ret= -NAND_ERASE_FAILED;
goto exit_0;
}
aml_nand_msg("nand test 1 : erase OK");
//write
offset = 0;
write_len = phydev->size;
verify_lag = 0; // 1 indicate verify data when write
ret = nand_write_ops_test(phydev, offset, write_len,data_buf,verify_lag);
if (ret < 0 ) {
aml_nand_msg("nand test 1 : write failed ");
ret= -NAND_WRITE_FAILED;
goto exit_0;
}
aml_nand_msg("nand test 1 : write OK");
//read disturb
for (i = 0; i < read_times; i++) {
offset = 0;
read_len = 4*phydev->erasesize;
ret = nand_read_ops_test(phydev, offset, read_len,data_buf);
if (ret < 0 ) {
aml_nand_msg("nand test 1 : read failed ");
ret= -NAND_WRITE_FAILED;
goto exit_0;
}
aml_nand_msg("nand test 1 : read %d times OK",i);
}
//erase
erase_off = 0;
erase_len = phydev->size ;
ret = nand_erase_ops_test(phydev, erase_off, erase_len);
if (ret < 0) {
aml_nand_msg("nand test 1 : erase failed ");
ret= -NAND_ERASE_FAILED;
goto exit_0;
}
}
if (data_buf) {
kfree(data_buf);
data_buf = NULL;
}
}
exit_0:
if (data_buf) {
kfree(data_buf);
data_buf = NULL;
}
return ret;
}
//nand_test 0 E/P/R Cycle Whole chip
static int amlnand_test0(void)
{
struct amlnand_phydev *phydev = NULL;
//struct amlnand_chip *aml_chip;
//struct phydev_ops *devops;
int ret =0, verify_lag =0;
unsigned char * data_buf = NULL;
uint64_t offset = 0 , write_len = 0,read_len=0;
uint64_t erase_len, erase_off;
list_for_each_entry(phydev,&nphy_dev_list,list){
data_buf = aml_nand_malloc(phydev->writesize);
if (!data_buf) {
aml_nand_msg("malloc failed");
ret = -1;
goto exit_0;
}
memset(data_buf, 0xa5, phydev->writesize);
if (strncmp(phydev->name,NAND_BOOT_NAME,strlen((const char*)NAND_BOOT_NAME))) {
aml_nand_msg("nand test 0 : phydev->name %s",phydev->name);
// aml_chip = (struct amlnand_chip *)phydev->priv;
// devops = &phydev->ops;
//erase
aml_nand_msg("nand test 0 : erase");
erase_off = 0;
erase_len = phydev->size ;
ret = nand_erase_ops_test(phydev, erase_off, erase_len);
if (ret < 0) {
aml_nand_msg("nand test 0 : erase failed ");
ret= -NAND_ERASE_FAILED;
goto exit_0;
}
//write
aml_nand_msg("nand test 0 : write");
offset = 0;
write_len = phydev->size;
verify_lag = 0; // 1 indicate verify data when write
ret = nand_write_ops_test(phydev, offset, write_len,data_buf,verify_lag);
if (ret < 0 ) {
aml_nand_msg("nand test 0 : write failed ");
ret= -NAND_WRITE_FAILED;
goto exit_0;
}
//read
aml_nand_msg("nand test 0 : read");
offset = 0;
read_len = phydev->size;
ret = nand_read_ops_test(phydev, offset, read_len,data_buf);
if (ret < 0 ) {
aml_nand_msg("nand test 0 : read failed ");
ret= -NAND_WRITE_FAILED;
goto exit_0;
}
//erase
erase_off = 0;
erase_len = phydev->size ;
ret = nand_erase_ops_test(phydev, erase_off, erase_len);
if (ret < 0) {
aml_nand_msg("nand test 0 : erase failed ");
ret= -NAND_ERASE_FAILED;
goto exit_0;
}
}
if (data_buf) {
kfree(data_buf);
data_buf = NULL;
}
}
exit_0:
if (data_buf) {
kfree(data_buf);
data_buf = NULL;
}
return ret;
}
int do_amlnand_test(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
{
int ret = 0;
//ulong addr;
//ulong data_write;
//loff_t off, size;
char *cmd;
if (argc < 2) {
printf("argc less than 2 :\n");
goto usage;
}
cmd = argv[1];
if (strncmp(cmd, "0", 1) == 0) {
ret = amlnand_test0();
if (ret < 0) {
aml_nand_msg("amlnand_test0: E/P/R Cycle failed");
}else
aml_nand_msg("amlnand_test0: E/P/R Cycle OK");
return 0;
}else if (strncmp(cmd, "1", 1) == 0){
ret = amlnand_test1();
if (ret < 0) {
aml_nand_msg("amlnand_test1: Read Distub test failed");
}else
aml_nand_msg("amlnand_test1: Read Distub test OK");
return 0;
}else if (strncmp(cmd, "2", 1) == 0){
ret = amlnand_test2();
if (ret < 0) {
aml_nand_msg("amlnand_test2: Endurance test failed");
}else
aml_nand_msg("amlnand_test2: Endurance test OK");
return 0;
}else if (strncmp(cmd, "3", 1) == 0){
ret = amlnand_test3();
if (ret < 0) {
aml_nand_msg("amlnand_test3: write and read random pages of every block test failed");
}else
aml_nand_msg("amlnand_test3: write and read random pages of every block test OK");
return 0;
}else if (strncmp(cmd, "4", 1) == 0){
ret = amlnand_test4();
if (ret < 0) {
aml_nand_msg("amlnand_test4: READ all page every block test failed");
}else
aml_nand_msg("amlnand_test4: READ all page every block test OK");
return 0;
}else{
goto usage;
}
usage:
cmd_usage(cmdtp);
return 1;
}
U_BOOT_CMD(amlnf_test, CONFIG_SYS_MAXARGS, 1, do_amlnand_test,
"AMLPHYNAND sub-system",
"amlnf_test 0 E/P/R Cycle Whole chip \n"
"amlnf_test 1 Read Distub test,read block 0~3 10k times \n"
"amlnf_test 2 Endurance test, E/W/R 10 block 3000 times \n"
"amlnf_test 3 write and read random pages of every block test \n"
"amlnf_test 4 READ all page every block \n"
"amlnf_test 5 ...... \n"
"amlnf_test 8 exit sync \n"
);