// SPDX-License-Identifier: GPL-2.0-or-later /* * Hardware monitoring driver for UCD90xxx Sequencer and System Health * Controller series * * Copyright (C) 2011 Ericsson AB.
*/
/* * It has been observed that the UCD90320 randomly fails register access when * doing another access right on the back of a register write. To mitigate this * make sure that there is a minimum delay between a write access and the * following access. The 500 is based on experimental data. At a delay of * 350us the issue seems to go away. Add a bit of extra margin to allow for * system to system differences.
*/ #define UCD90320_WAIT_DELAY_US 500
staticint ucd9000_get_fan_config(struct i2c_client *client, int fan)
{ int fan_config = 0; struct ucd9000_data *data
= to_ucd9000_data(pmbus_get_driver_info(client));
if (data->fan_data[fan][3] & 1)
fan_config |= PB_FAN_2_INSTALLED; /* Use lower bit position */
staticint ucd9000_read_byte_data(struct i2c_client *client, int page, int reg)
{ int ret = 0; int fan_config;
switch (reg) { case PMBUS_FAN_CONFIG_12: if (page > 0) return -ENXIO;
ret = ucd9000_get_fan_config(client, 0); if (ret < 0) return ret;
fan_config = ret << 4;
ret = ucd9000_get_fan_config(client, 1); if (ret < 0) return ret;
fan_config |= ret;
ret = fan_config; break; case PMBUS_FAN_CONFIG_34: if (page > 0) return -ENXIO;
ret = ucd9000_get_fan_config(client, 2); if (ret < 0) return ret;
fan_config = ret << 4;
ret = ucd9000_get_fan_config(client, 3); if (ret < 0) return ret;
fan_config |= ret;
ret = fan_config; break; default:
ret = -ENODATA; break;
} return ret;
}
switch (mid->driver_data) { case ucd9090:
data->gpio.ngpio = UCD9090_NUM_GPIOS; break; case ucd90120: case ucd90124: case ucd90160:
data->gpio.ngpio = UCD901XX_NUM_GPIOS; break; case ucd90320:
data->gpio.ngpio = UCD90320_NUM_GPIOS; break; case ucd90910:
data->gpio.ngpio = UCD90910_NUM_GPIOS; break; default: return; /* GPIO support is optional. */
}
/* * Pinmux support has not been added to the new gpio_chip. * This support should be added when possible given the mux * behavior of these IO devices.
*/
data->gpio.label = client->name;
data->gpio.get_direction = ucd9000_gpio_get_direction;
data->gpio.direction_input = ucd9000_gpio_direction_input;
data->gpio.direction_output = ucd9000_gpio_direction_output;
data->gpio.get = ucd9000_gpio_get;
data->gpio.set = ucd9000_gpio_set;
data->gpio.can_sleep = true;
data->gpio.base = -1;
data->gpio.parent = &client->dev;
ret = ucd9000_get_mfr_status(client, buffer); if (ret < 0) return ret;
/* * GPI fault bits are in sets of 8, two bytes from end of response.
*/
i = ret - 3 - entry->index / 8; if (i >= 0)
*val = !!(buffer[i] & BIT(entry->index % 8));
/* * Of the chips this driver supports, only the UCD9090, UCD90160, * UCD90320, and UCD90910 report GPI faults in their MFR_STATUS * register, so only create the GPI fault debugfs attributes for those * chips.
*/ if (mid->driver_data == ucd9090 || mid->driver_data == ucd90160 ||
mid->driver_data == ucd90320 || mid->driver_data == ucd90910) {
gpi_count = mid->driver_data == ucd90320 ? UCD90320_GPI_COUNT
: UCD9000_GPI_COUNT;
entries = devm_kcalloc(&client->dev,
gpi_count, sizeof(*entries),
GFP_KERNEL); if (!entries) return -ENOMEM;
for (i = 0; i < gpi_count; i++) {
entries[i].client = client;
entries[i].index = i;
scnprintf(name, UCD9000_DEBUGFS_NAME_LEN, "gpi%d_alarm", i + 1);
debugfs_create_file(name, 0444, data->debugfs,
&entries[i],
&ucd9000_debugfs_mfr_status_bit);
}
}
data = devm_kzalloc(&client->dev, sizeof(struct ucd9000_data),
GFP_KERNEL); if (!data) return -ENOMEM;
info = &data->info;
ret = i2c_smbus_read_byte_data(client, UCD9000_NUM_PAGES); if (ret < 0) {
dev_err(&client->dev, "Failed to read number of active pages\n"); return ret;
}
info->pages = ret; if (!info->pages) {
dev_err(&client->dev, "No pages configured\n"); return -ENODEV;
}
/* The internal temperature sensor is always active */
info->func[0] = PMBUS_HAVE_TEMP;
/* Everything else is configurable */
ret = i2c_smbus_read_block_data(client, UCD9000_MONITOR_CONFIG,
block_buffer); if (ret <= 0) {
dev_err(&client->dev, "Failed to read configuration data\n"); return -ENODEV;
} for (i = 0; i < ret; i++) { int page = UCD9000_MON_PAGE(block_buffer[i]);
if (page >= info->pages) continue;
switch (UCD9000_MON_TYPE(block_buffer[i])) { case UCD9000_MON_VOLTAGE: case UCD9000_MON_VOLTAGE_HW:
info->func[page] |= PMBUS_HAVE_VOUT
| PMBUS_HAVE_STATUS_VOUT; break; case UCD9000_MON_TEMPERATURE:
info->func[page] |= PMBUS_HAVE_TEMP2
| PMBUS_HAVE_STATUS_TEMP; break; case UCD9000_MON_CURRENT:
info->func[page] |= PMBUS_HAVE_IOUT
| PMBUS_HAVE_STATUS_IOUT; break; default: break;
}
}
/* Fan configuration */ if (mid->driver_data == ucd90124) { for (i = 0; i < UCD9000_NUM_FAN; i++) {
i2c_smbus_write_byte_data(client,
UCD9000_FAN_CONFIG_INDEX, i);
ret = i2c_smbus_read_block_data(client,
UCD9000_FAN_CONFIG,
data->fan_data[i]); if (ret < 0) return ret;
}
i2c_smbus_write_byte_data(client, UCD9000_FAN_CONFIG_INDEX, 0);
ret = pmbus_do_probe(client, info); if (ret) return ret;
ret = ucd9000_init_debugfs(client, mid, data); if (ret)
dev_warn(&client->dev, "Failed to register debugfs: %d\n",
ret);
return 0;
}
/* This is the driver that will be inserted */ staticstruct i2c_driver ucd9000_driver = {
.driver = {
.name = "ucd9000",
.of_match_table = of_match_ptr(ucd9000_of_match),
},
.probe = ucd9000_probe,
.id_table = ucd9000_id,
};
module_i2c_driver(ucd9000_driver);
MODULE_AUTHOR("Guenter Roeck");
MODULE_DESCRIPTION("PMBus driver for TI UCD90xxx");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS("PMBUS");
Messung V0.5
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.1Bemerkung:
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.