18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Base driver for Marvell 88PM800 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2012 Marvell International Ltd. 58c2ecf20Sopenharmony_ci * Haojian Zhuang <haojian.zhuang@marvell.com> 68c2ecf20Sopenharmony_ci * Joseph(Yossi) Hanin <yhanin@marvell.com> 78c2ecf20Sopenharmony_ci * Qiao Zhou <zhouqiao@marvell.com> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General 108c2ecf20Sopenharmony_ci * Public License. See the file "COPYING" in the main directory of this 118c2ecf20Sopenharmony_ci * archive for more details. 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, 148c2ecf20Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 158c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 168c2ecf20Sopenharmony_ci * GNU General Public License for more details. 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * You should have received a copy of the GNU General Public License 198c2ecf20Sopenharmony_ci * along with this program; if not, write to the Free Software 208c2ecf20Sopenharmony_ci * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 218c2ecf20Sopenharmony_ci */ 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include <linux/kernel.h> 248c2ecf20Sopenharmony_ci#include <linux/module.h> 258c2ecf20Sopenharmony_ci#include <linux/err.h> 268c2ecf20Sopenharmony_ci#include <linux/i2c.h> 278c2ecf20Sopenharmony_ci#include <linux/mfd/core.h> 288c2ecf20Sopenharmony_ci#include <linux/mfd/88pm80x.h> 298c2ecf20Sopenharmony_ci#include <linux/slab.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/* Interrupt Registers */ 328c2ecf20Sopenharmony_ci#define PM800_INT_STATUS1 (0x05) 338c2ecf20Sopenharmony_ci#define PM800_ONKEY_INT_STS1 (1 << 0) 348c2ecf20Sopenharmony_ci#define PM800_EXTON_INT_STS1 (1 << 1) 358c2ecf20Sopenharmony_ci#define PM800_CHG_INT_STS1 (1 << 2) 368c2ecf20Sopenharmony_ci#define PM800_BAT_INT_STS1 (1 << 3) 378c2ecf20Sopenharmony_ci#define PM800_RTC_INT_STS1 (1 << 4) 388c2ecf20Sopenharmony_ci#define PM800_CLASSD_OC_INT_STS1 (1 << 5) 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define PM800_INT_STATUS2 (0x06) 418c2ecf20Sopenharmony_ci#define PM800_VBAT_INT_STS2 (1 << 0) 428c2ecf20Sopenharmony_ci#define PM800_VSYS_INT_STS2 (1 << 1) 438c2ecf20Sopenharmony_ci#define PM800_VCHG_INT_STS2 (1 << 2) 448c2ecf20Sopenharmony_ci#define PM800_TINT_INT_STS2 (1 << 3) 458c2ecf20Sopenharmony_ci#define PM800_GPADC0_INT_STS2 (1 << 4) 468c2ecf20Sopenharmony_ci#define PM800_TBAT_INT_STS2 (1 << 5) 478c2ecf20Sopenharmony_ci#define PM800_GPADC2_INT_STS2 (1 << 6) 488c2ecf20Sopenharmony_ci#define PM800_GPADC3_INT_STS2 (1 << 7) 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci#define PM800_INT_STATUS3 (0x07) 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#define PM800_INT_STATUS4 (0x08) 538c2ecf20Sopenharmony_ci#define PM800_GPIO0_INT_STS4 (1 << 0) 548c2ecf20Sopenharmony_ci#define PM800_GPIO1_INT_STS4 (1 << 1) 558c2ecf20Sopenharmony_ci#define PM800_GPIO2_INT_STS4 (1 << 2) 568c2ecf20Sopenharmony_ci#define PM800_GPIO3_INT_STS4 (1 << 3) 578c2ecf20Sopenharmony_ci#define PM800_GPIO4_INT_STS4 (1 << 4) 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci#define PM800_INT_ENA_1 (0x09) 608c2ecf20Sopenharmony_ci#define PM800_ONKEY_INT_ENA1 (1 << 0) 618c2ecf20Sopenharmony_ci#define PM800_EXTON_INT_ENA1 (1 << 1) 628c2ecf20Sopenharmony_ci#define PM800_CHG_INT_ENA1 (1 << 2) 638c2ecf20Sopenharmony_ci#define PM800_BAT_INT_ENA1 (1 << 3) 648c2ecf20Sopenharmony_ci#define PM800_RTC_INT_ENA1 (1 << 4) 658c2ecf20Sopenharmony_ci#define PM800_CLASSD_OC_INT_ENA1 (1 << 5) 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci#define PM800_INT_ENA_2 (0x0A) 688c2ecf20Sopenharmony_ci#define PM800_VBAT_INT_ENA2 (1 << 0) 698c2ecf20Sopenharmony_ci#define PM800_VSYS_INT_ENA2 (1 << 1) 708c2ecf20Sopenharmony_ci#define PM800_VCHG_INT_ENA2 (1 << 2) 718c2ecf20Sopenharmony_ci#define PM800_TINT_INT_ENA2 (1 << 3) 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci#define PM800_INT_ENA_3 (0x0B) 748c2ecf20Sopenharmony_ci#define PM800_GPADC0_INT_ENA3 (1 << 0) 758c2ecf20Sopenharmony_ci#define PM800_GPADC1_INT_ENA3 (1 << 1) 768c2ecf20Sopenharmony_ci#define PM800_GPADC2_INT_ENA3 (1 << 2) 778c2ecf20Sopenharmony_ci#define PM800_GPADC3_INT_ENA3 (1 << 3) 788c2ecf20Sopenharmony_ci#define PM800_GPADC4_INT_ENA3 (1 << 4) 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci#define PM800_INT_ENA_4 (0x0C) 818c2ecf20Sopenharmony_ci#define PM800_GPIO0_INT_ENA4 (1 << 0) 828c2ecf20Sopenharmony_ci#define PM800_GPIO1_INT_ENA4 (1 << 1) 838c2ecf20Sopenharmony_ci#define PM800_GPIO2_INT_ENA4 (1 << 2) 848c2ecf20Sopenharmony_ci#define PM800_GPIO3_INT_ENA4 (1 << 3) 858c2ecf20Sopenharmony_ci#define PM800_GPIO4_INT_ENA4 (1 << 4) 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci/* number of INT_ENA & INT_STATUS regs */ 888c2ecf20Sopenharmony_ci#define PM800_INT_REG_NUM (4) 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci/* Interrupt Number in 88PM800 */ 918c2ecf20Sopenharmony_cienum { 928c2ecf20Sopenharmony_ci PM800_IRQ_ONKEY, /*EN1b0 *//*0 */ 938c2ecf20Sopenharmony_ci PM800_IRQ_EXTON, /*EN1b1 */ 948c2ecf20Sopenharmony_ci PM800_IRQ_CHG, /*EN1b2 */ 958c2ecf20Sopenharmony_ci PM800_IRQ_BAT, /*EN1b3 */ 968c2ecf20Sopenharmony_ci PM800_IRQ_RTC, /*EN1b4 */ 978c2ecf20Sopenharmony_ci PM800_IRQ_CLASSD, /*EN1b5 *//*5 */ 988c2ecf20Sopenharmony_ci PM800_IRQ_VBAT, /*EN2b0 */ 998c2ecf20Sopenharmony_ci PM800_IRQ_VSYS, /*EN2b1 */ 1008c2ecf20Sopenharmony_ci PM800_IRQ_VCHG, /*EN2b2 */ 1018c2ecf20Sopenharmony_ci PM800_IRQ_TINT, /*EN2b3 */ 1028c2ecf20Sopenharmony_ci PM800_IRQ_GPADC0, /*EN3b0 *//*10 */ 1038c2ecf20Sopenharmony_ci PM800_IRQ_GPADC1, /*EN3b1 */ 1048c2ecf20Sopenharmony_ci PM800_IRQ_GPADC2, /*EN3b2 */ 1058c2ecf20Sopenharmony_ci PM800_IRQ_GPADC3, /*EN3b3 */ 1068c2ecf20Sopenharmony_ci PM800_IRQ_GPADC4, /*EN3b4 */ 1078c2ecf20Sopenharmony_ci PM800_IRQ_GPIO0, /*EN4b0 *//*15 */ 1088c2ecf20Sopenharmony_ci PM800_IRQ_GPIO1, /*EN4b1 */ 1098c2ecf20Sopenharmony_ci PM800_IRQ_GPIO2, /*EN4b2 */ 1108c2ecf20Sopenharmony_ci PM800_IRQ_GPIO3, /*EN4b3 */ 1118c2ecf20Sopenharmony_ci PM800_IRQ_GPIO4, /*EN4b4 *//*19 */ 1128c2ecf20Sopenharmony_ci PM800_MAX_IRQ, 1138c2ecf20Sopenharmony_ci}; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci/* PM800: generation identification number */ 1168c2ecf20Sopenharmony_ci#define PM800_CHIP_GEN_ID_NUM 0x3 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic const struct i2c_device_id pm80x_id_table[] = { 1198c2ecf20Sopenharmony_ci {"88PM800", 0}, 1208c2ecf20Sopenharmony_ci {} /* NULL terminated */ 1218c2ecf20Sopenharmony_ci}; 1228c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, pm80x_id_table); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic struct resource rtc_resources[] = { 1258c2ecf20Sopenharmony_ci { 1268c2ecf20Sopenharmony_ci .name = "88pm80x-rtc", 1278c2ecf20Sopenharmony_ci .start = PM800_IRQ_RTC, 1288c2ecf20Sopenharmony_ci .end = PM800_IRQ_RTC, 1298c2ecf20Sopenharmony_ci .flags = IORESOURCE_IRQ, 1308c2ecf20Sopenharmony_ci }, 1318c2ecf20Sopenharmony_ci}; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic struct mfd_cell rtc_devs[] = { 1348c2ecf20Sopenharmony_ci { 1358c2ecf20Sopenharmony_ci .name = "88pm80x-rtc", 1368c2ecf20Sopenharmony_ci .num_resources = ARRAY_SIZE(rtc_resources), 1378c2ecf20Sopenharmony_ci .resources = &rtc_resources[0], 1388c2ecf20Sopenharmony_ci .id = -1, 1398c2ecf20Sopenharmony_ci }, 1408c2ecf20Sopenharmony_ci}; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic struct resource onkey_resources[] = { 1438c2ecf20Sopenharmony_ci { 1448c2ecf20Sopenharmony_ci .name = "88pm80x-onkey", 1458c2ecf20Sopenharmony_ci .start = PM800_IRQ_ONKEY, 1468c2ecf20Sopenharmony_ci .end = PM800_IRQ_ONKEY, 1478c2ecf20Sopenharmony_ci .flags = IORESOURCE_IRQ, 1488c2ecf20Sopenharmony_ci }, 1498c2ecf20Sopenharmony_ci}; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic const struct mfd_cell onkey_devs[] = { 1528c2ecf20Sopenharmony_ci { 1538c2ecf20Sopenharmony_ci .name = "88pm80x-onkey", 1548c2ecf20Sopenharmony_ci .num_resources = 1, 1558c2ecf20Sopenharmony_ci .resources = &onkey_resources[0], 1568c2ecf20Sopenharmony_ci .id = -1, 1578c2ecf20Sopenharmony_ci }, 1588c2ecf20Sopenharmony_ci}; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic const struct mfd_cell regulator_devs[] = { 1618c2ecf20Sopenharmony_ci { 1628c2ecf20Sopenharmony_ci .name = "88pm80x-regulator", 1638c2ecf20Sopenharmony_ci .id = -1, 1648c2ecf20Sopenharmony_ci }, 1658c2ecf20Sopenharmony_ci}; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic const struct regmap_irq pm800_irqs[] = { 1688c2ecf20Sopenharmony_ci /* INT0 */ 1698c2ecf20Sopenharmony_ci [PM800_IRQ_ONKEY] = { 1708c2ecf20Sopenharmony_ci .mask = PM800_ONKEY_INT_ENA1, 1718c2ecf20Sopenharmony_ci }, 1728c2ecf20Sopenharmony_ci [PM800_IRQ_EXTON] = { 1738c2ecf20Sopenharmony_ci .mask = PM800_EXTON_INT_ENA1, 1748c2ecf20Sopenharmony_ci }, 1758c2ecf20Sopenharmony_ci [PM800_IRQ_CHG] = { 1768c2ecf20Sopenharmony_ci .mask = PM800_CHG_INT_ENA1, 1778c2ecf20Sopenharmony_ci }, 1788c2ecf20Sopenharmony_ci [PM800_IRQ_BAT] = { 1798c2ecf20Sopenharmony_ci .mask = PM800_BAT_INT_ENA1, 1808c2ecf20Sopenharmony_ci }, 1818c2ecf20Sopenharmony_ci [PM800_IRQ_RTC] = { 1828c2ecf20Sopenharmony_ci .mask = PM800_RTC_INT_ENA1, 1838c2ecf20Sopenharmony_ci }, 1848c2ecf20Sopenharmony_ci [PM800_IRQ_CLASSD] = { 1858c2ecf20Sopenharmony_ci .mask = PM800_CLASSD_OC_INT_ENA1, 1868c2ecf20Sopenharmony_ci }, 1878c2ecf20Sopenharmony_ci /* INT1 */ 1888c2ecf20Sopenharmony_ci [PM800_IRQ_VBAT] = { 1898c2ecf20Sopenharmony_ci .reg_offset = 1, 1908c2ecf20Sopenharmony_ci .mask = PM800_VBAT_INT_ENA2, 1918c2ecf20Sopenharmony_ci }, 1928c2ecf20Sopenharmony_ci [PM800_IRQ_VSYS] = { 1938c2ecf20Sopenharmony_ci .reg_offset = 1, 1948c2ecf20Sopenharmony_ci .mask = PM800_VSYS_INT_ENA2, 1958c2ecf20Sopenharmony_ci }, 1968c2ecf20Sopenharmony_ci [PM800_IRQ_VCHG] = { 1978c2ecf20Sopenharmony_ci .reg_offset = 1, 1988c2ecf20Sopenharmony_ci .mask = PM800_VCHG_INT_ENA2, 1998c2ecf20Sopenharmony_ci }, 2008c2ecf20Sopenharmony_ci [PM800_IRQ_TINT] = { 2018c2ecf20Sopenharmony_ci .reg_offset = 1, 2028c2ecf20Sopenharmony_ci .mask = PM800_TINT_INT_ENA2, 2038c2ecf20Sopenharmony_ci }, 2048c2ecf20Sopenharmony_ci /* INT2 */ 2058c2ecf20Sopenharmony_ci [PM800_IRQ_GPADC0] = { 2068c2ecf20Sopenharmony_ci .reg_offset = 2, 2078c2ecf20Sopenharmony_ci .mask = PM800_GPADC0_INT_ENA3, 2088c2ecf20Sopenharmony_ci }, 2098c2ecf20Sopenharmony_ci [PM800_IRQ_GPADC1] = { 2108c2ecf20Sopenharmony_ci .reg_offset = 2, 2118c2ecf20Sopenharmony_ci .mask = PM800_GPADC1_INT_ENA3, 2128c2ecf20Sopenharmony_ci }, 2138c2ecf20Sopenharmony_ci [PM800_IRQ_GPADC2] = { 2148c2ecf20Sopenharmony_ci .reg_offset = 2, 2158c2ecf20Sopenharmony_ci .mask = PM800_GPADC2_INT_ENA3, 2168c2ecf20Sopenharmony_ci }, 2178c2ecf20Sopenharmony_ci [PM800_IRQ_GPADC3] = { 2188c2ecf20Sopenharmony_ci .reg_offset = 2, 2198c2ecf20Sopenharmony_ci .mask = PM800_GPADC3_INT_ENA3, 2208c2ecf20Sopenharmony_ci }, 2218c2ecf20Sopenharmony_ci [PM800_IRQ_GPADC4] = { 2228c2ecf20Sopenharmony_ci .reg_offset = 2, 2238c2ecf20Sopenharmony_ci .mask = PM800_GPADC4_INT_ENA3, 2248c2ecf20Sopenharmony_ci }, 2258c2ecf20Sopenharmony_ci /* INT3 */ 2268c2ecf20Sopenharmony_ci [PM800_IRQ_GPIO0] = { 2278c2ecf20Sopenharmony_ci .reg_offset = 3, 2288c2ecf20Sopenharmony_ci .mask = PM800_GPIO0_INT_ENA4, 2298c2ecf20Sopenharmony_ci }, 2308c2ecf20Sopenharmony_ci [PM800_IRQ_GPIO1] = { 2318c2ecf20Sopenharmony_ci .reg_offset = 3, 2328c2ecf20Sopenharmony_ci .mask = PM800_GPIO1_INT_ENA4, 2338c2ecf20Sopenharmony_ci }, 2348c2ecf20Sopenharmony_ci [PM800_IRQ_GPIO2] = { 2358c2ecf20Sopenharmony_ci .reg_offset = 3, 2368c2ecf20Sopenharmony_ci .mask = PM800_GPIO2_INT_ENA4, 2378c2ecf20Sopenharmony_ci }, 2388c2ecf20Sopenharmony_ci [PM800_IRQ_GPIO3] = { 2398c2ecf20Sopenharmony_ci .reg_offset = 3, 2408c2ecf20Sopenharmony_ci .mask = PM800_GPIO3_INT_ENA4, 2418c2ecf20Sopenharmony_ci }, 2428c2ecf20Sopenharmony_ci [PM800_IRQ_GPIO4] = { 2438c2ecf20Sopenharmony_ci .reg_offset = 3, 2448c2ecf20Sopenharmony_ci .mask = PM800_GPIO4_INT_ENA4, 2458c2ecf20Sopenharmony_ci }, 2468c2ecf20Sopenharmony_ci}; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic int device_gpadc_init(struct pm80x_chip *chip, 2498c2ecf20Sopenharmony_ci struct pm80x_platform_data *pdata) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci struct pm80x_subchip *subchip = chip->subchip; 2528c2ecf20Sopenharmony_ci struct regmap *map = subchip->regmap_gpadc; 2538c2ecf20Sopenharmony_ci int data = 0, mask = 0, ret = 0; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci if (!map) { 2568c2ecf20Sopenharmony_ci dev_warn(chip->dev, 2578c2ecf20Sopenharmony_ci "Warning: gpadc regmap is not available!\n"); 2588c2ecf20Sopenharmony_ci return -EINVAL; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci /* 2618c2ecf20Sopenharmony_ci * initialize GPADC without activating it turn on GPADC 2628c2ecf20Sopenharmony_ci * measurments 2638c2ecf20Sopenharmony_ci */ 2648c2ecf20Sopenharmony_ci ret = regmap_update_bits(map, 2658c2ecf20Sopenharmony_ci PM800_GPADC_MISC_CONFIG2, 2668c2ecf20Sopenharmony_ci PM800_GPADC_MISC_GPFSM_EN, 2678c2ecf20Sopenharmony_ci PM800_GPADC_MISC_GPFSM_EN); 2688c2ecf20Sopenharmony_ci if (ret < 0) 2698c2ecf20Sopenharmony_ci goto out; 2708c2ecf20Sopenharmony_ci /* 2718c2ecf20Sopenharmony_ci * This function configures the ADC as requires for 2728c2ecf20Sopenharmony_ci * CP implementation.CP does not "own" the ADC configuration 2738c2ecf20Sopenharmony_ci * registers and relies on AP. 2748c2ecf20Sopenharmony_ci * Reason: enable automatic ADC measurements needed 2758c2ecf20Sopenharmony_ci * for CP to get VBAT and RF temperature readings. 2768c2ecf20Sopenharmony_ci */ 2778c2ecf20Sopenharmony_ci ret = regmap_update_bits(map, PM800_GPADC_MEAS_EN1, 2788c2ecf20Sopenharmony_ci PM800_MEAS_EN1_VBAT, PM800_MEAS_EN1_VBAT); 2798c2ecf20Sopenharmony_ci if (ret < 0) 2808c2ecf20Sopenharmony_ci goto out; 2818c2ecf20Sopenharmony_ci ret = regmap_update_bits(map, PM800_GPADC_MEAS_EN2, 2828c2ecf20Sopenharmony_ci (PM800_MEAS_EN2_RFTMP | PM800_MEAS_GP0_EN), 2838c2ecf20Sopenharmony_ci (PM800_MEAS_EN2_RFTMP | PM800_MEAS_GP0_EN)); 2848c2ecf20Sopenharmony_ci if (ret < 0) 2858c2ecf20Sopenharmony_ci goto out; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci /* 2888c2ecf20Sopenharmony_ci * the defult of PM800 is GPADC operates at 100Ks/s rate 2898c2ecf20Sopenharmony_ci * and Number of GPADC slots with active current bias prior 2908c2ecf20Sopenharmony_ci * to GPADC sampling = 1 slot for all GPADCs set for 2918c2ecf20Sopenharmony_ci * Temprature mesurmants 2928c2ecf20Sopenharmony_ci */ 2938c2ecf20Sopenharmony_ci mask = (PM800_GPADC_GP_BIAS_EN0 | PM800_GPADC_GP_BIAS_EN1 | 2948c2ecf20Sopenharmony_ci PM800_GPADC_GP_BIAS_EN2 | PM800_GPADC_GP_BIAS_EN3); 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci if (pdata && (pdata->batt_det == 0)) 2978c2ecf20Sopenharmony_ci data = (PM800_GPADC_GP_BIAS_EN0 | PM800_GPADC_GP_BIAS_EN1 | 2988c2ecf20Sopenharmony_ci PM800_GPADC_GP_BIAS_EN2 | PM800_GPADC_GP_BIAS_EN3); 2998c2ecf20Sopenharmony_ci else 3008c2ecf20Sopenharmony_ci data = (PM800_GPADC_GP_BIAS_EN0 | PM800_GPADC_GP_BIAS_EN2 | 3018c2ecf20Sopenharmony_ci PM800_GPADC_GP_BIAS_EN3); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci ret = regmap_update_bits(map, PM800_GP_BIAS_ENA1, mask, data); 3048c2ecf20Sopenharmony_ci if (ret < 0) 3058c2ecf20Sopenharmony_ci goto out; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci dev_info(chip->dev, "pm800 device_gpadc_init: Done\n"); 3088c2ecf20Sopenharmony_ci return 0; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ciout: 3118c2ecf20Sopenharmony_ci dev_info(chip->dev, "pm800 device_gpadc_init: Failed!\n"); 3128c2ecf20Sopenharmony_ci return ret; 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cistatic int device_onkey_init(struct pm80x_chip *chip, 3168c2ecf20Sopenharmony_ci struct pm80x_platform_data *pdata) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci int ret; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci ret = mfd_add_devices(chip->dev, 0, &onkey_devs[0], 3218c2ecf20Sopenharmony_ci ARRAY_SIZE(onkey_devs), &onkey_resources[0], 0, 3228c2ecf20Sopenharmony_ci NULL); 3238c2ecf20Sopenharmony_ci if (ret) { 3248c2ecf20Sopenharmony_ci dev_err(chip->dev, "Failed to add onkey subdev\n"); 3258c2ecf20Sopenharmony_ci return ret; 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci return 0; 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic int device_rtc_init(struct pm80x_chip *chip, 3328c2ecf20Sopenharmony_ci struct pm80x_platform_data *pdata) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci int ret; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci if (pdata) { 3378c2ecf20Sopenharmony_ci rtc_devs[0].platform_data = pdata->rtc; 3388c2ecf20Sopenharmony_ci rtc_devs[0].pdata_size = 3398c2ecf20Sopenharmony_ci pdata->rtc ? sizeof(struct pm80x_rtc_pdata) : 0; 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci ret = mfd_add_devices(chip->dev, 0, &rtc_devs[0], 3428c2ecf20Sopenharmony_ci ARRAY_SIZE(rtc_devs), NULL, 0, NULL); 3438c2ecf20Sopenharmony_ci if (ret) { 3448c2ecf20Sopenharmony_ci dev_err(chip->dev, "Failed to add rtc subdev\n"); 3458c2ecf20Sopenharmony_ci return ret; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci return 0; 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cistatic int device_regulator_init(struct pm80x_chip *chip, 3528c2ecf20Sopenharmony_ci struct pm80x_platform_data *pdata) 3538c2ecf20Sopenharmony_ci{ 3548c2ecf20Sopenharmony_ci int ret; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci ret = mfd_add_devices(chip->dev, 0, ®ulator_devs[0], 3578c2ecf20Sopenharmony_ci ARRAY_SIZE(regulator_devs), NULL, 0, NULL); 3588c2ecf20Sopenharmony_ci if (ret) { 3598c2ecf20Sopenharmony_ci dev_err(chip->dev, "Failed to add regulator subdev\n"); 3608c2ecf20Sopenharmony_ci return ret; 3618c2ecf20Sopenharmony_ci } 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci return 0; 3648c2ecf20Sopenharmony_ci} 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_cistatic int device_irq_init_800(struct pm80x_chip *chip) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci struct regmap *map = chip->regmap; 3698c2ecf20Sopenharmony_ci unsigned long flags = IRQF_ONESHOT; 3708c2ecf20Sopenharmony_ci int data, mask, ret = -EINVAL; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci if (!map || !chip->irq) { 3738c2ecf20Sopenharmony_ci dev_err(chip->dev, "incorrect parameters\n"); 3748c2ecf20Sopenharmony_ci return -EINVAL; 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci /* 3788c2ecf20Sopenharmony_ci * irq_mode defines the way of clearing interrupt. it's read-clear by 3798c2ecf20Sopenharmony_ci * default. 3808c2ecf20Sopenharmony_ci */ 3818c2ecf20Sopenharmony_ci mask = 3828c2ecf20Sopenharmony_ci PM800_WAKEUP2_INV_INT | PM800_WAKEUP2_INT_CLEAR | 3838c2ecf20Sopenharmony_ci PM800_WAKEUP2_INT_MASK; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci data = PM800_WAKEUP2_INT_CLEAR; 3868c2ecf20Sopenharmony_ci ret = regmap_update_bits(map, PM800_WAKEUP2, mask, data); 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci if (ret < 0) 3898c2ecf20Sopenharmony_ci goto out; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci ret = 3928c2ecf20Sopenharmony_ci regmap_add_irq_chip(chip->regmap, chip->irq, flags, -1, 3938c2ecf20Sopenharmony_ci chip->regmap_irq_chip, &chip->irq_data); 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ciout: 3968c2ecf20Sopenharmony_ci return ret; 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_cistatic void device_irq_exit_800(struct pm80x_chip *chip) 4008c2ecf20Sopenharmony_ci{ 4018c2ecf20Sopenharmony_ci regmap_del_irq_chip(chip->irq, chip->irq_data); 4028c2ecf20Sopenharmony_ci} 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_cistatic struct regmap_irq_chip pm800_irq_chip = { 4058c2ecf20Sopenharmony_ci .name = "88pm800", 4068c2ecf20Sopenharmony_ci .irqs = pm800_irqs, 4078c2ecf20Sopenharmony_ci .num_irqs = ARRAY_SIZE(pm800_irqs), 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci .num_regs = 4, 4108c2ecf20Sopenharmony_ci .status_base = PM800_INT_STATUS1, 4118c2ecf20Sopenharmony_ci .mask_base = PM800_INT_ENA_1, 4128c2ecf20Sopenharmony_ci .ack_base = PM800_INT_STATUS1, 4138c2ecf20Sopenharmony_ci .mask_invert = 1, 4148c2ecf20Sopenharmony_ci}; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_cistatic int pm800_pages_init(struct pm80x_chip *chip) 4178c2ecf20Sopenharmony_ci{ 4188c2ecf20Sopenharmony_ci struct pm80x_subchip *subchip; 4198c2ecf20Sopenharmony_ci struct i2c_client *client = chip->client; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci int ret = 0; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci subchip = chip->subchip; 4248c2ecf20Sopenharmony_ci if (!subchip || !subchip->power_page_addr || !subchip->gpadc_page_addr) 4258c2ecf20Sopenharmony_ci return -ENODEV; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci /* PM800 block power page */ 4288c2ecf20Sopenharmony_ci subchip->power_page = i2c_new_dummy_device(client->adapter, 4298c2ecf20Sopenharmony_ci subchip->power_page_addr); 4308c2ecf20Sopenharmony_ci if (IS_ERR(subchip->power_page)) { 4318c2ecf20Sopenharmony_ci ret = PTR_ERR(subchip->power_page); 4328c2ecf20Sopenharmony_ci goto out; 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci subchip->regmap_power = devm_regmap_init_i2c(subchip->power_page, 4368c2ecf20Sopenharmony_ci &pm80x_regmap_config); 4378c2ecf20Sopenharmony_ci if (IS_ERR(subchip->regmap_power)) { 4388c2ecf20Sopenharmony_ci ret = PTR_ERR(subchip->regmap_power); 4398c2ecf20Sopenharmony_ci dev_err(chip->dev, 4408c2ecf20Sopenharmony_ci "Failed to allocate regmap_power: %d\n", ret); 4418c2ecf20Sopenharmony_ci goto out; 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci i2c_set_clientdata(subchip->power_page, chip); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci /* PM800 block GPADC */ 4478c2ecf20Sopenharmony_ci subchip->gpadc_page = i2c_new_dummy_device(client->adapter, 4488c2ecf20Sopenharmony_ci subchip->gpadc_page_addr); 4498c2ecf20Sopenharmony_ci if (IS_ERR(subchip->gpadc_page)) { 4508c2ecf20Sopenharmony_ci ret = PTR_ERR(subchip->gpadc_page); 4518c2ecf20Sopenharmony_ci goto out; 4528c2ecf20Sopenharmony_ci } 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci subchip->regmap_gpadc = devm_regmap_init_i2c(subchip->gpadc_page, 4558c2ecf20Sopenharmony_ci &pm80x_regmap_config); 4568c2ecf20Sopenharmony_ci if (IS_ERR(subchip->regmap_gpadc)) { 4578c2ecf20Sopenharmony_ci ret = PTR_ERR(subchip->regmap_gpadc); 4588c2ecf20Sopenharmony_ci dev_err(chip->dev, 4598c2ecf20Sopenharmony_ci "Failed to allocate regmap_gpadc: %d\n", ret); 4608c2ecf20Sopenharmony_ci goto out; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci i2c_set_clientdata(subchip->gpadc_page, chip); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ciout: 4658c2ecf20Sopenharmony_ci return ret; 4668c2ecf20Sopenharmony_ci} 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_cistatic void pm800_pages_exit(struct pm80x_chip *chip) 4698c2ecf20Sopenharmony_ci{ 4708c2ecf20Sopenharmony_ci struct pm80x_subchip *subchip; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci subchip = chip->subchip; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci if (subchip && subchip->power_page) 4758c2ecf20Sopenharmony_ci i2c_unregister_device(subchip->power_page); 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci if (subchip && subchip->gpadc_page) 4788c2ecf20Sopenharmony_ci i2c_unregister_device(subchip->gpadc_page); 4798c2ecf20Sopenharmony_ci} 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_cistatic int device_800_init(struct pm80x_chip *chip, 4828c2ecf20Sopenharmony_ci struct pm80x_platform_data *pdata) 4838c2ecf20Sopenharmony_ci{ 4848c2ecf20Sopenharmony_ci int ret; 4858c2ecf20Sopenharmony_ci unsigned int val; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci /* 4888c2ecf20Sopenharmony_ci * alarm wake up bit will be clear in device_irq_init(), 4898c2ecf20Sopenharmony_ci * read before that 4908c2ecf20Sopenharmony_ci */ 4918c2ecf20Sopenharmony_ci ret = regmap_read(chip->regmap, PM800_RTC_CONTROL, &val); 4928c2ecf20Sopenharmony_ci if (ret < 0) { 4938c2ecf20Sopenharmony_ci dev_err(chip->dev, "Failed to read RTC register: %d\n", ret); 4948c2ecf20Sopenharmony_ci goto out; 4958c2ecf20Sopenharmony_ci } 4968c2ecf20Sopenharmony_ci if (val & PM800_ALARM_WAKEUP) { 4978c2ecf20Sopenharmony_ci if (pdata && pdata->rtc) 4988c2ecf20Sopenharmony_ci pdata->rtc->rtc_wakeup = 1; 4998c2ecf20Sopenharmony_ci } 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci ret = device_gpadc_init(chip, pdata); 5028c2ecf20Sopenharmony_ci if (ret < 0) { 5038c2ecf20Sopenharmony_ci dev_err(chip->dev, "[%s]Failed to init gpadc\n", __func__); 5048c2ecf20Sopenharmony_ci goto out; 5058c2ecf20Sopenharmony_ci } 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci chip->regmap_irq_chip = &pm800_irq_chip; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci ret = device_irq_init_800(chip); 5108c2ecf20Sopenharmony_ci if (ret < 0) { 5118c2ecf20Sopenharmony_ci dev_err(chip->dev, "[%s]Failed to init pm800 irq\n", __func__); 5128c2ecf20Sopenharmony_ci goto out; 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci ret = device_onkey_init(chip, pdata); 5168c2ecf20Sopenharmony_ci if (ret) { 5178c2ecf20Sopenharmony_ci dev_err(chip->dev, "Failed to add onkey subdev\n"); 5188c2ecf20Sopenharmony_ci goto out_dev; 5198c2ecf20Sopenharmony_ci } 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci ret = device_rtc_init(chip, pdata); 5228c2ecf20Sopenharmony_ci if (ret) { 5238c2ecf20Sopenharmony_ci dev_err(chip->dev, "Failed to add rtc subdev\n"); 5248c2ecf20Sopenharmony_ci goto out; 5258c2ecf20Sopenharmony_ci } 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci ret = device_regulator_init(chip, pdata); 5288c2ecf20Sopenharmony_ci if (ret) { 5298c2ecf20Sopenharmony_ci dev_err(chip->dev, "Failed to add regulators subdev\n"); 5308c2ecf20Sopenharmony_ci goto out; 5318c2ecf20Sopenharmony_ci } 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci return 0; 5348c2ecf20Sopenharmony_ciout_dev: 5358c2ecf20Sopenharmony_ci mfd_remove_devices(chip->dev); 5368c2ecf20Sopenharmony_ci device_irq_exit_800(chip); 5378c2ecf20Sopenharmony_ciout: 5388c2ecf20Sopenharmony_ci return ret; 5398c2ecf20Sopenharmony_ci} 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_cistatic int pm800_probe(struct i2c_client *client, 5428c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 5438c2ecf20Sopenharmony_ci{ 5448c2ecf20Sopenharmony_ci int ret = 0; 5458c2ecf20Sopenharmony_ci struct pm80x_chip *chip; 5468c2ecf20Sopenharmony_ci struct pm80x_platform_data *pdata = dev_get_platdata(&client->dev); 5478c2ecf20Sopenharmony_ci struct pm80x_subchip *subchip; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci ret = pm80x_init(client); 5508c2ecf20Sopenharmony_ci if (ret) { 5518c2ecf20Sopenharmony_ci dev_err(&client->dev, "pm800_init fail\n"); 5528c2ecf20Sopenharmony_ci goto out_init; 5538c2ecf20Sopenharmony_ci } 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci chip = i2c_get_clientdata(client); 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci /* init subchip for PM800 */ 5588c2ecf20Sopenharmony_ci subchip = 5598c2ecf20Sopenharmony_ci devm_kzalloc(&client->dev, sizeof(struct pm80x_subchip), 5608c2ecf20Sopenharmony_ci GFP_KERNEL); 5618c2ecf20Sopenharmony_ci if (!subchip) { 5628c2ecf20Sopenharmony_ci ret = -ENOMEM; 5638c2ecf20Sopenharmony_ci goto err_subchip_alloc; 5648c2ecf20Sopenharmony_ci } 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci /* pm800 has 2 addtional pages to support power and gpadc. */ 5678c2ecf20Sopenharmony_ci subchip->power_page_addr = client->addr + 1; 5688c2ecf20Sopenharmony_ci subchip->gpadc_page_addr = client->addr + 2; 5698c2ecf20Sopenharmony_ci chip->subchip = subchip; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci ret = pm800_pages_init(chip); 5728c2ecf20Sopenharmony_ci if (ret) { 5738c2ecf20Sopenharmony_ci dev_err(&client->dev, "pm800_pages_init failed!\n"); 5748c2ecf20Sopenharmony_ci goto err_device_init; 5758c2ecf20Sopenharmony_ci } 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci ret = device_800_init(chip, pdata); 5788c2ecf20Sopenharmony_ci if (ret) { 5798c2ecf20Sopenharmony_ci dev_err(chip->dev, "Failed to initialize 88pm800 devices\n"); 5808c2ecf20Sopenharmony_ci goto err_device_init; 5818c2ecf20Sopenharmony_ci } 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci if (pdata && pdata->plat_config) 5848c2ecf20Sopenharmony_ci pdata->plat_config(chip, pdata); 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci return 0; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_cierr_device_init: 5898c2ecf20Sopenharmony_ci pm800_pages_exit(chip); 5908c2ecf20Sopenharmony_cierr_subchip_alloc: 5918c2ecf20Sopenharmony_ci pm80x_deinit(); 5928c2ecf20Sopenharmony_ciout_init: 5938c2ecf20Sopenharmony_ci return ret; 5948c2ecf20Sopenharmony_ci} 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_cistatic int pm800_remove(struct i2c_client *client) 5978c2ecf20Sopenharmony_ci{ 5988c2ecf20Sopenharmony_ci struct pm80x_chip *chip = i2c_get_clientdata(client); 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci mfd_remove_devices(chip->dev); 6018c2ecf20Sopenharmony_ci device_irq_exit_800(chip); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci pm800_pages_exit(chip); 6048c2ecf20Sopenharmony_ci pm80x_deinit(); 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci return 0; 6078c2ecf20Sopenharmony_ci} 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_cistatic struct i2c_driver pm800_driver = { 6108c2ecf20Sopenharmony_ci .driver = { 6118c2ecf20Sopenharmony_ci .name = "88PM800", 6128c2ecf20Sopenharmony_ci .pm = &pm80x_pm_ops, 6138c2ecf20Sopenharmony_ci }, 6148c2ecf20Sopenharmony_ci .probe = pm800_probe, 6158c2ecf20Sopenharmony_ci .remove = pm800_remove, 6168c2ecf20Sopenharmony_ci .id_table = pm80x_id_table, 6178c2ecf20Sopenharmony_ci}; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_cistatic int __init pm800_i2c_init(void) 6208c2ecf20Sopenharmony_ci{ 6218c2ecf20Sopenharmony_ci return i2c_add_driver(&pm800_driver); 6228c2ecf20Sopenharmony_ci} 6238c2ecf20Sopenharmony_cisubsys_initcall(pm800_i2c_init); 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_cistatic void __exit pm800_i2c_exit(void) 6268c2ecf20Sopenharmony_ci{ 6278c2ecf20Sopenharmony_ci i2c_del_driver(&pm800_driver); 6288c2ecf20Sopenharmony_ci} 6298c2ecf20Sopenharmony_cimodule_exit(pm800_i2c_exit); 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("PMIC Driver for Marvell 88PM800"); 6328c2ecf20Sopenharmony_ciMODULE_AUTHOR("Qiao Zhou <zhouqiao@marvell.com>"); 6338c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 634