Return-Path: Received: from mail-qk0-x243.google.com (mail-qk0-x243.google.com [IPv6:2607:f8b0:400d:c09::243]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 3svTQL54btzDsxZ for ; Thu, 13 Oct 2016 09:42:34 +1100 (AEDT) Authentication-Results: lists.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b=0z/zyq3M; dkim-atps=neutral Received: by mail-qk0-x243.google.com with SMTP id f128so3955075qkb.0 for ; Wed, 12 Oct 2016 15:42:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id; bh=YeQKJ0TI+9MrSEKflD5YUR1p4E9GUGrZkBVK6dv2hUw=; b=0z/zyq3MSLJK387OWzIgS/LlH9ImbMrzVpAmebB5XqlU6ppe+0FWcSmGPQPbttZtGk 2fFlSHHz997Q+IskMILZj9teZfiQDEE36tpLO7OouPXHzI+xDcpqQcqpDm07Z7vUELzz WStzWr/kIiq1BKL+8zMulxqJvdQS+m9awFEeBJazRxvOPWURXB5W1MrM/EmsE6mEEe3E A8pTGJP+yRp7VJ4QFEVMd9KQcgnpLlUOUYIqG8rWNe8EUpvQC+WQ52u5lQ2+jbkyu15P W3ObLserVuDgWP160o1TEgik/UTKgQ+Ee7a/sBhCcU7PKcFbCnvvYDS9wXydmbWMt64f H9IA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=YeQKJ0TI+9MrSEKflD5YUR1p4E9GUGrZkBVK6dv2hUw=; b=HRJ9Vioqn9/4p+ZZywq1lmoCJaOnJmAAIygLjUuOqakY7s1hWSgK57mkz2kE9aEdKG WziyFqfWcKXAAwXIhENUP5M+i9ZhtUqZep2/oaZ6ah7BQiJa5sbI2+0owGCp2DOqU2k0 NzBjJ3fd0m3BEq3avlCCjh5y09hq9eB96P4253NAEk6RJTPrWhgCUTAqcv25Llek7uhh Rxe2OSdngWM2hdK5sE/4PJPZaeP6H7rF7e/nzuZ8U9Jqf20O1o0YMm7opFGMAE3npQUn z/V/nm1We5GtxkZylVXfNLAtXeMdmkmi+WkGRv2G18eWWX5YCCCBSitLtijfuY9aeTQ5 wr1Q== X-Gm-Message-State: AA6/9RmfNRxYs23a0wY9fFrAMSg9CHtdMq3pDBbO2Y5YB3+7Y4TSOise45oQC23euqAqVQ== X-Received: by 10.55.207.209 with SMTP id v78mr3987287qkl.318.1476312152333; Wed, 12 Oct 2016 15:42:32 -0700 (PDT) Received: from eajames-austin-w350.austin.ibm.com ([32.97.110.52]) by smtp.gmail.com with ESMTPSA id a26sm3563045qka.2.2016.10.12.15.42.30 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 12 Oct 2016 15:42:31 -0700 (PDT) From: eajames.ibm@gmail.com To: openbmc@lists.ozlabs.org Cc: "Edward A. James" Subject: [RFC linux v3 1/4] Revert "hwmon: Add Power8 OCC hwmon driver" Date: Wed, 12 Oct 2016 17:42:22 -0500 Message-Id: <1476312142-20064-1-git-send-email-eajames.ibm@gmail.com> X-Mailer: git-send-email 1.9.1 X-BeenThere: openbmc@lists.ozlabs.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Development list for OpenBMC List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 12 Oct 2016 22:42:35 -0000 From: "Edward A. James" This reverts commit 7cddcb99cb42cc4a1d72f2d73a78394bb2dcb0f1. --- .../devicetree/bindings/i2c/i2c-ibm-occ.txt | 13 - drivers/hwmon/Kconfig | 13 - drivers/hwmon/Makefile | 1 - drivers/hwmon/power8_occ_i2c.c | 1254 -------------------- 4 files changed, 1281 deletions(-) delete mode 100644 Documentation/devicetree/bindings/i2c/i2c-ibm-occ.txt delete mode 100644 drivers/hwmon/power8_occ_i2c.c diff --git a/Documentation/devicetree/bindings/i2c/i2c-ibm-occ.txt b/Documentation/devicetree/bindings/i2c/i2c-ibm-occ.txt deleted file mode 100644 index 9aab2df..0000000 --- a/Documentation/devicetree/bindings/i2c/i2c-ibm-occ.txt +++ /dev/null @@ -1,13 +0,0 @@ -HWMON i2c driver for IBM POWER CPU OCC (On Chip Controller) - -Required properties: - - compatible: must be "ibm,power8-occ-i2c" - - reg: physical address - -Example: -i2c3: i2c-bus@100 { - occ@50 { - compatible = "ibm,occ-i2c"; - reg = <0x50>; - }; -}; diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index d4de8d5..367496c 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1226,19 +1226,6 @@ config SENSORS_NSA320 This driver can also be built as a module. If so, the module will be called nsa320-hwmon. -config SENSORS_POWER8_OCC_I2C - tristate "Power8 On Chip Controller i2c driver" - depends on I2C - help - If you say yes here you get support to monitor Power8 CPU - sensors via the On Chip Controller (OCC) over the i2c bus. - - Generally this is used by management controllers such as a BMC - on an OpenPower system. - - This driver can also be built as a module. If so, the module - will be called power8_occ_i2c. - config SENSORS_PCF8591 tristate "Philips PCF8591 ADC/DAC" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 4455478..d7ccb02 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -128,7 +128,6 @@ obj-$(CONFIG_SENSORS_NCT7802) += nct7802.o obj-$(CONFIG_SENSORS_NCT7904) += nct7904.o obj-$(CONFIG_SENSORS_NSA320) += nsa320-hwmon.o obj-$(CONFIG_SENSORS_NTC_THERMISTOR) += ntc_thermistor.o -obj-$(CONFIG_SENSORS_POWER8_OCC_I2C) += power8_occ_i2c.o obj-$(CONFIG_SENSORS_PC87360) += pc87360.o obj-$(CONFIG_SENSORS_PC87427) += pc87427.o obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o diff --git a/drivers/hwmon/power8_occ_i2c.c b/drivers/hwmon/power8_occ_i2c.c deleted file mode 100644 index 6de0e76..0000000 --- a/drivers/hwmon/power8_occ_i2c.c +++ /dev/null @@ -1,1254 +0,0 @@ -/* - * OCC HWMON driver - read IBM Power8 On Chip Controller sensor data via - * i2c. - * - * Copyright 2015 IBM Corp. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define OCC_I2C_ADDR 0x50 -#define OCC_I2C_NAME "occ-i2c" - -#define OCC_DATA_MAX 4096 /* 4KB at most */ -/* i2c read and write occ sensors */ -#define I2C_READ_ERROR 1 -#define I2C_WRITE_ERROR 2 - -/* Defined in POWER8 Processor Registers Specification */ -/* To generate attn to OCC */ -#define ATTN_DATA 0x0006B035 -/* For BMC to read/write SRAM */ -#define OCB_ADDRESS 0x0006B070 -#define OCB_DATA 0x0006B075 -#define OCB_STATUS_CONTROL_AND 0x0006B072 -#define OCB_STATUS_CONTROL_OR 0x0006B073 -/* See definition in: - * https://github.com/open-power/docs/blob/master/occ/OCC_OpenPwr_FW_Interfaces.pdf - */ -#define OCC_COMMAND_ADDR 0xFFFF6000 -#define OCC_RESPONSE_ADDR 0xFFFF7000 - -#define MAX_SENSOR_ATTR_LEN 32 - -enum sensor_t { - freq, - temp, - power, - caps, - MAX_OCC_SENSOR_TYPE -}; - -/* OCC sensor data format */ -struct occ_sensor { - uint16_t sensor_id; - uint16_t value; -}; - -struct power_sensor { - uint16_t sensor_id; - uint32_t update_tag; - uint32_t accumulator; - uint16_t value; -}; - -struct caps_sensor { - uint16_t curr_powercap; - uint16_t curr_powerreading; - uint16_t norm_powercap; - uint16_t max_powercap; - uint16_t min_powercap; - uint16_t user_powerlimit; -}; - -struct sensor_data_block { - uint8_t sensor_type[4]; - uint8_t reserved0; - uint8_t sensor_format; - uint8_t sensor_length; - uint8_t sensor_num; - struct occ_sensor *sensor; - struct power_sensor *power; - struct caps_sensor *caps; -}; - -struct occ_poll_header { - uint8_t status; - uint8_t ext_status; - uint8_t occs_present; - uint8_t config; - uint8_t occ_state; - uint8_t reserved0; - uint8_t reserved1; - uint8_t error_log_id; - uint32_t error_log_addr_start; - uint16_t error_log_length; - uint8_t reserved2; - uint8_t reserved3; - uint8_t occ_code_level[16]; - uint8_t sensor_eye_catcher[6]; - uint8_t sensor_block_num; - uint8_t sensor_data_version; -}; - -struct occ_response { - uint8_t sequence_num; - uint8_t cmd_type; - uint8_t rtn_status; - uint16_t data_length; - struct occ_poll_header header; - struct sensor_data_block *blocks; - uint16_t chk_sum; - int sensor_block_id[MAX_OCC_SENSOR_TYPE]; -}; - -struct sensor_attr_data { - enum sensor_t type; - uint32_t hwmon_index; - uint32_t attr_id; - char name[MAX_SENSOR_ATTR_LEN]; - struct device_attribute dev_attr; -}; - -struct sensor_group { - char *name; - struct sensor_attr_data *sattr; - struct attribute_group group; -}; - -/* data private to each client */ -struct occ_drv_data { - struct i2c_client *client; - struct device *hwmon_dev; - struct mutex update_lock; - bool valid; - unsigned long last_updated; - /* Minimum timer interval for sampling In jiffies */ - unsigned long update_interval; - unsigned long occ_online; - uint16_t user_powercap; - struct occ_response occ_resp; - struct sensor_group sensor_groups[MAX_OCC_SENSOR_TYPE]; -}; - -static void deinit_occ_resp_buf(struct occ_response *p) -{ - int i; - - if (!p) - return; - - if (!p->blocks) - return; - - for (i = 0; i < p->header.sensor_block_num; i++) { - kfree(p->blocks[i].sensor); - kfree(p->blocks[i].power); - kfree(p->blocks[i].caps); - } - - kfree(p->blocks); - - memset(p, 0, sizeof(*p)); - - for (i = 0; i < ARRAY_SIZE(p->sensor_block_id); i++) - p->sensor_block_id[i] = -1; -} - -static ssize_t occ_i2c_read(struct i2c_client *client, void *buf, size_t count) -{ - WARN_ON(count > OCC_DATA_MAX); - - dev_dbg(&client->dev, "i2c_read: reading %zu bytes @0x%x.\n", - count, client->addr); - return i2c_master_recv(client, buf, count); -} - -static ssize_t occ_i2c_write(struct i2c_client *client, const void *buf, - size_t count) -{ - WARN_ON(count > OCC_DATA_MAX); - - dev_dbg(&client->dev, "i2c_write: writing %zu bytes @0x%x.\n", - count, client->addr); - return i2c_master_send(client, buf, count); -} - -/* read 8-byte value and put into data[offset] */ -static int occ_getscomb(struct i2c_client *client, uint32_t address, - uint8_t *data, int offset) -{ - uint32_t ret; - char buf[8]; - int i; - - /* P8 i2c slave requires address to be shifted by 1 */ - address = address << 1; - - ret = occ_i2c_write(client, &address, - sizeof(address)); - - if (ret != sizeof(address)) - return -I2C_WRITE_ERROR; - - ret = occ_i2c_read(client, buf, sizeof(buf)); - if (ret != sizeof(buf)) - return -I2C_READ_ERROR; - - for (i = 0; i < 8; i++) - data[offset + i] = buf[7 - i]; - - return 0; -} - -static int occ_putscom(struct i2c_client *client, uint32_t address, - uint32_t data0, uint32_t data1) -{ - uint32_t buf[3]; - uint32_t ret; - - /* P8 i2c slave requires address to be shifted by 1 */ - address = address << 1; - - buf[0] = address; - buf[1] = data1; - buf[2] = data0; - - ret = occ_i2c_write(client, buf, sizeof(buf)); - if (ret != sizeof(buf)) - return I2C_WRITE_ERROR; - - return 0; -} - -static void *occ_get_sensor_by_type(struct occ_response *resp, enum sensor_t t) -{ - void *sensor; - - if (!resp->blocks) - return NULL; - - if (resp->sensor_block_id[t] == -1) - return NULL; - - switch (t) { - case temp: - case freq: - sensor = resp->blocks[resp->sensor_block_id[t]].sensor; - break; - case power: - sensor = resp->blocks[resp->sensor_block_id[t]].power; - break; - case caps: - sensor = resp->blocks[resp->sensor_block_id[t]].caps; - break; - default: - sensor = NULL; - } - - return sensor; -} - -static int occ_renew_sensor(struct occ_response *resp, uint8_t sensor_length, - uint8_t sensor_num, enum sensor_t t, int block) -{ - void *sensor; - int ret; - - sensor = occ_get_sensor_by_type(resp, t); - - /* empty sensor block, release older sensor data */ - if (sensor_num == 0 || sensor_length == 0) { - kfree(sensor); - return -1; - } - - if (!sensor || sensor_num != - resp->blocks[resp->sensor_block_id[t]].sensor_num) { - kfree(sensor); - switch (t) { - case temp: - case freq: - resp->blocks[block].sensor = - kcalloc(sensor_num, - sizeof(struct occ_sensor), GFP_KERNEL); - if (!resp->blocks[block].sensor) { - ret = -ENOMEM; - goto err; - } - break; - case power: - resp->blocks[block].power = - kcalloc(sensor_num, - sizeof(struct power_sensor), - GFP_KERNEL); - if (!resp->blocks[block].power) { - ret = -ENOMEM; - goto err; - } - break; - case caps: - resp->blocks[block].caps = - kcalloc(sensor_num, - sizeof(struct caps_sensor), GFP_KERNEL); - if (!resp->blocks[block].caps) { - ret = -ENOMEM; - goto err; - } - break; - default: - ret = -ENOMEM; - goto err; - } - } - - return 0; -err: - deinit_occ_resp_buf(resp); - return ret; -} - -#define RESP_DATA_LENGTH 3 -#define RESP_HEADER_OFFSET 5 -#define SENSOR_STR_OFFSET 37 -#define SENSOR_BLOCK_NUM_OFFSET 43 -#define SENSOR_BLOCK_OFFSET 45 - -static inline uint16_t get_occdata_length(uint8_t *data) -{ - return be16_to_cpup((const __be16 *)&data[RESP_DATA_LENGTH]); -} - -static int parse_occ_response(struct i2c_client *client, - uint8_t *data, struct occ_response *resp) -{ - int b; - int s; - int ret; - int dnum = SENSOR_BLOCK_OFFSET; - struct occ_sensor *f_sensor; - struct occ_sensor *t_sensor; - struct power_sensor *p_sensor; - struct caps_sensor *c_sensor; - uint8_t sensor_block_num; - uint8_t sensor_type[4]; - uint8_t sensor_format; - uint8_t sensor_length; - uint8_t sensor_num; - - /* check if the data is valid */ - if (strncmp(&data[SENSOR_STR_OFFSET], "SENSOR", 6) != 0) { - dev_dbg(&client->dev, - "ERROR: no SENSOR String in response\n"); - ret = -1; - goto err; - } - - sensor_block_num = data[SENSOR_BLOCK_NUM_OFFSET]; - if (sensor_block_num == 0) { - dev_dbg(&client->dev, "ERROR: SENSOR block num is 0\n"); - ret = -1; - goto err; - } - - /* if sensor block has changed, re-malloc */ - if (sensor_block_num != resp->header.sensor_block_num) { - deinit_occ_resp_buf(resp); - resp->blocks = kcalloc(sensor_block_num, - sizeof(struct sensor_data_block), GFP_KERNEL); - if (!resp->blocks) - return -ENOMEM; - } - - memcpy(&resp->header, &data[RESP_HEADER_OFFSET], sizeof(resp->header)); - resp->header.error_log_addr_start = - be32_to_cpu(resp->header.error_log_addr_start); - resp->header.error_log_length = - be16_to_cpu(resp->header.error_log_length); - - dev_dbg(&client->dev, "Reading %d sensor blocks\n", - resp->header.sensor_block_num); - for (b = 0; b < sensor_block_num; b++) { - /* 8-byte sensor block head */ - strncpy(sensor_type, &data[dnum], 4); - sensor_format = data[dnum+5]; - sensor_length = data[dnum+6]; - sensor_num = data[dnum+7]; - dnum = dnum + 8; - - dev_dbg(&client->dev, - "sensor block[%d]: type: %s, sensor_num: %d\n", - b, sensor_type, sensor_num); - - if (strncmp(sensor_type, "FREQ", 4) == 0) { - ret = occ_renew_sensor(resp, sensor_length, - sensor_num, freq, b); - if (ret) - continue; - - resp->sensor_block_id[freq] = b; - for (s = 0; s < sensor_num; s++) { - f_sensor = &resp->blocks[b].sensor[s]; - f_sensor->sensor_id = - be16_to_cpup((const __be16 *) - &data[dnum]); - f_sensor->value = be16_to_cpup((const __be16 *) - &data[dnum+2]); - dev_dbg(&client->dev, - "sensor[%d]-[%d]: id: %u, value: %u\n", - b, s, f_sensor->sensor_id, - f_sensor->value); - dnum = dnum + sensor_length; - } - } else if (strncmp(sensor_type, "TEMP", 4) == 0) { - ret = occ_renew_sensor(resp, sensor_length, - sensor_num, temp, b); - if (ret) - continue; - - resp->sensor_block_id[temp] = b; - for (s = 0; s < sensor_num; s++) { - t_sensor = &resp->blocks[b].sensor[s]; - t_sensor->sensor_id = - be16_to_cpup((const __be16 *) - &data[dnum]); - t_sensor->value = be16_to_cpup((const __be16 *) - &data[dnum+2]); - dev_dbg(&client->dev, - "sensor[%d]-[%d]: id: %u, value: %u\n", - b, s, t_sensor->sensor_id, - t_sensor->value); - dnum = dnum + sensor_length; - } - } else if (strncmp(sensor_type, "POWR", 4) == 0) { - ret = occ_renew_sensor(resp, sensor_length, - sensor_num, power, b); - if (ret) - continue; - - resp->sensor_block_id[power] = b; - for (s = 0; s < sensor_num; s++) { - p_sensor = &resp->blocks[b].power[s]; - p_sensor->sensor_id = - be16_to_cpup((const __be16 *) - &data[dnum]); - p_sensor->update_tag = - be32_to_cpup((const __be32 *) - &data[dnum+2]); - p_sensor->accumulator = - be32_to_cpup((const __be32 *) - &data[dnum+6]); - p_sensor->value = be16_to_cpup((const __be16 *) - &data[dnum+10]); - - dev_dbg(&client->dev, - "sensor[%d]-[%d]: id: %u, value: %u\n", - b, s, p_sensor->sensor_id, - p_sensor->value); - - dnum = dnum + sensor_length; - } - } else if (strncmp(sensor_type, "CAPS", 4) == 0) { - ret = occ_renew_sensor(resp, sensor_length, - sensor_num, caps, b); - if (ret) - continue; - - resp->sensor_block_id[caps] = b; - for (s = 0; s < sensor_num; s++) { - c_sensor = &resp->blocks[b].caps[s]; - c_sensor->curr_powercap = - be16_to_cpup((const __be16 *) - &data[dnum]); - c_sensor->curr_powerreading = - be16_to_cpup((const __be16 *) - &data[dnum+2]); - c_sensor->norm_powercap = - be16_to_cpup((const __be16 *) - &data[dnum+4]); - c_sensor->max_powercap = - be16_to_cpup((const __be16 *) - &data[dnum+6]); - c_sensor->min_powercap = - be16_to_cpup((const __be16 *) - &data[dnum+8]); - c_sensor->user_powerlimit = - be16_to_cpup((const __be16 *) - &data[dnum+10]); - - dnum = dnum + sensor_length; - dev_dbg(&client->dev, "CAPS sensor #%d:\n", s); - dev_dbg(&client->dev, "curr_powercap is %x\n", - c_sensor->curr_powercap); - dev_dbg(&client->dev, - "curr_powerreading is %x\n", - c_sensor->curr_powerreading); - dev_dbg(&client->dev, "norm_powercap is %x\n", - c_sensor->norm_powercap); - dev_dbg(&client->dev, "max_powercap is %x\n", - c_sensor->max_powercap); - dev_dbg(&client->dev, "min_powercap is %x\n", - c_sensor->min_powercap); - dev_dbg(&client->dev, "user_powerlimit is %x\n", - c_sensor->user_powerlimit); - } - - } else { - dev_dbg(&client->dev, - "ERROR: sensor type %s not supported\n", - resp->blocks[b].sensor_type); - ret = -1; - goto err; - } - - strncpy(resp->blocks[b].sensor_type, sensor_type, 4); - resp->blocks[b].sensor_format = sensor_format; - resp->blocks[b].sensor_length = sensor_length; - resp->blocks[b].sensor_num = sensor_num; - } - - return 0; -err: - deinit_occ_resp_buf(resp); - return ret; -} - - -/* Refer to OCC interface document for OCC command format - * https://github.com/open-power/docs/blob/master/occ/OCC_OpenPwr_FW_Interfaces.pdf - */ -static uint8_t occ_send_cmd(struct i2c_client *client, uint8_t seq, - uint8_t type, uint16_t length, uint8_t *data, uint8_t *resp) -{ - uint32_t cmd1, cmd2; - uint16_t checksum; - int i; - - length = cpu_to_le16(length); - cmd1 = (seq << 24) | (type << 16) | length; - memcpy(&cmd2, data, length); - cmd2 <<= ((4 - length) * 8); - - /* checksum: sum of every bytes of cmd1, cmd2 */ - checksum = 0; - for (i = 0; i < 4; i++) - checksum += (cmd1 >> (i * 8)) & 0xFF; - for (i = 0; i < 4; i++) - checksum += (cmd2 >> (i * 8)) & 0xFF; - cmd2 |= checksum << ((2 - length) * 8); - - /* Init OCB */ - occ_putscom(client, OCB_STATUS_CONTROL_OR, 0x08000000, 0x00000000); - occ_putscom(client, OCB_STATUS_CONTROL_AND, 0xFBFFFFFF, 0xFFFFFFFF); - - /* Send command */ - occ_putscom(client, OCB_ADDRESS, OCC_COMMAND_ADDR, 0x00000000); - occ_putscom(client, OCB_ADDRESS, OCC_COMMAND_ADDR, 0x00000000); - occ_putscom(client, OCB_DATA, cmd1, cmd2); - - /* Trigger attention */ - occ_putscom(client, ATTN_DATA, 0x01010000, 0x00000000); - - /* Get response data */ - occ_putscom(client, OCB_ADDRESS, OCC_RESPONSE_ADDR, 0x00000000); - occ_getscomb(client, OCB_DATA, resp, 0); - - /* return status */ - return resp[2]; -} - -static int occ_get_all(struct i2c_client *client, struct occ_response *occ_resp) -{ - uint8_t *occ_data; - uint16_t num_bytes; - int i; - int ret; - uint8_t poll_cmd_data; - - poll_cmd_data = 0x10; - - /* - * TODO: fetch header, and then allocate the rest of the buffer based - * on the header size. Assuming the OCC has a fixed sized header - */ - occ_data = devm_kzalloc(&client->dev, OCC_DATA_MAX, GFP_KERNEL); - - ret = occ_send_cmd(client, 0, 0, 1, &poll_cmd_data, occ_data); - if (ret) { - dev_err(&client->dev, "ERROR: OCC Poll: 0x%x\n", ret); - ret = -EINVAL; - goto out; - } - - num_bytes = get_occdata_length(occ_data); - - dev_dbg(&client->dev, "OCC data length: %d\n", num_bytes); - - if (num_bytes > OCC_DATA_MAX) { - dev_dbg(&client->dev, "ERROR: OCC data length must be < 4KB\n"); - ret = -EINVAL; - goto out; - } - - if (num_bytes <= 0) { - dev_dbg(&client->dev, "ERROR: OCC data length is zero\n"); - ret = -EINVAL; - goto out; - } - - /* read remaining data */ - for (i = 8; i < num_bytes + 8; i = i + 8) - occ_getscomb(client, OCB_DATA, occ_data, i); - - ret = parse_occ_response(client, occ_data, occ_resp); - -out: - devm_kfree(&client->dev, occ_data); - return ret; -} - - -static int occ_update_device(struct device *dev) -{ - struct occ_drv_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - int ret = 0; - - mutex_lock(&data->update_lock); - - if (time_after(jiffies, data->last_updated + data->update_interval) - || !data->valid) { - data->valid = 1; - ret = occ_get_all(client, &data->occ_resp); - if (ret) - data->valid = 0; - data->last_updated = jiffies; - } - mutex_unlock(&data->update_lock); - - return ret; -} - - -static void *occ_get_sensor(struct device *hwmon_dev, enum sensor_t t) -{ - struct device *dev = hwmon_dev->parent; - struct occ_drv_data *data = dev_get_drvdata(dev); - int ret; - - ret = occ_update_device(dev); - if (ret != 0) { - dev_dbg(dev, "ERROR: cannot get occ sensor data: %d\n", ret); - return NULL; - } - - return occ_get_sensor_by_type(&data->occ_resp, t); -} - -static int occ_get_sensor_value(struct device *hwmon_dev, enum sensor_t t, - int index) -{ - void *sensor; - - if (t == caps) - return -1; - - sensor = occ_get_sensor(hwmon_dev, t); - - if (!sensor) - return -1; - - if (t == power) - return ((struct power_sensor *)sensor)[index].value; - - return ((struct occ_sensor *)sensor)[index].value; -} - -static int occ_get_sensor_id(struct device *hwmon_dev, enum sensor_t t, - int index) -{ - void *sensor; - - if (t == caps) - return -1; - - sensor = occ_get_sensor(hwmon_dev, t); - - if (!sensor) - return -1; - - if (t == power) - return ((struct power_sensor *)sensor)[index].sensor_id; - - return ((struct occ_sensor *)sensor)[index].sensor_id; -} - -/* sysfs attributes for occ hwmon device */ - -static ssize_t show_input(struct device *hwmon_dev, - struct device_attribute *da, char *buf) -{ - struct sensor_attr_data *sdata = container_of(da, - struct sensor_attr_data, dev_attr); - int val; - - val = occ_get_sensor_value(hwmon_dev, sdata->type, - sdata->hwmon_index - 1); - if (sdata->type == temp) - /* in millidegree Celsius */ - val *= 1000; - - return snprintf(buf, PAGE_SIZE - 1, "%d\n", val); -} - -static ssize_t show_label(struct device *hwmon_dev, - struct device_attribute *da, char *buf) -{ - struct sensor_attr_data *sdata = container_of(da, - struct sensor_attr_data, dev_attr); - int val; - - val = occ_get_sensor_id(hwmon_dev, sdata->type, - sdata->hwmon_index - 1); - - return snprintf(buf, PAGE_SIZE - 1, "%d\n", val); -} - -static ssize_t show_caps(struct device *hwmon_dev, - struct device_attribute *da, char *buf) -{ - struct sensor_attr_data *sdata = container_of(da, - struct sensor_attr_data, dev_attr); - int nr = sdata->attr_id; - int n = sdata->hwmon_index - 1; - struct caps_sensor *sensor; - int val; - - sensor = occ_get_sensor(hwmon_dev, caps); - if (!sensor) { - val = -1; - return snprintf(buf, PAGE_SIZE - 1, "%d\n", val); - } - - switch (nr) { - case 0: - val = sensor[n].curr_powercap; - break; - case 1: - val = sensor[n].curr_powerreading; - break; - case 2: - val = sensor[n].norm_powercap; - break; - case 3: - val = sensor[n].max_powercap; - break; - case 4: - val = sensor[n].min_powercap; - break; - case 5: - val = sensor[n].user_powerlimit; - break; - default: - val = -1; - } - - return snprintf(buf, PAGE_SIZE - 1, "%d\n", val); -} - -static ssize_t show_update_interval(struct device *hwmon_dev, - struct device_attribute *attr, char *buf) -{ - struct device *dev = hwmon_dev->parent; - struct occ_drv_data *data = dev_get_drvdata(dev); - - return snprintf(buf, PAGE_SIZE - 1, "%u\n", - jiffies_to_msecs(data->update_interval)); -} - -static ssize_t set_update_interval(struct device *hwmon_dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct device *dev = hwmon_dev->parent; - struct occ_drv_data *data = dev_get_drvdata(dev); - unsigned long val; - int err; - - err = kstrtoul(buf, 10, &val); - if (err) - return err; - - data->update_interval = msecs_to_jiffies(val); - return count; -} -static DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO, - show_update_interval, set_update_interval); - -static ssize_t show_name(struct device *hwmon_dev, - struct device_attribute *attr, char *buf) -{ - return snprintf(buf, PAGE_SIZE - 1, "%s\n", OCC_I2C_NAME); -} -static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); - -static ssize_t show_user_powercap(struct device *hwmon_dev, - struct device_attribute *attr, char *buf) -{ - struct device *dev = hwmon_dev->parent; - struct occ_drv_data *data = dev_get_drvdata(dev); - - return snprintf(buf, PAGE_SIZE - 1, "%u\n", data->user_powercap); -} - - -static ssize_t set_user_powercap(struct device *hwmon_dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct device *dev = hwmon_dev->parent; - struct occ_drv_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - uint16_t val; - uint8_t resp[8]; - int err; - - err = kstrtou16(buf, 10, &val); - if (err) - return err; - - dev_dbg(dev, "set user powercap to: %u\n", val); - val = cpu_to_le16(val); - err = occ_send_cmd(client, 0, 0x22, 2, (uint8_t *)&val, resp); - if (err != 0) { - dev_dbg(dev, - "ERROR: Set User Powercap: wrong return status: %x\n", - err); - if (err == 0x13) - dev_info(dev, - "ERROR: set invalid powercap value: %x\n", val); - return -EINVAL; - } - data->user_powercap = val; - return count; -} -static DEVICE_ATTR(user_powercap, S_IWUSR | S_IRUGO, - show_user_powercap, set_user_powercap); - -static void deinit_sensor_groups(struct device *hwmon_dev, - struct sensor_group *sensor_groups) -{ - int cnt; - - for (cnt = 0; cnt < MAX_OCC_SENSOR_TYPE; cnt++) { - if (sensor_groups[cnt].group.attrs) - devm_kfree(hwmon_dev, sensor_groups[cnt].group.attrs); - if (sensor_groups[cnt].sattr) - devm_kfree(hwmon_dev, sensor_groups[cnt].sattr); - sensor_groups[cnt].group.attrs = NULL; - sensor_groups[cnt].sattr = NULL; - } -} - -static void occ_remove_hwmon_attrs(struct device *hwmon_dev) -{ - struct occ_drv_data *data = dev_get_drvdata(hwmon_dev->parent); - struct sensor_group *sensor_groups = data->sensor_groups; - int i; - - if (!hwmon_dev) - return; - - device_remove_file(hwmon_dev, &dev_attr_update_interval); - device_remove_file(hwmon_dev, &dev_attr_name); - device_remove_file(hwmon_dev, &dev_attr_user_powercap); - - for (i = 0; i < MAX_OCC_SENSOR_TYPE; i++) - sysfs_remove_group(&hwmon_dev->kobj, &sensor_groups[i].group); - - deinit_sensor_groups(hwmon_dev, sensor_groups); -} - -static void sensor_attr_init(struct sensor_attr_data *sdata, - char *sensor_group_name, - char *attr_name, - ssize_t (*show)(struct device *dev, - struct device_attribute *attr, - char *buf)) -{ - sysfs_attr_init(&sdata->dev_attr.attr); - - snprintf(sdata->name, MAX_SENSOR_ATTR_LEN, "%s%d_%s", - sensor_group_name, sdata->hwmon_index, attr_name); - sdata->dev_attr.attr.name = sdata->name; - sdata->dev_attr.attr.mode = S_IRUGO; - sdata->dev_attr.show = show; -} - -/* create hwmon sensor sysfs attributes */ -static int create_sensor_group(struct device *hwmon_dev, enum sensor_t type, - int sensor_num) -{ - struct occ_drv_data *data = dev_get_drvdata(hwmon_dev->parent); - struct sensor_group *sensor_groups = data->sensor_groups; - struct sensor_attr_data *sdata; - int ret; - int cnt; - - /* each sensor has 'label' and 'input' attributes */ - sensor_groups[type].group.attrs = devm_kzalloc(hwmon_dev, - sizeof(struct attribute *) * - sensor_num * 2 + 1, GFP_KERNEL); - if (!sensor_groups[type].group.attrs) { - ret = -ENOMEM; - goto err; - } - - sensor_groups[type].sattr = devm_kzalloc(hwmon_dev, - sizeof(struct sensor_attr_data) * - sensor_num * 2, GFP_KERNEL); - if (!sensor_groups[type].sattr) { - ret = -ENOMEM; - goto err; - } - - for (cnt = 0; cnt < sensor_num; cnt++) { - sdata = &sensor_groups[type].sattr[cnt]; - /* hwomon attributes index starts from 1 */ - sdata->hwmon_index = cnt + 1; - sdata->type = type; - sensor_attr_init(sdata, sensor_groups[type].name, "input", - show_input); - sensor_groups[type].group.attrs[cnt] = &sdata->dev_attr.attr; - - sdata = &sensor_groups[type].sattr[cnt + sensor_num]; - sdata->hwmon_index = cnt + 1; - sdata->type = type; - sensor_attr_init(sdata, sensor_groups[type].name, "label", - show_label); - sensor_groups[type].group.attrs[cnt + sensor_num] = - &sdata->dev_attr.attr; - } - - ret = sysfs_create_group(&hwmon_dev->kobj, &sensor_groups[type].group); - if (ret) - goto err; - - return ret; -err: - deinit_sensor_groups(hwmon_dev, sensor_groups); - return ret; -} - -static void caps_sensor_attr_init(struct sensor_attr_data *sdata, - char *attr_name, uint32_t hwmon_index, - uint32_t attr_id) -{ - sdata->type = caps; - sdata->hwmon_index = hwmon_index; - sdata->attr_id = attr_id; - - /* FIXME, to be compatible with user space app, we do not - * generate caps1_* attributes. - */ - if (sdata->hwmon_index == 1) - snprintf(sdata->name, MAX_SENSOR_ATTR_LEN, "%s_%s", - "caps", attr_name); - else - snprintf(sdata->name, MAX_SENSOR_ATTR_LEN, "%s%d_%s", - "caps", sdata->hwmon_index, attr_name); - - sysfs_attr_init(&sdata->dev_attr.attr); - sdata->dev_attr.attr.name = sdata->name; - sdata->dev_attr.attr.mode = S_IRUGO; - sdata->dev_attr.show = show_caps; -} - -static char *caps_sensor_name[] = { - "curr_powercap", - "curr_powerreading", - "norm_powercap", - "max_powercap", - "min_powercap", - "user_powerlimit", -}; - -static int create_caps_sensor_group(struct device *hwmon_dev, int sensor_num) -{ - struct occ_drv_data *data = dev_get_drvdata(hwmon_dev->parent); - struct sensor_group *sensor_groups = data->sensor_groups; - int field_num = ARRAY_SIZE(caps_sensor_name); - struct sensor_attr_data *sdata; - int ret; - int cnt; - int i; - - sensor_groups[caps].group.attrs = devm_kzalloc(hwmon_dev, - sizeof(struct attribute *) * - sensor_num * field_num + 1, - GFP_KERNEL); - if (!sensor_groups[caps].group.attrs) { - ret = -ENOMEM; - goto err; - } - - sensor_groups[caps].sattr = devm_kzalloc(hwmon_dev, - sizeof(struct sensor_attr_data) * - sensor_num * field_num, - GFP_KERNEL); - if (!sensor_groups[caps].sattr) { - ret = -ENOMEM; - goto err; - } - - for (cnt = 0; cnt < sensor_num; cnt++) { - for (i = 0; i < field_num; i++) { - sdata = &sensor_groups[caps].sattr[cnt * field_num + i]; - caps_sensor_attr_init(sdata, caps_sensor_name[i], - cnt + 1, i); - sensor_groups[caps].group.attrs[cnt * field_num + i] = - &sdata->dev_attr.attr; - } - } - - ret = sysfs_create_group(&hwmon_dev->kobj, &sensor_groups[caps].group); - if (ret) - goto err; - - return ret; -err: - deinit_sensor_groups(hwmon_dev, sensor_groups); - return ret; -} - -static int occ_create_hwmon_attrs(struct device *dev) -{ - struct occ_drv_data *drv_data = dev_get_drvdata(dev); - struct device *hwmon_dev = drv_data->hwmon_dev; - struct sensor_group *sensor_groups = drv_data->sensor_groups; - int i; - int sensor_num; - int ret; - struct occ_response *rsp; - enum sensor_t t; - - rsp = &drv_data->occ_resp; - - for (i = 0; i < ARRAY_SIZE(rsp->sensor_block_id); i++) - rsp->sensor_block_id[i] = -1; - - /* read sensor data from occ. */ - ret = occ_update_device(dev); - if (ret != 0) { - dev_dbg(dev, "ERROR: cannot get occ sensor data: %d\n", ret); - return ret; - } - if (!rsp->blocks) - return -1; - - ret = device_create_file(hwmon_dev, &dev_attr_name); - if (ret) - goto error; - - ret = device_create_file(hwmon_dev, &dev_attr_update_interval); - if (ret) - goto error; - - if (rsp->sensor_block_id[caps] >= 0) { - /* user powercap: only for master OCC */ - ret = device_create_file(hwmon_dev, &dev_attr_user_powercap); - if (ret) - goto error; - } - - sensor_groups[freq].name = "freq"; - sensor_groups[temp].name = "temp"; - sensor_groups[power].name = "power"; - sensor_groups[caps].name = "caps"; - - for (t = 0; t < MAX_OCC_SENSOR_TYPE; t++) { - if (rsp->sensor_block_id[t] < 0) - continue; - - sensor_num = - rsp->blocks[rsp->sensor_block_id[t]].sensor_num; - if (t == caps) - ret = create_caps_sensor_group(hwmon_dev, sensor_num); - else - ret = create_sensor_group(hwmon_dev, t, sensor_num); - if (ret) - goto error; - } - - return 0; -error: - dev_err(dev, "ERROR: cannot create hwmon attributes\n"); - occ_remove_hwmon_attrs(drv_data->hwmon_dev); - return ret; -} - -static ssize_t show_occ_online(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct occ_drv_data *data = dev_get_drvdata(dev); - - return snprintf(buf, PAGE_SIZE - 1, "%lu\n", data->occ_online); -} - -static ssize_t set_occ_online(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct occ_drv_data *data = dev_get_drvdata(dev); - unsigned long val; - int err; - - err = kstrtoul(buf, 10, &val); - if (err) - return err; - - if (val == 1) { - if (data->occ_online == 1) - return count; - - /* populate hwmon sysfs attr using sensor data */ - dev_dbg(dev, "occ register hwmon @0x%x\n", data->client->addr); - - data->hwmon_dev = hwmon_device_register(dev); - if (IS_ERR(data->hwmon_dev)) - return PTR_ERR(data->hwmon_dev); - - err = occ_create_hwmon_attrs(dev); - if (err) { - hwmon_device_unregister(data->hwmon_dev); - return err; - } - data->hwmon_dev->parent = dev; - } else if (val == 0) { - if (data->occ_online == 0) - return count; - - occ_remove_hwmon_attrs(data->hwmon_dev); - hwmon_device_unregister(data->hwmon_dev); - data->hwmon_dev = NULL; - } else - return -EINVAL; - - data->occ_online = val; - return count; -} - -static DEVICE_ATTR(online, S_IWUSR | S_IRUGO, - show_occ_online, set_occ_online); - -static int occ_create_i2c_sysfs_attr(struct device *dev) -{ - /* create an i2c sysfs attribute, to indicate whether OCC is active */ - return device_create_file(dev, &dev_attr_online); -} - - -/* device probe and removal */ - -enum occ_type { - occ_id, -}; - -static int occ_probe(struct i2c_client *client, const struct i2c_device_id *id) -{ - struct device *dev = &client->dev; - struct occ_drv_data *data; - - data = devm_kzalloc(dev, sizeof(struct occ_drv_data), GFP_KERNEL); - if (!data) - return -ENOMEM; - - data->client = client; - i2c_set_clientdata(client, data); - mutex_init(&data->update_lock); - data->update_interval = HZ; - - occ_create_i2c_sysfs_attr(dev); - - dev_info(dev, "occ i2c driver ready: i2c addr@0x%x\n", client->addr); - - return 0; -} - -static int occ_remove(struct i2c_client *client) -{ - struct occ_drv_data *data = i2c_get_clientdata(client); - - /* free allocated sensor memory */ - deinit_occ_resp_buf(&data->occ_resp); - - device_remove_file(&client->dev, &dev_attr_online); - - if (!data->hwmon_dev) - return 0; - - occ_remove_hwmon_attrs(data->hwmon_dev); - hwmon_device_unregister(data->hwmon_dev); - return 0; -} - -/* used by old-style board info. */ -static const struct i2c_device_id occ_ids[] = { - { OCC_I2C_NAME, occ_id, }, - { /* LIST END */ } -}; -MODULE_DEVICE_TABLE(i2c, occ_ids); - -/* use by device table */ -static const struct of_device_id i2c_occ_of_match[] = { - {.compatible = "ibm,occ-i2c"}, - {}, -}; -MODULE_DEVICE_TABLE(of, i2c_occ_of_match); - -/* i2c-core uses i2c-detect() to detect device in bellow address list. - * If exists, address will be assigned to client. - * It is also possible to read address from device table. - */ -static const unsigned short normal_i2c[] = {0x50, 0x51, I2C_CLIENT_END }; - -static struct i2c_driver occ_driver = { - .class = I2C_CLASS_HWMON, - .driver = { - .name = OCC_I2C_NAME, - .pm = NULL, - .of_match_table = i2c_occ_of_match, - }, - .probe = occ_probe, - .remove = occ_remove, - .id_table = occ_ids, - .address_list = normal_i2c, -}; - -module_i2c_driver(occ_driver); - -MODULE_AUTHOR("Li Yi "); -MODULE_DESCRIPTION("BMC OCC hwmon driver"); -MODULE_LICENSE("GPL"); -- 1.9.1