linux/drivers/mfd/menf21bmc.c
Andreas Werner dfbdcd7cef mfd: menf21bmc: Introduce MEN 14F021P00 BMC MFD Core driver
The MEN 14F021P00 Board Management Controller provides an
I2C interface to the host to access the feature implemented in the BMC.
The BMC is a PIC Microntroller assembled on CPCI Card from MEN Mikroelektronik
and on a few Box/Display Computer.

Added MFD Core driver, supporting the I2C communication to the device.

The MFD driver currently supports the following features:
 	- Watchdog
 	- LEDs
	- Hwmon (voltage monitoring)

Signed-off-by: Andreas Werner <andreas.werner@men.de>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
2014-09-24 13:30:16 +01:00

132 lines
3.3 KiB
C

/*
* MEN 14F021P00 Board Management Controller (BMC) MFD Core Driver.
*
* Copyright (C) 2014 MEN Mikro Elektronik Nuernberg GmbH
*
* 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.
*/
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/mfd/core.h>
#define BMC_CMD_WDT_EXIT_PROD 0x18
#define BMC_CMD_WDT_PROD_STAT 0x19
#define BMC_CMD_REV_MAJOR 0x80
#define BMC_CMD_REV_MINOR 0x81
#define BMC_CMD_REV_MAIN 0x82
static struct mfd_cell menf21bmc_cell[] = {
{ .name = "menf21bmc_wdt", },
{ .name = "menf21bmc_led", },
{ .name = "menf21bmc_hwmon", }
};
static int menf21bmc_wdt_exit_prod_mode(struct i2c_client *client)
{
int val, ret;
val = i2c_smbus_read_byte_data(client, BMC_CMD_WDT_PROD_STAT);
if (val < 0)
return val;
/*
* Production mode should be not active after delivery of the Board.
* To be sure we check it, inform the user and exit the mode
* if active.
*/
if (val == 0x00) {
dev_info(&client->dev,
"BMC in production mode. Exit production mode\n");
ret = i2c_smbus_write_byte(client, BMC_CMD_WDT_EXIT_PROD);
if (ret < 0)
return ret;
}
return 0;
}
static int
menf21bmc_probe(struct i2c_client *client, const struct i2c_device_id *ids)
{
int rev_major, rev_minor, rev_main;
int ret;
ret = i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_BYTE);
if (!ret)
return -ENODEV;
rev_major = i2c_smbus_read_word_data(client, BMC_CMD_REV_MAJOR);
if (rev_major < 0) {
dev_err(&client->dev, "failed to get BMC major revision\n");
return rev_major;
}
rev_minor = i2c_smbus_read_word_data(client, BMC_CMD_REV_MINOR);
if (rev_minor < 0) {
dev_err(&client->dev, "failed to get BMC minor revision\n");
return rev_minor;
}
rev_main = i2c_smbus_read_word_data(client, BMC_CMD_REV_MAIN);
if (rev_main < 0) {
dev_err(&client->dev, "failed to get BMC main revision\n");
return rev_main;
}
dev_info(&client->dev, "FW Revision: %02d.%02d.%02d\n",
rev_major, rev_minor, rev_main);
/*
* We have to exit the Production Mode of the BMC to activate the
* Watchdog functionality and the BIOS life sign monitoring.
*/
ret = menf21bmc_wdt_exit_prod_mode(client);
if (ret < 0) {
dev_err(&client->dev, "failed to leave production mode\n");
return ret;
}
ret = mfd_add_devices(&client->dev, 0, menf21bmc_cell,
ARRAY_SIZE(menf21bmc_cell), NULL, 0, NULL);
if (ret < 0) {
dev_err(&client->dev, "failed to add BMC sub-devices\n");
return ret;
}
return 0;
}
static int menf21bmc_remove(struct i2c_client *client)
{
mfd_remove_devices(&client->dev);
return 0;
}
static const struct i2c_device_id menf21bmc_id_table[] = {
{ "menf21bmc" },
{ }
};
MODULE_DEVICE_TABLE(i2c, menf21bmc_id_table);
static struct i2c_driver menf21bmc_driver = {
.driver.name = "menf21bmc",
.id_table = menf21bmc_id_table,
.probe = menf21bmc_probe,
.remove = menf21bmc_remove,
};
module_i2c_driver(menf21bmc_driver);
MODULE_DESCRIPTION("MEN 14F021P00 BMC mfd core driver");
MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>");
MODULE_LICENSE("GPL v2");