2018-04-06 08:21:14 +03:00
// SPDX-License-Identifier: GPL-2.0+
# define pr_fmt(fmt) "of_pmem: " fmt
2023-07-14 20:48:13 +03:00
# include <linux/of.h>
2018-04-06 08:21:14 +03:00
# include <linux/libnvdimm.h>
# include <linux/module.h>
# include <linux/ioport.h>
2023-07-14 20:48:13 +03:00
# include <linux/platform_device.h>
2018-04-06 08:21:14 +03:00
# include <linux/slab.h>
struct of_pmem_private {
struct nvdimm_bus_descriptor bus_desc ;
struct nvdimm_bus * bus ;
} ;
static int of_pmem_region_probe ( struct platform_device * pdev )
{
struct of_pmem_private * priv ;
struct device_node * np ;
struct nvdimm_bus * bus ;
bool is_volatile ;
int i ;
np = dev_of_node ( & pdev - > dev ) ;
if ( ! np )
return - ENXIO ;
priv = kzalloc ( sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
2023-09-14 10:03:27 +03:00
priv - > bus_desc . provider_name = devm_kstrdup ( & pdev - > dev , pdev - > name ,
GFP_KERNEL ) ;
if ( ! priv - > bus_desc . provider_name ) {
kfree ( priv ) ;
return - ENOMEM ;
}
2018-04-06 08:21:14 +03:00
priv - > bus_desc . module = THIS_MODULE ;
priv - > bus_desc . of_node = np ;
priv - > bus = bus = nvdimm_bus_register ( & pdev - > dev , & priv - > bus_desc ) ;
if ( ! bus ) {
kfree ( priv ) ;
return - ENODEV ;
}
platform_set_drvdata ( pdev , priv ) ;
is_volatile = ! ! of_find_property ( np , " volatile " , NULL ) ;
dev_dbg ( & pdev - > dev , " Registering %s regions from %pOF \n " ,
is_volatile ? " volatile " : " non-volatile " , np ) ;
for ( i = 0 ; i < pdev - > num_resources ; i + + ) {
struct nd_region_desc ndr_desc ;
struct nd_region * region ;
/*
* NB : libnvdimm copies the data from ndr_desc into it ' s own
* structures so passing a stack pointer is fine .
*/
memset ( & ndr_desc , 0 , sizeof ( ndr_desc ) ) ;
2018-04-16 19:58:16 +03:00
ndr_desc . numa_node = dev_to_node ( & pdev - > dev ) ;
2018-11-09 23:43:07 +03:00
ndr_desc . target_node = ndr_desc . numa_node ;
2018-04-06 08:21:14 +03:00
ndr_desc . res = & pdev - > resource [ i ] ;
ndr_desc . of_node = np ;
set_bit ( ND_REGION_PAGEMAP , & ndr_desc . flags ) ;
if ( is_volatile )
region = nvdimm_volatile_region_create ( bus , & ndr_desc ) ;
2020-03-24 06:48:21 +03:00
else {
set_bit ( ND_REGION_PERSIST_MEMCTRL , & ndr_desc . flags ) ;
2018-04-06 08:21:14 +03:00
region = nvdimm_pmem_region_create ( bus , & ndr_desc ) ;
2020-03-24 06:48:21 +03:00
}
2018-04-06 08:21:14 +03:00
if ( ! region )
dev_warn ( & pdev - > dev , " Unable to register region %pR from %pOF \n " ,
ndr_desc . res , np ) ;
else
dev_dbg ( & pdev - > dev , " Registered region %pR from %pOF \n " ,
ndr_desc . res , np ) ;
}
return 0 ;
}
static int of_pmem_region_remove ( struct platform_device * pdev )
{
struct of_pmem_private * priv = platform_get_drvdata ( pdev ) ;
nvdimm_bus_unregister ( priv - > bus ) ;
kfree ( priv ) ;
return 0 ;
}
static const struct of_device_id of_pmem_region_match [ ] = {
{ . compatible = " pmem-region " } ,
2020-07-01 10:22:35 +03:00
{ . compatible = " pmem-region-v2 " } ,
2018-04-06 08:21:14 +03:00
{ } ,
} ;
static struct platform_driver of_pmem_region_driver = {
. probe = of_pmem_region_probe ,
. remove = of_pmem_region_remove ,
. driver = {
. name = " of_pmem " ,
. of_match_table = of_pmem_region_match ,
} ,
} ;
module_platform_driver ( of_pmem_region_driver ) ;
MODULE_DEVICE_TABLE ( of , of_pmem_region_match ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " IBM Corporation " ) ;