2018-11-06 22:57:21 +08:00
// SPDX-License-Identifier: GPL-2.0+
2013-05-08 11:15:32 +08:00
/*
2018-11-06 22:57:21 +08:00
* Pvpanic Device Support
2013-05-08 11:15:32 +08:00
*
* Copyright ( C ) 2013 Fujitsu .
2018-11-06 22:57:16 +08:00
* Copyright ( C ) 2018 ZTE .
2013-05-08 11:15:32 +08:00
*/
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2020-12-09 22:36:41 +02:00
# include <linux/io.h>
2013-05-08 11:15:32 +08:00
# include <linux/kernel.h>
2020-01-02 10:35:13 +08:00
# include <linux/kexec.h>
2020-12-09 22:36:42 +02:00
# include <linux/mod_devicetable.h>
2013-05-08 11:15:32 +08:00
# include <linux/module.h>
2018-11-06 22:57:16 +08:00
# include <linux/platform_device.h>
2013-05-08 11:15:32 +08:00
# include <linux/types.h>
2020-12-09 22:36:42 +02:00
2020-01-02 10:35:12 +08:00
# include <uapi/misc/pvpanic.h>
2013-05-08 11:15:32 +08:00
2018-11-06 22:57:14 +08:00
static void __iomem * base ;
2021-01-10 19:53:57 +08:00
static unsigned int capability = PVPANIC_PANICKED | PVPANIC_CRASH_LOADED ;
2021-01-10 19:53:58 +08:00
static unsigned int events ;
2021-01-10 19:53:57 +08:00
static ssize_t capability_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
2021-01-29 11:08:01 -08:00
return sysfs_emit ( buf , " %x \n " , capability ) ;
2021-01-10 19:53:57 +08:00
}
static DEVICE_ATTR_RO ( capability ) ;
2021-01-10 19:53:58 +08:00
static ssize_t events_show ( struct device * dev , struct device_attribute * attr , char * buf )
{
2021-01-29 11:08:01 -08:00
return sysfs_emit ( buf , " %x \n " , events ) ;
2021-01-10 19:53:58 +08:00
}
static ssize_t events_store ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
{
unsigned int tmp ;
int err ;
err = kstrtouint ( buf , 16 , & tmp ) ;
if ( err )
return err ;
if ( ( tmp & capability ) ! = tmp )
return - EINVAL ;
events = tmp ;
return count ;
}
static DEVICE_ATTR_RW ( events ) ;
2021-01-10 19:53:57 +08:00
static struct attribute * pvpanic_dev_attrs [ ] = {
& dev_attr_capability . attr ,
2021-01-10 19:53:58 +08:00
& dev_attr_events . attr ,
2021-01-10 19:53:57 +08:00
NULL
} ;
ATTRIBUTE_GROUPS ( pvpanic_dev ) ;
2018-11-06 22:57:14 +08:00
2013-05-08 11:15:32 +08:00
MODULE_AUTHOR ( " Hu Tao <hutao@cn.fujitsu.com> " ) ;
MODULE_DESCRIPTION ( " pvpanic device driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
static void
pvpanic_send_event ( unsigned int event )
{
2021-01-10 19:53:58 +08:00
if ( event & capability & events )
2021-01-10 19:53:57 +08:00
iowrite8 ( event , base ) ;
2013-05-08 11:15:32 +08:00
}
static int
pvpanic_panic_notify ( struct notifier_block * nb , unsigned long code ,
void * unused )
{
2020-01-02 10:35:13 +08:00
unsigned int event = PVPANIC_PANICKED ;
if ( kexec_crash_loaded ( ) )
event = PVPANIC_CRASH_LOADED ;
pvpanic_send_event ( event ) ;
2013-05-08 11:15:32 +08:00
return NOTIFY_DONE ;
}
static struct notifier_block pvpanic_panic_nb = {
. notifier_call = pvpanic_panic_notify ,
2014-05-06 17:52:26 +02:00
. priority = 1 , /* let this called before broken drm_fb_helper */
2013-05-08 11:15:32 +08:00
} ;
2018-11-06 22:57:16 +08:00
static int pvpanic_mmio_probe ( struct platform_device * pdev )
{
2020-12-09 22:36:41 +02:00
struct device * dev = & pdev - > dev ;
struct resource * res ;
res = platform_get_mem_or_io ( pdev , 0 ) ;
2020-12-28 20:43:13 +02:00
if ( ! res )
return - EINVAL ;
switch ( resource_type ( res ) ) {
case IORESOURCE_IO :
2020-12-09 22:36:41 +02:00
base = devm_ioport_map ( dev , res - > start , resource_size ( res ) ) ;
2020-12-28 20:43:13 +02:00
if ( ! base )
return - ENOMEM ;
break ;
case IORESOURCE_MEM :
2020-12-09 22:36:41 +02:00
base = devm_ioremap_resource ( dev , res ) ;
2020-12-28 20:43:13 +02:00
if ( IS_ERR ( base ) )
return PTR_ERR ( base ) ;
break ;
default :
return - EINVAL ;
}
2018-11-06 22:57:16 +08:00
2021-01-10 19:53:57 +08:00
/* initlize capability by RDPT */
capability & = ioread8 ( base ) ;
2021-01-10 19:53:58 +08:00
events = capability ;
2021-01-10 19:53:57 +08:00
if ( capability )
atomic_notifier_chain_register ( & panic_notifier_list ,
& pvpanic_panic_nb ) ;
2018-11-06 22:57:16 +08:00
return 0 ;
}
static int pvpanic_mmio_remove ( struct platform_device * pdev )
{
2021-01-10 19:53:57 +08:00
if ( capability )
atomic_notifier_chain_unregister ( & panic_notifier_list ,
& pvpanic_panic_nb ) ;
2018-11-06 22:57:16 +08:00
return 0 ;
}
static const struct of_device_id pvpanic_mmio_match [ ] = {
{ . compatible = " qemu,pvpanic-mmio " , } ,
{ }
} ;
2020-12-09 22:36:41 +02:00
static const struct acpi_device_id pvpanic_device_ids [ ] = {
{ " QEMU0001 " , 0 } ,
{ " " , 0 }
} ;
MODULE_DEVICE_TABLE ( acpi , pvpanic_device_ids ) ;
2018-11-06 22:57:16 +08:00
static struct platform_driver pvpanic_mmio_driver = {
. driver = {
. name = " pvpanic-mmio " ,
. of_match_table = pvpanic_mmio_match ,
2020-12-09 22:36:41 +02:00
. acpi_match_table = pvpanic_device_ids ,
2021-01-10 19:53:57 +08:00
. dev_groups = pvpanic_dev_groups ,
2018-11-06 22:57:16 +08:00
} ,
. probe = pvpanic_mmio_probe ,
. remove = pvpanic_mmio_remove ,
} ;
2020-12-09 22:36:41 +02:00
module_platform_driver ( pvpanic_mmio_driver ) ;