1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * AMD MP2 1.1 communication interfaces 4 * 5 * Copyright (c) 2022, Advanced Micro Devices, Inc. 6 * All Rights Reserved. 7 * 8 * Author: Basavaraj Natikar <Basavaraj.Natikar@amd.com> 9 */ 10 #include <linux/amd-pmf-io.h> 11 #include <linux/io-64-nonatomic-lo-hi.h> 12 #include <linux/iopoll.h> 13 14 #include "amd_sfh_interface.h" 15 16 static struct amd_mp2_dev *emp2; 17 18 static int amd_sfh_wait_response(struct amd_mp2_dev *mp2, u8 sid, u32 cmd_id) 19 { 20 struct sfh_cmd_response cmd_resp; 21 22 /* Get response with status within a max of 10000 ms timeout */ 23 if (!readl_poll_timeout(mp2->mmio + amd_get_p2c_val(mp2, 0), cmd_resp.resp, 24 (cmd_resp.response.response == 0 && 25 cmd_resp.response.cmd_id == cmd_id && (sid == 0xff || 26 cmd_resp.response.sensor_id == sid)), 500, 10000000)) 27 return cmd_resp.response.response; 28 29 return -1; 30 } 31 32 static void amd_start_sensor(struct amd_mp2_dev *privdata, struct amd_mp2_sensor_info info) 33 { 34 struct sfh_cmd_base cmd_base; 35 36 cmd_base.ul = 0; 37 cmd_base.cmd.cmd_id = ENABLE_SENSOR; 38 cmd_base.cmd.intr_disable = 0; 39 cmd_base.cmd.sub_cmd_value = 1; 40 cmd_base.cmd.sensor_id = info.sensor_idx; 41 42 writel(cmd_base.ul, privdata->mmio + amd_get_c2p_val(privdata, 0)); 43 } 44 45 static void amd_stop_sensor(struct amd_mp2_dev *privdata, u16 sensor_idx) 46 { 47 struct sfh_cmd_base cmd_base; 48 49 cmd_base.ul = 0; 50 cmd_base.cmd.cmd_id = DISABLE_SENSOR; 51 cmd_base.cmd.intr_disable = 0; 52 cmd_base.cmd.sub_cmd_value = 1; 53 cmd_base.cmd.sensor_id = sensor_idx; 54 55 writeq(0x0, privdata->mmio + amd_get_c2p_val(privdata, 1)); 56 writel(cmd_base.ul, privdata->mmio + amd_get_c2p_val(privdata, 0)); 57 } 58 59 static void amd_stop_all_sensor(struct amd_mp2_dev *privdata) 60 { 61 struct sfh_cmd_base cmd_base; 62 63 cmd_base.ul = 0; 64 cmd_base.cmd.cmd_id = DISABLE_SENSOR; 65 cmd_base.cmd.intr_disable = 0; 66 /* 0xf indicates all sensors */ 67 cmd_base.cmd.sensor_id = 0xf; 68 69 writel(cmd_base.ul, privdata->mmio + amd_get_c2p_val(privdata, 0)); 70 } 71 72 static struct amd_mp2_ops amd_sfh_ops = { 73 .start = amd_start_sensor, 74 .stop = amd_stop_sensor, 75 .stop_all = amd_stop_all_sensor, 76 .response = amd_sfh_wait_response, 77 }; 78 79 void sfh_deinit_emp2(void) 80 { 81 emp2 = NULL; 82 } 83 84 void sfh_interface_init(struct amd_mp2_dev *mp2) 85 { 86 mp2->mp2_ops = &amd_sfh_ops; 87 emp2 = mp2; 88 } 89 90 static int amd_sfh_mode_info(u32 *platform_type, u32 *laptop_placement) 91 { 92 struct sfh_op_mode mode; 93 94 if (!platform_type || !laptop_placement) 95 return -EINVAL; 96 97 if (!emp2 || !emp2->dev_en.is_sra_present) 98 return -ENODEV; 99 100 mode.val = readl(emp2->mmio + amd_get_c2p_val(emp2, 3)); 101 102 *platform_type = mode.op_mode.devicemode; 103 104 if (mode.op_mode.ontablestate == 1) { 105 *laptop_placement = ON_TABLE; 106 } else if (mode.op_mode.ontablestate == 2) { 107 *laptop_placement = ON_LAP_MOTION; 108 } else if (mode.op_mode.inbagstate == 1) { 109 *laptop_placement = IN_BAG; 110 } else if (mode.op_mode.outbagstate == 1) { 111 *laptop_placement = OUT_OF_BAG; 112 } else if (mode.op_mode.ontablestate == 0 || mode.op_mode.inbagstate == 0 || 113 mode.op_mode.outbagstate == 0) { 114 *laptop_placement = LP_UNKNOWN; 115 pr_warn_once("Unknown laptop placement\n"); 116 } else if (mode.op_mode.ontablestate == 3 || mode.op_mode.inbagstate == 3 || 117 mode.op_mode.outbagstate == 3) { 118 *laptop_placement = LP_UNDEFINED; 119 pr_warn_once("Undefined laptop placement\n"); 120 } 121 122 return 0; 123 } 124 125 static int amd_sfh_hpd_info(u8 *user_present) 126 { 127 struct hpd_status hpdstatus; 128 129 if (!user_present) 130 return -EINVAL; 131 132 if (!emp2 || !emp2->dev_en.is_hpd_present || !emp2->dev_en.is_hpd_enabled) 133 return -ENODEV; 134 135 hpdstatus.val = readl(emp2->mmio + amd_get_c2p_val(emp2, 4)); 136 *user_present = hpdstatus.shpd.presence; 137 138 return 0; 139 } 140 141 static int amd_sfh_als_info(u32 *ambient_light) 142 { 143 struct sfh_als_data als_data; 144 void __iomem *sensoraddr; 145 146 if (!ambient_light) 147 return -EINVAL; 148 149 if (!emp2 || !emp2->dev_en.is_als_present) 150 return -ENODEV; 151 152 sensoraddr = emp2->vsbase + 153 (ALS_IDX * SENSOR_DATA_MEM_SIZE_DEFAULT) + 154 OFFSET_SENSOR_DATA_DEFAULT; 155 memcpy_fromio(&als_data, sensoraddr, sizeof(struct sfh_als_data)); 156 *ambient_light = amd_sfh_float_to_int(als_data.lux); 157 158 return 0; 159 } 160 161 int amd_get_sfh_info(struct amd_sfh_info *sfh_info, enum sfh_message_type op) 162 { 163 if (sfh_info) { 164 switch (op) { 165 case MT_HPD: 166 return amd_sfh_hpd_info(&sfh_info->user_present); 167 case MT_ALS: 168 return amd_sfh_als_info(&sfh_info->ambient_light); 169 case MT_SRA: 170 return amd_sfh_mode_info(&sfh_info->platform_type, 171 &sfh_info->laptop_placement); 172 } 173 } 174 return -EINVAL; 175 } 176 EXPORT_SYMBOL_GPL(amd_get_sfh_info); 177