diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-02-11 21:53:39 +0300 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-02-11 21:53:39 +0300 |
| commit | d70178215211a7c73ecabeb55eeb0f8ef002bcab (patch) | |
| tree | b3943a90930022fb5f36a14d48ccc2742ace3dc3 /drivers | |
| parent | 893ace4df0f96b8ad066651453e0519d4ffe35ca (diff) | |
| parent | af9b4a56f0000fb11057e204ddfb05d72ba4dba0 (diff) | |
| download | linux-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')
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, ®, 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); |
