* [PATCH v6 1/4] mfd: bd71837: mfd driver for ROHM BD71837 PMIC
2018-06-15 13:27 [PATCH v6 0/4] mfd/regulator/clk/input: bd71837: ROHM BD71837 PMIC driver Matti Vaittinen
@ 2018-06-15 13:28 ` Matti Vaittinen
2018-06-26 7:54 ` Linus Walleij
2018-06-15 13:30 ` [PATCH v6 2/4] mfd: bd71837: Devicetree bindings " Matti Vaittinen
` (2 subsequent siblings)
3 siblings, 1 reply; 8+ messages in thread
From: Matti Vaittinen @ 2018-06-15 13:28 UTC (permalink / raw)
To: mturquette, sboyd, robh+dt, mark.rutland, lee.jones, lgirdwood,
broonie, mazziesaccount, arnd, dmitry.torokhov, sre, chenjh,
andrew.smirnov, linus.walleij, kstewart, heiko, gregkh
Cc: linux-clk, devicetree, linux-kernel, linux-input, mikko.mutanen,
heikki.haikola
ROHM BD71837 PMIC MFD driver providing interrupts and support
for two subsystems:
- clk
- Regulators
Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
---
drivers/mfd/Kconfig | 13 ++
drivers/mfd/Makefile | 1 +
drivers/mfd/bd71837.c | 249 ++++++++++++++++++++++++++++++++++++++
include/linux/mfd/bd71837.h | 288 ++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 551 insertions(+)
create mode 100644 drivers/mfd/bd71837.c
create mode 100644 include/linux/mfd/bd71837.h
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index b860eb5aa194..7aa05fc9ed8e 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1787,6 +1787,19 @@ config MFD_STW481X
in various ST Microelectronics and ST-Ericsson embedded
Nomadik series.
+config MFD_BD71837
+ bool "BD71837 Power Management chip"
+ depends on I2C=y
+ depends on OF
+ select REGMAP_I2C
+ select REGMAP_IRQ
+ select MFD_CORE
+ help
+ Select this option to get support for the ROHM BD71837
+ Power Management chips. BD71837 is designed to power processors like
+ NXP i.MX8. It contains 8 BUCK outputs and 7 LDOs, voltage monitoring
+ and emergency shut down as well as 32,768KHz clock output.
+
config MFD_STM32_LPTIMER
tristate "Support for STM32 Low-Power Timer"
depends on (ARCH_STM32 && OF) || COMPILE_TEST
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index e9fd20dba18d..09dc9eb3782c 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -227,4 +227,5 @@ obj-$(CONFIG_MFD_STM32_TIMERS) += stm32-timers.o
obj-$(CONFIG_MFD_MXS_LRADC) += mxs-lradc.o
obj-$(CONFIG_MFD_SC27XX_PMIC) += sprd-sc27xx-spi.o
obj-$(CONFIG_RAVE_SP_CORE) += rave-sp.o
+obj-$(CONFIG_MFD_BD71837) += bd71837.o
diff --git a/drivers/mfd/bd71837.c b/drivers/mfd/bd71837.c
new file mode 100644
index 000000000000..114be66f0a25
--- /dev/null
+++ b/drivers/mfd/bd71837.c
@@ -0,0 +1,249 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2018 ROHM Semiconductors
+// bd71837.c -- ROHM BD71837MWV mfd driver
+//
+// Datasheet available from
+// https://www.rohm.com/datasheet/BD71837MWV/bd71837mwv-e
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/gpio.h>
+#include <linux/regmap.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/bd71837.h>
+
+static const struct resource irqs[] = {
+ {
+ .start = BD71837_INT_PWRBTN,
+ .end = BD71837_INT_PWRBTN,
+ .flags = IORESOURCE_IRQ,
+ .name = "pwr-btn",
+ }, {
+ .start = BD71837_INT_PWRBTN_L,
+ .end = BD71837_INT_PWRBTN_L,
+ .flags = IORESOURCE_IRQ,
+ .name = "pwr-btn-l",
+ }, {
+ .start = BD71837_INT_PWRBTN_S,
+ .end = BD71837_INT_PWRBTN_S,
+ .flags = IORESOURCE_IRQ,
+ .name = "pwr-btn-s",
+ },
+};
+
+/* bd71837 multi function cells */
+static struct mfd_cell bd71837_mfd_cells[] = {
+ {
+ .name = "bd71837-clk",
+ }, {
+ .name = "bd718xx-pwrkey",
+ .resources = &irqs[0],
+ .num_resources = ARRAY_SIZE(irqs),
+ }, {
+ .name = "bd71837-pmic",
+ },
+};
+
+static const struct regmap_irq bd71837_irqs[] = {
+ REGMAP_IRQ_REG(BD71837_INT_SWRST, 0, BD71837_INT_SWRST_MASK),
+ REGMAP_IRQ_REG(BD71837_INT_PWRBTN_S, 0, BD71837_INT_PWRBTN_S_MASK),
+ REGMAP_IRQ_REG(BD71837_INT_PWRBTN_L, 0, BD71837_INT_PWRBTN_L_MASK),
+ REGMAP_IRQ_REG(BD71837_INT_PWRBTN, 0, BD71837_INT_PWRBTN_MASK),
+ REGMAP_IRQ_REG(BD71837_INT_WDOG, 0, BD71837_INT_WDOG_MASK),
+ REGMAP_IRQ_REG(BD71837_INT_ON_REQ, 0, BD71837_INT_ON_REQ_MASK),
+ REGMAP_IRQ_REG(BD71837_INT_STBY_REQ, 0, BD71837_INT_STBY_REQ_MASK),
+};
+
+static struct regmap_irq_chip bd71837_irq_chip = {
+ .name = "bd71837-irq",
+ .irqs = bd71837_irqs,
+ .num_irqs = ARRAY_SIZE(bd71837_irqs),
+ .num_regs = 1,
+ .irq_reg_stride = 1,
+ .status_base = BD71837_REG_IRQ,
+ .mask_base = BD71837_REG_MIRQ,
+ .ack_base = BD71837_REG_IRQ,
+ .init_ack_masked = true,
+ .mask_invert = false,
+};
+
+static int bd71837_irq_exit(struct bd71837 *bd71837)
+{
+ if (bd71837->chip_irq > 0)
+ regmap_del_irq_chip(bd71837->chip_irq, bd71837->irq_data);
+ return 0;
+}
+
+static const struct regmap_range pmic_status_range = {
+ .range_min = BD71837_REG_IRQ,
+ .range_max = BD71837_REG_POW_STATE,
+};
+
+static const struct regmap_access_table volatile_regs = {
+ .yes_ranges = &pmic_status_range,
+ .n_yes_ranges = 1,
+};
+
+static const struct regmap_config bd71837_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .volatile_table = &volatile_regs,
+ .max_register = BD71837_MAX_REGISTER - 1,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id bd71837_of_match[] = {
+ { .compatible = "rohm,bd71837", .data = (void *)0},
+ { },
+};
+MODULE_DEVICE_TABLE(of, bd71837_of_match);
+
+static int bd71837_parse_dt(struct i2c_client *client, struct bd71837_board **b)
+{
+ struct device_node *np = client->dev.of_node;
+ struct bd71837_board *board_info;
+ unsigned int prop;
+ int r;
+ int rv = -ENOMEM;
+
+ board_info = devm_kzalloc(&client->dev, sizeof(*board_info),
+ GFP_KERNEL);
+ if (!board_info)
+ goto err_out;
+
+ if (client->irq) {
+ dev_dbg(&client->dev, "Got irq %d\n", client->irq);
+ board_info->gpio_intr = client->irq;
+ } else {
+ dev_err(&client->dev, "no pmic intr pin available\n");
+ rv = -ENOENT;
+ goto err_intr;
+ }
+ r = of_property_read_u32(np, "irq_base", &prop);
+ if (!r)
+ board_info->irq_base = prop;
+ else
+ board_info->irq_base = 0;
+
+ rv = 0;
+ *b = board_info;
+ if (0) {
+err_intr:
+ devm_kfree(&client->dev, board_info);
+err_out:
+ dev_err(&client->dev, "failed to parse device-tree (%d)\n", rv);
+ }
+ return rv;
+}
+#endif //CONFIG_OF
+
+static int bd71837_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct bd71837 *bd71837;
+ struct bd71837_board *pmic_plat_data;
+ int ret = -EINVAL;
+
+ pmic_plat_data = dev_get_platdata(&i2c->dev);
+
+ if (!pmic_plat_data && i2c->dev.of_node)
+ ret = bd71837_parse_dt(i2c, &pmic_plat_data);
+
+ if (!pmic_plat_data)
+ return ret;
+
+ bd71837 = devm_kzalloc(&i2c->dev, sizeof(struct bd71837), GFP_KERNEL);
+ if (bd71837 == NULL)
+ return -ENOMEM;
+
+ bd71837->of_plat_data = pmic_plat_data;
+ i2c_set_clientdata(i2c, bd71837);
+ bd71837->dev = &i2c->dev;
+ bd71837->i2c_client = i2c;
+ bd71837->chip_irq = pmic_plat_data->gpio_intr;
+
+ bd71837->regmap = devm_regmap_init_i2c(i2c, &bd71837_regmap_config);
+ if (IS_ERR(bd71837->regmap)) {
+ ret = PTR_ERR(bd71837->regmap);
+ dev_err(&i2c->dev, "regmap initialization failed: %d\n", ret);
+ goto err_out;
+ }
+
+ ret = bd71837_reg_read(bd71837, BD71837_REG_REV);
+ if (ret < 0) {
+ dev_err(bd71837->dev,
+ "%s(): Read BD71837_REG_DEVICE failed!\n", __func__);
+ goto err_out;
+ }
+
+ ret = regmap_add_irq_chip(bd71837->regmap, bd71837->chip_irq,
+ IRQF_ONESHOT, 0,
+ &bd71837_irq_chip, &bd71837->irq_data);
+ if (ret < 0) {
+ dev_err(bd71837->dev, "Failed to add irq_chip %d\n", ret);
+ goto err_out;
+ }
+
+ ret = mfd_add_devices(bd71837->dev, PLATFORM_DEVID_AUTO,
+ bd71837_mfd_cells, ARRAY_SIZE(bd71837_mfd_cells),
+ NULL, 0,
+ regmap_irq_get_domain(bd71837->irq_data));
+ if (ret)
+ regmap_del_irq_chip(bd71837->chip_irq, bd71837->irq_data);
+err_out:
+
+ return ret;
+}
+
+static int bd71837_i2c_remove(struct i2c_client *i2c)
+{
+ struct bd71837 *bd71837 = i2c_get_clientdata(i2c);
+
+ bd71837_irq_exit(bd71837);
+ mfd_remove_devices(bd71837->dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id bd71837_i2c_id[] = {
+ { .name = "bd71837", },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, bd71837_i2c_id);
+
+static struct i2c_driver bd71837_i2c_driver = {
+ .driver = {
+ .name = "bd71837-mfd",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(bd71837_of_match),
+ },
+ .probe = bd71837_i2c_probe,
+ .remove = bd71837_i2c_remove,
+ .id_table = bd71837_i2c_id,
+};
+
+static int __init bd71837_i2c_init(void)
+{
+ return i2c_add_driver(&bd71837_i2c_driver);
+}
+/* init early so consumer devices can complete system boot */
+subsys_initcall(bd71837_i2c_init);
+
+static void __exit bd71837_i2c_exit(void)
+{
+ i2c_del_driver(&bd71837_i2c_driver);
+}
+module_exit(bd71837_i2c_exit);
+
+MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
+MODULE_DESCRIPTION("BD71837 chip multi-function driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/mfd/bd71837.h b/include/linux/mfd/bd71837.h
new file mode 100644
index 000000000000..641b7dba3076
--- /dev/null
+++ b/include/linux/mfd/bd71837.h
@@ -0,0 +1,288 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2018 ROHM Semiconductors */
+
+/*
+ * ROHM BD71837MWV header file
+ */
+
+#ifndef __LINUX_MFD_BD71837_H__
+#define __LINUX_MFD_BD71837_H__
+
+#include <linux/regmap.h>
+
+enum {
+ BD71837_BUCK1 = 0,
+ BD71837_BUCK2,
+ BD71837_BUCK3,
+ BD71837_BUCK4,
+ BD71837_BUCK5,
+ BD71837_BUCK6,
+ BD71837_BUCK7,
+ BD71837_BUCK8,
+ BD71837_LDO1,
+ BD71837_LDO2,
+ BD71837_LDO3,
+ BD71837_LDO4,
+ BD71837_LDO5,
+ BD71837_LDO6,
+ BD71837_LDO7,
+ BD71837_REGULATOR_CNT,
+};
+
+#define BD71837_BUCK1_VOLTAGE_NUM 0x40
+#define BD71837_BUCK2_VOLTAGE_NUM 0x40
+#define BD71837_BUCK3_VOLTAGE_NUM 0x40
+#define BD71837_BUCK4_VOLTAGE_NUM 0x40
+
+#define BD71837_BUCK5_VOLTAGE_NUM 0x08
+#define BD71837_BUCK6_VOLTAGE_NUM 0x04
+#define BD71837_BUCK7_VOLTAGE_NUM 0x08
+#define BD71837_BUCK8_VOLTAGE_NUM 0x40
+
+#define BD71837_LDO1_VOLTAGE_NUM 0x04
+#define BD71837_LDO2_VOLTAGE_NUM 0x02
+#define BD71837_LDO3_VOLTAGE_NUM 0x10
+#define BD71837_LDO4_VOLTAGE_NUM 0x10
+#define BD71837_LDO5_VOLTAGE_NUM 0x10
+#define BD71837_LDO6_VOLTAGE_NUM 0x10
+#define BD71837_LDO7_VOLTAGE_NUM 0x10
+
+enum {
+ BD71837_REG_REV = 0x00,
+ BD71837_REG_SWRESET = 0x01,
+ BD71837_REG_I2C_DEV = 0x02,
+ BD71837_REG_PWRCTRL0 = 0x03,
+ BD71837_REG_PWRCTRL1 = 0x04,
+ BD71837_REG_BUCK1_CTRL = 0x05,
+ BD71837_REG_BUCK2_CTRL = 0x06,
+ BD71837_REG_BUCK3_CTRL = 0x07,
+ BD71837_REG_BUCK4_CTRL = 0x08,
+ BD71837_REG_BUCK5_CTRL = 0x09,
+ BD71837_REG_BUCK6_CTRL = 0x0A,
+ BD71837_REG_BUCK7_CTRL = 0x0B,
+ BD71837_REG_BUCK8_CTRL = 0x0C,
+ BD71837_REG_BUCK1_VOLT_RUN = 0x0D,
+ BD71837_REG_BUCK1_VOLT_IDLE = 0x0E,
+ BD71837_REG_BUCK1_VOLT_SUSP = 0x0F,
+ BD71837_REG_BUCK2_VOLT_RUN = 0x10,
+ BD71837_REG_BUCK2_VOLT_IDLE = 0x11,
+ BD71837_REG_BUCK3_VOLT_RUN = 0x12,
+ BD71837_REG_BUCK4_VOLT_RUN = 0x13,
+ BD71837_REG_BUCK5_VOLT = 0x14,
+ BD71837_REG_BUCK6_VOLT = 0x15,
+ BD71837_REG_BUCK7_VOLT = 0x16,
+ BD71837_REG_BUCK8_VOLT = 0x17,
+ BD71837_REG_LDO1_VOLT = 0x18,
+ BD71837_REG_LDO2_VOLT = 0x19,
+ BD71837_REG_LDO3_VOLT = 0x1A,
+ BD71837_REG_LDO4_VOLT = 0x1B,
+ BD71837_REG_LDO5_VOLT = 0x1C,
+ BD71837_REG_LDO6_VOLT = 0x1D,
+ BD71837_REG_LDO7_VOLT = 0x1E,
+ BD71837_REG_TRANS_COND0 = 0x1F,
+ BD71837_REG_TRANS_COND1 = 0x20,
+ BD71837_REG_VRFAULTEN = 0x21,
+ BD71837_REG_MVRFLTMASK0 = 0x22,
+ BD71837_REG_MVRFLTMASK1 = 0x23,
+ BD71837_REG_MVRFLTMASK2 = 0x24,
+ BD71837_REG_RCVCFG = 0x25,
+ BD71837_REG_RCVNUM = 0x26,
+ BD71837_REG_PWRONCONFIG0 = 0x27,
+ BD71837_REG_PWRONCONFIG1 = 0x28,
+ BD71837_REG_RESETSRC = 0x29,
+ BD71837_REG_MIRQ = 0x2A,
+ BD71837_REG_IRQ = 0x2B,
+ BD71837_REG_IN_MON = 0x2C,
+ BD71837_REG_POW_STATE = 0x2D,
+ BD71837_REG_OUT32K = 0x2E,
+ BD71837_REG_REGLOCK = 0x2F,
+ BD71837_REG_OTPVER = 0xFF,
+ BD71837_MAX_REGISTER = 0x100,
+};
+
+#define REGLOCK_PWRSEQ 0x1
+#define REGLOCK_VREG 0x10
+
+/* Generic BUCK control masks */
+#define BD71837_BUCK_SEL 0x02
+#define BD71837_BUCK_EN 0x01
+#define BD71837_BUCK_RUN_ON 0x04
+
+/* Generic LDO masks */
+#define BD71837_LDO_SEL 0x80
+#define BD71837_LDO_EN 0x40
+
+/* BD71837 BUCK ramp rate CTRL reg bits */
+#define BUCK_RAMPRATE_MASK 0xC0
+#define BUCK_RAMPRATE_10P00MV 0x0
+#define BUCK_RAMPRATE_5P00MV 0x1
+#define BUCK_RAMPRATE_2P50MV 0x2
+#define BUCK_RAMPRATE_1P25MV 0x3
+
+/* BD71837_REG_BUCK1_VOLT_RUN bits */
+#define BUCK1_RUN_MASK 0x3F
+#define BUCK1_RUN_DEFAULT 0x14
+
+/* BD71837_REG_BUCK1_VOLT_SUSP bits */
+#define BUCK1_SUSP_MASK 0x3F
+#define BUCK1_SUSP_DEFAULT 0x14
+
+/* BD71837_REG_BUCK1_VOLT_IDLE bits */
+#define BUCK1_IDLE_MASK 0x3F
+#define BUCK1_IDLE_DEFAULT 0x14
+
+/* BD71837_REG_BUCK2_VOLT_RUN bits */
+#define BUCK2_RUN_MASK 0x3F
+#define BUCK2_RUN_DEFAULT 0x1E
+
+/* BD71837_REG_BUCK2_VOLT_IDLE bits */
+#define BUCK2_IDLE_MASK 0x3F
+#define BUCK2_IDLE_DEFAULT 0x14
+
+/* BD71837_REG_BUCK3_VOLT_RUN bits */
+#define BUCK3_RUN_MASK 0x3F
+#define BUCK3_RUN_DEFAULT 0x1E
+
+/* BD71837_REG_BUCK4_VOLT_RUN bits */
+#define BUCK4_RUN_MASK 0x3F
+#define BUCK4_RUN_DEFAULT 0x1E
+
+/* BD71837_REG_BUCK5_VOLT bits */
+#define BUCK5_MASK 0x07
+#define BUCK5_DEFAULT 0x02
+
+/* BD71837_REG_BUCK6_VOLT bits */
+#define BUCK6_MASK 0x03
+#define BUCK6_DEFAULT 0x03
+
+/* BD71837_REG_BUCK7_VOLT bits */
+#define BUCK7_MASK 0x07
+#define BUCK7_DEFAULT 0x03
+
+/* BD71837_REG_BUCK8_VOLT bits */
+#define BUCK8_MASK 0x3F
+#define BUCK8_DEFAULT 0x1E
+
+/* BD71837_REG_IRQ bits */
+#define IRQ_SWRST 0x40
+#define IRQ_PWRON_S 0x20
+#define IRQ_PWRON_L 0x10
+#define IRQ_PWRON 0x08
+#define IRQ_WDOG 0x04
+#define IRQ_ON_REQ 0x02
+#define IRQ_STBY_REQ 0x01
+
+/* BD71837_REG_OUT32K bits */
+#define BD71837_OUT32K_EN 0x01
+
+/* BD71837 gated clock rate */
+#define BD71837_CLK_RATE 32768
+
+/* BD71837 irqs */
+enum {
+ BD71837_INT_STBY_REQ,
+ BD71837_INT_ON_REQ,
+ BD71837_INT_WDOG,
+ BD71837_INT_PWRBTN,
+ BD71837_INT_PWRBTN_L,
+ BD71837_INT_PWRBTN_S,
+ BD71837_INT_SWRST
+};
+
+/* BD71837 interrupt masks */
+#define BD71837_INT_SWRST_MASK 0x40
+#define BD71837_INT_PWRBTN_S_MASK 0x20
+#define BD71837_INT_PWRBTN_L_MASK 0x10
+#define BD71837_INT_PWRBTN_MASK 0x8
+#define BD71837_INT_WDOG_MASK 0x4
+#define BD71837_INT_ON_REQ_MASK 0x2
+#define BD71837_INT_STBY_REQ_MASK 0x1
+
+/* BD71837_REG_LDO1_VOLT bits */
+#define LDO1_MASK 0x03
+
+/* BD71837_REG_LDO1_VOLT bits */
+#define LDO2_MASK 0x20
+
+/* BD71837_REG_LDO3_VOLT bits */
+#define LDO3_MASK 0x0F
+
+/* BD71837_REG_LDO4_VOLT bits */
+#define LDO4_MASK 0x0F
+
+/* BD71837_REG_LDO5_VOLT bits */
+#define LDO5_MASK 0x0F
+
+/* BD71837_REG_LDO6_VOLT bits */
+#define LDO6_MASK 0x0F
+
+/* BD71837_REG_LDO7_VOLT bits */
+#define LDO7_MASK 0x0F
+
+struct bd71837;
+struct bd71837_pmic;
+struct bd71837_clk;
+
+/*
+ * Board platform data may be used to initialize regulators.
+ */
+
+struct bd71837_board {
+ struct regulator_init_data *init_data[BD71837_REGULATOR_CNT];
+ int gpio_intr;
+ int irq_base;
+};
+
+struct bd71837 {
+ struct device *dev;
+ struct i2c_client *i2c_client;
+ struct regmap *regmap;
+ unsigned long int id;
+
+ int chip_irq;
+ struct regmap_irq_chip_data *irq_data;
+
+ struct bd71837_pmic *pmic;
+ struct bd71837_clk *clk;
+
+ struct bd71837_board *of_plat_data;
+};
+
+/*
+ * bd71837 sub-driver chip access routines
+ */
+
+static inline int bd71837_reg_read(struct bd71837 *bd71837, u8 reg)
+{
+ int r, val;
+
+ r = regmap_read(bd71837->regmap, reg, &val);
+ if (r < 0)
+ return r;
+ return val;
+}
+
+static inline int bd71837_reg_write(struct bd71837 *bd71837, u8 reg,
+ unsigned int val)
+{
+ return regmap_write(bd71837->regmap, reg, val);
+}
+
+static inline int bd71837_set_bits(struct bd71837 *bd71837, u8 reg, u8 mask)
+{
+ return regmap_update_bits(bd71837->regmap, reg, mask, mask);
+}
+
+static inline int bd71837_clear_bits(struct bd71837 *bd71837, u8 reg,
+ u8 mask)
+{
+ return regmap_update_bits(bd71837->regmap, reg, mask, 0);
+}
+
+static inline int bd71837_update_bits(struct bd71837 *bd71837, u8 reg,
+ u8 mask, u8 val)
+{
+ return regmap_update_bits(bd71837->regmap, reg, mask, val);
+}
+
+#endif /* __LINUX_MFD_BD71837_H__ */
--
2.14.3
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH v6 3/4] clk: bd71837: Add driver for BD71837 PMIC clock
2018-06-15 13:27 [PATCH v6 0/4] mfd/regulator/clk/input: bd71837: ROHM BD71837 PMIC driver Matti Vaittinen
2018-06-15 13:28 ` [PATCH v6 1/4] mfd: bd71837: mfd driver for ROHM BD71837 PMIC Matti Vaittinen
2018-06-15 13:30 ` [PATCH v6 2/4] mfd: bd71837: Devicetree bindings " Matti Vaittinen
@ 2018-06-15 13:30 ` Matti Vaittinen
2018-06-18 14:10 ` Matti Vaittinen
2018-06-15 13:31 ` [PATCH v6 4/4] input/power: Add driver for BD71837/BD71847 PMIC power button Matti Vaittinen
3 siblings, 1 reply; 8+ messages in thread
From: Matti Vaittinen @ 2018-06-15 13:30 UTC (permalink / raw)
To: mturquette, sboyd, robh+dt, mark.rutland, lee.jones, lgirdwood,
broonie, mazziesaccount, arnd, dmitry.torokhov, sre, chenjh,
andrew.smirnov, linus.walleij, kstewart, heiko, gregkh
Cc: linux-clk, devicetree, linux-kernel, linux-input, mikko.mutanen,
heikki.haikola
Support BD71837 gateable 32768 Hz clock.
Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
---
drivers/clk/Kconfig | 6 ++
drivers/clk/Makefile | 1 +
drivers/clk/clk-bd71837.c | 145 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 152 insertions(+)
create mode 100644 drivers/clk/clk-bd71837.c
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 41492e980ef4..065421a9eb22 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -279,6 +279,12 @@ config COMMON_CLK_STM32H7
---help---
Support for stm32h7 SoC family clocks
+config COMMON_CLK_BD71837
+ tristate "Clock driver for ROHM BD71837 PMIC MFD"
+ depends on MFD_BD71837
+ help
+ This driver supports ROHM BD71837 PMIC clock.
+
source "drivers/clk/bcm/Kconfig"
source "drivers/clk/hisilicon/Kconfig"
source "drivers/clk/imgtec/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index de6d06ac790b..8393c4af7d5a 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -21,6 +21,7 @@ endif
obj-$(CONFIG_MACH_ASM9260) += clk-asm9260.o
obj-$(CONFIG_COMMON_CLK_AXI_CLKGEN) += clk-axi-clkgen.o
obj-$(CONFIG_ARCH_AXXIA) += clk-axm5516.o
+obj-$(CONFIG_COMMON_CLK_BD71837) += clk-bd71837.o
obj-$(CONFIG_COMMON_CLK_CDCE706) += clk-cdce706.o
obj-$(CONFIG_COMMON_CLK_CDCE925) += clk-cdce925.o
obj-$(CONFIG_ARCH_CLPS711X) += clk-clps711x.o
diff --git a/drivers/clk/clk-bd71837.c b/drivers/clk/clk-bd71837.c
new file mode 100644
index 000000000000..8b9aae33eade
--- /dev/null
+++ b/drivers/clk/clk-bd71837.c
@@ -0,0 +1,145 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2018 ROHM Semiconductors
+// bd71837.c -- ROHM BD71837MWV clock driver
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/mfd/bd71837.h>
+#include <linux/clk-provider.h>
+
+struct bd71837_clk {
+ struct clk_hw hw;
+ u8 reg;
+ u8 mask;
+ unsigned long rate;
+ struct platform_device *pdev;
+ struct bd71837 *mfd;
+};
+
+static int bd71837_clk_set(struct clk_hw *hw, int status)
+{
+ struct bd71837_clk *c = container_of(hw, struct bd71837_clk, hw);
+
+ return bd71837_update_bits(c->mfd, c->reg, c->mask, status);
+}
+
+static void bd71837_clk_disable(struct clk_hw *hw)
+{
+ int rv;
+ struct bd71837_clk *c = container_of(hw, struct bd71837_clk, hw);
+
+ rv = bd71837_clk_set(hw, 0);
+ if (rv)
+ dev_dbg(&c->pdev->dev, "Failed to disable 32K clk (%d)\n", rv);
+}
+
+static int bd71837_clk_enable(struct clk_hw *hw)
+{
+ return bd71837_clk_set(hw, 1);
+}
+
+static int bd71837_clk_is_enabled(struct clk_hw *hw)
+{
+ int enabled;
+ struct bd71837_clk *c = container_of(hw, struct bd71837_clk, hw);
+
+ enabled = c->mask;
+ enabled &= bd71837_reg_read(c->mfd, c->reg);
+
+ return enabled;
+}
+static unsigned long bd71837_clk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct bd71837_clk *c = container_of(hw, struct bd71837_clk, hw);
+
+ return c->rate;
+}
+
+static struct clk_ops bd71837_clk_ops = {
+ .prepare = &bd71837_clk_enable,
+ .unprepare = &bd71837_clk_disable,
+ .is_prepared = &bd71837_clk_is_enabled,
+};
+
+static int bd71837_clk_probe(struct platform_device *pdev)
+{
+ struct bd71837_clk *c;
+ int rval = -ENOMEM;
+ const char *parent_clk;
+ struct device *parent = pdev->dev.parent;
+ struct bd71837 *mfd = dev_get_drvdata(parent);
+ struct clk_init_data init = {
+ .name = "bd71837-32k-out",
+ .ops = &bd71837_clk_ops,
+ };
+
+ c = devm_kzalloc(&pdev->dev, sizeof(*c), GFP_KERNEL);
+ return -ENOMEM;
+
+ parent_clk = of_clk_get_parent_name(parent->of_node, 0);
+
+ init.parent_names = &parent_clk;
+ if (parent_clk) {
+ init.num_parents = 1;
+ } else {
+ /* If parent is not given from DT we assume the typical
+ * use-case with 32.768 KHz oscillator for RTC (Maybe we
+ * should just error out here and require parent?)
+ */
+ c->rate = BD71837_CLK_RATE;
+ bd71837_clk_ops.recalc_rate = &bd71837_clk_recalc_rate;
+ dev_warn(&pdev->dev, "No parent clk found - assuming 32,768 KHz\n");
+ }
+
+ c->reg = BD71837_REG_OUT32K;
+ c->mask = BD71837_OUT32K_EN;
+ c->mfd = mfd;
+ c->pdev = pdev;
+ c->hw.init = &init;
+
+ of_property_read_string_index(parent->of_node,
+ "clock-output-names", 0,
+ &init.name);
+
+ rval = devm_clk_hw_register(&pdev->dev, &c->hw);
+ if (!rval) {
+ if (parent->of_node) {
+ rval = of_clk_add_hw_provider(parent->of_node,
+ of_clk_hw_simple_get,
+ &c->hw);
+ if (rval)
+ dev_err(&pdev->dev,
+ "adding clk provider failed\n");
+ }
+ } else {
+ dev_err(&pdev->dev, "failed to register 32K clk");
+ }
+
+ return rval;
+}
+
+static int bd71837_clk_remove(struct platform_device *pdev)
+{
+ if (pdev->dev.parent->of_node)
+ of_clk_del_provider(pdev->dev.parent->of_node);
+ return 0;
+}
+
+static struct platform_driver bd71837_clk = {
+ .driver = {
+ .name = "bd71837-clk",
+ },
+ .probe = bd71837_clk_probe,
+ .remove = bd71837_clk_remove,
+};
+
+module_platform_driver(bd71837_clk);
+
+MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
+MODULE_DESCRIPTION("BD71837 chip clk driver");
+MODULE_LICENSE("GPL");
--
2.14.3
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH v6 4/4] input/power: Add driver for BD71837/BD71847 PMIC power button
2018-06-15 13:27 [PATCH v6 0/4] mfd/regulator/clk/input: bd71837: ROHM BD71837 PMIC driver Matti Vaittinen
` (2 preceding siblings ...)
2018-06-15 13:30 ` [PATCH v6 3/4] clk: bd71837: Add driver for BD71837 PMIC clock Matti Vaittinen
@ 2018-06-15 13:31 ` Matti Vaittinen
3 siblings, 0 replies; 8+ messages in thread
From: Matti Vaittinen @ 2018-06-15 13:31 UTC (permalink / raw)
To: mturquette, sboyd, robh+dt, mark.rutland, lee.jones, lgirdwood,
broonie, mazziesaccount, arnd, dmitry.torokhov, sre, chenjh,
andrew.smirnov, linus.walleij, kstewart, heiko, gregkh
Cc: linux-clk, devicetree, linux-kernel, linux-input, mikko.mutanen,
heikki.haikola
ROHM BD71837 PMIC power button driver providing power-key press
information to user-space.
Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
---
drivers/input/misc/Kconfig | 10 +++++
drivers/input/misc/Makefile | 1 +
drivers/input/misc/bd718xx-pwrkey.c | 90 +++++++++++++++++++++++++++++++++++++
include/linux/mfd/bd71837.h | 82 +++++++++++++++++++++++++++++++++
4 files changed, 183 insertions(+)
create mode 100644 drivers/input/misc/bd718xx-pwrkey.c
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 572b15fa18c2..694c05d3f9fb 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -96,6 +96,16 @@ config INPUT_ATMEL_CAPTOUCH
To compile this driver as a module, choose M here: the
module will be called atmel_captouch.
+config INPUT_BD718XX_PWRKEY
+ tristate "ROHM BD71837/BD71847 power key support"
+ depends on MFD_BD71837
+ help
+ Say Y here if you want support for the power key usually found
+ on boards using a ROHM BD71837/BD71847 compatible PMIC.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bd718xx-pwrkey.
+
config INPUT_BMA150
tristate "BMA150/SMB380 acceleration sensor support"
depends on I2C
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 72cde28649e2..ea5b81cbf2bf 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o
obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o
obj-$(CONFIG_INPUT_ATMEL_CAPTOUCH) += atmel_captouch.o
obj-$(CONFIG_INPUT_BMA150) += bma150.o
+obj-$(CONFIG_INPUT_BD718XX_PWRKEY) += bd718xx-pwrkey.o
obj-$(CONFIG_INPUT_CM109) += cm109.o
obj-$(CONFIG_INPUT_CMA3000) += cma3000_d0x.o
obj-$(CONFIG_INPUT_CMA3000_I2C) += cma3000_d0x_i2c.o
diff --git a/drivers/input/misc/bd718xx-pwrkey.c b/drivers/input/misc/bd718xx-pwrkey.c
new file mode 100644
index 000000000000..e8ac9475c3cf
--- /dev/null
+++ b/drivers/input/misc/bd718xx-pwrkey.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2018 ROHM Semiconductors
+// bd718xx-pwrkey.c -- ROHM BD71837MWV and BD71847 power button driver
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/mfd/bd71837.h>
+
+struct bd718xx_pwrkey {
+ struct input_dev *idev;
+ struct bd71837 *mfd;
+ int irq;
+};
+
+static irqreturn_t button_irq(int irq, void *_priv)
+{
+ struct input_dev *idev = (struct input_dev *)_priv;
+
+ input_report_key(idev, KEY_POWER, 1);
+ input_sync(idev);
+ input_report_key(idev, KEY_POWER, 0);
+ input_sync(idev);
+
+ return IRQ_HANDLED;
+}
+
+static int bd718xx_pwr_btn_probe(struct platform_device *pdev)
+{
+ int err = -ENOMEM;
+ struct bd718xx_pwrkey *pk;
+
+ pk = devm_kzalloc(&pdev->dev, sizeof(*pk), GFP_KERNEL);
+ if (!pk)
+ goto err_out;
+
+ pk->mfd = dev_get_drvdata(pdev->dev.parent);
+
+ pk->idev = devm_input_allocate_device(&pdev->dev);
+ if (!pk->idev)
+ goto err_out;
+
+ pk->idev->name = "bd718xx-pwrkey";
+ pk->idev->phys = "bd718xx-pwrkey/input0";
+ pk->idev->dev.parent = &pdev->dev;
+
+ input_set_capability(pk->idev, EV_KEY, KEY_POWER);
+
+ err = platform_get_irq_byname(pdev, "pwr-btn-s");
+ if (err < 0) {
+ dev_err(&pdev->dev, "could not get power key interrupt\n");
+ goto err_out;
+ }
+
+ pk->irq = err;
+ err = devm_request_threaded_irq(&pdev->dev, pk->irq, NULL, &button_irq,
+ 0, "bd718xx-pwrkey", pk);
+ if (err)
+ goto err_out;
+
+ platform_set_drvdata(pdev, pk);
+ err = regmap_update_bits(pk->mfd->regmap,
+ BD71837_REG_PWRONCONFIG0,
+ BD718XX_PWRBTN_SHORT_PRESS_MASK,
+ BD718XX_PWRBTN_SHORT_PRESS_10MS);
+ if (err)
+ goto err_out;
+
+ err = input_register_device(pk->idev);
+
+err_out:
+
+ return err;
+}
+
+static struct platform_driver bd718xx_pwr_btn_driver = {
+ .probe = bd718xx_pwr_btn_probe,
+ .driver = {
+ .name = "bd718xx-pwrkey",
+ },
+};
+module_platform_driver(bd718xx_pwr_btn_driver);
+MODULE_DESCRIPTION("Power button driver for buttons connected to ROHM bd71837/bd71847 PMIC");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
+
diff --git a/include/linux/mfd/bd71837.h b/include/linux/mfd/bd71837.h
index 641b7dba3076..410c0afa0452 100644
--- a/include/linux/mfd/bd71837.h
+++ b/include/linux/mfd/bd71837.h
@@ -100,6 +100,86 @@ enum {
BD71837_MAX_REGISTER = 0x100,
};
+/* register write induced reset settings */
+
+/* even though the bit zero is not SWRESET type we still want to write zero
+ * to it when changing type. Biz zero is 'SWRESET' trigger bit and if we
+ * write 1 to it we will trigger the action. So always write 0 to it when
+ * changning SWRESET action - no matter what we read from it.
+ */
+#define BD71837_SWRESET_TYPE_MASK 7
+#define BD71837_SWRESET_TYPE_DISABLED 0
+#define BD71837_SWRESET_TYPE_COLD 4
+#define BD71837_SWRESET_TYPE_WARM 6
+
+#define BD71837_SWRESET_RESET_MASK 1
+#define BD71837_SWRESET_RESET 1
+
+/* Poweroff state transition conditions */
+
+#define BD718XX_ON_REQ_POWEROFF_MASK 1
+#define BD718XX_SWRESET_POWEROFF_MASK 2
+#define BD718XX_WDOG_POWEROFF_MASK 4
+#define BD718XX_KEY_L_POWEROFF_MASK 8
+
+#define BD718XX_POWOFF_TO_SNVS 0
+#define BD718XX_POWOFF_TO_RDY 0xF
+
+#define BD718XX_POWOFF_TIME_MASK 0xF0
+enum {
+ BD718XX_POWOFF_TIME_5MS = 0,
+ BD718XX_POWOFF_TIME_10MS,
+ BD718XX_POWOFF_TIME_15MS,
+ BD718XX_POWOFF_TIME_20MS,
+ BD718XX_POWOFF_TIME_25MS,
+ BD718XX_POWOFF_TIME_30MS,
+ BD718XX_POWOFF_TIME_35MS,
+ BD718XX_POWOFF_TIME_40MS,
+ BD718XX_POWOFF_TIME_45MS,
+ BD718XX_POWOFF_TIME_50MS,
+ BD718XX_POWOFF_TIME_75MS,
+ BD718XX_POWOFF_TIME_100MS,
+ BD718XX_POWOFF_TIME_250MS,
+ BD718XX_POWOFF_TIME_500MS,
+ BD718XX_POWOFF_TIME_750MS,
+ BD718XX_POWOFF_TIME_1500MS
+};
+
+/* Poweron sequence state transition conditions */
+
+#define BD718XX_RDY_TO_SNVS_MASK 0xF
+#define BD718XX_SNVS_TO_RUN_MASK 0xF0
+
+#define BD718XX_PWR_TRIG_KEY_L 1
+#define BD718XX_PWR_TRIG_KEY_S 2
+#define BD718XX_PWR_TRIG_PMIC_ON 4
+#define BD718XX_PWR_TRIG_VSYS_UVLO 8
+#define BD718XX_RDY_TO_SNVS_SIFT 0
+#define BD718XX_SNVS_TO_RUN_SIFT 4
+
+/* Timeout value for detecting short press */
+
+#define BD718XX_PWRBTN_SHORT_PRESS_MASK 0xF
+
+enum {
+ BD718XX_PWRBTN_SHORT_PRESS_10MS = 0,
+ BD718XX_PWRBTN_SHORT_PRESS_500MS,
+ BD718XX_PWRBTN_SHORT_PRESS_1000MS,
+ BD718XX_PWRBTN_SHORT_PRESS_1500MS,
+ BD718XX_PWRBTN_SHORT_PRESS_2000MS,
+ BD718XX_PWRBTN_SHORT_PRESS_2500MS,
+ BD718XX_PWRBTN_SHORT_PRESS_3000MS,
+ BD718XX_PWRBTN_SHORT_PRESS_3500MS,
+ BD718XX_PWRBTN_SHORT_PRESS_4000MS,
+ BD718XX_PWRBTN_SHORT_PRESS_4500MS,
+ BD718XX_PWRBTN_SHORT_PRESS_5000MS,
+ BD718XX_PWRBTN_SHORT_PRESS_5500MS,
+ BD718XX_PWRBTN_SHORT_PRESS_6000MS,
+ BD718XX_PWRBTN_SHORT_PRESS_6500MS,
+ BD718XX_PWRBTN_SHORT_PRESS_7000MS,
+ BD718XX_PWRBTN_SHORT_PRESS_7500MS
+};
+
#define REGLOCK_PWRSEQ 0x1
#define REGLOCK_VREG 0x10
@@ -222,6 +302,7 @@ enum {
struct bd71837;
struct bd71837_pmic;
struct bd71837_clk;
+struct bd718xx_pwrkey;
/*
* Board platform data may be used to initialize regulators.
@@ -244,6 +325,7 @@ struct bd71837 {
struct bd71837_pmic *pmic;
struct bd71837_clk *clk;
+ struct bd718xx_pwrkey *pwrkey;
struct bd71837_board *of_plat_data;
};
--
2.14.3
^ permalink raw reply related [flat|nested] 8+ messages in thread