/* * drivers/power/supply/axp/axp20x/axp20x-regulator.c * (C) Copyright 2010-2016 * Allwinner Technology Co., Ltd. * caiyongheng * * regulator driver of axp20x * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * */ #include #include #include #include #include #include #include #include #include #include #include "../axp-core.h" #include "../axp-regulator.h" #include "axp20x.h" #include "axp20x-regulator.h" enum AXP_REGLS { VCC_DCDC2, VCC_DCDC3, VCC_LDO1, VCC_LDO2, VCC_LDO3, VCC_LDO4, VCC_LDOIO0, VCC_20X_MAX, }; struct axp20x_regulators { struct regulator_dev *regulators[VCC_20X_MAX]; struct axp_dev *chip; }; static const int axp20_ldo4_table[] = { 1250, 1300, 1400, 1500, 1600, 1700, 1800, 1900, 2000, 2500, 2700, 2800, 3000, 3100, 3200, 3300 }; #define AXP20X_LDO(_id, min, max, step1, vreg, shift, nbits,\ ereg, emask, enval, disval, switch_vol, step2, new_level,\ mode_addr, freq_addr, dvm_ereg, dvm_ebit, dvm_flag) \ AXP_LDO(AXP20X, _id, min, max, step1, vreg, shift, nbits,\ ereg, emask, enval, disval, switch_vol, step2, new_level,\ mode_addr, freq_addr, dvm_ereg, dvm_ebit, dvm_flag) #define AXP20X_LDO_SEL(_id, min, max, vreg, shift, nbits,\ ereg, emask, enval, disval, vtable,\ mode_addr, freq_addr, dvm_ereg, dvm_ebit, dvm_flag) \ AXP_LDO_SEL(AXP20X, _id, min, max, vreg, shift, nbits,\ ereg, emask, enval, disval, vtable,\ mode_addr, freq_addr, dvm_ereg, dvm_ebit, dvm_flag) #define AXP20X_DCDC(_id, min, max, step1, vreg, shift, nbits,\ ereg, emask, enval, disval, switch_vol, step2, new_level,\ mode_addr, mode_bit, freq_addr, dvm_ereg, dvm_ebit, dvm_flag) \ AXP_DCDC(AXP20X, _id, min, max, step1, vreg, shift, nbits,\ ereg, emask, enval, disval, switch_vol, step2, new_level,\ mode_addr, mode_bit, freq_addr, dvm_ereg, dvm_ebit, dvm_flag) static struct axp_regulator_info axp20x_regulator_info[] = { AXP20X_DCDC(2, 700, 2275, 25, DCDC2, 0, 6, DCDC2EN, 0x10, 0x10, 0x00, 0, 0, 0, 0x80, 0x04, 0x37, 0x25, 2, 0), AXP20X_DCDC(3, 700, 3500, 25, DCDC3, 0, 7, DCDC3EN, 0x02, 0x02, 0x00, 0, 0, 0, 0x80, 0x02, 0x37, 0x25, 3, 0), AXP20X_LDO(1, 3300, 3300, 0, LDO1, 0, 0, LDO1EN, 0x00, 0x00, 0x00, 0, 0, 0, 0, 0, 0, 0, 0), AXP20X_LDO(2, 1800, 3300, 100, LDO2, 4, 4, LDO2EN, 0x04, 0x04, 0x00, 0, 0, 0, 0, 0, 0, 0, 0), AXP20X_LDO(3, 700, 3500, 25, LDO3, 0, 7, LDO3EN, 0x40, 0x40, 0x00, 0, 0, 0, 0, 0, 0, 0, 0), AXP20X_LDO_SEL(4, 1250, 3300, LDO4, 0, 4, LDO4EN, 0x08, 0x08, 0, axp20_ldo4, 0, 0, 0, 0, 0), AXP20X_LDO(IO0, 1800, 3300, 100, LDOIO0, 4, 4, LDOIO0EN, 0x07, 0x03, 0x00, 0, 0, 0, 0, 0, 0, 0, 0), }; static struct regulator_init_data axp20x_regulator_init_data[] = { [VCC_DCDC2] = { .constraints = { .name = "axp20x_dcdc2", .min_uV = 700000, .max_uV = 2275000, .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS, }, }, [VCC_DCDC3] = { .constraints = { .name = "axp20x_dcdc3", .min_uV = 700000, .max_uV = 3500000, .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS, }, }, [VCC_LDO1] = { .constraints = { .name = "axp20x_rtc", /* ldo1 */ .min_uV = 3300000, .max_uV = 3300000, .always_on = 1, }, }, [VCC_LDO2] = { .constraints = { .name = "axp20x_ldo2", .min_uV = 1800000, .max_uV = 3300000, .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS, }, }, [VCC_LDO3] = { .constraints = { .name = "axp20x_ldo3", .min_uV = 700000, .max_uV = 3300000, .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS, }, }, [VCC_LDO4] = { .constraints = { .name = "axp20x_ldo4", .min_uV = 1250000, .max_uV = 3300000, .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS, }, }, [VCC_LDOIO0] = { .constraints = { .name = "axp20x_ldoio0", .min_uV = 1800000, .max_uV = 3300000, .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS, }, }, }; static s32 axp20x_regulator_dependence(const char *ldo_name) { s32 axp20x_dependence = 0; if (strstr(ldo_name, "dcdc2") != NULL) axp20x_dependence |= AXP20X_DCDC2_BIT; else if (strstr(ldo_name, "dcdc3") != NULL) axp20x_dependence |= AXP20X_DCDC3_BIT; else if (strstr(ldo_name, "ldo2") != NULL) axp20x_dependence |= AXP20X_LDO2_BIT; else if (strstr(ldo_name, "ldo3") != NULL) axp20x_dependence |= AXP20X_LDO3_BIT; else if (strstr(ldo_name, "ldo4") != NULL) axp20x_dependence |= AXP20X_LDO4_BIT; else if (strstr(ldo_name, "ldoio0") != NULL) axp20x_dependence |= AXP20X_LDOIO0_BIT; else if (strstr(ldo_name, "rtc") != NULL) axp20x_dependence |= AXP20X_RTC_BIT; else return -1; axp20x_dependence |= (0 << 30); return axp20x_dependence; } int axp20x_need_save_regulator; static axp_mem_data_t *regu_list; static u32 ldo_count; int axp20x_regulator_save(void) { struct regulator *regu = NULL; char *spy_id = NULL; char *rtc_id = NULL; int ret = 0, ldo_idx = 0; if (axp20x_need_save_regulator) { for (ldo_idx = 0; ldo_idx < ldo_count; ldo_idx++) { spy_id = (char *)((regu_list + ldo_idx)->id_name); rtc_id = strstr(spy_id, "rtc"); if (rtc_id != NULL) { (regu_list + ldo_idx)->mem_data = 0; rtc_id = NULL; continue; } regu = regulator_get(NULL, spy_id); if (IS_ERR(regu)) { pr_err("%s: fail to get regulator %s\n", __func__, spy_id); return -1; } ret = regulator_get_voltage(regu); if (ret < 0) { pr_err("%s: fail to get %s voltage!\n", __func__, spy_id); return -1; } (regu_list + ldo_idx)->mem_data = ret; ret = regulator_is_enabled(regu); if (ret > 0) (regu_list + ldo_idx)->mem_data |= (1 << 31); else (regu_list + ldo_idx)->mem_data &= (~(1 << 31)); regulator_put(regu); } } return 0; } void axp20x_regulator_restore(void) { struct regulator *regu = NULL; char *spy_id = NULL; int ret = 0, volt = 0, ldo_idx = 0; if (axp20x_need_save_regulator) { for (ldo_idx = 0; ldo_idx < ldo_count; ldo_idx++) { if (0 == (regu_list + ldo_idx)->mem_data) continue; spy_id = (char *)((regu_list + ldo_idx)->id_name); regu = regulator_get(NULL, spy_id); if (IS_ERR(regu)) { pr_err("%s: fail to get regulator %s\n", __func__, spy_id); continue; } ret = regulator_get_voltage(regu); volt = (regu_list + ldo_idx)->mem_data & 0x0fffffff; if (ret != volt) { ret = regulator_set_voltage(regu, volt, volt); if (ret != 0) { pr_err("%s: fail to set %s voltage!\n", __func__, spy_id); continue; } } if ((regu_list + ldo_idx)->mem_data & (1 << 31)) { ret = regu->rdev->desc->ops->enable(regu->rdev); if (ret != 0) { pr_err("%s: fail to enable %s!\n", __func__, spy_id); continue; } } else { ret = regu->rdev->desc->ops->disable( regu->rdev); if (ret != 0) { pr_err("%s: fail to enable %s!\n", __func__, spy_id); continue; } } regulator_put(regu); } } } static int axp20x_regulator_probe(struct platform_device *pdev) { s32 i, ret = 0; struct axp_regulator_info *info; struct axp20x_regulators *regu_data; struct axp_dev *axp_dev = dev_get_drvdata(pdev->dev.parent); if (pdev->dev.of_node) { ret = axp_regulator_dt_parse(pdev->dev.of_node, axp20x_regulator_init_data, axp20x_regulator_dependence); if (ret) { pr_err("%s parse device tree err\n", __func__); return -EINVAL; } } else { pr_err("axp20x regulator device tree err!\n"); return -EBUSY; } regu_data = devm_kzalloc(&pdev->dev, sizeof(*regu_data), GFP_KERNEL); if (!regu_data) return -ENOMEM; regu_data->chip = axp_dev; platform_set_drvdata(pdev, regu_data); for (i = 0; i < VCC_20X_MAX; i++) { info = &axp20x_regulator_info[i]; info->pmu_num = axp_dev->pmu_num; if (info->desc.id == AXP20X_ID_LDO4) { regu_data->regulators[i] = axp_regulator_sel_register(&pdev->dev, axp_dev->regmap, &axp20x_regulator_init_data[i], info); } else { regu_data->regulators[i] = axp_regulator_register(&pdev->dev, axp_dev->regmap, &axp20x_regulator_init_data[i], info); } if (IS_ERR(regu_data->regulators[i])) { dev_err(&pdev->dev, "failed to register regulator %s\n", info->desc.name); while (--i >= 0) axp_regulator_unregister( regu_data->regulators[i]); return -1; } if (info->desc.id >= AXP_DCDC_ID_START) { ret = axp_regulator_create_attrs( ®u_data->regulators[i]->dev); if (ret) dev_err(&pdev->dev, "failed to register regulator attr %s\n", info->desc.name); } } /* voltage not to the OTP default when wakeup */ if (axp20x_need_save_regulator == 0) { axp_regmap_set_bits(axp_dev->regmap, AXP20X_HOTOVER_CTL, 0x02); } else { ret = axp_get_ldo_count(pdev->dev.of_node, &ldo_count); if (ret) { dev_err(&pdev->dev, "failed to get ldo count\n"); return ret; } regu_list = (axp_mem_data_t *)kzalloc( sizeof(axp_mem_data_t) * ldo_count, GFP_KERNEL); if (!regu_list) { pr_err("%s: request regu_list failed\n", __func__); return -1; } ret = axp_mem_regu_init(pdev->dev.of_node, regu_list, ldo_count); if (ret) { dev_err(&pdev->dev, "failed to init mem regu\n"); return ret; } } /* we should set the sys power domain here*/ init_sys_pwr_dm(); return 0; } static int axp20x_regulator_remove(struct platform_device *pdev) { struct axp20x_regulators *regu_data = platform_get_drvdata(pdev); int i; for (i = 0; i < VCC_20X_MAX; i++) regulator_unregister(regu_data->regulators[i]); return 0; } static const struct of_device_id axp20x_regulator_dt_ids[] = { { .compatible = "axp203-regulator", }, { .compatible = "axp209-regulator", }, {}, }; MODULE_DEVICE_TABLE(of, axp20x_regulator_dt_ids); static struct platform_driver axp20x_regulator_driver = { .driver = { .name = "axp20x-regulator", .of_match_table = axp20x_regulator_dt_ids, }, .probe = axp20x_regulator_probe, .remove = axp20x_regulator_remove, }; static int __init axp20x_regulator_initcall(void) { int ret; ret = platform_driver_register(&axp20x_regulator_driver); if (IS_ERR_VALUE(ret)) { pr_err("%s: failed, errno %d\n", __func__, ret); return -EINVAL; } return 0; } subsys_initcall(axp20x_regulator_initcall); MODULE_DESCRIPTION("Regulator Driver of axp20x"); MODULE_AUTHOR("caiyongheng"); MODULE_LICENSE("GPL");