mirror of
https://github.com/followmsi/android_kernel_google_msm.git
synced 2024-11-06 23:17:41 +00:00
fae0afd7be
This branch is currently very outdated. Cherry pick the following changes in order to bring recent fixes to this branch. Change-Ids Ib99cd49623698d0295bc3c3cf880225495d530cb Ifba2bd1248e8bb47a0700dec4d321d32db4f2066 I0fc1e116c0f5a5648f99df967bdbb41fe5a07cbc I8e3e9068e7b9a3bfd24c07a00bb3953180ce0741 I2bf31a23199f942a9ac843b9940a0f7377256238 I96b47c8603a291c41c5769fab9483d83fefb6084 If3c5f00f4b75d03838ee556235697369fd2e47dd I32d50caf7b7a84db2654b69baafb40a441f46bf0 Ibd1020113de05e8214d41ffb975d4c95d3bb1ccf I67c2ac422b0ba1e5eca11ffccfe7e112c2e65a3d I2c149e6edd7c194e7f31ece95e2003ffde935806 I1f4b65cba149480fb4a8f91667ce45a876dff2f0 I2605afc2d6f9f3569ec783b2d23881f31cc9038a I77b40f9cb8295cff1d4a24dfc7d85b0e57262f6e I8ec5a68cad573218a0b0bd0073277b76978417ff I9eaf7f21c73dd1ceaf97589cbf82b722706c48c2 I089725e20ae20956530e570d918a87b72e59626e Ifa70a4daf01414dfafed7830e3a6a682c42e9364 Iaace5bc1d0611619f11b760ea6986eadb7346da1 I415d7831f15c0d690fd118ff190cd710a31368c6 I1e55bbfe37c7cc6f62581af11280a565ed648c06 I11938c569a25b026ee6b7b5125cec930676ec01d I07577d1aa57bfa4bee40b52c18b5bfcb1cdb7070 Icdb0a28b5f9456135c60badf726c367b02f6d762 I4f14b7419f7a90813650601638f6563422c13ae4 I016a33c358be2e7a2d26202f2e9b168dd54b6d34 I43e26a2932ab3e3d9d79bb5af7daf2364ca133b7 I333c810568cdc656d2e13f0eeb9511857335b834 I830bf2e679e76efe7ed9d8bb30a8d459d8b9a09a I08d8d319d096c050ae53d90eeac293c9b1bab2bf I445676d558e05a6c5e9dd1e76e80ba2f5e8cd6c1 I9f38515f35fcddfbdc34ae4f59ffcfc6682033d4 Ia32e581c11954163bc8ff20a7b32432a843a17b2 I07c346475fb4a294bc8eb8da8c2048cc23394e20 Ic3f2c4e5bafd3a1556412005564fc0b549e6813c Ic68717f4410170814d3e3b141de77b35eca12f7b Ifad79991298e9d779b38a33a270af5197d2fc060 I3060c5ba31b59666aedfafad430e21329c4479d4 I9c87282ba5819a76a6f6697a5e9de6c92d74a665 I8144e2b83748a96ab83ba14894bfa5bd59995895 I8cac84296ddb1e323f28e2476f78d0190f4a1b73 Icef2b7fa70f274f671bd1b9469eb6a333ab462f9 Idc4ceb7973d1a424834f8c2a4bc5916ef7b1e961 I1bc779e5890f6994442c562ee5abf37c06dc6d3d Ib1161d3d8c4a9378eabf44f9de1eae9556d82643 I11a17fc105dea398140abc1a59e6355e9e55b322 I0aa523321f1eed4a0a5461b1a894452f77e6ff99 I827bd74e9892969bb4891b31af1515789fa76c1c I1bb658339e5a6f0adb07fda3ee0bc6c03670cc09 I135261c851067a25b20b30482764b5a61145f186 I4971d605c3475949be64372dc6bd3355ed258527 I08f6a7830c4b9ad44fb96f6d304ce257a899e287 I96886f8d0f3f03905163d93c4f8f3e637d98127f Idf2cf7f40780cf46ba95228764790b818bc72db5 Ib046fd7149d71fc62c8f2df7a9747d6b54e95138 I546ff57d76e50ad86eb5853782fdcc1f348c9f0e I1293ecc2974bae4ad530ecb775cd19072e413c72 I8db5a202a2e63ca0a3e355a6083d426acb7342cb I71482f9a4430a8bbcd02c954a415ff3fab2a0c8f I0c5656798444764b52f4d9d8889f831d674a1610 I2ac4ccd967a7a47284573c9b645055ebeca4635c I93b830d42e7d44435c790832c11d5d38861015e0 If24985f47cc9fcd6c2eb84a73165878559008120 I39b908451f4b678ed63b872ffc46590e2c728b2c If7e5622067c77c4c8d64ee466d1c62e4dcb0e53f If58279329f6c76978223369a2d08d47a7b1e3579 I3621a0a204360343ba684e2217a68a6ef51a7850 I358909c38b0771fe0283386841b6123ddb2931a6 I2e8fcb4a15af3a66331a921d378bfd5d2dfe4533 I0bbad22c5fed8c32dd7de387194e94c8d4b9ad75 I34f51c6202cbaa805c496360e8dbd391890decc6 Iaab82ea254444bba233cd9ebad93d38426154ca4 Ica2565031546a8281820c17edc95d258ad31ba65 Ic282fff7167a8224ec1a1e0f7c247a9c8d73f390 Icdc707a9438adae5c8bb2d9b97af95e65c4bc2a1 I26c4e1fb971f22ec721e1dddcce6b301f5db6608 I8882575084eefa6a23bd59b8d2a54a53d793298e Ic1e7cf4da4d2bf73d889aee7bac6cf260b26d532 I472b9da10e9f45b3c254f8440526f3f9d5116931 I1560e0df1c89be1db1e1d1aeaaeef2e6da5f85c1 Ie2842b28648084d8970329d7c76fcf0d0666a847 I2dccaedb559fc5e9865b137f980024e068bdafa3 I61a3d44f81d829f1ef22fd3044c65e4b3518b92b I0d657c2e0d47af32cc35cd66b902be72952e5438 I22d2dbdae5c9c859d4baca2bdb7bea71da09b85b I8c99fcbbf2f7229728236ae1059f4c9cb8dbb065 I1bb841e463658c8db42eb15ecc1687262df86a2f Change-Id: Ib04f801b0085e7d5da3d73594bbc41c281b06026 Signed-off-by: Xiaozhe Shi <xiaozhes@codeaurora.org>
1001 lines
31 KiB
C
1001 lines
31 KiB
C
/*
|
|
* Copyright (c) 2011-2012, Code Aurora Forum. 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.
|
|
*/
|
|
|
|
#define pr_fmt(fmt) "%s: " fmt, __func__
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/string.h>
|
|
#include <linux/err.h>
|
|
#include <linux/msm_ssbi.h>
|
|
#include <linux/mfd/core.h>
|
|
#include <linux/mfd/pm8xxx/pm8921.h>
|
|
#include <linux/mfd/pm8xxx/core.h>
|
|
#include <linux/mfd/pm8xxx/regulator.h>
|
|
#include <linux/leds-pm8xxx.h>
|
|
|
|
#define REG_HWREV 0x002 /* PMIC4 revision */
|
|
#define REG_HWREV_2 0x0E8 /* PMIC4 revision 2 */
|
|
|
|
#define REG_MPP_BASE 0x050
|
|
#define REG_IRQ_BASE 0x1BB
|
|
|
|
#define REG_TEMP_ALARM_CTRL 0x1B
|
|
#define REG_TEMP_ALARM_PWM 0x9B
|
|
|
|
#define REG_BATT_ALARM_THRESH 0x023
|
|
#define REG_BATT_ALARM_CTRL1 0x024
|
|
#define REG_BATT_ALARM_CTRL2 0x021
|
|
#define REG_BATT_ALARM_PWM_CTRL 0x020
|
|
|
|
#define PM8921_VERSION_MASK 0xFFF0
|
|
#define PM8921_VERSION_VALUE 0x06F0
|
|
#define PM8922_VERSION_VALUE 0x0AF0
|
|
#define PM8917_VERSION_VALUE 0x0CF0
|
|
#define PM8921_REVISION_MASK 0x000F
|
|
|
|
#define REG_PM8921_PON_CNTRL_3 0x01D
|
|
#define PM8921_RESTART_REASON_MASK 0x07
|
|
|
|
#define SINGLE_IRQ_RESOURCE(_name, _irq) \
|
|
{ \
|
|
.name = _name, \
|
|
.start = _irq, \
|
|
.end = _irq, \
|
|
.flags = IORESOURCE_IRQ, \
|
|
}
|
|
|
|
struct pm8921 {
|
|
struct device *dev;
|
|
struct pm_irq_chip *irq_chip;
|
|
struct mfd_cell *mfd_regulators;
|
|
struct pm8xxx_regulator_core_platform_data *regulator_cdata;
|
|
u32 rev_registers;
|
|
u8 restart_reason;
|
|
};
|
|
|
|
static int pm8921_readb(const struct device *dev, u16 addr, u8 *val)
|
|
{
|
|
const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev);
|
|
const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data;
|
|
|
|
return msm_ssbi_read(pmic->dev->parent, addr, val, 1);
|
|
}
|
|
|
|
static int pm8921_writeb(const struct device *dev, u16 addr, u8 val)
|
|
{
|
|
const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev);
|
|
const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data;
|
|
|
|
return msm_ssbi_write(pmic->dev->parent, addr, &val, 1);
|
|
}
|
|
|
|
static int pm8921_read_buf(const struct device *dev, u16 addr, u8 *buf,
|
|
int cnt)
|
|
{
|
|
const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev);
|
|
const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data;
|
|
|
|
return msm_ssbi_read(pmic->dev->parent, addr, buf, cnt);
|
|
}
|
|
|
|
static int pm8921_write_buf(const struct device *dev, u16 addr, u8 *buf,
|
|
int cnt)
|
|
{
|
|
const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev);
|
|
const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data;
|
|
|
|
return msm_ssbi_write(pmic->dev->parent, addr, buf, cnt);
|
|
}
|
|
|
|
static int pm8921_read_irq_stat(const struct device *dev, int irq)
|
|
{
|
|
const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev);
|
|
const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data;
|
|
|
|
return pm8xxx_get_irq_stat(pmic->irq_chip, irq);
|
|
}
|
|
|
|
static enum pm8xxx_version pm8921_get_version(const struct device *dev)
|
|
{
|
|
const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev);
|
|
const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data;
|
|
enum pm8xxx_version version = -ENODEV;
|
|
|
|
if ((pmic->rev_registers & PM8921_VERSION_MASK) == PM8921_VERSION_VALUE)
|
|
version = PM8XXX_VERSION_8921;
|
|
else if ((pmic->rev_registers & PM8921_VERSION_MASK)
|
|
== PM8922_VERSION_VALUE)
|
|
version = PM8XXX_VERSION_8922;
|
|
else if ((pmic->rev_registers & PM8921_VERSION_MASK)
|
|
== PM8917_VERSION_VALUE)
|
|
version = PM8XXX_VERSION_8917;
|
|
|
|
return version;
|
|
}
|
|
|
|
static int pm8921_get_revision(const struct device *dev)
|
|
{
|
|
const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev);
|
|
const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data;
|
|
|
|
return pmic->rev_registers & PM8921_REVISION_MASK;
|
|
}
|
|
|
|
static u8 pm8921_restart_reason(const struct device *dev)
|
|
{
|
|
const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev);
|
|
const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data;
|
|
|
|
return pmic->restart_reason;
|
|
}
|
|
|
|
static struct pm8xxx_drvdata pm8921_drvdata = {
|
|
.pmic_readb = pm8921_readb,
|
|
.pmic_writeb = pm8921_writeb,
|
|
.pmic_read_buf = pm8921_read_buf,
|
|
.pmic_write_buf = pm8921_write_buf,
|
|
.pmic_read_irq_stat = pm8921_read_irq_stat,
|
|
.pmic_get_version = pm8921_get_version,
|
|
.pmic_get_revision = pm8921_get_revision,
|
|
.pmic_restart_reason = pm8921_restart_reason,
|
|
};
|
|
|
|
static struct resource gpio_cell_resources[] = {
|
|
[0] = {
|
|
.start = PM8921_IRQ_BLOCK_BIT(PM8921_GPIO_BLOCK_START, 0),
|
|
.end = PM8921_IRQ_BLOCK_BIT(PM8921_GPIO_BLOCK_START, 0)
|
|
+ PM8921_NR_GPIOS - 1,
|
|
.flags = IORESOURCE_IRQ,
|
|
},
|
|
};
|
|
|
|
static struct mfd_cell gpio_cell __devinitdata = {
|
|
.name = PM8XXX_GPIO_DEV_NAME,
|
|
.id = -1,
|
|
.resources = gpio_cell_resources,
|
|
.num_resources = ARRAY_SIZE(gpio_cell_resources),
|
|
};
|
|
|
|
static const struct resource adc_cell_resources[] __devinitconst = {
|
|
SINGLE_IRQ_RESOURCE(NULL, PM8921_ADC_EOC_USR_IRQ),
|
|
SINGLE_IRQ_RESOURCE(NULL, PM8921_ADC_BATT_TEMP_WARM_IRQ),
|
|
SINGLE_IRQ_RESOURCE(NULL, PM8921_ADC_BATT_TEMP_COLD_IRQ),
|
|
};
|
|
|
|
static struct mfd_cell adc_cell __devinitdata = {
|
|
.name = PM8XXX_ADC_DEV_NAME,
|
|
.id = -1,
|
|
.resources = adc_cell_resources,
|
|
.num_resources = ARRAY_SIZE(adc_cell_resources),
|
|
};
|
|
|
|
static struct resource mpp_cell_resources[] = {
|
|
{
|
|
.start = PM8921_IRQ_BLOCK_BIT(PM8921_MPP_BLOCK_START, 0),
|
|
.end = PM8921_IRQ_BLOCK_BIT(PM8921_MPP_BLOCK_START, 0)
|
|
+ PM8921_NR_MPPS - 1,
|
|
.flags = IORESOURCE_IRQ,
|
|
},
|
|
};
|
|
|
|
static struct mfd_cell mpp_cell __devinitdata = {
|
|
.name = PM8XXX_MPP_DEV_NAME,
|
|
.id = 0,
|
|
.resources = mpp_cell_resources,
|
|
.num_resources = ARRAY_SIZE(mpp_cell_resources),
|
|
};
|
|
|
|
static const struct resource rtc_cell_resources[] __devinitconst = {
|
|
[0] = SINGLE_IRQ_RESOURCE(NULL, PM8921_RTC_ALARM_IRQ),
|
|
[1] = {
|
|
.name = "pmic_rtc_base",
|
|
.start = PM8921_RTC_BASE,
|
|
.end = PM8921_RTC_BASE,
|
|
.flags = IORESOURCE_IO,
|
|
},
|
|
};
|
|
|
|
static struct mfd_cell rtc_cell __devinitdata = {
|
|
.name = PM8XXX_RTC_DEV_NAME,
|
|
.id = -1,
|
|
.resources = rtc_cell_resources,
|
|
.num_resources = ARRAY_SIZE(rtc_cell_resources),
|
|
};
|
|
|
|
static const struct resource resources_pwrkey[] __devinitconst = {
|
|
SINGLE_IRQ_RESOURCE(NULL, PM8921_PWRKEY_REL_IRQ),
|
|
SINGLE_IRQ_RESOURCE(NULL, PM8921_PWRKEY_PRESS_IRQ),
|
|
};
|
|
|
|
static struct mfd_cell pwrkey_cell __devinitdata = {
|
|
.name = PM8XXX_PWRKEY_DEV_NAME,
|
|
.id = -1,
|
|
.num_resources = ARRAY_SIZE(resources_pwrkey),
|
|
.resources = resources_pwrkey,
|
|
};
|
|
|
|
static const struct resource resources_keypad[] = {
|
|
SINGLE_IRQ_RESOURCE(NULL, PM8921_KEYPAD_IRQ),
|
|
SINGLE_IRQ_RESOURCE(NULL, PM8921_KEYSTUCK_IRQ),
|
|
};
|
|
|
|
static struct mfd_cell keypad_cell __devinitdata = {
|
|
.name = PM8XXX_KEYPAD_DEV_NAME,
|
|
.id = -1,
|
|
.num_resources = ARRAY_SIZE(resources_keypad),
|
|
.resources = resources_keypad,
|
|
};
|
|
|
|
static struct mfd_cell debugfs_cell __devinitdata = {
|
|
.name = "pm8xxx-debug",
|
|
.id = 0,
|
|
.platform_data = "pm8921-dbg",
|
|
.pdata_size = sizeof("pm8921-dbg"),
|
|
};
|
|
|
|
static struct mfd_cell pwm_cell __devinitdata = {
|
|
.name = PM8XXX_PWM_DEV_NAME,
|
|
.id = -1,
|
|
};
|
|
|
|
static const struct resource charger_cell_resources[] __devinitconst = {
|
|
SINGLE_IRQ_RESOURCE("USBIN_VALID_IRQ", PM8921_USBIN_VALID_IRQ),
|
|
SINGLE_IRQ_RESOURCE("USBIN_OV_IRQ", PM8921_USBIN_OV_IRQ),
|
|
SINGLE_IRQ_RESOURCE("BATT_INSERTED_IRQ", PM8921_BATT_INSERTED_IRQ),
|
|
SINGLE_IRQ_RESOURCE("VBATDET_LOW_IRQ", PM8921_VBATDET_LOW_IRQ),
|
|
SINGLE_IRQ_RESOURCE("USBIN_UV_IRQ", PM8921_USBIN_UV_IRQ),
|
|
SINGLE_IRQ_RESOURCE("VBAT_OV_IRQ", PM8921_VBAT_OV_IRQ),
|
|
SINGLE_IRQ_RESOURCE("CHGWDOG_IRQ", PM8921_CHGWDOG_IRQ),
|
|
SINGLE_IRQ_RESOURCE("VCP_IRQ", PM8921_VCP_IRQ),
|
|
SINGLE_IRQ_RESOURCE("ATCDONE_IRQ", PM8921_ATCDONE_IRQ),
|
|
SINGLE_IRQ_RESOURCE("ATCFAIL_IRQ", PM8921_ATCFAIL_IRQ),
|
|
SINGLE_IRQ_RESOURCE("CHGDONE_IRQ", PM8921_CHGDONE_IRQ),
|
|
SINGLE_IRQ_RESOURCE("CHGFAIL_IRQ", PM8921_CHGFAIL_IRQ),
|
|
SINGLE_IRQ_RESOURCE("CHGSTATE_IRQ", PM8921_CHGSTATE_IRQ),
|
|
SINGLE_IRQ_RESOURCE("LOOP_CHANGE_IRQ", PM8921_LOOP_CHANGE_IRQ),
|
|
SINGLE_IRQ_RESOURCE("FASTCHG_IRQ", PM8921_FASTCHG_IRQ),
|
|
SINGLE_IRQ_RESOURCE("TRKLCHG_IRQ", PM8921_TRKLCHG_IRQ),
|
|
SINGLE_IRQ_RESOURCE("BATT_REMOVED_IRQ", PM8921_BATT_REMOVED_IRQ),
|
|
SINGLE_IRQ_RESOURCE("BATTTEMP_HOT_IRQ", PM8921_BATTTEMP_HOT_IRQ),
|
|
SINGLE_IRQ_RESOURCE("CHGHOT_IRQ", PM8921_CHGHOT_IRQ),
|
|
SINGLE_IRQ_RESOURCE("BATTTEMP_COLD_IRQ", PM8921_BATTTEMP_COLD_IRQ),
|
|
SINGLE_IRQ_RESOURCE("CHG_GONE_IRQ", PM8921_CHG_GONE_IRQ),
|
|
SINGLE_IRQ_RESOURCE("BAT_TEMP_OK_IRQ", PM8921_BAT_TEMP_OK_IRQ),
|
|
SINGLE_IRQ_RESOURCE("COARSE_DET_LOW_IRQ", PM8921_COARSE_DET_LOW_IRQ),
|
|
SINGLE_IRQ_RESOURCE("VDD_LOOP_IRQ", PM8921_VDD_LOOP_IRQ),
|
|
SINGLE_IRQ_RESOURCE("VREG_OV_IRQ", PM8921_VREG_OV_IRQ),
|
|
SINGLE_IRQ_RESOURCE("VBATDET_IRQ", PM8921_VBATDET_IRQ),
|
|
SINGLE_IRQ_RESOURCE("BATFET_IRQ", PM8921_BATFET_IRQ),
|
|
SINGLE_IRQ_RESOURCE("PSI_IRQ", PM8921_PSI_IRQ),
|
|
SINGLE_IRQ_RESOURCE("DCIN_VALID_IRQ", PM8921_DCIN_VALID_IRQ),
|
|
SINGLE_IRQ_RESOURCE("DCIN_OV_IRQ", PM8921_DCIN_OV_IRQ),
|
|
SINGLE_IRQ_RESOURCE("DCIN_UV_IRQ", PM8921_DCIN_UV_IRQ),
|
|
};
|
|
|
|
static const struct resource bms_cell_resources[] __devinitconst = {
|
|
SINGLE_IRQ_RESOURCE("PM8921_BMS_SBI_WRITE_OK", PM8921_BMS_SBI_WRITE_OK),
|
|
SINGLE_IRQ_RESOURCE("PM8921_BMS_CC_THR", PM8921_BMS_CC_THR),
|
|
SINGLE_IRQ_RESOURCE("PM8921_BMS_VSENSE_THR", PM8921_BMS_VSENSE_THR),
|
|
SINGLE_IRQ_RESOURCE("PM8921_BMS_VSENSE_FOR_R", PM8921_BMS_VSENSE_FOR_R),
|
|
SINGLE_IRQ_RESOURCE("PM8921_BMS_OCV_FOR_R", PM8921_BMS_OCV_FOR_R),
|
|
SINGLE_IRQ_RESOURCE("PM8921_BMS_GOOD_OCV", PM8921_BMS_GOOD_OCV),
|
|
SINGLE_IRQ_RESOURCE("PM8921_BMS_VSENSE_AVG", PM8921_BMS_VSENSE_AVG),
|
|
};
|
|
|
|
static struct mfd_cell charger_cell __devinitdata = {
|
|
.name = PM8921_CHARGER_DEV_NAME,
|
|
.id = -1,
|
|
.resources = charger_cell_resources,
|
|
.num_resources = ARRAY_SIZE(charger_cell_resources),
|
|
};
|
|
|
|
static struct mfd_cell bms_cell __devinitdata = {
|
|
.name = PM8921_BMS_DEV_NAME,
|
|
.id = -1,
|
|
.resources = bms_cell_resources,
|
|
.num_resources = ARRAY_SIZE(bms_cell_resources),
|
|
};
|
|
|
|
static struct mfd_cell misc_cell __devinitdata = {
|
|
.name = PM8XXX_MISC_DEV_NAME,
|
|
.id = -1,
|
|
};
|
|
|
|
static struct mfd_cell leds_cell __devinitdata = {
|
|
.name = PM8XXX_LEDS_DEV_NAME,
|
|
.id = -1,
|
|
};
|
|
|
|
static const struct resource thermal_alarm_cell_resources[] __devinitconst = {
|
|
SINGLE_IRQ_RESOURCE("pm8921_tempstat_irq", PM8921_TEMPSTAT_IRQ),
|
|
SINGLE_IRQ_RESOURCE("pm8921_overtemp_irq", PM8921_OVERTEMP_IRQ),
|
|
};
|
|
|
|
static struct pm8xxx_tm_core_data thermal_alarm_cdata = {
|
|
.adc_channel = CHANNEL_DIE_TEMP,
|
|
.adc_type = PM8XXX_TM_ADC_PM8XXX_ADC,
|
|
.reg_addr_temp_alarm_ctrl = REG_TEMP_ALARM_CTRL,
|
|
.reg_addr_temp_alarm_pwm = REG_TEMP_ALARM_PWM,
|
|
.tm_name = "pm8921_tz",
|
|
.irq_name_temp_stat = "pm8921_tempstat_irq",
|
|
.irq_name_over_temp = "pm8921_overtemp_irq",
|
|
};
|
|
|
|
static struct mfd_cell thermal_alarm_cell __devinitdata = {
|
|
.name = PM8XXX_TM_DEV_NAME,
|
|
.id = -1,
|
|
.resources = thermal_alarm_cell_resources,
|
|
.num_resources = ARRAY_SIZE(thermal_alarm_cell_resources),
|
|
.platform_data = &thermal_alarm_cdata,
|
|
.pdata_size = sizeof(struct pm8xxx_tm_core_data),
|
|
};
|
|
|
|
static const struct resource batt_alarm_cell_resources[] __devinitconst = {
|
|
SINGLE_IRQ_RESOURCE("pm8921_batt_alarm_irq", PM8921_BATT_ALARM_IRQ),
|
|
};
|
|
|
|
static struct pm8xxx_batt_alarm_core_data batt_alarm_cdata = {
|
|
.irq_name = "pm8921_batt_alarm_irq",
|
|
.reg_addr_threshold = REG_BATT_ALARM_THRESH,
|
|
.reg_addr_ctrl1 = REG_BATT_ALARM_CTRL1,
|
|
.reg_addr_ctrl2 = REG_BATT_ALARM_CTRL2,
|
|
.reg_addr_pwm_ctrl = REG_BATT_ALARM_PWM_CTRL,
|
|
};
|
|
|
|
static struct mfd_cell batt_alarm_cell __devinitdata = {
|
|
.name = PM8XXX_BATT_ALARM_DEV_NAME,
|
|
.id = -1,
|
|
.resources = batt_alarm_cell_resources,
|
|
.num_resources = ARRAY_SIZE(batt_alarm_cell_resources),
|
|
.platform_data = &batt_alarm_cdata,
|
|
.pdata_size = sizeof(struct pm8xxx_batt_alarm_core_data),
|
|
};
|
|
|
|
static const struct resource ccadc_cell_resources[] __devinitconst = {
|
|
SINGLE_IRQ_RESOURCE("PM8921_BMS_CCADC_EOC", PM8921_BMS_CCADC_EOC),
|
|
};
|
|
|
|
static struct mfd_cell ccadc_cell __devinitdata = {
|
|
.name = PM8XXX_CCADC_DEV_NAME,
|
|
.id = -1,
|
|
.resources = ccadc_cell_resources,
|
|
.num_resources = ARRAY_SIZE(ccadc_cell_resources),
|
|
};
|
|
|
|
static struct mfd_cell vibrator_cell __devinitdata = {
|
|
.name = PM8XXX_VIBRATOR_DEV_NAME,
|
|
.id = -1,
|
|
};
|
|
|
|
static struct pm8xxx_vreg regulator_data[] = {
|
|
/* name pc_name ctrl test hpm_min */
|
|
NLDO("8921_l1", "8921_l1_pc", 0x0AE, 0x0AF, LDO_150),
|
|
NLDO("8921_l2", "8921_l2_pc", 0x0B0, 0x0B1, LDO_150),
|
|
PLDO("8921_l3", "8921_l3_pc", 0x0B2, 0x0B3, LDO_150),
|
|
PLDO("8921_l4", "8921_l4_pc", 0x0B4, 0x0B5, LDO_50),
|
|
PLDO("8921_l5", "8921_l5_pc", 0x0B6, 0x0B7, LDO_300),
|
|
PLDO("8921_l6", "8921_l6_pc", 0x0B8, 0x0B9, LDO_600),
|
|
PLDO("8921_l7", "8921_l7_pc", 0x0BA, 0x0BB, LDO_150),
|
|
PLDO("8921_l8", "8921_l8_pc", 0x0BC, 0x0BD, LDO_300),
|
|
PLDO("8921_l9", "8921_l9_pc", 0x0BE, 0x0BF, LDO_300),
|
|
PLDO("8921_l10", "8921_l10_pc", 0x0C0, 0x0C1, LDO_600),
|
|
PLDO("8921_l11", "8921_l11_pc", 0x0C2, 0x0C3, LDO_150),
|
|
NLDO("8921_l12", "8921_l12_pc", 0x0C4, 0x0C5, LDO_150),
|
|
PLDO("8921_l14", "8921_l14_pc", 0x0C8, 0x0C9, LDO_50),
|
|
PLDO("8921_l15", "8921_l15_pc", 0x0CA, 0x0CB, LDO_150),
|
|
PLDO("8921_l16", "8921_l16_pc", 0x0CC, 0x0CD, LDO_300),
|
|
PLDO("8921_l17", "8921_l17_pc", 0x0CE, 0x0CF, LDO_150),
|
|
NLDO("8921_l18", "8921_l18_pc", 0x0D0, 0x0D1, LDO_150),
|
|
PLDO("8921_l21", "8921_l21_pc", 0x0D6, 0x0D7, LDO_150),
|
|
PLDO("8921_l22", "8921_l22_pc", 0x0D8, 0x0D9, LDO_150),
|
|
PLDO("8921_l23", "8921_l23_pc", 0x0DA, 0x0DB, LDO_150),
|
|
NLDO1200("8921_l24", 0x0DC, 0x0DD, LDO_1200),
|
|
NLDO1200("8921_l25", 0x0DE, 0x0DF, LDO_1200),
|
|
NLDO1200("8921_l26", 0x0E0, 0x0E1, LDO_1200),
|
|
NLDO1200("8921_l27", 0x0E2, 0x0E3, LDO_1200),
|
|
NLDO1200("8921_l28", 0x0E4, 0x0E5, LDO_1200),
|
|
PLDO("8921_l29", "8921_l29_pc", 0x0E6, 0x0E7, LDO_150),
|
|
|
|
/* name pc_name ctrl test2 clk sleep hpm_min */
|
|
SMPS("8921_s1", "8921_s1_pc", 0x1D0, 0x1D5, 0x009, 0x1D2, SMPS_1500),
|
|
SMPS("8921_s2", "8921_s2_pc", 0x1D8, 0x1DD, 0x00A, 0x1DA, SMPS_1500),
|
|
SMPS("8921_s3", "8921_s3_pc", 0x1E0, 0x1E5, 0x00B, 0x1E2, SMPS_1500),
|
|
SMPS("8921_s4", "8921_s4_pc", 0x1E8, 0x1ED, 0x011, 0x1EA, SMPS_1500),
|
|
|
|
/* name ctrl fts_cnfg1 pfm pwr_cnfg hpm_min */
|
|
FTSMPS("8921_s5", 0x025, 0x02E, 0x026, 0x032, SMPS_2000),
|
|
FTSMPS("8921_s6", 0x036, 0x03F, 0x037, 0x043, SMPS_2000),
|
|
|
|
/* name pc_name ctrl test2 clk sleep hpm_min */
|
|
SMPS("8921_s7", "8921_s7_pc", 0x1F0, 0x1F5, 0x012, 0x1F2, SMPS_1500),
|
|
SMPS("8921_s8", "8921_s8_pc", 0x1F8, 0x1FD, 0x013, 0x1FA, SMPS_1500),
|
|
|
|
/* name pc_name ctrl test */
|
|
VS("8921_lvs1", "8921_lvs1_pc", 0x060, 0x061),
|
|
VS300("8921_lvs2", 0x062, 0x063),
|
|
VS("8921_lvs3", "8921_lvs3_pc", 0x064, 0x065),
|
|
VS("8921_lvs4", "8921_lvs4_pc", 0x066, 0x067),
|
|
VS("8921_lvs5", "8921_lvs5_pc", 0x068, 0x069),
|
|
VS("8921_lvs6", "8921_lvs6_pc", 0x06A, 0x06B),
|
|
VS("8921_lvs7", "8921_lvs7_pc", 0x06C, 0x06D),
|
|
VS300("8921_usb_otg", 0x06E, 0x06F),
|
|
VS300("8921_hdmi_mvs", 0x070, 0x071),
|
|
|
|
/* name ctrl */
|
|
NCP("8921_ncp", 0x090),
|
|
};
|
|
|
|
/*
|
|
* PM8917 adds 6 LDOs and a boost regulator beyond those available on PM8921.
|
|
* It also replaces SMPS 3 with FTSMPS 3. PM8917 does not have an NCP.
|
|
*/
|
|
static struct pm8xxx_vreg pm8917_regulator_data[] = {
|
|
/* name pc_name ctrl test hpm_min */
|
|
PLDO("8917_l30", "8917_l30_pc", 0x0A3, 0x0A4, LDO_150),
|
|
PLDO("8917_l31", "8917_l31_pc", 0x0A5, 0x0A6, LDO_150),
|
|
PLDO("8917_l32", "8917_l32_pc", 0x0A7, 0x0A8, LDO_150),
|
|
PLDO("8917_l33", "8917_l33_pc", 0x0C6, 0x0C7, LDO_150),
|
|
PLDO("8917_l34", "8917_l34_pc", 0x0D2, 0x0D3, LDO_150),
|
|
PLDO("8917_l35", "8917_l35_pc", 0x0D4, 0x0D5, LDO_300),
|
|
PLDO("8917_l36", "8917_l36_pc", 0x0A9, 0x0AA, LDO_50),
|
|
|
|
/* name ctrl */
|
|
BOOST("8917_boost", 0x04B),
|
|
};
|
|
|
|
#define MAX_NAME_COMPARISON_LEN 32
|
|
|
|
static int __devinit match_regulator(enum pm8xxx_version version,
|
|
struct pm8xxx_regulator_core_platform_data *core_data, const char *name)
|
|
{
|
|
int found = 0;
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(regulator_data); i++) {
|
|
if (regulator_data[i].rdesc.name
|
|
&& strncmp(regulator_data[i].rdesc.name, name,
|
|
MAX_NAME_COMPARISON_LEN) == 0) {
|
|
core_data->is_pin_controlled = false;
|
|
core_data->vreg = ®ulator_data[i];
|
|
found = 1;
|
|
break;
|
|
} else if (regulator_data[i].rdesc_pc.name
|
|
&& strncmp(regulator_data[i].rdesc_pc.name, name,
|
|
MAX_NAME_COMPARISON_LEN) == 0) {
|
|
core_data->is_pin_controlled = true;
|
|
core_data->vreg = ®ulator_data[i];
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (version == PM8XXX_VERSION_8917) {
|
|
for (i = 0; i < ARRAY_SIZE(pm8917_regulator_data); i++) {
|
|
if (pm8917_regulator_data[i].rdesc.name
|
|
&& strncmp(pm8917_regulator_data[i].rdesc.name,
|
|
name, MAX_NAME_COMPARISON_LEN) == 0) {
|
|
core_data->is_pin_controlled = false;
|
|
core_data->vreg = &pm8917_regulator_data[i];
|
|
found = 1;
|
|
break;
|
|
} else if (pm8917_regulator_data[i].rdesc_pc.name
|
|
&& strncmp(pm8917_regulator_data[i].rdesc_pc.name,
|
|
name, MAX_NAME_COMPARISON_LEN) == 0) {
|
|
core_data->is_pin_controlled = true;
|
|
core_data->vreg = &pm8917_regulator_data[i];
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
pr_err("could not find a match for regulator: %s\n", name);
|
|
|
|
return found;
|
|
}
|
|
|
|
static int __devinit
|
|
pm8921_add_regulators(const struct pm8921_platform_data *pdata,
|
|
struct pm8921 *pmic, int irq_base)
|
|
{
|
|
int ret = 0;
|
|
struct mfd_cell *mfd_regulators;
|
|
struct pm8xxx_regulator_core_platform_data *cdata;
|
|
enum pm8xxx_version version;
|
|
int i;
|
|
|
|
version = pm8xxx_get_version(pmic->dev);
|
|
|
|
/* Add one device for each regulator used by the board. */
|
|
mfd_regulators = kzalloc(sizeof(struct mfd_cell)
|
|
* (pdata->num_regulators), GFP_KERNEL);
|
|
if (!mfd_regulators) {
|
|
pr_err("Cannot allocate %d bytes for pm8921 regulator "
|
|
"mfd cells\n", sizeof(struct mfd_cell)
|
|
* (pdata->num_regulators));
|
|
return -ENOMEM;
|
|
}
|
|
cdata = kzalloc(sizeof(struct pm8xxx_regulator_core_platform_data)
|
|
* pdata->num_regulators, GFP_KERNEL);
|
|
if (!cdata) {
|
|
pr_err("Cannot allocate %d bytes for pm8921 regulator "
|
|
"core data\n", pdata->num_regulators
|
|
* sizeof(struct pm8xxx_regulator_core_platform_data));
|
|
kfree(mfd_regulators);
|
|
return -ENOMEM;
|
|
}
|
|
for (i = 0; i < ARRAY_SIZE(regulator_data); i++)
|
|
mutex_init(®ulator_data[i].pc_lock);
|
|
for (i = 0; i < ARRAY_SIZE(pm8917_regulator_data); i++)
|
|
mutex_init(&pm8917_regulator_data[i].pc_lock);
|
|
|
|
for (i = 0; i < pdata->num_regulators; i++) {
|
|
if (!pdata->regulator_pdatas[i].init_data.constraints.name) {
|
|
pr_err("name missing for regulator %d\n", i);
|
|
ret = -EINVAL;
|
|
goto bail;
|
|
}
|
|
if (!match_regulator(version, &cdata[i],
|
|
pdata->regulator_pdatas[i].init_data.constraints.name)) {
|
|
ret = -ENODEV;
|
|
goto bail;
|
|
}
|
|
cdata[i].pdata = &(pdata->regulator_pdatas[i]);
|
|
mfd_regulators[i].name = PM8XXX_REGULATOR_DEV_NAME;
|
|
mfd_regulators[i].id = cdata[i].pdata->id;
|
|
mfd_regulators[i].platform_data = &cdata[i];
|
|
mfd_regulators[i].pdata_size =
|
|
sizeof(struct pm8xxx_regulator_core_platform_data);
|
|
}
|
|
ret = mfd_add_devices(pmic->dev, 0, mfd_regulators,
|
|
pdata->num_regulators, NULL, irq_base);
|
|
if (ret)
|
|
goto bail;
|
|
|
|
pmic->mfd_regulators = mfd_regulators;
|
|
pmic->regulator_cdata = cdata;
|
|
return ret;
|
|
|
|
bail:
|
|
for (i = 0; i < ARRAY_SIZE(regulator_data); i++)
|
|
mutex_destroy(®ulator_data[i].pc_lock);
|
|
for (i = 0; i < ARRAY_SIZE(pm8917_regulator_data); i++)
|
|
mutex_destroy(&pm8917_regulator_data[i].pc_lock);
|
|
kfree(mfd_regulators);
|
|
kfree(cdata);
|
|
return ret;
|
|
}
|
|
|
|
static int __devinit
|
|
pm8921_add_subdevices(const struct pm8921_platform_data *pdata,
|
|
struct pm8921 *pmic)
|
|
{
|
|
int ret = 0, irq_base = 0;
|
|
struct pm_irq_chip *irq_chip;
|
|
enum pm8xxx_version version;
|
|
|
|
version = pm8xxx_get_version(pmic->dev);
|
|
|
|
if (pdata->irq_pdata) {
|
|
pdata->irq_pdata->irq_cdata.nirqs = PM8921_NR_IRQS;
|
|
pdata->irq_pdata->irq_cdata.base_addr = REG_IRQ_BASE;
|
|
irq_base = pdata->irq_pdata->irq_base;
|
|
irq_chip = pm8xxx_irq_init(pmic->dev, pdata->irq_pdata);
|
|
|
|
if (IS_ERR(irq_chip)) {
|
|
pr_err("Failed to init interrupts ret=%ld\n",
|
|
PTR_ERR(irq_chip));
|
|
return PTR_ERR(irq_chip);
|
|
}
|
|
pmic->irq_chip = irq_chip;
|
|
}
|
|
|
|
if (pdata->gpio_pdata) {
|
|
if (version == PM8XXX_VERSION_8917) {
|
|
gpio_cell_resources[0].end = gpio_cell_resources[0].end
|
|
+ PM8917_NR_GPIOS
|
|
- PM8921_NR_GPIOS;
|
|
pdata->gpio_pdata->gpio_cdata.ngpios = PM8917_NR_GPIOS;
|
|
} else {
|
|
pdata->gpio_pdata->gpio_cdata.ngpios = PM8921_NR_GPIOS;
|
|
}
|
|
gpio_cell.platform_data = pdata->gpio_pdata;
|
|
gpio_cell.pdata_size = sizeof(struct pm8xxx_gpio_platform_data);
|
|
ret = mfd_add_devices(pmic->dev, 0, &gpio_cell, 1,
|
|
NULL, irq_base);
|
|
if (ret) {
|
|
pr_err("Failed to add gpio subdevice ret=%d\n", ret);
|
|
goto bail;
|
|
}
|
|
}
|
|
|
|
if (pdata->mpp_pdata) {
|
|
if (version == PM8XXX_VERSION_8917) {
|
|
mpp_cell_resources[0].end = mpp_cell_resources[0].end
|
|
+ PM8917_NR_MPPS
|
|
- PM8921_NR_MPPS;
|
|
pdata->mpp_pdata->core_data.nmpps = PM8917_NR_MPPS;
|
|
} else {
|
|
pdata->mpp_pdata->core_data.nmpps = PM8921_NR_MPPS;
|
|
}
|
|
pdata->mpp_pdata->core_data.base_addr = REG_MPP_BASE;
|
|
mpp_cell.platform_data = pdata->mpp_pdata;
|
|
mpp_cell.pdata_size = sizeof(struct pm8xxx_mpp_platform_data);
|
|
ret = mfd_add_devices(pmic->dev, 0, &mpp_cell, 1, NULL,
|
|
irq_base);
|
|
if (ret) {
|
|
pr_err("Failed to add mpp subdevice ret=%d\n", ret);
|
|
goto bail;
|
|
}
|
|
}
|
|
|
|
if (pdata->rtc_pdata) {
|
|
rtc_cell.platform_data = pdata->rtc_pdata;
|
|
rtc_cell.pdata_size = sizeof(struct pm8xxx_rtc_platform_data);
|
|
ret = mfd_add_devices(pmic->dev, 0, &rtc_cell, 1, NULL,
|
|
irq_base);
|
|
if (ret) {
|
|
pr_err("Failed to add rtc subdevice ret=%d\n", ret);
|
|
goto bail;
|
|
}
|
|
}
|
|
|
|
if (pdata->pwrkey_pdata) {
|
|
pwrkey_cell.platform_data = pdata->pwrkey_pdata;
|
|
pwrkey_cell.pdata_size =
|
|
sizeof(struct pm8xxx_pwrkey_platform_data);
|
|
ret = mfd_add_devices(pmic->dev, 0, &pwrkey_cell, 1, NULL,
|
|
irq_base);
|
|
if (ret) {
|
|
pr_err("Failed to add pwrkey subdevice ret=%d\n", ret);
|
|
goto bail;
|
|
}
|
|
}
|
|
|
|
if (pdata->keypad_pdata) {
|
|
keypad_cell.platform_data = pdata->keypad_pdata;
|
|
keypad_cell.pdata_size =
|
|
sizeof(struct pm8xxx_keypad_platform_data);
|
|
ret = mfd_add_devices(pmic->dev, 0, &keypad_cell, 1, NULL,
|
|
irq_base);
|
|
if (ret) {
|
|
pr_err("Failed to add keypad subdevice ret=%d\n", ret);
|
|
goto bail;
|
|
}
|
|
}
|
|
|
|
if (pdata->charger_pdata) {
|
|
pdata->charger_pdata->charger_cdata.vbat_channel = CHANNEL_VBAT;
|
|
pdata->charger_pdata->charger_cdata.batt_temp_channel
|
|
= CHANNEL_BATT_THERM;
|
|
pdata->charger_pdata->charger_cdata.batt_id_channel
|
|
= CHANNEL_BATT_ID;
|
|
charger_cell.platform_data = pdata->charger_pdata;
|
|
charger_cell.pdata_size =
|
|
sizeof(struct pm8921_charger_platform_data);
|
|
ret = mfd_add_devices(pmic->dev, 0, &charger_cell, 1, NULL,
|
|
irq_base);
|
|
if (ret) {
|
|
pr_err("Failed to add charger subdevice ret=%d\n", ret);
|
|
goto bail;
|
|
}
|
|
}
|
|
|
|
if (pdata->adc_pdata) {
|
|
adc_cell.platform_data = pdata->adc_pdata;
|
|
adc_cell.pdata_size =
|
|
sizeof(struct pm8xxx_adc_platform_data);
|
|
ret = mfd_add_devices(pmic->dev, 0, &adc_cell, 1, NULL,
|
|
irq_base);
|
|
if (ret) {
|
|
pr_err("Failed to add regulator subdevices ret=%d\n",
|
|
ret);
|
|
}
|
|
}
|
|
|
|
if (pdata->bms_pdata) {
|
|
pdata->bms_pdata->bms_cdata.batt_temp_channel
|
|
= CHANNEL_BATT_THERM;
|
|
pdata->bms_pdata->bms_cdata.vbat_channel = CHANNEL_VBAT;
|
|
pdata->bms_pdata->bms_cdata.ref625mv_channel = CHANNEL_625MV;
|
|
pdata->bms_pdata->bms_cdata.ref1p25v_channel = CHANNEL_125V;
|
|
pdata->bms_pdata->bms_cdata.batt_id_channel = CHANNEL_BATT_ID;
|
|
bms_cell.platform_data = pdata->bms_pdata;
|
|
bms_cell.pdata_size = sizeof(struct pm8921_bms_platform_data);
|
|
ret = mfd_add_devices(pmic->dev, 0, &bms_cell, 1, NULL,
|
|
irq_base);
|
|
if (ret) {
|
|
pr_err("Failed to add bms subdevice ret=%d\n", ret);
|
|
goto bail;
|
|
}
|
|
}
|
|
|
|
if (pdata->num_regulators > 0 && pdata->regulator_pdatas) {
|
|
ret = pm8921_add_regulators(pdata, pmic, irq_base);
|
|
if (ret) {
|
|
pr_err("Failed to add regulator subdevices ret=%d\n",
|
|
ret);
|
|
goto bail;
|
|
}
|
|
}
|
|
|
|
ret = mfd_add_devices(pmic->dev, 0, &debugfs_cell, 1, NULL, irq_base);
|
|
if (ret) {
|
|
pr_err("Failed to add debugfs subdevice ret=%d\n", ret);
|
|
goto bail;
|
|
}
|
|
|
|
if (pdata->misc_pdata) {
|
|
misc_cell.platform_data = pdata->misc_pdata;
|
|
misc_cell.pdata_size = sizeof(struct pm8xxx_misc_platform_data);
|
|
ret = mfd_add_devices(pmic->dev, 0, &misc_cell, 1, NULL,
|
|
irq_base);
|
|
if (ret) {
|
|
pr_err("Failed to add misc subdevice ret=%d\n", ret);
|
|
goto bail;
|
|
}
|
|
}
|
|
|
|
ret = mfd_add_devices(pmic->dev, 0, &thermal_alarm_cell, 1, NULL,
|
|
irq_base);
|
|
if (ret) {
|
|
pr_err("Failed to add thermal alarm subdevice ret=%d\n",
|
|
ret);
|
|
goto bail;
|
|
}
|
|
|
|
ret = mfd_add_devices(pmic->dev, 0, &batt_alarm_cell, 1, NULL,
|
|
irq_base);
|
|
if (ret) {
|
|
pr_err("Failed to add battery alarm subdevice ret=%d\n",
|
|
ret);
|
|
goto bail;
|
|
}
|
|
|
|
if (version != PM8XXX_VERSION_8917) {
|
|
if (pdata->pwm_pdata) {
|
|
pwm_cell.platform_data = pdata->pwm_pdata;
|
|
pwm_cell.pdata_size =
|
|
sizeof(struct pm8xxx_pwm_platform_data);
|
|
}
|
|
ret = mfd_add_devices(pmic->dev, 0, &pwm_cell, 1, NULL, 0);
|
|
if (ret) {
|
|
pr_err("Failed to add pwm subdevice ret=%d\n", ret);
|
|
goto bail;
|
|
}
|
|
|
|
if (pdata->leds_pdata) {
|
|
leds_cell.platform_data = pdata->leds_pdata;
|
|
leds_cell.pdata_size =
|
|
sizeof(struct pm8xxx_led_platform_data);
|
|
ret = mfd_add_devices(pmic->dev, 0, &leds_cell,
|
|
1, NULL, 0);
|
|
if (ret) {
|
|
pr_err("Failed to add leds subdevice ret=%d\n",
|
|
ret);
|
|
goto bail;
|
|
}
|
|
}
|
|
|
|
if (pdata->vibrator_pdata) {
|
|
vibrator_cell.platform_data = pdata->vibrator_pdata;
|
|
vibrator_cell.pdata_size =
|
|
sizeof(struct pm8xxx_vibrator_platform_data);
|
|
ret = mfd_add_devices(pmic->dev, 0, &vibrator_cell,
|
|
1, NULL, 0);
|
|
if (ret) {
|
|
pr_err("Failed to add vibrator ret=%d\n", ret);
|
|
goto bail;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pdata->ccadc_pdata) {
|
|
pdata->ccadc_pdata->ccadc_cdata.batt_temp_channel
|
|
= CHANNEL_BATT_THERM;
|
|
ccadc_cell.platform_data = pdata->ccadc_pdata;
|
|
ccadc_cell.pdata_size =
|
|
sizeof(struct pm8xxx_ccadc_platform_data);
|
|
|
|
ret = mfd_add_devices(pmic->dev, 0, &ccadc_cell, 1, NULL,
|
|
irq_base);
|
|
if (ret) {
|
|
pr_err("Failed to add ccadc subdevice ret=%d\n", ret);
|
|
goto bail;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
bail:
|
|
if (pmic->irq_chip) {
|
|
pm8xxx_irq_exit(pmic->irq_chip);
|
|
pmic->irq_chip = NULL;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static const char * const pm8921_rev_names[] = {
|
|
[PM8XXX_REVISION_8921_TEST] = "test",
|
|
[PM8XXX_REVISION_8921_1p0] = "1.0",
|
|
[PM8XXX_REVISION_8921_1p1] = "1.1",
|
|
[PM8XXX_REVISION_8921_2p0] = "2.0",
|
|
[PM8XXX_REVISION_8921_3p0] = "3.0",
|
|
[PM8XXX_REVISION_8921_3p1] = "3.1",
|
|
};
|
|
|
|
static const char * const pm8922_rev_names[] = {
|
|
[PM8XXX_REVISION_8922_TEST] = "test",
|
|
[PM8XXX_REVISION_8922_1p0] = "1.0",
|
|
[PM8XXX_REVISION_8922_1p1] = "1.1",
|
|
[PM8XXX_REVISION_8922_2p0] = "2.0",
|
|
};
|
|
|
|
static const char * const pm8917_rev_names[] = {
|
|
[PM8XXX_REVISION_8917_TEST] = "test",
|
|
[PM8XXX_REVISION_8917_1p0] = "1.0",
|
|
};
|
|
|
|
static int __devinit pm8921_probe(struct platform_device *pdev)
|
|
{
|
|
const struct pm8921_platform_data *pdata = pdev->dev.platform_data;
|
|
const char *revision_name = "unknown";
|
|
struct pm8921 *pmic;
|
|
enum pm8xxx_version version;
|
|
int revision;
|
|
int rc;
|
|
u8 val;
|
|
|
|
if (!pdata) {
|
|
pr_err("missing platform data\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
pmic = kzalloc(sizeof(struct pm8921), GFP_KERNEL);
|
|
if (!pmic) {
|
|
pr_err("Cannot alloc pm8921 struct\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* Read PMIC chip revision */
|
|
rc = msm_ssbi_read(pdev->dev.parent, REG_HWREV, &val, sizeof(val));
|
|
if (rc) {
|
|
pr_err("Failed to read hw rev reg %d:rc=%d\n", REG_HWREV, rc);
|
|
goto err_read_rev;
|
|
}
|
|
pr_info("PMIC revision 1: %02X\n", val);
|
|
pmic->rev_registers = val;
|
|
|
|
/* Read PMIC chip revision 2 */
|
|
rc = msm_ssbi_read(pdev->dev.parent, REG_HWREV_2, &val, sizeof(val));
|
|
if (rc) {
|
|
pr_err("Failed to read hw rev 2 reg %d:rc=%d\n",
|
|
REG_HWREV_2, rc);
|
|
goto err_read_rev;
|
|
}
|
|
pr_info("PMIC revision 2: %02X\n", val);
|
|
pmic->rev_registers |= val << BITS_PER_BYTE;
|
|
|
|
pmic->dev = &pdev->dev;
|
|
pm8921_drvdata.pm_chip_data = pmic;
|
|
platform_set_drvdata(pdev, &pm8921_drvdata);
|
|
|
|
/* Print out human readable version and revision names. */
|
|
version = pm8xxx_get_version(pmic->dev);
|
|
revision = pm8xxx_get_revision(pmic->dev);
|
|
if (version == PM8XXX_VERSION_8921) {
|
|
if (revision >= 0 && revision < ARRAY_SIZE(pm8921_rev_names))
|
|
revision_name = pm8921_rev_names[revision];
|
|
pr_info("PMIC version: PM8921 rev %s\n", revision_name);
|
|
} else if (version == PM8XXX_VERSION_8922) {
|
|
if (revision >= 0 && revision < ARRAY_SIZE(pm8922_rev_names))
|
|
revision_name = pm8922_rev_names[revision];
|
|
pr_info("PMIC version: PM8922 rev %s\n", revision_name);
|
|
} else if (version == PM8XXX_VERSION_8917) {
|
|
if (revision >= 0 && revision < ARRAY_SIZE(pm8917_rev_names))
|
|
revision_name = pm8917_rev_names[revision];
|
|
pr_info("PMIC version: PM8917 rev %s\n", revision_name);
|
|
} else {
|
|
WARN_ON(version != PM8XXX_VERSION_8921
|
|
&& version != PM8XXX_VERSION_8922
|
|
&& version != PM8XXX_VERSION_8917);
|
|
}
|
|
|
|
/* Log human readable restart reason */
|
|
rc = msm_ssbi_read(pdev->dev.parent, REG_PM8921_PON_CNTRL_3, &val, 1);
|
|
if (rc) {
|
|
pr_err("Cannot read restart reason rc=%d\n", rc);
|
|
goto err_read_rev;
|
|
}
|
|
val &= PM8XXX_RESTART_REASON_MASK;
|
|
pr_info("PMIC Restart Reason: %s\n", pm8xxx_restart_reason_str[val]);
|
|
pmic->restart_reason = val;
|
|
|
|
rc = pm8921_add_subdevices(pdata, pmic);
|
|
if (rc) {
|
|
pr_err("Cannot add subdevices rc=%d\n", rc);
|
|
goto err;
|
|
}
|
|
|
|
/* gpio might not work if no irq device is found */
|
|
WARN_ON(pmic->irq_chip == NULL);
|
|
|
|
return 0;
|
|
|
|
err:
|
|
mfd_remove_devices(pmic->dev);
|
|
platform_set_drvdata(pdev, NULL);
|
|
kfree(pmic->mfd_regulators);
|
|
kfree(pmic->regulator_cdata);
|
|
err_read_rev:
|
|
kfree(pmic);
|
|
return rc;
|
|
}
|
|
|
|
static int __devexit pm8921_remove(struct platform_device *pdev)
|
|
{
|
|
struct pm8xxx_drvdata *drvdata;
|
|
struct pm8921 *pmic = NULL;
|
|
int i;
|
|
|
|
drvdata = platform_get_drvdata(pdev);
|
|
if (drvdata)
|
|
pmic = drvdata->pm_chip_data;
|
|
if (pmic) {
|
|
if (pmic->dev)
|
|
mfd_remove_devices(pmic->dev);
|
|
if (pmic->irq_chip)
|
|
pm8xxx_irq_exit(pmic->irq_chip);
|
|
if (pmic->mfd_regulators) {
|
|
for (i = 0; i < ARRAY_SIZE(regulator_data); i++)
|
|
mutex_destroy(®ulator_data[i].pc_lock);
|
|
for (i = 0; i < ARRAY_SIZE(pm8917_regulator_data); i++)
|
|
mutex_destroy(
|
|
&pm8917_regulator_data[i].pc_lock);
|
|
}
|
|
kfree(pmic->mfd_regulators);
|
|
kfree(pmic->regulator_cdata);
|
|
kfree(pmic);
|
|
}
|
|
platform_set_drvdata(pdev, NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct platform_driver pm8921_driver = {
|
|
.probe = pm8921_probe,
|
|
.remove = __devexit_p(pm8921_remove),
|
|
.driver = {
|
|
.name = "pm8921-core",
|
|
.owner = THIS_MODULE,
|
|
},
|
|
};
|
|
|
|
static int __init pm8921_init(void)
|
|
{
|
|
return platform_driver_register(&pm8921_driver);
|
|
}
|
|
postcore_initcall(pm8921_init);
|
|
|
|
static void __exit pm8921_exit(void)
|
|
{
|
|
platform_driver_unregister(&pm8921_driver);
|
|
}
|
|
module_exit(pm8921_exit);
|
|
|
|
MODULE_LICENSE("GPL v2");
|
|
MODULE_DESCRIPTION("PMIC 8921 core driver");
|
|
MODULE_VERSION("1.0");
|
|
MODULE_ALIAS("platform:pm8921-core");
|