Skip to content

Commit a22ddee

Browse files
kamel-bouharalag-linaro
authored andcommitted
mfd: Add max7360 support
Add core driver to support MAX7360 i2c chip, multi function device with keypad, GPIO, PWM, GPO and rotary encoder submodules. Signed-off-by: Kamel Bouhara <kamel.bouhara@bootlin.com> Co-developed-by: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com> Signed-off-by: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com> Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Link: https://lore.kernel.org/r/20250824-mdb-max7360-support-v14-2-435cfda2b1ea@bootlin.com Signed-off-by: Lee Jones <lee@kernel.org>
1 parent aee8144 commit a22ddee

4 files changed

Lines changed: 295 additions & 0 deletions

File tree

drivers/mfd/Kconfig

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2481,5 +2481,19 @@ config MFD_UPBOARD_FPGA
24812481
To compile this driver as a module, choose M here: the module will be
24822482
called upboard-fpga.
24832483

2484+
config MFD_MAX7360
2485+
tristate "Maxim MAX7360 I2C IO Expander"
2486+
depends on I2C
2487+
select MFD_CORE
2488+
select REGMAP_I2C
2489+
select REGMAP_IRQ
2490+
help
2491+
Say yes here to add support for Maxim MAX7360 device, embedding
2492+
keypad, rotary encoder, PWM and GPIO features.
2493+
2494+
This driver provides common support for accessing the device;
2495+
additional drivers must be enabled in order to use the functionality
2496+
of the device.
2497+
24842498
endmenu
24852499
endif

drivers/mfd/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ obj-$(CONFIG_MFD_DA9063) += da9063.o
163163
obj-$(CONFIG_MFD_DA9150) += da9150-core.o
164164

165165
obj-$(CONFIG_MFD_MAX14577) += max14577.o
166+
obj-$(CONFIG_MFD_MAX7360) += max7360.o
166167
obj-$(CONFIG_MFD_MAX77541) += max77541.o
167168
obj-$(CONFIG_MFD_MAX77620) += max77620.o
168169
obj-$(CONFIG_MFD_MAX77650) += max77650.o

