162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Core driver for STw4810/STw4811 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2013 ST-Ericsson SA 662306a36Sopenharmony_ci * Written on behalf of Linaro for ST-Ericsson 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Author: Linus Walleij <linus.walleij@linaro.org> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/err.h> 1262306a36Sopenharmony_ci#include <linux/i2c.h> 1362306a36Sopenharmony_ci#include <linux/init.h> 1462306a36Sopenharmony_ci#include <linux/mfd/core.h> 1562306a36Sopenharmony_ci#include <linux/mfd/stw481x.h> 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/regmap.h> 1862306a36Sopenharmony_ci#include <linux/spinlock.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* 2262306a36Sopenharmony_ci * This driver can only access the non-USB portions of STw4811, the register 2362306a36Sopenharmony_ci * range 0x00-0x10 dealing with USB is bound to the two special I2C pins used 2462306a36Sopenharmony_ci * for USB control. 2562306a36Sopenharmony_ci */ 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* Registers inside the power control address space */ 2862306a36Sopenharmony_ci#define STW_PC_VCORE_SEL 0x05U 2962306a36Sopenharmony_ci#define STW_PC_VAUX_SEL 0x06U 3062306a36Sopenharmony_ci#define STW_PC_VPLL_SEL 0x07U 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/** 3362306a36Sopenharmony_ci * stw481x_get_pctl_reg() - get a power control register 3462306a36Sopenharmony_ci * @stw481x: handle to the stw481x chip 3562306a36Sopenharmony_ci * @reg: power control register to fetch 3662306a36Sopenharmony_ci * 3762306a36Sopenharmony_ci * The power control registers is a set of one-time-programmable registers 3862306a36Sopenharmony_ci * in its own register space, accessed by writing addess bits to these 3962306a36Sopenharmony_ci * two registers: bits 7,6,5 of PCTL_REG_LO corresponds to the 3 LSBs of 4062306a36Sopenharmony_ci * the address and bits 8,9 of PCTL_REG_HI corresponds to the 2 MSBs of 4162306a36Sopenharmony_ci * the address, forming an address space of 5 bits, i.e. 32 registers 4262306a36Sopenharmony_ci * 0x00 ... 0x1f can be obtained. 4362306a36Sopenharmony_ci */ 4462306a36Sopenharmony_cistatic int stw481x_get_pctl_reg(struct stw481x *stw481x, u8 reg) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci u8 msb = (reg >> 3) & 0x03; 4762306a36Sopenharmony_ci u8 lsb = (reg << 5) & 0xe0; 4862306a36Sopenharmony_ci unsigned int val; 4962306a36Sopenharmony_ci u8 vrfy; 5062306a36Sopenharmony_ci int ret; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci ret = regmap_write(stw481x->map, STW_PCTL_REG_HI, msb); 5362306a36Sopenharmony_ci if (ret) 5462306a36Sopenharmony_ci return ret; 5562306a36Sopenharmony_ci ret = regmap_write(stw481x->map, STW_PCTL_REG_LO, lsb); 5662306a36Sopenharmony_ci if (ret) 5762306a36Sopenharmony_ci return ret; 5862306a36Sopenharmony_ci ret = regmap_read(stw481x->map, STW_PCTL_REG_HI, &val); 5962306a36Sopenharmony_ci if (ret) 6062306a36Sopenharmony_ci return ret; 6162306a36Sopenharmony_ci vrfy = (val & 0x03) << 3; 6262306a36Sopenharmony_ci ret = regmap_read(stw481x->map, STW_PCTL_REG_LO, &val); 6362306a36Sopenharmony_ci if (ret) 6462306a36Sopenharmony_ci return ret; 6562306a36Sopenharmony_ci vrfy |= ((val >> 5) & 0x07); 6662306a36Sopenharmony_ci if (vrfy != reg) 6762306a36Sopenharmony_ci return -EIO; 6862306a36Sopenharmony_ci return (val >> 1) & 0x0f; 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic int stw481x_startup(struct stw481x *stw481x) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci /* Voltages multiplied by 100 */ 7462306a36Sopenharmony_ci static const u8 vcore_val[] = { 7562306a36Sopenharmony_ci 100, 105, 110, 115, 120, 122, 124, 126, 128, 7662306a36Sopenharmony_ci 130, 132, 134, 136, 138, 140, 145 7762306a36Sopenharmony_ci }; 7862306a36Sopenharmony_ci static const u8 vpll_val[] = { 105, 120, 130, 180 }; 7962306a36Sopenharmony_ci static const u8 vaux_val[] = { 15, 18, 25, 28 }; 8062306a36Sopenharmony_ci u8 vcore; 8162306a36Sopenharmony_ci u8 vcore_slp; 8262306a36Sopenharmony_ci u8 vpll; 8362306a36Sopenharmony_ci u8 vaux; 8462306a36Sopenharmony_ci bool vaux_en; 8562306a36Sopenharmony_ci bool it_warn; 8662306a36Sopenharmony_ci int ret; 8762306a36Sopenharmony_ci unsigned int val; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci ret = regmap_read(stw481x->map, STW_CONF1, &val); 9062306a36Sopenharmony_ci if (ret) 9162306a36Sopenharmony_ci return ret; 9262306a36Sopenharmony_ci vaux_en = !!(val & STW_CONF1_PDN_VAUX); 9362306a36Sopenharmony_ci it_warn = !!(val & STW_CONF1_IT_WARN); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci dev_info(&stw481x->client->dev, "voltages %s\n", 9662306a36Sopenharmony_ci (val & STW_CONF1_V_MONITORING) ? "OK" : "LOW"); 9762306a36Sopenharmony_ci dev_info(&stw481x->client->dev, "MMC level shifter %s\n", 9862306a36Sopenharmony_ci (val & STW_CONF1_MMC_LS_STATUS) ? "high impedance" : "ON"); 9962306a36Sopenharmony_ci dev_info(&stw481x->client->dev, "VMMC: %s\n", 10062306a36Sopenharmony_ci (val & STW_CONF1_PDN_VMMC) ? "ON" : "disabled"); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci dev_info(&stw481x->client->dev, "STw481x power control registers:\n"); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci ret = stw481x_get_pctl_reg(stw481x, STW_PC_VCORE_SEL); 10562306a36Sopenharmony_ci if (ret < 0) 10662306a36Sopenharmony_ci return ret; 10762306a36Sopenharmony_ci vcore = ret & 0x0f; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci ret = stw481x_get_pctl_reg(stw481x, STW_PC_VAUX_SEL); 11062306a36Sopenharmony_ci if (ret < 0) 11162306a36Sopenharmony_ci return ret; 11262306a36Sopenharmony_ci vaux = (ret >> 2) & 3; 11362306a36Sopenharmony_ci vpll = (ret >> 4) & 1; /* Save bit 4 */ 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci ret = stw481x_get_pctl_reg(stw481x, STW_PC_VPLL_SEL); 11662306a36Sopenharmony_ci if (ret < 0) 11762306a36Sopenharmony_ci return ret; 11862306a36Sopenharmony_ci vpll |= (ret >> 1) & 2; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci dev_info(&stw481x->client->dev, "VCORE: %u.%uV %s\n", 12162306a36Sopenharmony_ci vcore_val[vcore] / 100, vcore_val[vcore] % 100, 12262306a36Sopenharmony_ci (ret & 4) ? "ON" : "OFF"); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci dev_info(&stw481x->client->dev, "VPLL: %u.%uV %s\n", 12562306a36Sopenharmony_ci vpll_val[vpll] / 100, vpll_val[vpll] % 100, 12662306a36Sopenharmony_ci (ret & 0x10) ? "ON" : "OFF"); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci dev_info(&stw481x->client->dev, "VAUX: %u.%uV %s\n", 12962306a36Sopenharmony_ci vaux_val[vaux] / 10, vaux_val[vaux] % 10, 13062306a36Sopenharmony_ci vaux_en ? "ON" : "OFF"); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci ret = regmap_read(stw481x->map, STW_CONF2, &val); 13362306a36Sopenharmony_ci if (ret) 13462306a36Sopenharmony_ci return ret; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci dev_info(&stw481x->client->dev, "TWARN: %s threshold, %s\n", 13762306a36Sopenharmony_ci it_warn ? "below" : "above", 13862306a36Sopenharmony_ci (val & STW_CONF2_MASK_TWARN) ? 13962306a36Sopenharmony_ci "enabled" : "mask through VDDOK"); 14062306a36Sopenharmony_ci dev_info(&stw481x->client->dev, "VMMC: %s\n", 14162306a36Sopenharmony_ci (val & STW_CONF2_VMMC_EXT) ? "internal" : "external"); 14262306a36Sopenharmony_ci dev_info(&stw481x->client->dev, "IT WAKE UP: %s\n", 14362306a36Sopenharmony_ci (val & STW_CONF2_MASK_IT_WAKE_UP) ? "enabled" : "masked"); 14462306a36Sopenharmony_ci dev_info(&stw481x->client->dev, "GPO1: %s\n", 14562306a36Sopenharmony_ci (val & STW_CONF2_GPO1) ? "low" : "high impedance"); 14662306a36Sopenharmony_ci dev_info(&stw481x->client->dev, "GPO2: %s\n", 14762306a36Sopenharmony_ci (val & STW_CONF2_GPO2) ? "low" : "high impedance"); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci ret = regmap_read(stw481x->map, STW_VCORE_SLEEP, &val); 15062306a36Sopenharmony_ci if (ret) 15162306a36Sopenharmony_ci return ret; 15262306a36Sopenharmony_ci vcore_slp = val & 0x0f; 15362306a36Sopenharmony_ci dev_info(&stw481x->client->dev, "VCORE SLEEP: %u.%uV\n", 15462306a36Sopenharmony_ci vcore_val[vcore_slp] / 100, vcore_val[vcore_slp] % 100); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci return 0; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci/* 16062306a36Sopenharmony_ci * MFD cells - we have one cell which is selected operation 16162306a36Sopenharmony_ci * mode, and we always have a GPIO cell. 16262306a36Sopenharmony_ci */ 16362306a36Sopenharmony_cistatic struct mfd_cell stw481x_cells[] = { 16462306a36Sopenharmony_ci { 16562306a36Sopenharmony_ci .of_compatible = "st,stw481x-vmmc", 16662306a36Sopenharmony_ci .name = "stw481x-vmmc-regulator", 16762306a36Sopenharmony_ci .id = -1, 16862306a36Sopenharmony_ci }, 16962306a36Sopenharmony_ci}; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic const struct regmap_config stw481x_regmap_config = { 17262306a36Sopenharmony_ci .reg_bits = 8, 17362306a36Sopenharmony_ci .val_bits = 8, 17462306a36Sopenharmony_ci}; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic int stw481x_probe(struct i2c_client *client) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci struct stw481x *stw481x; 17962306a36Sopenharmony_ci int ret; 18062306a36Sopenharmony_ci int i; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci stw481x = devm_kzalloc(&client->dev, sizeof(*stw481x), GFP_KERNEL); 18362306a36Sopenharmony_ci if (!stw481x) 18462306a36Sopenharmony_ci return -ENOMEM; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci i2c_set_clientdata(client, stw481x); 18762306a36Sopenharmony_ci stw481x->client = client; 18862306a36Sopenharmony_ci stw481x->map = devm_regmap_init_i2c(client, &stw481x_regmap_config); 18962306a36Sopenharmony_ci if (IS_ERR(stw481x->map)) { 19062306a36Sopenharmony_ci ret = PTR_ERR(stw481x->map); 19162306a36Sopenharmony_ci dev_err(&client->dev, "Failed to allocate register map: %d\n", 19262306a36Sopenharmony_ci ret); 19362306a36Sopenharmony_ci return ret; 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci ret = stw481x_startup(stw481x); 19762306a36Sopenharmony_ci if (ret) { 19862306a36Sopenharmony_ci dev_err(&client->dev, "chip initialization failed\n"); 19962306a36Sopenharmony_ci return ret; 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci /* Set up and register the platform devices. */ 20362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(stw481x_cells); i++) { 20462306a36Sopenharmony_ci /* One state holder for all drivers, this is simple */ 20562306a36Sopenharmony_ci stw481x_cells[i].platform_data = stw481x; 20662306a36Sopenharmony_ci stw481x_cells[i].pdata_size = sizeof(*stw481x); 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci ret = devm_mfd_add_devices(&client->dev, 0, stw481x_cells, 21062306a36Sopenharmony_ci ARRAY_SIZE(stw481x_cells), NULL, 0, NULL); 21162306a36Sopenharmony_ci if (ret) 21262306a36Sopenharmony_ci return ret; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci dev_info(&client->dev, "initialized STw481x device\n"); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci return ret; 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci/* 22062306a36Sopenharmony_ci * This ID table is completely unused, as this is a pure 22162306a36Sopenharmony_ci * device-tree probed driver, but it has to be here due to 22262306a36Sopenharmony_ci * the structure of the I2C core. 22362306a36Sopenharmony_ci */ 22462306a36Sopenharmony_cistatic const struct i2c_device_id stw481x_id[] = { 22562306a36Sopenharmony_ci { "stw481x", 0 }, 22662306a36Sopenharmony_ci { }, 22762306a36Sopenharmony_ci}; 22862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, stw481x_id); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic const struct of_device_id stw481x_match[] = { 23162306a36Sopenharmony_ci { .compatible = "st,stw4810", }, 23262306a36Sopenharmony_ci { .compatible = "st,stw4811", }, 23362306a36Sopenharmony_ci { }, 23462306a36Sopenharmony_ci}; 23562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, stw481x_match); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic struct i2c_driver stw481x_driver = { 23862306a36Sopenharmony_ci .driver = { 23962306a36Sopenharmony_ci .name = "stw481x", 24062306a36Sopenharmony_ci .of_match_table = stw481x_match, 24162306a36Sopenharmony_ci }, 24262306a36Sopenharmony_ci .probe = stw481x_probe, 24362306a36Sopenharmony_ci .id_table = stw481x_id, 24462306a36Sopenharmony_ci}; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cimodule_i2c_driver(stw481x_driver); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ciMODULE_AUTHOR("Linus Walleij"); 24962306a36Sopenharmony_ciMODULE_DESCRIPTION("STw481x PMIC driver"); 25062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 251