brintos

brintos / linux-shallow public Read only

0
0
Text · 14.7 KiB · 1ba4c5e Raw
618 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright 2017 IBM Corp.4 */5 6#include <linux/bitfield.h>7#include <linux/bitops.h>8#include <linux/debugfs.h>9#include <linux/device.h>10#include <linux/fs.h>11#include <linux/i2c.h>12#include <linux/jiffies.h>13#include <linux/leds.h>14#include <linux/module.h>15#include <linux/mutex.h>16#include <linux/of.h>17#include <linux/pmbus.h>18 19#include "pmbus.h"20 21#define CFFPS_CCIN_CMD				0xBD22#define CFFPS_FW_CMD				0xFA23#define CFFPS1_FW_NUM_BYTES			424#define CFFPS2_FW_NUM_WORDS			325#define CFFPS_SYS_CONFIG_CMD			0xDA26#define CFFPS_12VCS_VOUT_CMD			0xDE27 28#define CFFPS_INPUT_HISTORY_CMD			0xD629#define CFFPS_INPUT_HISTORY_SIZE		10130 31#define CFFPS_CCIN_REVISION			GENMASK(7, 0)32#define CFFPS_CCIN_REVISION_LEGACY		 0xde33#define CFFPS_CCIN_VERSION			GENMASK(15, 8)34#define CFFPS_CCIN_VERSION_1			 0x2b35#define CFFPS_CCIN_VERSION_2			 0x2e36#define CFFPS_CCIN_VERSION_3			 0x5137 38/* STATUS_MFR_SPECIFIC bits */39#define CFFPS_MFR_FAN_FAULT			BIT(0)40#define CFFPS_MFR_THERMAL_FAULT			BIT(1)41#define CFFPS_MFR_OV_FAULT			BIT(2)42#define CFFPS_MFR_UV_FAULT			BIT(3)43#define CFFPS_MFR_PS_KILL			BIT(4)44#define CFFPS_MFR_OC_FAULT			BIT(5)45#define CFFPS_MFR_VAUX_FAULT			BIT(6)46#define CFFPS_MFR_CURRENT_SHARE_WARNING		BIT(7)47 48#define CFFPS_LED_BLINK				(BIT(0) | BIT(6))49#define CFFPS_LED_ON				(BIT(1) | BIT(6))50#define CFFPS_LED_OFF				(BIT(2) | BIT(6))51#define CFFPS_BLINK_RATE_MS			25052 53enum {54	CFFPS_DEBUGFS_MAX_POWER_OUT = 0,55	CFFPS_DEBUGFS_CCIN,56	CFFPS_DEBUGFS_FW,57	CFFPS_DEBUGFS_ON_OFF_CONFIG,58	CFFPS_DEBUGFS_NUM_ENTRIES59};60 61enum versions { cffps1, cffps2, cffps_unknown };62 63struct ibm_cffps {64	enum versions version;65	struct i2c_client *client;66 67	u8 input_history[CFFPS_INPUT_HISTORY_SIZE];68 69	int debugfs_entries[CFFPS_DEBUGFS_NUM_ENTRIES];70 71	char led_name[32];72	u8 led_state;73	struct led_classdev led;74};75 76#define to_psu(x, y) container_of((x), struct ibm_cffps, debugfs_entries[(y)])77 78static ssize_t ibm_cffps_debugfs_read_input_history(struct file *file, char __user *buf,79						    size_t count, loff_t *ppos)80{81	int rc;82	u8 cmd = CFFPS_INPUT_HISTORY_CMD;83	struct ibm_cffps *psu = file->private_data;84	struct i2c_msg msg[2] = {85		{86			.addr = psu->client->addr,87			.flags = psu->client->flags,88			.len = 1,89			.buf = &cmd,90		}, {91			.addr = psu->client->addr,92			.flags = psu->client->flags | I2C_M_RD,93			.len = CFFPS_INPUT_HISTORY_SIZE,94			.buf = psu->input_history,95		},96	};97 98	if (!*ppos) {99		rc = pmbus_lock_interruptible(psu->client);100		if (rc)101			return rc;102 103		rc = pmbus_set_page(psu->client, 0, 0xff);104		if (rc) {105			pmbus_unlock(psu->client);106			return rc;107		}108 109		/*110		 * Use a raw i2c transfer, since we need more bytes111		 * than Linux I2C supports through smbus xfr (only 32).112		 */113		rc = i2c_transfer(psu->client->adapter, msg, 2);114		pmbus_unlock(psu->client);115		if (rc < 0)116			return rc;117	}118 119	return simple_read_from_buffer(buf, count, ppos,120				       psu->input_history + 1,121				       psu->input_history[0]);122}123 124static const struct file_operations ibm_cffps_input_history_fops = {125	.llseek = noop_llseek,126	.read = ibm_cffps_debugfs_read_input_history,127	.open = simple_open,128};129 130static ssize_t ibm_cffps_debugfs_read(struct file *file, char __user *buf,131				      size_t count, loff_t *ppos)132{133	int i, rc;134	int *idxp = file->private_data;135	int idx = *idxp;136	struct ibm_cffps *psu = to_psu(idxp, idx);137	char data[I2C_SMBUS_BLOCK_MAX + 2] = { 0 };138 139	rc = pmbus_lock_interruptible(psu->client);140	if (rc)141		return rc;142 143	rc = pmbus_set_page(psu->client, 0, 0xff);144	if (rc)145		goto unlock;146 147	switch (idx) {148	case CFFPS_DEBUGFS_MAX_POWER_OUT:149		if (psu->version == cffps1)150			rc = i2c_smbus_read_word_swapped(psu->client, PMBUS_MFR_POUT_MAX);151		else152			rc = i2c_smbus_read_word_data(psu->client, PMBUS_MFR_POUT_MAX);153		if (rc >= 0)154			rc = snprintf(data, I2C_SMBUS_BLOCK_MAX, "%d", rc);155		break;156	case CFFPS_DEBUGFS_CCIN:157		rc = i2c_smbus_read_word_swapped(psu->client, CFFPS_CCIN_CMD);158		if (rc >= 0)159			rc = snprintf(data, 5, "%04X", rc);160		break;161	case CFFPS_DEBUGFS_FW:162		switch (psu->version) {163		case cffps1:164			for (i = 0; i < CFFPS1_FW_NUM_BYTES; ++i) {165				rc = i2c_smbus_read_byte_data(psu->client, CFFPS_FW_CMD + i);166				if (rc < 0)167					goto unlock;168 169				snprintf(&data[i * 2], 3, "%02X", rc);170			}171 172			rc = i * 2;173			break;174		case cffps2:175			for (i = 0; i < CFFPS2_FW_NUM_WORDS; ++i) {176				rc = i2c_smbus_read_word_data(psu->client, CFFPS_FW_CMD + i);177				if (rc < 0)178					goto unlock;179 180				snprintf(&data[i * 4], 5, "%04X", rc);181			}182 183			rc = i * 4;184			break;185		default:186			rc = -EOPNOTSUPP;187			break;188		}189		break;190	case CFFPS_DEBUGFS_ON_OFF_CONFIG:191		rc = i2c_smbus_read_byte_data(psu->client, PMBUS_ON_OFF_CONFIG);192		if (rc >= 0)193			rc = snprintf(data, 3, "%02x", rc);194		break;195	default:196		rc = -EINVAL;197		break;198	}199 200unlock:201	pmbus_unlock(psu->client);202	if (rc < 0)203		return rc;204 205	data[rc] = '\n';206	rc += 2;207 208	return simple_read_from_buffer(buf, count, ppos, data, rc);209}210 211static ssize_t ibm_cffps_debugfs_write(struct file *file,212				       const char __user *buf, size_t count,213				       loff_t *ppos)214{215	u8 data;216	ssize_t rc;217	int *idxp = file->private_data;218	int idx = *idxp;219	struct ibm_cffps *psu = to_psu(idxp, idx);220 221	switch (idx) {222	case CFFPS_DEBUGFS_ON_OFF_CONFIG:223		rc = simple_write_to_buffer(&data, 1, ppos, buf, count);224		if (rc <= 0)225			return rc;226 227		rc = pmbus_lock_interruptible(psu->client);228		if (rc)229			return rc;230 231		rc = pmbus_set_page(psu->client, 0, 0xff);232		if (rc) {233			pmbus_unlock(psu->client);234			return rc;235		}236 237		rc = i2c_smbus_write_byte_data(psu->client, PMBUS_ON_OFF_CONFIG, data);238		pmbus_unlock(psu->client);239		if (rc)240			return rc;241 242		rc = 1;243		break;244	default:245		return -EINVAL;246	}247 248	return rc;249}250 251static const struct file_operations ibm_cffps_fops = {252	.llseek = noop_llseek,253	.read = ibm_cffps_debugfs_read,254	.write = ibm_cffps_debugfs_write,255	.open = simple_open,256};257 258static int ibm_cffps_read_byte_data(struct i2c_client *client, int page,259				    int reg)260{261	int rc, mfr;262 263	switch (reg) {264	case PMBUS_STATUS_VOUT:265	case PMBUS_STATUS_IOUT:266	case PMBUS_STATUS_TEMPERATURE:267	case PMBUS_STATUS_FAN_12:268		rc = pmbus_read_byte_data(client, page, reg);269		if (rc < 0)270			return rc;271 272		mfr = pmbus_read_byte_data(client, page,273					   PMBUS_STATUS_MFR_SPECIFIC);274		if (mfr < 0)275			/*276			 * Return the status register instead of an error,277			 * since we successfully read status.278			 */279			return rc;280 281		/* Add MFR_SPECIFIC bits to the standard pmbus status regs. */282		if (reg == PMBUS_STATUS_FAN_12) {283			if (mfr & CFFPS_MFR_FAN_FAULT)284				rc |= PB_FAN_FAN1_FAULT;285		} else if (reg == PMBUS_STATUS_TEMPERATURE) {286			if (mfr & CFFPS_MFR_THERMAL_FAULT)287				rc |= PB_TEMP_OT_FAULT;288		} else if (reg == PMBUS_STATUS_VOUT) {289			if (mfr & (CFFPS_MFR_OV_FAULT | CFFPS_MFR_VAUX_FAULT))290				rc |= PB_VOLTAGE_OV_FAULT;291			if (mfr & CFFPS_MFR_UV_FAULT)292				rc |= PB_VOLTAGE_UV_FAULT;293		} else if (reg == PMBUS_STATUS_IOUT) {294			if (mfr & CFFPS_MFR_OC_FAULT)295				rc |= PB_IOUT_OC_FAULT;296			if (mfr & CFFPS_MFR_CURRENT_SHARE_WARNING)297				rc |= PB_CURRENT_SHARE_FAULT;298		}299		break;300	default:301		rc = -ENODATA;302		break;303	}304 305	return rc;306}307 308static int ibm_cffps_read_word_data(struct i2c_client *client, int page,309				    int phase, int reg)310{311	int rc, mfr;312 313	switch (reg) {314	case PMBUS_STATUS_WORD:315		rc = pmbus_read_word_data(client, page, phase, reg);316		if (rc < 0)317			return rc;318 319		mfr = pmbus_read_byte_data(client, page,320					   PMBUS_STATUS_MFR_SPECIFIC);321		if (mfr < 0)322			/*323			 * Return the status register instead of an error,324			 * since we successfully read status.325			 */326			return rc;327 328		if (mfr & CFFPS_MFR_PS_KILL)329			rc |= PB_STATUS_OFF;330		break;331	case PMBUS_VIRT_READ_VMON:332		rc = pmbus_read_word_data(client, page, phase,333					  CFFPS_12VCS_VOUT_CMD);334		break;335	default:336		rc = -ENODATA;337		break;338	}339 340	return rc;341}342 343static int ibm_cffps_led_brightness_set(struct led_classdev *led_cdev,344					enum led_brightness brightness)345{346	int rc;347	u8 next_led_state;348	struct ibm_cffps *psu = container_of(led_cdev, struct ibm_cffps, led);349 350	if (brightness == LED_OFF) {351		next_led_state = CFFPS_LED_OFF;352	} else {353		brightness = LED_FULL;354 355		if (psu->led_state != CFFPS_LED_BLINK)356			next_led_state = CFFPS_LED_ON;357		else358			next_led_state = CFFPS_LED_BLINK;359	}360 361	dev_dbg(&psu->client->dev, "LED brightness set: %d. Command: %d.\n",362		brightness, next_led_state);363 364	rc = pmbus_lock_interruptible(psu->client);365	if (rc)366		return rc;367 368	rc = pmbus_set_page(psu->client, 0, 0xff);369	if (rc) {370		pmbus_unlock(psu->client);371		return rc;372	}373 374	rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD,375				       next_led_state);376	pmbus_unlock(psu->client);377	if (rc < 0)378		return rc;379 380	psu->led_state = next_led_state;381	led_cdev->brightness = brightness;382 383	return 0;384}385 386static int ibm_cffps_led_blink_set(struct led_classdev *led_cdev,387				   unsigned long *delay_on,388				   unsigned long *delay_off)389{390	int rc;391	struct ibm_cffps *psu = container_of(led_cdev, struct ibm_cffps, led);392 393	dev_dbg(&psu->client->dev, "LED blink set.\n");394 395	rc = pmbus_lock_interruptible(psu->client);396	if (rc)397		return rc;398 399	rc = pmbus_set_page(psu->client, 0, 0xff);400	if (rc) {401		pmbus_unlock(psu->client);402		return rc;403	}404 405	rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD,406				       CFFPS_LED_BLINK);407	pmbus_unlock(psu->client);408	if (rc < 0)409		return rc;410 411	psu->led_state = CFFPS_LED_BLINK;412	led_cdev->brightness = LED_FULL;413	*delay_on = CFFPS_BLINK_RATE_MS;414	*delay_off = CFFPS_BLINK_RATE_MS;415 416	return 0;417}418 419static void ibm_cffps_create_led_class(struct ibm_cffps *psu)420{421	int rc;422	struct i2c_client *client = psu->client;423	struct device *dev = &client->dev;424 425	snprintf(psu->led_name, sizeof(psu->led_name), "%s-%02x", client->name,426		 client->addr);427	psu->led.name = psu->led_name;428	psu->led.max_brightness = LED_FULL;429	psu->led.brightness_set_blocking = ibm_cffps_led_brightness_set;430	psu->led.blink_set = ibm_cffps_led_blink_set;431 432	rc = devm_led_classdev_register(dev, &psu->led);433	if (rc)434		dev_warn(dev, "failed to register led class: %d\n", rc);435	else436		i2c_smbus_write_byte_data(client, CFFPS_SYS_CONFIG_CMD,437					  CFFPS_LED_OFF);438}439 440static struct pmbus_driver_info ibm_cffps_info[] = {441	[cffps1] = {442		.pages = 1,443		.func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |444			PMBUS_HAVE_PIN | PMBUS_HAVE_FAN12 | PMBUS_HAVE_TEMP |445			PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3 |446			PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT |447			PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP |448			PMBUS_HAVE_STATUS_FAN12,449		.read_byte_data = ibm_cffps_read_byte_data,450		.read_word_data = ibm_cffps_read_word_data,451	},452	[cffps2] = {453		.pages = 2,454		.func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |455			PMBUS_HAVE_PIN | PMBUS_HAVE_FAN12 | PMBUS_HAVE_TEMP |456			PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3 |457			PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT |458			PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP |459			PMBUS_HAVE_STATUS_FAN12 | PMBUS_HAVE_VMON,460		.func[1] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |461			PMBUS_HAVE_PIN | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 |462			PMBUS_HAVE_TEMP3 | PMBUS_HAVE_STATUS_VOUT |463			PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_INPUT |464			PMBUS_HAVE_STATUS_TEMP,465		.read_byte_data = ibm_cffps_read_byte_data,466		.read_word_data = ibm_cffps_read_word_data,467	},468};469 470static struct pmbus_platform_data ibm_cffps_pdata = {471	.flags = PMBUS_SKIP_STATUS_CHECK | PMBUS_NO_CAPABILITY,472};473 474static const struct i2c_device_id ibm_cffps_id[] = {475	{ "ibm_cffps1", cffps1 },476	{ "ibm_cffps2", cffps2 },477	{ "ibm_cffps", cffps_unknown },478	{}479};480MODULE_DEVICE_TABLE(i2c, ibm_cffps_id);481 482static int ibm_cffps_probe(struct i2c_client *client)483{484	int i, rc;485	enum versions vs = cffps_unknown;486	struct dentry *debugfs;487	struct ibm_cffps *psu;488	const void *md = of_device_get_match_data(&client->dev);489	const struct i2c_device_id *id;490 491	if (md) {492		vs = (uintptr_t)md;493	} else {494		id = i2c_match_id(ibm_cffps_id, client);495		if (id)496			vs = (enum versions)id->driver_data;497	}498 499	if (vs == cffps_unknown) {500		u16 ccin_revision = 0;501		u16 ccin_version = CFFPS_CCIN_VERSION_1;502		int ccin = i2c_smbus_read_word_swapped(client, CFFPS_CCIN_CMD);503		char mfg_id[I2C_SMBUS_BLOCK_MAX + 2] = { 0 };504 505		if (ccin > 0) {506			ccin_revision = FIELD_GET(CFFPS_CCIN_REVISION, ccin);507			ccin_version = FIELD_GET(CFFPS_CCIN_VERSION, ccin);508		}509 510		rc = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, mfg_id);511		if (rc < 0) {512			dev_err(&client->dev, "Failed to read Manufacturer ID\n");513			return rc;514		}515 516		switch (ccin_version) {517		default:518		case CFFPS_CCIN_VERSION_1:519			if ((strncmp(mfg_id, "ACBE", 4) == 0) ||520				     (strncmp(mfg_id, "ARTE", 4) == 0))521				vs = cffps1;522			else523				vs = cffps2;524			break;525		case CFFPS_CCIN_VERSION_2:526			vs = cffps2;527			break;528		case CFFPS_CCIN_VERSION_3:529			if (ccin_revision == CFFPS_CCIN_REVISION_LEGACY)530				vs = cffps1;531			else532				vs = cffps2;533			break;534		}535 536		/* Set the client name to include the version number. */537		snprintf(client->name, I2C_NAME_SIZE, "cffps%d", vs + 1);538	}539 540	client->dev.platform_data = &ibm_cffps_pdata;541	rc = pmbus_do_probe(client, &ibm_cffps_info[vs]);542	if (rc)543		return rc;544 545	/*546	 * Don't fail the probe if there isn't enough memory for leds and547	 * debugfs.548	 */549	psu = devm_kzalloc(&client->dev, sizeof(*psu), GFP_KERNEL);550	if (!psu)551		return 0;552 553	psu->version = vs;554	psu->client = client;555 556	ibm_cffps_create_led_class(psu);557 558	/* Don't fail the probe if we can't create debugfs */559	debugfs = pmbus_get_debugfs_dir(client);560	if (!debugfs)561		return 0;562 563	for (i = 0; i < CFFPS_DEBUGFS_NUM_ENTRIES; ++i)564		psu->debugfs_entries[i] = i;565 566	debugfs_create_file("input_history", 0444, debugfs, psu, &ibm_cffps_input_history_fops);567	debugfs_create_file("max_power_out", 0444, debugfs,568			    &psu->debugfs_entries[CFFPS_DEBUGFS_MAX_POWER_OUT],569			    &ibm_cffps_fops);570	debugfs_create_file("ccin", 0444, debugfs,571			    &psu->debugfs_entries[CFFPS_DEBUGFS_CCIN],572			    &ibm_cffps_fops);573	debugfs_create_file("fw_version", 0444, debugfs,574			    &psu->debugfs_entries[CFFPS_DEBUGFS_FW],575			    &ibm_cffps_fops);576	debugfs_create_file("on_off_config", 0644, debugfs,577			    &psu->debugfs_entries[CFFPS_DEBUGFS_ON_OFF_CONFIG],578			    &ibm_cffps_fops);579 580	/* For compatibility with users of the old naming scheme. */581	debugfs_create_symlink(client->name, debugfs, ".");582 583	return 0;584}585 586static const struct of_device_id ibm_cffps_of_match[] = {587	{588		.compatible = "ibm,cffps1",589		.data = (void *)cffps1590	},591	{592		.compatible = "ibm,cffps2",593		.data = (void *)cffps2594	},595	{596		.compatible = "ibm,cffps",597		.data = (void *)cffps_unknown598	},599	{}600};601MODULE_DEVICE_TABLE(of, ibm_cffps_of_match);602 603static struct i2c_driver ibm_cffps_driver = {604	.driver = {605		.name = "ibm-cffps",606		.of_match_table = ibm_cffps_of_match,607	},608	.probe = ibm_cffps_probe,609	.id_table = ibm_cffps_id,610};611 612module_i2c_driver(ibm_cffps_driver);613 614MODULE_AUTHOR("Eddie James");615MODULE_DESCRIPTION("PMBus driver for IBM Common Form Factor power supplies");616MODULE_LICENSE("GPL");617MODULE_IMPORT_NS(PMBUS);618