brintos

brintos / linux-shallow public Read only

0
0
Text · 7.6 KiB · d48d8e5 Raw
358 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * sbrmi.c - hwmon driver for a SB-RMI mailbox4 *           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	020/* Mask for Status Register bit[1] */21#define SW_ALERT_MASK	0x222 23/* Software Interrupt for triggering */24#define START_CMD	0x8025#define TRIGGER_MAILBOX	0x0126 27/*28 * SB-RMI supports soft mailbox service request to MP1 (power management29 * firmware) through SBRMI inbound/outbound message registers.30 * SB-RMI message IDs31 */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 status82	 * 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) writes120	 * 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 to133	 * perform the requested read or write command134	 */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 generate142	 * an ALERT (if enabled) to initiator (BMC) to indicate completion143	 * of the requested command144	 */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 firmware166	 * 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 the181	 * ALERT to initiator182	 */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 microWatt235	 * mailbox read/write is in mWatt236	 */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 * const sbrmi_info[] = {269	HWMON_CHANNEL_INFO(power,270			   HWMON_P_INPUT | HWMON_P_CAP | HWMON_P_CAP_MAX),271	NULL272};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{302	struct device *dev = &client->dev;303	struct device *hwmon_dev;304	struct sbrmi_data *data;305	int ret;306 307	data = devm_kzalloc(dev, sizeof(struct sbrmi_data), GFP_KERNEL);308	if (!data)309		return -ENOMEM;310 311	data->client = client;312	mutex_init(&data->lock);313 314	/* Enable alert for SB-RMI sequence */315	ret = sbrmi_enable_alert(client);316	if (ret < 0)317		return ret;318 319	/* Cache maximum power limit */320	ret = sbrmi_get_max_pwr_limit(data);321	if (ret < 0)322		return ret;323 324	hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data,325							 &sbrmi_chip_info, NULL);326 327	return PTR_ERR_OR_ZERO(hwmon_dev);328}329 330static const struct i2c_device_id sbrmi_id[] = {331	{"sbrmi"},332	{}333};334MODULE_DEVICE_TABLE(i2c, sbrmi_id);335 336static const struct of_device_id __maybe_unused sbrmi_of_match[] = {337	{338		.compatible = "amd,sbrmi",339	},340	{ },341};342MODULE_DEVICE_TABLE(of, sbrmi_of_match);343 344static struct i2c_driver sbrmi_driver = {345	.driver = {346		.name = "sbrmi",347		.of_match_table = of_match_ptr(sbrmi_of_match),348	},349	.probe = sbrmi_probe,350	.id_table = sbrmi_id,351};352 353module_i2c_driver(sbrmi_driver);354 355MODULE_AUTHOR("Akshay Gupta <akshay.gupta@amd.com>");356MODULE_DESCRIPTION("Hwmon driver for AMD SB-RMI emulated sensor");357MODULE_LICENSE("GPL");358