From 7ca91a33775c4a33cb451f508f84a7820179c73b Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 25 Sep 2022 22:44:19 -0700 Subject: mfd: palmas: Stop including of_gpio.h It does not appear that any of palmas sub-drivers are using OF-based gpio APIs, so let's stop including this header. Signed-off-by: Dmitry Torokhov Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20220926054421.1546436-3-dmitry.torokhov@gmail.com --- include/linux/mfd/palmas.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/mfd/palmas.h b/include/linux/mfd/palmas.h index 1e61c7e9f50d..117d02708439 100644 --- a/include/linux/mfd/palmas.h +++ b/include/linux/mfd/palmas.h @@ -16,7 +16,6 @@ #include #include #include -#include #include #define PALMAS_NUM_CLIENTS 3 -- cgit v1.2.3 From 3c92699a167a543b2bc7d603e4a09aa78a99f809 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 25 Sep 2022 22:44:20 -0700 Subject: mfd: twl6040: Switch to using gpiod API This patch switches the dirver from legacy gpio API to a newer gpiod API so that we can eventually drop the former. Signed-off-by: Dmitry Torokhov Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20220926054421.1546436-4-dmitry.torokhov@gmail.com --- drivers/mfd/twl6040.c | 29 +++++++++++++---------------- include/linux/mfd/twl6040.h | 5 +++-- 2 files changed, 16 insertions(+), 18 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/twl6040.c b/drivers/mfd/twl6040.c index dd9543f6213f..fc97fa5a2d0c 100644 --- a/drivers/mfd/twl6040.c +++ b/drivers/mfd/twl6040.c @@ -17,9 +17,8 @@ #include #include #include -#include #include -#include +#include #include #include #include @@ -251,7 +250,7 @@ static int twl6040_power_up_automatic(struct twl6040 *twl6040) { int time_left; - gpio_set_value(twl6040->audpwron, 1); + gpiod_set_value_cansleep(twl6040->audpwron, 1); time_left = wait_for_completion_timeout(&twl6040->ready, msecs_to_jiffies(144)); @@ -262,7 +261,7 @@ static int twl6040_power_up_automatic(struct twl6040 *twl6040) intid = twl6040_reg_read(twl6040, TWL6040_REG_INTID); if (!(intid & TWL6040_READYINT)) { dev_err(twl6040->dev, "automatic power-up failed\n"); - gpio_set_value(twl6040->audpwron, 0); + gpiod_set_value_cansleep(twl6040->audpwron, 0); return -ETIMEDOUT; } } @@ -290,7 +289,7 @@ int twl6040_power(struct twl6040 *twl6040, int on) /* Allow writes to the chip */ regcache_cache_only(twl6040->regmap, false); - if (gpio_is_valid(twl6040->audpwron)) { + if (twl6040->audpwron) { /* use automatic power-up sequence */ ret = twl6040_power_up_automatic(twl6040); if (ret) { @@ -337,9 +336,9 @@ int twl6040_power(struct twl6040 *twl6040, int on) if (--twl6040->power_count) goto out; - if (gpio_is_valid(twl6040->audpwron)) { + if (twl6040->audpwron) { /* use AUDPWRON line */ - gpio_set_value(twl6040->audpwron, 0); + gpiod_set_value_cansleep(twl6040->audpwron, 0); /* power-down sequence latency */ usleep_range(500, 700); @@ -711,18 +710,16 @@ static int twl6040_probe(struct i2c_client *client) } /* ERRATA: Automatic power-up is not possible in ES1.0 */ - if (twl6040_get_revid(twl6040) > TWL6040_REV_ES1_0) - twl6040->audpwron = of_get_named_gpio(node, - "ti,audpwron-gpio", 0); - else - twl6040->audpwron = -EINVAL; - - if (gpio_is_valid(twl6040->audpwron)) { - ret = devm_gpio_request_one(&client->dev, twl6040->audpwron, - GPIOF_OUT_INIT_LOW, "audpwron"); + if (twl6040_get_revid(twl6040) > TWL6040_REV_ES1_0) { + twl6040->audpwron = devm_gpiod_get_optional(&client->dev, + "ti,audpwron", + GPIOD_OUT_LOW); + ret = PTR_ERR_OR_ZERO(twl6040->audpwron); if (ret) goto gpio_err; + gpiod_set_consumer_name(twl6040->audpwron, "audpwron"); + /* Clear any pending interrupt */ twl6040_reg_read(twl6040, TWL6040_REG_INTID); } diff --git a/include/linux/mfd/twl6040.h b/include/linux/mfd/twl6040.h index 1fc7450bd8ab..cb1e7a9ce317 100644 --- a/include/linux/mfd/twl6040.h +++ b/include/linux/mfd/twl6040.h @@ -196,13 +196,14 @@ struct twl6040_gpo_data { }; struct twl6040_platform_data { - int audpwron_gpio; /* audio power-on gpio */ + struct gpio_desc *audpwron_gpio; /* audio power-on gpio */ struct twl6040_codec_data *codec; struct twl6040_vibra_data *vibra; struct twl6040_gpo_data *gpo; }; +struct gpio_desc; struct regmap; struct regmap_irq_chips_data; @@ -218,7 +219,7 @@ struct twl6040 { struct mfd_cell cells[TWL6040_CELLS]; struct completion ready; - int audpwron; + struct gpio_desc *audpwron; int power_count; int rev; -- cgit v1.2.3 From 1f7caaa1743edbc40f3cd7e2bc0dff89698fd91d Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 25 Sep 2022 22:44:21 -0700 Subject: mfd: twl6040: Drop twl6040_platform_data and associated definitions As of df04b6242a58 ("mfd: twl6040: Remove support for legacy (pdata) mode") the driver no longer references the platform data, so we can drop its definition, as well as definitions of related structures. Signed-off-by: Dmitry Torokhov Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20220926054421.1546436-5-dmitry.torokhov@gmail.com --- include/linux/mfd/twl6040.h | 29 ----------------------------- 1 file changed, 29 deletions(-) (limited to 'include/linux') diff --git a/include/linux/mfd/twl6040.h b/include/linux/mfd/twl6040.h index cb1e7a9ce317..286a724e379a 100644 --- a/include/linux/mfd/twl6040.h +++ b/include/linux/mfd/twl6040.h @@ -174,35 +174,6 @@ #define TWL6040_GPO_MAX 3 -/* TODO: All platform data struct can be removed */ -struct twl6040_codec_data { - u16 hs_left_step; - u16 hs_right_step; - u16 hf_left_step; - u16 hf_right_step; -}; - -struct twl6040_vibra_data { - unsigned int vibldrv_res; /* left driver resistance */ - unsigned int vibrdrv_res; /* right driver resistance */ - unsigned int viblmotor_res; /* left motor resistance */ - unsigned int vibrmotor_res; /* right motor resistance */ - int vddvibl_uV; /* VDDVIBL volt, set 0 for fixed reg */ - int vddvibr_uV; /* VDDVIBR volt, set 0 for fixed reg */ -}; - -struct twl6040_gpo_data { - int gpio_base; -}; - -struct twl6040_platform_data { - struct gpio_desc *audpwron_gpio; /* audio power-on gpio */ - - struct twl6040_codec_data *codec; - struct twl6040_vibra_data *vibra; - struct twl6040_gpo_data *gpo; -}; - struct gpio_desc; struct regmap; struct regmap_irq_chips_data; -- cgit v1.2.3 From fc45720334ebc9f97335a9ce67896811354f0a1c Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 19 Oct 2022 17:29:32 +0200 Subject: mfd: Remove dm355evm_msp driver The DaVinci DM355EVM platform is gone after the removal of all unused board files, so the MTD device along with its sub-devices can be removed as well. Signed-off-by: Arnd Bergmann Acked-by: Bartosz Golaszewski Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20221019152947.3857217-7-arnd@kernel.org --- drivers/input/misc/Kconfig | 11 - drivers/input/misc/Makefile | 1 - drivers/input/misc/dm355evm_keys.c | 238 ------------------- drivers/mfd/Kconfig | 8 - drivers/mfd/Makefile | 1 - drivers/mfd/dm355evm_msp.c | 454 ------------------------------------- drivers/rtc/Kconfig | 6 - drivers/rtc/Makefile | 1 - drivers/rtc/rtc-dm355evm.c | 151 ------------ include/linux/mfd/dm355evm_msp.h | 79 ------- 10 files changed, 950 deletions(-) delete mode 100644 drivers/input/misc/dm355evm_keys.c delete mode 100644 drivers/mfd/dm355evm_msp.c delete mode 100644 drivers/rtc/rtc-dm355evm.c delete mode 100644 include/linux/mfd/dm355evm_msp.h (limited to 'include/linux') diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 9f088900f863..540633b164d4 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -662,17 +662,6 @@ config INPUT_DA9063_ONKEY To compile this driver as a module, choose M here: the module will be called da9063_onkey. -config INPUT_DM355EVM - tristate "TI DaVinci DM355 EVM Keypad and IR Remote" - depends on MFD_DM355EVM_MSP - select INPUT_SPARSEKMAP - help - Supports the pushbuttons and IR remote used with - the DM355 EVM board. - - To compile this driver as a module, choose M here: the - module will be called dm355evm_keys. - config INPUT_WM831X_ON tristate "WM831X ON pin" depends on MFD_WM831X diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 6abefc41037b..156f9c21f53b 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -31,7 +31,6 @@ obj-$(CONFIG_INPUT_DA7280_HAPTICS) += da7280.o obj-$(CONFIG_INPUT_DA9052_ONKEY) += da9052_onkey.o obj-$(CONFIG_INPUT_DA9055_ONKEY) += da9055_onkey.o obj-$(CONFIG_INPUT_DA9063_ONKEY) += da9063_onkey.o -obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o obj-$(CONFIG_INPUT_E3X0_BUTTON) += e3x0-button.o obj-$(CONFIG_INPUT_DRV260X_HAPTICS) += drv260x.o obj-$(CONFIG_INPUT_DRV2665_HAPTICS) += drv2665.o diff --git a/drivers/input/misc/dm355evm_keys.c b/drivers/input/misc/dm355evm_keys.c deleted file mode 100644 index 397ca7c787cc..000000000000 --- a/drivers/input/misc/dm355evm_keys.c +++ /dev/null @@ -1,238 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * dm355evm_keys.c - support buttons and IR remote on DM355 EVM board - * - * Copyright (c) 2008 by David Brownell - */ -#include -#include -#include -#include -#include -#include - -#include -#include - - -/* - * The MSP430 firmware on the DM355 EVM monitors on-board pushbuttons - * and an IR receptor used for the remote control. When any key is - * pressed, or its autorepeat kicks in, an event is sent. This driver - * read those events from the small (32 event) queue and reports them. - * - * Note that physically there can only be one of these devices. - * - * This driver was tested with firmware revision A4. - */ -struct dm355evm_keys { - struct input_dev *input; - struct device *dev; -}; - -/* These initial keycodes can be remapped */ -static const struct key_entry dm355evm_keys[] = { - /* - * Pushbuttons on the EVM board ... note that the labels for these - * are SW10/SW11/etc on the PC board. The left/right orientation - * comes only from the firmware's documentation, and presumes the - * power connector is immediately in front of you and the IR sensor - * is to the right. (That is, rotate the board counter-clockwise - * by 90 degrees from the SW10/etc and "DM355 EVM" labels.) - */ - { KE_KEY, 0x00d8, { KEY_OK } }, /* SW12 */ - { KE_KEY, 0x00b8, { KEY_UP } }, /* SW13 */ - { KE_KEY, 0x00e8, { KEY_DOWN } }, /* SW11 */ - { KE_KEY, 0x0078, { KEY_LEFT } }, /* SW14 */ - { KE_KEY, 0x00f0, { KEY_RIGHT } }, /* SW10 */ - - /* - * IR buttons ... codes assigned to match the universal remote - * provided with the EVM (Philips PM4S) using DVD code 0020. - * - * These event codes match firmware documentation, but other - * remote controls could easily send more RC5-encoded events. - * The PM4S manual was used in several cases to help select - * a keycode reflecting the intended usage. - * - * RC5 codes are 14 bits, with two start bits (0x3 prefix) - * and a toggle bit (masked out below). - */ - { KE_KEY, 0x300c, { KEY_POWER } }, /* NOTE: docs omit this */ - { KE_KEY, 0x3000, { KEY_NUMERIC_0 } }, - { KE_KEY, 0x3001, { KEY_NUMERIC_1 } }, - { KE_KEY, 0x3002, { KEY_NUMERIC_2 } }, - { KE_KEY, 0x3003, { KEY_NUMERIC_3 } }, - { KE_KEY, 0x3004, { KEY_NUMERIC_4 } }, - { KE_KEY, 0x3005, { KEY_NUMERIC_5 } }, - { KE_KEY, 0x3006, { KEY_NUMERIC_6 } }, - { KE_KEY, 0x3007, { KEY_NUMERIC_7 } }, - { KE_KEY, 0x3008, { KEY_NUMERIC_8 } }, - { KE_KEY, 0x3009, { KEY_NUMERIC_9 } }, - { KE_KEY, 0x3022, { KEY_ENTER } }, - { KE_KEY, 0x30ec, { KEY_MODE } }, /* "tv/vcr/..." */ - { KE_KEY, 0x300f, { KEY_SELECT } }, /* "info" */ - { KE_KEY, 0x3020, { KEY_CHANNELUP } }, /* "up" */ - { KE_KEY, 0x302e, { KEY_MENU } }, /* "in/out" */ - { KE_KEY, 0x3011, { KEY_VOLUMEDOWN } }, /* "left" */ - { KE_KEY, 0x300d, { KEY_MUTE } }, /* "ok" */ - { KE_KEY, 0x3010, { KEY_VOLUMEUP } }, /* "right" */ - { KE_KEY, 0x301e, { KEY_SUBTITLE } }, /* "cc" */ - { KE_KEY, 0x3021, { KEY_CHANNELDOWN } },/* "down" */ - { KE_KEY, 0x3022, { KEY_PREVIOUS } }, - { KE_KEY, 0x3026, { KEY_SLEEP } }, - { KE_KEY, 0x3172, { KEY_REWIND } }, /* NOTE: docs wrongly say 0x30ca */ - { KE_KEY, 0x3175, { KEY_PLAY } }, - { KE_KEY, 0x3174, { KEY_FASTFORWARD } }, - { KE_KEY, 0x3177, { KEY_RECORD } }, - { KE_KEY, 0x3176, { KEY_STOP } }, - { KE_KEY, 0x3169, { KEY_PAUSE } }, -}; - -/* - * Because we communicate with the MSP430 using I2C, and all I2C calls - * in Linux sleep, we use a threaded IRQ handler. The IRQ itself is - * active low, but we go through the GPIO controller so we can trigger - * on falling edges and not worry about enabling/disabling the IRQ in - * the keypress handling path. - */ -static irqreturn_t dm355evm_keys_irq(int irq, void *_keys) -{ - static u16 last_event; - struct dm355evm_keys *keys = _keys; - const struct key_entry *ke; - unsigned int keycode; - int status; - u16 event; - - /* For simplicity we ignore INPUT_COUNT and just read - * events until we get the "queue empty" indicator. - * Reading INPUT_LOW decrements the count. - */ - for (;;) { - status = dm355evm_msp_read(DM355EVM_MSP_INPUT_HIGH); - if (status < 0) { - dev_dbg(keys->dev, "input high err %d\n", - status); - break; - } - event = status << 8; - - status = dm355evm_msp_read(DM355EVM_MSP_INPUT_LOW); - if (status < 0) { - dev_dbg(keys->dev, "input low err %d\n", - status); - break; - } - event |= status; - if (event == 0xdead) - break; - - /* Press and release a button: two events, same code. - * Press and hold (autorepeat), then release: N events - * (N > 2), same code. For RC5 buttons the toggle bits - * distinguish (for example) "1-autorepeat" from "1 1"; - * but PCB buttons don't support that bit. - * - * So we must synthesize release events. We do that by - * mapping events to a press/release event pair; then - * to avoid adding extra events, skip the second event - * of each pair. - */ - if (event == last_event) { - last_event = 0; - continue; - } - last_event = event; - - /* ignore the RC5 toggle bit */ - event &= ~0x0800; - - /* find the key, or report it as unknown */ - ke = sparse_keymap_entry_from_scancode(keys->input, event); - keycode = ke ? ke->keycode : KEY_UNKNOWN; - dev_dbg(keys->dev, - "input event 0x%04x--> keycode %d\n", - event, keycode); - - /* report press + release */ - input_report_key(keys->input, keycode, 1); - input_sync(keys->input); - input_report_key(keys->input, keycode, 0); - input_sync(keys->input); - } - - return IRQ_HANDLED; -} - -/*----------------------------------------------------------------------*/ - -static int dm355evm_keys_probe(struct platform_device *pdev) -{ - struct dm355evm_keys *keys; - struct input_dev *input; - int irq; - int error; - - keys = devm_kzalloc(&pdev->dev, sizeof (*keys), GFP_KERNEL); - if (!keys) - return -ENOMEM; - - input = devm_input_allocate_device(&pdev->dev); - if (!input) - return -ENOMEM; - - keys->dev = &pdev->dev; - keys->input = input; - - input->name = "DM355 EVM Controls"; - input->phys = "dm355evm/input0"; - - input->id.bustype = BUS_I2C; - input->id.product = 0x0355; - input->id.version = dm355evm_msp_read(DM355EVM_MSP_FIRMREV); - - error = sparse_keymap_setup(input, dm355evm_keys, NULL); - if (error) - return error; - - /* REVISIT: flush the event queue? */ - - /* set up "threaded IRQ handler" */ - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - - error = devm_request_threaded_irq(&pdev->dev, irq, - NULL, dm355evm_keys_irq, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - dev_name(&pdev->dev), keys); - if (error) - return error; - - /* register */ - error = input_register_device(input); - if (error) - return error; - - return 0; -} - -/* REVISIT: add suspend/resume when DaVinci supports it. The IRQ should - * be able to wake up the system. When device_may_wakeup(&pdev->dev), call - * enable_irq_wake() on suspend, and disable_irq_wake() on resume. - */ - -/* - * I2C is used to talk to the MSP430, but this platform device is - * exposed by an MFD driver that manages I2C communications. - */ -static struct platform_driver dm355evm_keys_driver = { - .probe = dm355evm_keys_probe, - .driver = { - .name = "dm355evm_keys", - }, -}; -module_platform_driver(dm355evm_keys_driver); - -MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 9e77f4762999..20613a5648c1 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1460,14 +1460,6 @@ config MFD_TI_AM335X_TSCADC To compile this driver as a module, choose M here: the module will be called ti_am335x_tscadc. -config MFD_DM355EVM_MSP - bool "TI DaVinci DM355 EVM microcontroller" - depends on I2C=y && MACH_DAVINCI_DM355_EVM - help - This driver supports the MSP430 microcontroller used on these - boards. MSP430 firmware manages resets and power sequencing, - inputs from buttons and the IR remote, LEDs, an RTC, and more. - config MFD_LP3943 tristate "TI/National Semiconductor LP3943 MFD Driver" depends on I2C diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 9387c3ddab4e..6bfe2012e071 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -25,7 +25,6 @@ obj-$(CONFIG_MFD_TI_LP873X) += lp873x.o obj-$(CONFIG_MFD_TI_LP87565) += lp87565.o obj-$(CONFIG_MFD_DAVINCI_VOICECODEC) += davinci_voicecodec.o -obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o obj-$(CONFIG_MFD_TI_AM335X_TSCADC) += ti_am335x_tscadc.o obj-$(CONFIG_MFD_STA2X11) += sta2x11-mfd.o diff --git a/drivers/mfd/dm355evm_msp.c b/drivers/mfd/dm355evm_msp.c deleted file mode 100644 index 759c59690680..000000000000 --- a/drivers/mfd/dm355evm_msp.c +++ /dev/null @@ -1,454 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * dm355evm_msp.c - driver for MSP430 firmware on DM355EVM board - * - * Copyright (C) 2008 David Brownell - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -/* - * The DM355 is a DaVinci chip with video support but no C64+ DSP. Its - * EVM board has an MSP430 programmed with firmware for various board - * support functions. This driver exposes some of them directly, and - * supports other drivers (e.g. RTC, input) for more complex access. - * - * Because this firmware is entirely board-specific, this file embeds - * knowledge that would be passed as platform_data in a generic driver. - * - * This driver was tested with firmware revision A4. - */ - -#if IS_ENABLED(CONFIG_INPUT_DM355EVM) -#define msp_has_keyboard() true -#else -#define msp_has_keyboard() false -#endif - -#if IS_ENABLED(CONFIG_LEDS_GPIO) -#define msp_has_leds() true -#else -#define msp_has_leds() false -#endif - -#if IS_ENABLED(CONFIG_RTC_DRV_DM355EVM) -#define msp_has_rtc() true -#else -#define msp_has_rtc() false -#endif - -#if IS_ENABLED(CONFIG_VIDEO_TVP514X) -#define msp_has_tvp() true -#else -#define msp_has_tvp() false -#endif - - -/*----------------------------------------------------------------------*/ - -/* REVISIT for paranoia's sake, retry reads/writes on error */ - -static struct i2c_client *msp430; - -/** - * dm355evm_msp_write - Writes a register in dm355evm_msp - * @value: the value to be written - * @reg: register address - * - * Returns result of operation - 0 is success, else negative errno - */ -int dm355evm_msp_write(u8 value, u8 reg) -{ - return i2c_smbus_write_byte_data(msp430, reg, value); -} -EXPORT_SYMBOL(dm355evm_msp_write); - -/** - * dm355evm_msp_read - Reads a register from dm355evm_msp - * @reg: register address - * - * Returns result of operation - value, or negative errno - */ -int dm355evm_msp_read(u8 reg) -{ - return i2c_smbus_read_byte_data(msp430, reg); -} -EXPORT_SYMBOL(dm355evm_msp_read); - -/*----------------------------------------------------------------------*/ - -/* - * Many of the msp430 pins are just used as fixed-direction GPIOs. - * We could export a few more of them this way, if we wanted. - */ -#define MSP_GPIO(bit, reg) ((DM355EVM_MSP_ ## reg) << 3 | (bit)) - -static const u8 msp_gpios[] = { - /* eight leds */ - MSP_GPIO(0, LED), MSP_GPIO(1, LED), - MSP_GPIO(2, LED), MSP_GPIO(3, LED), - MSP_GPIO(4, LED), MSP_GPIO(5, LED), - MSP_GPIO(6, LED), MSP_GPIO(7, LED), - /* SW6 and the NTSC/nPAL jumper */ - MSP_GPIO(0, SWITCH1), MSP_GPIO(1, SWITCH1), - MSP_GPIO(2, SWITCH1), MSP_GPIO(3, SWITCH1), - MSP_GPIO(4, SWITCH1), - /* switches on MMC/SD sockets */ - /* - * Note: EVMDM355_ECP_VA4.pdf suggests that Bit 2 and 4 should be - * checked for card detection. However on the EVM bit 1 and 3 gives - * this status, for 0 and 1 instance respectively. The pdf also - * suggests that Bit 1 and 3 should be checked for write protection. - * However on the EVM bit 2 and 4 gives this status,for 0 and 1 - * instance respectively. - */ - MSP_GPIO(2, SDMMC), MSP_GPIO(1, SDMMC), /* mmc0 WP, nCD */ - MSP_GPIO(4, SDMMC), MSP_GPIO(3, SDMMC), /* mmc1 WP, nCD */ -}; - -static struct gpio_led evm_leds[] = { - { .name = "dm355evm::ds14", - .default_trigger = "heartbeat", }, - { .name = "dm355evm::ds15", - .default_trigger = "mmc0", }, - { .name = "dm355evm::ds16", - /* could also be a CE-ATA drive */ - .default_trigger = "mmc1", }, - { .name = "dm355evm::ds17", - .default_trigger = "nand-disk", }, - { .name = "dm355evm::ds18", }, - { .name = "dm355evm::ds19", }, - { .name = "dm355evm::ds20", }, - { .name = "dm355evm::ds21", }, -}; - -static struct gpio_led_platform_data evm_led_data = { - .num_leds = ARRAY_SIZE(evm_leds), - .leds = evm_leds, -}; - -static struct gpiod_lookup_table evm_leds_gpio_table = { - .dev_id = "leds-gpio", - .table = { - /* - * These GPIOs are on the dm355evm_msp - * GPIO chip at index 0..7 - */ - GPIO_LOOKUP_IDX("dm355evm_msp", 0, NULL, - 0, GPIO_ACTIVE_LOW), - GPIO_LOOKUP_IDX("dm355evm_msp", 1, NULL, - 1, GPIO_ACTIVE_LOW), - GPIO_LOOKUP_IDX("dm355evm_msp", 2, NULL, - 2, GPIO_ACTIVE_LOW), - GPIO_LOOKUP_IDX("dm355evm_msp", 3, NULL, - 3, GPIO_ACTIVE_LOW), - GPIO_LOOKUP_IDX("dm355evm_msp", 4, NULL, - 4, GPIO_ACTIVE_LOW), - GPIO_LOOKUP_IDX("dm355evm_msp", 5, NULL, - 5, GPIO_ACTIVE_LOW), - GPIO_LOOKUP_IDX("dm355evm_msp", 6, NULL, - 6, GPIO_ACTIVE_LOW), - GPIO_LOOKUP_IDX("dm355evm_msp", 7, NULL, - 7, GPIO_ACTIVE_LOW), - { }, - }, -}; - -#define MSP_GPIO_REG(offset) (msp_gpios[(offset)] >> 3) -#define MSP_GPIO_MASK(offset) BIT(msp_gpios[(offset)] & 0x07) - -static int msp_gpio_in(struct gpio_chip *chip, unsigned offset) -{ - switch (MSP_GPIO_REG(offset)) { - case DM355EVM_MSP_SWITCH1: - case DM355EVM_MSP_SWITCH2: - case DM355EVM_MSP_SDMMC: - return 0; - default: - return -EINVAL; - } -} - -static u8 msp_led_cache; - -static int msp_gpio_get(struct gpio_chip *chip, unsigned offset) -{ - int reg, status; - - reg = MSP_GPIO_REG(offset); - status = dm355evm_msp_read(reg); - if (status < 0) - return status; - if (reg == DM355EVM_MSP_LED) - msp_led_cache = status; - return !!(status & MSP_GPIO_MASK(offset)); -} - -static int msp_gpio_out(struct gpio_chip *chip, unsigned offset, int value) -{ - int mask, bits; - - /* NOTE: there are some other signals that could be - * packaged as output GPIOs, but they aren't as useful - * as the LEDs ... so for now we don't. - */ - if (MSP_GPIO_REG(offset) != DM355EVM_MSP_LED) - return -EINVAL; - - mask = MSP_GPIO_MASK(offset); - bits = msp_led_cache; - - bits &= ~mask; - if (value) - bits |= mask; - msp_led_cache = bits; - - return dm355evm_msp_write(bits, DM355EVM_MSP_LED); -} - -static void msp_gpio_set(struct gpio_chip *chip, unsigned offset, int value) -{ - msp_gpio_out(chip, offset, value); -} - -static struct gpio_chip dm355evm_msp_gpio = { - .label = "dm355evm_msp", - .owner = THIS_MODULE, - .direction_input = msp_gpio_in, - .get = msp_gpio_get, - .direction_output = msp_gpio_out, - .set = msp_gpio_set, - .base = -EINVAL, /* dynamic assignment */ - .ngpio = ARRAY_SIZE(msp_gpios), - .can_sleep = true, -}; - -/*----------------------------------------------------------------------*/ - -static struct device *add_child(struct i2c_client *client, const char *name, - void *pdata, unsigned pdata_len, - bool can_wakeup, int irq) -{ - struct platform_device *pdev; - int status; - - pdev = platform_device_alloc(name, -1); - if (!pdev) - return ERR_PTR(-ENOMEM); - - device_init_wakeup(&pdev->dev, can_wakeup); - pdev->dev.parent = &client->dev; - - if (pdata) { - status = platform_device_add_data(pdev, pdata, pdata_len); - if (status < 0) { - dev_dbg(&pdev->dev, "can't add platform_data\n"); - goto put_device; - } - } - - if (irq) { - struct resource r = { - .start = irq, - .flags = IORESOURCE_IRQ, - }; - - status = platform_device_add_resources(pdev, &r, 1); - if (status < 0) { - dev_dbg(&pdev->dev, "can't add irq\n"); - goto put_device; - } - } - - status = platform_device_add(pdev); - if (status) - goto put_device; - - return &pdev->dev; - -put_device: - platform_device_put(pdev); - dev_err(&client->dev, "failed to add device %s\n", name); - return ERR_PTR(status); -} - -static int add_children(struct i2c_client *client) -{ - static const struct { - int offset; - char *label; - } config_inputs[] = { - /* 8 == right after the LEDs */ - { 8 + 0, "sw6_1", }, - { 8 + 1, "sw6_2", }, - { 8 + 2, "sw6_3", }, - { 8 + 3, "sw6_4", }, - { 8 + 4, "NTSC/nPAL", }, - }; - - struct device *child; - int status; - int i; - - /* GPIO-ish stuff */ - dm355evm_msp_gpio.parent = &client->dev; - status = gpiochip_add_data(&dm355evm_msp_gpio, NULL); - if (status < 0) - return status; - - /* LED output */ - if (msp_has_leds()) { - gpiod_add_lookup_table(&evm_leds_gpio_table); - /* NOTE: these are the only fully programmable LEDs - * on the board, since GPIO-61/ds22 (and many signals - * going to DC7) must be used for AEMIF address lines - * unless the top 1 GB of NAND is unused... - */ - child = add_child(client, "leds-gpio", - &evm_led_data, sizeof(evm_led_data), - false, 0); - if (IS_ERR(child)) - return PTR_ERR(child); - } - - /* configuration inputs */ - for (i = 0; i < ARRAY_SIZE(config_inputs); i++) { - int gpio = dm355evm_msp_gpio.base + config_inputs[i].offset; - - gpio_request_one(gpio, GPIOF_IN, config_inputs[i].label); - - /* make it easy for userspace to see these */ - gpio_export(gpio, false); - } - - /* MMC/SD inputs -- right after the last config input */ - if (dev_get_platdata(&client->dev)) { - void (*mmcsd_setup)(unsigned) = dev_get_platdata(&client->dev); - - mmcsd_setup(dm355evm_msp_gpio.base + 8 + 5); - } - - /* RTC is a 32 bit counter, no alarm */ - if (msp_has_rtc()) { - child = add_child(client, "rtc-dm355evm", - NULL, 0, false, 0); - if (IS_ERR(child)) - return PTR_ERR(child); - } - - /* input from buttons and IR remote (uses the IRQ) */ - if (msp_has_keyboard()) { - child = add_child(client, "dm355evm_keys", - NULL, 0, true, client->irq); - if (IS_ERR(child)) - return PTR_ERR(child); - } - - return 0; -} - -/*----------------------------------------------------------------------*/ - -static void dm355evm_command(unsigned command) -{ - int status; - - status = dm355evm_msp_write(command, DM355EVM_MSP_COMMAND); - if (status < 0) - dev_err(&msp430->dev, "command %d failure %d\n", - command, status); -} - -static void dm355evm_power_off(void) -{ - dm355evm_command(MSP_COMMAND_POWEROFF); -} - -static void dm355evm_msp_remove(struct i2c_client *client) -{ - pm_power_off = NULL; - msp430 = NULL; -} - -static int -dm355evm_msp_probe(struct i2c_client *client, const struct i2c_device_id *id) -{ - int status; - const char *video = msp_has_tvp() ? "TVP5146" : "imager"; - - if (msp430) - return -EBUSY; - msp430 = client; - - /* display revision status; doubles as sanity check */ - status = dm355evm_msp_read(DM355EVM_MSP_FIRMREV); - if (status < 0) - goto fail; - dev_info(&client->dev, "firmware v.%02X, %s as video-in\n", - status, video); - - /* mux video input: either tvp5146 or some external imager */ - status = dm355evm_msp_write(msp_has_tvp() ? 0 : MSP_VIDEO_IMAGER, - DM355EVM_MSP_VIDEO_IN); - if (status < 0) - dev_warn(&client->dev, "error %d muxing %s as video-in\n", - status, video); - - /* init LED cache, and turn off the LEDs */ - msp_led_cache = 0xff; - dm355evm_msp_write(msp_led_cache, DM355EVM_MSP_LED); - - /* export capabilities we support */ - status = add_children(client); - if (status < 0) - goto fail; - - /* PM hookup */ - pm_power_off = dm355evm_power_off; - - return 0; - -fail: - /* FIXME remove children ... */ - dm355evm_msp_remove(client); - return status; -} - -static const struct i2c_device_id dm355evm_msp_ids[] = { - { "dm355evm_msp", 0 }, - { /* end of list */ }, -}; -MODULE_DEVICE_TABLE(i2c, dm355evm_msp_ids); - -static struct i2c_driver dm355evm_msp_driver = { - .driver.name = "dm355evm_msp", - .id_table = dm355evm_msp_ids, - .probe = dm355evm_msp_probe, - .remove = dm355evm_msp_remove, -}; - -static int __init dm355evm_msp_init(void) -{ - return i2c_add_driver(&dm355evm_msp_driver); -} -subsys_initcall(dm355evm_msp_init); - -static void __exit dm355evm_msp_exit(void) -{ - i2c_del_driver(&dm355evm_msp_driver); -} -module_exit(dm355evm_msp_exit); - -MODULE_DESCRIPTION("Interface to MSP430 firmware on DM355EVM"); -MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index bb63edb507da..35298c651730 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -540,12 +540,6 @@ config RTC_DRV_BQ32K This driver can also be built as a module. If so, the module will be called rtc-bq32k. -config RTC_DRV_DM355EVM - tristate "TI DaVinci DM355 EVM RTC" - depends on MFD_DM355EVM_MSP - help - Supports the RTC firmware in the MSP430 on the DM355 EVM. - config RTC_DRV_TWL92330 bool "TI TWL92330/Menelaus" depends on MENELAUS diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index aab22bc63432..c2d474985919 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -46,7 +46,6 @@ obj-$(CONFIG_RTC_DRV_DA9055) += rtc-da9055.o obj-$(CONFIG_RTC_DRV_DA9063) += rtc-da9063.o obj-$(CONFIG_RTC_DRV_DAVINCI) += rtc-davinci.o obj-$(CONFIG_RTC_DRV_DIGICOLOR) += rtc-digicolor.o -obj-$(CONFIG_RTC_DRV_DM355EVM) += rtc-dm355evm.o obj-$(CONFIG_RTC_DRV_DS1216) += rtc-ds1216.o obj-$(CONFIG_RTC_DRV_DS1286) += rtc-ds1286.o obj-$(CONFIG_RTC_DRV_DS1302) += rtc-ds1302.o diff --git a/drivers/rtc/rtc-dm355evm.c b/drivers/rtc/rtc-dm355evm.c deleted file mode 100644 index 94fb16ac3e0f..000000000000 --- a/drivers/rtc/rtc-dm355evm.c +++ /dev/null @@ -1,151 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * rtc-dm355evm.c - access battery-backed counter in MSP430 firmware - * - * Copyright (c) 2008 by David Brownell - */ -#include -#include -#include -#include - -#include -#include - - -/* - * The MSP430 firmware on the DM355 EVM uses a watch crystal to feed - * a 1 Hz counter. When a backup battery is supplied, that makes a - * reasonable RTC for applications where alarms and non-NTP drift - * compensation aren't important. - * - * The only real glitch is the inability to read or write all four - * counter bytes atomically: the count may increment in the middle - * of an operation, causing trouble when the LSB rolls over. - * - * This driver was tested with firmware revision A4. - */ -union evm_time { - u8 bytes[4]; - u32 value; -}; - -static int dm355evm_rtc_read_time(struct device *dev, struct rtc_time *tm) -{ - union evm_time time; - int status; - int tries = 0; - - do { - /* - * Read LSB(0) to MSB(3) bytes. Defend against the counter - * rolling over by re-reading until the value is stable, - * and assuming the four reads take at most a few seconds. - */ - status = dm355evm_msp_read(DM355EVM_MSP_RTC_0); - if (status < 0) - return status; - if (tries && time.bytes[0] == status) - break; - time.bytes[0] = status; - - status = dm355evm_msp_read(DM355EVM_MSP_RTC_1); - if (status < 0) - return status; - if (tries && time.bytes[1] == status) - break; - time.bytes[1] = status; - - status = dm355evm_msp_read(DM355EVM_MSP_RTC_2); - if (status < 0) - return status; - if (tries && time.bytes[2] == status) - break; - time.bytes[2] = status; - - status = dm355evm_msp_read(DM355EVM_MSP_RTC_3); - if (status < 0) - return status; - if (tries && time.bytes[3] == status) - break; - time.bytes[3] = status; - - } while (++tries < 5); - - dev_dbg(dev, "read timestamp %08x\n", time.value); - - rtc_time64_to_tm(le32_to_cpu(time.value), tm); - return 0; -} - -static int dm355evm_rtc_set_time(struct device *dev, struct rtc_time *tm) -{ - union evm_time time; - unsigned long value; - int status; - - value = rtc_tm_to_time64(tm); - time.value = cpu_to_le32(value); - - dev_dbg(dev, "write timestamp %08x\n", time.value); - - /* - * REVISIT handle non-atomic writes ... maybe just retry until - * byte[1] sticks (no rollover)? - */ - status = dm355evm_msp_write(time.bytes[0], DM355EVM_MSP_RTC_0); - if (status < 0) - return status; - - status = dm355evm_msp_write(time.bytes[1], DM355EVM_MSP_RTC_1); - if (status < 0) - return status; - - status = dm355evm_msp_write(time.bytes[2], DM355EVM_MSP_RTC_2); - if (status < 0) - return status; - - status = dm355evm_msp_write(time.bytes[3], DM355EVM_MSP_RTC_3); - if (status < 0) - return status; - - return 0; -} - -static const struct rtc_class_ops dm355evm_rtc_ops = { - .read_time = dm355evm_rtc_read_time, - .set_time = dm355evm_rtc_set_time, -}; - -/*----------------------------------------------------------------------*/ - -static int dm355evm_rtc_probe(struct platform_device *pdev) -{ - struct rtc_device *rtc; - - rtc = devm_rtc_allocate_device(&pdev->dev); - if (IS_ERR(rtc)) - return PTR_ERR(rtc); - - platform_set_drvdata(pdev, rtc); - - rtc->ops = &dm355evm_rtc_ops; - rtc->range_max = U32_MAX; - - return devm_rtc_register_device(rtc); -} - -/* - * I2C is used to talk to the MSP430, but this platform device is - * exposed by an MFD driver that manages I2C communications. - */ -static struct platform_driver rtc_dm355evm_driver = { - .probe = dm355evm_rtc_probe, - .driver = { - .name = "rtc-dm355evm", - }, -}; - -module_platform_driver(rtc_dm355evm_driver); - -MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/dm355evm_msp.h b/include/linux/mfd/dm355evm_msp.h deleted file mode 100644 index 372470350fab..000000000000 --- a/include/linux/mfd/dm355evm_msp.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * dm355evm_msp.h - support MSP430 microcontroller on DM355EVM board - */ -#ifndef __LINUX_I2C_DM355EVM_MSP -#define __LINUX_I2C_DM355EVM_MSP - -/* - * Written against Spectrum's writeup for the A4 firmware revision, - * and tweaked to match source and rev D2 schematics by removing CPLD - * and NOR flash hooks (which were last appropriate in rev B boards). - * - * Note that the firmware supports a flavor of write posting ... to be - * sure a write completes, issue another read or write. - */ - -/* utilities to access "registers" emulated by msp430 firmware */ -extern int dm355evm_msp_write(u8 value, u8 reg); -extern int dm355evm_msp_read(u8 reg); - - -/* command/control registers */ -#define DM355EVM_MSP_COMMAND 0x00 -# define MSP_COMMAND_NULL 0 -# define MSP_COMMAND_RESET_COLD 1 -# define MSP_COMMAND_RESET_WARM 2 -# define MSP_COMMAND_RESET_WARM_I 3 -# define MSP_COMMAND_POWEROFF 4 -# define MSP_COMMAND_IR_REINIT 5 -#define DM355EVM_MSP_STATUS 0x01 -# define MSP_STATUS_BAD_OFFSET BIT(0) -# define MSP_STATUS_BAD_COMMAND BIT(1) -# define MSP_STATUS_POWER_ERROR BIT(2) -# define MSP_STATUS_RXBUF_OVERRUN BIT(3) -#define DM355EVM_MSP_RESET 0x02 /* 0 bits == in reset */ -# define MSP_RESET_DC5 BIT(0) -# define MSP_RESET_TVP5154 BIT(2) -# define MSP_RESET_IMAGER BIT(3) -# define MSP_RESET_ETHERNET BIT(4) -# define MSP_RESET_SYS BIT(5) -# define MSP_RESET_AIC33 BIT(7) - -/* GPIO registers ... bit patterns mostly match the source MSP ports */ -#define DM355EVM_MSP_LED 0x03 /* active low (MSP P4) */ -#define DM355EVM_MSP_SWITCH1 0x04 /* (MSP P5, masked) */ -# define MSP_SWITCH1_SW6_1 BIT(0) -# define MSP_SWITCH1_SW6_2 BIT(1) -# define MSP_SWITCH1_SW6_3 BIT(2) -# define MSP_SWITCH1_SW6_4 BIT(3) -# define MSP_SWITCH1_J1 BIT(4) /* NTSC/PAL */ -# define MSP_SWITCH1_MSP_INT BIT(5) /* active low */ -#define DM355EVM_MSP_SWITCH2 0x05 /* (MSP P6, masked) */ -# define MSP_SWITCH2_SW10 BIT(3) -# define MSP_SWITCH2_SW11 BIT(4) -# define MSP_SWITCH2_SW12 BIT(5) -# define MSP_SWITCH2_SW13 BIT(6) -# define MSP_SWITCH2_SW14 BIT(7) -#define DM355EVM_MSP_SDMMC 0x06 /* (MSP P2, masked) */ -# define MSP_SDMMC_0_WP BIT(1) -# define MSP_SDMMC_0_CD BIT(2) /* active low */ -# define MSP_SDMMC_1_WP BIT(3) -# define MSP_SDMMC_1_CD BIT(4) /* active low */ -#define DM355EVM_MSP_FIRMREV 0x07 /* not a GPIO (out of order) */ -#define DM355EVM_MSP_VIDEO_IN 0x08 /* (MSP P3, masked) */ -# define MSP_VIDEO_IMAGER BIT(7) /* low == tvp5146 */ - -/* power supply registers are currently omitted */ - -/* RTC registers */ -#define DM355EVM_MSP_RTC_0 0x12 /* LSB */ -#define DM355EVM_MSP_RTC_1 0x13 -#define DM355EVM_MSP_RTC_2 0x14 -#define DM355EVM_MSP_RTC_3 0x15 /* MSB */ - -/* input event queue registers; code == ((HIGH << 8) | LOW) */ -#define DM355EVM_MSP_INPUT_COUNT 0x16 /* decrement by reading LOW */ -#define DM355EVM_MSP_INPUT_HIGH 0x17 -#define DM355EVM_MSP_INPUT_LOW 0x18 - -#endif /* __LINUX_I2C_DM355EVM_MSP */ -- cgit v1.2.3 From 707857d997ae39743eba939a5b3aaafbab04fa78 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 19 Oct 2022 17:03:39 +0200 Subject: mfd: Remove htc-i2cpld driver The HTC Herald machine was removed, so this driver is no longer used anywhere. Cc: Cory Maccarrone Signed-off-by: Arnd Bergmann Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20221019150410.3851944-17-arnd@kernel.org --- drivers/mfd/Kconfig | 9 - drivers/mfd/Makefile | 1 - drivers/mfd/htc-i2cpld.c | 627 ----------------------------------------------- include/linux/htcpld.h | 23 -- 4 files changed, 660 deletions(-) delete mode 100644 drivers/mfd/htc-i2cpld.c delete mode 100644 include/linux/htcpld.h (limited to 'include/linux') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 3bacbe1d0600..f4a3415f7297 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -559,15 +559,6 @@ config HTC_PASIC3 HTC Magician devices, respectively. Actual functionality is handled by the leds-pasic3 and ds1wm drivers. -config HTC_I2CPLD - bool "HTC I2C PLD chip support" - depends on I2C=y && GPIOLIB - help - If you say yes here you get support for the supposed CPLD - found on omap850 HTC devices like the HTC Wizard and HTC Herald. - This device provides input and output GPIOs through an I2C - interface to one or more sub-chips. - config MFD_INTEL_QUARK_I2C_GPIO tristate "Intel Quark MFD I2C GPIO" depends on PCI diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 86c51dc4ad6a..4dd479212b3a 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -19,7 +19,6 @@ obj-$(CONFIG_MFD_EXYNOS_LPASS) += exynos-lpass.o obj-$(CONFIG_MFD_GATEWORKS_GSC) += gateworks-gsc.o obj-$(CONFIG_HTC_PASIC3) += htc-pasic3.o -obj-$(CONFIG_HTC_I2CPLD) += htc-i2cpld.o obj-$(CONFIG_MFD_TI_LP873X) += lp873x.o obj-$(CONFIG_MFD_TI_LP87565) += lp87565.o diff --git a/drivers/mfd/htc-i2cpld.c b/drivers/mfd/htc-i2cpld.c deleted file mode 100644 index b45b1346ab54..000000000000 --- a/drivers/mfd/htc-i2cpld.c +++ /dev/null @@ -1,627 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * htc-i2cpld.c - * Chip driver for an unknown CPLD chip found on omap850 HTC devices like - * the HTC Wizard and HTC Herald. - * The cpld is located on the i2c bus and acts as an input/output GPIO - * extender. - * - * Copyright (C) 2009 Cory Maccarrone - * - * Based on work done in the linwizard project - * Copyright (C) 2008-2009 Angelo Arrifano - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct htcpld_chip { - spinlock_t lock; - - /* chip info */ - u8 reset; - u8 addr; - struct device *dev; - struct i2c_client *client; - - /* Output details */ - u8 cache_out; - struct gpio_chip chip_out; - - /* Input details */ - u8 cache_in; - struct gpio_chip chip_in; - - u16 irqs_enabled; - uint irq_start; - int nirqs; - - unsigned int flow_type; - /* - * Work structure to allow for setting values outside of any - * possible interrupt context - */ - struct work_struct set_val_work; -}; - -struct htcpld_data { - /* irq info */ - u16 irqs_enabled; - uint irq_start; - int nirqs; - uint chained_irq; - struct gpio_desc *int_reset_gpio_hi; - struct gpio_desc *int_reset_gpio_lo; - - /* htcpld info */ - struct htcpld_chip *chip; - unsigned int nchips; -}; - -/* There does not appear to be a way to proactively mask interrupts - * on the htcpld chip itself. So, we simply ignore interrupts that - * aren't desired. */ -static void htcpld_mask(struct irq_data *data) -{ - struct htcpld_chip *chip = irq_data_get_irq_chip_data(data); - chip->irqs_enabled &= ~(1 << (data->irq - chip->irq_start)); - pr_debug("HTCPLD mask %d %04x\n", data->irq, chip->irqs_enabled); -} -static void htcpld_unmask(struct irq_data *data) -{ - struct htcpld_chip *chip = irq_data_get_irq_chip_data(data); - chip->irqs_enabled |= 1 << (data->irq - chip->irq_start); - pr_debug("HTCPLD unmask %d %04x\n", data->irq, chip->irqs_enabled); -} - -static int htcpld_set_type(struct irq_data *data, unsigned int flags) -{ - struct htcpld_chip *chip = irq_data_get_irq_chip_data(data); - - if (flags & ~IRQ_TYPE_SENSE_MASK) - return -EINVAL; - - /* We only allow edge triggering */ - if (flags & (IRQ_TYPE_LEVEL_LOW|IRQ_TYPE_LEVEL_HIGH)) - return -EINVAL; - - chip->flow_type = flags; - return 0; -} - -static struct irq_chip htcpld_muxed_chip = { - .name = "htcpld", - .irq_mask = htcpld_mask, - .irq_unmask = htcpld_unmask, - .irq_set_type = htcpld_set_type, -}; - -/* To properly dispatch IRQ events, we need to read from the - * chip. This is an I2C action that could possibly sleep - * (which is bad in interrupt context) -- so we use a threaded - * interrupt handler to get around that. - */ -static irqreturn_t htcpld_handler(int irq, void *dev) -{ - struct htcpld_data *htcpld = dev; - unsigned int i; - unsigned long flags; - int irqpin; - - if (!htcpld) { - pr_debug("htcpld is null in ISR\n"); - return IRQ_HANDLED; - } - - /* - * For each chip, do a read of the chip and trigger any interrupts - * desired. The interrupts will be triggered from LSB to MSB (i.e. - * bit 0 first, then bit 1, etc.) - * - * For chips that have no interrupt range specified, just skip 'em. - */ - for (i = 0; i < htcpld->nchips; i++) { - struct htcpld_chip *chip = &htcpld->chip[i]; - struct i2c_client *client; - int val; - unsigned long uval, old_val; - - if (!chip) { - pr_debug("chip %d is null in ISR\n", i); - continue; - } - - if (chip->nirqs == 0) - continue; - - client = chip->client; - if (!client) { - pr_debug("client %d is null in ISR\n", i); - continue; - } - - /* Scan the chip */ - val = i2c_smbus_read_byte_data(client, chip->cache_out); - if (val < 0) { - /* Throw a warning and skip this chip */ - dev_warn(chip->dev, "Unable to read from chip: %d\n", - val); - continue; - } - - uval = (unsigned long)val; - - spin_lock_irqsave(&chip->lock, flags); - - /* Save away the old value so we can compare it */ - old_val = chip->cache_in; - - /* Write the new value */ - chip->cache_in = uval; - - spin_unlock_irqrestore(&chip->lock, flags); - - /* - * For each bit in the data (starting at bit 0), trigger - * associated interrupts. - */ - for (irqpin = 0; irqpin < chip->nirqs; irqpin++) { - unsigned oldb, newb, type = chip->flow_type; - - irq = chip->irq_start + irqpin; - - /* Run the IRQ handler, but only if the bit value - * changed, and the proper flags are set */ - oldb = (old_val >> irqpin) & 1; - newb = (uval >> irqpin) & 1; - - if ((!oldb && newb && (type & IRQ_TYPE_EDGE_RISING)) || - (oldb && !newb && (type & IRQ_TYPE_EDGE_FALLING))) { - pr_debug("fire IRQ %d\n", irqpin); - generic_handle_irq(irq); - } - } - } - - /* - * In order to continue receiving interrupts, the int_reset_gpio must - * be asserted. - */ - if (htcpld->int_reset_gpio_hi) - gpiod_set_value(htcpld->int_reset_gpio_hi, 1); - if (htcpld->int_reset_gpio_lo) - gpiod_set_value(htcpld->int_reset_gpio_lo, 0); - - return IRQ_HANDLED; -} - -/* - * The GPIO set routines can be called from interrupt context, especially if, - * for example they're attached to the led-gpio framework and a trigger is - * enabled. As such, we declared work above in the htcpld_chip structure, - * and that work is scheduled in the set routine. The kernel can then run - * the I2C functions, which will sleep, in process context. - */ -static void htcpld_chip_set(struct gpio_chip *chip, unsigned offset, int val) -{ - struct i2c_client *client; - struct htcpld_chip *chip_data = gpiochip_get_data(chip); - unsigned long flags; - - client = chip_data->client; - if (!client) - return; - - spin_lock_irqsave(&chip_data->lock, flags); - if (val) - chip_data->cache_out |= (1 << offset); - else - chip_data->cache_out &= ~(1 << offset); - spin_unlock_irqrestore(&chip_data->lock, flags); - - schedule_work(&(chip_data->set_val_work)); -} - -static void htcpld_chip_set_ni(struct work_struct *work) -{ - struct htcpld_chip *chip_data; - struct i2c_client *client; - - chip_data = container_of(work, struct htcpld_chip, set_val_work); - client = chip_data->client; - i2c_smbus_read_byte_data(client, chip_data->cache_out); -} - -static int htcpld_chip_get(struct gpio_chip *chip, unsigned offset) -{ - struct htcpld_chip *chip_data = gpiochip_get_data(chip); - u8 cache; - - if (!strncmp(chip->label, "htcpld-out", 10)) { - cache = chip_data->cache_out; - } else if (!strncmp(chip->label, "htcpld-in", 9)) { - cache = chip_data->cache_in; - } else - return -EINVAL; - - return (cache >> offset) & 1; -} - -static int htcpld_direction_output(struct gpio_chip *chip, - unsigned offset, int value) -{ - htcpld_chip_set(chip, offset, value); - return 0; -} - -static int htcpld_direction_input(struct gpio_chip *chip, - unsigned offset) -{ - /* - * No-op: this function can only be called on the input chip. - * We do however make sure the offset is within range. - */ - return (offset < chip->ngpio) ? 0 : -EINVAL; -} - -static int htcpld_chip_to_irq(struct gpio_chip *chip, unsigned offset) -{ - struct htcpld_chip *chip_data = gpiochip_get_data(chip); - - if (offset < chip_data->nirqs) - return chip_data->irq_start + offset; - else - return -EINVAL; -} - -static void htcpld_chip_reset(struct i2c_client *client) -{ - struct htcpld_chip *chip_data = i2c_get_clientdata(client); - if (!chip_data) - return; - - i2c_smbus_read_byte_data( - client, (chip_data->cache_out = chip_data->reset)); -} - -static int htcpld_setup_chip_irq( - struct platform_device *pdev, - int chip_index) -{ - struct htcpld_data *htcpld; - struct htcpld_chip *chip; - unsigned int irq, irq_end; - - /* Get the platform and driver data */ - htcpld = platform_get_drvdata(pdev); - chip = &htcpld->chip[chip_index]; - - /* Setup irq handlers */ - irq_end = chip->irq_start + chip->nirqs; - for (irq = chip->irq_start; irq < irq_end; irq++) { - irq_set_chip_and_handler(irq, &htcpld_muxed_chip, - handle_simple_irq); - irq_set_chip_data(irq, chip); - irq_clear_status_flags(irq, IRQ_NOREQUEST | IRQ_NOPROBE); - } - - return 0; -} - -static int htcpld_register_chip_i2c( - struct platform_device *pdev, - int chip_index) -{ - struct htcpld_data *htcpld; - struct device *dev = &pdev->dev; - struct htcpld_core_platform_data *pdata; - struct htcpld_chip *chip; - struct htcpld_chip_platform_data *plat_chip_data; - struct i2c_adapter *adapter; - struct i2c_client *client; - struct i2c_board_info info; - - /* Get the platform and driver data */ - pdata = dev_get_platdata(dev); - htcpld = platform_get_drvdata(pdev); - chip = &htcpld->chip[chip_index]; - plat_chip_data = &pdata->chip[chip_index]; - - adapter = i2c_get_adapter(pdata->i2c_adapter_id); - if (!adapter) { - /* Eek, no such I2C adapter! Bail out. */ - dev_warn(dev, "Chip at i2c address 0x%x: Invalid i2c adapter %d\n", - plat_chip_data->addr, pdata->i2c_adapter_id); - return -ENODEV; - } - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA)) { - dev_warn(dev, "i2c adapter %d non-functional\n", - pdata->i2c_adapter_id); - i2c_put_adapter(adapter); - return -EINVAL; - } - - memset(&info, 0, sizeof(struct i2c_board_info)); - info.addr = plat_chip_data->addr; - strscpy(info.type, "htcpld-chip", I2C_NAME_SIZE); - info.platform_data = chip; - - /* Add the I2C device. This calls the probe() function. */ - client = i2c_new_client_device(adapter, &info); - if (IS_ERR(client)) { - /* I2C device registration failed, contineu with the next */ - dev_warn(dev, "Unable to add I2C device for 0x%x\n", - plat_chip_data->addr); - i2c_put_adapter(adapter); - return PTR_ERR(client); - } - - i2c_set_clientdata(client, chip); - snprintf(client->name, I2C_NAME_SIZE, "Chip_0x%x", client->addr); - chip->client = client; - - /* Reset the chip */ - htcpld_chip_reset(client); - chip->cache_in = i2c_smbus_read_byte_data(client, chip->cache_out); - - return 0; -} - -static void htcpld_unregister_chip_i2c( - struct platform_device *pdev, - int chip_index) -{ - struct htcpld_data *htcpld; - struct htcpld_chip *chip; - - /* Get the platform and driver data */ - htcpld = platform_get_drvdata(pdev); - chip = &htcpld->chip[chip_index]; - - i2c_unregister_device(chip->client); -} - -static int htcpld_register_chip_gpio( - struct platform_device *pdev, - int chip_index) -{ - struct htcpld_data *htcpld; - struct device *dev = &pdev->dev; - struct htcpld_core_platform_data *pdata; - struct htcpld_chip *chip; - struct htcpld_chip_platform_data *plat_chip_data; - struct gpio_chip *gpio_chip; - int ret = 0; - - /* Get the platform and driver data */ - pdata = dev_get_platdata(dev); - htcpld = platform_get_drvdata(pdev); - chip = &htcpld->chip[chip_index]; - plat_chip_data = &pdata->chip[chip_index]; - - /* Setup the GPIO chips */ - gpio_chip = &(chip->chip_out); - gpio_chip->label = "htcpld-out"; - gpio_chip->parent = dev; - gpio_chip->owner = THIS_MODULE; - gpio_chip->get = htcpld_chip_get; - gpio_chip->set = htcpld_chip_set; - gpio_chip->direction_input = NULL; - gpio_chip->direction_output = htcpld_direction_output; - gpio_chip->base = plat_chip_data->gpio_out_base; - gpio_chip->ngpio = plat_chip_data->num_gpios; - - gpio_chip = &(chip->chip_in); - gpio_chip->label = "htcpld-in"; - gpio_chip->parent = dev; - gpio_chip->owner = THIS_MODULE; - gpio_chip->get = htcpld_chip_get; - gpio_chip->set = NULL; - gpio_chip->direction_input = htcpld_direction_input; - gpio_chip->direction_output = NULL; - gpio_chip->to_irq = htcpld_chip_to_irq; - gpio_chip->base = plat_chip_data->gpio_in_base; - gpio_chip->ngpio = plat_chip_data->num_gpios; - - /* Add the GPIO chips */ - ret = gpiochip_add_data(&(chip->chip_out), chip); - if (ret) { - dev_warn(dev, "Unable to register output GPIOs for 0x%x: %d\n", - plat_chip_data->addr, ret); - return ret; - } - - ret = gpiochip_add_data(&(chip->chip_in), chip); - if (ret) { - dev_warn(dev, "Unable to register input GPIOs for 0x%x: %d\n", - plat_chip_data->addr, ret); - gpiochip_remove(&(chip->chip_out)); - return ret; - } - - return 0; -} - -static int htcpld_setup_chips(struct platform_device *pdev) -{ - struct htcpld_data *htcpld; - struct device *dev = &pdev->dev; - struct htcpld_core_platform_data *pdata; - int i; - - /* Get the platform and driver data */ - pdata = dev_get_platdata(dev); - htcpld = platform_get_drvdata(pdev); - - /* Setup each chip's output GPIOs */ - htcpld->nchips = pdata->num_chip; - htcpld->chip = devm_kcalloc(dev, - htcpld->nchips, - sizeof(struct htcpld_chip), - GFP_KERNEL); - if (!htcpld->chip) - return -ENOMEM; - - /* Add the chips as best we can */ - for (i = 0; i < htcpld->nchips; i++) { - int ret; - - /* Setup the HTCPLD chips */ - htcpld->chip[i].reset = pdata->chip[i].reset; - htcpld->chip[i].cache_out = pdata->chip[i].reset; - htcpld->chip[i].cache_in = 0; - htcpld->chip[i].dev = dev; - htcpld->chip[i].irq_start = pdata->chip[i].irq_base; - htcpld->chip[i].nirqs = pdata->chip[i].num_irqs; - - INIT_WORK(&(htcpld->chip[i].set_val_work), &htcpld_chip_set_ni); - spin_lock_init(&(htcpld->chip[i].lock)); - - /* Setup the interrupts for the chip */ - if (htcpld->chained_irq) { - ret = htcpld_setup_chip_irq(pdev, i); - if (ret) - continue; - } - - /* Register the chip with I2C */ - ret = htcpld_register_chip_i2c(pdev, i); - if (ret) - continue; - - - /* Register the chips with the GPIO subsystem */ - ret = htcpld_register_chip_gpio(pdev, i); - if (ret) { - /* Unregister the chip from i2c and continue */ - htcpld_unregister_chip_i2c(pdev, i); - continue; - } - - dev_info(dev, "Registered chip at 0x%x\n", pdata->chip[i].addr); - } - - return 0; -} - -static int htcpld_core_probe(struct platform_device *pdev) -{ - struct htcpld_data *htcpld; - struct device *dev = &pdev->dev; - struct htcpld_core_platform_data *pdata; - struct resource *res; - int ret = 0; - - if (!dev) - return -ENODEV; - - pdata = dev_get_platdata(dev); - if (!pdata) { - dev_warn(dev, "Platform data not found for htcpld core!\n"); - return -ENXIO; - } - - htcpld = devm_kzalloc(dev, sizeof(struct htcpld_data), GFP_KERNEL); - if (!htcpld) - return -ENOMEM; - - /* Find chained irq */ - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (res) { - int flags; - htcpld->chained_irq = res->start; - - /* Setup the chained interrupt handler */ - flags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | - IRQF_ONESHOT; - ret = request_threaded_irq(htcpld->chained_irq, - NULL, htcpld_handler, - flags, pdev->name, htcpld); - if (ret) { - dev_warn(dev, "Unable to setup chained irq handler: %d\n", ret); - return ret; - } else - device_init_wakeup(dev, 0); - } - - /* Set the driver data */ - platform_set_drvdata(pdev, htcpld); - - /* Setup the htcpld chips */ - ret = htcpld_setup_chips(pdev); - if (ret) - return ret; - - /* Request the GPIO(s) for the int reset and set them up */ - htcpld->int_reset_gpio_hi = gpiochip_request_own_desc(&htcpld->chip[2].chip_out, - 7, "htcpld-core", GPIO_ACTIVE_HIGH, - GPIOD_OUT_HIGH); - if (IS_ERR(htcpld->int_reset_gpio_hi)) { - /* - * If it failed, that sucks, but we can probably - * continue on without it. - */ - htcpld->int_reset_gpio_hi = NULL; - dev_warn(dev, "Unable to request int_reset_gpio_hi -- interrupts may not work\n"); - } - - htcpld->int_reset_gpio_lo = gpiochip_request_own_desc(&htcpld->chip[2].chip_out, - 0, "htcpld-core", GPIO_ACTIVE_HIGH, - GPIOD_OUT_LOW); - if (IS_ERR(htcpld->int_reset_gpio_lo)) { - /* - * If it failed, that sucks, but we can probably - * continue on without it. - */ - htcpld->int_reset_gpio_lo = NULL; - dev_warn(dev, "Unable to request int_reset_gpio_lo -- interrupts may not work\n"); - } - - dev_info(dev, "Initialized successfully\n"); - return 0; -} - -/* The I2C Driver -- used internally */ -static const struct i2c_device_id htcpld_chip_id[] = { - { "htcpld-chip", 0 }, - { } -}; - -static struct i2c_driver htcpld_chip_driver = { - .driver = { - .name = "htcpld-chip", - }, - .id_table = htcpld_chip_id, -}; - -/* The Core Driver */ -static struct platform_driver htcpld_core_driver = { - .driver = { - .name = "i2c-htcpld", - }, -}; - -static int __init htcpld_core_init(void) -{ - int ret; - - /* Register the I2C Chip driver */ - ret = i2c_add_driver(&htcpld_chip_driver); - if (ret) - return ret; - - /* Probe for our chips */ - return platform_driver_probe(&htcpld_core_driver, htcpld_core_probe); -} -device_initcall(htcpld_core_init); diff --git a/include/linux/htcpld.h b/include/linux/htcpld.h deleted file mode 100644 index 5f8ac9b1d724..000000000000 --- a/include/linux/htcpld.h +++ /dev/null @@ -1,23 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __LINUX_HTCPLD_H -#define __LINUX_HTCPLD_H - -struct htcpld_chip_platform_data { - unsigned int addr; - unsigned int reset; - unsigned int num_gpios; - unsigned int gpio_out_base; - unsigned int gpio_in_base; - unsigned int irq_base; - unsigned int num_irqs; -}; - -struct htcpld_core_platform_data { - unsigned int i2c_adapter_id; - - struct htcpld_chip_platform_data *chip; - unsigned int num_chip; -}; - -#endif /* __LINUX_HTCPLD_H */ - -- cgit v1.2.3 From 245cb473e5388fcbc01c7284b6a4e1446cdbf054 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Sun, 23 Oct 2022 10:48:30 +0100 Subject: mfd: pcf50633: Remove #ifdef guards for PM related functions Use the new EXPORT_GPL_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr() macros to handle the .suspend/.resume callbacks. These macros allow the suspend and resume functions to be automatically dropped by the compiler when CONFIG_SUSPEND is disabled, without having to use #ifdef guards. This has the advantage of always compiling these functions in, independently of any Kconfig option. Thanks to that, bugs and other regressions are subsequently easier to catch. Signed-off-by: Paul Cercueil Signed-off-by: Lee Jones --- drivers/mfd/pcf50633-core.c | 22 +--------------------- drivers/mfd/pcf50633-irq.c | 13 ++++++++----- include/linux/mfd/pcf50633/core.h | 6 ++---- 3 files changed, 11 insertions(+), 30 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/pcf50633-core.c b/drivers/mfd/pcf50633-core.c index da39a7940560..0e4fc99e9f49 100644 --- a/drivers/mfd/pcf50633-core.c +++ b/drivers/mfd/pcf50633-core.c @@ -158,26 +158,6 @@ pcf50633_client_dev_register(struct pcf50633 *pcf, const char *name, } } -#ifdef CONFIG_PM_SLEEP -static int pcf50633_suspend(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct pcf50633 *pcf = i2c_get_clientdata(client); - - return pcf50633_irq_suspend(pcf); -} - -static int pcf50633_resume(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct pcf50633 *pcf = i2c_get_clientdata(client); - - return pcf50633_irq_resume(pcf); -} -#endif - -static SIMPLE_DEV_PM_OPS(pcf50633_pm, pcf50633_suspend, pcf50633_resume); - static const struct regmap_config pcf50633_regmap_config = { .reg_bits = 8, .val_bits = 8, @@ -299,7 +279,7 @@ MODULE_DEVICE_TABLE(i2c, pcf50633_id_table); static struct i2c_driver pcf50633_driver = { .driver = { .name = "pcf50633", - .pm = &pcf50633_pm, + .pm = pm_sleep_ptr(&pcf50633_pm), }, .id_table = pcf50633_id_table, .probe_new = pcf50633_probe, diff --git a/drivers/mfd/pcf50633-irq.c b/drivers/mfd/pcf50633-irq.c index 2096afb0ce9b..e85af7f1cb0b 100644 --- a/drivers/mfd/pcf50633-irq.c +++ b/drivers/mfd/pcf50633-irq.c @@ -7,6 +7,7 @@ * All rights reserved. */ +#include #include #include #include @@ -218,10 +219,10 @@ out: return IRQ_HANDLED; } -#ifdef CONFIG_PM - -int pcf50633_irq_suspend(struct pcf50633 *pcf) +static int pcf50633_suspend(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); + struct pcf50633 *pcf = i2c_get_clientdata(client); int ret; int i; u8 res[5]; @@ -257,8 +258,10 @@ out: return ret; } -int pcf50633_irq_resume(struct pcf50633 *pcf) +static int pcf50633_resume(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); + struct pcf50633 *pcf = i2c_get_clientdata(client); int ret; /* Write the saved mask registers */ @@ -273,7 +276,7 @@ int pcf50633_irq_resume(struct pcf50633 *pcf) return ret; } -#endif +EXPORT_GPL_SIMPLE_DEV_PM_OPS(pcf50633_pm, pcf50633_suspend, pcf50633_resume); int pcf50633_irq_init(struct pcf50633 *pcf, int irq) { diff --git a/include/linux/mfd/pcf50633/core.h b/include/linux/mfd/pcf50633/core.h index 3f752dc62a6c..539f27f8bd89 100644 --- a/include/linux/mfd/pcf50633/core.h +++ b/include/linux/mfd/pcf50633/core.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -226,9 +227,6 @@ static inline struct pcf50633 *dev_to_pcf50633(struct device *dev) int pcf50633_irq_init(struct pcf50633 *pcf, int irq); void pcf50633_irq_free(struct pcf50633 *pcf); -#ifdef CONFIG_PM -int pcf50633_irq_suspend(struct pcf50633 *pcf); -int pcf50633_irq_resume(struct pcf50633 *pcf); -#endif +extern const struct dev_pm_ops pcf50633_pm; #endif -- cgit v1.2.3 From 4d8a6ae23af64a37803c0d15922819d27b4b8b08 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Sun, 23 Oct 2022 10:48:48 +0100 Subject: mfd: stmfx: Remove #ifdef guards for PM related functions Use the new DEFINE_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr() macros to handle the .suspend/.resume callbacks. These macros allow the suspend and resume functions to be automatically dropped by the compiler when CONFIG_SUSPEND is disabled, without having to use #ifdef guards. This has the advantage of always compiling these functions in, independently of any Kconfig option. Thanks to that, bugs and other regressions are subsequently easier to catch. Signed-off-by: Paul Cercueil Signed-off-by: Lee Jones --- drivers/mfd/stmfx.c | 6 ++---- include/linux/mfd/stmfx.h | 2 -- 2 files changed, 2 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/stmfx.c b/drivers/mfd/stmfx.c index 025b8761e50f..e281971ba54e 100644 --- a/drivers/mfd/stmfx.c +++ b/drivers/mfd/stmfx.c @@ -473,7 +473,6 @@ static void stmfx_remove(struct i2c_client *client) stmfx_chip_exit(client); } -#ifdef CONFIG_PM_SLEEP static int stmfx_suspend(struct device *dev) { struct stmfx *stmfx = dev_get_drvdata(dev); @@ -539,9 +538,8 @@ static int stmfx_resume(struct device *dev) return 0; } -#endif -static SIMPLE_DEV_PM_OPS(stmfx_dev_pm_ops, stmfx_suspend, stmfx_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(stmfx_dev_pm_ops, stmfx_suspend, stmfx_resume); static const struct of_device_id stmfx_of_match[] = { { .compatible = "st,stmfx-0300", }, @@ -553,7 +551,7 @@ static struct i2c_driver stmfx_driver = { .driver = { .name = "stmfx-core", .of_match_table = stmfx_of_match, - .pm = &stmfx_dev_pm_ops, + .pm = pm_sleep_ptr(&stmfx_dev_pm_ops), }, .probe_new = stmfx_probe, .remove = stmfx_remove, diff --git a/include/linux/mfd/stmfx.h b/include/linux/mfd/stmfx.h index 744dce63946e..967a2e486800 100644 --- a/include/linux/mfd/stmfx.h +++ b/include/linux/mfd/stmfx.h @@ -113,10 +113,8 @@ struct stmfx { struct irq_domain *irq_domain; struct mutex lock; /* IRQ bus lock */ u8 irq_src; -#ifdef CONFIG_PM u8 bkp_sysctrl; u8 bkp_irqoutpin; -#endif }; int stmfx_function_enable(struct stmfx *stmfx, u32 func); -- cgit v1.2.3 From 74c17a0a49a6ad3b32cb130f25196d1f8d5d560e Mon Sep 17 00:00:00 2001 From: Jerome Neanne Date: Fri, 4 Nov 2022 16:23:09 +0100 Subject: mfd: tps65219: Add driver for TI TPS65219 PMIC The TPS65219 is a power management IC PMIC designed to supply a wide range of SoCs in both portable and stationary applications. Any SoC can control TPS65219 over a standard I2C interface. It contains the following components: - Regulators. - Over Temperature warning and Shut down. - GPIOs - Multi Function Pins (MFP) - power-button This patch adds support for tps65219 PMIC. At this time only the functionalities listed below are made available: - Regulators probe and functionalities - warm and cold reset support - SW shutdown support - Regulator warnings via IRQs - Power-button via IRQ Signed-off-by: Jerome Neanne Signed-off-by: Markus Schneider-Pargmann Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20221104152311.1098603-5-jneanne@baylibre.com --- MAINTAINERS | 1 + drivers/mfd/Kconfig | 14 ++ drivers/mfd/Makefile | 1 + drivers/mfd/tps65219.c | 299 +++++++++++++++++++++++++++++++++++++ include/linux/mfd/tps65219.h | 345 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 660 insertions(+) create mode 100644 drivers/mfd/tps65219.c create mode 100644 include/linux/mfd/tps65219.h (limited to 'include/linux') diff --git a/MAINTAINERS b/MAINTAINERS index cf0f18502372..43b7cccbbaa4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15110,6 +15110,7 @@ F: drivers/mfd/menelaus.c F: drivers/mfd/palmas.c F: drivers/mfd/tps65217.c F: drivers/mfd/tps65218.c +F: drivers/mfd/tps65219.c F: drivers/mfd/tps65910.c F: drivers/mfd/twl-core.[ch] F: drivers/mfd/twl4030*.c diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index d6f1b0d3d829..2d563277105c 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1625,6 +1625,20 @@ config MFD_TPS65218 This driver can also be built as a module. If so, the module will be called tps65218. +config MFD_TPS65219 + tristate "TI TPS65219 Power Management IC" + depends on I2C && OF + select MFD_CORE + select REGMAP_I2C + select REGMAP_IRQ + help + If you say yes here you get support for the TPS65219 series of Power + Management ICs. These include voltage regulators, GPIOs and + push/power button that is often used in portable devices. + + This driver can also be built as a module. If so, the module + will be called tps65219. + config MFD_TPS6586X bool "TI TPS6586x Power Management chips" depends on I2C=y diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 4dd479212b3a..457471478a93 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -97,6 +97,7 @@ obj-$(CONFIG_TPS6507X) += tps6507x.o obj-$(CONFIG_MFD_TPS65086) += tps65086.o obj-$(CONFIG_MFD_TPS65217) += tps65217.o obj-$(CONFIG_MFD_TPS65218) += tps65218.o +obj-$(CONFIG_MFD_TPS65219) += tps65219.o obj-$(CONFIG_MFD_TPS65910) += tps65910.o obj-$(CONFIG_MFD_TPS65912) += tps65912-core.o obj-$(CONFIG_MFD_TPS65912_I2C) += tps65912-i2c.o diff --git a/drivers/mfd/tps65219.c b/drivers/mfd/tps65219.c new file mode 100644 index 000000000000..0e402fda206b --- /dev/null +++ b/drivers/mfd/tps65219.c @@ -0,0 +1,299 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Driver for TPS65219 Integrated Power Management Integrated Chips (PMIC) +// +// Copyright (C) 2022 BayLibre Incorporated - https://www.baylibre.com/ + +#include +#include +#include + +#include +#include + +static int tps65219_warm_reset(struct tps65219 *tps) +{ + return regmap_update_bits(tps->regmap, TPS65219_REG_MFP_CTRL, + TPS65219_MFP_WARM_RESET_I2C_CTRL_MASK, + TPS65219_MFP_WARM_RESET_I2C_CTRL_MASK); +} + +static int tps65219_cold_reset(struct tps65219 *tps) +{ + return regmap_update_bits(tps->regmap, TPS65219_REG_MFP_CTRL, + TPS65219_MFP_COLD_RESET_I2C_CTRL_MASK, + TPS65219_MFP_COLD_RESET_I2C_CTRL_MASK); +} + +static int tps65219_restart(struct notifier_block *this, + unsigned long reboot_mode, void *cmd) +{ + struct tps65219 *tps; + + tps = container_of(this, struct tps65219, nb); + + if (reboot_mode == REBOOT_WARM) + tps65219_warm_reset(tps); + else + tps65219_cold_reset(tps); + + return NOTIFY_DONE; +} + +static struct notifier_block pmic_rst_restart_nb = { + .notifier_call = tps65219_restart, + .priority = 200, +}; + +static const struct resource tps65219_pwrbutton_resources[] = { + DEFINE_RES_IRQ_NAMED(TPS65219_INT_PB_FALLING_EDGE_DETECT, "falling"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_PB_RISING_EDGE_DETECT, "rising"), +}; + +static const struct resource tps65219_regulator_resources[] = { + DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO3_SCG, "LDO3_SCG"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO3_OC, "LDO3_OC"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO3_UV, "LDO3_UV"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO4_SCG, "LDO4_SCG"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO4_OC, "LDO4_OC"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO4_UV, "LDO4_UV"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO1_SCG, "LDO1_SCG"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO1_OC, "LDO1_OC"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO1_UV, "LDO1_UV"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO2_SCG, "LDO2_SCG"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO2_OC, "LDO2_OC"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO2_UV, "LDO2_UV"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK3_SCG, "BUCK3_SCG"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK3_OC, "BUCK3_OC"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK3_NEG_OC, "BUCK3_NEG_OC"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK3_UV, "BUCK3_UV"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK1_SCG, "BUCK1_SCG"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK1_OC, "BUCK1_OC"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK1_NEG_OC, "BUCK1_NEG_OC"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK1_UV, "BUCK1_UV"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK2_SCG, "BUCK2_SCG"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK2_OC, "BUCK2_OC"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK2_NEG_OC, "BUCK2_NEG_OC"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK2_UV, "BUCK2_UV"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK1_RV, "BUCK1_RV"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK2_RV, "BUCK2_RV"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK3_RV, "BUCK3_RV"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO1_RV, "LDO1_RV"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO2_RV, "LDO2_RV"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO3_RV, "LDO3_RV"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO4_RV, "LDO4_RV"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK1_RV_SD, "BUCK1_RV_SD"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK2_RV_SD, "BUCK2_RV_SD"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_BUCK3_RV_SD, "BUCK3_RV_SD"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO1_RV_SD, "LDO1_RV_SD"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO2_RV_SD, "LDO2_RV_SD"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO3_RV_SD, "LDO3_RV_SD"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_LDO4_RV_SD, "LDO4_RV_SD"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_TIMEOUT, "TIMEOUT"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_SENSOR_3_WARM, "SENSOR_3_WARM"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_SENSOR_2_WARM, "SENSOR_2_WARM"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_SENSOR_1_WARM, "SENSOR_1_WARM"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_SENSOR_0_WARM, "SENSOR_0_WARM"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_SENSOR_3_HOT, "SENSOR_3_HOT"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_SENSOR_2_HOT, "SENSOR_2_HOT"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_SENSOR_1_HOT, "SENSOR_1_HOT"), + DEFINE_RES_IRQ_NAMED(TPS65219_INT_SENSOR_0_HOT, "SENSOR_0_HOT"), +}; + +static const struct mfd_cell tps65219_cells[] = { + { + .name = "tps65219-regulator", + .resources = tps65219_regulator_resources, + .num_resources = ARRAY_SIZE(tps65219_regulator_resources), + }, + { .name = "tps65219-gpios", }, +}; + +static const struct mfd_cell tps65219_pwrbutton_cell = { + .name = "tps65219-pwrbutton", + .resources = tps65219_pwrbutton_resources, + .num_resources = ARRAY_SIZE(tps65219_pwrbutton_resources), +}; + +static const struct regmap_config tps65219_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = TPS65219_REG_FACTORY_CONFIG_2, +}; + +/* + * Mapping of main IRQ register bits to sub-IRQ register offsets so that we can + * access corect sub-IRQ registers based on bits that are set in main IRQ + * register. + */ +/* Timeout Residual Voltage Shutdown */ +static unsigned int bit0_offsets[] = { TPS65219_REG_INT_TO_RV_POS }; +static unsigned int bit1_offsets[] = { TPS65219_REG_INT_RV_POS }; /* Residual Voltage */ +static unsigned int bit2_offsets[] = { TPS65219_REG_INT_SYS_POS }; /* System */ +static unsigned int bit3_offsets[] = { TPS65219_REG_INT_BUCK_1_2_POS }; /* Buck 1-2 */ +static unsigned int bit4_offsets[] = { TPS65219_REG_INT_BUCK_3_POS }; /* Buck 3 */ +static unsigned int bit5_offsets[] = { TPS65219_REG_INT_LDO_1_2_POS }; /* LDO 1-2 */ +static unsigned int bit6_offsets[] = { TPS65219_REG_INT_LDO_3_4_POS }; /* LDO 3-4 */ +static unsigned int bit7_offsets[] = { TPS65219_REG_INT_PB_POS }; /* Power Button */ + +static struct regmap_irq_sub_irq_map tps65219_sub_irq_offsets[] = { + REGMAP_IRQ_MAIN_REG_OFFSET(bit0_offsets), + REGMAP_IRQ_MAIN_REG_OFFSET(bit1_offsets), + REGMAP_IRQ_MAIN_REG_OFFSET(bit2_offsets), + REGMAP_IRQ_MAIN_REG_OFFSET(bit3_offsets), + REGMAP_IRQ_MAIN_REG_OFFSET(bit4_offsets), + REGMAP_IRQ_MAIN_REG_OFFSET(bit5_offsets), + REGMAP_IRQ_MAIN_REG_OFFSET(bit6_offsets), + REGMAP_IRQ_MAIN_REG_OFFSET(bit7_offsets), +}; + +#define TPS65219_REGMAP_IRQ_REG(int_name, register_position) \ + REGMAP_IRQ_REG(int_name, register_position, int_name##_MASK) + +static struct regmap_irq tps65219_irqs[] = { + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO3_SCG, TPS65219_REG_INT_LDO_3_4_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO3_OC, TPS65219_REG_INT_LDO_3_4_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO3_UV, TPS65219_REG_INT_LDO_3_4_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO4_SCG, TPS65219_REG_INT_LDO_3_4_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO4_OC, TPS65219_REG_INT_LDO_3_4_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO4_UV, TPS65219_REG_INT_LDO_3_4_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO1_SCG, TPS65219_REG_INT_LDO_1_2_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO1_OC, TPS65219_REG_INT_LDO_1_2_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO1_UV, TPS65219_REG_INT_LDO_1_2_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO2_SCG, TPS65219_REG_INT_LDO_1_2_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO2_OC, TPS65219_REG_INT_LDO_1_2_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO2_UV, TPS65219_REG_INT_LDO_1_2_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK3_SCG, TPS65219_REG_INT_BUCK_3_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK3_OC, TPS65219_REG_INT_BUCK_3_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK3_NEG_OC, TPS65219_REG_INT_BUCK_3_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK3_UV, TPS65219_REG_INT_BUCK_3_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK2_SCG, TPS65219_REG_INT_BUCK_1_2_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK2_OC, TPS65219_REG_INT_BUCK_1_2_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK2_NEG_OC, TPS65219_REG_INT_BUCK_1_2_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK2_UV, TPS65219_REG_INT_BUCK_1_2_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK1_SCG, TPS65219_REG_INT_BUCK_1_2_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK1_OC, TPS65219_REG_INT_BUCK_1_2_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK1_NEG_OC, TPS65219_REG_INT_BUCK_1_2_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK1_UV, TPS65219_REG_INT_BUCK_1_2_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_SENSOR_3_WARM, TPS65219_REG_INT_SYS_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_SENSOR_2_WARM, TPS65219_REG_INT_SYS_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_SENSOR_1_WARM, TPS65219_REG_INT_SYS_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_SENSOR_0_WARM, TPS65219_REG_INT_SYS_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_SENSOR_3_HOT, TPS65219_REG_INT_SYS_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_SENSOR_2_HOT, TPS65219_REG_INT_SYS_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_SENSOR_1_HOT, TPS65219_REG_INT_SYS_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_SENSOR_0_HOT, TPS65219_REG_INT_SYS_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK1_RV, TPS65219_REG_INT_RV_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK2_RV, TPS65219_REG_INT_RV_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK3_RV, TPS65219_REG_INT_RV_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO1_RV, TPS65219_REG_INT_RV_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO2_RV, TPS65219_REG_INT_RV_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO3_RV, TPS65219_REG_INT_RV_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO4_RV, TPS65219_REG_INT_RV_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK1_RV_SD, TPS65219_REG_INT_TO_RV_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK2_RV_SD, TPS65219_REG_INT_TO_RV_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_BUCK3_RV_SD, TPS65219_REG_INT_TO_RV_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO1_RV_SD, TPS65219_REG_INT_TO_RV_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO2_RV_SD, TPS65219_REG_INT_TO_RV_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO3_RV_SD, TPS65219_REG_INT_TO_RV_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_LDO4_RV_SD, TPS65219_REG_INT_TO_RV_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_TIMEOUT, TPS65219_REG_INT_TO_RV_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_PB_FALLING_EDGE_DETECT, TPS65219_REG_INT_PB_POS), + TPS65219_REGMAP_IRQ_REG(TPS65219_INT_PB_RISING_EDGE_DETECT, TPS65219_REG_INT_PB_POS), +}; + +static struct regmap_irq_chip tps65219_irq_chip = { + .name = "tps65219_irq", + .main_status = TPS65219_REG_INT_SOURCE, + .num_main_regs = 1, + .num_main_status_bits = 8, + .irqs = tps65219_irqs, + .num_irqs = ARRAY_SIZE(tps65219_irqs), + .status_base = TPS65219_REG_INT_LDO_3_4, + .ack_base = TPS65219_REG_INT_LDO_3_4, + .clear_ack = 1, + .num_regs = 8, + .sub_reg_offsets = tps65219_sub_irq_offsets, +}; + +static int tps65219_probe(struct i2c_client *client) +{ + struct tps65219 *tps; + unsigned int chipid; + bool pwr_button; + int ret; + + tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL); + if (!tps) + return -ENOMEM; + + i2c_set_clientdata(client, tps); + + tps->dev = &client->dev; + + tps->regmap = devm_regmap_init_i2c(client, &tps65219_regmap_config); + if (IS_ERR(tps->regmap)) { + ret = PTR_ERR(tps->regmap); + dev_err(tps->dev, "Failed to allocate register map: %d\n", ret); + return ret; + } + + ret = devm_regmap_add_irq_chip(&client->dev, tps->regmap, client->irq, + IRQF_ONESHOT, 0, &tps65219_irq_chip, + &tps->irq_data); + if (ret) + return ret; + + ret = regmap_read(tps->regmap, TPS65219_REG_TI_DEV_ID, &chipid); + if (ret) { + dev_err(tps->dev, "Failed to read device ID: %d\n", ret); + return ret; + } + + ret = devm_mfd_add_devices(tps->dev, PLATFORM_DEVID_AUTO, + tps65219_cells, ARRAY_SIZE(tps65219_cells), + NULL, 0, regmap_irq_get_domain(tps->irq_data)); + if (ret) { + dev_err(tps->dev, "Failed to add child devices: %d\n", ret); + return ret; + } + + pwr_button = of_property_read_bool(tps->dev->of_node, "ti,power-button"); + if (pwr_button) { + ret = devm_mfd_add_devices(tps->dev, PLATFORM_DEVID_AUTO, + &tps65219_pwrbutton_cell, 1, NULL, 0, + regmap_irq_get_domain(tps->irq_data)); + if (ret) { + dev_err(tps->dev, "Failed to add power-button: %d\n", ret); + return ret; + } + } + + tps->nb = pmic_rst_restart_nb; + ret = register_restart_handler(&tps->nb); + if (ret) { + dev_err(tps->dev, "cannot register restart handler, %d\n", ret); + return ret; + } + + return 0; +} + +static const struct of_device_id of_tps65219_match_table[] = { + { .compatible = "ti,tps65219", }, + {} +}; +MODULE_DEVICE_TABLE(of, of_tps65219_match_table); + +static struct i2c_driver tps65219_driver = { + .driver = { + .name = "tps65219", + .of_match_table = of_tps65219_match_table, + }, + .probe_new = tps65219_probe, +}; +module_i2c_driver(tps65219_driver); + +MODULE_AUTHOR("Jerome Neanne "); +MODULE_DESCRIPTION("TPS65219 power management IC driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/tps65219.h b/include/linux/mfd/tps65219.h new file mode 100644 index 000000000000..e6826e34e2a6 --- /dev/null +++ b/include/linux/mfd/tps65219.h @@ -0,0 +1,345 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Functions to access TPS65219 Power Management IC. + * + * Copyright (C) 2022 BayLibre Incorporated - https://www.baylibre.com/ + */ + +#ifndef MFD_TPS65219_H +#define MFD_TPS65219_H + +#include +#include +#include + +struct regmap; +struct regmap_irq_chip_data; + +#define TPS65219_1V35 1350000 +#define TPS65219_1V8 1800000 + +/* TPS chip id list */ +#define TPS65219 0xF0 + +/* I2C ID for TPS65219 part */ +#define TPS65219_I2C_ID 0x24 + +/* All register addresses */ +#define TPS65219_REG_TI_DEV_ID 0x00 +#define TPS65219_REG_NVM_ID 0x01 +#define TPS65219_REG_ENABLE_CTRL 0x02 +#define TPS65219_REG_BUCKS_CONFIG 0x03 +#define TPS65219_REG_LDO4_VOUT 0x04 +#define TPS65219_REG_LDO3_VOUT 0x05 +#define TPS65219_REG_LDO2_VOUT 0x06 +#define TPS65219_REG_LDO1_VOUT 0x07 +#define TPS65219_REG_BUCK3_VOUT 0x8 +#define TPS65219_REG_BUCK2_VOUT 0x9 +#define TPS65219_REG_BUCK1_VOUT 0xA +#define TPS65219_REG_LDO4_SEQUENCE_SLOT 0xB +#define TPS65219_REG_LDO3_SEQUENCE_SLOT 0xC +#define TPS65219_REG_LDO2_SEQUENCE_SLOT 0xD +#define TPS65219_REG_LDO1_SEQUENCE_SLOT 0xE +#define TPS65219_REG_BUCK3_SEQUENCE_SLOT 0xF +#define TPS65219_REG_BUCK2_SEQUENCE_SLOT 0x10 +#define TPS65219_REG_BUCK1_SEQUENCE_SLOT 0x11 +#define TPS65219_REG_nRST_SEQUENCE_SLOT 0x12 +#define TPS65219_REG_GPIO_SEQUENCE_SLOT 0x13 +#define TPS65219_REG_GPO2_SEQUENCE_SLOT 0x14 +#define TPS65219_REG_GPO1_SEQUENCE_SLOT 0x15 +#define TPS65219_REG_POWER_UP_SLOT_DURATION_1 0x16 +#define TPS65219_REG_POWER_UP_SLOT_DURATION_2 0x17 +#define TPS65219_REG_POWER_UP_SLOT_DURATION_3 0x18 +#define TPS65219_REG_POWER_UP_SLOT_DURATION_4 0x19 +#define TPS65219_REG_POWER_DOWN_SLOT_DURATION_1 0x1A +#define TPS65219_REG_POWER_DOWN_SLOT_DURATION_2 0x1B +#define TPS65219_REG_POWER_DOWN_SLOT_DURATION_3 0x1C +#define TPS65219_REG_POWER_DOWN_SLOT_DURATION_4 0x1D +#define TPS65219_REG_GENERAL_CONFIG 0x1E +#define TPS65219_REG_MFP_1_CONFIG 0x1F +#define TPS65219_REG_MFP_2_CONFIG 0x20 +#define TPS65219_REG_STBY_1_CONFIG 0x21 +#define TPS65219_REG_STBY_2_CONFIG 0x22 +#define TPS65219_REG_OC_DEGL_CONFIG 0x23 +/* 'sub irq' MASK registers */ +#define TPS65219_REG_INT_MASK_UV 0x24 +#define TPS65219_REG_MASK_CONFIG 0x25 + +#define TPS65219_REG_I2C_ADDRESS_REG 0x26 +#define TPS65219_REG_USER_GENERAL_NVM_STORAGE 0x27 +#define TPS65219_REG_MANUFACTURING_VER 0x28 +#define TPS65219_REG_MFP_CTRL 0x29 +#define TPS65219_REG_DISCHARGE_CONFIG 0x2A +/* main irq registers */ +#define TPS65219_REG_INT_SOURCE 0x2B +/* 'sub irq' registers */ +#define TPS65219_REG_INT_LDO_3_4 0x2C +#define TPS65219_REG_INT_LDO_1_2 0x2D +#define TPS65219_REG_INT_BUCK_3 0x2E +#define TPS65219_REG_INT_BUCK_1_2 0x2F +#define TPS65219_REG_INT_SYSTEM 0x30 +#define TPS65219_REG_INT_RV 0x31 +#define TPS65219_REG_INT_TIMEOUT_RV_SD 0x32 +#define TPS65219_REG_INT_PB 0x33 + +#define TPS65219_REG_INT_LDO_3_4_POS 0 +#define TPS65219_REG_INT_LDO_1_2_POS 1 +#define TPS65219_REG_INT_BUCK_3_POS 2 +#define TPS65219_REG_INT_BUCK_1_2_POS 3 +#define TPS65219_REG_INT_SYS_POS 4 +#define TPS65219_REG_INT_RV_POS 5 +#define TPS65219_REG_INT_TO_RV_POS 6 +#define TPS65219_REG_INT_PB_POS 7 + +#define TPS65219_REG_USER_NVM_CMD 0x34 +#define TPS65219_REG_POWER_UP_STATUS 0x35 +#define TPS65219_REG_SPARE_2 0x36 +#define TPS65219_REG_SPARE_3 0x37 +#define TPS65219_REG_FACTORY_CONFIG_2 0x41 + +/* Register field definitions */ +#define TPS65219_DEVID_REV_MASK GENMASK(7, 0) +#define TPS65219_BUCKS_LDOS_VOUT_VSET_MASK GENMASK(5, 0) +#define TPS65219_BUCKS_UV_THR_SEL_MASK BIT(6) +#define TPS65219_BUCKS_BW_SEL_MASK BIT(7) +#define LDO_BYP_SHIFT 6 +#define TPS65219_LDOS_BYP_CONFIG_MASK BIT(LDO_BYP_SHIFT) +#define TPS65219_LDOS_LSW_CONFIG_MASK BIT(7) +/* Regulators enable control */ +#define TPS65219_ENABLE_BUCK1_EN_MASK BIT(0) +#define TPS65219_ENABLE_BUCK2_EN_MASK BIT(1) +#define TPS65219_ENABLE_BUCK3_EN_MASK BIT(2) +#define TPS65219_ENABLE_LDO1_EN_MASK BIT(3) +#define TPS65219_ENABLE_LDO2_EN_MASK BIT(4) +#define TPS65219_ENABLE_LDO3_EN_MASK BIT(5) +#define TPS65219_ENABLE_LDO4_EN_MASK BIT(6) +/* power ON-OFF sequence slot */ +#define TPS65219_BUCKS_LDOS_SEQUENCE_OFF_SLOT_MASK GENMASK(3, 0) +#define TPS65219_BUCKS_LDOS_SEQUENCE_ON_SLOT_MASK GENMASK(7, 4) +/* TODO: Not needed, same mapping as TPS65219_ENABLE_REGNAME_EN, factorize */ +#define TPS65219_STBY1_BUCK1_STBY_EN_MASK BIT(0) +#define TPS65219_STBY1_BUCK2_STBY_EN_MASK BIT(1) +#define TPS65219_STBY1_BUCK3_STBY_EN_MASK BIT(2) +#define TPS65219_STBY1_LDO1_STBY_EN_MASK BIT(3) +#define TPS65219_STBY1_LDO2_STBY_EN_MASK BIT(4) +#define TPS65219_STBY1_LDO3_STBY_EN_MASK BIT(5) +#define TPS65219_STBY1_LDO4_STBY_EN_MASK BIT(6) +/* STBY_2 config */ +#define TPS65219_STBY2_GPO1_STBY_EN_MASK BIT(0) +#define TPS65219_STBY2_GPO2_STBY_EN_MASK BIT(1) +#define TPS65219_STBY2_GPIO_STBY_EN_MASK BIT(2) +/* MFP Control */ +#define TPS65219_MFP_I2C_OFF_REQ_MASK BIT(0) +#define TPS65219_MFP_STBY_I2C_CTRL_MASK BIT(1) +#define TPS65219_MFP_COLD_RESET_I2C_CTRL_MASK BIT(2) +#define TPS65219_MFP_WARM_RESET_I2C_CTRL_MASK BIT(3) +#define TPS65219_MFP_GPIO_STATUS_MASK BIT(4) +/* MFP_1 Config */ +#define TPS65219_MFP_1_VSEL_DDR_SEL_MASK BIT(0) +#define TPS65219_MFP_1_VSEL_SD_POL_MASK BIT(1) +#define TPS65219_MFP_1_VSEL_RAIL_MASK BIT(2) +/* MFP_2 Config */ +#define TPS65219_MFP_2_MODE_STBY_MASK GENMASK(1, 0) +#define TPS65219_MFP_2_MODE_RESET_MASK BIT(2) +#define TPS65219_MFP_2_EN_PB_VSENSE_DEGL_MASK BIT(3) +#define TPS65219_MFP_2_EN_PB_VSENSE_MASK GENMASK(5, 4) +#define TPS65219_MFP_2_WARM_COLD_RESET_MASK BIT(6) +#define TPS65219_MFP_2_PU_ON_FSD_MASK BIT(7) +#define TPS65219_MFP_2_EN 0 +#define TPS65219_MFP_2_PB BIT(4) +#define TPS65219_MFP_2_VSENSE BIT(5) +/* MASK_UV Config */ +#define TPS65219_REG_MASK_UV_LDO1_UV_MASK BIT(0) +#define TPS65219_REG_MASK_UV_LDO2_UV_MASK BIT(1) +#define TPS65219_REG_MASK_UV_LDO3_UV_MASK BIT(2) +#define TPS65219_REG_MASK_UV_LDO4_UV_MASK BIT(3) +#define TPS65219_REG_MASK_UV_BUCK1_UV_MASK BIT(4) +#define TPS65219_REG_MASK_UV_BUCK2_UV_MASK BIT(5) +#define TPS65219_REG_MASK_UV_BUCK3_UV_MASK BIT(6) +#define TPS65219_REG_MASK_UV_RETRY_MASK BIT(7) +/* MASK Config */ +// SENSOR_N_WARM_MASK already defined in Thermal +#define TPS65219_REG_MASK_INT_FOR_RV_MASK BIT(4) +#define TPS65219_REG_MASK_EFFECT_MASK GENMASK(2, 1) +#define TPS65219_REG_MASK_INT_FOR_PB_MASK BIT(7) +/* UnderVoltage - Short to GND - OverCurrent*/ +/* LDO3-4 */ +#define TPS65219_INT_LDO3_SCG_MASK BIT(0) +#define TPS65219_INT_LDO3_OC_MASK BIT(1) +#define TPS65219_INT_LDO3_UV_MASK BIT(2) +#define TPS65219_INT_LDO4_SCG_MASK BIT(3) +#define TPS65219_INT_LDO4_OC_MASK BIT(4) +#define TPS65219_INT_LDO4_UV_MASK BIT(5) +/* LDO1-2 */ +#define TPS65219_INT_LDO1_SCG_MASK BIT(0) +#define TPS65219_INT_LDO1_OC_MASK BIT(1) +#define TPS65219_INT_LDO1_UV_MASK BIT(2) +#define TPS65219_INT_LDO2_SCG_MASK BIT(3) +#define TPS65219_INT_LDO2_OC_MASK BIT(4) +#define TPS65219_INT_LDO2_UV_MASK BIT(5) +/* BUCK3 */ +#define TPS65219_INT_BUCK3_SCG_MASK BIT(0) +#define TPS65219_INT_BUCK3_OC_MASK BIT(1) +#define TPS65219_INT_BUCK3_NEG_OC_MASK BIT(2) +#define TPS65219_INT_BUCK3_UV_MASK BIT(3) +/* BUCK1-2 */ +#define TPS65219_INT_BUCK1_SCG_MASK BIT(0) +#define TPS65219_INT_BUCK1_OC_MASK BIT(1) +#define TPS65219_INT_BUCK1_NEG_OC_MASK BIT(2) +#define TPS65219_INT_BUCK1_UV_MASK BIT(3) +#define TPS65219_INT_BUCK2_SCG_MASK BIT(4) +#define TPS65219_INT_BUCK2_OC_MASK BIT(5) +#define TPS65219_INT_BUCK2_NEG_OC_MASK BIT(6) +#define TPS65219_INT_BUCK2_UV_MASK BIT(7) +/* Thermal Sensor */ +#define TPS65219_INT_SENSOR_3_WARM_MASK BIT(0) +#define TPS65219_INT_SENSOR_2_WARM_MASK BIT(1) +#define TPS65219_INT_SENSOR_1_WARM_MASK BIT(2) +#define TPS65219_INT_SENSOR_0_WARM_MASK BIT(3) +#define TPS65219_INT_SENSOR_3_HOT_MASK BIT(4) +#define TPS65219_INT_SENSOR_2_HOT_MASK BIT(5) +#define TPS65219_INT_SENSOR_1_HOT_MASK BIT(6) +#define TPS65219_INT_SENSOR_0_HOT_MASK BIT(7) +/* Residual Voltage */ +#define TPS65219_INT_BUCK1_RV_MASK BIT(0) +#define TPS65219_INT_BUCK2_RV_MASK BIT(1) +#define TPS65219_INT_BUCK3_RV_MASK BIT(2) +#define TPS65219_INT_LDO1_RV_MASK BIT(3) +#define TPS65219_INT_LDO2_RV_MASK BIT(4) +#define TPS65219_INT_LDO3_RV_MASK BIT(5) +#define TPS65219_INT_LDO4_RV_MASK BIT(6) +/* Residual Voltage ShutDown */ +#define TPS65219_INT_BUCK1_RV_SD_MASK BIT(0) +#define TPS65219_INT_BUCK2_RV_SD_MASK BIT(1) +#define TPS65219_INT_BUCK3_RV_SD_MASK BIT(2) +#define TPS65219_INT_LDO1_RV_SD_MASK BIT(3) +#define TPS65219_INT_LDO2_RV_SD_MASK BIT(4) +#define TPS65219_INT_LDO3_RV_SD_MASK BIT(5) +#define TPS65219_INT_LDO4_RV_SD_MASK BIT(6) +#define TPS65219_INT_TIMEOUT_MASK BIT(7) +/* Power Button */ +#define TPS65219_INT_PB_FALLING_EDGE_DETECT_MASK BIT(0) +#define TPS65219_INT_PB_RISING_EDGE_DETECT_MASK BIT(1) +#define TPS65219_INT_PB_REAL_TIME_STATUS_MASK BIT(2) + +#define TPS65219_PB_POS 7 +#define TPS65219_TO_RV_POS 6 +#define TPS65219_RV_POS 5 +#define TPS65219_SYS_POS 4 +#define TPS65219_BUCK_1_2_POS 3 +#define TPS65219_BUCK_3_POS 2 +#define TPS65219_LDO_1_2_POS 1 +#define TPS65219_LDO_3_4_POS 0 + +/* IRQs */ +enum { + /* LDO3-4 register IRQs */ + TPS65219_INT_LDO3_SCG, + TPS65219_INT_LDO3_OC, + TPS65219_INT_LDO3_UV, + TPS65219_INT_LDO4_SCG, + TPS65219_INT_LDO4_OC, + TPS65219_INT_LDO4_UV, + /* LDO1-2 */ + TPS65219_INT_LDO1_SCG, + TPS65219_INT_LDO1_OC, + TPS65219_INT_LDO1_UV, + TPS65219_INT_LDO2_SCG, + TPS65219_INT_LDO2_OC, + TPS65219_INT_LDO2_UV, + /* BUCK3 */ + TPS65219_INT_BUCK3_SCG, + TPS65219_INT_BUCK3_OC, + TPS65219_INT_BUCK3_NEG_OC, + TPS65219_INT_BUCK3_UV, + /* BUCK1-2 */ + TPS65219_INT_BUCK1_SCG, + TPS65219_INT_BUCK1_OC, + TPS65219_INT_BUCK1_NEG_OC, + TPS65219_INT_BUCK1_UV, + TPS65219_INT_BUCK2_SCG, + TPS65219_INT_BUCK2_OC, + TPS65219_INT_BUCK2_NEG_OC, + TPS65219_INT_BUCK2_UV, + /* Thermal Sensor */ + TPS65219_INT_SENSOR_3_WARM, + TPS65219_INT_SENSOR_2_WARM, + TPS65219_INT_SENSOR_1_WARM, + TPS65219_INT_SENSOR_0_WARM, + TPS65219_INT_SENSOR_3_HOT, + TPS65219_INT_SENSOR_2_HOT, + TPS65219_INT_SENSOR_1_HOT, + TPS65219_INT_SENSOR_0_HOT, + /* Residual Voltage */ + TPS65219_INT_BUCK1_RV, + TPS65219_INT_BUCK2_RV, + TPS65219_INT_BUCK3_RV, + TPS65219_INT_LDO1_RV, + TPS65219_INT_LDO2_RV, + TPS65219_INT_LDO3_RV, + TPS65219_INT_LDO4_RV, + /* Residual Voltage ShutDown */ + TPS65219_INT_BUCK1_RV_SD, + TPS65219_INT_BUCK2_RV_SD, + TPS65219_INT_BUCK3_RV_SD, + TPS65219_INT_LDO1_RV_SD, + TPS65219_INT_LDO2_RV_SD, + TPS65219_INT_LDO3_RV_SD, + TPS65219_INT_LDO4_RV_SD, + TPS65219_INT_TIMEOUT, + /* Power Button */ + TPS65219_INT_PB_FALLING_EDGE_DETECT, + TPS65219_INT_PB_RISING_EDGE_DETECT, +}; + +enum tps65219_regulator_id { + /* DCDC's */ + TPS65219_BUCK_1, + TPS65219_BUCK_2, + TPS65219_BUCK_3, + /* LDOs */ + TPS65219_LDO_1, + TPS65219_LDO_2, + TPS65219_LDO_3, + TPS65219_LDO_4, +}; + +/* Number of step-down converters available */ +#define TPS65219_NUM_DCDC 3 +/* Number of LDO voltage regulators available */ +#define TPS65219_NUM_LDO 4 +/* Number of total regulators available */ +#define TPS65219_NUM_REGULATOR (TPS65219_NUM_DCDC + TPS65219_NUM_LDO) + +/* Define the TPS65219 IRQ numbers */ +enum tps65219_irqs { + /* INT source registers */ + TPS65219_TO_RV_SD_SET_IRQ, + TPS65219_RV_SET_IRQ, + TPS65219_SYS_SET_IRQ, + TPS65219_BUCK_1_2_SET_IRQ, + TPS65219_BUCK_3_SET_IRQ, + TPS65219_LDO_1_2_SET_IRQ, + TPS65219_LDO_3_4_SET_IRQ, + TPS65219_PB_SET_IRQ, +}; + +/** + * struct tps65219 - tps65219 sub-driver chip access routines + * + * Device data may be used to access the TPS65219 chip + * + * @dev: MFD device + * @regmap: Regmap for accessing the device registers + * @irq_data: Regmap irq data used for the irq chip + * @nb: notifier block for the restart handler + */ +struct tps65219 { + struct device *dev; + struct regmap *regmap; + + struct regmap_irq_chip_data *irq_data; + struct notifier_block nb; +}; + +#endif /* MFD_TPS65219_H */ -- cgit v1.2.3