123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674 |
- /*
- * Copyright (c) 2015-2018, The Linux Foundation. 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) "GFX_LDO: %s: " fmt, __func__
- #include <linux/bitops.h>
- #include <linux/debugfs.h>
- #include <linux/delay.h>
- #include <linux/io.h>
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/of.h>
- #include <linux/of_device.h>
- #include <linux/platform_device.h>
- #include <linux/regulator/driver.h>
- #include <linux/regulator/machine.h>
- #include <linux/regulator/of_regulator.h>
- #include <linux/slab.h>
- #include <linux/uaccess.h>
- #include <linux/regulator/msm-ldo-regulator.h>
- #define LDO_ATEST_REG 0x0
- #define LDO_CFG0_REG 0x4
- #define LDO_CFG1_REG 0x8
- #define LDO_CFG2_REG 0xC
- #define LDO_LD_DATA_REG 0x10
- #define LDO_VREF_TEST_CFG 0x14
- #define ENABLE_LDO_STATUS_BIT (BIT(8) | BIT(12))
- #define LDO_AUTOBYPASS_BIT BIT(20)
- #define LDO_VREF_SET_REG 0x18
- #define UPDATE_VREF_BIT BIT(31)
- #define SEL_RST_BIT BIT(16)
- #define VREF_VAL_MASK GENMASK(6, 0)
- #define PWRSWITCH_CTRL_REG 0x1C
- #define LDO_CLAMP_IO_BIT BIT(31)
- #define CPR_BYPASS_IN_LDO_MODE_BIT BIT(30)
- #define EN_LDOAP_CTRL_CPR_BIT BIT(29)
- #define CX_CPR_BYPASS_IN_LDO_MODE_BIT BIT(10)
- #define PWR_SRC_SEL_BIT BIT(9)
- #define ACK_SW_OVR_BIT BIT(8)
- #define LDO_PREON_SW_OVR_BIT BIT(7)
- #define LDO_BYPASS_BIT BIT(6)
- #define LDO_PDN_BIT BIT(5)
- #define LDO_UNDER_SW_CTRL_BIT BIT(4)
- #define BHS_EN_REST_BIT BIT(2)
- #define BHS_EN_FEW_BIT BIT(1)
- #define BHS_UNDER_SW_CTL BIT(0)
- #define LDO_STATUS1_REG 0x24
- #define PWRSWITCH_STATUS_REG 0x28
- #define LDO_VREF_SETTLED_BIT BIT(4)
- #define LDO_READY_BIT BIT(2)
- #define BHS_EN_REST_ACK_BIT BIT(1)
- #define REF_CURRENT_X1_REG 0x2C
- #define REF_CURRENT_X2_REG 0x30
- #define ADC_CTL_REG 0x34
- #define MIN_LDO_VOLTAGE 375000
- #define MAX_LDO_VOLTAGE 980000
- #define LDO_STEP_VOLATGE 5000
- #define MAX_LDO_REGS 11
- #define BYTES_PER_FUSE_ROW 8
- #define MAX_FUSE_ROW_BIT 63
- #define MIN_CORNER_OFFSET 1
- #define GFX_LDO_FUSE_STEP_VOLT 10000
- #define GFX_LDO_FUSE_SIZE 5
- enum direction {
- NO_CHANGE,
- UP,
- DOWN,
- };
- enum voltage_handling {
- VOLTAGE,
- CORNER,
- };
- struct fuse_param {
- unsigned int row;
- unsigned int bit_start;
- unsigned int bit_end;
- };
- struct ldo_config {
- u32 offset;
- u32 value;
- };
- struct msm_gfx_ldo {
- struct device *dev;
- struct regulator_desc rdesc;
- struct regulator_dev *rdev;
- struct regulator *vdd_cx;
- struct regulator *mem_acc_vreg;
- struct dentry *debugfs;
- u32 num_corners;
- u32 num_ldo_corners;
- u32 *open_loop_volt;
- u32 *ceiling_volt;
- u32 *floor_volt;
- u32 *ldo_corner_en_map;
- u32 *vdd_cx_corner_map;
- u32 *mem_acc_corner_map;
- const int *ref_volt;
- const struct fuse_param *ldo_enable_param;
- const struct fuse_param **init_volt_param;
- bool ldo_fuse_enable;
- bool ldo_mode_disable;
- struct ldo_config *ldo_init_config;
- void __iomem *efuse_base;
- phys_addr_t efuse_addr;
- void __iomem *ldo_base;
- phys_addr_t ldo_addr;
- bool vreg_enabled;
- enum msm_ldo_supply_mode mode;
- u32 corner;
- int ldo_voltage_uv;
- struct mutex ldo_mutex;
- enum voltage_handling ops_type;
- };
- #define MSM8953_LDO_FUSE_CORNERS 3
- #define LDO_MAX_OFFSET 0xFFFF
- static struct ldo_config msm8953_ldo_config[] = {
- {LDO_ATEST_REG, 0x00000203},
- {LDO_CFG0_REG, 0x05008600},
- {LDO_CFG1_REG, 0x0},
- {LDO_CFG2_REG, 0x0000C3FC},
- {LDO_VREF_TEST_CFG, 0x004B1102},
- {LDO_MAX_OFFSET, LDO_MAX_OFFSET},
- };
- static struct ldo_config sdm660_ldo_config[] = {
- {LDO_ATEST_REG, 0x00000080},
- {LDO_CFG0_REG, 0x0100A600},
- {LDO_CFG1_REG, 0x000000A0},
- {LDO_CFG2_REG, 0x0000C3FE},
- {LDO_LD_DATA_REG, 0x00000000},
- {LDO_VREF_TEST_CFG, 0x00401100},
- {REF_CURRENT_X1_REG, 0x00000230},
- {REF_CURRENT_X2_REG, 0x00000048},
- {ADC_CTL_REG, 0x00000000},
- {LDO_MAX_OFFSET, LDO_MAX_OFFSET},
- };
- static struct fuse_param msm8953_ldo_enable_param[] = {
- {65, 11, 11},
- {},
- };
- static const struct fuse_param
- msm8953_init_voltage_param[MSM8953_LDO_FUSE_CORNERS][2] = {
- { {73, 42, 46}, {} },
- { {73, 37, 41}, {} },
- { {73, 32, 36}, {} },
- };
- static const int msm8953_fuse_ref_volt[MSM8953_LDO_FUSE_CORNERS] = {
- 580000,
- 650000,
- 720000,
- };
- enum {
- MSM8953_SOC_ID,
- SDM660_SOC_ID,
- };
- static int convert_open_loop_voltage_fuse(int ref_volt, int step_volt,
- u32 fuse, int fuse_len)
- {
- int sign, steps;
- sign = (fuse & (1 << (fuse_len - 1))) ? -1 : 1;
- steps = fuse & ((1 << (fuse_len - 1)) - 1);
- return ref_volt + sign * steps * step_volt;
- }
- static int read_fuse_param(void __iomem *fuse_base_addr,
- const struct fuse_param *param, u64 *param_value)
- {
- u64 fuse_val, val;
- int bits;
- int bits_total = 0;
- *param_value = 0;
- while (param->row || param->bit_start || param->bit_end) {
- if (param->bit_start > param->bit_end
- || param->bit_end > MAX_FUSE_ROW_BIT) {
- pr_err("Invalid fuse parameter segment: row=%u, start=%u, end=%u\n",
- param->row, param->bit_start, param->bit_end);
- return -EINVAL;
- }
- bits = param->bit_end - param->bit_start + 1;
- if (bits_total + bits > 64) {
- pr_err("Invalid fuse parameter segments; total bits = %d\n",
- bits_total + bits);
- return -EINVAL;
- }
- fuse_val = readq_relaxed(fuse_base_addr
- + param->row * BYTES_PER_FUSE_ROW);
- val = (fuse_val >> param->bit_start) & ((1ULL << bits) - 1);
- *param_value |= val << bits_total;
- bits_total += bits;
- param++;
- }
- return 0;
- }
- static enum msm_ldo_supply_mode get_operating_mode(struct msm_gfx_ldo *ldo_vreg,
- int corner)
- {
- if (!ldo_vreg->ldo_mode_disable && ldo_vreg->ldo_fuse_enable
- && ldo_vreg->ldo_corner_en_map[corner])
- return LDO_MODE;
- return BHS_MODE;
- }
- static char *register_str[] = {
- "LDO_ATEST",
- "LDO_CFG0",
- "LDO_CFG1",
- "LDO_CFG2",
- "LDO_LD_DATA",
- "LDO_VREF_TEST_CFG",
- "LDO_VREF_SET",
- "PWRSWITCH_CTL",
- "LDO_STATUS0",
- "LDO_STATUS1",
- "PWRSWITCH_STATUS",
- };
- static void dump_registers(struct msm_gfx_ldo *ldo_vreg, char *func)
- {
- u32 reg[MAX_LDO_REGS];
- int i;
- for (i = 0; i < MAX_LDO_REGS; i++) {
- reg[i] = 0;
- reg[i] = readl_relaxed(ldo_vreg->ldo_base + (i * 4));
- pr_debug("%s -- %s = 0x%x\n", func, register_str[i], reg[i]);
- }
- }
- #define GET_VREF(a) DIV_ROUND_UP(a - MIN_LDO_VOLTAGE, LDO_STEP_VOLATGE)
- static void configure_ldo_voltage(struct msm_gfx_ldo *ldo_vreg, int new_uv)
- {
- int val = 0;
- u32 reg = 0;
- val = GET_VREF(new_uv);
- reg = readl_relaxed(ldo_vreg->ldo_base + LDO_VREF_SET_REG);
- /* set the new voltage */
- reg &= ~VREF_VAL_MASK;
- reg |= val & VREF_VAL_MASK;
- writel_relaxed(reg, ldo_vreg->ldo_base + LDO_VREF_SET_REG);
- /* Initiate VREF update */
- reg |= UPDATE_VREF_BIT;
- writel_relaxed(reg, ldo_vreg->ldo_base + LDO_VREF_SET_REG);
- /* complete the writes */
- mb();
- reg &= ~UPDATE_VREF_BIT;
- writel_relaxed(reg, ldo_vreg->ldo_base + LDO_VREF_SET_REG);
- ldo_vreg->ldo_voltage_uv = new_uv;
- /* complete the write sequence */
- mb();
- }
- static int ldo_update_voltage(struct msm_gfx_ldo *ldo_vreg, int new_uv)
- {
- int timeout = 50;
- u32 reg = 0;
- configure_ldo_voltage(ldo_vreg, new_uv);
- while (--timeout) {
- reg = readl_relaxed(ldo_vreg->ldo_base +
- PWRSWITCH_STATUS_REG);
- if (reg & (LDO_VREF_SETTLED_BIT | LDO_READY_BIT))
- break;
- udelay(10);
- }
- if (!timeout) {
- pr_err("LDO_VREF_SETTLED not set PWRSWITCH_STATUS = 0x%x\n",
- reg);
- return -EBUSY;
- }
- pr_debug("LDO voltage set to=%d uV VREF_REG=%x\n",
- ldo_vreg->ldo_voltage_uv,
- readl_relaxed(ldo_vreg->ldo_base + LDO_VREF_SET_REG));
- return 0;
- }
- static int enable_ldo_mode(struct msm_gfx_ldo *ldo_vreg, int new_uv)
- {
- u32 ctl = 0;
- /* set the ldo-vref */
- configure_ldo_voltage(ldo_vreg, new_uv);
- /* configure the LDO for power-up */
- ctl = readl_relaxed(ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
- /* Move BHS under SW control */
- ctl |= BHS_UNDER_SW_CTL;
- writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
- /* Set LDO under gdsc control */
- ctl &= ~LDO_UNDER_SW_CTRL_BIT;
- writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
- /* enable hw_pre-on to gdsc */
- ctl |= LDO_PREON_SW_OVR_BIT;
- writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
- /* remove LDO bypass */
- ctl &= ~LDO_BYPASS_BIT;
- writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
- /* set power-source as LDO */
- ctl |= PWR_SRC_SEL_BIT;
- writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
- /* clear fake-sw ack to gdsc */
- ctl &= ~ACK_SW_OVR_BIT;
- writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
- /* put CPR in bypass mode */
- if (ldo_vreg->ops_type == CORNER) {
- ctl |= CPR_BYPASS_IN_LDO_MODE_BIT;
- writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
- }
- /* complete all writes */
- mb();
- dump_registers(ldo_vreg, "enable_ldo_mode");
- return 0;
- }
- static int enable_bhs_mode(struct msm_gfx_ldo *ldo_vreg)
- {
- u32 ctl = 0;
- ctl = readl_relaxed(ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
- /* Put LDO under SW control */
- ctl |= LDO_UNDER_SW_CTRL_BIT;
- writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
- /* set power-source as BHS */
- ctl &= ~PWR_SRC_SEL_BIT;
- writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
- if (ldo_vreg->ops_type == CORNER) {
- /* clear GFX CPR in by-pass mode */
- ctl &= ~CPR_BYPASS_IN_LDO_MODE_BIT;
- writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
- }
- /* Enable the BHS control signals to gdsc */
- ctl &= ~BHS_EN_FEW_BIT;
- writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
- ctl &= ~BHS_EN_REST_BIT;
- writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
- /* Put BHS under GDSC control */
- ctl &= ~BHS_UNDER_SW_CTL;
- writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
- dump_registers(ldo_vreg, "enable_bhs_mode");
- return 0;
- }
- static int msm_gfx_ldo_corner_enable(struct regulator_dev *rdev)
- {
- struct msm_gfx_ldo *ldo_vreg = rdev_get_drvdata(rdev);
- int rc = 0, new_uv;
- enum msm_ldo_supply_mode enable_mode;
- mutex_lock(&ldo_vreg->ldo_mutex);
- pr_debug("regulator_enable requested. corner=%d\n",
- ldo_vreg->corner + MIN_CORNER_OFFSET);
- if (ldo_vreg->vdd_cx) {
- rc = regulator_set_voltage(ldo_vreg->vdd_cx,
- ldo_vreg->vdd_cx_corner_map[ldo_vreg->corner],
- INT_MAX);
- if (rc) {
- pr_err("Unable to set CX for corner %d rc=%d\n",
- ldo_vreg->corner + MIN_CORNER_OFFSET, rc);
- goto fail;
- }
- rc = regulator_enable(ldo_vreg->vdd_cx);
- if (rc) {
- pr_err("regulator_enable: vdd_cx: failed rc=%d\n", rc);
- goto fail;
- }
- }
- enable_mode = get_operating_mode(ldo_vreg, ldo_vreg->corner);
- if (enable_mode == LDO_MODE) {
- new_uv = ldo_vreg->open_loop_volt[ldo_vreg->corner];
- rc = enable_ldo_mode(ldo_vreg, new_uv);
- pr_debug("LDO voltage configured =%d uV corner=%d\n",
- ldo_vreg->ldo_voltage_uv,
- ldo_vreg->corner + MIN_CORNER_OFFSET);
- } else {
- rc = enable_bhs_mode(ldo_vreg);
- }
- if (rc) {
- pr_err("Failed to enable regulator in %s mode rc=%d\n",
- (enable_mode == LDO_MODE) ? "LDO" : "BHS", rc);
- goto disable_cx;
- }
- pr_debug("regulator_enable complete. mode=%s, corner=%d\n",
- (enable_mode == LDO_MODE) ? "LDO" : "BHS",
- ldo_vreg->corner + MIN_CORNER_OFFSET);
- ldo_vreg->mode = enable_mode;
- ldo_vreg->vreg_enabled = true;
- disable_cx:
- if (rc && ldo_vreg->vdd_cx) {
- rc = regulator_disable(ldo_vreg->vdd_cx);
- if (rc)
- pr_err("regulator_enable: vdd_cx: failed rc=%d\n", rc);
- }
- fail:
- mutex_unlock(&ldo_vreg->ldo_mutex);
- return rc;
- }
- static int msm_gfx_ldo_disable(struct regulator_dev *rdev)
- {
- int rc = 0;
- struct msm_gfx_ldo *ldo_vreg = rdev_get_drvdata(rdev);
- mutex_lock(&ldo_vreg->ldo_mutex);
- if (ldo_vreg->vdd_cx) {
- rc = regulator_disable(ldo_vreg->vdd_cx);
- if (rc) {
- pr_err("regulator_disable: vdd_cx: failed rc=%d\n", rc);
- goto done;
- }
- rc = regulator_set_voltage(ldo_vreg->vdd_cx, 0, INT_MAX);
- if (rc)
- pr_err("failed to set voltage on CX rc=%d\n", rc);
- }
- /* No additional configuration for LDO/BHS - taken care by gsdc */
- ldo_vreg->vreg_enabled = false;
- pr_debug("regulator_disabled complete\n");
- done:
- mutex_unlock(&ldo_vreg->ldo_mutex);
- return rc;
- }
- static int switch_mode_to_ldo(struct msm_gfx_ldo *ldo_vreg, int new_uv)
- {
- u32 ctl = 0, status = 0, timeout = 50;
- ctl = readl_relaxed(ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
- if (ldo_vreg->ops_type == CORNER) {
- /* enable CPR bypass mode for LDO */
- ctl |= CPR_BYPASS_IN_LDO_MODE_BIT;
- writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
- }
- /* fake ack to GDSC */
- ctl |= ACK_SW_OVR_BIT;
- writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
- /* set power-source as LDO */
- ctl |= PWR_SRC_SEL_BIT;
- writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
- /* Make sure BHS continues to power the rail */
- ctl |= BHS_EN_FEW_BIT;
- writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
- ctl |= BHS_EN_REST_BIT;
- writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
- /* move BHS to SW control */
- ctl |= BHS_UNDER_SW_CTL;
- writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
- /* set LDO under SW control */
- ctl |= LDO_UNDER_SW_CTRL_BIT;
- writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
- /* bypass LDO */
- ctl |= LDO_BYPASS_BIT;
- writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
- /* power-on LDO */
- ctl &= ~LDO_PDN_BIT;
- writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
- /* set the new LDO voltage */
- ldo_update_voltage(ldo_vreg, new_uv);
- pr_debug("LDO voltage =%d uV\n", ldo_vreg->ldo_voltage_uv);
- /* make sure that the configuration is complete */
- mb();
- /* power down BHS */
- ctl &= ~BHS_EN_FEW_BIT;
- writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
- ctl &= ~BHS_EN_REST_BIT;
- writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
- /* make sure that the configuration is complete */
- mb();
- /* wait for BHS to turn-off */
- while (--timeout) {
- status = readl_relaxed(ldo_vreg->ldo_base +
- PWRSWITCH_STATUS_REG);
- if (!(status & BHS_EN_REST_ACK_BIT))
- break;
- udelay(10);
- }
- if (!timeout)
- pr_err("BHS_EN_RESET_ACK not clear PWRSWITCH_STATUS = 0x%x\n",
- status);
- /* remove LDO bypass */
- ctl &= ~LDO_BYPASS_BIT;
- writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
- /* expose LDO to gdsc */
- ctl &= ~ACK_SW_OVR_BIT;
- writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
- ctl &= ~LDO_UNDER_SW_CTRL_BIT;
- writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
- dump_registers(ldo_vreg, "switch_mode_to_ldo");
- return 0;
- }
- static int switch_mode_to_bhs(struct msm_gfx_ldo *ldo_vreg)
- {
- u32 ctl = 0, status = 0, timeout = 50;
- ctl = readl_relaxed(ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
- /* fake ack to gdsc */
- ctl |= ACK_SW_OVR_BIT;
- writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
- /* select BHS as power source */
- ctl &= ~PWR_SRC_SEL_BIT;
- writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
- /* LDO stays ON */
- ctl &= ~LDO_PDN_BIT;
- writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
- /* Move LDO to SW control */
- ctl |= LDO_UNDER_SW_CTRL_BIT;
- writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
- /* Power-up BHS */
- ctl |= BHS_EN_FEW_BIT;
- writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
- ctl |= BHS_EN_REST_BIT;
- writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
- /* make sure that the configuration is complete */
- mb();
- /* wait for BHS to power-up */
- while (--timeout) {
- status = readl_relaxed(ldo_vreg->ldo_base +
- PWRSWITCH_STATUS_REG);
- if (status & BHS_EN_REST_ACK_BIT)
- break;
- udelay(10);
- }
- if (!timeout)
- pr_err("BHS_EN_RESET_ACK not set PWRSWITCH_STATUS = 0x%x\n",
- status);
- /* bypass LDO */
- ctl |= LDO_BYPASS_BIT;
- writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
- /* pull-down LDO */
- ctl |= LDO_PDN_BIT;
- writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
- /* Expose BHS to gdsc */
- ctl &= ~ACK_SW_OVR_BIT;
- writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
- ctl &= ~BHS_UNDER_SW_CTL;
- writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
- if (ldo_vreg->ops_type == CORNER) {
- /* Enable CPR in BHS mode */
- ctl &= ~CPR_BYPASS_IN_LDO_MODE_BIT;
- writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
- }
- /* make sure that all configuration is complete */
- mb();
- dump_registers(ldo_vreg, "switch_mode_to_bhs");
- return 0;
- }
- static int msm_gfx_ldo_set_corner(struct regulator_dev *rdev,
- int corner, int corner_max, unsigned int *selector)
- {
- struct msm_gfx_ldo *ldo_vreg = rdev_get_drvdata(rdev);
- int rc = 0, mem_acc_corner, new_uv;
- enum msm_ldo_supply_mode new_mode;
- enum direction dir = NO_CHANGE;
- corner -= MIN_CORNER_OFFSET;
- corner_max -= MIN_CORNER_OFFSET;
- mutex_lock(&ldo_vreg->ldo_mutex);
- if (corner == ldo_vreg->corner)
- goto done;
- pr_debug("set-voltage requested: old_mode=%s old_corner=%d new_corner=%d vreg_enabled=%d\n",
- ldo_vreg->mode == BHS_MODE ? "BHS" : "LDO",
- ldo_vreg->corner + MIN_CORNER_OFFSET,
- corner + MIN_CORNER_OFFSET,
- ldo_vreg->vreg_enabled);
- if (corner > ldo_vreg->corner)
- dir = UP;
- else if (corner < ldo_vreg->corner)
- dir = DOWN;
- if (ldo_vreg->mem_acc_vreg && dir == DOWN) {
- mem_acc_corner = ldo_vreg->mem_acc_corner_map[corner];
- rc = regulator_set_voltage(ldo_vreg->mem_acc_vreg,
- mem_acc_corner, mem_acc_corner);
- }
- if (!ldo_vreg->vreg_enabled) {
- ldo_vreg->corner = corner;
- goto done;
- }
- if (ldo_vreg->vdd_cx) {
- rc = regulator_set_voltage(ldo_vreg->vdd_cx,
- ldo_vreg->vdd_cx_corner_map[corner],
- INT_MAX);
- if (rc) {
- pr_err("Unable to set CX for corner %d rc=%d\n",
- corner + MIN_CORNER_OFFSET, rc);
- goto done;
- }
- }
- new_mode = get_operating_mode(ldo_vreg, corner);
- if (new_mode == BHS_MODE) {
- if (ldo_vreg->mode == LDO_MODE) {
- rc = switch_mode_to_bhs(ldo_vreg);
- if (rc)
- pr_err("Switch to BHS corner=%d failed rc=%d\n",
- corner + MIN_CORNER_OFFSET, rc);
- }
- } else { /* new mode - LDO */
- new_uv = ldo_vreg->open_loop_volt[ldo_vreg->corner];
- if (ldo_vreg->mode == BHS_MODE) {
- rc = switch_mode_to_ldo(ldo_vreg, new_uv);
- if (rc)
- pr_err("Switch to LDO failed corner=%d rc=%d\n",
- corner + MIN_CORNER_OFFSET, rc);
- } else {
- rc = ldo_update_voltage(ldo_vreg, new_uv);
- if (rc)
- pr_err("Update voltage failed corner=%d rc=%d\n",
- corner + MIN_CORNER_OFFSET, rc);
- }
- }
- if (!rc) {
- pr_debug("set-voltage complete. old_mode=%s new_mode=%s old_corner=%d new_corner=%d\n",
- ldo_vreg->mode == BHS_MODE ? "BHS" : "LDO",
- new_mode == BHS_MODE ? "BHS" : "LDO",
- ldo_vreg->corner + MIN_CORNER_OFFSET,
- corner + MIN_CORNER_OFFSET);
- ldo_vreg->mode = new_mode;
- ldo_vreg->corner = corner;
- }
- done:
- if (!rc && ldo_vreg->mem_acc_vreg && dir == UP) {
- mem_acc_corner = ldo_vreg->mem_acc_corner_map[corner];
- rc = regulator_set_voltage(ldo_vreg->mem_acc_vreg,
- mem_acc_corner, mem_acc_corner);
- }
- mutex_unlock(&ldo_vreg->ldo_mutex);
- return rc;
- }
- static int msm_gfx_ldo_get_corner(struct regulator_dev *rdev)
- {
- struct msm_gfx_ldo *ldo_vreg = rdev_get_drvdata(rdev);
- return ldo_vreg->corner + MIN_CORNER_OFFSET;
- }
- static int msm_gfx_ldo_is_enabled(struct regulator_dev *rdev)
- {
- struct msm_gfx_ldo *ldo_vreg = rdev_get_drvdata(rdev);
- return ldo_vreg->vreg_enabled;
- }
- /**
- * msm_gfx_ldo_list_corner_voltage() - return the ldo voltage mapped to
- * the specified voltage corner
- * @rdev: Regulator device pointer for the msm_gfx_ldo
- * @corner: Voltage corner
- *
- * Return: voltage value in microvolts or -EINVAL if the corner is out of range
- */
- static int msm_gfx_ldo_list_corner_voltage(struct regulator_dev *rdev,
- int corner)
- {
- struct msm_gfx_ldo *ldo_vreg = rdev_get_drvdata(rdev);
- corner -= MIN_CORNER_OFFSET;
- if (corner >= 0 && corner < ldo_vreg->num_corners)
- return ldo_vreg->open_loop_volt[corner];
- else
- return -EINVAL;
- }
- static struct regulator_ops msm_gfx_ldo_corner_ops = {
- .enable = msm_gfx_ldo_corner_enable,
- .disable = msm_gfx_ldo_disable,
- .is_enabled = msm_gfx_ldo_is_enabled,
- .set_voltage = msm_gfx_ldo_set_corner,
- .get_voltage = msm_gfx_ldo_get_corner,
- .list_corner_voltage = msm_gfx_ldo_list_corner_voltage,
- };
- static int msm_gfx_ldo_get_bypass(struct regulator_dev *rdev,
- bool *enable)
- {
- struct msm_gfx_ldo *ldo_vreg = rdev_get_drvdata(rdev);
- *enable = ldo_vreg->mode;
- return 0;
- }
- static int msm_gfx_ldo_set_bypass(struct regulator_dev *rdev,
- bool mode)
- {
- struct msm_gfx_ldo *ldo_vreg = rdev_get_drvdata(rdev);
- int rc = 0;
- mutex_lock(&ldo_vreg->ldo_mutex);
- if (ldo_vreg->mode == mode || !ldo_vreg->vreg_enabled)
- goto done;
- if (mode == LDO_MODE)
- rc = switch_mode_to_ldo(ldo_vreg, ldo_vreg->ldo_voltage_uv);
- else
- rc = switch_mode_to_bhs(ldo_vreg);
- if (rc) {
- pr_err("Failed to configure regulator in %s mode rc=%d\n",
- (mode == LDO_MODE) ? "LDO" : "BHS", rc);
- goto done;
- }
- pr_debug("regulator_set_bypass complete. mode=%s, voltage = %d uV\n",
- (mode == LDO_MODE) ? "LDO" : "BHS",
- (mode == LDO_MODE) ? ldo_vreg->ldo_voltage_uv : 0);
- ldo_vreg->mode = mode;
- done:
- mutex_unlock(&ldo_vreg->ldo_mutex);
- return rc;
- }
- static int msm_gfx_ldo_voltage_enable(struct regulator_dev *rdev)
- {
- struct msm_gfx_ldo *ldo_vreg = rdev_get_drvdata(rdev);
- int rc = 0;
- enum msm_ldo_supply_mode enable_mode;
- mutex_lock(&ldo_vreg->ldo_mutex);
- pr_debug("regulator_enable requested. voltage=%d\n",
- ldo_vreg->ldo_voltage_uv);
- enable_mode = ldo_vreg->mode;
- if (enable_mode == LDO_MODE)
- rc = enable_ldo_mode(ldo_vreg, ldo_vreg->ldo_voltage_uv);
- else
- rc = enable_bhs_mode(ldo_vreg);
- if (rc) {
- pr_err("Failed to enable regulator in %s mode rc=%d\n",
- (enable_mode == LDO_MODE) ? "LDO" : "BHS", rc);
- goto fail;
- }
- pr_debug("regulator_enable complete. mode=%s, voltage = %d uV\n",
- (enable_mode == LDO_MODE) ? "LDO" : "BHS",
- (enable_mode == LDO_MODE) ? ldo_vreg->ldo_voltage_uv : 0);
- ldo_vreg->vreg_enabled = true;
- fail:
- mutex_unlock(&ldo_vreg->ldo_mutex);
- return rc;
- }
- static int msm_gfx_ldo_set_voltage(struct regulator_dev *rdev,
- int new_uv, int max_uv, unsigned int *selector)
- {
- struct msm_gfx_ldo *ldo_vreg = rdev_get_drvdata(rdev);
- int rc = 0;
- mutex_lock(&ldo_vreg->ldo_mutex);
- if (new_uv == ldo_vreg->ldo_voltage_uv)
- goto done;
- if (!ldo_vreg->vreg_enabled || ldo_vreg->mode != LDO_MODE) {
- ldo_vreg->ldo_voltage_uv = new_uv;
- goto done;
- }
- /* update LDO voltage */
- rc = ldo_update_voltage(ldo_vreg, new_uv);
- if (rc)
- pr_err("Update voltage failed for [%d, %d], rc=%d\n",
- new_uv, max_uv, rc);
- done:
- mutex_unlock(&ldo_vreg->ldo_mutex);
- return rc;
- }
- static int msm_gfx_ldo_get_voltage(struct regulator_dev *rdev)
- {
- struct msm_gfx_ldo *ldo_vreg = rdev_get_drvdata(rdev);
- return ldo_vreg->ldo_voltage_uv;
- }
- static struct regulator_ops msm_gfx_ldo_voltage_ops = {
- .enable = msm_gfx_ldo_voltage_enable,
- .disable = msm_gfx_ldo_disable,
- .is_enabled = msm_gfx_ldo_is_enabled,
- .set_voltage = msm_gfx_ldo_set_voltage,
- .get_voltage = msm_gfx_ldo_get_voltage,
- .set_bypass = msm_gfx_ldo_set_bypass,
- .get_bypass = msm_gfx_ldo_get_bypass,
- };
- static int msm_gfx_ldo_adjust_init_voltage(struct msm_gfx_ldo *ldo_vreg)
- {
- int rc, len, size, i;
- u32 *volt_adjust;
- struct device_node *of_node = ldo_vreg->dev->of_node;
- char *prop_name = "qcom,ldo-init-voltage-adjustment";
- if (!of_find_property(of_node, prop_name, &len)) {
- /* No initial voltage adjustment needed. */
- return 0;
- }
- size = len / sizeof(u32);
- if (size != ldo_vreg->num_ldo_corners) {
- pr_err("%s length=%d is invalid: required:%d\n",
- prop_name, size, ldo_vreg->num_ldo_corners);
- return -EINVAL;
- }
- volt_adjust = devm_kcalloc(ldo_vreg->dev, size, sizeof(*volt_adjust),
- GFP_KERNEL);
- if (!volt_adjust)
- return -ENOMEM;
- rc = of_property_read_u32_array(of_node, prop_name, volt_adjust, size);
- if (rc) {
- pr_err("failed to read %s property rc=%d\n", prop_name, rc);
- return rc;
- }
- for (i = 0; i < ldo_vreg->num_corners; i++) {
- if (volt_adjust[i]) {
- ldo_vreg->open_loop_volt[i] += volt_adjust[i];
- pr_info("adjusted the open-loop voltage[%d] %d -> %d\n",
- i + MIN_CORNER_OFFSET,
- ldo_vreg->open_loop_volt[i] - volt_adjust[i],
- ldo_vreg->open_loop_volt[i]);
- }
- }
- return 0;
- }
- static int msm_gfx_ldo_voltage_init(struct msm_gfx_ldo *ldo_vreg)
- {
- struct device_node *of_node = ldo_vreg->dev->of_node;
- int i, rc, len;
- u64 efuse_bits;
- len = ldo_vreg->num_ldo_corners;
- ldo_vreg->open_loop_volt = devm_kcalloc(ldo_vreg->dev,
- len, sizeof(*ldo_vreg->open_loop_volt),
- GFP_KERNEL);
- ldo_vreg->ceiling_volt = devm_kcalloc(ldo_vreg->dev,
- len, sizeof(*ldo_vreg->ceiling_volt),
- GFP_KERNEL);
- ldo_vreg->floor_volt = devm_kcalloc(ldo_vreg->dev,
- len, sizeof(*ldo_vreg->floor_volt),
- GFP_KERNEL);
- if (!ldo_vreg->open_loop_volt || !ldo_vreg->ceiling_volt
- || !ldo_vreg->floor_volt)
- return -ENOMEM;
- rc = of_property_read_u32_array(of_node, "qcom,ldo-voltage-ceiling",
- ldo_vreg->ceiling_volt, len);
- if (rc) {
- pr_err("Unable to read qcom,ldo-voltage-ceiling rc=%d\n", rc);
- return rc;
- }
- rc = of_property_read_u32_array(of_node, "qcom,ldo-voltage-floor",
- ldo_vreg->floor_volt, len);
- if (rc) {
- pr_err("Unable to read qcom,ldo-voltage-floor rc=%d\n", rc);
- return rc;
- }
- for (i = 0; i < ldo_vreg->num_ldo_corners; i++) {
- rc = read_fuse_param(ldo_vreg->efuse_base,
- ldo_vreg->init_volt_param[i],
- &efuse_bits);
- if (rc) {
- pr_err("Unable to read init-voltage rc=%d\n", rc);
- return rc;
- }
- ldo_vreg->open_loop_volt[i] = convert_open_loop_voltage_fuse(
- ldo_vreg->ref_volt[i],
- GFX_LDO_FUSE_STEP_VOLT,
- efuse_bits,
- GFX_LDO_FUSE_SIZE);
- pr_info("LDO corner %d: target-volt = %d uV\n",
- i + MIN_CORNER_OFFSET, ldo_vreg->open_loop_volt[i]);
- }
- rc = msm_gfx_ldo_adjust_init_voltage(ldo_vreg);
- if (rc) {
- pr_err("Unable to adjust init voltages rc=%d\n", rc);
- return rc;
- }
- for (i = 0; i < ldo_vreg->num_ldo_corners; i++) {
- if (ldo_vreg->open_loop_volt[i] > ldo_vreg->ceiling_volt[i]) {
- pr_info("Warning: initial voltage[%d] %d above ceiling %d\n",
- i + MIN_CORNER_OFFSET,
- ldo_vreg->open_loop_volt[i],
- ldo_vreg->ceiling_volt[i]);
- ldo_vreg->open_loop_volt[i] = ldo_vreg->ceiling_volt[i];
- } else if (ldo_vreg->open_loop_volt[i] <
- ldo_vreg->floor_volt[i]) {
- pr_info("Warning: initial voltage[%d] %d below floor %d\n",
- i + MIN_CORNER_OFFSET,
- ldo_vreg->open_loop_volt[i],
- ldo_vreg->floor_volt[i]);
- ldo_vreg->open_loop_volt[i] = ldo_vreg->floor_volt[i];
- }
- }
- efuse_bits = 0;
- rc = read_fuse_param(ldo_vreg->efuse_base, ldo_vreg->ldo_enable_param,
- &efuse_bits);
- if (rc) {
- pr_err("Unable to read ldo_enable_param rc=%d\n", rc);
- return rc;
- }
- ldo_vreg->ldo_fuse_enable = !!efuse_bits;
- pr_info("LDO-mode fuse %s by default\n", ldo_vreg->ldo_fuse_enable ?
- "enabled" : "disabled");
- return rc;
- }
- static int msm_gfx_ldo_efuse_init(struct platform_device *pdev,
- struct msm_gfx_ldo *ldo_vreg)
- {
- struct resource *res;
- u32 len;
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "efuse_addr");
- if (!res || !res->start) {
- pr_err("efuse_addr missing: res=%p\n", res);
- return -EINVAL;
- }
- ldo_vreg->efuse_addr = res->start;
- len = res->end - res->start + 1;
- ldo_vreg->efuse_base = devm_ioremap(&pdev->dev,
- ldo_vreg->efuse_addr, len);
- if (!ldo_vreg->efuse_base) {
- pr_err("Unable to map efuse_addr %pa\n",
- &ldo_vreg->efuse_addr);
- return -EINVAL;
- }
- return 0;
- }
- static int msm_gfx_ldo_mem_acc_init(struct msm_gfx_ldo *ldo_vreg)
- {
- int rc;
- u32 len, size;
- struct device_node *of_node = ldo_vreg->dev->of_node;
- if (of_find_property(ldo_vreg->dev->of_node, "mem-acc-supply", NULL)) {
- ldo_vreg->mem_acc_vreg = devm_regulator_get(ldo_vreg->dev,
- "mem-acc");
- if (IS_ERR_OR_NULL(ldo_vreg->mem_acc_vreg)) {
- rc = PTR_RET(ldo_vreg->mem_acc_vreg);
- if (rc != -EPROBE_DEFER)
- pr_err("devm_regulator_get: mem-acc: rc=%d\n",
- rc);
- return rc;
- }
- } else {
- pr_debug("mem-acc-supply not specified\n");
- return 0;
- }
- if (!of_find_property(of_node, "qcom,mem-acc-corner-map", &len)) {
- pr_err("qcom,mem-acc-corner-map missing");
- return -EINVAL;
- }
- size = len / sizeof(u32);
- if (size != ldo_vreg->num_corners) {
- pr_err("qcom,mem-acc-corner-map length=%d is invalid: required:%u\n",
- size, ldo_vreg->num_corners);
- return -EINVAL;
- }
- ldo_vreg->mem_acc_corner_map = devm_kcalloc(ldo_vreg->dev, size,
- sizeof(*ldo_vreg->mem_acc_corner_map), GFP_KERNEL);
- if (!ldo_vreg->mem_acc_corner_map)
- return -ENOMEM;
- rc = of_property_read_u32_array(of_node, "qcom,mem-acc-corner-map",
- ldo_vreg->mem_acc_corner_map, size);
- if (rc)
- pr_err("Unable to read qcom,mem-acc-corner-map rc=%d\n", rc);
- return rc;
- }
- static int msm_gfx_ldo_init(struct platform_device *pdev,
- struct msm_gfx_ldo *ldo_vreg)
- {
- struct resource *res;
- u32 len, ctl;
- int i = 0;
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ldo_addr");
- if (!res || !res->start) {
- pr_err("ldo_addr missing: res=%p\n", res);
- return -EINVAL;
- }
- ldo_vreg->ldo_addr = res->start;
- len = res->end - res->start + 1;
- ldo_vreg->ldo_base = devm_ioremap(ldo_vreg->dev,
- ldo_vreg->ldo_addr, len);
- if (!ldo_vreg->ldo_base) {
- pr_err("Unable to map efuse_addr %pa\n",
- &ldo_vreg->ldo_addr);
- return -EINVAL;
- }
- /* HW initialization */
- /* clear clamp_io, enable CPR in auto-bypass*/
- ctl = readl_relaxed(ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
- ctl &= ~LDO_CLAMP_IO_BIT;
- ctl |= EN_LDOAP_CTRL_CPR_BIT;
- writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
- i = 0;
- while (ldo_vreg->ldo_init_config &&
- ldo_vreg->ldo_init_config[i].offset != LDO_MAX_OFFSET) {
- writel_relaxed(ldo_vreg->ldo_init_config[i].value,
- ldo_vreg->ldo_base +
- ldo_vreg->ldo_init_config[i].offset);
- i++;
- }
- /* complete the writes */
- mb();
- return 0;
- }
- static int ldo_parse_cx_parameters(struct msm_gfx_ldo *ldo_vreg)
- {
- struct device_node *of_node = ldo_vreg->dev->of_node;
- int rc, len, size;
- if (of_find_property(of_node, "vdd-cx-supply", NULL)) {
- ldo_vreg->vdd_cx = devm_regulator_get(ldo_vreg->dev, "vdd-cx");
- if (IS_ERR_OR_NULL(ldo_vreg->vdd_cx)) {
- rc = PTR_RET(ldo_vreg->vdd_cx);
- if (rc != -EPROBE_DEFER)
- pr_err("devm_regulator_get: vdd-cx: rc=%d\n",
- rc);
- return rc;
- }
- } else {
- pr_debug("vdd-cx-supply not specified\n");
- return 0;
- }
- if (!of_find_property(of_node, "qcom,vdd-cx-corner-map", &len)) {
- pr_err("qcom,vdd-cx-corner-map missing");
- return -EINVAL;
- }
- size = len / sizeof(u32);
- if (size != ldo_vreg->num_corners) {
- pr_err("qcom,vdd-cx-corner-map length=%d is invalid: required:%u\n",
- size, ldo_vreg->num_corners);
- return -EINVAL;
- }
- ldo_vreg->vdd_cx_corner_map = devm_kcalloc(ldo_vreg->dev, size,
- sizeof(*ldo_vreg->vdd_cx_corner_map), GFP_KERNEL);
- if (!ldo_vreg->vdd_cx_corner_map)
- return -ENOMEM;
- rc = of_property_read_u32_array(of_node, "qcom,vdd-cx-corner-map",
- ldo_vreg->vdd_cx_corner_map, size);
- if (rc)
- pr_err("Unable to read qcom,vdd-cx-corner-map rc=%d\n", rc);
- return rc;
- }
- static int msm_gfx_ldo_parse_dt(struct msm_gfx_ldo *ldo_vreg)
- {
- struct device_node *of_node = ldo_vreg->dev->of_node;
- int rc, size, len;
- rc = of_property_read_u32(of_node, "qcom,num-corners",
- &ldo_vreg->num_corners);
- if (rc < 0) {
- pr_err("Unable to read qcom,num-corners rc=%d\n", rc);
- return rc;
- }
- rc = of_property_read_u32(of_node, "qcom,num-ldo-corners",
- &ldo_vreg->num_ldo_corners);
- if (rc) {
- pr_err("Unable to read qcom,num-ldo-corners rc=%d\n", rc);
- return rc;
- }
- rc = of_property_read_u32(of_node, "qcom,init-corner",
- &ldo_vreg->corner);
- if (rc) {
- pr_err("Unable to read qcom,init-corner rc=%d\n", rc);
- return rc;
- }
- if (!of_find_property(of_node, "qcom,ldo-enable-corner-map", &len)) {
- pr_err("qcom,ldo-enable-corner-map missing\n");
- return -EINVAL;
- }
- size = len / sizeof(u32);
- if (size != ldo_vreg->num_corners) {
- pr_err("qcom,ldo-enable-corner-map length=%d is invalid: required:%u\n",
- size, ldo_vreg->num_corners);
- return -EINVAL;
- }
- ldo_vreg->ldo_corner_en_map = devm_kcalloc(ldo_vreg->dev, size,
- sizeof(*ldo_vreg->ldo_corner_en_map), GFP_KERNEL);
- if (!ldo_vreg->ldo_corner_en_map)
- return -ENOMEM;
- rc = of_property_read_u32_array(of_node, "qcom,ldo-enable-corner-map",
- ldo_vreg->ldo_corner_en_map, size);
- if (rc) {
- pr_err("Unable to read qcom,ldo-enable-corner-map rc=%d\n",
- rc);
- return rc;
- }
- rc = ldo_parse_cx_parameters(ldo_vreg);
- if (rc) {
- pr_err("Unable to parse CX parameters rc=%d\n", rc);
- return rc;
- }
- return 0;
- }
- static int msm_gfx_ldo_target_init(struct msm_gfx_ldo *ldo_vreg)
- {
- int i;
- /* MSM8953 */
- ldo_vreg->init_volt_param = devm_kzalloc(ldo_vreg->dev,
- (MSM8953_LDO_FUSE_CORNERS *
- sizeof(struct fuse_param *)), GFP_KERNEL);
- if (!ldo_vreg->init_volt_param)
- return -ENOMEM;
- for (i = 0; i < MSM8953_LDO_FUSE_CORNERS; i++)
- ldo_vreg->init_volt_param[i] =
- msm8953_init_voltage_param[i];
- ldo_vreg->ref_volt = msm8953_fuse_ref_volt;
- ldo_vreg->ldo_enable_param = msm8953_ldo_enable_param;
- return 0;
- }
- static int debugfs_ldo_mode_disable_set(void *data, u64 val)
- {
- struct msm_gfx_ldo *ldo_vreg = data;
- ldo_vreg->ldo_mode_disable = !!val;
- pr_debug("LDO-mode %s\n", ldo_vreg->ldo_mode_disable ?
- "disabled" : "enabled");
- return 0;
- }
- static int debugfs_ldo_mode_disable_get(void *data, u64 *val)
- {
- struct msm_gfx_ldo *ldo_vreg = data;
- *val = ldo_vreg->ldo_mode_disable;
- return 0;
- }
- DEFINE_SIMPLE_ATTRIBUTE(ldo_mode_disable_fops, debugfs_ldo_mode_disable_get,
- debugfs_ldo_mode_disable_set, "%llu\n");
- static int debugfs_ldo_set_voltage(void *data, u64 val)
- {
- struct msm_gfx_ldo *ldo_vreg = data;
- int rc = 0, timeout = 50;
- u32 reg = 0, voltage = 0;
- mutex_lock(&ldo_vreg->ldo_mutex);
- if (ldo_vreg->mode == BHS_MODE || !ldo_vreg->vreg_enabled ||
- val > MAX_LDO_VOLTAGE || val < MIN_LDO_VOLTAGE) {
- rc = -EINVAL;
- goto done;
- }
- voltage = GET_VREF((u32)val);
- reg = readl_relaxed(ldo_vreg->ldo_base + LDO_VREF_SET_REG);
- /* set the new voltage */
- reg &= ~VREF_VAL_MASK;
- reg |= voltage & VREF_VAL_MASK;
- writel_relaxed(reg, ldo_vreg->ldo_base + LDO_VREF_SET_REG);
- /* Initiate VREF update */
- reg |= UPDATE_VREF_BIT;
- writel_relaxed(reg, ldo_vreg->ldo_base + LDO_VREF_SET_REG);
- /* complete the writes */
- mb();
- reg &= ~UPDATE_VREF_BIT;
- writel_relaxed(reg, ldo_vreg->ldo_base + LDO_VREF_SET_REG);
- /* complete the writes */
- mb();
- while (--timeout) {
- reg = readl_relaxed(ldo_vreg->ldo_base +
- PWRSWITCH_STATUS_REG);
- if (reg & (LDO_VREF_SETTLED_BIT | LDO_READY_BIT))
- break;
- udelay(10);
- }
- if (!timeout) {
- pr_err("LDO_VREF_SETTLED not set PWRSWITCH_STATUS = 0x%x\n",
- reg);
- rc = -EBUSY;
- } else {
- ldo_vreg->ldo_voltage_uv = val;
- pr_debug("LDO voltage set to %d uV\n",
- ldo_vreg->ldo_voltage_uv);
- }
- done:
- mutex_unlock(&ldo_vreg->ldo_mutex);
- return rc;
- }
- static int debugfs_ldo_get_voltage(void *data, u64 *val)
- {
- struct msm_gfx_ldo *ldo_vreg = data;
- int rc = 0;
- u32 reg;
- mutex_lock(&ldo_vreg->ldo_mutex);
- if (ldo_vreg->mode == BHS_MODE || !ldo_vreg->vreg_enabled) {
- rc = -EINVAL;
- goto done;
- }
- reg = readl_relaxed(ldo_vreg->ldo_base + LDO_VREF_SET_REG);
- reg &= VREF_VAL_MASK;
- *val = (reg * LDO_STEP_VOLATGE) + MIN_LDO_VOLTAGE;
- done:
- mutex_unlock(&ldo_vreg->ldo_mutex);
- return rc;
- }
- DEFINE_SIMPLE_ATTRIBUTE(ldo_voltage_fops, debugfs_ldo_get_voltage,
- debugfs_ldo_set_voltage, "%llu\n");
- static int msm_gfx_ldo_debug_info_open(struct inode *inode, struct file *file)
- {
- file->private_data = inode->i_private;
- return 0;
- }
- static ssize_t msm_gfx_ldo_debug_info_read(struct file *file, char __user *buff,
- size_t count, loff_t *ppos)
- {
- struct msm_gfx_ldo *ldo_vreg = file->private_data;
- char *debugfs_buf;
- ssize_t len, ret = 0;
- u32 i = 0, reg[MAX_LDO_REGS];
- debugfs_buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
- if (!debugfs_buf)
- return -ENOMEM;
- mutex_lock(&ldo_vreg->ldo_mutex);
- len = snprintf(debugfs_buf + ret, PAGE_SIZE - ret,
- "Regulator_enable = %d Regulator mode = %s Corner = %d LDO-voltage = %d uV\n",
- ldo_vreg->vreg_enabled,
- ldo_vreg->mode == BHS_MODE ? "BHS" : "LDO",
- ldo_vreg->corner + MIN_CORNER_OFFSET,
- ldo_vreg->ldo_voltage_uv);
- ret += len;
- for (i = 0; i < MAX_LDO_REGS; i++) {
- reg[i] = 0;
- reg[i] = readl_relaxed(ldo_vreg->ldo_base + (i * 4));
- len = snprintf(debugfs_buf + ret, PAGE_SIZE - ret,
- "%s = 0x%x\n", register_str[i], reg[i]);
- ret += len;
- }
- mutex_unlock(&ldo_vreg->ldo_mutex);
- ret = simple_read_from_buffer(buff, count, ppos, debugfs_buf, ret);
- kfree(debugfs_buf);
- return ret;
- }
- static const struct file_operations msm_gfx_ldo_debug_info_fops = {
- .open = msm_gfx_ldo_debug_info_open,
- .read = msm_gfx_ldo_debug_info_read,
- };
- static void msm_gfx_ldo_debugfs_init(struct msm_gfx_ldo *ldo_vreg)
- {
- struct dentry *temp;
- ldo_vreg->debugfs = debugfs_create_dir("msm_gfx_ldo", NULL);
- if (!ldo_vreg->debugfs) {
- pr_err("Couldn't create debug dir\n");
- return;
- }
- temp = debugfs_create_file("debug_info", 0444, ldo_vreg->debugfs,
- ldo_vreg, &msm_gfx_ldo_debug_info_fops);
- if (IS_ERR_OR_NULL(temp)) {
- pr_err("debug_info node creation failed\n");
- return;
- }
- temp = debugfs_create_file("ldo_voltage", 0644, ldo_vreg->debugfs,
- ldo_vreg, &ldo_voltage_fops);
- if (IS_ERR_OR_NULL(temp)) {
- pr_err("ldo_voltage node creation failed\n");
- return;
- }
- temp = debugfs_create_file("ldo_mode_disable", 0644, ldo_vreg->debugfs,
- ldo_vreg, &ldo_mode_disable_fops);
- if (IS_ERR_OR_NULL(temp)) {
- pr_err("ldo_mode_disable node creation failed\n");
- return;
- }
- }
- static void msm_gfx_ldo_debugfs_remove(struct msm_gfx_ldo *ldo_vreg)
- {
- debugfs_remove_recursive(ldo_vreg->debugfs);
- }
- static int msm_gfx_ldo_corner_config_init(struct msm_gfx_ldo *ldo_vreg,
- struct platform_device *pdev)
- {
- int rc;
- rc = msm_gfx_ldo_target_init(ldo_vreg);
- if (rc) {
- pr_err("Unable to initialize target specific data rc=%d", rc);
- return rc;
- }
- rc = msm_gfx_ldo_parse_dt(ldo_vreg);
- if (rc) {
- pr_err("Unable to pasrse dt rc=%d\n", rc);
- return rc;
- }
- rc = msm_gfx_ldo_efuse_init(pdev, ldo_vreg);
- if (rc) {
- pr_err("efuse_init failed rc=%d\n", rc);
- return rc;
- }
- rc = msm_gfx_ldo_voltage_init(ldo_vreg);
- if (rc) {
- pr_err("ldo_voltage_init failed rc=%d\n", rc);
- return rc;
- }
- rc = msm_gfx_ldo_mem_acc_init(ldo_vreg);
- if (rc) {
- pr_err("Unable to initialize mem_acc rc=%d\n", rc);
- return rc;
- }
- return rc;
- };
- /* Data corresponds to the SoC revision */
- static const struct of_device_id msm_gfx_ldo_match_table[] = {
- {
- .compatible = "qcom,msm8953-gfx-ldo",
- .data = (void *)(uintptr_t)MSM8953_SOC_ID,
- },
- {
- .compatible = "qcom,sdm660-gfx-ldo",
- .data = (void *)(uintptr_t)SDM660_SOC_ID,
- },
- {}
- };
- static int msm_gfx_ldo_probe(struct platform_device *pdev)
- {
- struct msm_gfx_ldo *ldo_vreg;
- struct regulator_config reg_config = {};
- struct regulator_desc *rdesc;
- struct regulator_init_data *init_data = pdev->dev.platform_data;
- struct device *dev = &pdev->dev;
- const struct of_device_id *match;
- int soc_id, rc;
- match = of_match_device(msm_gfx_ldo_match_table, dev);
- if (!match)
- return -ENODEV;
- ldo_vreg = devm_kzalloc(dev, sizeof(*ldo_vreg), GFP_KERNEL);
- if (!ldo_vreg)
- return -ENOMEM;
- init_data = of_get_regulator_init_data(dev, dev->of_node, NULL);
- if (!init_data) {
- pr_err("regulator init data is missing\n");
- return -EINVAL;
- }
- init_data->constraints.input_uV = init_data->constraints.max_uV;
- init_data->constraints.valid_ops_mask
- |= REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS;
- ldo_vreg->rdesc.name = init_data->constraints.name;
- if (ldo_vreg->rdesc.name == NULL) {
- dev_err(dev, "regulator-name missing\n");
- return -EINVAL;
- }
- soc_id = (uintptr_t)match->data;
- ldo_vreg->dev = &pdev->dev;
- mutex_init(&ldo_vreg->ldo_mutex);
- platform_set_drvdata(pdev, ldo_vreg);
- switch (soc_id) {
- case MSM8953_SOC_ID:
- ldo_vreg->ldo_init_config = msm8953_ldo_config;
- ldo_vreg->ops_type = CORNER;
- rc = msm_gfx_ldo_corner_config_init(ldo_vreg, pdev);
- if (rc) {
- pr_err("ldo corner handling initialization failed, rc=%d\n",
- rc);
- return rc;
- }
- break;
- case SDM660_SOC_ID:
- ldo_vreg->ldo_init_config = sdm660_ldo_config;
- ldo_vreg->ops_type = VOLTAGE;
- init_data->constraints.valid_ops_mask
- |= REGULATOR_CHANGE_BYPASS;
- break;
- default:
- pr_err("invalid SOC ID = %d\n", soc_id);
- return -EINVAL;
- }
- /* HW initialization */
- rc = msm_gfx_ldo_init(pdev, ldo_vreg);
- if (rc) {
- pr_err("ldo_init failed rc=%d\n", rc);
- return rc;
- }
- rdesc = &ldo_vreg->rdesc;
- rdesc->owner = THIS_MODULE;
- rdesc->type = REGULATOR_VOLTAGE;
- if (ldo_vreg->ops_type == CORNER)
- rdesc->ops = &msm_gfx_ldo_corner_ops;
- else
- rdesc->ops = &msm_gfx_ldo_voltage_ops;
- reg_config.dev = &pdev->dev;
- reg_config.init_data = init_data;
- reg_config.driver_data = ldo_vreg;
- reg_config.of_node = pdev->dev.of_node;
- ldo_vreg->rdev = regulator_register(rdesc, ®_config);
- if (IS_ERR(ldo_vreg->rdev)) {
- rc = PTR_ERR(ldo_vreg->rdev);
- pr_err("regulator_register failed: rc=%d\n", rc);
- return rc;
- }
- msm_gfx_ldo_debugfs_init(ldo_vreg);
- return 0;
- }
- static int msm_gfx_ldo_remove(struct platform_device *pdev)
- {
- struct msm_gfx_ldo *ldo_vreg = platform_get_drvdata(pdev);
- regulator_unregister(ldo_vreg->rdev);
- msm_gfx_ldo_debugfs_remove(ldo_vreg);
- return 0;
- }
- static struct platform_driver msm_gfx_ldo_driver = {
- .driver = {
- .name = "qcom,msm-gfx-ldo",
- .of_match_table = msm_gfx_ldo_match_table,
- .owner = THIS_MODULE,
- },
- .probe = msm_gfx_ldo_probe,
- .remove = msm_gfx_ldo_remove,
- };
- static int msm_gfx_ldo_platform_init(void)
- {
- return platform_driver_register(&msm_gfx_ldo_driver);
- }
- arch_initcall(msm_gfx_ldo_platform_init);
- static void msm_gfx_ldo_platform_exit(void)
- {
- platform_driver_unregister(&msm_gfx_ldo_driver);
- }
- module_exit(msm_gfx_ldo_platform_exit);
- MODULE_DESCRIPTION("MSM GFX LDO driver");
- MODULE_LICENSE("GPL v2");
|