cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

sbrmi.c (7889B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * sbrmi.c - hwmon driver for a SB-RMI mailbox
      4 *           compliant AMD SoC device.
      5 *
      6 * Copyright (C) 2020-2021 Advanced Micro Devices, Inc.
      7 */
      8
      9#include <linux/delay.h>
     10#include <linux/err.h>
     11#include <linux/hwmon.h>
     12#include <linux/i2c.h>
     13#include <linux/init.h>
     14#include <linux/module.h>
     15#include <linux/mutex.h>
     16#include <linux/of.h>
     17
     18/* Do not allow setting negative power limit */
     19#define SBRMI_PWR_MIN	0
     20/* Mask for Status Register bit[1] */
     21#define SW_ALERT_MASK	0x2
     22
     23/* Software Interrupt for triggering */
     24#define START_CMD	0x80
     25#define TRIGGER_MAILBOX	0x01
     26
     27/*
     28 * SB-RMI supports soft mailbox service request to MP1 (power management
     29 * firmware) through SBRMI inbound/outbound message registers.
     30 * SB-RMI message IDs
     31 */
     32enum sbrmi_msg_id {
     33	SBRMI_READ_PKG_PWR_CONSUMPTION = 0x1,
     34	SBRMI_WRITE_PKG_PWR_LIMIT,
     35	SBRMI_READ_PKG_PWR_LIMIT,
     36	SBRMI_READ_PKG_MAX_PWR_LIMIT,
     37};
     38
     39/* SB-RMI registers */
     40enum sbrmi_reg {
     41	SBRMI_CTRL		= 0x01,
     42	SBRMI_STATUS,
     43	SBRMI_OUTBNDMSG0	= 0x30,
     44	SBRMI_OUTBNDMSG1,
     45	SBRMI_OUTBNDMSG2,
     46	SBRMI_OUTBNDMSG3,
     47	SBRMI_OUTBNDMSG4,
     48	SBRMI_OUTBNDMSG5,
     49	SBRMI_OUTBNDMSG6,
     50	SBRMI_OUTBNDMSG7,
     51	SBRMI_INBNDMSG0,
     52	SBRMI_INBNDMSG1,
     53	SBRMI_INBNDMSG2,
     54	SBRMI_INBNDMSG3,
     55	SBRMI_INBNDMSG4,
     56	SBRMI_INBNDMSG5,
     57	SBRMI_INBNDMSG6,
     58	SBRMI_INBNDMSG7,
     59	SBRMI_SW_INTERRUPT,
     60};
     61
     62/* Each client has this additional data */
     63struct sbrmi_data {
     64	struct i2c_client *client;
     65	struct mutex lock;
     66	u32 pwr_limit_max;
     67};
     68
     69struct sbrmi_mailbox_msg {
     70	u8 cmd;
     71	bool read;
     72	u32 data_in;
     73	u32 data_out;
     74};
     75
     76static int sbrmi_enable_alert(struct i2c_client *client)
     77{
     78	int ctrl;
     79
     80	/*
     81	 * Enable the SB-RMI Software alert status
     82	 * by writing 0 to bit 4 of Control register(0x1)
     83	 */
     84	ctrl = i2c_smbus_read_byte_data(client, SBRMI_CTRL);
     85	if (ctrl < 0)
     86		return ctrl;
     87
     88	if (ctrl & 0x10) {
     89		ctrl &= ~0x10;
     90		return i2c_smbus_write_byte_data(client,
     91						 SBRMI_CTRL, ctrl);
     92	}
     93
     94	return 0;
     95}
     96
     97static int rmi_mailbox_xfer(struct sbrmi_data *data,
     98			    struct sbrmi_mailbox_msg *msg)
     99{
    100	int i, ret, retry = 10;
    101	int sw_status;
    102	u8 byte;
    103
    104	mutex_lock(&data->lock);
    105
    106	/* Indicate firmware a command is to be serviced */
    107	ret = i2c_smbus_write_byte_data(data->client,
    108					SBRMI_INBNDMSG7, START_CMD);
    109	if (ret < 0)
    110		goto exit_unlock;
    111
    112	/* Write the command to SBRMI::InBndMsg_inst0 */
    113	ret = i2c_smbus_write_byte_data(data->client,
    114					SBRMI_INBNDMSG0, msg->cmd);
    115	if (ret < 0)
    116		goto exit_unlock;
    117
    118	/*
    119	 * For both read and write the initiator (BMC) writes
    120	 * Command Data In[31:0] to SBRMI::InBndMsg_inst[4:1]
    121	 * SBRMI_x3C(MSB):SBRMI_x39(LSB)
    122	 */
    123	for (i = 0; i < 4; i++) {
    124		byte = (msg->data_in >> i * 8) & 0xff;
    125		ret = i2c_smbus_write_byte_data(data->client,
    126						SBRMI_INBNDMSG1 + i, byte);
    127		if (ret < 0)
    128			goto exit_unlock;
    129	}
    130
    131	/*
    132	 * Write 0x01 to SBRMI::SoftwareInterrupt to notify firmware to
    133	 * perform the requested read or write command
    134	 */
    135	ret = i2c_smbus_write_byte_data(data->client,
    136					SBRMI_SW_INTERRUPT, TRIGGER_MAILBOX);
    137	if (ret < 0)
    138		goto exit_unlock;
    139
    140	/*
    141	 * Firmware will write SBRMI::Status[SwAlertSts]=1 to generate
    142	 * an ALERT (if enabled) to initiator (BMC) to indicate completion
    143	 * of the requested command
    144	 */
    145	do {
    146		sw_status = i2c_smbus_read_byte_data(data->client,
    147						     SBRMI_STATUS);
    148		if (sw_status < 0) {
    149			ret = sw_status;
    150			goto exit_unlock;
    151		}
    152		if (sw_status & SW_ALERT_MASK)
    153			break;
    154		usleep_range(50, 100);
    155	} while (retry--);
    156
    157	if (retry < 0) {
    158		dev_err(&data->client->dev,
    159			"Firmware fail to indicate command completion\n");
    160		ret = -EIO;
    161		goto exit_unlock;
    162	}
    163
    164	/*
    165	 * For a read operation, the initiator (BMC) reads the firmware
    166	 * response Command Data Out[31:0] from SBRMI::OutBndMsg_inst[4:1]
    167	 * {SBRMI_x34(MSB):SBRMI_x31(LSB)}.
    168	 */
    169	if (msg->read) {
    170		for (i = 0; i < 4; i++) {
    171			ret = i2c_smbus_read_byte_data(data->client,
    172						       SBRMI_OUTBNDMSG1 + i);
    173			if (ret < 0)
    174				goto exit_unlock;
    175			msg->data_out |= ret << i * 8;
    176		}
    177	}
    178
    179	/*
    180	 * BMC must write 1'b1 to SBRMI::Status[SwAlertSts] to clear the
    181	 * ALERT to initiator
    182	 */
    183	ret = i2c_smbus_write_byte_data(data->client, SBRMI_STATUS,
    184					sw_status | SW_ALERT_MASK);
    185
    186exit_unlock:
    187	mutex_unlock(&data->lock);
    188	return ret;
    189}
    190
    191static int sbrmi_read(struct device *dev, enum hwmon_sensor_types type,
    192		      u32 attr, int channel, long *val)
    193{
    194	struct sbrmi_data *data = dev_get_drvdata(dev);
    195	struct sbrmi_mailbox_msg msg = { 0 };
    196	int ret;
    197
    198	if (type != hwmon_power)
    199		return -EINVAL;
    200
    201	msg.read = true;
    202	switch (attr) {
    203	case hwmon_power_input:
    204		msg.cmd = SBRMI_READ_PKG_PWR_CONSUMPTION;
    205		ret = rmi_mailbox_xfer(data, &msg);
    206		break;
    207	case hwmon_power_cap:
    208		msg.cmd = SBRMI_READ_PKG_PWR_LIMIT;
    209		ret = rmi_mailbox_xfer(data, &msg);
    210		break;
    211	case hwmon_power_cap_max:
    212		msg.data_out = data->pwr_limit_max;
    213		ret = 0;
    214		break;
    215	default:
    216		return -EINVAL;
    217	}
    218	if (ret < 0)
    219		return ret;
    220	/* hwmon power attributes are in microWatt */
    221	*val = (long)msg.data_out * 1000;
    222	return ret;
    223}
    224
    225static int sbrmi_write(struct device *dev, enum hwmon_sensor_types type,
    226		       u32 attr, int channel, long val)
    227{
    228	struct sbrmi_data *data = dev_get_drvdata(dev);
    229	struct sbrmi_mailbox_msg msg = { 0 };
    230
    231	if (type != hwmon_power && attr != hwmon_power_cap)
    232		return -EINVAL;
    233	/*
    234	 * hwmon power attributes are in microWatt
    235	 * mailbox read/write is in mWatt
    236	 */
    237	val /= 1000;
    238
    239	val = clamp_val(val, SBRMI_PWR_MIN, data->pwr_limit_max);
    240
    241	msg.cmd = SBRMI_WRITE_PKG_PWR_LIMIT;
    242	msg.data_in = val;
    243	msg.read = false;
    244
    245	return rmi_mailbox_xfer(data, &msg);
    246}
    247
    248static umode_t sbrmi_is_visible(const void *data,
    249				enum hwmon_sensor_types type,
    250				u32 attr, int channel)
    251{
    252	switch (type) {
    253	case hwmon_power:
    254		switch (attr) {
    255		case hwmon_power_input:
    256		case hwmon_power_cap_max:
    257			return 0444;
    258		case hwmon_power_cap:
    259			return 0644;
    260		}
    261		break;
    262	default:
    263		break;
    264	}
    265	return 0;
    266}
    267
    268static const struct hwmon_channel_info *sbrmi_info[] = {
    269	HWMON_CHANNEL_INFO(power,
    270			   HWMON_P_INPUT | HWMON_P_CAP | HWMON_P_CAP_MAX),
    271	NULL
    272};
    273
    274static const struct hwmon_ops sbrmi_hwmon_ops = {
    275	.is_visible = sbrmi_is_visible,
    276	.read = sbrmi_read,
    277	.write = sbrmi_write,
    278};
    279
    280static const struct hwmon_chip_info sbrmi_chip_info = {
    281	.ops = &sbrmi_hwmon_ops,
    282	.info = sbrmi_info,
    283};
    284
    285static int sbrmi_get_max_pwr_limit(struct sbrmi_data *data)
    286{
    287	struct sbrmi_mailbox_msg msg = { 0 };
    288	int ret;
    289
    290	msg.cmd = SBRMI_READ_PKG_MAX_PWR_LIMIT;
    291	msg.read = true;
    292	ret = rmi_mailbox_xfer(data, &msg);
    293	if (ret < 0)
    294		return ret;
    295	data->pwr_limit_max = msg.data_out;
    296
    297	return ret;
    298}
    299
    300static int sbrmi_probe(struct i2c_client *client,
    301		       const struct i2c_device_id *id)
    302{
    303	struct device *dev = &client->dev;
    304	struct device *hwmon_dev;
    305	struct sbrmi_data *data;
    306	int ret;
    307
    308	data = devm_kzalloc(dev, sizeof(struct sbrmi_data), GFP_KERNEL);
    309	if (!data)
    310		return -ENOMEM;
    311
    312	data->client = client;
    313	mutex_init(&data->lock);
    314
    315	/* Enable alert for SB-RMI sequence */
    316	ret = sbrmi_enable_alert(client);
    317	if (ret < 0)
    318		return ret;
    319
    320	/* Cache maximum power limit */
    321	ret = sbrmi_get_max_pwr_limit(data);
    322	if (ret < 0)
    323		return ret;
    324
    325	hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data,
    326							 &sbrmi_chip_info, NULL);
    327
    328	return PTR_ERR_OR_ZERO(hwmon_dev);
    329}
    330
    331static const struct i2c_device_id sbrmi_id[] = {
    332	{"sbrmi", 0},
    333	{}
    334};
    335MODULE_DEVICE_TABLE(i2c, sbrmi_id);
    336
    337static const struct of_device_id __maybe_unused sbrmi_of_match[] = {
    338	{
    339		.compatible = "amd,sbrmi",
    340	},
    341	{ },
    342};
    343MODULE_DEVICE_TABLE(of, sbrmi_of_match);
    344
    345static struct i2c_driver sbrmi_driver = {
    346	.class = I2C_CLASS_HWMON,
    347	.driver = {
    348		.name = "sbrmi",
    349		.of_match_table = of_match_ptr(sbrmi_of_match),
    350	},
    351	.probe = sbrmi_probe,
    352	.id_table = sbrmi_id,
    353};
    354
    355module_i2c_driver(sbrmi_driver);
    356
    357MODULE_AUTHOR("Akshay Gupta <akshay.gupta@amd.com>");
    358MODULE_DESCRIPTION("Hwmon driver for AMD SB-RMI emulated sensor");
    359MODULE_LICENSE("GPL");