summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-02-11 21:53:39 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2026-02-11 21:53:39 +0300
commitd70178215211a7c73ecabeb55eeb0f8ef002bcab (patch)
treeb3943a90930022fb5f36a14d48ccc2742ace3dc3 /drivers
parent893ace4df0f96b8ad066651453e0519d4ffe35ca (diff)
parentaf9b4a56f0000fb11057e204ddfb05d72ba4dba0 (diff)
downloadlinux-d70178215211a7c73ecabeb55eeb0f8ef002bcab.tar.xz
Merge tag 'gpio-updates-for-v7.0-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux
Pull gpio updates from Bartosz Golaszewski: "There are two new drivers and some changes to GPIO core but mostly just GPIO driver updates across a wide array of files, adding support for new models as well as various refactoring changes. Nothing controversial and everything has spent a good measure of time in linux-next. GPIOLIB core: - shrink the GPIO bus driver stub code - rework software node support for "undefined" software nodes - provide and use devm_fwnode_gpiod_get_optional() - only compile the OF quirk for MT2701 when needed New drivers: - add the GPIO driver for ROHM bd72720 - add the gpio-line-mux driver providing 1-to-many mapping for a single real GPIO Driver changes: - refactor gpio-pca9570: use lock guard, add missing headers, use devres consistently - add support for a new model (G7 Aspeed sgpiom) to the aspeed-sgpio driver along with some prerequisite refactoring - use device_get_match_data() where applicable and save some lines - add support for more models to gpio-cadence - add the compatible property to reset-gpio and use it in shared GPIO management - drop unnecessary use of irqd_get_trigger_type() in gpio-max77759 - add support for a new variant to gpio-pca953x - extend build coverage with COMPILE_TEST for more drivers - constify configfs structures in gpio-sim and gpio-virtuser - add support for the K3 SoC to gpio-spacemit - implement the missing .get_direction() callback in gpio-max77620 - add support for Tegra264 to gpio-tegra186 - drop unneeded MODULE_ALIAS() from gpio-menz127 DT bindings: - document support for the opencores GPIO controller in gpio-mmio - document new variants for gpio-pca953x Documentation: - extensively describe interrupt source detection for gpio-pca953x and add more models to the list of supported variants" * tag 'gpio-updates-for-v7.0-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux: (59 commits) gpio: tegra186: Add support for Tegra264 dt-bindings: gpio: Add Tegra264 support gpio: spacemit-k1: Use PDR for pin direction, not SDR/CDR gpio: max77620: Implement .get_direction() callback gpio: aspeed-sgpio: Support G7 Aspeed sgpiom controller dt-bindings: gpio: aspeed,sgpio: Support ast2700 gpio: aspeed-sgpio: Convert IRQ functions to use llops callbacks gpio: aspeed-sgpio: Create llops to handle hardware access gpio: aspeed-sgpio: Remove unused bank name field gpio: aspeed-sgpio: Change the macro to support deferred probe regulator: bd71815: switch to devm_fwnode_gpiod_get_optional gpiolib: introduce devm_fwnode_gpiod_get_optional() wrapper gpio: mmio: Add compatible for opencores GPIO dt-bindings: gpio-mmio: Correct opencores GPIO gpio: pca9570: use lock guards gpio: pca9570: Don't use "proxy" headers gpio: pca9570: Use devm_mutex_init() for mutex initialization MAINTAINERS: Add ROHM BD72720 PMIC power: supply: bd71828-power: Support ROHM BD72720 power: supply: bd71828: Support wider register addresses ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/clk/Kconfig4
-rw-r--r--drivers/clk/clk-bd718x7.c10
-rw-r--r--drivers/gpio/Kconfig24
-rw-r--r--drivers/gpio/Makefile2
-rw-r--r--drivers/gpio/gpio-aggregator.c8
-rw-r--r--drivers/gpio/gpio-aspeed-sgpio.c362
-rw-r--r--drivers/gpio/gpio-aspeed.c7
-rw-r--r--drivers/gpio/gpio-bd72720.c281
-rw-r--r--drivers/gpio/gpio-cadence.c76
-rw-r--r--drivers/gpio/gpio-creg-snps.c4
-rw-r--r--drivers/gpio/gpio-line-mux.c116
-rw-r--r--drivers/gpio/gpio-max77620.c19
-rw-r--r--drivers/gpio/gpio-max77759.c10
-rw-r--r--drivers/gpio/gpio-menz127.c1
-rw-r--r--drivers/gpio/gpio-mmio.c1
-rw-r--r--drivers/gpio/gpio-pca953x.c6
-rw-r--r--drivers/gpio/gpio-pca9570.c19
-rw-r--r--drivers/gpio/gpio-realtek-otto.c5
-rw-r--r--drivers/gpio/gpio-sim.c16
-rw-r--r--drivers/gpio/gpio-spacemit-k1.c168
-rw-r--r--drivers/gpio/gpio-tegra186.c90
-rw-r--r--drivers/gpio/gpio-virtuser.c8
-rw-r--r--drivers/gpio/gpio-zynq.c12
-rw-r--r--drivers/gpio/gpiolib-of.c4
-rw-r--r--drivers/gpio/gpiolib-shared.c7
-rw-r--r--drivers/gpio/gpiolib-swnode.c9
-rw-r--r--drivers/gpio/gpiolib.c30
-rw-r--r--drivers/mfd/Kconfig18
-rw-r--r--drivers/mfd/rohm-bd71828.c555
-rw-r--r--drivers/power/supply/bd71828-power.c160
-rw-r--r--drivers/regulator/Kconfig8
-rw-r--r--drivers/regulator/bd71815-regulator.c15
-rw-r--r--drivers/regulator/bd71828-regulator.c1025
-rw-r--r--drivers/rtc/Kconfig3
-rw-r--r--drivers/rtc/rtc-bd70528.c21
35 files changed, 2681 insertions, 423 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 3a1611008e48..619bd63a3c77 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -475,8 +475,8 @@ config COMMON_CLK_BD718XX
tristate "Clock driver for 32K clk gates on ROHM PMICs"
depends on MFD_ROHM_BD718XX || MFD_ROHM_BD71828
help
- This driver supports ROHM BD71837, BD71847, BD71850, BD71815
- and BD71828 PMICs clock gates.
+ This driver supports ROHM BD71837, BD71847, BD71850, BD71815,
+ BD71828, and BD72720 PMICs clock gates.
config COMMON_CLK_FIXED_MMIO
bool "Clock driver for Memory Mapped Fixed values"
diff --git a/drivers/clk/clk-bd718x7.c b/drivers/clk/clk-bd718x7.c
index ac40b669d60b..1cae974e6d1d 100644
--- a/drivers/clk/clk-bd718x7.c
+++ b/drivers/clk/clk-bd718x7.c
@@ -19,7 +19,8 @@
#define BD71828_REG_OUT32K 0x4B
/* BD71837 and BD71847 */
#define BD718XX_REG_OUT32K 0x2E
-
+/* BD72720 */
+#define BD72720_REG_OUT32K 0x9a
/*
* BD71837, BD71847, and BD71828 all use bit [0] to clk output control
*/
@@ -118,6 +119,10 @@ static int bd71837_clk_probe(struct platform_device *pdev)
c->reg = BD71815_REG_OUT32K;
c->mask = CLK_OUT_EN_MASK;
break;
+ case ROHM_CHIP_TYPE_BD72720:
+ c->reg = BD72720_REG_OUT32K;
+ c->mask = CLK_OUT_EN_MASK;
+ break;
default:
dev_err(&pdev->dev, "Unknown clk chip\n");
return -EINVAL;
@@ -146,6 +151,7 @@ static const struct platform_device_id bd718x7_clk_id[] = {
{ "bd71847-clk", ROHM_CHIP_TYPE_BD71847 },
{ "bd71828-clk", ROHM_CHIP_TYPE_BD71828 },
{ "bd71815-clk", ROHM_CHIP_TYPE_BD71815 },
+ { "bd72720-clk", ROHM_CHIP_TYPE_BD72720 },
{ },
};
MODULE_DEVICE_TABLE(platform, bd718x7_clk_id);
@@ -161,6 +167,6 @@ static struct platform_driver bd71837_clk = {
module_platform_driver(bd71837_clk);
MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
-MODULE_DESCRIPTION("BD718(15/18/28/37/47/50) and chip clk driver");
+MODULE_DESCRIPTION("BD718(15/18/28/37/47/50) and BD72720 chip clk driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:bd718xx-clk");
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index bd185482a7fd..b45fb799e36c 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -600,7 +600,7 @@ config GPIO_RDA
config GPIO_REALTEK_OTTO
tristate "Realtek Otto GPIO support"
- depends on MACH_REALTEK_RTL
+ depends on MACH_REALTEK_RTL || COMPILE_TEST
default MACH_REALTEK_RTL
select GPIO_GENERIC
select GPIOLIB_IRQCHIP
@@ -1193,11 +1193,11 @@ config GPIO_PCA953X
8 bits: max7310, max7315, pca6107, pca9534, pca9538, pca9554,
pca9556, pca9557, pca9574, tca6408, tca9554, xra1202,
- pcal6408, pcal9554b, tca9538
+ pcal6408, pcal9554b, tca9538, tcal6408
16 bits: max7312, max7313, pca9535, pca9539, pca9555, pca9575,
tca6416, pca6416, pcal6416, pcal9535, pcal9555a, max7318,
- tca9539
+ tca9539, tcal6416
18 bits: tca6418
@@ -1317,6 +1317,15 @@ config GPIO_BD71828
This driver can also be built as a module. If so, the module
will be called gpio-bd71828.
+config GPIO_BD72720
+ tristate "ROHM BD72720 and BD73900 PMIC GPIO support"
+ depends on MFD_ROHM_BD71828
+ help
+ Support for GPIO on ROHM BD72720 and BD73900 PMICs. There are two
+ pins which can be configured to GPI or GPO, and three pins which can
+ be configured to GPO on the ROHM PMIC. The pin configuration is done
+ on OTP at manufacturing.
+
config GPIO_BD9571MWV
tristate "ROHM BD9571 GPIO support"
depends on MFD_BD9571MWV
@@ -1994,6 +2003,15 @@ config GPIO_LATCH
Say yes here to enable a driver for GPIO multiplexers based on latches
connected to other GPIOs.
+config GPIO_LINE_MUX
+ tristate "GPIO line mux driver"
+ depends on OF_GPIO
+ select MULTIPLEXER
+ help
+ Say Y here to support the GPIO line mux, which can provide virtual
+ GPIOs backed by a shared real GPIO and a multiplexer in a 1-to-many
+ fashion.
+
config GPIO_MOCKUP
tristate "GPIO Testing Driver (DEPRECATED)"
select IRQ_SIM
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 2421a8fd3733..c05f7d795c43 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -46,6 +46,7 @@ obj-$(CONFIG_GPIO_BCM_KONA) += gpio-bcm-kona.o
obj-$(CONFIG_GPIO_BCM_XGS_IPROC) += gpio-xgs-iproc.o
obj-$(CONFIG_GPIO_BD71815) += gpio-bd71815.o
obj-$(CONFIG_GPIO_BD71828) += gpio-bd71828.o
+obj-$(CONFIG_GPIO_BD72720) += gpio-bd72720.o
obj-$(CONFIG_GPIO_BD9571MWV) += gpio-bd9571mwv.o
obj-$(CONFIG_GPIO_BLZP1600) += gpio-blzp1600.o
obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o
@@ -90,6 +91,7 @@ obj-$(CONFIG_GPIO_IXP4XX) += gpio-ixp4xx.o
obj-$(CONFIG_GPIO_JANZ_TTL) += gpio-janz-ttl.o
obj-$(CONFIG_GPIO_KEMPLD) += gpio-kempld.o
obj-$(CONFIG_GPIO_LATCH) += gpio-latch.o
+obj-$(CONFIG_GPIO_LINE_MUX) += gpio-line-mux.o
obj-$(CONFIG_GPIO_LJCA) += gpio-ljca.o
obj-$(CONFIG_GPIO_LOGICVC) += gpio-logicvc.o
obj-$(CONFIG_GPIO_LOONGSON1) += gpio-loongson1.o
diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c
index 416f265d09d0..a4cd32674a96 100644
--- a/drivers/gpio/gpio-aggregator.c
+++ b/drivers/gpio/gpio-aggregator.c
@@ -1226,7 +1226,7 @@ gpio_aggregator_line_release(struct config_item *item)
kfree(line);
}
-static struct configfs_item_operations gpio_aggregator_line_item_ops = {
+static const struct configfs_item_operations gpio_aggregator_line_item_ops = {
.release = gpio_aggregator_line_release,
};
@@ -1247,7 +1247,7 @@ static void gpio_aggregator_device_release(struct config_item *item)
gpio_aggregator_free(aggr);
}
-static struct configfs_item_operations gpio_aggregator_device_item_ops = {
+static const struct configfs_item_operations gpio_aggregator_device_item_ops = {
.release = gpio_aggregator_device_release,
};
@@ -1292,7 +1292,7 @@ gpio_aggregator_device_make_group(struct config_group *group, const char *name)
return &line->group;
}
-static struct configfs_group_operations gpio_aggregator_device_group_ops = {
+static const struct configfs_group_operations gpio_aggregator_device_group_ops = {
.make_group = gpio_aggregator_device_make_group,
};
@@ -1328,7 +1328,7 @@ gpio_aggregator_make_group(struct config_group *group, const char *name)
return &aggr->group;
}
-static struct configfs_group_operations gpio_aggregator_group_ops = {
+static const struct configfs_group_operations gpio_aggregator_group_ops = {
.make_group = gpio_aggregator_make_group,
};
diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c
index 7622f9e9f54a..4225261f61c8 100644
--- a/drivers/gpio/gpio-aspeed-sgpio.c
+++ b/drivers/gpio/gpio-aspeed-sgpio.c
@@ -19,7 +19,31 @@
#include <linux/spinlock.h>
#include <linux/string.h>
-#define ASPEED_SGPIO_CTRL 0x54
+#define SGPIO_G7_IRQ_STS_BASE 0x40
+#define SGPIO_G7_IRQ_STS_OFFSET(x) (SGPIO_G7_IRQ_STS_BASE + (x) * 0x4)
+#define SGPIO_G7_CTRL_REG_BASE 0x80
+#define SGPIO_G7_CTRL_REG_OFFSET(x) (SGPIO_G7_CTRL_REG_BASE + (x) * 0x4)
+#define SGPIO_G7_OUT_DATA BIT(0)
+#define SGPIO_G7_PARALLEL_OUT_DATA BIT(1)
+#define SGPIO_G7_IRQ_EN BIT(2)
+#define SGPIO_G7_IRQ_TYPE0 BIT(3)
+#define SGPIO_G7_IRQ_TYPE1 BIT(4)
+#define SGPIO_G7_IRQ_TYPE2 BIT(5)
+#define SGPIO_G7_RST_TOLERANCE BIT(6)
+#define SGPIO_G7_INPUT_MASK BIT(9)
+#define SGPIO_G7_HW_BYPASS_EN BIT(10)
+#define SGPIO_G7_HW_IN_SEL BIT(11)
+#define SGPIO_G7_IRQ_STS BIT(12)
+#define SGPIO_G7_IN_DATA BIT(13)
+#define SGPIO_G7_PARALLEL_IN_DATA BIT(14)
+#define SGPIO_G7_SERIAL_OUT_SEL GENMASK(17, 16)
+#define SGPIO_G7_PARALLEL_OUT_SEL GENMASK(19, 18)
+#define SELECT_FROM_CSR 0
+#define SELECT_FROM_PARALLEL_IN 1
+#define SELECT_FROM_SERIAL_IN 2
+
+#define ASPEED_SGPIO_G4_CFG_OFFSET 0x54
+#define ASPEED_SGPIO_G7_CFG_OFFSET 0x0
#define ASPEED_SGPIO_CLK_DIV_MASK GENMASK(31, 16)
#define ASPEED_SGPIO_ENABLE BIT(0)
@@ -27,6 +51,8 @@
struct aspeed_sgpio_pdata {
const u32 pin_mask;
+ const struct aspeed_sgpio_llops *llops;
+ const u32 cfg_offset;
};
struct aspeed_sgpio {
@@ -36,6 +62,7 @@ struct aspeed_sgpio {
raw_spinlock_t lock;
void __iomem *base;
int irq;
+ const struct aspeed_sgpio_pdata *pdata;
};
struct aspeed_sgpio_bank {
@@ -43,7 +70,6 @@ struct aspeed_sgpio_bank {
u16 rdata_reg;
u16 irq_regs;
u16 tolerance_regs;
- const char names[4][3];
};
/*
@@ -59,28 +85,24 @@ static const struct aspeed_sgpio_bank aspeed_sgpio_banks[] = {
.rdata_reg = 0x0070,
.irq_regs = 0x0004,
.tolerance_regs = 0x0018,
- .names = { "A", "B", "C", "D" },
},
{
.val_regs = 0x001C,
.rdata_reg = 0x0074,
.irq_regs = 0x0020,
.tolerance_regs = 0x0034,
- .names = { "E", "F", "G", "H" },
},
{
.val_regs = 0x0038,
.rdata_reg = 0x0078,
.irq_regs = 0x003C,
.tolerance_regs = 0x0050,
- .names = { "I", "J", "K", "L" },
},
{
.val_regs = 0x0090,
.rdata_reg = 0x007C,
.irq_regs = 0x0094,
.tolerance_regs = 0x00A8,
- .names = { "M", "N", "O", "P" },
},
};
@@ -95,6 +117,15 @@ enum aspeed_sgpio_reg {
reg_tolerance,
};
+struct aspeed_sgpio_llops {
+ void (*reg_bit_set)(struct aspeed_sgpio *gpio, unsigned int offset,
+ const enum aspeed_sgpio_reg reg, bool val);
+ bool (*reg_bit_get)(struct aspeed_sgpio *gpio, unsigned int offset,
+ const enum aspeed_sgpio_reg reg);
+ int (*reg_bank_get)(struct aspeed_sgpio *gpio, unsigned int offset,
+ const enum aspeed_sgpio_reg reg);
+};
+
#define GPIO_VAL_VALUE 0x00
#define GPIO_IRQ_ENABLE 0x00
#define GPIO_IRQ_TYPE0 0x04
@@ -102,9 +133,9 @@ enum aspeed_sgpio_reg {
#define GPIO_IRQ_TYPE2 0x0C
#define GPIO_IRQ_STATUS 0x10
-static void __iomem *bank_reg(struct aspeed_sgpio *gpio,
- const struct aspeed_sgpio_bank *bank,
- const enum aspeed_sgpio_reg reg)
+static void __iomem *aspeed_sgpio_g4_bank_reg(struct aspeed_sgpio *gpio,
+ const struct aspeed_sgpio_bank *bank,
+ const enum aspeed_sgpio_reg reg)
{
switch (reg) {
case reg_val:
@@ -129,6 +160,30 @@ static void __iomem *bank_reg(struct aspeed_sgpio *gpio,
}
}
+static u32 aspeed_sgpio_g7_reg_mask(const enum aspeed_sgpio_reg reg)
+{
+ switch (reg) {
+ case reg_val:
+ case reg_rdata:
+ return SGPIO_G7_OUT_DATA;
+ case reg_irq_enable:
+ return SGPIO_G7_IRQ_EN;
+ case reg_irq_type0:
+ return SGPIO_G7_IRQ_TYPE0;
+ case reg_irq_type1:
+ return SGPIO_G7_IRQ_TYPE1;
+ case reg_irq_type2:
+ return SGPIO_G7_IRQ_TYPE2;
+ case reg_irq_status:
+ return SGPIO_G7_IRQ_STS;
+ case reg_tolerance:
+ return SGPIO_G7_RST_TOLERANCE;
+ default:
+ WARN_ON_ONCE(1);
+ return 0;
+ }
+}
+
#define GPIO_BANK(x) ((x) >> 6)
#define GPIO_OFFSET(x) ((x) & GENMASK(5, 0))
#define GPIO_BIT(x) BIT(GPIO_OFFSET(x) >> 1)
@@ -170,14 +225,13 @@ static bool aspeed_sgpio_is_input(unsigned int offset)
static int aspeed_sgpio_get(struct gpio_chip *gc, unsigned int offset)
{
struct aspeed_sgpio *gpio = gpiochip_get_data(gc);
- const struct aspeed_sgpio_bank *bank = to_bank(offset);
enum aspeed_sgpio_reg reg;
int rc = 0;
guard(raw_spinlock_irqsave)(&gpio->lock);
reg = aspeed_sgpio_is_input(offset) ? reg_val : reg_rdata;
- rc = !!(ioread32(bank_reg(gpio, bank, reg)) & GPIO_BIT(offset));
+ rc = gpio->pdata->llops->reg_bit_get(gpio, offset, reg);
return rc;
}
@@ -185,26 +239,11 @@ static int aspeed_sgpio_get(struct gpio_chip *gc, unsigned int offset)
static int sgpio_set_value(struct gpio_chip *gc, unsigned int offset, int val)
{
struct aspeed_sgpio *gpio = gpiochip_get_data(gc);
- const struct aspeed_sgpio_bank *bank = to_bank(offset);
- void __iomem *addr_r, *addr_w;
- u32 reg = 0;
if (aspeed_sgpio_is_input(offset))
return -EINVAL;
- /* Since this is an output, read the cached value from rdata, then
- * update val. */
- addr_r = bank_reg(gpio, bank, reg_rdata);
- addr_w = bank_reg(gpio, bank, reg_val);
-
- reg = ioread32(addr_r);
-
- if (val)
- reg |= GPIO_BIT(offset);
- else
- reg &= ~GPIO_BIT(offset);
-
- iowrite32(reg, addr_w);
+ gpio->pdata->llops->reg_bit_set(gpio, offset, reg_val, val);
return 0;
}
@@ -243,69 +282,34 @@ static int aspeed_sgpio_get_direction(struct gpio_chip *gc, unsigned int offset)
return !!aspeed_sgpio_is_input(offset);
}
-static void irqd_to_aspeed_sgpio_data(struct irq_data *d,
- struct aspeed_sgpio **gpio,
- const struct aspeed_sgpio_bank **bank,
- u32 *bit, int *offset)
-{
- struct aspeed_sgpio *internal;
-
- *offset = irqd_to_hwirq(d);
- internal = irq_data_get_irq_chip_data(d);
- WARN_ON(!internal);
-
- *gpio = internal;
- *bank = to_bank(*offset);
- *bit = GPIO_BIT(*offset);
-}
static void aspeed_sgpio_irq_ack(struct irq_data *d)
{
- const struct aspeed_sgpio_bank *bank;
- struct aspeed_sgpio *gpio;
- void __iomem *status_addr;
- int offset;
- u32 bit;
-
- irqd_to_aspeed_sgpio_data(d, &gpio, &bank, &bit, &offset);
-
- status_addr = bank_reg(gpio, bank, reg_irq_status);
+ struct aspeed_sgpio *gpio = irq_data_get_irq_chip_data(d);
+ int offset = irqd_to_hwirq(d);
guard(raw_spinlock_irqsave)(&gpio->lock);
- iowrite32(bit, status_addr);
+ gpio->pdata->llops->reg_bit_set(gpio, offset, reg_irq_status, 1);
}
static void aspeed_sgpio_irq_set_mask(struct irq_data *d, bool set)
{
- const struct aspeed_sgpio_bank *bank;
- struct aspeed_sgpio *gpio;
- u32 reg, bit;
- void __iomem *addr;
- int offset;
-
- irqd_to_aspeed_sgpio_data(d, &gpio, &bank, &bit, &offset);
- addr = bank_reg(gpio, bank, reg_irq_enable);
+ struct aspeed_sgpio *gpio = irq_data_get_irq_chip_data(d);
+ int offset = irqd_to_hwirq(d);
/* Unmasking the IRQ */
if (set)
- gpiochip_enable_irq(&gpio->chip, irqd_to_hwirq(d));
-
- scoped_guard(raw_spinlock_irqsave, &gpio->lock) {
- reg = ioread32(addr);
- if (set)
- reg |= bit;
- else
- reg &= ~bit;
-
- iowrite32(reg, addr);
+ gpiochip_enable_irq(&gpio->chip, offset);
+ scoped_guard(raw_spinlock_irqsave, &gpio->lock)
+ {
+ gpio->pdata->llops->reg_bit_set(gpio, offset, reg_irq_enable,
+ set);
}
/* Masking the IRQ */
if (!set)
- gpiochip_disable_irq(&gpio->chip, irqd_to_hwirq(d));
-
-
+ gpiochip_disable_irq(&gpio->chip, offset);
}
static void aspeed_sgpio_irq_mask(struct irq_data *d)
@@ -323,30 +327,25 @@ static int aspeed_sgpio_set_type(struct irq_data *d, unsigned int type)
u32 type0 = 0;
u32 type1 = 0;
u32 type2 = 0;
- u32 bit, reg;
- const struct aspeed_sgpio_bank *bank;
irq_flow_handler_t handler;
- struct aspeed_sgpio *gpio;
- void __iomem *addr;
- int offset;
-
- irqd_to_aspeed_sgpio_data(d, &gpio, &bank, &bit, &offset);
+ struct aspeed_sgpio *gpio = irq_data_get_irq_chip_data(d);
+ int offset = irqd_to_hwirq(d);
switch (type & IRQ_TYPE_SENSE_MASK) {
case IRQ_TYPE_EDGE_BOTH:
- type2 |= bit;
+ type2 = 1;
fallthrough;
case IRQ_TYPE_EDGE_RISING:
- type0 |= bit;
+ type0 = 1;
fallthrough;
case IRQ_TYPE_EDGE_FALLING:
handler = handle_edge_irq;
break;
case IRQ_TYPE_LEVEL_HIGH:
- type0 |= bit;
+ type0 = 1;
fallthrough;
case IRQ_TYPE_LEVEL_LOW:
- type1 |= bit;
+ type1 = 1;
handler = handle_level_irq;
break;
default:
@@ -354,20 +353,9 @@ static int aspeed_sgpio_set_type(struct irq_data *d, unsigned int type)
}
scoped_guard(raw_spinlock_irqsave, &gpio->lock) {
- addr = bank_reg(gpio, bank, reg_irq_type0);
- reg = ioread32(addr);
- reg = (reg & ~bit) | type0;
- iowrite32(reg, addr);
-
- addr = bank_reg(gpio, bank, reg_irq_type1);
- reg = ioread32(addr);
- reg = (reg & ~bit) | type1;
- iowrite32(reg, addr);
-
- addr = bank_reg(gpio, bank, reg_irq_type2);
- reg = ioread32(addr);
- reg = (reg & ~bit) | type2;
- iowrite32(reg, addr);
+ gpio->pdata->llops->reg_bit_set(gpio, offset, reg_irq_type0, type0);
+ gpio->pdata->llops->reg_bit_set(gpio, offset, reg_irq_type1, type1);
+ gpio->pdata->llops->reg_bit_set(gpio, offset, reg_irq_type2, type2);
}
irq_set_handler_locked(d, handler);
@@ -380,15 +368,14 @@ static void aspeed_sgpio_irq_handler(struct irq_desc *desc)
struct gpio_chip *gc = irq_desc_get_handler_data(desc);
struct irq_chip *ic = irq_desc_get_chip(desc);
struct aspeed_sgpio *data = gpiochip_get_data(gc);
- unsigned int i, p;
+ unsigned int i, p, banks;
unsigned long reg;
chained_irq_enter(ic, desc);
- for (i = 0; i < ARRAY_SIZE(aspeed_sgpio_banks); i++) {
- const struct aspeed_sgpio_bank *bank = &aspeed_sgpio_banks[i];
-
- reg = ioread32(bank_reg(data, bank, reg_irq_status));
+ banks = DIV_ROUND_UP(gc->ngpio, 64);
+ for (i = 0; i < banks; i++) {
+ reg = data->pdata->llops->reg_bank_get(data, i << 6, reg_irq_status);
for_each_set_bit(p, &reg, 32)
generic_handle_domain_irq(gc->irq.domain, (i * 32 + p) * 2);
@@ -399,12 +386,8 @@ static void aspeed_sgpio_irq_handler(struct irq_desc *desc)
static void aspeed_sgpio_irq_print_chip(struct irq_data *d, struct seq_file *p)
{
- const struct aspeed_sgpio_bank *bank;
- struct aspeed_sgpio *gpio;
- u32 bit;
- int offset;
+ struct aspeed_sgpio *gpio = irq_data_get_irq_chip_data(d);
- irqd_to_aspeed_sgpio_data(d, &gpio, &bank, &bit, &offset);
seq_puts(p, dev_name(gpio->dev));
}
@@ -422,7 +405,6 @@ static int aspeed_sgpio_setup_irqs(struct aspeed_sgpio *gpio,
struct platform_device *pdev)
{
int rc, i;
- const struct aspeed_sgpio_bank *bank;
struct gpio_irq_chip *irq;
rc = platform_get_irq(pdev, 0);
@@ -432,12 +414,11 @@ static int aspeed_sgpio_setup_irqs(struct aspeed_sgpio *gpio,
gpio->irq = rc;
/* Disable IRQ and clear Interrupt status registers for all SGPIO Pins. */
- for (i = 0; i < ARRAY_SIZE(aspeed_sgpio_banks); i++) {
- bank = &aspeed_sgpio_banks[i];
+ for (i = 0; i < gpio->chip.ngpio; i += 2) {
/* disable irq enable bits */
- iowrite32(0x00000000, bank_reg(gpio, bank, reg_irq_enable));
+ gpio->pdata->llops->reg_bit_set(gpio, i, reg_irq_enable, 0);
/* clear status bits */
- iowrite32(0xffffffff, bank_reg(gpio, bank, reg_irq_status));
+ gpio->pdata->llops->reg_bit_set(gpio, i, reg_irq_status, 1);
}
irq = &gpio->chip.irq;
@@ -451,42 +432,91 @@ static int aspeed_sgpio_setup_irqs(struct aspeed_sgpio *gpio,
irq->num_parents = 1;
/* Apply default IRQ settings */
- for (i = 0; i < ARRAY_SIZE(aspeed_sgpio_banks); i++) {
- bank = &aspeed_sgpio_banks[i];
+ for (i = 0; i < gpio->chip.ngpio; i += 2) {
/* set falling or level-low irq */
- iowrite32(0x00000000, bank_reg(gpio, bank, reg_irq_type0));
+ gpio->pdata->llops->reg_bit_set(gpio, i, reg_irq_type0, 0);
/* trigger type is edge */
- iowrite32(0x00000000, bank_reg(gpio, bank, reg_irq_type1));
+ gpio->pdata->llops->reg_bit_set(gpio, i, reg_irq_type1, 0);
/* single edge trigger */
- iowrite32(0x00000000, bank_reg(gpio, bank, reg_irq_type2));
+ gpio->pdata->llops->reg_bit_set(gpio, i, reg_irq_type2, 0);
}
return 0;
}
+static void aspeed_sgpio_g4_reg_bit_set(struct aspeed_sgpio *gpio, unsigned int offset,
+ const enum aspeed_sgpio_reg reg, bool val)
+{
+ const struct aspeed_sgpio_bank *bank = to_bank(offset);
+ void __iomem *addr = aspeed_sgpio_g4_bank_reg(gpio, bank, reg);
+ u32 temp;
+
+ if (reg == reg_val) {
+ /* Since this is an output, read the cached value from rdata, then update val. */
+ addr = aspeed_sgpio_g4_bank_reg(gpio, bank, reg_rdata);
+ temp = ioread32(addr);
+ if (val)
+ temp |= GPIO_BIT(offset);
+ else
+ temp &= ~GPIO_BIT(offset);
+
+ addr = aspeed_sgpio_g4_bank_reg(gpio, bank, reg_val);
+ iowrite32(temp, addr);
+ } else if (reg == reg_irq_status) {
+ if (val)
+ iowrite32(GPIO_BIT(offset), addr);
+ } else {
+ /* When setting other registers, we read from the register itself */
+ temp = ioread32(addr);
+ if (val)
+ temp |= GPIO_BIT(offset);
+ else
+ temp &= ~GPIO_BIT(offset);
+ iowrite32(temp, addr);
+ }
+}
+
+static bool aspeed_sgpio_g4_reg_bit_get(struct aspeed_sgpio *gpio, unsigned int offset,
+ const enum aspeed_sgpio_reg reg)
+{
+ const struct aspeed_sgpio_bank *bank = to_bank(offset);
+ void __iomem *addr = aspeed_sgpio_g4_bank_reg(gpio, bank, reg);
+
+ return !!(ioread32(addr) & GPIO_BIT(offset));
+}
+
+static int aspeed_sgpio_g4_reg_bank_get(struct aspeed_sgpio *gpio, unsigned int offset,
+ const enum aspeed_sgpio_reg reg)
+{
+ const struct aspeed_sgpio_bank *bank = to_bank(offset);
+ void __iomem *addr = aspeed_sgpio_g4_bank_reg(gpio, bank, reg);
+
+ if (reg == reg_irq_status)
+ return ioread32(addr);
+ else
+ return -EOPNOTSUPP;
+}
+
+static const struct aspeed_sgpio_llops aspeed_sgpio_g4_llops = {
+ .reg_bit_set = aspeed_sgpio_g4_reg_bit_set,
+ .reg_bit_get = aspeed_sgpio_g4_reg_bit_get,
+ .reg_bank_get = aspeed_sgpio_g4_reg_bank_get,
+};
+
static const struct aspeed_sgpio_pdata ast2400_sgpio_pdata = {
.pin_mask = GENMASK(9, 6),
+ .llops = &aspeed_sgpio_g4_llops,
+ .cfg_offset = ASPEED_SGPIO_G4_CFG_OFFSET,
};
static int aspeed_sgpio_reset_tolerance(struct gpio_chip *chip,
unsigned int offset, bool enable)
{
struct aspeed_sgpio *gpio = gpiochip_get_data(chip);
- void __iomem *reg;
- u32 val;
-
- reg = bank_reg(gpio, to_bank(offset), reg_tolerance);
guard(raw_spinlock_irqsave)(&gpio->lock);
- val = readl(reg);
-
- if (enable)
- val |= GPIO_BIT(offset);
- else
- val &= ~GPIO_BIT(offset);
-
- writel(val, reg);
+ gpio->pdata->llops->reg_bit_set(gpio, offset, reg_tolerance, enable);
return 0;
}
@@ -505,21 +535,77 @@ static int aspeed_sgpio_set_config(struct gpio_chip *chip, unsigned int offset,
static const struct aspeed_sgpio_pdata ast2600_sgpiom_pdata = {
.pin_mask = GENMASK(10, 6),
+ .llops = &aspeed_sgpio_g4_llops,
+ .cfg_offset = ASPEED_SGPIO_G4_CFG_OFFSET,
+};
+
+static void aspeed_sgpio_g7_reg_bit_set(struct aspeed_sgpio *gpio, unsigned int offset,
+ const enum aspeed_sgpio_reg reg, bool val)
+{
+ u32 mask = aspeed_sgpio_g7_reg_mask(reg);
+ void __iomem *addr = gpio->base + SGPIO_G7_CTRL_REG_OFFSET(offset >> 1);
+ u32 write_val;
+
+ if (mask) {
+ write_val = (ioread32(addr) & ~(mask)) | field_prep(mask, val);
+ iowrite32(write_val, addr);
+ }
+}
+
+static bool aspeed_sgpio_g7_reg_bit_get(struct aspeed_sgpio *gpio, unsigned int offset,
+ const enum aspeed_sgpio_reg reg)
+{
+ u32 mask = aspeed_sgpio_g7_reg_mask(reg);
+ void __iomem *addr;
+
+ addr = gpio->base + SGPIO_G7_CTRL_REG_OFFSET(offset >> 1);
+ if (reg == reg_val)
+ mask = SGPIO_G7_IN_DATA;
+
+ if (mask)
+ return field_get(mask, ioread32(addr));
+ else
+ return 0;
+}
+
+static int aspeed_sgpio_g7_reg_bank_get(struct aspeed_sgpio *gpio, unsigned int offset,
+ const enum aspeed_sgpio_reg reg)
+{
+ void __iomem *addr;
+
+ if (reg == reg_irq_status) {
+ addr = gpio->base + SGPIO_G7_IRQ_STS_OFFSET(offset >> 6);
+ return ioread32(addr);
+ } else {
+ return -EOPNOTSUPP;
+ }
+}
+
+static const struct aspeed_sgpio_llops aspeed_sgpio_g7_llops = {
+ .reg_bit_set = aspeed_sgpio_g7_reg_bit_set,
+ .reg_bit_get = aspeed_sgpio_g7_reg_bit_get,
+ .reg_bank_get = aspeed_sgpio_g7_reg_bank_get,
+};
+
+static const struct aspeed_sgpio_pdata ast2700_sgpiom_pdata = {
+ .pin_mask = GENMASK(11, 6),
+ .llops = &aspeed_sgpio_g7_llops,
+ .cfg_offset = ASPEED_SGPIO_G7_CFG_OFFSET,
};
static const struct of_device_id aspeed_sgpio_of_table[] = {
{ .compatible = "aspeed,ast2400-sgpio", .data = &ast2400_sgpio_pdata, },
{ .compatible = "aspeed,ast2500-sgpio", .data = &ast2400_sgpio_pdata, },
{ .compatible = "aspeed,ast2600-sgpiom", .data = &ast2600_sgpiom_pdata, },
+ { .compatible = "aspeed,ast2700-sgpiom", .data = &ast2700_sgpiom_pdata, },
{}
};
MODULE_DEVICE_TABLE(of, aspeed_sgpio_of_table);
-static int __init aspeed_sgpio_probe(struct platform_device *pdev)
+static int aspeed_sgpio_probe(struct platform_device *pdev)
{
u32 nr_gpios, sgpio_freq, sgpio_clk_div, gpio_cnt_regval, pin_mask;
- const struct aspeed_sgpio_pdata *pdata;
struct aspeed_sgpio *gpio;
unsigned long apb_freq;
int rc;
@@ -534,12 +620,11 @@ static int __init aspeed_sgpio_probe(struct platform_device *pdev)
gpio->dev = &pdev->dev;
- pdata = device_get_match_data(&pdev->dev);
- if (!pdata)
+ gpio->pdata = device_get_match_data(&pdev->dev);
+ if (!gpio->pdata)
return -EINVAL;
- pin_mask = pdata->pin_mask;
-
+ pin_mask = gpio->pdata->pin_mask;
rc = device_property_read_u32(&pdev->dev, "ngpios", &nr_gpios);
if (rc < 0) {
dev_err(&pdev->dev, "Could not read ngpios property\n");
@@ -583,7 +668,7 @@ static int __init aspeed_sgpio_probe(struct platform_device *pdev)
gpio_cnt_regval = ((nr_gpios / 8) << ASPEED_SGPIO_PINS_SHIFT) & pin_mask;
iowrite32(FIELD_PREP(ASPEED_SGPIO_CLK_DIV_MASK, sgpio_clk_div) | gpio_cnt_regval |
- ASPEED_SGPIO_ENABLE, gpio->base + ASPEED_SGPIO_CTRL);
+ ASPEED_SGPIO_ENABLE, gpio->base + gpio->pdata->cfg_offset);
raw_spin_lock_init(&gpio->lock);
@@ -611,11 +696,12 @@ static int __init aspeed_sgpio_probe(struct platform_device *pdev)
}
static struct platform_driver aspeed_sgpio_driver = {
+ .probe = aspeed_sgpio_probe,
.driver = {
.name = KBUILD_MODNAME,
.of_match_table = aspeed_sgpio_of_table,
},
};
-module_platform_driver_probe(aspeed_sgpio_driver, aspeed_sgpio_probe);
+module_platform_driver(aspeed_sgpio_driver);
MODULE_DESCRIPTION("Aspeed Serial GPIO Driver");
diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c
index cbdf781994dc..9115e56a1626 100644
--- a/drivers/gpio/gpio-aspeed.c
+++ b/drivers/gpio/gpio-aspeed.c
@@ -1302,7 +1302,6 @@ MODULE_DEVICE_TABLE(of, aspeed_gpio_of_table);
static int aspeed_gpio_probe(struct platform_device *pdev)
{
- const struct of_device_id *gpio_id;
struct gpio_irq_chip *girq;
struct aspeed_gpio *gpio;
int rc, irq, i, banks, err;
@@ -1320,8 +1319,8 @@ static int aspeed_gpio_probe(struct platform_device *pdev)
raw_spin_lock_init(&gpio->lock);
- gpio_id = of_match_node(aspeed_gpio_of_table, pdev->dev.of_node);
- if (!gpio_id)
+ gpio->config = device_get_match_data(&pdev->dev);
+ if (!gpio->config)
return -EINVAL;
gpio->clk = devm_clk_get_enabled(&pdev->dev, NULL);
@@ -1331,8 +1330,6 @@ static int aspeed_gpio_probe(struct platform_device *pdev)
gpio->clk = NULL;
}
- gpio->config = gpio_id->data;
-
if (!gpio->config->llops->reg_bit_set || !gpio->config->llops->reg_bit_get ||
!gpio->config->llops->reg_bank_get)
return -EINVAL;
diff --git a/drivers/gpio/gpio-bd72720.c b/drivers/gpio/gpio-bd72720.c
new file mode 100644
index 000000000000..6549dbf4c7ad
--- /dev/null
+++ b/drivers/gpio/gpio-bd72720.c
@@ -0,0 +1,281 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Support to GPIOs on ROHM BD72720 and BD79300
+ * Copyright 2025 ROHM Semiconductors.
+ * Author: Matti Vaittinen <mazziesaccount@gmail.com>
+ */
+
+#include <linux/gpio/driver.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/rohm-bd72720.h>
+
+#define BD72720_GPIO_OPEN_DRAIN 0
+#define BD72720_GPIO_CMOS BIT(1)
+#define BD72720_INT_GPIO1_IN_SRC 4
+/*
+ * The BD72720 has several "one time programmable" (OTP) configurations which
+ * can be set at manufacturing phase. A set of these options allow using pins
+ * as GPIO. The OTP configuration can't be read at run-time, so drivers rely on
+ * device-tree to advertise the correct options.
+ *
+ * Both DVS[0,1] pins can be configured to be used for:
+ * - OTP0: regulator RUN state control
+ * - OTP1: GPI
+ * - OTP2: GPO
+ * - OTP3: Power sequencer output
+ * Data-sheet also states that these PINs can always be used for IRQ but the
+ * driver limits this by allowing them to be used for IRQs with OTP1 only.
+ *
+ * Pins GPIO_EXTEN0 (GPIO3), GPIO_EXTEN1 (GPIO4), GPIO_FAULT_B (GPIO5) have OTP
+ * options for a specific (non GPIO) purposes, but also an option to configure
+ * them to be used as a GPO.
+ *
+ * OTP settings can be separately configured for each pin.
+ *
+ * DT properties:
+ * "rohm,pin-dvs0" and "rohm,pin-dvs1" can be set to one of the values:
+ * "dvs-input", "gpi", "gpo".
+ *
+ * "rohm,pin-exten0", "rohm,pin-exten1" and "rohm,pin-fault_b" can be set to:
+ * "gpo"
+ */
+
+enum bd72720_gpio_state {
+ BD72720_PIN_UNKNOWN,
+ BD72720_PIN_GPI,
+ BD72720_PIN_GPO,
+};
+
+enum {
+ BD72720_GPIO1,
+ BD72720_GPIO2,
+ BD72720_GPIO3,
+ BD72720_GPIO4,
+ BD72720_GPIO5,
+ BD72720_GPIO_EPDEN,
+ BD72720_NUM_GPIOS
+};
+
+struct bd72720_gpio {
+ /* chip.parent points the MFD which provides DT node and regmap */
+ struct gpio_chip chip;
+ /* dev points to the platform device for devm and prints */
+ struct device *dev;
+ struct regmap *regmap;
+ int gpio_is_input;
+};
+
+static int bd72720gpi_get(struct bd72720_gpio *bdgpio, unsigned int reg_offset)
+{
+ int ret, val, shift;
+
+ ret = regmap_read(bdgpio->regmap, BD72720_REG_INT_ETC1_SRC, &val);
+ if (ret)
+ return ret;
+
+ shift = BD72720_INT_GPIO1_IN_SRC + reg_offset;
+
+ return (val >> shift) & 1;
+}
+
+static int bd72720gpo_get(struct bd72720_gpio *bdgpio,
+ unsigned int offset)
+{
+ const int regs[] = { BD72720_REG_GPIO1_CTRL, BD72720_REG_GPIO2_CTRL,
+ BD72720_REG_GPIO3_CTRL, BD72720_REG_GPIO4_CTRL,
+ BD72720_REG_GPIO5_CTRL, BD72720_REG_EPDEN_CTRL };
+ int ret, val;
+
+ ret = regmap_read(bdgpio->regmap, regs[offset], &val);
+ if (ret)
+ return ret;
+
+ return val & BD72720_GPIO_HIGH;
+}
+
+static int bd72720gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+ struct bd72720_gpio *bdgpio = gpiochip_get_data(chip);
+
+ if (BIT(offset) & bdgpio->gpio_is_input)
+ return bd72720gpi_get(bdgpio, offset);
+
+ return bd72720gpo_get(bdgpio, offset);
+}
+
+static int bd72720gpo_set(struct gpio_chip *chip, unsigned int offset,
+ int value)
+{
+ struct bd72720_gpio *bdgpio = gpiochip_get_data(chip);
+ const int regs[] = { BD72720_REG_GPIO1_CTRL, BD72720_REG_GPIO2_CTRL,
+ BD72720_REG_GPIO3_CTRL, BD72720_REG_GPIO4_CTRL,
+ BD72720_REG_GPIO5_CTRL, BD72720_REG_EPDEN_CTRL };
+
+ if (BIT(offset) & bdgpio->gpio_is_input) {
+ dev_dbg(bdgpio->dev, "pin %d not output.\n", offset);
+ return -EINVAL;
+ }
+
+ if (value)
+ return regmap_set_bits(bdgpio->regmap, regs[offset],
+ BD72720_GPIO_HIGH);
+
+ return regmap_clear_bits(bdgpio->regmap, regs[offset],
+ BD72720_GPIO_HIGH);
+}
+
+static int bd72720_gpio_set_config(struct gpio_chip *chip, unsigned int offset,
+ unsigned long config)
+{
+ struct bd72720_gpio *bdgpio = gpiochip_get_data(chip);
+ const int regs[] = { BD72720_REG_GPIO1_CTRL, BD72720_REG_GPIO2_CTRL,
+ BD72720_REG_GPIO3_CTRL, BD72720_REG_GPIO4_CTRL,
+ BD72720_REG_GPIO5_CTRL, BD72720_REG_EPDEN_CTRL };
+
+ /*
+ * We can only set the output mode, which makes sense only when output
+ * OTP configuration is used.
+ */
+ if (BIT(offset) & bdgpio->gpio_is_input)
+ return -ENOTSUPP;
+
+ switch (pinconf_to_config_param(config)) {
+ case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+ return regmap_update_bits(bdgpio->regmap,
+ regs[offset],
+ BD72720_GPIO_DRIVE_MASK,
+ BD72720_GPIO_OPEN_DRAIN);
+ case PIN_CONFIG_DRIVE_PUSH_PULL:
+ return regmap_update_bits(bdgpio->regmap,
+ regs[offset],
+ BD72720_GPIO_DRIVE_MASK,
+ BD72720_GPIO_CMOS);
+ default:
+ break;
+ }
+
+ return -ENOTSUPP;
+}
+
+static int bd72720gpo_direction_get(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ struct bd72720_gpio *bdgpio = gpiochip_get_data(chip);
+
+ if (BIT(offset) & bdgpio->gpio_is_input)
+ return GPIO_LINE_DIRECTION_IN;
+
+ return GPIO_LINE_DIRECTION_OUT;
+}
+
+static int bd72720_valid_mask(struct gpio_chip *gc,
+ unsigned long *valid_mask,
+ unsigned int ngpios)
+{
+ static const char * const properties[] = {
+ "rohm,pin-dvs0", "rohm,pin-dvs1", "rohm,pin-exten0",
+ "rohm,pin-exten1", "rohm,pin-fault_b"
+ };
+ struct bd72720_gpio *g = gpiochip_get_data(gc);
+ const char *val;
+ int i, ret;
+
+ *valid_mask = BIT(BD72720_GPIO_EPDEN);
+
+ if (!gc->parent)
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(properties); i++) {
+ ret = fwnode_property_read_string(dev_fwnode(gc->parent),
+ properties[i], &val);
+
+ if (ret) {
+ if (ret == -EINVAL)
+ continue;
+
+ dev_err(g->dev, "pin %d (%s), bad configuration\n", i,
+ properties[i]);
+
+ return ret;
+ }
+
+ if (strcmp(val, "gpi") == 0) {
+ if (i != BD72720_GPIO1 && i != BD72720_GPIO2) {
+ dev_warn(g->dev,
+ "pin %d (%s) does not support INPUT mode",
+ i, properties[i]);
+ continue;
+ }
+
+ *valid_mask |= BIT(i);
+ g->gpio_is_input |= BIT(i);
+ } else if (strcmp(val, "gpo") == 0) {
+ *valid_mask |= BIT(i);
+ }
+ }
+
+ return 0;
+}
+
+/* Template for GPIO chip */
+static const struct gpio_chip bd72720gpo_chip = {
+ .label = "bd72720",
+ .owner = THIS_MODULE,
+ .get = bd72720gpio_get,
+ .get_direction = bd72720gpo_direction_get,
+ .set = bd72720gpo_set,
+ .set_config = bd72720_gpio_set_config,
+ .init_valid_mask = bd72720_valid_mask,
+ .can_sleep = true,
+ .ngpio = BD72720_NUM_GPIOS,
+ .base = -1,
+};
+
+static int gpo_bd72720_probe(struct platform_device *pdev)
+{
+ struct bd72720_gpio *g;
+ struct device *parent, *dev;
+
+ /*
+ * Bind devm lifetime to this platform device => use dev for devm.
+ * also the prints should originate from this device.
+ */
+ dev = &pdev->dev;
+ /* The device-tree and regmap come from MFD => use parent for that */
+ parent = dev->parent;
+
+ g = devm_kzalloc(dev, sizeof(*g), GFP_KERNEL);
+ if (!g)
+ return -ENOMEM;
+
+ g->chip = bd72720gpo_chip;
+ g->dev = dev;
+ g->chip.parent = parent;
+ g->regmap = dev_get_regmap(parent, NULL);
+
+ return devm_gpiochip_add_data(dev, &g->chip, g);
+}
+
+static const struct platform_device_id bd72720_gpio_id[] = {
+ { "bd72720-gpio" },
+ { },
+};
+MODULE_DEVICE_TABLE(platform, bd72720_gpio_id);
+
+static struct platform_driver gpo_bd72720_driver = {
+ .driver = {
+ .name = "bd72720-gpio",
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ },
+ .probe = gpo_bd72720_probe,
+ .id_table = bd72720_gpio_id,
+};
+module_platform_driver(gpo_bd72720_driver);
+
+MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
+MODULE_DESCRIPTION("GPIO interface for BD72720 and BD73900");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-cadence.c b/drivers/gpio/gpio-cadence.c
index b75734ca22dd..d7790fc35c22 100644
--- a/drivers/gpio/gpio-cadence.c
+++ b/drivers/gpio/gpio-cadence.c
@@ -2,6 +2,7 @@
/*
* Copyright 2017-2018 Cadence
+ * Copyright (C) 2025 Axiado Corporation.
*
* Authors:
* Jan Kotas <jank@cadence.com>
@@ -31,10 +32,23 @@
#define CDNS_GPIO_IRQ_VALUE 0x28
#define CDNS_GPIO_IRQ_ANY_EDGE 0x2c
+struct cdns_gpio_quirks {
+ bool skip_init;
+};
+
struct cdns_gpio_chip {
struct gpio_generic_chip gen_gc;
void __iomem *regs;
u32 bypass_orig;
+ const struct cdns_gpio_quirks *quirks;
+};
+
+static const struct cdns_gpio_quirks cdns_default_quirks = {
+ .skip_init = false,
+};
+
+static const struct cdns_gpio_quirks ax3000_gpio_quirks = {
+ .skip_init = true,
};
static int cdns_gpio_request(struct gpio_chip *chip, unsigned int offset)
@@ -84,6 +98,7 @@ static int cdns_gpio_irq_set_type(struct irq_data *d, unsigned int type)
struct cdns_gpio_chip *cgpio = gpiochip_get_data(chip);
u32 int_value;
u32 int_type;
+ u32 int_any;
u32 mask = BIT(d->hwirq);
int ret = 0;
@@ -91,24 +106,35 @@ static int cdns_gpio_irq_set_type(struct irq_data *d, unsigned int type)
int_value = ioread32(cgpio->regs + CDNS_GPIO_IRQ_VALUE) & ~mask;
int_type = ioread32(cgpio->regs + CDNS_GPIO_IRQ_TYPE) & ~mask;
-
/*
- * The GPIO controller doesn't have an ACK register.
- * All interrupt statuses are cleared on a status register read.
- * Don't support edge interrupts for now.
+ * Interrupt polarity and trigger behaviour is configured like this:
+ *
+ * (type, value)
+ * (0, 0) = Falling edge triggered
+ * (0, 1) = Rising edge triggered
+ * (1, 0) = Low level triggered
+ * (1, 1) = High level triggered
*/
+ int_any = ioread32(cgpio->regs + CDNS_GPIO_IRQ_ANY_EDGE) & ~mask;
if (type == IRQ_TYPE_LEVEL_HIGH) {
int_type |= mask;
int_value |= mask;
} else if (type == IRQ_TYPE_LEVEL_LOW) {
int_type |= mask;
+ } else if (type == IRQ_TYPE_EDGE_RISING) {
+ int_value |= mask;
+ } else if (type == IRQ_TYPE_EDGE_FALLING) {
+ /* edge trigger, int_value remains cleared for falling */
+ } else if (type == IRQ_TYPE_EDGE_BOTH) {
+ int_any |= mask;
} else {
return -EINVAL;
}
iowrite32(int_value, cgpio->regs + CDNS_GPIO_IRQ_VALUE);
iowrite32(int_type, cgpio->regs + CDNS_GPIO_IRQ_TYPE);
+ iowrite32(int_any, cgpio->regs + CDNS_GPIO_IRQ_ANY_EDGE);
return ret;
}
@@ -141,6 +167,19 @@ static const struct irq_chip cdns_gpio_irqchip = {
GPIOCHIP_IRQ_RESOURCE_HELPERS,
};
+static const struct of_device_id cdns_of_ids[] = {
+ {
+ .compatible = "axiado,ax3000-gpio",
+ .data = &ax3000_gpio_quirks
+ },
+ {
+ .compatible = "cdns,gpio-r1p02",
+ .data = &cdns_default_quirks
+ },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, cdns_of_ids);
+
static int cdns_gpio_probe(struct platform_device *pdev)
{
struct gpio_generic_chip_config config = { };
@@ -165,6 +204,10 @@ static int cdns_gpio_probe(struct platform_device *pdev)
return -EINVAL;
}
+ cgpio->quirks = device_get_match_data(&pdev->dev);
+ if (!cgpio->quirks)
+ cgpio->quirks = &cdns_default_quirks;
+
/*
* Set all pins as inputs by default, otherwise:
* gpiochip_lock_as_irq:
@@ -173,8 +216,15 @@ static int cdns_gpio_probe(struct platform_device *pdev)
* so it needs to be changed before gpio_generic_chip_init() is called.
*/
dir_prev = ioread32(cgpio->regs + CDNS_GPIO_DIRECTION_MODE);
- iowrite32(GENMASK(num_gpios - 1, 0),
- cgpio->regs + CDNS_GPIO_DIRECTION_MODE);
+
+ /*
+ * The AX3000 platform performs the required configuration at boot time
+ * before Linux boots, so this quirk disables pinmux initialization.
+ */
+ if (!cgpio->quirks->skip_init) {
+ iowrite32(GENMASK(num_gpios - 1, 0),
+ cgpio->regs + CDNS_GPIO_DIRECTION_MODE);
+ }
config.dev = &pdev->dev;
config.sz = 4;
@@ -240,9 +290,11 @@ static int cdns_gpio_probe(struct platform_device *pdev)
/*
* Enable gpio outputs, ignored for input direction
*/
- iowrite32(GENMASK(num_gpios - 1, 0),
- cgpio->regs + CDNS_GPIO_OUTPUT_EN);
- iowrite32(0, cgpio->regs + CDNS_GPIO_BYPASS_MODE);
+ if (!cgpio->quirks->skip_init) {
+ iowrite32(GENMASK(num_gpios - 1, 0),
+ cgpio->regs + CDNS_GPIO_OUTPUT_EN);
+ iowrite32(0, cgpio->regs + CDNS_GPIO_BYPASS_MODE);
+ }
platform_set_drvdata(pdev, cgpio);
return 0;
@@ -260,12 +312,6 @@ static void cdns_gpio_remove(struct platform_device *pdev)
iowrite32(cgpio->bypass_orig, cgpio->regs + CDNS_GPIO_BYPASS_MODE);
}
-static const struct of_device_id cdns_of_ids[] = {
- { .compatible = "cdns,gpio-r1p02" },
- { /* sentinel */ },
-};
-MODULE_DEVICE_TABLE(of, cdns_of_ids);
-
static struct platform_driver cdns_gpio_driver = {
.driver = {
.name = "cdns-gpio",
diff --git a/drivers/gpio/gpio-creg-snps.c b/drivers/gpio/gpio-creg-snps.c
index f8ea961fa1de..157ab90f5ba8 100644
--- a/drivers/gpio/gpio-creg-snps.c
+++ b/drivers/gpio/gpio-creg-snps.c
@@ -134,7 +134,6 @@ static const struct of_device_id creg_gpio_ids[] = {
static int creg_gpio_probe(struct platform_device *pdev)
{
- const struct of_device_id *match;
struct device *dev = &pdev->dev;
struct creg_gpio *hcg;
u32 ngpios;
@@ -148,8 +147,7 @@ static int creg_gpio_probe(struct platform_device *pdev)
if (IS_ERR(hcg->regs))
return PTR_ERR(hcg->regs);
- match = of_match_node(creg_gpio_ids, pdev->dev.of_node);
- hcg->layout = match->data;
+ hcg->layout = device_get_match_data(dev);
if (!hcg->layout)
return -EINVAL;
diff --git a/drivers/gpio/gpio-line-mux.c b/drivers/gpio/gpio-line-mux.c
new file mode 100644
index 000000000000..62548fbd3ca0
--- /dev/null
+++ b/drivers/gpio/gpio-line-mux.c
@@ -0,0 +1,116 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * GPIO line mux which acts as virtual gpiochip and provides a 1-to-many
+ * mapping between virtual GPIOs and a real GPIO + multiplexer.
+ *
+ * Copyright (c) 2025 Jonas Jelonek <jelonek.jonas@gmail.com>
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/gpio/driver.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/mux/consumer.h>
+#include <linux/platform_device.h>
+
+#define MUX_SELECT_DELAY_US 100
+
+struct gpio_lmux {
+ struct gpio_chip gc;
+ struct mux_control *mux;
+ struct gpio_desc *muxed_gpio;
+
+ u32 num_gpio_mux_states;
+ unsigned int gpio_mux_states[] __counted_by(num_gpio_mux_states);
+};
+
+static int gpio_lmux_gpio_get(struct gpio_chip *gc, unsigned int offset)
+{
+ struct gpio_lmux *glm = gpiochip_get_data(gc);
+ int ret;
+
+ ret = mux_control_select_delay(glm->mux, glm->gpio_mux_states[offset],
+ MUX_SELECT_DELAY_US);
+ if (ret < 0)
+ return ret;
+
+ ret = gpiod_get_raw_value_cansleep(glm->muxed_gpio);
+ mux_control_deselect(glm->mux);
+ return ret;
+}
+
+static int gpio_lmux_gpio_get_direction(struct gpio_chip *gc,
+ unsigned int offset)
+{
+ return GPIO_LINE_DIRECTION_IN;
+}
+
+static int gpio_lmux_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct gpio_lmux *glm;
+ unsigned int ngpio;
+ size_t size;
+ int ret;
+
+ ngpio = device_property_count_u32(dev, "gpio-line-mux-states");
+ if (!ngpio)
+ return -EINVAL;
+
+ size = struct_size(glm, gpio_mux_states, ngpio);
+ glm = devm_kzalloc(dev, size, GFP_KERNEL);
+ if (!glm)
+ return -ENOMEM;
+
+ glm->gc.base = -1;
+ glm->gc.can_sleep = true;
+ glm->gc.fwnode = dev_fwnode(dev);
+ glm->gc.label = dev_name(dev);
+ glm->gc.ngpio = ngpio;
+ glm->gc.owner = THIS_MODULE;
+ glm->gc.parent = dev;
+
+ glm->gc.get = gpio_lmux_gpio_get;
+ glm->gc.get_direction = gpio_lmux_gpio_get_direction;
+
+ glm->mux = devm_mux_control_get(dev, NULL);
+ if (IS_ERR(glm->mux))
+ return dev_err_probe(dev, PTR_ERR(glm->mux),
+ "could not get mux controller\n");
+
+ glm->muxed_gpio = devm_gpiod_get(dev, "muxed", GPIOD_IN);
+ if (IS_ERR(glm->muxed_gpio))
+ return dev_err_probe(dev, PTR_ERR(glm->muxed_gpio),
+ "could not get muxed-gpio\n");
+
+ glm->num_gpio_mux_states = ngpio;
+ ret = device_property_read_u32_array(dev, "gpio-line-mux-states",
+ &glm->gpio_mux_states[0], ngpio);
+ if (ret)
+ return dev_err_probe(dev, ret, "could not get mux states\n");
+
+ ret = devm_gpiochip_add_data(dev, &glm->gc, glm);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to add gpiochip\n");
+
+ return 0;
+}
+
+static const struct of_device_id gpio_lmux_of_match[] = {
+ { .compatible = "gpio-line-mux" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, gpio_lmux_of_match);
+
+static struct platform_driver gpio_lmux_driver = {
+ .driver = {
+ .name = "gpio-line-mux",
+ .of_match_table = gpio_lmux_of_match,
+ },
+ .probe = gpio_lmux_probe,
+};
+module_platform_driver(gpio_lmux_driver);
+
+MODULE_AUTHOR("Jonas Jelonek <jelonek.jonas@gmail.com>");
+MODULE_DESCRIPTION("GPIO line mux driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-max77620.c b/drivers/gpio/gpio-max77620.c
index 02eca400b307..e6c85411c695 100644
--- a/drivers/gpio/gpio-max77620.c
+++ b/drivers/gpio/gpio-max77620.c
@@ -132,6 +132,24 @@ static const struct irq_chip max77620_gpio_irqchip = {
GPIOCHIP_IRQ_RESOURCE_HELPERS,
};
+static int max77620_gpio_get_dir(struct gpio_chip *gc, unsigned int offset)
+{
+ struct max77620_gpio *mgpio = gpiochip_get_data(gc);
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(mgpio->rmap, GPIO_REG_ADDR(offset), &val);
+ if (ret < 0) {
+ dev_err(mgpio->dev, "CNFG_GPIOx read failed: %d\n", ret);
+ return ret;
+ }
+
+ if (val & MAX77620_CNFG_GPIO_DIR_MASK)
+ return GPIO_LINE_DIRECTION_IN;
+ else
+ return GPIO_LINE_DIRECTION_OUT;
+}
+
static int max77620_gpio_dir_input(struct gpio_chip *gc, unsigned int offset)
{
struct max77620_gpio *mgpio = gpiochip_get_data(gc);
@@ -308,6 +326,7 @@ static int max77620_gpio_probe(struct platform_device *pdev)
mgpio->gpio_chip.label = pdev->name;
mgpio->gpio_chip.parent = pdev->dev.parent;
+ mgpio->gpio_chip.get_direction = max77620_gpio_get_dir;
mgpio->gpio_chip.direction_input = max77620_gpio_dir_input;
mgpio->gpio_chip.get = max77620_gpio_get;
mgpio->gpio_chip.direction_output = max77620_gpio_dir_output;
diff --git a/drivers/gpio/gpio-max77759.c b/drivers/gpio/gpio-max77759.c
index 5e48eb03e7b3..3bf9f23d1532 100644
--- a/drivers/gpio/gpio-max77759.c
+++ b/drivers/gpio/gpio-max77759.c
@@ -435,8 +435,6 @@ static int max77759_gpio_probe(struct platform_device *pdev)
int irq;
struct gpio_irq_chip *girq;
int ret;
- unsigned long irq_flags;
- struct irq_data *irqd;
chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
@@ -486,13 +484,9 @@ static int max77759_gpio_probe(struct platform_device *pdev)
return dev_err_probe(&pdev->dev, ret,
"Failed to add GPIO chip\n");
- irq_flags = IRQF_ONESHOT | IRQF_SHARED;
- irqd = irq_get_irq_data(irq);
- if (irqd)
- irq_flags |= irqd_get_trigger_type(irqd);
-
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
- max77759_gpio_irqhandler, irq_flags,
+ max77759_gpio_irqhandler,
+ IRQF_ONESHOT | IRQF_SHARED,
dev_name(&pdev->dev), chip);
if (ret < 0)
return dev_err_probe(&pdev->dev, ret,
diff --git a/drivers/gpio/gpio-menz127.c b/drivers/gpio/gpio-menz127.c
index 52b13c6ae496..f51e7517f551 100644
--- a/drivers/gpio/gpio-menz127.c
+++ b/drivers/gpio/gpio-menz127.c
@@ -223,5 +223,4 @@ module_mcb_driver(men_z127_driver);
MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>");
MODULE_DESCRIPTION("MEN GPIO Controller");
MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("mcb:16z127");
MODULE_IMPORT_NS("MCB");
diff --git a/drivers/gpio/gpio-mmio.c b/drivers/gpio/gpio-mmio.c
index 5daf962b0323..edbcaad57d00 100644
--- a/drivers/gpio/gpio-mmio.c
+++ b/drivers/gpio/gpio-mmio.c
@@ -724,6 +724,7 @@ static const struct of_device_id gpio_mmio_of_match[] = {
{ .compatible = "wd,mbl-gpio" },
{ .compatible = "ni,169445-nand-gpio" },
{ .compatible = "intel,ixp4xx-expansion-bus-mmio-gpio" },
+ { .compatible = "opencores,gpio" },
{ }
};
MODULE_DEVICE_TABLE(of, gpio_mmio_of_match);
diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c
index f93a3dbb2daa..52e96cc5f67b 100644
--- a/drivers/gpio/gpio-pca953x.c
+++ b/drivers/gpio/gpio-pca953x.c
@@ -126,6 +126,9 @@ static const struct i2c_device_id pca953x_id[] = {
{ "tca9539", 16 | PCA953X_TYPE | PCA_INT, },
{ "tca9554", 8 | PCA953X_TYPE | PCA_INT, },
{ "xra1202", 8 | PCA953X_TYPE },
+
+ { "tcal6408", 8 | PCA953X_TYPE | PCA_LATCH_INT, },
+ { "tcal6416", 16 | PCA953X_TYPE | PCA_LATCH_INT, },
{ }
};
MODULE_DEVICE_TABLE(i2c, pca953x_id);
@@ -1469,6 +1472,9 @@ static const struct of_device_id pca953x_dt_ids[] = {
{ .compatible = "ti,tca9538", .data = OF_953X( 8, PCA_INT), },
{ .compatible = "ti,tca9539", .data = OF_953X(16, PCA_INT), },
+ { .compatible = "ti,tcal6408", .data = OF_953X( 8, PCA_LATCH_INT), },
+ { .compatible = "ti,tcal6416", .data = OF_953X(16, PCA_LATCH_INT), },
+
{ .compatible = "onnn,cat9554", .data = OF_953X( 8, PCA_INT), },
{ .compatible = "onnn,pca9654", .data = OF_953X( 8, PCA_INT), },
{ .compatible = "onnn,pca9655", .data = OF_953X(16, PCA_INT), },
diff --git a/drivers/gpio/gpio-pca9570.c b/drivers/gpio/gpio-pca9570.c
index c5a1287079a0..4a368803fb03 100644
--- a/drivers/gpio/gpio-pca9570.c
+++ b/drivers/gpio/gpio-pca9570.c
@@ -9,11 +9,16 @@
* Andrew F. Davis <afd@ti.com>
*/
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/device/devres.h>
+#include <linux/errno.h>
#include <linux/gpio/driver.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/property.h>
+#include <linux/types.h>
#define SLG7XL45106_GPO_REG 0xDB
@@ -94,7 +99,7 @@ static int pca9570_set(struct gpio_chip *chip, unsigned int offset, int value)
u8 buffer;
int ret;
- mutex_lock(&gpio->lock);
+ guard(mutex)(&gpio->lock);
buffer = gpio->out;
if (value)
@@ -104,18 +109,18 @@ static int pca9570_set(struct gpio_chip *chip, unsigned int offset, int value)
ret = pca9570_write(gpio, buffer);
if (ret)
- goto out;
+ return ret;
gpio->out = buffer;
-out:
- mutex_unlock(&gpio->lock);
- return ret;
+ return 0;
}
static int pca9570_probe(struct i2c_client *client)
{
+ struct device *dev = &client->dev;
struct pca9570 *gpio;
+ int ret;
gpio = devm_kzalloc(&client->dev, sizeof(*gpio), GFP_KERNEL);
if (!gpio)
@@ -132,7 +137,9 @@ static int pca9570_probe(struct i2c_client *client)
gpio->chip.ngpio = gpio->chip_data->ngpio;
gpio->chip.can_sleep = true;
- mutex_init(&gpio->lock);
+ ret = devm_mutex_init(dev, &gpio->lock);
+ if (ret)
+ return ret;
/* Read the current output level */
pca9570_read(gpio, &gpio->out);
diff --git a/drivers/gpio/gpio-realtek-otto.c b/drivers/gpio/gpio-realtek-otto.c
index de527f4fc6c2..4cf91528f547 100644
--- a/drivers/gpio/gpio-realtek-otto.c
+++ b/drivers/gpio/gpio-realtek-otto.c
@@ -359,8 +359,7 @@ static int realtek_gpio_probe(struct platform_device *pdev)
{
struct gpio_generic_chip_config config;
struct device *dev = &pdev->dev;
- unsigned long gen_gc_flags;
- unsigned int dev_flags;
+ unsigned long gen_gc_flags, dev_flags;
struct gpio_irq_chip *girq;
struct realtek_gpio_ctrl *ctrl;
struct resource *res;
@@ -372,7 +371,7 @@ static int realtek_gpio_probe(struct platform_device *pdev)
if (!ctrl)
return -ENOMEM;
- dev_flags = (unsigned int) device_get_match_data(dev);
+ dev_flags = (uintptr_t)device_get_match_data(dev);
ngpios = REALTEK_GPIO_MAX;
device_property_read_u32(dev, "ngpios", &ngpios);
diff --git a/drivers/gpio/gpio-sim.c b/drivers/gpio/gpio-sim.c
index a83f5238427c..437b4500f56b 100644
--- a/drivers/gpio/gpio-sim.c
+++ b/drivers/gpio/gpio-sim.c
@@ -1384,7 +1384,7 @@ static void gpio_sim_hog_config_item_release(struct config_item *item)
kfree(hog);
}
-static struct configfs_item_operations gpio_sim_hog_config_item_ops = {
+static const struct configfs_item_operations gpio_sim_hog_config_item_ops = {
.release = gpio_sim_hog_config_item_release,
};
@@ -1433,11 +1433,11 @@ static void gpio_sim_line_config_group_release(struct config_item *item)
kfree(line);
}
-static struct configfs_item_operations gpio_sim_line_config_item_ops = {
+static const struct configfs_item_operations gpio_sim_line_config_item_ops = {
.release = gpio_sim_line_config_group_release,
};
-static struct configfs_group_operations gpio_sim_line_config_group_ops = {
+static const struct configfs_group_operations gpio_sim_line_config_group_ops = {
.make_item = gpio_sim_line_config_make_hog_item,
};
@@ -1494,11 +1494,11 @@ static void gpio_sim_bank_config_group_release(struct config_item *item)
kfree(bank);
}
-static struct configfs_item_operations gpio_sim_bank_config_item_ops = {
+static const struct configfs_item_operations gpio_sim_bank_config_item_ops = {
.release = gpio_sim_bank_config_group_release,
};
-static struct configfs_group_operations gpio_sim_bank_config_group_ops = {
+static const struct configfs_group_operations gpio_sim_bank_config_group_ops = {
.make_group = gpio_sim_bank_config_make_line_group,
};
@@ -1549,11 +1549,11 @@ static void gpio_sim_device_config_group_release(struct config_item *item)
kfree(dev);
}
-static struct configfs_item_operations gpio_sim_device_config_item_ops = {
+static const struct configfs_item_operations gpio_sim_device_config_item_ops = {
.release = gpio_sim_device_config_group_release,
};
-static struct configfs_group_operations gpio_sim_device_config_group_ops = {
+static const struct configfs_group_operations gpio_sim_device_config_group_ops = {
.make_group = gpio_sim_device_config_make_bank_group,
};
@@ -1589,7 +1589,7 @@ gpio_sim_config_make_device_group(struct config_group *group, const char *name)
return &no_free_ptr(dev)->group;
}
-static struct configfs_group_operations gpio_sim_config_group_ops = {
+static const struct configfs_group_operations gpio_sim_config_group_ops = {
.make_group = gpio_sim_config_make_device_group,
};
diff --git a/drivers/gpio/gpio-spacemit-k1.c b/drivers/gpio/gpio-spacemit-k1.c
index eb66a15c002f..dbd2e81094b9 100644
--- a/drivers/gpio/gpio-spacemit-k1.c
+++ b/drivers/gpio/gpio-spacemit-k1.c
@@ -15,29 +15,37 @@
#include <linux/platform_device.h>
#include <linux/seq_file.h>
-/* register offset */
-#define SPACEMIT_GPLR 0x00 /* port level - R */
-#define SPACEMIT_GPDR 0x0c /* port direction - R/W */
-#define SPACEMIT_GPSR 0x18 /* port set - W */
-#define SPACEMIT_GPCR 0x24 /* port clear - W */
-#define SPACEMIT_GRER 0x30 /* port rising edge R/W */
-#define SPACEMIT_GFER 0x3c /* port falling edge R/W */
-#define SPACEMIT_GEDR 0x48 /* edge detect status - R/W1C */
-#define SPACEMIT_GSDR 0x54 /* (set) direction - W */
-#define SPACEMIT_GCDR 0x60 /* (clear) direction - W */
-#define SPACEMIT_GSRER 0x6c /* (set) rising edge detect enable - W */
-#define SPACEMIT_GCRER 0x78 /* (clear) rising edge detect enable - W */
-#define SPACEMIT_GSFER 0x84 /* (set) falling edge detect enable - W */
-#define SPACEMIT_GCFER 0x90 /* (clear) falling edge detect enable - W */
-#define SPACEMIT_GAPMASK 0x9c /* interrupt mask , 0 disable, 1 enable - R/W */
-
#define SPACEMIT_NR_BANKS 4
#define SPACEMIT_NR_GPIOS_PER_BANK 32
#define to_spacemit_gpio_bank(x) container_of((x), struct spacemit_gpio_bank, gc)
+#define to_spacemit_gpio_regs(gb) ((gb)->sg->data->offsets)
+
+enum spacemit_gpio_registers {
+ SPACEMIT_GPLR, /* port level - R */
+ SPACEMIT_GPDR, /* port direction - R/W */
+ SPACEMIT_GPSR, /* port set - W */
+ SPACEMIT_GPCR, /* port clear - W */
+ SPACEMIT_GRER, /* port rising edge R/W */
+ SPACEMIT_GFER, /* port falling edge R/W */
+ SPACEMIT_GEDR, /* edge detect status - R/W1C */
+ SPACEMIT_GSDR, /* (set) direction - W */
+ SPACEMIT_GCDR, /* (clear) direction - W */
+ SPACEMIT_GSRER, /* (set) rising edge detect enable - W */
+ SPACEMIT_GCRER, /* (clear) rising edge detect enable - W */
+ SPACEMIT_GSFER, /* (set) falling edge detect enable - W */
+ SPACEMIT_GCFER, /* (clear) falling edge detect enable - W */
+ SPACEMIT_GAPMASK, /* interrupt mask , 0 disable, 1 enable - R/W */
+ SPACEMIT_GCPMASK, /* interrupt mask for K3 */
+};
struct spacemit_gpio;
+struct spacemit_gpio_data {
+ const unsigned int *offsets;
+ u32 bank_offsets[SPACEMIT_NR_BANKS];
+};
+
struct spacemit_gpio_bank {
struct gpio_generic_chip chip;
struct spacemit_gpio *sg;
@@ -49,9 +57,22 @@ struct spacemit_gpio_bank {
struct spacemit_gpio {
struct device *dev;
+ const struct spacemit_gpio_data *data;
struct spacemit_gpio_bank sgb[SPACEMIT_NR_BANKS];
};
+static u32 spacemit_gpio_read(struct spacemit_gpio_bank *gb,
+ enum spacemit_gpio_registers reg)
+{
+ return readl(gb->base + to_spacemit_gpio_regs(gb)[reg]);
+}
+
+static void spacemit_gpio_write(struct spacemit_gpio_bank *gb,
+ enum spacemit_gpio_registers reg, u32 val)
+{
+ writel(val, gb->base + to_spacemit_gpio_regs(gb)[reg]);
+}
+
static u32 spacemit_gpio_bank_index(struct spacemit_gpio_bank *gb)
{
return (u32)(gb - gb->sg->sgb);
@@ -63,10 +84,10 @@ static irqreturn_t spacemit_gpio_irq_handler(int irq, void *dev_id)
unsigned long pending;
u32 n, gedr;
- gedr = readl(gb->base + SPACEMIT_GEDR);
+ gedr = spacemit_gpio_read(gb, SPACEMIT_GEDR);
if (!gedr)
return IRQ_NONE;
- writel(gedr, gb->base + SPACEMIT_GEDR);
+ spacemit_gpio_write(gb, SPACEMIT_GEDR, gedr);
pending = gedr & gb->irq_mask;
if (!pending)
@@ -82,7 +103,7 @@ static void spacemit_gpio_irq_ack(struct irq_data *d)
{
struct spacemit_gpio_bank *gb = irq_data_get_irq_chip_data(d);
- writel(BIT(irqd_to_hwirq(d)), gb->base + SPACEMIT_GEDR);
+ spacemit_gpio_write(gb, SPACEMIT_GEDR, BIT(irqd_to_hwirq(d)));
}
static void spacemit_gpio_irq_mask(struct irq_data *d)
@@ -91,13 +112,13 @@ static void spacemit_gpio_irq_mask(struct irq_data *d)
u32 bit = BIT(irqd_to_hwirq(d));
gb->irq_mask &= ~bit;
- writel(gb->irq_mask, gb->base + SPACEMIT_GAPMASK);
+ spacemit_gpio_write(gb, SPACEMIT_GAPMASK, gb->irq_mask);
if (bit & gb->irq_rising_edge)
- writel(bit, gb->base + SPACEMIT_GCRER);
+ spacemit_gpio_write(gb, SPACEMIT_GCRER, bit);
if (bit & gb->irq_falling_edge)
- writel(bit, gb->base + SPACEMIT_GCFER);
+ spacemit_gpio_write(gb, SPACEMIT_GCFER, bit);
}
static void spacemit_gpio_irq_unmask(struct irq_data *d)
@@ -108,12 +129,12 @@ static void spacemit_gpio_irq_unmask(struct irq_data *d)
gb->irq_mask |= bit;
if (bit & gb->irq_rising_edge)
- writel(bit, gb->base + SPACEMIT_GSRER);
+ spacemit_gpio_write(gb, SPACEMIT_GSRER, bit);
if (bit & gb->irq_falling_edge)
- writel(bit, gb->base + SPACEMIT_GSFER);
+ spacemit_gpio_write(gb, SPACEMIT_GSFER, bit);
- writel(gb->irq_mask, gb->base + SPACEMIT_GAPMASK);
+ spacemit_gpio_write(gb, SPACEMIT_GAPMASK, gb->irq_mask);
}
static int spacemit_gpio_irq_set_type(struct irq_data *d, unsigned int type)
@@ -123,18 +144,18 @@ static int spacemit_gpio_irq_set_type(struct irq_data *d, unsigned int type)
if (type & IRQ_TYPE_EDGE_RISING) {
gb->irq_rising_edge |= bit;
- writel(bit, gb->base + SPACEMIT_GSRER);
+ spacemit_gpio_write(gb, SPACEMIT_GSRER, bit);
} else {
gb->irq_rising_edge &= ~bit;
- writel(bit, gb->base + SPACEMIT_GCRER);
+ spacemit_gpio_write(gb, SPACEMIT_GCRER, bit);
}
if (type & IRQ_TYPE_EDGE_FALLING) {
gb->irq_falling_edge |= bit;
- writel(bit, gb->base + SPACEMIT_GSFER);
+ spacemit_gpio_write(gb, SPACEMIT_GSFER, bit);
} else {
gb->irq_falling_edge &= ~bit;
- writel(bit, gb->base + SPACEMIT_GCFER);
+ spacemit_gpio_write(gb, SPACEMIT_GCFER, bit);
}
return 0;
@@ -178,16 +199,16 @@ static int spacemit_gpio_add_bank(struct spacemit_gpio *sg,
struct gpio_chip *gc = &gb->chip.gc;
struct device *dev = sg->dev;
struct gpio_irq_chip *girq;
- void __iomem *dat, *set, *clr, *dirin, *dirout;
- int ret, bank_base[] = { 0x0, 0x4, 0x8, 0x100 };
+ void __iomem *dat, *set, *clr, *dirout;
+ int ret;
- gb->base = regs + bank_base[index];
+ gb->base = regs + sg->data->bank_offsets[index];
+ gb->sg = sg;
- dat = gb->base + SPACEMIT_GPLR;
- set = gb->base + SPACEMIT_GPSR;
- clr = gb->base + SPACEMIT_GPCR;
- dirin = gb->base + SPACEMIT_GCDR;
- dirout = gb->base + SPACEMIT_GSDR;
+ dat = gb->base + to_spacemit_gpio_regs(gb)[SPACEMIT_GPLR];
+ set = gb->base + to_spacemit_gpio_regs(gb)[SPACEMIT_GPSR];
+ clr = gb->base + to_spacemit_gpio_regs(gb)[SPACEMIT_GPCR];
+ dirout = gb->base + to_spacemit_gpio_regs(gb)[SPACEMIT_GPDR];
config = (struct gpio_generic_chip_config) {
.dev = dev,
@@ -196,9 +217,7 @@ static int spacemit_gpio_add_bank(struct spacemit_gpio *sg,
.set = set,
.clr = clr,
.dirout = dirout,
- .dirin = dirin,
- .flags = GPIO_GENERIC_UNREADABLE_REG_SET |
- GPIO_GENERIC_UNREADABLE_REG_DIR,
+ .flags = GPIO_GENERIC_UNREADABLE_REG_SET,
};
/* This registers 32 GPIO lines per bank */
@@ -206,8 +225,6 @@ static int spacemit_gpio_add_bank(struct spacemit_gpio *sg,
if (ret)
return dev_err_probe(dev, ret, "failed to init gpio chip\n");
- gb->sg = sg;
-
gc->label = dev_name(dev);
gc->request = gpiochip_generic_request;
gc->free = gpiochip_generic_free;
@@ -223,13 +240,13 @@ static int spacemit_gpio_add_bank(struct spacemit_gpio *sg,
gpio_irq_chip_set_chip(girq, &spacemit_gpio_chip);
/* Disable Interrupt */
- writel(0, gb->base + SPACEMIT_GAPMASK);
+ spacemit_gpio_write(gb, SPACEMIT_GAPMASK, 0);
/* Disable Edge Detection Settings */
- writel(0x0, gb->base + SPACEMIT_GRER);
- writel(0x0, gb->base + SPACEMIT_GFER);
+ spacemit_gpio_write(gb, SPACEMIT_GRER, 0x0);
+ spacemit_gpio_write(gb, SPACEMIT_GFER, 0x0);
/* Clear Interrupt */
- writel(0xffffffff, gb->base + SPACEMIT_GCRER);
- writel(0xffffffff, gb->base + SPACEMIT_GCFER);
+ spacemit_gpio_write(gb, SPACEMIT_GCRER, 0xffffffff);
+ spacemit_gpio_write(gb, SPACEMIT_GCFER, 0xffffffff);
ret = devm_request_threaded_irq(dev, irq, NULL,
spacemit_gpio_irq_handler,
@@ -260,6 +277,10 @@ static int spacemit_gpio_probe(struct platform_device *pdev)
if (!sg)
return -ENOMEM;
+ sg->data = of_device_get_match_data(dev);
+ if (!sg->data)
+ return dev_err_probe(dev, -EINVAL, "No available compatible data.");
+
regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(regs))
return PTR_ERR(regs);
@@ -287,8 +308,55 @@ static int spacemit_gpio_probe(struct platform_device *pdev)
return 0;
}
+static const unsigned int spacemit_gpio_k1_offsets[] = {
+ [SPACEMIT_GPLR] = 0x00,
+ [SPACEMIT_GPDR] = 0x0c,
+ [SPACEMIT_GPSR] = 0x18,
+ [SPACEMIT_GPCR] = 0x24,
+ [SPACEMIT_GRER] = 0x30,
+ [SPACEMIT_GFER] = 0x3c,
+ [SPACEMIT_GEDR] = 0x48,
+ [SPACEMIT_GSDR] = 0x54,
+ [SPACEMIT_GCDR] = 0x60,
+ [SPACEMIT_GSRER] = 0x6c,
+ [SPACEMIT_GCRER] = 0x78,
+ [SPACEMIT_GSFER] = 0x84,
+ [SPACEMIT_GCFER] = 0x90,
+ [SPACEMIT_GAPMASK] = 0x9c,
+ [SPACEMIT_GCPMASK] = 0xA8,
+};
+
+static const unsigned int spacemit_gpio_k3_offsets[] = {
+ [SPACEMIT_GPLR] = 0x0,
+ [SPACEMIT_GPDR] = 0x4,
+ [SPACEMIT_GPSR] = 0x8,
+ [SPACEMIT_GPCR] = 0xc,
+ [SPACEMIT_GRER] = 0x10,
+ [SPACEMIT_GFER] = 0x14,
+ [SPACEMIT_GEDR] = 0x18,
+ [SPACEMIT_GSDR] = 0x1c,
+ [SPACEMIT_GCDR] = 0x20,
+ [SPACEMIT_GSRER] = 0x24,
+ [SPACEMIT_GCRER] = 0x28,
+ [SPACEMIT_GSFER] = 0x2c,
+ [SPACEMIT_GCFER] = 0x30,
+ [SPACEMIT_GAPMASK] = 0x34,
+ [SPACEMIT_GCPMASK] = 0x38,
+};
+
+static const struct spacemit_gpio_data k1_gpio_data = {
+ .offsets = spacemit_gpio_k1_offsets,
+ .bank_offsets = { 0x0, 0x4, 0x8, 0x100 },
+};
+
+static const struct spacemit_gpio_data k3_gpio_data = {
+ .offsets = spacemit_gpio_k3_offsets,
+ .bank_offsets = { 0x0, 0x40, 0x80, 0x100 },
+};
+
static const struct of_device_id spacemit_gpio_dt_ids[] = {
- { .compatible = "spacemit,k1-gpio" },
+ { .compatible = "spacemit,k1-gpio", .data = &k1_gpio_data },
+ { .compatible = "spacemit,k3-gpio", .data = &k3_gpio_data },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, spacemit_gpio_dt_ids);
@@ -296,12 +364,12 @@ MODULE_DEVICE_TABLE(of, spacemit_gpio_dt_ids);
static struct platform_driver spacemit_gpio_driver = {
.probe = spacemit_gpio_probe,
.driver = {
- .name = "k1-gpio",
+ .name = "spacemit-gpio",
.of_match_table = spacemit_gpio_dt_ids,
},
};
module_platform_driver(spacemit_gpio_driver);
MODULE_AUTHOR("Yixun Lan <dlan@gentoo.org>");
-MODULE_DESCRIPTION("GPIO driver for SpacemiT K1 SoC");
+MODULE_DESCRIPTION("GPIO driver for SpacemiT K1/K3 SoC");
MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c
index b1498b59a921..9c874f07be75 100644
--- a/drivers/gpio/gpio-tegra186.c
+++ b/drivers/gpio/gpio-tegra186.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2016-2025 NVIDIA Corporation
+ * Copyright (c) 2016-2026 NVIDIA Corporation
*
* Author: Thierry Reding <treding@nvidia.com>
* Dipen Patel <dpatel@nvidia.com>
@@ -21,6 +21,7 @@
#include <dt-bindings/gpio/tegra234-gpio.h>
#include <dt-bindings/gpio/tegra241-gpio.h>
#include <dt-bindings/gpio/tegra256-gpio.h>
+#include <dt-bindings/gpio/nvidia,tegra264-gpio.h>
/* security registers */
#define TEGRA186_GPIO_CTL_SCR 0x0c
@@ -1001,7 +1002,9 @@ static int tegra186_gpio_probe(struct platform_device *pdev)
if (gpio->soc->num_irqs_per_bank > 1)
tegra186_gpio_init_route_mapping(gpio);
- np = of_find_matching_node(NULL, tegra186_pmc_of_match);
+ np = of_parse_phandle(pdev->dev.of_node, "wakeup-parent", 0);
+ if (!np)
+ np = of_find_matching_node(NULL, tegra186_pmc_of_match);
if (np) {
if (of_device_is_available(np)) {
irq->parent_domain = irq_find_host(np);
@@ -1277,6 +1280,80 @@ static const struct tegra_gpio_soc tegra241_aon_soc = {
.has_vm_support = false,
};
+#define TEGRA264_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \
+ TEGRA_GPIO_PORT(TEGRA264_MAIN, _name, _bank, _port, _pins)
+
+static const struct tegra_gpio_port tegra264_main_ports[] = {
+ TEGRA264_MAIN_GPIO_PORT(F, 3, 0, 8),
+ TEGRA264_MAIN_GPIO_PORT(G, 3, 1, 5),
+ TEGRA264_MAIN_GPIO_PORT(H, 1, 0, 8),
+ TEGRA264_MAIN_GPIO_PORT(J, 1, 1, 8),
+ TEGRA264_MAIN_GPIO_PORT(K, 1, 2, 8),
+ TEGRA264_MAIN_GPIO_PORT(L, 1, 3, 8),
+ TEGRA264_MAIN_GPIO_PORT(M, 1, 4, 6),
+ TEGRA264_MAIN_GPIO_PORT(P, 2, 0, 8),
+ TEGRA264_MAIN_GPIO_PORT(Q, 2, 1, 8),
+ TEGRA264_MAIN_GPIO_PORT(R, 2, 2, 8),
+ TEGRA264_MAIN_GPIO_PORT(S, 2, 3, 2),
+ TEGRA264_MAIN_GPIO_PORT(T, 0, 0, 7),
+ TEGRA264_MAIN_GPIO_PORT(U, 0, 1, 8),
+ TEGRA264_MAIN_GPIO_PORT(V, 0, 2, 8),
+ TEGRA264_MAIN_GPIO_PORT(W, 0, 3, 8),
+ TEGRA264_MAIN_GPIO_PORT(X, 0, 7, 6),
+ TEGRA264_MAIN_GPIO_PORT(Y, 0, 5, 8),
+ TEGRA264_MAIN_GPIO_PORT(Z, 0, 6, 8),
+ TEGRA264_MAIN_GPIO_PORT(AL, 0, 4, 3),
+};
+
+static const struct tegra_gpio_soc tegra264_main_soc = {
+ .num_ports = ARRAY_SIZE(tegra264_main_ports),
+ .ports = tegra264_main_ports,
+ .name = "tegra264-gpio",
+ .instance = 0,
+ .num_irqs_per_bank = 8,
+ .has_vm_support = true,
+};
+
+#define TEGRA264_AON_GPIO_PORT(_name, _bank, _port, _pins) \
+ TEGRA_GPIO_PORT(TEGRA264_AON, _name, _bank, _port, _pins)
+
+static const struct tegra_gpio_port tegra264_aon_ports[] = {
+ TEGRA264_AON_GPIO_PORT(AA, 0, 0, 8),
+ TEGRA264_AON_GPIO_PORT(BB, 0, 1, 2),
+ TEGRA264_AON_GPIO_PORT(CC, 0, 2, 8),
+ TEGRA264_AON_GPIO_PORT(DD, 0, 3, 8),
+ TEGRA264_AON_GPIO_PORT(EE, 0, 4, 4)
+};
+
+static const struct tegra_gpio_soc tegra264_aon_soc = {
+ .num_ports = ARRAY_SIZE(tegra264_aon_ports),
+ .ports = tegra264_aon_ports,
+ .name = "tegra264-gpio-aon",
+ .instance = 1,
+ .num_irqs_per_bank = 8,
+ .has_vm_support = true,
+};
+
+#define TEGRA264_UPHY_GPIO_PORT(_name, _bank, _port, _pins) \
+ TEGRA_GPIO_PORT(TEGRA264_UPHY, _name, _bank, _port, _pins)
+
+static const struct tegra_gpio_port tegra264_uphy_ports[] = {
+ TEGRA264_UPHY_GPIO_PORT(A, 0, 0, 6),
+ TEGRA264_UPHY_GPIO_PORT(B, 0, 1, 8),
+ TEGRA264_UPHY_GPIO_PORT(C, 0, 2, 3),
+ TEGRA264_UPHY_GPIO_PORT(D, 1, 0, 8),
+ TEGRA264_UPHY_GPIO_PORT(E, 1, 1, 4),
+};
+
+static const struct tegra_gpio_soc tegra264_uphy_soc = {
+ .num_ports = ARRAY_SIZE(tegra264_uphy_ports),
+ .ports = tegra264_uphy_ports,
+ .name = "tegra264-gpio-uphy",
+ .instance = 2,
+ .num_irqs_per_bank = 8,
+ .has_vm_support = true,
+};
+
#define TEGRA256_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \
TEGRA_GPIO_PORT(TEGRA256_MAIN, _name, _bank, _port, _pins)
@@ -1369,6 +1446,15 @@ static const struct of_device_id tegra186_gpio_of_match[] = {
.compatible = "nvidia,tegra256-gpio",
.data = &tegra256_main_soc
}, {
+ .compatible = "nvidia,tegra264-gpio",
+ .data = &tegra264_main_soc
+ }, {
+ .compatible = "nvidia,tegra264-gpio-aon",
+ .data = &tegra264_aon_soc
+ }, {
+ .compatible = "nvidia,tegra264-gpio-uphy",
+ .data = &tegra264_uphy_soc
+ }, {
/* sentinel */
}
};
diff --git a/drivers/gpio/gpio-virtuser.c b/drivers/gpio/gpio-virtuser.c
index 098e67d70ffa..b6c515e7a876 100644
--- a/drivers/gpio/gpio-virtuser.c
+++ b/drivers/gpio/gpio-virtuser.c
@@ -1631,7 +1631,7 @@ static void gpio_virtuser_lookup_config_group_release(struct config_item *item)
kfree(lookup);
}
-static struct configfs_item_operations gpio_virtuser_lookup_config_item_ops = {
+static const struct configfs_item_operations gpio_virtuser_lookup_config_item_ops = {
.release = gpio_virtuser_lookup_config_group_release,
};
@@ -1692,11 +1692,11 @@ static void gpio_virtuser_device_config_group_release(struct config_item *item)
kfree(dev);
}
-static struct configfs_item_operations gpio_virtuser_device_config_item_ops = {
+static const struct configfs_item_operations gpio_virtuser_device_config_item_ops = {
.release = gpio_virtuser_device_config_group_release,
};
-static struct configfs_group_operations gpio_virtuser_device_config_group_ops = {
+static const struct configfs_group_operations gpio_virtuser_device_config_group_ops = {
.make_group = gpio_virtuser_make_lookup_group,
};
@@ -1729,7 +1729,7 @@ gpio_virtuser_config_make_device_group(struct config_group *group,
return &no_free_ptr(dev)->group;
}
-static struct configfs_group_operations gpio_virtuser_config_group_ops = {
+static const struct configfs_group_operations gpio_virtuser_config_group_ops = {
.make_group = gpio_virtuser_config_make_device_group,
};
diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c
index 97780c57ab56..571e366624d2 100644
--- a/drivers/gpio/gpio-zynq.c
+++ b/drivers/gpio/gpio-zynq.c
@@ -903,18 +903,16 @@ static int zynq_gpio_probe(struct platform_device *pdev)
struct zynq_gpio *gpio;
struct gpio_chip *chip;
struct gpio_irq_chip *girq;
- const struct of_device_id *match;
gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
if (!gpio)
return -ENOMEM;
- match = of_match_node(zynq_gpio_of_match, pdev->dev.of_node);
- if (!match) {
- dev_err(&pdev->dev, "of_match_node() failed\n");
- return -EINVAL;
- }
- gpio->p_data = match->data;
+ gpio->p_data = device_get_match_data(&pdev->dev);
+ if (!gpio->p_data)
+ return dev_err_probe(&pdev->dev, -EINVAL,
+ "device_get_match_data() failed\n");
+
platform_set_drvdata(pdev, gpio);
gpio->base_addr = devm_platform_ioremap_resource(pdev, 0);
diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c
index 8657379e9165..ef1ac68b94b7 100644
--- a/drivers/gpio/gpiolib-of.c
+++ b/drivers/gpio/gpiolib-of.c
@@ -634,6 +634,7 @@ static struct gpio_desc *of_find_gpio_rename(struct device_node *np,
return ERR_PTR(-ENOENT);
}
+#if IS_ENABLED(CONFIG_SND_SOC_MT2701_CS42448)
static struct gpio_desc *of_find_mt2701_gpio(struct device_node *np,
const char *con_id,
unsigned int idx,
@@ -665,6 +666,7 @@ static struct gpio_desc *of_find_mt2701_gpio(struct device_node *np,
return desc;
}
+#endif
/*
* Trigger sources are special, they allow us to use any GPIO as a LED trigger
@@ -699,7 +701,9 @@ typedef struct gpio_desc *(*of_find_gpio_quirk)(struct device_node *np,
enum of_gpio_flags *of_flags);
static const of_find_gpio_quirk of_find_gpio_quirks[] = {
of_find_gpio_rename,
+#if IS_ENABLED(CONFIG_SND_SOC_MT2701_CS42448)
of_find_mt2701_gpio,
+#endif
of_find_trigger_gpio,
NULL
};
diff --git a/drivers/gpio/gpiolib-shared.c b/drivers/gpio/gpiolib-shared.c
index 9e6544203439..b3525d1f06a4 100644
--- a/drivers/gpio/gpiolib-shared.c
+++ b/drivers/gpio/gpiolib-shared.c
@@ -455,12 +455,7 @@ int gpio_shared_add_proxy_lookup(struct device *consumer, const char *con_id,
list_for_each_entry(ref, &entry->refs, list) {
guard(mutex)(&ref->lock);
- /*
- * FIXME: use device_is_compatible() once the reset-gpio
- * drivers gains a compatible string which it currently
- * does not have.
- */
- if (!ref->fwnode && strstarts(dev_name(consumer), "reset.gpio.")) {
+ if (!ref->fwnode && device_is_compatible(consumer, "reset-gpio")) {
if (!gpio_shared_dev_is_reset_gpio(consumer, entry, ref))
continue;
} else if (!device_match_fwnode(consumer, ref->fwnode)) {
diff --git a/drivers/gpio/gpiolib-swnode.c b/drivers/gpio/gpiolib-swnode.c
index b44f35d68459..21478b45c127 100644
--- a/drivers/gpio/gpiolib-swnode.c
+++ b/drivers/gpio/gpiolib-swnode.c
@@ -18,19 +18,18 @@
#include <linux/gpio/consumer.h>
#include <linux/gpio/driver.h>
+#include <linux/gpio/property.h>
#include "gpiolib.h"
#include "gpiolib-swnode.h"
-#define GPIOLIB_SWNODE_UNDEFINED_NAME "swnode-gpio-undefined"
-
static struct gpio_device *swnode_get_gpio_device(struct fwnode_handle *fwnode)
{
const struct software_node *gdev_node;
struct gpio_device *gdev;
gdev_node = to_software_node(fwnode);
- if (!gdev_node || !gdev_node->name)
+ if (!gdev_node)
goto fwnode_lookup;
/*
@@ -38,7 +37,7 @@ static struct gpio_device *swnode_get_gpio_device(struct fwnode_handle *fwnode)
* primarily used as a key for internal chip selects in SPI bindings.
*/
if (IS_ENABLED(CONFIG_GPIO_SWNODE_UNDEFINED) &&
- !strcmp(gdev_node->name, GPIOLIB_SWNODE_UNDEFINED_NAME))
+ gdev_node == &swnode_gpio_undefined)
return ERR_PTR(-ENOENT);
fwnode_lookup:
@@ -140,7 +139,7 @@ int swnode_gpio_count(const struct fwnode_handle *fwnode, const char *con_id)
* a key for internal chip selects in SPI bindings.
*/
const struct software_node swnode_gpio_undefined = {
- .name = GPIOLIB_SWNODE_UNDEFINED_NAME,
+ .name = "swnode-gpio-undefined",
};
EXPORT_SYMBOL_NS_GPL(swnode_gpio_undefined, "GPIO_SWNODE");
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 1578cf3a8c74..c52200eaaaff 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -5261,27 +5261,21 @@ void gpiod_put_array(struct gpio_descs *descs)
}
EXPORT_SYMBOL_GPL(gpiod_put_array);
-static int gpio_stub_drv_probe(struct device *dev)
-{
- /*
- * The DT node of some GPIO chips have a "compatible" property, but
- * never have a struct device added and probed by a driver to register
- * the GPIO chip with gpiolib. In such cases, fw_devlink=on will cause
- * the consumers of the GPIO chip to get probe deferred forever because
- * they will be waiting for a device associated with the GPIO chip
- * firmware node to get added and bound to a driver.
- *
- * To allow these consumers to probe, we associate the struct
- * gpio_device of the GPIO chip with the firmware node and then simply
- * bind it to this stub driver.
- */
- return 0;
-}
-
+/*
+ * The DT node of some GPIO chips have a "compatible" property, but
+ * never have a struct device added and probed by a driver to register
+ * the GPIO chip with gpiolib. In such cases, fw_devlink=on will cause
+ * the consumers of the GPIO chip to get probe deferred forever because
+ * they will be waiting for a device associated with the GPIO chip
+ * firmware node to get added and bound to a driver.
+ *
+ * To allow these consumers to probe, we associate the struct
+ * gpio_device of the GPIO chip with the firmware node and then simply
+ * bind it to this stub driver.
+ */
static struct device_driver gpio_stub_drv = {
.name = "gpio_stub_drv",
.bus = &gpio_bus_type,
- .probe = gpio_stub_drv_probe,
};
static int __init gpiolib_dev_init(void)
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index aace5766b38a..699f095f831e 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -2217,20 +2217,22 @@ config MFD_ROHM_BD718XX
and emergency shut down as well as 32,768KHz clock output.
config MFD_ROHM_BD71828
- tristate "ROHM BD71828 and BD71815 Power Management IC"
+ tristate "ROHM BD718[15/28/79], BD72720 and BD73900 PMICs"
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 BD71828 and BD71815
- Power Management ICs. BD71828GW and BD71815AGW are single-chip power
- management ICs mainly for battery-powered portable devices.
- The BD71828 integrates 7 buck converters and 7 LDOs. The BD71815
- has 5 bucks, 7 LDOs, and a boost for driving LEDs. Both ICs provide
- also a single-cell linear charger, a Coulomb counter, a real-time
- clock (RTC), GPIOs and a 32.768 kHz clock gate.
+ Select this option to get support for the ROHM BD71815, BD71828,
+ BD71879, BD72720 and BD73900 Power Management ICs (PMICs). These are
+ single-chip Power Management ICs (PMIC), mainly for battery-powered
+ portable devices.
+ The BD71815 has 5 bucks, 7 LDOs, and a boost for driving LEDs.
+ The BD718[28/79] have 7 buck converters and 7 LDOs.
+ The BD72720 and the BD73900 have 10 bucks and 11 LDOs.
+ All ICs provide a single-cell linear charger, a Coulomb counter,
+ a Real-Time Clock (RTC), GPIOs and a 32.768 kHz clock gate.
config MFD_ROHM_BD957XMUF
tristate "ROHM BD9576MUF and BD9573MUF Power Management ICs"
diff --git a/drivers/mfd/rohm-bd71828.c b/drivers/mfd/rohm-bd71828.c
index 84a64c3b9c9f..e54152a03510 100644
--- a/drivers/mfd/rohm-bd71828.c
+++ b/drivers/mfd/rohm-bd71828.c
@@ -1,8 +1,9 @@
// SPDX-License-Identifier: GPL-2.0-only
-//
-// Copyright (C) 2019 ROHM Semiconductors
-//
-// ROHM BD71828/BD71815 PMIC driver
+/*
+ * Copyright (C) 2019 ROHM Semiconductors
+ *
+ * ROHM BD718[15/28/79] and BD72720 PMIC driver
+ */
#include <linux/gpio_keys.h>
#include <linux/i2c.h>
@@ -13,12 +14,29 @@
#include <linux/mfd/core.h>
#include <linux/mfd/rohm-bd71815.h>
#include <linux/mfd/rohm-bd71828.h>
+#include <linux/mfd/rohm-bd72720.h>
#include <linux/mfd/rohm-generic.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/types.h>
+#define BD72720_TYPED_IRQ_REG(_irq, _stat_offset, _mask, _type_offset) \
+ [_irq] = { \
+ .reg_offset = (_stat_offset), \
+ .mask = (_mask), \
+ { \
+ .type_reg_offset = (_type_offset), \
+ .type_reg_mask = BD72720_GPIO_IRQ_TYPE_MASK, \
+ .type_rising_val = BD72720_GPIO_IRQ_TYPE_RISING, \
+ .type_falling_val = BD72720_GPIO_IRQ_TYPE_FALLING, \
+ .type_level_low_val = BD72720_GPIO_IRQ_TYPE_LOW, \
+ .type_level_high_val = BD72720_GPIO_IRQ_TYPE_HIGH, \
+ .types_supported = IRQ_TYPE_EDGE_BOTH | \
+ IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW, \
+ }, \
+ }
+
static struct gpio_keys_button button = {
.code = KEY_POWER,
.gpio = -1,
@@ -43,6 +61,12 @@ static const struct resource bd71828_rtc_irqs[] = {
DEFINE_RES_IRQ_NAMED(BD71828_INT_RTC2, "bd70528-rtc-alm-2"),
};
+static const struct resource bd72720_rtc_irqs[] = {
+ DEFINE_RES_IRQ_NAMED(BD72720_INT_RTC0, "bd70528-rtc-alm-0"),
+ DEFINE_RES_IRQ_NAMED(BD72720_INT_RTC1, "bd70528-rtc-alm-1"),
+ DEFINE_RES_IRQ_NAMED(BD72720_INT_RTC2, "bd70528-rtc-alm-2"),
+};
+
static const struct resource bd71815_power_irqs[] = {
DEFINE_RES_IRQ_NAMED(BD71815_INT_DCIN_RMV, "bd71815-dcin-rmv"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_CLPS_OUT, "bd71815-dcin-clps-out"),
@@ -156,56 +180,181 @@ static struct mfd_cell bd71828_mfd_cells[] = {
},
};
-static const struct regmap_range bd71815_volatile_ranges[] = {
+static const struct resource bd72720_power_irqs[] = {
+ DEFINE_RES_IRQ_NAMED(BD72720_INT_VBUS_RMV, "bd72720_int_vbus_rmv"),
+ DEFINE_RES_IRQ_NAMED(BD72720_INT_VBUS_DET, "bd72720_int_vbus_det"),
+ DEFINE_RES_IRQ_NAMED(BD72720_INT_VBUS_MON_RES, "bd72720_int_vbus_mon_res"),
+ DEFINE_RES_IRQ_NAMED(BD72720_INT_VBUS_MON_DET, "bd72720_int_vbus_mon_det"),
+ DEFINE_RES_IRQ_NAMED(BD72720_INT_VSYS_MON_RES, "bd72720_int_vsys_mon_res"),
+ DEFINE_RES_IRQ_NAMED(BD72720_INT_VSYS_MON_DET, "bd72720_int_vsys_mon_det"),
+ DEFINE_RES_IRQ_NAMED(BD72720_INT_VSYS_UV_RES, "bd72720_int_vsys_uv_res"),
+ DEFINE_RES_IRQ_NAMED(BD72720_INT_VSYS_UV_DET, "bd72720_int_vsys_uv_det"),
+ DEFINE_RES_IRQ_NAMED(BD72720_INT_VSYS_LO_RES, "bd72720_int_vsys_lo_res"),
+ DEFINE_RES_IRQ_NAMED(BD72720_INT_VSYS_LO_DET, "bd72720_int_vsys_lo_det"),
+ DEFINE_RES_IRQ_NAMED(BD72720_INT_VSYS_OV_RES, "bd72720_int_vsys_ov_res"),
+ DEFINE_RES_IRQ_NAMED(BD72720_INT_VSYS_OV_DET, "bd72720_int_vsys_ov_det"),
+ DEFINE_RES_IRQ_NAMED(BD72720_INT_BAT_ILIM, "bd72720_int_bat_ilim"),
+ DEFINE_RES_IRQ_NAMED(BD72720_INT_CHG_DONE, "bd72720_int_chg_done"),
+ DEFINE_RES_IRQ_NAMED(BD72720_INT_EXTEMP_TOUT, "bd72720_int_extemp_tout"),
+ DEFINE_RES_IRQ_NAMED(BD72720_INT_CHG_WDT_EXP, "bd72720_int_chg_wdt_exp"),
+ DEFINE_RES_IRQ_NAMED(BD72720_INT_BAT_MNT_OUT, "bd72720_int_bat_mnt_out"),
+ DEFINE_RES_IRQ_NAMED(BD72720_INT_BAT_MNT_IN, "bd72720_int_bat_mnt_in"),
+ DEFINE_RES_IRQ_NAMED(BD72720_INT_CHG_TRNS, "bd72720_int_chg_trns"),
+
+ DEFINE_RES_IRQ_NAMED(BD72720_INT_VBAT_MON_RES, "bd72720_int_vbat_mon_res"),
+ DEFINE_RES_IRQ_NAMED(BD72720_INT_VBAT_MON_DET, "bd72720_int_vbat_mon_det"),
+ DEFINE_RES_IRQ_NAMED(BD72720_INT_VBAT_SHT_RES, "bd72720_int_vbat_sht_res"),
+ DEFINE_RES_IRQ_NAMED(BD72720_INT_VBAT_SHT_DET, "bd72720_int_vbat_sht_det"),
+ DEFINE_RES_IRQ_NAMED(BD72720_INT_VBAT_LO_RES, "bd72720_int_vbat_lo_res"),
+ DEFINE_RES_IRQ_NAMED(BD72720_INT_VBAT_LO_DET, "bd72720_int_vbat_lo_det"),
+ DEFINE_RES_IRQ_NAMED(BD72720_INT_VBAT_OV_RES, "bd72720_int_vbat_ov_res"),
+ DEFINE_RES_IRQ_NAMED(BD72720_INT_VBAT_OV_DET, "bd72720_int_vbat_ov_det"),
+ DEFINE_RES_IRQ_NAMED(BD72720_INT_BAT_RMV, "bd72720_int_bat_rmv"),
+ DEFINE_RES_IRQ_NAMED(BD72720_INT_BAT_DET, "bd72720_int_bat_det"),
+ DEFINE_RES_IRQ_NAMED(BD72720_INT_DBAT_DET, "bd72720_int_dbat_det"),
+ DEFINE_RES_IRQ_NAMED(BD72720_INT_BAT_TEMP_TRNS, "bd72720_int_bat_temp_trns"),
+ DEFINE_RES_IRQ_NAMED(BD72720_INT_LOBTMP_RES, "bd72720_int_lobtmp_res"),
+ DEFINE_RES_IRQ_NAMED(BD72720_INT_LOBTMP_DET, "bd72720_int_lobtmp_det"),
+ DEFINE_RES_IRQ_NAMED(BD72720_INT_OVBTMP_RES, "bd72720_int_ovbtmp_res"),
+ DEFINE_RES_IRQ_NAMED(BD72720_INT_OVBTMP_DET, "bd72720_int_ovbtmp_det"),
+ DEFINE_RES_IRQ_NAMED(BD72720_INT_OCUR1_RES, "bd72720_int_ocur1_res"),
+ DEFINE_RES_IRQ_NAMED(BD72720_INT_OCUR1_DET, "bd72720_int_ocur1_det"),
+ DEFINE_RES_IRQ_NAMED(BD72720_INT_OCUR2_RES, "bd72720_int_ocur2_res"),
+ DEFINE_RES_IRQ_NAMED(BD72720_INT_OCUR2_DET, "bd72720_int_ocur2_det"),
+ DEFINE_RES_IRQ_NAMED(BD72720_INT_OCUR3_RES, "bd72720_int_ocur3_res"),
+ DEFINE_RES_IRQ_NAMED(BD72720_INT_OCUR3_DET, "bd72720_int_ocur3_det"),
+ DEFINE_RES_IRQ_NAMED(BD72720_INT_CC_MON1_DET, "bd72720_int_cc_mon1_det"),
+ DEFINE_RES_IRQ_NAMED(BD72720_INT_CC_MON2_DET, "bd72720_int_cc_mon2_det"),
+ DEFINE_RES_IRQ_NAMED(BD72720_INT_CC_MON3_DET, "bd72720_int_cc_mon3_det"),
+};
+
+static const struct mfd_cell bd72720_mfd_cells[] = {
+ { .name = "bd72720-pmic", },
+ { .name = "bd72720-gpio", },
+ { .name = "bd72720-led", },
+ { .name = "bd72720-clk", },
{
- .range_min = BD71815_REG_SEC,
- .range_max = BD71815_REG_YEAR,
- }, {
- .range_min = BD71815_REG_CONF,
- .range_max = BD71815_REG_BAT_TEMP,
- }, {
- .range_min = BD71815_REG_VM_IBAT_U,
- .range_max = BD71815_REG_CC_CTRL,
- }, {
- .range_min = BD71815_REG_CC_STAT,
- .range_max = BD71815_REG_CC_CURCD_L,
+ .name = "bd72720-power",
+ .resources = bd72720_power_irqs,
+ .num_resources = ARRAY_SIZE(bd72720_power_irqs),
}, {
- .range_min = BD71815_REG_VM_BTMP_MON,
- .range_max = BD71815_REG_VM_BTMP_MON,
+ .name = "bd72720-rtc",
+ .resources = bd72720_rtc_irqs,
+ .num_resources = ARRAY_SIZE(bd72720_rtc_irqs),
}, {
- .range_min = BD71815_REG_INT_STAT,
- .range_max = BD71815_REG_INT_UPDATE,
- }, {
- .range_min = BD71815_REG_VM_VSYS_U,
- .range_max = BD71815_REG_REX_CTRL_1,
- }, {
- .range_min = BD71815_REG_FULL_CCNTD_3,
- .range_max = BD71815_REG_CCNTD_CHG_2,
+ .name = "gpio-keys",
+ .platform_data = &bd71828_powerkey_data,
+ .pdata_size = sizeof(bd71828_powerkey_data),
},
};
+static const struct regmap_range bd71815_volatile_ranges[] = {
+ regmap_reg_range(BD71815_REG_SEC, BD71815_REG_YEAR),
+ regmap_reg_range(BD71815_REG_CONF, BD71815_REG_BAT_TEMP),
+ regmap_reg_range(BD71815_REG_VM_IBAT_U, BD71815_REG_CC_CTRL),
+ regmap_reg_range(BD71815_REG_CC_STAT, BD71815_REG_CC_CURCD_L),
+ regmap_reg_range(BD71815_REG_VM_BTMP_MON, BD71815_REG_VM_BTMP_MON),
+ regmap_reg_range(BD71815_REG_INT_STAT, BD71815_REG_INT_UPDATE),
+ regmap_reg_range(BD71815_REG_VM_VSYS_U, BD71815_REG_REX_CTRL_1),
+ regmap_reg_range(BD71815_REG_FULL_CCNTD_3, BD71815_REG_CCNTD_CHG_2),
+};
+
static const struct regmap_range bd71828_volatile_ranges[] = {
- {
- .range_min = BD71828_REG_PS_CTRL_1,
- .range_max = BD71828_REG_PS_CTRL_1,
- }, {
- .range_min = BD71828_REG_PS_CTRL_3,
- .range_max = BD71828_REG_PS_CTRL_3,
- }, {
- .range_min = BD71828_REG_RTC_SEC,
- .range_max = BD71828_REG_RTC_YEAR,
- }, {
- /*
- * For now make all charger registers volatile because many
- * needs to be and because the charger block is not that
- * performance critical.
- */
- .range_min = BD71828_REG_CHG_STATE,
- .range_max = BD71828_REG_CHG_FULL,
- }, {
- .range_min = BD71828_REG_INT_MAIN,
- .range_max = BD71828_REG_IO_STAT,
- },
+ regmap_reg_range(BD71828_REG_PS_CTRL_1, BD71828_REG_PS_CTRL_1),
+ regmap_reg_range(BD71828_REG_PS_CTRL_3, BD71828_REG_PS_CTRL_3),
+ regmap_reg_range(BD71828_REG_RTC_SEC, BD71828_REG_RTC_YEAR),
+ /*
+ * For now make all charger registers volatile because many
+ * needs to be and because the charger block is not that
+ * performance critical.
+ */
+ regmap_reg_range(BD71828_REG_CHG_STATE, BD71828_REG_CHG_FULL),
+ regmap_reg_range(BD71828_REG_INT_MAIN, BD71828_REG_IO_STAT),
+};
+
+static const struct regmap_range bd72720_volatile_ranges_4b[] = {
+ regmap_reg_range(BD72720_REG_RESETSRC_1, BD72720_REG_RESETSRC_2),
+ regmap_reg_range(BD72720_REG_POWER_STATE, BD72720_REG_POWER_STATE),
+ /* The state indicator bit changes when new state is reached */
+ regmap_reg_range(BD72720_REG_PS_CTRL_1, BD72720_REG_PS_CTRL_1),
+ regmap_reg_range(BD72720_REG_RCVNUM, BD72720_REG_RCVNUM),
+ regmap_reg_range(BD72720_REG_CONF, BD72720_REG_HALL_STAT),
+ regmap_reg_range(BD72720_REG_RTC_SEC, BD72720_REG_RTC_YEAR),
+ regmap_reg_range(BD72720_REG_INT_LVL1_STAT, BD72720_REG_INT_ETC2_SRC),
+};
+
+static const struct regmap_range bd72720_precious_ranges_4b[] = {
+ regmap_reg_range(BD72720_REG_INT_LVL1_STAT, BD72720_REG_INT_ETC2_STAT),
+};
+
+/*
+ * The BD72720 is an odd beast in that it contains two separate sets of
+ * registers, both starting from address 0x0. The twist is that these "pages"
+ * are behind different I2C slave addresses. Most of the registers are behind
+ * a slave address 0x4b, which will be used as the "main" address for this
+ * device.
+ *
+ * Most of the charger related registers are located behind slave address 0x4c.
+ * It is tempting to push the dealing with the charger registers and the extra
+ * 0x4c device in power-supply driver - but perhaps it's better for the sake of
+ * the cleaner re-use to deal with setting up all of the regmaps here.
+ * Furthermore, the LED stuff may need access to both of these devices.
+ *
+ * Instead of providing one of the regmaps to sub-devices in MFD platform data,
+ * we create one more 'wrapper regmap' with custom read/write operations. These
+ * custom accessors will select which of the 'real' regmaps to use, based on
+ * the register address.
+ *
+ * The register addresses are 8-bit, so we add offset 0x100 to the addresses
+ * behind the secondary slave 0x4c. The 'wrapper' regmap can then detect the
+ * correct slave address based on the register address and call regmap_write()
+ * and regmap_read() using correct 'real' regmap. This way the registers of
+ * both of the slaves can be accessed using one 'wrapper' regmap.
+ *
+ * NOTE: The added offsets mean that the defined addresses for slave 0x4c must
+ * be used through the 'wrapper' regmap because the offset must be stripped
+ * from the register addresses. The 0x4b can be accessed both indirectly using
+ * the 'wrapper' regmap, and directly using the 'real' regmap.
+ */
+#define BD72720_SECONDARY_I2C_SLAVE 0x4c
+#define BD72720_SECONDARY_I2C_REG_OFFSET 0x100
+
+struct bd72720_regmaps {
+ struct regmap *map1_4b;
+ struct regmap *map2_4c;
+};
+
+/* Translate the slave 0x4c wrapper register address to a real one */
+#define BD72720_REG_UNWRAP(reg) ((reg) - BD72720_SECONDARY_I2C_REG_OFFSET)
+
+/* Ranges given to 'real' 0x4c regmap must use unwrapped addresses. */
+#define BD72720_UNWRAP_REG_RANGE(startreg, endreg) \
+ regmap_reg_range(BD72720_REG_UNWRAP(startreg), BD72720_REG_UNWRAP(endreg))
+
+static const struct regmap_range bd72720_volatile_ranges_4c[] = {
+ /* Status information */
+ BD72720_UNWRAP_REG_RANGE(BD72720_REG_CHG_STATE, BD72720_REG_CHG_EN),
+ /*
+ * Under certain circumstances, write to some bits may be
+ * ignored
+ */
+ BD72720_UNWRAP_REG_RANGE(BD72720_REG_CHG_CTRL, BD72720_REG_CHG_CTRL),
+ /*
+ * TODO: Ensure this is used to advertise state, not (only?) to
+ * control it.
+ */
+ BD72720_UNWRAP_REG_RANGE(BD72720_REG_VSYS_STATE_STAT, BD72720_REG_VSYS_STATE_STAT),
+ /* Measured data */
+ BD72720_UNWRAP_REG_RANGE(BD72720_REG_VM_VBAT_U, BD72720_REG_VM_VF_L),
+ /* Self clearing bits */
+ BD72720_UNWRAP_REG_RANGE(BD72720_REG_VM_VSYS_SA_MINMAX_CTRL,
+ BD72720_REG_VM_VSYS_SA_MINMAX_CTRL),
+ /* Counters, self clearing bits */
+ BD72720_UNWRAP_REG_RANGE(BD72720_REG_CC_CURCD_U, BD72720_REG_CC_CTRL),
+ /* Self clearing bits */
+ BD72720_UNWRAP_REG_RANGE(BD72720_REG_CC_CCNTD_CTRL, BD72720_REG_CC_CCNTD_CTRL),
+ /* Self clearing bits */
+ BD72720_UNWRAP_REG_RANGE(BD72720_REG_IMPCHK_CTRL, BD72720_REG_IMPCHK_CTRL),
};
static const struct regmap_access_table bd71815_volatile_regs = {
@@ -218,6 +367,21 @@ static const struct regmap_access_table bd71828_volatile_regs = {
.n_yes_ranges = ARRAY_SIZE(bd71828_volatile_ranges),
};
+static const struct regmap_access_table bd72720_volatile_regs_4b = {
+ .yes_ranges = &bd72720_volatile_ranges_4b[0],
+ .n_yes_ranges = ARRAY_SIZE(bd72720_volatile_ranges_4b),
+};
+
+static const struct regmap_access_table bd72720_precious_regs_4b = {
+ .yes_ranges = &bd72720_precious_ranges_4b[0],
+ .n_yes_ranges = ARRAY_SIZE(bd72720_precious_ranges_4b),
+};
+
+static const struct regmap_access_table bd72720_volatile_regs_4c = {
+ .yes_ranges = &bd72720_volatile_ranges_4c[0],
+ .n_yes_ranges = ARRAY_SIZE(bd72720_volatile_ranges_4c),
+};
+
static const struct regmap_config bd71815_regmap = {
.reg_bits = 8,
.val_bits = 8,
@@ -234,10 +398,79 @@ static const struct regmap_config bd71828_regmap = {
.cache_type = REGCACHE_MAPLE,
};
+static int regmap_write_wrapper(void *context, unsigned int reg, unsigned int val)
+{
+ struct bd72720_regmaps *maps = context;
+
+ if (reg < BD72720_SECONDARY_I2C_REG_OFFSET)
+ return regmap_write(maps->map1_4b, reg, val);
+
+ reg = BD72720_REG_UNWRAP(reg);
+
+ return regmap_write(maps->map2_4c, reg, val);
+}
+
+static int regmap_read_wrapper(void *context, unsigned int reg, unsigned int *val)
+{
+ struct bd72720_regmaps *maps = context;
+
+ if (reg < BD72720_SECONDARY_I2C_REG_OFFSET)
+ return regmap_read(maps->map1_4b, reg, val);
+
+ reg = BD72720_REG_UNWRAP(reg);
+
+ return regmap_read(maps->map2_4c, reg, val);
+}
+
+static const struct regmap_config bd72720_wrapper_map_config = {
+ .name = "wrap-map",
+ .reg_bits = 9,
+ .val_bits = 8,
+ .max_register = BD72720_REG_IMPCHK_CTRL,
+ /*
+ * We don't want to duplicate caches. It would be a bit faster to
+ * have the cache in this 'wrapper regmap', and not in the 'real
+ * regmaps' bd72720_regmap_4b and bd72720_regmap_4c below. This would
+ * require all the subdevices to use the wrapper-map in order to be
+ * able to benefit from the cache.
+ * Currently most of the sub-devices use only the same slave-address
+ * as this MFD driver. Now, because we don't add the offset to the
+ * registers belonging to this slave, those devices can use either the
+ * wrapper map, or the bd72720_regmap_4b directly. This means majority
+ * of our sub devices don't need to care which regmap they get using
+ * the dev_get_regmap(). This unifies the code between the BD72720 and
+ * those variants which don't have this 'multiple slave addresses'
+ * -hassle.
+ * So, for a small performance penalty, we simplify the code for the
+ * sub-devices by having the caches in the wrapped regmaps and not here.
+ */
+ .cache_type = REGCACHE_NONE,
+ .reg_write = regmap_write_wrapper,
+ .reg_read = regmap_read_wrapper,
+};
+
+static const struct regmap_config bd72720_regmap_4b = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .volatile_table = &bd72720_volatile_regs_4b,
+ .precious_table = &bd72720_precious_regs_4b,
+ .max_register = BD72720_REG_INT_ETC2_SRC,
+ .cache_type = REGCACHE_MAPLE,
+};
+
+static const struct regmap_config bd72720_regmap_4c = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .volatile_table = &bd72720_volatile_regs_4c,
+ .max_register = BD72720_REG_UNWRAP(BD72720_REG_IMPCHK_CTRL),
+ .cache_type = REGCACHE_MAPLE,
+};
+
/*
* Mapping of main IRQ register bits to sub-IRQ register offsets so that we can
* access corect sub-IRQ registers based on bits that are set in main IRQ
- * register. BD71815 and BD71828 have same sub-register-block offests.
+ * register. BD71815 and BD71828 have same sub-register-block offests, the
+ * BD72720 has a different one.
*/
static unsigned int bit0_offsets[] = {11}; /* RTC IRQ */
@@ -249,6 +482,15 @@ static unsigned int bit5_offsets[] = {3}; /* VSYS IRQ */
static unsigned int bit6_offsets[] = {1, 2}; /* DCIN IRQ */
static unsigned int bit7_offsets[] = {0}; /* BUCK IRQ */
+static unsigned int bd72720_bit0_offsets[] = {0, 1}; /* PS1 and PS2 */
+static unsigned int bd72720_bit1_offsets[] = {2, 3}; /* DVS1 and DVS2 */
+static unsigned int bd72720_bit2_offsets[] = {4}; /* VBUS */
+static unsigned int bd72720_bit3_offsets[] = {5}; /* VSYS */
+static unsigned int bd72720_bit4_offsets[] = {6}; /* CHG */
+static unsigned int bd72720_bit5_offsets[] = {7, 8}; /* BAT1 and BAT2 */
+static unsigned int bd72720_bit6_offsets[] = {9}; /* IBAT */
+static unsigned int bd72720_bit7_offsets[] = {10, 11}; /* ETC1 and ETC2 */
+
static const struct regmap_irq_sub_irq_map bd718xx_sub_irq_offsets[] = {
REGMAP_IRQ_MAIN_REG_OFFSET(bit0_offsets),
REGMAP_IRQ_MAIN_REG_OFFSET(bit1_offsets),
@@ -260,6 +502,17 @@ static const struct regmap_irq_sub_irq_map bd718xx_sub_irq_offsets[] = {
REGMAP_IRQ_MAIN_REG_OFFSET(bit7_offsets),
};
+static const struct regmap_irq_sub_irq_map bd72720_sub_irq_offsets[] = {
+ REGMAP_IRQ_MAIN_REG_OFFSET(bd72720_bit0_offsets),
+ REGMAP_IRQ_MAIN_REG_OFFSET(bd72720_bit1_offsets),
+ REGMAP_IRQ_MAIN_REG_OFFSET(bd72720_bit2_offsets),
+ REGMAP_IRQ_MAIN_REG_OFFSET(bd72720_bit3_offsets),
+ REGMAP_IRQ_MAIN_REG_OFFSET(bd72720_bit4_offsets),
+ REGMAP_IRQ_MAIN_REG_OFFSET(bd72720_bit5_offsets),
+ REGMAP_IRQ_MAIN_REG_OFFSET(bd72720_bit6_offsets),
+ REGMAP_IRQ_MAIN_REG_OFFSET(bd72720_bit7_offsets),
+};
+
static const struct regmap_irq bd71815_irqs[] = {
REGMAP_IRQ_REG(BD71815_INT_BUCK1_OCP, 0, BD71815_INT_BUCK1_OCP_MASK),
REGMAP_IRQ_REG(BD71815_INT_BUCK2_OCP, 0, BD71815_INT_BUCK2_OCP_MASK),
@@ -433,6 +686,117 @@ static const struct regmap_irq bd71828_irqs[] = {
REGMAP_IRQ_REG(BD71828_INT_RTC2, 11, BD71828_INT_RTC2_MASK),
};
+static const struct regmap_irq bd72720_irqs[] = {
+ REGMAP_IRQ_REG(BD72720_INT_LONGPUSH, 0, BD72720_INT_LONGPUSH_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_MIDPUSH, 0, BD72720_INT_MIDPUSH_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_SHORTPUSH, 0, BD72720_INT_SHORTPUSH_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_PUSH, 0, BD72720_INT_PUSH_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_HALL_DET, 0, BD72720_INT_HALL_DET_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_HALL_TGL, 0, BD72720_INT_HALL_TGL_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_WDOG, 0, BD72720_INT_WDOG_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_SWRESET, 0, BD72720_INT_SWRESET_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_SEQ_DONE, 1, BD72720_INT_SEQ_DONE_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_PGFAULT, 1, BD72720_INT_PGFAULT_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_BUCK1_DVS, 2, BD72720_INT_BUCK1_DVS_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_BUCK2_DVS, 2, BD72720_INT_BUCK2_DVS_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_BUCK3_DVS, 2, BD72720_INT_BUCK3_DVS_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_BUCK4_DVS, 2, BD72720_INT_BUCK4_DVS_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_BUCK5_DVS, 2, BD72720_INT_BUCK5_DVS_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_BUCK6_DVS, 2, BD72720_INT_BUCK6_DVS_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_BUCK7_DVS, 2, BD72720_INT_BUCK7_DVS_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_BUCK8_DVS, 2, BD72720_INT_BUCK8_DVS_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_BUCK9_DVS, 3, BD72720_INT_BUCK9_DVS_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_BUCK10_DVS, 3, BD72720_INT_BUCK10_DVS_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_LDO1_DVS, 3, BD72720_INT_LDO1_DVS_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_LDO2_DVS, 3, BD72720_INT_LDO2_DVS_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_LDO3_DVS, 3, BD72720_INT_LDO3_DVS_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_LDO4_DVS, 3, BD72720_INT_LDO4_DVS_MASK),
+
+ REGMAP_IRQ_REG(BD72720_INT_VBUS_RMV, 4, BD72720_INT_VBUS_RMV_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_VBUS_DET, 4, BD72720_INT_VBUS_DET_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_VBUS_MON_RES, 4, BD72720_INT_VBUS_MON_RES_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_VBUS_MON_DET, 4, BD72720_INT_VBUS_MON_DET_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_VSYS_MON_RES, 5, BD72720_INT_VSYS_MON_RES_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_VSYS_MON_DET, 5, BD72720_INT_VSYS_MON_DET_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_VSYS_UV_RES, 5, BD72720_INT_VSYS_UV_RES_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_VSYS_UV_DET, 5, BD72720_INT_VSYS_UV_DET_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_VSYS_LO_RES, 5, BD72720_INT_VSYS_LO_RES_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_VSYS_LO_DET, 5, BD72720_INT_VSYS_LO_DET_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_VSYS_OV_RES, 5, BD72720_INT_VSYS_OV_RES_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_VSYS_OV_DET, 5, BD72720_INT_VSYS_OV_DET_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_BAT_ILIM, 6, BD72720_INT_BAT_ILIM_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_CHG_DONE, 6, BD72720_INT_CHG_DONE_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_EXTEMP_TOUT, 6, BD72720_INT_EXTEMP_TOUT_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_CHG_WDT_EXP, 6, BD72720_INT_CHG_WDT_EXP_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_BAT_MNT_OUT, 6, BD72720_INT_BAT_MNT_OUT_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_BAT_MNT_IN, 6, BD72720_INT_BAT_MNT_IN_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_CHG_TRNS, 6, BD72720_INT_CHG_TRNS_MASK),
+
+ REGMAP_IRQ_REG(BD72720_INT_VBAT_MON_RES, 7, BD72720_INT_VBAT_MON_RES_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_VBAT_MON_DET, 7, BD72720_INT_VBAT_MON_DET_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_VBAT_SHT_RES, 7, BD72720_INT_VBAT_SHT_RES_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_VBAT_SHT_DET, 7, BD72720_INT_VBAT_SHT_DET_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_VBAT_LO_RES, 7, BD72720_INT_VBAT_LO_RES_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_VBAT_LO_DET, 7, BD72720_INT_VBAT_LO_DET_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_VBAT_OV_RES, 7, BD72720_INT_VBAT_OV_RES_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_VBAT_OV_DET, 7, BD72720_INT_VBAT_OV_DET_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_BAT_RMV, 8, BD72720_INT_BAT_RMV_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_BAT_DET, 8, BD72720_INT_BAT_DET_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_DBAT_DET, 8, BD72720_INT_DBAT_DET_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_BAT_TEMP_TRNS, 8, BD72720_INT_BAT_TEMP_TRNS_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_LOBTMP_RES, 8, BD72720_INT_LOBTMP_RES_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_LOBTMP_DET, 8, BD72720_INT_LOBTMP_DET_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_OVBTMP_RES, 8, BD72720_INT_OVBTMP_RES_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_OVBTMP_DET, 8, BD72720_INT_OVBTMP_DET_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_OCUR1_RES, 9, BD72720_INT_OCUR1_RES_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_OCUR1_DET, 9, BD72720_INT_OCUR1_DET_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_OCUR2_RES, 9, BD72720_INT_OCUR2_RES_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_OCUR2_DET, 9, BD72720_INT_OCUR2_DET_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_OCUR3_RES, 9, BD72720_INT_OCUR3_RES_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_OCUR3_DET, 9, BD72720_INT_OCUR3_DET_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_CC_MON1_DET, 10, BD72720_INT_CC_MON1_DET_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_CC_MON2_DET, 10, BD72720_INT_CC_MON2_DET_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_CC_MON3_DET, 10, BD72720_INT_CC_MON3_DET_MASK),
+/*
+ * The GPIO1_IN and GPIO2_IN IRQs are generated from the PMIC's GPIO1 and GPIO2
+ * pins. Eg, they may be wired to other devices which can then use the PMIC as
+ * an interrupt controller. The GPIO1 and GPIO2 can have the IRQ type
+ * specified. All of the types (falling, rising, and both edges as well as low
+ * and high levels) are supported.
+ */
+ BD72720_TYPED_IRQ_REG(BD72720_INT_GPIO1_IN, 10, BD72720_INT_GPIO1_IN_MASK, 0),
+ BD72720_TYPED_IRQ_REG(BD72720_INT_GPIO2_IN, 10, BD72720_INT_GPIO2_IN_MASK, 1),
+ REGMAP_IRQ_REG(BD72720_INT_VF125_RES, 11, BD72720_INT_VF125_RES_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_VF125_DET, 11, BD72720_INT_VF125_DET_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_VF_RES, 11, BD72720_INT_VF_RES_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_VF_DET, 11, BD72720_INT_VF_DET_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_RTC0, 11, BD72720_INT_RTC0_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_RTC1, 11, BD72720_INT_RTC1_MASK),
+ REGMAP_IRQ_REG(BD72720_INT_RTC2, 11, BD72720_INT_RTC2_MASK),
+};
+
+static int bd72720_set_type_config(unsigned int **buf, unsigned int type,
+ const struct regmap_irq *irq_data,
+ int idx, void *irq_drv_data)
+{
+ const struct regmap_irq_type *t = &irq_data->type;
+
+ /*
+ * The regmap IRQ ecpects IRQ_TYPE_EDGE_BOTH to be written to register
+ * as logical OR of the type_falling_val and type_rising_val. This is
+ * not how the BD72720 implements this configuration, hence we need
+ * to handle this specific case separately.
+ */
+ if (type == IRQ_TYPE_EDGE_BOTH) {
+ buf[0][idx] &= ~t->type_reg_mask;
+ buf[0][idx] |= BD72720_GPIO_IRQ_TYPE_BOTH;
+
+ return 0;
+ }
+
+ return regmap_irq_set_type_config_simple(buf, type, irq_data, idx, irq_drv_data);
+}
+
static const struct regmap_irq_chip bd71828_irq_chip = {
.name = "bd71828_irq",
.main_status = BD71828_REG_INT_MAIN,
@@ -465,6 +829,28 @@ static const struct regmap_irq_chip bd71815_irq_chip = {
.irq_reg_stride = 1,
};
+static const unsigned int bd72720_irq_type_base[] = { BD72720_REG_GPIO1_CTRL };
+
+static const struct regmap_irq_chip bd72720_irq_chip = {
+ .name = "bd72720_irq",
+ .main_status = BD72720_REG_INT_LVL1_STAT,
+ .irqs = &bd72720_irqs[0],
+ .num_irqs = ARRAY_SIZE(bd72720_irqs),
+ .status_base = BD72720_REG_INT_PS1_STAT,
+ .unmask_base = BD72720_REG_INT_PS1_EN,
+ .config_base = &bd72720_irq_type_base[0],
+ .num_config_bases = 1,
+ .num_config_regs = 2,
+ .set_type_config = bd72720_set_type_config,
+ .ack_base = BD72720_REG_INT_PS1_STAT,
+ .init_ack_masked = true,
+ .num_regs = 12,
+ .num_main_regs = 1,
+ .sub_reg_offsets = &bd72720_sub_irq_offsets[0],
+ .num_main_status_bits = 8,
+ .irq_reg_stride = 1,
+};
+
static int set_clk_mode(struct device *dev, struct regmap *regmap,
int clkmode_reg)
{
@@ -511,11 +897,39 @@ static void bd71828_remove_poweroff(void *data)
pm_power_off = NULL;
}
+static struct regmap *bd72720_do_regmaps(struct i2c_client *i2c)
+{
+ struct bd72720_regmaps *maps;
+ struct i2c_client *secondary_i2c;
+
+ secondary_i2c = devm_i2c_new_dummy_device(&i2c->dev, i2c->adapter,
+ BD72720_SECONDARY_I2C_SLAVE);
+ if (IS_ERR(secondary_i2c)) {
+ dev_err_probe(&i2c->dev, PTR_ERR(secondary_i2c), "Failed to get secondary I2C\n");
+
+ return ERR_CAST(secondary_i2c);
+ }
+
+ maps = devm_kzalloc(&i2c->dev, sizeof(*maps), GFP_KERNEL);
+ if (!maps)
+ return ERR_PTR(-ENOMEM);
+
+ maps->map1_4b = devm_regmap_init_i2c(i2c, &bd72720_regmap_4b);
+ if (IS_ERR(maps->map1_4b))
+ return maps->map1_4b;
+
+ maps->map2_4c = devm_regmap_init_i2c(secondary_i2c, &bd72720_regmap_4c);
+ if (IS_ERR(maps->map2_4c))
+ return maps->map2_4c;
+
+ return devm_regmap_init(&i2c->dev, NULL, maps, &bd72720_wrapper_map_config);
+}
+
static int bd71828_i2c_probe(struct i2c_client *i2c)
{
struct regmap_irq_chip_data *irq_data;
int ret;
- struct regmap *regmap;
+ struct regmap *regmap = NULL;
const struct regmap_config *regmap_config;
const struct regmap_irq_chip *irqchip;
unsigned int chip_type;
@@ -523,6 +937,7 @@ static int bd71828_i2c_probe(struct i2c_client *i2c)
int cells;
int button_irq;
int clkmode_reg;
+ int main_lvl_mask_reg = 0, main_lvl_val = 0;
if (!i2c->irq) {
dev_err(&i2c->dev, "No IRQ configured\n");
@@ -554,15 +969,34 @@ static int bd71828_i2c_probe(struct i2c_client *i2c)
*/
button_irq = 0;
break;
+ case ROHM_CHIP_TYPE_BD72720:
+ {
+ mfd = bd72720_mfd_cells;
+ cells = ARRAY_SIZE(bd72720_mfd_cells);
+
+ regmap = bd72720_do_regmaps(i2c);
+ if (IS_ERR(regmap))
+ return dev_err_probe(&i2c->dev, PTR_ERR(regmap),
+ "Failed to initialize Regmap\n");
+
+ irqchip = &bd72720_irq_chip;
+ clkmode_reg = BD72720_REG_OUT32K;
+ button_irq = BD72720_INT_SHORTPUSH;
+ main_lvl_mask_reg = BD72720_REG_INT_LVL1_EN;
+ main_lvl_val = BD72720_MASK_LVL1_EN_ALL;
+ break;
+ }
default:
dev_err(&i2c->dev, "Unknown device type");
return -EINVAL;
}
- regmap = devm_regmap_init_i2c(i2c, regmap_config);
- if (IS_ERR(regmap))
- return dev_err_probe(&i2c->dev, PTR_ERR(regmap),
+ if (!regmap) {
+ regmap = devm_regmap_init_i2c(i2c, regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(&i2c->dev, PTR_ERR(regmap),
"Failed to initialize Regmap\n");
+ }
ret = devm_regmap_add_irq_chip(&i2c->dev, regmap, i2c->irq,
IRQF_ONESHOT, 0, irqchip, &irq_data);
@@ -573,6 +1007,20 @@ static int bd71828_i2c_probe(struct i2c_client *i2c)
dev_dbg(&i2c->dev, "Registered %d IRQs for chip\n",
irqchip->num_irqs);
+ /*
+ * On some ICs the main IRQ register has corresponding mask register.
+ * This is not handled by the regmap IRQ. Let's enable all the main
+ * level IRQs here. Further writes to the main level MASK is not
+ * needed because masking is handled by the per IRQ 2.nd level MASK
+ * registers. 2.nd level masks are handled by the regmap IRQ.
+ */
+ if (main_lvl_mask_reg) {
+ ret = regmap_write(regmap, main_lvl_mask_reg, main_lvl_val);
+ if (ret) {
+ return dev_err_probe(&i2c->dev, ret,
+ "Failed to enable main level IRQs\n");
+ }
+ }
if (button_irq) {
ret = regmap_irq_get_virq(irq_data, button_irq);
if (ret < 0)
@@ -614,6 +1062,9 @@ static const struct of_device_id bd71828_of_match[] = {
}, {
.compatible = "rohm,bd71815",
.data = (void *)ROHM_CHIP_TYPE_BD71815,
+ }, {
+ .compatible = "rohm,bd72720",
+ .data = (void *)ROHM_CHIP_TYPE_BD72720,
},
{ },
};
diff --git a/drivers/power/supply/bd71828-power.c b/drivers/power/supply/bd71828-power.c
index f667baedeb77..438e220a9cb7 100644
--- a/drivers/power/supply/bd71828-power.c
+++ b/drivers/power/supply/bd71828-power.c
@@ -5,6 +5,7 @@
#include <linux/kernel.h>
#include <linux/mfd/rohm-bd71815.h>
#include <linux/mfd/rohm-bd71828.h>
+#include <linux/mfd/rohm-bd72720.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
@@ -44,19 +45,21 @@
#define VBAT_LOW_TH 0x00D4
struct pwr_regs {
- u8 vbat_avg;
- u8 ibat;
- u8 ibat_avg;
- u8 btemp_vth;
- u8 chg_state;
- u8 bat_temp;
- u8 dcin_stat;
- u8 dcin_collapse_limit;
- u8 chg_set1;
- u8 chg_en;
- u8 vbat_alm_limit_u;
- u8 conf;
- u8 vdcin;
+ unsigned int vbat_avg;
+ unsigned int ibat;
+ unsigned int ibat_avg;
+ unsigned int btemp_vth;
+ unsigned int chg_state;
+ unsigned int bat_temp;
+ unsigned int dcin_stat;
+ unsigned int dcin_online_mask;
+ unsigned int dcin_collapse_limit;
+ unsigned int chg_set1;
+ unsigned int chg_en;
+ unsigned int vbat_alm_limit_u;
+ unsigned int conf;
+ unsigned int vdcin;
+ unsigned int vdcin_himask;
};
static const struct pwr_regs pwr_regs_bd71828 = {
@@ -67,12 +70,14 @@ static const struct pwr_regs pwr_regs_bd71828 = {
.chg_state = BD71828_REG_CHG_STATE,
.bat_temp = BD71828_REG_BAT_TEMP,
.dcin_stat = BD71828_REG_DCIN_STAT,
+ .dcin_online_mask = BD7182x_MASK_DCIN_DET,
.dcin_collapse_limit = BD71828_REG_DCIN_CLPS,
.chg_set1 = BD71828_REG_CHG_SET1,
.chg_en = BD71828_REG_CHG_EN,
.vbat_alm_limit_u = BD71828_REG_ALM_VBAT_LIMIT_U,
.conf = BD71828_REG_CONF,
.vdcin = BD71828_REG_VDCIN_U,
+ .vdcin_himask = BD7182x_MASK_VDCIN_U,
};
static const struct pwr_regs pwr_regs_bd71815 = {
@@ -85,6 +90,7 @@ static const struct pwr_regs pwr_regs_bd71815 = {
.chg_state = BD71815_REG_CHG_STATE,
.bat_temp = BD71815_REG_BAT_TEMP,
.dcin_stat = BD71815_REG_DCIN_STAT,
+ .dcin_online_mask = BD7182x_MASK_DCIN_DET,
.dcin_collapse_limit = BD71815_REG_DCIN_CLPS,
.chg_set1 = BD71815_REG_CHG_SET1,
.chg_en = BD71815_REG_CHG_SET1,
@@ -92,6 +98,31 @@ static const struct pwr_regs pwr_regs_bd71815 = {
.conf = BD71815_REG_CONF,
.vdcin = BD71815_REG_VM_DCIN_U,
+ .vdcin_himask = BD7182x_MASK_VDCIN_U,
+};
+
+static struct pwr_regs pwr_regs_bd72720 = {
+ .vbat_avg = BD72720_REG_VM_SA_VBAT_U,
+ .ibat = BD72720_REG_CC_CURCD_U,
+ .ibat_avg = BD72720_REG_CC_SA_CURCD_U,
+ .btemp_vth = BD72720_REG_VM_BTMP_U,
+ /*
+ * Note, state 0x40 IMP_CHK. not documented
+ * on other variants but was still handled in
+ * existing code. No memory traces as to why.
+ */
+ .chg_state = BD72720_REG_CHG_STATE,
+ .bat_temp = BD72720_REG_CHG_BAT_TEMP_STAT,
+ .dcin_stat = BD72720_REG_INT_VBUS_SRC,
+ .dcin_online_mask = BD72720_MASK_DCIN_DET,
+ .dcin_collapse_limit = -1, /* Automatic. Setting not supported */
+ .chg_set1 = BD72720_REG_CHG_SET_1,
+ .chg_en = BD72720_REG_CHG_EN,
+ /* 15mV note in data-sheet */
+ .vbat_alm_limit_u = BD72720_REG_ALM_VBAT_TH_U,
+ .conf = BD72720_REG_CONF, /* o XSTB, only PON. Seprate slave addr */
+ .vdcin = BD72720_REG_VM_VBUS_U, /* 10 bits not 11 as with other ICs */
+ .vdcin_himask = BD72720_MASK_VDCIN_U,
};
struct bd71828_power {
@@ -298,7 +329,7 @@ static int get_chg_online(struct bd71828_power *pwr, int *chg_online)
dev_err(pwr->dev, "Failed to read DCIN status\n");
return ret;
}
- *chg_online = ((r & BD7182x_MASK_DCIN_DET) != 0);
+ *chg_online = ((r & pwr->regs->dcin_online_mask) != 0);
return 0;
}
@@ -329,8 +360,8 @@ static int bd71828_bat_inserted(struct bd71828_power *pwr)
ret = val & BD7182x_MASK_CONF_PON;
if (ret)
- regmap_update_bits(pwr->regmap, pwr->regs->conf,
- BD7182x_MASK_CONF_PON, 0);
+ if (regmap_update_bits(pwr->regmap, pwr->regs->conf, BD7182x_MASK_CONF_PON, 0))
+ dev_err(pwr->dev, "Failed to write CONF register\n");
return ret;
}
@@ -358,11 +389,13 @@ static int bd71828_init_hardware(struct bd71828_power *pwr)
int ret;
/* TODO: Collapse limit should come from device-tree ? */
- ret = regmap_write(pwr->regmap, pwr->regs->dcin_collapse_limit,
- BD7182x_DCIN_COLLAPSE_DEFAULT);
- if (ret) {
- dev_err(pwr->dev, "Failed to write DCIN collapse limit\n");
- return ret;
+ if (pwr->regs->dcin_collapse_limit != (unsigned int)-1) {
+ ret = regmap_write(pwr->regmap, pwr->regs->dcin_collapse_limit,
+ BD7182x_DCIN_COLLAPSE_DEFAULT);
+ if (ret) {
+ dev_err(pwr->dev, "Failed to write DCIN collapse limit\n");
+ return ret;
+ }
}
ret = pwr->bat_inserted(pwr);
@@ -419,7 +452,7 @@ static int bd71828_charger_get_property(struct power_supply *psy,
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
ret = bd7182x_read16_himask(pwr, pwr->regs->vdcin,
- BD7182x_MASK_VDCIN_U, &tmp);
+ pwr->regs->vdcin_himask, &tmp);
if (ret)
return ret;
@@ -630,6 +663,9 @@ BD_ISR_AC(dcin_ovp_det, "DCIN OVER VOLTAGE", true)
BD_ISR_DUMMY(dcin_mon_det, "DCIN voltage below threshold")
BD_ISR_DUMMY(dcin_mon_res, "DCIN voltage above threshold")
+BD_ISR_DUMMY(vbus_curr_limit, "VBUS current limited")
+BD_ISR_DUMMY(vsys_ov_res, "VSYS over-voltage cleared")
+BD_ISR_DUMMY(vsys_ov_det, "VSYS over-voltage")
BD_ISR_DUMMY(vsys_uv_res, "VSYS under-voltage cleared")
BD_ISR_DUMMY(vsys_uv_det, "VSYS under-voltage")
BD_ISR_DUMMY(vsys_low_res, "'VSYS low' cleared")
@@ -878,6 +914,51 @@ static int bd7182x_get_irqs(struct platform_device *pdev,
BDIRQ("bd71828-temp-125-over", bd71828_temp_vf125_det),
BDIRQ("bd71828-temp-125-under", bd71828_temp_vf125_res),
};
+ static const struct bd7182x_irq_res bd72720_irqs[] = {
+ BDIRQ("bd72720_int_vbus_rmv", BD_ISR_NAME(dcin_removed)),
+ BDIRQ("bd72720_int_vbus_det", bd7182x_dcin_detected),
+ BDIRQ("bd72720_int_vbus_mon_res", BD_ISR_NAME(dcin_mon_res)),
+ BDIRQ("bd72720_int_vbus_mon_det", BD_ISR_NAME(dcin_mon_det)),
+ BDIRQ("bd72720_int_vsys_mon_res", BD_ISR_NAME(vsys_mon_res)),
+ BDIRQ("bd72720_int_vsys_mon_det", BD_ISR_NAME(vsys_mon_det)),
+ BDIRQ("bd72720_int_vsys_uv_res", BD_ISR_NAME(vsys_uv_res)),
+ BDIRQ("bd72720_int_vsys_uv_det", BD_ISR_NAME(vsys_uv_det)),
+ BDIRQ("bd72720_int_vsys_lo_res", BD_ISR_NAME(vsys_low_res)),
+ BDIRQ("bd72720_int_vsys_lo_det", BD_ISR_NAME(vsys_low_det)),
+ BDIRQ("bd72720_int_vsys_ov_res", BD_ISR_NAME(vsys_ov_res)),
+ BDIRQ("bd72720_int_vsys_ov_det", BD_ISR_NAME(vsys_ov_det)),
+ BDIRQ("bd72720_int_bat_ilim", BD_ISR_NAME(vbus_curr_limit)),
+ BDIRQ("bd72720_int_chg_done", bd718x7_chg_done),
+ BDIRQ("bd72720_int_extemp_tout", BD_ISR_NAME(chg_wdg_temp)),
+ BDIRQ("bd72720_int_chg_wdt_exp", BD_ISR_NAME(chg_wdg)),
+ BDIRQ("bd72720_int_bat_mnt_out", BD_ISR_NAME(rechg_res)),
+ BDIRQ("bd72720_int_bat_mnt_in", BD_ISR_NAME(rechg_det)),
+ BDIRQ("bd72720_int_chg_trns", BD_ISR_NAME(chg_state_changed)),
+
+ BDIRQ("bd72720_int_vbat_mon_res", BD_ISR_NAME(bat_mon_res)),
+ BDIRQ("bd72720_int_vbat_mon_det", BD_ISR_NAME(bat_mon)),
+ BDIRQ("bd72720_int_vbat_sht_res", BD_ISR_NAME(bat_short_res)),
+ BDIRQ("bd72720_int_vbat_sht_det", BD_ISR_NAME(bat_short)),
+ BDIRQ("bd72720_int_vbat_lo_res", BD_ISR_NAME(bat_low_res)),
+ BDIRQ("bd72720_int_vbat_lo_det", BD_ISR_NAME(bat_low)),
+ BDIRQ("bd72720_int_vbat_ov_res", BD_ISR_NAME(bat_ov_res)),
+ BDIRQ("bd72720_int_vbat_ov_det", BD_ISR_NAME(bat_ov)),
+ BDIRQ("bd72720_int_bat_rmv", BD_ISR_NAME(bat_removed)),
+ BDIRQ("bd72720_int_bat_det", BD_ISR_NAME(bat_det)),
+ BDIRQ("bd72720_int_dbat_det", BD_ISR_NAME(bat_dead)),
+ BDIRQ("bd72720_int_bat_temp_trns", BD_ISR_NAME(temp_transit)),
+ BDIRQ("bd72720_int_lobtmp_res", BD_ISR_NAME(temp_bat_low_res)),
+ BDIRQ("bd72720_int_lobtmp_det", BD_ISR_NAME(temp_bat_low)),
+ BDIRQ("bd72720_int_ovbtmp_res", BD_ISR_NAME(temp_bat_hi_res)),
+ BDIRQ("bd72720_int_ovbtmp_det", BD_ISR_NAME(temp_bat_hi)),
+ BDIRQ("bd72720_int_ocur1_res", BD_ISR_NAME(bat_oc1_res)),
+ BDIRQ("bd72720_int_ocur1_det", BD_ISR_NAME(bat_oc1)),
+ BDIRQ("bd72720_int_ocur2_res", BD_ISR_NAME(bat_oc2_res)),
+ BDIRQ("bd72720_int_ocur2_det", BD_ISR_NAME(bat_oc2)),
+ BDIRQ("bd72720_int_ocur3_res", BD_ISR_NAME(bat_oc3_res)),
+ BDIRQ("bd72720_int_ocur3_det", BD_ISR_NAME(bat_oc3)),
+ BDIRQ("bd72720_int_cc_mon2_det", BD_ISR_NAME(bat_cc_mon)),
+ };
int num_irqs;
const struct bd7182x_irq_res *irqs;
@@ -890,6 +971,10 @@ static int bd7182x_get_irqs(struct platform_device *pdev,
irqs = &bd71815_irqs[0];
num_irqs = ARRAY_SIZE(bd71815_irqs);
break;
+ case ROHM_CHIP_TYPE_BD72720:
+ irqs = &bd72720_irqs[0];
+ num_irqs = ARRAY_SIZE(bd72720_irqs);
+ break;
default:
return -EINVAL;
}
@@ -958,21 +1043,27 @@ static int bd71828_power_probe(struct platform_device *pdev)
struct power_supply_config ac_cfg = {};
struct power_supply_config bat_cfg = {};
int ret;
- struct regmap *regmap;
-
- regmap = dev_get_regmap(pdev->dev.parent, NULL);
- if (!regmap) {
- dev_err(&pdev->dev, "No parent regmap\n");
- return -EINVAL;
- }
pwr = devm_kzalloc(&pdev->dev, sizeof(*pwr), GFP_KERNEL);
if (!pwr)
return -ENOMEM;
- pwr->regmap = regmap;
- pwr->dev = &pdev->dev;
+ /*
+ * The BD72720 MFD device registers two regmaps. Power-supply driver
+ * uses the "wrap-map", which provides access to both of the I2C slave
+ * addresses used by the BD72720
+ */
pwr->chip_type = platform_get_device_id(pdev)->driver_data;
+ if (pwr->chip_type != ROHM_CHIP_TYPE_BD72720)
+ pwr->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ else
+ pwr->regmap = dev_get_regmap(pdev->dev.parent, "wrap-map");
+ if (!pwr->regmap) {
+ dev_err(&pdev->dev, "No parent regmap\n");
+ return -EINVAL;
+ }
+
+ pwr->dev = &pdev->dev;
switch (pwr->chip_type) {
case ROHM_CHIP_TYPE_BD71828:
@@ -985,6 +1076,12 @@ static int bd71828_power_probe(struct platform_device *pdev)
pwr->get_temp = bd71815_get_temp;
pwr->regs = &pwr_regs_bd71815;
break;
+ case ROHM_CHIP_TYPE_BD72720:
+ pwr->bat_inserted = bd71828_bat_inserted;
+ pwr->regs = &pwr_regs_bd72720;
+ pwr->get_temp = bd71828_get_temp;
+ dev_dbg(pwr->dev, "Found ROHM BD72720\n");
+ break;
default:
dev_err(pwr->dev, "Unknown PMIC\n");
return -EINVAL;
@@ -1030,6 +1127,7 @@ static int bd71828_power_probe(struct platform_device *pdev)
static const struct platform_device_id bd71828_charger_id[] = {
{ "bd71815-power", ROHM_CHIP_TYPE_BD71815 },
{ "bd71828-power", ROHM_CHIP_TYPE_BD71828 },
+ { "bd72720-power", ROHM_CHIP_TYPE_BD72720 },
{ },
};
MODULE_DEVICE_TABLE(platform, bd71828_charger_id);
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 35d58bc72a56..a708fc63f581 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -241,13 +241,13 @@ config REGULATOR_BD71815
will be called bd71815-regulator.
config REGULATOR_BD71828
- tristate "ROHM BD71828 Power Regulator"
+ tristate "ROHM BD71828, BD72720 and BD73900 Power Regulators"
depends on MFD_ROHM_BD71828
select REGULATOR_ROHM
help
- This driver supports voltage regulators on ROHM BD71828 PMIC.
- This will enable support for the software controllable buck
- and LDO regulators.
+ This driver supports voltage regulators on ROHM BD71828,
+ BD71879, BD72720 and BD73900 PMICs. This will enable
+ support for the software controllable buck and LDO regulators.
This driver can also be built as a module. If so, the module
will be called bd71828-regulator.
diff --git a/drivers/regulator/bd71815-regulator.c b/drivers/regulator/bd71815-regulator.c
index 8da57a7bb2f1..668714f35464 100644
--- a/drivers/regulator/bd71815-regulator.c
+++ b/drivers/regulator/bd71815-regulator.c
@@ -571,15 +571,12 @@ static int bd7181x_probe(struct platform_device *pdev)
return -ENODEV;
}
- ldo4_en = devm_fwnode_gpiod_get(&pdev->dev,
- dev_fwnode(pdev->dev.parent),
- "rohm,vsel", GPIOD_ASIS, "ldo4-en");
- if (IS_ERR(ldo4_en)) {
- ret = PTR_ERR(ldo4_en);
- if (ret != -ENOENT)
- return ret;
- ldo4_en = NULL;
- }
+ ldo4_en = devm_fwnode_gpiod_get_optional(&pdev->dev,
+ dev_fwnode(pdev->dev.parent),
+ "rohm,vsel", GPIOD_ASIS,
+ "ldo4-en");
+ if (IS_ERR(ldo4_en))
+ return PTR_ERR(ldo4_en);
/* Disable to go to ship-mode */
ret = regmap_update_bits(regmap, BD71815_REG_PWRCTRL, RESTARTEN, 0);
diff --git a/drivers/regulator/bd71828-regulator.c b/drivers/regulator/bd71828-regulator.c
index 87de87793fa1..c8f3343cfe23 100644
--- a/drivers/regulator/bd71828-regulator.c
+++ b/drivers/regulator/bd71828-regulator.c
@@ -3,12 +3,15 @@
// bd71828-regulator.c ROHM BD71828GW-DS1 regulator driver
//
+#include <linux/cleanup.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/mfd/rohm-bd71828.h>
+#include <linux/mfd/rohm-bd72720.h>
#include <linux/module.h>
+#include <linux/mod_devicetable.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
@@ -16,6 +19,7 @@
#include <linux/regulator/machine.h>
#include <linux/regulator/of_regulator.h>
+#define BD72720_MASK_LDON_HEAD GENMASK(2, 0)
struct reg_init {
unsigned int reg;
unsigned int mask;
@@ -28,7 +32,7 @@ struct bd71828_regulator_data {
int reg_init_amnt;
};
-static const struct reg_init buck1_inits[] = {
+static const struct reg_init bd71828_buck1_inits[] = {
/*
* DVS Buck voltages can be changed by register values or via GPIO.
* Use register accesses by default.
@@ -40,7 +44,7 @@ static const struct reg_init buck1_inits[] = {
},
};
-static const struct reg_init buck2_inits[] = {
+static const struct reg_init bd71828_buck2_inits[] = {
{
.reg = BD71828_REG_PS_CTRL_1,
.mask = BD71828_MASK_DVS_BUCK2_CTRL,
@@ -48,7 +52,7 @@ static const struct reg_init buck2_inits[] = {
},
};
-static const struct reg_init buck6_inits[] = {
+static const struct reg_init bd71828_buck6_inits[] = {
{
.reg = BD71828_REG_PS_CTRL_1,
.mask = BD71828_MASK_DVS_BUCK6_CTRL,
@@ -56,7 +60,7 @@ static const struct reg_init buck6_inits[] = {
},
};
-static const struct reg_init buck7_inits[] = {
+static const struct reg_init bd71828_buck7_inits[] = {
{
.reg = BD71828_REG_PS_CTRL_1,
.mask = BD71828_MASK_DVS_BUCK7_CTRL,
@@ -64,6 +68,26 @@ static const struct reg_init buck7_inits[] = {
},
};
+#define BD72720_MASK_DVS_BUCK1_CTRL BIT(4)
+#define BD72720_MASK_DVS_LDO1_CTRL BIT(5)
+
+static const struct reg_init bd72720_buck1_inits[] = {
+ {
+ .reg = BD72720_REG_PS_CTRL_2,
+ .mask = BD72720_MASK_DVS_BUCK1_CTRL,
+ .val = 0, /* Disable "run-level" control */
+ },
+};
+
+static const struct reg_init bd72720_ldo1_inits[] = {
+ {
+ .reg = BD72720_REG_PS_CTRL_2,
+ .mask = BD72720_MASK_DVS_LDO1_CTRL,
+ .val = 0, /* Disable "run-level" control */
+ },
+};
+
+/* BD71828 Buck voltages */
static const struct linear_range bd71828_buck1267_volts[] = {
REGULATOR_LINEAR_RANGE(500000, 0x00, 0xef, 6250),
REGULATOR_LINEAR_RANGE(2000000, 0xf0, 0xff, 0),
@@ -84,13 +108,79 @@ static const struct linear_range bd71828_buck5_volts[] = {
REGULATOR_LINEAR_RANGE(3300000, 0x10, 0x1f, 0),
};
+/* BD71828 LDO voltages */
static const struct linear_range bd71828_ldo_volts[] = {
REGULATOR_LINEAR_RANGE(800000, 0x00, 0x31, 50000),
REGULATOR_LINEAR_RANGE(3300000, 0x32, 0x3f, 0),
};
+/* BD72720 Buck voltages */
+static const struct linear_range bd72720_buck1234_volts[] = {
+ REGULATOR_LINEAR_RANGE(500000, 0x00, 0xc0, 6250),
+ REGULATOR_LINEAR_RANGE(1700000, 0xc1, 0xff, 0),
+};
+
+static const struct linear_range bd72720_buck589_volts[] = {
+ REGULATOR_LINEAR_RANGE(500000, 0x00, 0x78, 10000),
+ REGULATOR_LINEAR_RANGE(1700000, 0x79, 0xff, 0),
+};
+
+static const struct linear_range bd72720_buck67_volts[] = {
+ REGULATOR_LINEAR_RANGE(1500000, 0x00, 0xb4, 10000),
+ REGULATOR_LINEAR_RANGE(3300000, 0xb5, 0xff, 0),
+};
+
+/*
+ * The BUCK10 on BD72720 has two modes of operation, depending on a LDON_HEAD
+ * setting. When LDON_HEAD is 0x0, the behaviour is as with other bucks, eg.
+ * voltage can be set to a values indicated below using the VSEL register.
+ *
+ * However, when LDON_HEAD is set to 0x1 ... 0x7, BUCK 10 voltage is, according
+ * to the data-sheet, "automatically adjusted following LDON_HEAD setting and
+ * clamped to BUCK10_VID setting".
+ *
+ * Again, reading the data-sheet shows a "typical connection" where the BUCK10
+ * is used to supply the LDOs 1-4. My assumption is that in practice, this
+ * means that the BUCK10 voltage will be adjusted based on the maximum output
+ * of the LDO 1-4 (to minimize power loss). This makes sense.
+ *
+ * Auto-adjusting regulators aren't something I really like to model in the
+ * driver though - and, if the auto-adjustment works as intended, then there
+ * should really be no need to software to care about the buck10 voltages.
+ * If enable/disable control is still needed, we can implement buck10 as a
+ * regulator with only the enable/disable ops - and device-tree can be used
+ * to model the supply-relations. I believe this could allow the regulator
+ * framework to automagically disable the BUCK10 if all LDOs that are being
+ * supplied by it are disabled.
+ */
+static const struct linear_range bd72720_buck10_volts[] = {
+ REGULATOR_LINEAR_RANGE(500000, 0x00, 0xc0, 6250),
+ REGULATOR_LINEAR_RANGE(1700000, 0xc1, 0xff, 0),
+};
+
+/* BD72720 LDO voltages */
+static const struct linear_range bd72720_ldo1234_volts[] = {
+ REGULATOR_LINEAR_RANGE(500000, 0x00, 0x50, 6250),
+ REGULATOR_LINEAR_RANGE(1000000, 0x51, 0x7f, 0),
+};
+
+static const struct linear_range bd72720_ldo57891011_volts[] = {
+ REGULATOR_LINEAR_RANGE(750000, 0x00, 0xff, 10000),
+};
+
+static const struct linear_range bd72720_ldo6_volts[] = {
+ REGULATOR_LINEAR_RANGE(600000, 0x00, 0x78, 10000),
+ REGULATOR_LINEAR_RANGE(1800000, 0x79, 0x7f, 0),
+};
+
static const unsigned int bd71828_ramp_delay[] = { 2500, 5000, 10000, 20000 };
+/*
+ * BD72720 supports setting both the ramp-up and ramp-down values
+ * separately. Do we need to support ramp-down setting?
+ */
+static const unsigned int bd72720_ramp_delay[] = { 5000, 7500, 10000, 12500 };
+
static int buck_set_hw_dvs_levels(struct device_node *np,
const struct regulator_desc *desc,
struct regulator_config *cfg)
@@ -102,9 +192,9 @@ static int buck_set_hw_dvs_levels(struct device_node *np,
return rohm_regulator_set_dvs_levels(&data->dvs, np, desc, cfg->regmap);
}
-static int ldo6_parse_dt(struct device_node *np,
- const struct regulator_desc *desc,
- struct regulator_config *cfg)
+static int bd71828_ldo6_parse_dt(struct device_node *np,
+ const struct regulator_desc *desc,
+ struct regulator_config *cfg)
{
int ret, i;
uint32_t uv = 0;
@@ -171,6 +261,24 @@ static const struct regulator_ops bd71828_ldo6_ops = {
.is_enabled = regulator_is_enabled_regmap,
};
+static const struct regulator_ops bd72720_regulator_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .list_voltage = regulator_list_voltage_linear_range,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .set_ramp_delay = regulator_set_ramp_delay_regmap,
+};
+
+static const struct regulator_ops bd72720_buck10_ldon_head_op = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_ramp_delay = regulator_set_ramp_delay_regmap,
+};
+
static const struct bd71828_regulator_data bd71828_rdata[] = {
{
.desc = {
@@ -212,8 +320,8 @@ static const struct bd71828_regulator_data bd71828_rdata[] = {
*/
.lpsr_on_mask = BD71828_MASK_LPSR_EN,
},
- .reg_inits = buck1_inits,
- .reg_init_amnt = ARRAY_SIZE(buck1_inits),
+ .reg_inits = bd71828_buck1_inits,
+ .reg_init_amnt = ARRAY_SIZE(bd71828_buck1_inits),
},
{
.desc = {
@@ -253,8 +361,8 @@ static const struct bd71828_regulator_data bd71828_rdata[] = {
.lpsr_reg = BD71828_REG_BUCK2_SUSP_VOLT,
.lpsr_mask = BD71828_MASK_BUCK1267_VOLT,
},
- .reg_inits = buck2_inits,
- .reg_init_amnt = ARRAY_SIZE(buck2_inits),
+ .reg_inits = bd71828_buck2_inits,
+ .reg_init_amnt = ARRAY_SIZE(bd71828_buck2_inits),
},
{
.desc = {
@@ -399,8 +507,8 @@ static const struct bd71828_regulator_data bd71828_rdata[] = {
.lpsr_reg = BD71828_REG_BUCK6_SUSP_VOLT,
.lpsr_mask = BD71828_MASK_BUCK1267_VOLT,
},
- .reg_inits = buck6_inits,
- .reg_init_amnt = ARRAY_SIZE(buck6_inits),
+ .reg_inits = bd71828_buck6_inits,
+ .reg_init_amnt = ARRAY_SIZE(bd71828_buck6_inits),
},
{
.desc = {
@@ -440,8 +548,8 @@ static const struct bd71828_regulator_data bd71828_rdata[] = {
.lpsr_reg = BD71828_REG_BUCK7_SUSP_VOLT,
.lpsr_mask = BD71828_MASK_BUCK1267_VOLT,
},
- .reg_inits = buck7_inits,
- .reg_init_amnt = ARRAY_SIZE(buck7_inits),
+ .reg_inits = bd71828_buck7_inits,
+ .reg_init_amnt = ARRAY_SIZE(bd71828_buck7_inits),
},
{
.desc = {
@@ -633,7 +741,7 @@ static const struct bd71828_regulator_data bd71828_rdata[] = {
* LDO6 only supports enable/disable for all states.
* Voltage for LDO6 is fixed.
*/
- .of_parse_cb = ldo6_parse_dt,
+ .of_parse_cb = bd71828_ldo6_parse_dt,
},
}, {
.desc = {
@@ -677,22 +785,890 @@ static const struct bd71828_regulator_data bd71828_rdata[] = {
},
};
+#define BD72720_BUCK10_DESC_INDEX 10
+#define BD72720_NUM_BUCK_VOLTS 0x100
+#define BD72720_NUM_LDO_VOLTS 0x100
+#define BD72720_NUM_LDO12346_VOLTS 0x80
+
+static const struct bd71828_regulator_data bd72720_rdata[] = {
+ {
+ .desc = {
+ .name = "buck1",
+ .of_match = of_match_ptr("buck1"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD72720_BUCK1,
+ .type = REGULATOR_VOLTAGE,
+
+ /*
+ * The BD72720 BUCK1 and LDO1 support GPIO toggled
+ * sub-RUN states called RUN0, RUN1, RUN2 and RUN3.
+ * The "operating mode" (sub-RUN states or normal)
+ * can be changed by a register.
+ *
+ * When the sub-RUN states are used, the voltage and
+ * enable state depend on a state specific
+ * configuration. The voltage and enable configuration
+ * for BUCK1 and LDO1 can be defined for each sub-RUN
+ * state using BD72720_REG_[BUCK,LDO]1_VSEL_R[0,1,2,3]
+ * voltage selection registers and the bits
+ * BD72720_MASK_RUN_[0,1,2,3]_EN in the enable registers.
+ * The PMIC will change both the BUCK1 and LDO1 voltages
+ * to the states defined in these registers when
+ * "DVS GPIOs" are toggled.
+ *
+ * If RUN 0 .. RUN 4 states are to be used, the normal
+ * voltage configuration mechanisms do not apply
+ * and we should overwrite the ops and ignore the
+ * voltage setting/getting registers which are setup
+ * here. This is not supported for now. If you need
+ * this functionality, you may try merging functionality
+ * from a downstream driver:
+ * https://rohmsemiconductor.github.io/Linux-Kernel-PMIC-Drivers/BD72720/
+ */
+ .ops = &bd72720_regulator_ops,
+ .linear_ranges = bd72720_buck1234_volts,
+ .n_linear_ranges = ARRAY_SIZE(bd72720_buck1234_volts),
+ .n_voltages = BD72720_NUM_BUCK_VOLTS,
+ .enable_reg = BD72720_REG_BUCK1_ON,
+ .enable_mask = BD72720_MASK_RUN_B_EN,
+ .vsel_reg = BD72720_REG_BUCK1_VSEL_RB,
+ .vsel_mask = BD72720_MASK_BUCK_VSEL,
+
+ .ramp_delay_table = bd72720_ramp_delay,
+ .n_ramp_values = ARRAY_SIZE(bd72720_ramp_delay),
+ .ramp_reg = BD72720_REG_BUCK1_MODE,
+ .ramp_mask = BD72720_MASK_RAMP_UP_DELAY,
+ .owner = THIS_MODULE,
+ .of_parse_cb = buck_set_hw_dvs_levels,
+ },
+ .dvs = {
+ .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+ ROHM_DVS_LEVEL_SUSPEND |
+ ROHM_DVS_LEVEL_LPSR, /* Deep idle in data-sheet */
+ .run_reg = BD72720_REG_BUCK1_VSEL_RB,
+ .run_mask = BD72720_MASK_BUCK_VSEL,
+ .idle_reg = BD72720_REG_BUCK1_VSEL_I,
+ .idle_mask = BD72720_MASK_BUCK_VSEL,
+ .idle_on_mask = BD72720_MASK_IDLE_EN,
+ .suspend_reg = BD72720_REG_BUCK1_VSEL_S,
+ .suspend_mask = BD72720_MASK_BUCK_VSEL,
+ .suspend_on_mask = BD72720_MASK_SUSPEND_EN,
+ .lpsr_reg = BD72720_REG_BUCK1_VSEL_DI,
+ .lpsr_mask = BD72720_MASK_BUCK_VSEL,
+ .lpsr_on_mask = BD72720_MASK_DEEP_IDLE_EN,
+ },
+ .reg_inits = bd72720_buck1_inits,
+ .reg_init_amnt = ARRAY_SIZE(bd72720_buck1_inits),
+ }, {
+ .desc = {
+ .name = "buck2",
+ .of_match = of_match_ptr("buck2"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD72720_BUCK2,
+ .type = REGULATOR_VOLTAGE,
+ .ops = &bd72720_regulator_ops,
+ .linear_ranges = bd72720_buck1234_volts,
+ .n_linear_ranges = ARRAY_SIZE(bd72720_buck1234_volts),
+ .n_voltages = BD72720_NUM_BUCK_VOLTS,
+ .enable_reg = BD72720_REG_BUCK2_ON,
+ .enable_mask = BD72720_MASK_RUN_B_EN,
+ .vsel_reg = BD72720_REG_BUCK2_VSEL_R,
+ .vsel_mask = BD72720_MASK_BUCK_VSEL,
+
+ .ramp_delay_table = bd72720_ramp_delay,
+ .n_ramp_values = ARRAY_SIZE(bd72720_ramp_delay),
+ .ramp_reg = BD72720_REG_BUCK2_MODE,
+ .ramp_mask = BD72720_MASK_RAMP_UP_DELAY,
+ .owner = THIS_MODULE,
+ .of_parse_cb = buck_set_hw_dvs_levels,
+ },
+ .dvs = {
+ .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+ ROHM_DVS_LEVEL_SUSPEND |
+ ROHM_DVS_LEVEL_LPSR,
+ .run_reg = BD72720_REG_BUCK2_VSEL_R,
+ .run_mask = BD72720_MASK_BUCK_VSEL,
+ .idle_reg = BD72720_REG_BUCK2_VSEL_I,
+ .idle_mask = BD72720_MASK_BUCK_VSEL,
+ .idle_on_mask = BD72720_MASK_IDLE_EN,
+ .suspend_reg = BD72720_REG_BUCK2_VSEL_S,
+ .suspend_mask = BD72720_MASK_BUCK_VSEL,
+ .suspend_on_mask = BD72720_MASK_SUSPEND_EN,
+ .lpsr_reg = BD72720_REG_BUCK2_VSEL_DI,
+ .lpsr_mask = BD72720_MASK_BUCK_VSEL,
+ .lpsr_on_mask = BD72720_MASK_DEEP_IDLE_EN,
+ },
+ }, {
+ .desc = {
+ .name = "buck3",
+ .of_match = of_match_ptr("buck3"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD72720_BUCK3,
+ .type = REGULATOR_VOLTAGE,
+ .ops = &bd72720_regulator_ops,
+ .linear_ranges = bd72720_buck1234_volts,
+ .n_linear_ranges = ARRAY_SIZE(bd72720_buck1234_volts),
+ .n_voltages = BD72720_NUM_BUCK_VOLTS,
+ .enable_reg = BD72720_REG_BUCK3_ON,
+ .enable_mask = BD72720_MASK_RUN_B_EN,
+ .vsel_reg = BD72720_REG_BUCK3_VSEL_R,
+ .vsel_mask = BD72720_MASK_BUCK_VSEL,
+
+ .ramp_delay_table = bd72720_ramp_delay,
+ .n_ramp_values = ARRAY_SIZE(bd72720_ramp_delay),
+ .ramp_reg = BD72720_REG_BUCK3_MODE,
+ .ramp_mask = BD72720_MASK_RAMP_UP_DELAY,
+ .owner = THIS_MODULE,
+ .of_parse_cb = buck_set_hw_dvs_levels,
+ },
+ .dvs = {
+ .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+ ROHM_DVS_LEVEL_SUSPEND |
+ ROHM_DVS_LEVEL_LPSR,
+ .run_reg = BD72720_REG_BUCK3_VSEL_R,
+ .run_mask = BD72720_MASK_BUCK_VSEL,
+ .idle_reg = BD72720_REG_BUCK3_VSEL_I,
+ .idle_mask = BD72720_MASK_BUCK_VSEL,
+ .idle_on_mask = BD72720_MASK_IDLE_EN,
+ .suspend_reg = BD72720_REG_BUCK3_VSEL_S,
+ .suspend_mask = BD72720_MASK_BUCK_VSEL,
+ .suspend_on_mask = BD72720_MASK_SUSPEND_EN,
+ .lpsr_reg = BD72720_REG_BUCK3_VSEL_DI,
+ .lpsr_mask = BD72720_MASK_BUCK_VSEL,
+ .lpsr_on_mask = BD72720_MASK_DEEP_IDLE_EN,
+ },
+ }, {
+ .desc = {
+ .name = "buck4",
+ .of_match = of_match_ptr("buck4"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD72720_BUCK4,
+ .type = REGULATOR_VOLTAGE,
+ .ops = &bd72720_regulator_ops,
+ .linear_ranges = bd72720_buck1234_volts,
+ .n_linear_ranges = ARRAY_SIZE(bd72720_buck1234_volts),
+ .n_voltages = BD72720_NUM_BUCK_VOLTS,
+ .enable_reg = BD72720_REG_BUCK4_ON,
+ .enable_mask = BD72720_MASK_RUN_B_EN,
+ .vsel_reg = BD72720_REG_BUCK4_VSEL_R,
+ .vsel_mask = BD72720_MASK_BUCK_VSEL,
+
+ .ramp_delay_table = bd72720_ramp_delay,
+ .n_ramp_values = ARRAY_SIZE(bd72720_ramp_delay),
+ .ramp_reg = BD72720_REG_BUCK4_MODE,
+ .ramp_mask = BD72720_MASK_RAMP_UP_DELAY,
+ .owner = THIS_MODULE,
+ .of_parse_cb = buck_set_hw_dvs_levels,
+ },
+ .dvs = {
+ .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+ ROHM_DVS_LEVEL_SUSPEND |
+ ROHM_DVS_LEVEL_LPSR,
+ .run_reg = BD72720_REG_BUCK4_VSEL_R,
+ .run_mask = BD72720_MASK_BUCK_VSEL,
+ .idle_reg = BD72720_REG_BUCK4_VSEL_I,
+ .idle_mask = BD72720_MASK_BUCK_VSEL,
+ .idle_on_mask = BD72720_MASK_IDLE_EN,
+ .suspend_reg = BD72720_REG_BUCK4_VSEL_S,
+ .suspend_mask = BD72720_MASK_BUCK_VSEL,
+ .suspend_on_mask = BD72720_MASK_SUSPEND_EN,
+ .lpsr_reg = BD72720_REG_BUCK4_VSEL_DI,
+ .lpsr_mask = BD72720_MASK_BUCK_VSEL,
+ .lpsr_on_mask = BD72720_MASK_DEEP_IDLE_EN,
+ },
+ }, {
+ .desc = {
+ .name = "buck5",
+ .of_match = of_match_ptr("buck5"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD72720_BUCK5,
+ .type = REGULATOR_VOLTAGE,
+ .ops = &bd72720_regulator_ops,
+ .linear_ranges = bd72720_buck589_volts,
+ .n_linear_ranges = ARRAY_SIZE(bd72720_buck589_volts),
+ .n_voltages = BD72720_NUM_BUCK_VOLTS,
+ .enable_reg = BD72720_REG_BUCK5_ON,
+ .enable_mask = BD72720_MASK_RUN_B_EN,
+ .vsel_reg = BD72720_REG_BUCK5_VSEL,
+ .vsel_mask = BD72720_MASK_BUCK_VSEL,
+
+ .ramp_delay_table = bd72720_ramp_delay,
+ .n_ramp_values = ARRAY_SIZE(bd72720_ramp_delay),
+ .ramp_reg = BD72720_REG_BUCK5_MODE,
+ .ramp_mask = BD72720_MASK_RAMP_UP_DELAY,
+ .owner = THIS_MODULE,
+ .of_parse_cb = buck_set_hw_dvs_levels,
+ },
+ .dvs = {
+ .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+ ROHM_DVS_LEVEL_SUSPEND |
+ ROHM_DVS_LEVEL_LPSR,
+ .run_reg = BD72720_REG_BUCK5_VSEL,
+ .run_mask = BD72720_MASK_BUCK_VSEL,
+ .idle_on_mask = BD72720_MASK_IDLE_EN,
+ .suspend_on_mask = BD72720_MASK_SUSPEND_EN,
+ .lpsr_on_mask = BD72720_MASK_DEEP_IDLE_EN,
+ },
+ }, {
+ .desc = {
+ .name = "buck6",
+ .of_match = of_match_ptr("buck6"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD72720_BUCK6,
+ .type = REGULATOR_VOLTAGE,
+ .ops = &bd72720_regulator_ops,
+ .linear_ranges = bd72720_buck67_volts,
+ .n_linear_ranges = ARRAY_SIZE(bd72720_buck67_volts),
+ .n_voltages = BD72720_NUM_BUCK_VOLTS,
+ .enable_reg = BD72720_REG_BUCK6_ON,
+ .enable_mask = BD72720_MASK_RUN_B_EN,
+ .vsel_reg = BD72720_REG_BUCK6_VSEL,
+ .vsel_mask = BD72720_MASK_BUCK_VSEL,
+
+ .ramp_delay_table = bd72720_ramp_delay,
+ .n_ramp_values = ARRAY_SIZE(bd72720_ramp_delay),
+ .ramp_reg = BD72720_REG_BUCK6_MODE,
+ .ramp_mask = BD72720_MASK_RAMP_UP_DELAY,
+ .owner = THIS_MODULE,
+ .of_parse_cb = buck_set_hw_dvs_levels,
+ },
+ .dvs = {
+ .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+ ROHM_DVS_LEVEL_SUSPEND |
+ ROHM_DVS_LEVEL_LPSR,
+ .run_reg = BD72720_REG_BUCK6_VSEL,
+ .run_mask = BD72720_MASK_BUCK_VSEL,
+ .idle_on_mask = BD72720_MASK_IDLE_EN,
+ .suspend_on_mask = BD72720_MASK_SUSPEND_EN,
+ .lpsr_on_mask = BD72720_MASK_DEEP_IDLE_EN,
+ },
+ }, {
+ .desc = {
+ .name = "buck7",
+ .of_match = of_match_ptr("buck7"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD72720_BUCK7,
+ .type = REGULATOR_VOLTAGE,
+ .ops = &bd72720_regulator_ops,
+ .linear_ranges = bd72720_buck67_volts,
+ .n_linear_ranges = ARRAY_SIZE(bd72720_buck67_volts),
+ .n_voltages = BD72720_NUM_BUCK_VOLTS,
+ .enable_reg = BD72720_REG_BUCK7_ON,
+ .enable_mask = BD72720_MASK_RUN_B_EN,
+ .vsel_reg = BD72720_REG_BUCK7_VSEL,
+ .vsel_mask = BD72720_MASK_BUCK_VSEL,
+
+ .ramp_delay_table = bd72720_ramp_delay,
+ .n_ramp_values = ARRAY_SIZE(bd72720_ramp_delay),
+ .ramp_reg = BD72720_REG_BUCK7_MODE,
+ .ramp_mask = BD72720_MASK_RAMP_UP_DELAY,
+ .owner = THIS_MODULE,
+ .of_parse_cb = buck_set_hw_dvs_levels,
+ },
+ .dvs = {
+ .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+ ROHM_DVS_LEVEL_SUSPEND |
+ ROHM_DVS_LEVEL_LPSR,
+ .run_reg = BD72720_REG_BUCK7_VSEL,
+ .run_mask = BD72720_MASK_BUCK_VSEL,
+ .idle_on_mask = BD72720_MASK_IDLE_EN,
+ .suspend_on_mask = BD72720_MASK_SUSPEND_EN,
+ .lpsr_on_mask = BD72720_MASK_DEEP_IDLE_EN,
+ },
+ }, {
+ .desc = {
+ .name = "buck8",
+ .of_match = of_match_ptr("buck8"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD72720_BUCK8,
+ .type = REGULATOR_VOLTAGE,
+ .ops = &bd72720_regulator_ops,
+ .linear_ranges = bd72720_buck589_volts,
+ .n_linear_ranges = ARRAY_SIZE(bd72720_buck589_volts),
+ .n_voltages = BD72720_NUM_BUCK_VOLTS,
+ .enable_reg = BD72720_REG_BUCK8_ON,
+ .enable_mask = BD72720_MASK_RUN_B_EN,
+ .vsel_reg = BD72720_REG_BUCK8_VSEL,
+ .vsel_mask = BD72720_MASK_BUCK_VSEL,
+
+ .ramp_delay_table = bd72720_ramp_delay,
+ .n_ramp_values = ARRAY_SIZE(bd72720_ramp_delay),
+ .ramp_reg = BD72720_REG_BUCK8_MODE,
+ .ramp_mask = BD72720_MASK_RAMP_UP_DELAY,
+ .owner = THIS_MODULE,
+ .of_parse_cb = buck_set_hw_dvs_levels,
+ },
+ .dvs = {
+ .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+ ROHM_DVS_LEVEL_SUSPEND |
+ ROHM_DVS_LEVEL_LPSR,
+ .run_reg = BD72720_REG_BUCK8_VSEL,
+ .run_mask = BD72720_MASK_BUCK_VSEL,
+ .idle_on_mask = BD72720_MASK_IDLE_EN,
+ .suspend_on_mask = BD72720_MASK_SUSPEND_EN,
+ .lpsr_on_mask = BD72720_MASK_DEEP_IDLE_EN,
+ },
+ }, {
+ .desc = {
+ .name = "buck9",
+ .of_match = of_match_ptr("buck9"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD72720_BUCK9,
+ .type = REGULATOR_VOLTAGE,
+ .ops = &bd72720_regulator_ops,
+ .linear_ranges = bd72720_buck589_volts,
+ .n_linear_ranges = ARRAY_SIZE(bd72720_buck589_volts),
+ .n_voltages = BD72720_NUM_BUCK_VOLTS,
+ .enable_reg = BD72720_REG_BUCK9_ON,
+ .enable_mask = BD72720_MASK_RUN_B_EN,
+ .vsel_reg = BD72720_REG_BUCK9_VSEL,
+ .vsel_mask = BD72720_MASK_BUCK_VSEL,
+
+ .ramp_delay_table = bd72720_ramp_delay,
+ .n_ramp_values = ARRAY_SIZE(bd72720_ramp_delay),
+ .ramp_reg = BD72720_REG_BUCK9_MODE,
+ .ramp_mask = BD72720_MASK_RAMP_UP_DELAY,
+ .owner = THIS_MODULE,
+ .of_parse_cb = buck_set_hw_dvs_levels,
+ },
+ .dvs = {
+ .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+ ROHM_DVS_LEVEL_SUSPEND |
+ ROHM_DVS_LEVEL_LPSR,
+ .run_reg = BD72720_REG_BUCK9_VSEL,
+ .run_mask = BD72720_MASK_BUCK_VSEL,
+ .idle_on_mask = BD72720_MASK_IDLE_EN,
+ .suspend_on_mask = BD72720_MASK_SUSPEND_EN,
+ .lpsr_on_mask = BD72720_MASK_DEEP_IDLE_EN,
+ },
+ }, {
+ .desc = {
+ .name = "buck10",
+ .of_match = of_match_ptr("buck10"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD72720_BUCK10,
+ .type = REGULATOR_VOLTAGE,
+ .ops = &bd72720_regulator_ops,
+ .linear_ranges = bd72720_buck10_volts,
+ .n_linear_ranges = ARRAY_SIZE(bd72720_buck10_volts),
+ .n_voltages = BD72720_NUM_BUCK_VOLTS,
+ .enable_reg = BD72720_REG_BUCK10_ON,
+ .enable_mask = BD72720_MASK_RUN_B_EN,
+ .vsel_reg = BD72720_REG_BUCK10_VSEL,
+ .vsel_mask = BD72720_MASK_BUCK_VSEL,
+
+ .ramp_delay_table = bd72720_ramp_delay,
+ .n_ramp_values = ARRAY_SIZE(bd72720_ramp_delay),
+ .ramp_reg = BD72720_REG_BUCK10_MODE,
+ .ramp_mask = BD72720_MASK_RAMP_UP_DELAY,
+ .owner = THIS_MODULE,
+ .of_parse_cb = buck_set_hw_dvs_levels,
+ },
+ .dvs = {
+ .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+ ROHM_DVS_LEVEL_SUSPEND |
+ ROHM_DVS_LEVEL_LPSR,
+ .run_reg = BD72720_REG_BUCK10_VSEL,
+ .run_mask = BD72720_MASK_BUCK_VSEL,
+ .idle_on_mask = BD72720_MASK_IDLE_EN,
+ .suspend_on_mask = BD72720_MASK_SUSPEND_EN,
+ .lpsr_on_mask = BD72720_MASK_DEEP_IDLE_EN,
+ },
+ }, {
+ .desc = {
+ .name = "ldo1",
+ .of_match = of_match_ptr("ldo1"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD72720_LDO1,
+ .type = REGULATOR_VOLTAGE,
+ .ops = &bd72720_regulator_ops,
+ .linear_ranges = bd72720_ldo1234_volts,
+ .n_linear_ranges = ARRAY_SIZE(bd72720_ldo1234_volts),
+ .n_voltages = BD72720_NUM_LDO12346_VOLTS,
+ .enable_reg = BD72720_REG_LDO1_ON,
+ .enable_mask = BD72720_MASK_RUN_B_EN,
+ .vsel_reg = BD72720_REG_LDO1_VSEL_RB,
+ .vsel_mask = BD72720_MASK_LDO12346_VSEL,
+
+ .ramp_delay_table = bd72720_ramp_delay,
+ .n_ramp_values = ARRAY_SIZE(bd72720_ramp_delay),
+ .ramp_reg = BD72720_REG_LDO1_MODE1,
+ .ramp_mask = BD72720_MASK_RAMP_UP_DELAY,
+ .owner = THIS_MODULE,
+ .of_parse_cb = buck_set_hw_dvs_levels,
+ },
+ .dvs = {
+ .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+ ROHM_DVS_LEVEL_SUSPEND |
+ ROHM_DVS_LEVEL_LPSR,
+ .run_reg = BD72720_REG_LDO1_VSEL_RB,
+ .run_mask = BD72720_MASK_LDO12346_VSEL,
+ .idle_reg = BD72720_REG_LDO1_VSEL_I,
+ .idle_mask = BD72720_MASK_LDO12346_VSEL,
+ .idle_on_mask = BD72720_MASK_IDLE_EN,
+ .suspend_reg = BD72720_REG_LDO1_VSEL_S,
+ .suspend_mask = BD72720_MASK_LDO12346_VSEL,
+ .suspend_on_mask = BD72720_MASK_SUSPEND_EN,
+ .lpsr_reg = BD72720_REG_LDO1_VSEL_DI,
+ .lpsr_mask = BD72720_MASK_LDO12346_VSEL,
+ .lpsr_on_mask = BD72720_MASK_DEEP_IDLE_EN,
+ },
+ .reg_inits = bd72720_ldo1_inits,
+ .reg_init_amnt = ARRAY_SIZE(bd72720_ldo1_inits),
+ }, {
+ .desc = {
+ .name = "ldo2",
+ .of_match = of_match_ptr("ldo2"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD72720_LDO2,
+ .type = REGULATOR_VOLTAGE,
+ .ops = &bd72720_regulator_ops,
+ .linear_ranges = bd72720_ldo1234_volts,
+ .n_linear_ranges = ARRAY_SIZE(bd72720_ldo1234_volts),
+ .n_voltages = BD72720_NUM_LDO12346_VOLTS,
+ .enable_reg = BD72720_REG_LDO2_ON,
+ .enable_mask = BD72720_MASK_RUN_B_EN,
+ .vsel_reg = BD72720_REG_LDO2_VSEL_R,
+ .vsel_mask = BD72720_MASK_LDO12346_VSEL,
+
+ .ramp_delay_table = bd72720_ramp_delay,
+ .n_ramp_values = ARRAY_SIZE(bd72720_ramp_delay),
+ .ramp_reg = BD72720_REG_LDO2_MODE,
+ .ramp_mask = BD72720_MASK_RAMP_UP_DELAY,
+ .owner = THIS_MODULE,
+ .of_parse_cb = buck_set_hw_dvs_levels,
+ },
+ .dvs = {
+ .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+ ROHM_DVS_LEVEL_SUSPEND |
+ ROHM_DVS_LEVEL_LPSR,
+ .run_reg = BD72720_REG_LDO2_VSEL_R,
+ .run_mask = BD72720_MASK_LDO12346_VSEL,
+ .idle_reg = BD72720_REG_LDO2_VSEL_I,
+ .idle_mask = BD72720_MASK_LDO12346_VSEL,
+ .idle_on_mask = BD72720_MASK_IDLE_EN,
+ .suspend_reg = BD72720_REG_LDO2_VSEL_S,
+ .suspend_mask = BD72720_MASK_LDO12346_VSEL,
+ .suspend_on_mask = BD72720_MASK_SUSPEND_EN,
+ .lpsr_reg = BD72720_REG_LDO2_VSEL_DI,
+ .lpsr_mask = BD72720_MASK_LDO12346_VSEL,
+ .lpsr_on_mask = BD72720_MASK_DEEP_IDLE_EN,
+ },
+ }, {
+ .desc = {
+ .name = "ldo3",
+ .of_match = of_match_ptr("ldo3"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD72720_LDO3,
+ .type = REGULATOR_VOLTAGE,
+ .ops = &bd72720_regulator_ops,
+ .linear_ranges = bd72720_ldo1234_volts,
+ .n_linear_ranges = ARRAY_SIZE(bd72720_ldo1234_volts),
+ .n_voltages = BD72720_NUM_LDO12346_VOLTS,
+ .enable_reg = BD72720_REG_LDO3_ON,
+ .enable_mask = BD72720_MASK_RUN_B_EN,
+ .vsel_reg = BD72720_REG_LDO3_VSEL_R,
+ .vsel_mask = BD72720_MASK_LDO12346_VSEL,
+
+ .ramp_delay_table = bd72720_ramp_delay,
+ .n_ramp_values = ARRAY_SIZE(bd72720_ramp_delay),
+ .ramp_reg = BD72720_REG_LDO3_MODE,
+ .ramp_mask = BD72720_MASK_RAMP_UP_DELAY,
+ .owner = THIS_MODULE,
+ .of_parse_cb = buck_set_hw_dvs_levels,
+ },
+ .dvs = {
+ .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+ ROHM_DVS_LEVEL_SUSPEND |
+ ROHM_DVS_LEVEL_LPSR,
+ .run_reg = BD72720_REG_LDO3_VSEL_R,
+ .run_mask = BD72720_MASK_LDO12346_VSEL,
+ .idle_reg = BD72720_REG_LDO3_VSEL_I,
+ .idle_mask = BD72720_MASK_LDO12346_VSEL,
+ .idle_on_mask = BD72720_MASK_IDLE_EN,
+ .suspend_reg = BD72720_REG_LDO3_VSEL_S,
+ .suspend_mask = BD72720_MASK_LDO12346_VSEL,
+ .suspend_on_mask = BD72720_MASK_SUSPEND_EN,
+ .lpsr_reg = BD72720_REG_LDO3_VSEL_DI,
+ .lpsr_mask = BD72720_MASK_LDO12346_VSEL,
+ .lpsr_on_mask = BD72720_MASK_DEEP_IDLE_EN,
+ },
+ }, {
+ .desc = {
+ .name = "ldo4",
+ .of_match = of_match_ptr("ldo4"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD72720_LDO4,
+ .type = REGULATOR_VOLTAGE,
+ .ops = &bd72720_regulator_ops,
+ .linear_ranges = bd72720_ldo1234_volts,
+ .n_linear_ranges = ARRAY_SIZE(bd72720_ldo1234_volts),
+ .n_voltages = BD72720_NUM_LDO12346_VOLTS,
+ .enable_reg = BD72720_REG_LDO4_ON,
+ .enable_mask = BD72720_MASK_RUN_B_EN,
+ .vsel_reg = BD72720_REG_LDO4_VSEL_R,
+ .vsel_mask = BD72720_MASK_LDO12346_VSEL,
+
+ .ramp_delay_table = bd72720_ramp_delay,
+ .n_ramp_values = ARRAY_SIZE(bd72720_ramp_delay),
+ .ramp_reg = BD72720_REG_LDO4_MODE,
+ .ramp_mask = BD72720_MASK_RAMP_UP_DELAY,
+ .owner = THIS_MODULE,
+ .of_parse_cb = buck_set_hw_dvs_levels,
+ },
+ .dvs = {
+ .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+ ROHM_DVS_LEVEL_SUSPEND |
+ ROHM_DVS_LEVEL_LPSR,
+ .run_reg = BD72720_REG_LDO4_VSEL_R,
+ .run_mask = BD72720_MASK_LDO12346_VSEL,
+ .idle_reg = BD72720_REG_LDO4_VSEL_I,
+ .idle_mask = BD72720_MASK_LDO12346_VSEL,
+ .idle_on_mask = BD72720_MASK_IDLE_EN,
+ .suspend_reg = BD72720_REG_LDO4_VSEL_S,
+ .suspend_mask = BD72720_MASK_LDO12346_VSEL,
+ .suspend_on_mask = BD72720_MASK_SUSPEND_EN,
+ .lpsr_reg = BD72720_REG_LDO4_VSEL_DI,
+ .lpsr_mask = BD72720_MASK_LDO12346_VSEL,
+ .lpsr_on_mask = BD72720_MASK_DEEP_IDLE_EN,
+ },
+ }, {
+ .desc = {
+ .name = "ldo5",
+ .of_match = of_match_ptr("ldo5"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD72720_LDO5,
+ .type = REGULATOR_VOLTAGE,
+ .ops = &bd72720_regulator_ops,
+ .linear_ranges = bd72720_ldo57891011_volts,
+ .n_linear_ranges = ARRAY_SIZE(bd72720_ldo57891011_volts),
+ .n_voltages = BD72720_NUM_LDO_VOLTS,
+ .enable_reg = BD72720_REG_LDO5_ON,
+ .enable_mask = BD72720_MASK_RUN_B_EN,
+ .vsel_reg = BD72720_REG_LDO5_VSEL,
+ .vsel_mask = BD72720_MASK_LDO_VSEL,
+
+ .ramp_delay_table = bd72720_ramp_delay,
+ .n_ramp_values = ARRAY_SIZE(bd72720_ramp_delay),
+ .ramp_reg = BD72720_REG_LDO5_MODE,
+ .ramp_mask = BD72720_MASK_RAMP_UP_DELAY,
+ .owner = THIS_MODULE,
+ .of_parse_cb = buck_set_hw_dvs_levels,
+ },
+ .dvs = {
+ .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+ ROHM_DVS_LEVEL_SUSPEND |
+ ROHM_DVS_LEVEL_LPSR,
+ .run_reg = BD72720_REG_LDO5_VSEL,
+ .run_mask = BD72720_MASK_LDO_VSEL,
+ .idle_on_mask = BD72720_MASK_IDLE_EN,
+ .suspend_on_mask = BD72720_MASK_SUSPEND_EN,
+ .lpsr_on_mask = BD72720_MASK_DEEP_IDLE_EN,
+ },
+ }, {
+ .desc = {
+ .name = "ldo6",
+ .of_match = of_match_ptr("ldo6"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD72720_LDO6,
+ .type = REGULATOR_VOLTAGE,
+ .ops = &bd72720_regulator_ops,
+ .linear_ranges = bd72720_ldo6_volts,
+ .n_linear_ranges = ARRAY_SIZE(bd72720_ldo6_volts),
+ .n_voltages = BD72720_NUM_LDO12346_VOLTS,
+ .enable_reg = BD72720_REG_LDO6_ON,
+ .enable_mask = BD72720_MASK_RUN_B_EN,
+ .vsel_reg = BD72720_REG_LDO6_VSEL,
+ .vsel_mask = BD72720_MASK_LDO12346_VSEL,
+
+ .ramp_delay_table = bd72720_ramp_delay,
+ .n_ramp_values = ARRAY_SIZE(bd72720_ramp_delay),
+ .ramp_reg = BD72720_REG_LDO6_MODE,
+ .ramp_mask = BD72720_MASK_RAMP_UP_DELAY,
+ .owner = THIS_MODULE,
+ .of_parse_cb = buck_set_hw_dvs_levels,
+ },
+ .dvs = {
+ .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+ ROHM_DVS_LEVEL_SUSPEND |
+ ROHM_DVS_LEVEL_LPSR,
+ .run_reg = BD72720_REG_LDO6_VSEL,
+ .run_mask = BD72720_MASK_LDO12346_VSEL,
+ .idle_on_mask = BD72720_MASK_IDLE_EN,
+ .suspend_on_mask = BD72720_MASK_SUSPEND_EN,
+ .lpsr_on_mask = BD72720_MASK_DEEP_IDLE_EN,
+ },
+ }, {
+ .desc = {
+ .name = "ldo7",
+ .of_match = of_match_ptr("ldo7"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD72720_LDO7,
+ .type = REGULATOR_VOLTAGE,
+ .ops = &bd72720_regulator_ops,
+ .linear_ranges = bd72720_ldo57891011_volts,
+ .n_linear_ranges = ARRAY_SIZE(bd72720_ldo57891011_volts),
+ .n_voltages = BD72720_NUM_LDO_VOLTS,
+ .enable_reg = BD72720_REG_LDO7_ON,
+ .enable_mask = BD72720_MASK_RUN_B_EN,
+ .vsel_reg = BD72720_REG_LDO7_VSEL,
+ .vsel_mask = BD72720_MASK_LDO_VSEL,
+
+ .ramp_delay_table = bd72720_ramp_delay,
+ .n_ramp_values = ARRAY_SIZE(bd72720_ramp_delay),
+ .ramp_reg = BD72720_REG_LDO7_MODE,
+ .ramp_mask = BD72720_MASK_RAMP_UP_DELAY,
+ .owner = THIS_MODULE,
+ .of_parse_cb = buck_set_hw_dvs_levels,
+ },
+ .dvs = {
+ .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+ ROHM_DVS_LEVEL_SUSPEND |
+ ROHM_DVS_LEVEL_LPSR,
+ .run_reg = BD72720_REG_LDO7_VSEL,
+ .run_mask = BD72720_MASK_LDO_VSEL,
+ .idle_on_mask = BD72720_MASK_IDLE_EN,
+ .suspend_on_mask = BD72720_MASK_SUSPEND_EN,
+ .lpsr_on_mask = BD72720_MASK_DEEP_IDLE_EN,
+ },
+ }, {
+ .desc = {
+ .name = "ldo8",
+ .of_match = of_match_ptr("ldo8"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD72720_LDO8,
+ .type = REGULATOR_VOLTAGE,
+ .ops = &bd72720_regulator_ops,
+ .linear_ranges = bd72720_ldo57891011_volts,
+ .n_linear_ranges = ARRAY_SIZE(bd72720_ldo57891011_volts),
+ .n_voltages = BD72720_NUM_LDO_VOLTS,
+ .enable_reg = BD72720_REG_LDO8_ON,
+ .enable_mask = BD72720_MASK_RUN_B_EN,
+ .vsel_reg = BD72720_REG_LDO8_VSEL,
+ .vsel_mask = BD72720_MASK_LDO_VSEL,
+
+ .ramp_delay_table = bd72720_ramp_delay,
+ .n_ramp_values = ARRAY_SIZE(bd72720_ramp_delay),
+ .ramp_reg = BD72720_REG_LDO8_MODE,
+ .ramp_mask = BD72720_MASK_RAMP_UP_DELAY,
+ .owner = THIS_MODULE,
+ .of_parse_cb = buck_set_hw_dvs_levels,
+ },
+ .dvs = {
+ .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+ ROHM_DVS_LEVEL_SUSPEND |
+ ROHM_DVS_LEVEL_LPSR,
+ .run_reg = BD72720_REG_LDO8_VSEL,
+ .run_mask = BD72720_MASK_LDO_VSEL,
+ .idle_on_mask = BD72720_MASK_IDLE_EN,
+ .suspend_on_mask = BD72720_MASK_SUSPEND_EN,
+ .lpsr_on_mask = BD72720_MASK_DEEP_IDLE_EN,
+ },
+ }, {
+ .desc = {
+ .name = "ldo9",
+ .of_match = of_match_ptr("ldo9"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD72720_LDO9,
+ .type = REGULATOR_VOLTAGE,
+ .ops = &bd72720_regulator_ops,
+ .linear_ranges = bd72720_ldo57891011_volts,
+ .n_linear_ranges = ARRAY_SIZE(bd72720_ldo57891011_volts),
+ .n_voltages = BD72720_NUM_LDO_VOLTS,
+ .enable_reg = BD72720_REG_LDO9_ON,
+ .enable_mask = BD72720_MASK_RUN_B_EN,
+ .vsel_reg = BD72720_REG_LDO9_VSEL,
+ .vsel_mask = BD72720_MASK_LDO_VSEL,
+
+ .ramp_delay_table = bd72720_ramp_delay,
+ .n_ramp_values = ARRAY_SIZE(bd72720_ramp_delay),
+ .ramp_reg = BD72720_REG_LDO9_MODE,
+ .ramp_mask = BD72720_MASK_RAMP_UP_DELAY,
+ .owner = THIS_MODULE,
+ .of_parse_cb = buck_set_hw_dvs_levels,
+ },
+ .dvs = {
+ .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+ ROHM_DVS_LEVEL_SUSPEND |
+ ROHM_DVS_LEVEL_LPSR,
+ .run_reg = BD72720_REG_LDO9_VSEL,
+ .run_mask = BD72720_MASK_LDO_VSEL,
+ .idle_on_mask = BD72720_MASK_IDLE_EN,
+ .suspend_on_mask = BD72720_MASK_SUSPEND_EN,
+ .lpsr_on_mask = BD72720_MASK_DEEP_IDLE_EN,
+ },
+ }, {
+ .desc = {
+ .name = "ldo10",
+ .of_match = of_match_ptr("ldo10"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD72720_LDO10,
+ .type = REGULATOR_VOLTAGE,
+ .ops = &bd72720_regulator_ops,
+ .linear_ranges = bd72720_ldo57891011_volts,
+ .n_linear_ranges = ARRAY_SIZE(bd72720_ldo57891011_volts),
+ .n_voltages = BD72720_NUM_LDO_VOLTS,
+ .enable_reg = BD72720_REG_LDO10_ON,
+ .enable_mask = BD72720_MASK_RUN_B_EN,
+ .vsel_reg = BD72720_REG_LDO10_VSEL,
+ .vsel_mask = BD72720_MASK_LDO_VSEL,
+
+ .ramp_delay_table = bd72720_ramp_delay,
+ .n_ramp_values = ARRAY_SIZE(bd72720_ramp_delay),
+ .ramp_reg = BD72720_REG_LDO10_MODE,
+ .ramp_mask = BD72720_MASK_RAMP_UP_DELAY,
+ .owner = THIS_MODULE,
+ .of_parse_cb = buck_set_hw_dvs_levels,
+ },
+ .dvs = {
+ .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+ ROHM_DVS_LEVEL_SUSPEND |
+ ROHM_DVS_LEVEL_LPSR,
+ .run_reg = BD72720_REG_LDO10_VSEL,
+ .run_mask = BD72720_MASK_LDO_VSEL,
+ .idle_on_mask = BD72720_MASK_IDLE_EN,
+ .suspend_on_mask = BD72720_MASK_SUSPEND_EN,
+ .lpsr_on_mask = BD72720_MASK_DEEP_IDLE_EN,
+ },
+ }, {
+ .desc = {
+ .name = "ldo11",
+ .of_match = of_match_ptr("ldo11"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD72720_LDO11,
+ .type = REGULATOR_VOLTAGE,
+ .ops = &bd72720_regulator_ops,
+ .linear_ranges = bd72720_ldo57891011_volts,
+ .n_linear_ranges = ARRAY_SIZE(bd72720_ldo57891011_volts),
+ .n_voltages = BD72720_NUM_LDO_VOLTS,
+ .enable_reg = BD72720_REG_LDO11_ON,
+ .enable_mask = BD72720_MASK_RUN_B_EN,
+ .vsel_reg = BD72720_REG_LDO11_VSEL,
+ .vsel_mask = BD72720_MASK_LDO_VSEL,
+
+ .ramp_delay_table = bd72720_ramp_delay,
+ .n_ramp_values = ARRAY_SIZE(bd72720_ramp_delay),
+ .ramp_reg = BD72720_REG_LDO11_MODE,
+ .ramp_mask = BD72720_MASK_RAMP_UP_DELAY,
+ .owner = THIS_MODULE,
+ .of_parse_cb = buck_set_hw_dvs_levels,
+ },
+ .dvs = {
+ .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+ ROHM_DVS_LEVEL_SUSPEND |
+ ROHM_DVS_LEVEL_LPSR,
+ .run_reg = BD72720_REG_LDO11_VSEL,
+ .run_mask = BD72720_MASK_LDO_VSEL,
+ .idle_on_mask = BD72720_MASK_IDLE_EN,
+ .suspend_on_mask = BD72720_MASK_SUSPEND_EN,
+ .lpsr_on_mask = BD72720_MASK_DEEP_IDLE_EN,
+ },
+ },
+};
+
+static int bd72720_buck10_ldon_head_mode(struct device *dev,
+ struct device_node *npreg,
+ struct regmap *regmap,
+ struct regulator_desc *buck10_desc)
+{
+ struct device_node *np __free(device_node) =
+ of_get_child_by_name(npreg, "buck10");
+ uint32_t ldon_head;
+ int ldon_val;
+ int ret;
+
+ if (!np) {
+ dev_err(dev, "failed to find buck10 regulator node\n");
+ return -ENODEV;
+ }
+
+ ret = of_property_read_u32(np, "rohm,ldon-head-microvolt", &ldon_head);
+ if (ret == -EINVAL)
+ return 0;
+ if (ret)
+ return ret;
+
+ /*
+ * LDON_HEAD mode means the BUCK10 is used to supply LDOs 1-4 and
+ * the BUCK 10 voltage is automatically set to follow LDO 1-4
+ * settings. Thus the BUCK10 should not allow voltage [g/s]etting.
+ */
+ buck10_desc->ops = &bd72720_buck10_ldon_head_op;
+
+ ldon_val = ldon_head / 50000 + 1;
+ if (ldon_head > 300000) {
+ dev_warn(dev, "Unsupported LDON_HEAD, clamping to 300 mV\n");
+ ldon_val = 7;
+ }
+
+ return regmap_update_bits(regmap, BD72720_REG_LDO1_MODE2,
+ BD72720_MASK_LDON_HEAD, ldon_val);
+}
+
+static int bd72720_dt_parse(struct device *dev,
+ struct regulator_desc *buck10_desc,
+ struct regmap *regmap)
+{
+ struct device_node *nproot __free(device_node) =
+ of_get_child_by_name(dev->parent->of_node, "regulators");
+
+ if (!nproot) {
+ dev_err(dev, "failed to find regulators node\n");
+ return -ENODEV;
+ }
+
+ return bd72720_buck10_ldon_head_mode(dev, nproot, regmap, buck10_desc);
+}
+
static int bd71828_probe(struct platform_device *pdev)
{
- int i, j, ret;
+ int i, j, ret, num_regulators;
struct regulator_config config = {
.dev = pdev->dev.parent,
};
+ enum rohm_chip_type chip = platform_get_device_id(pdev)->driver_data;
+ struct bd71828_regulator_data *rdata;
config.regmap = dev_get_regmap(pdev->dev.parent, NULL);
if (!config.regmap)
return -ENODEV;
- for (i = 0; i < ARRAY_SIZE(bd71828_rdata); i++) {
+ switch (chip) {
+ case ROHM_CHIP_TYPE_BD72720:
+ rdata = devm_kmemdup(&pdev->dev, bd72720_rdata,
+ sizeof(bd72720_rdata), GFP_KERNEL);
+ if (!rdata)
+ return -ENOMEM;
+
+ ret = bd72720_dt_parse(&pdev->dev, &rdata[BD72720_BUCK10_DESC_INDEX].desc,
+ config.regmap);
+ if (ret)
+ return ret;
+
+ num_regulators = ARRAY_SIZE(bd72720_rdata);
+ break;
+
+ case ROHM_CHIP_TYPE_BD71828:
+ rdata = devm_kmemdup(&pdev->dev, bd71828_rdata,
+ sizeof(bd71828_rdata), GFP_KERNEL);
+ if (!rdata)
+ return -ENOMEM;
+
+ num_regulators = ARRAY_SIZE(bd71828_rdata);
+
+ break;
+ default:
+ return dev_err_probe(&pdev->dev, -EINVAL,
+ "Unsupported device\n");
+ }
+
+ for (i = 0; i < num_regulators; i++) {
struct regulator_dev *rdev;
- const struct bd71828_regulator_data *rd;
+ struct bd71828_regulator_data *rd;
+
+ rd = &rdata[i];
- rd = &bd71828_rdata[i];
+ config.driver_data = rd;
rdev = devm_regulator_register(&pdev->dev,
&rd->desc, &config);
if (IS_ERR(rdev))
@@ -714,12 +1690,20 @@ static int bd71828_probe(struct platform_device *pdev)
return 0;
}
+static const struct platform_device_id bd71828_pmic_id[] = {
+ { "bd71828-pmic", ROHM_CHIP_TYPE_BD71828 },
+ { "bd72720-pmic", ROHM_CHIP_TYPE_BD72720 },
+ { },
+};
+MODULE_DEVICE_TABLE(platform, bd71828_pmic_id);
+
static struct platform_driver bd71828_regulator = {
.driver = {
.name = "bd71828-pmic",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
.probe = bd71828_probe,
+ .id_table = bd71828_pmic_id,
};
module_platform_driver(bd71828_regulator);
@@ -727,4 +1711,3 @@ module_platform_driver(bd71828_regulator);
MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
MODULE_DESCRIPTION("BD71828 voltage regulator driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:bd71828-pmic");
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 50dc779f7f98..7ac18985e438 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -570,7 +570,8 @@ config RTC_DRV_BD70528
depends on MFD_ROHM_BD71828
help
If you say Y here you will get support for the RTC
- block on ROHM BD71815 and BD71828 Power Management IC.
+ block on ROHM BD71815, BD71828 and BD72720 Power
+ Management ICs.
This driver can also be built as a module. If so, the module
will be called rtc-bd70528.
diff --git a/drivers/rtc/rtc-bd70528.c b/drivers/rtc/rtc-bd70528.c
index 954ac4ef53e8..4c8599761b2e 100644
--- a/drivers/rtc/rtc-bd70528.c
+++ b/drivers/rtc/rtc-bd70528.c
@@ -7,6 +7,7 @@
#include <linux/bcd.h>
#include <linux/mfd/rohm-bd71815.h>
#include <linux/mfd/rohm-bd71828.h>
+#include <linux/mfd/rohm-bd72720.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
@@ -262,13 +263,13 @@ static int bd70528_probe(struct platform_device *pdev)
/*
* See also BD718XX_ALM_EN_OFFSET:
- * This works for BD71828 and BD71815 as they have same offset
- * between ALM0 start and ALM0_MASK. If new ICs are to be
- * added this requires proper check as ALM0_MASK is not located
- * at the end of ALM0 block - but after all ALM blocks so if
- * amount of ALMs differ the offset to enable/disable is likely
- * to be incorrect and enable/disable must be given as own
- * reg address here.
+ * This works for BD71828, BD71815, and BD72720 as they all
+ * have same offset between the ALM0 start and the ALM0_MASK.
+ * If new ICs are to be added this requires proper check as
+ * the ALM0_MASK is not located at the end of ALM0 block -
+ * but after all ALM blocks. If amount of ALMs differ, the
+ * offset to enable/disable is likely to be incorrect and
+ * enable/disable must be given as own reg address here.
*/
bd_rtc->bd718xx_alm_block_start = BD71815_REG_RTC_ALM_START;
hour_reg = BD71815_REG_HOUR;
@@ -278,6 +279,11 @@ static int bd70528_probe(struct platform_device *pdev)
bd_rtc->bd718xx_alm_block_start = BD71828_REG_RTC_ALM_START;
hour_reg = BD71828_REG_RTC_HOUR;
break;
+ case ROHM_CHIP_TYPE_BD72720:
+ bd_rtc->reg_time_start = BD72720_REG_RTC_START;
+ bd_rtc->bd718xx_alm_block_start = BD72720_REG_RTC_ALM_START;
+ hour_reg = BD72720_REG_RTC_HOUR;
+ break;
default:
dev_err(&pdev->dev, "Unknown chip\n");
return -ENOENT;
@@ -337,6 +343,7 @@ static int bd70528_probe(struct platform_device *pdev)
static const struct platform_device_id bd718x7_rtc_id[] = {
{ "bd71828-rtc", ROHM_CHIP_TYPE_BD71828 },
{ "bd71815-rtc", ROHM_CHIP_TYPE_BD71815 },
+ { "bd72720-rtc", ROHM_CHIP_TYPE_BD72720 },
{ },
};
MODULE_DEVICE_TABLE(platform, bd718x7_rtc_id);