From 6d3d2edad4760fcc4bd1af9fae5a99b45b462a81 Mon Sep 17 00:00:00 2001 From: Jieun Yi Date: Fri, 11 May 2018 13:38:54 +0900 Subject: [PATCH] [9610] drivers: mfd: created mfd driver for S2MPU09 Change-Id: Ia4ecc49230a49f9b6af59a3520ac191f2e546106 Signed-off-by: Jieun Yi --- drivers/mfd/Kconfig | 11 + drivers/mfd/Makefile | 1 + drivers/mfd/s2mpu09-core.c | 592 ++++++++++++++++++ drivers/mfd/s2mpu09-irq.c | 311 +++++++++ include/linux/mfd/samsung/s2mpu09-regulator.h | 369 +++++++++++ include/linux/mfd/samsung/s2mpu09.h | 119 ++++ 6 files changed, 1403 insertions(+) create mode 100644 drivers/mfd/s2mpu09-core.c create mode 100644 drivers/mfd/s2mpu09-irq.c create mode 100644 include/linux/mfd/samsung/s2mpu09-regulator.h create mode 100644 include/linux/mfd/samsung/s2mpu09.h diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 7ed65424eb5e..c7215b4df5c3 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1012,6 +1012,17 @@ config MFD_S2MU004 additional drivers must be enabled in order to use the functionality of the device +config MFD_S2MPU09 + bool "SAMSUNG Electronics S2MPU09 PMIC Series Support" + depends on I2C=y + select MFD_CORE + select REGULATOR + help + Support for the Samsung Electronics MFD series. + This driver provides common support for accessing the device, + additional drivers must be enabled in order to use the functionality + of the device + config MFD_SI476X_CORE tristate "Silicon Laboratories 4761/64/68 AM/FM radio." depends on I2C diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 052f7b821ab7..0f1e9fdfb9c8 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -201,6 +201,7 @@ obj-$(CONFIG_MFD_RK808) += rk808.o obj-$(CONFIG_MFD_RN5T618) += rn5t618.o obj-$(CONFIG_MFD_SEC_CORE) += sec-core.o sec-irq.o obj-$(CONFIG_MFD_S2MU004) += s2mu004_core.o s2mu004_irq.o +obj-$(CONFIG_MFD_S2MPU09) += s2mpu09-core.o s2mpu09-irq.o obj-$(CONFIG_MFD_SYSCON) += syscon.o obj-$(CONFIG_MFD_LM3533) += lm3533-core.o lm3533-ctrlbank.o obj-$(CONFIG_MFD_VEXPRESS_SYSREG) += vexpress-sysreg.o diff --git a/drivers/mfd/s2mpu09-core.c b/drivers/mfd/s2mpu09-core.c new file mode 100644 index 000000000000..a655cd094f66 --- /dev/null +++ b/drivers/mfd/s2mpu09-core.c @@ -0,0 +1,592 @@ + +/* + * s2mpu09-core.c - mfd core driver for the s2mpu09 + * + * Copyright (C) 2016 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_OF +#include +#include +#endif /* CONFIG_OF */ + +#define I2C_ADDR_TOP 0x00 +#define I2C_ADDR_PMIC 0x01 +#define I2C_ADDR_RTC 0x02 + +struct device_node *acpm_mfd_node; + +static struct mfd_cell s2mpu09_devs[] = { + { .name = "s2mpu09-regulator", }, + { .name = "s2mpu09-rtc", }, +}; + +#if defined(CONFIG_EXYNOS_ACPM) +int s2mpu09_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest) +{ + int ret; + + ret = exynos_acpm_read_reg(i2c->addr, reg, dest); + if (ret) { + pr_err("[%s] acpm ipc fail!\n", __func__); + return ret; + } + return 0; +} +EXPORT_SYMBOL_GPL(s2mpu09_read_reg); + +int s2mpu09_bulk_read(struct i2c_client *i2c, u8 reg, int count, u8 *buf) +{ + int ret; + + ret = exynos_acpm_bulk_read(i2c->addr, reg, count, buf); + if (ret) { + pr_err("[%s] acpm ipc fail!\n", __func__); + return ret; + } + return 0; +} +EXPORT_SYMBOL_GPL(s2mpu09_bulk_read); + +int s2mpu09_write_reg(struct i2c_client *i2c, u8 reg, u8 value) +{ + int ret; + + ret = exynos_acpm_write_reg(i2c->addr, reg, value); + if (ret) { + pr_err("[%s] acpm ipc fail!\n", __func__); + return ret; + } + return ret; +} +EXPORT_SYMBOL_GPL(s2mpu09_write_reg); + +int s2mpu09_bulk_write(struct i2c_client *i2c, u8 reg, int count, u8 *buf) +{ + int ret; + + ret = exynos_acpm_bulk_write(i2c->addr, reg, count, buf); + if (ret) { + pr_err("[%s] acpm ipc fail!\n", __func__); + return ret; + } + return ret; +} +EXPORT_SYMBOL_GPL(s2mpu09_bulk_write); + +int s2mpu09_update_reg(struct i2c_client *i2c, u8 reg, u8 val, u8 mask) +{ + int ret; + + ret = exynos_acpm_update_reg(i2c->addr, reg, val, mask); + if (ret) { + pr_err("[%s] acpm ipc fail!\n", __func__); + return ret; + } + return ret; +} +EXPORT_SYMBOL_GPL(s2mpu09_update_reg); +#else +int s2mpu09_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest) +{ + struct s2mpu09_dev *s2mpu09 = i2c_get_clientdata(i2c); + int ret; + + mutex_lock(&s2mpu09->i2c_lock); + ret = i2c_smbus_read_byte_data(i2c, reg); + mutex_unlock(&s2mpu09->i2c_lock); + if (ret < 0) { + pr_info("%s:%s reg(0x%x), ret(%d)\n", + MFD_DEV_NAME, __func__, reg, ret); + return ret; + } + + ret &= 0xff; + *dest = ret; + return 0; +} +EXPORT_SYMBOL_GPL(s2mpu09_read_reg); + +int s2mpu09_bulk_read(struct i2c_client *i2c, u8 reg, int count, u8 *buf) +{ + struct s2mpu09_dev *s2mpu09 = i2c_get_clientdata(i2c); + int ret; + + mutex_lock(&s2mpu09->i2c_lock); + ret = i2c_smbus_read_i2c_block_data(i2c, reg, count, buf); + mutex_unlock(&s2mpu09->i2c_lock); + if (ret < 0) + return ret; + + return 0; +} +EXPORT_SYMBOL_GPL(s2mpu09_bulk_read); + +int s2mpu09_read_word(struct i2c_client *i2c, u8 reg) +{ + struct s2mpu09_dev *s2mpu09 = i2c_get_clientdata(i2c); + int ret; + + mutex_lock(&s2mpu09->i2c_lock); + ret = i2c_smbus_read_word_data(i2c, reg); + mutex_unlock(&s2mpu09->i2c_lock); + if (ret < 0) + return ret; + + return ret; +} +EXPORT_SYMBOL_GPL(s2mpu09_read_word); + +int s2mpu09_write_reg(struct i2c_client *i2c, u8 reg, u8 value) +{ + struct s2mpu09_dev *s2mpu09 = i2c_get_clientdata(i2c); + int ret; + + mutex_lock(&s2mpu09->i2c_lock); + ret = i2c_smbus_write_byte_data(i2c, reg, value); + mutex_unlock(&s2mpu09->i2c_lock); + if (ret < 0) + pr_info("%s:%s reg(0x%x), ret(%d)\n", + MFD_DEV_NAME, __func__, reg, ret); + + return ret; +} +EXPORT_SYMBOL_GPL(s2mpu09_write_reg); + +int s2mpu09_bulk_write(struct i2c_client *i2c, u8 reg, int count, u8 *buf) +{ + struct s2mpu09_dev *s2mpu09 = i2c_get_clientdata(i2c); + int ret; + + mutex_lock(&s2mpu09->i2c_lock); + ret = i2c_smbus_write_i2c_block_data(i2c, reg, count, buf); + mutex_unlock(&s2mpu09->i2c_lock); + if (ret < 0) + return ret; + + return 0; +} +EXPORT_SYMBOL_GPL(s2mpu09_bulk_write); + +int s2mpu09_write_word(struct i2c_client *i2c, u8 reg, u16 value) +{ + struct s2mpu09_dev *s2mpu09 = i2c_get_clientdata(i2c); + int ret; + + mutex_lock(&s2mpu09->i2c_lock); + ret = i2c_smbus_write_word_data(i2c, reg, value); + mutex_unlock(&s2mpu09->i2c_lock); + if (ret < 0) + return ret; + return 0; +} +EXPORT_SYMBOL_GPL(s2mpu09_write_word); + +int s2mpu09_update_reg(struct i2c_client *i2c, u8 reg, u8 val, u8 mask) +{ + struct s2mpu09_dev *s2mpu09 = i2c_get_clientdata(i2c); + int ret; + u8 old_val, new_val; + + mutex_lock(&s2mpu09->i2c_lock); + ret = i2c_smbus_read_byte_data(i2c, reg); + if (ret >= 0) { + old_val = ret & 0xff; + new_val = (val & mask) | (old_val & (~mask)); + ret = i2c_smbus_write_byte_data(i2c, reg, new_val); + } + mutex_unlock(&s2mpu09->i2c_lock); + return ret; +} +EXPORT_SYMBOL_GPL(s2mpu09_update_reg); + +#endif + +#if defined(CONFIG_OF) +static int of_s2mpu09_dt(struct device *dev, + struct s2mpu09_platform_data *pdata, + struct s2mpu09_dev *s2mpu09) +{ + struct device_node *np = dev->of_node; + int ret, strlen; + const char *status; + u32 val; + + if (!np) + return -EINVAL; + + acpm_mfd_node = np; + + pdata->irq_gpio = of_get_named_gpio(np, "s2mpu09,irq-gpio", 0); + + status = of_get_property(np, "s2mpu09,wakeup", &strlen); + if (status == NULL) + return -EINVAL; + if (strlen > 0) { + if (!strcmp(status, "enabled") || !strcmp(status, "okay")) + pdata->wakeup = true; + else + pdata->wakeup = false; + } + + if (of_get_property(np, "i2c-speedy-address", NULL)) + pdata->use_i2c_speedy = true; + + pr_info("%s: irq-gpio: %u \n", __func__, pdata->irq_gpio); + + /* WTSR, SMPL */ + pdata->wtsr_smpl = devm_kzalloc(dev, sizeof(*pdata->wtsr_smpl), + GFP_KERNEL); + if (!pdata->wtsr_smpl) + return -ENOMEM; + + status = of_get_property(np, "wtsr_en", &strlen); + if (status == NULL) + return -EINVAL; + if (strlen > 0) { + if (!strcmp(status, "enabled") || !strcmp(status, "okay")) + pdata->wtsr_smpl->wtsr_en = true; + else + pdata->wtsr_smpl->wtsr_en = false; + } + + status = of_get_property(np, "smpl_en", &strlen); + if (status == NULL) + return -EINVAL; + if (strlen > 0) { + if (!strcmp(status, "enabled") || !strcmp(status, "okay")) + pdata->wtsr_smpl->smpl_en = true; + else + pdata->wtsr_smpl->smpl_en = false; + } + + ret = of_property_read_u32(np, "wtsr_timer_val", + &pdata->wtsr_smpl->wtsr_timer_val); + if (ret) + return -EINVAL; + + ret = of_property_read_u32(np, "smpl_timer_val", + &pdata->wtsr_smpl->smpl_timer_val); + if (ret) + return -EINVAL; + + ret = of_property_read_u32(np, "check_jigon", &val); + if (ret) + return -EINVAL; + pdata->wtsr_smpl->check_jigon = !!val; + + /* init time */ + pdata->init_time = devm_kzalloc(dev, sizeof(*pdata->init_time), + GFP_KERNEL); + if (!pdata->init_time) + return -ENOMEM; + + ret = of_property_read_u32(np, "init_time,sec", + &pdata->init_time->tm_sec); + if (ret) + return -EINVAL; + + ret = of_property_read_u32(np, "init_time,min", + &pdata->init_time->tm_min); + if (ret) + return -EINVAL; + + ret = of_property_read_u32(np, "init_time,hour", + &pdata->init_time->tm_hour); + if (ret) + return -EINVAL; + + ret = of_property_read_u32(np, "init_time,mday", + &pdata->init_time->tm_mday); + if (ret) + return -EINVAL; + + ret = of_property_read_u32(np, "init_time,mon", + &pdata->init_time->tm_mon); + if (ret) + return -EINVAL; + + ret = of_property_read_u32(np, "init_time,year", + &pdata->init_time->tm_year); + if (ret) + return -EINVAL; + + ret = of_property_read_u32(np, "init_time,wday", + &pdata->init_time->tm_wday); + if (ret) + return -EINVAL; + + /* rtc optimize */ + ret = of_property_read_u32(np, "osc-bias-up", &val); + if (!ret) + pdata->osc_bias_up = val; + else + pdata->osc_bias_up = -1; + + ret = of_property_read_u32(np, "rtc_cap_sel", &val); + if (!ret) + pdata->cap_sel = val; + else + pdata->cap_sel = -1; + + ret = of_property_read_u32(np, "rtc_osc_xin", &val); + if (!ret) + pdata->osc_xin = val; + else + pdata->osc_xin = -1; + + ret = of_property_read_u32(np, "rtc_osc_xout", &val); + if (!ret) + pdata->osc_xout = val; + else + pdata->osc_xout = -1; + + return 0; +} +#else +static int of_s2mpu09_dt(struct device *dev, + struct s2mpu09_platform_data *pdata) +{ + return 0; +} +#endif /* CONFIG_OF */ + +static int s2mpu09_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *dev_id) +{ + struct s2mpu09_dev *s2mpu09; + struct s2mpu09_platform_data *pdata = i2c->dev.platform_data; + + u8 reg_data; + int ret = 0; + + pr_info("%s:%s\n", MFD_DEV_NAME, __func__); + + s2mpu09 = kzalloc(sizeof(struct s2mpu09_dev), GFP_KERNEL); + if (!s2mpu09) { + dev_err(&i2c->dev, "%s: Failed to alloc mem for s2mpu09\n", + __func__); + return -ENOMEM; + } + + if (i2c->dev.of_node) { + pdata = devm_kzalloc(&i2c->dev, + sizeof(struct s2mpu09_platform_data), GFP_KERNEL); + if (!pdata) { + dev_err(&i2c->dev, "Failed to allocate memory\n"); + ret = -ENOMEM; + goto err; + } + + ret = of_s2mpu09_dt(&i2c->dev, pdata, s2mpu09); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to get device of_node\n"); + goto err; + } + + i2c->dev.platform_data = pdata; + } else + pdata = i2c->dev.platform_data; + + + s2mpu09->dev = &i2c->dev; + s2mpu09->i2c = i2c; + s2mpu09->irq = i2c->irq; + s2mpu09->device_type = S2MPU09X; + + if (pdata) { + s2mpu09->pdata = pdata; + + pdata->irq_base = irq_alloc_descs(-1, 0, S2MPU09_IRQ_NR, -1); + if (pdata->irq_base < 0) { + pr_err("%s:%s irq_alloc_descs Fail! ret(%d)\n", + MFD_DEV_NAME, __func__, pdata->irq_base); + ret = -EINVAL; + goto err; + } else + s2mpu09->irq_base = pdata->irq_base; + + s2mpu09->irq_gpio = pdata->irq_gpio; + s2mpu09->wakeup = pdata->wakeup; + } else { + ret = -EINVAL; + goto err; + } + mutex_init(&s2mpu09->i2c_lock); + + i2c_set_clientdata(i2c, s2mpu09); + + /* TODO */ + if (s2mpu09_read_reg(i2c, S2MPU09_PMIC_REG_PMICID, ®_data) < 0) { + dev_err(s2mpu09->dev, + "device not found on this channel (this is not an error)\n"); + ret = -ENODEV; + goto err_w_lock; + } else { + /* print rev */ + s2mpu09->pmic_rev = reg_data; + } + + s2mpu09->pmic = i2c_new_dummy(i2c->adapter, I2C_ADDR_PMIC); + s2mpu09->rtc = i2c_new_dummy(i2c->adapter, I2C_ADDR_RTC); + + if (pdata->use_i2c_speedy) { + dev_err(s2mpu09->dev, "use_i2c_speedy was true\n"); + s2mpu09->pmic->flags |= I2C_CLIENT_SPEEDY; + s2mpu09->rtc->flags |= I2C_CLIENT_SPEEDY; + } + + i2c_set_clientdata(s2mpu09->pmic, s2mpu09); + i2c_set_clientdata(s2mpu09->rtc, s2mpu09); + + pr_info("%s device found: rev.0x%2x\n", __func__, s2mpu09->pmic_rev); + + ret = s2mpu09_irq_init(s2mpu09); + if (ret < 0) + goto err_irq_init; + + ret = mfd_add_devices(s2mpu09->dev, -1, s2mpu09_devs, + ARRAY_SIZE(s2mpu09_devs), NULL, 0, NULL); + if (ret < 0) + goto err_mfd; + + device_init_wakeup(s2mpu09->dev, pdata->wakeup); + + return ret; + +err_mfd: + mfd_remove_devices(s2mpu09->dev); +err_irq_init: + i2c_unregister_device(s2mpu09->i2c); +err_w_lock: + mutex_destroy(&s2mpu09->i2c_lock); +err: + kfree(s2mpu09); + return ret; +} + +static int s2mpu09_i2c_remove(struct i2c_client *i2c) +{ + struct s2mpu09_dev *s2mpu09 = i2c_get_clientdata(i2c); + + mfd_remove_devices(s2mpu09->dev); + i2c_unregister_device(s2mpu09->i2c); + kfree(s2mpu09); + + return 0; +} + +static const struct i2c_device_id s2mpu09_i2c_id[] = { + { MFD_DEV_NAME, TYPE_S2MPU09 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, s2mpu09_i2c_id); + +#if defined(CONFIG_OF) +static struct of_device_id s2mpu09_i2c_dt_ids[] = { + { .compatible = "samsung,s2mpu09mfd" }, + { }, +}; +#endif /* CONFIG_OF */ + +#if defined(CONFIG_PM) +static int s2mpu09_suspend(struct device *dev) +{ + struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); + struct s2mpu09_dev *s2mpu09 = i2c_get_clientdata(i2c); + + if (device_may_wakeup(dev)) + enable_irq_wake(s2mpu09->irq); + + disable_irq(s2mpu09->irq); + + return 0; +} + +static int s2mpu09_resume(struct device *dev) +{ + struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); + struct s2mpu09_dev *s2mpu09 = i2c_get_clientdata(i2c); + +#if !defined(CONFIG_SAMSUNG_PRODUCT_SHIP) + pr_info("%s:%s\n", MFD_DEV_NAME, __func__); +#endif /* CONFIG_SAMSUNG_PRODUCT_SHIP */ + + if (device_may_wakeup(dev)) + disable_irq_wake(s2mpu09->irq); + + enable_irq(s2mpu09->irq); + + return 0; +} +#else +#define s2mpu09_suspend NULL +#define s2mpu09_resume NULL +#endif /* CONFIG_PM */ + +const struct dev_pm_ops s2mpu09_pm = { + .suspend_late = s2mpu09_suspend, + .resume_early = s2mpu09_resume, +}; + +static struct i2c_driver s2mpu09_i2c_driver = { + .driver = { + .name = MFD_DEV_NAME, + .owner = THIS_MODULE, +#if defined(CONFIG_PM) + .pm = &s2mpu09_pm, +#endif /* CONFIG_PM */ +#if defined(CONFIG_OF) + .of_match_table = s2mpu09_i2c_dt_ids, +#endif /* CONFIG_OF */ + }, + .probe = s2mpu09_i2c_probe, + .remove = s2mpu09_i2c_remove, + .id_table = s2mpu09_i2c_id, +}; + +static int __init s2mpu09_i2c_init(void) +{ + pr_info("%s:%s\n", MFD_DEV_NAME, __func__); + return i2c_add_driver(&s2mpu09_i2c_driver); +} +/* init early so consumer devices can complete system boot */ +subsys_initcall(s2mpu09_i2c_init); + +static void __exit s2mpu09_i2c_exit(void) +{ + i2c_del_driver(&s2mpu09_i2c_driver); +} +module_exit(s2mpu09_i2c_exit); + +MODULE_DESCRIPTION("s2mpu09 multi-function core driver"); +MODULE_AUTHOR("Samsung Electronics"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/s2mpu09-irq.c b/drivers/mfd/s2mpu09-irq.c new file mode 100644 index 000000000000..ebf18d094cf0 --- /dev/null +++ b/drivers/mfd/s2mpu09-irq.c @@ -0,0 +1,311 @@ +/* + * s2mpu09-irq.c - Interrupt controller support for S2MPU09 + * + * Copyright (C) 2016 Samsung Electronics Co.Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * +*/ + +#include +#include +#include +#include +#include +#include + +static const u8 s2mpu09_mask_reg[] = { + /* TODO: Need to check other INTMASK */ + [PMIC_INT1] = S2MPU09_PMIC_REG_INT1M, + [PMIC_INT2] = S2MPU09_PMIC_REG_INT2M, + [PMIC_INT3] = S2MPU09_PMIC_REG_INT3M, + [PMIC_INT4] = S2MPU09_PMIC_REG_INT4M, + [PMIC_INT5] = S2MPU09_PMIC_REG_INT5M, +}; + +static struct i2c_client *get_i2c(struct s2mpu09_dev *s2mpu09, + enum s2mpu09_irq_source src) +{ + switch (src) { + case PMIC_INT1 ... PMIC_INT5: + return s2mpu09->pmic; + default: + return ERR_PTR(-EINVAL); + } +} + +struct s2mpu09_irq_data { + int mask; + enum s2mpu09_irq_source group; +}; + +#define DECLARE_IRQ(idx, _group, _mask) \ + [(idx)] = { .group = (_group), .mask = (_mask) } +static const struct s2mpu09_irq_data s2mpu09_irqs[] = { + DECLARE_IRQ(S2MPU09_PMIC_IRQ_PWRONR_INT1, PMIC_INT1, 1 << 1), + DECLARE_IRQ(S2MPU09_PMIC_IRQ_PWRONF_INT1, PMIC_INT1, 1 << 0), + DECLARE_IRQ(S2MPU09_PMIC_IRQ_JIGONBF_INT1, PMIC_INT1, 1 << 2), + DECLARE_IRQ(S2MPU09_PMIC_IRQ_JIGONBR_INT1, PMIC_INT1, 1 << 3), + DECLARE_IRQ(S2MPU09_PMIC_IRQ_ACOKF_INT1, PMIC_INT1, 1 << 4), + DECLARE_IRQ(S2MPU09_PMIC_IRQ_ACOKR_INT1, PMIC_INT1, 1 << 5), + DECLARE_IRQ(S2MPU09_PMIC_IRQ_PWRON1S_INT1, PMIC_INT1, 1 << 6), + DECLARE_IRQ(S2MPU09_PMIC_IRQ_MRB_INT1, PMIC_INT1, 1 << 7), + + DECLARE_IRQ(S2MPU09_PMIC_IRQ_RTC60S_INT2, PMIC_INT2, 1 << 0), + DECLARE_IRQ(S2MPU09_PMIC_IRQ_RTCA1_INT2, PMIC_INT2, 1 << 1), + DECLARE_IRQ(S2MPU09_PMIC_IRQ_RTCA0_INT2, PMIC_INT2, 1 << 2), + DECLARE_IRQ(S2MPU09_PMIC_IRQ_SMPL_INT2, PMIC_INT2, 1 << 3), + DECLARE_IRQ(S2MPU09_PMIC_IRQ_RTC1S_INT2, PMIC_INT2, 1 << 4), + DECLARE_IRQ(S2MPU09_PMIC_IRQ_WTSR_INT2, PMIC_INT2, 1 << 5), + DECLARE_IRQ(S2MPU09_PMIC_IRQ_WTSRB_INT2, PMIC_INT2, 1 << 7), + + DECLARE_IRQ(S2MPU09_PMIC_IRQ_120C_INT3, PMIC_INT3, 1 << 0), + DECLARE_IRQ(S2MPU09_PMIC_IRQ_140C_INT3, PMIC_INT3, 1 << 1), + DECLARE_IRQ(S2MPU09_PMIC_IRQ_TSD_INT3, PMIC_INT3, 1 << 2), + DECLARE_IRQ(S2MPU09_PMIC_IRQ_OCPB1_INT3, PMIC_INT3, 1 << 6), + DECLARE_IRQ(S2MPU09_PMIC_IRQ_OCPB2_INT3, PMIC_INT3, 1 << 7), + + DECLARE_IRQ(S2MPU09_PMIC_IRQ_OCPB3_INT4, PMIC_INT4, 1 << 0), + DECLARE_IRQ(S2MPU09_PMIC_IRQ_OCPB4_INT4, PMIC_INT4, 1 << 1), + DECLARE_IRQ(S2MPU09_PMIC_IRQ_OCPB5_INT4, PMIC_INT4, 1 << 2), + DECLARE_IRQ(S2MPU09_PMIC_IRQ_OCPB6_INT4, PMIC_INT4, 1 << 3), + DECLARE_IRQ(S2MPU09_PMIC_IRQ_OCPB7_INT4, PMIC_INT4, 1 << 4), + DECLARE_IRQ(S2MPU09_PMIC_IRQ_OCPB8_INT4, PMIC_INT4, 1 << 5), + DECLARE_IRQ(S2MPU09_PMIC_IRQ_OCPB9_INT4, PMIC_INT4, 1 << 6), + DECLARE_IRQ(S2MPU09_PMIC_IRQ_OCPB10_INT4, PMIC_INT4, 1 << 7), + + DECLARE_IRQ(S2MPU09_PMIC_IRQ_SCLDO2_INT5, PMIC_INT5, 1 << 0), + DECLARE_IRQ(S2MPU09_PMIC_IRQ_SCLDO18_INT5, PMIC_INT5, 1 << 1), + DECLARE_IRQ(S2MPU09_PMIC_IRQ_SCLDO19_INT5, PMIC_INT5, 1 << 2), + DECLARE_IRQ(S2MPU09_PMIC_IRQ_SCLDO33_INT5, PMIC_INT5, 1 << 3), + DECLARE_IRQ(S2MPU09_PMIC_IRQ_SCLDO34_INT5, PMIC_INT5, 1 << 4), + DECLARE_IRQ(S2MPU09_PMIC_IRQ_SCLDO35_INT5, PMIC_INT5, 1 << 5), +}; + +static void s2mpu09_irq_lock(struct irq_data *data) +{ + struct s2mpu09_dev *s2mpu09 = irq_get_chip_data(data->irq); + + mutex_lock(&s2mpu09->irqlock); +} + +static void s2mpu09_irq_sync_unlock(struct irq_data *data) +{ + struct s2mpu09_dev *s2mpu09 = irq_get_chip_data(data->irq); + int i; + + for (i = 0; i < S2MPU09_IRQ_GROUP_NR; i++) { + u8 mask_reg = s2mpu09_mask_reg[i]; + struct i2c_client *i2c = get_i2c(s2mpu09, i); + + if (mask_reg == S2MPU09_REG_INVALID || + IS_ERR_OR_NULL(i2c)) + continue; + s2mpu09->irq_masks_cache[i] = s2mpu09->irq_masks_cur[i]; + + s2mpu09_write_reg(i2c, s2mpu09_mask_reg[i], + s2mpu09->irq_masks_cur[i]); + } + + mutex_unlock(&s2mpu09->irqlock); +} + +static const inline struct s2mpu09_irq_data * +irq_to_s2mpu09_irq(struct s2mpu09_dev *s2mpu09, int irq) +{ + return &s2mpu09_irqs[irq - s2mpu09->irq_base]; +} + +static void s2mpu09_irq_mask(struct irq_data *data) +{ + struct s2mpu09_dev *s2mpu09 = irq_get_chip_data(data->irq); + const struct s2mpu09_irq_data *irq_data = + irq_to_s2mpu09_irq(s2mpu09, data->irq); + + if (irq_data->group >= S2MPU09_IRQ_GROUP_NR) + return; + + s2mpu09->irq_masks_cur[irq_data->group] |= irq_data->mask; +} + +static void s2mpu09_irq_unmask(struct irq_data *data) +{ + struct s2mpu09_dev *s2mpu09 = irq_get_chip_data(data->irq); + const struct s2mpu09_irq_data *irq_data = + irq_to_s2mpu09_irq(s2mpu09, data->irq); + + if (irq_data->group >= S2MPU09_IRQ_GROUP_NR) + return; + + s2mpu09->irq_masks_cur[irq_data->group] &= ~irq_data->mask; +} + +static struct irq_chip s2mpu09_irq_chip = { + .name = MFD_DEV_NAME, + .irq_bus_lock = s2mpu09_irq_lock, + .irq_bus_sync_unlock = s2mpu09_irq_sync_unlock, + .irq_mask = s2mpu09_irq_mask, + .irq_unmask = s2mpu09_irq_unmask, +}; + +static irqreturn_t s2mpu09_irq_thread(int irq, void *data) +{ + struct s2mpu09_dev *s2mpu09 = data; + u8 irq_reg[S2MPU09_IRQ_GROUP_NR] = {0}; + u8 irq_src; + int i, ret; + + //pr_debug("%s: irq gpio pre-state(0x%02x)\n", __func__, + pr_err("%s: irq gpio pre-state(0x%02x)\n", __func__, + gpio_get_value(s2mpu09->irq_gpio)); + + ret = s2mpu09_read_reg(s2mpu09->i2c, + S2MPU09_PMIC_REG_INTSRC, &irq_src); + pr_err("%s: interrupt source(0x%02x)\n", __func__, irq_src); + if (ret) { + pr_err("%s:%s Failed to read interrupt source: %d\n", + MFD_DEV_NAME, __func__, ret); + return IRQ_NONE; + } + + pr_info("%s: interrupt source(0x%02x)\n", __func__, irq_src); + + if (irq_src & S2MPU09_IRQSRC_PMIC) { + /* PMIC_INT */ + ret = s2mpu09_bulk_read(s2mpu09->pmic, S2MPU09_PMIC_REG_INT1, + S2MPU09_NUM_IRQ_PMIC_REGS, &irq_reg[PMIC_INT1]); + if (ret) { + pr_err("%s:%s Failed to read pmic interrupt: %d\n", + MFD_DEV_NAME, __func__, ret); + return IRQ_NONE; + } + + pr_info("%s: pmic interrupt(0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x)\n", + __func__, irq_reg[PMIC_INT1], irq_reg[PMIC_INT2], irq_reg[PMIC_INT3], + irq_reg[PMIC_INT4], irq_reg[PMIC_INT5]); + } + + /* Apply masking */ + for (i = 0; i < S2MPU09_IRQ_GROUP_NR; i++) + irq_reg[i] &= ~s2mpu09->irq_masks_cur[i]; + + /* Report */ + for (i = 0; i < S2MPU09_IRQ_NR; i++) { + if (irq_reg[s2mpu09_irqs[i].group] & s2mpu09_irqs[i].mask) + handle_nested_irq(s2mpu09->irq_base + i); + } + + return IRQ_HANDLED; +} + +int s2mpu09_irq_init(struct s2mpu09_dev *s2mpu09) +{ + int i; + int ret; + u8 i2c_data; + int cur_irq; + + if (!s2mpu09->irq_gpio) { + dev_warn(s2mpu09->dev, "No interrupt specified.\n"); + s2mpu09->irq_base = 0; + return 0; + } + + if (!s2mpu09->irq_base) { + dev_err(s2mpu09->dev, "No interrupt base specified.\n"); + return 0; + } + + mutex_init(&s2mpu09->irqlock); + + s2mpu09->irq = gpio_to_irq(s2mpu09->irq_gpio); + pr_info("%s:%s irq=%d, irq->gpio=%d\n", MFD_DEV_NAME, __func__, + s2mpu09->irq, s2mpu09->irq_gpio); + + ret = gpio_request(s2mpu09->irq_gpio, "pmic_irq"); + if (ret) { + dev_err(s2mpu09->dev, "%s: failed requesting gpio %d\n", + __func__, s2mpu09->irq_gpio); + return ret; + } + gpio_direction_input(s2mpu09->irq_gpio); + gpio_free(s2mpu09->irq_gpio); + + /* Mask individual interrupt sources */ + for (i = 0; i < S2MPU09_IRQ_GROUP_NR; i++) { + struct i2c_client *i2c; + + s2mpu09->irq_masks_cur[i] = 0xff; + s2mpu09->irq_masks_cache[i] = 0xff; + + i2c = get_i2c(s2mpu09, i); + + if (IS_ERR_OR_NULL(i2c)) + continue; + if (s2mpu09_mask_reg[i] == S2MPU09_REG_INVALID) + continue; + + s2mpu09_write_reg(i2c, s2mpu09_mask_reg[i], 0xff); + } + + /* Register with genirq */ + for (i = 0; i < S2MPU09_IRQ_NR; i++) { + cur_irq = i + s2mpu09->irq_base; + irq_set_chip_data(cur_irq, s2mpu09); + irq_set_chip_and_handler(cur_irq, &s2mpu09_irq_chip, + handle_level_irq); + irq_set_nested_thread(cur_irq, 1); +#ifdef CONFIG_ARM + set_irq_flags(cur_irq, IRQF_VALID); +#else + irq_set_noprobe(cur_irq); +#endif + } + + s2mpu09_write_reg(s2mpu09->i2c, S2MPU09_PMIC_REG_INTSRC_MASK, 0xff); + /* Unmask s2mpu09 interrupt */ + ret = s2mpu09_read_reg(s2mpu09->i2c, S2MPU09_PMIC_REG_INTSRC_MASK, + &i2c_data); + if (ret) { + pr_err("%s:%s fail to read intsrc mask reg\n", + MFD_DEV_NAME, __func__); + return ret; + } + + i2c_data &= ~(S2MPU09_IRQSRC_PMIC); /* Unmask pmic interrupt */ + s2mpu09_write_reg(s2mpu09->i2c, S2MPU09_PMIC_REG_INTSRC_MASK, + i2c_data); + + pr_info("%s:%s s2mpu09_PMIC_REG_INTSRC_MASK=0x%02x\n", + MFD_DEV_NAME, __func__, i2c_data); + + ret = request_threaded_irq(s2mpu09->irq, NULL, s2mpu09_irq_thread, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "s2mpu09-irq", s2mpu09); + + if (ret) { + dev_err(s2mpu09->dev, "Failed to request IRQ %d: %d\n", + s2mpu09->irq, ret); + return ret; + } + + return 0; +} + +void s2mpu09_irq_exit(struct s2mpu09_dev *s2mpu09) +{ + if (s2mpu09->irq) + free_irq(s2mpu09->irq, s2mpu09); +} diff --git a/include/linux/mfd/samsung/s2mpu09-regulator.h b/include/linux/mfd/samsung/s2mpu09-regulator.h new file mode 100644 index 000000000000..da0fd9e83864 --- /dev/null +++ b/include/linux/mfd/samsung/s2mpu09-regulator.h @@ -0,0 +1,369 @@ +/* + * s2mpu09-regulator.h - Voltage regulator driver for the s2mpu09 + * + * Copyright (C) 2016 Samsung Electrnoics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __LINUX_MFD_S2MPU09_PRIV_H +#define __LINUX_MFD_S2MPU09_PRIV_H + +#include +#define S2MPU09_REG_INVALID (0xff) +#define S2MPU09_IRQSRC_PMIC (1 << 0) + +/* PMIC Top-Level Registers */ +#define S2MPU09_PMIC_REG_PMICID 0x00 +#define S2MPU09_PMIC_REG_INTSRC 0x01 +#define S2MPU09_PMIC_REG_INTSRC_MASK 0x02 + +/* Slave addr = 0xCC */ +/* PMIC Registers */ +#define S2MPU09_PMIC_REG_INT1 0x00 +#define S2MPU09_PMIC_REG_INT2 0x01 +#define S2MPU09_PMIC_REG_INT3 0x02 +#define S2MPU09_PMIC_REG_INT4 0x03 +#define S2MPU09_PMIC_REG_INT5 0x04 +#define S2MPU09_PMIC_REG_INT1M 0x05 +#define S2MPU09_PMIC_REG_INT2M 0x06 +#define S2MPU09_PMIC_REG_INT3M 0x07 +#define S2MPU09_PMIC_REG_INT4M 0x08 +#define S2MPU09_PMIC_REG_INT5M 0x09 +#define S2MPU09_PMIC_REG_STATUS1 0x0A +#define S2MPU09_PMIC_REG_STATUS2 0x0B +#define S2MPU09_PMIC_REG_PWRONSRC 0x0C +#define S2MPU09_PMIC_REG_OFFSRC 0x0D + +#define S2MPU09_PMIC_REG_BUCHG 0x0E +#define S2MPU09_PMIC_REG_RTCBUF 0x0F +#define S2MPU09_PMIC_REG_CTRL1 0x10 +#define S2MPU09_PMIC_REG_CTRL2 0x11 +#define S2MPU09_PMIC_REG_CTRL3 0x12 +#define S2MPU09_PMIC_REG_ETCOTP 0x13 +#define S2MPU09_PMIC_REG_UVLOOTP 0x14 +#define S2MPU09_PMIC_REG_UVLOTRIM 0x15 +#define S2MPU09_PMIC_REG_CFG1 0x16 +#define S2MPU09_PMIC_REG_CFG2 0x17 + +#define S2MPU09_PMIC_REG_B1CTRL 0x18 +#define S2MPU09_PMIC_REG_B1OUT 0x19 +#define S2MPU09_PMIC_REG_B2CTRL 0x1A +#define S2MPU09_PMIC_REG_B2OUT 0x1B +#define S2MPU09_PMIC_REG_B3CTRL 0x1C +#define S2MPU09_PMIC_REG_B3OUT 0x1D +#define S2MPU09_PMIC_REG_B4CTRL 0x1E +#define S2MPU09_PMIC_REG_B4OUT 0x1F +#define S2MPU09_PMIC_REG_B5CTRL 0x20 +#define S2MPU09_PMIC_REG_B5OUT 0x21 +#define S2MPU09_PMIC_REG_B6CTRL 0x22 +#define S2MPU09_PMIC_REG_B6OUT 0x23 +#define S2MPU09_PMIC_REG_B7CTRL 0x24 +#define S2MPU09_PMIC_REG_B7OUT 0x25 +#define S2MPU09_PMIC_REG_B8CTRL 0x26 +#define S2MPU09_PMIC_REG_B8OUT1 0x27 +#define S2MPU09_PMIC_REG_B8OUT2 0x28 +#define S2MPU09_PMIC_REG_B9CTRL 0x29 +#define S2MPU09_PMIC_REG_B9OUT1 0x2A +#define S2MPU09_PMIC_REG_B9OUT2 0x2B +#define S2MPU09_PMIC_REG_B10CTRL 0x2C +#define S2MPU09_PMIC_REG_B10OUT 0x2D + +#define S2MPU09_PMIC_REG_BUCKRAMP1 0x2E +#define S2MPU09_PMIC_REG_BUCKRAMP2 0x2F +#define S2MPU09_PMIC_REG_LDORAMP1 0x30 +#define S2MPU09_PMIC_REG_OCPWARN1 0x31 +#define S2MPU09_PMIC_REG_BUCK2_PH_DVS 0x32 +#define S2MPU09_PMIC_REG_LDO8_DVS 0x33 +#define S2MPU09_PMIC_REG_LDO9_DVS 0x34 +#define S2MPU09_PMIC_REG_LDO10_DVS 0x35 +#define S2MPU09_PMIC_REG_LDO11_DVS 0x36 +#define S2MPU09_PMIC_REG_LDO36_DVS 0x37 +#define S2MPU09_PMIC_REG_LDO43_DVS 0x38 + +#define S2MPU09_PMIC_REG_L1CTRL 0x39 +#define S2MPU09_PMIC_REG_L2CTRL1 0x3A +#define S2MPU09_PMIC_REG_L2CTRL2 0x3B +#define S2MPU09_PMIC_REG_L3CTRL 0x3C +#define S2MPU09_PMIC_REG_L4CTRL 0x3D +#define S2MPU09_PMIC_REG_L5CTRL 0x3E +#define S2MPU09_PMIC_REG_L6CTRL 0x3F +#define S2MPU09_PMIC_REG_L7CTRL 0x40 +#define S2MPU09_PMIC_REG_L8CTRL 0x41 +#define S2MPU09_PMIC_REG_L9CTRL 0x42 +#define S2MPU09_PMIC_REG_L10CTRL 0x43 +#define S2MPU09_PMIC_REG_L11CTRL 0x44 +#define S2MPU09_PMIC_REG_L12CTRL 0x45 +#define S2MPU09_PMIC_REG_L13CTRL 0x46 +#define S2MPU09_PMIC_REG_L14CTRL 0x47 +#define S2MPU09_PMIC_REG_L15CTRL 0x48 +#define S2MPU09_PMIC_REG_L16CTRL 0x49 +#define S2MPU09_PMIC_REG_L17CTRL 0x4A +#define S2MPU09_PMIC_REG_L18CTRL 0x4B +#define S2MPU09_PMIC_REG_L19CTRL 0x4C +#define S2MPU09_PMIC_REG_L20CTRL 0x4D +#define S2MPU09_PMIC_REG_L21CTRL 0x4E +#define S2MPU09_PMIC_REG_L22CTRL 0x4F +#define S2MPU09_PMIC_REG_L23CTRL 0x50 +#define S2MPU09_PMIC_REG_L24CTRL 0x51 +#define S2MPU09_PMIC_REG_L25CTRL 0x52 +#define S2MPU09_PMIC_REG_L26CTRL 0x53 +#define S2MPU09_PMIC_REG_L27CTRL 0x54 +#define S2MPU09_PMIC_REG_L28CTRL 0x55 +#define S2MPU09_PMIC_REG_L29CTRL 0x56 +#define S2MPU09_PMIC_REG_L30CTRL 0x57 +#define S2MPU09_PMIC_REG_L31CTRL 0x58 +#define S2MPU09_PMIC_REG_L32CTRL 0x59 +#define S2MPU09_PMIC_REG_L33CTRL 0x5A +#define S2MPU09_PMIC_REG_L34CTRL 0x5B +#define S2MPU09_PMIC_REG_L35CTRL 0x5C +#define S2MPU09_PMIC_REG_L36CTRL 0x5D +#define S2MPU09_PMIC_REG_L37CTRL 0x5E +#define S2MPU09_PMIC_REG_L38CTRL 0x5F +#define S2MPU09_PMIC_REG_L39CTRL 0x60 +#define S2MPU09_PMIC_REG_L40CTRL 0x61 +#define S2MPU09_PMIC_REG_L41CTRL 0x62 +#define S2MPU09_PMIC_REG_L42CTRL 0x63 +#define S2MPU09_PMIC_REG_L43CTRL 0x64 +#define S2MPU09_PMIC_REG_L44CTRL 0x65 + +#define S2MPU09_PMIC_REG_LDO_DSCH1 0x66 +#define S2MPU09_PMIC_REG_LDO_DSCH2 0x67 +#define S2MPU09_PMIC_REG_LDO_DSCH3 0x68 +#define S2MPU09_PMIC_REG_LDO_DSCH4 0x69 +#define S2MPU09_PMIC_REG_LDO_DSCH5 0x6A +#define S2MPU09_PMIC_REG_LDO_DSCH6 0x6B +#define S2MPU09_PMIC_REG_LDO_SIM_RF 0x6C +#define S2MPU09_PMIC_REG_LDO_DRAM1 0x6D +#define S2MPU09_PMIC_REG_LDO_DRAM2 0x6E +#define S2MPU09_PMIC_REG_LDO_CTRL1 0x6F +#define S2MPU09_PMIC_REG_LDO_CTRL2 0x70 +#define S2MPU09_PMIC_REG_OFF_SEQ 0x75 +#define S2MPU09_PMIC_REG_TCXO_CTRL 0x76 + +#define S2MPU09_PMIC_REG_SELMIF0 0xA7 +#define S2MPU09_PMIC_REG_SELMIF1 0xA8 +#define S2MPU09_PMIC_REG_EXT_EN 0xAD +#define S2MPU09_PMIC_REG_EXT_CTRL 0xFF + +/* S2MPU09 regulator ids */ + +enum S2MPU09_regulators { + S2MPU09_LDO1, + S2MPU09_LDO2, + S2MPU09_LDO3, + S2MPU09_LDO4, + S2MPU09_LDO5, + S2MPU09_LDO6, + S2MPU09_LDO7, + S2MPU09_LDO8, + S2MPU09_LDO9, + S2MPU09_LDO10, + S2MPU09_LDO11, + S2MPU09_LDO12, + S2MPU09_LDO13, + S2MPU09_LDO14, +/* S2MPU09_LDO15, + S2MPU09_LDO16, + S2MPU09_LDO17, + S2MPU09_LDO18, + S2MPU09_LDO19, + S2MPU09_LDO20, + S2MPU09_LDO21, + S2MPU09_LDO22, + S2MPU09_LDO23, + S2MPU09_LDO24, + S2MPU09_LDO25, + S2MPU09_LDO24, + S2MPU09_LDO25, + S2MPU09_LDO26, + S2MPU09_LDO27, + S2MPU09_LDO28, + S2MPU09_LDO29, + S2MPU09_LDO30, + S2MPU09_LDO31, + S2MPU09_LDO32, +*/ S2MPU09_LDO33, + S2MPU09_LDO34, + S2MPU09_LDO35, + S2MPU09_LDO36, + S2MPU09_LDO37, + S2MPU09_LDO38, + S2MPU09_LDO39, + S2MPU09_LDO40, + S2MPU09_LDO41, + S2MPU09_LDO42, + S2MPU09_LDO43, + S2MPU09_LDO44, + S2MPU09_BUCK1, + S2MPU09_BUCK2, + S2MPU09_BUCK3, + S2MPU09_BUCK4, + S2MPU09_BUCK5, + S2MPU09_BUCK6, + S2MPU09_BUCK7, + S2MPU09_BUCK8, + S2MPU09_BUCK9, +// S2MPU09_BUCK10, + S2MPU09_REG_MAX, +}; + +#define S2MPU09_BUCK_MIN1 300000 +#define S2MPU09_BUCK_MIN2 600000 +#define S2MPU09_LDO_MIN1 300000 +#define S2MPU09_LDO_MIN2 400000 +#define S2MPU09_LDO_MIN3 700000 +#define S2MPU09_LDO_MIN4 1800000 +#define S2MPU09_BUCK_STEP1 6250 +#define S2MPU09_BUCK_STEP2 12500 +#define S2MPU09_LDO_STEP1 12500 +#define S2MPU09_LDO_STEP2 25000 +#define S2MPU09_LDO_VSEL_MASK 0x3F +#define S2MPU09_BUCK_VSEL_MASK 0xFF +#define S2MPU09_ENABLE_MASK (0x03 << S2MPU09_ENABLE_SHIFT) +#define S2MPU09_SW_ENABLE_MASK 0x03 +#define S2MPU09_RAMP_DELAY 12000 + +#define S2MPU09_ENABLE_TIME_LDO 128 +#define S2MPU09_ENABLE_TIME_BUCK1 110 +#define S2MPU09_ENABLE_TIME_BUCK2 110 +#define S2MPU09_ENABLE_TIME_BUCK3 110 +#define S2MPU09_ENABLE_TIME_BUCK4 150 +#define S2MPU09_ENABLE_TIME_BUCK5 150 +#define S2MPU09_ENABLE_TIME_BUCK6 150 +#define S2MPU09_ENABLE_TIME_BUCK7 150 +#define S2MPU09_ENABLE_TIME_BUCK8 150 +#define S2MPU09_ENABLE_TIME_BUCK9 150 +#define S2MPU09_ENABLE_TIME_BUCK10 150 + +#define S2MPU09_ENABLE_SHIFT 0x06 +#define S2MPU09_LDO_N_VOLTAGES (S2MPU09_LDO_VSEL_MASK + 1) +#define S2MPU09_BUCK_N_VOLTAGES (S2MPU09_BUCK_VSEL_MASK + 1) + +#define S2MPU09_PMIC_EN_SHIFT 6 +#define S2MPU09_REGULATOR_MAX (S2MPU09_REG_MAX) +#define SEC_PMIC_REV(iodev) (iodev)->pmic_rev + +/* + * sec_opmode_data - regulator operation mode data + * @id: regulator id + * @mode: regulator operation mode + */ + +enum s2mpu09_irq_source { + PMIC_INT1 = 0, + PMIC_INT2, + PMIC_INT3, + PMIC_INT4, + PMIC_INT5, + + S2MPU09_IRQ_GROUP_NR, +}; + +#define S2MPU09_NUM_IRQ_PMIC_REGS 5 + +enum s2mpu09_irq { + /* PMIC */ + S2MPU09_PMIC_IRQ_PWRONF_INT1, + S2MPU09_PMIC_IRQ_PWRONR_INT1, + S2MPU09_PMIC_IRQ_JIGONBF_INT1, + S2MPU09_PMIC_IRQ_JIGONBR_INT1, + S2MPU09_PMIC_IRQ_ACOKF_INT1, + S2MPU09_PMIC_IRQ_ACOKR_INT1, + S2MPU09_PMIC_IRQ_PWRON1S_INT1, + S2MPU09_PMIC_IRQ_MRB_INT1, + + S2MPU09_PMIC_IRQ_RTC60S_INT2, + S2MPU09_PMIC_IRQ_RTCA1_INT2, + S2MPU09_PMIC_IRQ_RTCA0_INT2, + S2MPU09_PMIC_IRQ_SMPL_INT2, + S2MPU09_PMIC_IRQ_RTC1S_INT2, + S2MPU09_PMIC_IRQ_WTSR_INT2, + S2MPU09_PMIC_IRQ_WTSRB_INT2, + + S2MPU09_PMIC_IRQ_120C_INT3, + S2MPU09_PMIC_IRQ_140C_INT3, + S2MPU09_PMIC_IRQ_TSD_INT3, + S2MPU09_PMIC_IRQ_OCPB1_INT3, + S2MPU09_PMIC_IRQ_OCPB2_INT3, + + S2MPU09_PMIC_IRQ_OCPB3_INT4, + S2MPU09_PMIC_IRQ_OCPB4_INT4, + S2MPU09_PMIC_IRQ_OCPB5_INT4, + S2MPU09_PMIC_IRQ_OCPB6_INT4, + S2MPU09_PMIC_IRQ_OCPB7_INT4, + S2MPU09_PMIC_IRQ_OCPB8_INT4, + S2MPU09_PMIC_IRQ_OCPB9_INT4, + S2MPU09_PMIC_IRQ_OCPB10_INT4, + + S2MPU09_PMIC_IRQ_SCLDO2_INT5, + S2MPU09_PMIC_IRQ_SCLDO18_INT5, + S2MPU09_PMIC_IRQ_SCLDO19_INT5, + S2MPU09_PMIC_IRQ_SCLDO33_INT5, + S2MPU09_PMIC_IRQ_SCLDO34_INT5, + S2MPU09_PMIC_IRQ_SCLDO35_INT5, + + S2MPU09_IRQ_NR, +}; + + +enum sec_device_type { + S2MPU09X, +}; + +struct s2mpu09_dev { + struct device *dev; + struct i2c_client *i2c; + struct i2c_client *pmic; + struct i2c_client *rtc; + struct mutex i2c_lock; + struct apm_ops *ops; + + int type; + int device_type; + int irq; + int irq_base; + int irq_gpio; + bool wakeup; + struct mutex irqlock; + int irq_masks_cur[S2MPU09_IRQ_GROUP_NR]; + int irq_masks_cache[S2MPU09_IRQ_GROUP_NR]; + + /* pmic REV register */ + u8 pmic_rev; /* pmic Rev */ + + struct s2mpu09_platform_data *pdata; +}; + +enum s2mpu09_types { + TYPE_S2MPU09, +}; + +extern int s2mpu09_irq_init(struct s2mpu09_dev *s2mpu09); +extern void s2mpu09_irq_exit(struct s2mpu09_dev *s2mpu09); + +/* S2MPU09 shared i2c API function */ +extern int s2mpu09_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest); +extern int s2mpu09_bulk_read(struct i2c_client *i2c, u8 reg, int count, + u8 *buf); +extern int s2mpu09_write_reg(struct i2c_client *i2c, u8 reg, u8 value); +extern int s2mpu09_bulk_write(struct i2c_client *i2c, u8 reg, int count, + u8 *buf); +extern int s2mpu09_write_word(struct i2c_client *i2c, u8 reg, u16 value); +extern int s2mpu09_read_word(struct i2c_client *i2c, u8 reg); + +extern int s2mpu09_update_reg(struct i2c_client *i2c, u8 reg, u8 val, u8 mask); + +#endif /* __LINUX_MFD_S2MPU09_PRIV_H */ diff --git a/include/linux/mfd/samsung/s2mpu09.h b/include/linux/mfd/samsung/s2mpu09.h new file mode 100644 index 000000000000..86b7ccdee418 --- /dev/null +++ b/include/linux/mfd/samsung/s2mpu09.h @@ -0,0 +1,119 @@ +/* + * s2mpu09.h - Driver for the s2mpu09 + * + * Copyright (C) 2016 Samsung Electrnoics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __S2MPU09_MFD_H__ +#define __S2MPU09_MFD_H__ +#include +#include + +#define MFD_DEV_NAME "s2mpu09" +/** + * sec_regulator_data - regulator data + * @id: regulator id + * @initdata: regulator init data (contraints, supplies, ...) + */ +struct s2mpu09_regulator_data { + int id; + struct regulator_init_data *initdata; + struct device_node *reg_node; +}; + +/* + * sec_opmode_data - regulator operation mode data + * @id: regulator id + * @mode: regulator operation mode + */ +struct sec_opmode_data { + int id; + unsigned int mode; +}; + +/* + * samsung regulator operation mode + * SEC_OPMODE_OFF Regulator always OFF + * SEC_OPMODE_ON Regulator always ON + * SEC_OPMODE_LOWPOWER Regulator is on in low-power mode + * SEC_OPMODE_SUSPEND Regulator is changed by PWREN pin + * If PWREN is high, regulator is on + * If PWREN is low, regulator is off + */ + +/* TODO */ +enum sec_opmode { + SEC_OPMODE_OFF, + SEC_OPMODE_SUSPEND, + SEC_OPMODE_LOWPOWER, + SEC_OPMODE_ON, + SEC_OPMODE_TCXO = 0x2, + SEC_OPMODE_MIF = 0x2, +}; + +/** + * struct sec_wtsr_smpl - settings for WTSR/SMPL + * @wtsr_en: WTSR Function Enable Control + * @smpl_en: SMPL Function Enable Control + * @wtsr_timer_val: Set the WTSR timer Threshold + * @smpl_timer_val: Set the SMPL timer Threshold + * @check_jigon: if this value is true, do not enable SMPL function when + * JIGONB is low(JIG cable is attached) + */ +struct sec_wtsr_smpl { + bool wtsr_en; + bool smpl_en; + int wtsr_timer_val; + int smpl_timer_val; + bool check_jigon; +}; + +struct s2mpu09_platform_data { + /* IRQ */ + int irq_base; + int irq_gpio; + bool wakeup; + + int num_regulators; + struct s2mpu09_regulator_data *regulators; + struct sec_opmode_data *opmode; + struct mfd_cell *sub_devices; + int num_subdevs; + + int (*cfg_pmic_irq)(void); + int device_type; + int ono; + int buck_ramp_delay; + + /* ---- RTC ---- */ + struct sec_wtsr_smpl *wtsr_smpl; +/* struct sec_rtc_time *init_time; */ + struct rtc_time *init_time; + int osc_bias_up; + int cap_sel; + int osc_xin; + int osc_xout; + + bool use_i2c_speedy; +}; + +struct s2mpu09 { + struct regmap *regmap; +}; + +#endif /* __S2MPU09_MFD_H__ */ -- 2.20.1