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_hpd_info(u8 *user_present) 91 { 92 struct hpd_status hpdstatus; 93 94 if (!user_present) 95 return -EINVAL; 96 97 if (!emp2 || !emp2->dev_en.is_hpd_present) 98 return -ENODEV; 99 100 hpdstatus.val = readl(emp2->mmio + AMD_C2P_MSG(4)); 101 *user_present = hpdstatus.shpd.presence; 102 103 return 0; 104 } 105 106 static int amd_sfh_als_info(u32 *ambient_light) 107 { 108 struct sfh_als_data als_data; 109 void __iomem *sensoraddr; 110 111 if (!ambient_light) 112 return -EINVAL; 113 114 if (!emp2 || !emp2->dev_en.is_als_present) 115 return -ENODEV; 116 117 sensoraddr = emp2->vsbase + 118 (ALS_IDX * SENSOR_DATA_MEM_SIZE_DEFAULT) + 119 OFFSET_SENSOR_DATA_DEFAULT; 120 memcpy_fromio(&als_data, sensoraddr, sizeof(struct sfh_als_data)); 121 *ambient_light = amd_sfh_float_to_int(als_data.lux); 122 123 return 0; 124 } 125 126 int amd_get_sfh_info(struct amd_sfh_info *sfh_info, enum sfh_message_type op) 127 { 128 if (sfh_info) { 129 switch (op) { 130 case MT_HPD: 131 return amd_sfh_hpd_info(&sfh_info->user_present); 132 case MT_ALS: 133 return amd_sfh_als_info(&sfh_info->ambient_light); 134 } 135 } 136 return -EINVAL; 137 } 138 EXPORT_SYMBOL_GPL(amd_get_sfh_info); 139