Change to using upstream spmi bus architecture. All the spmi devices, marked by spmi-dev-container, become platform devices. spmi-slave-container devices become spmi_devices each representing a slave. The read/write functions use regmap api's instead of calls to spmi_ext_register_read/write() implemented by the spmi bus. This regmap is instantiated per slave. The spmi bus helper functions like spmi_get_irq get changed to their platform bus equivalents. Change Kconfig files include * Remove dependence on OF_SPMI, MSM_QPNP_INT * There were few places where an earlier commit dcc2aedc80746acee589e4b47d3e6adf5d3ec253 missed adding dependence on SPMI along with MSM_SPMI. Fix them. * Add depends on ARCH_MSM. ARCH_MSM is used for internal builds. Change the nodes in DTSI files to confirm to the modified drivers. Update their binding docs to drop spmi-dev-container and spmi-slave-container; Finally update defconfig to use upstream SPMI. Change-Id: Ic85bff27c09c84b152cb38acbc3cadd05c0ec57a Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
258 lines
6.8 KiB
C
258 lines
6.8 KiB
C
/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 and
|
|
* only version 2 as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/spmi.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/regmap.h>
|
|
#include <linux/err.h>
|
|
#include <linux/qpnp/qpnp-revid.h>
|
|
#include <linux/of.h>
|
|
|
|
#define REVID_REVISION1 0x0
|
|
#define REVID_REVISION2 0x1
|
|
#define REVID_REVISION3 0x2
|
|
#define REVID_REVISION4 0x3
|
|
#define REVID_TYPE 0x4
|
|
#define REVID_SUBTYPE 0x5
|
|
#define REVID_STATUS1 0x8
|
|
#define REVID_SPARE_0 0x60
|
|
|
|
#define QPNP_REVID_DEV_NAME "qcom,qpnp-revid"
|
|
|
|
static const char *const pmic_names[] = {
|
|
"Unknown PMIC",
|
|
"PM8941",
|
|
"PM8841",
|
|
"PM8019",
|
|
"PM8226",
|
|
"PM8110",
|
|
"PMA8084",
|
|
"PMI8962",
|
|
"PMD9635",
|
|
"PM8994",
|
|
"PMI8994",
|
|
"PM8916",
|
|
"PM8004",
|
|
"PM8909",
|
|
"PM2433",
|
|
"PMD9655",
|
|
"PM8950",
|
|
"PMI8950",
|
|
"PMK8001",
|
|
"PMI8996",
|
|
[25] = "PM8937",
|
|
[55] = "PMI8937",
|
|
};
|
|
|
|
struct revid_chip {
|
|
struct list_head link;
|
|
struct device_node *dev_node;
|
|
struct pmic_revid_data data;
|
|
};
|
|
|
|
static LIST_HEAD(revid_chips);
|
|
static DEFINE_MUTEX(revid_chips_lock);
|
|
|
|
static struct of_device_id qpnp_revid_match_table[] = {
|
|
{ .compatible = QPNP_REVID_DEV_NAME },
|
|
{}
|
|
};
|
|
|
|
static u8 qpnp_read_byte(struct regmap *regmap, u16 addr)
|
|
{
|
|
int rc;
|
|
int val;
|
|
|
|
rc = regmap_read(regmap, addr, &val);
|
|
if (rc) {
|
|
pr_err("read failed rc=%d\n", rc);
|
|
return 0;
|
|
}
|
|
return (u8)val;
|
|
}
|
|
|
|
/**
|
|
* get_revid_data - Return the revision information of PMIC
|
|
* @dev_node: Pointer to the revid peripheral of the PMIC for which
|
|
* revision information is seeked
|
|
*
|
|
* CONTEXT: Should be called in non atomic context
|
|
*
|
|
* RETURNS: pointer to struct pmic_revid_data filled with the information
|
|
* about the PMIC revision
|
|
*/
|
|
struct pmic_revid_data *get_revid_data(struct device_node *dev_node)
|
|
{
|
|
struct revid_chip *revid_chip;
|
|
|
|
if (!dev_node)
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
mutex_lock(&revid_chips_lock);
|
|
list_for_each_entry(revid_chip, &revid_chips, link) {
|
|
if (dev_node == revid_chip->dev_node) {
|
|
mutex_unlock(&revid_chips_lock);
|
|
return &revid_chip->data;
|
|
}
|
|
}
|
|
mutex_unlock(&revid_chips_lock);
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
EXPORT_SYMBOL(get_revid_data);
|
|
|
|
#define PM8941_PERIPHERAL_SUBTYPE 0x01
|
|
#define PM8226_PERIPHERAL_SUBTYPE 0x04
|
|
#define PMD9655_PERIPHERAL_SUBTYPE 0x0F
|
|
#define PMI8950_PERIPHERAL_SUBTYPE 0x11
|
|
#define PMI8937_PERIPHERAL_SUBTYPE 0x37
|
|
static size_t build_pmic_string(char *buf, size_t n, int sid,
|
|
u8 subtype, u8 rev1, u8 rev2, u8 rev3, u8 rev4)
|
|
{
|
|
size_t pos = 0;
|
|
/*
|
|
* In early versions of PM8941 and PM8226, the major revision number
|
|
* started incrementing from 0 (eg 0 = v1.0, 1 = v2.0).
|
|
* Increment the major revision number here if the chip is an early
|
|
* version of PM8941 or PM8226.
|
|
*/
|
|
if (((int)subtype == PM8941_PERIPHERAL_SUBTYPE
|
|
|| (int)subtype == PM8226_PERIPHERAL_SUBTYPE)
|
|
&& rev4 < 0x02)
|
|
rev4++;
|
|
|
|
pos += snprintf(buf + pos, n - pos, "PMIC@SID%d", sid);
|
|
if (subtype >= ARRAY_SIZE(pmic_names) || subtype == 0)
|
|
pos += snprintf(buf + pos, n - pos, ": %s (subtype: 0x%02X)",
|
|
pmic_names[0], subtype);
|
|
else
|
|
pos += snprintf(buf + pos, n - pos, ": %s",
|
|
pmic_names[subtype]);
|
|
pos += snprintf(buf + pos, n - pos, " v%d.%d", rev4, rev3);
|
|
if (rev2 || rev1)
|
|
pos += snprintf(buf + pos, n - pos, ".%d", rev2);
|
|
if (rev1)
|
|
pos += snprintf(buf + pos, n - pos, ".%d", rev1);
|
|
return pos;
|
|
}
|
|
|
|
#define PMIC_PERIPHERAL_TYPE 0x51
|
|
#define PMIC_STRING_MAXLENGTH 80
|
|
static int qpnp_revid_probe(struct platform_device *pdev)
|
|
{
|
|
u8 rev1, rev2, rev3, rev4, pmic_type, pmic_subtype, pmic_status;
|
|
u8 option1, option2, option3, option4, spare0;
|
|
unsigned int base;
|
|
int rc;
|
|
char pmic_string[PMIC_STRING_MAXLENGTH] = {'\0'};
|
|
struct revid_chip *revid_chip;
|
|
struct regmap *regmap;
|
|
|
|
regmap = dev_get_regmap(pdev->dev.parent, NULL);
|
|
if (!regmap) {
|
|
dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
rc = of_property_read_u32(pdev->dev.of_node, "reg", &base);
|
|
if (rc < 0) {
|
|
dev_err(&pdev->dev,
|
|
"Couldn't find reg in node = %s rc = %d\n",
|
|
pdev->dev.of_node->full_name, rc);
|
|
return rc;
|
|
}
|
|
pmic_type = qpnp_read_byte(regmap, base + REVID_TYPE);
|
|
if (pmic_type != PMIC_PERIPHERAL_TYPE) {
|
|
pr_err("Invalid REVID peripheral type: %02X\n", pmic_type);
|
|
return -EINVAL;
|
|
}
|
|
|
|
rev1 = qpnp_read_byte(regmap, base + REVID_REVISION1);
|
|
rev2 = qpnp_read_byte(regmap, base + REVID_REVISION2);
|
|
rev3 = qpnp_read_byte(regmap, base + REVID_REVISION3);
|
|
rev4 = qpnp_read_byte(regmap, base + REVID_REVISION4);
|
|
|
|
pmic_subtype = qpnp_read_byte(regmap, base + REVID_SUBTYPE);
|
|
if (pmic_subtype != PMD9655_PERIPHERAL_SUBTYPE)
|
|
pmic_status = qpnp_read_byte(regmap, base + REVID_STATUS1);
|
|
else
|
|
pmic_status = 0;
|
|
|
|
/* special case for PMI8937 */
|
|
if (pmic_subtype == PMI8950_PERIPHERAL_SUBTYPE) {
|
|
/* read spare register */
|
|
spare0 = qpnp_read_byte(regmap, base + REVID_SPARE_0);
|
|
if (spare0)
|
|
pmic_subtype = PMI8937_PERIPHERAL_SUBTYPE;
|
|
}
|
|
|
|
revid_chip = devm_kzalloc(&pdev->dev, sizeof(struct revid_chip),
|
|
GFP_KERNEL);
|
|
if (!revid_chip)
|
|
return -ENOMEM;
|
|
|
|
revid_chip->dev_node = pdev->dev.of_node;
|
|
revid_chip->data.rev1 = rev1;
|
|
revid_chip->data.rev2 = rev2;
|
|
revid_chip->data.rev3 = rev3;
|
|
revid_chip->data.rev4 = rev4;
|
|
revid_chip->data.pmic_subtype = pmic_subtype;
|
|
revid_chip->data.pmic_type = pmic_type;
|
|
|
|
if (pmic_subtype < ARRAY_SIZE(pmic_names))
|
|
revid_chip->data.pmic_name = pmic_names[pmic_subtype];
|
|
else
|
|
revid_chip->data.pmic_name = pmic_names[0];
|
|
|
|
mutex_lock(&revid_chips_lock);
|
|
list_add(&revid_chip->link, &revid_chips);
|
|
mutex_unlock(&revid_chips_lock);
|
|
|
|
option1 = pmic_status & 0x3;
|
|
option2 = (pmic_status >> 2) & 0x3;
|
|
option3 = (pmic_status >> 4) & 0x3;
|
|
option4 = (pmic_status >> 6) & 0x3;
|
|
|
|
build_pmic_string(pmic_string, PMIC_STRING_MAXLENGTH,
|
|
to_spmi_device(pdev->dev.parent)->usid,
|
|
pmic_subtype, rev1, rev2, rev3, rev4);
|
|
pr_info("%s options: %d, %d, %d, %d\n",
|
|
pmic_string, option1, option2, option3, option4);
|
|
return 0;
|
|
}
|
|
|
|
static struct platform_driver qpnp_revid_driver = {
|
|
.probe = qpnp_revid_probe,
|
|
.driver = {
|
|
.name = QPNP_REVID_DEV_NAME,
|
|
.owner = THIS_MODULE,
|
|
.of_match_table = qpnp_revid_match_table,
|
|
},
|
|
};
|
|
|
|
static int __init qpnp_revid_init(void)
|
|
{
|
|
return platform_driver_register(&qpnp_revid_driver);
|
|
}
|
|
|
|
static void __exit qpnp_revid_exit(void)
|
|
{
|
|
return platform_driver_unregister(&qpnp_revid_driver);
|
|
}
|
|
|
|
subsys_initcall(qpnp_revid_init);
|
|
module_exit(qpnp_revid_exit);
|
|
|
|
MODULE_DESCRIPTION("QPNP REVID DRIVER");
|
|
MODULE_LICENSE("GPL v2");
|
|
MODULE_ALIAS("platform:" QPNP_REVID_DEV_NAME);
|