From patchwork Sat Jan 30 16:38:51 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laxman Dewangan X-Patchwork-Id: 576047 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from mail-oi0-x238.google.com (mail-oi0-x238.google.com [IPv6:2607:f8b0:4003:c06::238]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4B083140B95 for ; Sun, 31 Jan 2016 03:51:04 +1100 (AEDT) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=googlegroups.com header.i=@googlegroups.com header.b=LCS33xjw; dkim-atps=neutral Received: by mail-oi0-x238.google.com with SMTP id s2sf14928882oie.0 for ; Sat, 30 Jan 2016 08:51:04 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlegroups.com; s=20120806; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-type:x-original-sender :x-original-authentication-results:reply-to:precedence:mailing-list :list-id:x-spam-checked-in-group:list-post:list-help:list-archive :sender:list-subscribe:list-unsubscribe; bh=UuFRs2D+ggaQGApHeC4Exu6mOj57YAfMm4mScndIy1o=; b=LCS33xjwa5ZZPwWZnwecG/be3TowPXrh4rSIT1Nt+zUjw1Vh4evqDlGGvnWWc+MD7C d+F1AZMHz8fNVpct5pGXirRmnq4Zr5TxPOz2UBs6V1RPBSB0T2yDHcP5cVMj7TJwh+c9 +wH7TSkmXfjKOWh56VR9JbBuO1SWOs6/KCAwJkwRR4Qr1oILX5cfbBfnCrEU7iNJ7etY kpByQVgmMLcf2bsGLKM8U2iSOOUiVW8IyuVdmwdGxIS3DYehVMewhsjWeX8hyhohTkji Cqbi1xLNnvV6J4lkvDt5krQ98LE782hB2NxUBLUrinRns2wg4uC2vPQIUMKusX7oonI/ hAag== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-type:x-original-sender :x-original-authentication-results:reply-to:precedence:mailing-list :list-id:x-spam-checked-in-group:list-post:list-help:list-archive :sender:list-subscribe:list-unsubscribe; bh=UuFRs2D+ggaQGApHeC4Exu6mOj57YAfMm4mScndIy1o=; b=e4/UWMyqoAR4vIVILgzX3Wc4wsmxVqtSQgTrH8H7GxVKprrkkIoQfR8cUythkYGLex 45b6gLAdjH0zMogZc6zNJvX1awu+73IcgXM1Nv66KSU5C4hWibm8y0xbv2mzWiVtTsJw nHwIL/6rEK+flGqG5CUud0un5zkwH0+gRuabLRvcwoUi3ApyEex6+fmfdzVLKEOGlILt kF/+p5QHgk1h5TglumxTfXh7aGY4hdZCXerbzt46zeKc4Goo8rwl6hxVWEtwRUp4FO2o zZAu6JHQHYvOzbWCBBMSFFetArcixdc7bkLz25r0vTm6LqEVFkQJyqO1a5PrCAl3qq3T ND3Q== X-Gm-Message-State: AG10YOQCRJm8RFUa6rkiWmsTwWA9TWrq8ZSQ2/QLGfDK6IySAuI5/x8eJ+To8K53/4Wljw== X-Received: by 10.140.102.41 with SMTP id v38mr343170qge.2.1454172662528; Sat, 30 Jan 2016 08:51:02 -0800 (PST) X-BeenThere: rtc-linux@googlegroups.com Received: by 10.140.29.136 with SMTP id b8ls2802361qgb.96.gmail; Sat, 30 Jan 2016 08:51:02 -0800 (PST) X-Received: by 10.129.131.16 with SMTP id t16mr8663243ywf.37.1454172662145; Sat, 30 Jan 2016 08:51:02 -0800 (PST) Received: from hqemgate14.nvidia.com (hqemgate14.nvidia.com. [216.228.121.143]) by gmr-mx.google.com with ESMTPS id jg4si2588980pac.2.2016.01.30.08.51.01 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sat, 30 Jan 2016 08:51:02 -0800 (PST) Received-SPF: pass (google.com: domain of ldewangan@nvidia.com designates 216.228.121.143 as permitted sender) client-ip=216.228.121.143; Received: from hqnvupgp07.nvidia.com (Not Verified[216.228.121.13]) by hqemgate14.nvidia.com id ; Sat, 30 Jan 2016 08:50:59 -0800 Received: from hqemhub03.nvidia.com ([172.20.150.15]) by hqnvupgp07.nvidia.com (PGP Universal service); Sat, 30 Jan 2016 08:51:24 -0800 X-PGP-Universal: processed; by hqnvupgp07.nvidia.com on Sat, 30 Jan 2016 08:51:24 -0800 Received: from ldewanganubuntu-System-Product-Name.nvidia.com (172.20.144.16) by hqemhub03.nvidia.com (172.20.150.15) with Microsoft SMTP Server (TLS) id 8.3.406.0; Sat, 30 Jan 2016 08:51:00 -0800 From: Laxman Dewangan To: , , , , , , , , , , CC: , , , , , , , , , Laxman Dewangan , Mallikarjun Kasoju Subject: [rtc-linux] [PATCH V7 8/8] regulator: max77620: add regulator driver for max77620/max20024 Date: Sat, 30 Jan 2016 22:08:51 +0530 Message-ID: <1454171931-27752-9-git-send-email-ldewangan@nvidia.com> X-Mailer: git-send-email 2.1.4 In-Reply-To: <1454171931-27752-1-git-send-email-ldewangan@nvidia.com> References: <1454171931-27752-1-git-send-email-ldewangan@nvidia.com> MIME-Version: 1.0 X-Original-Sender: ldewangan@nvidia.com X-Original-Authentication-Results: gmr-mx.google.com; spf=pass (google.com: domain of ldewangan@nvidia.com designates 216.228.121.143 as permitted sender) smtp.mailfrom=ldewangan@nvidia.com Reply-To: rtc-linux@googlegroups.com Precedence: list Mailing-list: list rtc-linux@googlegroups.com; contact rtc-linux+owners@googlegroups.com List-ID: X-Spam-Checked-In-Group: rtc-linux@googlegroups.com X-Google-Group-Id: 712029733259 List-Post: , List-Help: , List-Archive: , List-Unsubscribe: , MAXIM Semiconductor's PMIC, MAX77620 and MAX20024 have the multiple DCDC and LDOs. This supplies the power to different components of the system. Also these rails has configuration for ramp time, flexible power sequence, slew rate etc. Add regulator driver to access these rails via regulator APIs. Signed-off-by: Laxman Dewangan Signed-off-by: Mallikarjun Kasoju Reviewed-by: Krzysztof Kozlowski --- - Cleanup code based on comment received on mfd/rtc. - Avoid duplication on error message. Changes form V2: - Run coccicheck and checkpatch in strict mode for the alignment. - Refactor Regulator driver to use core API for DT parsing. - Update based on API changes. Changes from V3: - Change all sys initcall to module driver. - change the max77620_read argument to unisgned int from u8. Changes from V4: - Added DT binding document as devicetree/bindings/regulator/regulator-max77620.txt - Detail out properties. Changes from V5: - Separate out DT binding doc for regulator. Changes from V6: - Use direct regmap APIs. - Drop active-discharge configuration as it wil be move to core and through callback. Follow on patches are palnned for this. drivers/mfd/max77620.c | 4 +- drivers/regulator/Kconfig | 9 + drivers/regulator/Makefile | 1 + drivers/regulator/max77620-regulator.c | 825 +++++++++++++++++++++++++++++++++ 3 files changed, 835 insertions(+) create mode 100644 drivers/regulator/max77620-regulator.c diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 8155e80..b92214b 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -343,6 +343,15 @@ config REGULATOR_MAX1586 regulator via I2C bus. The provided regulator is suitable for PXA27x chips to control VCC_CORE and VCC_USIM voltages. +config REGULATOR_MAX77620 + tristate "Maxim 77620/MAX20024 voltage regulator" + depends on MFD_MAX77620 + help + This driver controls Maxim MAX77620 voltage output regulator + via I2C bus. The provided regulator is suitable for Tegra + chip to control Step-Down DC-DC and LDOs. Say Y here to + enable the regulator driver. + config REGULATOR_MAX8649 tristate "Maxim 8649 voltage regulator" depends on I2C diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 980b194..2564c00 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -46,6 +46,7 @@ obj-$(CONFIG_REGULATOR_LP8755) += lp8755.o obj-$(CONFIG_REGULATOR_LTC3589) += ltc3589.o obj-$(CONFIG_REGULATOR_MAX14577) += max14577.o obj-$(CONFIG_REGULATOR_MAX1586) += max1586.o +obj-$(CONFIG_REGULATOR_MAX77620) += max77620-regulator.o obj-$(CONFIG_REGULATOR_MAX8649) += max8649.o obj-$(CONFIG_REGULATOR_MAX8660) += max8660.o obj-$(CONFIG_REGULATOR_MAX8907) += max8907-regulator.o diff --git a/drivers/regulator/max77620-regulator.c b/drivers/regulator/max77620-regulator.c new file mode 100644 index 0000000..fee8ad9 --- /dev/null +++ b/drivers/regulator/max77620-regulator.c @@ -0,0 +1,825 @@ +/* + * Maxim MAX77620 Regulator driver + * + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * + * Author: Mallikarjun Kasoju + * Laxman Dewangan + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define max77620_rails(_name) "max77620-"#_name + +/* Power Mode */ +#define MAX77620_POWER_MODE_NORMAL 3 +#define MAX77620_POWER_MODE_LPM 2 +#define MAX77620_POWER_MODE_GLPM 1 +#define MAX77620_POWER_MODE_DISABLE 0 + +/* SD Slew Rate */ +#define MAX77620_SD_SR_13_75 0 +#define MAX77620_SD_SR_27_5 1 +#define MAX77620_SD_SR_55 2 +#define MAX77620_SD_SR_100 3 + +enum max77620_regulators { + MAX77620_REGULATOR_ID_SD0, + MAX77620_REGULATOR_ID_SD1, + MAX77620_REGULATOR_ID_SD2, + MAX77620_REGULATOR_ID_SD3, + MAX77620_REGULATOR_ID_SD4, + MAX77620_REGULATOR_ID_LDO0, + MAX77620_REGULATOR_ID_LDO1, + MAX77620_REGULATOR_ID_LDO2, + MAX77620_REGULATOR_ID_LDO3, + MAX77620_REGULATOR_ID_LDO4, + MAX77620_REGULATOR_ID_LDO5, + MAX77620_REGULATOR_ID_LDO6, + MAX77620_REGULATOR_ID_LDO7, + MAX77620_REGULATOR_ID_LDO8, + MAX77620_NUM_REGS, +}; + +/* Regulator types */ +enum max77620_regulator_type { + MAX77620_REGULATOR_TYPE_SD, + MAX77620_REGULATOR_TYPE_LDO_N, + MAX77620_REGULATOR_TYPE_LDO_P, +}; + +struct max77620_regulator_info { + u8 type; + u32 min_uV; + u32 max_uV; + u32 step_uV; + u8 fps_addr; + u8 volt_addr; + u8 cfg_addr; + u8 volt_mask; + u8 power_mode_mask; + u8 power_mode_shift; + u8 remote_sense_addr; + u8 remote_sense_mask; + struct regulator_desc desc; +}; + +struct max77620_regulator_pdata { + struct regulator_init_data *reg_idata; + int active_fps_src; + int active_fps_pd_slot; + int active_fps_pu_slot; + int suspend_fps_src; + int suspend_fps_pd_slot; + int suspend_fps_pu_slot; + int current_mode; +}; + +struct max77620_regulator { + struct device *dev; + struct regmap *rmap; + struct max77620_regulator_info *rinfo[MAX77620_NUM_REGS]; + struct max77620_regulator_pdata reg_pdata[MAX77620_NUM_REGS]; + struct regulator_desc *rdesc[MAX77620_NUM_REGS]; + int enable_power_mode[MAX77620_NUM_REGS]; + int current_power_mode[MAX77620_NUM_REGS]; + int active_fps_src[MAX77620_NUM_REGS]; +}; + +#define fps_src_name(fps_src) \ + (fps_src == MAX77620_FPS_SRC_0 ? "FPS_SRC_0" : \ + fps_src == MAX77620_FPS_SRC_1 ? "FPS_SRC_1" : \ + fps_src == MAX77620_FPS_SRC_2 ? "FPS_SRC_2" : "FPS_SRC_NONE") + +static int max77620_regulator_get_fps_src(struct max77620_regulator *pmic, + int id) +{ + struct max77620_regulator_info *rinfo = pmic->rinfo[id]; + unsigned int val; + int ret; + + ret = regmap_read(pmic->rmap, rinfo->fps_addr, &val); + if (ret < 0) { + dev_err(pmic->dev, "Reg 0x%02x read failed %d\n", + rinfo->fps_addr, ret); + return ret; + } + + return (val & MAX77620_FPS_SRC_MASK) >> MAX77620_FPS_SRC_SHIFT; +} + +static int max77620_regulator_set_fps_src(struct max77620_regulator *pmic, + int fps_src, int id) +{ + struct max77620_regulator_info *rinfo = pmic->rinfo[id]; + unsigned int val; + int ret; + + switch (fps_src) { + case MAX77620_FPS_SRC_0: + case MAX77620_FPS_SRC_1: + case MAX77620_FPS_SRC_2: + case MAX77620_FPS_SRC_NONE: + break; + + case MAX77620_FPS_SRC_DEF: + ret = regmap_read(pmic->rmap, rinfo->fps_addr, &val); + if (ret < 0) { + dev_err(pmic->dev, "Reg 0x%02x read failed %d\n", + rinfo->fps_addr, ret); + return ret; + } + ret = (val & MAX77620_FPS_SRC_MASK) >> MAX77620_FPS_SRC_SHIFT; + pmic->active_fps_src[id] = ret; + return 0; + + default: + dev_err(pmic->dev, "Invalid FPS %d for regulator %d\n", + fps_src, id); + return -EINVAL; + } + + ret = regmap_update_bits(pmic->rmap, rinfo->fps_addr, + MAX77620_FPS_SRC_MASK, + fps_src << MAX77620_FPS_SRC_SHIFT); + if (ret < 0) { + dev_err(pmic->dev, "Reg 0x%02x update failed %d\n", + rinfo->fps_addr, ret); + return ret; + } + pmic->active_fps_src[id] = fps_src; + + return 0; +} + +static int max77620_regulator_set_fps_slots(struct max77620_regulator *pmic, + int id, bool is_suspend) +{ + struct max77620_regulator_pdata *rpdata = &pmic->reg_pdata[id]; + struct max77620_regulator_info *rinfo = pmic->rinfo[id]; + unsigned int val = 0; + unsigned int mask = 0; + int pu = rpdata->active_fps_pu_slot; + int pd = rpdata->active_fps_pd_slot; + int ret = 0; + + if (is_suspend) { + pu = rpdata->suspend_fps_pu_slot; + pd = rpdata->suspend_fps_pd_slot; + } + + /* FPS power up period setting */ + if (pu >= 0) { + val |= (pu << MAX77620_FPS_PU_PERIOD_SHIFT); + mask |= MAX77620_FPS_PU_PERIOD_MASK; + } + + /* FPS power down period setting */ + if (pd >= 0) { + val |= (pd << MAX77620_FPS_PD_PERIOD_SHIFT); + mask |= MAX77620_FPS_PD_PERIOD_MASK; + } + + if (mask) { + ret = regmap_update_bits(pmic->rmap, rinfo->fps_addr, + mask, val); + if (ret < 0) { + dev_err(pmic->dev, "Reg 0x%02x update failed: %d\n", + rinfo->fps_addr, ret); + return ret; + } + } + + return ret; +} + +static int max77620_regulator_set_power_mode(struct max77620_regulator *pmic, + int power_mode, int id) +{ + struct max77620_regulator_info *rinfo = pmic->rinfo[id]; + u8 mask = rinfo->power_mode_mask; + u8 shift = rinfo->power_mode_shift; + u8 addr; + int ret; + + switch (rinfo->type) { + case MAX77620_REGULATOR_TYPE_SD: + addr = rinfo->cfg_addr; + break; + default: + addr = rinfo->volt_addr; + break; + } + + ret = regmap_update_bits(pmic->rmap, addr, mask, power_mode << shift); + if (ret < 0) { + dev_err(pmic->dev, "Regulator %d mode set failed: %d\n", + id, ret); + return ret; + } + pmic->current_power_mode[id] = power_mode; + + return ret; +} + +static int max77620_regulator_get_power_mode(struct max77620_regulator *pmic, + int id) +{ + struct max77620_regulator_info *rinfo = pmic->rinfo[id]; + unsigned int val, addr; + u8 mask = rinfo->power_mode_mask; + u8 shift = rinfo->power_mode_shift; + int ret; + + switch (rinfo->type) { + case MAX77620_REGULATOR_TYPE_SD: + addr = rinfo->cfg_addr; + break; + default: + addr = rinfo->volt_addr; + break; + } + + ret = regmap_read(pmic->rmap, addr, &val); + if (ret < 0) { + dev_err(pmic->dev, "Regulator %d: Reg 0x%02x read failed: %d\n", + id, addr, ret); + return ret; + } + + return (val & mask) >> shift; +} + +static int max77620_read_slew_rate(struct max77620_regulator *pmic, int id) +{ + struct max77620_regulator_info *rinfo = pmic->rinfo[id]; + unsigned int rval; + int slew_rate; + int ret; + + switch (rinfo->type) { + case MAX77620_REGULATOR_TYPE_SD: + ret = regmap_read(pmic->rmap, rinfo->cfg_addr, &rval); + if (ret < 0) { + dev_err(pmic->dev, "Register 0x%02x read failed: %d\n", + rinfo->cfg_addr, ret); + return ret; + } + + slew_rate = (rval >> MAX77620_SD_SR_SHIFT) & 0x3; + switch (slew_rate) { + case 0: + slew_rate = 13750; + break; + case 1: + slew_rate = 27500; + break; + case 2: + slew_rate = 55000; + break; + case 3: + slew_rate = 100000; + break; + } + rinfo->desc.ramp_delay = slew_rate; + break; + default: + ret = regmap_read(pmic->rmap, rinfo->cfg_addr, &rval); + if (ret < 0) { + dev_err(pmic->dev, "Register 0x%02x read failed: %d\n", + rinfo->cfg_addr, ret); + return ret; + } + slew_rate = rval & 0x1; + switch (slew_rate) { + case 0: + slew_rate = 100000; + break; + case 1: + slew_rate = 5000; + break; + } + rinfo->desc.ramp_delay = slew_rate; + break; + } + + return 0; +} + +static int max77620_init_pmic(struct max77620_regulator *pmic, int id) +{ + struct max77620_regulator_pdata *rpdata = &pmic->reg_pdata[id]; + int ret; + + /* Update power mode */ + ret = max77620_regulator_get_power_mode(pmic, id); + if (ret < 0) + return ret; + + pmic->current_power_mode[id] = ret; + pmic->enable_power_mode[id] = MAX77620_POWER_MODE_NORMAL; + + if (rpdata->active_fps_src == MAX77620_FPS_SRC_DEF) { + ret = max77620_regulator_get_fps_src(pmic, id); + if (ret < 0) + return ret; + rpdata->active_fps_src = ret; + } + + /* If rails are externally control of FPS then enable it always. */ + if (rpdata->active_fps_src == MAX77620_FPS_SRC_NONE) { + ret = max77620_regulator_set_power_mode(pmic, + pmic->enable_power_mode[id], id); + if (ret < 0) + return ret; + } else { + if (pmic->current_power_mode[id] != + pmic->enable_power_mode[id]) { + ret = max77620_regulator_set_power_mode(pmic, + pmic->enable_power_mode[id], id); + if (ret < 0) + return ret; + } + } + + ret = max77620_regulator_set_fps_src(pmic, rpdata->active_fps_src, id); + if (ret < 0) + return ret; + + ret = max77620_regulator_set_fps_slots(pmic, id, false); + if (ret < 0) + return ret; + + return 0; +} + +static int max77620_regulator_enable(struct regulator_dev *rdev) +{ + struct max77620_regulator *pmic = rdev_get_drvdata(rdev); + int id = rdev_get_id(rdev); + + if (pmic->active_fps_src[id] != MAX77620_FPS_SRC_NONE) + return 0; + + return max77620_regulator_set_power_mode(pmic, + pmic->enable_power_mode[id], id); +} + +static int max77620_regulator_disable(struct regulator_dev *rdev) +{ + struct max77620_regulator *pmic = rdev_get_drvdata(rdev); + int id = rdev_get_id(rdev); + + if (pmic->active_fps_src[id] != MAX77620_FPS_SRC_NONE) + return 0; + + return max77620_regulator_set_power_mode(pmic, + MAX77620_POWER_MODE_DISABLE, id); +} + +static int max77620_regulator_is_enabled(struct regulator_dev *rdev) +{ + struct max77620_regulator *pmic = rdev_get_drvdata(rdev); + int id = rdev_get_id(rdev); + int ret = 1; + + if (pmic->active_fps_src[id] != MAX77620_FPS_SRC_NONE) + return 1; + + ret = max77620_regulator_get_power_mode(pmic, id); + if (ret < 0) + return ret; + + if (ret != MAX77620_POWER_MODE_DISABLE) + return 1; + + return 0; +} + +static int max77620_regulator_set_mode(struct regulator_dev *rdev, + unsigned int mode) +{ + struct max77620_regulator *pmic = rdev_get_drvdata(rdev); + int id = rdev_get_id(rdev); + struct max77620_regulator_info *rinfo = pmic->rinfo[id]; + struct max77620_regulator_pdata *rpdata = &pmic->reg_pdata[id]; + bool fpwm = false; + int power_mode; + int ret; + u8 val; + + switch (mode) { + case REGULATOR_MODE_FAST: + fpwm = true; + power_mode = MAX77620_POWER_MODE_NORMAL; + break; + + case REGULATOR_MODE_NORMAL: + power_mode = MAX77620_POWER_MODE_NORMAL; + break; + + case REGULATOR_MODE_IDLE: + power_mode = MAX77620_POWER_MODE_LPM; + break; + + default: + dev_err(pmic->dev, "Regulator %d mode %d is invalid\n", + id, mode); + return -EINVAL; + } + + if (rinfo->type != MAX77620_REGULATOR_TYPE_SD) + goto skip_fpwm; + + val = (fpwm) ? MAX77620_SD_FPWM_MASK : 0; + ret = regmap_update_bits(pmic->rmap, rinfo->cfg_addr, + MAX77620_SD_FPWM_MASK, val); + if (ret < 0) { + dev_err(pmic->dev, "Reg 0x%02x update failed: %d\n", + rinfo->cfg_addr, ret); + return ret; + } + rpdata->current_mode = mode; + +skip_fpwm: + ret = max77620_regulator_set_power_mode(pmic, power_mode, id); + if (ret < 0) + return ret; + + pmic->enable_power_mode[id] = power_mode; + + return 0; +} + +static unsigned int max77620_regulator_get_mode(struct regulator_dev *rdev) +{ + struct max77620_regulator *pmic = rdev_get_drvdata(rdev); + int id = rdev_get_id(rdev); + struct max77620_regulator_info *rinfo = pmic->rinfo[id]; + int fpwm = 0; + int ret; + int pm_mode, reg_mode; + unsigned int val; + + ret = max77620_regulator_get_power_mode(pmic, id); + if (ret < 0) + return 0; + + pm_mode = ret; + + if (rinfo->type == MAX77620_REGULATOR_TYPE_SD) { + ret = regmap_read(pmic->rmap, rinfo->cfg_addr, &val); + if (ret < 0) { + dev_err(pmic->dev, "Reg 0x%02x read failed: %d\n", + rinfo->cfg_addr, ret); + return ret; + } + fpwm = !!(val & MAX77620_SD_FPWM_MASK); + } + + switch (pm_mode) { + case MAX77620_POWER_MODE_NORMAL: + case MAX77620_POWER_MODE_DISABLE: + if (fpwm) + reg_mode = REGULATOR_MODE_FAST; + else + reg_mode = REGULATOR_MODE_NORMAL; + break; + case MAX77620_POWER_MODE_LPM: + case MAX77620_POWER_MODE_GLPM: + reg_mode = REGULATOR_MODE_IDLE; + break; + default: + return 0; + } + + return reg_mode; +} + +static int max77620_regulator_set_ramp_delay(struct regulator_dev *rdev, + int ramp_delay) +{ + struct max77620_regulator *pmic = rdev_get_drvdata(rdev); + int id = rdev_get_id(rdev); + struct max77620_regulator_info *rinfo = pmic->rinfo[id]; + int ret, val; + u8 mask; + + if (rinfo->type == MAX77620_REGULATOR_TYPE_SD) { + if (ramp_delay <= 13750) + val = 0; + else if (ramp_delay <= 27500) + val = 1; + else if (ramp_delay <= 55000) + val = 2; + else + val = 3; + val <<= MAX77620_SD_SR_SHIFT; + mask = MAX77620_SD_SR_MASK; + } else { + if (ramp_delay <= 5000) + val = 1; + else + val = 0; + mask = MAX77620_LDO_SLEW_RATE_MASK; + } + + ret = regmap_update_bits(pmic->rmap, rinfo->cfg_addr, mask, val); + if (ret < 0) + dev_err(pmic->dev, "Reg 0x%02x update failed: %d\n", + rinfo->cfg_addr, ret); + + return ret; +} + +static int max77620_of_parse_cb(struct device_node *np, + const struct regulator_desc *desc, + struct regulator_config *config) +{ + struct max77620_regulator *pmic = config->driver_data; + struct max77620_regulator_pdata *rpdata = &pmic->reg_pdata[desc->id]; + u32 pval; + int ret; + + ret = of_property_read_u32(np, "maxim,active-fps-source", &pval); + rpdata->active_fps_src = (!ret) ? pval : MAX77620_FPS_SRC_DEF; + + ret = of_property_read_u32(np, "maxim,active-fps-power-up-slot", &pval); + rpdata->active_fps_pu_slot = (!ret) ? pval : -1; + + ret = of_property_read_u32( + np, "maxim,active-fps-power-down-slot", &pval); + rpdata->active_fps_pd_slot = (!ret) ? pval : -1; + + ret = of_property_read_u32(np, "maxim,suspend-fps-source", &pval); + rpdata->suspend_fps_src = (!ret) ? pval : -1; + + ret = of_property_read_u32( + np, "maxim,suspend-fps-power-up-slot", &pval); + rpdata->suspend_fps_pu_slot = (!ret) ? pval : -1; + + ret = of_property_read_u32( + np, "maxim,suspend-fps-power-down-slot", &pval); + rpdata->suspend_fps_pd_slot = (!ret) ? pval : -1; + + return max77620_init_pmic(pmic, desc->id); +} + +static struct regulator_ops max77620_regulator_ops = { + .is_enabled = max77620_regulator_is_enabled, + .enable = max77620_regulator_enable, + .disable = max77620_regulator_disable, + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .set_mode = max77620_regulator_set_mode, + .get_mode = max77620_regulator_get_mode, + .set_ramp_delay = max77620_regulator_set_ramp_delay, + .set_voltage_time_sel = regulator_set_voltage_time_sel, +}; + +#define MAX77620_SD_CNF2_ROVS_EN_NONE 0 +#define RAIL_SD(_id, _name, _sname, _volt_mask, _min_uV, _max_uV, \ + _step_uV, _rs_add, _rs_mask) \ + [MAX77620_REGULATOR_ID_##_id] = { \ + .type = MAX77620_REGULATOR_TYPE_SD, \ + .volt_mask = MAX77620_##_volt_mask##_VOLT_MASK, \ + .volt_addr = MAX77620_REG_##_id, \ + .cfg_addr = MAX77620_REG_##_id##_CFG, \ + .fps_addr = MAX77620_REG_FPS_##_id, \ + .remote_sense_addr = _rs_add, \ + .remote_sense_mask = MAX77620_SD_CNF2_ROVS_EN_##_rs_mask, \ + .min_uV = _min_uV, \ + .max_uV = _max_uV, \ + .step_uV = _step_uV, \ + .power_mode_mask = MAX77620_SD_POWER_MODE_MASK, \ + .power_mode_shift = MAX77620_SD_POWER_MODE_SHIFT, \ + .desc = { \ + .name = max77620_rails(_name), \ + .of_match = of_match_ptr(#_name), \ + .regulators_node = of_match_ptr("regulators"), \ + .of_parse_cb = max77620_of_parse_cb, \ + .supply_name = _sname, \ + .id = MAX77620_REGULATOR_ID_##_id, \ + .ops = &max77620_regulator_ops, \ + .n_voltages = ((_max_uV - _min_uV) / _step_uV) + 1, \ + .min_uV = _min_uV, \ + .uV_step = _step_uV, \ + .enable_time = 500, \ + .vsel_mask = MAX77620_##_volt_mask##_VOLT_MASK, \ + .vsel_reg = MAX77620_REG_##_id, \ + .type = REGULATOR_VOLTAGE, \ + }, \ + } + +#define RAIL_LDO(_id, _name, _sname, _type, _min_uV, _max_uV, _step_uV) \ + [MAX77620_REGULATOR_ID_##_id] = { \ + .type = MAX77620_REGULATOR_TYPE_LDO_##_type, \ + .volt_mask = MAX77620_LDO_VOLT_MASK, \ + .volt_addr = MAX77620_REG_##_id##_CFG, \ + .cfg_addr = MAX77620_REG_##_id##_CFG2, \ + .fps_addr = MAX77620_REG_FPS_##_id, \ + .remote_sense_addr = 0xFF, \ + .min_uV = _min_uV, \ + .max_uV = _max_uV, \ + .step_uV = _step_uV, \ + .power_mode_mask = MAX77620_LDO_POWER_MODE_MASK, \ + .power_mode_shift = MAX77620_LDO_POWER_MODE_SHIFT, \ + .desc = { \ + .name = max77620_rails(_name), \ + .of_match = of_match_ptr(#_name), \ + .regulators_node = of_match_ptr("regulators"), \ + .of_parse_cb = max77620_of_parse_cb, \ + .supply_name = _sname, \ + .id = MAX77620_REGULATOR_ID_##_id, \ + .ops = &max77620_regulator_ops, \ + .n_voltages = ((_max_uV - _min_uV) / _step_uV) + 1, \ + .min_uV = _min_uV, \ + .uV_step = _step_uV, \ + .enable_time = 500, \ + .vsel_mask = MAX77620_LDO_VOLT_MASK, \ + .vsel_reg = MAX77620_REG_##_id##_CFG, \ + .type = REGULATOR_VOLTAGE, \ + }, \ + } + +static struct max77620_regulator_info max77620_regs_info[MAX77620_NUM_REGS] = { + RAIL_SD(SD0, sd0, "in-sd0", SD0, 600000, 1400000, 12500, 0x22, SD0), + RAIL_SD(SD1, sd1, "in-sd1", SD1, 600000, 1550000, 12500, 0x22, SD1), + RAIL_SD(SD2, sd2, "in-sd2", SDX, 600000, 3787500, 12500, 0xFF, NONE), + RAIL_SD(SD3, sd3, "in-sd3", SDX, 600000, 3787500, 12500, 0xFF, NONE), + RAIL_SD(SD4, sd4, "in-sd4", SDX, 600000, 3787500, 12500, 0xFF, NONE), + + RAIL_LDO(LDO0, ldo0, "in-ldo0-1", N, 800000, 2375000, 25000), + RAIL_LDO(LDO1, ldo1, "in-ldo0-1", N, 800000, 2375000, 25000), + RAIL_LDO(LDO2, ldo2, "in-ldo2", P, 800000, 3950000, 50000), + RAIL_LDO(LDO3, ldo3, "in-ldo3-5", P, 800000, 3950000, 50000), + RAIL_LDO(LDO4, ldo4, "in-ldo4-6", P, 800000, 1587500, 12500), + RAIL_LDO(LDO5, ldo5, "in-ldo3-5", P, 800000, 3950000, 50000), + RAIL_LDO(LDO6, ldo6, "in-ldo4-6", P, 800000, 3950000, 50000), + RAIL_LDO(LDO7, ldo7, "in-ldo7-8", N, 800000, 3950000, 50000), + RAIL_LDO(LDO8, ldo8, "in-ldo7-8", N, 800000, 3950000, 50000), +}; + +static struct max77620_regulator_info max20024_regs_info[MAX77620_NUM_REGS] = { + RAIL_SD(SD0, sd0, "in-sd0", SD0, 800000, 1587500, 12500, 0x22, SD0), + RAIL_SD(SD1, sd1, "in-sd1", SD1, 600000, 3387500, 12500, 0x22, SD1), + RAIL_SD(SD2, sd2, "in-sd2", SDX, 600000, 3787500, 12500, 0xFF, NONE), + RAIL_SD(SD3, sd3, "in-sd3", SDX, 600000, 3787500, 12500, 0xFF, NONE), + RAIL_SD(SD4, sd4, "in-sd4", SDX, 600000, 3787500, 12500, 0xFF, NONE), + + RAIL_LDO(LDO0, ldo0, "in-ldo0-1", N, 800000, 2375000, 25000), + RAIL_LDO(LDO1, ldo1, "in-ldo0-1", N, 800000, 2375000, 25000), + RAIL_LDO(LDO2, ldo2, "in-ldo2", P, 800000, 3950000, 50000), + RAIL_LDO(LDO3, ldo3, "in-ldo3-5", P, 800000, 3950000, 50000), + RAIL_LDO(LDO4, ldo4, "in-ldo4-6", P, 800000, 1587500, 12500), + RAIL_LDO(LDO5, ldo5, "in-ldo3-5", P, 800000, 3950000, 50000), + RAIL_LDO(LDO6, ldo6, "in-ldo4-6", P, 800000, 3950000, 50000), + RAIL_LDO(LDO7, ldo7, "in-ldo7-8", N, 800000, 3950000, 50000), + RAIL_LDO(LDO8, ldo8, "in-ldo7-8", N, 800000, 3950000, 50000), +}; + +static int max77620_regulator_probe(struct platform_device *pdev) +{ + struct max77620_chip *max77620_chip = dev_get_drvdata(pdev->dev.parent); + struct max77620_regulator_info *rinfo; + struct device *dev = &pdev->dev; + struct regulator_config config = { }; + struct max77620_regulator *pmic; + int ret = 0; + int id; + + pmic = devm_kzalloc(dev, sizeof(*pmic), GFP_KERNEL); + if (!pmic) + return -ENOMEM; + + platform_set_drvdata(pdev, pmic); + pmic->dev = dev; + pmic->rmap = max77620_chip->rmap; + if (!dev->of_node) + dev->of_node = pdev->dev.parent->of_node; + + switch (max77620_chip->chip_id) { + case MAX77620: + rinfo = max77620_regs_info; + break; + default: + rinfo = max20024_regs_info; + break; + } + + config.regmap = pmic->rmap; + config.dev = dev; + config.driver_data = pmic; + + for (id = 0; id < MAX77620_NUM_REGS; id++) { + struct regulator_dev *rdev; + struct regulator_desc *rdesc; + + if ((max77620_chip->chip_id == MAX77620) && + (id == MAX77620_REGULATOR_ID_SD4)) + continue; + + rdesc = &rinfo[id].desc; + pmic->rinfo[id] = &max77620_regs_info[id]; + pmic->enable_power_mode[id] = MAX77620_POWER_MODE_NORMAL; + pmic->rdesc[id] = rdesc; + + ret = max77620_read_slew_rate(pmic, id); + if (ret < 0) + return ret; + + rdev = devm_regulator_register(dev, rdesc, &config); + if (IS_ERR(rdev)) { + ret = PTR_ERR(rdev); + dev_err(dev, "Regulator registration %s failed: %d\n", + rdesc->name, ret); + return ret; + } + } + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int max77620_regulator_suspend(struct device *dev) +{ + struct max77620_regulator *pmic = dev_get_drvdata(dev); + struct max77620_regulator_pdata *reg_pdata; + int id; + + for (id = 0; id < MAX77620_NUM_REGS; id++) { + reg_pdata = &pmic->reg_pdata[id]; + + max77620_regulator_set_fps_slots(pmic, id, true); + if (reg_pdata->suspend_fps_src < 0) + continue; + + max77620_regulator_set_fps_src(pmic, reg_pdata->suspend_fps_src, + id); + } + + return 0; +} + +static int max77620_regulator_resume(struct device *dev) +{ + struct max77620_regulator *pmic = dev_get_drvdata(dev); + struct max77620_regulator_pdata *reg_pdata; + int id; + + for (id = 0; id < MAX77620_NUM_REGS; id++) { + reg_pdata = &pmic->reg_pdata[id]; + + max77620_regulator_set_fps_slots(pmic, id, false); + if (reg_pdata->active_fps_src < 0) + continue; + max77620_regulator_set_fps_src(pmic, reg_pdata->active_fps_src, + id); + } + + return 0; +} +#endif + +static const struct dev_pm_ops max77620_regulator_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(max77620_regulator_suspend, + max77620_regulator_resume) +}; + +static const struct platform_device_id max77620_regulator_devtype[] = { + { .name = "max77620-pmic", }, + { .name = "max20024-pmic", }, + {}, +}; +MODULE_DEVICE_TABLE(platform, max77620_regulator_devtype); + +static struct platform_driver max77620_regulator_driver = { + .probe = max77620_regulator_probe, + .id_table = max77620_regulator_devtype, + .driver = { + .name = "max77620-pmic", + .pm = &max77620_regulator_pm_ops, + }, +}; + +module_platform_driver(max77620_regulator_driver); + +MODULE_DESCRIPTION("MAX77620/MAX20024 regulator driver"); +MODULE_AUTHOR("Mallikarjun Kasoju "); +MODULE_AUTHOR("Laxman Dewangan "); +MODULE_ALIAS("platform:max77620-pmic"); +MODULE_LICENSE("GPL v2");