mirror of
https://github.com/torvalds/linux.git
synced 2026-08-04 22:38:46 +02:00
Immutable branch between MFD, GPIO, Input and PWM due for the v6.17 merge window
-----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEdrbJNaO+IJqU8IdIUa+KL4f8d2EFAmhlNWcACgkQUa+KL4f8 d2FKARAAqS2hF4Kf86wdNBaQofYbabXFIZYCbB4OYN/9bHj0yI2Yz/Z8THbEFxVx e/C7ArAf6iVU9ojVUuTnjAB7Nn2qPQ6SqIkNuzF2M9iZM9P1e//7UBzBJDcPnEKA mYUw6XqrskI59QVqfEs8AC1TYa9QYgNDLtOyEHuQ4YdEn1J4BoO0hS8PRTXATISK aSy9n5OlqqgTmVHY18F/Sps39ZqWx2dDQxH4sfJ4hN3marfdk5AxEKYO+sWpHaVC ahi1qqBni1pUXCFpl5Yf09UhTCwDdvpBGwyoPf5bhH6YB3HXxnP8+53dUXNZh2y1 CVILgPyMfdcMxBjYVSNP+uosnMdPVsM/88SLDiadBe7uAi4Qq2ZKWubXS4TQFkSI Du6uKKSIa630ovt8G9kfnN+wEMaxlPTpJ1sITGc6RScN6Q7h80lNnVXxfJZV961C fvbS1TPByMKr3NB0fqHW5DiKKwEdNgQwgK2uc+qyNIMiRMFMGzUja7c3hEWqGHE9 4vGwiMo+Vfohnq2HAeG17N7CPJynSiqxRUL/aSJRmNRAlFwRW9cbqUQ9d7fkaD9j tiNqzTC8qb0bwhlFTxO8hR/VWPU1L4PY7OghKENYfDV2MyvP0njeHFWpZOw3c68A /lObItFnc4Tq4l2q1WZuuVHFfCvhtNV+ge7wNvfxGLzkK/uGfe0= =Vo0F -----END PGP SIGNATURE----- Merge tag 'ib-mfd-gpio-input-pwm-v6.17' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd into gpio/for-next Immutable branch between MFD, GPIO, Input and PWM due for the v6.17 merge window
This commit is contained in:
commit
93c0505787
|
|
@ -15,14 +15,21 @@ description:
|
|||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- adi,adp5585-00 # Default
|
||||
- adi,adp5585-01 # 11 GPIOs
|
||||
- adi,adp5585-02 # No pull-up resistors by default on special pins
|
||||
- adi,adp5585-03 # Alternate I2C address
|
||||
- adi,adp5585-04 # Pull-down resistors on all pins by default
|
||||
- const: adi,adp5585
|
||||
oneOf:
|
||||
- items:
|
||||
- enum:
|
||||
- adi,adp5585-00 # Default
|
||||
- adi,adp5585-01 # 11 GPIOs
|
||||
- adi,adp5585-02 # No pull-up resistors by default on special pins
|
||||
- adi,adp5585-03 # Alternate I2C address
|
||||
- adi,adp5585-04 # Pull-down resistors on all pins by default
|
||||
- const: adi,adp5585
|
||||
- items:
|
||||
- enum:
|
||||
- adi,adp5589-00 # Default
|
||||
- adi,adp5589-01 # R4 defaulted to RESET1 output
|
||||
- adi,adp5589-02 # Pull-down resistors by default on special pins
|
||||
- const: adi,adp5589
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
|
@ -32,6 +39,9 @@ properties:
|
|||
|
||||
vdd-supply: true
|
||||
|
||||
reset-gpios:
|
||||
maxItems: 1
|
||||
|
||||
gpio-controller: true
|
||||
|
||||
'#gpio-cells':
|
||||
|
|
@ -42,6 +52,84 @@ properties:
|
|||
"#pwm-cells":
|
||||
const: 3
|
||||
|
||||
interrupt-controller: true
|
||||
|
||||
'#interrupt-cells':
|
||||
const: 2
|
||||
|
||||
poll-interval:
|
||||
enum: [10, 20, 30, 40]
|
||||
default: 10
|
||||
|
||||
adi,keypad-pins:
|
||||
description: Specifies the pins used for the keypad matrix.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
|
||||
adi,unlock-events:
|
||||
description:
|
||||
Specifies a maximum of 2 events that can be used to unlock the keypad.
|
||||
If this property is set, the keyboard will be locked and only unlocked
|
||||
after these keys/gpis are pressed. The value 127 serves as a wildcard which
|
||||
means any key can be used for unlocking.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
items:
|
||||
anyOf:
|
||||
- minimum: 1
|
||||
maximum: 88
|
||||
- minimum: 97
|
||||
maximum: 115
|
||||
- const: 127
|
||||
|
||||
adi,unlock-trigger-sec:
|
||||
description:
|
||||
Defines the time in which the second unlock event must occur after the
|
||||
first unlock event has occurred.
|
||||
maximum: 7
|
||||
default: 0
|
||||
|
||||
adi,reset1-events:
|
||||
description:
|
||||
Defines the trigger events (key/gpi presses) that can generate reset
|
||||
conditions one the reset1 block.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
minItems: 1
|
||||
maxItems: 3
|
||||
|
||||
adi,reset2-events:
|
||||
description:
|
||||
Defines the trigger events (key/gpi presses) that can generate reset
|
||||
conditions one the reset2 block.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
adi,reset1-active-high:
|
||||
description: Sets the reset1 signal as active high.
|
||||
type: boolean
|
||||
|
||||
adi,reset2-active-high:
|
||||
description: Sets the reset2 signal as active high.
|
||||
type: boolean
|
||||
|
||||
adi,rst-passthrough-enable:
|
||||
description: Allows the RST pin to override (OR with) the reset1 signal.
|
||||
type: boolean
|
||||
|
||||
adi,reset-trigger-ms:
|
||||
description:
|
||||
Defines the length of time that the reset events must be active before a
|
||||
reset signal is generated. All events must be active at the same time for
|
||||
the same duration.
|
||||
enum: [0, 1000, 1500, 2000, 2500, 3000, 3500, 4000]
|
||||
default: 0
|
||||
|
||||
adi,reset-pulse-width-us:
|
||||
description: Defines the pulse width of the reset signals.
|
||||
enum: [500, 1000, 2000, 10000]
|
||||
default: 500
|
||||
|
||||
patternProperties:
|
||||
"-hog(-[0-9]+)?$":
|
||||
type: object
|
||||
|
|
@ -49,14 +137,28 @@ patternProperties:
|
|||
required:
|
||||
- gpio-hog
|
||||
|
||||
dependencies:
|
||||
linux,keymap:
|
||||
- adi,keypad-pins
|
||||
- interrupts
|
||||
interrupt-controller:
|
||||
- interrupts
|
||||
adi,unlock-trigger-sec:
|
||||
- adi,unlock-events
|
||||
adi,reset1-active-high:
|
||||
- adi,reset1-events
|
||||
adi,rst-passtrough-enable:
|
||||
- adi,reset1-events
|
||||
adi,reset2-active-high:
|
||||
- adi,reset2-events
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- gpio-controller
|
||||
- "#gpio-cells"
|
||||
- "#pwm-cells"
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/input/matrix-keymap.yaml#
|
||||
- $ref: /schemas/input/input.yaml#
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
|
|
@ -64,9 +166,60 @@ allOf:
|
|||
const: adi,adp5585-01
|
||||
then:
|
||||
properties:
|
||||
adi,unlock-events: false
|
||||
adi,unlock-trigger-sec: false
|
||||
gpio-reserved-ranges: false
|
||||
else:
|
||||
reset-gpios: false
|
||||
adi,keypad-pins:
|
||||
minItems: 2
|
||||
maxItems: 11
|
||||
items:
|
||||
minimum: 0
|
||||
maximum: 10
|
||||
adi,reset1-events:
|
||||
items:
|
||||
anyOf:
|
||||
- minimum: 1
|
||||
maximum: 30
|
||||
- minimum: 37
|
||||
maximum: 47
|
||||
adi,reset2-events:
|
||||
items:
|
||||
anyOf:
|
||||
- minimum: 1
|
||||
maximum: 30
|
||||
- minimum: 37
|
||||
maximum: 47
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- adi,adp5585-00
|
||||
- adi,adp5585-02
|
||||
- adi,adp5585-03
|
||||
- adi,adp5585-04
|
||||
then:
|
||||
properties:
|
||||
adi,unlock-events: false
|
||||
adi,unlock-trigger-sec: false
|
||||
adi,keypad-pins:
|
||||
minItems: 2
|
||||
maxItems: 10
|
||||
items:
|
||||
enum: [0, 1, 2, 3, 4, 6, 7, 8, 9, 10]
|
||||
adi,reset1-events:
|
||||
items:
|
||||
anyOf:
|
||||
- minimum: 1
|
||||
maximum: 25
|
||||
- enum: [37, 38, 39, 40, 41, 43, 44, 45, 46, 47]
|
||||
adi,reset2-events:
|
||||
items:
|
||||
anyOf:
|
||||
- minimum: 1
|
||||
maximum: 25
|
||||
- enum: [37, 38, 39, 40, 41, 43, 44, 45, 46, 47]
|
||||
gpio-reserved-ranges:
|
||||
maxItems: 1
|
||||
items:
|
||||
|
|
@ -74,10 +227,44 @@ allOf:
|
|||
- const: 5
|
||||
- const: 1
|
||||
|
||||
additionalProperties: false
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- adi,adp5589-00
|
||||
- adi,adp5589-01
|
||||
- adi,adp5589-02
|
||||
then:
|
||||
properties:
|
||||
gpio-reserved-ranges: false
|
||||
adi,keypad-pins:
|
||||
minItems: 2
|
||||
maxItems: 19
|
||||
items:
|
||||
minimum: 0
|
||||
maximum: 18
|
||||
adi,reset1-events:
|
||||
items:
|
||||
anyOf:
|
||||
- minimum: 1
|
||||
maximum: 88
|
||||
- minimum: 97
|
||||
maximum: 115
|
||||
adi,reset2-events:
|
||||
items:
|
||||
anyOf:
|
||||
- minimum: 1
|
||||
maximum: 88
|
||||
- minimum: 97
|
||||
maximum: 115
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/input/input.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
|
@ -93,6 +280,33 @@ examples:
|
|||
gpio-reserved-ranges = <5 1>;
|
||||
|
||||
#pwm-cells = <3>;
|
||||
|
||||
interrupts = <16 IRQ_TYPE_EDGE_FALLING>;
|
||||
interrupt-parent = <&gpio>;
|
||||
|
||||
adi,reset1-events = <1 43>;
|
||||
adi,reset2-events = <2 3>;
|
||||
adi,reset-trigger-ms = <2000>;
|
||||
|
||||
/*
|
||||
* col0, col1, col2
|
||||
* row0, row1, row2
|
||||
*/
|
||||
adi,keypad-pins = <0 1 2 6 7 8>;
|
||||
|
||||
linux,keymap = <
|
||||
MATRIX_KEY(0x00, 0x00, KEY_1)
|
||||
MATRIX_KEY(0x00, 0x01, KEY_2)
|
||||
MATRIX_KEY(0x00, 0x02, KEY_3)
|
||||
|
||||
MATRIX_KEY(0x01, 0x00, KEY_A)
|
||||
MATRIX_KEY(0x01, 0x01, KEY_B)
|
||||
MATRIX_KEY(0x01, 0x02, KEY_C)
|
||||
|
||||
MATRIX_KEY(0x02, 0x00, BTN_1)
|
||||
MATRIX_KEY(0x02, 0x01, BTN_2)
|
||||
MATRIX_KEY(0x02, 0x02, BTN_3)
|
||||
>;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -39,8 +39,6 @@ properties:
|
|||
- ad,adm9240
|
||||
# AD5110 - Nonvolatile Digital Potentiometer
|
||||
- adi,ad5110
|
||||
# Analog Devices ADP5589 Keypad Decoder and I/O Expansion
|
||||
- adi,adp5589
|
||||
# Analog Devices LT7182S Dual Channel 6A, 20V PolyPhase Step-Down Silent Switcher
|
||||
- adi,lt7182s
|
||||
# AMS iAQ-Core VOC Sensor
|
||||
|
|
|
|||
|
|
@ -550,6 +550,7 @@ L: linux-pwm@vger.kernel.org
|
|||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/*/adi,adp5585*.yaml
|
||||
F: drivers/gpio/gpio-adp5585.c
|
||||
F: drivers/input/keyboard/adp5585-keys.c
|
||||
F: drivers/mfd/adp5585.c
|
||||
F: drivers/pwm/pwm-adp5585.c
|
||||
F: include/linux/mfd/adp5585.h
|
||||
|
|
|
|||
|
|
@ -1263,6 +1263,7 @@ config GPIO_ADP5520
|
|||
config GPIO_ADP5585
|
||||
tristate "GPIO Support for ADP5585"
|
||||
depends on MFD_ADP5585
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
This option enables support for the GPIO function found in the Analog
|
||||
Devices ADP5585.
|
||||
|
|
|
|||
|
|
@ -4,67 +4,131 @@
|
|||
*
|
||||
* Copyright 2022 NXP
|
||||
* Copyright 2024 Ideas on Board Oy
|
||||
* Copyright 2025 Analog Devices, Inc.
|
||||
*/
|
||||
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/container_of.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/mfd/adp5585.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#define ADP5585_GPIO_MAX 11
|
||||
/*
|
||||
* Bank 0 covers pins "GPIO 1/R0" to "GPIO 6/R5", numbered 0 to 5 by the
|
||||
* driver, and bank 1 covers pins "GPIO 7/C0" to "GPIO 11/C4", numbered 6 to
|
||||
* 10. Some variants of the ADP5585 don't support "GPIO 6/R5". As the driver
|
||||
* uses identical GPIO numbering for all variants to avoid confusion, GPIO 5 is
|
||||
* marked as reserved in the device tree for variants that don't support it.
|
||||
*/
|
||||
#define ADP5585_BANK(n) ((n) >= 6 ? 1 : 0)
|
||||
#define ADP5585_BIT(n) ((n) >= 6 ? BIT((n) - 6) : BIT(n))
|
||||
|
||||
/*
|
||||
* Bank 0 covers pins "GPIO 1/R0" to "GPIO 8/R7", numbered 0 to 7 by the
|
||||
* driver, bank 1 covers pins "GPIO 9/C0" to "GPIO 16/C7", numbered 8 to
|
||||
* 15 and bank 3 covers pins "GPIO 17/C8" to "GPIO 19/C10", numbered 16 to 18.
|
||||
*/
|
||||
#define ADP5589_BANK(n) ((n) >> 3)
|
||||
#define ADP5589_BIT(n) BIT((n) & 0x7)
|
||||
|
||||
struct adp5585_gpio_chip {
|
||||
int (*bank)(unsigned int off);
|
||||
int (*bit)(unsigned int off);
|
||||
unsigned int debounce_dis_a;
|
||||
unsigned int rpull_cfg_a;
|
||||
unsigned int gpo_data_a;
|
||||
unsigned int gpo_out_a;
|
||||
unsigned int gpio_dir_a;
|
||||
unsigned int gpi_stat_a;
|
||||
unsigned int gpi_int_lvl_a;
|
||||
unsigned int gpi_ev_a;
|
||||
unsigned int gpi_ev_min;
|
||||
unsigned int gpi_ev_max;
|
||||
bool has_bias_hole;
|
||||
};
|
||||
|
||||
struct adp5585_gpio_dev {
|
||||
struct gpio_chip gpio_chip;
|
||||
struct notifier_block nb;
|
||||
const struct adp5585_gpio_chip *info;
|
||||
struct regmap *regmap;
|
||||
unsigned long irq_mask;
|
||||
unsigned long irq_en;
|
||||
unsigned long irq_active_high;
|
||||
/* used for irqchip bus locking */
|
||||
struct mutex bus_lock;
|
||||
};
|
||||
|
||||
static int adp5585_gpio_bank(unsigned int off)
|
||||
{
|
||||
return ADP5585_BANK(off);
|
||||
}
|
||||
|
||||
static int adp5585_gpio_bit(unsigned int off)
|
||||
{
|
||||
return ADP5585_BIT(off);
|
||||
}
|
||||
|
||||
static int adp5589_gpio_bank(unsigned int off)
|
||||
{
|
||||
return ADP5589_BANK(off);
|
||||
}
|
||||
|
||||
static int adp5589_gpio_bit(unsigned int off)
|
||||
{
|
||||
return ADP5589_BIT(off);
|
||||
}
|
||||
|
||||
static int adp5585_gpio_get_direction(struct gpio_chip *chip, unsigned int off)
|
||||
{
|
||||
struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(chip);
|
||||
unsigned int bank = ADP5585_BANK(off);
|
||||
unsigned int bit = ADP5585_BIT(off);
|
||||
const struct adp5585_gpio_chip *info = adp5585_gpio->info;
|
||||
unsigned int val;
|
||||
|
||||
regmap_read(adp5585_gpio->regmap, ADP5585_GPIO_DIRECTION_A + bank, &val);
|
||||
regmap_read(adp5585_gpio->regmap, info->gpio_dir_a + info->bank(off), &val);
|
||||
|
||||
return val & bit ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN;
|
||||
return val & info->bit(off) ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN;
|
||||
}
|
||||
|
||||
static int adp5585_gpio_direction_input(struct gpio_chip *chip, unsigned int off)
|
||||
{
|
||||
struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(chip);
|
||||
unsigned int bank = ADP5585_BANK(off);
|
||||
unsigned int bit = ADP5585_BIT(off);
|
||||
const struct adp5585_gpio_chip *info = adp5585_gpio->info;
|
||||
|
||||
return regmap_clear_bits(adp5585_gpio->regmap,
|
||||
ADP5585_GPIO_DIRECTION_A + bank, bit);
|
||||
return regmap_clear_bits(adp5585_gpio->regmap, info->gpio_dir_a + info->bank(off),
|
||||
info->bit(off));
|
||||
}
|
||||
|
||||
static int adp5585_gpio_direction_output(struct gpio_chip *chip, unsigned int off, int val)
|
||||
{
|
||||
struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(chip);
|
||||
unsigned int bank = ADP5585_BANK(off);
|
||||
unsigned int bit = ADP5585_BIT(off);
|
||||
const struct adp5585_gpio_chip *info = adp5585_gpio->info;
|
||||
unsigned int bank = info->bank(off);
|
||||
unsigned int bit = info->bit(off);
|
||||
int ret;
|
||||
|
||||
ret = regmap_update_bits(adp5585_gpio->regmap,
|
||||
ADP5585_GPO_DATA_OUT_A + bank, bit,
|
||||
val ? bit : 0);
|
||||
ret = regmap_update_bits(adp5585_gpio->regmap, info->gpo_data_a + bank,
|
||||
bit, val ? bit : 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return regmap_set_bits(adp5585_gpio->regmap,
|
||||
ADP5585_GPIO_DIRECTION_A + bank, bit);
|
||||
return regmap_set_bits(adp5585_gpio->regmap, info->gpio_dir_a + bank,
|
||||
bit);
|
||||
}
|
||||
|
||||
static int adp5585_gpio_get_value(struct gpio_chip *chip, unsigned int off)
|
||||
{
|
||||
struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(chip);
|
||||
unsigned int bank = ADP5585_BANK(off);
|
||||
unsigned int bit = ADP5585_BIT(off);
|
||||
const struct adp5585_gpio_chip *info = adp5585_gpio->info;
|
||||
unsigned int bank = info->bank(off);
|
||||
unsigned int bit = info->bit(off);
|
||||
unsigned int reg;
|
||||
unsigned int val;
|
||||
|
||||
|
|
@ -79,8 +143,8 @@ static int adp5585_gpio_get_value(struct gpio_chip *chip, unsigned int off)
|
|||
* .direction_input(), .direction_output() or .set() operations racing
|
||||
* with this.
|
||||
*/
|
||||
regmap_read(adp5585_gpio->regmap, ADP5585_GPIO_DIRECTION_A + bank, &val);
|
||||
reg = val & bit ? ADP5585_GPO_DATA_OUT_A : ADP5585_GPI_STATUS_A;
|
||||
regmap_read(adp5585_gpio->regmap, info->gpio_dir_a + bank, &val);
|
||||
reg = val & bit ? info->gpo_data_a : info->gpi_stat_a;
|
||||
regmap_read(adp5585_gpio->regmap, reg + bank, &val);
|
||||
|
||||
return !!(val & bit);
|
||||
|
|
@ -90,17 +154,17 @@ static int adp5585_gpio_set_value(struct gpio_chip *chip, unsigned int off,
|
|||
int val)
|
||||
{
|
||||
struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(chip);
|
||||
unsigned int bank = ADP5585_BANK(off);
|
||||
unsigned int bit = ADP5585_BIT(off);
|
||||
const struct adp5585_gpio_chip *info = adp5585_gpio->info;
|
||||
unsigned int bit = adp5585_gpio->info->bit(off);
|
||||
|
||||
return regmap_update_bits(adp5585_gpio->regmap,
|
||||
ADP5585_GPO_DATA_OUT_A + bank,
|
||||
return regmap_update_bits(adp5585_gpio->regmap, info->gpo_data_a + info->bank(off),
|
||||
bit, val ? bit : 0);
|
||||
}
|
||||
|
||||
static int adp5585_gpio_set_bias(struct adp5585_gpio_dev *adp5585_gpio,
|
||||
unsigned int off, unsigned int bias)
|
||||
{
|
||||
const struct adp5585_gpio_chip *info = adp5585_gpio->info;
|
||||
unsigned int bit, reg, mask, val;
|
||||
|
||||
/*
|
||||
|
|
@ -108,8 +172,10 @@ static int adp5585_gpio_set_bias(struct adp5585_gpio_dev *adp5585_gpio,
|
|||
* consecutive registers ADP5585_RPULL_CONFIG_*, with a hole of 4 bits
|
||||
* after R5.
|
||||
*/
|
||||
bit = off * 2 + (off > 5 ? 4 : 0);
|
||||
reg = ADP5585_RPULL_CONFIG_A + bit / 8;
|
||||
bit = off * 2;
|
||||
if (info->has_bias_hole)
|
||||
bit += (off > 5 ? 4 : 0);
|
||||
reg = info->rpull_cfg_a + bit / 8;
|
||||
mask = ADP5585_Rx_PULL_CFG_MASK << (bit % 8);
|
||||
val = bias << (bit % 8);
|
||||
|
||||
|
|
@ -119,22 +185,22 @@ static int adp5585_gpio_set_bias(struct adp5585_gpio_dev *adp5585_gpio,
|
|||
static int adp5585_gpio_set_drive(struct adp5585_gpio_dev *adp5585_gpio,
|
||||
unsigned int off, enum pin_config_param drive)
|
||||
{
|
||||
unsigned int bank = ADP5585_BANK(off);
|
||||
unsigned int bit = ADP5585_BIT(off);
|
||||
const struct adp5585_gpio_chip *info = adp5585_gpio->info;
|
||||
unsigned int bit = adp5585_gpio->info->bit(off);
|
||||
|
||||
return regmap_update_bits(adp5585_gpio->regmap,
|
||||
ADP5585_GPO_OUT_MODE_A + bank, bit,
|
||||
info->gpo_out_a + info->bank(off), bit,
|
||||
drive == PIN_CONFIG_DRIVE_OPEN_DRAIN ? bit : 0);
|
||||
}
|
||||
|
||||
static int adp5585_gpio_set_debounce(struct adp5585_gpio_dev *adp5585_gpio,
|
||||
unsigned int off, unsigned int debounce)
|
||||
{
|
||||
unsigned int bank = ADP5585_BANK(off);
|
||||
unsigned int bit = ADP5585_BIT(off);
|
||||
const struct adp5585_gpio_chip *info = adp5585_gpio->info;
|
||||
unsigned int bit = adp5585_gpio->info->bit(off);
|
||||
|
||||
return regmap_update_bits(adp5585_gpio->regmap,
|
||||
ADP5585_DEBOUNCE_DIS_A + bank, bit,
|
||||
info->debounce_dis_a + info->bank(off), bit,
|
||||
debounce ? 0 : bit);
|
||||
}
|
||||
|
||||
|
|
@ -172,11 +238,175 @@ static int adp5585_gpio_set_config(struct gpio_chip *chip, unsigned int off,
|
|||
};
|
||||
}
|
||||
|
||||
static int adp5585_gpio_request(struct gpio_chip *chip, unsigned int off)
|
||||
{
|
||||
struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(chip);
|
||||
const struct adp5585_gpio_chip *info = adp5585_gpio->info;
|
||||
struct device *dev = chip->parent;
|
||||
struct adp5585_dev *adp5585 = dev_get_drvdata(dev->parent);
|
||||
const struct adp5585_regs *regs = adp5585->regs;
|
||||
int ret;
|
||||
|
||||
ret = test_and_set_bit(off, adp5585->pin_usage);
|
||||
if (ret)
|
||||
return -EBUSY;
|
||||
|
||||
/* make sure it's configured for GPIO */
|
||||
return regmap_clear_bits(adp5585_gpio->regmap,
|
||||
regs->pin_cfg_a + info->bank(off),
|
||||
info->bit(off));
|
||||
}
|
||||
|
||||
static void adp5585_gpio_free(struct gpio_chip *chip, unsigned int off)
|
||||
{
|
||||
struct device *dev = chip->parent;
|
||||
struct adp5585_dev *adp5585 = dev_get_drvdata(dev->parent);
|
||||
|
||||
clear_bit(off, adp5585->pin_usage);
|
||||
}
|
||||
|
||||
static int adp5585_gpio_key_event(struct notifier_block *nb, unsigned long key,
|
||||
void *data)
|
||||
{
|
||||
struct adp5585_gpio_dev *adp5585_gpio = container_of(nb, struct adp5585_gpio_dev, nb);
|
||||
struct device *dev = adp5585_gpio->gpio_chip.parent;
|
||||
unsigned long key_press = (unsigned long)data;
|
||||
unsigned int irq, irq_type;
|
||||
struct irq_data *irqd;
|
||||
bool active_high;
|
||||
unsigned int off;
|
||||
|
||||
/* make sure the event is for me */
|
||||
if (key < adp5585_gpio->info->gpi_ev_min || key > adp5585_gpio->info->gpi_ev_max)
|
||||
return NOTIFY_DONE;
|
||||
|
||||
off = key - adp5585_gpio->info->gpi_ev_min;
|
||||
active_high = test_bit(off, &adp5585_gpio->irq_active_high);
|
||||
|
||||
irq = irq_find_mapping(adp5585_gpio->gpio_chip.irq.domain, off);
|
||||
if (!irq)
|
||||
return NOTIFY_BAD;
|
||||
|
||||
irqd = irq_get_irq_data(irq);
|
||||
if (!irqd) {
|
||||
dev_err(dev, "Could not get irq(%u) data\n", irq);
|
||||
return NOTIFY_BAD;
|
||||
}
|
||||
|
||||
dev_dbg_ratelimited(dev, "gpio-keys event(%u) press=%lu, a_high=%u\n",
|
||||
off, key_press, active_high);
|
||||
|
||||
if (!active_high)
|
||||
key_press = !key_press;
|
||||
|
||||
irq_type = irqd_get_trigger_type(irqd);
|
||||
|
||||
if ((irq_type & IRQ_TYPE_EDGE_RISING && key_press) ||
|
||||
(irq_type & IRQ_TYPE_EDGE_FALLING && !key_press))
|
||||
handle_nested_irq(irq);
|
||||
|
||||
return NOTIFY_STOP;
|
||||
}
|
||||
|
||||
static void adp5585_irq_bus_lock(struct irq_data *d)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(gc);
|
||||
|
||||
mutex_lock(&adp5585_gpio->bus_lock);
|
||||
}
|
||||
|
||||
static void adp5585_irq_bus_sync_unlock(struct irq_data *d)
|
||||
{
|
||||
struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
|
||||
struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(chip);
|
||||
const struct adp5585_gpio_chip *info = adp5585_gpio->info;
|
||||
irq_hw_number_t hwirq = irqd_to_hwirq(d);
|
||||
bool active_high = test_bit(hwirq, &adp5585_gpio->irq_active_high);
|
||||
bool enabled = test_bit(hwirq, &adp5585_gpio->irq_en);
|
||||
bool masked = test_bit(hwirq, &adp5585_gpio->irq_mask);
|
||||
unsigned int bank = adp5585_gpio->info->bank(hwirq);
|
||||
unsigned int bit = adp5585_gpio->info->bit(hwirq);
|
||||
|
||||
if (masked && !enabled)
|
||||
goto out_unlock;
|
||||
if (!masked && enabled)
|
||||
goto out_unlock;
|
||||
|
||||
regmap_update_bits(adp5585_gpio->regmap, info->gpi_int_lvl_a + bank, bit,
|
||||
active_high ? bit : 0);
|
||||
regmap_update_bits(adp5585_gpio->regmap, info->gpi_ev_a + bank, bit,
|
||||
masked ? 0 : bit);
|
||||
assign_bit(hwirq, &adp5585_gpio->irq_en, !masked);
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&adp5585_gpio->bus_lock);
|
||||
}
|
||||
|
||||
static void adp5585_irq_mask(struct irq_data *d)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(gc);
|
||||
irq_hw_number_t hwirq = irqd_to_hwirq(d);
|
||||
|
||||
__set_bit(hwirq, &adp5585_gpio->irq_mask);
|
||||
gpiochip_disable_irq(gc, hwirq);
|
||||
}
|
||||
|
||||
static void adp5585_irq_unmask(struct irq_data *d)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(gc);
|
||||
irq_hw_number_t hwirq = irqd_to_hwirq(d);
|
||||
|
||||
gpiochip_enable_irq(gc, hwirq);
|
||||
__clear_bit(hwirq, &adp5585_gpio->irq_mask);
|
||||
}
|
||||
|
||||
static int adp5585_irq_set_type(struct irq_data *d, unsigned int type)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct adp5585_gpio_dev *adp5585_gpio = gpiochip_get_data(gc);
|
||||
irq_hw_number_t hwirq = irqd_to_hwirq(d);
|
||||
|
||||
if (!(type & IRQ_TYPE_EDGE_BOTH))
|
||||
return -EINVAL;
|
||||
|
||||
assign_bit(hwirq, &adp5585_gpio->irq_active_high,
|
||||
type == IRQ_TYPE_EDGE_RISING);
|
||||
|
||||
irq_set_handler_locked(d, handle_edge_irq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct irq_chip adp5585_irq_chip = {
|
||||
.name = "adp5585",
|
||||
.irq_mask = adp5585_irq_mask,
|
||||
.irq_unmask = adp5585_irq_unmask,
|
||||
.irq_bus_lock = adp5585_irq_bus_lock,
|
||||
.irq_bus_sync_unlock = adp5585_irq_bus_sync_unlock,
|
||||
.irq_set_type = adp5585_irq_set_type,
|
||||
.flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_IMMUTABLE,
|
||||
GPIOCHIP_IRQ_RESOURCE_HELPERS,
|
||||
};
|
||||
|
||||
static void adp5585_gpio_unreg_notifier(void *data)
|
||||
{
|
||||
struct adp5585_gpio_dev *adp5585_gpio = data;
|
||||
struct device *dev = adp5585_gpio->gpio_chip.parent;
|
||||
struct adp5585_dev *adp5585 = dev_get_drvdata(dev->parent);
|
||||
|
||||
blocking_notifier_chain_unregister(&adp5585->event_notifier,
|
||||
&adp5585_gpio->nb);
|
||||
}
|
||||
|
||||
static int adp5585_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct adp5585_dev *adp5585 = dev_get_drvdata(pdev->dev.parent);
|
||||
const struct platform_device_id *id = platform_get_device_id(pdev);
|
||||
struct adp5585_gpio_dev *adp5585_gpio;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct gpio_irq_chip *girq;
|
||||
struct gpio_chip *gc;
|
||||
int ret;
|
||||
|
||||
|
|
@ -186,6 +416,10 @@ static int adp5585_gpio_probe(struct platform_device *pdev)
|
|||
|
||||
adp5585_gpio->regmap = adp5585->regmap;
|
||||
|
||||
adp5585_gpio->info = (const struct adp5585_gpio_chip *)id->driver_data;
|
||||
if (!adp5585_gpio->info)
|
||||
return -ENODEV;
|
||||
|
||||
device_set_of_node_from_dev(dev, dev->parent);
|
||||
|
||||
gc = &adp5585_gpio->gpio_chip;
|
||||
|
|
@ -196,13 +430,43 @@ static int adp5585_gpio_probe(struct platform_device *pdev)
|
|||
gc->get = adp5585_gpio_get_value;
|
||||
gc->set_rv = adp5585_gpio_set_value;
|
||||
gc->set_config = adp5585_gpio_set_config;
|
||||
gc->request = adp5585_gpio_request;
|
||||
gc->free = adp5585_gpio_free;
|
||||
gc->can_sleep = true;
|
||||
|
||||
gc->base = -1;
|
||||
gc->ngpio = ADP5585_GPIO_MAX;
|
||||
gc->ngpio = adp5585->n_pins;
|
||||
gc->label = pdev->name;
|
||||
gc->owner = THIS_MODULE;
|
||||
|
||||
if (device_property_present(dev->parent, "interrupt-controller")) {
|
||||
if (!adp5585->irq)
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"Unable to serve as interrupt controller without IRQ\n");
|
||||
|
||||
girq = &adp5585_gpio->gpio_chip.irq;
|
||||
gpio_irq_chip_set_chip(girq, &adp5585_irq_chip);
|
||||
girq->handler = handle_bad_irq;
|
||||
girq->threaded = true;
|
||||
|
||||
adp5585_gpio->nb.notifier_call = adp5585_gpio_key_event;
|
||||
ret = blocking_notifier_chain_register(&adp5585->event_notifier,
|
||||
&adp5585_gpio->nb);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_add_action_or_reset(dev, adp5585_gpio_unreg_notifier,
|
||||
adp5585_gpio);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* everything masked by default */
|
||||
adp5585_gpio->irq_mask = ~0UL;
|
||||
|
||||
ret = devm_mutex_init(dev, &adp5585_gpio->bus_lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = devm_gpiochip_add_data(dev, &adp5585_gpio->gpio_chip,
|
||||
adp5585_gpio);
|
||||
if (ret)
|
||||
|
|
@ -211,8 +475,40 @@ static int adp5585_gpio_probe(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct adp5585_gpio_chip adp5585_gpio_chip_info = {
|
||||
.bank = adp5585_gpio_bank,
|
||||
.bit = adp5585_gpio_bit,
|
||||
.debounce_dis_a = ADP5585_DEBOUNCE_DIS_A,
|
||||
.rpull_cfg_a = ADP5585_RPULL_CONFIG_A,
|
||||
.gpo_data_a = ADP5585_GPO_DATA_OUT_A,
|
||||
.gpo_out_a = ADP5585_GPO_OUT_MODE_A,
|
||||
.gpio_dir_a = ADP5585_GPIO_DIRECTION_A,
|
||||
.gpi_stat_a = ADP5585_GPI_STATUS_A,
|
||||
.has_bias_hole = true,
|
||||
.gpi_ev_min = ADP5585_GPI_EVENT_START,
|
||||
.gpi_ev_max = ADP5585_GPI_EVENT_END,
|
||||
.gpi_int_lvl_a = ADP5585_GPI_INT_LEVEL_A,
|
||||
.gpi_ev_a = ADP5585_GPI_EVENT_EN_A,
|
||||
};
|
||||
|
||||
static const struct adp5585_gpio_chip adp5589_gpio_chip_info = {
|
||||
.bank = adp5589_gpio_bank,
|
||||
.bit = adp5589_gpio_bit,
|
||||
.debounce_dis_a = ADP5589_DEBOUNCE_DIS_A,
|
||||
.rpull_cfg_a = ADP5589_RPULL_CONFIG_A,
|
||||
.gpo_data_a = ADP5589_GPO_DATA_OUT_A,
|
||||
.gpo_out_a = ADP5589_GPO_OUT_MODE_A,
|
||||
.gpio_dir_a = ADP5589_GPIO_DIRECTION_A,
|
||||
.gpi_stat_a = ADP5589_GPI_STATUS_A,
|
||||
.gpi_ev_min = ADP5589_GPI_EVENT_START,
|
||||
.gpi_ev_max = ADP5589_GPI_EVENT_END,
|
||||
.gpi_int_lvl_a = ADP5589_GPI_INT_LEVEL_A,
|
||||
.gpi_ev_a = ADP5589_GPI_EVENT_EN_A,
|
||||
};
|
||||
|
||||
static const struct platform_device_id adp5585_gpio_id_table[] = {
|
||||
{ "adp5585-gpio" },
|
||||
{ "adp5585-gpio", (kernel_ulong_t)&adp5585_gpio_chip_info },
|
||||
{ "adp5589-gpio", (kernel_ulong_t)&adp5589_gpio_chip_info },
|
||||
{ /* Sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, adp5585_gpio_id_table);
|
||||
|
|
|
|||
|
|
@ -37,6 +37,17 @@ config KEYBOARD_ADP5520
|
|||
To compile this driver as a module, choose M here: the module will
|
||||
be called adp5520-keys.
|
||||
|
||||
config KEYBOARD_ADP5585
|
||||
tristate "ADP558x keypad support"
|
||||
depends on MFD_ADP5585
|
||||
select INPUT_MATRIXKMAP
|
||||
help
|
||||
This option enables support for the KEYPAD function found in the Analog
|
||||
Devices ADP5585 and similar devices.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called adp5585-keys.
|
||||
|
||||
config KEYBOARD_ADP5588
|
||||
tristate "ADP5588/87 I2C QWERTY Keypad and IO Expander"
|
||||
depends on I2C
|
||||
|
|
@ -50,16 +61,6 @@ config KEYBOARD_ADP5588
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called adp5588-keys.
|
||||
|
||||
config KEYBOARD_ADP5589
|
||||
tristate "ADP5585/ADP5589 I2C QWERTY Keypad and IO Expander"
|
||||
depends on I2C
|
||||
help
|
||||
Say Y here if you want to use a ADP5585/ADP5589 attached to your
|
||||
system I2C bus.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called adp5589-keys.
|
||||
|
||||
config KEYBOARD_AMIGA
|
||||
tristate "Amiga keyboard"
|
||||
depends on AMIGA
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@
|
|||
|
||||
obj-$(CONFIG_KEYBOARD_ADC) += adc-keys.o
|
||||
obj-$(CONFIG_KEYBOARD_ADP5520) += adp5520-keys.o
|
||||
obj-$(CONFIG_KEYBOARD_ADP5585) += adp5585-keys.o
|
||||
obj-$(CONFIG_KEYBOARD_ADP5588) += adp5588-keys.o
|
||||
obj-$(CONFIG_KEYBOARD_ADP5589) += adp5589-keys.o
|
||||
obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o
|
||||
obj-$(CONFIG_KEYBOARD_APPLESPI) += applespi.o
|
||||
obj-$(CONFIG_KEYBOARD_ATARI) += atakbd.o
|
||||
|
|
|
|||
371
drivers/input/keyboard/adp5585-keys.c
Normal file
371
drivers/input/keyboard/adp5585-keys.c
Normal file
|
|
@ -0,0 +1,371 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Analog Devices ADP5585 Keys driver
|
||||
*
|
||||
* Copyright (C) 2025 Analog Devices, Inc.
|
||||
*/
|
||||
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/container_of.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/find.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/matrix_keypad.h>
|
||||
#include <linux/mfd/adp5585.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
/* As needed for the matrix parsing code */
|
||||
#define ADP5589_MAX_KEYMAPSIZE 123
|
||||
|
||||
struct adp5585_kpad_chip {
|
||||
u8 key_ev_min;
|
||||
u8 key_ev_max;
|
||||
u8 max_rows;
|
||||
u8 max_cols;
|
||||
};
|
||||
|
||||
struct adp5585_kpad {
|
||||
const struct adp5585_kpad_chip *info;
|
||||
struct notifier_block nb;
|
||||
struct input_dev *input;
|
||||
unsigned short keycode[ADP5589_MAX_KEYMAPSIZE];
|
||||
struct device *dev;
|
||||
unsigned long keypad;
|
||||
int row_shift;
|
||||
};
|
||||
|
||||
static int adp5585_keys_validate_events(const struct adp5585_kpad *kpad,
|
||||
const u32 *events, u32 n_events)
|
||||
{
|
||||
unsigned int ev;
|
||||
u32 row, col;
|
||||
|
||||
for (ev = 0; ev < n_events; ev++) {
|
||||
if (events[ev] < kpad->info->key_ev_min ||
|
||||
events[ev] > kpad->info->key_ev_max)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* if the event is to be generated by the keymap, we need to make
|
||||
* sure that the pins are part of it!
|
||||
*/
|
||||
row = (events[ev] - 1) / kpad->info->max_cols;
|
||||
col = (events[ev] - 1) % kpad->info->max_cols;
|
||||
|
||||
if (test_bit(row, &kpad->keypad) &&
|
||||
test_bit(col + kpad->info->max_rows, &kpad->keypad))
|
||||
continue;
|
||||
|
||||
return dev_err_probe(kpad->dev, -EINVAL,
|
||||
"Invalid unlock/reset event(%u) not used in the keypad\n",
|
||||
events[ev]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adp5585_keys_check_special_events(const struct adp5585_dev *adp5585,
|
||||
const struct adp5585_kpad *kpad)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = adp5585_keys_validate_events(kpad, adp5585->unlock_keys,
|
||||
adp5585->nkeys_unlock);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = adp5585_keys_validate_events(kpad, adp5585->reset1_keys,
|
||||
adp5585->nkeys_reset1);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
return adp5585_keys_validate_events(kpad, adp5585->reset2_keys,
|
||||
adp5585->nkeys_reset2);
|
||||
}
|
||||
|
||||
static void adp5585_keys_pins_free(void *data)
|
||||
{
|
||||
struct adp5585_kpad *kpad = data;
|
||||
struct adp5585_dev *adp5585 = dev_get_drvdata(kpad->dev->parent);
|
||||
unsigned int pin;
|
||||
|
||||
for_each_set_bit(pin, &kpad->keypad, adp5585->n_pins)
|
||||
clear_bit(pin, adp5585->pin_usage);
|
||||
}
|
||||
|
||||
static int adp5585_keys_parse_fw(const struct adp5585_dev *adp5585,
|
||||
struct adp5585_kpad *kpad)
|
||||
{
|
||||
struct device *dev = kpad->dev;
|
||||
u32 cols = 0, rows = 0, pin;
|
||||
int error, n_pins;
|
||||
|
||||
/*
|
||||
* We do not check for errors (or no value) since the input device is
|
||||
* only added if this property is present in the first place.
|
||||
*/
|
||||
n_pins = device_property_count_u32(dev, "adi,keypad-pins");
|
||||
if (n_pins > adp5585->n_pins)
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"Too many keypad pins (%d) defined (max=%d)\n",
|
||||
n_pins, adp5585->n_pins);
|
||||
|
||||
unsigned int *keypad_pins __free(kfree) = kcalloc(n_pins, sizeof(*keypad_pins),
|
||||
GFP_KERNEL);
|
||||
if (!keypad_pins)
|
||||
return -ENOMEM;
|
||||
|
||||
error = device_property_read_u32_array(dev, "adi,keypad-pins",
|
||||
keypad_pins, n_pins);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/*
|
||||
* We can add the action here since it makes the code easier and nothing
|
||||
* "bad" will happen out of it. Worst case, it will be a no-op and no
|
||||
* bit will set.
|
||||
*/
|
||||
error = devm_add_action_or_reset(dev, adp5585_keys_pins_free, kpad);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
for (pin = 0; pin < n_pins; pin++) {
|
||||
if (keypad_pins[pin] >= adp5585->n_pins)
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"Invalid keypad pin(%u) defined\n",
|
||||
keypad_pins[pin]);
|
||||
|
||||
if (test_and_set_bit(keypad_pins[pin], adp5585->pin_usage))
|
||||
return dev_err_probe(dev, -EBUSY,
|
||||
"Keypad pin(%u) already used\n",
|
||||
keypad_pins[pin]);
|
||||
|
||||
__set_bit(keypad_pins[pin], &kpad->keypad);
|
||||
}
|
||||
|
||||
/*
|
||||
* Note that given that we get a mask (and the HW allows it), we
|
||||
* can have holes in our keypad (eg: row0, row1 and row7 enabled).
|
||||
* However, for the matrix parsing functions we need to pass the
|
||||
* number of rows/cols as the maximum row/col used plus 1. This
|
||||
* pretty much means we will also have holes in our SW keypad.
|
||||
*/
|
||||
|
||||
rows = find_last_bit(&kpad->keypad, kpad->info->max_rows) + 1;
|
||||
if (rows == kpad->info->max_rows + 1)
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"Now rows defined in the keypad!\n");
|
||||
|
||||
cols = find_last_bit(&kpad->keypad, kpad->info->max_cols + kpad->info->max_rows);
|
||||
if (cols < kpad->info->max_rows)
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"No columns defined in the keypad!\n");
|
||||
|
||||
cols = cols + 1 - kpad->info->max_rows;
|
||||
|
||||
error = matrix_keypad_build_keymap(NULL, NULL, rows, cols,
|
||||
kpad->keycode, kpad->input);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
kpad->row_shift = get_count_order(cols);
|
||||
|
||||
if (device_property_read_bool(kpad->dev, "autorepeat"))
|
||||
__set_bit(EV_REP, kpad->input->evbit);
|
||||
|
||||
error = adp5585_keys_check_special_events(adp5585, kpad);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adp5585_keys_setup(const struct adp5585_dev *adp5585,
|
||||
struct adp5585_kpad *kpad)
|
||||
{
|
||||
unsigned long keys_bits, start = 0, nbits = kpad->info->max_rows;
|
||||
const struct adp5585_regs *regs = adp5585->regs;
|
||||
unsigned int i = 0, max_cols = kpad->info->max_cols;
|
||||
int error;
|
||||
|
||||
/*
|
||||
* Take care as the below assumes max_rows is always less or equal than
|
||||
* 8 which is true for the supported devices. If we happen to add
|
||||
* another device we need to make sure this still holds true. Although
|
||||
* adding a new device is very unlikely.
|
||||
*/
|
||||
do {
|
||||
keys_bits = bitmap_read(&kpad->keypad, start, nbits);
|
||||
if (keys_bits) {
|
||||
error = regmap_write(adp5585->regmap, regs->pin_cfg_a + i,
|
||||
keys_bits);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
start += nbits;
|
||||
if (max_cols > 8) {
|
||||
nbits = 8;
|
||||
max_cols -= nbits;
|
||||
} else {
|
||||
nbits = max_cols;
|
||||
}
|
||||
|
||||
i++;
|
||||
} while (start < kpad->info->max_rows + kpad->info->max_cols);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adp5585_keys_ev_handle(struct notifier_block *nb, unsigned long key,
|
||||
void *data)
|
||||
{
|
||||
struct adp5585_kpad *kpad = container_of(nb, struct adp5585_kpad, nb);
|
||||
unsigned long key_press = (unsigned long)data;
|
||||
unsigned int row, col, code;
|
||||
|
||||
/* make sure the event is for us */
|
||||
if (key < kpad->info->key_ev_min || key > kpad->info->key_ev_max)
|
||||
return NOTIFY_DONE;
|
||||
|
||||
/*
|
||||
* Unlikely but lets be on the safe side! We do not return any error
|
||||
* because the event was indeed for us but with some weird value. So,
|
||||
* we still want the caller know that the right handler was called.
|
||||
*/
|
||||
if (!key)
|
||||
return NOTIFY_BAD;
|
||||
|
||||
row = (key - 1) / (kpad->info->max_cols);
|
||||
col = (key - 1) % (kpad->info->max_cols);
|
||||
code = MATRIX_SCAN_CODE(row, col, kpad->row_shift);
|
||||
|
||||
dev_dbg_ratelimited(kpad->dev, "report key(%lu) r(%d) c(%d) code(%d)\n",
|
||||
key, row, col, kpad->keycode[code]);
|
||||
|
||||
input_report_key(kpad->input, kpad->keycode[code], key_press);
|
||||
input_sync(kpad->input);
|
||||
|
||||
return NOTIFY_STOP;
|
||||
}
|
||||
|
||||
static void adp5585_keys_unreg_notifier(void *data)
|
||||
{
|
||||
struct adp5585_kpad *kpad = data;
|
||||
struct adp5585_dev *adp5585 = dev_get_drvdata(kpad->dev->parent);
|
||||
|
||||
blocking_notifier_chain_unregister(&adp5585->event_notifier,
|
||||
&kpad->nb);
|
||||
}
|
||||
|
||||
static int adp5585_keys_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct platform_device_id *id = platform_get_device_id(pdev);
|
||||
struct adp5585_dev *adp5585 = dev_get_drvdata(pdev->dev.parent);
|
||||
struct device *dev = &pdev->dev;
|
||||
struct adp5585_kpad *kpad;
|
||||
unsigned int revid;
|
||||
const char *phys;
|
||||
int error;
|
||||
|
||||
kpad = devm_kzalloc(dev, sizeof(*kpad), GFP_KERNEL);
|
||||
if (!kpad)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!adp5585->irq)
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"IRQ is mandatory for the keypad\n");
|
||||
|
||||
kpad->dev = dev;
|
||||
|
||||
kpad->input = devm_input_allocate_device(dev);
|
||||
if (!kpad->input)
|
||||
return -ENOMEM;
|
||||
|
||||
kpad->info = (const struct adp5585_kpad_chip *)id->driver_data;
|
||||
if (!kpad->info)
|
||||
return -ENODEV;
|
||||
|
||||
error = regmap_read(adp5585->regmap, ADP5585_ID, &revid);
|
||||
if (error)
|
||||
return dev_err_probe(dev, error, "Failed to read device ID\n");
|
||||
|
||||
phys = devm_kasprintf(dev, GFP_KERNEL, "%s/input0", pdev->name);
|
||||
if (!phys)
|
||||
return -ENOMEM;
|
||||
|
||||
kpad->input->name = pdev->name;
|
||||
kpad->input->phys = phys;
|
||||
|
||||
kpad->input->id.bustype = BUS_I2C;
|
||||
kpad->input->id.vendor = 0x0001;
|
||||
kpad->input->id.product = 0x0001;
|
||||
kpad->input->id.version = revid & ADP5585_REV_ID_MASK;
|
||||
|
||||
device_set_of_node_from_dev(dev, dev->parent);
|
||||
|
||||
error = adp5585_keys_parse_fw(adp5585, kpad);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = adp5585_keys_setup(adp5585, kpad);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
kpad->nb.notifier_call = adp5585_keys_ev_handle;
|
||||
error = blocking_notifier_chain_register(&adp5585->event_notifier,
|
||||
&kpad->nb);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = devm_add_action_or_reset(dev, adp5585_keys_unreg_notifier, kpad);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = input_register_device(kpad->input);
|
||||
if (error)
|
||||
return dev_err_probe(dev, error,
|
||||
"Failed to register input device\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct adp5585_kpad_chip adp5585_kpad_chip_info = {
|
||||
.max_rows = 6,
|
||||
.max_cols = 5,
|
||||
.key_ev_min = ADP5585_ROW5_KEY_EVENT_START,
|
||||
.key_ev_max = ADP5585_ROW5_KEY_EVENT_END,
|
||||
};
|
||||
|
||||
static const struct adp5585_kpad_chip adp5589_kpad_chip_info = {
|
||||
.max_rows = 8,
|
||||
.max_cols = 11,
|
||||
.key_ev_min = ADP5589_KEY_EVENT_START,
|
||||
.key_ev_max = ADP5589_KEY_EVENT_END,
|
||||
};
|
||||
|
||||
static const struct platform_device_id adp5585_keys_id_table[] = {
|
||||
{ "adp5585-keys", (kernel_ulong_t)&adp5585_kpad_chip_info },
|
||||
{ "adp5589-keys", (kernel_ulong_t)&adp5589_kpad_chip_info },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, adp5585_keys_id_table);
|
||||
|
||||
static struct platform_driver adp5585_keys_driver = {
|
||||
.driver = {
|
||||
.name = "adp5585-keys",
|
||||
},
|
||||
.probe = adp5585_keys_probe,
|
||||
.id_table = adp5585_keys_id_table,
|
||||
};
|
||||
module_platform_driver(adp5585_keys_driver);
|
||||
|
||||
MODULE_AUTHOR("Nuno Sá <nuno.sa@analog.com>");
|
||||
MODULE_DESCRIPTION("ADP5585 Keys Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -4,22 +4,40 @@
|
|||
*
|
||||
* Copyright 2022 NXP
|
||||
* Copyright 2024 Ideas on Board Oy
|
||||
* Copyright 2025 Analog Devices Inc.
|
||||
*/
|
||||
|
||||
#include <linux/array_size.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/mfd/adp5585.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
static const struct mfd_cell adp5585_devs[] = {
|
||||
{ .name = "adp5585-gpio", },
|
||||
{ .name = "adp5585-pwm", },
|
||||
enum {
|
||||
ADP5585_DEV_GPIO,
|
||||
ADP5585_DEV_PWM,
|
||||
ADP5585_DEV_INPUT,
|
||||
ADP5585_DEV_MAX
|
||||
};
|
||||
|
||||
static const struct mfd_cell adp5585_devs[ADP5585_DEV_MAX] = {
|
||||
MFD_CELL_NAME("adp5585-gpio"),
|
||||
MFD_CELL_NAME("adp5585-pwm"),
|
||||
MFD_CELL_NAME("adp5585-keys"),
|
||||
};
|
||||
|
||||
static const struct mfd_cell adp5589_devs[] = {
|
||||
MFD_CELL_NAME("adp5589-gpio"),
|
||||
MFD_CELL_NAME("adp5589-pwm"),
|
||||
MFD_CELL_NAME("adp5589-keys"),
|
||||
};
|
||||
|
||||
static const struct regmap_range adp5585_volatile_ranges[] = {
|
||||
|
|
@ -31,6 +49,15 @@ static const struct regmap_access_table adp5585_volatile_regs = {
|
|||
.n_yes_ranges = ARRAY_SIZE(adp5585_volatile_ranges),
|
||||
};
|
||||
|
||||
static const struct regmap_range adp5589_volatile_ranges[] = {
|
||||
regmap_reg_range(ADP5585_ID, ADP5589_GPI_STATUS_C),
|
||||
};
|
||||
|
||||
static const struct regmap_access_table adp5589_volatile_regs = {
|
||||
.yes_ranges = adp5589_volatile_ranges,
|
||||
.n_yes_ranges = ARRAY_SIZE(adp5589_volatile_ranges),
|
||||
};
|
||||
|
||||
/*
|
||||
* Chip variants differ in the default configuration of pull-up and pull-down
|
||||
* resistors, and therefore have different default register values:
|
||||
|
|
@ -74,46 +101,597 @@ static const u8 adp5585_regmap_defaults_04[ADP5585_MAX_REG + 1] = {
|
|||
/* 0x38 */ 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
enum adp5585_regmap_type {
|
||||
ADP5585_REGMAP_00,
|
||||
ADP5585_REGMAP_02,
|
||||
ADP5585_REGMAP_04,
|
||||
static const u8 adp5589_regmap_defaults_00[ADP5589_MAX_REG + 1] = {
|
||||
/* 0x00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* 0x08 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* 0x10 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* 0x18 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* 0x20 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* 0x28 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* 0x30 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* 0x38 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* 0x40 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* 0x48 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static const struct regmap_config adp5585_regmap_configs[] = {
|
||||
[ADP5585_REGMAP_00] = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = ADP5585_MAX_REG,
|
||||
.volatile_table = &adp5585_volatile_regs,
|
||||
.cache_type = REGCACHE_MAPLE,
|
||||
.reg_defaults_raw = adp5585_regmap_defaults_00,
|
||||
.num_reg_defaults_raw = sizeof(adp5585_regmap_defaults_00),
|
||||
},
|
||||
[ADP5585_REGMAP_02] = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = ADP5585_MAX_REG,
|
||||
.volatile_table = &adp5585_volatile_regs,
|
||||
.cache_type = REGCACHE_MAPLE,
|
||||
.reg_defaults_raw = adp5585_regmap_defaults_02,
|
||||
.num_reg_defaults_raw = sizeof(adp5585_regmap_defaults_02),
|
||||
},
|
||||
[ADP5585_REGMAP_04] = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = ADP5585_MAX_REG,
|
||||
.volatile_table = &adp5585_volatile_regs,
|
||||
.cache_type = REGCACHE_MAPLE,
|
||||
.reg_defaults_raw = adp5585_regmap_defaults_04,
|
||||
.num_reg_defaults_raw = sizeof(adp5585_regmap_defaults_04),
|
||||
},
|
||||
static const u8 adp5589_regmap_defaults_01[ADP5589_MAX_REG + 1] = {
|
||||
/* 0x00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* 0x08 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* 0x10 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* 0x18 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* 0x20 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* 0x28 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* 0x30 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* 0x38 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
|
||||
/* 0x40 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* 0x48 */ 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static const u8 adp5589_regmap_defaults_02[ADP5589_MAX_REG + 1] = {
|
||||
/* 0x00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* 0x08 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* 0x10 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* 0x18 */ 0x00, 0x41, 0x01, 0x00, 0x11, 0x04, 0x00, 0x00,
|
||||
/* 0x20 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* 0x28 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* 0x30 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* 0x38 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* 0x40 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* 0x48 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static const u8 *adp5585_regmap_defaults[ADP5585_MAX] = {
|
||||
[ADP5585_00] = adp5585_regmap_defaults_00,
|
||||
[ADP5585_01] = adp5585_regmap_defaults_00,
|
||||
[ADP5585_02] = adp5585_regmap_defaults_02,
|
||||
[ADP5585_03] = adp5585_regmap_defaults_00,
|
||||
[ADP5585_04] = adp5585_regmap_defaults_04,
|
||||
[ADP5589_00] = adp5589_regmap_defaults_00,
|
||||
[ADP5589_01] = adp5589_regmap_defaults_01,
|
||||
[ADP5589_02] = adp5589_regmap_defaults_02,
|
||||
};
|
||||
|
||||
static const struct regmap_config adp5585_regmap_config_template = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = ADP5585_MAX_REG,
|
||||
.volatile_table = &adp5585_volatile_regs,
|
||||
.cache_type = REGCACHE_MAPLE,
|
||||
.num_reg_defaults_raw = ADP5585_MAX_REG + 1,
|
||||
};
|
||||
|
||||
static const struct regmap_config adp5589_regmap_config_template = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = ADP5589_MAX_REG,
|
||||
.volatile_table = &adp5589_volatile_regs,
|
||||
.cache_type = REGCACHE_MAPLE,
|
||||
.num_reg_defaults_raw = ADP5589_MAX_REG + 1,
|
||||
};
|
||||
|
||||
static const struct adp5585_regs adp5585_regs = {
|
||||
.ext_cfg = ADP5585_PIN_CONFIG_C,
|
||||
.int_en = ADP5585_INT_EN,
|
||||
.gen_cfg = ADP5585_GENERAL_CFG,
|
||||
.poll_ptime_cfg = ADP5585_POLL_PTIME_CFG,
|
||||
.reset_cfg = ADP5585_RESET_CFG,
|
||||
.reset1_event_a = ADP5585_RESET1_EVENT_A,
|
||||
.reset2_event_a = ADP5585_RESET2_EVENT_A,
|
||||
.pin_cfg_a = ADP5585_PIN_CONFIG_A,
|
||||
};
|
||||
|
||||
static const struct adp5585_regs adp5589_regs = {
|
||||
.ext_cfg = ADP5589_PIN_CONFIG_D,
|
||||
.int_en = ADP5589_INT_EN,
|
||||
.gen_cfg = ADP5589_GENERAL_CFG,
|
||||
.poll_ptime_cfg = ADP5589_POLL_PTIME_CFG,
|
||||
.reset_cfg = ADP5589_RESET_CFG,
|
||||
.reset1_event_a = ADP5589_RESET1_EVENT_A,
|
||||
.reset2_event_a = ADP5589_RESET2_EVENT_A,
|
||||
.pin_cfg_a = ADP5589_PIN_CONFIG_A,
|
||||
};
|
||||
|
||||
static int adp5585_validate_event(const struct adp5585_dev *adp5585, unsigned int ev)
|
||||
{
|
||||
if (adp5585->has_pin6) {
|
||||
if (ev >= ADP5585_ROW5_KEY_EVENT_START && ev <= ADP5585_ROW5_KEY_EVENT_END)
|
||||
return 0;
|
||||
if (ev >= ADP5585_GPI_EVENT_START && ev <= ADP5585_GPI_EVENT_END)
|
||||
return 0;
|
||||
|
||||
return dev_err_probe(adp5585->dev, -EINVAL,
|
||||
"Invalid unlock/reset event(%u) for this device\n", ev);
|
||||
}
|
||||
|
||||
if (ev >= ADP5585_KEY_EVENT_START && ev <= ADP5585_KEY_EVENT_END)
|
||||
return 0;
|
||||
if (ev >= ADP5585_GPI_EVENT_START && ev <= ADP5585_GPI_EVENT_END) {
|
||||
/*
|
||||
* Some variants of the adp5585 do not have the Row 5
|
||||
* (meaning pin 6 or GPIO 6) available. Instead that pin serves
|
||||
* as a reset pin. So, we need to make sure no event is
|
||||
* configured for it.
|
||||
*/
|
||||
if (ev == (ADP5585_GPI_EVENT_START + 5))
|
||||
return dev_err_probe(adp5585->dev, -EINVAL,
|
||||
"Invalid unlock/reset event(%u). R5 not available\n",
|
||||
ev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return dev_err_probe(adp5585->dev, -EINVAL,
|
||||
"Invalid unlock/reset event(%u) for this device\n", ev);
|
||||
}
|
||||
|
||||
static int adp5589_validate_event(const struct adp5585_dev *adp5585, unsigned int ev)
|
||||
{
|
||||
if (ev >= ADP5589_KEY_EVENT_START && ev <= ADP5589_KEY_EVENT_END)
|
||||
return 0;
|
||||
if (ev >= ADP5589_GPI_EVENT_START && ev <= ADP5589_GPI_EVENT_END)
|
||||
return 0;
|
||||
|
||||
return dev_err_probe(adp5585->dev, -EINVAL,
|
||||
"Invalid unlock/reset event(%u) for this device\n", ev);
|
||||
}
|
||||
|
||||
static struct regmap_config *adp5585_fill_variant_config(struct adp5585_dev *adp5585)
|
||||
{
|
||||
struct regmap_config *regmap_config;
|
||||
|
||||
switch (adp5585->variant) {
|
||||
case ADP5585_00:
|
||||
case ADP5585_01:
|
||||
case ADP5585_02:
|
||||
case ADP5585_03:
|
||||
case ADP5585_04:
|
||||
adp5585->id = ADP5585_MAN_ID_VALUE;
|
||||
adp5585->regs = &adp5585_regs;
|
||||
adp5585->n_pins = ADP5585_PIN_MAX;
|
||||
adp5585->reset2_out = ADP5585_RESET2_OUT;
|
||||
if (adp5585->variant == ADP5585_01)
|
||||
adp5585->has_pin6 = true;
|
||||
regmap_config = devm_kmemdup(adp5585->dev, &adp5585_regmap_config_template,
|
||||
sizeof(*regmap_config), GFP_KERNEL);
|
||||
break;
|
||||
case ADP5589_00:
|
||||
case ADP5589_01:
|
||||
case ADP5589_02:
|
||||
adp5585->id = ADP5589_MAN_ID_VALUE;
|
||||
adp5585->regs = &adp5589_regs;
|
||||
adp5585->has_unlock = true;
|
||||
adp5585->has_pin6 = true;
|
||||
adp5585->n_pins = ADP5589_PIN_MAX;
|
||||
adp5585->reset2_out = ADP5589_RESET2_OUT;
|
||||
regmap_config = devm_kmemdup(adp5585->dev, &adp5589_regmap_config_template,
|
||||
sizeof(*regmap_config), GFP_KERNEL);
|
||||
break;
|
||||
default:
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
if (!regmap_config)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
regmap_config->reg_defaults_raw = adp5585_regmap_defaults[adp5585->variant];
|
||||
|
||||
return regmap_config;
|
||||
}
|
||||
|
||||
static int adp5585_parse_ev_array(const struct adp5585_dev *adp5585, const char *prop, u32 *events,
|
||||
u32 *n_events, u32 max_evs, bool reset_ev)
|
||||
{
|
||||
struct device *dev = adp5585->dev;
|
||||
unsigned int ev;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* The device has the capability of handling special events through GPIs or a Keypad:
|
||||
* unlock events: Unlock the keymap until one of the configured events is detected.
|
||||
* reset events: Generate a reset pulse when one of the configured events is detected.
|
||||
*/
|
||||
ret = device_property_count_u32(dev, prop);
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
|
||||
*n_events = ret;
|
||||
|
||||
if (!adp5585->has_unlock && !reset_ev)
|
||||
return dev_err_probe(dev, -EOPNOTSUPP, "Unlock keys not supported\n");
|
||||
|
||||
if (*n_events > max_evs)
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"Invalid number of keys(%u > %u) for %s\n",
|
||||
*n_events, max_evs, prop);
|
||||
|
||||
ret = device_property_read_u32_array(dev, prop, events, *n_events);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (ev = 0; ev < *n_events; ev++) {
|
||||
if (!reset_ev && events[ev] == ADP5589_UNLOCK_WILDCARD)
|
||||
continue;
|
||||
|
||||
if (adp5585->id == ADP5585_MAN_ID_VALUE)
|
||||
ret = adp5585_validate_event(adp5585, events[ev]);
|
||||
else
|
||||
ret = adp5589_validate_event(adp5585, events[ev]);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adp5585_unlock_ev_parse(struct adp5585_dev *adp5585)
|
||||
{
|
||||
struct device *dev = adp5585->dev;
|
||||
int ret;
|
||||
|
||||
ret = adp5585_parse_ev_array(adp5585, "adi,unlock-events", adp5585->unlock_keys,
|
||||
&adp5585->nkeys_unlock, ARRAY_SIZE(adp5585->unlock_keys),
|
||||
false);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (!adp5585->nkeys_unlock)
|
||||
return 0;
|
||||
|
||||
ret = device_property_read_u32(dev, "adi,unlock-trigger-sec", &adp5585->unlock_time);
|
||||
if (!ret) {
|
||||
if (adp5585->unlock_time > ADP5585_MAX_UNLOCK_TIME_SEC)
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"Invalid unlock time(%u > %d)\n",
|
||||
adp5585->unlock_time,
|
||||
ADP5585_MAX_UNLOCK_TIME_SEC);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adp5585_reset_ev_parse(struct adp5585_dev *adp5585)
|
||||
{
|
||||
struct device *dev = adp5585->dev;
|
||||
u32 prop_val;
|
||||
int ret;
|
||||
|
||||
ret = adp5585_parse_ev_array(adp5585, "adi,reset1-events", adp5585->reset1_keys,
|
||||
&adp5585->nkeys_reset1,
|
||||
ARRAY_SIZE(adp5585->reset1_keys), true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adp5585_parse_ev_array(adp5585, "adi,reset2-events",
|
||||
adp5585->reset2_keys,
|
||||
&adp5585->nkeys_reset2,
|
||||
ARRAY_SIZE(adp5585->reset2_keys), true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!adp5585->nkeys_reset1 && !adp5585->nkeys_reset2)
|
||||
return 0;
|
||||
|
||||
if (adp5585->nkeys_reset1 && device_property_read_bool(dev, "adi,reset1-active-high"))
|
||||
adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET1_POL, 1);
|
||||
|
||||
if (adp5585->nkeys_reset2 && device_property_read_bool(dev, "adi,reset2-active-high"))
|
||||
adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET2_POL, 1);
|
||||
|
||||
if (device_property_read_bool(dev, "adi,rst-passthrough-enable"))
|
||||
adp5585->reset_cfg |= FIELD_PREP(ADP5585_RST_PASSTHRU_EN, 1);
|
||||
|
||||
ret = device_property_read_u32(dev, "adi,reset-trigger-ms", &prop_val);
|
||||
if (!ret) {
|
||||
switch (prop_val) {
|
||||
case 0:
|
||||
adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET_TRIG_TIME, 0);
|
||||
break;
|
||||
case 1000:
|
||||
adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET_TRIG_TIME, 1);
|
||||
break;
|
||||
case 1500:
|
||||
adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET_TRIG_TIME, 2);
|
||||
break;
|
||||
case 2000:
|
||||
adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET_TRIG_TIME, 3);
|
||||
break;
|
||||
case 2500:
|
||||
adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET_TRIG_TIME, 4);
|
||||
break;
|
||||
case 3000:
|
||||
adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET_TRIG_TIME, 5);
|
||||
break;
|
||||
case 3500:
|
||||
adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET_TRIG_TIME, 6);
|
||||
break;
|
||||
case 4000:
|
||||
adp5585->reset_cfg |= FIELD_PREP(ADP5585_RESET_TRIG_TIME, 7);
|
||||
break;
|
||||
default:
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"Invalid value(%u) for adi,reset-trigger-ms\n",
|
||||
prop_val);
|
||||
}
|
||||
}
|
||||
|
||||
ret = device_property_read_u32(dev, "adi,reset-pulse-width-us", &prop_val);
|
||||
if (!ret) {
|
||||
switch (prop_val) {
|
||||
case 500:
|
||||
adp5585->reset_cfg |= FIELD_PREP(ADP5585_PULSE_WIDTH, 0);
|
||||
break;
|
||||
case 1000:
|
||||
adp5585->reset_cfg |= FIELD_PREP(ADP5585_PULSE_WIDTH, 1);
|
||||
break;
|
||||
case 2000:
|
||||
adp5585->reset_cfg |= FIELD_PREP(ADP5585_PULSE_WIDTH, 2);
|
||||
break;
|
||||
case 10000:
|
||||
adp5585->reset_cfg |= FIELD_PREP(ADP5585_PULSE_WIDTH, 3);
|
||||
break;
|
||||
default:
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"Invalid value(%u) for adi,reset-pulse-width-us\n",
|
||||
prop_val);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adp5585_add_devices(const struct adp5585_dev *adp5585)
|
||||
{
|
||||
struct device *dev = adp5585->dev;
|
||||
const struct mfd_cell *cells;
|
||||
int ret;
|
||||
|
||||
if (adp5585->id == ADP5585_MAN_ID_VALUE)
|
||||
cells = adp5585_devs;
|
||||
else
|
||||
cells = adp5589_devs;
|
||||
|
||||
if (device_property_present(dev, "#pwm-cells")) {
|
||||
/* Make sure the PWM output pin is not used by the GPIO or INPUT devices */
|
||||
__set_bit(ADP5585_PWM_OUT, adp5585->pin_usage);
|
||||
ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO,
|
||||
&cells[ADP5585_DEV_PWM], 1, NULL, 0, NULL);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to add PWM device\n");
|
||||
}
|
||||
|
||||
if (device_property_present(dev, "#gpio-cells")) {
|
||||
ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO,
|
||||
&cells[ADP5585_DEV_GPIO], 1, NULL, 0, NULL);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to add GPIO device\n");
|
||||
}
|
||||
|
||||
if (device_property_present(adp5585->dev, "adi,keypad-pins")) {
|
||||
ret = devm_mfd_add_devices(adp5585->dev, PLATFORM_DEVID_AUTO,
|
||||
&cells[ADP5585_DEV_INPUT], 1, NULL, 0, NULL);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to add input device\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void adp5585_osc_disable(void *data)
|
||||
{
|
||||
const struct adp5585_dev *adp5585 = data;
|
||||
|
||||
regmap_write(adp5585->regmap, ADP5585_GENERAL_CFG, 0);
|
||||
}
|
||||
|
||||
static void adp5585_report_events(struct adp5585_dev *adp5585, int ev_cnt)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ev_cnt; i++) {
|
||||
unsigned long key_val, key_press;
|
||||
unsigned int key;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(adp5585->regmap, ADP5585_FIFO_1 + i, &key);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
key_val = FIELD_GET(ADP5585_KEY_EVENT_MASK, key);
|
||||
key_press = FIELD_GET(ADP5585_KEV_EV_PRESS_MASK, key);
|
||||
|
||||
blocking_notifier_call_chain(&adp5585->event_notifier, key_val, (void *)key_press);
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t adp5585_irq(int irq, void *data)
|
||||
{
|
||||
struct adp5585_dev *adp5585 = data;
|
||||
unsigned int status, ev_cnt;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(adp5585->regmap, ADP5585_INT_STATUS, &status);
|
||||
if (ret)
|
||||
return IRQ_HANDLED;
|
||||
|
||||
if (status & ADP5585_OVRFLOW_INT)
|
||||
dev_err_ratelimited(adp5585->dev, "Event overflow error\n");
|
||||
|
||||
if (!(status & ADP5585_EVENT_INT))
|
||||
goto out_irq;
|
||||
|
||||
ret = regmap_read(adp5585->regmap, ADP5585_STATUS, &ev_cnt);
|
||||
if (ret)
|
||||
goto out_irq;
|
||||
|
||||
ev_cnt = FIELD_GET(ADP5585_EC_MASK, ev_cnt);
|
||||
if (!ev_cnt)
|
||||
goto out_irq;
|
||||
|
||||
adp5585_report_events(adp5585, ev_cnt);
|
||||
out_irq:
|
||||
regmap_write(adp5585->regmap, ADP5585_INT_STATUS, status);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int adp5585_setup(struct adp5585_dev *adp5585)
|
||||
{
|
||||
const struct adp5585_regs *regs = adp5585->regs;
|
||||
unsigned int reg_val = 0, i;
|
||||
int ret;
|
||||
|
||||
/* If pin_6 (ROW5/GPI6) is not available, make sure to mark it as "busy" */
|
||||
if (!adp5585->has_pin6)
|
||||
__set_bit(ADP5585_ROW5, adp5585->pin_usage);
|
||||
|
||||
/* Configure the device with reset and unlock events */
|
||||
for (i = 0; i < adp5585->nkeys_unlock; i++) {
|
||||
ret = regmap_write(adp5585->regmap, ADP5589_UNLOCK1 + i,
|
||||
adp5585->unlock_keys[i] | ADP5589_UNLOCK_EV_PRESS);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (adp5585->nkeys_unlock) {
|
||||
ret = regmap_update_bits(adp5585->regmap, ADP5589_UNLOCK_TIMERS,
|
||||
ADP5589_UNLOCK_TIMER, adp5585->unlock_time);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_set_bits(adp5585->regmap, ADP5589_LOCK_CFG, ADP5589_LOCK_EN);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < adp5585->nkeys_reset1; i++) {
|
||||
ret = regmap_write(adp5585->regmap, regs->reset1_event_a + i,
|
||||
adp5585->reset1_keys[i] | ADP5585_RESET_EV_PRESS);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Mark that pin as not usable for the INPUT and GPIO devices. */
|
||||
__set_bit(ADP5585_RESET1_OUT, adp5585->pin_usage);
|
||||
}
|
||||
|
||||
for (i = 0; i < adp5585->nkeys_reset2; i++) {
|
||||
ret = regmap_write(adp5585->regmap, regs->reset2_event_a + i,
|
||||
adp5585->reset2_keys[i] | ADP5585_RESET_EV_PRESS);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
__set_bit(adp5585->reset2_out, adp5585->pin_usage);
|
||||
}
|
||||
|
||||
if (adp5585->nkeys_reset1 || adp5585->nkeys_reset2) {
|
||||
ret = regmap_write(adp5585->regmap, regs->reset_cfg, adp5585->reset_cfg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* If there's a reset1 event, then R4 is used as an output for the reset signal */
|
||||
if (adp5585->nkeys_reset1)
|
||||
reg_val = ADP5585_R4_EXTEND_CFG_RESET1;
|
||||
/* If there's a reset2 event, then C4 is used as an output for the reset signal */
|
||||
if (adp5585->nkeys_reset2)
|
||||
reg_val |= ADP5585_C4_EXTEND_CFG_RESET2;
|
||||
|
||||
ret = regmap_update_bits(adp5585->regmap, regs->ext_cfg,
|
||||
ADP5585_C4_EXTEND_CFG_MASK | ADP5585_R4_EXTEND_CFG_MASK,
|
||||
reg_val);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Clear any possible event by reading all the FIFO entries */
|
||||
for (i = 0; i < ADP5585_EV_MAX; i++) {
|
||||
ret = regmap_read(adp5585->regmap, ADP5585_FIFO_1 + i, ®_val);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_write(adp5585->regmap, regs->poll_ptime_cfg, adp5585->ev_poll_time);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Enable the internal oscillator, as it's shared between multiple
|
||||
* functions.
|
||||
*/
|
||||
ret = regmap_write(adp5585->regmap, regs->gen_cfg,
|
||||
ADP5585_OSC_FREQ_500KHZ | ADP5585_INT_CFG | ADP5585_OSC_EN);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return devm_add_action_or_reset(adp5585->dev, adp5585_osc_disable, adp5585);
|
||||
}
|
||||
|
||||
static int adp5585_parse_fw(struct adp5585_dev *adp5585)
|
||||
{
|
||||
unsigned int prop_val;
|
||||
int ret;
|
||||
|
||||
ret = device_property_read_u32(adp5585->dev, "poll-interval", &prop_val);
|
||||
if (!ret) {
|
||||
adp5585->ev_poll_time = prop_val / 10 - 1;
|
||||
/*
|
||||
* ev_poll_time is the raw value to be written on the register and 0 to 3 are the
|
||||
* valid values.
|
||||
*/
|
||||
if (adp5585->ev_poll_time > 3)
|
||||
return dev_err_probe(adp5585->dev, -EINVAL,
|
||||
"Invalid value(%u) for poll-interval\n", prop_val);
|
||||
}
|
||||
|
||||
ret = adp5585_unlock_ev_parse(adp5585);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return adp5585_reset_ev_parse(adp5585);
|
||||
}
|
||||
|
||||
static void adp5585_irq_disable(void *data)
|
||||
{
|
||||
struct adp5585_dev *adp5585 = data;
|
||||
|
||||
regmap_write(adp5585->regmap, adp5585->regs->int_en, 0);
|
||||
}
|
||||
|
||||
static int adp5585_irq_enable(struct i2c_client *i2c,
|
||||
struct adp5585_dev *adp5585)
|
||||
{
|
||||
const struct adp5585_regs *regs = adp5585->regs;
|
||||
unsigned int stat;
|
||||
int ret;
|
||||
|
||||
if (i2c->irq <= 0)
|
||||
return 0;
|
||||
|
||||
ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, adp5585_irq,
|
||||
IRQF_ONESHOT, i2c->name, adp5585);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Clear any possible outstanding interrupt before enabling them. We do that by reading
|
||||
* the status register and writing back the same value.
|
||||
*/
|
||||
ret = regmap_read(adp5585->regmap, ADP5585_INT_STATUS, &stat);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_write(adp5585->regmap, ADP5585_INT_STATUS, stat);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_write(adp5585->regmap, regs->int_en, ADP5585_OVRFLOW_IEN | ADP5585_EVENT_IEN);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return devm_add_action_or_reset(&i2c->dev, adp5585_irq_disable, adp5585);
|
||||
}
|
||||
|
||||
static int adp5585_i2c_probe(struct i2c_client *i2c)
|
||||
{
|
||||
const struct regmap_config *regmap_config;
|
||||
struct regmap_config *regmap_config;
|
||||
struct adp5585_dev *adp5585;
|
||||
struct gpio_desc *gpio;
|
||||
unsigned int id;
|
||||
int ret;
|
||||
|
||||
|
|
@ -122,8 +700,36 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
|
|||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(i2c, adp5585);
|
||||
adp5585->dev = &i2c->dev;
|
||||
adp5585->irq = i2c->irq;
|
||||
BLOCKING_INIT_NOTIFIER_HEAD(&adp5585->event_notifier);
|
||||
|
||||
adp5585->variant = (enum adp5585_variant)(uintptr_t)i2c_get_match_data(i2c);
|
||||
if (!adp5585->variant)
|
||||
return -ENODEV;
|
||||
|
||||
regmap_config = adp5585_fill_variant_config(adp5585);
|
||||
if (IS_ERR(regmap_config))
|
||||
return PTR_ERR(regmap_config);
|
||||
|
||||
ret = devm_regulator_get_enable(&i2c->dev, "vdd");
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
gpio = devm_gpiod_get_optional(&i2c->dev, "reset", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(gpio))
|
||||
return PTR_ERR(gpio);
|
||||
|
||||
/*
|
||||
* Note the timings are not documented anywhere in the datasheet. They are just
|
||||
* reasonable values that work.
|
||||
*/
|
||||
if (gpio) {
|
||||
fsleep(30);
|
||||
gpiod_set_value_cansleep(gpio, 0);
|
||||
fsleep(60);
|
||||
}
|
||||
|
||||
regmap_config = i2c_get_match_data(i2c);
|
||||
adp5585->regmap = devm_regmap_init_i2c(i2c, regmap_config);
|
||||
if (IS_ERR(adp5585->regmap))
|
||||
return dev_err_probe(&i2c->dev, PTR_ERR(adp5585->regmap),
|
||||
|
|
@ -134,24 +740,37 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
|
|||
return dev_err_probe(&i2c->dev, ret,
|
||||
"Failed to read device ID\n");
|
||||
|
||||
if ((id & ADP5585_MAN_ID_MASK) != ADP5585_MAN_ID_VALUE)
|
||||
id &= ADP5585_MAN_ID_MASK;
|
||||
if (id != adp5585->id)
|
||||
return dev_err_probe(&i2c->dev, -ENODEV,
|
||||
"Invalid device ID 0x%02x\n", id);
|
||||
|
||||
ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO,
|
||||
adp5585_devs, ARRAY_SIZE(adp5585_devs),
|
||||
NULL, 0, NULL);
|
||||
if (ret)
|
||||
return dev_err_probe(&i2c->dev, ret,
|
||||
"Failed to add child devices\n");
|
||||
adp5585->pin_usage = devm_bitmap_zalloc(&i2c->dev, adp5585->n_pins, GFP_KERNEL);
|
||||
if (!adp5585->pin_usage)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
ret = adp5585_parse_fw(adp5585);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adp5585_setup(adp5585);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adp5585_add_devices(adp5585);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return adp5585_irq_enable(i2c, adp5585);
|
||||
}
|
||||
|
||||
static int adp5585_suspend(struct device *dev)
|
||||
{
|
||||
struct adp5585_dev *adp5585 = dev_get_drvdata(dev);
|
||||
|
||||
if (adp5585->irq)
|
||||
disable_irq(adp5585->irq);
|
||||
|
||||
regcache_cache_only(adp5585->regmap, true);
|
||||
|
||||
return 0;
|
||||
|
|
@ -160,11 +779,19 @@ static int adp5585_suspend(struct device *dev)
|
|||
static int adp5585_resume(struct device *dev)
|
||||
{
|
||||
struct adp5585_dev *adp5585 = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
regcache_cache_only(adp5585->regmap, false);
|
||||
regcache_mark_dirty(adp5585->regmap);
|
||||
|
||||
return regcache_sync(adp5585->regmap);
|
||||
ret = regcache_sync(adp5585->regmap);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (adp5585->irq)
|
||||
enable_irq(adp5585->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(adp5585_pm, adp5585_suspend, adp5585_resume);
|
||||
|
|
@ -172,19 +799,31 @@ static DEFINE_SIMPLE_DEV_PM_OPS(adp5585_pm, adp5585_suspend, adp5585_resume);
|
|||
static const struct of_device_id adp5585_of_match[] = {
|
||||
{
|
||||
.compatible = "adi,adp5585-00",
|
||||
.data = &adp5585_regmap_configs[ADP5585_REGMAP_00],
|
||||
.data = (void *)ADP5585_00,
|
||||
}, {
|
||||
.compatible = "adi,adp5585-01",
|
||||
.data = &adp5585_regmap_configs[ADP5585_REGMAP_00],
|
||||
.data = (void *)ADP5585_01,
|
||||
}, {
|
||||
.compatible = "adi,adp5585-02",
|
||||
.data = &adp5585_regmap_configs[ADP5585_REGMAP_02],
|
||||
.data = (void *)ADP5585_02,
|
||||
}, {
|
||||
.compatible = "adi,adp5585-03",
|
||||
.data = &adp5585_regmap_configs[ADP5585_REGMAP_00],
|
||||
.data = (void *)ADP5585_03,
|
||||
}, {
|
||||
.compatible = "adi,adp5585-04",
|
||||
.data = &adp5585_regmap_configs[ADP5585_REGMAP_04],
|
||||
.data = (void *)ADP5585_04,
|
||||
}, {
|
||||
.compatible = "adi,adp5589-00",
|
||||
.data = (void *)ADP5589_00,
|
||||
}, {
|
||||
.compatible = "adi,adp5589-01",
|
||||
.data = (void *)ADP5589_01,
|
||||
}, {
|
||||
.compatible = "adi,adp5589-02",
|
||||
.data = (void *)ADP5589_02,
|
||||
}, {
|
||||
.compatible = "adi,adp5589",
|
||||
.data = (void *)ADP5589_00,
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
|
|
|||
|
|
@ -33,21 +33,33 @@
|
|||
#define ADP5585_PWM_MIN_PERIOD_NS (2ULL * NSEC_PER_SEC / ADP5585_PWM_OSC_FREQ_HZ)
|
||||
#define ADP5585_PWM_MAX_PERIOD_NS (2ULL * 0xffff * NSEC_PER_SEC / ADP5585_PWM_OSC_FREQ_HZ)
|
||||
|
||||
struct adp5585_pwm_chip {
|
||||
unsigned int pwm_cfg;
|
||||
unsigned int pwm_offt_low;
|
||||
unsigned int pwm_ont_low;
|
||||
};
|
||||
|
||||
struct adp5585_pwm {
|
||||
const struct adp5585_pwm_chip *info;
|
||||
struct regmap *regmap;
|
||||
unsigned int ext_cfg;
|
||||
};
|
||||
|
||||
static int pwm_adp5585_request(struct pwm_chip *chip, struct pwm_device *pwm)
|
||||
{
|
||||
struct regmap *regmap = pwmchip_get_drvdata(chip);
|
||||
struct adp5585_pwm *adp5585_pwm = pwmchip_get_drvdata(chip);
|
||||
|
||||
/* Configure the R3 pin as PWM output. */
|
||||
return regmap_update_bits(regmap, ADP5585_PIN_CONFIG_C,
|
||||
return regmap_update_bits(adp5585_pwm->regmap, adp5585_pwm->ext_cfg,
|
||||
ADP5585_R3_EXTEND_CFG_MASK,
|
||||
ADP5585_R3_EXTEND_CFG_PWM_OUT);
|
||||
}
|
||||
|
||||
static void pwm_adp5585_free(struct pwm_chip *chip, struct pwm_device *pwm)
|
||||
{
|
||||
struct regmap *regmap = pwmchip_get_drvdata(chip);
|
||||
struct adp5585_pwm *adp5585_pwm = pwmchip_get_drvdata(chip);
|
||||
|
||||
regmap_update_bits(regmap, ADP5585_PIN_CONFIG_C,
|
||||
regmap_update_bits(adp5585_pwm->regmap, adp5585_pwm->ext_cfg,
|
||||
ADP5585_R3_EXTEND_CFG_MASK,
|
||||
ADP5585_R3_EXTEND_CFG_GPIO4);
|
||||
}
|
||||
|
|
@ -56,15 +68,16 @@ static int pwm_adp5585_apply(struct pwm_chip *chip,
|
|||
struct pwm_device *pwm,
|
||||
const struct pwm_state *state)
|
||||
{
|
||||
struct regmap *regmap = pwmchip_get_drvdata(chip);
|
||||
struct adp5585_pwm *adp5585_pwm = pwmchip_get_drvdata(chip);
|
||||
const struct adp5585_pwm_chip *info = adp5585_pwm->info;
|
||||
struct regmap *regmap = adp5585_pwm->regmap;
|
||||
u64 period, duty_cycle;
|
||||
u32 on, off;
|
||||
__le16 val;
|
||||
int ret;
|
||||
|
||||
if (!state->enabled) {
|
||||
regmap_clear_bits(regmap, ADP5585_GENERAL_CFG, ADP5585_OSC_EN);
|
||||
regmap_clear_bits(regmap, ADP5585_PWM_CFG, ADP5585_PWM_EN);
|
||||
regmap_clear_bits(regmap, info->pwm_cfg, ADP5585_PWM_EN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -85,45 +98,43 @@ static int pwm_adp5585_apply(struct pwm_chip *chip,
|
|||
off = div_u64(period, NSEC_PER_SEC / ADP5585_PWM_OSC_FREQ_HZ) - on;
|
||||
|
||||
val = cpu_to_le16(off);
|
||||
ret = regmap_bulk_write(regmap, ADP5585_PWM_OFFT_LOW, &val, 2);
|
||||
ret = regmap_bulk_write(regmap, info->pwm_offt_low, &val, 2);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val = cpu_to_le16(on);
|
||||
ret = regmap_bulk_write(regmap, ADP5585_PWM_ONT_LOW, &val, 2);
|
||||
ret = regmap_bulk_write(regmap, info->pwm_ont_low, &val, 2);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Enable PWM in continuous mode and no external AND'ing. */
|
||||
ret = regmap_update_bits(regmap, ADP5585_PWM_CFG,
|
||||
ret = regmap_update_bits(regmap, info->pwm_cfg,
|
||||
ADP5585_PWM_IN_AND | ADP5585_PWM_MODE |
|
||||
ADP5585_PWM_EN, ADP5585_PWM_EN);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_set_bits(regmap, ADP5585_GENERAL_CFG, ADP5585_OSC_EN);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return regmap_set_bits(regmap, ADP5585_PWM_CFG, ADP5585_PWM_EN);
|
||||
return regmap_set_bits(regmap, info->pwm_cfg, ADP5585_PWM_EN);
|
||||
}
|
||||
|
||||
static int pwm_adp5585_get_state(struct pwm_chip *chip,
|
||||
struct pwm_device *pwm,
|
||||
struct pwm_state *state)
|
||||
{
|
||||
struct regmap *regmap = pwmchip_get_drvdata(chip);
|
||||
struct adp5585_pwm *adp5585_pwm = pwmchip_get_drvdata(chip);
|
||||
const struct adp5585_pwm_chip *info = adp5585_pwm->info;
|
||||
struct regmap *regmap = adp5585_pwm->regmap;
|
||||
unsigned int on, off;
|
||||
unsigned int val;
|
||||
__le16 on_off;
|
||||
int ret;
|
||||
|
||||
ret = regmap_bulk_read(regmap, ADP5585_PWM_OFFT_LOW, &on_off, 2);
|
||||
ret = regmap_bulk_read(regmap, info->pwm_offt_low, &on_off, 2);
|
||||
if (ret)
|
||||
return ret;
|
||||
off = le16_to_cpu(on_off);
|
||||
|
||||
ret = regmap_bulk_read(regmap, ADP5585_PWM_ONT_LOW, &on_off, 2);
|
||||
ret = regmap_bulk_read(regmap, info->pwm_ont_low, &on_off, 2);
|
||||
if (ret)
|
||||
return ret;
|
||||
on = le16_to_cpu(on_off);
|
||||
|
|
@ -133,7 +144,7 @@ static int pwm_adp5585_get_state(struct pwm_chip *chip,
|
|||
|
||||
state->polarity = PWM_POLARITY_NORMAL;
|
||||
|
||||
regmap_read(regmap, ADP5585_PWM_CFG, &val);
|
||||
regmap_read(regmap, info->pwm_cfg, &val);
|
||||
state->enabled = !!(val & ADP5585_PWM_EN);
|
||||
|
||||
return 0;
|
||||
|
|
@ -148,18 +159,28 @@ static const struct pwm_ops adp5585_pwm_ops = {
|
|||
|
||||
static int adp5585_pwm_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct platform_device_id *id = platform_get_device_id(pdev);
|
||||
struct device *dev = &pdev->dev;
|
||||
struct adp5585_dev *adp5585 = dev_get_drvdata(dev->parent);
|
||||
struct adp5585_pwm *adp5585_pwm;
|
||||
struct pwm_chip *chip;
|
||||
int ret;
|
||||
|
||||
chip = devm_pwmchip_alloc(dev, ADP5585_PWM_CHAN_NUM, 0);
|
||||
chip = devm_pwmchip_alloc(dev, ADP5585_PWM_CHAN_NUM,
|
||||
sizeof(*adp5585_pwm));
|
||||
if (IS_ERR(chip))
|
||||
return PTR_ERR(chip);
|
||||
|
||||
adp5585_pwm = pwmchip_get_drvdata(chip);
|
||||
adp5585_pwm->regmap = adp5585->regmap;
|
||||
adp5585_pwm->ext_cfg = adp5585->regs->ext_cfg;
|
||||
|
||||
adp5585_pwm->info = (const struct adp5585_pwm_chip *)id->driver_data;
|
||||
if (!adp5585_pwm->info)
|
||||
return -ENODEV;
|
||||
|
||||
device_set_of_node_from_dev(dev, dev->parent);
|
||||
|
||||
pwmchip_set_drvdata(chip, adp5585->regmap);
|
||||
chip->ops = &adp5585_pwm_ops;
|
||||
|
||||
ret = devm_pwmchip_add(dev, chip);
|
||||
|
|
@ -169,8 +190,21 @@ static int adp5585_pwm_probe(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct adp5585_pwm_chip adp5589_pwm_chip_info = {
|
||||
.pwm_cfg = ADP5585_PWM_CFG,
|
||||
.pwm_offt_low = ADP5585_PWM_OFFT_LOW,
|
||||
.pwm_ont_low = ADP5585_PWM_ONT_LOW,
|
||||
};
|
||||
|
||||
static const struct adp5585_pwm_chip adp5585_pwm_chip_info = {
|
||||
.pwm_cfg = ADP5589_PWM_CFG,
|
||||
.pwm_offt_low = ADP5589_PWM_OFFT_LOW,
|
||||
.pwm_ont_low = ADP5589_PWM_ONT_LOW,
|
||||
};
|
||||
|
||||
static const struct platform_device_id adp5585_pwm_id_table[] = {
|
||||
{ "adp5585-pwm" },
|
||||
{ "adp5585-pwm", (kernel_ulong_t)&adp5585_pwm_chip_info },
|
||||
{ "adp5589-pwm", (kernel_ulong_t)&adp5589_pwm_chip_info },
|
||||
{ /* Sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, adp5585_pwm_id_table);
|
||||
|
|
|
|||
|
|
@ -10,13 +10,20 @@
|
|||
#define __MFD_ADP5585_H_
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/notifier.h>
|
||||
|
||||
#define ADP5585_ID 0x00
|
||||
#define ADP5585_MAN_ID_VALUE 0x20
|
||||
#define ADP5585_MAN_ID_MASK GENMASK(7, 4)
|
||||
#define ADP5585_REV_ID_MASK GENMASK(3, 0)
|
||||
#define ADP5585_INT_STATUS 0x01
|
||||
#define ADP5585_OVRFLOW_INT BIT(2)
|
||||
#define ADP5585_EVENT_INT BIT(0)
|
||||
#define ADP5585_STATUS 0x02
|
||||
#define ADP5585_EC_MASK GENMASK(4, 0)
|
||||
#define ADP5585_FIFO_1 0x03
|
||||
#define ADP5585_KEV_EV_PRESS_MASK BIT(7)
|
||||
#define ADP5585_KEY_EVENT_MASK GENMASK(6, 0)
|
||||
#define ADP5585_FIFO_2 0x04
|
||||
#define ADP5585_FIFO_3 0x05
|
||||
#define ADP5585_FIFO_4 0x06
|
||||
|
|
@ -32,6 +39,7 @@
|
|||
#define ADP5585_FIFO_14 0x10
|
||||
#define ADP5585_FIFO_15 0x11
|
||||
#define ADP5585_FIFO_16 0x12
|
||||
#define ADP5585_EV_MAX (ADP5585_FIFO_16 - ADP5585_FIFO_1 + 1)
|
||||
#define ADP5585_GPI_INT_STAT_A 0x13
|
||||
#define ADP5585_GPI_INT_STAT_B 0x14
|
||||
#define ADP5585_GPI_STATUS_A 0x15
|
||||
|
|
@ -60,6 +68,7 @@
|
|||
#define ADP5585_GPIO_DIRECTION_A 0x27
|
||||
#define ADP5585_GPIO_DIRECTION_B 0x28
|
||||
#define ADP5585_RESET1_EVENT_A 0x29
|
||||
#define ADP5585_RESET_EV_PRESS BIT(7)
|
||||
#define ADP5585_RESET1_EVENT_B 0x2a
|
||||
#define ADP5585_RESET1_EVENT_C 0x2b
|
||||
#define ADP5585_RESET2_EVENT_A 0x2c
|
||||
|
|
@ -104,23 +113,114 @@
|
|||
#define ADP5585_INT_CFG BIT(1)
|
||||
#define ADP5585_RST_CFG BIT(0)
|
||||
#define ADP5585_INT_EN 0x3c
|
||||
#define ADP5585_OVRFLOW_IEN BIT(2)
|
||||
#define ADP5585_EVENT_IEN BIT(0)
|
||||
|
||||
#define ADP5585_MAX_REG ADP5585_INT_EN
|
||||
|
||||
/*
|
||||
* Bank 0 covers pins "GPIO 1/R0" to "GPIO 6/R5", numbered 0 to 5 by the
|
||||
* driver, and bank 1 covers pins "GPIO 7/C0" to "GPIO 11/C4", numbered 6 to
|
||||
* 10. Some variants of the ADP5585 don't support "GPIO 6/R5". As the driver
|
||||
* uses identical GPIO numbering for all variants to avoid confusion, GPIO 5 is
|
||||
* marked as reserved in the device tree for variants that don't support it.
|
||||
*/
|
||||
#define ADP5585_BANK(n) ((n) >= 6 ? 1 : 0)
|
||||
#define ADP5585_BIT(n) ((n) >= 6 ? BIT((n) - 6) : BIT(n))
|
||||
#define ADP5585_PIN_MAX 11
|
||||
#define ADP5585_MAX_UNLOCK_TIME_SEC 7
|
||||
#define ADP5585_KEY_EVENT_START 1
|
||||
#define ADP5585_KEY_EVENT_END 25
|
||||
#define ADP5585_GPI_EVENT_START 37
|
||||
#define ADP5585_GPI_EVENT_END 47
|
||||
#define ADP5585_ROW5_KEY_EVENT_START 1
|
||||
#define ADP5585_ROW5_KEY_EVENT_END 30
|
||||
#define ADP5585_PWM_OUT 3
|
||||
#define ADP5585_RESET1_OUT 4
|
||||
#define ADP5585_RESET2_OUT 9
|
||||
#define ADP5585_ROW5 5
|
||||
|
||||
/* ADP5589 */
|
||||
#define ADP5589_MAN_ID_VALUE 0x10
|
||||
#define ADP5589_GPI_STATUS_A 0x16
|
||||
#define ADP5589_GPI_STATUS_C 0x18
|
||||
#define ADP5589_RPULL_CONFIG_A 0x19
|
||||
#define ADP5589_GPI_INT_LEVEL_A 0x1e
|
||||
#define ADP5589_GPI_EVENT_EN_A 0x21
|
||||
#define ADP5589_DEBOUNCE_DIS_A 0x27
|
||||
#define ADP5589_GPO_DATA_OUT_A 0x2a
|
||||
#define ADP5589_GPO_OUT_MODE_A 0x2d
|
||||
#define ADP5589_GPIO_DIRECTION_A 0x30
|
||||
#define ADP5589_UNLOCK1 0x33
|
||||
#define ADP5589_UNLOCK_EV_PRESS BIT(7)
|
||||
#define ADP5589_UNLOCK_TIMERS 0x36
|
||||
#define ADP5589_UNLOCK_TIMER GENMASK(2, 0)
|
||||
#define ADP5589_LOCK_CFG 0x37
|
||||
#define ADP5589_LOCK_EN BIT(0)
|
||||
#define ADP5589_RESET1_EVENT_A 0x38
|
||||
#define ADP5589_RESET2_EVENT_A 0x3B
|
||||
#define ADP5589_RESET_CFG 0x3D
|
||||
#define ADP5585_RESET2_POL BIT(7)
|
||||
#define ADP5585_RESET1_POL BIT(6)
|
||||
#define ADP5585_RST_PASSTHRU_EN BIT(5)
|
||||
#define ADP5585_RESET_TRIG_TIME GENMASK(4, 2)
|
||||
#define ADP5585_PULSE_WIDTH GENMASK(1, 0)
|
||||
#define ADP5589_PWM_OFFT_LOW 0x3e
|
||||
#define ADP5589_PWM_ONT_LOW 0x40
|
||||
#define ADP5589_PWM_CFG 0x42
|
||||
#define ADP5589_POLL_PTIME_CFG 0x48
|
||||
#define ADP5589_PIN_CONFIG_A 0x49
|
||||
#define ADP5589_PIN_CONFIG_D 0x4C
|
||||
#define ADP5589_GENERAL_CFG 0x4d
|
||||
#define ADP5589_INT_EN 0x4e
|
||||
#define ADP5589_MAX_REG ADP5589_INT_EN
|
||||
|
||||
#define ADP5589_PIN_MAX 19
|
||||
#define ADP5589_KEY_EVENT_START 1
|
||||
#define ADP5589_KEY_EVENT_END 88
|
||||
#define ADP5589_GPI_EVENT_START 97
|
||||
#define ADP5589_GPI_EVENT_END 115
|
||||
#define ADP5589_UNLOCK_WILDCARD 127
|
||||
#define ADP5589_RESET2_OUT 12
|
||||
|
||||
struct regmap;
|
||||
|
||||
enum adp5585_variant {
|
||||
ADP5585_00 = 1,
|
||||
ADP5585_01,
|
||||
ADP5585_02,
|
||||
ADP5585_03,
|
||||
ADP5585_04,
|
||||
ADP5589_00,
|
||||
ADP5589_01,
|
||||
ADP5589_02,
|
||||
ADP5585_MAX
|
||||
};
|
||||
|
||||
struct adp5585_regs {
|
||||
unsigned int gen_cfg;
|
||||
unsigned int ext_cfg;
|
||||
unsigned int int_en;
|
||||
unsigned int poll_ptime_cfg;
|
||||
unsigned int reset_cfg;
|
||||
unsigned int reset1_event_a;
|
||||
unsigned int reset2_event_a;
|
||||
unsigned int pin_cfg_a;
|
||||
};
|
||||
|
||||
struct adp5585_dev {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
const struct adp5585_regs *regs;
|
||||
struct blocking_notifier_head event_notifier;
|
||||
unsigned long *pin_usage;
|
||||
unsigned int n_pins;
|
||||
unsigned int reset2_out;
|
||||
enum adp5585_variant variant;
|
||||
unsigned int id;
|
||||
bool has_unlock;
|
||||
bool has_pin6;
|
||||
int irq;
|
||||
unsigned int ev_poll_time;
|
||||
unsigned int unlock_time;
|
||||
unsigned int unlock_keys[2];
|
||||
unsigned int nkeys_unlock;
|
||||
unsigned int reset1_keys[3];
|
||||
unsigned int nkeys_reset1;
|
||||
unsigned int reset2_keys[2];
|
||||
unsigned int nkeys_reset2;
|
||||
u8 reset_cfg;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user