Signed-off-by: Samuel Dionne-Riel <sam...@dionne-riel.com> --- arch/sandbox/dts/test.dts | 10 +++ configs/sandbox_defconfig | 2 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/vibrator/Kconfig | 21 +++++++ drivers/vibrator/Makefile | 5 ++ drivers/vibrator/vibrator-uclass.c | 62 +++++++++++++++++++ include/dm/uclass-id.h | 1 + include/vibrator.h | 87 +++++++++++++++++++++++++++ test/dm/Makefile | 1 + test/dm/vibrator.c | 97 ++++++++++++++++++++++++++++++ 11 files changed, 289 insertions(+) create mode 100644 drivers/vibrator/Kconfig create mode 100644 drivers/vibrator/Makefile create mode 100644 drivers/vibrator/vibrator-uclass.c create mode 100644 include/vibrator.h create mode 100644 test/dm/vibrator.c
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 2cea4a43c8..3633b8fb9f 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -1608,6 +1608,16 @@ compatible = "sandbox,regmap_test"; }; }; + + vibrator_left { + compatible = "gpio-vibrator"; + enable-gpios = <&gpio_a 1 0 GPIO_ACTIVE_HIGH>; + }; + + vibrator_right { + compatible = "gpio-vibrator"; + enable-gpios = <&gpio_a 2 0 GPIO_ACTIVE_HIGH>; + }; }; #include "sandbox_pmic.dtsi" diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index c390afe9de..43f9972178 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -273,6 +273,8 @@ CONFIG_USB_GADGET=y CONFIG_USB_GADGET_DOWNLOAD=y CONFIG_USB_ETHER=y CONFIG_USB_ETH_CDC=y +CONFIG_VIBRATOR=y +CONFIG_VIBRATOR_GPIO=y CONFIG_DM_VIDEO=y CONFIG_VIDEO_COPY=y CONFIG_CONSOLE_ROTATION=y diff --git a/drivers/Kconfig b/drivers/Kconfig index b26ca8cf70..a15674f22c 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -134,6 +134,8 @@ source "drivers/usb/Kconfig" source "drivers/ufs/Kconfig" +source "drivers/vibrator/Kconfig" + source "drivers/video/Kconfig" source "drivers/virtio/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 4e7cf28440..2290d4a5d8 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_$(SPL_TPL_)RTC) += rtc/ obj-$(CONFIG_$(SPL_TPL_)SERIAL) += serial/ obj-$(CONFIG_$(SPL_TPL_)SPI) += spi/ obj-$(CONFIG_$(SPL_TPL_)TIMER) += timer/ +obj-$(CONFIG_$(SPL_TPL_)VIBRATOR) += vibrator/ obj-$(CONFIG_$(SPL_TPL_)VIRTIO) += virtio/ obj-$(CONFIG_$(SPL_)DM_MAILBOX) += mailbox/ obj-$(CONFIG_$(SPL_)REMOTEPROC) += remoteproc/ diff --git a/drivers/vibrator/Kconfig b/drivers/vibrator/Kconfig new file mode 100644 index 0000000000..f988aa63b9 --- /dev/null +++ b/drivers/vibrator/Kconfig @@ -0,0 +1,21 @@ +menu "Vibrator Feedback Support" + +config VIBRATOR + bool "Enable vibration motor support" + depends on DM + help + Many boards have vibration motorss which can be used to signal status or + alerts. U-Boot provides a uclass API to implement this feature. Vibration + motor drivers can provide access to board-specific vibration motors. Use + of the device tree for configuration is encouraged. + +config SPL_VIBRATOR + bool "Enable vibration motor support in SPL" + depends on SPL && SPL_DM + help + The vibration motor subsystem adds a small amount of overhead to the image. + If this is acceptable and you have a need to use vibration motors in SPL, + enable this option. You will need to enable device tree in SPL + for this to work. + +endmenu diff --git a/drivers/vibrator/Makefile b/drivers/vibrator/Makefile new file mode 100644 index 0000000000..326838ff7a --- /dev/null +++ b/drivers/vibrator/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (c) 2021 Samuel Dionne-Riel <sam...@dionne-riel.com> + +obj-y += vibrator-uclass.o diff --git a/drivers/vibrator/vibrator-uclass.c b/drivers/vibrator/vibrator-uclass.c new file mode 100644 index 0000000000..ffb6522a19 --- /dev/null +++ b/drivers/vibrator/vibrator-uclass.c @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2021 Samuel Dionne-Riel <sam...@dionne-riel.com> + * Copyright (c) 2015 Google, Inc + * Largely derived from `drivers/led/led-uclass.c` + * Original written by Simon Glass <s...@chromium.org> + */ + +#define LOG_CATEGORY UCLASS_VIBRATOR + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <vibrator.h> +#include <dm/device-internal.h> +#include <dm/root.h> +#include <dm/uclass-internal.h> + +int vibrator_get_by_label(const char *label, struct udevice **devp) +{ + struct udevice *dev; + struct uclass *uc; + int ret; + + ret = uclass_get(UCLASS_VIBRATOR, &uc); + if (ret) + return ret; + uclass_foreach_dev(dev, uc) { + struct vibrator_uc_plat *uc_plat = dev_get_uclass_plat(dev); + + if (uc_plat->label && strcmp(label, uc_plat->label) == 0) + return uclass_get_device_tail(dev, 0, devp); + } + + return -ENODEV; +} + +int vibrator_set_state(struct udevice *dev, enum vibrator_state_t state) +{ + struct vibrator_ops *ops = vibrator_get_ops(dev); + + if (!ops->set_state) + return -ENOSYS; + + return ops->set_state(dev, state); +} + +enum vibrator_state_t vibrator_get_state(struct udevice *dev) +{ + struct vibrator_ops *ops = vibrator_get_ops(dev); + + if (!ops->get_state) + return -ENOSYS; + + return ops->get_state(dev); +} + +UCLASS_DRIVER(vibrator) = { + .id = UCLASS_VIBRATOR, + .name = "vibrator", + .per_device_plat_auto = sizeof(struct vibrator_uc_plat), +}; diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 0e26e1d138..265369d07b 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -125,6 +125,7 @@ enum uclass_id { UCLASS_USB_DEV_GENERIC, /* USB generic device */ UCLASS_USB_HUB, /* USB hub */ UCLASS_USB_GADGET_GENERIC, /* USB generic device */ + UCLASS_VIBRATOR, /* Vibration feedback devices (phone vibration) */ UCLASS_VIDEO, /* Video or LCD device */ UCLASS_VIDEO_BRIDGE, /* Video bridge, e.g. DisplayPort to LVDS */ UCLASS_VIDEO_CONSOLE, /* Text console driver for video device */ diff --git a/include/vibrator.h b/include/vibrator.h new file mode 100644 index 0000000000..9b04c7303a --- /dev/null +++ b/include/vibrator.h @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2021 Samuel Dionne-Riel <sam...@dionne-riel.com> + * Copyright (c) 2015 Google, Inc + * Largely derived from `include/led.h` + * Original written by Simon Glass <s...@chromium.org> + */ + +#ifndef __VIBRATOR_H +#define __VIBRATOR_H + +struct udevice; + +/** + * struct vibrator_uc_plat - Platform data the uclass stores about each device + * + * @label: VIBRATOR label + */ +struct vibrator_uc_plat { + const char *label; +}; + +/** + * struct vibrator_uc_priv - Private data the uclass stores about each device + * + * @period_ms: Flash period in milliseconds + */ +struct vibrator_uc_priv { + int period_ms; +}; + +enum vibrator_state_t { + VIBRATOR_STATE_OFF = 0, + VIBRATOR_STATE_ON = 1, + VIBRATOR_STATE_TOGGLE, + + VIBRATOR_STATE_COUNT, +}; + +struct vibrator_ops { + /** + * set_state() - set the state of an VIBRATOR + * + * @dev: VIBRATOR device to change + * @state: VIBRATOR state to set + * @return 0 if OK, -ve on error + */ + int (*set_state)(struct udevice *dev, enum vibrator_state_t state); + + /** + * vibrator_get_state() - get the state of an VIBRATOR + * + * @dev: VIBRATOR device to change + * @return VIBRATOR state vibrator_state_t, or -ve on error + */ + enum vibrator_state_t (*get_state)(struct udevice *dev); +}; + +#define vibrator_get_ops(dev) ((struct vibrator_ops *)(dev)->driver->ops) + +/** + * vibrator_get_by_label() - Find an VIBRATOR device by label + * + * @label: VIBRATOR label to look up + * @devp: Returns the associated device, if found + * @return 0 if found, -ENODEV if not found, other -ve on error + */ +int vibrator_get_by_label(const char *label, struct udevice **devp); + +/** + * vibrator_set_state() - set the state of an VIBRATOR + * + * @dev: VIBRATOR device to change + * @state: VIBRATOR state to set + * @return 0 if OK, -ve on error + */ +int vibrator_set_state(struct udevice *dev, enum vibrator_state_t state); + +/** + * vibrator_get_state() - get the state of an VIBRATOR + * + * @dev: VIBRATOR device to change + * @return VIBRATOR state vibrator_state_t, or -ve on error + */ +enum vibrator_state_t vibrator_get_state(struct udevice *dev); + +#endif diff --git a/test/dm/Makefile b/test/dm/Makefile index d46552fbf3..dffed0e7fc 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -106,6 +106,7 @@ obj-$(CONFIG_TEE) += tee.o obj-$(CONFIG_TIMER) += timer.o obj-$(CONFIG_DM_USB) += usb.o obj-$(CONFIG_DM_VIDEO) += video.o +obj-$(CONFIG_VIBRATOR) += vibrator.o obj-$(CONFIG_VIRTIO_SANDBOX) += virtio.o ifeq ($(CONFIG_WDT_GPIO)$(CONFIG_WDT_SANDBOX),yy) obj-y += wdt.o diff --git a/test/dm/vibrator.c b/test/dm/vibrator.c new file mode 100644 index 0000000000..ab5b548431 --- /dev/null +++ b/test/dm/vibrator.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2021 Samuel Dionne-Riel <sam...@dionne-riel.com> + */ + +#include <common.h> +#include <dm.h> +#include <vibrator.h> +#include <asm/gpio.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +/* Base test of the vibrator uclass */ +static int dm_test_vibrator_base(struct unit_test_state *uts) +{ + struct udevice *dev; + + ut_assertok(uclass_get_device(UCLASS_VIBRATOR, 0, &dev)); + ut_assertok(uclass_get_device(UCLASS_VIBRATOR, 1, &dev)); + ut_asserteq(-ENODEV, uclass_get_device(UCLASS_VIBRATOR, 2, &dev)); + + return 0; +} +DM_TEST(dm_test_vibrator_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test of the vibrator uclass using the vibrator_gpio driver */ +static int dm_test_vibrator_gpio(struct unit_test_state *uts) +{ + const int offset = 1; + struct udevice *dev, *gpio; + + /* + * Check that we can manipulate a vibration motor. Vibrator 1 is connected to GPIO + * bank gpio_a, offset 1. + */ + ut_assertok(uclass_get_device(UCLASS_VIBRATOR, 0, &dev)); + ut_assertok(uclass_get_device(UCLASS_GPIO, 1, &gpio)); + ut_asserteq(0, sandbox_gpio_get_value(gpio, offset)); + ut_assertok(vibrator_set_state(dev, VIBRATOR_STATE_ON)); + ut_asserteq(1, sandbox_gpio_get_value(gpio, offset)); + ut_asserteq(VIBRATOR_STATE_ON, vibrator_get_state(dev)); + + ut_assertok(vibrator_set_state(dev, VIBRATOR_STATE_OFF)); + ut_asserteq(0, sandbox_gpio_get_value(gpio, offset)); + ut_asserteq(VIBRATOR_STATE_OFF, vibrator_get_state(dev)); + + return 0; +} +DM_TEST(dm_test_vibrator_gpio, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test that we can toggle vibration motors */ +static int dm_test_vibrator_toggle(struct unit_test_state *uts) +{ + const int offset = 1; + struct udevice *dev, *gpio; + + /* + * Check that we can manipulate a vibration motor. Vibrator 1 is connected to GPIO + * bank gpio_a, offset 1. + */ + ut_assertok(uclass_get_device(UCLASS_VIBRATOR, 0, &dev)); + ut_assertok(uclass_get_device(UCLASS_GPIO, 1, &gpio)); + ut_asserteq(0, sandbox_gpio_get_value(gpio, offset)); + ut_assertok(vibrator_set_state(dev, VIBRATOR_STATE_TOGGLE)); + ut_asserteq(1, sandbox_gpio_get_value(gpio, offset)); + ut_asserteq(VIBRATOR_STATE_ON, vibrator_get_state(dev)); + + ut_assertok(vibrator_set_state(dev, VIBRATOR_STATE_TOGGLE)); + ut_asserteq(0, sandbox_gpio_get_value(gpio, offset)); + ut_asserteq(VIBRATOR_STATE_OFF, vibrator_get_state(dev)); + + return 0; +} +DM_TEST(dm_test_vibrator_toggle, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test obtaining a vibration motor by label */ +static int dm_test_vibrator_label(struct unit_test_state *uts) +{ + struct udevice *dev, *cmp; + + ut_assertok(vibrator_get_by_label("vibrator_left", &dev)); + ut_asserteq(1, device_active(dev)); + ut_assertok(uclass_get_device(UCLASS_VIBRATOR, 0, &cmp)); + ut_asserteq_ptr(dev, cmp); + + ut_assertok(vibrator_get_by_label("vibrator_right", &dev)); + ut_asserteq(1, device_active(dev)); + ut_assertok(uclass_get_device(UCLASS_VIBRATOR, 1, &cmp)); + ut_asserteq_ptr(dev, cmp); + + ut_asserteq(-ENODEV, vibrator_get_by_label("doesnotexist", &dev)); + + return 0; +} +DM_TEST(dm_test_vibrator_label, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + -- 2.34.0