18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Regulator driver for TPS6524x PMIC 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2010 Texas Instruments 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or 78c2ecf20Sopenharmony_ci * modify it under the terms of the GNU General Public License as 88c2ecf20Sopenharmony_ci * published by the Free Software Foundation version 2. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, 118c2ecf20Sopenharmony_ci * whether express or implied; without even the implied warranty of 128c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 138c2ecf20Sopenharmony_ci * General Public License for more details. 148c2ecf20Sopenharmony_ci */ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/kernel.h> 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include <linux/err.h> 198c2ecf20Sopenharmony_ci#include <linux/errno.h> 208c2ecf20Sopenharmony_ci#include <linux/slab.h> 218c2ecf20Sopenharmony_ci#include <linux/spi/spi.h> 228c2ecf20Sopenharmony_ci#include <linux/regulator/driver.h> 238c2ecf20Sopenharmony_ci#include <linux/regulator/machine.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define REG_LDO_SET 0x0 268c2ecf20Sopenharmony_ci#define LDO_ILIM_MASK 1 /* 0 = 400-800, 1 = 900-1500 */ 278c2ecf20Sopenharmony_ci#define LDO_VSEL_MASK 0x0f 288c2ecf20Sopenharmony_ci#define LDO2_ILIM_SHIFT 12 298c2ecf20Sopenharmony_ci#define LDO2_VSEL_SHIFT 4 308c2ecf20Sopenharmony_ci#define LDO1_ILIM_SHIFT 8 318c2ecf20Sopenharmony_ci#define LDO1_VSEL_SHIFT 0 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define REG_BLOCK_EN 0x1 348c2ecf20Sopenharmony_ci#define BLOCK_MASK 1 358c2ecf20Sopenharmony_ci#define BLOCK_LDO1_SHIFT 0 368c2ecf20Sopenharmony_ci#define BLOCK_LDO2_SHIFT 1 378c2ecf20Sopenharmony_ci#define BLOCK_LCD_SHIFT 2 388c2ecf20Sopenharmony_ci#define BLOCK_USB_SHIFT 3 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define REG_DCDC_SET 0x2 418c2ecf20Sopenharmony_ci#define DCDC_VDCDC_MASK 0x1f 428c2ecf20Sopenharmony_ci#define DCDC_VDCDC1_SHIFT 0 438c2ecf20Sopenharmony_ci#define DCDC_VDCDC2_SHIFT 5 448c2ecf20Sopenharmony_ci#define DCDC_VDCDC3_SHIFT 10 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#define REG_DCDC_EN 0x3 478c2ecf20Sopenharmony_ci#define DCDCDCDC_EN_MASK 0x1 488c2ecf20Sopenharmony_ci#define DCDCDCDC1_EN_SHIFT 0 498c2ecf20Sopenharmony_ci#define DCDCDCDC1_PG_MSK BIT(1) 508c2ecf20Sopenharmony_ci#define DCDCDCDC2_EN_SHIFT 2 518c2ecf20Sopenharmony_ci#define DCDCDCDC2_PG_MSK BIT(3) 528c2ecf20Sopenharmony_ci#define DCDCDCDC3_EN_SHIFT 4 538c2ecf20Sopenharmony_ci#define DCDCDCDC3_PG_MSK BIT(5) 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#define REG_USB 0x4 568c2ecf20Sopenharmony_ci#define USB_ILIM_SHIFT 0 578c2ecf20Sopenharmony_ci#define USB_ILIM_MASK 0x3 588c2ecf20Sopenharmony_ci#define USB_TSD_SHIFT 2 598c2ecf20Sopenharmony_ci#define USB_TSD_MASK 0x3 608c2ecf20Sopenharmony_ci#define USB_TWARN_SHIFT 4 618c2ecf20Sopenharmony_ci#define USB_TWARN_MASK 0x3 628c2ecf20Sopenharmony_ci#define USB_IWARN_SD BIT(6) 638c2ecf20Sopenharmony_ci#define USB_FAST_LOOP BIT(7) 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci#define REG_ALARM 0x5 668c2ecf20Sopenharmony_ci#define ALARM_LDO1 BIT(0) 678c2ecf20Sopenharmony_ci#define ALARM_DCDC1 BIT(1) 688c2ecf20Sopenharmony_ci#define ALARM_DCDC2 BIT(2) 698c2ecf20Sopenharmony_ci#define ALARM_DCDC3 BIT(3) 708c2ecf20Sopenharmony_ci#define ALARM_LDO2 BIT(4) 718c2ecf20Sopenharmony_ci#define ALARM_USB_WARN BIT(5) 728c2ecf20Sopenharmony_ci#define ALARM_USB_ALARM BIT(6) 738c2ecf20Sopenharmony_ci#define ALARM_LCD BIT(9) 748c2ecf20Sopenharmony_ci#define ALARM_TEMP_WARM BIT(10) 758c2ecf20Sopenharmony_ci#define ALARM_TEMP_HOT BIT(11) 768c2ecf20Sopenharmony_ci#define ALARM_NRST BIT(14) 778c2ecf20Sopenharmony_ci#define ALARM_POWERUP BIT(15) 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci#define REG_INT_ENABLE 0x6 808c2ecf20Sopenharmony_ci#define INT_LDO1 BIT(0) 818c2ecf20Sopenharmony_ci#define INT_DCDC1 BIT(1) 828c2ecf20Sopenharmony_ci#define INT_DCDC2 BIT(2) 838c2ecf20Sopenharmony_ci#define INT_DCDC3 BIT(3) 848c2ecf20Sopenharmony_ci#define INT_LDO2 BIT(4) 858c2ecf20Sopenharmony_ci#define INT_USB_WARN BIT(5) 868c2ecf20Sopenharmony_ci#define INT_USB_ALARM BIT(6) 878c2ecf20Sopenharmony_ci#define INT_LCD BIT(9) 888c2ecf20Sopenharmony_ci#define INT_TEMP_WARM BIT(10) 898c2ecf20Sopenharmony_ci#define INT_TEMP_HOT BIT(11) 908c2ecf20Sopenharmony_ci#define INT_GLOBAL_EN BIT(15) 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci#define REG_INT_STATUS 0x7 938c2ecf20Sopenharmony_ci#define STATUS_LDO1 BIT(0) 948c2ecf20Sopenharmony_ci#define STATUS_DCDC1 BIT(1) 958c2ecf20Sopenharmony_ci#define STATUS_DCDC2 BIT(2) 968c2ecf20Sopenharmony_ci#define STATUS_DCDC3 BIT(3) 978c2ecf20Sopenharmony_ci#define STATUS_LDO2 BIT(4) 988c2ecf20Sopenharmony_ci#define STATUS_USB_WARN BIT(5) 998c2ecf20Sopenharmony_ci#define STATUS_USB_ALARM BIT(6) 1008c2ecf20Sopenharmony_ci#define STATUS_LCD BIT(9) 1018c2ecf20Sopenharmony_ci#define STATUS_TEMP_WARM BIT(10) 1028c2ecf20Sopenharmony_ci#define STATUS_TEMP_HOT BIT(11) 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci#define REG_SOFTWARE_RESET 0xb 1058c2ecf20Sopenharmony_ci#define REG_WRITE_ENABLE 0xd 1068c2ecf20Sopenharmony_ci#define REG_REV_ID 0xf 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci#define N_DCDC 3 1098c2ecf20Sopenharmony_ci#define N_LDO 2 1108c2ecf20Sopenharmony_ci#define N_SWITCH 2 1118c2ecf20Sopenharmony_ci#define N_REGULATORS (N_DCDC + N_LDO + N_SWITCH) 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci#define CMD_READ(reg) ((reg) << 6) 1148c2ecf20Sopenharmony_ci#define CMD_WRITE(reg) (BIT(5) | (reg) << 6) 1158c2ecf20Sopenharmony_ci#define STAT_CLK BIT(3) 1168c2ecf20Sopenharmony_ci#define STAT_WRITE BIT(2) 1178c2ecf20Sopenharmony_ci#define STAT_INVALID BIT(1) 1188c2ecf20Sopenharmony_ci#define STAT_WP BIT(0) 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistruct field { 1218c2ecf20Sopenharmony_ci int reg; 1228c2ecf20Sopenharmony_ci int shift; 1238c2ecf20Sopenharmony_ci int mask; 1248c2ecf20Sopenharmony_ci}; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistruct supply_info { 1278c2ecf20Sopenharmony_ci const char *name; 1288c2ecf20Sopenharmony_ci int n_voltages; 1298c2ecf20Sopenharmony_ci const unsigned int *voltages; 1308c2ecf20Sopenharmony_ci int n_ilimsels; 1318c2ecf20Sopenharmony_ci const unsigned int *ilimsels; 1328c2ecf20Sopenharmony_ci struct field enable, voltage, ilimsel; 1338c2ecf20Sopenharmony_ci}; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistruct tps6524x { 1368c2ecf20Sopenharmony_ci struct device *dev; 1378c2ecf20Sopenharmony_ci struct spi_device *spi; 1388c2ecf20Sopenharmony_ci struct mutex lock; 1398c2ecf20Sopenharmony_ci struct regulator_desc desc[N_REGULATORS]; 1408c2ecf20Sopenharmony_ci}; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic int __read_reg(struct tps6524x *hw, int reg) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci int error = 0; 1458c2ecf20Sopenharmony_ci u16 cmd = CMD_READ(reg), in; 1468c2ecf20Sopenharmony_ci u8 status; 1478c2ecf20Sopenharmony_ci struct spi_message m; 1488c2ecf20Sopenharmony_ci struct spi_transfer t[3]; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci spi_message_init(&m); 1518c2ecf20Sopenharmony_ci memset(t, 0, sizeof(t)); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci t[0].tx_buf = &cmd; 1548c2ecf20Sopenharmony_ci t[0].len = 2; 1558c2ecf20Sopenharmony_ci t[0].bits_per_word = 12; 1568c2ecf20Sopenharmony_ci spi_message_add_tail(&t[0], &m); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci t[1].rx_buf = ∈ 1598c2ecf20Sopenharmony_ci t[1].len = 2; 1608c2ecf20Sopenharmony_ci t[1].bits_per_word = 16; 1618c2ecf20Sopenharmony_ci spi_message_add_tail(&t[1], &m); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci t[2].rx_buf = &status; 1648c2ecf20Sopenharmony_ci t[2].len = 1; 1658c2ecf20Sopenharmony_ci t[2].bits_per_word = 4; 1668c2ecf20Sopenharmony_ci spi_message_add_tail(&t[2], &m); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci error = spi_sync(hw->spi, &m); 1698c2ecf20Sopenharmony_ci if (error < 0) 1708c2ecf20Sopenharmony_ci return error; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci dev_dbg(hw->dev, "read reg %d, data %x, status %x\n", 1738c2ecf20Sopenharmony_ci reg, in, status); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if (!(status & STAT_CLK) || (status & STAT_WRITE)) 1768c2ecf20Sopenharmony_ci return -EIO; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (status & STAT_INVALID) 1798c2ecf20Sopenharmony_ci return -EINVAL; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci return in; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic int read_reg(struct tps6524x *hw, int reg) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci int ret; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci mutex_lock(&hw->lock); 1898c2ecf20Sopenharmony_ci ret = __read_reg(hw, reg); 1908c2ecf20Sopenharmony_ci mutex_unlock(&hw->lock); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci return ret; 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic int __write_reg(struct tps6524x *hw, int reg, int val) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci int error = 0; 1988c2ecf20Sopenharmony_ci u16 cmd = CMD_WRITE(reg), out = val; 1998c2ecf20Sopenharmony_ci u8 status; 2008c2ecf20Sopenharmony_ci struct spi_message m; 2018c2ecf20Sopenharmony_ci struct spi_transfer t[3]; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci spi_message_init(&m); 2048c2ecf20Sopenharmony_ci memset(t, 0, sizeof(t)); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci t[0].tx_buf = &cmd; 2078c2ecf20Sopenharmony_ci t[0].len = 2; 2088c2ecf20Sopenharmony_ci t[0].bits_per_word = 12; 2098c2ecf20Sopenharmony_ci spi_message_add_tail(&t[0], &m); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci t[1].tx_buf = &out; 2128c2ecf20Sopenharmony_ci t[1].len = 2; 2138c2ecf20Sopenharmony_ci t[1].bits_per_word = 16; 2148c2ecf20Sopenharmony_ci spi_message_add_tail(&t[1], &m); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci t[2].rx_buf = &status; 2178c2ecf20Sopenharmony_ci t[2].len = 1; 2188c2ecf20Sopenharmony_ci t[2].bits_per_word = 4; 2198c2ecf20Sopenharmony_ci spi_message_add_tail(&t[2], &m); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci error = spi_sync(hw->spi, &m); 2228c2ecf20Sopenharmony_ci if (error < 0) 2238c2ecf20Sopenharmony_ci return error; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci dev_dbg(hw->dev, "wrote reg %d, data %x, status %x\n", 2268c2ecf20Sopenharmony_ci reg, out, status); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci if (!(status & STAT_CLK) || !(status & STAT_WRITE)) 2298c2ecf20Sopenharmony_ci return -EIO; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci if (status & (STAT_INVALID | STAT_WP)) 2328c2ecf20Sopenharmony_ci return -EINVAL; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci return error; 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic int __rmw_reg(struct tps6524x *hw, int reg, int mask, int val) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci int ret; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci ret = __read_reg(hw, reg); 2428c2ecf20Sopenharmony_ci if (ret < 0) 2438c2ecf20Sopenharmony_ci return ret; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci ret &= ~mask; 2468c2ecf20Sopenharmony_ci ret |= val; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci ret = __write_reg(hw, reg, ret); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci return (ret < 0) ? ret : 0; 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistatic int rmw_protect(struct tps6524x *hw, int reg, int mask, int val) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci int ret; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci mutex_lock(&hw->lock); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci ret = __write_reg(hw, REG_WRITE_ENABLE, 1); 2608c2ecf20Sopenharmony_ci if (ret) { 2618c2ecf20Sopenharmony_ci dev_err(hw->dev, "failed to set write enable\n"); 2628c2ecf20Sopenharmony_ci goto error; 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci ret = __rmw_reg(hw, reg, mask, val); 2668c2ecf20Sopenharmony_ci if (ret) 2678c2ecf20Sopenharmony_ci dev_err(hw->dev, "failed to rmw register %d\n", reg); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci ret = __write_reg(hw, REG_WRITE_ENABLE, 0); 2708c2ecf20Sopenharmony_ci if (ret) { 2718c2ecf20Sopenharmony_ci dev_err(hw->dev, "failed to clear write enable\n"); 2728c2ecf20Sopenharmony_ci goto error; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cierror: 2768c2ecf20Sopenharmony_ci mutex_unlock(&hw->lock); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci return ret; 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_cistatic int read_field(struct tps6524x *hw, const struct field *field) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci int tmp; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci tmp = read_reg(hw, field->reg); 2868c2ecf20Sopenharmony_ci if (tmp < 0) 2878c2ecf20Sopenharmony_ci return tmp; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci return (tmp >> field->shift) & field->mask; 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistatic int write_field(struct tps6524x *hw, const struct field *field, 2938c2ecf20Sopenharmony_ci int val) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci if (val & ~field->mask) 2968c2ecf20Sopenharmony_ci return -EOVERFLOW; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci return rmw_protect(hw, field->reg, 2998c2ecf20Sopenharmony_ci field->mask << field->shift, 3008c2ecf20Sopenharmony_ci val << field->shift); 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic const unsigned int dcdc1_voltages[] = { 3048c2ecf20Sopenharmony_ci 800000, 825000, 850000, 875000, 3058c2ecf20Sopenharmony_ci 900000, 925000, 950000, 975000, 3068c2ecf20Sopenharmony_ci 1000000, 1025000, 1050000, 1075000, 3078c2ecf20Sopenharmony_ci 1100000, 1125000, 1150000, 1175000, 3088c2ecf20Sopenharmony_ci 1200000, 1225000, 1250000, 1275000, 3098c2ecf20Sopenharmony_ci 1300000, 1325000, 1350000, 1375000, 3108c2ecf20Sopenharmony_ci 1400000, 1425000, 1450000, 1475000, 3118c2ecf20Sopenharmony_ci 1500000, 1525000, 1550000, 1575000, 3128c2ecf20Sopenharmony_ci}; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cistatic const unsigned int dcdc2_voltages[] = { 3158c2ecf20Sopenharmony_ci 1400000, 1450000, 1500000, 1550000, 3168c2ecf20Sopenharmony_ci 1600000, 1650000, 1700000, 1750000, 3178c2ecf20Sopenharmony_ci 1800000, 1850000, 1900000, 1950000, 3188c2ecf20Sopenharmony_ci 2000000, 2050000, 2100000, 2150000, 3198c2ecf20Sopenharmony_ci 2200000, 2250000, 2300000, 2350000, 3208c2ecf20Sopenharmony_ci 2400000, 2450000, 2500000, 2550000, 3218c2ecf20Sopenharmony_ci 2600000, 2650000, 2700000, 2750000, 3228c2ecf20Sopenharmony_ci 2800000, 2850000, 2900000, 2950000, 3238c2ecf20Sopenharmony_ci}; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cistatic const unsigned int dcdc3_voltages[] = { 3268c2ecf20Sopenharmony_ci 2400000, 2450000, 2500000, 2550000, 2600000, 3278c2ecf20Sopenharmony_ci 2650000, 2700000, 2750000, 2800000, 2850000, 3288c2ecf20Sopenharmony_ci 2900000, 2950000, 3000000, 3050000, 3100000, 3298c2ecf20Sopenharmony_ci 3150000, 3200000, 3250000, 3300000, 3350000, 3308c2ecf20Sopenharmony_ci 3400000, 3450000, 3500000, 3550000, 3600000, 3318c2ecf20Sopenharmony_ci}; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_cistatic const unsigned int ldo1_voltages[] = { 3348c2ecf20Sopenharmony_ci 4300000, 4350000, 4400000, 4450000, 3358c2ecf20Sopenharmony_ci 4500000, 4550000, 4600000, 4650000, 3368c2ecf20Sopenharmony_ci 4700000, 4750000, 4800000, 4850000, 3378c2ecf20Sopenharmony_ci 4900000, 4950000, 5000000, 5050000, 3388c2ecf20Sopenharmony_ci}; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_cistatic const unsigned int ldo2_voltages[] = { 3418c2ecf20Sopenharmony_ci 1100000, 1150000, 1200000, 1250000, 3428c2ecf20Sopenharmony_ci 1300000, 1700000, 1750000, 1800000, 3438c2ecf20Sopenharmony_ci 1850000, 1900000, 3150000, 3200000, 3448c2ecf20Sopenharmony_ci 3250000, 3300000, 3350000, 3400000, 3458c2ecf20Sopenharmony_ci}; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistatic const unsigned int fixed_5000000_voltage[] = { 3488c2ecf20Sopenharmony_ci 5000000 3498c2ecf20Sopenharmony_ci}; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cistatic const unsigned int ldo_ilimsel[] = { 3528c2ecf20Sopenharmony_ci 400000, 1500000 3538c2ecf20Sopenharmony_ci}; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_cistatic const unsigned int usb_ilimsel[] = { 3568c2ecf20Sopenharmony_ci 200000, 400000, 800000, 1000000 3578c2ecf20Sopenharmony_ci}; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_cistatic const unsigned int fixed_2400000_ilimsel[] = { 3608c2ecf20Sopenharmony_ci 2400000 3618c2ecf20Sopenharmony_ci}; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cistatic const unsigned int fixed_1200000_ilimsel[] = { 3648c2ecf20Sopenharmony_ci 1200000 3658c2ecf20Sopenharmony_ci}; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_cistatic const unsigned int fixed_400000_ilimsel[] = { 3688c2ecf20Sopenharmony_ci 400000 3698c2ecf20Sopenharmony_ci}; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci#define __MK_FIELD(_reg, _mask, _shift) \ 3728c2ecf20Sopenharmony_ci { .reg = (_reg), .mask = (_mask), .shift = (_shift), } 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_cistatic const struct supply_info supply_info[N_REGULATORS] = { 3758c2ecf20Sopenharmony_ci { 3768c2ecf20Sopenharmony_ci .name = "DCDC1", 3778c2ecf20Sopenharmony_ci .n_voltages = ARRAY_SIZE(dcdc1_voltages), 3788c2ecf20Sopenharmony_ci .voltages = dcdc1_voltages, 3798c2ecf20Sopenharmony_ci .n_ilimsels = ARRAY_SIZE(fixed_2400000_ilimsel), 3808c2ecf20Sopenharmony_ci .ilimsels = fixed_2400000_ilimsel, 3818c2ecf20Sopenharmony_ci .enable = __MK_FIELD(REG_DCDC_EN, DCDCDCDC_EN_MASK, 3828c2ecf20Sopenharmony_ci DCDCDCDC1_EN_SHIFT), 3838c2ecf20Sopenharmony_ci .voltage = __MK_FIELD(REG_DCDC_SET, DCDC_VDCDC_MASK, 3848c2ecf20Sopenharmony_ci DCDC_VDCDC1_SHIFT), 3858c2ecf20Sopenharmony_ci }, 3868c2ecf20Sopenharmony_ci { 3878c2ecf20Sopenharmony_ci .name = "DCDC2", 3888c2ecf20Sopenharmony_ci .n_voltages = ARRAY_SIZE(dcdc2_voltages), 3898c2ecf20Sopenharmony_ci .voltages = dcdc2_voltages, 3908c2ecf20Sopenharmony_ci .n_ilimsels = ARRAY_SIZE(fixed_1200000_ilimsel), 3918c2ecf20Sopenharmony_ci .ilimsels = fixed_1200000_ilimsel, 3928c2ecf20Sopenharmony_ci .enable = __MK_FIELD(REG_DCDC_EN, DCDCDCDC_EN_MASK, 3938c2ecf20Sopenharmony_ci DCDCDCDC2_EN_SHIFT), 3948c2ecf20Sopenharmony_ci .voltage = __MK_FIELD(REG_DCDC_SET, DCDC_VDCDC_MASK, 3958c2ecf20Sopenharmony_ci DCDC_VDCDC2_SHIFT), 3968c2ecf20Sopenharmony_ci }, 3978c2ecf20Sopenharmony_ci { 3988c2ecf20Sopenharmony_ci .name = "DCDC3", 3998c2ecf20Sopenharmony_ci .n_voltages = ARRAY_SIZE(dcdc3_voltages), 4008c2ecf20Sopenharmony_ci .voltages = dcdc3_voltages, 4018c2ecf20Sopenharmony_ci .n_ilimsels = ARRAY_SIZE(fixed_1200000_ilimsel), 4028c2ecf20Sopenharmony_ci .ilimsels = fixed_1200000_ilimsel, 4038c2ecf20Sopenharmony_ci .enable = __MK_FIELD(REG_DCDC_EN, DCDCDCDC_EN_MASK, 4048c2ecf20Sopenharmony_ci DCDCDCDC3_EN_SHIFT), 4058c2ecf20Sopenharmony_ci .voltage = __MK_FIELD(REG_DCDC_SET, DCDC_VDCDC_MASK, 4068c2ecf20Sopenharmony_ci DCDC_VDCDC3_SHIFT), 4078c2ecf20Sopenharmony_ci }, 4088c2ecf20Sopenharmony_ci { 4098c2ecf20Sopenharmony_ci .name = "LDO1", 4108c2ecf20Sopenharmony_ci .n_voltages = ARRAY_SIZE(ldo1_voltages), 4118c2ecf20Sopenharmony_ci .voltages = ldo1_voltages, 4128c2ecf20Sopenharmony_ci .n_ilimsels = ARRAY_SIZE(ldo_ilimsel), 4138c2ecf20Sopenharmony_ci .ilimsels = ldo_ilimsel, 4148c2ecf20Sopenharmony_ci .enable = __MK_FIELD(REG_BLOCK_EN, BLOCK_MASK, 4158c2ecf20Sopenharmony_ci BLOCK_LDO1_SHIFT), 4168c2ecf20Sopenharmony_ci .voltage = __MK_FIELD(REG_LDO_SET, LDO_VSEL_MASK, 4178c2ecf20Sopenharmony_ci LDO1_VSEL_SHIFT), 4188c2ecf20Sopenharmony_ci .ilimsel = __MK_FIELD(REG_LDO_SET, LDO_ILIM_MASK, 4198c2ecf20Sopenharmony_ci LDO1_ILIM_SHIFT), 4208c2ecf20Sopenharmony_ci }, 4218c2ecf20Sopenharmony_ci { 4228c2ecf20Sopenharmony_ci .name = "LDO2", 4238c2ecf20Sopenharmony_ci .n_voltages = ARRAY_SIZE(ldo2_voltages), 4248c2ecf20Sopenharmony_ci .voltages = ldo2_voltages, 4258c2ecf20Sopenharmony_ci .n_ilimsels = ARRAY_SIZE(ldo_ilimsel), 4268c2ecf20Sopenharmony_ci .ilimsels = ldo_ilimsel, 4278c2ecf20Sopenharmony_ci .enable = __MK_FIELD(REG_BLOCK_EN, BLOCK_MASK, 4288c2ecf20Sopenharmony_ci BLOCK_LDO2_SHIFT), 4298c2ecf20Sopenharmony_ci .voltage = __MK_FIELD(REG_LDO_SET, LDO_VSEL_MASK, 4308c2ecf20Sopenharmony_ci LDO2_VSEL_SHIFT), 4318c2ecf20Sopenharmony_ci .ilimsel = __MK_FIELD(REG_LDO_SET, LDO_ILIM_MASK, 4328c2ecf20Sopenharmony_ci LDO2_ILIM_SHIFT), 4338c2ecf20Sopenharmony_ci }, 4348c2ecf20Sopenharmony_ci { 4358c2ecf20Sopenharmony_ci .name = "USB", 4368c2ecf20Sopenharmony_ci .n_voltages = ARRAY_SIZE(fixed_5000000_voltage), 4378c2ecf20Sopenharmony_ci .voltages = fixed_5000000_voltage, 4388c2ecf20Sopenharmony_ci .n_ilimsels = ARRAY_SIZE(usb_ilimsel), 4398c2ecf20Sopenharmony_ci .ilimsels = usb_ilimsel, 4408c2ecf20Sopenharmony_ci .enable = __MK_FIELD(REG_BLOCK_EN, BLOCK_MASK, 4418c2ecf20Sopenharmony_ci BLOCK_USB_SHIFT), 4428c2ecf20Sopenharmony_ci .ilimsel = __MK_FIELD(REG_USB, USB_ILIM_MASK, 4438c2ecf20Sopenharmony_ci USB_ILIM_SHIFT), 4448c2ecf20Sopenharmony_ci }, 4458c2ecf20Sopenharmony_ci { 4468c2ecf20Sopenharmony_ci .name = "LCD", 4478c2ecf20Sopenharmony_ci .n_voltages = ARRAY_SIZE(fixed_5000000_voltage), 4488c2ecf20Sopenharmony_ci .voltages = fixed_5000000_voltage, 4498c2ecf20Sopenharmony_ci .n_ilimsels = ARRAY_SIZE(fixed_400000_ilimsel), 4508c2ecf20Sopenharmony_ci .ilimsels = fixed_400000_ilimsel, 4518c2ecf20Sopenharmony_ci .enable = __MK_FIELD(REG_BLOCK_EN, BLOCK_MASK, 4528c2ecf20Sopenharmony_ci BLOCK_LCD_SHIFT), 4538c2ecf20Sopenharmony_ci }, 4548c2ecf20Sopenharmony_ci}; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_cistatic int set_voltage_sel(struct regulator_dev *rdev, unsigned selector) 4578c2ecf20Sopenharmony_ci{ 4588c2ecf20Sopenharmony_ci const struct supply_info *info; 4598c2ecf20Sopenharmony_ci struct tps6524x *hw; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci hw = rdev_get_drvdata(rdev); 4628c2ecf20Sopenharmony_ci info = &supply_info[rdev_get_id(rdev)]; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci if (rdev->desc->n_voltages == 1) 4658c2ecf20Sopenharmony_ci return -EINVAL; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci return write_field(hw, &info->voltage, selector); 4688c2ecf20Sopenharmony_ci} 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_cistatic int get_voltage_sel(struct regulator_dev *rdev) 4718c2ecf20Sopenharmony_ci{ 4728c2ecf20Sopenharmony_ci const struct supply_info *info; 4738c2ecf20Sopenharmony_ci struct tps6524x *hw; 4748c2ecf20Sopenharmony_ci int ret; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci hw = rdev_get_drvdata(rdev); 4778c2ecf20Sopenharmony_ci info = &supply_info[rdev_get_id(rdev)]; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci if (rdev->desc->n_voltages == 1) 4808c2ecf20Sopenharmony_ci return 0; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci ret = read_field(hw, &info->voltage); 4838c2ecf20Sopenharmony_ci if (ret < 0) 4848c2ecf20Sopenharmony_ci return ret; 4858c2ecf20Sopenharmony_ci if (WARN_ON(ret >= info->n_voltages)) 4868c2ecf20Sopenharmony_ci return -EIO; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci return ret; 4898c2ecf20Sopenharmony_ci} 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_cistatic int set_current_limit(struct regulator_dev *rdev, int min_uA, 4928c2ecf20Sopenharmony_ci int max_uA) 4938c2ecf20Sopenharmony_ci{ 4948c2ecf20Sopenharmony_ci const struct supply_info *info; 4958c2ecf20Sopenharmony_ci struct tps6524x *hw; 4968c2ecf20Sopenharmony_ci int i; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci hw = rdev_get_drvdata(rdev); 4998c2ecf20Sopenharmony_ci info = &supply_info[rdev_get_id(rdev)]; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci if (info->n_ilimsels == 1) 5028c2ecf20Sopenharmony_ci return -EINVAL; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci for (i = info->n_ilimsels - 1; i >= 0; i--) { 5058c2ecf20Sopenharmony_ci if (min_uA <= info->ilimsels[i] && 5068c2ecf20Sopenharmony_ci max_uA >= info->ilimsels[i]) 5078c2ecf20Sopenharmony_ci return write_field(hw, &info->ilimsel, i); 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci return -EINVAL; 5118c2ecf20Sopenharmony_ci} 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_cistatic int get_current_limit(struct regulator_dev *rdev) 5148c2ecf20Sopenharmony_ci{ 5158c2ecf20Sopenharmony_ci const struct supply_info *info; 5168c2ecf20Sopenharmony_ci struct tps6524x *hw; 5178c2ecf20Sopenharmony_ci int ret; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci hw = rdev_get_drvdata(rdev); 5208c2ecf20Sopenharmony_ci info = &supply_info[rdev_get_id(rdev)]; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci if (info->n_ilimsels == 1) 5238c2ecf20Sopenharmony_ci return info->ilimsels[0]; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci ret = read_field(hw, &info->ilimsel); 5268c2ecf20Sopenharmony_ci if (ret < 0) 5278c2ecf20Sopenharmony_ci return ret; 5288c2ecf20Sopenharmony_ci if (WARN_ON(ret >= info->n_ilimsels)) 5298c2ecf20Sopenharmony_ci return -EIO; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci return info->ilimsels[ret]; 5328c2ecf20Sopenharmony_ci} 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_cistatic int enable_supply(struct regulator_dev *rdev) 5358c2ecf20Sopenharmony_ci{ 5368c2ecf20Sopenharmony_ci const struct supply_info *info; 5378c2ecf20Sopenharmony_ci struct tps6524x *hw; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci hw = rdev_get_drvdata(rdev); 5408c2ecf20Sopenharmony_ci info = &supply_info[rdev_get_id(rdev)]; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci return write_field(hw, &info->enable, 1); 5438c2ecf20Sopenharmony_ci} 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_cistatic int disable_supply(struct regulator_dev *rdev) 5468c2ecf20Sopenharmony_ci{ 5478c2ecf20Sopenharmony_ci const struct supply_info *info; 5488c2ecf20Sopenharmony_ci struct tps6524x *hw; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci hw = rdev_get_drvdata(rdev); 5518c2ecf20Sopenharmony_ci info = &supply_info[rdev_get_id(rdev)]; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci return write_field(hw, &info->enable, 0); 5548c2ecf20Sopenharmony_ci} 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_cistatic int is_supply_enabled(struct regulator_dev *rdev) 5578c2ecf20Sopenharmony_ci{ 5588c2ecf20Sopenharmony_ci const struct supply_info *info; 5598c2ecf20Sopenharmony_ci struct tps6524x *hw; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci hw = rdev_get_drvdata(rdev); 5628c2ecf20Sopenharmony_ci info = &supply_info[rdev_get_id(rdev)]; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci return read_field(hw, &info->enable); 5658c2ecf20Sopenharmony_ci} 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_cistatic const struct regulator_ops regulator_ops = { 5688c2ecf20Sopenharmony_ci .is_enabled = is_supply_enabled, 5698c2ecf20Sopenharmony_ci .enable = enable_supply, 5708c2ecf20Sopenharmony_ci .disable = disable_supply, 5718c2ecf20Sopenharmony_ci .get_voltage_sel = get_voltage_sel, 5728c2ecf20Sopenharmony_ci .set_voltage_sel = set_voltage_sel, 5738c2ecf20Sopenharmony_ci .list_voltage = regulator_list_voltage_table, 5748c2ecf20Sopenharmony_ci .map_voltage = regulator_map_voltage_ascend, 5758c2ecf20Sopenharmony_ci .set_current_limit = set_current_limit, 5768c2ecf20Sopenharmony_ci .get_current_limit = get_current_limit, 5778c2ecf20Sopenharmony_ci}; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_cistatic int pmic_probe(struct spi_device *spi) 5808c2ecf20Sopenharmony_ci{ 5818c2ecf20Sopenharmony_ci struct tps6524x *hw; 5828c2ecf20Sopenharmony_ci struct device *dev = &spi->dev; 5838c2ecf20Sopenharmony_ci const struct supply_info *info = supply_info; 5848c2ecf20Sopenharmony_ci struct regulator_init_data *init_data; 5858c2ecf20Sopenharmony_ci struct regulator_config config = { }; 5868c2ecf20Sopenharmony_ci struct regulator_dev *rdev; 5878c2ecf20Sopenharmony_ci int i; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci init_data = dev_get_platdata(dev); 5908c2ecf20Sopenharmony_ci if (!init_data) { 5918c2ecf20Sopenharmony_ci dev_err(dev, "could not find regulator platform data\n"); 5928c2ecf20Sopenharmony_ci return -EINVAL; 5938c2ecf20Sopenharmony_ci } 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci hw = devm_kzalloc(&spi->dev, sizeof(struct tps6524x), GFP_KERNEL); 5968c2ecf20Sopenharmony_ci if (!hw) 5978c2ecf20Sopenharmony_ci return -ENOMEM; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci spi_set_drvdata(spi, hw); 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci memset(hw, 0, sizeof(struct tps6524x)); 6028c2ecf20Sopenharmony_ci hw->dev = dev; 6038c2ecf20Sopenharmony_ci hw->spi = spi; 6048c2ecf20Sopenharmony_ci mutex_init(&hw->lock); 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci for (i = 0; i < N_REGULATORS; i++, info++, init_data++) { 6078c2ecf20Sopenharmony_ci hw->desc[i].name = info->name; 6088c2ecf20Sopenharmony_ci hw->desc[i].id = i; 6098c2ecf20Sopenharmony_ci hw->desc[i].n_voltages = info->n_voltages; 6108c2ecf20Sopenharmony_ci hw->desc[i].volt_table = info->voltages; 6118c2ecf20Sopenharmony_ci hw->desc[i].ops = ®ulator_ops; 6128c2ecf20Sopenharmony_ci hw->desc[i].type = REGULATOR_VOLTAGE; 6138c2ecf20Sopenharmony_ci hw->desc[i].owner = THIS_MODULE; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci config.dev = dev; 6168c2ecf20Sopenharmony_ci config.init_data = init_data; 6178c2ecf20Sopenharmony_ci config.driver_data = hw; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci rdev = devm_regulator_register(dev, &hw->desc[i], &config); 6208c2ecf20Sopenharmony_ci if (IS_ERR(rdev)) 6218c2ecf20Sopenharmony_ci return PTR_ERR(rdev); 6228c2ecf20Sopenharmony_ci } 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci return 0; 6258c2ecf20Sopenharmony_ci} 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_cistatic struct spi_driver pmic_driver = { 6288c2ecf20Sopenharmony_ci .probe = pmic_probe, 6298c2ecf20Sopenharmony_ci .driver = { 6308c2ecf20Sopenharmony_ci .name = "tps6524x", 6318c2ecf20Sopenharmony_ci }, 6328c2ecf20Sopenharmony_ci}; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_cimodule_spi_driver(pmic_driver); 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("TPS6524X PMIC Driver"); 6378c2ecf20Sopenharmony_ciMODULE_AUTHOR("Cyril Chemparathy"); 6388c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 6398c2ecf20Sopenharmony_ciMODULE_ALIAS("spi:tps6524x"); 640