drivers/mfd/max7360.c

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/*
3+
* Maxim MAX7360 Core Driver
4+
*
5+
* Copyright 2025 Bootlin
6+
*
7+
* Authors:
8+
* Kamel Bouhara <kamel.bouhara@bootlin.com>
9+
* Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
10+
*/
11+
12+
#include <linux/array_size.h>
13+
#include <linux/bits.h>
14+
#include <linux/delay.h>
15+
#include <linux/device/devres.h>
16+
#include <linux/dev_printk.h>
17+
#include <linux/err.h>
18+
#include <linux/i2c.h>
19+
#include <linux/interrupt.h>
20+
#include <linux/mfd/core.h>
21+
#include <linux/mfd/max7360.h>
22+
#include <linux/mod_devicetable.h>
23+
#include <linux/module.h>
24+
#include <linux/regmap.h>
25+
#include <linux/types.h>
26+
27+
static const struct mfd_cell max7360_cells[] = {
28+
{ .name = "max7360-pinctrl" },
29+
{ .name = "max7360-pwm" },
30+
{ .name = "max7360-keypad" },
31+
{ .name = "max7360-rotary" },
32+
{
33+
.name = "max7360-gpo",
34+
.of_compatible = "maxim,max7360-gpo",
35+
},
36+
{
37+
.name = "max7360-gpio",
38+
.of_compatible = "maxim,max7360-gpio",
39+
},
40+
};
41+
42+
static const struct regmap_range max7360_volatile_ranges[] = {
43+
regmap_reg_range(MAX7360_REG_KEYFIFO, MAX7360_REG_KEYFIFO),
44+
regmap_reg_range(MAX7360_REG_I2C_TIMEOUT, MAX7360_REG_RTR_CNT),
45+
};
46+
47+
static const struct regmap_access_table max7360_volatile_table = {
48+
.yes_ranges = max7360_volatile_ranges,
49+
.n_yes_ranges = ARRAY_SIZE(max7360_volatile_ranges),
50+
};
51+
52+
static const struct regmap_config max7360_regmap_config = {
53+
.reg_bits = 8,
54+
.val_bits = 8,
55+
.max_register = MAX7360_REG_PWMCFG(MAX7360_PORT_PWM_COUNT - 1),
56+
.volatile_table = &max7360_volatile_table,
57+
.cache_type = REGCACHE_MAPLE,
58+
};
59+
60+
static int max7360_mask_irqs(struct regmap *regmap)
61+
{
62+
struct device *dev = regmap_get_device(regmap);
63+
unsigned int val;
64+
int ret;
65+
66+
/*
67+
* GPIO/PWM interrupts are not masked on reset: as the MAX7360 "INTI"
68+
* interrupt line is shared between GPIOs and rotary encoder, this could
69+
* result in repeated spurious interrupts on the rotary encoder driver
70+
* if the GPIO driver is not loaded. Mask them now to avoid this
71+
* situation.
72+
*/
73+
for (unsigned int i = 0; i < MAX7360_PORT_PWM_COUNT; i++) {
74+
ret = regmap_write_bits(regmap, MAX7360_REG_PWMCFG(i),
75+
MAX7360_PORT_CFG_INTERRUPT_MASK,
76+
MAX7360_PORT_CFG_INTERRUPT_MASK);
77+
if (ret)
78+
return dev_err_probe(dev, ret,
79+
"Failed to write MAX7360 port configuration\n");
80+
}
81+
82+
/* Read GPIO in register, to ACK any pending IRQ. */
83+
ret = regmap_read(regmap, MAX7360_REG_GPIOIN, &val);
84+
if (ret)
85+
return dev_err_probe(dev, ret, "Failed to read GPIO values\n");
86+
87+
return 0;
88+
}
89+
90+
static int max7360_reset(struct regmap *regmap)
91+
{
92+
struct device *dev = regmap_get_device(regmap);
93+
int ret;
94+
95+
ret = regmap_write(regmap, MAX7360_REG_GPIOCFG, MAX7360_GPIO_CFG_GPIO_RST);
96+
if (ret) {
97+
dev_err(dev, "Failed to reset GPIO configuration: %x\n", ret);
98+
return ret;
99+
}
100+
101+
ret = regcache_drop_region(regmap, MAX7360_REG_GPIOCFG, MAX7360_REG_GPIO_LAST);
102+
if (ret) {
103+
dev_err(dev, "Failed to drop regmap cache: %x\n", ret);
104+
return ret;
105+
}
106+
107+
ret = regmap_write(regmap, MAX7360_REG_SLEEP, 0);
108+
if (ret) {
109+
dev_err(dev, "Failed to reset autosleep configuration: %x\n", ret);
110+
return ret;
111+
}
112+
113+
ret = regmap_write(regmap, MAX7360_REG_DEBOUNCE, 0);
114+
if (ret)
115+
dev_err(dev, "Failed to reset GPO port count: %x\n", ret);
116+
117+
return ret;
118+
}
119+
120+
static int max7360_probe(struct i2c_client *client)
121+
{
122+
struct device *dev = &client->dev;
123+
struct regmap *regmap;
124+
int ret;
125+
126+
regmap = devm_regmap_init_i2c(client, &max7360_regmap_config);
127+
if (IS_ERR(regmap))
128+
return dev_err_probe(dev, PTR_ERR(regmap), "Failed to initialise regmap\n");
129+
130+
ret = max7360_reset(regmap);
131+
if (ret)
132+
return dev_err_probe(dev, ret, "Failed to reset device\n");
133+
134+
/* Get the device out of shutdown mode. */
135+
ret = regmap_write_bits(regmap, MAX7360_REG_GPIOCFG,
136+
MAX7360_GPIO_CFG_GPIO_EN,
137+
MAX7360_GPIO_CFG_GPIO_EN);
138+
if (ret)
139+
return dev_err_probe(dev, ret, "Failed to enable GPIO and PWM module\n");
140+
141+
ret = max7360_mask_irqs(regmap);
142+
if (ret)
143+
return dev_err_probe(dev, ret, "Could not mask interrupts\n");
144+
145+
ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE,
146+
max7360_cells, ARRAY_SIZE(max7360_cells),
147+
NULL, 0, NULL);
148+
if (ret)
149+
return dev_err_probe(dev, ret, "Failed to register child devices\n");
150+
151+
return 0;
152+
}
153+
154+
static const struct of_device_id max7360_dt_match[] = {
155+
{ .compatible = "maxim,max7360" },
156+
{}
157+
};
158+
MODULE_DEVICE_TABLE(of, max7360_dt_match);
159+
160+
static struct i2c_driver max7360_driver = {
161+
.driver = {
162+
.name = "max7360",
163+
.of_match_table = max7360_dt_match,
164+
},
165+
.probe = max7360_probe,
166+
};
167+
module_i2c_driver(max7360_driver);
168+
169+
MODULE_DESCRIPTION("Maxim MAX7360 I2C IO Expander core driver");
170+
MODULE_AUTHOR("Kamel Bouhara <kamel.bouhara@bootlin.com>");
171+
MODULE_LICENSE("GPL");

