/* * pmic_whiskey_cove.c - CherryTrail regulator driver * * Copyright (c) 2014, Intel Corporation. * 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. * * 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 #include #include #include #include #include #include #include /* voltage control regulator offsets */ /* buck boost regulators */ #define WCOVE_V3P3A_CTRL 0x5e /* buck regulators */ #define WCOVE_V1P8A_CTRL 0x56 #define WCOVE_V1P05A_CTRL 0x3b #define WCOVE_V1P15_CTRL 0x3c #define WCOVE_VDDQ_CTRL 0x58 /* boot regulators */ /* ldo regulators */ #define WCOVE_VPROG1A_CTRL 0x90 #define WCOVE_VPROG1B_CTRL 0x91 #define WCOVE_VPROG1F_CTRL 0x95 #define WCOVE_V1P8SX_CTRL 0x57 #define WCOVE_V1P2A_CTRL 0x59 #define WCOVE_V1P2SX_CTRL 0x5a #define WCOVE_VSDIO_CTRL 0x67 #define WCOVE_V2P8SX_CTRL 0x5d #define WCOVE_V3P3SD_CTRL 0x5f #define WCOVE_VPROG2D_CTRL 0x99 #define WCOVE_VPROG3A_CTRL 0x9a #define WCOVE_VPROG3B_CTRL 0x9b #define WCOVE_VPROG4A_CTRL 0x9c #define WCOVE_VPROG4B_CTRL 0x9d #define WCOVE_VPROG4C_CTRL 0x9e #define WCOVE_VPROG4D_CTRL 0x9f #define WCOVE_VPROG5A_CTRL 0xa0 #define WCOVE_VPROG5B_CTRL 0xa1 #define WCOVE_VPROG6A_CTRL 0xa2 #define WCOVE_VPROG6B_CTRL 0xa3 /* voltage selector regulator offsets */ /* buck boost regulators */ #define WCOVE_V3P3A_VSEL 0x68 /* buck regulators */ #define WCOVE_V1P8A_VSEL 0x5b #define WCOVE_V1P05A_VSEL 0x3d #define WCOVE_V1P15_VSEL 0x3e #define WCOVE_VDDQ_VSEL 0x5c /* boot regulators */ /* ldo regulators */ #define WCOVE_VPROG1A_VSEL 0xc0 #define WCOVE_VPROG1B_VSEL 0xc1 #define WCOVE_V1P8SX_VSEL 0xc2 #define WCOVE_V1P2SX_VSEL 0xc3 #define WCOVE_V1P2A_VSEL 0xc4 #define WCOVE_VPROG1F_VSEL 0xc5 #define WCOVE_VSDIO_VSEL 0xc6 #define WCOVE_V2P8SX_VSEL 0xc7 #define WCOVE_V3P3SD_VSEL 0xc8 #define WCOVE_VPROG2D_VSEL 0xc9 #define WCOVE_VPROG3A_VSEL 0xca #define WCOVE_VPROG3B_VSEL 0xcb #define WCOVE_VPROG4A_VSEL 0xcc #define WCOVE_VPROG4B_VSEL 0xcd #define WCOVE_VPROG4C_VSEL 0xce #define WCOVE_VPROG4D_VSEL 0xcf #define WCOVE_VPROG5A_VSEL 0xd0 #define WCOVE_VPROG5B_VSEL 0xd1 #define WCOVE_VPROG6A_VSEL 0xd2 #define WCOVE_VPROG6B_VSEL 0xd3 /* number of voltage variations exposed */ /* buck boost regulators */ #define WCOVE_V3P3A_VRANGE 8 /* buck regulators */ #define WCOVE_V1P8A_VRANGE 256 #define WCOVE_V1P05A_VRANGE 256 #define WCOVE_VDDQ_VRANGE 120 /* boot regulators */ /* ldo regulators */ #define WCOVE_VPROG1A_VRANGE 53 #define WCOVE_VPROG1B_VRANGE 53 #define WCOVE_VPROG1F_VRANGE 53 #define WCOVE_V1P8SX_VRANGE 53 #define WCOVE_V1P2SX_VRANGE 53 #define WCOVE_V1P2A_VRANGE 53 #define WCOVE_VSDIO_VRANGE 53 #define WCOVE_V2P8SX_VRANGE 53 #define WCOVE_V3P3SD_VRANGE 53 #define WCOVE_VPROG2D_VRANGE 53 #define WCOVE_VPROG3A_VRANGE 53 #define WCOVE_VPROG3B_VRANGE 53 #define WCOVE_VPROG4A_VRANGE 53 #define WCOVE_VPROG4B_VRANGE 53 #define WCOVE_VPROG4C_VRANGE 53 #define WCOVE_VPROG4D_VRANGE 53 #define WCOVE_VPROG5A_VRANGE 53 #define WCOVE_VPROG5B_VRANGE 53 #define WCOVE_VPROG6A_VRANGE 53 #define WCOVE_VPROG6B_VRANGE 53 /* voltage tables */ static unsigned int WCOVE_V3P3A_VSEL_TABLE[WCOVE_V3P3A_VRANGE], WCOVE_V1P8A_VSEL_TABLE[WCOVE_V1P8A_VRANGE], WCOVE_V1P05A_VSEL_TABLE[WCOVE_V1P05A_VRANGE], WCOVE_VDDQ_VSEL_TABLE[WCOVE_VDDQ_VRANGE], WCOVE_V1P8SX_VSEL_TABLE[WCOVE_V1P8SX_VRANGE], WCOVE_V1P2SX_VSEL_TABLE[WCOVE_V1P2SX_VRANGE], WCOVE_V1P2A_VSEL_TABLE[WCOVE_V1P2A_VRANGE], WCOVE_V2P8SX_VSEL_TABLE[WCOVE_V2P8SX_VRANGE], WCOVE_VSDIO_VSEL_TABLE[WCOVE_VSDIO_VRANGE], WCOVE_V3P3SD_VSEL_TABLE[WCOVE_V3P3SD_VRANGE], WCOVE_VPROG1A_VSEL_TABLE[WCOVE_VPROG1A_VRANGE], WCOVE_VPROG1B_VSEL_TABLE[WCOVE_VPROG1B_VRANGE], WCOVE_VPROG1F_VSEL_TABLE[WCOVE_VPROG1F_VRANGE], WCOVE_VPROG2D_VSEL_TABLE[WCOVE_VPROG2D_VRANGE], WCOVE_VPROG3A_VSEL_TABLE[WCOVE_VPROG3A_VRANGE], WCOVE_VPROG3B_VSEL_TABLE[WCOVE_VPROG3B_VRANGE], WCOVE_VPROG4A_VSEL_TABLE[WCOVE_VPROG4A_VRANGE], WCOVE_VPROG4B_VSEL_TABLE[WCOVE_VPROG4B_VRANGE], WCOVE_VPROG4C_VSEL_TABLE[WCOVE_VPROG4C_VRANGE], WCOVE_VPROG4D_VSEL_TABLE[WCOVE_VPROG4D_VRANGE], WCOVE_VPROG5A_VSEL_TABLE[WCOVE_VPROG5A_VRANGE], WCOVE_VPROG5B_VSEL_TABLE[WCOVE_VPROG5B_VRANGE], WCOVE_VPROG6A_VSEL_TABLE[WCOVE_VPROG6A_VRANGE], WCOVE_VPROG6B_VSEL_TABLE[WCOVE_VPROG6B_VRANGE]; static int wcove_regulator_enable(struct regulator_dev *rdev) { struct wcove_regulator_info *pmic_info = rdev_get_drvdata(rdev); int reg_val; reg_val = intel_mid_pmic_readb(pmic_info->vctl_reg); if (reg_val < 0) { dev_err(&rdev->dev, "error reading pmic, %x\n", reg_val); return reg_val; } reg_val &= ~pmic_info->vctl_mask; reg_val |= pmic_info->reg_enbl_mask; return intel_mid_pmic_writeb(pmic_info->vctl_reg, reg_val); } static int wcove_regulator_disable(struct regulator_dev *rdev) { struct wcove_regulator_info *pmic_info = rdev_get_drvdata(rdev); int reg_val; reg_val = intel_mid_pmic_readb(pmic_info->vctl_reg); if (reg_val < 0) { dev_err(&rdev->dev, "error reading pmic, %x\n", reg_val); return reg_val; } reg_val &= ~pmic_info->vctl_mask; reg_val |= pmic_info->reg_dsbl_mask; return intel_mid_pmic_writeb(pmic_info->vctl_reg, reg_val); } static int wcove_regulator_is_enabled(struct regulator_dev *rdev) { struct wcove_regulator_info *pmic_info = rdev_get_drvdata(rdev); int reg_val; reg_val = intel_mid_pmic_readb(pmic_info->vctl_reg); if (reg_val < 0) { dev_err(&rdev->dev, "error reading pmic, %x\n", reg_val); return reg_val; } reg_val &= pmic_info->vctl_mask; return reg_val & pmic_info->reg_enbl_mask; } static int wcove_regulator_get_voltage_sel(struct regulator_dev *rdev) { struct wcove_regulator_info *pmic_info = rdev_get_drvdata(rdev); int reg_val, vsel; reg_val = intel_mid_pmic_readb(pmic_info->vsel_reg); if (reg_val < 0) { dev_err(&rdev->dev, "error reading pmic, %x\n", reg_val); return reg_val; } vsel = (reg_val & pmic_info->vsel_mask) - pmic_info->start; return vsel; } static int wcove_regulator_set_voltage_sel(struct regulator_dev *rdev, unsigned selector) { struct wcove_regulator_info *pmic_info = rdev_get_drvdata(rdev); int reg_val; reg_val = intel_mid_pmic_readb(pmic_info->vsel_reg); if (reg_val < 0) { dev_err(&rdev->dev, "error reading pmic, %x\n", reg_val); return reg_val; } reg_val &= ~pmic_info->vsel_mask; reg_val |= (selector + pmic_info->start); return intel_mid_pmic_writeb(pmic_info->vsel_reg, reg_val); } /* regulator ops */ static struct regulator_ops wcove_regulator_ops = { .enable = wcove_regulator_enable, .disable = wcove_regulator_disable, .is_enabled = wcove_regulator_is_enabled, .get_voltage_sel = wcove_regulator_get_voltage_sel, .set_voltage_sel = wcove_regulator_set_voltage_sel, .list_voltage = regulator_list_voltage_table, }; #define WCOVE_REG(_id, minv, maxv, strt, vselmsk, vscale, vctlmsk, enbl, dsbl) \ {\ .desc = {\ .name = ""#_id,\ .ops = &wcove_regulator_ops,\ .type = REGULATOR_VOLTAGE,\ .id = WCOVE_ID_##_id,\ .owner = THIS_MODULE,\ },\ .vctl_reg = WCOVE_##_id##_CTRL,\ .vsel_reg = WCOVE_##_id##_VSEL,\ .min_mV = (minv),\ .max_mV = (maxv),\ .start = (strt),\ .vsel_mask = (vselmsk),\ .scale = (vscale),\ .nvolts = WCOVE_##_id##_VRANGE,\ .vctl_mask = (vctlmsk),\ .reg_enbl_mask = (enbl),\ .reg_dsbl_mask = (dsbl),\ .vtable = WCOVE_##_id##_VSEL_TABLE,\ } /* Regulator descriptions */ static struct wcove_regulator_info regulators_info[] = { WCOVE_REG(V3P3A, 3000, 3350, 0x0, 0x07, 50, 0x1, 0x1, 0x0), WCOVE_REG(V1P8A, 250, 2100, 0x0, 0xff, 10, 0x1, 0x1, 0x0), WCOVE_REG(V1P05A, 250, 2100, 0x0, 0xff, 10, 0x1, 0x1, 0x0), WCOVE_REG(VDDQ, 250, 1440, 0x0, 0x7f, 10, 0x1, 0x1, 0x0), WCOVE_REG(V1P8SX, 800, 3400, 0x0b, 0x3f, 50, 0x07, 0x1, 0x0), WCOVE_REG(V1P2A, 800, 3400, 0x0b, 0x3f, 50, 0x07, 0x1, 0x0), WCOVE_REG(V1P2SX, 800, 3400, 0x0b, 0x3f, 50, 0x07, 0x1, 0x0), WCOVE_REG(V2P8SX, 800, 3400, 0x0b, 0x3f, 50, 0x07, 0x1, 0x0), WCOVE_REG(VSDIO, 800, 3400, 0x0b, 0x3f, 50, 0x07, 0x01, 0x0), WCOVE_REG(V3P3SD, 800, 3400, 0x0b, 0x3f, 50, 0x07, 0x01, 0x0), WCOVE_REG(VPROG1A, 800, 3400, 0x0b, 0x3f, 50, 0x07, 0x01, 0x0), WCOVE_REG(VPROG1B, 800, 3400, 0x0b, 0x3f, 50, 0x07, 0x01, 0x0), WCOVE_REG(VPROG1F, 800, 3400, 0x0b, 0x3f, 50, 0x07, 0x01, 0x0), WCOVE_REG(VPROG2D, 800, 3400, 0x0b, 0x3f, 50, 0x07, 0x01, 0x0), WCOVE_REG(VPROG3A, 800, 3400, 0x0b, 0x3f, 50, 0x07, 0x01, 0x0), WCOVE_REG(VPROG3B, 800, 3400, 0x0b, 0x3f, 50, 0x07, 0x01, 0x0), WCOVE_REG(VPROG4A, 800, 3400, 0x0b, 0x3f, 50, 0x07, 0x01, 0x0), WCOVE_REG(VPROG4B, 800, 3400, 0x0b, 0x3f, 50, 0x07, 0x01, 0x0), WCOVE_REG(VPROG4C, 800, 3400, 0x0b, 0x3f, 50, 0x07, 0x01, 0x0), WCOVE_REG(VPROG4D, 800, 3400, 0x0b, 0x3f, 50, 0x07, 0x01, 0x0), WCOVE_REG(VPROG5A, 800, 3400, 0x0b, 0x3f, 50, 0x07, 0x01, 0x0), WCOVE_REG(VPROG5B, 800, 3400, 0x0b, 0x3f, 50, 0x07, 0x01, 0x0), WCOVE_REG(VPROG6A, 800, 3400, 0x0b, 0x3f, 50, 0x07, 0x01, 0x0), WCOVE_REG(VPROG6B, 800, 3400, 0x0b, 0x3f, 50, 0x07, 0x01, 0x0), }; static inline struct wcove_regulator_info *wcove_find_regulator_info(int id) { struct wcove_regulator_info *reg_info; int i; for (i = 0; i < ARRAY_SIZE(regulators_info); i++) { if (regulators_info[i].desc.id == id) { reg_info = ®ulators_info[i]; return reg_info; } } return NULL; } static void initialize_vtable(struct wcove_regulator_info *reg_info) { unsigned int i, volt; for (i = 0; i < reg_info->nvolts; i++) { volt = reg_info->min_mV + (i * reg_info->scale); if (volt < reg_info->min_mV) volt = reg_info->min_mV; if (volt > reg_info->max_mV) volt = reg_info->max_mV; /* set value in uV */ reg_info->vtable[i] = volt*1000; } reg_info->desc.volt_table = reg_info->vtable; reg_info->desc.n_voltages = reg_info->nvolts; } static int wcove_regulator_probe(struct platform_device *pdev) { struct wcove_regulator_info *pdata = dev_get_platdata(&pdev->dev); struct regulator_config config = { }; struct wcove_regulator_info *reg_info = NULL; if (!pdata) { dev_err(&pdev->dev, "No regulator info\n"); return -EINVAL; } reg_info = wcove_find_regulator_info(pdev->id); if (reg_info == NULL) { dev_err(&pdev->dev, "invalid regulator %d\n", pdev->id); return -EINVAL; } initialize_vtable(reg_info); config.dev = &pdev->dev; config.init_data = pdata->init_data; config.driver_data = reg_info; pdata->regulator = regulator_register(®_info->desc, &config); if (IS_ERR(pdata->regulator)) { dev_err(&pdev->dev, "failed to register regulator as %s\n", reg_info->desc.name); return PTR_ERR(pdata->regulator); } platform_set_drvdata(pdev, pdata->regulator); dev_dbg(&pdev->dev, "registered whiskey cove regulator as %s\n", dev_name(&pdata->regulator->dev)); return 0; } static int wcove_regulator_remove(struct platform_device *pdev) { regulator_unregister(platform_get_drvdata(pdev)); return 0; } static const struct platform_device_id wcove_regulator_id_table[] = { { "wcove_regulator", 0}, { }, }; MODULE_DEVICE_TABLE(platform, wcove_regulator_id_table); static struct platform_driver wcove_regulator_driver = { .driver = { .name = "wcove_regulator", .owner = THIS_MODULE, }, .probe = wcove_regulator_probe, .remove = wcove_regulator_remove, .id_table = wcove_regulator_id_table, }; static int __init wcove_regulator_init(void) { return platform_driver_register(&wcove_regulator_driver); } fs_initcall(wcove_regulator_init); static void __exit wcove_regulator_exit(void) { platform_driver_unregister(&wcove_regulator_driver); } module_exit(wcove_regulator_exit); MODULE_DESCRIPTION("WhiskeyCove regulator driver"); MODULE_AUTHOR("Nitheesh K L