text: // SPDX-License-Identifier: Apache-2.0
  /*
   * OPAL Sensor APIs
   *
   * Copyright 2013-2018 IBM Corp.
   */
  
  #include <sensor.h>
  #include <skiboot.h>
  #include <device.h>
  #include <opal.h>
  #include <dts.h>
  #include <lock.h>
  #include <occ.h>
  
  struct dt_node *sensor_node;
  
  static struct lock async_read_list_lock = LOCK_UNLOCKED;
  static LIST_HEAD(async_read_list);
  
  struct sensor_async_read {
  	struct list_node link;
  	u64 *sensor_data64;
  	u32 *sensor_data32;
  	int token;
  };
  
  static int add_to_async_read_list(int token, u32 *data32, u64 *data64)
  {
  	struct sensor_async_read *req;
  
  	req = zalloc(sizeof(*req));
  	if (!req)
  		return OPAL_NO_MEM;
  
  	req->token = token;
  	req->sensor_data64 = data64;
  	req->sensor_data32 = data32;
  
  	lock(&async_read_list_lock);
  	list_add_tail(&async_read_list, &req->link);
  	unlock(&async_read_list_lock);
  
  	return OPAL_ASYNC_COMPLETION;
  }
  
  void check_sensor_read(int token)
  {
  	struct sensor_async_read *req = NULL;
  
  	lock(&async_read_list_lock);
  	if (list_empty(&async_read_list))
  		goto out;
  
  	list_for_each(&async_read_list, req, link) {
  		if (req->token == token)
  			break;
  	}
  	if (!req)
  		goto out;
  
  	*req->sensor_data32 = *req->sensor_data64;
  	free(req->sensor_data64);
  	list_del(&req->link);
  	free(req);
  out:
  	unlock(&async_read_list_lock);
  }
  
  static s64 opal_sensor_read_u64(u32 sensor_hndl, int token, u64 *sensor_data)
  {
  	switch (sensor_get_family(sensor_hndl)) {
  	case SENSOR_DTS:
  		return dts_sensor_read(sensor_hndl, token, sensor_data);
  	case SENSOR_OCC:
  		return occ_sensor_read(sensor_hndl, sensor_data);
  	default:
  		break;
  	}
  
  	if (platform.sensor_read)
  		return platform.sensor_read(sensor_hndl, token, sensor_data);
  
  	return OPAL_UNSUPPORTED;
  }
  
  static int64_t opal_sensor_read(uint32_t sensor_hndl, int token,
  				uint32_t *sensor_data)
  {
  	u64 *val;
  	s64 ret;
  
  	val = zalloc(sizeof(*val));
  	if (!val)
  		return OPAL_NO_MEM;
  
  	ret = opal_sensor_read_u64(sensor_hndl, token, val);
  	if (!ret) {
  		*sensor_data = *val;
  		free(val);
  	} else if (ret == OPAL_ASYNC_COMPLETION) {
  		ret = add_to_async_read_list(token, sensor_data, val);
  	}
  
  	return ret;
  }
  
  static int opal_sensor_group_clear(u32 group_hndl, int token)
  {
  	switch (sensor_get_family(group_hndl)) {
  	case SENSOR_OCC:
  		return occ_sensor_group_clear(group_hndl, token);
  	default:
  		break;
  	}
  
  	return OPAL_UNSUPPORTED;
  }
  
  static int opal_sensor_group_enable(u32 group_hndl, int token, bool enable)
  {
  	switch (sensor_get_family(group_hndl)) {
  	case SENSOR_OCC:
  		return occ_sensor_group_enable(group_hndl, token, enable);
  	default:
  		break;
  	}
  
  	return OPAL_UNSUPPORTED;
  }
  void sensor_init(void)
  {
  	sensor_node = dt_new(opal_node, "sensors");
  
  	dt_add_property_string(sensor_node, "compatible", "ibm,opal-sensor");
  	dt_add_property_cells(sensor_node, "#address-cells", 1);
  	dt_add_property_cells(sensor_node, "#size-cells", 0);
  
  	/* Register OPAL interface */
  	opal_register(OPAL_SENSOR_READ, opal_sensor_read, 3);
  	opal_register(OPAL_SENSOR_GROUP_CLEAR, opal_sensor_group_clear, 2);
  	opal_register(OPAL_SENSOR_READ_U64, opal_sensor_read_u64, 3);
  	opal_register(OPAL_SENSOR_GROUP_ENABLE, opal_sensor_group_enable, 3);
  }
  