2019-06-04 10:11:33 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2009-03-25 11:48:38 +01:00
/*
* Copyright © 2008 Ilya Yanok , Emcraft Systems
*/
# include <linux/slab.h>
# include <linux/module.h>
# include <linux/mtd/mtd.h>
2017-08-04 17:29:10 +02:00
# include <linux/mtd/rawnand.h>
2009-03-25 11:48:38 +01:00
# include <linux/mtd/partitions.h>
2013-11-10 23:19:08 -06:00
# include <linux/of_address.h>
2009-03-25 11:48:38 +01: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 {
2020-11-13 13:34:24 +01:00
struct nand_controller controller ;
2009-03-25 11:48:38 +01:00
struct nand_chip nand_chip ;
void __iomem * io_base ;
struct device * dev ;
} ;
/**
* socrates_nand_write_buf - write buffer to chip
2018-09-06 14:05:23 +02:00
* @ this : NAND chip object
2009-03-25 11:48:38 +01:00
* @ buf : data buffer
* @ len : number of bytes to write
*/
2018-09-06 14:05:23 +02:00
static void socrates_nand_write_buf ( struct nand_chip * this , const uint8_t * buf ,
int len )
2009-03-25 11:48:38 +01:00
{
int i ;
2015-12-10 09:00:41 +01:00
struct socrates_nand_host * host = nand_get_controller_data ( this ) ;
2009-03-25 11:48:38 +01: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
2018-09-06 14:05:22 +02:00
* @ this : NAND chip object
2009-03-25 11:48:38 +01:00
* @ buf : buffer to store date
* @ len : number of bytes to read
*/
2018-09-06 14:05:22 +02:00
static void socrates_nand_read_buf ( struct nand_chip * this , uint8_t * buf ,
int len )
2009-03-25 11:48:38 +01:00
{
int i ;
2015-12-10 09:00:41 +01:00
struct socrates_nand_host * host = nand_get_controller_data ( this ) ;
2009-03-25 11:48:38 +01: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
*/
2018-09-06 14:05:22 +02:00
static uint8_t socrates_nand_read_byte ( struct nand_chip * this )
2009-03-25 11:48:38 +01:00
{
uint8_t byte ;
2018-09-06 14:05:22 +02:00
socrates_nand_read_buf ( this , & byte , sizeof ( byte ) ) ;
2009-03-25 11:48:38 +01:00
return byte ;
}
/*
* Hardware specific access to control - lines
*/
2018-09-06 14:05:26 +02:00
static void socrates_nand_cmd_ctrl ( struct nand_chip * nand_chip , int cmd ,
unsigned int ctrl )
2009-03-25 11:48:38 +01:00
{
2015-12-10 09:00:41 +01:00
struct socrates_nand_host * host = nand_get_controller_data ( nand_chip ) ;
2009-03-25 11:48:38 +01: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 .
*/
2018-09-06 14:05:27 +02:00
static int socrates_nand_device_ready ( struct nand_chip * nand_chip )
2009-03-25 11:48:38 +01:00
{
2015-12-10 09:00:41 +01:00
struct socrates_nand_host * host = nand_get_controller_data ( nand_chip ) ;
2009-03-25 11:48:38 +01:00
if ( in_be32 ( host - > io_base ) & FPGA_NAND_BUSY )
return 0 ; /* busy */
return 1 ;
}
2020-11-13 13:34:24 +01:00
static int socrates_attach_chip ( struct nand_chip * chip )
{
2021-09-29 00:22:47 +02:00
if ( chip - > ecc . engine_type = = NAND_ECC_ENGINE_TYPE_SOFT & &
chip - > ecc . algo = = NAND_ECC_ALGO_UNKNOWN )
2020-12-03 20:03:39 +01:00
chip - > ecc . algo = NAND_ECC_ALGO_HAMMING ;
2020-11-13 13:34:24 +01:00
return 0 ;
}
static const struct nand_controller_ops socrates_ops = {
. attach_chip = socrates_attach_chip ,
} ;
2009-03-25 11:48:38 +01:00
/*
* Probe for the NAND device .
*/
2012-11-19 13:23:07 -05:00
static int socrates_nand_probe ( struct platform_device * ofdev )
2009-03-25 11:48:38 +01: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 15:31:46 +05:30
host = devm_kzalloc ( & ofdev - > dev , sizeof ( * host ) , GFP_KERNEL ) ;
if ( ! host )
2009-03-25 11:48:38 +01:00
return - ENOMEM ;
2010-06-03 02:37:17 +02:00
host - > io_base = of_iomap ( ofdev - > dev . of_node , 0 ) ;
2009-03-25 11:48:38 +01:00
if ( host - > io_base = = NULL ) {
2013-10-08 15:38:08 +05:30
dev_err ( & ofdev - > dev , " ioremap failed \n " ) ;
2009-03-25 11:48:38 +01:00
return - EIO ;
}
nand_chip = & host - > nand_chip ;
2015-12-11 15:04:06 +01:00
mtd = nand_to_mtd ( nand_chip ) ;
2009-03-25 11:48:38 +01:00
host - > dev = & ofdev - > dev ;
2020-11-13 13:34:24 +01:00
nand_controller_init ( & host - > controller ) ;
host - > controller . ops = & socrates_ops ;
nand_chip - > controller = & host - > controller ;
2015-12-10 09:00:41 +01:00
/* link the private data structures */
nand_set_controller_data ( nand_chip , host ) ;
2015-10-30 20:33:25 -07:00
nand_set_flash_node ( nand_chip , ofdev - > dev . of_node ) ;
2009-03-25 11:48:38 +01:00
mtd - > name = " socrates_nand " ;
mtd - > dev . parent = & ofdev - > dev ;
2018-09-07 00:38:36 +02:00
nand_chip - > legacy . cmd_ctrl = socrates_nand_cmd_ctrl ;
2018-09-07 00:38:35 +02:00
nand_chip - > legacy . read_byte = socrates_nand_read_byte ;
nand_chip - > legacy . write_buf = socrates_nand_write_buf ;
nand_chip - > legacy . read_buf = socrates_nand_read_buf ;
2018-09-07 00:38:37 +02:00
nand_chip - > legacy . dev_ready = socrates_nand_device_ready ;
2009-03-25 11:48:38 +01:00
/* TODO: I have no idea what real delay is. */
2018-09-07 00:38:41 +02:00
nand_chip - > legacy . chip_delay = 20 ; /* 20us command delay time */
2009-03-25 11:48:38 +01:00
2021-09-29 00:22:47 +02:00
/*
* This driver assumes that the default ECC engine should be TYPE_SOFT .
* Set - > engine_type before registering the NAND devices in order to
* provide a driver specific default value .
*/
nand_chip - > ecc . engine_type = NAND_ECC_ENGINE_TYPE_SOFT ;
2009-03-25 11:48:38 +01:00
dev_set_drvdata ( & ofdev - > dev , host ) ;
2018-09-06 14:05:14 +02:00
res = nand_scan ( nand_chip , 1 ) ;
2016-11-04 19:43:10 +09:00
if ( res )
2009-03-25 11:48:38 +01:00
goto out ;
2015-10-30 20:33:25 -07:00
res = mtd_device_register ( mtd , NULL , 0 ) ;
2009-03-25 11:48:38 +01:00
if ( ! res )
return res ;
2020-05-19 15:00:23 +02:00
nand_cleanup ( nand_chip ) ;
2009-03-25 11:48:38 +01:00
out :
iounmap ( host - > io_base ) ;
return res ;
}
/*
* Remove a NAND device .
*/
2012-11-19 13:26:04 -05:00
static int socrates_nand_remove ( struct platform_device * ofdev )
2009-03-25 11:48:38 +01:00
{
struct socrates_nand_host * host = dev_get_drvdata ( & ofdev - > dev ) ;
2020-05-19 15:00:24 +02:00
struct nand_chip * chip = & host - > nand_chip ;
int ret ;
2009-03-25 11:48:38 +01:00
2020-05-19 15:00:24 +02:00
ret = mtd_device_unregister ( nand_to_mtd ( chip ) ) ;
WARN_ON ( ret ) ;
nand_cleanup ( chip ) ;
2009-03-25 11:48:38 +01:00
iounmap ( host - > io_base ) ;
return 0 ;
}
2010-01-09 15:10:46 +01:00
static const struct of_device_id socrates_nand_match [ ] =
2009-03-25 11:48:38 +01:00
{
{
. compatible = " abb,socrates-nand " ,
} ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , socrates_nand_match ) ;
2011-02-17 02:43:24 -07:00
static struct platform_driver socrates_nand_driver = {
2010-04-13 16:13:02 -07:00
. driver = {
. name = " socrates_nand " ,
. of_match_table = socrates_nand_match ,
} ,
2009-03-25 11:48:38 +01:00
. probe = socrates_nand_probe ,
2012-11-19 13:21:24 -05:00
. remove = socrates_nand_remove ,
2009-03-25 11:48:38 +01:00
} ;
2011-11-27 20:45:03 +08:00
module_platform_driver ( socrates_nand_driver ) ;
2009-03-25 11:48:38 +01:00
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Ilya Yanok " ) ;
MODULE_DESCRIPTION ( " NAND driver for Socrates board " ) ;