get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/811313/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 811313,
    "url": "http://patchwork.ozlabs.org/api/patches/811313/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/openbmc/patch/20170908043919.6924-3-andrew@aj.id.au/",
    "project": {
        "id": 56,
        "url": "http://patchwork.ozlabs.org/api/projects/56/?format=api",
        "name": "OpenBMC development",
        "link_name": "openbmc",
        "list_id": "openbmc.lists.ozlabs.org",
        "list_email": "openbmc@lists.ozlabs.org",
        "web_url": "http://github.com/openbmc/",
        "scm_url": "",
        "webscm_url": "",
        "list_archive_url": "",
        "list_archive_url_format": "",
        "commit_url_format": ""
    },
    "msgid": "<20170908043919.6924-3-andrew@aj.id.au>",
    "list_archive_url": null,
    "date": "2017-09-08T04:39:18",
    "name": "[v3,2/3] hwmon: pmbus: Add fan control support",
    "commit_ref": null,
    "pull_url": null,
    "state": "not-applicable",
    "archived": true,
    "hash": "66990862b19825ee708da9bc46f977247c1d2c6c",
    "submitter": {
        "id": 68332,
        "url": "http://patchwork.ozlabs.org/api/people/68332/?format=api",
        "name": "Andrew Jeffery",
        "email": "andrew@aj.id.au"
    },
    "delegate": null,
    "mbox": "http://patchwork.ozlabs.org/project/openbmc/patch/20170908043919.6924-3-andrew@aj.id.au/mbox/",
    "series": [
        {
            "id": 2097,
            "url": "http://patchwork.ozlabs.org/api/series/2097/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/openbmc/list/?series=2097",
            "date": "2017-09-08T04:39:16",
            "name": "pmbus: Expand fan support and add MAX31785 driver",
            "version": 3,
            "mbox": "http://patchwork.ozlabs.org/series/2097/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/811313/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/811313/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<openbmc-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org>",
        "X-Original-To": [
            "incoming@patchwork.ozlabs.org",
            "openbmc@lists.ozlabs.org"
        ],
        "Delivered-To": [
            "patchwork-incoming@bilbo.ozlabs.org",
            "openbmc@lists.ozlabs.org"
        ],
        "Received": [
            "from lists.ozlabs.org (lists.ozlabs.org [103.22.144.68])\n\t(using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits))\n\t(No client certificate requested)\n\tby ozlabs.org (Postfix) with ESMTPS id 3xpPlx4Xnvz9s82\n\tfor <incoming@patchwork.ozlabs.org>;\n\tFri,  8 Sep 2017 14:41:17 +1000 (AEST)",
            "from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3])\n\tby lists.ozlabs.org (Postfix) with ESMTP id 3xpPlx2sjyzDrZt\n\tfor <incoming@patchwork.ozlabs.org>;\n\tFri,  8 Sep 2017 14:41:17 +1000 (AEST)",
            "from out1-smtp.messagingengine.com (out1-smtp.messagingengine.com\n\t[66.111.4.25])\n\t(using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits))\n\t(No client certificate requested)\n\tby lists.ozlabs.org (Postfix) with ESMTPS id 3xpPkX1XmNzDrZ8\n\tfor <openbmc@lists.ozlabs.org>; Fri,  8 Sep 2017 14:40:04 +1000 (AEST)",
            "from compute4.internal (compute4.nyi.internal [10.202.2.44])\n\tby mailout.nyi.internal (Postfix) with ESMTP id 10F1820C2C;\n\tFri,  8 Sep 2017 00:40:02 -0400 (EDT)",
            "from frontend1 ([10.202.2.160])\n\tby compute4.internal (MEProxy); Fri, 08 Sep 2017 00:40:02 -0400",
            "from keelia.ozlabs.ibm.com (unknown [122.99.82.10])\n\tby mail.messagingengine.com (Postfix) with ESMTPA id 46C9B7E426;\n\tFri,  8 Sep 2017 00:39:58 -0400 (EDT)"
        ],
        "Authentication-Results": [
            "ozlabs.org;\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n\tunprotected) header.d=aj.id.au header.i=@aj.id.au header.b=\"QwiUxdOX\";\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n\tunprotected) header.d=messagingengine.com\n\theader.i=@messagingengine.com header.b=\"rH3mkp+L\"; \n\tdkim-atps=neutral",
            "lists.ozlabs.org;\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n\tunprotected) header.d=aj.id.au header.i=@aj.id.au header.b=\"QwiUxdOX\";\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n\tunprotected) header.d=messagingengine.com\n\theader.i=@messagingengine.com header.b=\"rH3mkp+L\"; \n\tdkim-atps=neutral",
            "ozlabs.org;\n\tspf=pass (mailfrom) smtp.mailfrom=aj.id.au\n\t(client-ip=66.111.4.25; helo=out1-smtp.messagingengine.com;\n\tenvelope-from=andrew@aj.id.au; receiver=<UNKNOWN>)",
            "lists.ozlabs.org; dkim=pass (2048-bit key;\n\tunprotected) header.d=aj.id.au header.i=@aj.id.au header.b=\"QwiUxdOX\";\n\tdkim=pass (2048-bit key;\n\tunprotected) header.d=messagingengine.com\n\theader.i=@messagingengine.com\n\theader.b=\"rH3mkp+L\"; dkim-atps=neutral"
        ],
        "DKIM-Signature": [
            "v=1; a=rsa-sha256; c=relaxed/relaxed; d=aj.id.au; h=cc\n\t:date:from:in-reply-to:message-id:references:subject:to\n\t:x-me-sender:x-me-sender:x-sasl-enc:x-sasl-enc; s=fm1; bh=C+sJz6\n\t62l2yuGXF4rsR+E0BEGQY3blUm2tfYJiYEq6s=; b=QwiUxdOX2LAdM6LX+O1v1O\n\tKB2Bgn/g5OZso9N+cHFT4KnKdzvVtUw8fOtx9mAy75k82IPkdpHbk8Qs30Ssp1Vo\n\tIwYUEzlUJxEoYYl1FXPO6anphG9TsoLB0qTTjAOc2hOzNhlG36aaycorNhkrF0Jo\n\ttH9b9q4nfbCJZPbW20bahtC3dhOn3FoiABhkm2oFaCUyHGaEjIzl5czgm6T8EYHm\n\tWbWYVVroXuKZn7jBXHeTDw4+fUirvwl1n2vuyqijLA5PooWEO3FyyIlaUCNoVHeE\n\tGZwjU++Sr59oq3oihus+6m9iQqtyRJY/CJO+Kv9AFT8BwRX8TOby84lhEcGQ0q2A\n\t==",
            "v=1; a=rsa-sha256; c=relaxed/relaxed; d=\n\tmessagingengine.com; h=cc:date:from:in-reply-to:message-id\n\t:references:subject:to:x-me-sender:x-me-sender:x-sasl-enc\n\t:x-sasl-enc; s=fm1; bh=C+sJz662l2yuGXF4rsR+E0BEGQY3blUm2tfYJiYEq\n\t6s=; b=rH3mkp+LVnPuP0ThFaKVlA1QkpXcNL4AD1JETX7JwuaaEFva4MMhKxFqo\n\tcCiOikVKWVyueUS84DEaYFwpLVZDC+tIj4swl3DYAghzFc/rYBXk6Qx0NHjM9WMQ\n\tRASdVqC556Y93XHqAMsLKOAge1livD5POk03IJZpPf8uxGeEsizyd+x0b+F8eeZ/\n\tgCqBI32x4tKzk0HzyUApu8Li6bGWI46znYsn/QVV9gU+nk0pwk8ajvNW0TlKyfV/\n\t+TKzyU+L+Q4V5qpqiQYdVEU60uiV3RhV4TBOmVeMqHvcX8Ilko0KF5lQVCIk+IIK\n\tSoX4cthWhK53PQz5t2RIRBuG8ZZ1w=="
        ],
        "X-ME-Sender": "<xms:IR-yWfvS1PxIeOhNZHMA3qkjBXP1YzGO9H8IbdzA-c24hxsD7lyaGw>",
        "X-Sasl-enc": "St1blQtSRU1zrm6gyKrzu5HjDBqihvxUekI4fiyD54Al 1504845601",
        "From": "Andrew Jeffery <andrew@aj.id.au>",
        "To": "linux@roeck-us.net,\n\tlinux-hwmon@vger.kernel.org",
        "Subject": "[PATCH v3 2/3] hwmon: pmbus: Add fan control support",
        "Date": "Fri,  8 Sep 2017 14:39:18 +1000",
        "Message-Id": "<20170908043919.6924-3-andrew@aj.id.au>",
        "X-Mailer": "git-send-email 2.11.0",
        "In-Reply-To": "<20170908043919.6924-1-andrew@aj.id.au>",
        "References": "<20170908043919.6924-1-andrew@aj.id.au>",
        "X-BeenThere": "openbmc@lists.ozlabs.org",
        "X-Mailman-Version": "2.1.23",
        "Precedence": "list",
        "List-Id": "Development list for OpenBMC <openbmc.lists.ozlabs.org>",
        "List-Unsubscribe": "<https://lists.ozlabs.org/options/openbmc>,\n\t<mailto:openbmc-request@lists.ozlabs.org?subject=unsubscribe>",
        "List-Archive": "<http://lists.ozlabs.org/pipermail/openbmc/>",
        "List-Post": "<mailto:openbmc@lists.ozlabs.org>",
        "List-Help": "<mailto:openbmc-request@lists.ozlabs.org?subject=help>",
        "List-Subscribe": "<https://lists.ozlabs.org/listinfo/openbmc>,\n\t<mailto:openbmc-request@lists.ozlabs.org?subject=subscribe>",
        "Cc": "mark.rutland@arm.com, devicetree@vger.kernel.org, jdelvare@suse.com,\n\tAndrew Jeffery <andrew@aj.id.au>, openbmc@lists.ozlabs.org,\n\tlinux-kernel@vger.kernel.org, robh+dt@kernel.org",
        "Errors-To": "openbmc-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org",
        "Sender": "\"openbmc\"\n\t<openbmc-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org>"
    },
    "content": "Expose fanX_target, pwmX and pwmX_enable hwmon sysfs attributes.\n\nFans in a PMBus device are driven by the configuration of two registers:\nFAN_CONFIG_x_y and FAN_COMMAND_x: FAN_CONFIG_x_y dictates how the fan\nand the tacho operate (if installed), while FAN_COMMAND_x sets the\ndesired fan rate. The unit of FAN_COMMAND_x is dependent on the\noperational fan mode, RPM or PWM percent duty, as determined by the\ncorresponding FAN_CONFIG_x_y.\n\nThe mapping of fanX_target, pwmX and pwmX_enable onto FAN_CONFIG_x_y and\nFAN_COMMAND_x is implemented with the addition of virtual registers and\ngeneric implementations in the core:\n\n1. PMBUS_VIRT_FAN_TARGET_x\n2. PMBUS_VIRT_PWM_x\n3. PMBUS_VIRT_PWM_ENABLE_x\n\nThe virtual registers facilitate the necessary side-effects of each\naccess. Examples of the read case, assuming m = 1, b = 0, R = 0:\n\n             Read     |              With              || Gives\n         -------------------------------------------------------\n           Attribute  | FAN_CONFIG_x_y | FAN_COMMAND_y || Value\n         ----------------------------------------------++-------\n          fanX_target | ~PB_FAN_z_RPM  | 0x0001        || 1\n          pwm1        | ~PB_FAN_z_RPM  | 0x0064        || 255\n          pwmX_enable | ~PB_FAN_z_RPM  | 0x0001        || 1\n          fanX_target |  PB_FAN_z_RPM  | 0x0001        || 1\n          pwm1        |  PB_FAN_z_RPM  | 0x0064        || 0\n          pwmX_enable |  PB_FAN_z_RPM  | 0x0001        || 1\n\nAnd the write case:\n\n             Write    | With  ||               Sets\n         -------------+-------++----------------+---------------\n           Attribute  | Value || FAN_CONFIG_x_y | FAN_COMMAND_x\n         -------------+-------++----------------+---------------\n          fanX_target | 1     ||  PB_FAN_z_RPM  | 0x0001\n          pwmX        | 255   || ~PB_FAN_z_RPM  | 0x0064\n          pwmX_enable | 1     || ~PB_FAN_z_RPM  | 0x0064\n\nAlso, the DIRECT mode scaling of some controllers is different between\nRPM and PWM percent duty control modes, so PSC_PWM is introduced to\ncapture the necessary coefficients.\n\nSigned-off-by: Andrew Jeffery <andrew@aj.id.au>\n---\n drivers/hwmon/pmbus/pmbus.h      |  29 +++++\n drivers/hwmon/pmbus/pmbus_core.c | 224 ++++++++++++++++++++++++++++++++++++---\n 2 files changed, 238 insertions(+), 15 deletions(-)",
    "diff": "diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h\nindex bfcb13bae34b..a863b8fed16f 100644\n--- a/drivers/hwmon/pmbus/pmbus.h\n+++ b/drivers/hwmon/pmbus/pmbus.h\n@@ -190,6 +190,28 @@ enum pmbus_regs {\n \tPMBUS_VIRT_VMON_UV_FAULT_LIMIT,\n \tPMBUS_VIRT_VMON_OV_FAULT_LIMIT,\n \tPMBUS_VIRT_STATUS_VMON,\n+\n+\t/*\n+\t * RPM and PWM Fan control\n+\t *\n+\t * Drivers wanting to expose PWM control must define the behaviour of\n+\t * PMBUS_VIRT_PWM_ENABLE_[1-4] in the {read,write}_word_data callback.\n+\t *\n+\t * pmbus core provides default implementations for\n+\t * PMBUS_VIRT_FAN_TARGET_[1-4] and PMBUS_VIRT_PWM_[1-4].\n+\t */\n+\tPMBUS_VIRT_FAN_TARGET_1,\n+\tPMBUS_VIRT_FAN_TARGET_2,\n+\tPMBUS_VIRT_FAN_TARGET_3,\n+\tPMBUS_VIRT_FAN_TARGET_4,\n+\tPMBUS_VIRT_PWM_1,\n+\tPMBUS_VIRT_PWM_2,\n+\tPMBUS_VIRT_PWM_3,\n+\tPMBUS_VIRT_PWM_4,\n+\tPMBUS_VIRT_PWM_ENABLE_1,\n+\tPMBUS_VIRT_PWM_ENABLE_2,\n+\tPMBUS_VIRT_PWM_ENABLE_3,\n+\tPMBUS_VIRT_PWM_ENABLE_4,\n };\n \n /*\n@@ -223,6 +245,8 @@ enum pmbus_regs {\n #define PB_FAN_1_RPM\t\t\tBIT(6)\n #define PB_FAN_1_INSTALLED\t\tBIT(7)\n \n+enum pmbus_fan_mode { percent = 0, rpm };\n+\n /*\n  * STATUS_BYTE, STATUS_WORD (lower)\n  */\n@@ -313,6 +337,7 @@ enum pmbus_sensor_classes {\n \tPSC_POWER,\n \tPSC_TEMPERATURE,\n \tPSC_FAN,\n+\tPSC_PWM,\n \tPSC_NUM_CLASSES\t\t/* Number of power sensor classes */\n };\n \n@@ -339,6 +364,8 @@ enum pmbus_sensor_classes {\n #define PMBUS_HAVE_STATUS_FAN34\tBIT(17)\n #define PMBUS_HAVE_VMON\t\tBIT(18)\n #define PMBUS_HAVE_STATUS_VMON\tBIT(19)\n+#define PMBUS_HAVE_PWM12\tBIT(20)\n+#define PMBUS_HAVE_PWM34\tBIT(21)\n \n enum pmbus_data_format { linear = 0, direct, vid };\n enum vrm_version { vr11 = 0, vr12 };\n@@ -413,6 +440,8 @@ int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg,\n \t\t\t  u8 value);\n int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg,\n \t\t\t   u8 mask, u8 value);\n+int pmbus_update_fan(struct i2c_client *client, int page, int id,\n+\t\t     u8 config, u8 mask, u16 command);\n void pmbus_clear_faults(struct i2c_client *client);\n bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg);\n bool pmbus_check_word_register(struct i2c_client *client, int page, int reg);\ndiff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c\nindex f1eff6b6c798..5ed9cbf1daf9 100644\n--- a/drivers/hwmon/pmbus/pmbus_core.c\n+++ b/drivers/hwmon/pmbus/pmbus_core.c\n@@ -63,6 +63,7 @@ struct pmbus_sensor {\n \tu16 reg;\t\t/* register */\n \tenum pmbus_sensor_classes class;\t/* sensor class */\n \tbool update;\t\t/* runtime sensor update needed */\n+\tbool convert;\t\t/* Whether or not to apply linear/vid/direct */\n \tint data;\t\t/* Sensor data.\n \t\t\t\t   Negative if there was a read error */\n };\n@@ -118,6 +119,27 @@ struct pmbus_data {\n \tu8 currpage;\n };\n \n+static const int pmbus_fan_rpm_mask[] = {\n+\tPB_FAN_1_RPM,\n+\tPB_FAN_2_RPM,\n+\tPB_FAN_1_RPM,\n+\tPB_FAN_2_RPM,\n+};\n+\n+static const int pmbus_fan_config_registers[] = {\n+\tPMBUS_FAN_CONFIG_12,\n+\tPMBUS_FAN_CONFIG_12,\n+\tPMBUS_FAN_CONFIG_34,\n+\tPMBUS_FAN_CONFIG_34\n+};\n+\n+static const int pmbus_fan_command_registers[] = {\n+\tPMBUS_FAN_COMMAND_1,\n+\tPMBUS_FAN_COMMAND_2,\n+\tPMBUS_FAN_COMMAND_3,\n+\tPMBUS_FAN_COMMAND_4,\n+};\n+\n void pmbus_clear_cache(struct i2c_client *client)\n {\n \tstruct pmbus_data *data = i2c_get_clientdata(client);\n@@ -188,6 +210,31 @@ int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg, u16 word)\n }\n EXPORT_SYMBOL_GPL(pmbus_write_word_data);\n \n+int pmbus_update_fan(struct i2c_client *client, int page, int id,\n+\t\t\t       u8 config, u8 mask, u16 command)\n+{\n+\tint from, rv;\n+\tu8 to;\n+\n+\tfrom = pmbus_read_byte_data(client, page,\n+\t\t\t\t    pmbus_fan_config_registers[id]);\n+\tif (from < 0)\n+\t\treturn from;\n+\n+\tto = (from & ~mask) | (config & mask);\n+\n+\tif (to != from) {\n+\t\trv = pmbus_write_byte_data(client, page,\n+\t\t\t\t\t   pmbus_fan_config_registers[id], to);\n+\t\tif (rv < 0)\n+\t\t\treturn rv;\n+\t}\n+\n+\treturn pmbus_write_word_data(client, page,\n+\t\t\t\t     pmbus_fan_command_registers[id], command);\n+}\n+EXPORT_SYMBOL_GPL(pmbus_update_fan);\n+\n /*\n  * _pmbus_write_word_data() is similar to pmbus_write_word_data(), but checks if\n  * a device specific mapping function exists and calls it if necessary.\n@@ -204,8 +251,40 @@ static int _pmbus_write_word_data(struct i2c_client *client, int page, int reg,\n \t\tif (status != -ENODATA)\n \t\t\treturn status;\n \t}\n-\tif (reg >= PMBUS_VIRT_BASE)\n-\t\treturn -ENXIO;\n+\tif (reg >= PMBUS_VIRT_BASE) {\n+\t\tint id, bit;\n+\n+\t\tswitch (reg) {\n+\t\tcase PMBUS_VIRT_FAN_TARGET_1:\n+\t\tcase PMBUS_VIRT_FAN_TARGET_2:\n+\t\tcase PMBUS_VIRT_FAN_TARGET_3:\n+\t\tcase PMBUS_VIRT_FAN_TARGET_4:\n+\t\t\tid = reg - PMBUS_VIRT_FAN_TARGET_1;\n+\t\t\tbit = pmbus_fan_rpm_mask[id];\n+\t\t\tstatus = pmbus_update_fan(client, page, id, bit, bit,\n+\t\t\t\t\t\t  word);\n+\t\t\tbreak;\n+\t\tcase PMBUS_VIRT_PWM_1:\n+\t\tcase PMBUS_VIRT_PWM_2:\n+\t\tcase PMBUS_VIRT_PWM_3:\n+\t\tcase PMBUS_VIRT_PWM_4:\n+\t\t{\n+\t\t\tu32 command = word;\n+\n+\t\t\tid = reg - PMBUS_VIRT_PWM_1;\n+\t\t\tbit = pmbus_fan_rpm_mask[id];\n+\t\t\tcommand *= 100;\n+\t\t\tcommand /= 255;\n+\t\t\tstatus = pmbus_update_fan(client, page, id, 0, bit,\n+\t\t\t\t\t\t  command);\n+\t\t\tbreak;\n+\t\t}\n+\t\tdefault:\n+\t\t\tstatus = -ENXIO;\n+\t\t\tbreak;\n+\t\t}\n+\t\treturn status;\n+\t}\n \treturn pmbus_write_word_data(client, page, reg, word);\n }\n \n@@ -221,6 +300,9 @@ int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 reg)\n }\n EXPORT_SYMBOL_GPL(pmbus_read_word_data);\n \n+static int pmbus_get_fan_command(struct i2c_client *client, int page, int id,\n+\t\t\t\t enum pmbus_fan_mode mode);\n+\n /*\n  * _pmbus_read_word_data() is similar to pmbus_read_word_data(), but checks if\n  * a device specific mapping function exists and calls it if necessary.\n@@ -236,8 +318,42 @@ static int _pmbus_read_word_data(struct i2c_client *client, int page, int reg)\n \t\tif (status != -ENODATA)\n \t\t\treturn status;\n \t}\n-\tif (reg >= PMBUS_VIRT_BASE)\n-\t\treturn -ENXIO;\n+\tif (reg >= PMBUS_VIRT_BASE) {\n+\t\tint id;\n+\n+\t\tswitch (reg) {\n+\t\tcase PMBUS_VIRT_FAN_TARGET_1:\n+\t\tcase PMBUS_VIRT_FAN_TARGET_2:\n+\t\tcase PMBUS_VIRT_FAN_TARGET_3:\n+\t\tcase PMBUS_VIRT_FAN_TARGET_4:\n+\t\t\tid = reg - PMBUS_VIRT_FAN_TARGET_1;\n+\t\t\tstatus = pmbus_get_fan_command(client, page, id, rpm);\n+\t\t\tbreak;\n+\t\tcase PMBUS_VIRT_PWM_1:\n+\t\tcase PMBUS_VIRT_PWM_2:\n+\t\tcase PMBUS_VIRT_PWM_3:\n+\t\tcase PMBUS_VIRT_PWM_4:\n+\t\t{\n+\t\t\tint rv;\n+\n+\t\t\tid = reg - PMBUS_VIRT_PWM_1;\n+\t\t\trv = pmbus_get_fan_command(client, page, id, percent);\n+\t\t\tif (rv < 0)\n+\t\t\t\treturn rv;\n+\n+\t\t\trv *= 255;\n+\t\t\trv /= 100;\n+\n+\t\t\tstatus = rv;\n+\t\t\tbreak;\n+\t\t}\n+\t\tdefault:\n+\t\t\tstatus = -ENXIO;\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\treturn status;\n+\t}\n \treturn pmbus_read_word_data(client, page, reg);\n }\n \n@@ -304,6 +420,28 @@ static int _pmbus_read_byte_data(struct i2c_client *client, int page, int reg)\n \treturn pmbus_read_byte_data(client, page, reg);\n }\n \n+static int pmbus_get_fan_command(struct i2c_client *client, int page, int id,\n+\t\t\t\t enum pmbus_fan_mode mode)\n+{\n+\tint config;\n+\n+\tconfig = _pmbus_read_byte_data(client, page,\n+\t\t\t\t       pmbus_fan_config_registers[id]);\n+\tif (config < 0)\n+\t\treturn config;\n+\n+\t/*\n+\t * We can't meaningfully translate between PWM and RPM, so if the\n+\t * attribute mode (fan[1-*]_target is RPM, pwm[1-*] and pwm[1-*]_enable\n+\t * are PWM) doesn't match the hardware mode, then report 0 instead.\n+\t */\n+\tif ((mode == rpm) != (!!(config & pmbus_fan_rpm_mask[id])))\n+\t\treturn 0;\n+\n+\treturn _pmbus_read_word_data(client, page,\n+\t\t\t\t     pmbus_fan_command_registers[id]);\n+}\n+\n static void pmbus_clear_fault_page(struct i2c_client *client, int page)\n {\n \t_pmbus_write_byte(client, page, PMBUS_CLEAR_FAULTS);\n@@ -489,7 +627,7 @@ static long pmbus_reg2data_direct(struct pmbus_data *data,\n \t/* X = 1/m * (Y * 10^-R - b) */\n \tR = -R;\n \t/* scale result to milli-units for everything but fans */\n-\tif (sensor->class != PSC_FAN) {\n+\tif (!(sensor->class == PSC_FAN || sensor->class == PSC_PWM)) {\n \t\tR += 3;\n \t\tb *= 1000;\n \t}\n@@ -539,6 +677,9 @@ static long pmbus_reg2data(struct pmbus_data *data, struct pmbus_sensor *sensor)\n {\n \tlong val;\n \n+\tif (!sensor->convert)\n+\t\treturn sensor->data;\n+\n \tswitch (data->info->format[sensor->class]) {\n \tcase direct:\n \t\tval = pmbus_reg2data_direct(data, sensor);\n@@ -642,7 +783,7 @@ static u16 pmbus_data2reg_direct(struct pmbus_data *data,\n \t}\n \n \t/* Calculate Y = (m * X + b) * 10^R */\n-\tif (sensor->class != PSC_FAN) {\n+\tif (!(sensor->class == PSC_FAN || sensor->class == PSC_PWM)) {\n \t\tR -= 3;\t\t/* Adjust R and b for data in milli-units */\n \t\tb *= 1000;\n \t}\n@@ -673,6 +814,9 @@ static u16 pmbus_data2reg(struct pmbus_data *data,\n {\n \tu16 regval;\n \n+\tif (!sensor->convert)\n+\t\treturn val;\n+\n \tswitch (data->info->format[sensor->class]) {\n \tcase direct:\n \t\tregval = pmbus_data2reg_direct(data, sensor, val);\n@@ -895,12 +1039,18 @@ static struct pmbus_sensor *pmbus_add_sensor(struct pmbus_data *data,\n \t\treturn NULL;\n \ta = &sensor->attribute;\n \n-\tsnprintf(sensor->name, sizeof(sensor->name), \"%s%d_%s\",\n-\t\t name, seq, type);\n+\tif (type)\n+\t\tsnprintf(sensor->name, sizeof(sensor->name), \"%s%d_%s\",\n+\t\t\t name, seq, type);\n+\telse\n+\t\tsnprintf(sensor->name, sizeof(sensor->name), \"%s%d\",\n+\t\t\t name, seq);\n+\n \tsensor->page = page;\n \tsensor->reg = reg;\n \tsensor->class = class;\n \tsensor->update = update;\n+\tsensor->convert = true;\n \tpmbus_dev_attr_init(a, sensor->name,\n \t\t\t    readonly ? S_IRUGO : S_IRUGO | S_IWUSR,\n \t\t\t    pmbus_show_sensor, pmbus_set_sensor);\n@@ -1558,13 +1708,6 @@ static const int pmbus_fan_registers[] = {\n \tPMBUS_READ_FAN_SPEED_4\n };\n \n-static const int pmbus_fan_config_registers[] = {\n-\tPMBUS_FAN_CONFIG_12,\n-\tPMBUS_FAN_CONFIG_12,\n-\tPMBUS_FAN_CONFIG_34,\n-\tPMBUS_FAN_CONFIG_34\n-};\n-\n static const int pmbus_fan_status_registers[] = {\n \tPMBUS_STATUS_FAN_12,\n \tPMBUS_STATUS_FAN_12,\n@@ -1587,6 +1730,48 @@ static const u32 pmbus_fan_status_flags[] = {\n };\n \n /* Fans */\n+static int pmbus_add_fan_ctrl(struct i2c_client *client,\n+\t\tstruct pmbus_data *data, int index, int page, int id,\n+\t\tu8 config)\n+{\n+\tstruct pmbus_sensor *sensor;\n+\tint rv;\n+\n+\trv = _pmbus_read_word_data(client, page,\n+\t\t\t\t   pmbus_fan_command_registers[id]);\n+\tif (rv < 0)\n+\t\treturn rv;\n+\n+\tsensor = pmbus_add_sensor(data, \"fan\", \"target\", index, page,\n+\t\t\t\t  PMBUS_VIRT_FAN_TARGET_1 + id, PSC_FAN,\n+\t\t\t\t  true, false);\n+\n+\tif (!sensor)\n+\t\treturn -ENOMEM;\n+\n+\tif (!((data->info->func[page] & PMBUS_HAVE_PWM12) ||\n+\t\t\t(data->info->func[page] & PMBUS_HAVE_PWM34)))\n+\t\treturn 0;\n+\n+\tsensor = pmbus_add_sensor(data, \"pwm\", NULL, index, page,\n+\t\t\t\t  PMBUS_VIRT_PWM_1 + id, PSC_PWM,\n+\t\t\t\t  true, false);\n+\n+\tif (!sensor)\n+\t\treturn -ENOMEM;\n+\n+\tsensor = pmbus_add_sensor(data, \"pwm\", \"enable\", index, page,\n+\t\t\t\t  PMBUS_VIRT_PWM_ENABLE_1 + id, PSC_PWM,\n+\t\t\t\t  true, false);\n+\n+\tif (!sensor)\n+\t\treturn -ENOMEM;\n+\n+\tsensor->convert = false;\n+\n+\treturn 0;\n+}\n+\n static int pmbus_add_fan_attributes(struct i2c_client *client,\n \t\t\t\t    struct pmbus_data *data)\n {\n@@ -1624,6 +1809,15 @@ static int pmbus_add_fan_attributes(struct i2c_client *client,\n \t\t\t\t\t     PSC_FAN, true, true) == NULL)\n \t\t\t\treturn -ENOMEM;\n \n+\t\t\t/* Fan control */\n+\t\t\tif (pmbus_check_word_register(client, page,\n+\t\t\t\t\tpmbus_fan_command_registers[f])) {\n+\t\t\t\tret = pmbus_add_fan_ctrl(client, data, index,\n+\t\t\t\t\t\t\t page, f, regval);\n+\t\t\t\tif (ret < 0)\n+\t\t\t\t\treturn ret;\n+\t\t\t}\n+\n \t\t\t/*\n \t\t\t * Each fan status register covers multiple fans,\n \t\t\t * so we have to do some magic.\n",
    "prefixes": [
        "v3",
        "2/3"
    ]
}