include/linux/mfd/max7360.h

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/* SPDX-License-Identifier: GPL-2.0-only */
2+
3+
#ifndef __LINUX_MFD_MAX7360_H
4+
#define __LINUX_MFD_MAX7360_H
5+
6+
#include <linux/bits.h>
7+
8+
#define MAX7360_MAX_KEY_ROWS 8
9+
#define MAX7360_MAX_KEY_COLS 8
10+
#define MAX7360_MAX_KEY_NUM (MAX7360_MAX_KEY_ROWS * MAX7360_MAX_KEY_COLS)
11+
#define MAX7360_ROW_SHIFT 3
12+
13+
#define MAX7360_MAX_GPIO 8
14+
#define MAX7360_MAX_GPO 6
15+
#define MAX7360_PORT_PWM_COUNT 8
16+
#define MAX7360_PORT_RTR_PIN (MAX7360_PORT_PWM_COUNT - 1)
17+
18+
/*
19+
* MAX7360 registers
20+
*/
21+
#define MAX7360_REG_KEYFIFO 0x00
22+
#define MAX7360_REG_CONFIG 0x01
23+
#define MAX7360_REG_DEBOUNCE 0x02
24+
#define MAX7360_REG_INTERRUPT 0x03
25+
#define MAX7360_REG_PORTS 0x04
26+
#define MAX7360_REG_KEYREP 0x05
27+
#define MAX7360_REG_SLEEP 0x06
28+
29+
/*
30+
* MAX7360 GPIO registers
31+
*
32+
* All these registers are reset together when writing bit 3 of
33+
* MAX7360_REG_GPIOCFG.
34+
*/
35+
#define MAX7360_REG_GPIOCFG 0x40
36+
#define MAX7360_REG_GPIOCTRL 0x41
37+
#define MAX7360_REG_GPIODEB 0x42
38+
#define MAX7360_REG_GPIOCURR 0x43
39+
#define MAX7360_REG_GPIOOUTM 0x44
40+
#define MAX7360_REG_PWMCOM 0x45
41+
#define MAX7360_REG_RTRCFG 0x46
42+
#define MAX7360_REG_I2C_TIMEOUT 0x48
43+
#define MAX7360_REG_GPIOIN 0x49
44+
#define MAX7360_REG_RTR_CNT 0x4A
45+
#define MAX7360_REG_PWMBASE 0x50
46+
#define MAX7360_REG_PWMCFGBASE 0x58
47+
48+
#define MAX7360_REG_GPIO_LAST 0x5F
49+
50+
#define MAX7360_REG_PWM(x) (MAX7360_REG_PWMBASE + (x))
51+
#define MAX7360_REG_PWMCFG(x) (MAX7360_REG_PWMCFGBASE + (x))
52+
53+
/*
54+
* Configuration register bits
55+
*/
56+
#define MAX7360_FIFO_EMPTY 0x3F
57+
#define MAX7360_FIFO_OVERFLOW 0x7F
58+
#define MAX7360_FIFO_RELEASE BIT(6)
59+
#define MAX7360_FIFO_COL GENMASK(5, 3)
60+
#define MAX7360_FIFO_ROW GENMASK(2, 0)
61+
62+
#define MAX7360_CFG_SLEEP BIT(7)
63+
#define MAX7360_CFG_INTERRUPT BIT(5)
64+
#define MAX7360_CFG_KEY_RELEASE BIT(3)
65+
#define MAX7360_CFG_WAKEUP BIT(1)
66+
#define MAX7360_CFG_TIMEOUT BIT(0)
67+
68+
#define MAX7360_DEBOUNCE GENMASK(4, 0)
69+
#define MAX7360_DEBOUNCE_MIN 9
70+
#define MAX7360_DEBOUNCE_MAX 40
71+
#define MAX7360_PORTS GENMASK(8, 5)
72+
73+
#define MAX7360_INTERRUPT_TIME_MASK GENMASK(4, 0)
74+
#define MAX7360_INTERRUPT_FIFO_MASK GENMASK(7, 5)
75+
76+
#define MAX7360_PORT_CFG_INTERRUPT_MASK BIT(7)
77+
#define MAX7360_PORT_CFG_INTERRUPT_EDGES BIT(6)
78+
#define MAX7360_PORT_CFG_COMMON_PWM BIT(5)
79+
80+
/*
81+
* Autosleep register values
82+
*/
83+
#define MAX7360_AUTOSLEEP_8192MS 0x01
84+
#define MAX7360_AUTOSLEEP_4096MS 0x02
85+
#define MAX7360_AUTOSLEEP_2048MS 0x03
86+
#define MAX7360_AUTOSLEEP_1024MS 0x04
87+
#define MAX7360_AUTOSLEEP_512MS 0x05
88+
#define MAX7360_AUTOSLEEP_256MS 0x06
89+
90+
#define MAX7360_GPIO_CFG_RTR_EN BIT(7)
91+
#define MAX7360_GPIO_CFG_GPIO_EN BIT(4)
92+
#define MAX7360_GPIO_CFG_GPIO_RST BIT(3)
93+
94+
#define MAX7360_ROT_DEBOUNCE GENMASK(3, 0)
95+
#define MAX7360_ROT_DEBOUNCE_MIN 0
96+
#define MAX7360_ROT_DEBOUNCE_MAX 15
97+
#define MAX7360_ROT_INTCNT GENMASK(6, 4)
98+
#define MAX7360_ROT_INTCNT_DLY BIT(7)
99+
100+
#define MAX7360_INT_INTI 0
101+
#define MAX7360_INT_INTK 1
102+
103+
#define MAX7360_INT_GPIO 0
104+
#define MAX7360_INT_KEYPAD 1
105+
#define MAX7360_INT_ROTARY 2
106+
107+
#define MAX7360_NR_INTERNAL_IRQS 3
108+
109+
#endif

0 commit comments

Comments
 (0)