forked from Minki/linux
ACPI / PMIC: Add opregion driver for Intel BXT WhiskeyCove PMIC
This patch adds operation region driver for Intel BXT WhiskeyCove PMIC. The register mapping is done as per the BXT WC data sheet. Signed-off-by: Ajay Thomas <ajay.thomas.david.rajamanickam@intel.com> Signed-off-by: Bin Gao <bin.gao@intel.com> Reviewed-by: Aaron Lu <aaron.lu@intel.com> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
This commit is contained in:
parent
d8ba819124
commit
9b928c78bb
@ -519,6 +519,12 @@ config XPOWER_PMIC_OPREGION
|
||||
help
|
||||
This config adds ACPI operation region support for XPower AXP288 PMIC.
|
||||
|
||||
config BXT_WC_PMIC_OPREGION
|
||||
bool "ACPI operation region support for BXT WhiskeyCove PMIC"
|
||||
depends on INTEL_SOC_PMIC
|
||||
help
|
||||
This config adds ACPI operation region support for BXT WhiskeyCove PMIC.
|
||||
|
||||
endif
|
||||
|
||||
endif # ACPI
|
||||
|
@ -99,5 +99,6 @@ obj-$(CONFIG_ACPI_EXTLOG) += acpi_extlog.o
|
||||
obj-$(CONFIG_PMIC_OPREGION) += pmic/intel_pmic.o
|
||||
obj-$(CONFIG_CRC_PMIC_OPREGION) += pmic/intel_pmic_crc.o
|
||||
obj-$(CONFIG_XPOWER_PMIC_OPREGION) += pmic/intel_pmic_xpower.o
|
||||
obj-$(CONFIG_BXT_WC_PMIC_OPREGION) += pmic/intel_pmic_bxtwc.o
|
||||
|
||||
video-objs += acpi_video.o video_detect.o
|
||||
|
424
drivers/acpi/pmic/intel_pmic_bxtwc.c
Normal file
424
drivers/acpi/pmic/intel_pmic_bxtwc.c
Normal file
@ -0,0 +1,424 @@
|
||||
/*
|
||||
* intel_pmic_bxtwc.c - Intel BXT WhiskeyCove PMIC operation region driver
|
||||
*
|
||||
* Copyright (C) 2015 Intel Corporation. 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 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/acpi.h>
|
||||
#include <linux/mfd/intel_soc_pmic.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include "intel_pmic.h"
|
||||
|
||||
#define WHISKEY_COVE_ALRT_HIGH_BIT_MASK 0x0F
|
||||
#define WHISKEY_COVE_ADC_HIGH_BIT(x) (((x & 0x0F) << 8))
|
||||
#define WHISKEY_COVE_ADC_CURSRC(x) (((x & 0xF0) >> 4))
|
||||
#define VR_MODE_DISABLED 0
|
||||
#define VR_MODE_AUTO BIT(0)
|
||||
#define VR_MODE_NORMAL BIT(1)
|
||||
#define VR_MODE_SWITCH BIT(2)
|
||||
#define VR_MODE_ECO (BIT(0)|BIT(1))
|
||||
#define VSWITCH2_OUTPUT BIT(5)
|
||||
#define VSWITCH1_OUTPUT BIT(4)
|
||||
#define VUSBPHY_CHARGE BIT(1)
|
||||
|
||||
static struct pmic_table power_table[] = {
|
||||
{
|
||||
.address = 0x0,
|
||||
.reg = 0x63,
|
||||
.bit = VR_MODE_AUTO,
|
||||
}, /* VDD1 -> VDD1CNT */
|
||||
{
|
||||
.address = 0x04,
|
||||
.reg = 0x65,
|
||||
.bit = VR_MODE_AUTO,
|
||||
}, /* VDD2 -> VDD2CNT */
|
||||
{
|
||||
.address = 0x08,
|
||||
.reg = 0x67,
|
||||
.bit = VR_MODE_AUTO,
|
||||
}, /* VDD3 -> VDD3CNT */
|
||||
{
|
||||
.address = 0x0c,
|
||||
.reg = 0x6d,
|
||||
.bit = VR_MODE_AUTO,
|
||||
}, /* VLFX -> VFLEXCNT */
|
||||
{
|
||||
.address = 0x10,
|
||||
.reg = 0x6f,
|
||||
.bit = VR_MODE_NORMAL,
|
||||
}, /* VP1A -> VPROG1ACNT */
|
||||
{
|
||||
.address = 0x14,
|
||||
.reg = 0x70,
|
||||
.bit = VR_MODE_NORMAL,
|
||||
}, /* VP1B -> VPROG1BCNT */
|
||||
{
|
||||
.address = 0x18,
|
||||
.reg = 0x71,
|
||||
.bit = VR_MODE_NORMAL,
|
||||
}, /* VP1C -> VPROG1CCNT */
|
||||
{
|
||||
.address = 0x1c,
|
||||
.reg = 0x72,
|
||||
.bit = VR_MODE_NORMAL,
|
||||
}, /* VP1D -> VPROG1DCNT */
|
||||
{
|
||||
.address = 0x20,
|
||||
.reg = 0x73,
|
||||
.bit = VR_MODE_NORMAL,
|
||||
}, /* VP2A -> VPROG2ACNT */
|
||||
{
|
||||
.address = 0x24,
|
||||
.reg = 0x74,
|
||||
.bit = VR_MODE_NORMAL,
|
||||
}, /* VP2B -> VPROG2BCNT */
|
||||
{
|
||||
.address = 0x28,
|
||||
.reg = 0x75,
|
||||
.bit = VR_MODE_NORMAL,
|
||||
}, /* VP2C -> VPROG2CCNT */
|
||||
{
|
||||
.address = 0x2c,
|
||||
.reg = 0x76,
|
||||
.bit = VR_MODE_NORMAL,
|
||||
}, /* VP3A -> VPROG3ACNT */
|
||||
{
|
||||
.address = 0x30,
|
||||
.reg = 0x77,
|
||||
.bit = VR_MODE_NORMAL,
|
||||
}, /* VP3B -> VPROG3BCNT */
|
||||
{
|
||||
.address = 0x34,
|
||||
.reg = 0x78,
|
||||
.bit = VSWITCH2_OUTPUT,
|
||||
}, /* VSW2 -> VLD0CNT Bit 5*/
|
||||
{
|
||||
.address = 0x38,
|
||||
.reg = 0x78,
|
||||
.bit = VSWITCH1_OUTPUT,
|
||||
}, /* VSW1 -> VLD0CNT Bit 4 */
|
||||
{
|
||||
.address = 0x3c,
|
||||
.reg = 0x78,
|
||||
.bit = VUSBPHY_CHARGE,
|
||||
}, /* VUPY -> VLDOCNT Bit 1 */
|
||||
{
|
||||
.address = 0x40,
|
||||
.reg = 0x7b,
|
||||
.bit = VR_MODE_NORMAL,
|
||||
}, /* VRSO -> VREFSOCCNT*/
|
||||
{
|
||||
.address = 0x44,
|
||||
.reg = 0xA0,
|
||||
.bit = VR_MODE_NORMAL,
|
||||
}, /* VP1E -> VPROG1ECNT */
|
||||
{
|
||||
.address = 0x48,
|
||||
.reg = 0xA1,
|
||||
.bit = VR_MODE_NORMAL,
|
||||
}, /* VP1F -> VPROG1FCNT */
|
||||
{
|
||||
.address = 0x4c,
|
||||
.reg = 0xA2,
|
||||
.bit = VR_MODE_NORMAL,
|
||||
}, /* VP2D -> VPROG2DCNT */
|
||||
{
|
||||
.address = 0x50,
|
||||
.reg = 0xA3,
|
||||
.bit = VR_MODE_NORMAL,
|
||||
}, /* VP4A -> VPROG4ACNT */
|
||||
{
|
||||
.address = 0x54,
|
||||
.reg = 0xA4,
|
||||
.bit = VR_MODE_NORMAL,
|
||||
}, /* VP4B -> VPROG4BCNT */
|
||||
{
|
||||
.address = 0x58,
|
||||
.reg = 0xA5,
|
||||
.bit = VR_MODE_NORMAL,
|
||||
}, /* VP4C -> VPROG4CCNT */
|
||||
{
|
||||
.address = 0x5c,
|
||||
.reg = 0xA6,
|
||||
.bit = VR_MODE_NORMAL,
|
||||
}, /* VP4D -> VPROG4DCNT */
|
||||
{
|
||||
.address = 0x60,
|
||||
.reg = 0xA7,
|
||||
.bit = VR_MODE_NORMAL,
|
||||
}, /* VP5A -> VPROG5ACNT */
|
||||
{
|
||||
.address = 0x64,
|
||||
.reg = 0xA8,
|
||||
.bit = VR_MODE_NORMAL,
|
||||
}, /* VP5B -> VPROG5BCNT */
|
||||
{
|
||||
.address = 0x68,
|
||||
.reg = 0xA9,
|
||||
.bit = VR_MODE_NORMAL,
|
||||
}, /* VP6A -> VPROG6ACNT */
|
||||
{
|
||||
.address = 0x6c,
|
||||
.reg = 0xAA,
|
||||
.bit = VR_MODE_NORMAL,
|
||||
}, /* VP6B -> VPROG6BCNT */
|
||||
{
|
||||
.address = 0x70,
|
||||
.reg = 0x36,
|
||||
.bit = BIT(2),
|
||||
}, /* SDWN_N -> MODEMCTRL Bit 2 */
|
||||
{
|
||||
.address = 0x74,
|
||||
.reg = 0x36,
|
||||
.bit = BIT(0),
|
||||
} /* MOFF -> MODEMCTRL Bit 0 */
|
||||
};
|
||||
|
||||
static struct pmic_table thermal_table[] = {
|
||||
{
|
||||
.address = 0x00,
|
||||
.reg = 0x4F39
|
||||
},
|
||||
{
|
||||
.address = 0x04,
|
||||
.reg = 0x4F24
|
||||
},
|
||||
{
|
||||
.address = 0x08,
|
||||
.reg = 0x4F26
|
||||
},
|
||||
{
|
||||
.address = 0x0c,
|
||||
.reg = 0x4F3B
|
||||
},
|
||||
{
|
||||
.address = 0x10,
|
||||
.reg = 0x4F28
|
||||
},
|
||||
{
|
||||
.address = 0x14,
|
||||
.reg = 0x4F2A
|
||||
},
|
||||
{
|
||||
.address = 0x18,
|
||||
.reg = 0x4F3D
|
||||
},
|
||||
{
|
||||
.address = 0x1c,
|
||||
.reg = 0x4F2C
|
||||
},
|
||||
{
|
||||
.address = 0x20,
|
||||
.reg = 0x4F2E
|
||||
},
|
||||
{
|
||||
.address = 0x24,
|
||||
.reg = 0x4F3F
|
||||
},
|
||||
{
|
||||
.address = 0x28,
|
||||
.reg = 0x4F30
|
||||
},
|
||||
{
|
||||
.address = 0x30,
|
||||
.reg = 0x4F41
|
||||
},
|
||||
{
|
||||
.address = 0x34,
|
||||
.reg = 0x4F32
|
||||
},
|
||||
{
|
||||
.address = 0x3c,
|
||||
.reg = 0x4F43
|
||||
},
|
||||
{
|
||||
.address = 0x40,
|
||||
.reg = 0x4F34
|
||||
},
|
||||
{
|
||||
.address = 0x48,
|
||||
.reg = 0x4F6A,
|
||||
.bit = 0,
|
||||
},
|
||||
{
|
||||
.address = 0x4C,
|
||||
.reg = 0x4F6A,
|
||||
.bit = 1
|
||||
},
|
||||
{
|
||||
.address = 0x50,
|
||||
.reg = 0x4F6A,
|
||||
.bit = 2
|
||||
},
|
||||
{
|
||||
.address = 0x54,
|
||||
.reg = 0x4F6A,
|
||||
.bit = 4
|
||||
},
|
||||
{
|
||||
.address = 0x58,
|
||||
.reg = 0x4F6A,
|
||||
.bit = 5
|
||||
},
|
||||
{
|
||||
.address = 0x5C,
|
||||
.reg = 0x4F6A,
|
||||
.bit = 3
|
||||
},
|
||||
};
|
||||
|
||||
static int intel_bxtwc_pmic_get_power(struct regmap *regmap, int reg,
|
||||
int bit, u64 *value)
|
||||
{
|
||||
int data;
|
||||
|
||||
if (regmap_read(regmap, reg, &data))
|
||||
return -EIO;
|
||||
|
||||
*value = (data & bit) ? 1 : 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_bxtwc_pmic_update_power(struct regmap *regmap, int reg,
|
||||
int bit, bool on)
|
||||
{
|
||||
u8 val, mask = bit;
|
||||
|
||||
if (on)
|
||||
val = 0xFF;
|
||||
else
|
||||
val = 0x0;
|
||||
|
||||
return regmap_update_bits(regmap, reg, mask, val);
|
||||
}
|
||||
|
||||
static int intel_bxtwc_pmic_get_raw_temp(struct regmap *regmap, int reg)
|
||||
{
|
||||
unsigned int val, adc_val, reg_val;
|
||||
u8 temp_l, temp_h, cursrc;
|
||||
unsigned long rlsb;
|
||||
static const unsigned long rlsb_array[] = {
|
||||
0, 260420, 130210, 65100, 32550, 16280,
|
||||
8140, 4070, 2030, 0, 260420, 130210 };
|
||||
|
||||
if (regmap_read(regmap, reg, &val))
|
||||
return -EIO;
|
||||
temp_l = (u8) val;
|
||||
|
||||
if (regmap_read(regmap, (reg - 1), &val))
|
||||
return -EIO;
|
||||
temp_h = (u8) val;
|
||||
|
||||
reg_val = temp_l | WHISKEY_COVE_ADC_HIGH_BIT(temp_h);
|
||||
cursrc = WHISKEY_COVE_ADC_CURSRC(temp_h);
|
||||
rlsb = rlsb_array[cursrc];
|
||||
adc_val = reg_val * rlsb / 1000;
|
||||
|
||||
return adc_val;
|
||||
}
|
||||
|
||||
static int
|
||||
intel_bxtwc_pmic_update_aux(struct regmap *regmap, int reg, int raw)
|
||||
{
|
||||
u32 bsr_num;
|
||||
u16 resi_val, count = 0, thrsh = 0;
|
||||
u8 alrt_h, alrt_l, cursel = 0;
|
||||
|
||||
bsr_num = raw;
|
||||
bsr_num /= (1 << 5);
|
||||
|
||||
count = fls(bsr_num) - 1;
|
||||
|
||||
cursel = clamp_t(s8, (count - 7), 0, 7);
|
||||
thrsh = raw / (1 << (4 + cursel));
|
||||
|
||||
resi_val = (cursel << 9) | thrsh;
|
||||
alrt_h = (resi_val >> 8) & WHISKEY_COVE_ALRT_HIGH_BIT_MASK;
|
||||
if (regmap_update_bits(regmap,
|
||||
reg - 1,
|
||||
WHISKEY_COVE_ALRT_HIGH_BIT_MASK,
|
||||
alrt_h))
|
||||
return -EIO;
|
||||
|
||||
alrt_l = (u8)resi_val;
|
||||
return regmap_write(regmap, reg, alrt_l);
|
||||
}
|
||||
|
||||
static int
|
||||
intel_bxtwc_pmic_get_policy(struct regmap *regmap, int reg, int bit, u64 *value)
|
||||
{
|
||||
u8 mask = BIT(bit);
|
||||
unsigned int val;
|
||||
|
||||
if (regmap_read(regmap, reg, &val))
|
||||
return -EIO;
|
||||
|
||||
*value = (val & mask) >> bit;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
intel_bxtwc_pmic_update_policy(struct regmap *regmap,
|
||||
int reg, int bit, int enable)
|
||||
{
|
||||
u8 mask = BIT(bit), val = enable << bit;
|
||||
|
||||
return regmap_update_bits(regmap, reg, mask, val);
|
||||
}
|
||||
|
||||
static struct intel_pmic_opregion_data intel_bxtwc_pmic_opregion_data = {
|
||||
.get_power = intel_bxtwc_pmic_get_power,
|
||||
.update_power = intel_bxtwc_pmic_update_power,
|
||||
.get_raw_temp = intel_bxtwc_pmic_get_raw_temp,
|
||||
.update_aux = intel_bxtwc_pmic_update_aux,
|
||||
.get_policy = intel_bxtwc_pmic_get_policy,
|
||||
.update_policy = intel_bxtwc_pmic_update_policy,
|
||||
.power_table = power_table,
|
||||
.power_table_count = ARRAY_SIZE(power_table),
|
||||
.thermal_table = thermal_table,
|
||||
.thermal_table_count = ARRAY_SIZE(thermal_table),
|
||||
};
|
||||
|
||||
static int intel_bxtwc_pmic_opregion_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent);
|
||||
|
||||
return intel_pmic_install_opregion_handler(&pdev->dev,
|
||||
ACPI_HANDLE(pdev->dev.parent),
|
||||
pmic->regmap,
|
||||
&intel_bxtwc_pmic_opregion_data);
|
||||
}
|
||||
|
||||
static struct platform_device_id bxt_wc_opregion_id_table[] = {
|
||||
{ .name = "bxt_wcove_region" },
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver intel_bxtwc_pmic_opregion_driver = {
|
||||
.probe = intel_bxtwc_pmic_opregion_probe,
|
||||
.driver = {
|
||||
.name = "bxt_whiskey_cove_pmic",
|
||||
},
|
||||
.id_table = bxt_wc_opregion_id_table,
|
||||
};
|
||||
|
||||
static int __init intel_bxtwc_pmic_opregion_driver_init(void)
|
||||
{
|
||||
return platform_driver_register(&intel_bxtwc_pmic_opregion_driver);
|
||||
}
|
||||
|
||||
device_initcall(intel_bxtwc_pmic_opregion_driver_init);
|
||||
|
||||
MODULE_DESCRIPTION("BXT WhiskeyCove ACPI opregion driver");
|
||||
MODULE_LICENSE("GPL");
|
Loading…
Reference in New Issue
Block a user