| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Intel BXT WhiskeyCove PMIC operation region driver |
| * |
| * Copyright (C) 2015 Intel Corporation. All rights reserved. |
| */ |
| |
| #include <linux/init.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 const 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 const 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 const 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, |
| .lpat_raw_to_temp = acpi_lpat_raw_to_temp, |
| .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 const 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, |
| }; |
| builtin_platform_driver(intel_bxtwc_pmic_opregion_driver); |