2020-10-10 01:31:36 +05:30
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* AMD MP2 PCIe communication driver
2021-09-23 17:59:30 +05:30
* Copyright 2020 - 2021 Advanced Micro Devices , Inc .
2020-10-10 01:31:36 +05:30
*
* Authors : Shyam Sundar S K < Shyam - sundar . S - k @ amd . com >
* Sandeep Singh < Sandeep . singh @ amd . com >
2021-09-23 17:59:30 +05:30
* Basavaraj Natikar < Basavaraj . Natikar @ amd . com >
2020-10-10 01:31:36 +05:30
*/
# include <linux/bitops.h>
# include <linux/delay.h>
# include <linux/dma-mapping.h>
2021-01-28 13:12:19 +01:00
# include <linux/dmi.h>
2020-10-10 01:31:36 +05:30
# include <linux/interrupt.h>
# include <linux/io-64-nonatomic-lo-hi.h>
2021-08-02 19:33:38 +05:30
# include <linux/iopoll.h>
2020-10-10 01:31:36 +05:30
# include <linux/module.h>
# include <linux/slab.h>
# include "amd_sfh_pcie.h"
2022-07-12 23:48:36 +05:30
# include "sfh1_1/amd_sfh_init.h"
2020-10-10 01:31:36 +05:30
# define DRIVER_NAME "pcie_mp2_amd"
# define DRIVER_DESC "AMD(R) PCIe MP2 Communication Driver"
2020-10-28 14:30:10 +05:30
# define ACEL_EN BIT(0)
# define GYRO_EN BIT(1)
2021-01-28 13:12:18 +01:00
# define MAGNO_EN BIT(2)
2021-06-18 13:48:38 +05:30
# define HPD_EN BIT(16)
2020-10-10 01:31:36 +05:30
# define ALS_EN BIT(19)
2023-04-11 21:49:01 +05:30
# define ACS_EN BIT(22)
2020-10-10 01:31:36 +05:30
2021-01-28 13:12:18 +01:00
static int sensor_mask_override = - 1 ;
module_param_named ( sensor_mask , sensor_mask_override , int , 0444 ) ;
MODULE_PARM_DESC ( sensor_mask , " override the detected sensors mask " ) ;
2021-08-02 19:33:38 +05:30
static int amd_sfh_wait_response_v2 ( struct amd_mp2_dev * mp2 , u8 sid , u32 sensor_sts )
{
union cmd_response cmd_resp ;
2022-01-31 22:48:32 +05:30
/* Get response with status within a max of 1600 ms timeout */
2021-08-02 19:33:38 +05:30
if ( ! readl_poll_timeout ( mp2 - > mmio + AMD_P2C_MSG ( 0 ) , cmd_resp . resp ,
( cmd_resp . response_v2 . response = = sensor_sts & &
cmd_resp . response_v2 . status = = 0 & & ( sid = = 0xff | |
2022-01-31 22:48:32 +05:30
cmd_resp . response_v2 . sensor_id = = sid ) ) , 500 , 1600000 ) )
2021-08-02 19:33:38 +05:30
return cmd_resp . response_v2 . response ;
return SENSOR_DISABLED ;
}
2021-06-18 13:48:36 +05:30
static void amd_start_sensor_v2 ( struct amd_mp2_dev * privdata , struct amd_mp2_sensor_info info )
{
union sfh_cmd_base cmd_base ;
cmd_base . ul = 0 ;
cmd_base . cmd_v2 . cmd_id = ENABLE_SENSOR ;
2022-02-08 17:51:10 +05:30
cmd_base . cmd_v2 . intr_disable = 1 ;
2021-06-18 13:48:36 +05:30
cmd_base . cmd_v2 . period = info . period ;
cmd_base . cmd_v2 . sensor_id = info . sensor_idx ;
cmd_base . cmd_v2 . length = 16 ;
if ( info . sensor_idx = = als_idx )
cmd_base . cmd_v2 . mem_type = USE_C2P_REG ;
writeq ( info . dma_address , privdata - > mmio + AMD_C2P_MSG1 ) ;
writel ( cmd_base . ul , privdata - > mmio + AMD_C2P_MSG0 ) ;
}
static void amd_stop_sensor_v2 ( struct amd_mp2_dev * privdata , u16 sensor_idx )
{
union sfh_cmd_base cmd_base ;
cmd_base . ul = 0 ;
cmd_base . cmd_v2 . cmd_id = DISABLE_SENSOR ;
2022-02-08 17:51:10 +05:30
cmd_base . cmd_v2 . intr_disable = 1 ;
2021-06-18 13:48:36 +05:30
cmd_base . cmd_v2 . period = 0 ;
cmd_base . cmd_v2 . sensor_id = sensor_idx ;
cmd_base . cmd_v2 . length = 16 ;
2021-07-13 16:31:07 -07:00
writeq ( 0x0 , privdata - > mmio + AMD_C2P_MSG1 ) ;
2021-06-18 13:48:36 +05:30
writel ( cmd_base . ul , privdata - > mmio + AMD_C2P_MSG0 ) ;
}
static void amd_stop_all_sensor_v2 ( struct amd_mp2_dev * privdata )
{
union sfh_cmd_base cmd_base ;
cmd_base . cmd_v2 . cmd_id = STOP_ALL_SENSORS ;
2022-02-08 17:51:10 +05:30
cmd_base . cmd_v2 . intr_disable = 1 ;
2021-06-18 13:48:36 +05:30
cmd_base . cmd_v2 . period = 0 ;
cmd_base . cmd_v2 . sensor_id = 0 ;
writel ( cmd_base . ul , privdata - > mmio + AMD_C2P_MSG0 ) ;
}
2022-07-12 23:48:35 +05:30
void amd_sfh_clear_intr_v2 ( struct amd_mp2_dev * privdata )
2022-02-08 17:51:11 +05:30
{
if ( readl ( privdata - > mmio + AMD_P2C_MSG ( 4 ) ) ) {
writel ( 0 , privdata - > mmio + AMD_P2C_MSG ( 4 ) ) ;
writel ( 0xf , privdata - > mmio + AMD_P2C_MSG ( 5 ) ) ;
}
}
2022-07-12 23:48:35 +05:30
void amd_sfh_clear_intr ( struct amd_mp2_dev * privdata )
2022-02-08 17:51:11 +05:30
{
if ( privdata - > mp2_ops - > clear_intr )
privdata - > mp2_ops - > clear_intr ( privdata ) ;
}
2022-02-08 17:51:12 +05:30
static irqreturn_t amd_sfh_irq_handler ( int irq , void * data )
{
amd_sfh_clear_intr ( data ) ;
return IRQ_HANDLED ;
}
2022-07-12 23:48:35 +05:30
int amd_sfh_irq_init_v2 ( struct amd_mp2_dev * privdata )
2022-02-08 17:51:12 +05:30
{
int rc ;
pci_intx ( privdata - > pdev , true ) ;
rc = devm_request_irq ( & privdata - > pdev - > dev , privdata - > pdev - > irq ,
amd_sfh_irq_handler , 0 , DRIVER_NAME , privdata ) ;
if ( rc ) {
dev_err ( & privdata - > pdev - > dev , " failed to request irq %d err=%d \n " ,
privdata - > pdev - > irq , rc ) ;
return rc ;
}
return 0 ;
}
2022-05-09 18:50:20 +05:30
static int amd_sfh_dis_sts_v2 ( struct amd_mp2_dev * privdata )
{
return ( readl ( privdata - > mmio + AMD_P2C_MSG ( 1 ) ) &
SENSOR_DISCOVERY_STATUS_MASK ) > > SENSOR_DISCOVERY_STATUS_SHIFT ;
}
2022-07-12 23:48:33 +05:30
static void amd_start_sensor ( struct amd_mp2_dev * privdata , struct amd_mp2_sensor_info info )
2020-10-10 01:31:36 +05:30
{
union sfh_cmd_param cmd_param ;
union sfh_cmd_base cmd_base ;
/* fill up command register */
memset ( & cmd_base , 0 , sizeof ( cmd_base ) ) ;
cmd_base . s . cmd_id = ENABLE_SENSOR ;
cmd_base . s . period = info . period ;
cmd_base . s . sensor_id = info . sensor_idx ;
/* fill up command param register */
memset ( & cmd_param , 0 , sizeof ( cmd_param ) ) ;
cmd_param . s . buf_layout = 1 ;
cmd_param . s . buf_length = 16 ;
2021-01-03 14:53:55 +01:00
writeq ( info . dma_address , privdata - > mmio + AMD_C2P_MSG2 ) ;
2020-10-10 01:31:36 +05:30
writel ( cmd_param . ul , privdata - > mmio + AMD_C2P_MSG1 ) ;
writel ( cmd_base . ul , privdata - > mmio + AMD_C2P_MSG0 ) ;
}
2022-07-12 23:48:33 +05:30
static void amd_stop_sensor ( struct amd_mp2_dev * privdata , u16 sensor_idx )
2020-10-10 01:31:36 +05:30
{
union sfh_cmd_base cmd_base ;
/* fill up command register */
memset ( & cmd_base , 0 , sizeof ( cmd_base ) ) ;
cmd_base . s . cmd_id = DISABLE_SENSOR ;
cmd_base . s . period = 0 ;
cmd_base . s . sensor_id = sensor_idx ;
writeq ( 0x0 , privdata - > mmio + AMD_C2P_MSG2 ) ;
writel ( cmd_base . ul , privdata - > mmio + AMD_C2P_MSG0 ) ;
}
2022-07-12 23:48:33 +05:30
static void amd_stop_all_sensors ( struct amd_mp2_dev * privdata )
2020-10-10 01:31:36 +05:30
{
union sfh_cmd_base cmd_base ;
/* fill up command register */
memset ( & cmd_base , 0 , sizeof ( cmd_base ) ) ;
cmd_base . s . cmd_id = STOP_ALL_SENSORS ;
cmd_base . s . period = 0 ;
cmd_base . s . sensor_id = 0 ;
writel ( cmd_base . ul , privdata - > mmio + AMD_C2P_MSG0 ) ;
}
2021-01-28 13:12:19 +01:00
static const struct dmi_system_id dmi_sensor_mask_overrides [ ] = {
{
. matches = {
DMI_MATCH ( DMI_PRODUCT_NAME , " HP ENVY x360 Convertible 13-ag0xxx " ) ,
} ,
. driver_data = ( void * ) ( ACEL_EN | MAGNO_EN ) ,
} ,
{
. matches = {
DMI_MATCH ( DMI_PRODUCT_NAME , " HP ENVY x360 Convertible 15-cp0xxx " ) ,
} ,
. driver_data = ( void * ) ( ACEL_EN | MAGNO_EN ) ,
} ,
{ }
} ;
2020-10-10 01:31:36 +05:30
int amd_mp2_get_sensor_num ( struct amd_mp2_dev * privdata , u8 * sensor_id )
{
int activestatus , num_of_sensors = 0 ;
2021-01-28 13:12:19 +01:00
const struct dmi_system_id * dmi_id ;
2021-01-28 13:12:17 +01:00
2021-01-28 13:12:19 +01:00
if ( sensor_mask_override = = - 1 ) {
dmi_id = dmi_first_match ( dmi_sensor_mask_overrides ) ;
if ( dmi_id )
sensor_mask_override = ( long ) dmi_id - > driver_data ;
}
2021-01-28 13:12:18 +01:00
if ( sensor_mask_override > = 0 ) {
activestatus = sensor_mask_override ;
} else {
2021-06-18 13:48:36 +05:30
activestatus = privdata - > mp2_acs > > 4 ;
2021-01-28 13:12:18 +01:00
}
2020-10-10 01:31:36 +05:30
if ( ACEL_EN & activestatus )
sensor_id [ num_of_sensors + + ] = accel_idx ;
if ( GYRO_EN & activestatus )
sensor_id [ num_of_sensors + + ] = gyro_idx ;
if ( MAGNO_EN & activestatus )
sensor_id [ num_of_sensors + + ] = mag_idx ;
if ( ALS_EN & activestatus )
sensor_id [ num_of_sensors + + ] = als_idx ;
2021-06-18 13:48:38 +05:30
if ( HPD_EN & activestatus )
sensor_id [ num_of_sensors + + ] = HPD_IDX ;
2023-04-11 21:49:01 +05:30
if ( ACS_EN & activestatus )
sensor_id [ num_of_sensors + + ] = ACS_IDX ;
2020-10-10 01:31:36 +05:30
return num_of_sensors ;
}
static void amd_mp2_pci_remove ( void * privdata )
{
2021-06-18 13:48:36 +05:30
struct amd_mp2_dev * mp2 = privdata ;
2020-10-10 01:31:36 +05:30
amd_sfh_hid_client_deinit ( privdata ) ;
2021-06-18 13:48:36 +05:30
mp2 - > mp2_ops - > stop_all ( mp2 ) ;
2022-02-08 17:51:12 +05:30
pci_intx ( mp2 - > pdev , false ) ;
2022-02-08 17:51:11 +05:30
amd_sfh_clear_intr ( mp2 ) ;
2021-06-18 13:48:36 +05:30
}
2022-07-12 23:48:30 +05:30
static struct amd_mp2_ops amd_sfh_ops_v2 = {
2021-06-18 13:48:36 +05:30
. start = amd_start_sensor_v2 ,
. stop = amd_stop_sensor_v2 ,
. stop_all = amd_stop_all_sensor_v2 ,
2021-08-02 19:33:38 +05:30
. response = amd_sfh_wait_response_v2 ,
2022-02-08 17:51:11 +05:30
. clear_intr = amd_sfh_clear_intr_v2 ,
2022-02-08 17:51:12 +05:30
. init_intr = amd_sfh_irq_init_v2 ,
2022-05-09 18:50:20 +05:30
. discovery_status = amd_sfh_dis_sts_v2 ,
2022-07-12 23:48:32 +05:30
. remove = amd_mp2_pci_remove ,
2021-06-18 13:48:36 +05:30
} ;
2022-07-12 23:48:30 +05:30
static struct amd_mp2_ops amd_sfh_ops = {
2021-06-18 13:48:36 +05:30
. start = amd_start_sensor ,
. stop = amd_stop_sensor ,
. stop_all = amd_stop_all_sensors ,
2022-07-12 23:48:32 +05:30
. remove = amd_mp2_pci_remove ,
2021-06-18 13:48:36 +05:30
} ;
static void mp2_select_ops ( struct amd_mp2_dev * privdata )
{
u8 acs ;
privdata - > mp2_acs = readl ( privdata - > mmio + AMD_P2C_MSG3 ) ;
acs = privdata - > mp2_acs & GENMASK ( 3 , 0 ) ;
switch ( acs ) {
case V2_STATUS :
privdata - > mp2_ops = & amd_sfh_ops_v2 ;
break ;
default :
privdata - > mp2_ops = & amd_sfh_ops ;
break ;
}
2020-10-10 01:31:36 +05:30
}
2022-07-12 23:48:35 +05:30
int amd_sfh_irq_init ( struct amd_mp2_dev * privdata )
2022-02-08 17:51:12 +05:30
{
if ( privdata - > mp2_ops - > init_intr )
return privdata - > mp2_ops - > init_intr ( privdata ) ;
return 0 ;
}
2022-08-16 19:21:20 +09:00
static const struct dmi_system_id dmi_nodevs [ ] = {
{
/*
* Google Chromebooks use Chrome OS Embedded Controller Sensor
* Hub instead of Sensor Hub Fusion and leaves MP2
* uninitialized , which disables all functionalities , even
* including the registers necessary for feature detections .
*/
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Google " ) ,
} ,
} ,
{ }
} ;
2020-10-10 01:31:36 +05:30
static int amd_mp2_pci_probe ( struct pci_dev * pdev , const struct pci_device_id * id )
{
struct amd_mp2_dev * privdata ;
int rc ;
2022-08-16 19:21:20 +09:00
if ( dmi_first_match ( dmi_nodevs ) )
return - ENODEV ;
2020-10-10 01:31:36 +05:30
privdata = devm_kzalloc ( & pdev - > dev , sizeof ( * privdata ) , GFP_KERNEL ) ;
if ( ! privdata )
return - ENOMEM ;
privdata - > pdev = pdev ;
2021-09-23 17:59:29 +05:30
dev_set_drvdata ( & pdev - > dev , privdata ) ;
2020-10-10 01:31:36 +05:30
rc = pcim_enable_device ( pdev ) ;
if ( rc )
return rc ;
rc = pcim_iomap_regions ( pdev , BIT ( 2 ) , DRIVER_NAME ) ;
if ( rc )
return rc ;
privdata - > mmio = pcim_iomap_table ( pdev ) [ 2 ] ;
pci_set_master ( pdev ) ;
2021-09-23 17:59:28 +05:30
rc = dma_set_mask_and_coherent ( & pdev - > dev , DMA_BIT_MASK ( 64 ) ) ;
2020-10-10 01:31:36 +05:30
if ( rc ) {
2022-01-15 16:24:50 +01:00
dev_err ( & pdev - > dev , " failed to set DMA mask \n " ) ;
return rc ;
2020-10-10 01:31:36 +05:30
}
2021-06-18 13:48:37 +05:30
privdata - > cl_data = devm_kzalloc ( & pdev - > dev , sizeof ( struct amdtp_cl_data ) , GFP_KERNEL ) ;
if ( ! privdata - > cl_data )
return - ENOMEM ;
2022-07-12 23:48:36 +05:30
privdata - > sfh1_1_ops = ( const struct amd_sfh1_1_ops * ) id - > driver_data ;
if ( privdata - > sfh1_1_ops ) {
rc = privdata - > sfh1_1_ops - > init ( privdata ) ;
if ( rc )
return rc ;
goto init_done ;
}
2021-09-23 17:59:27 +05:30
mp2_select_ops ( privdata ) ;
2022-02-08 17:51:12 +05:30
rc = amd_sfh_irq_init ( privdata ) ;
if ( rc ) {
dev_err ( & pdev - > dev , " amd_sfh_irq_init failed \n " ) ;
return rc ;
}
2021-09-23 17:59:27 +05:30
rc = amd_sfh_hid_client_init ( privdata ) ;
2022-02-08 17:51:11 +05:30
if ( rc ) {
amd_sfh_clear_intr ( privdata ) ;
2022-05-12 13:18:48 -05:00
if ( rc ! = - EOPNOTSUPP )
dev_err ( & pdev - > dev , " amd_sfh_hid_client_init failed \n " ) ;
2020-10-10 01:31:36 +05:30
return rc ;
2022-02-08 17:51:11 +05:30
}
2022-07-12 23:48:36 +05:30
init_done :
2022-02-08 17:51:11 +05:30
amd_sfh_clear_intr ( privdata ) ;
2020-10-10 01:31:36 +05:30
2022-07-12 23:48:32 +05:30
return devm_add_action_or_reset ( & pdev - > dev , privdata - > mp2_ops - > remove , privdata ) ;
2020-10-10 01:31:36 +05:30
}
2023-04-11 21:40:27 +05:30
static void amd_sfh_shutdown ( struct pci_dev * pdev )
{
struct amd_mp2_dev * mp2 = pci_get_drvdata ( pdev ) ;
if ( mp2 & & mp2 - > mp2_ops )
mp2 - > mp2_ops - > stop_all ( mp2 ) ;
}
2021-08-02 19:33:40 +05:30
static int __maybe_unused amd_mp2_pci_resume ( struct device * dev )
{
2021-09-23 17:59:29 +05:30
struct amd_mp2_dev * mp2 = dev_get_drvdata ( dev ) ;
2021-08-02 19:33:40 +05:30
2022-07-12 23:48:31 +05:30
mp2 - > mp2_ops - > resume ( mp2 ) ;
2022-02-08 17:51:08 +05:30
2021-08-02 19:33:40 +05:30
return 0 ;
}
static int __maybe_unused amd_mp2_pci_suspend ( struct device * dev )
{
2021-09-23 17:59:29 +05:30
struct amd_mp2_dev * mp2 = dev_get_drvdata ( dev ) ;
2021-08-02 19:33:40 +05:30
2022-07-12 23:48:31 +05:30
mp2 - > mp2_ops - > suspend ( mp2 ) ;
2022-02-08 17:51:08 +05:30
2021-08-02 19:33:40 +05:30
return 0 ;
}
static SIMPLE_DEV_PM_OPS ( amd_mp2_pm_ops , amd_mp2_pci_suspend ,
amd_mp2_pci_resume ) ;
2020-10-10 01:31:36 +05:30
static const struct pci_device_id amd_mp2_pci_tbl [ ] = {
{ PCI_VDEVICE ( AMD , PCI_DEVICE_ID_AMD_MP2 ) } ,
2022-07-12 23:48:36 +05:30
{ PCI_VDEVICE ( AMD , PCI_DEVICE_ID_AMD_MP2_1_1 ) ,
. driver_data = ( kernel_ulong_t ) & sfh1_1_ops } ,
2020-10-10 01:31:36 +05:30
{ }
} ;
MODULE_DEVICE_TABLE ( pci , amd_mp2_pci_tbl ) ;
static struct pci_driver amd_mp2_pci_driver = {
. name = DRIVER_NAME ,
. id_table = amd_mp2_pci_tbl ,
. probe = amd_mp2_pci_probe ,
2021-08-02 19:33:40 +05:30
. driver . pm = & amd_mp2_pm_ops ,
2023-04-11 21:40:27 +05:30
. shutdown = amd_sfh_shutdown ,
2020-10-10 01:31:36 +05:30
} ;
module_pci_driver ( amd_mp2_pci_driver ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
MODULE_LICENSE ( " Dual BSD/GPL " ) ;
MODULE_AUTHOR ( " Shyam Sundar S K <Shyam-sundar.S-k@amd.com> " ) ;
MODULE_AUTHOR ( " Sandeep Singh <Sandeep.singh@amd.com> " ) ;
2021-09-23 17:59:30 +05:30
MODULE_AUTHOR ( " Basavaraj Natikar <Basavaraj.Natikar@amd.com> " ) ;