[PATCH] i5k_amb: Support Intel 5400 chipset - Kernel

This is a discussion on [PATCH] i5k_amb: Support Intel 5400 chipset - Kernel ; Minor rework to support the Intel 5400 chipset. Signed-off-by: Darrick J. Wong --- drivers/hwmon/i5k_amb.c | 39 ++++++++++++++++++++++++++++++++++----- include/linux/pci_ids.h | 3 +++ 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/drivers/hwmon/i5k_amb.c b/drivers/hwmon/i5k_amb.c index 6ac5c6f..f9e2ed6 100644 --- a/drivers/hwmon/i5k_amb.c +++ b/drivers/hwmon/i5k_amb.c @@ ...

+ Reply to Thread
Results 1 to 11 of 11

Thread: [PATCH] i5k_amb: Support Intel 5400 chipset

  1. [PATCH] i5k_amb: Support Intel 5400 chipset

    Minor rework to support the Intel 5400 chipset.

    Signed-off-by: Darrick J. Wong
    ---

    drivers/hwmon/i5k_amb.c | 39 ++++++++++++++++++++++++++++++++++-----
    include/linux/pci_ids.h | 3 +++
    2 files changed, 37 insertions(+), 5 deletions(-)

    diff --git a/drivers/hwmon/i5k_amb.c b/drivers/hwmon/i5k_amb.c
    index 6ac5c6f..f9e2ed6 100644
    --- a/drivers/hwmon/i5k_amb.c
    +++ b/drivers/hwmon/i5k_amb.c
    @@ -111,6 +111,7 @@ struct i5k_amb_data {
    void __iomem *amb_mmio;
    struct i5k_device_attribute *attrs;
    unsigned int num_attrs;
    + unsigned long chipset_id;
    };

    static ssize_t show_name(struct device *dev, struct device_attribute *devattr,
    @@ -382,7 +383,8 @@ err:
    return res;
    }

    -static int __devinit i5k_find_amb_registers(struct i5k_amb_data *data)
    +static int __devinit i5k_find_amb_registers(struct i5k_amb_data *data,
    + unsigned long devid)
    {
    struct pci_dev *pcidev;
    u32 val32;
    @@ -390,7 +392,7 @@ static int __devinit i5k_find_amb_registers(struct i5k_amb_data *data)

    /* Find AMB register memory space */
    pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
    - PCI_DEVICE_ID_INTEL_5000_ERR,
    + devid,
    NULL);
    if (!pcidev)
    return -ENODEV;
    @@ -409,6 +411,8 @@ static int __devinit i5k_find_amb_registers(struct i5k_amb_data *data)
    goto out;
    }

    + data->chipset_id = devid;
    +
    res = 0;
    out:
    pci_dev_put(pcidev);
    @@ -441,10 +445,30 @@ out:
    return res;
    }

    +static unsigned long i5k_channel_pci_id(struct i5k_amb_data *data,
    + unsigned long channel)
    +{
    + switch (data->chipset_id) {
    + case PCI_DEVICE_ID_INTEL_5000_ERR:
    + return PCI_DEVICE_ID_INTEL_5000_FBD0 + channel;
    + case PCI_DEVICE_ID_INTEL_5400_ERR:
    + return PCI_DEVICE_ID_INTEL_5400_FBD0 + channel;
    + default:
    + BUG();
    + }
    +}
    +
    +static unsigned long chipset_ids[] = {
    + PCI_DEVICE_ID_INTEL_5000_ERR,
    + PCI_DEVICE_ID_INTEL_5400_ERR,
    + 0
    +};
    +
    static int __devinit i5k_amb_probe(struct platform_device *pdev)
    {
    struct i5k_amb_data *data;
    struct resource *reso;
    + int i;
    int res = -ENODEV;

    data = kzalloc(sizeof(*data), GFP_KERNEL);
    @@ -452,19 +476,24 @@ static int __devinit i5k_amb_probe(struct platform_device *pdev)
    return -ENOMEM;

    /* Figure out where the AMB registers live */
    - res = i5k_find_amb_registers(data);
    + i = 0;
    + do {
    + res = i5k_find_amb_registers(data, chipset_ids[i]);
    + i++;
    + } while (res && chipset_ids[i]);
    +
    if (res)
    goto err;

    /* Copy the DIMM presence map for the first two channels */
    res = i5k_channel_probe(&data->amb_present[0],
    - PCI_DEVICE_ID_INTEL_5000_FBD0);
    + i5k_channel_pci_id(data, 0));
    if (res)
    goto err;

    /* Copy the DIMM presence map for the optional second two channels */
    i5k_channel_probe(&data->amb_present[2],
    - PCI_DEVICE_ID_INTEL_5000_FBD1);
    + i5k_channel_pci_id(data, 1));

    /* Set up resource regions */
    reso = request_mem_region(data->amb_base, data->amb_len, DRVNAME);
    diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
    index 70eb3c8..bb6e5e2 100644
    --- a/include/linux/pci_ids.h
    +++ b/include/linux/pci_ids.h
    @@ -2382,6 +2382,9 @@
    #define PCI_DEVICE_ID_INTEL_ICH10_4 0x3a30
    #define PCI_DEVICE_ID_INTEL_ICH10_5 0x3a60
    #define PCI_DEVICE_ID_INTEL_IOAT_SNB 0x402f
    +#define PCI_DEVICE_ID_INTEL_5400_ERR 0x4030
    +#define PCI_DEVICE_ID_INTEL_5400_FBD0 0x4035
    +#define PCI_DEVICE_ID_INTEL_5400_FBD1 0x4036
    #define PCI_DEVICE_ID_INTEL_IOAT_SCNB 0x65ff
    #define PCI_DEVICE_ID_INTEL_TOLAPAI_0 0x5031
    #define PCI_DEVICE_ID_INTEL_TOLAPAI_1 0x5032
    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  2. Re: [PATCH] i5k_amb: Support Intel 5400 chipset

    On Fri, 2 May 2008 21:55:59 -0700
    "Darrick J. Wong" wrote:

    > Minor rework to support the Intel 5400 chipset.


    Thanks, I added this to the for-git-hwmon part of the -mm queue.

    I haven't heard from Mark for quite some time so soon I might declare him
    absent and I'll go through my hwmon patches with a view to merging them into
    2.6.26 directly.

    That queue currently consists of:

    hdaps-invert-the-axes-for-hdaps-on-lenovo-r61i-thinkpads.patch
    adt746x-logical-bitwise-confusion-in-set_max_duty_at_crit.patch
    adt7473-minor-documentation-update.patch
    i5k_amb-support-intel-5400-chipset.patch

    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  3. Re: [lm-sensors] [PATCH] i5k_amb: Support Intel 5400 chipset

    Hi Andrew,

    On Mon, 5 May 2008 13:56:21 -0700, Andrew Morton wrote:
    > On Fri, 2 May 2008 21:55:59 -0700
    > "Darrick J. Wong" wrote:
    >
    > > Minor rework to support the Intel 5400 chipset.

    >
    > Thanks, I added this to the for-git-hwmon part of the -mm queue.
    >
    > I haven't heard from Mark for quite some time so soon I might declare him
    > absent and I'll go through my hwmon patches with a view to merging them into
    > 2.6.26 directly.


    For Mark's status see:
    http://lists.lm-sensors.org/pipermai...ay/023006.html

    I guess he'll be happy if you merge a couple patches for him.

    >
    > That queue currently consists of:
    >
    > hdaps-invert-the-axes-for-hdaps-on-lenovo-r61i-thinkpads.patch
    > adt746x-logical-bitwise-confusion-in-set_max_duty_at_crit.patch


    The adt7473:set_max_duty_at_crit() code was originally confusing but
    correct. After this patch the code is clear but broken. Is it
    considered an improvement?

    (I would have told you before if that patch had been submitted to the
    lm-sensors list as it should have, given that it modifies a hardware
    monitoring driver... Not to mention that the patch file name doesn't
    match the driver file name, just to add to the confusion.)

    > adt7473-minor-documentation-update.patch


    I reviewed and acked that one, it's ready to go.

    > i5k_amb-support-intel-5400-chipset.patch


    --
    Jean Delvare
    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  4. Re: [lm-sensors] [PATCH] i5k_amb: Support Intel 5400 chipset

    On Mon, 5 May 2008 23:22:45 +0200
    Jean Delvare wrote:

    > Hi Andrew,
    >
    > On Mon, 5 May 2008 13:56:21 -0700, Andrew Morton wrote:
    > > On Fri, 2 May 2008 21:55:59 -0700
    > > "Darrick J. Wong" wrote:
    > >
    > > > Minor rework to support the Intel 5400 chipset.

    > >
    > > Thanks, I added this to the for-git-hwmon part of the -mm queue.
    > >
    > > I haven't heard from Mark for quite some time so soon I might declare him
    > > absent and I'll go through my hwmon patches with a view to merging them into
    > > 2.6.26 directly.

    >
    > For Mark's status see:
    > http://lists.lm-sensors.org/pipermai...ay/023006.html
    >
    > I guess he'll be happy if you merge a couple patches for him.


    hm, OK.

    > >
    > > That queue currently consists of:
    > >
    > > hdaps-invert-the-axes-for-hdaps-on-lenovo-r61i-thinkpads.patch
    > > adt746x-logical-bitwise-confusion-in-set_max_duty_at_crit.patch

    >
    > The adt7473:set_max_duty_at_crit() code was originally confusing but
    > correct. After this patch the code is clear but broken. Is it
    > considered an improvement?


    I'll drop it.

    > (I would have told you before if that patch had been submitted to the
    > lm-sensors list as it should have, given that it modifies a hardware
    > monitoring driver... Not to mention that the patch file name doesn't
    > match the driver file name, just to add to the confusion.)


    Well, y'know, stuff happens. Stuff like you forgetting to cc that
    patch's author on this email.

    > > adt7473-minor-documentation-update.patch

    >
    > I reviewed and acked that one, it's ready to go.


    OK, thanks.

    > > i5k_amb-support-intel-5400-chipset.patch


    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  5. ibmaem driver status?

    On Mon, May 05, 2008 at 01:56:21PM -0700, Andrew Morton wrote:
    > On Fri, 2 May 2008 21:55:59 -0700
    > "Darrick J. Wong" wrote:
    >
    > > Minor rework to support the Intel 5400 chipset.

    >
    > Thanks, I added this to the for-git-hwmon part of the -mm queue.
    >
    > I haven't heard from Mark for quite some time so soon I might declare him
    > absent and I'll go through my hwmon patches with a view to merging them into
    > 2.6.26 directly.


    Jean/Mark:

    Any thoughts on merging the ibmaem driver? I haven't heard anything in
    a good week and a half; if it's in Mark's backlog that's not a problem.
    (Or at least not as big of one as "silently bitrotting" )

    --D
    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  6. Re: ibmaem driver status?

    On Mon, 5 May 2008 16:04:40 -0700
    "Darrick J. Wong" wrote:

    > On Mon, May 05, 2008 at 01:56:21PM -0700, Andrew Morton wrote:
    > > On Fri, 2 May 2008 21:55:59 -0700
    > > "Darrick J. Wong" wrote:
    > >
    > > > Minor rework to support the Intel 5400 chipset.

    > >
    > > Thanks, I added this to the for-git-hwmon part of the -mm queue.
    > >
    > > I haven't heard from Mark for quite some time so soon I might declare him
    > > absent and I'll go through my hwmon patches with a view to merging them into
    > > 2.6.26 directly.

    >
    > Jean/Mark:
    >
    > Any thoughts on merging the ibmaem driver? I haven't heard anything in
    > a good week and a half; if it's in Mark's backlog that's not a problem.
    > (Or at least not as big of one as "silently bitrotting" )
    >


    Am just looking at it now. Your email was mysteriously truncated partway
    through the patch. Resend, please.

    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  7. [resend] [PATCH v2] ibmaem: New driver for power/energy meters in IBM System X hardware

    [resend due to truncation]
    Refactor the registration function to shrink the macros. Let me know if
    more aggressive de-macroing is desirable.
    ---
    New driver for power meters in IBM System X hardware, with a few
    cleanups suggested by Anthony Liguori.

    Signed-off-by: Darrick J. Wong
    ---

    Documentation/hwmon/ibmaem | 37 +
    drivers/hwmon/Kconfig | 14 +
    drivers/hwmon/Makefile | 1
    drivers/hwmon/ibmaem.c | 1157 ++++++++++++++++++++++++++++++++++++++++++++
    4 files changed, 1209 insertions(+), 0 deletions(-)

    diff --git a/Documentation/hwmon/ibmaem b/Documentation/hwmon/ibmaem
    new file mode 100644
    index 0000000..2fefaf5
    --- /dev/null
    +++ b/Documentation/hwmon/ibmaem
    @@ -0,0 +1,37 @@
    +Kernel driver ibmaem
    +======================
    +
    +Supported systems:
    + * Any recent IBM System X server with Active Energy Manager support.
    + This includes the x3350, x3550, x3650, x3655, x3755, x3850 M2,
    + x3950 M2, and certain HS2x/LS2x/QS2x blades. The IPMI host interface
    + driver ("ipmi-si") needs to be loaded for this driver to do anything.
    + Prefix: 'ibmaem'
    + Datasheet: Not available
    +
    +Author: Darrick J. Wong
    +
    +Description
    +-----------
    +
    +This driver implements sensor reading support for the energy and power
    +meters available on various IBM System X hardware through the BMC. All
    +sensor banks will be exported as platform devices; this driver can talk
    +to both v1 and v2 interfaces. This driver is completely separate from the
    +older ibmpex driver.
    +
    +The v1 AEM interface has a simple set of features to monitor energy use.
    +There is a register that displays an estimate of raw energy consumption
    +since the last BMC reset, and a power sensor that returns average power
    +use over a configurable interval.
    +
    +The v2 AEM interface is a bit more sophisticated, being able to present
    +a wider range of energy and power use registers, the power cap as
    +set by the AEM software, and temperature sensors.
    +
    +Special Features
    +----------------
    +
    +The "power_cap" value displays the current system power cap, as set by
    +the Active Energy Manager software. Setting the power cap from the host
    +is not currently supported.
    diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
    index 4dc76bc..00ff533 100644
    --- a/drivers/hwmon/Kconfig
    +++ b/drivers/hwmon/Kconfig
    @@ -330,6 +330,20 @@ config SENSORS_CORETEMP
    sensor inside your CPU. Supported all are all known variants
    of Intel Core family.

    +config SENSORS_IBMAEM
    + tristate "IBM Active Energy Manager temperature/power sensors and control"
    + select IPMI_SI
    + depends on IPMI_HANDLER
    + help
    + If you say yes here you get support for the temperature and
    + power sensors and capping hardware in various IBM System X
    + servers that support Active Energy Manager. This includes
    + the x3350, x3550, x3650, x3655, x3755, x3850 M2, x3950 M2,
    + and certain HS2x/LS2x/QS2x blades.
    +
    + This driver can also be built as a module. If so, the module
    + will be called ibmaem.
    +
    config SENSORS_IBMPEX
    tristate "IBM PowerExecutive temperature/power sensors"
    select IPMI_SI
    diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
    index 3bdb05a..d098677 100644
    --- a/drivers/hwmon/Makefile
    +++ b/drivers/hwmon/Makefile
    @@ -41,6 +41,7 @@ obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o
    obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o
    obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o
    obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o
    +obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o
    obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o
    obj-$(CONFIG_SENSORS_IT87) += it87.o
    obj-$(CONFIG_SENSORS_K8TEMP) += k8temp.o
    diff --git a/drivers/hwmon/ibmaem.c b/drivers/hwmon/ibmaem.c
    new file mode 100644
    index 0000000..59327d8
    --- /dev/null
    +++ b/drivers/hwmon/ibmaem.c
    @@ -0,0 +1,1157 @@
    +/*
    + * A hwmon driver for the IBM Active Energy Manager temperature/power sensors
    + * and capping functionality.
    + * Copyright (C) 2008 IBM
    + *
    + * Author: Darrick J. Wong
    + *
    + * 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.
    + *
    + * You should have received a copy of the GNU General Public License
    + * along with this program; if not, write to the Free Software
    + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    + */
    +
    +#include
    +#include
    +#include
    +#include
    +#include
    +#include
    +#include
    +#include
    +#include
    +#include
    +#include
    +
    +#define REFRESH_INTERVAL (HZ)
    +#define DRVNAME "aem"
    +
    +#define AEM_NETFN 0x2E
    +
    +#define AEM_FIND_FW_CMD 0x80
    +#define AEM_ELEMENT_CMD 0x81
    +#define AEM_FW_INSTANCE_CMD 0x82
    +
    +#define AEM_READ_ELEMENT_CFG 0x80
    +#define AEM_READ_BUFFER 0x81
    +#define AEM_READ_REGISTER 0x82
    +#define AEM_WRITE_REGISTER 0x83
    +#define AEM_SET_REG_MASK 0x84
    +#define AEM_CLEAR_REG_MASK 0x85
    +#define AEM_READ_ELEMENT_CFG2 0x86
    +
    +#define AEM_CONTROL_ELEMENT 0
    +#define AEM_ENERGY_ELEMENT 1
    +#define AEM_CLOCK_ELEMENT 4
    +#define AEM_POWER_CAP_ELEMENT 7
    +#define AEM_EXHAUST_ELEMENT 9
    +#define AEM_POWER_ELEMENT 10
    +
    +#define AEM_MODULE_TYPE_ID 0x0001
    +
    +#define AEM2_NUM_ENERGY_REGS 2
    +#define AEM2_NUM_PCAP_REGS 6
    +#define AEM2_NUM_TEMP_REGS 2
    +#define AEM2_NUM_SENSORS 14
    +
    +#define AEM1_NUM_ENERGY_REGS 1
    +#define AEM1_NUM_SENSORS 3
    +
    +#define POWER_CAP 0
    +#define POWER_CAP_MAX_HOTPLUG 1
    +#define POWER_CAP_MAX 2
    +#define POWER_CAP_MIN_WARNING 3
    +#define POWER_CAP_MIN 4
    +#define POWER_AUX 5
    +
    +#define AEM_DEFAULT_POWER_INTERVAL 1000
    +#define AEM_MIN_POWER_INTERVAL 200
    +
    +static DEFINE_IDR(aem1_idr);
    +static DEFINE_SPINLOCK(aem1_idr_lock);
    +static DEFINE_IDR(aem2_idr);
    +static DEFINE_SPINLOCK(aem2_idr_lock);
    +
    +struct aem_ipmi_data;
    +struct aem1_data;
    +struct aem2_data;
    +
    +static void aem_register_bmc(int iface, struct device *dev);
    +static void aem_bmc_gone(int iface);
    +static void aem_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data);
    +static void aem_init_aem1(struct aem_ipmi_data *probe);
    +static void aem1_delete(struct aem1_data *data);
    +static void aem_init_aem2(struct aem_ipmi_data *probe);
    +static void aem2_delete(struct aem2_data *data);
    +static void aem1_remove_sensors(struct aem1_data *data);
    +static int aem1_find_sensors(struct aem1_data *data);
    +static void aem2_remove_sensors(struct aem2_data *data);
    +static int aem2_find_sensors(struct aem2_data *data);
    +
    +static struct device_driver aem_driver = {
    + .name = DRVNAME,
    + .bus = &platform_bus_type,
    +};
    +
    +struct aem_ipmi_data {
    + struct completion read_complete;
    + struct ipmi_addr address;
    + ipmi_user_t user;
    + int interface;
    +
    + struct kernel_ipmi_msg tx_message;
    + long tx_msgid;
    +
    + void *rx_msg_data;
    + unsigned short rx_msg_len;
    + unsigned char rx_result;
    + int rx_recv_type;
    +
    + struct device *bmc_device;
    +};
    +
    +struct aem_fw_data {
    + struct device *hwmon_dev;
    + struct platform_device *pdev;
    + struct mutex lock;
    + char valid;
    + unsigned long last_updated; /* In jiffies */
    + u8 ver_major;
    + u8 ver_minor;
    + u8 module_handle;
    + int id;
    + struct aem_ipmi_data ipmi;
    +};
    +
    +struct aem_ro_sensor_template {
    + char *label;
    + ssize_t (*show)(struct device *dev,
    + struct device_attribute *devattr,
    + char *buf);
    + int index;
    +};
    +
    +struct aem_rw_sensor_template {
    + char *label;
    + ssize_t (*show)(struct device *dev,
    + struct device_attribute *devattr,
    + char *buf);
    + ssize_t (*set)(struct device *dev,
    + struct device_attribute *devattr,
    + const char *buf, size_t count);
    + int index;
    +};
    +
    +struct aem1_data {
    + struct aem_fw_data fw;
    + struct list_head list;
    +
    + /*
    + * Available sensors:
    + * Energy meter
    + * Power meter
    + */
    + struct sensor_device_attribute sensors[AEM1_NUM_SENSORS];
    +
    + /* energy use */
    + u64 energy[AEM1_NUM_ENERGY_REGS];
    +
    + int power_period[AEM1_NUM_ENERGY_REGS];
    +};
    +
    +struct aem2_data {
    + struct aem_fw_data fw;
    + struct list_head list;
    +
    + /*
    + * Available sensors:
    + * Two energy meters
    + * Two power meters
    + * Two temperature sensors
    + * Six powercap registers
    + */
    + struct sensor_device_attribute sensors[AEM2_NUM_SENSORS];
    +
    + /* energy use */
    + u64 energy[AEM2_NUM_ENERGY_REGS];
    +
    + /* power caps */
    + u16 pcap[AEM2_NUM_PCAP_REGS];
    +
    + /* exhaust temperature */
    + u8 temp[AEM2_NUM_TEMP_REGS];
    +
    + /* power sampling interval */
    + int power_period[AEM2_NUM_ENERGY_REGS];
    +};
    +
    +/* Data structures returned by the AEM firmware */
    +struct aem_iana_id {
    + u8 bytes[3];
    +};
    +static struct aem_iana_id system_x_id = {
    + .bytes = {0x4D, 0x4F, 0x00}
    +};
    +
    +/* These are used to find AEM1 instances */
    +struct aem_find_firmware_req {
    + struct aem_iana_id id;
    + u8 rsvd;
    + u16 index;
    + u16 module_type_id;
    +} __attribute__ ((packed));
    +
    +struct aem_find_firmware_resp {
    + struct aem_iana_id id;
    + u8 num_instances;
    +} __attribute__ ((packed));
    +
    +/* These are used to find AEM2 instances */
    +struct aem_find_instance_req {
    + struct aem_iana_id id;
    + u8 instance_number;
    + u16 module_type_id;
    +} __attribute__ ((packed));
    +
    +struct aem_find_instance_resp {
    + struct aem_iana_id id;
    + u8 num_instances;
    + u8 major;
    + u8 minor;
    + u8 module_handle;
    + u16 record_id;
    +} __attribute__ ((packed));
    +
    +/* These are used to query sensors */
    +struct aem_read_sensor_req {
    + struct aem_iana_id id;
    + u8 module_handle;
    + u8 element;
    + u8 subcommand;
    + u8 reg;
    + u8 rx_buf_size;
    +} __attribute__ ((packed));
    +
    +struct aem_read_sensor_resp {
    + struct aem_iana_id id;
    + u8 bytes[0];
    +} __attribute__ ((packed));
    +
    +/* Data structures to talk to the IPMI layer */
    +struct aem_driver_data {
    + struct list_head aem1_devices;
    + struct list_head aem2_devices;
    + struct ipmi_smi_watcher bmc_events;
    + struct ipmi_user_hndl ipmi_hndlrs;
    +};
    +
    +static struct aem_driver_data driver_data = {
    + .aem1_devices = LIST_HEAD_INIT(driver_data.aem1_devices),
    + .aem2_devices = LIST_HEAD_INIT(driver_data.aem2_devices),
    + .bmc_events = {
    + .owner = THIS_MODULE,
    + .new_smi = aem_register_bmc,
    + .smi_gone = aem_bmc_gone,
    + },
    + .ipmi_hndlrs = {
    + .ipmi_recv_hndl = aem_msg_handler,
    + },
    +};
    +
    +/* Functions to talk to the IPMI layer */
    +
    +/* Initialize IPMI address, message buffers and user data */
    +static int aem_init_ipmi_data(struct aem_ipmi_data *data,
    + int iface, struct device *bmc)
    +{
    + int err;
    +
    + init_completion(&data->read_complete);
    + data->bmc_device = bmc;
    +
    + /* Initialize IPMI address */
    + data->address.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
    + data->address.channel = IPMI_BMC_CHANNEL;
    + data->address.data[0] = 0;
    + data->interface = iface;
    +
    + /* Initialize message buffers */
    + data->tx_msgid = 0;
    + data->tx_message.netfn = AEM_NETFN;
    +
    + /* Create IPMI messaging interface user */
    + err = ipmi_create_user(data->interface, &driver_data.ipmi_hndlrs,
    + data, &data->user);
    + if (err < 0) {
    + dev_err(bmc, "Unable to register user with IPMI "
    + "interface %d\n", data->interface);
    + return -EACCES;
    + }
    +
    + return 0;
    +}
    +
    +/* Send an IPMI command */
    +static int aem_send_message(struct aem_ipmi_data *data)
    +{
    + int err;
    +
    + err = ipmi_validate_addr(&data->address, sizeof(data->address));
    + if (err)
    + goto out;
    +
    + data->tx_msgid++;
    + err = ipmi_request_settime(data->user, &data->address, data->tx_msgid,
    + &data->tx_message, data, 0, 0, 0);
    + if (err)
    + goto out1;
    +
    + return 0;
    +out1:
    + dev_err(data->bmc_device, "request_settime=%x\n", err);
    + return err;
    +out:
    + dev_err(data->bmc_device, "validate_addr=%x\n", err);
    + return err;
    +}
    +
    +/* Probe a BMC for AEM firmware instances */
    +static void aem_register_bmc(int iface, struct device *dev)
    +{
    + struct aem_ipmi_data probe;
    +
    + if (aem_init_ipmi_data(&probe, iface, dev))
    + return;
    +
    + aem_init_aem1(&probe);
    + aem_init_aem2(&probe);
    +
    + ipmi_destroy_user(probe.user);
    +}
    +
    +/* Handle BMC deletion */
    +static void aem_bmc_gone(int iface)
    +{
    + struct aem1_data *p1, *next1;
    + struct aem2_data *p2, *next2;
    +
    + list_for_each_entry_safe(p1, next1, &driver_data.aem1_devices, list) {
    + if (p1->fw.ipmi.interface == iface)
    + aem1_delete(p1);
    + }
    +
    + list_for_each_entry_safe(p2, next2, &driver_data.aem2_devices, list) {
    + if (p2->fw.ipmi.interface == iface)
    + aem2_delete(p2);
    + }
    +}
    +
    +/* Dispatch IPMI messages to callers */
    +static void aem_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
    +{
    + unsigned short rx_len;
    + struct aem_ipmi_data *data = (struct aem_ipmi_data *)user_msg_data;
    +
    + if (msg->msgid != data->tx_msgid) {
    + dev_err(data->bmc_device, "Mismatch between received msgid "
    + "(%02x) and transmitted msgid (%02x)!\n",
    + (int)msg->msgid,
    + (int)data->tx_msgid);
    + ipmi_free_recv_msg(msg);
    + return;
    + }
    +
    + data->rx_recv_type = msg->recv_type;
    + if (msg->msg.data_len > 0)
    + data->rx_result = msg->msg.data[0];
    + else
    + data->rx_result = IPMI_UNKNOWN_ERR_COMPLETION_CODE;
    +
    + if (msg->msg.data_len > 1) {
    + rx_len = msg->msg.data_len - 1;
    + if (data->rx_msg_len < rx_len)
    + rx_len = data->rx_msg_len;
    + data->rx_msg_len = rx_len;
    + memcpy(data->rx_msg_data, msg->msg.data + 1, data->rx_msg_len);
    + } else
    + data->rx_msg_len = 0;
    +
    + ipmi_free_recv_msg(msg);
    + complete(&data->read_complete);
    +}
    +
    +/* ID functions */
    +
    +/* Obtain an id */
    +#define AEM_IDR_GET(type) \
    +static int type##_idr_get(int *id) \
    +{ \
    + int i, err; \
    +\
    +again: \
    + if (unlikely(idr_pre_get(&type##_idr, GFP_KERNEL) == 0)) \
    + return -ENOMEM; \
    +\
    + spin_lock(&type##_idr_lock); \
    + err = idr_get_new(&type##_idr, NULL, &i); \
    + spin_unlock(&type##_idr_lock); \
    +\
    + if (unlikely(err == -EAGAIN)) \
    + goto again; \
    + else if (unlikely(err)) \
    + return err; \
    +\
    + *id = i & MAX_ID_MASK; \
    + return 0; \
    +}
    +
    +/* Release an object ID */
    +#define AEM_IDR_PUT(type) \
    +static void type##_idr_put(int id) \
    +{ \
    + spin_lock(&type##_idr_lock); \
    + idr_remove(&type##_idr, id); \
    + spin_unlock(&type##_idr_lock); \
    +}
    +
    +/* AEM Device Name */
    +static ssize_t show_name(struct device *dev, struct device_attribute *devattr,
    + char *buf)
    +{
    + struct aem_fw_data *fwdata = dev_get_drvdata(dev);
    +
    + return sprintf(buf, "%s%d\n", DRVNAME, fwdata->ver_major);
    +}
    +static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
    +
    +/* Probe functions for AEM1 devices */
    +
    +/* Get an object ID for AEM1 device */
    +AEM_IDR_GET(aem1)
    +
    +/* Release an object ID for an AEM1 device */
    +AEM_IDR_PUT(aem1)
    +
    +/* Retrieve version and module handle for an AEM1 instance */
    +static int aem_find_aem1_count(struct aem_ipmi_data *data)
    +{
    + struct aem_find_firmware_req ff_req;
    + struct aem_find_firmware_resp ff_resp;
    +
    + ff_req.id = system_x_id;
    + ff_req.index = 0;
    + ff_req.module_type_id = cpu_to_be16(AEM_MODULE_TYPE_ID);
    +
    + data->tx_message.cmd = AEM_FIND_FW_CMD;
    + data->tx_message.data = (char *)&ff_req;
    + data->tx_message.data_len = sizeof(ff_req);
    +
    + data->rx_msg_data = &ff_resp;
    + data->rx_msg_len = sizeof(ff_resp);
    +
    + aem_send_message(data);
    +
    + wait_for_completion(&data->read_complete);
    +
    + if (data->rx_result || data->rx_msg_len != sizeof(ff_resp) ||
    + memcmp(&ff_resp.id, &system_x_id, sizeof(system_x_id)))
    + return -ENOENT;
    +
    + return ff_resp.num_instances;
    +}
    +
    +/* Find and initialize one AEM1 instance */
    +static int aem_init_aem1_inst(struct aem_ipmi_data *probe, u8 module_handle)
    +{
    + struct aem1_data *data;
    + int i;
    + int res = -ENOMEM;
    +
    + data = kzalloc(sizeof(*data), GFP_KERNEL);
    + if (!data)
    + return res;
    + mutex_init(&data->fw.lock);
    +
    + /* Copy instance data */
    + data->fw.ver_major = 1;
    + data->fw.ver_minor = 0;
    + data->fw.module_handle = module_handle;
    + for (i = 0; i < AEM1_NUM_ENERGY_REGS; i++)
    + data->power_period[i] = AEM_DEFAULT_POWER_INTERVAL;
    +
    + /* Create sub-device for this fw instance */
    + if (aem1_idr_get(&data->fw.id))
    + goto id_err;
    +
    + data->fw.pdev = platform_device_alloc(DRVNAME "1", data->fw.id);
    + data->fw.pdev->dev.driver = &aem_driver;
    + if (IS_ERR(data->fw.pdev))
    + goto dev_err;
    +
    + res = platform_device_add(data->fw.pdev);
    + if (res)
    + goto ipmi_err;
    +
    + dev_set_drvdata(&data->fw.pdev->dev, &data->fw);
    +
    + /* Set up IPMI interface */
    + if (aem_init_ipmi_data(&data->fw.ipmi, probe->interface,
    + probe->bmc_device))
    + goto ipmi_err;
    +
    + /* Register with hwmon */
    + data->fw.hwmon_dev = hwmon_device_register(&data->fw.pdev->dev);
    +
    + if (IS_ERR(data->fw.hwmon_dev)) {
    + dev_err(&data->fw.pdev->dev, "Unable to register hwmon "
    + "device for IPMI interface %d\n",
    + probe->interface);
    + goto hwmon_reg_err;
    + }
    +
    + /* Find sensors */
    + if (aem1_find_sensors(data))
    + goto sensor_err;
    +
    + /* Add to our list of AEM1 devices */
    + list_add_tail(&data->list, &driver_data.aem1_devices);
    + dev_info(data->fw.ipmi.bmc_device, "Found AEM v%d.%d at 0x%X\n",
    + data->fw.ver_major, data->fw.ver_minor,
    + data->fw.module_handle);
    + return 0;
    +
    +sensor_err:
    + hwmon_device_unregister(data->fw.hwmon_dev);
    +hwmon_reg_err:
    + ipmi_destroy_user(data->fw.ipmi.user);
    +ipmi_err:
    + dev_set_drvdata(&data->fw.pdev->dev, NULL);
    + platform_device_unregister(data->fw.pdev);
    +dev_err:
    + aem1_idr_put(data->fw.id);
    +id_err:
    + kfree(data);
    +
    + return res;
    +}
    +
    +/* Find and initialize all AEM1 instances */
    +static void aem_init_aem1(struct aem_ipmi_data *probe)
    +{
    + int num, i, err;
    +
    + num = aem_find_aem1_count(probe);
    + for (i = 0; i < num; i++) {
    + err = aem_init_aem1_inst(probe, i);
    + if (err) {
    + dev_err(probe->bmc_device,
    + "Error %d initializing AEM1 0x%X\n",
    + err, i);
    + return;
    + }
    + }
    +}
    +
    +/* Delete an AEM instance */
    +#define AEM_DELETE(type) \
    +static void type##_delete(struct type##_data *data) \
    +{ \
    + list_del(&data->list); \
    + type##_remove_sensors(data); \
    + hwmon_device_unregister(data->fw.hwmon_dev); \
    + ipmi_destroy_user(data->fw.ipmi.user); \
    + dev_set_drvdata(&data->fw.pdev->dev, NULL); \
    + platform_device_unregister(data->fw.pdev); \
    + type##_idr_put(data->fw.id); \
    + kfree(data); \
    +}
    +
    +/* Delete an AEM1 instance */
    +AEM_DELETE(aem1)
    +
    +/* Probe functions for AEM2 devices */
    +
    +/* Retrieve version and module handle for an AEM2 instance */
    +static int aem_find_aem2(struct aem_ipmi_data *data,
    + struct aem_find_instance_resp *fi_resp,
    + int instance_num)
    +{
    + struct aem_find_instance_req fi_req;
    +
    + fi_req.id = system_x_id;
    + fi_req.instance_number = instance_num;
    + fi_req.module_type_id = cpu_to_be16(AEM_MODULE_TYPE_ID);
    +
    + data->tx_message.cmd = AEM_FW_INSTANCE_CMD;
    + data->tx_message.data = (char *)&fi_req;
    + data->tx_message.data_len = sizeof(fi_req);
    +
    + data->rx_msg_data = fi_resp;
    + data->rx_msg_len = sizeof(*fi_resp);
    +
    + aem_send_message(data);
    +
    + wait_for_completion(&data->read_complete);
    +
    + if (data->rx_result || data->rx_msg_len != sizeof(*fi_resp) ||
    + memcmp(&fi_resp->id, &system_x_id, sizeof(system_x_id)))
    + return -ENOENT;
    +
    + return 0;
    +}
    +
    +/* Get an object ID for AEM2 device */
    +AEM_IDR_GET(aem2)
    +
    +/* Release an object ID for an AEM2 device */
    +AEM_IDR_PUT(aem2)
    +
    +/* Find and initialize one AEM2 instance */
    +static int aem_init_aem2_inst(struct aem_ipmi_data *probe,
    + struct aem_find_instance_resp *fi_resp)
    +{
    + struct aem2_data *data;
    + int i;
    + int res = -ENOMEM;
    +
    + data = kzalloc(sizeof(*data), GFP_KERNEL);
    + if (!data)
    + return res;
    + mutex_init(&data->fw.lock);
    +
    + /* Copy instance data */
    + data->fw.ver_major = fi_resp->major;
    + data->fw.ver_minor = fi_resp->minor;
    + data->fw.module_handle = fi_resp->module_handle;
    + for (i = 0; i < AEM2_NUM_ENERGY_REGS; i++)
    + data->power_period[i] = AEM_DEFAULT_POWER_INTERVAL;
    +
    + /* Create sub-device for this fw instance */
    + if (aem2_idr_get(&data->fw.id))
    + goto id_err;
    +
    + data->fw.pdev = platform_device_alloc(DRVNAME "2", data->fw.id);
    + data->fw.pdev->dev.driver = &aem_driver;
    + if (IS_ERR(data->fw.pdev))
    + goto dev_err;
    +
    + res = platform_device_add(data->fw.pdev);
    + if (res)
    + goto ipmi_err;
    +
    + dev_set_drvdata(&data->fw.pdev->dev, &data->fw);
    +
    + /* Set up IPMI interface */
    + if (aem_init_ipmi_data(&data->fw.ipmi, probe->interface,
    + probe->bmc_device))
    + goto ipmi_err;
    +
    + /* Register with hwmon */
    + data->fw.hwmon_dev = hwmon_device_register(&data->fw.pdev->dev);
    +
    + if (IS_ERR(data->fw.hwmon_dev)) {
    + dev_err(&data->fw.pdev->dev, "Unable to register hwmon "
    + "device for IPMI interface %d\n",
    + probe->interface);
    + goto hwmon_reg_err;
    + }
    +
    + /* Find sensors */
    + if (aem2_find_sensors(data))
    + goto sensor_err;
    +
    + /* Add to our list of AEM2 devices */
    + list_add_tail(&data->list, &driver_data.aem2_devices);
    + dev_info(data->fw.ipmi.bmc_device, "Found AEM v%d.%d at 0x%X\n",
    + data->fw.ver_major, data->fw.ver_minor,
    + data->fw.module_handle);
    + return 0;
    +
    +sensor_err:
    + hwmon_device_unregister(data->fw.hwmon_dev);
    +hwmon_reg_err:
    + ipmi_destroy_user(data->fw.ipmi.user);
    +ipmi_err:
    + dev_set_drvdata(&data->fw.pdev->dev, NULL);
    + platform_device_unregister(data->fw.pdev);
    +dev_err:
    + aem2_idr_put(data->fw.id);
    +id_err:
    + kfree(data);
    +
    + return res;
    +}
    +
    +/* Find and initialize all AEM2 instances */
    +static void aem_init_aem2(struct aem_ipmi_data *probe)
    +{
    + struct aem_find_instance_resp fi_resp;
    + int err;
    + int i = 0;
    +
    + while (!aem_find_aem2(probe, &fi_resp, i)) {
    + if (fi_resp.major != 2) {
    + dev_err(probe->bmc_device, "Unknown AEM v%d; please "
    + "report this to the maintainer.\n",
    + fi_resp.major);
    + i++;
    + continue;
    + }
    + err = aem_init_aem2_inst(probe, &fi_resp);
    + if (err) {
    + dev_err(probe->bmc_device,
    + "Error %d initializing AEM2 0x%X\n",
    + err, fi_resp.module_handle);
    + return;
    + }
    + i++;
    + }
    +}
    +
    +/* Delete an AEM2 instance */
    +AEM_DELETE(aem2)
    +
    +/* Sensor support functions */
    +
    +/* Read a sensor value */
    +static int aem_read_sensor(struct aem_fw_data *fwdata, u8 elt, u8 reg,
    + void *data, size_t size)
    +{
    + int rs_size;
    + struct aem_read_sensor_req rs_req;
    + struct aem_read_sensor_resp *rs_resp;
    + struct aem_ipmi_data *ipmi = &fwdata->ipmi;
    +
    + /* AEM registers are 1, 2, 4 or 8 bytes */
    + switch (size) {
    + case 1:
    + case 2:
    + case 4:
    + case 8:
    + break;
    + default:
    + return -EINVAL;
    + }
    +
    + rs_req.id = system_x_id;
    + rs_req.module_handle = fwdata->module_handle;
    + rs_req.element = elt;
    + rs_req.subcommand = AEM_READ_REGISTER;
    + rs_req.reg = reg;
    + rs_req.rx_buf_size = size;
    +
    + ipmi->tx_message.cmd = AEM_ELEMENT_CMD;
    + ipmi->tx_message.data = (char *)&rs_req;
    + ipmi->tx_message.data_len = sizeof(rs_req);
    +
    + rs_size = sizeof(*rs_resp) + size;
    + rs_resp = kzalloc(rs_size, GFP_KERNEL);
    + if (!rs_resp)
    + return -ENOMEM;
    +
    + ipmi->rx_msg_data = rs_resp;
    + ipmi->rx_msg_len = rs_size;
    +
    + aem_send_message(ipmi);
    +
    + wait_for_completion(&ipmi->read_complete);
    +
    + if (ipmi->rx_result || ipmi->rx_msg_len != rs_size ||
    + memcmp(&rs_resp->id, &system_x_id, sizeof(system_x_id))) {
    + kfree(rs_resp);
    + return -ENOENT;
    + }
    +
    + switch (size) {
    + case 1: {
    + u8 *x = data;
    + *x = rs_resp->bytes[0];
    + break;
    + }
    + case 2: {
    + u16 *x = data;
    + *x = be16_to_cpup((u16 *)rs_resp->bytes);
    + break;
    + }
    + case 4: {
    + u32 *x = data;
    + *x = be32_to_cpup((u32 *)rs_resp->bytes);
    + break;
    + }
    + case 8: {
    + u64 *x = data;
    + *x = be64_to_cpup((u64 *)rs_resp->bytes);
    + break;
    + }
    + }
    +
    + return 0;
    +}
    +
    +/* Update AEM1 energy sensors */
    +static void update_aem1_energy_sensors(struct aem1_data *data)
    +{
    + aem_read_sensor(&data->fw, AEM_ENERGY_ELEMENT, 0, &data->energy, 8);
    +}
    +
    +/* Update all AEM1 sensors */
    +static void update_aem1_sensors(struct aem1_data *data)
    +{
    + mutex_lock(&data->fw.lock);
    + if (time_before(jiffies, data->fw.last_updated + REFRESH_INTERVAL) &&
    + data->fw.valid)
    + goto out;
    +
    + update_aem1_energy_sensors(data);
    +out:
    + mutex_unlock(&data->fw.lock);
    +}
    +
    +/* Update AEM2 energy sensors */
    +static void update_aem2_energy_sensors(struct aem2_data *data)
    +{
    + aem_read_sensor(&data->fw, AEM_ENERGY_ELEMENT, 0, &data->energy[0], 8);
    + aem_read_sensor(&data->fw, AEM_ENERGY_ELEMENT, 1, &data->energy[1], 8);
    +}
    +
    +/* Update all AEM2 sensors */
    +static void update_aem2_sensors(struct aem2_data *data)
    +{
    + int i;
    +
    + mutex_lock(&data->fw.lock);
    + if (time_before(jiffies, data->fw.last_updated + REFRESH_INTERVAL) &&
    + data->fw.valid)
    + goto out;
    +
    + update_aem2_energy_sensors(data);
    + aem_read_sensor(&data->fw, AEM_EXHAUST_ELEMENT, 0, &data->temp[0], 1);
    + aem_read_sensor(&data->fw, AEM_EXHAUST_ELEMENT, 1, &data->temp[1], 1);
    +
    + for (i = POWER_CAP; i <= POWER_AUX; i++)
    + aem_read_sensor(&data->fw, AEM_POWER_CAP_ELEMENT, i,
    + &data->pcap[i], 2);
    +out:
    + mutex_unlock(&data->fw.lock);
    +}
    +
    +/* sysfs support functions */
    +
    +#define AEM_SHOW_POWER(type) \
    +static ssize_t type##_show_power(struct device *dev, \
    + struct device_attribute *devattr, \
    + char *buf) \
    +{ \
    + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); \
    + struct aem_fw_data *fwdata = dev_get_drvdata(dev); \
    + struct type##_data * a = container_of(fwdata, \
    + struct type##_data, fw); \
    + u64 before, after; \
    + signed long leftover; \
    +\
    + mutex_lock(&fwdata->lock); \
    + update_##type##_energy_sensors(a); \
    + before = a->energy[attr->index]; \
    +\
    + leftover = schedule_timeout_interruptible( \
    + msecs_to_jiffies(a->power_period[attr->index]) \
    + ); \
    + if (leftover) { \
    + mutex_unlock(&fwdata->lock); \
    + return 0; \
    + } \
    +\
    + update_##type##_energy_sensors(a); \
    + after = a->energy[attr->index]; \
    + mutex_unlock(&fwdata->lock); \
    +\
    + return sprintf(buf, "%llu\n", (after - before) * 1000000 / \
    + a->power_period[attr->index]); \
    +}
    +
    +/* Display energy use */
    +#define AEM_SHOW_ENERGY(type) \
    +static ssize_t type##_show_energy(struct device *dev, \
    + struct device_attribute *devattr, \
    + char *buf) \
    +{ \
    + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); \
    + struct aem_fw_data *fwdata = dev_get_drvdata(dev); \
    + struct type##_data * a = container_of(fwdata, struct type##_data, fw); \
    + update_##type##_sensors(a); \
    +\
    + return sprintf(buf, "%llu\n", a->energy[attr->index] * 1000); \
    +}
    +
    +/* Display power interval registers */
    +#define AEM_SHOW_POWER_PERIOD(type) \
    +static ssize_t type##_show_power_period(struct device *dev, \
    + struct device_attribute *devattr, \
    + char *buf) \
    +{ \
    + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); \
    + struct aem_fw_data *fwdata = dev_get_drvdata(dev); \
    + struct type##_data * a = container_of(fwdata, struct type##_data, fw); \
    + update_##type##_sensors(a); \
    +\
    + return sprintf(buf, "%d\n", a->power_period[attr->index]); \
    +}
    +
    +/* Set power interval registers */
    +#define AEM_SET_POWER_PERIOD(type) \
    +static ssize_t type##_set_power_period(struct device *dev, \
    + struct device_attribute *devattr, \
    + const char *buf, size_t count) \
    +{ \
    + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); \
    + struct aem_fw_data *fwdata = dev_get_drvdata(dev); \
    + struct type##_data * a = container_of(fwdata, struct type##_data, fw); \
    + int temp = simple_strtol(buf, NULL, 10); \
    +\
    + if (temp < AEM_MIN_POWER_INTERVAL) \
    + return -EINVAL; \
    +\
    + mutex_lock(&a->fw.lock); \
    + a->power_period[attr->index] = temp; \
    + mutex_unlock(&a->fw.lock); \
    +\
    + return count; \
    +}
    +
    +/* Remove sensors attached to an AEM device */
    +#define AEM_REMOVE_SENSORS(type, num_sensors) \
    +static void type##_remove_sensors(struct type##_data *data) \
    +{ \
    + int i; \
    +\
    + for (i = 0; i < num_sensors; i++) { \
    + if (!data->sensors[i].dev_attr.attr.name) \
    + continue; \
    + device_remove_file(&data->fw.pdev->dev, \
    + &data->sensors[i].dev_attr); \
    + } \
    +\
    + device_remove_file(&data->fw.pdev->dev, \
    + &sensor_dev_attr_name.dev_attr); \
    +}
    +
    +/* Discover sensors on an AEM device */
    +static int register_sensors(struct device *dev,
    + struct sensor_device_attribute *sensors,
    + struct aem_ro_sensor_template *ro,
    + struct aem_rw_sensor_template *rw)
    +{
    + int err, idx;
    +
    + /* Set up read-only sensors */
    + idx = 0;
    + while (ro->label) {
    + sensors[idx].dev_attr.attr.name = ro->label;
    + sensors[idx].dev_attr.attr.mode = S_IRUGO;
    + sensors[idx].dev_attr.show = ro->show;
    + sensors[idx].index = ro->index;
    +
    + err = device_create_file(dev, &sensors[idx].dev_attr);
    + if (err) {
    + sensors[idx].dev_attr.attr.name = NULL;
    + return err;
    + }
    + idx++;
    + ro++;
    + }
    +
    + /* Set up read-write sensors */
    + while (rw->label) {
    + sensors[idx].dev_attr.attr.name = rw->label;
    + sensors[idx].dev_attr.attr.mode = S_IRUGO | S_IWUSR;
    + sensors[idx].dev_attr.show = rw->show;
    + sensors[idx].dev_attr.store = rw->set;
    + sensors[idx].index = rw->index;
    +
    + err = device_create_file(dev, &sensors[idx].dev_attr);
    + if (err) {
    + sensors[idx].dev_attr.attr.name = NULL;
    + return err;
    + }
    + idx++;
    + rw++;
    + }
    +
    + err = device_create_file(dev, &sensor_dev_attr_name.dev_attr);
    + return err;
    +}
    +
    +#define AEM_FIND_SENSORS(type) \
    +static int type##_find_sensors(struct type##_data *data) \
    +{ \
    +\
    + int err; \
    +\
    + err = register_sensors(&data->fw.pdev->dev, data->sensors, \
    + type##_ro_sensors, type##_rw_sensors); \
    + if (err) \
    + goto exit_remove; \
    +\
    + return 0; \
    +\
    +exit_remove: \
    + type##_remove_sensors(data); \
    + return err; \
    +}
    +
    +/* sysfs support functions for AEM1 sensors */
    +
    +/* Display power use */
    +AEM_SHOW_POWER(aem1)
    +
    +/* Display energy use */
    +AEM_SHOW_ENERGY(aem1)
    +
    +/* Display power interval registers */
    +AEM_SHOW_POWER_PERIOD(aem1)
    +
    +/* Set power interval registers */
    +AEM_SET_POWER_PERIOD(aem1)
    +
    +/* sysfs support functions for AEM2 sensors */
    +
    +/* Display power use */
    +AEM_SHOW_POWER(aem2)
    +
    +/* Display energy use */
    +AEM_SHOW_ENERGY(aem2)
    +
    +/* Display temperature use */
    +static ssize_t aem2_show_temp(struct device *dev,
    + struct device_attribute *devattr,
    + char *buf)
    +{
    + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
    + struct aem_fw_data *fwdata = dev_get_drvdata(dev);
    + struct aem2_data *a2 = container_of(fwdata, struct aem2_data, fw);
    + update_aem2_sensors(a2);
    +
    + return sprintf(buf, "%u\n", a2->temp[attr->index] * 1000);
    +}
    +
    +/* Display power-capping registers */
    +static ssize_t aem2_show_pcap_value(struct device *dev,
    + struct device_attribute *devattr,
    + char *buf)
    +{
    + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
    + struct aem_fw_data *fwdata = dev_get_drvdata(dev);
    + struct aem2_data *a2 = container_of(fwdata, struct aem2_data, fw);
    + update_aem2_sensors(a2);
    +
    + return sprintf(buf, "%u\n", a2->pcap[attr->index] * 100000);
    +}
    +
    +/* Display power interval registers */
    +AEM_SHOW_POWER_PERIOD(aem2)
    +
    +/* Set power interval registers */
    +AEM_SET_POWER_PERIOD(aem2)
    +
    +/* Sensor probe functions */
    +
    +/* Remove sensors attached to an AEM1 device */
    +AEM_REMOVE_SENSORS(aem1, AEM1_NUM_SENSORS)
    +
    +/* Description of AEM1 sensors */
    +static struct aem_ro_sensor_template aem1_ro_sensors[] = {
    +{"energy1_input", aem1_show_energy, 0},
    +{"power1_average", aem1_show_power, 0},
    +{NULL, NULL, 0},
    +};
    +
    +static struct aem_rw_sensor_template aem1_rw_sensors[] = {
    +{"power1_average_interval", aem1_show_power_period, aem1_set_power_period, 0},
    +{NULL, NULL, NULL, 0},
    +};
    +
    +/* Discover sensors on an AEM1 device */
    +AEM_FIND_SENSORS(aem1)
    +
    +/* Remove sensors attached to an AEM2 device */
    +AEM_REMOVE_SENSORS(aem2, AEM2_NUM_SENSORS)
    +
    +/* Description of AEM2 sensors */
    +static struct aem_ro_sensor_template aem2_ro_sensors[] = {
    +{"energy1_input", aem2_show_energy, 0},
    +{"energy2_input", aem2_show_energy, 1},
    +{"power1_average", aem2_show_power, 0},
    +{"power2_average", aem2_show_power, 1},
    +{"temp1_input", aem2_show_temp, 0},
    +{"temp2_input", aem2_show_temp, 1},
    +
    +{"power4_average", aem2_show_pcap_value, POWER_CAP_MAX_HOTPLUG},
    +{"power5_average", aem2_show_pcap_value, POWER_CAP_MAX},
    +{"power6_average", aem2_show_pcap_value, POWER_CAP_MIN_WARNING},
    +{"power7_average", aem2_show_pcap_value, POWER_CAP_MIN},
    +
    +{"power3_average", aem2_show_pcap_value, POWER_AUX},
    +{NULL, NULL, 0},
    +{"power_cap", aem2_show_pcap_value, POWER_CAP},
    +};
    +
    +static struct aem_rw_sensor_template aem2_rw_sensors[] = {
    +{"power1_average_interval", aem2_show_power_period, aem2_set_power_period, 0},
    +{"power2_average_interval", aem2_show_power_period, aem2_set_power_period, 1},
    +{NULL, NULL, NULL, 0},
    +};
    +
    +/* Discover sensors on an AEM2 device */
    +AEM_FIND_SENSORS(aem2)
    +
    +/* Module init/exit routines */
    +
    +static int __init aem_init(void)
    +{
    + int res;
    +
    + res = driver_register(&aem_driver);
    + if (res) {
    + printk(KERN_ERR "Can't register aem_driver\n");
    + return res;
    + }
    +
    + res = ipmi_smi_watcher_register(&driver_data.bmc_events);
    + if (res)
    + goto ipmi_reg_err;
    + return 0;
    +
    +ipmi_reg_err:
    + driver_unregister(&aem_driver);
    + return res;
    +
    +}
    +
    +static void __exit aem_exit(void)
    +{
    + struct aem1_data *p1, *next1;
    + struct aem2_data *p2, *next2;
    +
    + ipmi_smi_watcher_unregister(&driver_data.bmc_events);
    + driver_unregister(&aem_driver);
    + list_for_each_entry_safe(p1, next1, &driver_data.aem1_devices, list)
    + aem1_delete(p1);
    + list_for_each_entry_safe(p2, next2, &driver_data.aem2_devices, list)
    + aem2_delete(p2);
    +}
    +
    +MODULE_AUTHOR("Darrick J. Wong ");
    +MODULE_DESCRIPTION("IBM Active Energy Manager power/temp sensor driver");
    +MODULE_LICENSE("GPL");
    +
    +module_init(aem_init);
    +module_exit(aem_exit);
    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  8. Re: [resend] [PATCH v2] ibmaem: New driver for power/energy meters in IBM System X hardware

    On Tue, 6 May 2008 15:38:13 -0700 "Darrick J. Wong" wrote:

    > [resend due to truncation]
    > Refactor the registration function to shrink the macros. Let me know if
    > more aggressive de-macroing is desirable.
    > ---
    > New driver for power meters in IBM System X hardware, with a few
    > cleanups suggested by Anthony Liguori.
    >
    > Signed-off-by: Darrick J. Wong
    > ---
    >
    > index 0000000..2fefaf5
    > --- /dev/null
    > +++ b/Documentation/hwmon/ibmaem
    > @@ -0,0 +1,37 @@
    > +Kernel driver ibmaem
    > +======================
    > +
    > +Supported systems:
    > + * Any recent IBM System X server with Active Energy Manager support.
    > + This includes the x3350, x3550, x3650, x3655, x3755, x3850 M2,
    > + x3950 M2, and certain HS2x/LS2x/QS2x blades. The IPMI host interface
    > + driver ("ipmi-si") needs to be loaded for this driver to do anything.
    > + Prefix: 'ibmaem'
    > + Datasheet: Not available
    > +
    > +Author: Darrick J. Wong
    > +
    > +Description
    > +-----------
    > +
    > +This driver implements sensor reading support for the energy and power
    > +meters available on various IBM System X hardware through the BMC. All
    > +sensor banks will be exported as platform devices; this driver can talk
    > +to both v1 and v2 interfaces. This driver is completely separate from the
    > +older ibmpex driver.
    > +
    > +The v1 AEM interface has a simple set of features to monitor energy use.
    > +There is a register that displays an estimate of raw energy consumption
    > +since the last BMC reset, and a power sensor that returns average power
    > +use over a configurable interval.
    > +
    > +The v2 AEM interface is a bit more sophisticated, being able to present
    > +a wider range of energy and power use registers, the power cap as
    > +set by the AEM software, and temperature sensors.
    > +
    > +Special Features
    > +----------------
    > +
    > +The "power_cap" value displays the current system power cap, as set by
    > +the Active Energy Manager software. Setting the power cap from the host
    > +is not currently supported.
    > diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
    > index 4dc76bc..00ff533 100644
    > --- a/drivers/hwmon/Kconfig
    > +++ b/drivers/hwmon/Kconfig
    > @@ -330,6 +330,20 @@ config SENSORS_CORETEMP
    > sensor inside your CPU. Supported all are all known variants
    > of Intel Core family.
    >
    > +config SENSORS_IBMAEM
    > + tristate "IBM Active Energy Manager temperature/power sensors and control"
    > + select IPMI_SI
    > + depends on IPMI_HANDLER


    hm. IMPI_SI depends on IPMI_HANDLER, so this looks OK. But this stuff
    explodes in our facs so often...

    > + help
    > + If you say yes here you get support for the temperature and
    > + power sensors and capping hardware in various IBM System X
    > + servers that support Active Energy Manager. This includes
    > + the x3350, x3550, x3650, x3655, x3755, x3850 M2, x3950 M2,
    > + and certain HS2x/LS2x/QS2x blades.
    > +
    > + This driver can also be built as a module. If so, the module
    > + will be called ibmaem.
    > +
    >
    > ...
    >
    > +static DEFINE_IDR(aem1_idr);
    > +static DEFINE_SPINLOCK(aem1_idr_lock);
    > +static DEFINE_IDR(aem2_idr);
    > +static DEFINE_SPINLOCK(aem2_idr_lock);
    > +
    > +struct aem_ipmi_data;
    > +struct aem1_data;
    > +struct aem2_data;
    > +
    > +static void aem_register_bmc(int iface, struct device *dev);
    > +static void aem_bmc_gone(int iface);
    > +static void aem_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data);
    > +static void aem_init_aem1(struct aem_ipmi_data *probe);
    > +static void aem1_delete(struct aem1_data *data);
    > +static void aem_init_aem2(struct aem_ipmi_data *probe);
    > +static void aem2_delete(struct aem2_data *data);
    > +static void aem1_remove_sensors(struct aem1_data *data);
    > +static int aem1_find_sensors(struct aem1_data *data);
    > +static void aem2_remove_sensors(struct aem2_data *data);
    > +static int aem2_find_sensors(struct aem2_data *data);


    If these were moved below the struct definitions, we wouldn't need the
    forward declarations of the structs.

    > +static struct device_driver aem_driver = {
    > + .name = DRVNAME,
    > + .bus = &platform_bus_type,
    > +};
    > +
    > +struct aem_ipmi_data {
    > + struct completion read_complete;
    > + struct ipmi_addr address;
    > + ipmi_user_t user;
    > + int interface;
    > +
    > + struct kernel_ipmi_msg tx_message;
    > + long tx_msgid;
    > +
    > + void *rx_msg_data;
    > + unsigned short rx_msg_len;
    > + unsigned char rx_result;
    > + int rx_recv_type;
    > +
    > + struct device *bmc_device;
    > +};
    > +
    > +struct aem_fw_data {
    > + struct device *hwmon_dev;
    > + struct platform_device *pdev;
    > + struct mutex lock;
    > + char valid;
    > + unsigned long last_updated; /* In jiffies */
    > + u8 ver_major;
    > + u8 ver_minor;
    > + u8 module_handle;
    > + int id;
    > + struct aem_ipmi_data ipmi;
    > +};
    > +
    > +struct aem_ro_sensor_template {
    > + char *label;
    > + ssize_t (*show)(struct device *dev,
    > + struct device_attribute *devattr,
    > + char *buf);
    > + int index;
    > +};
    > +
    > +struct aem_rw_sensor_template {
    > + char *label;
    > + ssize_t (*show)(struct device *dev,
    > + struct device_attribute *devattr,
    > + char *buf);
    > + ssize_t (*set)(struct device *dev,
    > + struct device_attribute *devattr,
    > + const char *buf, size_t count);
    > + int index;
    > +};
    > +
    > +struct aem1_data {
    > + struct aem_fw_data fw;
    > + struct list_head list;
    > +
    > + /*
    > + * Available sensors:
    > + * Energy meter
    > + * Power meter
    > + */
    > + struct sensor_device_attribute sensors[AEM1_NUM_SENSORS];
    > +
    > + /* energy use */
    > + u64 energy[AEM1_NUM_ENERGY_REGS];
    > +
    > + int power_period[AEM1_NUM_ENERGY_REGS];
    > +};


    It'd be nice to document the units in which these physical quantities are
    recorded. BTUs/fortnight?

    > +struct aem2_data {
    > + struct aem_fw_data fw;
    > + struct list_head list;
    > +
    > + /*
    > + * Available sensors:
    > + * Two energy meters
    > + * Two power meters
    > + * Two temperature sensors
    > + * Six powercap registers
    > + */
    > + struct sensor_device_attribute sensors[AEM2_NUM_SENSORS];
    > +
    > + /* energy use */
    > + u64 energy[AEM2_NUM_ENERGY_REGS];
    > +
    > + /* power caps */
    > + u16 pcap[AEM2_NUM_PCAP_REGS];
    > +
    > + /* exhaust temperature */
    > + u8 temp[AEM2_NUM_TEMP_REGS];
    > +
    > + /* power sampling interval */
    > + int power_period[AEM2_NUM_ENERGY_REGS];
    > +};


    ditto.

    > +/* Data structures returned by the AEM firmware */
    > +struct aem_iana_id {
    > + u8 bytes[3];
    > +};
    > +static struct aem_iana_id system_x_id = {
    > + .bytes = {0x4D, 0x4F, 0x00}
    > +};
    > +
    > +/* These are used to find AEM1 instances */
    > +struct aem_find_firmware_req {
    > + struct aem_iana_id id;
    > + u8 rsvd;
    > + u16 index;
    > + u16 module_type_id;
    > +} __attribute__ ((packed));


    We have a __packed helper in compiler-gcc.h

    > +struct aem_find_firmware_resp {
    > + struct aem_iana_id id;
    > + u8 num_instances;
    > +} __attribute__ ((packed));
    > +
    > +/* These are used to find AEM2 instances */
    > +struct aem_find_instance_req {
    > + struct aem_iana_id id;
    > + u8 instance_number;
    > + u16 module_type_id;
    > +} __attribute__ ((packed));
    > +
    >
    > ...
    >
    > +/* Probe a BMC for AEM firmware instances */
    > +static void aem_register_bmc(int iface, struct device *dev)
    > +{
    > + struct aem_ipmi_data probe;


    184 bytes of stack. OK, but worth keeping an eye on.

    > + if (aem_init_ipmi_data(&probe, iface, dev))
    > + return;
    > +
    > + aem_init_aem1(&probe);
    > + aem_init_aem2(&probe);
    > +
    > + ipmi_destroy_user(probe.user);
    > +}
    > +
    >
    > ...
    >
    > +/* Dispatch IPMI messages to callers */
    > +static void aem_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
    > +{
    > + unsigned short rx_len;
    > + struct aem_ipmi_data *data = (struct aem_ipmi_data *)user_msg_data;


    unneeded and undesirable cast of void*.

    > + if (msg->msgid != data->tx_msgid) {
    > + dev_err(data->bmc_device, "Mismatch between received msgid "
    > + "(%02x) and transmitted msgid (%02x)!\n",
    > + (int)msg->msgid,
    > + (int)data->tx_msgid);
    > + ipmi_free_recv_msg(msg);
    > + return;
    > + }
    > +
    > + data->rx_recv_type = msg->recv_type;
    > + if (msg->msg.data_len > 0)
    > + data->rx_result = msg->msg.data[0];
    > + else
    > + data->rx_result = IPMI_UNKNOWN_ERR_COMPLETION_CODE;
    > +
    > + if (msg->msg.data_len > 1) {
    > + rx_len = msg->msg.data_len - 1;
    > + if (data->rx_msg_len < rx_len)
    > + rx_len = data->rx_msg_len;
    > + data->rx_msg_len = rx_len;
    > + memcpy(data->rx_msg_data, msg->msg.data + 1, data->rx_msg_len);
    > + } else
    > + data->rx_msg_len = 0;
    > +
    > + ipmi_free_recv_msg(msg);
    > + complete(&data->read_complete);
    > +}
    > +
    >
    > ...
    >
    > +/* Obtain an id */
    > +#define AEM_IDR_GET(type) \
    > +static int type##_idr_get(int *id) \
    > +{ \
    > + int i, err; \
    > +\
    > +again: \
    > + if (unlikely(idr_pre_get(&type##_idr, GFP_KERNEL) == 0)) \
    > + return -ENOMEM; \
    > +\
    > + spin_lock(&type##_idr_lock); \
    > + err = idr_get_new(&type##_idr, NULL, &i); \
    > + spin_unlock(&type##_idr_lock); \
    > +\
    > + if (unlikely(err == -EAGAIN)) \
    > + goto again; \
    > + else if (unlikely(err)) \
    > + return err; \
    > +\
    > + *id = i & MAX_ID_MASK; \
    > + return 0; \
    > +}
    > +
    > +/* Release an object ID */
    > +#define AEM_IDR_PUT(type) \
    > +static void type##_idr_put(int id) \
    > +{ \
    > + spin_lock(&type##_idr_lock); \
    > + idr_remove(&type##_idr, id); \
    > + spin_unlock(&type##_idr_lock); \
    > +}


    ick.

    >
    > ...
    >
    > +/* Find and initialize all AEM1 instances */
    > +static void aem_init_aem1(struct aem_ipmi_data *probe)
    > +{
    > + int num, i, err;
    > +
    > + num = aem_find_aem1_count(probe);
    > + for (i = 0; i < num; i++) {
    > + err = aem_init_aem1_inst(probe, i);
    > + if (err) {
    > + dev_err(probe->bmc_device,
    > + "Error %d initializing AEM1 0x%X\n",
    > + err, i);
    > + return;


    Should the error really be dropped on the floor?

    > + }
    > + }
    > +}
    > +
    >
    > ...
    >
    > +/* Find and initialize one AEM2 instance */
    > +static int aem_init_aem2_inst(struct aem_ipmi_data *probe,
    > + struct aem_find_instance_resp *fi_resp)
    > +{
    > + struct aem2_data *data;
    > + int i;
    > + int res = -ENOMEM;
    > +
    > + data = kzalloc(sizeof(*data), GFP_KERNEL);
    > + if (!data)
    > + return res;
    > + mutex_init(&data->fw.lock);
    > +
    > + /* Copy instance data */
    > + data->fw.ver_major = fi_resp->major;
    > + data->fw.ver_minor = fi_resp->minor;
    > + data->fw.module_handle = fi_resp->module_handle;
    > + for (i = 0; i < AEM2_NUM_ENERGY_REGS; i++)
    > + data->power_period[i] = AEM_DEFAULT_POWER_INTERVAL;
    > +
    > + /* Create sub-device for this fw instance */
    > + if (aem2_idr_get(&data->fw.id))
    > + goto id_err;
    > +
    > + data->fw.pdev = platform_device_alloc(DRVNAME "2", data->fw.id);


    platform_device_alloc() can fail.

    > + data->fw.pdev->dev.driver = &aem_driver;


    which will result in an oops here.


    > + if (IS_ERR(data->fw.pdev))


    platform_device_alloc() returns NULL on failure, not ERR_PTR.

    > + goto dev_err;
    > +
    > + res = platform_device_add(data->fw.pdev);
    > + if (res)
    > + goto ipmi_err;
    > +
    > + dev_set_drvdata(&data->fw.pdev->dev, &data->fw);
    > +
    > + /* Set up IPMI interface */
    > + if (aem_init_ipmi_data(&data->fw.ipmi, probe->interface,
    > + probe->bmc_device))
    > + goto ipmi_err;
    > +
    > + /* Register with hwmon */
    > + data->fw.hwmon_dev = hwmon_device_register(&data->fw.pdev->dev);
    > +
    > + if (IS_ERR(data->fw.hwmon_dev)) {
    > + dev_err(&data->fw.pdev->dev, "Unable to register hwmon "
    > + "device for IPMI interface %d\n",
    > + probe->interface);
    > + goto hwmon_reg_err;
    > + }
    > +
    > + /* Find sensors */
    > + if (aem2_find_sensors(data))
    > + goto sensor_err;
    > +
    > + /* Add to our list of AEM2 devices */
    > + list_add_tail(&data->list, &driver_data.aem2_devices);
    > + dev_info(data->fw.ipmi.bmc_device, "Found AEM v%d.%d at 0x%X\n",
    > + data->fw.ver_major, data->fw.ver_minor,
    > + data->fw.module_handle);
    > + return 0;
    > +
    > +sensor_err:
    > + hwmon_device_unregister(data->fw.hwmon_dev);
    > +hwmon_reg_err:
    > + ipmi_destroy_user(data->fw.ipmi.user);
    > +ipmi_err:
    > + dev_set_drvdata(&data->fw.pdev->dev, NULL);
    > + platform_device_unregister(data->fw.pdev);
    > +dev_err:
    > + aem2_idr_put(data->fw.id);
    > +id_err:
    > + kfree(data);
    > +
    > + return res;
    > +}
    > +
    > +/* Find and initialize all AEM2 instances */
    > +static void aem_init_aem2(struct aem_ipmi_data *probe)
    > +{
    > + struct aem_find_instance_resp fi_resp;
    > + int err;
    > + int i = 0;
    > +
    > + while (!aem_find_aem2(probe, &fi_resp, i)) {
    > + if (fi_resp.major != 2) {
    > + dev_err(probe->bmc_device, "Unknown AEM v%d; please "
    > + "report this to the maintainer.\n",
    > + fi_resp.major);
    > + i++;
    > + continue;
    > + }
    > + err = aem_init_aem2_inst(probe, &fi_resp);
    > + if (err) {
    > + dev_err(probe->bmc_device,
    > + "Error %d initializing AEM2 0x%X\n",
    > + err, fi_resp.module_handle);
    > + return;
    > + }
    > + i++;
    > + }
    > +}
    > +
    > +/* Delete an AEM2 instance */
    > +AEM_DELETE(aem2)
    > +
    > +/* Sensor support functions */
    > +
    > +/* Read a sensor value */
    > +static int aem_read_sensor(struct aem_fw_data *fwdata, u8 elt, u8 reg,
    > + void *data, size_t size)
    > +{
    > + int rs_size;
    > + struct aem_read_sensor_req rs_req;
    > + struct aem_read_sensor_resp *rs_resp;
    > + struct aem_ipmi_data *ipmi = &fwdata->ipmi;
    > +
    > + /* AEM registers are 1, 2, 4 or 8 bytes */
    > + switch (size) {
    > + case 1:
    > + case 2:
    > + case 4:
    > + case 8:
    > + break;
    > + default:
    > + return -EINVAL;
    > + }
    > +
    > + rs_req.id = system_x_id;
    > + rs_req.module_handle = fwdata->module_handle;
    > + rs_req.element = elt;
    > + rs_req.subcommand = AEM_READ_REGISTER;
    > + rs_req.reg = reg;
    > + rs_req.rx_buf_size = size;
    > +
    > + ipmi->tx_message.cmd = AEM_ELEMENT_CMD;
    > + ipmi->tx_message.data = (char *)&rs_req;
    > + ipmi->tx_message.data_len = sizeof(rs_req);
    > +
    > + rs_size = sizeof(*rs_resp) + size;
    > + rs_resp = kzalloc(rs_size, GFP_KERNEL);
    > + if (!rs_resp)
    > + return -ENOMEM;
    > +
    > + ipmi->rx_msg_data = rs_resp;
    > + ipmi->rx_msg_len = rs_size;
    > +
    > + aem_send_message(ipmi);
    > +
    > + wait_for_completion(&ipmi->read_complete);
    > +
    > + if (ipmi->rx_result || ipmi->rx_msg_len != rs_size ||
    > + memcmp(&rs_resp->id, &system_x_id, sizeof(system_x_id))) {
    > + kfree(rs_resp);
    > + return -ENOENT;
    > + }
    > +
    > + switch (size) {
    > + case 1: {
    > + u8 *x = data;
    > + *x = rs_resp->bytes[0];
    > + break;
    > + }
    > + case 2: {
    > + u16 *x = data;
    > + *x = be16_to_cpup((u16 *)rs_resp->bytes);
    > + break;
    > + }
    > + case 4: {
    > + u32 *x = data;
    > + *x = be32_to_cpup((u32 *)rs_resp->bytes);
    > + break;
    > + }
    > + case 8: {
    > + u64 *x = data;
    > + *x = be64_to_cpup((u64 *)rs_resp->bytes);
    > + break;
    > + }
    > + }
    > +
    > + return 0;
    > +}
    > +
    > +/* Update AEM1 energy sensors */
    > +static void update_aem1_energy_sensors(struct aem1_data *data)
    > +{
    > + aem_read_sensor(&data->fw, AEM_ENERGY_ELEMENT, 0, &data->energy, 8);
    > +}
    > +
    > +/* Update all AEM1 sensors */
    > +static void update_aem1_sensors(struct aem1_data *data)
    > +{
    > + mutex_lock(&data->fw.lock);
    > + if (time_before(jiffies, data->fw.last_updated + REFRESH_INTERVAL) &&
    > + data->fw.valid)
    > + goto out;
    > +
    > + update_aem1_energy_sensors(data);
    > +out:
    > + mutex_unlock(&data->fw.lock);
    > +}
    > +
    > +/* Update AEM2 energy sensors */
    > +static void update_aem2_energy_sensors(struct aem2_data *data)
    > +{
    > + aem_read_sensor(&data->fw, AEM_ENERGY_ELEMENT, 0, &data->energy[0], 8);
    > + aem_read_sensor(&data->fw, AEM_ENERGY_ELEMENT, 1, &data->energy[1], 8);
    > +}
    > +
    > +/* Update all AEM2 sensors */
    > +static void update_aem2_sensors(struct aem2_data *data)
    > +{
    > + int i;
    > +
    > + mutex_lock(&data->fw.lock);
    > + if (time_before(jiffies, data->fw.last_updated + REFRESH_INTERVAL) &&
    > + data->fw.valid)
    > + goto out;
    > +
    > + update_aem2_energy_sensors(data);
    > + aem_read_sensor(&data->fw, AEM_EXHAUST_ELEMENT, 0, &data->temp[0], 1);
    > + aem_read_sensor(&data->fw, AEM_EXHAUST_ELEMENT, 1, &data->temp[1], 1);
    > +
    > + for (i = POWER_CAP; i <= POWER_AUX; i++)
    > + aem_read_sensor(&data->fw, AEM_POWER_CAP_ELEMENT, i,
    > + &data->pcap[i], 2);
    > +out:
    > + mutex_unlock(&data->fw.lock);
    > +}
    > +
    > +/* sysfs support functions */
    > +
    > +#define AEM_SHOW_POWER(type) \
    > +static ssize_t type##_show_power(struct device *dev, \
    > + struct device_attribute *devattr, \
    > + char *buf) \
    > +{ \
    > + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); \
    > + struct aem_fw_data *fwdata = dev_get_drvdata(dev); \
    > + struct type##_data * a = container_of(fwdata, \
    > + struct type##_data, fw); \
    > + u64 before, after; \
    > + signed long leftover; \
    > +\
    > + mutex_lock(&fwdata->lock); \
    > + update_##type##_energy_sensors(a); \
    > + before = a->energy[attr->index]; \
    > +\
    > + leftover = schedule_timeout_interruptible( \
    > + msecs_to_jiffies(a->power_period[attr->index]) \
    > + ); \
    > + if (leftover) { \
    > + mutex_unlock(&fwdata->lock); \
    > + return 0; \
    > + } \
    > +\
    > + update_##type##_energy_sensors(a); \
    > + after = a->energy[attr->index]; \
    > + mutex_unlock(&fwdata->lock); \
    > +\
    > + return sprintf(buf, "%llu\n", (after - before) * 1000000 / \
    > + a->power_period[attr->index]); \
    > +}
    > +
    > +/* Display energy use */
    > +#define AEM_SHOW_ENERGY(type) \
    > +static ssize_t type##_show_energy(struct device *dev, \
    > + struct device_attribute *devattr, \
    > + char *buf) \
    > +{ \
    > + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); \
    > + struct aem_fw_data *fwdata = dev_get_drvdata(dev); \
    > + struct type##_data * a = container_of(fwdata, struct type##_data, fw); \
    > + update_##type##_sensors(a); \
    > +\
    > + return sprintf(buf, "%llu\n", a->energy[attr->index] * 1000); \
    > +}
    > +
    > +/* Display power interval registers */
    > +#define AEM_SHOW_POWER_PERIOD(type) \
    > +static ssize_t type##_show_power_period(struct device *dev, \
    > + struct device_attribute *devattr, \
    > + char *buf) \
    > +{ \
    > + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); \
    > + struct aem_fw_data *fwdata = dev_get_drvdata(dev); \
    > + struct type##_data * a = container_of(fwdata, struct type##_data, fw); \
    > + update_##type##_sensors(a); \
    > +\
    > + return sprintf(buf, "%d\n", a->power_period[attr->index]); \
    > +}
    > +
    > +/* Set power interval registers */
    > +#define AEM_SET_POWER_PERIOD(type) \
    > +static ssize_t type##_set_power_period(struct device *dev, \
    > + struct device_attribute *devattr, \
    > + const char *buf, size_t count) \
    > +{ \
    > + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); \
    > + struct aem_fw_data *fwdata = dev_get_drvdata(dev); \
    > + struct type##_data * a = container_of(fwdata, struct type##_data, fw); \
    > + int temp = simple_strtol(buf, NULL, 10); \
    > +\
    > + if (temp < AEM_MIN_POWER_INTERVAL) \
    > + return -EINVAL; \
    > +\
    > + mutex_lock(&a->fw.lock); \
    > + a->power_period[attr->index] = temp; \
    > + mutex_unlock(&a->fw.lock); \
    > +\
    > + return count; \
    > +}
    > +
    > +/* Remove sensors attached to an AEM device */
    > +#define AEM_REMOVE_SENSORS(type, num_sensors) \
    > +static void type##_remove_sensors(struct type##_data *data) \
    > +{ \
    > + int i; \
    > +\
    > + for (i = 0; i < num_sensors; i++) { \
    > + if (!data->sensors[i].dev_attr.attr.name) \
    > + continue; \
    > + device_remove_file(&data->fw.pdev->dev, \
    > + &data->sensors[i].dev_attr); \
    > + } \
    > +\
    > + device_remove_file(&data->fw.pdev->dev, \
    > + &sensor_dev_attr_name.dev_attr); \
    > +}


    ho hum.


    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  9. Re: [resend] [PATCH v2] ibmaem: New driver for power/energy meters in IBM System X hardware

    On Tue, 6 May 2008 15:38:13 -0700 "Darrick J. Wong" wrote:

    > [resend due to truncation]
    > Refactor the registration function to shrink the macros. Let me know if
    > more aggressive de-macroing is desirable.
    > ---
    > New driver for power meters in IBM System X hardware, with a few
    > cleanups suggested by Anthony Liguori.
    >
    > ...
    >
    > + int temp = simple_strtol(buf, NULL, 10); \


    A checkpatch version which you might not have says

    WARNING: consider using strict_strtol in preference to simple_strtol
    #1015: FILE: drivers/hwmon/ibmaem.c:918:
    + int temp = simple_strtol(buf, NULL, 10); \

    total: 0 errors, 1 warnings, 1221 lines checked

    because the code as you have it will accept input of the form "12foo".
    strict_strtol() will error on that.

    afacit the calling code would perhaps need a little changing to handle
    such errors appropriately.
    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  10. Re: [resend] [PATCH v2] ibmaem: New driver for power/energy meters in IBM System X hardware

    On Tue, 6 May 2008 17:36:07 -0700, Andrew Morton wrote:
    > A checkpatch version which you might not have says
    >
    > WARNING: consider using strict_strtol in preference to simple_strtol
    > #1015: FILE: drivers/hwmon/ibmaem.c:918:
    > + int temp = simple_strtol(buf, NULL, 10); \
    >
    > total: 0 errors, 1 warnings, 1221 lines checked
    >
    > because the code as you have it will accept input of the form "12foo".
    > strict_strtol() will error on that.


    As a side note, I really wonder why this new function wasn't named just
    strtol(), especially if the general idea is to get rid of the "simple"
    variant after some time.

    --
    Jean Delvare
    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  11. Re: [resend] [PATCH v2] ibmaem: New driver for power/energy meters in IBM System X hardware


    On Wed, 7 May 2008 08:01:05 +0200 Jean Delvare wrote:

    > On Tue, 6 May 2008 17:36:07 -0700, Andrew Morton wrote:
    > > A checkpatch version which you might not have says
    > >
    > > WARNING: consider using strict_strtol in preference to simple_strtol
    > > #1015: FILE: drivers/hwmon/ibmaem.c:918:
    > > + int temp = simple_strtol(buf, NULL, 10); \
    > >
    > > total: 0 errors, 1 warnings, 1221 lines checked
    > >
    > > because the code as you have it will accept input of the form "12foo".
    > > strict_strtol() will error on that.

    >
    > As a side note, I really wonder why this new function wasn't named just
    > strtol(), especially if the general idea is to get rid of the "simple"
    > variant after some time.


    didn't think of doing that.

    But:

    y:/home/akpm> cat t.c
    #include
    #include

    main()
    {
    unsigned long u = strtoul("12foo", 0, 10);
    printf("%lu\n", u);
    }
    y:/home/akpm> gcc t.c
    y:/home/akpm> ./a.out
    12


    strtoul() has different behaviour.
    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

+ Reply to Thread