mirror of
https://github.com/nxp-imx/linux-imx.git
synced 2025-09-02 18:06:13 +02:00

in 5451781dad
commit, it added the check that
the regulator need to be disabled before calling regulator_put().
If not do so, the kernel will print warning message as below.
To fix this, need to disable regulator before probe function return if this
PMIC is not found. regulator_put() will be called when probe fails in this case
as devm_regulator_get() already called in probe function.
[ 0.269916] i2c i2c-2: Max17135 PMIC not found!
[ 0.270004] ------------[ cut here ]------------
[ 0.270145] WARNING: CPU: 0 PID: 1 at drivers/regulator/core.c:2039 _regulator_put.part.4+0x100/0x120
[ 0.270166] Modules linked in:
[ 0.270196] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 5.3.0-rc3-next-20190809-02777-g23dc3ed #22
[ 0.270214] Hardware name: Freescale i.MX6 Quad/DualLite (Device Tree)
[ 0.270250] [<c0112868>] (unwind_backtrace) from [<c010cd9c>] (show_stack+0x10/0x14)
[ 0.270281] [<c010cd9c>] (show_stack) from [<c0d26ad4>] (dump_stack+0xd8/0x110)
[ 0.270310] [<c0d26ad4>] (dump_stack) from [<c01362c4>] (__warn.part.3+0xa8/0xe8)
[ 0.270335] [<c01362c4>] (__warn.part.3) from [<c013645c>] (warn_slowpath_null+0x40/0x4c)
[ 0.270361] [<c013645c>] (warn_slowpath_null) from [<c05e6520>] (_regulator_put.part.4+0x100/0x120)
[ 0.270385] [<c05e6520>] (_regulator_put.part.4) from [<c05e656c>] (regulator_put+0x2c/0x3c)
[ 0.270414] [<c05e656c>] (regulator_put) from [<c06dce90>] (release_nodes+0x1ac/0x1f8)
[ 0.270444] [<c06dce90>] (release_nodes) from [<c06d8968>] (really_probe+0x104/0x340)
[ 0.270467] [<c06d8968>] (really_probe) from [<c06d8d50>] (driver_probe_device+0x84/0x194)
[ 0.270492] [<c06d8d50>] (driver_probe_device) from [<c06d6e44>] (bus_for_each_drv+0x7c/0xc4)
[ 0.270516] [<c06d6e44>] (bus_for_each_drv) from [<c06d87e8>] (__device_attach+0xcc/0x140)
[ 0.270539] [<c06d87e8>] (__device_attach) from [<c06d7b00>] (bus_probe_device+0x88/0x90)
[ 0.270563] [<c06d7b00>] (bus_probe_device) from [<c06d49d0>] (device_add+0x608/0x754)
[ 0.270591] [<c06d49d0>] (device_add) from [<c088c4a0>] (i2c_new_client_device+0x12c/0x20c)
[ 0.270615] [<c088c4a0>] (i2c_new_client_device) from [<c088c588>] (i2c_new_device+0x8/0x14)
[ 0.270641] [<c088c588>] (i2c_new_device) from [<c088f5e0>] (of_i2c_register_devices+0x90/0x110)
[ 0.270666] [<c088f5e0>] (of_i2c_register_devices) from [<c088c950>] (i2c_register_adapter+0x148/0x3ec)
[ 0.270691] [<c088c950>] (i2c_register_adapter) from [<c0892b24>] (i2c_imx_probe+0x37c/0x830)
[ 0.270719] [<c0892b24>] (i2c_imx_probe) from [<c06dac38>] (platform_drv_probe+0x48/0x98)
[ 0.270747] [<c06dac38>] (platform_drv_probe) from [<c06d8a40>] (really_probe+0x1dc/0x340)
[ 0.270773] [<c06d8a40>] (really_probe) from [<c06d8d50>] (driver_probe_device+0x84/0x194)
[ 0.270797] [<c06d8d50>] (driver_probe_device) from [<c06d9044>] (device_driver_attach+0x58/0x60)
[ 0.270821] [<c06d9044>] (device_driver_attach) from [<c06d90a4>] (__driver_attach+0x58/0xd0)
[ 0.270844] [<c06d90a4>] (__driver_attach) from [<c06d6d70>] (bus_for_each_dev+0x70/0xb4)
[ 0.270867] [<c06d6d70>] (bus_for_each_dev) from [<c06d7d9c>] (bus_add_driver+0x198/0x1d0)
[ 0.270890] [<c06d7d9c>] (bus_add_driver) from [<c06d9d30>] (driver_register+0x74/0x108)
[ 0.270916] [<c06d9d30>] (driver_register) from [<c0103078>] (do_one_initcall+0x80/0x338)
[ 0.270946] [<c0103078>] (do_one_initcall) from [<c1301214>] (kernel_init_freeable+0x2fc/0x3d8)
[ 0.270973] [<c1301214>] (kernel_init_freeable) from [<c0d3f59c>] (kernel_init+0x8/0x110)
[ 0.270997] [<c0d3f59c>] (kernel_init) from [<c01010b4>] (ret_from_fork+0x14/0x20)
[ 0.271015] Exception stack(0xec0e9fb0 to 0xec0e9ff8)
[ 0.271038] 9fa0: 00000000 00000000 00000000 00000000
[ 0.271059] 9fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
[ 0.271077] 9fe0: 00000000 00000000 00000000 00000000 00000013 00000000
[ 0.271094] irq event stamp: 70901
[ 0.271125] hardirqs last enabled at (70909): [<c019be00>] console_unlock+0x418/0x5f4
[ 0.271149] hardirqs last disabled at (70916): [<c019ba70>] console_unlock+0x88/0x5f4
[ 0.271173] softirqs last enabled at (69156): [<c01024e4>] __do_softirq+0x2c4/0x514
[ 0.271198] softirqs last disabled at (68881): [<c013da4c>] irq_exit+0x100/0x188
[ 0.271268] ---[ end trace 579e47ca40f2be36 ]---
Signed-off-by: Robby Cai <robby.cai@nxp.com>
274 lines
6.1 KiB
C
274 lines
6.1 KiB
C
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
/*
|
|
* Copyright (C) 2010-2015 Freescale Semiconductor, Inc.
|
|
* Copyright 2019 NXP
|
|
*/
|
|
|
|
/*!
|
|
* @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;
|
|
}
|
|
EXPORT_SYMBOL(max17135_reg_read);
|
|
|
|
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;
|
|
}
|
|
EXPORT_SYMBOL(max17135_reg_write);
|
|
|
|
#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)
|
|
{
|
|
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:
|
|
if (!IS_ERR(gpio_regulator))
|
|
regulator_disable(gpio_regulator);
|
|
kfree(max17135);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static void max17135_remove(struct i2c_client *i2c)
|
|
{
|
|
struct max17135 *max17135 = i2c_get_clientdata(i2c);
|
|
|
|
mfd_remove_devices(max17135->dev);
|
|
|
|
if (!IS_ERR(gpio_regulator))
|
|
regulator_disable(gpio_regulator);
|
|
}
|
|
|
|
/* 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);
|