2009-03-25 13:48:38 +03:00
/*
* drivers / mtd / nand / socrates_nand . c
*
* Copyright © 2008 Ilya Yanok , Emcraft Systems
*
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
*/
# include <linux/slab.h>
# include <linux/module.h>
# include <linux/mtd/mtd.h>
# include <linux/mtd/nand.h>
# include <linux/mtd/partitions.h>
2013-11-11 09:19:08 +04:00
# include <linux/of_address.h>
2009-03-25 13:48:38 +03:00
# include <linux/of_platform.h>
# include <linux/io.h>
# define FPGA_NAND_CMD_MASK (0x7 << 28)
# define FPGA_NAND_CMD_COMMAND (0x0 << 28)
# define FPGA_NAND_CMD_ADDR (0x1 << 28)
# define FPGA_NAND_CMD_READ (0x2 << 28)
# define FPGA_NAND_CMD_WRITE (0x3 << 28)
# define FPGA_NAND_BUSY (0x1 << 15)
# define FPGA_NAND_ENABLE (0x1 << 31)
# define FPGA_NAND_DATA_SHIFT 16
struct socrates_nand_host {
struct nand_chip nand_chip ;
void __iomem * io_base ;
struct device * dev ;
} ;
/**
* socrates_nand_write_buf - write buffer to chip
* @ mtd : MTD device structure
* @ buf : data buffer
* @ len : number of bytes to write
*/
static void socrates_nand_write_buf ( struct mtd_info * mtd ,
const uint8_t * buf , int len )
{
int i ;
2015-12-01 14:03:04 +03:00
struct nand_chip * this = mtd_to_nand ( mtd ) ;
2015-12-10 11:00:41 +03:00
struct socrates_nand_host * host = nand_get_controller_data ( this ) ;
2009-03-25 13:48:38 +03:00
for ( i = 0 ; i < len ; i + + ) {
out_be32 ( host - > io_base , FPGA_NAND_ENABLE |
FPGA_NAND_CMD_WRITE |
( buf [ i ] < < FPGA_NAND_DATA_SHIFT ) ) ;
}
}
/**
* socrates_nand_read_buf - read chip data into buffer
* @ mtd : MTD device structure
* @ buf : buffer to store date
* @ len : number of bytes to read
*/
static void socrates_nand_read_buf ( struct mtd_info * mtd , uint8_t * buf , int len )
{
int i ;
2015-12-01 14:03:04 +03:00
struct nand_chip * this = mtd_to_nand ( mtd ) ;
2015-12-10 11:00:41 +03:00
struct socrates_nand_host * host = nand_get_controller_data ( this ) ;
2009-03-25 13:48:38 +03:00
uint32_t val ;
val = FPGA_NAND_ENABLE | FPGA_NAND_CMD_READ ;
out_be32 ( host - > io_base , val ) ;
for ( i = 0 ; i < len ; i + + ) {
buf [ i ] = ( in_be32 ( host - > io_base ) > >
FPGA_NAND_DATA_SHIFT ) & 0xff ;
}
}
/**
* socrates_nand_read_byte - read one byte from the chip
* @ mtd : MTD device structure
*/
static uint8_t socrates_nand_read_byte ( struct mtd_info * mtd )
{
uint8_t byte ;
socrates_nand_read_buf ( mtd , & byte , sizeof ( byte ) ) ;
return byte ;
}
/**
* socrates_nand_read_word - read one word from the chip
* @ mtd : MTD device structure
*/
static uint16_t socrates_nand_read_word ( struct mtd_info * mtd )
{
uint16_t word ;
socrates_nand_read_buf ( mtd , ( uint8_t * ) & word , sizeof ( word ) ) ;
return word ;
}
/*
* Hardware specific access to control - lines
*/
static void socrates_nand_cmd_ctrl ( struct mtd_info * mtd , int cmd ,
unsigned int ctrl )
{
2015-12-01 14:03:04 +03:00
struct nand_chip * nand_chip = mtd_to_nand ( mtd ) ;
2015-12-10 11:00:41 +03:00
struct socrates_nand_host * host = nand_get_controller_data ( nand_chip ) ;
2009-03-25 13:48:38 +03:00
uint32_t val ;
if ( cmd = = NAND_CMD_NONE )
return ;
if ( ctrl & NAND_CLE )
val = FPGA_NAND_CMD_COMMAND ;
else
val = FPGA_NAND_CMD_ADDR ;
if ( ctrl & NAND_NCE )
val | = FPGA_NAND_ENABLE ;
val | = ( cmd & 0xff ) < < FPGA_NAND_DATA_SHIFT ;
out_be32 ( host - > io_base , val ) ;
}
/*
* Read the Device Ready pin .
*/
static int socrates_nand_device_ready ( struct mtd_info * mtd )
{
2015-12-01 14:03:04 +03:00
struct nand_chip * nand_chip = mtd_to_nand ( mtd ) ;
2015-12-10 11:00:41 +03:00
struct socrates_nand_host * host = nand_get_controller_data ( nand_chip ) ;
2009-03-25 13:48:38 +03:00
if ( in_be32 ( host - > io_base ) & FPGA_NAND_BUSY )
return 0 ; /* busy */
return 1 ;
}
/*
* Probe for the NAND device .
*/
2012-11-19 22:23:07 +04:00
static int socrates_nand_probe ( struct platform_device * ofdev )
2009-03-25 13:48:38 +03:00
{
struct socrates_nand_host * host ;
struct mtd_info * mtd ;
struct nand_chip * nand_chip ;
int res ;
/* Allocate memory for the device structure (and zero it) */
2013-10-08 14:01:46 +04:00
host = devm_kzalloc ( & ofdev - > dev , sizeof ( * host ) , GFP_KERNEL ) ;
if ( ! host )
2009-03-25 13:48:38 +03:00
return - ENOMEM ;
2010-06-03 04:37:17 +04:00
host - > io_base = of_iomap ( ofdev - > dev . of_node , 0 ) ;
2009-03-25 13:48:38 +03:00
if ( host - > io_base = = NULL ) {
2013-10-08 14:08:08 +04:00
dev_err ( & ofdev - > dev , " ioremap failed \n " ) ;
2009-03-25 13:48:38 +03:00
return - EIO ;
}
nand_chip = & host - > nand_chip ;
2015-12-11 17:04:06 +03:00
mtd = nand_to_mtd ( nand_chip ) ;
2009-03-25 13:48:38 +03:00
host - > dev = & ofdev - > dev ;
2015-12-10 11:00:41 +03:00
/* link the private data structures */
nand_set_controller_data ( nand_chip , host ) ;
2015-10-31 06:33:25 +03:00
nand_set_flash_node ( nand_chip , ofdev - > dev . of_node ) ;
2009-03-25 13:48:38 +03:00
mtd - > name = " socrates_nand " ;
mtd - > dev . parent = & ofdev - > dev ;
/*should never be accessed directly */
nand_chip - > IO_ADDR_R = ( void * ) 0xdeadbeef ;
nand_chip - > IO_ADDR_W = ( void * ) 0xdeadbeef ;
nand_chip - > cmd_ctrl = socrates_nand_cmd_ctrl ;
nand_chip - > read_byte = socrates_nand_read_byte ;
nand_chip - > read_word = socrates_nand_read_word ;
nand_chip - > write_buf = socrates_nand_write_buf ;
nand_chip - > read_buf = socrates_nand_read_buf ;
nand_chip - > dev_ready = socrates_nand_device_ready ;
nand_chip - > ecc . mode = NAND_ECC_SOFT ; /* enable ECC */
2016-04-08 13:23:51 +03:00
nand_chip - > ecc . algo = NAND_ECC_HAMMING ;
2009-03-25 13:48:38 +03:00
/* TODO: I have no idea what real delay is. */
nand_chip - > chip_delay = 20 ; /* 20us command delay time */
dev_set_drvdata ( & ofdev - > dev , host ) ;
2016-11-04 13:43:10 +03:00
res = nand_scan ( mtd , 1 ) ;
if ( res )
2009-03-25 13:48:38 +03:00
goto out ;
2015-10-31 06:33:25 +03:00
res = mtd_device_register ( mtd , NULL , 0 ) ;
2009-03-25 13:48:38 +03:00
if ( ! res )
return res ;
nand_release ( mtd ) ;
out :
iounmap ( host - > io_base ) ;
return res ;
}
/*
* Remove a NAND device .
*/
2012-11-19 22:26:04 +04:00
static int socrates_nand_remove ( struct platform_device * ofdev )
2009-03-25 13:48:38 +03:00
{
struct socrates_nand_host * host = dev_get_drvdata ( & ofdev - > dev ) ;
2015-12-11 17:04:06 +03:00
struct mtd_info * mtd = nand_to_mtd ( & host - > nand_chip ) ;
2009-03-25 13:48:38 +03:00
nand_release ( mtd ) ;
iounmap ( host - > io_base ) ;
return 0 ;
}
2010-01-09 17:10:46 +03:00
static const struct of_device_id socrates_nand_match [ ] =
2009-03-25 13:48:38 +03:00
{
{
. compatible = " abb,socrates-nand " ,
} ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , socrates_nand_match ) ;
2011-02-17 12:43:24 +03:00
static struct platform_driver socrates_nand_driver = {
2010-04-14 03:13:02 +04:00
. driver = {
. name = " socrates_nand " ,
. of_match_table = socrates_nand_match ,
} ,
2009-03-25 13:48:38 +03:00
. probe = socrates_nand_probe ,
2012-11-19 22:21:24 +04:00
. remove = socrates_nand_remove ,
2009-03-25 13:48:38 +03:00
} ;
2011-11-27 16:45:03 +04:00
module_platform_driver ( socrates_nand_driver ) ;
2009-03-25 13:48:38 +03:00
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Ilya Yanok " ) ;
MODULE_DESCRIPTION ( " NAND driver for Socrates board " ) ;