diff options
Diffstat (limited to 'drivers/mfd/max17135-core.c')
-rw-r--r-- | drivers/mfd/max17135-core.c | 281 |
1 files changed, 281 insertions, 0 deletions
diff --git a/drivers/mfd/max17135-core.c b/drivers/mfd/max17135-core.c new file mode 100644 index 000000000000..f663f8068067 --- /dev/null +++ b/drivers/mfd/max17135-core.c @@ -0,0 +1,281 @@ +/* + * Copyright (C) 2010-2015 Freescale Semiconductor, Inc. + * + * 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 + * + */ + +/*! + * @file pmic/core/max17135.c + * @brief This file contains MAX17135 specific PMIC code. This implementaion + * may differ for each PMIC chip. + * + * @ingroup PMIC_CORE + */ + +/* + * Includes + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/device.h> +#include <linux/i2c.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/uaccess.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <linux/platform_device.h> +#include <linux/regulator/machine.h> +#include <linux/pmic_status.h> +#include <linux/mfd/core.h> +#include <linux/mfd/max17135.h> +#include <asm/mach-types.h> + +static int max17135_detect(struct i2c_client *client, + struct i2c_board_info *info); +struct i2c_client *max17135_client; +static struct regulator *gpio_regulator; + +static struct mfd_cell max17135_devs[] = { + { .name = "max17135-pmic", }, + { .name = "max17135-sns", }, +}; + +static const unsigned short normal_i2c[] = {0x48, I2C_CLIENT_END}; + +int max17135_reg_read(int reg_num, unsigned int *reg_val) +{ + int result; + + if (max17135_client == NULL) + return PMIC_ERROR; + + if ((reg_num == REG_MAX17135_EXT_TEMP) || + (reg_num == REG_MAX17135_INT_TEMP)) { + result = i2c_smbus_read_word_data(max17135_client, reg_num); + if (result < 0) { + dev_err(&max17135_client->dev, + "Unable to read MAX17135 register via I2C\n"); + return PMIC_ERROR; + } + /* Swap bytes for dword read */ + result = (result >> 8) | ((result & 0xFF) << 8); + } else { + result = i2c_smbus_read_byte_data(max17135_client, reg_num); + if (result < 0) { + dev_err(&max17135_client->dev, + "Unable to read MAX17135 register via I2C\n"); + return PMIC_ERROR; + } + } + + *reg_val = result; + return PMIC_SUCCESS; +} + +int max17135_reg_write(int reg_num, const unsigned int reg_val) +{ + int result; + + if (max17135_client == NULL) + return PMIC_ERROR; + + result = i2c_smbus_write_byte_data(max17135_client, reg_num, reg_val); + if (result < 0) { + dev_err(&max17135_client->dev, + "Unable to write MAX17135 register via I2C\n"); + return PMIC_ERROR; + } + + return PMIC_SUCCESS; +} + +#ifdef CONFIG_OF +static struct max17135_platform_data *max17135_i2c_parse_dt_pdata( + struct device *dev) +{ + struct max17135_platform_data *pdata; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + dev_err(dev, "could not allocate memory for pdata\n"); + return ERR_PTR(-ENOMEM); + } + + return pdata; +} +#else +static struct max17135_platform_data *max17135_i2c_parse_dt_pdata( + struct device *dev) +{ + return NULL; +} +#endif /* !CONFIG_OF */ + +static int max17135_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct max17135 *max17135; + struct max17135_platform_data *pdata = client->dev.platform_data; + struct device_node *np = client->dev.of_node; + int ret = 0; + + if (!np) + return -ENODEV; + + gpio_regulator = devm_regulator_get(&client->dev, "SENSOR"); + if (!IS_ERR(gpio_regulator)) { + ret = regulator_enable(gpio_regulator); + if (ret) { + dev_err(&client->dev, "gpio set voltage error\n"); + return ret; + } + } + + /* Create the PMIC data structure */ + max17135 = kzalloc(sizeof(struct max17135), GFP_KERNEL); + if (max17135 == NULL) + return -ENOMEM; + + /* Initialize the PMIC data structure */ + i2c_set_clientdata(client, max17135); + max17135->dev = &client->dev; + max17135->i2c_client = client; + + max17135_client = client; + ret = max17135_detect(client, NULL); + if (ret) + goto err1; + + mfd_add_devices(max17135->dev, -1, max17135_devs, + ARRAY_SIZE(max17135_devs), + NULL, 0, NULL); + + if (max17135->dev->of_node) { + pdata = max17135_i2c_parse_dt_pdata(max17135->dev); + if (IS_ERR(pdata)) { + ret = PTR_ERR(pdata); + goto err2; + } + + } + max17135->pdata = pdata; + + dev_info(&client->dev, "PMIC MAX17135 for eInk display\n"); + + return ret; +err2: + mfd_remove_devices(max17135->dev); +err1: + kfree(max17135); + + return ret; +} + + +static int max17135_remove(struct i2c_client *i2c) +{ + struct max17135 *max17135 = i2c_get_clientdata(i2c); + + mfd_remove_devices(max17135->dev); + return 0; +} + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int max17135_detect(struct i2c_client *client, + struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = client->adapter; + u8 chip_rev, chip_id; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + /* detection */ + if (i2c_smbus_read_byte_data(client, + REG_MAX17135_PRODUCT_REV) != 0) { + dev_err(&adapter->dev, + "Max17135 PMIC not found!\n"); + return -ENODEV; + } + + /* identification */ + chip_rev = i2c_smbus_read_byte_data(client, + REG_MAX17135_PRODUCT_REV); + chip_id = i2c_smbus_read_byte_data(client, + REG_MAX17135_PRODUCT_ID); + + if (chip_rev != 0x00 || chip_id != 0x4D) { /* identification failed */ + dev_info(&adapter->dev, + "Unsupported chip (man_id=0x%02X, " + "chip_id=0x%02X).\n", chip_rev, chip_id); + return -ENODEV; + } + + if (info) + strlcpy(info->type, "max17135_sensor", I2C_NAME_SIZE); + + return 0; +} + +static const struct i2c_device_id max17135_id[] = { + { "max17135", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max17135_id); + +static const struct of_device_id max17135_dt_ids[] = { + { + .compatible = "maxim,max17135", + .data = (void *) &max17135_id[0], + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, max17135_dt_ids); + + +static struct i2c_driver max17135_driver = { + .driver = { + .name = "max17135", + .owner = THIS_MODULE, + .of_match_table = max17135_dt_ids, + }, + .probe = max17135_probe, + .remove = max17135_remove, + .id_table = max17135_id, + .detect = max17135_detect, + .address_list = &normal_i2c[0], +}; + +static int __init max17135_init(void) +{ + return i2c_add_driver(&max17135_driver); +} + +static void __exit max17135_exit(void) +{ + i2c_del_driver(&max17135_driver); +} + +/* + * Module entry points + */ +subsys_initcall(max17135_init); +module_exit(max17135_exit); |