2007-11-29 03:37:31 +03:00
/*
* Copyright ( C ) 2006 - 2007 PA Semi , Inc
*
* Author : Egor Martovetsky < egor @ pasemi . com >
* Maintained by : Olof Johansson < olof @ lixom . net >
*
* Driver for the PWRficient onchip NAND flash interface
*
* 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 .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# undef DEBUG
# include <linux/slab.h>
# include <linux/module.h>
# include <linux/mtd/mtd.h>
# include <linux/mtd/nand.h>
# include <linux/mtd/nand_ecc.h>
2013-09-17 23:28:33 +04:00
# include <linux/of_address.h>
# include <linux/of_irq.h>
2007-11-29 03:37:31 +03:00
# include <linux/of_platform.h>
# include <linux/platform_device.h>
# include <linux/pci.h>
# include <asm/io.h>
# define LBICTRL_LPCCTL_NR 0x00004000
# define CLE_PIN_CTL 15
# define ALE_PIN_CTL 14
static unsigned int lpcctl ;
static struct mtd_info * pasemi_nand_mtd ;
static const char driver_name [ ] = " pasemi-nand " ;
static void pasemi_read_buf ( struct mtd_info * mtd , u_char * buf , int len )
{
2015-12-01 14:03:04 +03:00
struct nand_chip * chip = mtd_to_nand ( mtd ) ;
2007-11-29 03:37:31 +03:00
while ( len > 0x800 ) {
memcpy_fromio ( buf , chip - > IO_ADDR_R , 0x800 ) ;
buf + = 0x800 ;
len - = 0x800 ;
}
memcpy_fromio ( buf , chip - > IO_ADDR_R , len ) ;
}
static void pasemi_write_buf ( struct mtd_info * mtd , const u_char * buf , int len )
{
2015-12-01 14:03:04 +03:00
struct nand_chip * chip = mtd_to_nand ( mtd ) ;
2007-11-29 03:37:31 +03:00
while ( len > 0x800 ) {
memcpy_toio ( chip - > IO_ADDR_R , buf , 0x800 ) ;
buf + = 0x800 ;
len - = 0x800 ;
}
memcpy_toio ( chip - > IO_ADDR_R , buf , len ) ;
}
static void pasemi_hwcontrol ( struct mtd_info * mtd , int cmd ,
unsigned int ctrl )
{
2015-12-01 14:03:04 +03:00
struct nand_chip * chip = mtd_to_nand ( mtd ) ;
2007-11-29 03:37:31 +03:00
if ( cmd = = NAND_CMD_NONE )
return ;
if ( ctrl & NAND_CLE )
out_8 ( chip - > IO_ADDR_W + ( 1 < < CLE_PIN_CTL ) , cmd ) ;
else
out_8 ( chip - > IO_ADDR_W + ( 1 < < ALE_PIN_CTL ) , cmd ) ;
/* Push out posted writes */
eieio ( ) ;
inl ( lpcctl ) ;
}
int pasemi_device_ready ( struct mtd_info * mtd )
{
return ! ! ( inl ( lpcctl ) & LBICTRL_LPCCTL_NR ) ;
}
2012-11-19 22:23:07 +04:00
static int pasemi_nand_probe ( struct platform_device * ofdev )
2007-11-29 03:37:31 +03:00
{
2016-04-13 12:48:05 +03:00
struct device * dev = & ofdev - > dev ;
2007-11-29 03:37:31 +03:00
struct pci_dev * pdev ;
2016-04-13 12:48:05 +03:00
struct device_node * np = dev - > of_node ;
2007-11-29 03:37:31 +03:00
struct resource res ;
struct nand_chip * chip ;
int err = 0 ;
err = of_address_to_resource ( np , 0 , & res ) ;
if ( err )
return - EINVAL ;
/* We only support one device at the moment */
if ( pasemi_nand_mtd )
return - ENODEV ;
2016-04-13 12:48:05 +03:00
dev_dbg ( dev , " pasemi_nand at %pR \n " , & res ) ;
2007-11-29 03:37:31 +03:00
/* Allocate memory for MTD device structure and private data */
2015-12-10 11:00:18 +03:00
chip = kzalloc ( sizeof ( struct nand_chip ) , GFP_KERNEL ) ;
if ( ! chip ) {
2007-11-29 03:37:31 +03:00
err = - ENOMEM ;
goto out ;
}
2015-12-10 11:00:18 +03:00
pasemi_nand_mtd = nand_to_mtd ( chip ) ;
2007-11-29 03:37:31 +03:00
/* Link the private data with the MTD structure */
2016-04-13 12:48:05 +03:00
pasemi_nand_mtd - > dev . parent = dev ;
2007-11-29 03:37:31 +03:00
chip - > IO_ADDR_R = of_iomap ( np , 0 ) ;
chip - > IO_ADDR_W = chip - > IO_ADDR_R ;
if ( ! chip - > IO_ADDR_R ) {
err = - EIO ;
goto out_mtd ;
}
pdev = pci_get_device ( PCI_VENDOR_ID_PASEMI , 0xa008 , NULL ) ;
if ( ! pdev ) {
err = - ENODEV ;
goto out_ior ;
}
lpcctl = pci_resource_start ( pdev , 0 ) ;
[MTD] [NAND] drivers/mtd/nand/pasemi_nand.c: Add missing pci_dev_put
pci_get_device increments a reference count that should be decremented
using pci_dev_put.
The semantic patch that finds the problem is as follows:
(http://www.emn.fr/x-info/coccinelle/)
// <smpl>
@r exists@
local idexpression x;
statement S,S1;
position p1,p2,p3;
expression E,E1;
type T,T1;
expression *ptr != NULL;
@@
(
if ((x@p1 = pci_get_device(...)) == NULL) S
|
x@p1 = pci_get_device(...);
)
... when != pci_dev_put(...,(T)x,...)
when != if (...) { <+... pci_dev_put(...,(T)x,...) ...+> }
when != true x == NULL || ...
when != x = E
when != E = (T)x
when any
(
if (x == NULL || ...) S1
|
if@p2 (...) {
... when != pci_dev_put(...,(T1)x,...)
when != if (...) { <+... pci_dev_put(...,(T1)x,...) ...+> }
when != x = E1
when != E1 = (T1)x
(
return \(0\|<+...x...+>\|ptr\);
|
return@p3 ...;
)
}
)
@ script:python @
p1 << r.p1;
p3 << r.p3;
@@
print "* file: %s pci_get_device: %s return: %s" % (p1[0].file,p1[0].line,p3[0].line)
// </smpl>
Signed-off-by: Julia Lawall <julia@diku.dk>
Acked-by: Olof Johansson <olof@lixom.net>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2008-12-02 01:00:55 +03:00
pci_dev_put ( pdev ) ;
2007-11-29 03:37:31 +03:00
if ( ! request_region ( lpcctl , 4 , driver_name ) ) {
err = - EBUSY ;
goto out_ior ;
}
chip - > cmd_ctrl = pasemi_hwcontrol ;
chip - > dev_ready = pasemi_device_ready ;
chip - > read_buf = pasemi_read_buf ;
chip - > write_buf = pasemi_write_buf ;
chip - > chip_delay = 0 ;
chip - > ecc . mode = NAND_ECC_SOFT ;
2016-04-08 13:23:49 +03:00
chip - > ecc . algo = NAND_ECC_HAMMING ;
2007-11-29 03:37:31 +03:00
/* Enable the following for a flash based bad block table */
2011-06-01 03:31:23 +04:00
chip - > bbt_options = NAND_BBT_USE_FLASH ;
2007-11-29 03:37:31 +03:00
2011-03-31 05:57:33 +04:00
/* Scan to find existence of the device */
2016-11-04 13:42:56 +03:00
err = nand_scan ( pasemi_nand_mtd , 1 ) ;
if ( err )
2007-11-29 03:37:31 +03:00
goto out_lpc ;
2011-05-23 13:23:40 +04:00
if ( mtd_device_register ( pasemi_nand_mtd , NULL , 0 ) ) {
2016-04-13 12:48:05 +03:00
dev_err ( dev , " Unable to register MTD device \n " ) ;
2007-11-29 03:37:31 +03:00
err = - ENODEV ;
goto out_lpc ;
}
2016-04-13 12:48:05 +03:00
dev_info ( dev , " PA Semi NAND flash at %pR, control at I/O %x \n " , & res ,
lpcctl ) ;
2007-11-29 03:37:31 +03:00
return 0 ;
out_lpc :
release_region ( lpcctl , 4 ) ;
out_ior :
iounmap ( chip - > IO_ADDR_R ) ;
out_mtd :
2015-12-10 11:00:18 +03:00
kfree ( chip ) ;
2007-11-29 03:37:31 +03:00
out :
return err ;
}
2012-11-19 22:26:04 +04:00
static int pasemi_nand_remove ( struct platform_device * ofdev )
2007-11-29 03:37:31 +03:00
{
struct nand_chip * chip ;
if ( ! pasemi_nand_mtd )
return 0 ;
2015-12-01 14:03:04 +03:00
chip = mtd_to_nand ( pasemi_nand_mtd ) ;
2007-11-29 03:37:31 +03:00
/* Release resources, unregister device */
nand_release ( pasemi_nand_mtd ) ;
release_region ( lpcctl , 4 ) ;
iounmap ( chip - > IO_ADDR_R ) ;
/* Free the MTD device structure */
2015-12-10 11:00:18 +03:00
kfree ( chip ) ;
2007-11-29 03:37:31 +03:00
pasemi_nand_mtd = NULL ;
return 0 ;
}
2010-01-09 17:10:46 +03:00
static const struct of_device_id pasemi_nand_match [ ] =
2007-11-29 03:37:31 +03:00
{
{
. compatible = " pasemi,localbus-nand " ,
} ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , pasemi_nand_match ) ;
2011-02-17 12:43:24 +03:00
static struct platform_driver pasemi_nand_driver =
2007-11-29 03:37:31 +03:00
{
2010-04-14 03:13:02 +04:00
. driver = {
2013-11-12 23:07:16 +04:00
. name = driver_name ,
2010-04-14 03:13:02 +04:00
. of_match_table = pasemi_nand_match ,
} ,
2007-11-29 03:37:31 +03:00
. probe = pasemi_nand_probe ,
. remove = pasemi_nand_remove ,
} ;
2011-11-27 16:45:03 +04:00
module_platform_driver ( pasemi_nand_driver ) ;
2007-11-29 03:37:31 +03:00
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Egor Martovetsky <egor@pasemi.com> " ) ;
MODULE_DESCRIPTION ( " NAND flash interface driver for PA Semi PWRficient " ) ;