2018-11-11 08:55:09 +01:00
// SPDX-License-Identifier: GPL-2.0
2006-05-21 18:11:55 +01:00
/*
* Copyright ( C ) 2006 Jonathan McDowell < noodles @ earth . li >
*
2018-02-05 23:02:02 +01:00
* Derived from drivers / mtd / nand / toto . c ( removed in v2 .6 .28 )
2018-02-05 23:02:03 +01:00
* Copyright ( c ) 2003 Texas Instruments
* Copyright ( c ) 2002 Thomas Gleixner < tgxl @ linutronix . de >
*
2010-12-14 21:09:40 +01:00
* Converted to platform driver by Janusz Krzysztofik < jkrzyszt @ tis . icnet . pl >
2018-02-05 23:02:00 +01:00
* Partially stolen from plat_nand . c
2006-05-21 18:11:55 +01:00
*
* Overview :
* This is a device driver for the NAND flash device found on the
* Amstrad E3 ( Delta ) .
*/
# include <linux/slab.h>
# include <linux/module.h>
# include <linux/delay.h>
2018-09-20 00:17:29 +02:00
# include <linux/gpio/consumer.h>
2006-05-21 18:11:55 +01:00
# include <linux/mtd/mtd.h>
2017-08-04 17:29:10 +02:00
# include <linux/mtd/rawnand.h>
2006-05-21 18:11:55 +01:00
# include <linux/mtd/partitions.h>
2018-11-21 12:08:05 +01:00
# include <linux/platform_device.h>
2018-11-11 08:55:08 +01:00
# include <linux/sizes.h>
2006-05-21 18:11:55 +01:00
/*
* MTD structure for E3 ( Delta )
*/
2018-09-20 00:52:54 +02:00
struct ams_delta_nand {
2018-11-11 08:55:13 +01:00
struct nand_controller base ;
2018-09-20 00:52:54 +02:00
struct nand_chip nand_chip ;
struct gpio_desc * gpiod_rdy ;
struct gpio_desc * gpiod_nce ;
struct gpio_desc * gpiod_nre ;
struct gpio_desc * gpiod_nwp ;
struct gpio_desc * gpiod_nwe ;
struct gpio_desc * gpiod_ale ;
struct gpio_desc * gpiod_cle ;
2018-11-21 12:08:05 +01:00
struct gpio_descs * data_gpiods ;
2018-09-20 00:52:55 +02:00
bool data_in ;
2018-09-20 00:52:54 +02:00
} ;
2006-05-21 18:11:55 +01:00
/*
* Define partitions for flash devices
*/
2017-08-28 13:54:57 +05:30
static const struct mtd_partition partition_info [ ] = {
2006-05-21 18:11:55 +01:00
{ . name = " Kernel " ,
. offset = 0 ,
. size = 3 * SZ_1M + SZ_512K } ,
{ . name = " u-boot " ,
. offset = 3 * SZ_1M + SZ_512K ,
. size = SZ_256K } ,
{ . name = " u-boot params " ,
. offset = 3 * SZ_1M + SZ_512K + SZ_256K ,
. size = SZ_256K } ,
{ . name = " Amstrad LDR " ,
. offset = 4 * SZ_1M ,
. size = SZ_256K } ,
{ . name = " File system " ,
. offset = 4 * SZ_1M + 1 * SZ_256K ,
. size = 27 * SZ_1M } ,
{ . name = " PBL reserved " ,
. offset = 32 * SZ_1M - 3 * SZ_256K ,
. size = 3 * SZ_256K } ,
} ;
2018-11-21 12:08:05 +01:00
static void ams_delta_write_commit ( struct ams_delta_nand * priv )
2006-05-21 18:11:55 +01:00
{
2018-09-20 00:52:54 +02:00
gpiod_set_value ( priv - > gpiod_nwe , 0 ) ;
2006-05-21 18:11:55 +01:00
ndelay ( 40 ) ;
2018-09-20 00:52:54 +02:00
gpiod_set_value ( priv - > gpiod_nwe , 1 ) ;
2006-05-21 18:11:55 +01:00
}
2018-11-21 12:08:05 +01:00
static void ams_delta_io_write ( struct ams_delta_nand * priv , u8 byte )
{
struct gpio_descs * data_gpiods = priv - > data_gpiods ;
DECLARE_BITMAP ( values , BITS_PER_TYPE ( byte ) ) = { byte , } ;
gpiod_set_raw_array_value ( data_gpiods - > ndescs , data_gpiods - > desc ,
data_gpiods - > info , values ) ;
ams_delta_write_commit ( priv ) ;
}
static void ams_delta_dir_output ( struct ams_delta_nand * priv , u8 byte )
{
struct gpio_descs * data_gpiods = priv - > data_gpiods ;
DECLARE_BITMAP ( values , BITS_PER_TYPE ( byte ) ) = { byte , } ;
int i ;
for ( i = 0 ; i < data_gpiods - > ndescs ; i + + )
gpiod_direction_output_raw ( data_gpiods - > desc [ i ] ,
test_bit ( i , values ) ) ;
ams_delta_write_commit ( priv ) ;
priv - > data_in = false ;
}
2018-11-11 08:55:10 +01:00
static u8 ams_delta_io_read ( struct ams_delta_nand * priv )
2006-05-21 18:11:55 +01:00
{
2018-11-11 08:55:10 +01:00
u8 res ;
2018-11-21 12:08:05 +01:00
struct gpio_descs * data_gpiods = priv - > data_gpiods ;
DECLARE_BITMAP ( values , BITS_PER_TYPE ( res ) ) = { 0 , } ;
2006-05-21 18:11:55 +01:00
2018-09-20 00:52:54 +02:00
gpiod_set_value ( priv - > gpiod_nre , 0 ) ;
2006-05-21 18:11:55 +01:00
ndelay ( 40 ) ;
2018-11-21 12:08:05 +01:00
gpiod_get_raw_array_value ( data_gpiods - > ndescs , data_gpiods - > desc ,
data_gpiods - > info , values ) ;
2018-09-20 00:52:54 +02:00
gpiod_set_value ( priv - > gpiod_nre , 1 ) ;
2006-05-21 18:11:55 +01:00
2018-11-21 12:08:05 +01:00
res = values [ 0 ] ;
2006-05-21 18:11:55 +01:00
return res ;
}
2018-11-21 12:08:05 +01:00
static void ams_delta_dir_input ( struct ams_delta_nand * priv )
2018-09-20 00:52:55 +02:00
{
2018-11-21 12:08:05 +01:00
struct gpio_descs * data_gpiods = priv - > data_gpiods ;
int i ;
for ( i = 0 ; i < data_gpiods - > ndescs ; i + + )
gpiod_direction_input ( data_gpiods - > desc [ i ] ) ;
priv - > data_in = true ;
2018-09-20 00:52:55 +02:00
}
2018-11-11 08:55:10 +01:00
static void ams_delta_write_buf ( struct ams_delta_nand * priv , const u8 * buf ,
2006-05-21 18:11:55 +01:00
int len )
{
2018-11-21 12:08:05 +01:00
int i = 0 ;
2006-05-21 18:11:55 +01:00
2018-11-21 12:08:05 +01:00
if ( len > 0 & & priv - > data_in )
ams_delta_dir_output ( priv , buf [ i + + ] ) ;
2018-09-20 00:52:55 +02:00
2018-11-21 12:08:05 +01:00
while ( i < len )
ams_delta_io_write ( priv , buf [ i + + ] ) ;
2006-05-21 18:11:55 +01:00
}
2018-11-11 08:55:10 +01:00
static void ams_delta_read_buf ( struct ams_delta_nand * priv , u8 * buf , int len )
2006-05-21 18:11:55 +01:00
{
int i ;
2018-09-20 00:52:55 +02:00
if ( ! priv - > data_in )
2018-11-21 12:08:05 +01:00
ams_delta_dir_input ( priv ) ;
2018-09-20 00:52:55 +02:00
for ( i = 0 ; i < len ; i + + )
buf [ i ] = ams_delta_io_read ( priv ) ;
}
2018-11-11 08:55:21 +01:00
static void ams_delta_ctrl_cs ( struct ams_delta_nand * priv , bool assert )
2006-05-23 23:25:53 +02:00
{
2018-11-11 08:55:21 +01:00
gpiod_set_value ( priv - > gpiod_nce , assert ? 0 : 1 ) ;
2006-05-23 23:25:53 +02:00
}
2018-10-15 21:41:30 +02:00
static int ams_delta_exec_op ( struct nand_chip * this ,
const struct nand_operation * op , bool check_only )
2006-05-21 18:11:55 +01:00
{
2018-09-20 00:52:54 +02:00
struct ams_delta_nand * priv = nand_get_controller_data ( this ) ;
2018-10-15 21:41:30 +02:00
const struct nand_op_instr * instr ;
int ret = 0 ;
if ( check_only )
return 0 ;
2018-11-11 08:55:21 +01:00
ams_delta_ctrl_cs ( priv , 1 ) ;
2018-10-15 21:41:30 +02:00
for ( instr = op - > instrs ; instr < op - > instrs + op - > ninstrs ; instr + + ) {
switch ( instr - > type ) {
case NAND_OP_CMD_INSTR :
gpiod_set_value ( priv - > gpiod_cle , 1 ) ;
ams_delta_write_buf ( priv , & instr - > ctx . cmd . opcode , 1 ) ;
gpiod_set_value ( priv - > gpiod_cle , 0 ) ;
break ;
case NAND_OP_ADDR_INSTR :
gpiod_set_value ( priv - > gpiod_ale , 1 ) ;
ams_delta_write_buf ( priv , instr - > ctx . addr . addrs ,
instr - > ctx . addr . naddrs ) ;
gpiod_set_value ( priv - > gpiod_ale , 0 ) ;
break ;
case NAND_OP_DATA_IN_INSTR :
ams_delta_read_buf ( priv , instr - > ctx . data . buf . in ,
instr - > ctx . data . len ) ;
break ;
case NAND_OP_DATA_OUT_INSTR :
ams_delta_write_buf ( priv , instr - > ctx . data . buf . out ,
instr - > ctx . data . len ) ;
break ;
case NAND_OP_WAITRDY_INSTR :
ret = priv - > gpiod_rdy ?
nand_gpio_waitrdy ( this , priv - > gpiod_rdy ,
instr - > ctx . waitrdy . timeout_ms ) :
nand_soft_waitrdy ( this ,
instr - > ctx . waitrdy . timeout_ms ) ;
break ;
}
if ( ret )
break ;
}
2018-09-20 00:52:54 +02:00
2018-11-11 08:55:21 +01:00
ams_delta_ctrl_cs ( priv , 0 ) ;
2018-10-15 21:41:30 +02:00
return ret ;
2006-05-21 18:11:55 +01:00
}
2018-11-11 08:55:23 +01:00
static const struct nand_controller_ops ams_delta_ops = {
. exec_op = ams_delta_exec_op ,
} ;
2006-05-21 18:11:55 +01:00
/*
* Main initialization routine
*/
2012-11-19 13:23:07 -05:00
static int ams_delta_init ( struct platform_device * pdev )
2006-05-21 18:11:55 +01:00
{
2018-09-20 00:52:54 +02:00
struct ams_delta_nand * priv ;
2006-05-21 18:11:55 +01:00
struct nand_chip * this ;
2018-09-20 00:52:54 +02:00
struct mtd_info * mtd ;
2018-11-21 12:08:04 +01:00
struct gpio_descs * data_gpiods ;
2006-05-21 18:11:55 +01:00
int err = 0 ;
/* Allocate memory for MTD device structure and private data */
2018-09-20 00:52:54 +02:00
priv = devm_kzalloc ( & pdev - > dev , sizeof ( struct ams_delta_nand ) ,
GFP_KERNEL ) ;
2018-11-11 08:55:10 +01:00
if ( ! priv )
2018-09-20 00:52:54 +02:00
return - ENOMEM ;
2018-11-11 08:55:10 +01:00
2018-09-20 00:52:54 +02:00
this = & priv - > nand_chip ;
2006-05-21 18:11:55 +01:00
2018-09-20 00:52:54 +02:00
mtd = nand_to_mtd ( this ) ;
mtd - > dev . parent = & pdev - > dev ;
2006-05-21 18:11:55 +01:00
2018-09-20 00:52:54 +02:00
nand_set_controller_data ( this , priv ) ;
2010-12-15 15:43:44 +01:00
2018-09-20 00:52:54 +02:00
priv - > gpiod_rdy = devm_gpiod_get_optional ( & pdev - > dev , " rdy " , GPIOD_IN ) ;
if ( IS_ERR ( priv - > gpiod_rdy ) ) {
err = PTR_ERR ( priv - > gpiod_rdy ) ;
2018-09-20 00:17:29 +02:00
dev_warn ( & pdev - > dev , " RDY GPIO request failed (%d) \n " , err ) ;
2018-11-21 12:08:05 +01:00
return err ;
2006-05-21 18:11:55 +01:00
}
2018-09-20 00:17:29 +02:00
2006-05-23 12:00:46 +02:00
this - > ecc . mode = NAND_ECC_SOFT ;
2016-04-08 12:23:44 +02:00
this - > ecc . algo = NAND_ECC_HAMMING ;
2006-05-21 18:11:55 +01:00
2018-09-20 00:52:54 +02:00
platform_set_drvdata ( pdev , priv ) ;
2010-12-15 15:43:44 +01:00
2006-05-21 18:11:55 +01:00
/* Set chip enabled, but */
2018-09-20 00:52:54 +02:00
priv - > gpiod_nwp = devm_gpiod_get ( & pdev - > dev , " nwp " , GPIOD_OUT_HIGH ) ;
if ( IS_ERR ( priv - > gpiod_nwp ) ) {
err = PTR_ERR ( priv - > gpiod_nwp ) ;
2018-09-20 00:17:29 +02:00
dev_err ( & pdev - > dev , " NWP GPIO request failed (%d) \n " , err ) ;
2018-11-21 12:08:05 +01:00
return err ;
2018-09-20 00:17:29 +02:00
}
2018-09-20 00:52:54 +02:00
priv - > gpiod_nce = devm_gpiod_get ( & pdev - > dev , " nce " , GPIOD_OUT_HIGH ) ;
if ( IS_ERR ( priv - > gpiod_nce ) ) {
err = PTR_ERR ( priv - > gpiod_nce ) ;
2018-09-20 00:17:29 +02:00
dev_err ( & pdev - > dev , " NCE GPIO request failed (%d) \n " , err ) ;
2018-11-21 12:08:05 +01:00
return err ;
2018-09-20 00:17:29 +02:00
}
2018-09-20 00:52:54 +02:00
priv - > gpiod_nre = devm_gpiod_get ( & pdev - > dev , " nre " , GPIOD_OUT_HIGH ) ;
if ( IS_ERR ( priv - > gpiod_nre ) ) {
err = PTR_ERR ( priv - > gpiod_nre ) ;
2018-09-20 00:17:29 +02:00
dev_err ( & pdev - > dev , " NRE GPIO request failed (%d) \n " , err ) ;
2018-11-21 12:08:05 +01:00
return err ;
2018-09-20 00:17:29 +02:00
}
2018-09-20 00:52:54 +02:00
priv - > gpiod_nwe = devm_gpiod_get ( & pdev - > dev , " nwe " , GPIOD_OUT_HIGH ) ;
if ( IS_ERR ( priv - > gpiod_nwe ) ) {
err = PTR_ERR ( priv - > gpiod_nwe ) ;
2018-09-20 00:17:29 +02:00
dev_err ( & pdev - > dev , " NWE GPIO request failed (%d) \n " , err ) ;
2018-11-21 12:08:05 +01:00
return err ;
2018-09-20 00:17:29 +02:00
}
2018-09-20 00:52:54 +02:00
priv - > gpiod_ale = devm_gpiod_get ( & pdev - > dev , " ale " , GPIOD_OUT_LOW ) ;
if ( IS_ERR ( priv - > gpiod_ale ) ) {
err = PTR_ERR ( priv - > gpiod_ale ) ;
2018-09-20 00:17:29 +02:00
dev_err ( & pdev - > dev , " ALE GPIO request failed (%d) \n " , err ) ;
2018-11-21 12:08:05 +01:00
return err ;
2018-09-20 00:17:29 +02:00
}
2018-09-20 00:52:54 +02:00
priv - > gpiod_cle = devm_gpiod_get ( & pdev - > dev , " cle " , GPIOD_OUT_LOW ) ;
if ( IS_ERR ( priv - > gpiod_cle ) ) {
err = PTR_ERR ( priv - > gpiod_cle ) ;
2018-09-20 00:17:29 +02:00
dev_err ( & pdev - > dev , " CLE GPIO request failed (%d) \n " , err ) ;
2018-11-21 12:08:05 +01:00
return err ;
2018-09-20 00:17:29 +02:00
}
2006-05-21 18:11:55 +01:00
2018-11-21 12:08:04 +01:00
/* Request array of data pins, initialize them as input */
data_gpiods = devm_gpiod_get_array ( & pdev - > dev , " data " , GPIOD_IN ) ;
if ( IS_ERR ( data_gpiods ) ) {
err = PTR_ERR ( data_gpiods ) ;
dev_err ( & pdev - > dev , " data GPIO request failed: %d \n " , err ) ;
2018-11-21 12:08:05 +01:00
return err ;
2018-11-21 12:08:04 +01:00
}
2018-11-21 12:08:05 +01:00
priv - > data_gpiods = data_gpiods ;
2018-11-21 12:08:04 +01:00
priv - > data_in = true ;
2018-09-20 00:52:55 +02:00
2018-11-11 08:55:13 +01:00
/* Initialize the NAND controller object embedded in ams_delta_nand. */
2018-11-11 08:55:23 +01:00
priv - > base . ops = & ams_delta_ops ;
2018-11-11 08:55:13 +01:00
nand_controller_init ( & priv - > base ) ;
this - > controller = & priv - > base ;
2011-03-30 22:57:33 -03:00
/* Scan to find existence of the device */
2018-09-06 14:05:14 +02:00
err = nand_scan ( this , 1 ) ;
2016-11-04 19:42:49 +09:00
if ( err )
2018-11-21 12:08:05 +01:00
return err ;
2006-05-21 18:11:55 +01:00
/* Register the partitions */
2018-11-11 08:55:12 +01:00
err = mtd_device_register ( mtd , partition_info ,
ARRAY_SIZE ( partition_info ) ) ;
if ( err )
goto err_nand_cleanup ;
2006-05-21 18:11:55 +01:00
2018-11-11 08:55:11 +01:00
return 0 ;
2006-05-21 18:11:55 +01:00
2018-11-11 08:55:12 +01:00
err_nand_cleanup :
nand_cleanup ( this ) ;
2006-05-21 18:11:55 +01:00
return err ;
}
/*
* Clean up routine
*/
2012-11-19 13:26:04 -05:00
static int ams_delta_cleanup ( struct platform_device * pdev )
2006-05-21 18:11:55 +01:00
{
2018-09-20 00:52:54 +02:00
struct ams_delta_nand * priv = platform_get_drvdata ( pdev ) ;
struct mtd_info * mtd = nand_to_mtd ( & priv - > nand_chip ) ;
2010-12-15 15:43:44 +01:00
2018-11-21 12:08:05 +01:00
/* Unregister device */
2018-09-20 00:52:54 +02:00
nand_release ( mtd_to_nand ( mtd ) ) ;
2006-05-21 18:11:55 +01:00
2010-12-14 21:09:40 +01:00
return 0 ;
}
static struct platform_driver ams_delta_nand_driver = {
. probe = ams_delta_init ,
2012-11-19 13:21:24 -05:00
. remove = ams_delta_cleanup ,
2010-12-14 21:09:40 +01:00
. driver = {
. name = " ams-delta-nand " ,
} ,
} ;
2011-11-27 20:45:03 +08:00
module_platform_driver ( ams_delta_nand_driver ) ;
2006-05-21 18:11:55 +01:00
2018-11-11 08:55:09 +01:00
MODULE_LICENSE ( " GPL v2 " ) ;
2006-05-21 18:11:55 +01:00
MODULE_AUTHOR ( " Jonathan McDowell <noodles@earth.li> " ) ;
MODULE_DESCRIPTION ( " Glue layer for NAND flash on Amstrad E3 (Delta) " ) ;