2019-06-04 10:11:33 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2006-05-11 22:35:28 +01:00
/*
* ( C ) 2005 , 2006 Red Hat Inc .
*
* Author : David Woodhouse < dwmw2 @ infradead . org >
2006-05-13 04:12:40 +01:00
* Tom Sylla < tom . sylla @ amd . com >
2006-05-11 22:35:28 +01:00
*
* Overview :
2006-11-30 08:17:38 +00:00
* This is a device driver for the NAND flash controller found on
2006-05-11 22:35:28 +01:00
* the AMD CS5535 / CS5536 companion chipsets for the Geode processor .
2008-02-09 08:16:36 +00:00
* mtd - id for command line partitioning is cs553x_nand_cs [ 0 - 3 ]
* where 0 - 3 reflects the chip select for NAND .
2006-05-11 22:35:28 +01:00
*/
2008-02-09 08:16:36 +00:00
# include <linux/kernel.h>
2006-05-11 22:35:28 +01:00
# include <linux/slab.h>
# include <linux/init.h>
# include <linux/module.h>
# include <linux/delay.h>
# include <linux/mtd/mtd.h>
2021-04-13 18:18:34 +02:00
# include <linux/mtd/nand-ecc-sw-hamming.h>
2017-08-04 17:29:10 +02:00
# include <linux/mtd/rawnand.h>
2006-05-11 22:35:28 +01:00
# include <linux/mtd/partitions.h>
2020-05-01 11:06:49 +02:00
# include <linux/iopoll.h>
2006-05-11 22:35:28 +01:00
# include <asm/msr.h>
# define NR_CS553X_CONTROLLERS 4
2006-05-26 02:06:27 +01:00
# define MSR_DIVIL_GLD_CAP 0x51400000 /* DIVIL capabilitiies */
# define CAP_CS5535 0x2df000ULL
# define CAP_CS5536 0x5df500ULL
2006-05-11 22:35:28 +01:00
/* NAND Timing MSRs */
# define MSR_NANDF_DATA 0x5140001b /* NAND Flash Data Timing MSR */
# define MSR_NANDF_CTL 0x5140001c /* NAND Flash Control Timing */
# define MSR_NANDF_RSVD 0x5140001d /* Reserved */
/* NAND BAR MSRs */
# define MSR_DIVIL_LBAR_FLSH0 0x51400010 /* Flash Chip Select 0 */
# define MSR_DIVIL_LBAR_FLSH1 0x51400011 /* Flash Chip Select 1 */
# define MSR_DIVIL_LBAR_FLSH2 0x51400012 /* Flash Chip Select 2 */
# define MSR_DIVIL_LBAR_FLSH3 0x51400013 /* Flash Chip Select 3 */
/* Each made up of... */
# define FLSH_LBAR_EN (1ULL<<32)
# define FLSH_NOR_NAND (1ULL<<33) /* 1 for NAND */
# define FLSH_MEM_IO (1ULL<<34) /* 1 for MMIO */
/* I/O BARs have BASE_ADDR in bits 15:4, IO_MASK in 47:36 */
/* MMIO BARs have BASE_ADDR in bits 31:12, MEM_MASK in 63:44 */
/* Pin function selection MSR (IDE vs. flash on the IDE pins) */
# define MSR_DIVIL_BALL_OPTS 0x51400015
2006-05-13 18:07:53 +01:00
# define PIN_OPT_IDE (1<<0) /* 0 for flash, 1 for IDE */
2006-05-11 22:35:28 +01:00
/* Registers within the NAND flash controller BAR -- memory mapped */
# define MM_NAND_DATA 0x00 /* 0 to 0x7ff, in fact */
# define MM_NAND_CTL 0x800 /* Any even address 0x800-0x80e */
# define MM_NAND_IO 0x801 /* Any odd address 0x801-0x80f */
# define MM_NAND_STS 0x810
# define MM_NAND_ECC_LSB 0x811
# define MM_NAND_ECC_MSB 0x812
# define MM_NAND_ECC_COL 0x813
# define MM_NAND_LAC 0x814
# define MM_NAND_ECC_CTL 0x815
/* Registers within the NAND flash controller BAR -- I/O mapped */
# define IO_NAND_DATA 0x00 /* 0 to 3, in fact */
# define IO_NAND_CTL 0x04
# define IO_NAND_IO 0x05
# define IO_NAND_STS 0x06
# define IO_NAND_ECC_CTL 0x08
# define IO_NAND_ECC_LSB 0x09
# define IO_NAND_ECC_MSB 0x0a
# define IO_NAND_ECC_COL 0x0b
# define IO_NAND_LAC 0x0c
# define CS_NAND_CTL_DIST_EN (1<<4) /* Enable NAND Distract interrupt */
# define CS_NAND_CTL_RDY_INT_MASK (1<<3) /* Enable RDY/BUSY# interrupt */
# define CS_NAND_CTL_ALE (1<<2)
# define CS_NAND_CTL_CLE (1<<1)
# define CS_NAND_CTL_CE (1<<0) /* Keep low; 1 to reset */
# define CS_NAND_STS_FLASH_RDY (1<<3)
# define CS_NAND_CTLR_BUSY (1<<2)
# define CS_NAND_CMD_COMP (1<<1)
# define CS_NAND_DIST_ST (1<<0)
# define CS_NAND_ECC_PARITY (1<<2)
# define CS_NAND_ECC_CLRECC (1<<1)
# define CS_NAND_ECC_ENECC (1<<0)
2020-05-01 11:06:47 +02:00
struct cs553x_nand_controller {
struct nand_controller base ;
struct nand_chip chip ;
2020-05-01 11:06:48 +02:00
void __iomem * mmio ;
2020-05-01 11:06:47 +02:00
} ;
2020-05-01 11:06:48 +02:00
static struct cs553x_nand_controller *
to_cs553x ( struct nand_controller * controller )
{
return container_of ( controller , struct cs553x_nand_controller , base ) ;
}
2020-05-01 11:06:49 +02:00
static int cs553x_write_ctrl_byte ( struct cs553x_nand_controller * cs553x ,
u32 ctl , u8 data )
{
u8 status ;
int ret ;
writeb ( ctl , cs553x - > mmio + MM_NAND_CTL ) ;
writeb ( data , cs553x - > mmio + MM_NAND_IO ) ;
ret = readb_poll_timeout_atomic ( cs553x - > mmio + MM_NAND_STS , status ,
! ( status & CS_NAND_CTLR_BUSY ) , 1 ,
100000 ) ;
if ( ret )
return ret ;
return 0 ;
}
static void cs553x_data_in ( struct cs553x_nand_controller * cs553x , void * buf ,
unsigned int len )
{
writeb ( 0 , cs553x - > mmio + MM_NAND_CTL ) ;
while ( unlikely ( len > 0x800 ) ) {
memcpy_fromio ( buf , cs553x - > mmio , 0x800 ) ;
buf + = 0x800 ;
len - = 0x800 ;
}
memcpy_fromio ( buf , cs553x - > mmio , len ) ;
}
static void cs553x_data_out ( struct cs553x_nand_controller * cs553x ,
const void * buf , unsigned int len )
{
writeb ( 0 , cs553x - > mmio + MM_NAND_CTL ) ;
while ( unlikely ( len > 0x800 ) ) {
memcpy_toio ( cs553x - > mmio , buf , 0x800 ) ;
buf + = 0x800 ;
len - = 0x800 ;
}
memcpy_toio ( cs553x - > mmio , buf , len ) ;
}
static int cs553x_wait_ready ( struct cs553x_nand_controller * cs553x ,
unsigned int timeout_ms )
{
u8 mask = CS_NAND_CTLR_BUSY | CS_NAND_STS_FLASH_RDY ;
u8 status ;
return readb_poll_timeout ( cs553x - > mmio + MM_NAND_STS , status ,
( status & mask ) = = CS_NAND_STS_FLASH_RDY , 100 ,
timeout_ms * 1000 ) ;
}
static int cs553x_exec_instr ( struct cs553x_nand_controller * cs553x ,
const struct nand_op_instr * instr )
{
unsigned int i ;
int ret = 0 ;
switch ( instr - > type ) {
case NAND_OP_CMD_INSTR :
ret = cs553x_write_ctrl_byte ( cs553x , CS_NAND_CTL_CLE ,
instr - > ctx . cmd . opcode ) ;
break ;
case NAND_OP_ADDR_INSTR :
for ( i = 0 ; i < instr - > ctx . addr . naddrs ; i + + ) {
ret = cs553x_write_ctrl_byte ( cs553x , CS_NAND_CTL_ALE ,
instr - > ctx . addr . addrs [ i ] ) ;
if ( ret )
break ;
}
break ;
case NAND_OP_DATA_IN_INSTR :
cs553x_data_in ( cs553x , instr - > ctx . data . buf . in ,
instr - > ctx . data . len ) ;
break ;
case NAND_OP_DATA_OUT_INSTR :
cs553x_data_out ( cs553x , instr - > ctx . data . buf . out ,
instr - > ctx . data . len ) ;
break ;
case NAND_OP_WAITRDY_INSTR :
ret = cs553x_wait_ready ( cs553x , instr - > ctx . waitrdy . timeout_ms ) ;
break ;
}
if ( instr - > delay_ns )
ndelay ( instr - > delay_ns ) ;
return ret ;
}
static int cs553x_exec_op ( struct nand_chip * this ,
const struct nand_operation * op ,
bool check_only )
{
struct cs553x_nand_controller * cs553x = to_cs553x ( this - > controller ) ;
unsigned int i ;
int ret ;
if ( check_only )
return true ;
/* De-assert the CE pin */
writeb ( 0 , cs553x - > mmio + MM_NAND_CTL ) ;
for ( i = 0 ; i < op - > ninstrs ; i + + ) {
ret = cs553x_exec_instr ( cs553x , & op - > instrs [ i ] ) ;
if ( ret )
break ;
}
/* Re-assert the CE pin. */
writeb ( CS_NAND_CTL_CE , cs553x - > mmio + MM_NAND_CTL ) ;
return ret ;
}
2018-09-06 14:05:17 +02:00
static void cs_enable_hwecc ( struct nand_chip * this , int mode )
2006-05-13 04:12:40 +01:00
{
2020-05-01 11:06:48 +02:00
struct cs553x_nand_controller * cs553x = to_cs553x ( this - > controller ) ;
2006-05-13 04:12:40 +01:00
2020-05-01 11:06:48 +02:00
writeb ( 0x07 , cs553x - > mmio + MM_NAND_ECC_CTL ) ;
2006-05-13 04:12:40 +01:00
}
2018-09-06 14:05:18 +02:00
static int cs_calculate_ecc ( struct nand_chip * this , const u_char * dat ,
u_char * ecc_code )
2006-05-13 04:12:40 +01:00
{
2020-05-01 11:06:48 +02:00
struct cs553x_nand_controller * cs553x = to_cs553x ( this - > controller ) ;
2006-05-13 04:12:40 +01:00
uint32_t ecc ;
2020-05-01 11:06:48 +02:00
ecc = readl ( cs553x - > mmio + MM_NAND_STS ) ;
2006-05-13 04:12:40 +01:00
ecc_code [ 1 ] = ecc > > 8 ;
ecc_code [ 0 ] = ecc > > 16 ;
ecc_code [ 2 ] = ecc > > 24 ;
return 0 ;
}
2021-04-13 18:18:34 +02:00
static int cs553x_ecc_correct ( struct nand_chip * chip ,
unsigned char * buf ,
unsigned char * read_ecc ,
unsigned char * calc_ecc )
{
return ecc_sw_hamming_correct ( buf , read_ecc , calc_ecc ,
chip - > ecc . size , false ) ;
}
2020-05-01 11:06:47 +02:00
static struct cs553x_nand_controller * controllers [ 4 ] ;
2006-05-11 22:35:28 +01:00
2020-11-13 13:34:10 +01:00
static int cs553x_attach_chip ( struct nand_chip * chip )
{
if ( chip - > ecc . engine_type ! = NAND_ECC_ENGINE_TYPE_ON_HOST )
return 0 ;
chip - > ecc . size = 256 ;
chip - > ecc . bytes = 3 ;
chip - > ecc . hwctl = cs_enable_hwecc ;
chip - > ecc . calculate = cs_calculate_ecc ;
2021-04-13 18:18:34 +02:00
chip - > ecc . correct = cs553x_ecc_correct ;
2020-11-13 13:34:10 +01:00
chip - > ecc . strength = 1 ;
return 0 ;
}
2020-05-01 11:06:49 +02:00
static const struct nand_controller_ops cs553x_nand_controller_ops = {
. exec_op = cs553x_exec_op ,
2020-11-13 13:34:10 +01:00
. attach_chip = cs553x_attach_chip ,
2020-05-01 11:06:49 +02:00
} ;
2006-05-11 22:35:28 +01:00
static int __init cs553x_init_one ( int cs , int mmio , unsigned long adr )
{
2020-05-01 11:06:47 +02:00
struct cs553x_nand_controller * controller ;
2006-05-11 22:35:28 +01:00
int err = 0 ;
struct nand_chip * this ;
struct mtd_info * new_mtd ;
2018-02-22 22:01:22 +05:30
pr_notice ( " Probing CS553x NAND controller CS#%d at %sIO 0x%08lx \n " ,
cs , mmio ? " MM " : " P " , adr ) ;
2006-05-11 22:35:28 +01:00
if ( ! mmio ) {
2018-02-22 22:01:22 +05:30
pr_notice ( " PIO mode not yet implemented for CS553X NAND controller \n " ) ;
2006-05-11 22:35:28 +01:00
return - ENXIO ;
}
/* Allocate memory for MTD device structure and private data */
2020-05-01 11:06:47 +02:00
controller = kzalloc ( sizeof ( * controller ) , GFP_KERNEL ) ;
if ( ! controller ) {
2006-05-11 22:35:28 +01:00
err = - ENOMEM ;
goto out ;
}
2020-05-01 11:06:47 +02:00
this = & controller - > chip ;
nand_controller_init ( & controller - > base ) ;
2020-05-01 11:06:49 +02:00
controller - > base . ops = & cs553x_nand_controller_ops ;
2020-05-01 11:06:47 +02:00
this - > controller = & controller - > base ;
2015-12-10 08:59:57 +01:00
new_mtd = nand_to_mtd ( this ) ;
2006-05-11 22:35:28 +01:00
/* Link the private data with the MTD structure */
2006-05-14 01:20:46 +01:00
new_mtd - > owner = THIS_MODULE ;
2006-05-11 22:35:28 +01:00
/* map physical address */
2020-05-01 11:06:48 +02:00
controller - > mmio = ioremap ( adr , 4096 ) ;
if ( ! controller - > mmio ) {
2018-02-22 22:01:22 +05:30
pr_warn ( " ioremap cs553x NAND @0x%08lx failed \n " , adr ) ;
2006-05-11 22:35:28 +01:00
err = - EIO ;
goto out_mtd ;
}
/* Enable the following for a flash based bad block table */
2011-05-31 16:31:23 -07:00
this - > bbt_options = NAND_BBT_USE_FLASH ;
2006-05-11 22:35:28 +01:00
2015-06-01 23:10:51 +02:00
new_mtd - > name = kasprintf ( GFP_KERNEL , " cs553x_nand_cs%d " , cs ) ;
if ( ! new_mtd - > name ) {
err = - ENOMEM ;
goto out_ior ;
}
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:51 +09:00
if ( err )
2015-06-01 23:10:51 +02:00
goto out_free ;
2006-05-11 22:35:28 +01:00
2020-05-01 11:06:47 +02:00
controllers [ cs ] = controller ;
2006-05-11 22:35:28 +01:00
goto out ;
2015-06-01 23:10:51 +02:00
out_free :
kfree ( new_mtd - > name ) ;
2006-05-11 22:35:28 +01:00
out_ior :
2020-05-01 11:06:48 +02:00
iounmap ( controller - > mmio ) ;
2006-05-11 22:35:28 +01:00
out_mtd :
2020-05-01 11:06:47 +02:00
kfree ( controller ) ;
2006-05-11 22:35:28 +01:00
out :
return err ;
}
2006-05-26 02:06:27 +01:00
static int is_geode ( void )
{
/* These are the CPUs which will have a CS553[56] companion chip */
if ( boot_cpu_data . x86_vendor = = X86_VENDOR_AMD & &
boot_cpu_data . x86 = = 5 & &
boot_cpu_data . x86_model = = 10 )
return 1 ; /* Geode LX */
if ( ( boot_cpu_data . x86_vendor = = X86_VENDOR_NSC | |
boot_cpu_data . x86_vendor = = X86_VENDOR_CYRIX ) & &
boot_cpu_data . x86 = = 5 & &
boot_cpu_data . x86_model = = 5 )
return 1 ; /* Geode GX (née GX2) */
return 0 ;
}
2006-05-16 13:54:50 +01:00
static int __init cs553x_init ( void )
2006-05-11 22:35:28 +01:00
{
int err = - ENXIO ;
int i ;
uint64_t val ;
2008-02-09 08:16:36 +00:00
2006-05-26 02:06:27 +01:00
/* If the CPU isn't a Geode GX or LX, abort */
if ( ! is_geode ( ) )
return - ENXIO ;
/* If it doesn't have the CS553[56], abort */
rdmsrl ( MSR_DIVIL_GLD_CAP , val ) ;
val & = ~ 0xFFULL ;
if ( val ! = CAP_CS5535 & & val ! = CAP_CS5536 )
2006-05-11 22:35:28 +01:00
return - ENXIO ;
2006-05-26 02:06:27 +01:00
/* If it doesn't have the NAND controller enabled, abort */
2006-05-11 22:35:28 +01:00
rdmsrl ( MSR_DIVIL_BALL_OPTS , val ) ;
2008-02-09 08:16:36 +00:00
if ( val & PIN_OPT_IDE ) {
2018-02-22 22:01:22 +05:30
pr_info ( " CS553x NAND controller: Flash I/O not enabled in MSR_DIVIL_BALL_OPTS. \n " ) ;
2006-05-11 22:35:28 +01:00
return - ENXIO ;
}
2006-05-13 18:07:53 +01:00
for ( i = 0 ; i < NR_CS553X_CONTROLLERS ; i + + ) {
rdmsrl ( MSR_DIVIL_LBAR_FLSH0 + i , val ) ;
2006-05-11 22:35:28 +01:00
if ( ( val & ( FLSH_LBAR_EN | FLSH_NOR_NAND ) ) = = ( FLSH_LBAR_EN | FLSH_NOR_NAND ) )
err = cs553x_init_one ( i , ! ! ( val & FLSH_MEM_IO ) , val & 0xFFFFFFFF ) ;
}
2006-05-13 18:07:53 +01:00
2006-11-30 08:17:38 +00:00
/* Register all devices together here. This means we can easily hack it to
2006-05-11 22:35:28 +01:00
do mtdconcat etc . if we want to . */
2006-05-13 18:07:53 +01:00
for ( i = 0 ; i < NR_CS553X_CONTROLLERS ; i + + ) {
2020-05-01 11:06:47 +02:00
if ( controllers [ i ] ) {
2006-05-11 22:35:28 +01:00
/* If any devices registered, return success. Else the last error. */
2020-05-01 11:06:47 +02:00
mtd_device_register ( nand_to_mtd ( & controllers [ i ] - > chip ) ,
NULL , 0 ) ;
2006-05-11 22:35:28 +01:00
err = 0 ;
}
}
return err ;
}
2006-05-13 18:07:53 +01:00
2006-05-11 22:35:28 +01:00
module_init ( cs553x_init ) ;
2006-05-13 18:07:53 +01:00
static void __exit cs553x_cleanup ( void )
2006-05-11 22:35:28 +01:00
{
int i ;
2006-05-13 18:07:53 +01:00
for ( i = 0 ; i < NR_CS553X_CONTROLLERS ; i + + ) {
2020-05-01 11:06:47 +02:00
struct cs553x_nand_controller * controller = controllers [ i ] ;
struct nand_chip * this = & controller - > chip ;
struct mtd_info * mtd = nand_to_mtd ( this ) ;
2020-05-19 14:59:41 +02:00
int ret ;
2006-05-11 22:35:28 +01:00
if ( ! mtd )
2008-02-09 08:16:36 +00:00
continue ;
2006-05-11 22:35:28 +01:00
/* Release resources, unregister device */
2020-05-19 14:59:41 +02:00
ret = mtd_device_unregister ( mtd ) ;
WARN_ON ( ret ) ;
nand_cleanup ( this ) ;
2015-12-10 08:59:57 +01:00
kfree ( mtd - > name ) ;
2020-05-01 11:06:47 +02:00
controllers [ i ] = NULL ;
2006-05-11 22:35:28 +01:00
2008-02-03 17:22:34 +02:00
/* unmap physical address */
2020-05-01 11:06:48 +02:00
iounmap ( controller - > mmio ) ;
2006-05-11 22:35:28 +01:00
/* Free the MTD device structure */
2020-05-01 11:06:47 +02:00
kfree ( controller ) ;
2006-05-11 22:35:28 +01:00
}
}
2006-05-13 18:07:53 +01:00
2006-05-11 22:35:28 +01:00
module_exit ( cs553x_cleanup ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " David Woodhouse <dwmw2@infradead.org> " ) ;
MODULE_DESCRIPTION ( " NAND controller driver for AMD CS5535/CS5536 companion chip " ) ;