* Re: [PATCH v1 2/2] hid-asus: change the report_id used for HID LED control
From: Luke Jones @ 2024-05-29 1:30 UTC (permalink / raw)
To: Jiri Kosina
Cc: Hans de Goede, Ilpo Järvinen, corentin.chary,
platform-driver-x86, linux-kernel, linux-input, bentiss
In-Reply-To: <20240529012447.145088-2-luke@ljones.dev>
Sorry about the doubleup. Wifi is spotty and it looked like the initial send didn't work.
See https://lore.kernel.org/linux-input/20240529012827.146005-1-luke@ljones.dev/T/#t for cover-letter
On Wed, 29 May 2024, at 1:24 PM, Luke D. Jones wrote:
> On some laptops the report_id used for LED brightness control must be
> 0x5D instead of 0x5A.
>
> Signed-off-by: Luke D. Jones <luke@ljones.dev>
> ---
> drivers/hid/hid-asus.c | 26 +++++++++++++++++++++++++-
> 1 file changed, 25 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
> index 4cba8e143031..ec3556cc4eef 100644
> --- a/drivers/hid/hid-asus.c
> +++ b/drivers/hid/hid-asus.c
> @@ -94,6 +94,8 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
>
> #define TRKID_SGN ((TRKID_MAX + 1) >> 1)
>
> +static const char * const use_alt_led_report_id[] = { "GU605", "GA403" };
> +
> struct asus_kbd_leds {
> struct led_classdev cdev;
> struct hid_device *hdev;
> @@ -101,6 +103,7 @@ struct asus_kbd_leds {
> unsigned int brightness;
> spinlock_t lock;
> bool removed;
> + int report_id;
> };
>
> struct asus_touchpad_info {
> @@ -473,7 +476,7 @@ static enum led_brightness asus_kbd_backlight_get(struct led_classdev *led_cdev)
> static void asus_kbd_backlight_work(struct work_struct *work)
> {
> struct asus_kbd_leds *led = container_of(work, struct asus_kbd_leds, work);
> - u8 buf[] = { FEATURE_KBD_REPORT_ID, 0xba, 0xc5, 0xc4, 0x00 };
> + u8 buf[] = { led->report_id, 0xba, 0xc5, 0xc4, 0x00 };
> int ret;
> unsigned long flags;
>
> @@ -513,6 +516,23 @@ static bool asus_kbd_wmi_led_control_present(struct hid_device *hdev)
> return !!(value & ASUS_WMI_DSTS_PRESENCE_BIT);
> }
>
> +static bool asus_kbd_is_input_led(void)
> +{
> + const char *product;
> + int i;
> +
> + product = dmi_get_system_info(DMI_PRODUCT_NAME);
> + if (!product)
> + return false;
> +
> + for (i = 0; i < ARRAY_SIZE(use_alt_led_report_id); i++) {
> + if (strstr(product, use_alt_led_report_id[i]))
> + return true;
> + }
> +
> + return false;
> +}
> +
> static int asus_kbd_register_leds(struct hid_device *hdev)
> {
> struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
> @@ -555,6 +575,10 @@ static int asus_kbd_register_leds(struct hid_device *hdev)
> if (!drvdata->kbd_backlight)
> return -ENOMEM;
>
> + drvdata->kbd_backlight->report_id = FEATURE_KBD_REPORT_ID;
> + if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD && asus_kbd_is_input_led())
> + drvdata->kbd_backlight->report_id = FEATURE_KBD_LED_REPORT_ID1;
> +
> drvdata->kbd_backlight->removed = false;
> drvdata->kbd_backlight->brightness = 0;
> drvdata->kbd_backlight->hdev = hdev;
> --
> 2.45.1
>
>
^ permalink raw reply
* Re: [PATCH HID 03/13] HID: bpf: implement HID-BPF through bpf_struct_ops
From: Alexei Starovoitov @ 2024-05-29 4:02 UTC (permalink / raw)
To: Benjamin Tissoires
Cc: Shuah Khan, Jiri Kosina, Jonathan Corbet, Alexei Starovoitov,
open list:KERNEL SELFTEST FRAMEWORK, LKML, bpf,
open list:HID CORE LAYER, open list:DOCUMENTATION
In-Reply-To: <20240528-hid_bpf_struct_ops-v1-3-8c6663df27d8@kernel.org>
On Tue, May 28, 2024 at 6:15 AM Benjamin Tissoires <bentiss@kernel.org> wrote:
>
> We do this implementation in several steps to not have the CI failing:
> - first (this patch), we add struct_ops while keeping the existing infra
> available
> - then we change the selftests, the examples and the existing in-tree
> HID-BPF programs
> - then we remove the existing trace points making old HID-BPF obsolete
>
> There are a few advantages of struct_ops over tracing:
> - compatibility with sleepable programs (for hid_hw_raw_request() in
> a later patch)
> - a lot simpler in the kernel: it's a simple rcu protected list
> - we can add more parameters to the function called without much trouble
> - the "attach" is now generic through BPF-core: the caller just needs to
> set hid_id and flags before calling __load().
> - all the BPF tough part is not handled in BPF-core through generic
> processing
> - hid_bpf_ctx is now only writable where it needs be
>
> Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>
> ---
> drivers/hid/bpf/Makefile | 2 +-
> drivers/hid/bpf/hid_bpf_dispatch.c | 52 +++++++-
> drivers/hid/bpf/hid_bpf_dispatch.h | 4 +
> drivers/hid/bpf/hid_bpf_jmp_table.c | 3 +
> drivers/hid/bpf/hid_bpf_struct_ops.c | 246 +++++++++++++++++++++++++++++++++++
> include/linux/hid_bpf.h | 64 ++++++++-
> 6 files changed, 362 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/hid/bpf/Makefile b/drivers/hid/bpf/Makefile
> index cf55120cf7d6..1cb3f31e9335 100644
> --- a/drivers/hid/bpf/Makefile
> +++ b/drivers/hid/bpf/Makefile
> @@ -8,4 +8,4 @@ LIBBPF_INCLUDE = $(srctree)/tools/lib
> obj-$(CONFIG_HID_BPF) += hid_bpf.o
> CFLAGS_hid_bpf_dispatch.o += -I$(LIBBPF_INCLUDE)
> CFLAGS_hid_bpf_jmp_table.o += -I$(LIBBPF_INCLUDE)
> -hid_bpf-objs += hid_bpf_dispatch.o hid_bpf_jmp_table.o
> +hid_bpf-objs += hid_bpf_dispatch.o hid_bpf_jmp_table.o hid_bpf_struct_ops.o
> diff --git a/drivers/hid/bpf/hid_bpf_dispatch.c b/drivers/hid/bpf/hid_bpf_dispatch.c
> index c8bb79ce2354..7216c3c7713d 100644
> --- a/drivers/hid/bpf/hid_bpf_dispatch.c
> +++ b/drivers/hid/bpf/hid_bpf_dispatch.c
> @@ -58,6 +58,7 @@ dispatch_hid_bpf_device_event(struct hid_device *hdev, enum hid_report_type type
> },
> .data = hdev->bpf.device_data,
> };
> + struct hid_bpf_ops *e;
> int ret;
>
> if (type >= HID_REPORT_TYPES)
> @@ -70,9 +71,25 @@ dispatch_hid_bpf_device_event(struct hid_device *hdev, enum hid_report_type type
> memset(ctx_kern.data, 0, hdev->bpf.allocated_data);
> memcpy(ctx_kern.data, data, *size);
>
> + rcu_read_lock();
> + list_for_each_entry_rcu(e, &hdev->bpf.prog_list, list) {
In the typical case there will be only one prog per device, right?
The for_each is future proofing?
> + if (e->hid_device_event) {
> + ret = e->hid_device_event(&ctx_kern.ctx, type);
> + if (ret < 0) {
> + rcu_read_unlock();
> + return ERR_PTR(ret);
> + }
> +
> + if (ret)
> + ctx_kern.ctx.retval = ret;
> + }
> + }
> + rcu_read_unlock();
> +
> ret = hid_bpf_prog_run(hdev, HID_BPF_PROG_TYPE_DEVICE_EVENT, &ctx_kern);
> if (ret < 0)
> return ERR_PTR(ret);
> + ret = ctx_kern.ctx.retval;
>
> if (ret) {
> if (ret > ctx_kern.ctx.allocated_size)
> @@ -122,7 +139,10 @@ u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int *s
>
> memcpy(ctx_kern.data, rdesc, min_t(unsigned int, *size, HID_MAX_DESCRIPTOR_SIZE));
>
> - ret = hid_bpf_prog_run(hdev, HID_BPF_PROG_TYPE_RDESC_FIXUP, &ctx_kern);
> + if (hdev->bpf.rdesc_ops)
> + ret = hdev->bpf.rdesc_ops->hid_rdesc_fixup(&ctx_kern.ctx);
> + else
> + ret = hid_bpf_prog_run(hdev, HID_BPF_PROG_TYPE_RDESC_FIXUP, &ctx_kern);
This is for backward compat?
I don't see it's being removed in the later patches.
> if (ret < 0)
> goto ignore_bpf;
>
> @@ -150,7 +170,7 @@ static int device_match_id(struct device *dev, const void *id)
> return hdev->id == *(int *)id;
> }
>
> -static struct hid_device *hid_get_device(unsigned int hid_id)
> +struct hid_device *hid_get_device(unsigned int hid_id)
> {
> struct device *dev;
>
> @@ -164,7 +184,7 @@ static struct hid_device *hid_get_device(unsigned int hid_id)
> return to_hid_device(dev);
> }
>
> -static void hid_put_device(struct hid_device *hid)
> +void hid_put_device(struct hid_device *hid)
> {
> put_device(&hid->dev);
> }
> @@ -205,7 +225,7 @@ static int __hid_bpf_allocate_data(struct hid_device *hdev, u8 **data, u32 *size
> return 0;
> }
>
> -static int hid_bpf_allocate_event_data(struct hid_device *hdev)
> +int hid_bpf_allocate_event_data(struct hid_device *hdev)
> {
> /* hdev->bpf.device_data is already allocated, abort */
> if (hdev->bpf.device_data)
> @@ -592,14 +612,22 @@ static const struct btf_kfunc_id_set hid_bpf_syscall_kfunc_set = {
>
> int hid_bpf_connect_device(struct hid_device *hdev)
> {
> - struct hid_bpf_prog_list *prog_list;
> + bool need_to_allocate = false;
> + struct hid_bpf_ops *e;
>
> rcu_read_lock();
> - prog_list = rcu_dereference(hdev->bpf.progs[HID_BPF_PROG_TYPE_DEVICE_EVENT]);
> + list_for_each_entry_rcu(e, &hdev->bpf.prog_list, list) {
> + if (e->hid_device_event) {
> + need_to_allocate = true;
> + break;
> + }
> + }
> + if (rcu_dereference(hdev->bpf.progs[HID_BPF_PROG_TYPE_DEVICE_EVENT]))
> + need_to_allocate = true;
> rcu_read_unlock();
>
> /* only allocate BPF data if there are programs attached */
> - if (!prog_list)
> + if (!need_to_allocate)
> return 0;
>
> return hid_bpf_allocate_event_data(hdev);
> @@ -623,12 +651,15 @@ void hid_bpf_destroy_device(struct hid_device *hdev)
> hdev->bpf.destroyed = true;
>
> __hid_bpf_destroy_device(hdev);
> + __hid_bpf_ops_destroy_device(hdev);
> }
> EXPORT_SYMBOL_GPL(hid_bpf_destroy_device);
>
> void hid_bpf_device_init(struct hid_device *hdev)
> {
> spin_lock_init(&hdev->bpf.progs_lock);
> + INIT_LIST_HEAD(&hdev->bpf.prog_list);
> + mutex_init(&hdev->bpf.prog_list_lock);
> }
> EXPORT_SYMBOL_GPL(hid_bpf_device_init);
>
> @@ -662,6 +693,13 @@ static int __init hid_bpf_init(void)
> return 0;
> }
>
> + /* register struct_ops kfuncs after we are sure we can load our preloaded bpf program */
> + err = register_btf_kfunc_id_set(BPF_PROG_TYPE_STRUCT_OPS, &hid_bpf_kfunc_set);
> + if (err) {
> + pr_warn("error while setting HID BPF tracing kfuncs: %d", err);
> + return 0;
> + }
> +
> /* register syscalls after we are sure we can load our preloaded bpf program */
> err = register_btf_kfunc_id_set(BPF_PROG_TYPE_SYSCALL, &hid_bpf_syscall_kfunc_set);
> if (err) {
> diff --git a/drivers/hid/bpf/hid_bpf_dispatch.h b/drivers/hid/bpf/hid_bpf_dispatch.h
> index fbe0639d09f2..e52c43d81650 100644
> --- a/drivers/hid/bpf/hid_bpf_dispatch.h
> +++ b/drivers/hid/bpf/hid_bpf_dispatch.h
> @@ -10,12 +10,16 @@ struct hid_bpf_ctx_kern {
> u8 *data;
> };
>
> +struct hid_device *hid_get_device(unsigned int hid_id);
> +void hid_put_device(struct hid_device *hid);
> +int hid_bpf_allocate_event_data(struct hid_device *hdev);
> int hid_bpf_preload_skel(void);
> void hid_bpf_free_links_and_skel(void);
> int hid_bpf_get_prog_attach_type(struct bpf_prog *prog);
> int __hid_bpf_attach_prog(struct hid_device *hdev, enum hid_bpf_prog_type prog_type, int prog_fd,
> struct bpf_prog *prog, __u32 flags);
> void __hid_bpf_destroy_device(struct hid_device *hdev);
> +void __hid_bpf_ops_destroy_device(struct hid_device *hdev);
> int hid_bpf_prog_run(struct hid_device *hdev, enum hid_bpf_prog_type type,
> struct hid_bpf_ctx_kern *ctx_kern);
> int hid_bpf_reconnect(struct hid_device *hdev);
> diff --git a/drivers/hid/bpf/hid_bpf_jmp_table.c b/drivers/hid/bpf/hid_bpf_jmp_table.c
> index aa8e1c79cdf5..8a54ba447718 100644
> --- a/drivers/hid/bpf/hid_bpf_jmp_table.c
> +++ b/drivers/hid/bpf/hid_bpf_jmp_table.c
> @@ -81,6 +81,9 @@ static int hid_bpf_program_count(struct hid_device *hdev,
> if (type >= HID_BPF_PROG_TYPE_MAX)
> return -EINVAL;
>
> + if (type == HID_BPF_PROG_TYPE_RDESC_FIXUP && hdev->bpf.rdesc_ops)
> + n += 1;
> +
> FOR_ENTRIES(i, jmp_table.tail, jmp_table.head) {
> struct hid_bpf_prog_entry *entry = &jmp_table.entries[i];
>
> diff --git a/drivers/hid/bpf/hid_bpf_struct_ops.c b/drivers/hid/bpf/hid_bpf_struct_ops.c
> new file mode 100644
> index 000000000000..be514a98e55b
> --- /dev/null
> +++ b/drivers/hid/bpf/hid_bpf_struct_ops.c
> @@ -0,0 +1,246 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +
> +/*
> + * HID-BPF support for Linux
> + *
> + * Copyright (c) 2024 Benjamin Tissoires
> + */
> +
> +#include <linux/bitops.h>
> +#include <linux/bpf_verifier.h>
> +#include <linux/bpf.h>
> +#include <linux/btf.h>
> +#include <linux/btf_ids.h>
> +#include <linux/filter.h>
> +#include <linux/hid.h>
> +#include <linux/hid_bpf.h>
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/workqueue.h>
> +#include "hid_bpf_dispatch.h"
> +
> +static struct btf *hid_bpf_ops_btf;
> +
> +static int hid_bpf_ops_init(struct btf *btf)
> +{
> + hid_bpf_ops_btf = btf;
> + return 0;
> +}
> +
> +static bool hid_bpf_ops_is_valid_access(int off, int size,
> + enum bpf_access_type type,
> + const struct bpf_prog *prog,
> + struct bpf_insn_access_aux *info)
> +{
> + return bpf_tracing_btf_ctx_access(off, size, type, prog, info);
> +}
> +
> +static int hid_bpf_ops_check_member(const struct btf_type *t,
> + const struct btf_member *member,
> + const struct bpf_prog *prog)
> +{
> + u32 moff = __btf_member_bit_offset(t, member) / 8;
> +
this is good...
> + switch (moff) {
> + case offsetof(struct hid_bpf_ops, hid_rdesc_fixup):
> + break;
> + default:
> + if (prog->sleepable)
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int hid_bpf_ops_btf_struct_access(struct bpf_verifier_log *log,
> + const struct bpf_reg_state *reg,
> + int off, int size)
> +{
> + const struct btf_type *state;
> + const struct btf_type *t;
> + s32 type_id;
> +
> + type_id = btf_find_by_name_kind(reg->btf, "hid_bpf_ctx",
> + BTF_KIND_STRUCT);
> + if (type_id < 0)
> + return -EINVAL;
> +
> + t = btf_type_by_id(reg->btf, reg->btf_id);
> + state = btf_type_by_id(reg->btf, type_id);
> + if (t != state) {
> + bpf_log(log, "only access to hid_bpf_ctx is supported\n");
> + return -EACCES;
> + }
> +
> + /* out-of-bound access in hid_bpf_ctx */
> + if (off + size > sizeof(struct hid_bpf_ctx)) {
> + bpf_log(log, "write access at off %d with size %d\n", off, size);
> + return -EACCES;
> + }
> +
> + if (off < offsetof(struct hid_bpf_ctx, retval)) {
> + bpf_log(log,
> + "write access at off %d with size %d on read-only part of hid_bpf_ctx\n",
> + off, size);
> + return -EACCES;
> + }
> +
> + return NOT_INIT;
> +}
> +
> +static const struct bpf_verifier_ops hid_bpf_verifier_ops = {
> + .is_valid_access = hid_bpf_ops_is_valid_access,
> + .btf_struct_access = hid_bpf_ops_btf_struct_access,
> +};
> +
> +static int hid_bpf_ops_init_member(const struct btf_type *t,
> + const struct btf_member *member,
> + void *kdata, const void *udata)
> +{
> + u32 flags;
> +
> + switch (member->offset) {
> + case offsetof(struct hid_bpf_ops, hid_id) * 8:
but here you're open coding it and adding ugly * 8
Just do:
moff = __btf_member_bit_offset(t, member) / 8;
switch (moff) {
case offsetof(struct tcp_congestion_ops, flags):
the way bpf_tcp_ca.c is doing?
Open code of ->offset is asking for trouble.
> + /* For hid_id and flags fields, this function has to copy it
> + * and return 1 to indicate that the data has been handled by
> + * the struct_ops type, or the verifier will reject the map if
> + * the value of those fields is not zero.
> + */
> + ((struct hid_bpf_ops *)kdata)->hid_id = ((struct hid_bpf_ops *)udata)->hid_id;
> + return 1;
> + case offsetof(struct hid_bpf_ops, flags) * 8:
> + flags = ((struct hid_bpf_ops *)udata)->flags;
> + if (flags & ~HID_BPF_FLAG_MASK)
> + return -EINVAL;
> + ((struct hid_bpf_ops *)kdata)->flags = flags;
> + return 1;
> + }
> + return 0;
> +}
> +
> +static int hid_bpf_reg(void *kdata)
> +{
> + struct hid_bpf_ops *ops = kdata;
> + struct hid_device *hdev;
> + int count, err = 0;
> +
> + hdev = hid_get_device(ops->hid_id);
> + if (IS_ERR(hdev))
> + return PTR_ERR(hdev);
> +
> + ops->hdev = hdev;
> +
> + mutex_lock(&hdev->bpf.prog_list_lock);
> +
> + count = list_count_nodes(&hdev->bpf.prog_list);
> + if (count >= HID_BPF_MAX_PROGS_PER_DEV) {
> + err = -E2BIG;
> + goto out_unlock;
> + }
> +
> + if (ops->hid_rdesc_fixup) {
> + if (hdev->bpf.rdesc_ops) {
> + err = -EINVAL;
> + goto out_unlock;
> + }
> +
> + hdev->bpf.rdesc_ops = ops;
> + }
> +
> + if (ops->hid_device_event) {
> + err = hid_bpf_allocate_event_data(hdev);
> + if (err)
> + goto out_unlock;
> + }
> +
> + if (ops->flags & HID_BPF_FLAG_INSERT_HEAD)
> + list_add_rcu(&ops->list, &hdev->bpf.prog_list);
> + else
> + list_add_tail_rcu(&ops->list, &hdev->bpf.prog_list);
Looks like future proofing, but I feel it's too little to materialize.
Take a look at include/linux/bpf_mprog.h
I suspect it might be useful here too.
> +
> +out_unlock:
> + mutex_unlock(&hdev->bpf.prog_list_lock);
> +
> + if (err) {
> + if (hdev->bpf.rdesc_ops == ops)
> + hdev->bpf.rdesc_ops = NULL;
> + hid_put_device(hdev);
> + } else if (ops->hid_rdesc_fixup) {
> + hid_bpf_reconnect(hdev);
> + }
> +
> + return err;
> +}
> +
> +static void hid_bpf_unreg(void *kdata)
> +{
> + struct hid_bpf_ops *ops = kdata;
> + struct hid_device *hdev;
> + bool reconnect = false;
> +
> + hdev = ops->hdev;
> +
> + /* check if __hid_bpf_ops_destroy_device() has been called */
> + if (!hdev)
> + return;
> +
> + mutex_lock(&hdev->bpf.prog_list_lock);
> +
> + list_del_rcu(&ops->list);
> +
> + reconnect = hdev->bpf.rdesc_ops == ops;
> + if (reconnect)
> + hdev->bpf.rdesc_ops = NULL;
> +
> + mutex_unlock(&hdev->bpf.prog_list_lock);
> +
> + if (reconnect)
> + hid_bpf_reconnect(hdev);
> +
> + hid_put_device(hdev);
> +}
> +
> +static int __hid_bpf_device_event(struct hid_bpf_ctx *ctx, enum hid_report_type type)
> +{
> + return 0;
> +}
> +
> +static int __hid_bpf_rdesc_fixup(struct hid_bpf_ctx *ctx)
> +{
> + return 0;
> +}
> +
> +static struct hid_bpf_ops __bpf_hid_bpf_ops = {
> + .hid_device_event = __hid_bpf_device_event,
> + .hid_rdesc_fixup = __hid_bpf_rdesc_fixup,
> +};
> +
> +static struct bpf_struct_ops bpf_hid_bpf_ops = {
> + .verifier_ops = &hid_bpf_verifier_ops,
> + .init = hid_bpf_ops_init,
> + .check_member = hid_bpf_ops_check_member,
> + .init_member = hid_bpf_ops_init_member,
> + .reg = hid_bpf_reg,
> + .unreg = hid_bpf_unreg,
> + .name = "hid_bpf_ops",
> + .cfi_stubs = &__bpf_hid_bpf_ops,
> + .owner = THIS_MODULE,
> +};
> +
> +void __hid_bpf_ops_destroy_device(struct hid_device *hdev)
> +{
> + struct hid_bpf_ops *e;
> +
> + rcu_read_lock();
> + list_for_each_entry_rcu(e, &hdev->bpf.prog_list, list) {
> + hid_put_device(hdev);
> + e->hdev = NULL;
> + }
> + rcu_read_unlock();
> +}
> +
> +static int __init hid_bpf_struct_ops_init(void)
> +{
> + return register_bpf_struct_ops(&bpf_hid_bpf_ops, hid_bpf_ops);
> +}
> +late_initcall(hid_bpf_struct_ops_init);
> diff --git a/include/linux/hid_bpf.h b/include/linux/hid_bpf.h
> index a66103618e6e..96495e977204 100644
> --- a/include/linux/hid_bpf.h
> +++ b/include/linux/hid_bpf.h
> @@ -65,11 +65,12 @@ struct hid_bpf_ctx {
> * @HID_BPF_FLAG_INSERT_HEAD: insert the given program before any other program
> * currently attached to the device. This doesn't
> * guarantee that this program will always be first
> - * @HID_BPF_FLAG_MAX: sentinel value, not to be used by the callers
> */
> enum hid_bpf_attach_flags {
> HID_BPF_FLAG_NONE = 0,
> HID_BPF_FLAG_INSERT_HEAD = _BITUL(0),
> +
> + /* private: internal use only */
> HID_BPF_FLAG_MAX,
> };
>
> @@ -112,6 +113,63 @@ struct hid_ops {
>
> extern struct hid_ops *hid_ops;
>
> +/**
> + * struct hid_bpf_ops - A BPF struct_ops of callbacks allowing to attach HID-BPF
> + * programs to a HID device
> + * @hid_id: the HID uniq ID to attach to. This is writeable before ``load()``, and
> + * cannot be changed after
> + * @flags: &enum hid_bpf_attach_flags to assign flags before ``load()``.
> + * Writeable only before ``load()``
> + */
> +struct hid_bpf_ops {
> + /* hid_id needs to stay first so we can easily change it
> + * from userspace.
> + */
hmm.
patch 5 just does:
+ skel->struct_ops.mouse_invert->hid_id = hid_id;
I don't see a reason why it needs to be first.
> + int hid_id;
> + u32 flags;
> +
> + /* private: internal use only */
> + struct list_head list;
> +
> + /* public: rest is public */
> +
> +/* fast path fields are put first to fill one cache line */
> +
> + /**
> + * @hid_device_event: called whenever an event is coming in from the device
> + *
> + * It has the following arguments:
> + *
> + * ``ctx``: The HID-BPF context as &struct hid_bpf_ctx
> + *
> + * Return: %0 on success and keep processing; a positive
> + * value to change the incoming size buffer; a negative
> + * error code to interrupt the processing of this event
> + *
> + * Context: Interrupt context.
> + */
> + int (*hid_device_event)(struct hid_bpf_ctx *ctx, enum hid_report_type report_type);
> +
> +/* control/slow paths put last */
> +
> + /**
> + * @hid_rdesc_fixup: called when the probe function parses the report descriptor
> + * of the HID device
> + *
> + * It has the following arguments:
> + *
> + * ``ctx``: The HID-BPF context as &struct hid_bpf_ctx
> + *
> + * Return: %0 on success and keep processing; a positive
> + * value to change the incoming size buffer; a negative
> + * error code to interrupt the processing of this device
> + */
> + int (*hid_rdesc_fixup)(struct hid_bpf_ctx *ctx);
It's fine since you want to keep the prog mostly unchanged,
but since the whole api is improving maybe it's time
to do something about hid_bpf_ctx in all callbacks?
Pass into prog what prog needs to see.
In the old api all progs had to have a common hid_bpf_ctx.
With struct-ops it can be fine tuned.
Overall it looks great.
hid_id did the trick :)
^ permalink raw reply
* RE: [PATCH 1/2] HID: intel-ish-hid: fix cache management mistake
From: Zhang, Lixu @ 2024-05-29 6:46 UTC (permalink / raw)
To: Arnd Bergmann, Srinivas Pandruvada, Jiri Kosina,
Benjamin Tissoires
Cc: Arnd Bergmann, linux-input@vger.kernel.org,
linux-kernel@vger.kernel.org
In-Reply-To: <20240528115802.3122955-1-arnd@kernel.org>
>-----Original Message-----
>From: Arnd Bergmann <arnd@kernel.org>
>Sent: Tuesday, May 28, 2024 7:58 PM
>To: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>; Jiri Kosina
><jikos@kernel.org>; Benjamin Tissoires <bentiss@kernel.org>; Zhang, Lixu
><lixu.zhang@intel.com>
>Cc: Arnd Bergmann <arnd@arndb.de>; linux-input@vger.kernel.org; linux-
>kernel@vger.kernel.org
>Subject: [PATCH 1/2] HID: intel-ish-hid: fix cache management mistake
>
>From: Arnd Bergmann <arnd@arndb.de>
>
>The low-level cache operation on a coherent buffer is incorrect, as coherent
>DMA memory may not actually be cached. Instead, use a DMA barrier that
>ensures that the data is visible to the DMA master before the address is and
>move the memcpy() before the reference.
>
>Fixes: 579a267e4617 ("HID: intel-ish-hid: Implement loading firmware from
>host feature")
>Signed-off-by: Arnd Bergmann <arnd@arndb.de>
>---
>I noticed this one while looking at the bug that was fixed in
>236049723826 ("HID: intel-ish-hid: Fix build error for COMPILE_TEST")
>---
> drivers/hid/intel-ish-hid/ishtp/loader.c | 6 +++---
> 1 file changed, 3 insertions(+), 3 deletions(-)
>
>diff --git a/drivers/hid/intel-ish-hid/ishtp/loader.c b/drivers/hid/intel-ish-
>hid/ishtp/loader.c
>index 2785b04a2f5a..062d1b25eaa7 100644
>--- a/drivers/hid/intel-ish-hid/ishtp/loader.c
>+++ b/drivers/hid/intel-ish-hid/ishtp/loader.c
>@@ -33,7 +33,6 @@
>
> #define dev_fmt(fmt) "ISH loader: " fmt
>
>-#include <linux/cacheflush.h>
> #include <linux/container_of.h>
> #include <linux/dev_printk.h>
> #include <linux/dma-mapping.h>
>@@ -175,10 +174,11 @@ static int prepare_dma_bufs(struct ishtp_device
>*dev,
> return -ENOMEM;
>
> fragment->fragment_tbl[i].ddr_adrs =
>cpu_to_le64(dma_addr);
>+
>+ memcpy(dma_bufs[i], ish_fw->data + offset, fragment-
>>fragment_tbl[i].length);
fragment->fragment_tbl[i].length was used before assignment.
>+ dma_wmb();
I tested it on the platform, but it didn't wok.
Thanks,
Lixu
> fragment->fragment_tbl[i].length = clamp(ish_fw->size -
>offset, 0, fragment_size);
> fragment->fragment_tbl[i].fw_off = offset;
>- memcpy(dma_bufs[i], ish_fw->data + offset, fragment-
>>fragment_tbl[i].length);
>- clflush_cache_range(dma_bufs[i], fragment_size);
>
> offset += fragment->fragment_tbl[i].length;
> }
>--
>2.39.2
^ permalink raw reply
* RE: [PATCH 2/2] HID: intel-ish-hid: fix endian-conversion
From: Zhang, Lixu @ 2024-05-29 7:05 UTC (permalink / raw)
To: Arnd Bergmann, Srinivas Pandruvada, Jiri Kosina,
Benjamin Tissoires
Cc: Arnd Bergmann, linux-input@vger.kernel.org,
linux-kernel@vger.kernel.org
In-Reply-To: <20240528115802.3122955-2-arnd@kernel.org>
>-----Original Message-----
>From: Arnd Bergmann <arnd@kernel.org>
>Sent: Tuesday, May 28, 2024 7:58 PM
>To: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>; Jiri Kosina
><jikos@kernel.org>; Benjamin Tissoires <bentiss@kernel.org>; Zhang, Lixu
><lixu.zhang@intel.com>
>Cc: Arnd Bergmann <arnd@arndb.de>; linux-input@vger.kernel.org; linux-
>kernel@vger.kernel.org
>Subject: [PATCH 2/2] HID: intel-ish-hid: fix endian-conversion
>
>From: Arnd Bergmann <arnd@arndb.de>
>
>The newly added file causes a ton of sparse warnings about the incorrect use of
>__le32 and similar types:
>
>drivers/hid/intel-ish-hid/ishtp/loader.c:179:17: warning: cast from restricted
>__le32
>drivers/hid/intel-ish-hid/ishtp/loader.c:182:50: warning: incorrect type in
>assignment (different base types)
>drivers/hid/intel-ish-hid/ishtp/loader.c:182:50: expected restricted __le32
>[usertype] length
>drivers/hid/intel-ish-hid/ishtp/loader.c:182:50: got restricted __le16
>[usertype]
>drivers/hid/intel-ish-hid/ishtp/loader.c:183:50: warning: incorrect type in
>assignment (different base types)
>drivers/hid/intel-ish-hid/ishtp/loader.c:183:50: expected restricted __le32
>[usertype] fw_off
>drivers/hid/intel-ish-hid/ishtp/loader.c:183:50: got restricted __le16
>[usertype]
>
>Add the necessary conversions and use temporary variables where appropriate
>to avoid converting back.
>
>Fixes: 579a267e4617 ("HID: intel-ish-hid: Implement loading firmware from
>host feature")
>Signed-off-by: Arnd Bergmann <arnd@arndb.de>
>---
>I noticed this one while looking at the bug that was fixed in
>236049723826 ("HID: intel-ish-hid: Fix build error for COMPILE_TEST")
>---
> drivers/hid/intel-ish-hid/ishtp/loader.c | 69 +++++++++++++-----------
>drivers/hid/intel-ish-hid/ishtp/loader.h | 33 +++++++-----
> 2 files changed, 58 insertions(+), 44 deletions(-)
>
>diff --git a/drivers/hid/intel-ish-hid/ishtp/loader.c b/drivers/hid/intel-ish-
>hid/ishtp/loader.c
>index 062d1b25eaa7..1d4cb99d2130 100644
>--- a/drivers/hid/intel-ish-hid/ishtp/loader.c
>+++ b/drivers/hid/intel-ish-hid/ishtp/loader.c
...
>@@ -162,25 +165,30 @@ static void release_dma_bufs(struct ishtp_device
>*dev, static int prepare_dma_bufs(struct ishtp_device *dev,
> const struct firmware *ish_fw,
> struct loader_xfer_dma_fragment *fragment,
>- void **dma_bufs, u32 fragment_size)
>+ void **dma_bufs, u32 fragment_size, u32
>fragment_count)
See below.
> {
> dma_addr_t dma_addr;
> u32 offset = 0;
>+ u32 length;
> int i;
>
> for (i = 0; i < fragment->fragment_cnt && offset < ish_fw->size; i++) {
You added a parameter fragment_count, but you didn't use it. Did you intend to use it here?
Thanks,
Lixu
> dma_bufs[i] = dma_alloc_coherent(dev->devc, fragment_size,
>&dma_addr, GFP_KERNEL);
>+ dma_bufs[i] = dma_alloc_coherent(dev->devc, fragment_size,
>+ &dma, GFP_KERNEL);
> if (!dma_bufs[i])
> return -ENOMEM;
^ permalink raw reply
* Re: [PATCH 1/2] HID: intel-ish-hid: fix cache management mistake
From: Arnd Bergmann @ 2024-05-29 7:06 UTC (permalink / raw)
To: Lixu Zhang, Arnd Bergmann, srinivas.pandruvada@linux.intel.com,
Jiri Kosina, Benjamin Tissoires
Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <DM4PR11MB5995875EEC9ACCFC43D8B86A93F22@DM4PR11MB5995.namprd11.prod.outlook.com>
On Wed, May 29, 2024, at 08:46, Zhang, Lixu wrote:
>>
>>-#include <linux/cacheflush.h>
>> #include <linux/container_of.h>
>> #include <linux/dev_printk.h>
>> #include <linux/dma-mapping.h>
>>@@ -175,10 +174,11 @@ static int prepare_dma_bufs(struct ishtp_device
>>*dev,
>> return -ENOMEM;
>>
>> fragment->fragment_tbl[i].ddr_adrs =
>>cpu_to_le64(dma_addr);
>>+
>>+ memcpy(dma_bufs[i], ish_fw->data + offset, fragment-
>>>fragment_tbl[i].length);
> fragment->fragment_tbl[i].length was used before assignment.
>
>>+ dma_wmb();
> I tested it on the platform, but it didn't wok.
>
What behavior do you see instead? If the manual cache
flush works around a bug, that would indicate that the
device itself is not coherent and the dma_alloc_coherent()
in the architecture is broken.
Arnd
^ permalink raw reply
* Re: [PATCH HID 03/13] HID: bpf: implement HID-BPF through bpf_struct_ops
From: Benjamin Tissoires @ 2024-05-29 7:38 UTC (permalink / raw)
To: Alexei Starovoitov
Cc: Shuah Khan, Jiri Kosina, Jonathan Corbet, Alexei Starovoitov,
open list:KERNEL SELFTEST FRAMEWORK, LKML, bpf,
open list:HID CORE LAYER, open list:DOCUMENTATION
In-Reply-To: <CAADnVQJbc4uVmhh+ABPMBGFjTYwQQp3wGLeTBYEUWti3R8V8aw@mail.gmail.com>
On May 28 2024, Alexei Starovoitov wrote:
> On Tue, May 28, 2024 at 6:15 AM Benjamin Tissoires <bentiss@kernel.org> wrote:
> >
> > We do this implementation in several steps to not have the CI failing:
> > - first (this patch), we add struct_ops while keeping the existing infra
> > available
> > - then we change the selftests, the examples and the existing in-tree
> > HID-BPF programs
> > - then we remove the existing trace points making old HID-BPF obsolete
> >
> > There are a few advantages of struct_ops over tracing:
> > - compatibility with sleepable programs (for hid_hw_raw_request() in
> > a later patch)
> > - a lot simpler in the kernel: it's a simple rcu protected list
> > - we can add more parameters to the function called without much trouble
> > - the "attach" is now generic through BPF-core: the caller just needs to
> > set hid_id and flags before calling __load().
> > - all the BPF tough part is not handled in BPF-core through generic
> > processing
> > - hid_bpf_ctx is now only writable where it needs be
> >
> > Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>
> > ---
> > drivers/hid/bpf/Makefile | 2 +-
> > drivers/hid/bpf/hid_bpf_dispatch.c | 52 +++++++-
> > drivers/hid/bpf/hid_bpf_dispatch.h | 4 +
> > drivers/hid/bpf/hid_bpf_jmp_table.c | 3 +
> > drivers/hid/bpf/hid_bpf_struct_ops.c | 246 +++++++++++++++++++++++++++++++++++
> > include/linux/hid_bpf.h | 64 ++++++++-
> > 6 files changed, 362 insertions(+), 9 deletions(-)
> >
> > diff --git a/drivers/hid/bpf/Makefile b/drivers/hid/bpf/Makefile
> > index cf55120cf7d6..1cb3f31e9335 100644
> > --- a/drivers/hid/bpf/Makefile
> > +++ b/drivers/hid/bpf/Makefile
> > @@ -8,4 +8,4 @@ LIBBPF_INCLUDE = $(srctree)/tools/lib
> > obj-$(CONFIG_HID_BPF) += hid_bpf.o
> > CFLAGS_hid_bpf_dispatch.o += -I$(LIBBPF_INCLUDE)
> > CFLAGS_hid_bpf_jmp_table.o += -I$(LIBBPF_INCLUDE)
> > -hid_bpf-objs += hid_bpf_dispatch.o hid_bpf_jmp_table.o
> > +hid_bpf-objs += hid_bpf_dispatch.o hid_bpf_jmp_table.o hid_bpf_struct_ops.o
> > diff --git a/drivers/hid/bpf/hid_bpf_dispatch.c b/drivers/hid/bpf/hid_bpf_dispatch.c
> > index c8bb79ce2354..7216c3c7713d 100644
> > --- a/drivers/hid/bpf/hid_bpf_dispatch.c
> > +++ b/drivers/hid/bpf/hid_bpf_dispatch.c
> > @@ -58,6 +58,7 @@ dispatch_hid_bpf_device_event(struct hid_device *hdev, enum hid_report_type type
> > },
> > .data = hdev->bpf.device_data,
> > };
> > + struct hid_bpf_ops *e;
> > int ret;
> >
> > if (type >= HID_REPORT_TYPES)
> > @@ -70,9 +71,25 @@ dispatch_hid_bpf_device_event(struct hid_device *hdev, enum hid_report_type type
> > memset(ctx_kern.data, 0, hdev->bpf.allocated_data);
> > memcpy(ctx_kern.data, data, *size);
> >
> > + rcu_read_lock();
> > + list_for_each_entry_rcu(e, &hdev->bpf.prog_list, list) {
>
> In the typical case there will be only one prog per device, right?
90% of the time there is only one prog per device, but I need to have
more than one for the following reasons:
- when one prog is loaded, the output data gets modified, so to be able
to access the raw data from the device I need to add a "tracing" program
*before* the one that changes data to forward raw data to userspace
- we already have situation where for convenience multiple programs are
attached to one device (see drivers/hid/bpf/progs/XPPen__ArtistPro16Gen2.bpf.c)
- once firewalling will be in place, we will likely have one program for
the firewall and one for the device fix
> The for_each is future proofing?
>
> > + if (e->hid_device_event) {
> > + ret = e->hid_device_event(&ctx_kern.ctx, type);
> > + if (ret < 0) {
> > + rcu_read_unlock();
> > + return ERR_PTR(ret);
> > + }
> > +
> > + if (ret)
> > + ctx_kern.ctx.retval = ret;
> > + }
> > + }
> > + rcu_read_unlock();
> > +
> > ret = hid_bpf_prog_run(hdev, HID_BPF_PROG_TYPE_DEVICE_EVENT, &ctx_kern);
> > if (ret < 0)
> > return ERR_PTR(ret);
> > + ret = ctx_kern.ctx.retval;
> >
> > if (ret) {
> > if (ret > ctx_kern.ctx.allocated_size)
> > @@ -122,7 +139,10 @@ u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int *s
> >
> > memcpy(ctx_kern.data, rdesc, min_t(unsigned int, *size, HID_MAX_DESCRIPTOR_SIZE));
> >
> > - ret = hid_bpf_prog_run(hdev, HID_BPF_PROG_TYPE_RDESC_FIXUP, &ctx_kern);
> > + if (hdev->bpf.rdesc_ops)
> > + ret = hdev->bpf.rdesc_ops->hid_rdesc_fixup(&ctx_kern.ctx);
> > + else
> > + ret = hid_bpf_prog_run(hdev, HID_BPF_PROG_TYPE_RDESC_FIXUP, &ctx_kern);
>
> This is for backward compat?
yes
> I don't see it's being removed in the later patches.
see patch 8/13, all the tracing capabilities are gone.
I made a backward compatibility step here so I do not break the
selftests while changing the API.
>
> > if (ret < 0)
> > goto ignore_bpf;
> >
> > @@ -150,7 +170,7 @@ static int device_match_id(struct device *dev, const void *id)
> > return hdev->id == *(int *)id;
> > }
> >
> > -static struct hid_device *hid_get_device(unsigned int hid_id)
> > +struct hid_device *hid_get_device(unsigned int hid_id)
> > {
> > struct device *dev;
> >
> > @@ -164,7 +184,7 @@ static struct hid_device *hid_get_device(unsigned int hid_id)
> > return to_hid_device(dev);
> > }
> >
> > -static void hid_put_device(struct hid_device *hid)
> > +void hid_put_device(struct hid_device *hid)
> > {
> > put_device(&hid->dev);
> > }
> > @@ -205,7 +225,7 @@ static int __hid_bpf_allocate_data(struct hid_device *hdev, u8 **data, u32 *size
> > return 0;
> > }
> >
> > -static int hid_bpf_allocate_event_data(struct hid_device *hdev)
> > +int hid_bpf_allocate_event_data(struct hid_device *hdev)
> > {
> > /* hdev->bpf.device_data is already allocated, abort */
> > if (hdev->bpf.device_data)
> > @@ -592,14 +612,22 @@ static const struct btf_kfunc_id_set hid_bpf_syscall_kfunc_set = {
> >
> > int hid_bpf_connect_device(struct hid_device *hdev)
> > {
> > - struct hid_bpf_prog_list *prog_list;
> > + bool need_to_allocate = false;
> > + struct hid_bpf_ops *e;
> >
> > rcu_read_lock();
> > - prog_list = rcu_dereference(hdev->bpf.progs[HID_BPF_PROG_TYPE_DEVICE_EVENT]);
> > + list_for_each_entry_rcu(e, &hdev->bpf.prog_list, list) {
> > + if (e->hid_device_event) {
> > + need_to_allocate = true;
> > + break;
> > + }
> > + }
> > + if (rcu_dereference(hdev->bpf.progs[HID_BPF_PROG_TYPE_DEVICE_EVENT]))
> > + need_to_allocate = true;
> > rcu_read_unlock();
> >
> > /* only allocate BPF data if there are programs attached */
> > - if (!prog_list)
> > + if (!need_to_allocate)
> > return 0;
> >
> > return hid_bpf_allocate_event_data(hdev);
> > @@ -623,12 +651,15 @@ void hid_bpf_destroy_device(struct hid_device *hdev)
> > hdev->bpf.destroyed = true;
> >
> > __hid_bpf_destroy_device(hdev);
> > + __hid_bpf_ops_destroy_device(hdev);
> > }
> > EXPORT_SYMBOL_GPL(hid_bpf_destroy_device);
> >
> > void hid_bpf_device_init(struct hid_device *hdev)
> > {
> > spin_lock_init(&hdev->bpf.progs_lock);
> > + INIT_LIST_HEAD(&hdev->bpf.prog_list);
> > + mutex_init(&hdev->bpf.prog_list_lock);
> > }
> > EXPORT_SYMBOL_GPL(hid_bpf_device_init);
> >
> > @@ -662,6 +693,13 @@ static int __init hid_bpf_init(void)
> > return 0;
> > }
> >
> > + /* register struct_ops kfuncs after we are sure we can load our preloaded bpf program */
> > + err = register_btf_kfunc_id_set(BPF_PROG_TYPE_STRUCT_OPS, &hid_bpf_kfunc_set);
> > + if (err) {
> > + pr_warn("error while setting HID BPF tracing kfuncs: %d", err);
> > + return 0;
> > + }
> > +
> > /* register syscalls after we are sure we can load our preloaded bpf program */
> > err = register_btf_kfunc_id_set(BPF_PROG_TYPE_SYSCALL, &hid_bpf_syscall_kfunc_set);
> > if (err) {
> > diff --git a/drivers/hid/bpf/hid_bpf_dispatch.h b/drivers/hid/bpf/hid_bpf_dispatch.h
> > index fbe0639d09f2..e52c43d81650 100644
> > --- a/drivers/hid/bpf/hid_bpf_dispatch.h
> > +++ b/drivers/hid/bpf/hid_bpf_dispatch.h
> > @@ -10,12 +10,16 @@ struct hid_bpf_ctx_kern {
> > u8 *data;
> > };
> >
> > +struct hid_device *hid_get_device(unsigned int hid_id);
> > +void hid_put_device(struct hid_device *hid);
> > +int hid_bpf_allocate_event_data(struct hid_device *hdev);
> > int hid_bpf_preload_skel(void);
> > void hid_bpf_free_links_and_skel(void);
> > int hid_bpf_get_prog_attach_type(struct bpf_prog *prog);
> > int __hid_bpf_attach_prog(struct hid_device *hdev, enum hid_bpf_prog_type prog_type, int prog_fd,
> > struct bpf_prog *prog, __u32 flags);
> > void __hid_bpf_destroy_device(struct hid_device *hdev);
> > +void __hid_bpf_ops_destroy_device(struct hid_device *hdev);
> > int hid_bpf_prog_run(struct hid_device *hdev, enum hid_bpf_prog_type type,
> > struct hid_bpf_ctx_kern *ctx_kern);
> > int hid_bpf_reconnect(struct hid_device *hdev);
> > diff --git a/drivers/hid/bpf/hid_bpf_jmp_table.c b/drivers/hid/bpf/hid_bpf_jmp_table.c
> > index aa8e1c79cdf5..8a54ba447718 100644
> > --- a/drivers/hid/bpf/hid_bpf_jmp_table.c
> > +++ b/drivers/hid/bpf/hid_bpf_jmp_table.c
> > @@ -81,6 +81,9 @@ static int hid_bpf_program_count(struct hid_device *hdev,
> > if (type >= HID_BPF_PROG_TYPE_MAX)
> > return -EINVAL;
> >
> > + if (type == HID_BPF_PROG_TYPE_RDESC_FIXUP && hdev->bpf.rdesc_ops)
> > + n += 1;
> > +
> > FOR_ENTRIES(i, jmp_table.tail, jmp_table.head) {
> > struct hid_bpf_prog_entry *entry = &jmp_table.entries[i];
> >
> > diff --git a/drivers/hid/bpf/hid_bpf_struct_ops.c b/drivers/hid/bpf/hid_bpf_struct_ops.c
> > new file mode 100644
> > index 000000000000..be514a98e55b
> > --- /dev/null
> > +++ b/drivers/hid/bpf/hid_bpf_struct_ops.c
> > @@ -0,0 +1,246 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +
> > +/*
> > + * HID-BPF support for Linux
> > + *
> > + * Copyright (c) 2024 Benjamin Tissoires
> > + */
> > +
> > +#include <linux/bitops.h>
> > +#include <linux/bpf_verifier.h>
> > +#include <linux/bpf.h>
> > +#include <linux/btf.h>
> > +#include <linux/btf_ids.h>
> > +#include <linux/filter.h>
> > +#include <linux/hid.h>
> > +#include <linux/hid_bpf.h>
> > +#include <linux/init.h>
> > +#include <linux/module.h>
> > +#include <linux/workqueue.h>
> > +#include "hid_bpf_dispatch.h"
> > +
> > +static struct btf *hid_bpf_ops_btf;
> > +
> > +static int hid_bpf_ops_init(struct btf *btf)
> > +{
> > + hid_bpf_ops_btf = btf;
> > + return 0;
> > +}
> > +
> > +static bool hid_bpf_ops_is_valid_access(int off, int size,
> > + enum bpf_access_type type,
> > + const struct bpf_prog *prog,
> > + struct bpf_insn_access_aux *info)
> > +{
> > + return bpf_tracing_btf_ctx_access(off, size, type, prog, info);
> > +}
> > +
> > +static int hid_bpf_ops_check_member(const struct btf_type *t,
> > + const struct btf_member *member,
> > + const struct bpf_prog *prog)
> > +{
> > + u32 moff = __btf_member_bit_offset(t, member) / 8;
> > +
>
> this is good...
>
> > + switch (moff) {
> > + case offsetof(struct hid_bpf_ops, hid_rdesc_fixup):
> > + break;
> > + default:
> > + if (prog->sleepable)
> > + return -EINVAL;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int hid_bpf_ops_btf_struct_access(struct bpf_verifier_log *log,
> > + const struct bpf_reg_state *reg,
> > + int off, int size)
> > +{
> > + const struct btf_type *state;
> > + const struct btf_type *t;
> > + s32 type_id;
> > +
> > + type_id = btf_find_by_name_kind(reg->btf, "hid_bpf_ctx",
> > + BTF_KIND_STRUCT);
> > + if (type_id < 0)
> > + return -EINVAL;
> > +
> > + t = btf_type_by_id(reg->btf, reg->btf_id);
> > + state = btf_type_by_id(reg->btf, type_id);
> > + if (t != state) {
> > + bpf_log(log, "only access to hid_bpf_ctx is supported\n");
> > + return -EACCES;
> > + }
> > +
> > + /* out-of-bound access in hid_bpf_ctx */
> > + if (off + size > sizeof(struct hid_bpf_ctx)) {
> > + bpf_log(log, "write access at off %d with size %d\n", off, size);
> > + return -EACCES;
> > + }
> > +
> > + if (off < offsetof(struct hid_bpf_ctx, retval)) {
> > + bpf_log(log,
> > + "write access at off %d with size %d on read-only part of hid_bpf_ctx\n",
> > + off, size);
> > + return -EACCES;
> > + }
> > +
> > + return NOT_INIT;
> > +}
> > +
> > +static const struct bpf_verifier_ops hid_bpf_verifier_ops = {
> > + .is_valid_access = hid_bpf_ops_is_valid_access,
> > + .btf_struct_access = hid_bpf_ops_btf_struct_access,
> > +};
> > +
> > +static int hid_bpf_ops_init_member(const struct btf_type *t,
> > + const struct btf_member *member,
> > + void *kdata, const void *udata)
> > +{
> > + u32 flags;
> > +
> > + switch (member->offset) {
> > + case offsetof(struct hid_bpf_ops, hid_id) * 8:
>
> but here you're open coding it and adding ugly * 8
> Just do:
> moff = __btf_member_bit_offset(t, member) / 8;
> switch (moff) {
> case offsetof(struct tcp_congestion_ops, flags):
>
> the way bpf_tcp_ca.c is doing?
> Open code of ->offset is asking for trouble.
heh, I just monkey-copied tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c
Will change it :)
>
> > + /* For hid_id and flags fields, this function has to copy it
> > + * and return 1 to indicate that the data has been handled by
> > + * the struct_ops type, or the verifier will reject the map if
> > + * the value of those fields is not zero.
> > + */
> > + ((struct hid_bpf_ops *)kdata)->hid_id = ((struct hid_bpf_ops *)udata)->hid_id;
> > + return 1;
> > + case offsetof(struct hid_bpf_ops, flags) * 8:
> > + flags = ((struct hid_bpf_ops *)udata)->flags;
> > + if (flags & ~HID_BPF_FLAG_MASK)
> > + return -EINVAL;
> > + ((struct hid_bpf_ops *)kdata)->flags = flags;
> > + return 1;
> > + }
> > + return 0;
> > +}
> > +
> > +static int hid_bpf_reg(void *kdata)
> > +{
> > + struct hid_bpf_ops *ops = kdata;
> > + struct hid_device *hdev;
> > + int count, err = 0;
> > +
> > + hdev = hid_get_device(ops->hid_id);
> > + if (IS_ERR(hdev))
> > + return PTR_ERR(hdev);
> > +
> > + ops->hdev = hdev;
> > +
> > + mutex_lock(&hdev->bpf.prog_list_lock);
> > +
> > + count = list_count_nodes(&hdev->bpf.prog_list);
> > + if (count >= HID_BPF_MAX_PROGS_PER_DEV) {
> > + err = -E2BIG;
> > + goto out_unlock;
> > + }
> > +
> > + if (ops->hid_rdesc_fixup) {
> > + if (hdev->bpf.rdesc_ops) {
> > + err = -EINVAL;
> > + goto out_unlock;
> > + }
> > +
> > + hdev->bpf.rdesc_ops = ops;
> > + }
> > +
> > + if (ops->hid_device_event) {
> > + err = hid_bpf_allocate_event_data(hdev);
> > + if (err)
> > + goto out_unlock;
> > + }
> > +
> > + if (ops->flags & HID_BPF_FLAG_INSERT_HEAD)
> > + list_add_rcu(&ops->list, &hdev->bpf.prog_list);
> > + else
> > + list_add_tail_rcu(&ops->list, &hdev->bpf.prog_list);
>
> Looks like future proofing, but I feel it's too little to materialize.
again, the case already happens, so I need a list (or equivalent of) :)
> Take a look at include/linux/bpf_mprog.h
> I suspect it might be useful here too.
From the quick look of it this is interesting, but for plain struct
bpf_prog, right?
So I would have to adapt this include for struct_ops?
>
> > +
> > +out_unlock:
> > + mutex_unlock(&hdev->bpf.prog_list_lock);
> > +
> > + if (err) {
> > + if (hdev->bpf.rdesc_ops == ops)
> > + hdev->bpf.rdesc_ops = NULL;
> > + hid_put_device(hdev);
> > + } else if (ops->hid_rdesc_fixup) {
> > + hid_bpf_reconnect(hdev);
> > + }
> > +
> > + return err;
> > +}
> > +
> > +static void hid_bpf_unreg(void *kdata)
> > +{
> > + struct hid_bpf_ops *ops = kdata;
> > + struct hid_device *hdev;
> > + bool reconnect = false;
> > +
> > + hdev = ops->hdev;
> > +
> > + /* check if __hid_bpf_ops_destroy_device() has been called */
> > + if (!hdev)
> > + return;
> > +
> > + mutex_lock(&hdev->bpf.prog_list_lock);
> > +
> > + list_del_rcu(&ops->list);
> > +
> > + reconnect = hdev->bpf.rdesc_ops == ops;
> > + if (reconnect)
> > + hdev->bpf.rdesc_ops = NULL;
> > +
> > + mutex_unlock(&hdev->bpf.prog_list_lock);
> > +
> > + if (reconnect)
> > + hid_bpf_reconnect(hdev);
> > +
> > + hid_put_device(hdev);
> > +}
> > +
> > +static int __hid_bpf_device_event(struct hid_bpf_ctx *ctx, enum hid_report_type type)
> > +{
> > + return 0;
> > +}
> > +
> > +static int __hid_bpf_rdesc_fixup(struct hid_bpf_ctx *ctx)
> > +{
> > + return 0;
> > +}
> > +
> > +static struct hid_bpf_ops __bpf_hid_bpf_ops = {
> > + .hid_device_event = __hid_bpf_device_event,
> > + .hid_rdesc_fixup = __hid_bpf_rdesc_fixup,
> > +};
> > +
> > +static struct bpf_struct_ops bpf_hid_bpf_ops = {
> > + .verifier_ops = &hid_bpf_verifier_ops,
> > + .init = hid_bpf_ops_init,
> > + .check_member = hid_bpf_ops_check_member,
> > + .init_member = hid_bpf_ops_init_member,
> > + .reg = hid_bpf_reg,
> > + .unreg = hid_bpf_unreg,
> > + .name = "hid_bpf_ops",
> > + .cfi_stubs = &__bpf_hid_bpf_ops,
> > + .owner = THIS_MODULE,
> > +};
> > +
> > +void __hid_bpf_ops_destroy_device(struct hid_device *hdev)
> > +{
> > + struct hid_bpf_ops *e;
> > +
> > + rcu_read_lock();
> > + list_for_each_entry_rcu(e, &hdev->bpf.prog_list, list) {
> > + hid_put_device(hdev);
> > + e->hdev = NULL;
> > + }
> > + rcu_read_unlock();
> > +}
> > +
> > +static int __init hid_bpf_struct_ops_init(void)
> > +{
> > + return register_bpf_struct_ops(&bpf_hid_bpf_ops, hid_bpf_ops);
> > +}
> > +late_initcall(hid_bpf_struct_ops_init);
> > diff --git a/include/linux/hid_bpf.h b/include/linux/hid_bpf.h
> > index a66103618e6e..96495e977204 100644
> > --- a/include/linux/hid_bpf.h
> > +++ b/include/linux/hid_bpf.h
> > @@ -65,11 +65,12 @@ struct hid_bpf_ctx {
> > * @HID_BPF_FLAG_INSERT_HEAD: insert the given program before any other program
> > * currently attached to the device. This doesn't
> > * guarantee that this program will always be first
> > - * @HID_BPF_FLAG_MAX: sentinel value, not to be used by the callers
> > */
> > enum hid_bpf_attach_flags {
> > HID_BPF_FLAG_NONE = 0,
> > HID_BPF_FLAG_INSERT_HEAD = _BITUL(0),
> > +
> > + /* private: internal use only */
> > HID_BPF_FLAG_MAX,
> > };
> >
> > @@ -112,6 +113,63 @@ struct hid_ops {
> >
> > extern struct hid_ops *hid_ops;
> >
> > +/**
> > + * struct hid_bpf_ops - A BPF struct_ops of callbacks allowing to attach HID-BPF
> > + * programs to a HID device
> > + * @hid_id: the HID uniq ID to attach to. This is writeable before ``load()``, and
> > + * cannot be changed after
> > + * @flags: &enum hid_bpf_attach_flags to assign flags before ``load()``.
> > + * Writeable only before ``load()``
> > + */
> > +struct hid_bpf_ops {
> > + /* hid_id needs to stay first so we can easily change it
> > + * from userspace.
> > + */
>
> hmm.
> patch 5 just does:
> + skel->struct_ops.mouse_invert->hid_id = hid_id;
>
> I don't see a reason why it needs to be first.
See the selftests (and my external loader, udev-hid-bpf):
map = bpf_object__find_map_by_name(*self->skel->skeleton->obj,
programs[i].name + 4);
ASSERT_OK_PTR(map) TH_LOG("can not find struct_ops by name '%s'",
programs[i].name + 4);
/* hid_id is the first field of struct hid_bpf_ops */
ops_hid_id = bpf_map__initial_value(map, NULL);
ASSERT_OK_PTR(ops_hid_id) TH_LOG("unable to retrieve struct_ops data");
*ops_hid_id = self->hid_id;
Basically, I want to have to deal with .bpf.o and not the skeletons for
the generic case. I want to be able to drop a file in /lib/firmware/hid
and have udev-hid-bpf load it without having to recompile udev-hid-bpf.
I couldn't seem to be able to access the struct_ops BTF information from
libbpf, so I decided to have hid_id and flags first and consider this to
be ABI while I figure out how to get to the real offsets :)
>
> > + int hid_id;
> > + u32 flags;
> > +
> > + /* private: internal use only */
> > + struct list_head list;
> > +
> > + /* public: rest is public */
> > +
> > +/* fast path fields are put first to fill one cache line */
> > +
> > + /**
> > + * @hid_device_event: called whenever an event is coming in from the device
> > + *
> > + * It has the following arguments:
> > + *
> > + * ``ctx``: The HID-BPF context as &struct hid_bpf_ctx
> > + *
> > + * Return: %0 on success and keep processing; a positive
> > + * value to change the incoming size buffer; a negative
> > + * error code to interrupt the processing of this event
> > + *
> > + * Context: Interrupt context.
> > + */
> > + int (*hid_device_event)(struct hid_bpf_ctx *ctx, enum hid_report_type report_type);
> > +
> > +/* control/slow paths put last */
> > +
> > + /**
> > + * @hid_rdesc_fixup: called when the probe function parses the report descriptor
> > + * of the HID device
> > + *
> > + * It has the following arguments:
> > + *
> > + * ``ctx``: The HID-BPF context as &struct hid_bpf_ctx
> > + *
> > + * Return: %0 on success and keep processing; a positive
> > + * value to change the incoming size buffer; a negative
> > + * error code to interrupt the processing of this device
> > + */
> > + int (*hid_rdesc_fixup)(struct hid_bpf_ctx *ctx);
>
> It's fine since you want to keep the prog mostly unchanged,
> but since the whole api is improving maybe it's time
> to do something about hid_bpf_ctx in all callbacks?
> Pass into prog what prog needs to see.
> In the old api all progs had to have a common hid_bpf_ctx.
> With struct-ops it can be fine tuned.
I agree, but I had a few cons:
- right now, I can recompile the same bpf.c with just a -D in the
cflags. Both types of program are compatible, which allows to write
the file once and recompile it for both pre/post struct_ops.
This will be very useful while HID-BPF struct_ops is not in mainline
distributions
- I need to have access to a few things: the struct hid_device, and the
incoming buffer. And I don't think I can teach the verifier about the
incoming buffer size without having to store it into a dedicated
struct, which results into the same hid_bpf_ctx...
- I like how hid_bpf_allocate_context() works with the same struct that
can be later used with the same functions hid_bpf_input_event() or
such (arguably I could return the struct hid_device directly)
I'll try to give it more thoughts :)
>
> Overall it looks great.
> hid_id did the trick :)
Thanks!
I agree, relying on struct_ops allowed to do a heavy cleanup in my code,
and TBH, even if I had only one callback, I think the gain is clear,
because it's much straightforward and you can also authorize some fine
grain write on your own structs (like changing the name of the
hid_device, or its flags, which is not part of this series, but which
will likely come later).
Cheers,
Benjamin
^ permalink raw reply
* Re: [PATCH v1 1/2] hid-asus: use hid for brightness control on keyboard
From: Hans de Goede @ 2024-05-29 8:21 UTC (permalink / raw)
To: Luke D. Jones, jikos
Cc: ilpo.jarvinen, corentin.chary, platform-driver-x86, linux-kernel,
linux-input, bentiss
In-Reply-To: <20240529012827.146005-2-luke@ljones.dev>
Hi Luke,
Thank you for the new version, much better.
On 5/29/24 3:28 AM, Luke D. Jones wrote:
> On almost all ASUS ROG series laptops the MCU used for the USB keyboard
> also has a HID packet used for setting the brightness. This is usually
> the same as the WMI method. But in some laptops the WMI method either
> is missing or doesn't work, so we should default to the HID control.
>
> Signed-off-by: Luke D. Jones <luke@ljones.dev>
> ---
> drivers/hid/hid-asus.c | 6 ++++
> drivers/platform/x86/asus-wmi.c | 35 +++++++++++++++++++++-
> include/linux/platform_data/x86/asus-wmi.h | 10 +++++++
> 3 files changed, 50 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
> index 02de2bf4f790..4cba8e143031 100644
> --- a/drivers/hid/hid-asus.c
> +++ b/drivers/hid/hid-asus.c
> @@ -492,12 +492,18 @@ static void asus_kbd_backlight_work(struct work_struct *work)
> */
> static bool asus_kbd_wmi_led_control_present(struct hid_device *hdev)
> {
> + struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
> u32 value;
> int ret;
>
> if (!IS_ENABLED(CONFIG_ASUS_WMI))
> return false;
>
> + if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD && asus_use_hid_led()) {
> + hid_info(hdev, "using HID for asus::kbd_backlight\n");
> + return false;
> + }
> +
> ret = asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS,
> ASUS_WMI_DEVID_KBD_BACKLIGHT, 0, &value);
> hid_dbg(hdev, "WMI backlight check: rc %d value %x", ret, value);
> diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
> index 3f9b6285c9a6..54cb07c79fcf 100644
> --- a/drivers/platform/x86/asus-wmi.c
> +++ b/drivers/platform/x86/asus-wmi.c
> @@ -144,6 +144,15 @@ module_param(fnlock_default, bool, 0444);
>
> static const char * const ashs_ids[] = { "ATK4001", "ATK4002", NULL };
>
> +static const char * const use_hid_led_matches[] = {
> + "ROG Zephyrus",
> + "ROG Strix",
> + "ROG Flow",
> + "GA403",
> + "GU605",
> + "RC71L",
> +};
> +
> static int throttle_thermal_policy_write(struct asus_wmi *);
>
> static bool ashs_present(void)
> @@ -1642,6 +1651,29 @@ static int micmute_led_set(struct led_classdev *led_cdev,
> return err < 0 ? err : 0;
> }
>
> +bool asus_use_hid_led(void)
> +{
> + const char *product, *board;
> + int i;
> +
> + product = dmi_get_system_info(DMI_PRODUCT_FAMILY);
> + if (!product)
> + return false;
> +
> + board = dmi_get_system_info(DMI_BOARD_NAME);
> + if (!board)
> + return false;
> +
> + for (i = 0; i < ARRAY_SIZE(use_hid_led_matches); i++) {
> + if (strstr(product, use_hid_led_matches[i]))
> + return true;
> + if (strstr(board, use_hid_led_matches[i]))
> + return true;
> + }
> + return false;
> +}
> +EXPORT_SYMBOL_GPL(asus_use_hid_led);
> +
With the new helper in this form you are pretty much re-inventing
dmi_check_system().
So IMHO you should just replace use_hid_led_matches[] with
a dmi_system_id array and simplify asus_use_hid_led() to just
a single "dmi_check_system(asus_use_hid_led_dmi_ids)" call.
Using dmi_system_id-s also allows you to specify if the string
which is being matched should be matched against DMI_PRODUCT_FAMILY
or DMI_BOARD_NAME. Note the normal DMI_MATCH() macro to fill in
dmi_system_id array entries does non exact matching using strstr()
just like you do above.
Note you need to terminate the dmi_system_id array with an empty
{} entry.
> static void asus_wmi_led_exit(struct asus_wmi *asus)
> {
> led_classdev_unregister(&asus->kbd_led);
> @@ -1681,7 +1713,8 @@ static int asus_wmi_led_init(struct asus_wmi *asus)
> goto error;
> }
>
> - if (!kbd_led_read(asus, &led_val, NULL)) {
> + if (!kbd_led_read(asus, &led_val, NULL) && !asus_use_hid_led()) {
> + pr_info("using asus-wmi for asus::kbd_backlight\n");
> asus->kbd_led_wk = led_val;
> asus->kbd_led.name = "asus::kbd_backlight";
> asus->kbd_led.flags = LED_BRIGHT_HW_CHANGED;
> diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
> index 3eb5cd6773ad..6833035f7006 100644
> --- a/include/linux/platform_data/x86/asus-wmi.h
> +++ b/include/linux/platform_data/x86/asus-wmi.h
> @@ -160,4 +160,14 @@ static inline int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1,
> }
> #endif
>
> +/* To be used by both hid-asus and asus-wmi to determine which controls kbd_brightness */
> +#if IS_ENABLED(CONFIG_ASUS_WMI)
> +bool asus_use_hid_led(void);
> +#else
> +static inline bool asus_use_hid_led(void)
> +{
> + return true;
> +}
> +#endif
> +
> #endif /* __PLATFORM_DATA_X86_ASUS_WMI_H */
Regards,
Hans
^ permalink raw reply
* Re: [PATCH v1 2/2] hid-asus: change the report_id used for HID LED control
From: Hans de Goede @ 2024-05-29 8:25 UTC (permalink / raw)
To: Luke D. Jones, jikos
Cc: ilpo.jarvinen, corentin.chary, platform-driver-x86, linux-kernel,
linux-input, bentiss
In-Reply-To: <20240529012827.146005-3-luke@ljones.dev>
Hi,
On 5/29/24 3:28 AM, Luke D. Jones wrote:
> On some laptops the report_id used for LED brightness control must be
> 0x5D instead of 0x5A.
>
> Signed-off-by: Luke D. Jones <luke@ljones.dev>
> ---
> drivers/hid/hid-asus.c | 26 +++++++++++++++++++++++++-
> 1 file changed, 25 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
> index 4cba8e143031..ec3556cc4eef 100644
> --- a/drivers/hid/hid-asus.c
> +++ b/drivers/hid/hid-asus.c
> @@ -94,6 +94,8 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
>
> #define TRKID_SGN ((TRKID_MAX + 1) >> 1)
>
> +static const char * const use_alt_led_report_id[] = { "GU605", "GA403" };
> +
> struct asus_kbd_leds {
> struct led_classdev cdev;
> struct hid_device *hdev;
> @@ -101,6 +103,7 @@ struct asus_kbd_leds {
> unsigned int brightness;
> spinlock_t lock;
> bool removed;
> + int report_id;
> };
>
> struct asus_touchpad_info {
> @@ -473,7 +476,7 @@ static enum led_brightness asus_kbd_backlight_get(struct led_classdev *led_cdev)
> static void asus_kbd_backlight_work(struct work_struct *work)
> {
> struct asus_kbd_leds *led = container_of(work, struct asus_kbd_leds, work);
> - u8 buf[] = { FEATURE_KBD_REPORT_ID, 0xba, 0xc5, 0xc4, 0x00 };
> + u8 buf[] = { led->report_id, 0xba, 0xc5, 0xc4, 0x00 };
> int ret;
> unsigned long flags;
>
> @@ -513,6 +516,23 @@ static bool asus_kbd_wmi_led_control_present(struct hid_device *hdev)
> return !!(value & ASUS_WMI_DSTS_PRESENCE_BIT);
> }
>
> +static bool asus_kbd_is_input_led(void)
> +{
> + const char *product;
> + int i;
> +
> + product = dmi_get_system_info(DMI_PRODUCT_NAME);
> + if (!product)
> + return false;
> +
> + for (i = 0; i < ARRAY_SIZE(use_alt_led_report_id); i++) {
> + if (strstr(product, use_alt_led_report_id[i]))
> + return true;
> + }
> +
> + return false;
> +}
> +
This again feels like you are re-inventing dmi_check_system() please
change use_alt_led_report_id to a dmi_system_id array and drop this
function (you can directly call dmi_check_system(use_alt_led_report_id)
instead).
> static int asus_kbd_register_leds(struct hid_device *hdev)
> {
> struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
> @@ -555,6 +575,10 @@ static int asus_kbd_register_leds(struct hid_device *hdev)
> if (!drvdata->kbd_backlight)
> return -ENOMEM;
>
> + drvdata->kbd_backlight->report_id = FEATURE_KBD_REPORT_ID;
> + if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD && asus_kbd_is_input_led())
> + drvdata->kbd_backlight->report_id = FEATURE_KBD_LED_REPORT_ID1;
> +
> drvdata->kbd_backlight->removed = false;
> drvdata->kbd_backlight->brightness = 0;
> drvdata->kbd_backlight->hdev = hdev;
Regards,
Hans
^ permalink raw reply
* [PATCH v12 0/3] Input: Add TouchNetix axiom touchscreen driver
From: Kamel Bouhara @ 2024-05-29 9:10 UTC (permalink / raw)
To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Henrik Rydberg, linux-input, linux-kernel, devicetree,
Marco Felsch, Jeff LaBundy
Cc: catalin.popescu, mark.satterthwaite, Thomas Petazzoni,
Gregory Clement, bsp-development.geo, Kamel Bouhara
Add a new driver for the TouchNetix's axiom family of touchscreen
controller. This driver only support i2c and can be later adapted for
SPI and USB support.
--
Changes in v12:
- Fix REGMAP_I2C not selected
- Rebase on v6.10-rc10
Changes in v11:
- Fix regulators name to match dt-binding
- Enable regulators before reset is asserted
Changes in v10:
- Set regulators as required
- Enable power supply before reset
- Fix ref count due to regulator requested twice
- Rebase on v6.9-rc4
Changes in v9:
- Fix issue reported in https://lore.kernel.org/oe-kbuild-all/202402201157.BKo97uWl-lkp@intel.com/
- Rebase on v6.8-rc2
Changes in v8:
- Fix missing call to input_report_slot_state()
- Fix issue reported in https://lore.kernel.org/oe-kbuild-all/202402020623.8T1Ah513-lkp@intel.com/
Changes in v7:
- Remove startup time from dt-binding
- Fix usage table not correctly populated
Changes in v6:
- Fix missing unevaluatedProperties.in dt-binding
- Use __le16 to correctly deal with device endianness
- Use standart kernel types s/char/u8/
- Use regmap api as driver might support spi later
- Use get_unaligned_le16() for the sake of clarity
- Use devm_regulator_enable_optional()
Changes in v5:
- Fix wrong message constructed in axiom_i2c_read
- Delay required between i2c reads is >= 250us
- Do not split report reading in two phases as we'll
have to wait 500us
- Use lower-case in properties names
- Make regulators properties are required in dt-binding
- Fix bug report: https://lore.kernel.org/lkml/202312051457.y3N1q3sZ-lkp@intel.com/
- Fix bug report: https://lore.kernel.org/lkml/6f8e3b64-5b21-4a50-8680-063ef7a93bdb@suswa.mountain/
Changes in v4:
- Cleanup unused headers and macros
- Use standard kernel type
- Namespace structures and functions
- Use packed struct when possible to avoid bitfield operators
- Fix missing break when address is found in axiom_populate_target_address()
- Split reads in two steps for the reports, first length then report
itself so we only read required bytes
- Get poll-interval from devicetree
- Add VDDI/VDDA regulators
- Add a startup delay of 110 ms required after VDDA/VDDI is applied
- Remove axiom_i2c_write() as it is no more used
Changes in v3:
- Remove irq-gpios property in dt-binding
- Use a generic node name
- Fix issues reported in https://lore.kernel.org/oe-kbuild-all/202310100300.oAC2M62R-lkp@intel.com/
Changes in v2:
- Add device tree binding documentation
- Move core functions in axiom_i2c as we only care about i2c support now
- Use static function when required
- Use syntax dev_err_probe()
- Add an hardware based reset
Kamel Bouhara (3):
dt-bindings: vendor-prefixes: Add TouchNetix AS
dt-bindings: input: Add TouchNetix axiom touchscreen
Input: Add TouchNetix axiom i2c touchscreen driver
.../input/touchscreen/touchnetix,ax54a.yaml | 62 ++
.../devicetree/bindings/vendor-prefixes.yaml | 2 +
MAINTAINERS | 8 +
drivers/input/touchscreen/Kconfig | 13 +
drivers/input/touchscreen/Makefile | 1 +
drivers/input/touchscreen/touchnetix_axiom.c | 657 ++++++++++++++++++
6 files changed, 743 insertions(+)
create mode 100644 Documentation/devicetree/bindings/input/touchscreen/touchnetix,ax54a.yaml
create mode 100644 drivers/input/touchscreen/touchnetix_axiom.c
--
2.25.1
^ permalink raw reply
* [PATCH v12 1/3] dt-bindings: vendor-prefixes: Add TouchNetix AS
From: Kamel Bouhara @ 2024-05-29 9:10 UTC (permalink / raw)
To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Henrik Rydberg, linux-input, linux-kernel, devicetree,
Marco Felsch, Jeff LaBundy
Cc: catalin.popescu, mark.satterthwaite, Thomas Petazzoni,
Gregory Clement, bsp-development.geo, Kamel Bouhara,
Krzysztof Kozlowski
In-Reply-To: <20240529091004.107256-1-kamel.bouhara@bootlin.com>
Add vendor prefix for TouchNetix AS (https://www.touchnetix.com/products/).
Signed-off-by: Kamel Bouhara <kamel.bouhara@bootlin.com>
Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
---
Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
index fbf47f0bacf1..f102b3abf3fc 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
@@ -1489,6 +1489,8 @@ patternProperties:
description: Toradex AG
"^toshiba,.*":
description: Toshiba Corporation
+ "^touchnetix,.*":
+ description: TouchNetix AS
"^toumaz,.*":
description: Toumaz
"^tpk,.*":
--
2.25.1
^ permalink raw reply related
* [PATCH v12 2/3] dt-bindings: input: Add TouchNetix axiom touchscreen
From: Kamel Bouhara @ 2024-05-29 9:10 UTC (permalink / raw)
To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Henrik Rydberg, linux-input, linux-kernel, devicetree,
Marco Felsch, Jeff LaBundy
Cc: catalin.popescu, mark.satterthwaite, Thomas Petazzoni,
Gregory Clement, bsp-development.geo, Kamel Bouhara,
Krzysztof Kozlowski
In-Reply-To: <20240529091004.107256-1-kamel.bouhara@bootlin.com>
Add the TouchNetix axiom I2C touchscreen device tree bindings
documentation.
Signed-off-by: Kamel Bouhara <kamel.bouhara@bootlin.com>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
---
.../input/touchscreen/touchnetix,ax54a.yaml | 62 +++++++++++++++++++
MAINTAINERS | 6 ++
2 files changed, 68 insertions(+)
create mode 100644 Documentation/devicetree/bindings/input/touchscreen/touchnetix,ax54a.yaml
diff --git a/Documentation/devicetree/bindings/input/touchscreen/touchnetix,ax54a.yaml b/Documentation/devicetree/bindings/input/touchscreen/touchnetix,ax54a.yaml
new file mode 100644
index 000000000000..66229a4d6f15
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/touchscreen/touchnetix,ax54a.yaml
@@ -0,0 +1,62 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/input/touchscreen/touchnetix,ax54a.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: TouchNetix Axiom series touchscreen controller
+
+maintainers:
+ - Kamel Bouhara <kamel.bouhara@bootlin.com>
+
+allOf:
+ - $ref: /schemas/input/touchscreen/touchscreen.yaml#
+ - $ref: /schemas/input/input.yaml#
+
+properties:
+ compatible:
+ const: touchnetix,ax54a
+
+ reg:
+ const: 0x66
+
+ interrupts:
+ maxItems: 1
+
+ reset-gpios:
+ maxItems: 1
+
+ vdda-supply:
+ description: Analog power supply regulator on VDDA pin
+
+ vddi-supply:
+ description: I/O power supply regulator on VDDI pin
+
+required:
+ - compatible
+ - reg
+ - vdda-supply
+ - vddi-supply
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ touchscreen@66 {
+ compatible = "touchnetix,ax54a";
+ reg = <0x66>;
+ interrupt-parent = <&gpio2>;
+ interrupts = <2 IRQ_TYPE_EDGE_FALLING>;
+ reset-gpios = <&gpio1 1 GPIO_ACTIVE_HIGH>;
+ vdda-supply = <&vdda_reg>;
+ vddi-supply = <&vddi_reg>;
+ poll-interval = <20>;
+ };
+ };
+...
diff --git a/MAINTAINERS b/MAINTAINERS
index d6c90161c7bf..a15f0b6ffcbe 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -22742,6 +22742,12 @@ L: platform-driver-x86@vger.kernel.org
S: Maintained
F: drivers/platform/x86/toshiba-wmi.c
+TOUCHNETIX AXIOM I2C TOUCHSCREEN DRIVER
+M: Kamel Bouhara <kamel.bouhara@bootlin.com>
+L: linux-input@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/input/touchscreen/touchnetix,ax54a.yaml
+
TPM DEVICE DRIVER
M: Peter Huewe <peterhuewe@gmx.de>
M: Jarkko Sakkinen <jarkko@kernel.org>
--
2.25.1
^ permalink raw reply related
* [PATCH v12 3/3] Input: Add TouchNetix axiom i2c touchscreen driver
From: Kamel Bouhara @ 2024-05-29 9:10 UTC (permalink / raw)
To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Henrik Rydberg, linux-input, linux-kernel, devicetree,
Marco Felsch, Jeff LaBundy
Cc: catalin.popescu, mark.satterthwaite, Thomas Petazzoni,
Gregory Clement, bsp-development.geo, Kamel Bouhara
In-Reply-To: <20240529091004.107256-1-kamel.bouhara@bootlin.com>
Add a new driver for the TouchNetix's axiom family of
touchscreen controllers. This driver only supports i2c
and can be later adapted for SPI and USB support.
Signed-off-by: Kamel Bouhara <kamel.bouhara@bootlin.com>
---
MAINTAINERS | 2 +
drivers/input/touchscreen/Kconfig | 13 +
drivers/input/touchscreen/Makefile | 1 +
drivers/input/touchscreen/touchnetix_axiom.c | 657 +++++++++++++++++++
4 files changed, 673 insertions(+)
create mode 100644 drivers/input/touchscreen/touchnetix_axiom.c
diff --git a/MAINTAINERS b/MAINTAINERS
index a15f0b6ffcbe..f1733213ebaf 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -22744,9 +22744,11 @@ F: drivers/platform/x86/toshiba-wmi.c
TOUCHNETIX AXIOM I2C TOUCHSCREEN DRIVER
M: Kamel Bouhara <kamel.bouhara@bootlin.com>
+M: bsp-development.geo@leica-geosystems.com
L: linux-input@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/input/touchscreen/touchnetix,ax54a.yaml
+F: drivers/input/touchscreen/touchnetix_axiom.c
TPM DEVICE DRIVER
M: Peter Huewe <peterhuewe@gmx.de>
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index c821fe3ee794..8659030591e6 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -834,6 +834,19 @@ config TOUCHSCREEN_MIGOR
To compile this driver as a module, choose M here: the
module will be called migor_ts.
+config TOUCHSCREEN_TOUCHNETIX_AXIOM
+ tristate "TouchNetix AXIOM based touchscreen controllers"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Say Y here if you have a axiom touchscreen connected to
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called axiom.
+
config TOUCHSCREEN_TOUCHRIGHT
tristate "Touchright serial touchscreen"
select SERIO
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index a81cb5aa21a5..6ce7b804adc7 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -91,6 +91,7 @@ obj-$(CONFIG_TOUCHSCREEN_SUR40) += sur40.o
obj-$(CONFIG_TOUCHSCREEN_SURFACE3_SPI) += surface3_spi.o
obj-$(CONFIG_TOUCHSCREEN_TI_AM335X_TSC) += ti_am335x_tsc.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o
+obj-$(CONFIG_TOUCHSCREEN_TOUCHNETIX_AXIOM) += touchnetix_axiom.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o
obj-$(CONFIG_TOUCHSCREEN_TS4800) += ts4800-ts.o
diff --git a/drivers/input/touchscreen/touchnetix_axiom.c b/drivers/input/touchscreen/touchnetix_axiom.c
new file mode 100644
index 000000000000..09550847392e
--- /dev/null
+++ b/drivers/input/touchscreen/touchnetix_axiom.c
@@ -0,0 +1,657 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * TouchNetix axiom Touchscreen Driver
+ *
+ * Copyright (C) 2020-2023 TouchNetix Ltd.
+ *
+ * Author(s): Bart Prescott <bartp@baasheep.co.uk>
+ * Pedro Torruella <pedro.torruella@touchnetix.com>
+ * Mark Satterthwaite <mark.satterthwaite@touchnetix.com>
+ * Hannah Rossiter <hannah.rossiter@touchnetix.com>
+ * Kamel Bouhara <kamel.bouhara@bootlin.com>
+ *
+ */
+#include <linux/bitfield.h>
+#include <linux/crc16.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/input/touchscreen.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/regmap.h>
+
+#include <asm/unaligned.h>
+#define AXIOM_PROX_LEVEL -128
+#define AXIOM_DMA_OPS_DELAY_USEC 250
+#define AXIOM_STARTUP_TIME_MS 110
+/*
+ * Register group u31 has 2 pages for usage table entries.
+ */
+#define AXIOM_U31_MAX_USAGES 0xff
+#define AXIOM_U31_BYTES_PER_USAGE 6
+#define AXIOM_U31_PAGE0_LENGTH 0x0C
+#define AXIOM_U31_BOOTMODE_MASK BIT(7)
+#define AXIOM_U31_DEVID_MASK GENMASK(14, 0)
+
+#define AXIOM_MAX_REPORT_LEN 0x7f
+
+#define AXIOM_CMD_HEADER_READ_MASK BIT(15)
+#define AXIOM_U41_MAX_TARGETS 10
+
+#define AXIOM_U46_AUX_CHANNELS 4
+#define AXIOM_U46_AUX_MASK GENMASK(11, 0)
+
+#define AXIOM_COMMS_MAX_USAGE_PAGES 3
+#define AXIOM_COMMS_PAGE_SIZE 256
+#define AXIOM_COMMS_REPORT_LEN_MASK GENMASK(6, 0)
+
+#define AXIOM_REPORT_USAGE_ID 0x34
+#define AXIOM_DEVINFO_USAGE_ID 0x31
+#define AXIOM_USAGE_2HB_REPORT_ID 0x01
+#define AXIOM_USAGE_2AUX_REPORT_ID 0x46
+#define AXIOM_USAGE_2DCTS_REPORT_ID 0x41
+
+#define AXIOM_PAGE_OFFSET_MASK GENMASK(6, 0)
+
+struct axiom_devinfo {
+ __le16 device_id;
+ u8 fw_minor;
+ u8 fw_major;
+ u8 fw_info_extra;
+ u8 tcp_revision;
+ u8 bootloader_fw_minor;
+ u8 bootloader_fw_major;
+ __le16 jedec_id;
+ u8 num_usages;
+} __packed;
+
+/*
+ * Describes parameters of a specific usage, essentially a single element of
+ * the "Usage Table"
+ */
+struct axiom_usage_entry {
+ u8 id;
+ u8 is_report;
+ u8 start_page;
+ u8 num_pages;
+};
+
+/*
+ * Represents state of a touch or target when detected prior to a touch (eg.
+ * hover or proximity events).
+ */
+enum axiom_target_state {
+ AXIOM_TARGET_STATE_NOT_PRESENT = 0,
+ AXIOM_TARGET_STATE_PROX = 1,
+ AXIOM_TARGET_STATE_HOVER = 2,
+ AXIOM_TARGET_STATE_TOUCHING = 3,
+};
+
+struct axiom_u41_target {
+ enum axiom_target_state state;
+ u16 x;
+ u16 y;
+ s8 z;
+ bool insert;
+ bool touch;
+};
+
+struct axiom_target_report {
+ u8 index;
+ u8 present;
+ u16 x;
+ u16 y;
+ s8 z;
+};
+
+struct axiom_cmd_header {
+ __le16 target_address;
+ __le16 length;
+} __packed;
+
+struct axiom_data {
+ struct axiom_devinfo devinfo;
+ struct device *dev;
+ struct gpio_desc *reset_gpio;
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ u32 max_report_len;
+ u8 rx_buf[AXIOM_COMMS_MAX_USAGE_PAGES * AXIOM_COMMS_PAGE_SIZE];
+ struct axiom_u41_target targets[AXIOM_U41_MAX_TARGETS];
+ struct axiom_usage_entry usage_table[AXIOM_U31_MAX_USAGES];
+ bool usage_table_populated;
+ struct regmap *regmap;
+ struct touchscreen_properties prop;
+};
+
+static const struct regmap_config axiom_i2c_regmap_config = {
+ .reg_bits = 32,
+ .reg_format_endian = REGMAP_ENDIAN_LITTLE,
+ .val_bits = 8,
+ .val_format_endian = REGMAP_ENDIAN_LITTLE,
+};
+
+/*
+ * axiom devices are typically configured to report touches at a rate
+ * of 100Hz (10ms) for systems that require polling for reports.
+ * When reports are polled, it will be expected to occasionally
+ * observe the overflow bit being set in the reports.
+ * This indicates that reports are not being read fast enough.
+ */
+#define POLL_INTERVAL_DEFAULT_MS 10
+
+/* Translate usage/page/offset triplet into physical address. */
+static u16 axiom_usage_to_target_address(struct axiom_data *ts, u8 usage, u8 page,
+ char offset)
+{
+ /* At the moment the convention is that u31 is always at physical address 0x0 */
+ if (!ts->usage_table_populated) {
+ if (usage == AXIOM_DEVINFO_USAGE_ID)
+ return ((page << 8) + offset);
+ else
+ return 0xffff;
+ }
+
+ if (page >= ts->usage_table[usage].num_pages) {
+ dev_err(ts->dev, "Invalid usage table! usage: u%02x, page: %02x, offset: %02x\n",
+ usage, page, offset);
+ return 0xffff;
+ }
+
+ return ((ts->usage_table[usage].start_page + page) << 8) + offset;
+}
+
+static int axiom_read(struct axiom_data *ts, u8 usage, u8 page, void *buf, u16 len)
+{
+ struct axiom_cmd_header cmd_header;
+ u32 preamble;
+ int ret;
+
+ cmd_header.target_address = cpu_to_le16(axiom_usage_to_target_address(ts, usage, page, 0));
+ cmd_header.length = cpu_to_le16(len | AXIOM_CMD_HEADER_READ_MASK);
+
+ preamble = get_unaligned_le32(&cmd_header);
+
+ ret = regmap_write(ts->regmap, preamble, 0);
+ if (ret) {
+ dev_err(ts->dev, "failed to write preamble, error %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_raw_read(ts->regmap, 0, buf, len);
+ if (ret) {
+ dev_err(ts->dev, "failed to read target address %04x, error %d\n",
+ cmd_header.target_address, ret);
+ return ret;
+ }
+
+ /* Wait device's DMA operations */
+ usleep_range(AXIOM_DMA_OPS_DELAY_USEC, AXIOM_DMA_OPS_DELAY_USEC + 50);
+
+ return 0;
+}
+
+/*
+ * One of the main purposes for reading the usage table is to identify
+ * which usages reside at which target address.
+ * When performing subsequent reads or writes to AXIOM, the target address
+ * is used to specify which usage is being accessed.
+ * Consider the following discovery code which will build up the usage table.
+ */
+static u32 axiom_populate_usage_table(struct axiom_data *ts)
+{
+ struct axiom_usage_entry *usage_table;
+ u8 *rx_data = ts->rx_buf;
+ u32 max_report_len = 0;
+ u32 usage_id;
+ int error;
+
+ usage_table = ts->usage_table;
+
+ /* Read the second page of usage u31 to get the usage table */
+ error = axiom_read(ts, AXIOM_DEVINFO_USAGE_ID, 1, rx_data,
+ (AXIOM_U31_BYTES_PER_USAGE * ts->devinfo.num_usages));
+
+ if (error)
+ return error;
+
+ for (usage_id = 0; usage_id < ts->devinfo.num_usages; usage_id++) {
+ u16 offset = (usage_id * AXIOM_U31_BYTES_PER_USAGE);
+ u8 id = rx_data[offset + 0];
+ u8 start_page = rx_data[offset + 1];
+ u8 num_pages = rx_data[offset + 2];
+ u32 max_offset = ((rx_data[offset + 3] & AXIOM_PAGE_OFFSET_MASK) + 1) * 2;
+
+ usage_table[id].is_report = !num_pages;
+
+ /* Store the entry into the usage table */
+ usage_table[id].id = id;
+ usage_table[id].start_page = start_page;
+ usage_table[id].num_pages = num_pages;
+
+ dev_dbg(ts->dev, "Usage u%02x Info: %*ph\n", id, AXIOM_U31_BYTES_PER_USAGE,
+ &rx_data[offset]);
+
+ /* Identify the max report length the module will receive */
+ if (usage_table[id].is_report && max_offset > max_report_len)
+ max_report_len = max_offset;
+ }
+
+ ts->usage_table_populated = true;
+
+ return max_report_len;
+}
+
+static int axiom_discover(struct axiom_data *ts)
+{
+ int error;
+
+ /*
+ * Fetch the first page of usage u31 to get the
+ * device information and the number of usages
+ */
+ error = axiom_read(ts, AXIOM_DEVINFO_USAGE_ID, 0, &ts->devinfo, AXIOM_U31_PAGE0_LENGTH);
+ if (error)
+ return error;
+
+ dev_dbg(ts->dev, " Boot Mode : %s\n",
+ FIELD_GET(AXIOM_U31_BOOTMODE_MASK,
+ le16_to_cpu(ts->devinfo.device_id)) ? "BLP" : "TCP");
+ dev_dbg(ts->dev, " Device ID : %04lx\n",
+ FIELD_GET(AXIOM_U31_DEVID_MASK, le16_to_cpu(ts->devinfo.device_id)));
+ dev_dbg(ts->dev, " Firmware Rev : %02x.%02x\n", ts->devinfo.fw_major,
+ ts->devinfo.fw_minor);
+ dev_dbg(ts->dev, " Bootloader Rev : %02x.%02x\n", ts->devinfo.bootloader_fw_major,
+ ts->devinfo.bootloader_fw_minor);
+ dev_dbg(ts->dev, " FW Extra Info : %04x\n", ts->devinfo.fw_info_extra);
+ dev_dbg(ts->dev, " Silicon : %04x\n", le16_to_cpu(ts->devinfo.jedec_id));
+ dev_dbg(ts->dev, " Number usages : %04x\n", ts->devinfo.num_usages);
+
+ ts->max_report_len = axiom_populate_usage_table(ts);
+ if (!ts->max_report_len || !ts->devinfo.num_usages ||
+ ts->max_report_len > AXIOM_MAX_REPORT_LEN) {
+ dev_err(ts->dev, "Invalid report length or usages number");
+ return -EINVAL;
+ }
+
+ dev_dbg(ts->dev, "Max Report Length: %u\n", ts->max_report_len);
+
+ return 0;
+}
+
+/*
+ * Support function to axiom_process_u41_report.
+ * Generates input-subsystem events for every target.
+ * After calling this function the caller shall issue
+ * a Sync to the input sub-system.
+ */
+static bool axiom_process_u41_report_target(struct axiom_data *ts,
+ struct axiom_target_report *target)
+{
+ struct input_dev *input_dev = ts->input_dev;
+ struct axiom_u41_target *target_prev_state;
+ enum axiom_target_state current_state;
+ int id;
+
+ /* Verify the target index */
+ if (target->index >= AXIOM_U41_MAX_TARGETS) {
+ dev_err(ts->dev, "Invalid target index! %u\n", target->index);
+ return false;
+ }
+
+ target_prev_state = &ts->targets[target->index];
+
+ current_state = AXIOM_TARGET_STATE_NOT_PRESENT;
+
+ if (target->present) {
+ if (target->z >= 0)
+ current_state = AXIOM_TARGET_STATE_TOUCHING;
+ else if (target->z > AXIOM_PROX_LEVEL && target->z < 0)
+ current_state = AXIOM_TARGET_STATE_HOVER;
+ else if (target->z == AXIOM_PROX_LEVEL)
+ current_state = AXIOM_TARGET_STATE_PROX;
+ }
+
+ if (target_prev_state->state == current_state &&
+ target_prev_state->x == target->x &&
+ target_prev_state->y == target->y &&
+ target_prev_state->z == target->z)
+ return false;
+
+ id = target->index;
+
+ dev_dbg(ts->dev, "U41 Target T%u, present:%u, x:%u, y:%u, z:%d\n",
+ target->index, target->present,
+ target->x, target->y, target->z);
+
+ switch (current_state) {
+ case AXIOM_TARGET_STATE_NOT_PRESENT:
+ case AXIOM_TARGET_STATE_PROX:
+ if (!target_prev_state->insert)
+ break;
+ target_prev_state->insert = false;
+
+ if (!id)
+ input_report_key(input_dev, BTN_TOUCH, 0);
+
+ input_mt_report_slot_inactive(input_dev);
+ /*
+ * make sure the previous coordinates are
+ * all off screen when the finger comes back
+ */
+ target->x = 65535;
+ target->y = 65535;
+ target->z = AXIOM_PROX_LEVEL;
+ break;
+ case AXIOM_TARGET_STATE_HOVER:
+ case AXIOM_TARGET_STATE_TOUCHING:
+ target_prev_state->insert = true;
+ input_report_abs(input_dev, ABS_MT_TRACKING_ID, id);
+ input_report_abs(input_dev, ABS_MT_POSITION_X, target->x);
+ input_report_abs(input_dev, ABS_MT_POSITION_Y, target->y);
+
+ if (current_state == AXIOM_TARGET_STATE_TOUCHING) {
+ input_report_abs(input_dev, ABS_MT_DISTANCE, 0);
+ input_report_abs(input_dev, ABS_DISTANCE, 0);
+ input_report_abs(input_dev, ABS_MT_PRESSURE, target->z);
+ input_report_abs(input_dev, ABS_PRESSURE, target->z);
+ } else {
+ input_report_abs(input_dev, ABS_MT_DISTANCE, -target->z);
+ input_report_abs(input_dev, ABS_DISTANCE, -target->z);
+ input_report_abs(input_dev, ABS_MT_PRESSURE, 0);
+ input_report_abs(input_dev, ABS_PRESSURE, 0);
+ }
+
+ if (!id)
+ input_report_key(input_dev, BTN_TOUCH, (current_state ==
+ AXIOM_TARGET_STATE_TOUCHING));
+ break;
+ default:
+ break;
+ }
+
+ target_prev_state->state = current_state;
+ target_prev_state->x = target->x;
+ target_prev_state->y = target->y;
+ target_prev_state->z = target->z;
+
+ return true;
+}
+
+/*
+ * U41 is the output report of the 2D CTS and contains the status of targets
+ * (including contacts and pre-contacts) along with their X,Y,Z values.
+ * When a target has been removed (no longer detected),
+ * the corresponding X,Y,Z values will be zeroed.
+ */
+static bool axiom_process_u41_report(struct axiom_data *ts, u8 *rx_buf)
+{
+ struct axiom_target_report target;
+ bool update_done = false;
+ u16 target_status;
+ int i;
+
+ target_status = get_unaligned_le16(rx_buf + 1);
+
+ for (i = 0; i < AXIOM_U41_MAX_TARGETS; i++) {
+ u8 *target_step = &rx_buf[i * 4];
+
+ target.index = i;
+ input_mt_slot(ts->input_dev, i);
+ input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true);
+ target.present = ((target_status & (1 << i)) != 0) ? 1 : 0;
+ target.x = get_unaligned_le16(target_step + 3);
+ target.y = get_unaligned_le16(target_step + 5);
+ target.z = (s8)(rx_buf[i + 43]);
+ touchscreen_report_pos(ts->input_dev, &ts->prop, target.x, target.y, true);
+ update_done |= axiom_process_u41_report_target(ts, &target);
+ }
+
+ return update_done;
+}
+
+/*
+ * U46 report contains a low level measurement data generated by the capacitive
+ * displacement sensor (CDS) algorithms from the auxiliary channels.
+ * This information is useful when tuning multi-press to assess mechanical
+ * consistency in the unit's construction.
+ */
+static void axiom_process_u46_report(struct axiom_data *ts, u8 *rx_buf)
+{
+ struct input_dev *input_dev = ts->input_dev;
+ u32 event_value;
+ u16 aux_value;
+ int i;
+
+ for (i = 0; i < AXIOM_U46_AUX_CHANNELS; i++) {
+ u8 *target_step = &rx_buf[i * 2];
+
+ input_mt_slot(input_dev, i);
+ input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, true);
+ aux_value = get_unaligned_le16(target_step + 1) & AXIOM_U46_AUX_MASK;
+ event_value = (i << 16) | (aux_value);
+ input_event(input_dev, EV_MSC, MSC_RAW, event_value);
+ }
+}
+
+/*
+ * Validates the crc and demultiplexes the axiom reports to the appropriate
+ * report handler
+ */
+static int axiom_handle_events(struct axiom_data *ts)
+{
+ struct input_dev *input_dev = ts->input_dev;
+ u8 *report_data = ts->rx_buf;
+ struct device *dev = ts->dev;
+ u16 crc_report;
+ u8 *crc_bytes;
+ u16 crc_calc;
+ int error;
+ u8 len;
+
+ error = axiom_read(ts, AXIOM_REPORT_USAGE_ID, 0, report_data, ts->max_report_len);
+ if (error)
+ return error;
+
+ len = (report_data[0] & AXIOM_COMMS_REPORT_LEN_MASK) << 1;
+ if (len <= 2) {
+ dev_err(dev, "Zero length report discarded.\n");
+ return -ENODATA;
+ }
+
+ /* Validate the report CRC */
+ crc_bytes = &report_data[len];
+
+ crc_report = get_unaligned_le16(crc_bytes - 2);
+ /* Length is in 16 bit words and remove the size of the CRC16 itself */
+ crc_calc = crc16(0, report_data, (len - 2));
+
+ if (crc_calc != crc_report) {
+ dev_err(dev,
+ "CRC mismatch! Expected: %#x, Calculated CRC: %#x.\n",
+ crc_report, crc_calc);
+ return -EINVAL;
+ }
+
+ switch (report_data[1]) {
+ case AXIOM_USAGE_2DCTS_REPORT_ID:
+ if (axiom_process_u41_report(ts, &report_data[1])) {
+ input_mt_sync_frame(input_dev);
+ input_sync(input_dev);
+ }
+ break;
+
+ case AXIOM_USAGE_2AUX_REPORT_ID:
+ /* This is an aux report (force) */
+ axiom_process_u46_report(ts, &report_data[1]);
+ input_mt_sync(input_dev);
+ input_sync(input_dev);
+ break;
+
+ case AXIOM_USAGE_2HB_REPORT_ID:
+ /* This is a heartbeat report */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void axiom_i2c_poll(struct input_dev *input_dev)
+{
+ struct axiom_data *ts = input_get_drvdata(input_dev);
+
+ axiom_handle_events(ts);
+}
+
+static irqreturn_t axiom_irq(int irq, void *dev_id)
+{
+ struct axiom_data *ts = dev_id;
+
+ axiom_handle_events(ts);
+
+ return IRQ_HANDLED;
+}
+
+static void axiom_reset(struct gpio_desc *reset_gpio)
+{
+ gpiod_set_value_cansleep(reset_gpio, 1);
+ usleep_range(1000, 2000);
+ gpiod_set_value_cansleep(reset_gpio, 0);
+ msleep(AXIOM_STARTUP_TIME_MS);
+}
+
+static int axiom_i2c_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct input_dev *input_dev;
+ struct axiom_data *ts;
+ u32 poll_interval;
+ int target;
+ int error;
+
+ ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
+ if (!ts)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, ts);
+ ts->client = client;
+ ts->dev = dev;
+
+ ts->regmap = devm_regmap_init_i2c(client, &axiom_i2c_regmap_config);
+ error = PTR_ERR_OR_ZERO(ts->regmap);
+ if (error) {
+ dev_err(dev, "Failed to initialize regmap: %d\n", error);
+ return error;
+ }
+
+ error = devm_regulator_get_enable(dev, "vddi");
+ if (error)
+ return dev_err_probe(&client->dev, error,
+ "Failed to enable VDDI regulator\n");
+
+ error = devm_regulator_get_enable(dev, "vdda");
+ if (error)
+ return dev_err_probe(&client->dev, error,
+ "Failed to enable VDDA regulator\n");
+
+ ts->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(ts->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(ts->reset_gpio), "failed to get reset GPIO\n");
+
+ if (ts->reset_gpio)
+ axiom_reset(ts->reset_gpio);
+ else
+ msleep(AXIOM_STARTUP_TIME_MS);
+
+ error = axiom_discover(ts);
+ if (error)
+ return dev_err_probe(dev, error, "Failed touchscreen discover\n");
+
+ input_dev = devm_input_allocate_device(ts->dev);
+ if (!input_dev)
+ return -ENOMEM;
+
+ input_dev->name = "TouchNetix axiom Touchscreen";
+ input_dev->phys = "input/axiom_ts";
+
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, 65535, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, 65535, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_DISTANCE, 0, 127, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 127, 0, 0);
+
+ touchscreen_parse_properties(input_dev, true, &ts->prop);
+
+ /* Registers the axiom device as a touchscreen instead of a mouse pointer */
+ error = input_mt_init_slots(input_dev, AXIOM_U41_MAX_TARGETS, INPUT_MT_DIRECT);
+ if (error)
+ return error;
+
+ /* Enables the raw data for up to 4 force channels to be sent to the input subsystem */
+ set_bit(EV_REL, input_dev->evbit);
+ set_bit(EV_MSC, input_dev->evbit);
+ /* Declare that we support "RAW" Miscellaneous events */
+ set_bit(MSC_RAW, input_dev->mscbit);
+
+ ts->input_dev = input_dev;
+ input_set_drvdata(ts->input_dev, ts);
+
+ /* Ensure that all reports are initialised to not be present. */
+ for (target = 0; target < AXIOM_U41_MAX_TARGETS; target++)
+ ts->targets[target].state = AXIOM_TARGET_STATE_NOT_PRESENT;
+
+ error = devm_request_threaded_irq(dev, client->irq, NULL,
+ axiom_irq, IRQF_ONESHOT, dev_name(dev), ts);
+ if (error) {
+ dev_info(dev, "Request irq failed, falling back to polling mode");
+
+ error = input_setup_polling(input_dev, axiom_i2c_poll);
+ if (error)
+ return dev_err_probe(ts->dev, error, "Unable to set up polling mode\n");
+
+ if (!device_property_read_u32(ts->dev, "poll-interval", &poll_interval))
+ input_set_poll_interval(input_dev, poll_interval);
+ else
+ input_set_poll_interval(input_dev, POLL_INTERVAL_DEFAULT_MS);
+ }
+
+ return input_register_device(input_dev);
+}
+
+static const struct i2c_device_id axiom_i2c_id_table[] = {
+ { "ax54a" },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, axiom_i2c_id_table);
+
+static const struct of_device_id axiom_i2c_of_match[] = {
+ { .compatible = "touchnetix,ax54a", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, axiom_i2c_of_match);
+
+static struct i2c_driver axiom_i2c_driver = {
+ .driver = {
+ .name = "axiom",
+ .of_match_table = axiom_i2c_of_match,
+ },
+ .id_table = axiom_i2c_id_table,
+ .probe = axiom_i2c_probe,
+};
+module_i2c_driver(axiom_i2c_driver);
+
+MODULE_AUTHOR("Bart Prescott <bartp@baasheep.co.uk>");
+MODULE_AUTHOR("Pedro Torruella <pedro.torruella@touchnetix.com>");
+MODULE_AUTHOR("Mark Satterthwaite <mark.satterthwaite@touchnetix.com>");
+MODULE_AUTHOR("Hannah Rossiter <hannah.rossiter@touchnetix.com>");
+MODULE_AUTHOR("Kamel Bouhara <kamel.bouhara@bootlin.com>");
+MODULE_DESCRIPTION("TouchNetix axiom touchscreen I2C bus driver");
+MODULE_LICENSE("GPL");
--
2.25.1
^ permalink raw reply related
* Re: [PATCH v1 2/2] hid-asus: change the report_id used for HID LED control
From: Luke Jones @ 2024-05-29 9:27 UTC (permalink / raw)
To: Hans de Goede, Jiri Kosina
Cc: Ilpo Järvinen, corentin.chary, platform-driver-x86,
linux-kernel, linux-input, bentiss
In-Reply-To: <ea85df16-53b4-4e25-b7c7-3f74486cdcf9@redhat.com>
On Wed, 29 May 2024, at 8:25 PM, Hans de Goede wrote:
> Hi,
>
> On 5/29/24 3:28 AM, Luke D. Jones wrote:
> > On some laptops the report_id used for LED brightness control must be
> > 0x5D instead of 0x5A.
> >
> > Signed-off-by: Luke D. Jones <luke@ljones.dev>
> > ---
> > drivers/hid/hid-asus.c | 26 +++++++++++++++++++++++++-
> > 1 file changed, 25 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
> > index 4cba8e143031..ec3556cc4eef 100644
> > --- a/drivers/hid/hid-asus.c
> > +++ b/drivers/hid/hid-asus.c
> > @@ -94,6 +94,8 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
> >
> > #define TRKID_SGN ((TRKID_MAX + 1) >> 1)
> >
> > +static const char * const use_alt_led_report_id[] = { "GU605", "GA403" };
> > +
> > struct asus_kbd_leds {
> > struct led_classdev cdev;
> > struct hid_device *hdev;
> > @@ -101,6 +103,7 @@ struct asus_kbd_leds {
> > unsigned int brightness;
> > spinlock_t lock;
> > bool removed;
> > + int report_id;
> > };
> >
> > struct asus_touchpad_info {
> > @@ -473,7 +476,7 @@ static enum led_brightness asus_kbd_backlight_get(struct led_classdev *led_cdev)
> > static void asus_kbd_backlight_work(struct work_struct *work)
> > {
> > struct asus_kbd_leds *led = container_of(work, struct asus_kbd_leds, work);
> > - u8 buf[] = { FEATURE_KBD_REPORT_ID, 0xba, 0xc5, 0xc4, 0x00 };
> > + u8 buf[] = { led->report_id, 0xba, 0xc5, 0xc4, 0x00 };
> > int ret;
> > unsigned long flags;
> >
> > @@ -513,6 +516,23 @@ static bool asus_kbd_wmi_led_control_present(struct hid_device *hdev)
> > return !!(value & ASUS_WMI_DSTS_PRESENCE_BIT);
> > }
> >
> > +static bool asus_kbd_is_input_led(void)
> > +{
> > + const char *product;
> > + int i;
> > +
> > + product = dmi_get_system_info(DMI_PRODUCT_NAME);
> > + if (!product)
> > + return false;
> > +
> > + for (i = 0; i < ARRAY_SIZE(use_alt_led_report_id); i++) {
> > + if (strstr(product, use_alt_led_report_id[i]))
> > + return true;
> > + }
> > +
> > + return false;
> > +}
> > +
>
>
> This again feels like you are re-inventing dmi_check_system() please
> change use_alt_led_report_id to a dmi_system_id array and drop this
> function (you can directly call dmi_check_system(use_alt_led_report_id)
> instead).
I wasn't actually aware of that, so i guess I've learned a whole lot of new stuff today :)
Thanks mate, I'll get round to a v2 in the next few days.
>
> > static int asus_kbd_register_leds(struct hid_device *hdev)
> > {
> > struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
> > @@ -555,6 +575,10 @@ static int asus_kbd_register_leds(struct hid_device *hdev)
> > if (!drvdata->kbd_backlight)
> > return -ENOMEM;
> >
> > + drvdata->kbd_backlight->report_id = FEATURE_KBD_REPORT_ID;
> > + if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD && asus_kbd_is_input_led())
> > + drvdata->kbd_backlight->report_id = FEATURE_KBD_LED_REPORT_ID1;
> > +
> > drvdata->kbd_backlight->removed = false;
> > drvdata->kbd_backlight->brightness = 0;
> > drvdata->kbd_backlight->hdev = hdev;
>
> Regards,
>
> Hans
>
>
>
^ permalink raw reply
* [PATCH] HID: uclogic: avoid linking common code into multiple modules
From: Arnd Bergmann @ 2024-05-29 9:48 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires
Cc: Arnd Bergmann, Rahul Rameshbabu, Fabio Baltieri, Ivan Gorinov,
Johannes Roith, José Expósito, linux-input,
linux-kernel
From: Arnd Bergmann <arnd@arndb.de>
The hid-uclogic-params.o and hid-uclogic-rdesc.o files are linked
into both the driver module and the unit test, which triggers a
W=1 warning:
scripts/Makefile.build:236: drivers/hid/Makefile: hid-uclogic-rdesc.o is added to multiple modules: hid-uclogic hid-uclogic-test
scripts/Makefile.build:236: drivers/hid/Makefile: hid-uclogic-params.o is added to multiple modules: hid-uclogic hid-uclogic-test
Avoids this by moving these two files into a separate module
that is used by the driver and the unit test.
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
---
I have made patches for all such warnings in the tree, this is one I'm not
sure about, maybe there is a better fix.
---
drivers/hid/Makefile | 12 ++----
drivers/hid/hid-uclogic-params.c | 8 ++++
drivers/hid/hid-uclogic-rdesc.c | 72 ++++++++++++++++++++++++++++++++
3 files changed, 84 insertions(+), 8 deletions(-)
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index ce71b53ea6c5..864dfbae8ace 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -133,10 +133,8 @@ obj-$(CONFIG_HID_TOPSEED) += hid-topseed.o
obj-$(CONFIG_HID_TOPRE) += hid-topre.o
obj-$(CONFIG_HID_TWINHAN) += hid-twinhan.o
obj-$(CONFIG_HID_U2FZERO) += hid-u2fzero.o
-hid-uclogic-objs := hid-uclogic-core.o \
- hid-uclogic-rdesc.o \
- hid-uclogic-params.o
-obj-$(CONFIG_HID_UCLOGIC) += hid-uclogic.o
+hid-uclogic-objs := hid-uclogic-core.o
+obj-$(CONFIG_HID_UCLOGIC) += hid-uclogic.o hid-uclogic-rdesc.o hid-uclogic-params.o
obj-$(CONFIG_HID_UDRAW_PS3) += hid-udraw-ps3.o
obj-$(CONFIG_HID_LED) += hid-led.o
obj-$(CONFIG_HID_XIAOMI) += hid-xiaomi.o
@@ -154,10 +152,8 @@ obj-$(CONFIG_HID_WINWING) += hid-winwing.o
obj-$(CONFIG_HID_SENSOR_HUB) += hid-sensor-hub.o
obj-$(CONFIG_HID_SENSOR_CUSTOM_SENSOR) += hid-sensor-custom.o
-hid-uclogic-test-objs := hid-uclogic-rdesc.o \
- hid-uclogic-params.o \
- hid-uclogic-rdesc-test.o
-obj-$(CONFIG_HID_KUNIT_TEST) += hid-uclogic-test.o
+hid-uclogic-test-objs := hid-uclogic-rdesc-test.o
+obj-$(CONFIG_HID_KUNIT_TEST) += hid-uclogic-test.o hid-uclogic-params.o hid-uclogic-params.o
obj-$(CONFIG_USB_HID) += usbhid/
obj-$(CONFIG_USB_MOUSE) += usbhid/
diff --git a/drivers/hid/hid-uclogic-params.c b/drivers/hid/hid-uclogic-params.c
index 5bab006ec165..97ae7e4f61e1 100644
--- a/drivers/hid/hid-uclogic-params.c
+++ b/drivers/hid/hid-uclogic-params.c
@@ -133,6 +133,7 @@ void uclogic_params_hid_dbg(const struct hid_device *hdev,
}
hid_dbg(hdev, "}\n");
}
+EXPORT_SYMBOL_GPL(uclogic_params_hid_dbg);
/**
* uclogic_params_get_str_desc - retrieve a string descriptor from a HID
@@ -660,6 +661,7 @@ void uclogic_params_cleanup(struct uclogic_params *params)
memset(params, 0, sizeof(*params));
}
}
+EXPORT_SYMBOL_GPL(uclogic_params_cleanup);
/**
* uclogic_params_get_desc() - Get a replacement report descriptor for a
@@ -732,6 +734,7 @@ int uclogic_params_get_desc(const struct uclogic_params *params,
kfree(desc);
return rc;
}
+EXPORT_SYMBOL_GPL(uclogic_params_get_desc);
/**
* uclogic_params_init_invalid() - initialize tablet interface parameters,
@@ -1859,7 +1862,12 @@ int uclogic_params_init(struct uclogic_params *params,
uclogic_params_cleanup(&p);
return rc;
}
+EXPORT_SYMBOL_GPL(uclogic_params_init);
#ifdef CONFIG_HID_KUNIT_TEST
#include "hid-uclogic-params-test.c"
#endif
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Nikolai Kondrashov");
+MODULE_DESCRIPTION("HID driver for UC-Logic devices tablet initialization and parameter retrieval");
diff --git a/drivers/hid/hid-uclogic-rdesc.c b/drivers/hid/hid-uclogic-rdesc.c
index b6dfdf6356a6..d4f1ee79e0a1 100644
--- a/drivers/hid/hid-uclogic-rdesc.c
+++ b/drivers/hid/hid-uclogic-rdesc.c
@@ -59,9 +59,11 @@ __u8 uclogic_rdesc_wp4030u_fixed_arr[] = {
0xC0, /* End Collection, */
0xC0 /* End Collection */
};
+EXPORT_SYMBOL_GPL(uclogic_rdesc_wp4030u_fixed_arr);
const size_t uclogic_rdesc_wp4030u_fixed_size =
sizeof(uclogic_rdesc_wp4030u_fixed_arr);
+EXPORT_SYMBOL_GPL(uclogic_rdesc_wp4030u_fixed_size);
/* Fixed WP5540U report descriptor */
__u8 uclogic_rdesc_wp5540u_fixed_arr[] = {
@@ -136,9 +138,11 @@ __u8 uclogic_rdesc_wp5540u_fixed_arr[] = {
0xC0, /* End Collection, */
0xC0 /* End Collection */
};
+EXPORT_SYMBOL_GPL(uclogic_rdesc_wp5540u_fixed_arr);
const size_t uclogic_rdesc_wp5540u_fixed_size =
sizeof(uclogic_rdesc_wp5540u_fixed_arr);
+EXPORT_SYMBOL_GPL(uclogic_rdesc_wp5540u_fixed_size);
/* Fixed WP8060U report descriptor */
__u8 uclogic_rdesc_wp8060u_fixed_arr[] = {
@@ -213,9 +217,11 @@ __u8 uclogic_rdesc_wp8060u_fixed_arr[] = {
0xC0, /* End Collection, */
0xC0 /* End Collection */
};
+EXPORT_SYMBOL_GPL(uclogic_rdesc_wp8060u_fixed_arr);
const size_t uclogic_rdesc_wp8060u_fixed_size =
sizeof(uclogic_rdesc_wp8060u_fixed_arr);
+EXPORT_SYMBOL_GPL(uclogic_rdesc_wp8060u_fixed_size);
/* Fixed WP1062 report descriptor */
__u8 uclogic_rdesc_wp1062_fixed_arr[] = {
@@ -261,9 +267,11 @@ __u8 uclogic_rdesc_wp1062_fixed_arr[] = {
0xC0, /* End Collection, */
0xC0 /* End Collection */
};
+EXPORT_SYMBOL_GPL(uclogic_rdesc_wp1062_fixed_arr);
const size_t uclogic_rdesc_wp1062_fixed_size =
sizeof(uclogic_rdesc_wp1062_fixed_arr);
+EXPORT_SYMBOL_GPL(uclogic_rdesc_wp1062_fixed_size);
/* Fixed PF1209 report descriptor */
__u8 uclogic_rdesc_pf1209_fixed_arr[] = {
@@ -338,9 +346,11 @@ __u8 uclogic_rdesc_pf1209_fixed_arr[] = {
0xC0, /* End Collection, */
0xC0 /* End Collection */
};
+EXPORT_SYMBOL_GPL(uclogic_rdesc_pf1209_fixed_arr);
const size_t uclogic_rdesc_pf1209_fixed_size =
sizeof(uclogic_rdesc_pf1209_fixed_arr);
+EXPORT_SYMBOL_GPL(uclogic_rdesc_pf1209_fixed_size);
/* Fixed PID 0522 tablet report descriptor, interface 0 (stylus) */
__u8 uclogic_rdesc_twhl850_fixed0_arr[] = {
@@ -384,9 +394,11 @@ __u8 uclogic_rdesc_twhl850_fixed0_arr[] = {
0xC0, /* End Collection, */
0xC0 /* End Collection */
};
+EXPORT_SYMBOL_GPL(uclogic_rdesc_twhl850_fixed0_arr);
const size_t uclogic_rdesc_twhl850_fixed0_size =
sizeof(uclogic_rdesc_twhl850_fixed0_arr);
+EXPORT_SYMBOL_GPL(uclogic_rdesc_twhl850_fixed0_size);
/* Fixed PID 0522 tablet report descriptor, interface 1 (mouse) */
__u8 uclogic_rdesc_twhl850_fixed1_arr[] = {
@@ -424,9 +436,11 @@ __u8 uclogic_rdesc_twhl850_fixed1_arr[] = {
0xC0, /* End Collection, */
0xC0 /* End Collection */
};
+EXPORT_SYMBOL_GPL(uclogic_rdesc_twhl850_fixed1_arr);
const size_t uclogic_rdesc_twhl850_fixed1_size =
sizeof(uclogic_rdesc_twhl850_fixed1_arr);
+EXPORT_SYMBOL_GPL(uclogic_rdesc_twhl850_fixed1_size);
/* Fixed PID 0522 tablet report descriptor, interface 2 (frame buttons) */
__u8 uclogic_rdesc_twhl850_fixed2_arr[] = {
@@ -450,9 +464,11 @@ __u8 uclogic_rdesc_twhl850_fixed2_arr[] = {
0x80, /* Input, */
0xC0 /* End Collection */
};
+EXPORT_SYMBOL_GPL(uclogic_rdesc_twhl850_fixed2_arr);
const size_t uclogic_rdesc_twhl850_fixed2_size =
sizeof(uclogic_rdesc_twhl850_fixed2_arr);
+EXPORT_SYMBOL_GPL(uclogic_rdesc_twhl850_fixed2_size);
/* Fixed TWHA60 report descriptor, interface 0 (stylus) */
__u8 uclogic_rdesc_twha60_fixed0_arr[] = {
@@ -499,9 +515,11 @@ __u8 uclogic_rdesc_twha60_fixed0_arr[] = {
0xC0, /* End Collection, */
0xC0 /* End Collection */
};
+EXPORT_SYMBOL_GPL(uclogic_rdesc_twha60_fixed0_arr);
const size_t uclogic_rdesc_twha60_fixed0_size =
sizeof(uclogic_rdesc_twha60_fixed0_arr);
+EXPORT_SYMBOL_GPL(uclogic_rdesc_twha60_fixed0_size);
/* Fixed TWHA60 report descriptor, interface 1 (frame buttons) */
__u8 uclogic_rdesc_twha60_fixed1_arr[] = {
@@ -527,9 +545,11 @@ __u8 uclogic_rdesc_twha60_fixed1_arr[] = {
0x81, 0x01, /* Input (Constant), */
0xC0 /* End Collection */
};
+EXPORT_SYMBOL_GPL(uclogic_rdesc_twha60_fixed1_arr);
const size_t uclogic_rdesc_twha60_fixed1_size =
sizeof(uclogic_rdesc_twha60_fixed1_arr);
+EXPORT_SYMBOL_GPL(uclogic_rdesc_twha60_fixed1_size);
/* Fixed report descriptor template for (tweaked) v1 pen reports */
const __u8 uclogic_rdesc_v1_pen_template_arr[] = {
@@ -581,9 +601,11 @@ const __u8 uclogic_rdesc_v1_pen_template_arr[] = {
0xC0, /* End Collection, */
0xC0 /* End Collection */
};
+EXPORT_SYMBOL_GPL(uclogic_rdesc_v1_pen_template_arr);
const size_t uclogic_rdesc_v1_pen_template_size =
sizeof(uclogic_rdesc_v1_pen_template_arr);
+EXPORT_SYMBOL_GPL(uclogic_rdesc_v1_pen_template_size);
/* Fixed report descriptor template for (tweaked) v2 pen reports */
const __u8 uclogic_rdesc_v2_pen_template_arr[] = {
@@ -647,9 +669,11 @@ const __u8 uclogic_rdesc_v2_pen_template_arr[] = {
0xC0, /* End Collection, */
0xC0 /* End Collection */
};
+EXPORT_SYMBOL_GPL(uclogic_rdesc_v2_pen_template_arr);
const size_t uclogic_rdesc_v2_pen_template_size =
sizeof(uclogic_rdesc_v2_pen_template_arr);
+EXPORT_SYMBOL_GPL(uclogic_rdesc_v2_pen_template_size);
/*
* Expand to the contents of a generic frame buttons report descriptor.
@@ -702,16 +726,22 @@ const size_t uclogic_rdesc_v2_pen_template_size =
const __u8 uclogic_rdesc_v1_frame_arr[] = {
UCLOGIC_RDESC_FRAME_BUTTONS_BYTES(UCLOGIC_RDESC_V1_FRAME_ID, 8)
};
+EXPORT_SYMBOL_GPL(uclogic_rdesc_v1_frame_arr);
+
const size_t uclogic_rdesc_v1_frame_size =
sizeof(uclogic_rdesc_v1_frame_arr);
+EXPORT_SYMBOL_GPL(uclogic_rdesc_v1_frame_size);
/* Fixed report descriptor for (tweaked) v2 frame button reports */
const __u8 uclogic_rdesc_v2_frame_buttons_arr[] = {
UCLOGIC_RDESC_FRAME_BUTTONS_BYTES(UCLOGIC_RDESC_V2_FRAME_BUTTONS_ID,
12)
};
+EXPORT_SYMBOL_GPL(uclogic_rdesc_v2_frame_buttons_arr);
+
const size_t uclogic_rdesc_v2_frame_buttons_size =
sizeof(uclogic_rdesc_v2_frame_buttons_arr);
+EXPORT_SYMBOL_GPL(uclogic_rdesc_v2_frame_buttons_size);
/* Fixed report descriptor for (tweaked) v2 frame touch ring reports */
const __u8 uclogic_rdesc_v2_frame_touch_ring_arr[] = {
@@ -758,8 +788,11 @@ const __u8 uclogic_rdesc_v2_frame_touch_ring_arr[] = {
0xC0, /* End Collection, */
0xC0 /* End Collection */
};
+EXPORT_SYMBOL_GPL(uclogic_rdesc_v2_frame_touch_ring_arr);
+
const size_t uclogic_rdesc_v2_frame_touch_ring_size =
sizeof(uclogic_rdesc_v2_frame_touch_ring_arr);
+EXPORT_SYMBOL_GPL(uclogic_rdesc_v2_frame_touch_ring_size);
/* Fixed report descriptor for (tweaked) v2 frame touch strip reports */
const __u8 uclogic_rdesc_v2_frame_touch_strip_arr[] = {
@@ -806,8 +839,11 @@ const __u8 uclogic_rdesc_v2_frame_touch_strip_arr[] = {
0xC0, /* End Collection, */
0xC0 /* End Collection */
};
+EXPORT_SYMBOL_GPL(uclogic_rdesc_v2_frame_touch_strip_arr);
+
const size_t uclogic_rdesc_v2_frame_touch_strip_size =
sizeof(uclogic_rdesc_v2_frame_touch_strip_arr);
+EXPORT_SYMBOL_GPL(uclogic_rdesc_v2_frame_touch_strip_size);
/* Fixed report descriptor for (tweaked) v2 frame dial reports */
const __u8 uclogic_rdesc_v2_frame_dial_arr[] = {
@@ -856,14 +892,22 @@ const __u8 uclogic_rdesc_v2_frame_dial_arr[] = {
0xC0, /* End Collection, */
0xC0 /* End Collection */
};
+EXPORT_SYMBOL_GPL(uclogic_rdesc_v2_frame_dial_arr);
+
const size_t uclogic_rdesc_v2_frame_dial_size =
sizeof(uclogic_rdesc_v2_frame_dial_arr);
+EXPORT_SYMBOL_GPL(uclogic_rdesc_v2_frame_dial_size);
const __u8 uclogic_ugee_v2_probe_arr[] = {
0x02, 0xb0, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
+EXPORT_SYMBOL_GPL(uclogic_ugee_v2_probe_arr);
+
const size_t uclogic_ugee_v2_probe_size = sizeof(uclogic_ugee_v2_probe_arr);
+EXPORT_SYMBOL_GPL(uclogic_ugee_v2_probe_size);
+
const int uclogic_ugee_v2_probe_endpoint = 0x03;
+EXPORT_SYMBOL_GPL(uclogic_ugee_v2_probe_endpoint);
/* Fixed report descriptor template for UGEE v2 pen reports */
const __u8 uclogic_rdesc_ugee_v2_pen_template_arr[] = {
@@ -935,8 +979,11 @@ const __u8 uclogic_rdesc_ugee_v2_pen_template_arr[] = {
0xc0, /* End Collection, */
0xc0, /* End Collection */
};
+EXPORT_SYMBOL_GPL(uclogic_rdesc_ugee_v2_pen_template_arr);
+
const size_t uclogic_rdesc_ugee_v2_pen_template_size =
sizeof(uclogic_rdesc_ugee_v2_pen_template_arr);
+EXPORT_SYMBOL_GPL(uclogic_rdesc_ugee_v2_pen_template_size);
/* Fixed report descriptor template for UGEE v2 frame reports (buttons only) */
const __u8 uclogic_rdesc_ugee_v2_frame_btn_template_arr[] = {
@@ -964,8 +1011,11 @@ const __u8 uclogic_rdesc_ugee_v2_frame_btn_template_arr[] = {
0xC0, /* End Collection, */
0xC0 /* End Collection */
};
+EXPORT_SYMBOL_GPL(uclogic_rdesc_ugee_v2_frame_btn_template_arr);
+
const size_t uclogic_rdesc_ugee_v2_frame_btn_template_size =
sizeof(uclogic_rdesc_ugee_v2_frame_btn_template_arr);
+EXPORT_SYMBOL_GPL(uclogic_rdesc_ugee_v2_frame_btn_template_size);
/* Fixed report descriptor template for UGEE v2 frame reports (dial) */
const __u8 uclogic_rdesc_ugee_v2_frame_dial_template_arr[] = {
@@ -1004,8 +1054,11 @@ const __u8 uclogic_rdesc_ugee_v2_frame_dial_template_arr[] = {
0xC0, /* End Collection, */
0xC0 /* End Collection */
};
+EXPORT_SYMBOL_GPL(uclogic_rdesc_ugee_v2_frame_dial_template_arr);
+
const size_t uclogic_rdesc_ugee_v2_frame_dial_template_size =
sizeof(uclogic_rdesc_ugee_v2_frame_dial_template_arr);
+EXPORT_SYMBOL_GPL(uclogic_rdesc_ugee_v2_frame_dial_template_size);
/* Fixed report descriptor template for UGEE v2 frame reports (mouse) */
const __u8 uclogic_rdesc_ugee_v2_frame_mouse_template_arr[] = {
@@ -1038,8 +1091,11 @@ const __u8 uclogic_rdesc_ugee_v2_frame_mouse_template_arr[] = {
0xC0, /* End Collection, */
0xC0 /* End Collection */
};
+EXPORT_SYMBOL_GPL(uclogic_rdesc_ugee_v2_frame_mouse_template_arr);
+
const size_t uclogic_rdesc_ugee_v2_frame_mouse_template_size =
sizeof(uclogic_rdesc_ugee_v2_frame_mouse_template_arr);
+EXPORT_SYMBOL_GPL(uclogic_rdesc_ugee_v2_frame_mouse_template_size);
/* Fixed report descriptor template for UGEE v2 battery reports */
const __u8 uclogic_rdesc_ugee_v2_battery_template_arr[] = {
@@ -1072,8 +1128,11 @@ const __u8 uclogic_rdesc_ugee_v2_battery_template_arr[] = {
0x81, 0x01, /* Input (Constant), */
0xC0 /* End Collection */
};
+EXPORT_SYMBOL_GPL(uclogic_rdesc_ugee_v2_battery_template_arr);
+
const size_t uclogic_rdesc_ugee_v2_battery_template_size =
sizeof(uclogic_rdesc_ugee_v2_battery_template_arr);
+EXPORT_SYMBOL_GPL(uclogic_rdesc_ugee_v2_battery_template_size);
/* Fixed report descriptor for Ugee EX07 frame */
const __u8 uclogic_rdesc_ugee_ex07_frame_arr[] = {
@@ -1099,8 +1158,11 @@ const __u8 uclogic_rdesc_ugee_ex07_frame_arr[] = {
0xC0, /* End Collection, */
0xC0 /* End Collection */
};
+EXPORT_SYMBOL_GPL(uclogic_rdesc_ugee_ex07_frame_arr);
+
const size_t uclogic_rdesc_ugee_ex07_frame_size =
sizeof(uclogic_rdesc_ugee_ex07_frame_arr);
+EXPORT_SYMBOL_GPL(uclogic_rdesc_ugee_ex07_frame_size);
/* Fixed report descriptor for Ugee G5 frame controls */
const __u8 uclogic_rdesc_ugee_g5_frame_arr[] = {
@@ -1153,8 +1215,10 @@ const __u8 uclogic_rdesc_ugee_g5_frame_arr[] = {
0xC0, /* End Collection, */
0xC0 /* End Collection */
};
+EXPORT_SYMBOL_GPL(uclogic_rdesc_ugee_g5_frame_arr);
const size_t uclogic_rdesc_ugee_g5_frame_size =
sizeof(uclogic_rdesc_ugee_g5_frame_arr);
+EXPORT_SYMBOL_GPL(uclogic_rdesc_ugee_g5_frame_size);
/* Fixed report descriptor for XP-Pen Deco 01 frame controls */
const __u8 uclogic_rdesc_xppen_deco01_frame_arr[] = {
@@ -1187,9 +1251,11 @@ const __u8 uclogic_rdesc_xppen_deco01_frame_arr[] = {
0xC0, /* End Collection, */
0xC0 /* End Collection */
};
+EXPORT_SYMBOL_GPL(uclogic_rdesc_xppen_deco01_frame_arr);
const size_t uclogic_rdesc_xppen_deco01_frame_size =
sizeof(uclogic_rdesc_xppen_deco01_frame_arr);
+EXPORT_SYMBOL_GPL(uclogic_rdesc_xppen_deco01_frame_size);
/**
* uclogic_rdesc_template_apply() - apply report descriptor parameters to a
@@ -1242,3 +1308,9 @@ __u8 *uclogic_rdesc_template_apply(const __u8 *template_ptr,
return rdesc_ptr;
}
+EXPORT_SYMBOL_GPL(uclogic_rdesc_template_apply);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Martin Rusko");
+MODULE_AUTHOR("Nikolai Kondrashov");
+MODULE_DESCRIPTION("HID driver for UC-Logic devices original and fixed report descriptors");
--
2.39.2
^ permalink raw reply related
* [PATCH V1 4/5] Add aw963xx series related interfaces to the aw_sar driver.
From: wangshuaijie @ 2024-05-29 13:06 UTC (permalink / raw)
To: dmitry.torokhov, robh, krzk+dt, conor+dt, jeff, linux-input,
devicetree, linux-kernel
Cc: wangshuaijie, liweilei, kangjiajun
In-Reply-To: <20240529130608.783624-1-wangshuaijie@awinic.com>
From: shuaijie wang <wangshuaijie@awinic.com>
Signed-off-by: shuaijie wang <wangshuaijie@awinic.com>
---
drivers/input/misc/aw_sar/aw963xx/aw963xx.c | 986 ++++++++++++++++++++
drivers/input/misc/aw_sar/aw963xx/aw963xx.h | 749 +++++++++++++++
2 files changed, 1735 insertions(+)
create mode 100644 drivers/input/misc/aw_sar/aw963xx/aw963xx.c
create mode 100644 drivers/input/misc/aw_sar/aw963xx/aw963xx.h
diff --git a/drivers/input/misc/aw_sar/aw963xx/aw963xx.c b/drivers/input/misc/aw_sar/aw963xx/aw963xx.c
new file mode 100644
index 000000000000..4105756749d7
--- /dev/null
+++ b/drivers/input/misc/aw_sar/aw963xx/aw963xx.c
@@ -0,0 +1,986 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AWINIC sar sensor driver (aw963xx)
+ *
+ * Author: Shuaijie Wang<wangshuaijie@awinic.com>
+ *
+ * Copyright (c) 2024 awinic Technology CO., LTD
+ */
+#include "aw963xx.h"
+#include "../aw_sar.h"
+
+#define AW963XX_I2C_NAME "aw963xx_sar"
+
+static void aw963xx_set_cs_as_irq(struct aw_sar *p_sar, int flag);
+static void aw963xx_get_ref_ch_enable(struct aw_sar *p_sar);
+
+static int32_t aw963xx_read_init_over_irq(void *load_bin_para)
+{
+ struct aw_sar *p_sar = (struct aw_sar *)load_bin_para;
+ uint32_t cnt = 1000;
+ uint32_t reg;
+ int32_t ret;
+
+ while (cnt--) {
+ ret = aw_sar_i2c_read(p_sar->i2c, REG_IRQSRC, ®);
+ if (ret != 0) {
+ dev_err(p_sar->dev, "i2c error %d", ret);
+ return ret;
+ }
+ if ((reg & 0x01) == 0x01) {
+ aw_sar_i2c_read(p_sar->i2c, REG_FWVER, ®);
+ return 0;
+ }
+ mdelay(1);
+ }
+
+ aw_sar_i2c_read(p_sar->i2c, REG_FWVER, ®);
+
+ return -EINVAL;
+}
+
+static void aw963xx_convert_little_endian_2_big_endian(struct aw_bin *aw_bin)
+{
+ uint32_t start_index = aw_bin->header_info[0].valid_data_addr;
+ uint32_t fw_len = aw_bin->header_info[0].reg_num;
+ uint32_t uints = fw_len / AW963XX_SRAM_UPDATE_ONE_UINT_SIZE;
+ uint8_t tmp1;
+ uint8_t tmp2;
+ uint8_t tmp3;
+ uint8_t tmp4;
+ int i;
+
+ for (i = 0; i < uints; i++) {
+ tmp1 = aw_bin->info.data[start_index + i * AW963XX_SRAM_UPDATE_ONE_UINT_SIZE + 3];
+ tmp2 = aw_bin->info.data[start_index + i * AW963XX_SRAM_UPDATE_ONE_UINT_SIZE + 2];
+ tmp3 = aw_bin->info.data[start_index + i * AW963XX_SRAM_UPDATE_ONE_UINT_SIZE + 1];
+ tmp4 = aw_bin->info.data[start_index + i * AW963XX_SRAM_UPDATE_ONE_UINT_SIZE];
+ aw_bin->info.data[start_index + i * AW963XX_SRAM_UPDATE_ONE_UINT_SIZE] = tmp1;
+ aw_bin->info.data[start_index + i * AW963XX_SRAM_UPDATE_ONE_UINT_SIZE + 1] = tmp2;
+ aw_bin->info.data[start_index + i * AW963XX_SRAM_UPDATE_ONE_UINT_SIZE + 2] = tmp3;
+ aw_bin->info.data[start_index + i * AW963XX_SRAM_UPDATE_ONE_UINT_SIZE + 3] = tmp4;
+ }
+}
+
+/**
+ * @brief |----------------code ram-----------------|
+ * 0x2000 0x4fff
+ * |--- app wrote here ---|--fill with 0xff--|
+ *
+ * if the size of app is less than the size of code ram, the rest of the
+ * ram is filled with 0xff.
+ * @param load_bin_para
+ * @param offset the rear addr of app
+ * @return int32_t
+ */
+static int32_t aw963xx_sram_fill_not_wrote_area(void *load_bin_para, uint32_t offset)
+{
+ uint32_t last_pack_len = (AW963XX_SRAM_END_ADDR - offset) %
+ AW963XX_SRAM_UPDATE_ONE_PACK_SIZE;
+ uint32_t pack_cnt = last_pack_len == 0 ?
+ ((AW963XX_SRAM_END_ADDR - offset) / AW963XX_SRAM_UPDATE_ONE_PACK_SIZE) :
+ ((AW963XX_SRAM_END_ADDR - offset) / AW963XX_SRAM_UPDATE_ONE_PACK_SIZE) + 1;
+ uint8_t buf[AW963XX_SRAM_UPDATE_ONE_PACK_SIZE + 2] = { 0 };
+ struct aw_sar *p_sar = (struct aw_sar *)load_bin_para;
+ uint32_t download_addr_with_ofst;
+ uint8_t *r_buf;
+ int32_t ret;
+ uint32_t i;
+
+ r_buf = devm_kzalloc(p_sar->dev, AW963XX_SRAM_UPDATE_ONE_PACK_SIZE, GFP_KERNEL);
+ if (!r_buf)
+ return -ENOMEM;
+
+ memset(buf, 0xff, sizeof(buf));
+ for (i = 0; i < pack_cnt; i++) {
+ memset(r_buf, 0, AW963XX_SRAM_UPDATE_ONE_PACK_SIZE);
+ download_addr_with_ofst = offset + i * AW963XX_SRAM_UPDATE_ONE_PACK_SIZE;
+ buf[0] = (uint8_t)(download_addr_with_ofst >> OFFSET_BIT_8);
+ buf[1] = (uint8_t)(download_addr_with_ofst);
+ if (i != (pack_cnt - 1)) {
+ ret = aw_sar_i2c_write_seq(p_sar->i2c, buf,
+ AW963XX_SRAM_UPDATE_ONE_PACK_SIZE + 2);
+ if (ret != 0) {
+ dev_err(p_sar->dev, "cnt%d, write_seq error!", i);
+ devm_kfree(p_sar->dev, r_buf);
+ return ret;
+ }
+ ret = aw_sar_i2c_read_seq(p_sar->i2c, buf, 2, r_buf,
+ AW963XX_SRAM_UPDATE_ONE_PACK_SIZE);
+ if (ret != 0) {
+ dev_err(p_sar->dev, "cnt%d, read_seq error!", i);
+ devm_kfree(p_sar->dev, r_buf);
+ return ret;
+ }
+ if (memcmp(&buf[2], r_buf, AW963XX_SRAM_UPDATE_ONE_PACK_SIZE) != 0) {
+ dev_err(p_sar->dev, "read is not equal to write ");
+ devm_kfree(p_sar->dev, r_buf);
+ return -EINVAL;
+ }
+ } else {
+ ret = aw_sar_i2c_write_seq(p_sar->i2c, buf, last_pack_len + 2);
+ if (ret != 0) {
+ dev_err(p_sar->dev, "cnt%d, write_seq error!", i);
+ devm_kfree(p_sar->dev, r_buf);
+ return ret;
+ }
+ ret = aw_sar_i2c_read_seq(p_sar->i2c, buf, 2, r_buf, last_pack_len);
+ if (ret != 0) {
+ dev_err(p_sar->dev, "cnt%d, read_seq error!", i);
+ devm_kfree(p_sar->dev, r_buf);
+ return ret;
+ }
+ if (memcmp(&buf[2], r_buf, last_pack_len) != 0) {
+ dev_err(p_sar->dev, "read is not equal to write ");
+ devm_kfree(p_sar->dev, r_buf);
+ return -EINVAL;
+ }
+ }
+ }
+
+ devm_kfree(p_sar->dev, r_buf);
+
+ return 0;
+}
+
+static int32_t aw963xx_sram_data_write(struct aw_bin *aw_bin, void *load_bin_para)
+{
+ uint8_t buf[AW963XX_SRAM_UPDATE_ONE_PACK_SIZE + 2] = { 0 };
+ uint32_t start_index = aw_bin->header_info[0].valid_data_addr;
+ uint32_t fw_bin_version = aw_bin->header_info[0].app_version;
+ uint32_t download_addr = AW963XX_RAM_START_ADDR;
+ uint32_t fw_len = aw_bin->header_info[0].reg_num;
+ uint32_t last_pack_len = fw_len % AW963XX_SRAM_UPDATE_ONE_PACK_SIZE;
+ struct aw_sar *p_sar = (struct aw_sar *)load_bin_para;
+ uint32_t download_addr_with_ofst = 0;
+ uint32_t pack_cnt;
+ uint8_t *r_buf;
+ int32_t ret;
+ uint32_t i;
+
+ r_buf = devm_kzalloc(p_sar->dev, AW963XX_SRAM_UPDATE_ONE_PACK_SIZE, GFP_KERNEL);
+ if (!r_buf)
+ return -ENOMEM;
+
+ pack_cnt = ((fw_len % AW963XX_SRAM_UPDATE_ONE_PACK_SIZE) == 0) ?
+ (fw_len / AW963XX_SRAM_UPDATE_ONE_PACK_SIZE) :
+ (fw_len / AW963XX_SRAM_UPDATE_ONE_PACK_SIZE) + 1;
+
+ dev_info(p_sar->dev, "fw_bin_version = 0x%x", fw_bin_version);
+ for (i = 0; i < pack_cnt; i++) {
+ memset(r_buf, 0, AW963XX_SRAM_UPDATE_ONE_PACK_SIZE);
+ download_addr_with_ofst = download_addr + i * AW963XX_SRAM_UPDATE_ONE_PACK_SIZE;
+ buf[0] = (uint8_t)(download_addr_with_ofst >> OFFSET_BIT_8);
+ buf[1] = (uint8_t)(download_addr_with_ofst);
+ if (i != (pack_cnt - 1)) {
+ memcpy(&buf[2], &aw_bin->info.data[start_index +
+ i * AW963XX_SRAM_UPDATE_ONE_PACK_SIZE],
+ AW963XX_SRAM_UPDATE_ONE_PACK_SIZE);
+ ret = aw_sar_i2c_write_seq(p_sar->i2c, buf,
+ AW963XX_SRAM_UPDATE_ONE_PACK_SIZE + 2);
+ if (ret != 0) {
+ dev_err(p_sar->dev, "cnt%d, write_seq error!", i);
+ goto err_out;
+ }
+ ret = aw_sar_i2c_read_seq(p_sar->i2c, buf, 2, r_buf,
+ AW963XX_SRAM_UPDATE_ONE_PACK_SIZE);
+ if (ret != 0) {
+ dev_err(p_sar->dev, "cnt%d, read_seq error!", i);
+ goto err_out;
+ }
+ if (memcmp(&buf[2], r_buf, AW963XX_SRAM_UPDATE_ONE_PACK_SIZE) != 0) {
+ dev_err(p_sar->dev, "read is not equal to write ");
+ ret = -EIO;
+ goto err_out;
+ }
+ } else { // last pack process
+ memcpy(&buf[2], &aw_bin->info.data[start_index +
+ i * AW963XX_SRAM_UPDATE_ONE_PACK_SIZE], last_pack_len);
+ ret = aw_sar_i2c_write_seq(p_sar->i2c, buf, last_pack_len + 2);
+ if (ret != 0) {
+ dev_err(p_sar->dev, "cnt%d, write_seq error!", i);
+ goto err_out;
+ }
+ ret = aw_sar_i2c_read_seq(p_sar->i2c, buf, 2, r_buf, last_pack_len);
+ if (ret != 0) {
+ dev_err(p_sar->dev, "cnt%d, read_seq error!", i);
+ goto err_out;
+ }
+ if (memcmp(&buf[2], r_buf, last_pack_len) != 0) {
+ dev_err(p_sar->dev, "read is not equal to write ");
+ ret = -EIO;
+ goto err_out;
+ }
+ /* fill 0xff in the area that not worte. */
+ ret = aw963xx_sram_fill_not_wrote_area(load_bin_para,
+ download_addr_with_ofst + last_pack_len);
+ if (ret != 0) {
+ dev_err(p_sar->dev, "cnt%d, sram_fill_not_wrote_area error!", i);
+ goto err_out;
+ }
+ }
+ }
+
+err_out:
+ devm_kfree(p_sar->dev, r_buf);
+
+ return ret;
+}
+
+static int32_t aw963xx_update_firmware(struct aw_bin *aw_bin, void *load_bin_para)
+{
+ struct aw_sar *p_sar = (struct aw_sar *)load_bin_para;
+ struct aw963xx *aw963xx = (struct aw963xx *)p_sar->priv_data;
+ struct i2c_client *i2c = p_sar->i2c;
+ int32_t ret;
+
+ if (aw963xx->start_mode == AW963XX_ROM_MODE) {
+ dev_info(p_sar->dev, "no need to update fw.");
+ return 0;
+ }
+
+ //step1: close coderam shutdown mode
+ aw_sar_i2c_write(i2c, 0xfff4, 0x3c00d11f);
+ aw_sar_i2c_write(i2c, 0xc400, 0x21660000);
+
+ // step 2: reset mcu only and set boot mode to 1. (0xf800 0x00010100)
+ aw_sar_i2c_write(i2c, REG_CPU_MODE_SET, AW963XX_RESET_CPU_SET_BOOT_SATRT);
+
+ // step 3: enable data ram. (0xFFE4 0x3C000000)
+ aw_sar_i2c_write(i2c, REG_RAM_PASSWORD, AW963XX_NO_ENCRYPTION);
+
+ // setp 4: convert LD to BD
+ aw963xx_convert_little_endian_2_big_endian(aw_bin);
+
+ // step 5: write ram data.
+ ret = aw963xx_sram_data_write(aw_bin, load_bin_para);
+ if (ret == 0)
+ dev_info(p_sar->dev, "sram_data_write OK");
+ else
+ dev_err(p_sar->dev, "sram_data_write error");
+
+ mdelay(3);
+
+ // step 6: exit reset mcu and boot cpu in ram. (0xf800 0x00000100)
+ aw_sar_i2c_write(i2c, REG_CPU_MODE_SET, AW963XX_EXIT_RESET_CPU_SET_BOOT_SATRT);
+
+ // step 7: reset cpu (0xFF0C 0x0)
+ aw_sar_i2c_write(i2c, REG_CPU_RESET, AW963XX_RESET_SET);
+
+ //step 8: Wait for chip initialization to complete
+ msleep(AW963XX_CHIP_INIT_MAX_TIME_MS);
+
+ return aw963xx_read_init_over_irq(load_bin_para);
+}
+
+static int32_t aw963xx_load_reg_bin(struct aw_bin *aw_bin, void *load_bin_para)
+{
+ struct aw_sar *p_sar = (struct aw_sar *)load_bin_para;
+ struct aw963xx *aw963xx = (struct aw963xx *)p_sar->priv_data;
+ int32_t ret;
+
+ dev_dbg(p_sar->dev, "reg chip name: %s, soc chip name: %s, len = %d",
+ p_sar->chip_name, aw_bin->header_info[0].chip_type, aw_bin->info.len);
+
+ ret = strncmp(p_sar->chip_name, aw_bin->header_info[0].chip_type,
+ sizeof(aw_bin->header_info[0].chip_type));
+ if (ret != 0)
+ dev_err(p_sar->dev, "load_binname(%s) incompatible with chip type(%s)",
+ p_sar->chip_name, aw_bin->header_info[0].chip_type);
+
+ p_sar->load_bin.bin_data_ver = aw_bin->header_info[0].bin_data_ver;
+
+ ret = aw_sar_load_reg(aw_bin, p_sar->i2c);
+
+ if (!strncmp(p_sar->chip_name, AW96308, sizeof(AW96308)) ||
+ !strncmp(p_sar->chip_name, AW96305BFOR, sizeof(AW96305BFOR))) {
+ dev_dbg(p_sar->dev, "set cs%d as irq", aw963xx->irq_mux);
+ aw963xx_set_cs_as_irq(p_sar, aw963xx->irq_mux);
+ }
+
+ return ret;
+}
+
+static void aw963xx_irq_handle_func(uint32_t irq_status, void *data)
+{
+ struct aw_sar *p_sar = (struct aw_sar *)data;
+ uint32_t ch_th[AW963XX_CHANNEL_NUM_MAX] = { 0 };
+ uint32_t curr_status_val[4] = { 0 };
+ int32_t ret;
+ int8_t i;
+ int8_t j;
+
+ if (((irq_status & 0x01) == 1) && (p_sar->driver_code_initover_flag == 1)) {
+ dev_dbg(p_sar->dev, "not healthy!");
+ p_sar->fault_flag = AW_SAR_UNHEALTHY;
+ }
+
+ for (i = 0; i < AW963XX_VALID_TH; i++)
+ ret = aw_sar_i2c_read(p_sar->i2c, REG_STAT0 + i * (REG_STAT1 - REG_STAT0),
+ &curr_status_val[i]);
+
+ for (j = 0; j < AW963XX_CHANNEL_NUM_MAX; j++) {
+ if (!p_sar->channels_arr[j].input)
+ continue;
+
+ for (i = 0; i < AW963XX_VALID_TH; i++)
+ ch_th[j] |= ((curr_status_val[i] >> j) & 0x01) << i;
+
+ if (p_sar->channels_arr[j].last_channel_info != ch_th[j]) {
+ if ((ch_th[j] >> 3 & 0x01) == 1) { //th3
+ input_report_abs(p_sar->channels_arr[j].input, ABS_DISTANCE, 4);
+ } else if ((ch_th[j] >> 2 & 0x01) == 1) { //th2
+ input_report_abs(p_sar->channels_arr[j].input, ABS_DISTANCE, 3);
+ } else if ((ch_th[j] >> 1 & 0x01) == 1) { //th1
+ input_report_abs(p_sar->channels_arr[j].input, ABS_DISTANCE, 2);
+ } else if ((ch_th[j] >> 0 & 0x01) == 1) { //th0
+ input_report_abs(p_sar->channels_arr[j].input, ABS_DISTANCE, 1);
+ } else { //far
+ input_report_abs(p_sar->channels_arr[j].input, ABS_DISTANCE, 0);
+ }
+ input_sync(p_sar->channels_arr[j].input);
+ p_sar->channels_arr[j].last_channel_info = ch_th[j];
+ }
+ }
+}
+
+static ssize_t aw963xx_operation_mode_get(void *data, char *buf)
+{
+ struct aw_sar *p_sar = (struct aw_sar *)data;
+ ssize_t len = 0;
+
+ if (p_sar->last_mode == AW963XX_ACTIVE_MODE)
+ len += snprintf(buf + len, PAGE_SIZE - len, "operation mode: Active\n");
+ else if (p_sar->last_mode == AW963XX_SLEEP_MODE)
+ len += snprintf(buf + len, PAGE_SIZE - len, "operation mode: Sleep\n");
+ else if (p_sar->last_mode == AW963XX_DEEPSLEEP_MODE)
+ len += snprintf(buf + len, PAGE_SIZE - len, "operation mode: DeepSleep\n");
+ else
+ len += snprintf(buf + len, PAGE_SIZE - len, "operation mode: Unconfirmed\n");
+
+ return len;
+}
+
+static void aw963xx_sar_chip_info_get(void *data, char *buf, ssize_t *p_len)
+{
+ struct aw_sar *p_sar = (struct aw_sar *)data;
+ uint32_t reg_data;
+
+ *p_len += snprintf(buf + *p_len, PAGE_SIZE - *p_len,
+ "sar%u\n", p_sar->dts_info.sar_num);
+ *p_len += snprintf(buf + *p_len, PAGE_SIZE - *p_len, "The driver supports UI\n");
+
+ aw_sar_i2c_read(p_sar->i2c, REG_CHIP_ID0, ®_data);
+ *p_len += snprintf(buf + *p_len, PAGE_SIZE - *p_len, "chipid is 0x%08x\n", reg_data);
+
+ aw_sar_i2c_read(p_sar->i2c, REG_IRQEN, ®_data);
+ *p_len += snprintf(buf + *p_len, PAGE_SIZE - *p_len, "REG_HOSTIRQEN is 0x%08x\n", reg_data);
+
+ *p_len += snprintf(buf + *p_len, PAGE_SIZE - *p_len, "aw963xx Bin data version:0x%08x\n",
+ p_sar->load_bin.bin_data_ver);
+}
+
+static int32_t aw963xx_get_signed_cap(void *data, uint16_t reg_addr)
+{
+ struct aw_sar *p_sar = (struct aw_sar *)data;
+ int32_t s_ofst_c = 0;
+ uint32_t off_m_bit;
+ uint32_t off_c_bit;
+ uint32_t reg_data;
+ uint32_t off_c;
+ uint32_t off_m;
+ int32_t off_f;
+ uint32_t i;
+
+ aw_sar_i2c_read(p_sar->i2c, reg_addr, ®_data);
+
+ off_f = ((reg_data >> AW_BIT16) & ONE_WORD) * AW963XX_STEP_LEN_UNSIGNED_CAP_FINE_ADJ;
+ off_c = (reg_data >> AW_BIT8) & ONE_WORD;
+ off_m = reg_data & ONE_WORD;
+
+ for (i = 0; i < 8; i++) {
+ off_m_bit = (off_m >> i) & 0x01;
+ off_c_bit = (off_c >> i) & 0x01;
+ s_ofst_c += ((1 - 2 * off_m_bit) * off_c_bit * aw_sar_pow2(i)) *
+ AW963XX_STEP_LEN_UNSIGNED_CAP_ROUGH_ADJ;
+ }
+
+ return (s_ofst_c + off_f);
+}
+
+static uint32_t aw963xx_get_unsigned_cap(void *data, uint16_t reg_addr)
+{
+ struct aw_sar *p_sar = (struct aw_sar *)data;
+ uint32_t reg_data;
+ uint32_t rough;
+ uint32_t fine;
+
+ aw_sar_i2c_read(p_sar->i2c, reg_addr, ®_data);
+
+ rough = ((reg_data >> AW_BIT8) & ONE_WORD) * AW963XX_STEP_LEN_UNSIGNED_CAP_ROUGH_ADJ;
+ fine = ((reg_data >> AW_BIT16) & ONE_WORD) * AW963XX_STEP_LEN_UNSIGNED_CAP_FINE_ADJ;
+
+ return (rough + fine);
+}
+
+static void aw963xx_get_ref_ch_enable(struct aw_sar *p_sar)
+{
+ struct aw963xx *aw963xx = (struct aw963xx *)p_sar->priv_data;
+ uint32_t refa_ch;
+ uint32_t refb_ch;
+ uint32_t reg_data;
+ int32_t i;
+
+ for (i = 0; i < AW963XX_CHANNEL_NUM_MAX; i++) {
+ aw_sar_i2c_read(p_sar->i2c,
+ REG_FILTCTRL0_CH0 +
+ i * (REG_FILTCTRL0_CH1 - REG_FILTCTRL0_CH0),
+ ®_data);
+ if ((reg_data >> AW963XX_FILTCTRL0_CHX_REFAEN) & 0x01) {
+ refa_ch = (reg_data >> AW963XX_FILTCTRL0_CHX_REFASEL) & 0x1f;
+ aw963xx->ref_ch_en[refa_ch] = AW963XX_REF_EN;
+ }
+ if ((reg_data >> AW963XX_FILTCTRL0_CHX_REFBEN) & 0x01) {
+ refb_ch = (reg_data >> AW963XX_FILTCTRL0_CHX_REFBSEL) & 0x1f;
+ aw963xx->ref_ch_en[refb_ch] = AW963XX_REF_EN;
+ }
+ }
+}
+
+//Note: Because the kernel cannot handle floating-point types, it expands mul by 10 times
+static uint8_t aw963xx_get_offset_multiple(struct aw_sar *p_sar, uint8_t ch)
+{
+ uint32_t reg_data;
+ uint8_t mul = 1;
+
+ aw_sar_i2c_read(p_sar->i2c, REG_AFECFG2_CH0 + ch * (REG_AFECFG2_CH1 - REG_AFECFG2_CH0),
+ ®_data);
+ if ((reg_data >> 27) & 0x1) {
+ if (((reg_data >> 29) & 0x3) == 0)
+ mul = 16;
+ else if (((reg_data >> 29) & 0x3) == 1)
+ mul = 20;
+ else if (((reg_data >> 29) & 0x3) == 2)
+ mul = 26;
+ else if (((reg_data >> 29) & 0x3) == 3)
+ mul = 40;
+ return mul;
+ }
+
+ aw_sar_i2c_read(p_sar->i2c, REG_AFECFG3_CH0 +
+ ch * (REG_AFECFG3_CH1 - REG_AFECFG3_CH0),
+ ®_data);
+ if ((reg_data >> 11) & 0x1)
+ mul = 20;
+ else
+ mul = 10;
+
+ return mul;
+}
+
+static ssize_t aw963xx_get_cap_offset(void *data, char *buf)
+{
+ struct aw_sar *p_sar = (struct aw_sar *)data;
+ struct aw963xx *aw963xx = (struct aw963xx *)p_sar->priv_data;
+ int32_t signed_cap_ofst;
+ uint32_t mode = 0xff;
+ uint32_t reg_data;
+ uint32_t cap_ofst;
+ uint8_t mul = 10;
+ ssize_t len = 0;
+ uint32_t tmp;
+ uint32_t i;
+
+ aw963xx_get_ref_ch_enable(p_sar);
+
+ for (i = 0; i < AW963XX_CHANNEL_NUM_MAX; i++) {
+ aw_sar_i2c_read(p_sar->i2c,
+ REG_AFESOFTCFG0_CH0 +
+ i * (REG_AFESOFTCFG0_CH1 - REG_AFESOFTCFG0_CH0),
+ ®_data);
+ mul = aw963xx_get_offset_multiple(p_sar, i);
+ mode = reg_data & 0x0ff;
+ switch (mode) {
+ case AW963XX_UNSIGNED_CAP: //self-capacitance mode unsigned cail
+ cap_ofst = aw963xx_get_unsigned_cap(p_sar,
+ REG_AFECFG1_CH0 + i * (REG_AFECFG1_CH1 - REG_AFECFG1_CH0));
+ //Because it has been expanded by 1000 times before,
+ //the accuracy of removing mul's expansion loss can be ignored
+ cap_ofst = cap_ofst * mul / 10;
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "unsigned cap ofst ch%u: %u.%u pf\r\n",
+ i,
+ cap_ofst / AW963XX_STEP_LEN_UNSIGNED_CAP_ENLARGE,
+ cap_ofst % AW963XX_STEP_LEN_UNSIGNED_CAP_ENLARGE);
+ break;
+ case AW963XX_SIGNED_CAP: //self-capacitance mode signed cail
+ signed_cap_ofst = aw963xx_get_signed_cap(p_sar,
+ REG_AFECFG1_CH0 + i * (REG_AFECFG1_CH1 - REG_AFECFG1_CH0));
+ signed_cap_ofst = signed_cap_ofst * mul / 10;
+ if (signed_cap_ofst < 0) {
+ tmp = -signed_cap_ofst;
+ dev_info(p_sar->dev, "cap_ofst2 = 0x%x",
+ signed_cap_ofst);
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "signed cap ofst ch%u: -%u.%upf\r\n",
+ i,
+ tmp / AW963XX_STEP_LEN_UNSIGNED_CAP_ENLARGE,
+ tmp % AW963XX_STEP_LEN_UNSIGNED_CAP_ENLARGE);
+ } else {
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "signed cap ofst ch%u: %d.%dpf\r\n",
+ i,
+ signed_cap_ofst /
+ AW963XX_STEP_LEN_UNSIGNED_CAP_ENLARGE,
+ signed_cap_ofst %
+ AW963XX_STEP_LEN_UNSIGNED_CAP_ENLARGE);
+ }
+ break;
+ case AW963XX_MUTUAL_CAP: //mutual-capacitance mode
+ if (aw963xx->ref_ch_en[i] == AW963XX_REF_EN) {
+ cap_ofst = aw963xx_get_unsigned_cap(p_sar,
+ REG_AFECFG1_M_CH0 +
+ i * (REG_AFECFG1_M_CH1 - REG_AFECFG1_M_CH0));
+ cap_ofst = cap_ofst * mul / 10;
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "ref unsigned cap ofst ch%u: %u.%udpf\r\n",
+ i,
+ cap_ofst / AW963XX_STEP_LEN_UNSIGNED_CAP_ENLARGE,
+ cap_ofst % AW963XX_STEP_LEN_UNSIGNED_CAP_ENLARGE);
+ } else {
+ signed_cap_ofst = aw963xx_get_signed_cap(p_sar,
+ REG_AFECFG1_CH0 +
+ i * (REG_AFECFG1_CH1 - REG_AFECFG1_CH0));
+ signed_cap_ofst = signed_cap_ofst * mul / 10;
+ if (signed_cap_ofst < 0) {
+ tmp = -signed_cap_ofst;
+ dev_info(p_sar->dev,
+ "cap_ofst2 = 0x%x",
+ signed_cap_ofst);
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "mutual cap ofst ch%u: -%u.%udpf\r\n",
+ i,
+ tmp / AW963XX_STEP_LEN_UNSIGNED_CAP_ENLARGE,
+ tmp % AW963XX_STEP_LEN_UNSIGNED_CAP_ENLARGE);
+ } else {
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "mutual cap ofst ch%u: %d.%dpf\r\n",
+ i,
+ signed_cap_ofst /
+ AW963XX_STEP_LEN_UNSIGNED_CAP_ENLARGE,
+ signed_cap_ofst %
+ AW963XX_STEP_LEN_UNSIGNED_CAP_ENLARGE);
+ }
+ }
+ break;
+ default:
+ dev_info(p_sar->dev, "aw963xx ofst error 0x%x", reg_data & 0x0f);
+ break;
+ }
+ }
+
+ return len;
+}
+
+static void aw963xx_set_cs_as_irq(struct aw_sar *p_sar, int flag)
+{
+ if (flag == AW963XX_CS2_IRQ) {
+ aw_sar_i2c_write(p_sar->i2c, 0xfff4, 0x3c00d013);
+ aw_sar_i2c_write(p_sar->i2c, 0xc100, 0x00000020);
+ aw_sar_i2c_write(p_sar->i2c, 0xe018, 0x00000004);
+ } else if (flag == AW963XX_CS5_IRQ) {
+ aw_sar_i2c_write(p_sar->i2c, 0xfff4, 0x3c00d013);
+ aw_sar_i2c_write(p_sar->i2c, 0xc100, 0x00000800);
+ aw_sar_i2c_write(p_sar->i2c, 0xe018, 0x00000020);
+ } else {
+ aw_sar_i2c_write(p_sar->i2c, 0xfff4, 0x3c00d013);
+ aw_sar_i2c_write(p_sar->i2c, 0xc100, 0x00000000);
+ aw_sar_i2c_write(p_sar->i2c, 0xe018, 0x00000000);
+ }
+}
+
+int32_t aw963xx_check_chipid(void *data)
+{
+ struct aw_sar *p_sar = (struct aw_sar *)data;
+ uint32_t reg_val;
+ int32_t ret;
+
+ if (!p_sar)
+ return -EINVAL;
+
+ ret = aw_sar_i2c_read(p_sar->i2c, REG_CHIP_ID0, ®_val);
+ if (ret < 0) {
+ dev_err(p_sar->dev, "read CHIP ID failed: %d", ret);
+ return ret;
+ }
+
+ switch (reg_val) {
+ case AW96303_CHIP_ID:
+ dev_info(p_sar->dev, "aw96303 detected, 0x%04x", reg_val);
+ memcpy(p_sar->chip_name, AW96303, 8);
+ ret = 0;
+ break;
+ case AW96305_CHIP_ID:
+ dev_info(p_sar->dev, "aw96305 detected, 0x%04x", reg_val);
+ memcpy(p_sar->chip_name, AW96305, 8);
+ ret = 0;
+ break;
+ case AW96305BFOR_CHIP_ID:
+ dev_info(p_sar->dev, "aw96305bfor detected, 0x%04x", reg_val);
+ memcpy(p_sar->chip_name, AW96305BFOR, 8);
+ ret = 0;
+ break;
+ case AW96308_CHIP_ID:
+ dev_info(p_sar->dev, "aw96308 detected, 0x%04x", reg_val);
+ memcpy(p_sar->chip_name, AW96308, 8);
+ ret = 0;
+ break;
+ case AW96310_CHIP_ID:
+ dev_info(p_sar->dev, "aw96310 detected, 0x%04x", reg_val);
+ memcpy(p_sar->chip_name, AW96310, 8);
+ ret = 0;
+ break;
+ default:
+ dev_info(p_sar->dev, "chip id error, 0x%04x", reg_val);
+ ret = -EIO;
+ break;
+ }
+
+ return ret;
+}
+
+/**********************mode operation start*******************************/
+static void aw963xx_enable_clock(void *i2c)
+{
+ aw_sar_i2c_write_bits(i2c, REG_CHIPSTAT, ~AW963XX_CPU_OSC_CTRL_MASK,
+ AW963XX_CPU_OSC_CTRL_MASK);
+}
+
+static uint32_t aw963xx_rc_irqscr(void *i2c)
+{
+ uint32_t val;
+
+ aw_sar_i2c_read(i2c, REG_IRQSRC, &val);
+
+ return val;
+}
+
+static void aw963xx_set_active_cmd(void *i2c)
+{
+ aw_sar_i2c_write(i2c, REG_CMD, AW963XX_ACTIVE_MODE);
+}
+
+static void aw963xx_set_sleep_cmd(void *i2c)
+{
+ aw_sar_i2c_write(i2c, REG_CMD, AW963XX_SLEEP_MODE);
+}
+
+static void aw963xx_set_deepsleep_cmd(void *i2c)
+{
+ aw_sar_i2c_write(i2c, REG_CMD, AW963XX_DEEPSLEEP_MODE);
+}
+
+static const struct aw_sar_mode_set_t g_aw963xx_mode_set[] = {
+ {
+ .chip_id = AW_SAR_NONE_CHECK_CHIP,
+ .chip_mode = {
+ .curr_mode = AW963XX_ACTIVE_MODE,
+ .last_mode = AW963XX_DEEPSLEEP_MODE,
+ },
+ .mode_switch_ops = {
+ .enable_clock = aw963xx_enable_clock,
+ .rc_irqscr = NULL,
+ .mode_update = aw963xx_set_active_cmd,
+ },
+ },
+ {
+ .chip_id = AW_SAR_NONE_CHECK_CHIP,
+ .chip_mode = {
+ .curr_mode = AW963XX_ACTIVE_MODE,
+ .last_mode = AW963XX_SLEEP_MODE,
+ },
+ .mode_switch_ops = {
+ .enable_clock = NULL,
+ .rc_irqscr = NULL,
+ .mode_update = aw963xx_set_active_cmd,
+ },
+ },
+ {
+ .chip_id = AW_SAR_NONE_CHECK_CHIP,
+ .chip_mode = {
+ .curr_mode = AW963XX_ACTIVE_MODE,
+ .last_mode = AW963XX_ACTIVE_MODE,
+ },
+ .mode_switch_ops = {
+ .enable_clock = NULL,
+ .rc_irqscr = NULL,
+ .mode_update = aw963xx_set_active_cmd,
+ },
+ },
+ {
+ .chip_id = AW_SAR_NONE_CHECK_CHIP,
+ .chip_mode = {
+ .curr_mode = AW963XX_SLEEP_MODE,
+ .last_mode = AW963XX_DEEPSLEEP_MODE,
+ },
+ .mode_switch_ops = {
+ .enable_clock = aw963xx_enable_clock,
+ .rc_irqscr = aw963xx_rc_irqscr,
+ .mode_update = aw963xx_set_sleep_cmd,
+ },
+ },
+ {
+ .chip_id = AW_SAR_NONE_CHECK_CHIP,
+ .chip_mode = {
+ .curr_mode = AW963XX_SLEEP_MODE,
+ .last_mode = AW963XX_ACTIVE_MODE,
+ },
+ .mode_switch_ops = {
+ .enable_clock = NULL,
+ .rc_irqscr = aw963xx_rc_irqscr,
+ .mode_update = aw963xx_set_sleep_cmd,
+ },
+ },
+ {
+ .chip_id = AW_SAR_NONE_CHECK_CHIP,
+ .chip_mode = {
+ .curr_mode = AW963XX_DEEPSLEEP_MODE,
+ .last_mode = AW963XX_SLEEP_MODE,
+ },
+ .mode_switch_ops = {
+ .enable_clock = NULL,
+ .rc_irqscr = aw963xx_rc_irqscr,
+ .mode_update = aw963xx_set_deepsleep_cmd,
+ },
+ },
+ {
+ .chip_id = AW_SAR_NONE_CHECK_CHIP,
+ .chip_mode = {
+ .curr_mode = AW963XX_DEEPSLEEP_MODE,
+ .last_mode = AW963XX_ACTIVE_MODE,
+ },
+ .mode_switch_ops = {
+ .enable_clock = NULL,
+ .rc_irqscr = aw963xx_rc_irqscr,
+ .mode_update = aw963xx_set_deepsleep_cmd,
+ },
+ },
+};
+
+static void aw963xx_sar_get_firmware_info(void *data, char *buf, ssize_t *p_len)
+{
+ struct aw_sar *p_sar = (struct aw_sar *)data;
+ uint32_t reg_data;
+
+ aw_sar_i2c_read(p_sar->i2c, REG_FWVER, ®_data);
+ *p_len += snprintf(buf + *p_len, PAGE_SIZE - *p_len, "firmware is 0x%08x\n", reg_data);
+}
+
+static int32_t aw963xx_parse_dts(void *data)
+{
+ struct aw_sar *p_sar = (struct aw_sar *)data;
+ struct aw963xx *aw963xx = (struct aw963xx *)p_sar->priv_data;
+ struct device_node *np = p_sar->i2c->dev.of_node;
+ int32_t val;
+
+ val = of_property_read_u32(np, "irq-mux", &aw963xx->irq_mux);
+ if (val != 0)
+ dev_err(p_sar->dev, "irq-mux not detected");
+ else
+ dev_info(p_sar->dev, "irq-mux = %d", aw963xx->irq_mux);
+
+ val = of_property_read_u32(np, "start-mode", &aw963xx->start_mode);
+ if (val != 0)
+ dev_err(p_sar->dev, "start-mode not detected");
+ else
+ dev_info(p_sar->dev, "start-mode = %d", aw963xx->start_mode);
+
+ return 0;
+}
+
+static const struct aw_sar_mode_t g_aw963xx_mode = {
+ .mode_set_arr = &g_aw963xx_mode_set[0],
+ .mode_set_arr_len = ARRAY_SIZE(g_aw963xx_mode_set),
+ .p_set_mode_node_fn = NULL,
+ .p_get_mode_node_fn = aw963xx_operation_mode_get,
+};
+
+static const struct aw_sar_diff_t g_aw963xx_diff = {
+ .diff0_reg = REG_DIFF_CH0,
+ .diff_step = REG_DIFF_CH1 - REG_DIFF_CH0,
+ .rm_float = AW963XX_DATA_PROCESS_FACTOR,
+ .p_get_diff_node_fn = NULL,
+};
+
+static const struct aw_sar_offset_t g_aw963xx_offset = {
+ .p_get_offset_node_fn = aw963xx_get_cap_offset,
+};
+
+static const struct aw_sar_aot_t g_aw963xx_aot = {
+ .aot_reg = REG_SCANCTRL1,
+ .aot_mask = ~0xfff,
+ .aot_flag = 0xfff,
+};
+
+static const struct aw_sar_para_load_t g_aw963xx_reg_arr_para = {
+ .reg_arr = aw963xx_reg_default,
+ .reg_arr_len = ARRAY_SIZE(aw963xx_reg_default),
+};
+
+static const struct aw_sar_regulator_config_t g_regulator_config = {
+ .vcc_name = "vcc",
+ .min_uV = AW963XX_SAR_VCC_MIN_UV,
+ .max_uV = AW963XX_SAR_VCC_MAX_UV,
+};
+
+static const struct aw_sar_reg_list_t g_aw963xx_reg_list = {
+ .reg_none_access = REG_NONE_ACCESS,
+ .reg_rd_access = REG_RD_ACCESS,
+ .reg_wd_access = REG_WR_ACCESS,
+ .reg_perm = (struct aw_sar_reg_data *)&g_aw963xx_reg_access[0],
+ .reg_num = ARRAY_SIZE(g_aw963xx_reg_access),
+};
+
+static const struct aw_sar_chip_mode_t g_aw963xx_chip_mode = {
+ .init_mode = AW963XX_ACTIVE_MODE,
+ .active = AW963XX_ACTIVE_MODE,
+ .pre_init_mode = AW963XX_SLEEP_MODE,
+};
+
+static const struct aw_sar_load_bin_t g_aw963xx_load_reg_bin = {
+ .bin_name = "aw963xx_reg",
+ .bin_opera_func = aw963xx_load_reg_bin,
+ .p_update_fn = NULL,
+};
+
+static const struct aw_sar_load_bin_t g_aw963xx_load_fw_bin = {
+ .bin_name = "aw963xx_fw",
+ .bin_opera_func = aw963xx_update_firmware,
+ .p_get_prot_update_fw_node_fn = aw963xx_sar_get_firmware_info,
+ .bin_load_fail_opera = NULL,
+};
+
+static const struct aw_sar_get_chip_info_t g_aw963xx_get_chip_info = {
+ .p_get_chip_info_node_fn = aw963xx_sar_chip_info_get,
+};
+
+static const struct aw_sar_check_chipid_t g_aw963xx_check_chipid = {
+ .p_check_chipid_fn = aw963xx_check_chipid,
+};
+
+static const struct aw_sar_irq_init_t g_aw963xx_irq_init = {
+ .flags = GPIOF_DIR_IN | GPIOF_INIT_HIGH,
+ .irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ .handler = NULL,
+ .thread_fn = NULL,
+ .rc_irq_fn = aw963xx_rc_irqscr,
+ .irq_spec_handler_fn = aw963xx_irq_handle_func,
+
+ .p_irq_init_fn = NULL,
+ .p_irq_deinit_fn = NULL,
+};
+
+static const struct aw_sar_soft_rst_t g_aw963xx_soft_rst = {
+ .reg_rst = REG_SA_RSTNALL,
+ .reg_rst_val = AW963XX_SOFT_RST_EN,
+ .delay_ms = AW963XX_CHIP_INIT_MAX_TIME_MS,
+ .p_soft_reset_fn = NULL,
+};
+
+static const struct aw_sar_init_over_irq_t g_aw963xx_init_over_irq = {
+ .wait_times = 100,
+ .daley_step = 1,
+ .reg_irqsrc = REG_IRQSRC,
+ .irq_offset_bit = 0,
+ .irq_mask = 0x1,
+ .irq_flag = 0x1,
+
+ .p_check_init_over_irq_fn = NULL,
+ .p_get_err_type_fn = NULL,
+};
+
+static const struct aw_sar_pm_t g_aw963xx_pm_chip_mode = {
+ .suspend_set_mode = AW963XX_SLEEP_MODE,
+ .resume_set_mode = AW963XX_ACTIVE_MODE,
+ .shutdown_set_mode = AW963XX_SLEEP_MODE,
+};
+
+static const struct aw_sar_platform_config g_aw963xx_platform_config = {
+ .p_add_parse_dts_fn = &aw963xx_parse_dts,
+ .p_regulator_config = &g_regulator_config,
+ .p_irq_init = &g_aw963xx_irq_init,
+ .p_pm_chip_mode = &g_aw963xx_pm_chip_mode,
+};
+
+static void aw963xx_power_on_prox_detection(void *data, uint8_t en_flag)
+{
+
+}
+
+static const struct aw_sar_power_on_prox_detection_t g_aw933xx_power_on_prox_detection = {
+ .p_power_on_prox_detection_en_fn = aw963xx_power_on_prox_detection,
+ .irq_en_cali_bit = 3,
+ .power_on_prox_en_flag = true,
+};
+
+static const struct aw_sar_chip_config g_aw963xx_chip_config = {
+ .ch_num_max = AW963XX_CHANNEL_NUM_MAX,
+ .p_platform_config = &g_aw963xx_platform_config,
+
+ .p_check_chipid = &g_aw963xx_check_chipid,
+ .p_soft_rst = &g_aw963xx_soft_rst,
+ .p_init_over_irq = &g_aw963xx_init_over_irq,
+ .p_fw_bin = &g_aw963xx_load_fw_bin,
+ .p_reg_bin = &g_aw963xx_load_reg_bin,
+ .p_chip_mode = &g_aw963xx_chip_mode,
+
+ //Node usage parameters
+ .p_reg_list = &g_aw963xx_reg_list,
+ .p_reg_arr = &g_aw963xx_reg_arr_para,
+ .p_aot = &g_aw963xx_aot,
+ .p_diff = &g_aw963xx_diff,
+ .p_offset = &g_aw963xx_offset,
+ .p_mode = &g_aw963xx_mode,
+ .p_prox_fw = &g_aw963xx_load_fw_bin,
+ .p_get_chip_info = &g_aw963xx_get_chip_info,
+ .p_aw_sar_awrw = NULL,
+ .p_boot_bin = NULL,
+
+ .p_other_operation = NULL,
+ .p_other_opera_free = NULL,
+
+ .power_on_prox_detection = &g_aw933xx_power_on_prox_detection,
+};
+
+int32_t aw963xx_init(struct aw_sar *p_sar)
+{
+ struct aw963xx *aw963xx;
+
+ if (!p_sar)
+ return -EINVAL;
+
+ p_sar->priv_data = devm_kzalloc(p_sar->dev, sizeof(struct aw963xx), GFP_KERNEL);
+ if (!p_sar->priv_data)
+ return -ENOMEM;
+
+ //Chip private function operation
+ p_sar->p_sar_para = &g_aw963xx_chip_config;
+
+ aw963xx = (struct aw963xx *)p_sar->priv_data;
+
+ return 0;
+}
+
+void aw963xx_deinit(struct aw_sar *p_sar)
+{
+ struct aw963xx *aw963xx;
+
+ if ((!p_sar) || (!p_sar->priv_data))
+ return;
+
+ aw963xx = (struct aw963xx *)p_sar->priv_data;
+
+
+ if (p_sar->priv_data != NULL)
+ devm_kfree(p_sar->dev, p_sar->priv_data);
+}
diff --git a/drivers/input/misc/aw_sar/aw963xx/aw963xx.h b/drivers/input/misc/aw_sar/aw963xx/aw963xx.h
new file mode 100644
index 000000000000..a9ebbf2334b2
--- /dev/null
+++ b/drivers/input/misc/aw_sar/aw963xx/aw963xx.h
@@ -0,0 +1,749 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef AW963XX_H_
+#define AW963XX_H_
+#include "../comm/aw_sar_type.h"
+
+#define AW963XX_CHANNEL_NUM_MAX (12)
+#define AW963XX_VALID_TH (2)
+#define AW963XX_DATA_PROCESS_FACTOR (1024)
+#define AW963XX_SAR_VCC_MIN_UV (1700000)
+#define AW963XX_SAR_VCC_MAX_UV (3600000)
+#define AW963XX_SRAM_UPDATE_ONE_PACK_SIZE (1024)
+#define AW963XX_SRAM_UPDATE_ONE_UINT_SIZE (4)
+#define AW963XX_SRAM_START_ADDR (0x2000)
+#define AW963XX_SRAM_END_ADDR (0x4ff4 + 4)
+#define AW963XX_SRAM_SIZE (AW963XX_SRAM_END_ADDR - AW963XX_SRAM_START_ADDR)
+
+#define AW963XX_STEP_LEN_UNSIGNED_CAP_ROUGH_ADJ (9900)
+#define AW963XX_STEP_LEN_UNSIGNED_CAP_FINE_ADJ (152)
+#define AW963XX_STEP_LEN_UNSIGNED_CAP_ENLARGE (10000)
+
+#define AW963XX_CPU_OSC_CTRL_MASK (1)
+
+#define ONE_WORD (0xff)
+#define AW_BIT8 (8)
+#define AW_BIT16 (16)
+#define AW96303 ("AW96303")
+#define AW96305 ("AW96305")
+#define AW96305BFOR ("AW96305BFOR")
+#define AW96308 ("AW96308")
+#define AW96310 ("AW96310")
+
+enum aw963xx_cap_mode {
+ AW963XX_UNSIGNED_CAP = 0,
+ AW963XX_SIGNED_CAP = 4,
+ AW963XX_MUTUAL_CAP = 5,
+};
+
+enum aw963xx_approach_state {
+ AW963XX_FAR_AWAY = 0,
+ AW963XX_APPROACH = 1,
+};
+
+enum aw963xx_cs_2_irq {
+ AW963XX_CS2_IRQ = 2,
+ AW963XX_CS5_IRQ = 5,
+};
+
+enum aw963xx_operation_mode {
+ AW963XX_ACTIVE_MODE = 0x01,
+ AW963XX_SLEEP_MODE = 0x02,
+ AW963XX_DEEPSLEEP_MODE = 0x03,
+};
+
+enum aw963xx_chip_id {
+ AW96303_CHIP_ID = 0xA9630340,
+ AW96305_CHIP_ID = 0xA9630520,
+ AW96305BFOR_CHIP_ID = 0xA9630500,
+ AW96308_CHIP_ID = 0xA9630810,
+ AW96310_CHIP_ID = 0xA9631010,
+};
+
+enum aw963xx_boot_mode {
+ AW963XX_ROM_MODE = 0,
+ AW963XX_RAM_MODE = 1,
+};
+
+struct aw963xx {
+ uint32_t irq_mux;
+ uint32_t start_mode;
+ uint32_t ref_ch_en[AW963XX_CHANNEL_NUM_MAX];
+ void *p_aw_sar;
+
+ uint32_t last_blfilta[AW963XX_CHANNEL_NUM_MAX];
+ uint32_t last_irq_en;
+
+};
+
+#define REG_CPU_MODE_SET (0xf800)
+#define AW963XX_RESET_CPU_SET_BOOT_SATRT (0x00010100)
+#define AW963XX_EXIT_RESET_CPU_SET_BOOT_SATRT (0x00000100)
+#define REG_CPU_RESET (0XFF0C)
+#define AW963XX_RESET_SET (0)
+#define REG_RAM_PASSWORD (0xFFE4)
+#define AW963XX_NO_ENCRYPTION (0x3C000000)
+#define AW963XX_FILTCTRL0_CHX_REFAEN (16)
+#define AW963XX_FILTCTRL0_CHX_REFASEL (11)
+#define AW963XX_FILTCTRL0_CHX_REFBEN (9)
+#define AW963XX_FILTCTRL0_CHX_REFBSEL (4)
+#define AW963XX_REF_EN (1)
+#define REG_SA_RSTNALL (0xFF0C)
+#define AW963XX_SOFT_RST_EN (0)
+#define AW963XX_CHIP_INIT_MAX_TIME_MS (30)
+
+#define AW963XX_RAM_START_ADDR (0x2000)
+
+#define AFE_BASE_ADDR (0x0000)
+#define DSP_BASE_ADDR (0x0000)
+#define STAT_BASE_ADDR (0x0000)
+#define DATA_BASE_ADDR (0x0000)
+#define SFR_BASE_ADDR (0x0000)
+#define HIDDEN_BASE_ADDR (0x0000)
+
+#define REG_STAT0 ((0x0020) + STAT_BASE_ADDR)
+#define REG_STAT1 ((0x0024) + STAT_BASE_ADDR)
+#define REG_FWVER ((0x005C) + STAT_BASE_ADDR)
+
+#define REG_DIFF_CH0 ((0x01EC) + DATA_BASE_ADDR)
+#define REG_DIFF_CH1 ((0x0304) + DATA_BASE_ADDR)
+
+#define REG_FILTCTRL0_CH0 ((0x0130) + DSP_BASE_ADDR)
+#define REG_FILTCTRL0_CH1 ((0x0248) + DSP_BASE_ADDR)
+
+#define REG_SCANCTRL1 ((0x0004) + AFE_BASE_ADDR)
+#define REG_AFESOFTCFG0_CH0 ((0x010C) + AFE_BASE_ADDR)
+#define REG_AFECFG1_CH0 ((0x0118) + AFE_BASE_ADDR)
+#define REG_AFECFG3_CH0 ((0x0120) + AFE_BASE_ADDR)
+#define REG_AFESOFTCFG0_CH1 ((0x0224) + AFE_BASE_ADDR)
+#define REG_AFECFG1_CH1 ((0x0230) + AFE_BASE_ADDR)
+#define REG_AFECFG3_CH1 ((0x0238) + AFE_BASE_ADDR)
+
+#define REG_AFECFG1_M_CH0 ((0x10F8) + HIDDEN_BASE_ADDR)
+#define REG_AFECFG1_M_CH1 ((0x1184) + HIDDEN_BASE_ADDR)
+
+#define REG_IRQSRC ((0xF080) + SFR_BASE_ADDR)
+#define REG_IRQEN ((0xF084) + SFR_BASE_ADDR)
+#define REG_CHIPSTAT ((0xFF00) + SFR_BASE_ADDR)
+#define REG_CHIP_ID0 ((0xFF10) + SFR_BASE_ADDR)
+
+#define REG_CMD ((0xf008) + SFR_BASE_ADDR)
+#define REG_AFECFG2_CH0 ((0x011c) + AFE_BASE_ADDR)
+#define REG_AFECFG2_CH1 ((0x0234) + AFE_BASE_ADDR)
+
+struct reg_data {
+ unsigned char rw;
+ unsigned short reg;
+};
+/********************************************
+ * Register Access
+ *******************************************/
+#define REG_NONE_ACCESS (0)
+#define REG_RD_ACCESS (1 << 0)
+#define REG_WR_ACCESS (1 << 1)
+static const struct reg_data g_aw963xx_reg_access[] = {
+ { .reg = REG_SCANCTRL1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, },
+ { .reg = REG_AFESOFTCFG0_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, },
+ { .reg = REG_AFECFG1_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, },
+ { .reg = REG_AFECFG3_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, },
+ { .reg = REG_AFESOFTCFG0_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, },
+ { .reg = REG_AFECFG1_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, },
+ { .reg = REG_AFECFG3_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, },
+ { .reg = REG_STAT0, .rw = REG_RD_ACCESS, },
+ { .reg = REG_STAT1, .rw = REG_RD_ACCESS, },
+ { .reg = REG_FWVER, .rw = REG_RD_ACCESS, },
+ { .reg = REG_DIFF_CH0, .rw = REG_RD_ACCESS, },
+ { .reg = REG_DIFF_CH1, .rw = REG_RD_ACCESS, },
+ { .reg = REG_FILTCTRL0_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, },
+ { .reg = REG_FILTCTRL0_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, },
+ { .reg = REG_AFECFG1_M_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, },
+ { .reg = REG_AFECFG1_M_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, },
+ { .reg = REG_IRQSRC, .rw = REG_RD_ACCESS, },
+ { .reg = REG_IRQEN, .rw = REG_RD_ACCESS | REG_WR_ACCESS, },
+ { .reg = REG_CHIPSTAT, .rw = REG_RD_ACCESS, },
+ { .reg = REG_CHIP_ID0, .rw = REG_RD_ACCESS, },
+};
+static const uint32_t aw963xx_reg_default[] = {
+ 0x0000, 0x000000FF,
+ 0x0004, 0x00000FFF,
+ 0x0008, 0x00000FFF,
+ 0x000C, 0x00000064,
+ 0x0010, 0x0000705F,
+ 0x0014, 0x80000000,
+ 0x0060, 0x00000000,
+ 0x0064, 0x00000000,
+ 0x0068, 0x00000000,
+ 0x006C, 0x00000000,
+ 0x0070, 0x00000000,
+ 0x0074, 0x00000000,
+ 0x0078, 0x00000000,
+ 0x007C, 0x00000000,
+ 0x0080, 0x00000000,
+ 0x0084, 0x00000000,
+ 0x0088, 0x00000000,
+ 0x008C, 0x00000000,
+ 0x0168, 0x00000000,
+ 0x016C, 0x003F0000,
+ 0x0170, 0x00050100,
+ 0x0174, 0x00000000,
+ 0x0178, 0x341C9207,
+ 0x017C, 0x00008000,
+ 0x0180, 0x00000909,
+ 0x0184, 0x00000001,
+ 0x0188, 0x00000000,
+ 0x0318, 0x00000000,
+ 0x031C, 0x003F0000,
+ 0x0320, 0x00050100,
+ 0x0324, 0x00000000,
+ 0x0328, 0x341C9207,
+ 0x032C, 0x00008000,
+ 0x0330, 0x00000909,
+ 0x0334, 0x00000008,
+ 0x0338, 0x00000000,
+ 0x04C8, 0x00000000,
+ 0x04CC, 0x003F0000,
+ 0x04D0, 0x00050100,
+ 0x04D4, 0x00000000,
+ 0x04D8, 0x341C9207,
+ 0x04DC, 0x00008000,
+ 0x04E0, 0x00000909,
+ 0x04E4, 0x00000040,
+ 0x04E8, 0x00000000,
+ 0x0678, 0x00000000,
+ 0x067C, 0x003F0000,
+ 0x0680, 0x00050100,
+ 0x0684, 0x00000000,
+ 0x0688, 0x341C9207,
+ 0x068C, 0x00008000,
+ 0x0690, 0x00000909,
+ 0x0694, 0x00000200,
+ 0x0698, 0x00000000,
+ 0x0828, 0x00000000,
+ 0x082C, 0x003F0000,
+ 0x0830, 0x00050100,
+ 0x0834, 0x00000000,
+ 0x0838, 0x341C9207,
+ 0x083C, 0x00008000,
+ 0x0840, 0x00000909,
+ 0x0844, 0x00001000,
+ 0x0848, 0x00000000,
+ 0x09D8, 0x00000000,
+ 0x09DC, 0x003F0000,
+ 0x09E0, 0x00050100,
+ 0x09E4, 0x00000000,
+ 0x09E8, 0x341C9207,
+ 0x09EC, 0x00008000,
+ 0x09F0, 0x00000909,
+ 0x09F4, 0x00008000,
+ 0x09F8, 0x00000000,
+ 0x0B88, 0x00000000,
+ 0x0B8C, 0x003F0000,
+ 0x0B90, 0x00050100,
+ 0x0B94, 0x00000000,
+ 0x0B98, 0x341C9207,
+ 0x0B9C, 0x00008000,
+ 0x0BA0, 0x00000909,
+ 0x0BA4, 0x00040000,
+ 0x0BA8, 0x00000000,
+ 0x0D38, 0x00000000,
+ 0x0D3C, 0x003F0000,
+ 0x0D40, 0x00050100,
+ 0x0D44, 0x00000000,
+ 0x0D48, 0x341C9207,
+ 0x0D4C, 0x00008000,
+ 0x0D50, 0x00000909,
+ 0x0D54, 0x00200000,
+ 0x0D58, 0x00000000,
+ 0x0EE8, 0x00000000,
+ 0x0EEC, 0x003F0000,
+ 0x0EF0, 0x00050100,
+ 0x0EF4, 0x00000000,
+ 0x0EF8, 0x341C9207,
+ 0x0EFC, 0x00008000,
+ 0x0F00, 0x00000909,
+ 0x0F04, 0x00000000,
+ 0x0F08, 0x00000000,
+ 0x1098, 0x00000000,
+ 0x109C, 0x003F0000,
+ 0x10A0, 0x00050100,
+ 0x10A4, 0x00000000,
+ 0x10A8, 0x341C9207,
+ 0x10AC, 0x00008000,
+ 0x10B0, 0x00000909,
+ 0x10B4, 0x00000000,
+ 0x10B8, 0x00000000,
+ 0x1248, 0x00000000,
+ 0x124C, 0x003F0000,
+ 0x1250, 0x00050100,
+ 0x1254, 0x00000000,
+ 0x1258, 0x341C9207,
+ 0x125C, 0x00008000,
+ 0x1260, 0x00000909,
+ 0x1264, 0x00000000,
+ 0x1268, 0x00000000,
+ 0x13F8, 0x00000000,
+ 0x13FC, 0x003F0000,
+ 0x1400, 0x00050100,
+ 0x1404, 0x00000000,
+ 0x1408, 0x341C9207,
+ 0x140C, 0x00008000,
+ 0x1410, 0x00000909,
+ 0x1414, 0x00000000,
+ 0x1418, 0x00000000,
+ 0x018C, 0xE0400000,
+ 0x0190, 0x00000000,
+ 0x0194, 0x00000000,
+ 0x0198, 0x000A0000,
+ 0x019C, 0x000008D2,
+ 0x01A0, 0x00000000,
+ 0x01A4, 0x00000040,
+ 0x01A8, 0x000186A0,
+ 0x01AC, 0x00030D40,
+ 0x01B0, 0x00061A80,
+ 0x01B4, 0x000C3500,
+ 0x01B8, 0x00000000,
+ 0x01BC, 0x00000000,
+ 0x01C0, 0x00000000,
+ 0x01C4, 0x00000000,
+ 0x01C8, 0x00000000,
+ 0x01CC, 0x00000000,
+ 0x033C, 0xE0400000,
+ 0x0340, 0x00000000,
+ 0x0344, 0x00000000,
+ 0x0348, 0x000A0000,
+ 0x034C, 0x000008D2,
+ 0x0350, 0x00000000,
+ 0x0354, 0x00000040,
+ 0x0358, 0x000186A0,
+ 0x035C, 0x00030D40,
+ 0x0360, 0x00061A80,
+ 0x0364, 0x000C3500,
+ 0x0368, 0x00000000,
+ 0x036C, 0x00000000,
+ 0x0370, 0x00000000,
+ 0x0374, 0x00000000,
+ 0x0378, 0x00000000,
+ 0x037C, 0x00000000,
+ 0x04EC, 0xE0400000,
+ 0x04F0, 0x00000000,
+ 0x04F4, 0x00000000,
+ 0x04F8, 0x000A0000,
+ 0x04FC, 0x000008D2,
+ 0x0500, 0x00000000,
+ 0x0504, 0x00000040,
+ 0x0508, 0x000186A0,
+ 0x050C, 0x00030D40,
+ 0x0510, 0x00061A80,
+ 0x0514, 0x000C3500,
+ 0x0518, 0x00000000,
+ 0x051C, 0x00000000,
+ 0x0520, 0x00000000,
+ 0x0524, 0x00000000,
+ 0x0528, 0x00000000,
+ 0x052C, 0x00000000,
+ 0x069C, 0xE0400000,
+ 0x06A0, 0x00000000,
+ 0x06A4, 0x00000000,
+ 0x06A8, 0x000A0000,
+ 0x06AC, 0x000008D2,
+ 0x06B0, 0x00000000,
+ 0x06B4, 0x00000040,
+ 0x06B8, 0x000186A0,
+ 0x06BC, 0x00030D40,
+ 0x06C0, 0x00061A80,
+ 0x06C4, 0x000C3500,
+ 0x06C8, 0x00000000,
+ 0x06CC, 0x00000000,
+ 0x06D0, 0x00000000,
+ 0x06D4, 0x00000000,
+ 0x06D8, 0x00000000,
+ 0x06DC, 0x00000000,
+ 0x084C, 0xE0400000,
+ 0x0850, 0x00000000,
+ 0x0854, 0x00000000,
+ 0x0858, 0x000A0000,
+ 0x085C, 0x000008D2,
+ 0x0860, 0x00000000,
+ 0x0864, 0x00000040,
+ 0x0868, 0x000186A0,
+ 0x086C, 0x00030D40,
+ 0x0870, 0x00061A80,
+ 0x0874, 0x000C3500,
+ 0x0878, 0x00000000,
+ 0x087C, 0x00000000,
+ 0x0880, 0x00000000,
+ 0x0884, 0x00000000,
+ 0x0888, 0x00000000,
+ 0x088C, 0x00000000,
+ 0x09FC, 0xE0400000,
+ 0x0A00, 0x00000000,
+ 0x0A04, 0x00000000,
+ 0x0A08, 0x000A0000,
+ 0x0A0C, 0x000008D2,
+ 0x0A10, 0x00000000,
+ 0x0A14, 0x00000040,
+ 0x0A18, 0x000186A0,
+ 0x0A1C, 0x00030D40,
+ 0x0A20, 0x00061A80,
+ 0x0A24, 0x000C3500,
+ 0x0A28, 0x00000000,
+ 0x0A2C, 0x00000000,
+ 0x0A30, 0x00000000,
+ 0x0A34, 0x00000000,
+ 0x0A38, 0x00000000,
+ 0x0A3C, 0x00000000,
+ 0x0BAC, 0xE0400000,
+ 0x0BB0, 0x00000000,
+ 0x0BB4, 0x00000000,
+ 0x0BB8, 0x000A0000,
+ 0x0BBC, 0x000008D2,
+ 0x0BC0, 0x00000000,
+ 0x0BC4, 0x00000040,
+ 0x0BC8, 0x000186A0,
+ 0x0BCC, 0x00030D40,
+ 0x0BD0, 0x00061A80,
+ 0x0BD4, 0x000C3500,
+ 0x0BD8, 0x00000000,
+ 0x0BDC, 0x00000000,
+ 0x0BE0, 0x00000000,
+ 0x0BE4, 0x00000000,
+ 0x0BE8, 0x00000000,
+ 0x0BEC, 0x00000000,
+ 0x0D5C, 0xE0400000,
+ 0x0D60, 0x00000000,
+ 0x0D64, 0x00000000,
+ 0x0D68, 0x000A0000,
+ 0x0D6C, 0x000008D2,
+ 0x0D70, 0x00000000,
+ 0x0D74, 0x00000040,
+ 0x0D78, 0x000186A0,
+ 0x0D7C, 0x00030D40,
+ 0x0D80, 0x00061A80,
+ 0x0D84, 0x000C3500,
+ 0x0D88, 0x00000000,
+ 0x0D8C, 0x00000000,
+ 0x0D90, 0x00000000,
+ 0x0D94, 0x00000000,
+ 0x0D98, 0x00000000,
+ 0x0D9C, 0x00000000,
+ 0x0F0C, 0xE0400000,
+ 0x0F10, 0x00000000,
+ 0x0F14, 0x00000000,
+ 0x0F18, 0x000A0000,
+ 0x0F1C, 0x000008D2,
+ 0x0F20, 0x00000000,
+ 0x0F24, 0x00000040,
+ 0x0F28, 0x000186A0,
+ 0x0F2C, 0x00000000,
+ 0x0F30, 0x00000000,
+ 0x0F34, 0x00000000,
+ 0x0F38, 0x00000000,
+ 0x0F3C, 0x00000000,
+ 0x0F40, 0x00000000,
+ 0x0F44, 0x00000000,
+ 0x0F48, 0x00000000,
+ 0x0F4C, 0x00000000,
+ 0x10BC, 0xE0400000,
+ 0x10C0, 0x00000000,
+ 0x10C4, 0x00000000,
+ 0x10C8, 0x000A0000,
+ 0x10CC, 0x000008D2,
+ 0x10D0, 0x00000000,
+ 0x10D4, 0x00000040,
+ 0x10D8, 0x00000000,
+ 0x10DC, 0x00000000,
+ 0x10E0, 0x00000000,
+ 0x10E4, 0x00000000,
+ 0x10E8, 0x00000000,
+ 0x10EC, 0x00000000,
+ 0x10F0, 0x00000000,
+ 0x10F4, 0x00000000,
+ 0x10F8, 0x00000000,
+ 0x10FC, 0x00000000,
+ 0x126C, 0xE0400000,
+ 0x1270, 0x00000000,
+ 0x1274, 0x00000000,
+ 0x1278, 0x000A0000,
+ 0x127C, 0x000008D2,
+ 0x1280, 0x00000000,
+ 0x1284, 0x00000040,
+ 0x1288, 0x00000000,
+ 0x128C, 0x00000000,
+ 0x1290, 0x00000000,
+ 0x1294, 0x00000000,
+ 0x1298, 0x00000000,
+ 0x129C, 0x00000000,
+ 0x12A0, 0x00000000,
+ 0x12A4, 0x00000000,
+ 0x12A8, 0x00000000,
+ 0x12AC, 0x00000000,
+ 0x141C, 0xE0400000,
+ 0x1420, 0x00000000,
+ 0x1424, 0x00000000,
+ 0x1428, 0x000A0000,
+ 0x142C, 0x000008D2,
+ 0x1430, 0x00000000,
+ 0x1434, 0x00000040,
+ 0x1438, 0x00000000,
+ 0x143C, 0x00000000,
+ 0x1440, 0x00000000,
+ 0x1444, 0x00000000,
+ 0x1448, 0x00000000,
+ 0x144C, 0x00000000,
+ 0x1450, 0x00000000,
+ 0x1454, 0x00000000,
+ 0x1458, 0x00000000,
+ 0x145C, 0x00000000,
+ 0x01D0, 0x00000000,
+ 0x01D4, 0x00000000,
+ 0x01D8, 0x00000000,
+ 0x01DC, 0x00000000,
+ 0x01E0, 0x00000000,
+ 0x01E4, 0xFFFFFFFF,
+ 0x01E8, 0x00000000,
+ 0x01EC, 0x00000000,
+ 0x01F0, 0x00000000,
+ 0x01F4, 0x00000000,
+ 0x01F8, 0x070004B0,
+ 0x01FC, 0x0E000000,
+ 0x0200, 0xF2000000,
+ 0x0204, 0x02000000,
+ 0x0208, 0x02000000,
+ 0x020C, 0x00002000,
+ 0x0210, 0x00010000,
+ 0x0214, 0x80007530,
+ 0x0220, 0x00000000,
+ 0x0224, 0x00000000,
+ 0x0380, 0x00000000,
+ 0x0384, 0x00000000,
+ 0x0388, 0x00000000,
+ 0x038C, 0x00000000,
+ 0x0390, 0x00000000,
+ 0x0394, 0xFFFFFFFF,
+ 0x0398, 0x00000000,
+ 0x039C, 0x00000000,
+ 0x03A0, 0x00000000,
+ 0x03A4, 0x00000000,
+ 0x03A8, 0x070004B0,
+ 0x03AC, 0x0E000000,
+ 0x03B0, 0xF2000000,
+ 0x03B4, 0x02000000,
+ 0x03B8, 0x02000000,
+ 0x03BC, 0x00002000,
+ 0x03C0, 0x00010000,
+ 0x03C4, 0x80007530,
+ 0x03D0, 0x00000000,
+ 0x03D4, 0x00000000,
+ 0x0530, 0x00000000,
+ 0x0534, 0x00000000,
+ 0x0538, 0x00000000,
+ 0x053C, 0x00000000,
+ 0x0540, 0x00000000,
+ 0x0544, 0xFFFFFFFF,
+ 0x0548, 0x00000000,
+ 0x054C, 0x00000000,
+ 0x0550, 0x00000000,
+ 0x0554, 0x00000000,
+ 0x0558, 0x070004B0,
+ 0x055C, 0x0E000000,
+ 0x0560, 0xF2000000,
+ 0x0564, 0x02000000,
+ 0x0568, 0x02000000,
+ 0x056C, 0x00002000,
+ 0x0570, 0x00010000,
+ 0x0574, 0x80007530,
+ 0x0580, 0x00000000,
+ 0x0584, 0x00000000,
+ 0x06E0, 0x00000000,
+ 0x06E4, 0x00000000,
+ 0x06E8, 0x00000000,
+ 0x06EC, 0x00000000,
+ 0x06F0, 0x00000000,
+ 0x06F4, 0xFFFFFFFF,
+ 0x06F8, 0x00000000,
+ 0x06FC, 0x00000000,
+ 0x0700, 0x00000000,
+ 0x0704, 0x00000000,
+ 0x0708, 0x070004B0,
+ 0x070C, 0x0E000000,
+ 0x0710, 0xF2000000,
+ 0x0714, 0x02000000,
+ 0x0718, 0x02000000,
+ 0x071C, 0x00002000,
+ 0x0720, 0x00010000,
+ 0x0724, 0x80007530,
+ 0x0730, 0x00000000,
+ 0x0734, 0x00000000,
+ 0x0890, 0x00000000,
+ 0x0894, 0x00000000,
+ 0x0898, 0x00000000,
+ 0x089C, 0x00000000,
+ 0x08A0, 0x00000000,
+ 0x08A4, 0xFFFFFFFF,
+ 0x08A8, 0x00000000,
+ 0x08AC, 0x00000000,
+ 0x08B0, 0x00000000,
+ 0x08B4, 0x00000000,
+ 0x08B8, 0x070004B0,
+ 0x08BC, 0x0E000000,
+ 0x08C0, 0xF2000000,
+ 0x08C4, 0x02000000,
+ 0x08C8, 0x02000000,
+ 0x08CC, 0x00002000,
+ 0x08D0, 0x00010000,
+ 0x08D4, 0x80007530,
+ 0x08E0, 0x00000000,
+ 0x08E4, 0x00000000,
+ 0x0A40, 0x00000000,
+ 0x0A44, 0x00000000,
+ 0x0A48, 0x00000000,
+ 0x0A4C, 0x00000000,
+ 0x0A50, 0x00000000,
+ 0x0A54, 0xFFFFFFFF,
+ 0x0A58, 0x00000000,
+ 0x0A5C, 0x00000000,
+ 0x0A60, 0x00000000,
+ 0x0A64, 0x00000000,
+ 0x0A68, 0x070004B0,
+ 0x0A6C, 0x0E000000,
+ 0x0A70, 0xF2000000,
+ 0x0A74, 0x02000000,
+ 0x0A78, 0x02000000,
+ 0x0A7C, 0x00002000,
+ 0x0A80, 0x00010000,
+ 0x0A84, 0x80007530,
+ 0x0A90, 0x00000000,
+ 0x0A94, 0x00000000,
+ 0x0BF0, 0x00000000,
+ 0x0BF4, 0x00000000,
+ 0x0BF8, 0x00000000,
+ 0x0BFC, 0x00000000,
+ 0x0C00, 0x00000000,
+ 0x0C04, 0xFFFFFFFF,
+ 0x0C08, 0x00000000,
+ 0x0C0C, 0x00000000,
+ 0x0C10, 0x00000000,
+ 0x0C14, 0x00000000,
+ 0x0C18, 0x070004B0,
+ 0x0C1C, 0x0E000000,
+ 0x0C20, 0xF2000000,
+ 0x0C24, 0x02000000,
+ 0x0C28, 0x02000000,
+ 0x0C2C, 0x00002000,
+ 0x0C30, 0x00010000,
+ 0x0C34, 0x80007530,
+ 0x0C40, 0x00000000,
+ 0x0C44, 0x00000000,
+ 0x0DA0, 0x00000000,
+ 0x0DA4, 0x00000000,
+ 0x0DA8, 0x00000000,
+ 0x0DAC, 0x00000000,
+ 0x0DB0, 0x00000000,
+ 0x0DB4, 0xFFFFFFFF,
+ 0x0DB8, 0x00000000,
+ 0x0DBC, 0x00000000,
+ 0x0DC0, 0x00000000,
+ 0x0DC4, 0x00000000,
+ 0x0DC8, 0x070004B0,
+ 0x0DCC, 0x0E000000,
+ 0x0DD0, 0xF2000000,
+ 0x0DD4, 0x02000000,
+ 0x0DD8, 0x02000000,
+ 0x0DDC, 0x00002000,
+ 0x0DE0, 0x00010000,
+ 0x0DE4, 0x80007530,
+ 0x0DF0, 0x00000000,
+ 0x0DF4, 0x00000000,
+ 0x0F50, 0x00000000,
+ 0x0F54, 0x00000000,
+ 0x0F58, 0x00000000,
+ 0x0F5C, 0x00000000,
+ 0x0F60, 0x00000000,
+ 0x0F64, 0xFFFFFFFF,
+ 0x0F68, 0x00000000,
+ 0x0F6C, 0x00000000,
+ 0x0F70, 0x00000000,
+ 0x0F74, 0x00000000,
+ 0x0F78, 0x070004B0,
+ 0x0F7C, 0x0E000000,
+ 0x0F80, 0xF2000000,
+ 0x0F84, 0x02000000,
+ 0x0F88, 0x02000000,
+ 0x0F8C, 0x00002000,
+ 0x0F90, 0x00010000,
+ 0x0F94, 0x80007530,
+ 0x0FA0, 0x00000000,
+ 0x0FA4, 0x00000000,
+ 0x1100, 0x00000000,
+ 0x1104, 0x00000000,
+ 0x1108, 0x00000000,
+ 0x110C, 0x00000000,
+ 0x1110, 0x00000000,
+ 0x1114, 0xFFFFFFFF,
+ 0x1118, 0x00000000,
+ 0x111C, 0x00000000,
+ 0x1120, 0x00000000,
+ 0x1124, 0x00000000,
+ 0x1128, 0x070004B0,
+ 0x112C, 0x0E000000,
+ 0x1130, 0xF2000000,
+ 0x1134, 0x02000000,
+ 0x1138, 0x02000000,
+ 0x113C, 0x00002000,
+ 0x1140, 0x00010000,
+ 0x1144, 0x80007530,
+ 0x1150, 0x00000000,
+ 0x1154, 0x00000000,
+ 0x12B0, 0x00000000,
+ 0x12B4, 0x00000000,
+ 0x12B8, 0x00000000,
+ 0x12BC, 0x00000000,
+ 0x12C0, 0x00000000,
+ 0x12C4, 0xFFFFFFFF,
+ 0x12C8, 0x00000000,
+ 0x12CC, 0x00000000,
+ 0x12D0, 0x00000000,
+ 0x12D4, 0x00000000,
+ 0x12D8, 0x070004B0,
+ 0x12DC, 0x0E000000,
+ 0x12E0, 0xF2000000,
+ 0x12E4, 0x02000000,
+ 0x12E8, 0x02000000,
+ 0x12EC, 0x00002000,
+ 0x12F0, 0x00010000,
+ 0x12F4, 0x80007530,
+ 0x1300, 0x00000000,
+ 0x1304, 0x00000000,
+ 0x1460, 0x00000000,
+ 0x1464, 0x00000000,
+ 0x1468, 0x00000000,
+ 0x146C, 0x00000000,
+ 0x1470, 0x00000000,
+ 0x1474, 0xFFFFFFFF,
+ 0x1478, 0x00000000,
+ 0x147C, 0x00000000,
+ 0x1480, 0x00000000,
+ 0x1484, 0x00000000,
+ 0x1488, 0x070004B0,
+ 0x148C, 0x0E000000,
+ 0x1490, 0xF2000000,
+ 0x1494, 0x02000000,
+ 0x1498, 0x02000000,
+ 0x149C, 0x00002000,
+ 0x14A0, 0x00010000,
+ 0x14A4, 0x80007530,
+ 0x14B0, 0x00000000,
+ 0x14B4, 0x00000000,
+ 0xF084, 0x00000006,
+ 0x004C, 0xFFFFFFFF,
+ 0x0050, 0xFFFFFFFF,
+ 0x0054, 0xFFFFFFFF,
+ 0x0058, 0xFFFFFFFF,
+ 0x0090, 0x00000000,
+ 0x0094, 0x00000064,
+ 0x0098, 0x40000000,
+ 0x009C, 0x00000000,
+ 0x00A0, 0x00140014,
+ 0x00A4, 0x0019000D,
+ 0x00A8, 0x0096004B,
+};
+#endif
--
2.45.1
^ permalink raw reply related
* [PATCH V1 2/5] Add universal interface for the aw_sar driver.
From: wangshuaijie @ 2024-05-29 13:06 UTC (permalink / raw)
To: dmitry.torokhov, robh, krzk+dt, conor+dt, jeff, linux-input,
devicetree, linux-kernel
Cc: wangshuaijie, liweilei, kangjiajun
In-Reply-To: <20240529130608.783624-1-wangshuaijie@awinic.com>
From: shuaijie wang <wangshuaijie@awinic.com>
Add i2c read-write interfaces and interfaces for parsing bin files.
Signed-off-by: shuaijie wang <wangshuaijie@awinic.com>
---
.../misc/aw_sar/comm/aw_sar_chip_interface.h | 27 +
.../misc/aw_sar/comm/aw_sar_comm_interface.c | 656 ++++++++++++++++++
.../misc/aw_sar/comm/aw_sar_comm_interface.h | 172 +++++
drivers/input/misc/aw_sar/comm/aw_sar_type.h | 396 +++++++++++
4 files changed, 1251 insertions(+)
create mode 100644 drivers/input/misc/aw_sar/comm/aw_sar_chip_interface.h
create mode 100644 drivers/input/misc/aw_sar/comm/aw_sar_comm_interface.c
create mode 100644 drivers/input/misc/aw_sar/comm/aw_sar_comm_interface.h
create mode 100644 drivers/input/misc/aw_sar/comm/aw_sar_type.h
diff --git a/drivers/input/misc/aw_sar/comm/aw_sar_chip_interface.h b/drivers/input/misc/aw_sar/comm/aw_sar_chip_interface.h
new file mode 100644
index 000000000000..d406e48e8136
--- /dev/null
+++ b/drivers/input/misc/aw_sar/comm/aw_sar_chip_interface.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef _SAR_SUPPORT_CHIP_H_
+#define _SAR_SUPPORT_CHIP_H_
+#include "aw_sar_type.h"
+
+enum aw_sar_driver_list_t {
+ AW_SAR_AW9610X,
+ AW_SAR_AW963XX,
+
+ AW_SAR_DRIVER_MAX,
+};
+
+int32_t aw9610x_check_chipid(void *data);
+int32_t aw9610x_init(struct aw_sar *p_sar);
+void aw9610x_deinit(struct aw_sar *p_sar);
+
+int32_t aw963xx_check_chipid(void *data);
+int32_t aw963xx_init(struct aw_sar *p_sar);
+void aw963xx_deinit(struct aw_sar *p_sar);
+
+
+static const struct aw_sar_driver_type g_aw_sar_driver_list[] = {
+ { AW_SAR_AW9610X, aw9610x_check_chipid, aw9610x_init, aw9610x_deinit },
+ { AW_SAR_AW963XX, aw963xx_check_chipid, aw963xx_init, aw963xx_deinit },
+};
+
+#endif
diff --git a/drivers/input/misc/aw_sar/comm/aw_sar_comm_interface.c b/drivers/input/misc/aw_sar/comm/aw_sar_comm_interface.c
new file mode 100644
index 000000000000..1d62ebb60acc
--- /dev/null
+++ b/drivers/input/misc/aw_sar/comm/aw_sar_comm_interface.c
@@ -0,0 +1,656 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "aw_sar_comm_interface.h"
+
+#define AW_I2C_RW_RETRY_TIME_MIN (2000)
+#define AW_I2C_RW_RETRY_TIME_MAX (3000)
+#define AW_RETRIES (5)
+
+static int32_t awinic_i2c_write(struct i2c_client *i2c, uint8_t *tr_data, uint16_t len)
+{
+ struct i2c_msg msg;
+
+ msg.addr = i2c->addr;
+ msg.flags = 0;
+ msg.len = len;
+ msg.buf = tr_data;
+
+ return i2c_transfer(i2c->adapter, &msg, 1);
+}
+
+static int32_t awinic_i2c_read(struct i2c_client *i2c, uint8_t *addr,
+ uint8_t addr_len, uint8_t *data, uint16_t data_len)
+{
+ struct i2c_msg msg[2];
+
+ msg[0].addr = i2c->addr;
+ msg[0].flags = 0;
+ msg[0].len = addr_len;
+ msg[0].buf = addr;
+
+ msg[1].addr = i2c->addr;
+ msg[1].flags = 1;
+ msg[1].len = data_len;
+ msg[1].buf = data;
+
+ return i2c_transfer(i2c->adapter, msg, 2);
+}
+
+/**
+ * @brief Read register interface
+ *
+ * @param i2c: i2c client.
+ * @param reg_addr16: 16 bit register address.
+ * @param *reg_data32: 32 bit register data.
+ * @return 0 if init succeeded.
+ */
+int32_t aw_sar_i2c_read(struct i2c_client *i2c, uint16_t reg_addr16, uint32_t *reg_data32)
+{
+ uint8_t r_buf[6] = { 0 };
+ int8_t cnt = 5;
+ int32_t ret;
+
+ if (!i2c)
+ return -EINVAL;
+
+ r_buf[0] = (unsigned char)(reg_addr16 >> OFFSET_BIT_8);
+ r_buf[1] = (unsigned char)(reg_addr16);
+
+ do {
+ ret = awinic_i2c_read(i2c, r_buf, 2, &r_buf[2], 4);
+ if (ret < 0)
+ dev_err(&i2c->dev, "i2c read error reg: 0x%04x, ret= %d cnt= %d",
+ reg_addr16, ret, cnt);
+ else
+ break;
+ usleep_range(2000, 3000);
+ } while (cnt--);
+
+ if (cnt < 0) {
+ dev_err(&i2c->dev, "i2c read error!");
+ return ret;
+ }
+
+ *reg_data32 = ((uint32_t)r_buf[5] << OFFSET_BIT_0) | ((uint32_t)r_buf[4] << OFFSET_BIT_8) |
+ ((uint32_t)r_buf[3] << OFFSET_BIT_16) | ((uint32_t)r_buf[2] << OFFSET_BIT_24);
+
+ return 0;
+}
+
+/**
+ * @brief write register interface
+ *
+ * @param i2c: i2c client.
+ * @param reg_addr16: 16 bit register address.
+ * @param reg_data32: 32 bit register data.
+ * @return 0 if init succeeded.
+ */
+int32_t aw_sar_i2c_write(struct i2c_client *i2c, uint16_t reg_addr16, uint32_t reg_data32)
+{
+ uint8_t w_buf[6] = { 0 };
+ int8_t cnt = 5;
+ int32_t ret;
+
+ if (!i2c)
+ return -EINVAL;
+
+ /*reg_addr*/
+ w_buf[0] = (uint8_t)(reg_addr16 >> OFFSET_BIT_8);
+ w_buf[1] = (uint8_t)(reg_addr16);
+ /*data*/
+ w_buf[2] = (uint8_t)(reg_data32 >> OFFSET_BIT_24);
+ w_buf[3] = (uint8_t)(reg_data32 >> OFFSET_BIT_16);
+ w_buf[4] = (uint8_t)(reg_data32 >> OFFSET_BIT_8);
+ w_buf[5] = (uint8_t)(reg_data32);
+
+ do {
+ ret = awinic_i2c_write(i2c, w_buf, ARRAY_SIZE(w_buf));
+ if (ret < 0) {
+ dev_err(&i2c->dev,
+ "i2c write error reg: 0x%04x data: 0x%08x, ret= %d cnt= %d",
+ reg_addr16, reg_data32, ret, cnt);
+ } else {
+ break;
+ }
+ } while (cnt--);
+
+ if (cnt < 0) {
+ dev_err(&i2c->dev, "i2c write error!");
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * @brief Write the corresponding bit of the register
+ *
+ * @param i2c:i2c client.
+ * @param reg_addr16: 16 bit register address.
+ * @param mask: Write the corresponding bit as 0
+ * @param val: Write corresponding data to the register
+ * @return 0 if init succeeded.
+ */
+int32_t
+aw_sar_i2c_write_bits(struct i2c_client *i2c, uint16_t reg_addr16, uint32_t mask, uint32_t val)
+{
+ uint32_t reg_val;
+
+ aw_sar_i2c_read(i2c, reg_addr16, ®_val);
+ reg_val &= mask;
+ reg_val |= (val & (~mask));
+ aw_sar_i2c_write(i2c, reg_addr16, reg_val);
+
+ return 0;
+}
+
+/**
+ * @brief Continuously write data to the chip
+ *
+ * @param i2c:i2c client.
+ * @param *tr_data: Data written
+ * @param len: Length of data written
+ * @return 0 if init succeeded.
+ */
+int32_t aw_sar_i2c_write_seq(struct i2c_client *i2c, uint8_t *tr_data, uint16_t len)
+{
+ int8_t cnt = AW_RETRIES;
+ int32_t ret;
+
+ do {
+ ret = awinic_i2c_write(i2c, tr_data, len);
+ if (ret < 0)
+ dev_err(&i2c->dev, "awinic i2c write seq error %d", ret);
+ else
+ break;
+ usleep_range(AW_I2C_RW_RETRY_TIME_MIN, AW_I2C_RW_RETRY_TIME_MAX);
+ } while (cnt--);
+
+ if (cnt < 0) {
+ dev_err(&i2c->dev, "awinic i2c write error!");
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * @brief Continuously Read data from chip
+ *
+ * @param i2c:i2c client.
+ * @param *addr: Read address
+ * @param addr_len: Length of read address (byte)
+ * @param *data: Data written
+ * @param data_len: Length of data written
+ * @return 0 if init succeeded.
+ */
+int32_t aw_sar_i2c_read_seq(struct i2c_client *i2c, uint8_t *addr,
+ uint8_t addr_len, uint8_t *data, uint16_t data_len)
+{
+ int8_t cnt = AW_RETRIES;
+ int32_t ret;
+
+ do {
+ ret = awinic_i2c_read(i2c, addr, addr_len, data, data_len);
+ if (ret < 0)
+ dev_err(&i2c->dev, "awinic sar i2c write error %d", ret);
+ else
+ break;
+ usleep_range(AW_I2C_RW_RETRY_TIME_MIN, AW_I2C_RW_RETRY_TIME_MAX);
+ } while (cnt--);
+
+ if (cnt < 0) {
+ dev_err(&i2c->dev, "awinic sar i2c read error!");
+ return ret;
+ }
+
+ return 0;
+}
+
+/******************************Parse bin file code start****************************************/
+
+#define AWINIC_CODE_VERSION "V0.0.7-V1.0.4" /* "code version"-"excel version" */
+
+enum bin_header_version_enum {
+ HEADER_VERSION_1_0_0 = 0x01000000,
+};
+
+enum data_type_enum {
+ DATA_TYPE_REGISTER = 0x00000000,
+ DATA_TYPE_DSP_REG = 0x00000010,
+ DATA_TYPE_DSP_CFG = 0x00000011,
+ DATA_TYPE_SOC_REG = 0x00000020,
+ DATA_TYPE_SOC_APP = 0x00000021,
+ DATA_TYPE_MULTI_BINS = 0x00002000,
+};
+
+#define BigLittleSwap16(A) ((((unsigned short int)(A) & 0xff00) >> 8) | \
+ (((unsigned short int)(A) & 0x00ff) << 8))
+
+#define BigLittleSwap32(A) ((((unsigned long)(A) & 0xff000000) >> 24) | \
+ (((unsigned long)(A) & 0x00ff0000) >> 8) | \
+ (((unsigned long)(A) & 0x0000ff00) << 8) | \
+ (((unsigned long)(A) & 0x000000ff) << 24))
+
+static int aw_parse_bin_header_1_0_0(struct aw_bin *bin);
+
+/**
+ *
+ * Interface function
+ *
+ * return value:
+ * value = 0 :success;
+ * value = -1 :check bin header version
+ * value = -2 :check bin data type
+ * value = -3 :check sum or check bin data len error
+ * value = -4 :check data version
+ * value = -5 :check register num
+ * value = -6 :check dsp reg num
+ * value = -7 :check soc app num
+ * value = -8 :bin is NULL point
+ *
+ **/
+
+/********************************************************
+ *
+ * check sum data
+ *
+ ********************************************************/
+static enum aw_bin_err_val aw_check_sum(struct aw_bin *bin, int bin_num)
+{
+ unsigned char *p_check_sum;
+ unsigned int sum_data = 0;
+ unsigned int check_sum;
+ unsigned int i;
+
+ p_check_sum = &(bin->info.data[(bin->header_info[bin_num].valid_data_addr -
+ bin->header_info[bin_num].header_len)]);
+ check_sum = AW_SAR_GET_32_DATA(*(p_check_sum + 3), *(p_check_sum + 2),
+ *(p_check_sum + 1), *(p_check_sum));
+
+ for (i = 4; i < bin->header_info[bin_num].bin_data_len +
+ bin->header_info[bin_num].header_len; i++)
+ sum_data += *(p_check_sum + i);
+
+ if (sum_data != check_sum) {
+ p_check_sum = NULL;
+ return AW_BIN_ERROR_SUM_OR_DATA_LEN;
+ }
+ p_check_sum = NULL;
+
+ return AW_BIN_ERROR_NONE;
+}
+
+static enum aw_bin_err_val aw_check_register_num_v1(struct aw_bin *bin, int bin_num)
+{
+ unsigned int check_register_num;
+ unsigned int parse_register_num;
+ char *p_check_sum;
+
+ p_check_sum =
+ &(bin->info.data[(bin->header_info[bin_num].valid_data_addr)]);
+ parse_register_num = AW_SAR_GET_32_DATA(*(p_check_sum + 3), *(p_check_sum + 2),
+ *(p_check_sum + 1), *(p_check_sum));
+ check_register_num = (bin->header_info[bin_num].bin_data_len - 4) /
+ (bin->header_info[bin_num].reg_byte_len +
+ bin->header_info[bin_num].data_byte_len);
+ if (parse_register_num != check_register_num) {
+ p_check_sum = NULL;
+ return AW_BIN_ERROR_REGISTER_NUM;
+ }
+ bin->header_info[bin_num].reg_num = parse_register_num;
+ bin->header_info[bin_num].valid_data_len = bin->header_info[bin_num].bin_data_len - 4;
+ p_check_sum = NULL;
+ bin->header_info[bin_num].valid_data_addr =
+ bin->header_info[bin_num].valid_data_addr + 4;
+
+ return AW_BIN_ERROR_NONE;
+}
+
+static enum aw_bin_err_val aw_check_dsp_reg_num_v1(struct aw_bin *bin, int bin_num)
+{
+ unsigned int check_dsp_reg_num;
+ unsigned int parse_dsp_reg_num;
+ char *p_check_sum;
+
+ p_check_sum =
+ &(bin->info.data[(bin->header_info[bin_num].valid_data_addr)]);
+ parse_dsp_reg_num = AW_SAR_GET_32_DATA(*(p_check_sum + 7),
+ *(p_check_sum + 6),
+ *(p_check_sum + 5),
+ *(p_check_sum + 4));
+ bin->header_info[bin_num].reg_data_byte_len =
+ AW_SAR_GET_32_DATA(*(p_check_sum + 11), *(p_check_sum + 10),
+ *(p_check_sum + 9), *(p_check_sum + 8));
+ check_dsp_reg_num = (bin->header_info[bin_num].bin_data_len -
+ 12) / bin->header_info[bin_num].reg_data_byte_len;
+ if (parse_dsp_reg_num != check_dsp_reg_num) {
+ p_check_sum = NULL;
+ return AW_BIN_ERROR_DSP_REG_NUM;
+ }
+ bin->header_info[bin_num].download_addr =
+ AW_SAR_GET_32_DATA(*(p_check_sum + 3), *(p_check_sum + 2),
+ *(p_check_sum + 1), *(p_check_sum));
+ bin->header_info[bin_num].reg_num = parse_dsp_reg_num;
+ bin->header_info[bin_num].valid_data_len = bin->header_info[bin_num].bin_data_len - 12;
+ p_check_sum = NULL;
+ bin->header_info[bin_num].valid_data_addr =
+ bin->header_info[bin_num].valid_data_addr + 12;
+
+ return AW_BIN_ERROR_NONE;
+}
+
+static enum aw_bin_err_val aw_check_soc_app_num_v1(struct aw_bin *bin, int bin_num)
+{
+ unsigned int check_soc_app_num;
+ unsigned int parse_soc_app_num;
+ char *p_check_sum;
+
+ p_check_sum = &(bin->info.data[(bin->header_info[bin_num].valid_data_addr)]);
+ bin->header_info[bin_num].app_version = AW_SAR_GET_32_DATA(*(p_check_sum + 3),
+ *(p_check_sum + 2),
+ *(p_check_sum + 1),
+ *(p_check_sum));
+ parse_soc_app_num = AW_SAR_GET_32_DATA(*(p_check_sum + 11), *(p_check_sum + 10),
+ *(p_check_sum + 9), *(p_check_sum + 8));
+ check_soc_app_num = bin->header_info[bin_num].bin_data_len - 12;
+ if (parse_soc_app_num != check_soc_app_num) {
+ p_check_sum = NULL;
+ return AW_BIN_ERROR_SOC_APP_NUM;
+ }
+ bin->header_info[bin_num].reg_num = parse_soc_app_num;
+ bin->header_info[bin_num].download_addr =
+ AW_SAR_GET_32_DATA(*(p_check_sum + 7), *(p_check_sum + 6),
+ *(p_check_sum + 5), *(p_check_sum + 4));
+ bin->header_info[bin_num].valid_data_len =
+ bin->header_info[bin_num].bin_data_len - 12;
+ p_check_sum = NULL;
+ bin->header_info[bin_num].valid_data_addr =
+ bin->header_info[bin_num].valid_data_addr + 12;
+
+ return AW_BIN_ERROR_NONE;
+}
+
+/************************
+ *
+ ***bin header 1_0_0
+ ***
+ ************************/
+static void aw_get_single_bin_header_1_0_0(struct aw_bin *bin)
+{
+ int i;
+
+ bin->header_info[bin->all_bin_parse_num].header_len = 60;
+ bin->header_info[bin->all_bin_parse_num].check_sum =
+ AW_SAR_GET_32_DATA(*(bin->p_addr + 3), *(bin->p_addr + 2),
+ *(bin->p_addr + 1), *(bin->p_addr));
+ bin->header_info[bin->all_bin_parse_num].header_ver =
+ AW_SAR_GET_32_DATA(*(bin->p_addr + 7), *(bin->p_addr + 6),
+ *(bin->p_addr + 5), *(bin->p_addr + 4));
+ bin->header_info[bin->all_bin_parse_num].bin_data_type =
+ AW_SAR_GET_32_DATA(*(bin->p_addr + 11), *(bin->p_addr + 10),
+ *(bin->p_addr + 9), *(bin->p_addr + 8));
+ bin->header_info[bin->all_bin_parse_num].bin_data_ver =
+ AW_SAR_GET_32_DATA(*(bin->p_addr + 15), *(bin->p_addr + 14),
+ *(bin->p_addr + 13), *(bin->p_addr + 12));
+ bin->header_info[bin->all_bin_parse_num].bin_data_len =
+ AW_SAR_GET_32_DATA(*(bin->p_addr + 19), *(bin->p_addr + 18),
+ *(bin->p_addr + 17), *(bin->p_addr + 16));
+ bin->header_info[bin->all_bin_parse_num].ui_ver =
+ AW_SAR_GET_32_DATA(*(bin->p_addr + 23), *(bin->p_addr + 22),
+ *(bin->p_addr + 21), *(bin->p_addr + 20));
+ bin->header_info[bin->all_bin_parse_num].reg_byte_len =
+ AW_SAR_GET_32_DATA(*(bin->p_addr + 35), *(bin->p_addr + 34),
+ *(bin->p_addr + 33), *(bin->p_addr + 32));
+ bin->header_info[bin->all_bin_parse_num].data_byte_len =
+ AW_SAR_GET_32_DATA(*(bin->p_addr + 39), *(bin->p_addr + 38),
+ *(bin->p_addr + 37), *(bin->p_addr + 36));
+ bin->header_info[bin->all_bin_parse_num].device_addr =
+ AW_SAR_GET_32_DATA(*(bin->p_addr + 43), *(bin->p_addr + 42),
+ *(bin->p_addr + 41), *(bin->p_addr + 40));
+ for (i = 0; i < 8; i++) {
+ bin->header_info[bin->all_bin_parse_num].chip_type[i] =
+ *(bin->p_addr + 24 + i);
+ }
+// bin->header_info[bin->all_bin_parse_num].chip_type[i] = '\0';
+// DBG("enter chip_type is %s\n", bin->header_info[bin->all_bin_parse_num].chip_type);
+
+ bin->header_info[bin->all_bin_parse_num].reg_num = 0x00000000;
+ bin->header_info[bin->all_bin_parse_num].reg_data_byte_len = 0x00000000;
+ bin->header_info[bin->all_bin_parse_num].download_addr = 0x00000000;
+ bin->header_info[bin->all_bin_parse_num].app_version = 0x00000000;
+ bin->header_info[bin->all_bin_parse_num].valid_data_len = 0x00000000;
+ bin->all_bin_parse_num += 1;
+}
+
+static enum aw_bin_err_val aw_parse_each_of_multi_bins_1_0_0(unsigned int bin_num,
+ int bin_serial_num, struct aw_bin *bin)
+{
+ unsigned int bin_start_addr;
+ unsigned int valid_data_len;
+ enum aw_bin_err_val ret;
+
+ if (!bin_serial_num) {
+ bin_start_addr = AW_SAR_GET_32_DATA(*(bin->p_addr + 67),
+ *(bin->p_addr + 66),
+ *(bin->p_addr + 65),
+ *(bin->p_addr + 64));
+ bin->p_addr += (60 + bin_start_addr);
+ bin->header_info[bin->all_bin_parse_num].valid_data_addr =
+ bin->header_info[bin->all_bin_parse_num -
+ 1].valid_data_addr + 4 + 8 * bin_num + 60;
+ } else {
+ valid_data_len =
+ bin->header_info[bin->all_bin_parse_num - 1].bin_data_len;
+ bin->p_addr += (60 + valid_data_len);
+ bin->header_info[bin->all_bin_parse_num].valid_data_addr =
+ bin->header_info[bin->all_bin_parse_num -
+ 1].valid_data_addr +
+ bin->header_info[bin->all_bin_parse_num - 1].bin_data_len +
+ 60;
+ }
+
+ ret = aw_parse_bin_header_1_0_0(bin);
+ return ret;
+}
+
+/* Get the number of bins in multi bins, and set a for loop, loop processing each bin data */
+static enum aw_bin_err_val aw_get_multi_bin_header_1_0_0(struct aw_bin *bin)
+{
+ unsigned int bin_num;
+ enum aw_bin_err_val ret;
+ int i;
+
+ bin_num = AW_SAR_GET_32_DATA(*(bin->p_addr + 63),
+ *(bin->p_addr + 62),
+ *(bin->p_addr + 61), *(bin->p_addr + 60));
+ if (bin->multi_bin_parse_num == 1)
+ bin->header_info[bin->all_bin_parse_num].valid_data_addr = 60;
+ aw_get_single_bin_header_1_0_0(bin);
+
+ for (i = 0; i < bin_num; i++) {
+ ret = aw_parse_each_of_multi_bins_1_0_0(bin_num, i, bin);
+ if (ret < 0)
+ return ret;
+ }
+ return AW_BIN_ERROR_NONE;
+}
+
+/********************************************************
+ *
+ * If the bin framework header version is 1.0.0,
+ * determine the data type of bin, and then perform different processing
+ * according to the data type
+ * If it is a single bin data type, write the data directly into the structure array
+ * If it is a multi-bin data type, first obtain the number of bins,
+ * and then recursively call the bin frame header processing function
+ * according to the bin number to process the frame header information of each bin separately
+ *
+ ********************************************************/
+static enum aw_bin_err_val aw_parse_bin_header_1_0_0(struct aw_bin *bin)
+{
+ unsigned int bin_data_type;
+ enum aw_bin_err_val ret;
+
+ bin_data_type = AW_SAR_GET_32_DATA(*(bin->p_addr + 11),
+ *(bin->p_addr + 10),
+ *(bin->p_addr + 9), *(bin->p_addr + 8));
+ switch (bin_data_type) {
+ case DATA_TYPE_REGISTER:
+ case DATA_TYPE_DSP_REG:
+ case DATA_TYPE_SOC_APP:
+ // Divided into two processing methods,
+ // one is single bin processing,
+ // and the other is single bin processing in multi bin
+ bin->single_bin_parse_num += 1;
+ if (!bin->multi_bin_parse_num)
+ bin->header_info[bin->all_bin_parse_num].valid_data_addr = 60;
+ aw_get_single_bin_header_1_0_0(bin);
+ break;
+ case DATA_TYPE_MULTI_BINS:
+ /* Get the number of times to enter multi bins */
+ bin->multi_bin_parse_num += 1;
+ ret = aw_get_multi_bin_header_1_0_0(bin);
+ if (ret < 0)
+ return ret;
+ break;
+ default:
+ return AW_BIN_ERROR_DATA_TYPE;
+ }
+ return AW_BIN_ERROR_NONE;
+}
+
+/* get the bin's header version */
+static enum aw_bin_err_val aw_check_bin_header_version(struct aw_bin *bin)
+{
+ unsigned int header_version;
+ enum aw_bin_err_val ret;
+
+ header_version = AW_SAR_GET_32_DATA(*(bin->p_addr + 7), *(bin->p_addr + 6),
+ *(bin->p_addr + 5), *(bin->p_addr + 4));
+
+
+ // Write data to the corresponding structure array
+ // according to different formats of the bin frame header version
+ switch (header_version) {
+ case HEADER_VERSION_1_0_0:
+ ret = aw_parse_bin_header_1_0_0(bin);
+ return ret;
+ default:
+ return AW_BIN_ERROR_HEADER_VERSION;
+ }
+}
+
+/**
+ * @brief Parse bin file
+ *
+ * @param bin: Store the contents of the parsed bin file
+ * @return 0 if init succeeded, other if error
+ */
+enum aw_bin_err_val aw_sar_parsing_bin_file(struct aw_bin *bin)
+{
+ enum aw_bin_err_val ret;
+ int i;
+
+ if (!bin)
+ return AW_BIN_ERROR_NULL_POINT;
+ bin->p_addr = bin->info.data;
+ bin->all_bin_parse_num = 0;
+ bin->multi_bin_parse_num = 0;
+ bin->single_bin_parse_num = 0;
+
+ /* filling bins header info */
+ ret = aw_check_bin_header_version(bin);
+ if (ret < 0)
+ return ret;
+ bin->p_addr = NULL;
+
+ /* check bin header info */
+ for (i = 0; i < bin->all_bin_parse_num; i++) {
+ /* check sum */
+ ret = aw_check_sum(bin, i);
+ if (ret < 0)
+ return ret;
+
+ /* check register num */
+ if (bin->header_info[i].bin_data_type == DATA_TYPE_REGISTER) {
+ ret = aw_check_register_num_v1(bin, i);
+ if (ret < 0)
+ return ret;
+ /* check dsp reg num */
+ } else if (bin->header_info[i].bin_data_type == DATA_TYPE_DSP_REG) {
+ ret = aw_check_dsp_reg_num_v1(bin, i);
+ if (ret < 0)
+ return ret;
+ /* check soc app num */
+ } else if (bin->header_info[i].bin_data_type == DATA_TYPE_SOC_APP) {
+ ret = aw_check_soc_app_num_v1(bin, i);
+ if (ret < 0)
+ return ret;
+ } else {
+ bin->header_info[i].valid_data_len = bin->header_info[i].bin_data_len;
+ }
+ }
+
+ return AW_BIN_ERROR_NONE;
+}
+/*********************************Parse bin file code end************************************/
+
+/**
+ * @brief Calculate the second power
+ *
+ * @param cnt: ifrequency
+ * @return the second power
+ */
+uint32_t aw_sar_pow2(uint32_t cnt)
+{
+ uint32_t sum = 1;
+ uint32_t i;
+
+ if (cnt == 0) {
+ sum = 1;
+ } else {
+ for (i = 0; i < cnt; i++)
+ sum *= 2;
+ }
+
+ return sum;
+}
+
+/**
+ * @brief Calculate the second power
+ *
+ * @param *aw_bin: Information after parsing bin file
+ * @param *i2c: i2c client.
+ * @return 0 if init succeeded.
+ */
+int32_t aw_sar_load_reg(struct aw_bin *aw_bin, struct i2c_client *i2c)
+{
+ uint32_t start_addr = aw_bin->header_info[0].valid_data_addr;
+ uint16_t reg_addr;
+ uint32_t reg_data;
+ int32_t ret;
+ uint32_t i;
+
+ for (i = 0; i < aw_bin->header_info[0].valid_data_len; i += 6, start_addr += 6) {
+ reg_addr = (aw_bin->info.data[start_addr]) |
+ aw_bin->info.data[start_addr + 1] << OFFSET_BIT_8;
+ reg_data = aw_bin->info.data[start_addr + 2] |
+ (aw_bin->info.data[start_addr + 3] << OFFSET_BIT_8) |
+ (aw_bin->info.data[start_addr + 4] << OFFSET_BIT_16) |
+ (aw_bin->info.data[start_addr + 5] << OFFSET_BIT_24);
+
+ ret = aw_sar_i2c_write(i2c, reg_addr, reg_data);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "i2c write err");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+void aw_sar_delay_ms(uint32_t ms)
+{
+ mdelay(ms);
+}
+
diff --git a/drivers/input/misc/aw_sar/comm/aw_sar_comm_interface.h b/drivers/input/misc/aw_sar/comm/aw_sar_comm_interface.h
new file mode 100644
index 000000000000..6f35193f2aee
--- /dev/null
+++ b/drivers/input/misc/aw_sar/comm/aw_sar_comm_interface.h
@@ -0,0 +1,172 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef _AW_SAR_PLAT_HW_INTERFACE_H_
+#define _AW_SAR_PLAT_HW_INTERFACE_H_
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/of_gpio.h>
+#include <linux/power_supply.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+enum aw_sar_chip_list_t {
+ AW_SAR_NONE_CHECK_CHIP,
+
+ SAR_AW9610X = 1 << 1,
+ SAR_AW9610XA = 1 << 2,
+
+ SAR_AW96303 = 1 << 6,
+ SAR_AW96305 = 1 << 7,
+ SAR_AW96308 = 1 << 8,
+ SAR_AW96310 = 1 << 9,
+ SAR_AW96312 = 1 << 10,
+};
+
+enum AW_SAR_UPDATE_FW_MODE {
+ PROT_UPDATE_FW,
+ REG_UPDATE_FW,
+};
+
+#ifndef AW_TRUE
+#define AW_TRUE (1)
+#endif
+
+#ifndef AW_FALSE
+#define AW_FALSE (0)
+#endif
+
+#define AW_ERR_IRQ_INIT_OVER (0xAA)
+
+enum aw_sar_rst_val {
+ AW_OK,
+ AW_BIN_PARA_INVALID,
+ AW_PROT_UPDATE_ERR,
+ AW_REG_LOAD_ERR,
+};
+
+#ifndef OFFSET_BIT_0
+#define OFFSET_BIT_0 (0)
+#endif
+
+#ifndef OFFSET_BIT_8
+#define OFFSET_BIT_8 (8)
+#endif
+
+#ifndef OFFSET_BIT_16
+#define OFFSET_BIT_16 (16)
+#endif
+
+#ifndef OFFSET_BIT_24
+#define OFFSET_BIT_24 (24)
+#endif
+
+#define AW_SAR_GET_32_DATA(w, x, y, z) ((unsigned int)(((w) << 24) | ((x) << 16) | ((y) << 8) | (z)))
+
+enum AW_SAR_HOST_IRQ_STAT {
+ IRQ_ENABLE,
+ IRQ_DISABLE,
+};
+
+#define AW_SAR_BIN_NUM_MAX 100
+
+enum aw_bin_err_val {
+ AW_BIN_ERROR_NONE = 0,
+ AW_BIN_ERROR_HEADER_VERSION = -1,
+ AW_BIN_ERROR_DATA_TYPE = -2,
+ AW_BIN_ERROR_SUM_OR_DATA_LEN = -3,
+ AW_BIN_ERROR_DATA_VERSION = -4,
+ AW_BIN_ERROR_REGISTER_NUM = -5,
+ AW_BIN_ERROR_DSP_REG_NUM = -6,
+ AW_BIN_ERROR_SOC_APP_NUM = -7,
+ AW_BIN_ERROR_NULL_POINT = -8,
+};
+
+/**
+ * struct bin_header_info -
+ * @header_len: Frame header length
+ * @check_sum: Frame header information-Checksum
+ * @header_ver: Frame header information-Frame header version
+ * @bin_data_type: Frame header information-Data type
+ * @bin_data_ver: Frame header information-Data version
+ * @bin_data_len: Frame header information-Data length
+ * @ui_ver: Frame header information-ui version
+ * @chip_type: Frame header information-chip type
+ * @reg_byte_len: Frame header information-reg byte len
+ * @data_byte_len: Frame header information-data byte len
+ * @device_addr: Frame header information-device addr
+ * @valid_data_len: Length of valid data obtained after parsing
+ * @valid_data_addr: The offset address of the valid data obtained
+ * after parsing relative to info
+ * @reg_num: The number of registers obtained after parsing
+ * @reg_data_byte_len: The byte length of the register obtained after parsing
+ * @download_addr: The starting address or download address obtained after parsing
+ * @app_version: The software version number obtained after parsing
+ */
+struct bin_header_info {
+ unsigned int header_len;
+ unsigned int check_sum;
+ unsigned int header_ver;
+ unsigned int bin_data_type;
+ unsigned int bin_data_ver;
+ unsigned int bin_data_len;
+ unsigned int ui_ver;
+ unsigned char chip_type[8];
+ unsigned int reg_byte_len;
+ unsigned int data_byte_len;
+ unsigned int device_addr;
+ unsigned int valid_data_len;
+ unsigned int valid_data_addr;
+ unsigned int reg_num;
+ unsigned int reg_data_byte_len;
+ unsigned int download_addr;
+ unsigned int app_version;
+};
+
+/**
+ * struct bin_container -
+ * @len: The size of the bin file obtained from the firmware
+ * @data: Store the bin file obtained from the firmware
+ */
+struct bin_container {
+ unsigned int len;
+ unsigned char data[];
+};
+
+/**
+ * struct aw_bin -
+ * @p_addr: Offset pointer (backward offset pointer to obtain frame header information and
+ * important information)
+ * @all_bin_parse_num: The number of all bin files
+ * @multi_bin_parse_num: The number of single bin files
+ * @single_bin_parse_num: The number of multiple bin files
+ * @header_info: Frame header information and
+ * other important data obtained after parsing
+ * @info: Obtained bin file data that needs to be parsed
+ */
+struct aw_bin {
+ char *p_addr;
+ unsigned int all_bin_parse_num;
+ unsigned int multi_bin_parse_num;
+ unsigned int single_bin_parse_num;
+ struct bin_header_info header_info[AW_SAR_BIN_NUM_MAX];
+ struct bin_container info;
+};
+
+//I2C communication API
+extern int32_t aw_sar_i2c_read(struct i2c_client *i2c, uint16_t reg_addr16, uint32_t *reg_data32);
+extern int32_t aw_sar_i2c_write(struct i2c_client *i2c, uint16_t reg_addr16, uint32_t reg_data32);
+extern int32_t aw_sar_i2c_write_bits(struct i2c_client *i2c, uint16_t reg_addr16,
+ uint32_t mask, uint32_t val);
+extern int32_t aw_sar_i2c_write_seq(struct i2c_client *i2c, uint8_t *tr_data, uint16_t len);
+extern int32_t aw_sar_i2c_read_seq(struct i2c_client *i2c, uint8_t *addr,
+ uint8_t addr_len, uint8_t *data, uint16_t data_len);
+extern void aw_sar_delay_ms(uint32_t ms);
+
+extern enum aw_bin_err_val aw_sar_parsing_bin_file(struct aw_bin *bin);
+extern uint32_t aw_sar_pow2(uint32_t cnt);
+extern int32_t aw_sar_load_reg(struct aw_bin *aw_bin, struct i2c_client *i2c);
+
+#endif
diff --git a/drivers/input/misc/aw_sar/comm/aw_sar_type.h b/drivers/input/misc/aw_sar/comm/aw_sar_type.h
new file mode 100644
index 000000000000..8f217f8e21dd
--- /dev/null
+++ b/drivers/input/misc/aw_sar/comm/aw_sar_type.h
@@ -0,0 +1,396 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef _SAR_TYPE_H_
+#define _SAR_TYPE_H_
+
+#include "aw_sar_comm_interface.h"
+
+typedef int32_t (*aw_sar_chip_other_operation_t)(void *data);
+typedef void (*aw_sar_chip_other_opera_free_t)(void *data);
+
+enum aw_i2c_flags {
+ AW_SAR_I2C_WR,
+ AW_SAR_I2C_RD,
+ AW_SAR_PACKAGE_RD,
+};
+
+enum sar_health_check {
+ AW_SAR_HEALTHY = 0,
+ AW_SAR_UNHEALTHY = 1,
+};
+typedef int32_t (*aw_sar_bin_opera_t)(struct aw_bin *aw_bin, void *load_bin_para);
+typedef int32_t (*aw_sar_bin_load_fail_opera_t)(struct aw_bin *aw_bin, void *load_bin_para);
+
+struct aw_sar_get_chip_info_t {
+ void (*p_get_chip_info_node_fn)(void *data, char *buf, ssize_t *p_len);
+};
+
+struct aw_sar_load_bin_t {
+ const uint8_t *bin_name;
+ aw_sar_bin_opera_t bin_opera_func;
+ aw_sar_bin_load_fail_opera_t bin_load_fail_opera;
+
+ void (*p_get_prot_update_fw_node_fn)(void *data, char *buf, ssize_t *p_len);
+
+ /*Perform different operations to update parameters*/
+ int32_t (*p_update_fn)(void *data);
+};
+
+struct aw_sar_reg_data {
+ unsigned char rw;
+ unsigned short reg;
+};
+
+struct aw_sar_awrw_t {
+ ssize_t (*p_set_awrw_node_fn)(void *data, const char *buf, size_t count);
+ ssize_t (*p_get_awrw_node_fn)(void *data, char *buf);
+};
+
+struct aw_sar_reg_list_t {
+ uint8_t reg_none_access;
+ uint8_t reg_rd_access;
+ uint8_t reg_wd_access;
+ const struct aw_sar_reg_data *reg_perm;
+ uint32_t reg_num;
+};
+
+typedef void (*aw_sar_update_work_t)(struct work_struct *work);
+struct aw_sar_update_static_t {
+ aw_sar_update_work_t update_work_func;
+ uint32_t delay_ms;
+};
+
+typedef irqreturn_t (*aw_sar_irq_t)(int irq, void *data);
+typedef uint32_t (*sar_rc_irqscr_t)(void *i2c);
+/*
+ * If the return value is 1, there is an initialization completion interrupt;
+ * if the return value is 0, there is no
+ */
+typedef uint32_t (*aw_sar_is_init_over_irq)(uint32_t irq_status);
+typedef void (*aw_sar_irq_spec_handler_t)(uint32_t irq_status, void *data);
+
+struct aw_sar_check_chipid_t {
+ /*Read chipid and check chipid, Must be implemented externally*/
+ int32_t (*p_check_chipid_fn)(void *data);
+};
+
+struct aw_sar_irq_init_t {
+ unsigned long flags;
+ unsigned long irq_flags;
+ irq_handler_t handler;
+ irq_handler_t thread_fn;
+ /*Interrupt processing parameters*/
+ sar_rc_irqscr_t rc_irq_fn;
+ //aw_sar_is_init_over_irq is_init_over_irq_fn;
+ aw_sar_irq_spec_handler_t irq_spec_handler_fn;
+
+ /*Use a different initialization interrupt to initialize the operation*/
+ int32_t (*p_irq_init_fn)(void *data);
+ /*Release interrupt resource*/
+// void const (*p_irq_deinit_fn)(void *data);
+ int (*p_irq_deinit_fn)(void *data);
+};
+
+struct aw_sar_pm_t {
+ uint32_t suspend_set_mode;
+ uint32_t resume_set_mode;
+ uint32_t shutdown_set_mode;
+ //system api
+ int32_t (*p_suspend_fn)(void *data);
+ int32_t (*p_resume_fn)(void *data);
+ int32_t (*p_shutdown_fn)(void *data);
+};
+
+struct aw_sar_chip_mode_t {
+ uint32_t init_mode;
+ uint32_t active;
+ uint32_t pre_init_mode;
+};
+
+struct aw_sar_regulator_config_t {
+ //Note that "_sar_num" after VCC name is defined by SAR C auto add
+ const uint8_t *vcc_name;
+ int32_t min_uV;
+ int32_t max_uV;
+};
+
+struct aw_channels_info {
+ uint16_t used;
+ uint32_t last_channel_info;
+ struct input_dev *input;
+ uint8_t name[20];
+};
+
+struct aw_sar_dts_info {
+ uint32_t sar_num;
+ int32_t irq_gpio;
+ uint32_t channel_use_flag;
+ bool use_regulator_flag;
+ bool use_inter_pull_up;
+ bool use_pm;
+ bool update_fw_flag;
+ bool use_plug_cail_flag;
+ bool monitor_esd_flag;
+};
+
+struct aw_sar_irq_init_comm_t {
+ int32_t to_irq;
+ uint8_t host_irq_stat;
+ void *data;
+ uint8_t label[30];
+ uint8_t dev_id[30];
+};
+
+struct aw_sar_load_bin_comm_t {
+ uint8_t bin_name[30];
+ uint32_t bin_data_ver;
+ aw_sar_bin_opera_t bin_opera_func;
+ aw_sar_bin_load_fail_opera_t bin_load_fail_opera_func;
+};
+
+struct aw_awrw_info {
+ uint8_t rw_flag;
+ uint8_t addr_len;
+ uint8_t data_len;
+ uint8_t reg_num;
+ uint32_t i2c_tranfar_data_len;
+ uint8_t *p_i2c_tranfar_data;
+};
+
+/****************mode set start******************/
+typedef void (*sar_enable_clock_t)(void *i2c);
+typedef void (*sar_operation_irq_t)(int to_irq);
+typedef void (*sar_mode_update_t)(void *i2c);
+
+struct aw_sar_mode_switch_ops {
+ sar_enable_clock_t enable_clock;
+ sar_rc_irqscr_t rc_irqscr;
+ sar_mode_update_t mode_update;
+};
+
+struct aw_sar_chip_mode {
+ uint8_t curr_mode;
+ uint8_t last_mode;
+};
+
+struct aw_sar_mode_set_t {
+ uint8_t chip_id;
+ struct aw_sar_chip_mode chip_mode;
+ struct aw_sar_mode_switch_ops mode_switch_ops;
+};
+
+struct aw_sar_mode_t {
+ const struct aw_sar_mode_set_t *mode_set_arr;
+ uint8_t mode_set_arr_len;
+ ssize_t (*p_set_mode_node_fn)(void *data, uint8_t curr_mode);
+ ssize_t (*p_get_mode_node_fn)(void *data, char *buf);
+};
+/********************mode set end****************/
+
+struct aw_sar_init_over_irq_t {
+ int16_t wait_times;
+ uint8_t daley_step;
+ uint32_t reg_irqsrc;
+ uint32_t irq_offset_bit;
+ uint32_t irq_mask;
+ uint32_t irq_flag;
+ /*
+ * Perform different verification initialization
+ * to complete the interrupt operation
+ */
+ int32_t (*p_check_init_over_irq_fn)(void *data);
+ /*
+ * When initialization fails, get the corresponding error type and
+ * apply it to the chip with flash
+ */
+ int32_t (*p_get_err_type_fn)(void *data);
+};
+
+struct aw_sar_soft_rst_t {
+ uint16_t reg_rst;
+ uint32_t reg_rst_val;
+ uint32_t delay_ms;
+ /*Perform different soft reset operations*/
+ int32_t (*p_soft_reset_fn)(void *data);
+};
+
+struct aw_sar_aot_t {
+ uint32_t aot_reg;
+ uint32_t aot_mask;
+ uint32_t aot_flag;
+ ssize_t (*p_set_aot_node_fn)(void *data);
+};
+
+struct aw_sar_diff_t {
+ uint16_t diff0_reg;
+ uint16_t diff_step;
+ //Data format:S21.10, Floating point types generally need to be removed
+ uint32_t rm_float;
+ ssize_t (*p_get_diff_node_fn)(void *data, char *buf);
+};
+
+struct aw_sar_offset_t {
+ ssize_t (*p_get_offset_node_fn)(void *data, char *buf);
+};
+
+struct aw_sar_pinctrl {
+ struct pinctrl *pinctrl;
+ struct pinctrl_state *default_sta;
+ struct pinctrl_state *int_out_high;
+ struct pinctrl_state *int_out_low;
+};
+
+//update reg node
+struct aw_sar_para_load_t {
+ const uint32_t *reg_arr;
+ uint32_t reg_arr_len;
+};
+
+struct aw_sar_platform_config {
+ /*The chip needs to parse more DTS contents for addition*/
+ int32_t (*p_add_parse_dts_fn)(void *data);
+
+ const struct aw_sar_regulator_config_t *p_regulator_config;
+
+ /*The chip needs to add more nodes*/
+ int32_t (*p_add_node_create_fn)(void *data);
+ /*Release the added node*/
+ int32_t (*p_add_node_free_fn)(void *data);
+
+ /*Use a different initialization interrupt to initialize the operation*/
+ int32_t (*p_input_init_fn)(void *data);
+ /*Release input resource*/
+ int32_t (*p_input_deinit_fn)(void *data);
+
+ //The parameters passed in are required for interrupt initialization
+ const struct aw_sar_irq_init_t *p_irq_init;
+
+ //The chip is set to different modes in different power management interfaces
+ const struct aw_sar_pm_t *p_pm_chip_mode;
+};
+
+struct aw_sar_power_on_prox_detection_t {
+ //en_flag is true enable
+ void (*p_power_on_prox_detection_en_fn)(void *data, uint8_t en_flag);
+ uint32_t irq_en_cali_bit;
+ uint8_t power_on_prox_en_flag;
+};
+
+
+/*Parameter passed in by different SAR sensors.
+ *It must be implemented in each sensor code.
+ *If it is not necessary that some members can be assigned null,
+ *the corresponding function will not be implemented
+ */
+struct aw_sar_chip_config {
+ uint8_t ch_num_max; //Number of channels of the chip
+
+ //Chip related platform content configuration
+ const struct aw_sar_platform_config *p_platform_config;
+ //Parameters required for verification of chipid
+ const struct aw_sar_check_chipid_t *p_check_chipid;
+ //Parameters required for soft reset
+ const struct aw_sar_soft_rst_t *p_soft_rst;
+ //Verify the parameters required to initialize a complete interrupt
+ const struct aw_sar_init_over_irq_t *p_init_over_irq;
+ //Parameters required for load boot bin file,
+ //If the chip does not have flash, please ignore and assign the value to null
+ const struct aw_sar_load_bin_t *p_fw_bin;
+ //Parameters required for load register bin file
+ const struct aw_sar_load_bin_t *p_reg_bin;
+ //The mode set before and after the initialization of the chip
+ const struct aw_sar_chip_mode_t *p_chip_mode;
+
+ //Node usage parameters
+ //Register permission table
+ const struct aw_sar_reg_list_t *p_reg_list;
+ //Default register table
+ const struct aw_sar_para_load_t *p_reg_arr;
+ //Parameters required for set Auto-Offset-Tuning(aot)
+ const struct aw_sar_aot_t *p_aot;
+ //Parameters required for get chip diff val
+ const struct aw_sar_diff_t *p_diff;
+ //Parameters required for get chip offset val
+ const struct aw_sar_offset_t *p_offset;
+ //Set the parameters of different working modes of the chip
+ const struct aw_sar_mode_t *p_mode;
+ //Upgrading firmware using the debug node protocol
+ const struct aw_sar_load_bin_t *p_prox_fw;
+ //Upgrading firmware using the debug node reg
+ const struct aw_sar_load_bin_t *p_reg_fw;
+ //Obtain the necessary information of the chip
+ const struct aw_sar_get_chip_info_t *p_get_chip_info;
+ //Continuous read/write register interface
+ const struct aw_sar_awrw_t *p_aw_sar_awrw;
+ //Parameters required for load boot bin file,
+ //If the chip does not have flash, please ignore and assign the value to null
+ const struct aw_sar_load_bin_t *p_boot_bin;
+
+ /*Other operations during initialization, Add according to different usage*/
+ aw_sar_chip_other_operation_t p_other_operation;
+ /*If requested by resources, please release*/
+ aw_sar_chip_other_opera_free_t p_other_opera_free;
+
+ const struct aw_sar_power_on_prox_detection_t *power_on_prox_detection;
+};
+
+struct aw_sar {
+ struct i2c_client *i2c;
+ struct device *dev;
+ struct regulator *vcc;
+ struct delayed_work update_work;
+ //Set pin pull-up mode
+ struct aw_sar_pinctrl pinctrl;
+ /* eds work */
+ struct delayed_work monitor_work;
+ struct workqueue_struct *monitor_wq;
+
+ uint8_t chip_type;
+ uint8_t chip_name[20];
+
+ bool power_enable;
+ bool fw_fail_flag;
+ uint8_t last_mode;
+
+ /*handler_anomalies*/
+ uint8_t fault_flag;
+ uint8_t driver_code_initover_flag;
+ /*handler_anomalies*/
+
+ uint8_t ret_val;
+ uint8_t curr_use_driver_type;
+ int32_t prot_update_state;
+
+ uint8_t aot_irq_num;
+ uint8_t enter_irq_handle_num;
+ uint8_t exit_power_on_prox_detection;
+
+ struct work_struct ps_notify_work;
+ struct notifier_block ps_notif;
+ bool ps_is_present;
+
+ //Parameters related to platform logic
+ struct aw_sar_dts_info dts_info;
+ struct aw_sar_load_bin_comm_t load_bin;
+ struct aw_sar_irq_init_comm_t irq_init;
+ struct aw_awrw_info awrw_info;
+ struct aw_channels_info *channels_arr;
+
+ //Private arguments required for public functions
+ const struct aw_sar_chip_config *p_sar_para;
+ //Private arguments required for private functions
+ void *priv_data;
+};
+
+//Determine whether the chip exists by verifying chipid
+typedef int32_t (*aw_sar_who_am_i_t)(void *data);
+typedef int32_t (*aw_sar_chip_init_t)(struct aw_sar *p_sar);
+typedef void (*aw_sar_chip_deinit_t)(struct aw_sar *p_sar);
+
+struct aw_sar_driver_type {
+ uint8_t driver_type;
+ aw_sar_who_am_i_t p_who_am_i;
+ aw_sar_chip_init_t p_chip_init;
+ aw_sar_chip_deinit_t p_chip_deinit;
+};
+
+#endif
--
2.45.1
^ permalink raw reply related
* [PATCH V1 1/5] dt-bindings: input: Add YAML to Awinic sar sensor.
From: wangshuaijie @ 2024-05-29 13:06 UTC (permalink / raw)
To: dmitry.torokhov, robh, krzk+dt, conor+dt, jeff, linux-input,
devicetree, linux-kernel
Cc: wangshuaijie, liweilei, kangjiajun
In-Reply-To: <20240529130608.783624-1-wangshuaijie@awinic.com>
From: shuaijie wang <wangshuaijie@awinic.com>
Add the awinic,aw_sar.yaml file to adapt to the awinic sar sensor driver.
Signed-off-by: shuaijie wang <wangshuaijie@awinic.com>
---
.../bindings/input/awinic,aw_sar.yaml | 110 ++++++++++++++++++
1 file changed, 110 insertions(+)
create mode 100644 Documentation/devicetree/bindings/input/awinic,aw_sar.yaml
diff --git a/Documentation/devicetree/bindings/input/awinic,aw_sar.yaml b/Documentation/devicetree/bindings/input/awinic,aw_sar.yaml
new file mode 100644
index 000000000000..ed4ec29c9b4d
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/awinic,aw_sar.yaml
@@ -0,0 +1,110 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/input/awinic,aw_sar.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Awinic sar sensor driver family
+
+maintainers:
+ - Shuaijie Wang <wangshuaijie@awinic.com>
+
+properties:
+ compatible:
+ enum:
+ - awinic,aw_aw96103
+ - awinic,aw_aw96105
+ - awinic,aw_aw96303
+ - awinic,aw_aw96305
+ - awinic,aw_aw96308
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ sar-num:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ set the index of the sar sensor.
+
+ vcc0-supply:
+ description:
+ Optional regulator for chip, 1.7V-3.6V.
+
+ channel_use_flag:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ The flag of channels used.
+ Configure according to the specific chip channel used.
+ Bit[31:0] Each bit represents a channel.
+ If the customer uses ch0 and ch2, then channel_use_flag=<0x05>
+
+ aw_sar,update_fw:
+ type: boolean
+ description:
+ Choose if you want to update the firmware.
+
+ aw_sar,monitor_esd:
+ type: boolean
+ description:
+ Choose if you want to monitor ESD.
+
+ aw_sar,pin_set_inter_pull-up:
+ type: boolean
+ description:
+ Choose if you want to set the interrupt pin to internal pull-up.
+
+ aw_sar,using_pm_ops:
+ type: boolean
+ description:
+ Choose if you want to use suspend and resume related function.
+
+ aw_sar,use_plug_cail:
+ type: boolean
+ description:
+ Choose If you want to perform calibration when plugging and unplugging the charger.
+
+ start-mode:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ When connecting to aw963xx, select the location where the firmware starts.
+ set 0 if start in rom.
+ set 1 if start in ram
+
+ irq-mux:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ set csx as irq pin. config this field when connect to aw96308/aw96305BFOR
+
+required:
+ - compatible
+ - reg
+ - sar-num
+ - interrupts
+ - channel_use_flag
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ awinic_sar@12 {
+ compatible = "awinic,aw_sar";
+ reg = <0x12>;
+ sar-num = < 0 >;
+ interrupt-parent = < &tlmm >;
+ interrupts = <72 0>;
+ //vcc0-supply = <&pm660l_l4>;
+ channel_use_flag = <0xff>;
+ aw_sar,update_fw;
+ //aw_sar,monitor_esd;
+ start-mode = < 1 >;
+ irq-mux = < 2 >;
+ };
+ };
--
2.45.1
^ permalink raw reply related
* [PATCH V1 3/5] Add aw9610x series related interfaces to the aw_sar driver.
From: wangshuaijie @ 2024-05-29 13:06 UTC (permalink / raw)
To: dmitry.torokhov, robh, krzk+dt, conor+dt, jeff, linux-input,
devicetree, linux-kernel
Cc: wangshuaijie, liweilei, kangjiajun
In-Reply-To: <20240529130608.783624-1-wangshuaijie@awinic.com>
From: shuaijie wang <wangshuaijie@awinic.com>
Signed-off-by: shuaijie wang <wangshuaijie@awinic.com>
---
drivers/input/misc/aw_sar/aw9610x/aw9610x.c | 884 ++++++++++++++++++++
drivers/input/misc/aw_sar/aw9610x/aw9610x.h | 324 +++++++
2 files changed, 1208 insertions(+)
create mode 100644 drivers/input/misc/aw_sar/aw9610x/aw9610x.c
create mode 100644 drivers/input/misc/aw_sar/aw9610x/aw9610x.h
diff --git a/drivers/input/misc/aw_sar/aw9610x/aw9610x.c b/drivers/input/misc/aw_sar/aw9610x/aw9610x.c
new file mode 100644
index 000000000000..a7745649813f
--- /dev/null
+++ b/drivers/input/misc/aw_sar/aw9610x/aw9610x.c
@@ -0,0 +1,884 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AWINIC sar sensor driver (aw9610x)
+ *
+ * Author: Shuaijie Wang<wangshuaijie@awinic.com>
+ *
+ * Copyright (c) 2024 awinic Technology CO., LTD
+ */
+#include "aw9610x.h"
+
+#define AW9610X_I2C_NAME "aw9610x_sar"
+
+static struct aw_sar *g_aw_sar;
+
+static int32_t aw9610x_baseline_filter(struct aw_sar *p_sar)
+{
+ struct aw9610x *aw9610x = (struct aw9610x *)p_sar->priv_data;
+ uint32_t status0;
+ uint32_t status1;
+ uint8_t i;
+
+ aw_sar_i2c_read(p_sar->i2c, REG_STAT1, &status1);
+ aw_sar_i2c_read(p_sar->i2c, REG_STAT0, &status0);
+
+ for (i = 0; i < AW9610X_CHANNEL_MAX; i++) {
+ if (((status1 >> i) & 0x01) == 1) {
+ if (aw9610x->satu_flag[i] == 0) {
+ aw_sar_i2c_read(p_sar->i2c, REG_BLFILT_CH0 + i * AW_CL1SPE_DEAL_OS,
+ &aw9610x->satu_data[i]);
+ aw_sar_i2c_write(p_sar->i2c, REG_BLFILT_CH0 + i * AW_CL1SPE_DEAL_OS,
+ ((aw9610x->satu_data[i] | 0x1fc) & 0x3fffffff));
+ aw9610x->satu_flag[i] = 1;
+ }
+ } else if (((status1 >> i) & 0x01) == 0) {
+ if (aw9610x->satu_flag[i] == 1) {
+ if (((status0 >> (i + 24)) & 0x01) == 0) {
+ aw_sar_i2c_write(p_sar->i2c,
+ REG_BLFILT_CH0 + i * AW_CL1SPE_DEAL_OS,
+ aw9610x->satu_data[i]);
+ aw9610x->satu_flag[i] = 0;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+static void aw9610x_saturat_release_handle(struct aw_sar *p_sar)
+{
+ struct aw9610x *aw9610x = (struct aw9610x *)p_sar->priv_data;
+ uint32_t satu_irq;
+ uint32_t status0;
+ uint8_t i;
+
+ satu_irq = (aw9610x->irq_status >> 7) & 0x01;
+ if (satu_irq == 1) {
+ aw9610x_baseline_filter(p_sar);
+ } else {
+ aw_sar_i2c_read(p_sar->i2c, REG_STAT0, &status0);
+ for (i = 0; i < AW9610X_CHANNEL_MAX; i++) {
+ if (aw9610x->satu_flag[i] == 1) {
+ if (((status0 >> (i + 24)) & 0x01) == 0) {
+ aw_sar_i2c_write(p_sar->i2c,
+ REG_BLFILT_CH0 + i * AW_CL1SPE_DEAL_OS,
+ aw9610x->satu_data[i]);
+ aw9610x->satu_flag[i] = 0;
+ }
+ }
+ }
+ }
+}
+
+static void aw9610x_irq_handle(struct aw_sar *p_sar)
+{
+ uint32_t curr_status_val;
+ uint32_t curr_status;
+ uint8_t i;
+
+ aw_sar_i2c_read(p_sar->i2c, REG_STAT0, &curr_status_val);
+ if (!p_sar->channels_arr) {
+ dev_err(p_sar->dev, "input err!!!");
+ return;
+ }
+
+ for (i = 0; i < AW9610X_CHANNEL_MAX; i++) {
+ curr_status =
+ (((uint8_t)(curr_status_val >> (24 + i)) & 0x1))
+#ifdef AW_INPUT_TRIGGER_TH1
+ | (((uint8_t)(curr_status_val >> (16 + i)) & 0x1) << 1)
+#endif
+#ifdef AW_INPUT_TRIGGER_TH2
+ | (((uint8_t)(curr_status_val >> (8 + i)) & 0x1) << 2)
+#endif
+#ifdef AW_INPUT_TRIGGER_TH3
+ | (((uint8_t)(curr_status_val >> (i)) & 0x1) << 3)
+#endif
+ ;
+
+ if (p_sar->channels_arr[i].used == AW_FALSE)
+ continue;
+
+ if (p_sar->channels_arr[i].last_channel_info == curr_status)
+ continue;
+
+ switch (curr_status) {
+ case AW9610X_FAR:
+ input_report_abs(p_sar->channels_arr[i].input, ABS_DISTANCE, 0);
+ break;
+ case AW9610X_TRIGGER_TH0:
+ input_report_abs(p_sar->channels_arr[i].input, ABS_DISTANCE, 1);
+ break;
+#ifdef AW_INPUT_TRIGGER_TH1
+ case AW9610X_TRIGGER_TH1:
+ input_report_abs(p_sar->channels_arr[i].input, ABS_DISTANCE, 2);
+ break;
+#endif
+#ifdef AW_INPUT_TRIGGER_TH2
+ case AW9610X_TRIGGER_TH2:
+ input_report_abs(p_sar->channels_arr[i].input, ABS_DISTANCE, 3);
+ break;
+#endif
+#ifdef AW_INPUT_TRIGGER_TH3
+ case AW9610X_TRIGGER_TH3:
+ input_report_abs(p_sar->channels_arr[i].input, ABS_DISTANCE, 4);
+ break;
+#endif
+ default:
+ dev_err(p_sar->dev, "error abs distance");
+ return;
+ }
+ input_sync(p_sar->channels_arr[i].input);
+
+ p_sar->channels_arr[i].last_channel_info = curr_status;
+ }
+}
+
+static void aw9610x_version_aw9610x_private(struct aw_sar *p_sar)
+{
+ struct aw9610x *aw9610x = (struct aw9610x *)p_sar->priv_data;
+
+ if (aw9610x->satu_release == AW9610X_FUNC_ON)
+ aw9610x_saturat_release_handle(p_sar);
+}
+
+static void aw9610x_irq_handle_func(uint32_t irq_status, void *data)
+{
+ struct aw_sar *p_sar = (struct aw_sar *)data;
+ struct aw9610x *aw9610x = (struct aw9610x *)p_sar->priv_data;
+
+ dev_info(p_sar->dev, "IRQSRC = 0x%x", irq_status);
+
+ switch (aw9610x->vers) {
+ case AW9610X:
+ aw9610x_version_aw9610x_private(p_sar);
+ break;
+ case AW9610XA:
+ break;
+ default:
+ break;
+ }
+
+ aw9610x_irq_handle(p_sar);
+}
+
+int32_t aw9610x_check_chipid(void *data)
+{
+ struct aw_sar *p_sar = (struct aw_sar *)data;
+ uint32_t reg_val;
+ int32_t ret;
+
+ if (!p_sar)
+ return -EINVAL;
+
+ ret = aw_sar_i2c_read(p_sar->i2c, REG_CHIPID, ®_val);
+ if (ret < 0) {
+ dev_err(p_sar->dev, "read CHIP ID failed: %d", ret);
+ return ret;
+ }
+ reg_val = reg_val >> 16;
+
+ if (reg_val != AW9610X_CHIP_ID) {
+ dev_err(p_sar->dev, "unsupport dev, chipid is (0x%04x)", reg_val);
+ return -EINVAL;
+ }
+ dev_info(p_sar->dev, "aw9610x detected, 0x%04x", reg_val);
+ memcpy(p_sar->chip_name, "AW9610X", 8);
+
+ return 0;
+}
+
+static const struct aw_sar_check_chipid_t g_aw9610x_check_chipid = {
+ .p_check_chipid_fn = aw9610x_check_chipid,
+};
+
+static ssize_t aw9610x_operation_mode_get(void *data, char *buf)
+{
+ struct aw_sar *p_sar = (struct aw_sar *)data;
+ struct aw9610x *aw9610x = (struct aw9610x *)p_sar->priv_data;
+ ssize_t len = 0;
+
+ if (p_sar->last_mode == AW9610X_ACTIVE_MODE)
+ len += snprintf(buf + len, PAGE_SIZE - len, "operation mode: Active\n");
+ else if (p_sar->last_mode == AW9610X_SLEEP_MODE)
+ len += snprintf(buf + len, PAGE_SIZE - len, "operation mode: Sleep\n");
+ else if ((p_sar->last_mode == AW9610X_DEEPSLEEP_MODE) && (aw9610x->vers == AW9610XA))
+ len += snprintf(buf + len, PAGE_SIZE - len, "operation mode: DeepSleep\n");
+ else
+ len += snprintf(buf + len, PAGE_SIZE - len, "operation mode: Unconfirmed\n");
+
+ return len;
+}
+
+static void aw9610x_chip_info_get(void *data, char *buf, ssize_t *p_len)
+{
+ struct aw_sar *p_sar = (struct aw_sar *)data;
+ struct aw9610x *aw9610x = (struct aw9610x *)p_sar->priv_data;
+ uint32_t reg_data;
+
+ *p_len += snprintf(buf + *p_len, PAGE_SIZE - *p_len,
+ "sar%u\n", p_sar->dts_info.sar_num);
+ *p_len += snprintf(buf + *p_len, PAGE_SIZE - *p_len, "The driver supports UI\n");
+
+ aw_sar_i2c_read(p_sar->i2c, REG_CHIPID, ®_data);
+ *p_len += snprintf(buf + *p_len, PAGE_SIZE - *p_len, "chipid is 0x%08x\n", reg_data);
+
+ aw_sar_i2c_read(p_sar->i2c, REG_IRQEN, ®_data);
+ *p_len += snprintf(buf + *p_len, PAGE_SIZE - *p_len, "REG_HOSTIRQEN is 0x%08x\n", reg_data);
+
+ *p_len += snprintf(buf + *p_len, PAGE_SIZE - *p_len,
+ "chip_name:%s bin_prase_chip_name:%s\n",
+ aw9610x->chip_name, aw9610x->chip_type);
+}
+
+static const struct aw_sar_get_chip_info_t g_aw9610x_get_chip_info = {
+ .p_get_chip_info_node_fn = aw9610x_chip_info_get,
+};
+
+static void aw9610x_reg_version_comp(struct aw_sar *p_sar, struct aw_bin *aw_bin)
+{
+ struct aw9610x *aw9610x = (struct aw9610x *)p_sar->priv_data;
+ uint32_t blfilt1_data;
+ uint32_t blfilt1_tmp;
+ uint8_t i;
+
+ if ((aw9610x->chip_name[7] == 'A') &&
+ (aw_bin->header_info[0].chip_type[7] == '\0')) {
+ for (i = 0; i < 6; i++) {
+ aw_sar_i2c_read(p_sar->i2c, REG_BLFILT_CH0 + (0x3c * i), &blfilt1_data);
+ blfilt1_tmp = (blfilt1_data >> 25) & 0x1;
+ if (blfilt1_tmp == 1)
+ aw_sar_i2c_write_bits(p_sar->i2c, REG_BLRSTRNG_CH0 + (0x3c * i),
+ ~(0x3f), 1 << i);
+ }
+ }
+}
+
+static int32_t aw9610x_load_reg_bin(struct aw_bin *aw_bin, void *load_bin_para)
+{
+ struct aw_sar *p_sar = (struct aw_sar *)load_bin_para;
+ struct aw9610x *aw9610x = (struct aw9610x *)p_sar->priv_data;
+ int32_t ret;
+
+ dev_info(p_sar->dev, "reg chip name: %s, soc chip name: %s, len = %d",
+ p_sar->chip_name, aw_bin->header_info[0].chip_type, aw_bin->info.len);
+
+ snprintf(aw9610x->chip_type, sizeof(aw9610x->chip_type), "%s",
+ aw_bin->header_info[0].chip_type);
+ ret = strncmp(aw9610x->chip_name, aw_bin->header_info[0].chip_type,
+ sizeof(aw_bin->header_info[0].chip_type));
+ if (ret != 0)
+ dev_err(p_sar->dev, "load_binname(%s) incompatible with chip type(%s)",
+ p_sar->chip_name, aw_bin->header_info[0].chip_type);
+
+ p_sar->load_bin.bin_data_ver = aw_bin->header_info[0].bin_data_ver;
+
+ ret = aw_sar_load_reg(aw_bin, p_sar->i2c);
+ aw9610x_reg_version_comp(p_sar, aw_bin);
+
+ return ret;
+}
+
+static ssize_t aw9610x_get_self_cap_offset(void *data, char *buf)
+{
+ struct aw_sar *p_sar = (struct aw_sar *)data;
+ uint8_t temp_data[20] = { 0 };
+ uint32_t coff_data_int;
+ uint32_t coff_data_dec;
+ uint32_t coff_data;
+ uint32_t reg_val;
+ ssize_t len = 0;
+ uint8_t i;
+
+ for (i = 0; i < AW9610X_CHANNEL_MAX; i++) {
+ aw_sar_i2c_read(p_sar->i2c,
+ REG_AFECFG1_CH0 + i * AW_CL1SPE_CALI_OS, ®_val);
+ coff_data = (reg_val >> 24) * 900 + ((reg_val >> 16) & 0xff) * 13;
+ coff_data_int = coff_data / 1000;
+ coff_data_dec = coff_data % 1000;
+ snprintf(temp_data, sizeof(temp_data), "%u.%u", coff_data_int, coff_data_dec);
+ len += snprintf(buf+len, PAGE_SIZE-len, "PARASITIC_DATA_CH%d = %s pf\n",
+ i, temp_data);
+ }
+
+ return len;
+}
+
+static const struct aw_sar_offset_t g_aw9610x_offset = {
+ .p_get_offset_node_fn = aw9610x_get_self_cap_offset,
+};
+
+static uint32_t attr_buf[] = {
+ 8, 10,
+ 9, 100,
+ 10, 1000,
+};
+
+static void aw9610x_addrblock_load(struct aw_sar *p_sar, const char *buf)
+{
+ struct aw9610x *aw9610x = p_sar->priv_data;
+ uint8_t addr_bytes = aw9610x->aw_i2c_package.addr_bytes;
+ uint8_t reg_num = aw9610x->aw_i2c_package.reg_num;
+ uint32_t addrbuf[4] = { 0 };
+ uint8_t temp_buf[2] = { 0 };
+ uint32_t i;
+
+ for (i = 0; i < addr_bytes; i++) {
+ if (reg_num < attr_buf[1]) {
+ temp_buf[0] = buf[attr_buf[0] + i * 5];
+ temp_buf[1] = buf[attr_buf[0] + i * 5 + 1];
+ } else if (reg_num >= attr_buf[1] && reg_num < attr_buf[3]) {
+ temp_buf[0] = buf[attr_buf[2] + i * 5];
+ temp_buf[1] = buf[attr_buf[2] + i * 5 + 1];
+ } else if (reg_num >= attr_buf[3] && reg_num < attr_buf[5]) {
+ temp_buf[0] = buf[attr_buf[4] + i * 5];
+ temp_buf[1] = buf[attr_buf[4] + i * 5 + 1];
+ }
+ if (sscanf(temp_buf, "%02x", &addrbuf[i]) == 1)
+ aw9610x->aw_i2c_package.init_addr[i] = (uint8_t)addrbuf[i];
+ }
+}
+
+static int32_t aw9610x_awrw_write_seq(struct aw_sar *p_sar)
+{
+ struct aw9610x *aw9610x = (struct aw9610x *)p_sar->priv_data;
+ uint8_t addr_bytes = aw9610x->aw_i2c_package.addr_bytes;
+ uint8_t data_bytes = aw9610x->aw_i2c_package.data_bytes;
+ uint8_t reg_num = aw9610x->aw_i2c_package.reg_num;
+ uint8_t *p_reg_data = aw9610x->aw_i2c_package.p_reg_data;
+ uint8_t w_buf[228];
+ uint32_t msg_idx;
+ uint8_t msg_cnt;
+
+ for (msg_idx = 0; msg_idx < addr_bytes; msg_idx++)
+ w_buf[msg_idx] = aw9610x->aw_i2c_package.init_addr[msg_idx];
+
+ msg_cnt = addr_bytes;
+ for (msg_idx = 0; msg_idx < data_bytes * reg_num; msg_idx++) {
+ w_buf[msg_cnt] = *p_reg_data++;
+ msg_cnt++;
+ }
+
+ return aw_sar_i2c_write_seq(p_sar->i2c, w_buf, msg_cnt);
+}
+
+static void aw9610x_datablock_load(struct aw_sar *p_sar, const char *buf)
+{
+ struct aw9610x *aw9610x = p_sar->priv_data;
+ uint8_t addr_bytes = aw9610x->aw_i2c_package.addr_bytes;
+ uint8_t data_bytes = aw9610x->aw_i2c_package.data_bytes;
+ uint8_t reg_num = aw9610x->aw_i2c_package.reg_num;
+ uint8_t reg_data[220] = { 0 };
+ uint32_t databuf[220] = { 0 };
+ uint8_t temp_buf[2] = { 0 };
+ uint32_t i;
+
+ for (i = 0; i < data_bytes * reg_num; i++) {
+ if (reg_num < attr_buf[1]) {
+ temp_buf[0] = buf[attr_buf[0] + (addr_bytes + i) * 5];
+ temp_buf[1] =
+ buf[attr_buf[0] + (addr_bytes + i) * 5 + 1];
+ } else if (reg_num >= attr_buf[1] && reg_num < attr_buf[3]) {
+ temp_buf[0] = buf[attr_buf[2] + (addr_bytes + i) * 5];
+ temp_buf[1] =
+ buf[attr_buf[2] + (addr_bytes + i) * 5 + 1];
+ } else if (reg_num >= attr_buf[3] && reg_num < attr_buf[5]) {
+ temp_buf[0] = buf[attr_buf[4] + (addr_bytes + i) * 5];
+ temp_buf[1] =
+ buf[attr_buf[4] + (addr_bytes + i) * 5 + 1];
+ }
+ sscanf(temp_buf, "%02x", &databuf[i]);
+ reg_data[i] = (uint8_t)databuf[i];
+ }
+ aw9610x->aw_i2c_package.p_reg_data = reg_data;
+ aw9610x_awrw_write_seq(p_sar);
+}
+
+static int32_t aw9610x_awrw_read_seq(struct aw_sar *p_sar, uint8_t *reg_data)
+{
+ struct aw9610x *aw9610x = (struct aw9610x *)p_sar->priv_data;
+ uint8_t data_bytes = aw9610x->aw_i2c_package.data_bytes;
+ uint8_t addr_bytes = aw9610x->aw_i2c_package.addr_bytes;
+ uint8_t reg_num = aw9610x->aw_i2c_package.reg_num;
+ uint16_t msg_cnt = (uint16_t)(data_bytes * reg_num);
+ uint8_t w_buf[4];
+ uint8_t buf[228];
+ uint32_t msg_idx;
+ int32_t ret;
+
+ for (msg_idx = 0; msg_idx < addr_bytes; msg_idx++)
+ w_buf[msg_idx] = aw9610x->aw_i2c_package.init_addr[msg_idx];
+
+ ret = aw_sar_i2c_read_seq(p_sar->i2c, w_buf, 2, (uint8_t *)buf, msg_cnt);
+
+ for (msg_idx = 0; msg_idx < msg_cnt; msg_idx++)
+ reg_data[msg_idx] = buf[msg_idx];
+
+ return ret;
+}
+
+static ssize_t aw9610x_awrw_get(void *data, char *buf)
+{
+ struct aw_sar *p_sar = (struct aw_sar *)data;
+ struct aw9610x *aw9610x = (struct aw9610x *)p_sar->priv_data;
+ uint8_t data_bytes = aw9610x->aw_i2c_package.data_bytes;
+ uint8_t reg_num = aw9610x->aw_i2c_package.reg_num;
+ uint8_t reg_data[228] = { 0 };
+ ssize_t len = 0;
+ uint8_t i;
+
+ aw9610x_awrw_read_seq(p_sar, reg_data);
+ for (i = 0; i < reg_num * data_bytes; i++)
+ len += snprintf(buf + len, PAGE_SIZE - len, "0x%02x,", reg_data[i]);
+
+ snprintf(buf + len - 1, PAGE_SIZE - len, "\n");
+
+ return len;
+};
+
+static ssize_t aw9610x_awrw_set(void *data, const char *buf, size_t count)
+{
+ struct aw_sar *p_sar = (struct aw_sar *)data;
+ struct aw9610x *aw9610x = (struct aw9610x *)p_sar->priv_data;
+ uint32_t datatype[3] = { 0 };
+
+ if (sscanf(buf, "%u %u %u", &datatype[0], &datatype[1], &datatype[2]) == 3) {
+ aw9610x->aw_i2c_package.addr_bytes = (uint8_t)datatype[0];
+ aw9610x->aw_i2c_package.data_bytes = (uint8_t)datatype[1];
+ aw9610x->aw_i2c_package.reg_num = (uint8_t)datatype[2];
+
+ aw9610x_addrblock_load(p_sar, buf);
+ if (count > 7 + 5 * aw9610x->aw_i2c_package.addr_bytes)
+ aw9610x_datablock_load(p_sar, buf);
+ }
+
+ return count;
+}
+
+static int32_t aw9610x_get_chip_version(void *data)
+{
+ struct aw_sar *p_sar = (struct aw_sar *)data;
+ struct aw9610x *aw9610x = (struct aw9610x *)p_sar->priv_data;
+ uint32_t firmvers;
+ uint32_t fw_ver;
+ int32_t ret;
+
+ aw_sar_i2c_read(p_sar->i2c, REG_FWVER, &firmvers);
+
+ ret = aw_sar_i2c_read(p_sar->i2c, REG_FWVER2, &fw_ver);
+ if (ret < 0) {
+ dev_err(p_sar->dev, "read REG_FWVER2 err!");
+ return ret;
+ }
+ snprintf(aw9610x->chip_name, sizeof(aw9610x->chip_name), "AW9610X");
+ p_sar->chip_type = AW_SAR_NONE_CHECK_CHIP;
+
+ if (fw_ver == AW_CHIP_AW9610XA) {
+ aw9610x->vers = AW9610XA;
+ memcpy(aw9610x->chip_name + strlen(aw9610x->chip_name), "A", 2);
+ p_sar->chip_type = SAR_AW9610XA;
+ } else {
+ aw9610x->vers = AW9610X;
+ p_sar->chip_type = SAR_AW9610X;
+ aw9610x->chip_name[7] = '\0';
+ }
+ dev_info(p_sar->dev, "the IC is = %s", aw9610x->chip_name);
+
+ return 0;
+}
+
+#ifdef AW9610X_TVS_ABNORMAL_CAIL
+static ssize_t aw9610x_set_aot(void *data)
+{
+ struct aw_sar *p_sar = (struct aw_sar *)data;
+ struct aw9610x *aw9610x = (struct aw9610x *)p_sar->priv_data;
+ uint32_t max_delay_ms = AW9610X_AOT_OVER_DELAY_MAX_MS;
+ uint32_t irqen_reg_val;
+ uint32_t reg_val_tmp;
+ uint32_t scan_over_cnt;
+ uint32_t scan_over_en;
+ uint32_t ch_en;
+ uint32_t i;
+
+ //1. disable chip irq
+ aw_sar_i2c_read(p_sar->i2c, REG_IRQEN, &irqen_reg_val);
+ aw_sar_i2c_write(p_sar->i2c, REG_IRQEN, AW_REG_IRQEN_CLOSE);
+
+ //2. aot cail
+ aw_sar_i2c_write_bits(p_sar->i2c, REG_SCANCTRL0, ~(AW9610X_AOT_MASK << AW9610X_AOT_BIT),
+ AW9610X_AOT_MASK << AW9610X_AOT_BIT);
+ // aot over
+ for (i = 0; i < max_delay_ms; i++) {
+ aw_sar_i2c_read(p_sar->i2c, REG_IRQSRC, ®_val_tmp);
+ if (((reg_val_tmp >> AW_REG_IRQSRC_AOT_OVER_BIT) & 0x01) == 1)
+ break;
+ mdelay(1);
+ }
+
+ //3. scan 8 cnt over
+ aw_sar_i2c_read(p_sar->i2c, REG_SCANCTRL0, &ch_en);
+ aw_sar_i2c_read(p_sar->i2c, REG_CHINTEN, &scan_over_en);
+ if ((ch_en & AW9610X_AOT_MASK) != (scan_over_en & AW9610X_AOT_MASK))
+ aw_sar_i2c_write_bits(p_sar->i2c, REG_CHINTEN, ~(AW9610X_AOT_MASK),
+ ch_en & (AW9610X_AOT_MASK));
+
+ for (scan_over_cnt = 0; scan_over_cnt < AW9610X_AOT_SCAN_OVER_CNT; scan_over_cnt++) {
+ for (i = 0; i < max_delay_ms; i++) {
+ aw_sar_i2c_read(p_sar->i2c, REG_IRQSRC, ®_val_tmp);
+ if (((reg_val_tmp >> REG_IRQSRC_SCAN_OVER_BIT) & 0x01) == 1)
+ break;
+ mdelay(1);
+ }
+ }
+ if ((ch_en & AW9610X_AOT_MASK) != (scan_over_en & AW9610X_AOT_MASK))
+ aw_sar_i2c_write_bits(p_sar->i2c, REG_CHINTEN, ~(AW9610X_AOT_MASK),
+ ch_en & (AW9610X_AOT_MASK));
+
+ if (aw9610x->vers == AW9610XA)
+ //4. chip set sleep mode
+ aw_sar_i2c_write(p_sar->i2c, REG_CMD, AW9610X_SLEEP_MODE);
+ else if (aw9610x->vers == AW9610X)
+ aw_sar_i2c_write(p_sar->i2c, REG_CMD, AW9610X_DEEPSLEEP_MODE);
+
+ for (i = 0; i < max_delay_ms; i++) {
+ aw_sar_i2c_read(p_sar->i2c, REG_WST, ®_val_tmp);
+ if ((reg_val_tmp & 0xFF) == REG_REG_WST_SLEEP_MODE)
+ break;
+ mdelay(1);
+ }
+
+ //5. write baseline data
+ for (i = 0; i < AW9610X_CHANNEL_MAX; i++) {
+ aw_sar_i2c_read(p_sar->i2c, REG_COMP_CH0 + i * AW9610X_REG_OFFSET_STEP,
+ ®_val_tmp);
+ aw_sar_i2c_write(p_sar->i2c, REG_BASELINE_CH0 + i * AW9610X_REG_OFFSET_STEP,
+ reg_val_tmp);
+ }
+
+ //6. chip set active, irq recovery
+ aw_sar_i2c_write(p_sar->i2c, REG_CMD, AW9610X_ACTIVE_MODE);
+ aw_sar_i2c_write(p_sar->i2c, REG_IRQEN, irqen_reg_val);
+
+ return 0;
+}
+#else
+static ssize_t aw9610x_set_aot(void *data)
+{
+ struct aw_sar *p_sar = (struct aw_sar *)data;
+
+ aw_sar_i2c_write_bits(p_sar->i2c, REG_SCANCTRL0, ~(AW9610X_AOT_MASK << AW9610X_AOT_BIT),
+ (AW9610X_AOT_MASK) << AW9610X_AOT_BIT);
+ return 0;
+}
+#endif
+
+static const struct aw_sar_aot_t g_aw9610x_aot = {
+ .p_set_aot_node_fn = aw9610x_set_aot,
+};
+
+/**********************mode operation start*******************************/
+static void aw9610x_enable_clock(void *i2c)
+{
+ aw_sar_i2c_write(i2c, REG_OSCEN, AW9610X_CPU_WORK_MASK);
+}
+
+static uint32_t aw9610x_rc_irqscr(void *i2c)
+{
+ uint32_t val;
+
+ aw_sar_i2c_read(i2c, REG_IRQSRC, &val);
+
+ return val;
+}
+
+//Note: TVS exceptions need to be handled after active
+static void aw9610x_set_active_cmd(void *i2c)
+{
+ aw_sar_i2c_write(i2c, REG_CMD, AW9610X_ACTIVE_MODE);
+
+#ifdef AW9610X_TVS_ABNORMAL_CAIL
+ if (g_aw_sar != NULL)
+ aw9610x_set_aot(g_aw_sar);
+#endif
+}
+
+static void aw9610x_set_sleep_cmd(void *i2c)
+{
+ aw_sar_i2c_write(i2c, REG_CMD, AW9610X_SLEEP_MODE);
+}
+
+static void aw9610x_set_deepsleep_cmd(void *i2c)
+{
+ aw_sar_i2c_write(i2c, REG_CMD, AW9610X_DEEPSLEEP_MODE);
+}
+
+static const struct aw_sar_mode_set_t g_aw9610x_mode_set[] = {
+ {
+ .chip_id = SAR_AW9610XA | SAR_AW9610X,
+ .chip_mode = {
+ .curr_mode = AW9610X_ACTIVE_MODE,
+ .last_mode = AW9610X_DEEPSLEEP_MODE,
+ },
+ .mode_switch_ops = {
+ .enable_clock = aw9610x_enable_clock,
+ .rc_irqscr = NULL,
+ .mode_update = aw9610x_set_active_cmd,
+ },
+ },
+ {
+ .chip_id = SAR_AW9610XA | SAR_AW9610X,
+ .chip_mode = {
+ .curr_mode = AW9610X_ACTIVE_MODE,
+ .last_mode = AW9610X_SLEEP_MODE,
+ },
+ .mode_switch_ops = {
+ .enable_clock = NULL,
+ .rc_irqscr = NULL,
+ .mode_update = aw9610x_set_active_cmd,
+ },
+ },
+ {
+ .chip_id = SAR_AW9610XA | SAR_AW9610X,
+ .chip_mode = {
+ .curr_mode = AW9610X_ACTIVE_MODE,
+ .last_mode = AW9610X_ACTIVE_MODE,
+ },
+ .mode_switch_ops = {
+ .enable_clock = NULL,
+ .rc_irqscr = NULL,
+ .mode_update = aw9610x_set_active_cmd,
+ },
+ },
+ {
+ .chip_id = SAR_AW9610XA | SAR_AW9610X,
+ .chip_mode = {
+ .curr_mode = AW9610X_SLEEP_MODE,
+ .last_mode = AW9610X_DEEPSLEEP_MODE,
+ },
+ .mode_switch_ops = {
+ .enable_clock = aw9610x_enable_clock,
+ .rc_irqscr = NULL,
+ .mode_update = aw9610x_set_sleep_cmd,
+ },
+ },
+ {
+ .chip_id = SAR_AW9610XA | SAR_AW9610X,
+ .chip_mode = {
+ .curr_mode = AW9610X_SLEEP_MODE,
+ .last_mode = AW9610X_ACTIVE_MODE,
+ },
+ .mode_switch_ops = {
+ .enable_clock = NULL,
+ .rc_irqscr = NULL,
+ .mode_update = aw9610x_set_sleep_cmd,
+ },
+ },
+ {
+ .chip_id = SAR_AW9610XA | SAR_AW9610X,
+ .chip_mode = {
+ .curr_mode = AW9610X_DEEPSLEEP_MODE,
+ .last_mode = AW9610X_SLEEP_MODE,
+ },
+ .mode_switch_ops = {
+ .enable_clock = NULL,
+ .rc_irqscr = NULL,
+ .mode_update = aw9610x_set_deepsleep_cmd,
+ },
+ },
+ {
+ .chip_id = SAR_AW9610XA,
+ .chip_mode = {
+ .curr_mode = AW9610X_DEEPSLEEP_MODE,
+ .last_mode = AW9610X_ACTIVE_MODE,
+ },
+ .mode_switch_ops = {
+ .enable_clock = NULL,
+ .rc_irqscr = aw9610x_rc_irqscr,
+ .mode_update = aw9610x_set_deepsleep_cmd,
+ },
+ },
+ {
+ .chip_id = SAR_AW9610X,
+ .chip_mode = {
+ .curr_mode = AW9610X_DEEPSLEEP_MODE,
+ .last_mode = AW9610X_ACTIVE_MODE,
+ },
+ .mode_switch_ops = {
+ .enable_clock = NULL,
+ .rc_irqscr = NULL,
+ .mode_update = NULL,
+ },
+ },
+};
+/**********************mode operation end*******************************/
+
+static const struct aw_sar_irq_init_t g_aw9610x_irq_init = {
+ .flags = GPIOF_DIR_IN | GPIOF_INIT_HIGH,
+ .irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ .handler = NULL,
+ .thread_fn = NULL,
+ .rc_irq_fn = aw9610x_rc_irqscr,
+ .irq_spec_handler_fn = aw9610x_irq_handle_func,
+};
+
+static const struct aw_sar_soft_rst_t g_aw9610x_soft_rst = {
+ .reg_rst = REG_RESET,
+ .reg_rst_val = 0,
+ .delay_ms = 20,
+};
+
+static const struct aw_sar_init_over_irq_t g_aw9610x_init_over_irq = {
+ .wait_times = 20,
+ .daley_step = 1,
+ .reg_irqsrc = REG_IRQSRC,
+ .irq_offset_bit = 0,
+ .irq_mask = 0x1,
+ .irq_flag = 0x1,
+};
+
+static const struct aw_sar_load_bin_t g_aw9610x_load_reg_bin = {
+ .bin_name = "aw9610x",
+ .bin_opera_func = aw9610x_load_reg_bin,
+ .p_update_fn = NULL,
+};
+
+static const struct aw_sar_para_load_t g_aw9610x_reg_arr_para = {
+ .reg_arr = aw9610x_reg_default,
+ .reg_arr_len = ARRAY_SIZE(aw9610x_reg_default),
+};
+
+static const struct aw_sar_diff_t g_aw9610x_diff = {
+ .diff0_reg = REG_DIFF_CH0,
+ .diff_step = 4,
+ .rm_float = AW9610x_DATA_PROCESS_FACTOR,
+};
+
+static const struct aw_sar_mode_t g_aw9610x_mode = {
+ .mode_set_arr = &g_aw9610x_mode_set[0],
+ .mode_set_arr_len = ARRAY_SIZE(g_aw9610x_mode_set),
+ .p_set_mode_node_fn = NULL,
+ .p_get_mode_node_fn = aw9610x_operation_mode_get,
+};
+
+static const struct aw_sar_reg_list_t g_aw9610x_reg_list = {
+ .reg_none_access = REG_NONE_ACCESS,
+ .reg_rd_access = REG_RD_ACCESS,
+ .reg_wd_access = REG_WR_ACCESS,
+ .reg_perm = (struct aw_sar_reg_data *)&g_aw9610x_reg_access[0],
+ .reg_num = ARRAY_SIZE(g_aw9610x_reg_access),
+};
+
+static const struct aw_sar_pm_t g_aw9610x_pm_chip_mode = {
+ .suspend_set_mode = AW9610X_SLEEP_MODE,
+ .resume_set_mode = AW9610X_ACTIVE_MODE,
+ .shutdown_set_mode = AW9610X_SLEEP_MODE,
+};
+
+static const struct aw_sar_chip_mode_t g_aw9610x_chip_mode = {
+ .init_mode = AW9610X_ACTIVE_MODE,
+ .active = AW9610X_ACTIVE_MODE,
+ .pre_init_mode = AW9610X_SLEEP_MODE,
+};
+
+static const struct aw_sar_regulator_config_t g_regulator_config = {
+ .vcc_name = "vcc",
+ .min_uV = AW9610X_SAR_VCC_MIN_UV,
+ .max_uV = AW9610X_SAR_VCC_MAX_UV,
+};
+
+struct aw_sar_awrw_t g_aw9610x_awrw = {
+ .p_set_awrw_node_fn = aw9610x_awrw_set,
+ .p_get_awrw_node_fn = aw9610x_awrw_get,
+};
+
+static const struct aw_sar_platform_config g_aw9610x_platform_config = {
+ .p_regulator_config = &g_regulator_config,
+ .p_irq_init = &g_aw9610x_irq_init,
+ .p_pm_chip_mode = &g_aw9610x_pm_chip_mode,
+};
+
+static void aw9610x_power_on_prox_detection(void *data, uint8_t en_flag)
+{
+ struct aw_sar *p_sar = (struct aw_sar *)data;
+ struct aw9610x *aw9610x = (struct aw9610x *)p_sar->priv_data;
+ uint8_t ch;
+
+ if (en_flag == true) {
+ for (ch = 0; ch < AW9610X_CHANNEL_MAX; ch++) {
+ aw_sar_i2c_read(p_sar->i2c,
+ REG_BLFILT_CH0 + (REG_BLFILT_CH1 - REG_BLFILT_CH0) * ch,
+ &(aw9610x->last_blfilta[ch]));
+ aw_sar_i2c_write_bits(p_sar->i2c,
+ REG_BLFILT_CH0 + (REG_BLFILT_CH1 - REG_BLFILT_CH0) * ch,
+ ~(0x3f << 13), (1 << 13));
+ }
+ aw_sar_i2c_read(p_sar->i2c, REG_IRQEN, &aw9610x->last_irq_en);
+ aw_sar_i2c_write_bits(p_sar->i2c, REG_IRQEN, ~(1 << 3), 1 << 3);
+ } else if (en_flag == false) {
+ for (ch = 0; ch < AW9610X_CHANNEL_MAX; ch++) {
+ aw_sar_i2c_write(p_sar->i2c,
+ REG_BLFILT_CH0 + (REG_BLFILT_CH1 - REG_BLFILT_CH0) * ch,
+ aw9610x->last_blfilta[ch]);
+ }
+ aw_sar_i2c_write(p_sar->i2c, REG_IRQEN, aw9610x->last_irq_en);
+ }
+}
+
+static const struct aw_sar_power_on_prox_detection_t g_aw9610x_power_on_prox_detection = {
+ .p_power_on_prox_detection_en_fn = aw9610x_power_on_prox_detection,
+ .irq_en_cali_bit = 3,
+ .power_on_prox_en_flag = true,
+};
+
+static const struct aw_sar_chip_config g_aw9610x_chip_config = {
+ .ch_num_max = AW9610X_CHANNEL_MAX,
+
+ .p_platform_config = &g_aw9610x_platform_config,
+
+ .p_check_chipid = &g_aw9610x_check_chipid,
+ .p_soft_rst = &g_aw9610x_soft_rst,
+ .p_init_over_irq = &g_aw9610x_init_over_irq,
+ .p_fw_bin = NULL,
+ .p_reg_bin = &g_aw9610x_load_reg_bin,
+ .p_chip_mode = &g_aw9610x_chip_mode,
+
+ //Node usage parameters
+ .p_reg_list = &g_aw9610x_reg_list,
+ .p_reg_arr = &g_aw9610x_reg_arr_para,
+ .p_aot = &g_aw9610x_aot,
+ .p_diff = &g_aw9610x_diff,
+ .p_offset = &g_aw9610x_offset,
+ .p_mode = &g_aw9610x_mode,
+ .p_prox_fw = NULL,
+ .p_get_chip_info = &g_aw9610x_get_chip_info,
+ .p_aw_sar_awrw = &g_aw9610x_awrw,
+ .p_boot_bin = NULL,
+
+ .p_other_operation = aw9610x_get_chip_version,
+ .p_other_opera_free = NULL,
+ .power_on_prox_detection = &g_aw9610x_power_on_prox_detection,
+};
+
+int32_t aw9610x_init(struct aw_sar *p_sar)
+{
+ if (!p_sar)
+ return -EINVAL;
+
+ g_aw_sar = p_sar;
+
+ p_sar->priv_data = devm_kzalloc(p_sar->dev, sizeof(struct aw9610x), GFP_KERNEL);
+ if (!p_sar->priv_data)
+ return -ENOMEM;
+
+ //Chip private function operation
+ p_sar->p_sar_para = &g_aw9610x_chip_config;
+
+ return 0;
+}
+
+void aw9610x_deinit(struct aw_sar *p_sar)
+{
+ if (p_sar->priv_data != NULL)
+ devm_kfree(p_sar->dev, p_sar->priv_data);
+}
diff --git a/drivers/input/misc/aw_sar/aw9610x/aw9610x.h b/drivers/input/misc/aw_sar/aw9610x/aw9610x.h
new file mode 100644
index 000000000000..6f72e4c20374
--- /dev/null
+++ b/drivers/input/misc/aw_sar/aw9610x/aw9610x.h
@@ -0,0 +1,324 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef _AW9610X_H_
+#define _AW9610X_H_
+#include "../comm/aw_sar_type.h"
+
+//#define AW9610X_TVS_ABNORMAL_CAIL
+#define AW9610X_AOT_SCAN_OVER_CNT (32)
+
+#define AW9610X_CHIP_ID (0xa961)
+#define AW9610x_DATA_PROCESS_FACTOR (1024)
+#define AW_CHIP_AW9610XA (0x03000b00)
+#define AW9610X_CPU_WORK_MASK (1)
+
+#define AW9610X_SAR_VCC_MIN_UV (1700000)
+#define AW9610X_SAR_VCC_MAX_UV (3600000)
+
+#define AW_REG_IRQEN_CLOSE (0)
+#define AW_REG_IRQSRC_AOT_OVER_BIT (3)
+#define REG_IRQSRC_SCAN_OVER_BIT (4)
+#define REG_REG_WST_SLEEP_MODE (0x3)
+#define AW9610X_AOT_OVER_DELAY_MAX_MS (6000)
+#define AW9610X_AOT_MASK (0x3f)
+#define AW9610X_AOT_BIT (8)
+#define AW9610X_REG_OFFSET_STEP (4)
+
+enum aw9610x_sar_vers {
+ AW9610X = 2,
+ AW9610XA = 6,
+ AW9610XB = 0xa,
+};
+
+enum aw9610x_operation_mode {
+ AW9610X_ACTIVE_MODE = 1,
+ AW9610X_SLEEP_MODE,
+ AW9610X_DEEPSLEEP_MODE,
+ AW9610XB_DEEPSLEEP_MODE,
+};
+
+/**********************************************
+ *spereg addr offset
+ **********************************************/
+enum aw9610x_spereg_addr_offset {
+ AW_CL1SPE_CALI_OS = 20,
+ AW_CL1SPE_DEAL_OS = 60,
+ AW_CL2SPE_CALI_OS = 4,
+ AW_CL2SPE_DEAL_OS = 4,
+};
+
+
+/**********************************************
+ *the flag of i2c read/write
+ **********************************************/
+enum aw9610x_function_flag {
+ AW9610X_FUNC_OFF,
+ AW9610X_FUNC_ON,
+};
+
+/**********************************************
+ * multiple sar define
+ **********************************************/
+enum aw9610x_multiple_sar {
+ AW_SAR0,
+ AW_SAR1,
+ AW_SAR_MAX,
+};
+
+#define AW9610X_CHANNEL_MAX (6)
+
+enum aw9610x_irq_trigger_position {
+ AW9610X_FAR,
+ AW9610X_TRIGGER_TH0,
+ AW9610X_TRIGGER_TH1 = 0x03,
+ AW9610X_TRIGGER_TH2 = 0x07,
+ AW9610X_TRIGGER_TH3 = 0x0f,
+};
+
+struct aw_i2c_package {
+ uint8_t addr_bytes;
+ uint8_t data_bytes;
+ uint8_t reg_num;
+ uint8_t init_addr[4];
+ uint8_t *p_reg_data;
+};
+
+struct aw9610x {
+ uint8_t vers;
+ uint8_t channel;
+ uint32_t irq_status;
+ uint8_t chip_name[9];
+ uint8_t chip_type[9];
+ bool satu_release;
+
+ struct aw_i2c_package aw_i2c_package;
+
+ uint8_t satu_flag[6];
+ uint32_t satu_data[6];
+ uint32_t last_blfilta[AW9610X_CHANNEL_MAX];
+ uint32_t last_irq_en;
+};
+
+/********************************************
+ * Register List
+ ********************************************/
+#define AFE_BASE_ADDR (0x0000)
+#define DSP_BASE_ADDR (0x0000)
+#define STAT_BASE_ADDR (0x0000)
+#define SFR_BASE_ADDR (0x0000)
+#define DATA_BASE_ADDR (0x0000)
+
+#define REG_SCANCTRL0 ((0x0000) + AFE_BASE_ADDR)
+#define REG_AFECFG1_CH0 ((0x0014) + AFE_BASE_ADDR)
+
+#define REG_FWVER ((0x0088) + STAT_BASE_ADDR)
+#define REG_WST ((0x008C) + STAT_BASE_ADDR)
+#define REG_STAT0 ((0x0090) + STAT_BASE_ADDR)
+#define REG_STAT1 ((0x0094) + STAT_BASE_ADDR)
+#define REG_CHINTEN ((0x009C) + STAT_BASE_ADDR)
+
+#define REG_BLFILT_CH0 ((0x00A8) + DSP_BASE_ADDR)
+#define REG_BLRSTRNG_CH0 ((0x00B4) + DSP_BASE_ADDR)
+#define REG_BLFILT_CH1 ((0x00E4) + DSP_BASE_ADDR)
+
+#define REG_COMP_CH0 ((0x0210) + DATA_BASE_ADDR)
+#define REG_BASELINE_CH0 ((0x0228) + DATA_BASE_ADDR)
+#define REG_DIFF_CH0 ((0x0240) + DATA_BASE_ADDR)
+#define REG_FWVER2 ((0x0410) + DATA_BASE_ADDR)
+
+#define REG_CMD ((0xF008) + SFR_BASE_ADDR)
+#define REG_IRQSRC ((0xF080) + SFR_BASE_ADDR)
+#define REG_IRQEN ((0xF084) + SFR_BASE_ADDR)
+#define REG_OSCEN ((0xFF00) + SFR_BASE_ADDR)
+#define REG_RESET ((0xFF0C) + SFR_BASE_ADDR)
+#define REG_CHIPID ((0xFF10) + SFR_BASE_ADDR)
+
+struct aw_reg_data {
+ unsigned char rw;
+ unsigned short reg;
+};
+/********************************************
+ * Register Access
+ *******************************************/
+#define REG_NONE_ACCESS (0)
+#define REG_RD_ACCESS (1 << 0)
+#define REG_WR_ACCESS (1 << 1)
+
+static const struct aw_reg_data g_aw9610x_reg_access[] = {
+ { .reg = REG_SCANCTRL0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, },
+ { .reg = REG_AFECFG1_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, },
+
+ { .reg = REG_FWVER, .rw = REG_RD_ACCESS, },
+ { .reg = REG_WST, .rw = REG_RD_ACCESS, },
+ { .reg = REG_STAT0, .rw = REG_RD_ACCESS, },
+ { .reg = REG_STAT1, .rw = REG_RD_ACCESS, },
+ { .reg = REG_CHINTEN, .rw = REG_RD_ACCESS | REG_WR_ACCESS, },
+
+ { .reg = REG_BLFILT_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, },
+ { .reg = REG_BLRSTRNG_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, },
+ { .reg = REG_BLFILT_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, },
+
+
+ { .reg = REG_COMP_CH0, .rw = REG_RD_ACCESS, },
+ { .reg = REG_BASELINE_CH0, .rw = REG_RD_ACCESS, },
+ { .reg = REG_DIFF_CH0, .rw = REG_RD_ACCESS, },
+ { .reg = REG_FWVER2, .rw = REG_RD_ACCESS, },
+
+ { .reg = REG_CMD, .rw = REG_NONE_ACCESS, },
+ { .reg = REG_IRQSRC, .rw = REG_RD_ACCESS, },
+ { .reg = REG_IRQEN, .rw = REG_RD_ACCESS | REG_WR_ACCESS, },
+ { .reg = REG_OSCEN, .rw = REG_RD_ACCESS | REG_WR_ACCESS, },
+ { .reg = REG_RESET, .rw = REG_RD_ACCESS | REG_WR_ACCESS, },
+ { .reg = REG_CHIPID, .rw = REG_RD_ACCESS, },
+};
+
+
+/******************************************************
+ * Register Detail
+ ******************************************************/
+static const uint32_t aw9610x_reg_default[] = {
+ 0x0000, 0x00003f3f,
+ 0x0004, 0x00000064,
+ 0x0008, 0x0017c11e,
+ 0x000c, 0x05000000,
+ 0x0010, 0x00093ffd,
+ 0x0014, 0x19240009,
+ 0x0018, 0xd81c0207,
+ 0x001c, 0xff000000,
+ 0x0020, 0x00241900,
+ 0x0024, 0x00093ff7,
+ 0x0028, 0x58020009,
+ 0x002c, 0xd81c0207,
+ 0x0030, 0xff000000,
+ 0x0034, 0x00025800,
+ 0x0038, 0x00093fdf,
+ 0x003c, 0x7d3b0009,
+ 0x0040, 0xd81c0207,
+ 0x0044, 0xff000000,
+ 0x0048, 0x003b7d00,
+ 0x004c, 0x00093f7f,
+ 0x0050, 0xe9310009,
+ 0x0054, 0xd81c0207,
+ 0x0058, 0xff000000,
+ 0x005c, 0x0031e900,
+ 0x0060, 0x00093dff,
+ 0x0064, 0x1a0c0009,
+ 0x0068, 0xd81c0207,
+ 0x006c, 0xff000000,
+ 0x0070, 0x000c1a00,
+ 0x0074, 0x80093fff,
+ 0x0078, 0x043d0009,
+ 0x007c, 0xd81c0207,
+ 0x0080, 0xff000000,
+ 0x0084, 0x003d0400,
+ 0x00a0, 0xe6400000,
+ 0x00a4, 0x00000000,
+ 0x00a8, 0x010408d2,
+ 0x00ac, 0x00000000,
+ 0x00b0, 0x00000000,
+ 0x00b8, 0x00005fff,
+ 0x00bc, 0x00000000,
+ 0x00c0, 0x00000000,
+ 0x00c4, 0x00000000,
+ 0x00c8, 0x00000000,
+ 0x00cc, 0x00000000,
+ 0x00d0, 0x00000000,
+ 0x00d4, 0x00000000,
+ 0x00d8, 0x00000000,
+ 0x00dc, 0xe6447800,
+ 0x00e0, 0x78000000,
+ 0x00e4, 0x010408d2,
+ 0x00e8, 0x00000000,
+ 0x00ec, 0x00000000,
+ 0x00f4, 0x00005fff,
+ 0x00f8, 0x00000000,
+ 0x00fc, 0x00000000,
+ 0x0100, 0x00000000,
+ 0x0104, 0x00000000,
+ 0x0108, 0x00000000,
+ 0x010c, 0x02000000,
+ 0x0110, 0x00000000,
+ 0x0114, 0x00000000,
+ 0x0118, 0xe6447800,
+ 0x011c, 0x78000000,
+ 0x0120, 0x010408d2,
+ 0x0124, 0x00000000,
+ 0x0128, 0x00000000,
+ 0x0130, 0x00005fff,
+ 0x0134, 0x00000000,
+ 0x0138, 0x00000000,
+ 0x013c, 0x00000000,
+ 0x0140, 0x00000000,
+ 0x0144, 0x00000000,
+ 0x0148, 0x02000000,
+ 0x014c, 0x00000000,
+ 0x0150, 0x00000000,
+ 0x0154, 0xe6447800,
+ 0x0158, 0x78000000,
+ 0x015c, 0x010408d2,
+ 0x0160, 0x00000000,
+ 0x0164, 0x00000000,
+ 0x016c, 0x00005fff,
+ 0x0170, 0x00000000,
+ 0x0174, 0x00000000,
+ 0x0178, 0x00000000,
+ 0x017c, 0x00000000,
+ 0x0180, 0x00000000,
+ 0x0184, 0x02000000,
+ 0x0188, 0x00000000,
+ 0x018c, 0x00000000,
+ 0x0190, 0xe6447800,
+ 0x0194, 0x78000000,
+ 0x0198, 0x010408d2,
+ 0x019c, 0x00000000,
+ 0x01a0, 0x00000000,
+ 0x01a8, 0x00005fff,
+ 0x01ac, 0x00000000,
+ 0x01b0, 0x00000000,
+ 0x01b4, 0x00000000,
+ 0x01b8, 0x00000000,
+ 0x01bc, 0x00000000,
+ 0x01c0, 0x02000000,
+ 0x01c4, 0x00000000,
+ 0x01c8, 0x00000000,
+ 0x01cc, 0xe6407800,
+ 0x01d0, 0x78000000,
+ 0x01d4, 0x010408d2,
+ 0x01d8, 0x00000000,
+ 0x01dc, 0x00000000,
+ 0x01e4, 0x00005fff,
+ 0x01e8, 0x00000000,
+ 0x01ec, 0x00000000,
+ 0x01f0, 0x00000000,
+ 0x01f4, 0x00000000,
+ 0x01f8, 0x00000000,
+ 0x01fc, 0x02000000,
+ 0x0200, 0x00000000,
+ 0x0204, 0x00000000,
+ 0x0208, 0x00000008,
+ 0x020c, 0x0000000d,
+ 0x41fc, 0x00000000,
+ 0x4400, 0x00000000,
+ 0x4410, 0x00000000,
+ 0x4420, 0x00000000,
+ 0x4430, 0x00000000,
+ 0x4440, 0x00000000,
+ 0x4450, 0x00000000,
+ 0x4460, 0x00000000,
+ 0x4470, 0x00000000,
+ 0xf080, 0x00003018,
+ 0xf084, 0x00000fff,
+ 0xf800, 0x00000000,
+ 0xf804, 0x00002e00,
+ 0xf8d0, 0x00000001,
+ 0xf8d4, 0x00000000,
+ 0xff00, 0x00000301,
+ 0xff0c, 0x01000000,
+ 0xffe0, 0x00000000,
+ 0xfff4, 0x00004011,
+ 0x0090, 0x00000000,
+ 0x0094, 0x00000000,
+ 0x0098, 0x00000000,
+ 0x009c, 0x3f3f3f3f,
+};
+
+#endif
--
2.45.1
^ permalink raw reply related
* [PATCH V1 0/5] Add support for Awinic SAR sensor.
From: wangshuaijie @ 2024-05-29 13:06 UTC (permalink / raw)
To: dmitry.torokhov, robh, krzk+dt, conor+dt, jeff, linux-input,
devicetree, linux-kernel
Cc: wangshuaijie, liweilei, kangjiajun
From: shuaijie wang <wangshuaijie@awinic.com>
Add drivers that support Awinic SAR (Specific Absorption Rate)
sensors to the Linux kernel.
The AW9610X series and AW963XX series are high-sensitivity
capacitive proximity detection sensors.
This device detects human proximity and assists electronic devices
in reducing SAR to pass SAR related certifications.
The device reduces RF power and reduces harm when detecting human proximity.
Increase power and improve signal quality when the human body is far away.
This patch implements device initialization, registration,
I/O operation handling and interrupt handling, and passed basic testing.
shuaijie wang (5):
dt-bindings: input: Add YAML to Awinic sar sensor.
Add universal interface for the aw_sar driver.
Add aw9610x series related interfaces to the aw_sar driver.
Add aw963xx series related interfaces to the aw_sar driver.
Add support for Awinic sar sensor.
.../bindings/input/awinic,aw_sar.yaml | 110 +
drivers/input/misc/Kconfig | 9 +
drivers/input/misc/Makefile | 1 +
drivers/input/misc/aw_sar/Makefile | 2 +
drivers/input/misc/aw_sar/aw9610x/aw9610x.c | 884 +++++++
drivers/input/misc/aw_sar/aw9610x/aw9610x.h | 324 +++
drivers/input/misc/aw_sar/aw963xx/aw963xx.c | 986 ++++++++
drivers/input/misc/aw_sar/aw963xx/aw963xx.h | 749 ++++++
drivers/input/misc/aw_sar/aw_sar.c | 2039 +++++++++++++++++
drivers/input/misc/aw_sar/aw_sar.h | 15 +
.../misc/aw_sar/comm/aw_sar_chip_interface.h | 27 +
.../misc/aw_sar/comm/aw_sar_comm_interface.c | 656 ++++++
.../misc/aw_sar/comm/aw_sar_comm_interface.h | 172 ++
drivers/input/misc/aw_sar/comm/aw_sar_type.h | 396 ++++
14 files changed, 6370 insertions(+)
create mode 100644 Documentation/devicetree/bindings/input/awinic,aw_sar.yaml
create mode 100644 drivers/input/misc/aw_sar/Makefile
create mode 100644 drivers/input/misc/aw_sar/aw9610x/aw9610x.c
create mode 100644 drivers/input/misc/aw_sar/aw9610x/aw9610x.h
create mode 100644 drivers/input/misc/aw_sar/aw963xx/aw963xx.c
create mode 100644 drivers/input/misc/aw_sar/aw963xx/aw963xx.h
create mode 100644 drivers/input/misc/aw_sar/aw_sar.c
create mode 100644 drivers/input/misc/aw_sar/aw_sar.h
create mode 100644 drivers/input/misc/aw_sar/comm/aw_sar_chip_interface.h
create mode 100644 drivers/input/misc/aw_sar/comm/aw_sar_comm_interface.c
create mode 100644 drivers/input/misc/aw_sar/comm/aw_sar_comm_interface.h
create mode 100644 drivers/input/misc/aw_sar/comm/aw_sar_type.h
base-commit: e0cce98fe279b64f4a7d81b7f5c3a23d80b92fbc
--
2.45.1
^ permalink raw reply
* [PATCH V1 5/5] Add support for Awinic sar sensor.
From: wangshuaijie @ 2024-05-29 13:06 UTC (permalink / raw)
To: dmitry.torokhov, robh, krzk+dt, conor+dt, jeff, linux-input,
devicetree, linux-kernel
Cc: wangshuaijie, liweilei, kangjiajun
In-Reply-To: <20240529130608.783624-1-wangshuaijie@awinic.com>
From: shuaijie wang <wangshuaijie@awinic.com>
Signed-off-by: shuaijie wang <wangshuaijie@awinic.com>
---
drivers/input/misc/Kconfig | 9 +
drivers/input/misc/Makefile | 1 +
drivers/input/misc/aw_sar/Makefile | 2 +
drivers/input/misc/aw_sar/aw_sar.c | 2039 ++++++++++++++++++++++++++++
drivers/input/misc/aw_sar/aw_sar.h | 15 +
5 files changed, 2066 insertions(+)
create mode 100644 drivers/input/misc/aw_sar/Makefile
create mode 100644 drivers/input/misc/aw_sar/aw_sar.c
create mode 100644 drivers/input/misc/aw_sar/aw_sar.h
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 6ba984d7f0b1..ac56fdd21839 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -939,4 +939,13 @@ config INPUT_STPMIC1_ONKEY
To compile this driver as a module, choose M here: the
module will be called stpmic1_onkey.
+config AWINIC_SAR
+ tristate "Awinic sar sensor support"
+ depends on I2C
+ help
+ Say Y to enable support for the Awinic sar sensor driver.
+
+ To compile this driver as a module, choose M here: the
+ module will be called awinic_sar.
+
endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 04296a4abe8e..6ee1870ea677 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -90,3 +90,4 @@ obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o
obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o
obj-$(CONFIG_INPUT_YEALINK) += yealink.o
obj-$(CONFIG_INPUT_IDEAPAD_SLIDEBAR) += ideapad_slidebar.o
+obj-$(CONFIG_AWINIC_SAR) += aw_sar/
diff --git a/drivers/input/misc/aw_sar/Makefile b/drivers/input/misc/aw_sar/Makefile
new file mode 100644
index 000000000000..c357ecaa4f98
--- /dev/null
+++ b/drivers/input/misc/aw_sar/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_AWINIC_SAR) += awinic_sar.o
+awinic_sar-objs := ./comm/aw_sar_comm_interface.o aw_sar.o ./aw9610x/aw9610x.o ./aw963xx/aw963xx.o
diff --git a/drivers/input/misc/aw_sar/aw_sar.c b/drivers/input/misc/aw_sar/aw_sar.c
new file mode 100644
index 000000000000..c0c37658a482
--- /dev/null
+++ b/drivers/input/misc/aw_sar/aw_sar.c
@@ -0,0 +1,2039 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AWINIC sar sensor driver
+ *
+ * Author: Shuaijie Wang<wangshuaijie@awinic.com>
+ *
+ * Copyright (c) 2024 awinic Technology CO., LTD
+ */
+#include "./comm/aw_sar_chip_interface.h"
+#include "aw_sar.h"
+
+#define AW_SAR_I2C_NAME "awinic_sar"
+
+/*
+ * Please check which power_supply on your platform
+ * can get the charger insertion information, then select it.
+ * eg: "usb"/"charger"/"mtk-master-charger"/"mtk_charger_type"
+ */
+#define USB_POWER_SUPPLY_NAME "charger"
+/*
+ * Check which of your power_supply properties is available
+ * for the charger insertion information and select it.
+ * eg: POWER_SUPPLY_PROP_ONLINE/POWER_SUPPLY_PROP_PRESENT
+ */
+#define AW_USB_PROP_ONLINE POWER_SUPPLY_PROP_ONLINE
+
+#define AW_I2C_RW_RETRY_TIME_MIN (2000)
+#define AW_I2C_RW_RETRY_TIME_MAX (3000)
+#define AW_RETRIES (5)
+
+#define AW_SAR_AWRW_OffSET (20)
+#define AW_SAR_AWRW_DATA_WIDTH (5)
+#define AW_DATA_OffSET_2 (2)
+#define AW_DATA_OffSET_3 (3)
+#define AW_POWER_ON_SYSFS_DELAY_MS (5000)
+#define AW_SAR_MONITOR_ESD_DELAY_MS (5000)
+#define AW_SAR_OFFSET_LEN (15)
+#define AW_SAR_VCC_MIN_UV (1700000)
+#define AW_SAR_VCC_MAX_UV (3600000)
+
+static struct mutex aw_sar_lock;
+
+static int32_t aw_sar_get_chip_info(struct aw_sar *p_sar);
+static void aw_sar_sensor_free(struct aw_sar *p_sar);
+
+//Because disable/enable_irq api Therefore, IRQ is embedded
+void aw_sar_disable_irq(struct aw_sar *p_sar)
+{
+ if (p_sar->irq_init.host_irq_stat == IRQ_ENABLE) {
+ disable_irq(p_sar->irq_init.to_irq);
+ p_sar->irq_init.host_irq_stat = IRQ_DISABLE;
+ }
+}
+
+void aw_sar_enable_irq(struct aw_sar *p_sar)
+{
+ if (p_sar->irq_init.host_irq_stat == IRQ_DISABLE) {
+ enable_irq(p_sar->irq_init.to_irq);
+ p_sar->irq_init.host_irq_stat = IRQ_ENABLE;
+ }
+}
+
+//Chip logic part start
+//Load default array function
+static int32_t
+aw_sar_para_loaded_func(struct i2c_client *i2c, const struct aw_sar_para_load_t *para_load)
+{
+ int32_t ret;
+ int32_t i;
+
+ for (i = 0; i < para_load->reg_arr_len; i = i + 2) {
+ ret = aw_sar_i2c_write(i2c, (uint16_t)para_load->reg_arr[i],
+ para_load->reg_arr[i + 1]);
+ if (ret != 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+//Mode setting function
+static void aw_sar_mode_set_func(struct i2c_client *i2c, int to_irq,
+ struct aw_sar_mode_set_t *mode_set_para,
+ const struct aw_sar_mode_set_t *mode_set, uint8_t len)
+{
+ uint8_t i;
+
+ for (i = 0; i < len; i++) {
+ if ((mode_set[i].chip_mode.curr_mode == mode_set_para->chip_mode.curr_mode) &&
+ (mode_set[i].chip_mode.last_mode == mode_set_para->chip_mode.last_mode) &&
+ ((mode_set[i].chip_id == AW_SAR_NONE_CHECK_CHIP) ||
+ ((mode_set[i].chip_id & mode_set_para->chip_id) != 0))) {
+ if (mode_set[i].mode_switch_ops.enable_clock != NULL)
+ mode_set[i].mode_switch_ops.enable_clock(i2c);
+ if (mode_set[i].mode_switch_ops.rc_irqscr != NULL)
+ mode_set[i].mode_switch_ops.rc_irqscr(i2c);
+ if (mode_set[i].mode_switch_ops.mode_update != NULL)
+ mode_set[i].mode_switch_ops.mode_update(i2c);
+ break;
+ }
+ }
+}
+
+static int32_t aw_sar_check_init_over_irq_func(struct i2c_client *i2c,
+ const struct aw_sar_init_over_irq_t *p_check_irq)
+{
+ int16_t cnt = p_check_irq->wait_times;
+ uint32_t irq_stat;
+ int32_t ret;
+
+ do {
+ ret = aw_sar_i2c_read(i2c, p_check_irq->reg_irqsrc, &irq_stat);
+ if (ret < 0)
+ return ret;
+ if (((irq_stat >> p_check_irq->irq_offset_bit) & p_check_irq->irq_mask) ==
+ p_check_irq->irq_flag)
+ return 0;
+ mdelay(1);
+ } while (cnt--);
+
+ if (cnt < 0)
+ dev_err(&i2c->dev, "init over irq error!");
+
+ return AW_ERR_IRQ_INIT_OVER;
+}
+
+static int32_t
+aw_sar_soft_reset_func(struct i2c_client *i2c, const struct aw_sar_soft_rst_t *p_soft_rst)
+{
+ int32_t ret;
+
+ ret = aw_sar_i2c_write(i2c, p_soft_rst->reg_rst, p_soft_rst->reg_rst_val);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "soft_reset error: %d", ret);
+ return ret;
+ }
+
+ msleep(p_soft_rst->delay_ms);
+
+ return 0;
+}
+//Chip logic part end
+
+static int32_t aw_sar_parse_bin(const struct firmware *cont, struct aw_sar *p_sar)
+{
+ enum aw_bin_err_val bin_ret;
+ struct aw_bin *aw_bin;
+ int32_t ret;
+
+ if (!cont) {
+ dev_err(p_sar->dev, "def_reg_bin request error!");
+ return -EINVAL;
+ }
+
+ dev_dbg(p_sar->dev, "Bin file size: %d", (uint32_t)cont->size);
+
+ aw_bin = devm_kzalloc(p_sar->dev, cont->size + sizeof(struct aw_bin), GFP_KERNEL);
+ if (!aw_bin) {
+ release_firmware(cont);
+ dev_err(p_sar->dev, "failed to allcating memory!");
+ return -ENOMEM;
+ }
+
+ aw_bin->info.len = cont->size;
+ memcpy(aw_bin->info.data, cont->data, cont->size);
+
+ bin_ret = aw_sar_parsing_bin_file(aw_bin);
+ if (bin_ret < 0) {
+ dev_err(p_sar->dev, "parse bin fail! bin_ret = %d", bin_ret);
+ goto err;
+ }
+
+ //Write bin file execution process
+ if (p_sar->load_bin.bin_opera_func != NULL) {
+ ret = p_sar->load_bin.bin_opera_func(aw_bin, p_sar);
+ if (ret != 0) {
+ dev_err(p_sar->dev, "load_bin_to_chip error!");
+ if (p_sar->load_bin.bin_load_fail_opera_func != NULL) {
+ ret = p_sar->load_bin.bin_load_fail_opera_func(aw_bin, p_sar);
+ if (ret != 0) {
+ dev_err(p_sar->dev, "bin_load_fail_opera_func error!");
+ goto err;
+ }
+ } else {
+ goto err;
+ }
+ }
+ } else {
+ dev_err(p_sar->dev, "bin_opera_func is null error!");
+ }
+
+ if (aw_bin != NULL)
+ devm_kfree(p_sar->dev, aw_bin);
+
+ return 0;
+err:
+ if (aw_bin != NULL)
+ devm_kfree(p_sar->dev, aw_bin);
+
+ return -EINVAL;
+}
+
+static int32_t aw_sar_load_bin_comm(struct aw_sar *p_sar)
+{
+ const struct firmware *fw;
+ int32_t ret;
+
+ ret = request_firmware(&fw, p_sar->load_bin.bin_name, p_sar->dev);
+ if (ret != 0) {
+ dev_err(p_sar->dev, "parse %s error!", p_sar->load_bin.bin_name);
+ return ret;
+ }
+
+ ret = aw_sar_parse_bin(fw, p_sar);
+ if (ret != 0) {
+ dev_err(p_sar->dev, "reg_bin %s load error!", p_sar->load_bin.bin_name);
+ return ret;
+ }
+ release_firmware(fw);
+
+ return 0;
+}
+
+static int32_t aw_sar_parse_dts_comm(struct device *dev, struct device_node *np,
+ struct aw_sar_dts_info *p_dts_info)
+{
+ int32_t val;
+
+ val = of_property_read_u32(np, "sar-num", &p_dts_info->sar_num);
+ dev_info(dev, "sar num = %d", p_dts_info->sar_num);
+ if (val != 0) {
+ dev_err(dev, "multiple sar failed!");
+ return -EINVAL;
+ }
+
+ p_dts_info->irq_gpio = of_get_named_gpio(np, "irq-gpio", 0);
+ if (p_dts_info->irq_gpio < 0) {
+ p_dts_info->irq_gpio = -1;
+ dev_err(dev, "no irq gpio provided.");
+ return -EINVAL;
+ }
+
+ val = of_property_read_u32(np, "channel_use_flag", &p_dts_info->channel_use_flag);
+ if (val != 0) {
+ dev_err(dev, "channel_use_flag failed!");
+ return -EINVAL;
+ }
+
+ //GPIO is set as internal pull-up input
+ p_dts_info->use_inter_pull_up = of_property_read_bool(np, "aw_sar,pin_set_inter_pull-up");
+ p_dts_info->use_pm = of_property_read_bool(np, "aw_sar,using_pm_ops");
+ p_dts_info->update_fw_flag = of_property_read_bool(np, "aw_sar,update_fw");
+ p_dts_info->use_plug_cail_flag = of_property_read_bool(np, "aw_sar,use_plug_cail");
+ p_dts_info->monitor_esd_flag = of_property_read_bool(np, "aw_sar,monitor_esd");
+
+ return 0;
+}
+
+static int32_t aw_sar_parse_dts(struct aw_sar *p_sar)
+{
+ int32_t ret;
+
+ ret = aw_sar_parse_dts_comm(p_sar->dev, p_sar->i2c->dev.of_node, &p_sar->dts_info);
+
+ //Special requirements of SAR chip
+ if (p_sar->p_sar_para->p_platform_config->p_add_parse_dts_fn != NULL)
+ ret |= p_sar->p_sar_para->p_platform_config->p_add_parse_dts_fn(p_sar);
+
+ return ret;
+}
+
+static irqreturn_t aw_sar_irq(int32_t irq, void *data)
+{
+ struct aw_sar *p_sar = (struct aw_sar *)data;
+ uint32_t irq_status;
+
+ //step1: read clear interrupt
+ if (p_sar->p_sar_para->p_platform_config->p_irq_init->rc_irq_fn != NULL)
+ irq_status = p_sar->p_sar_para->p_platform_config->p_irq_init->rc_irq_fn(p_sar->i2c);
+
+ //step2: Read the status register for status reporting
+ if (p_sar->p_sar_para->p_platform_config->p_irq_init->irq_spec_handler_fn != NULL)
+ p_sar->p_sar_para->p_platform_config->p_irq_init->irq_spec_handler_fn(irq_status,
+ p_sar);
+
+ //step3: The chip
+
+ if ((!p_sar->dts_info.monitor_esd_flag) && (p_sar->fault_flag == AW_SAR_UNHEALTHY)) {
+ p_sar->fault_flag = AW_SAR_HEALTHY;
+ disable_irq_nosync(p_sar->irq_init.to_irq);
+ p_sar->irq_init.host_irq_stat = IRQ_DISABLE;
+ //aw_sar_soft_reset(p_sar);
+ schedule_delayed_work(&p_sar->update_work, msecs_to_jiffies(500));
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int32_t aw_sar_irq_init_comm(struct aw_sar *p_sar, const struct aw_sar_irq_init_t *p_irq_init)
+{
+ irq_handler_t thread_fn = p_irq_init->thread_fn;
+ int32_t ret;
+
+ snprintf(p_sar->irq_init.label, sizeof(p_sar->irq_init.label),
+ "aw_sar%u_gpio", p_sar->dts_info.sar_num);
+ snprintf(p_sar->irq_init.dev_id, sizeof(p_sar->irq_init.dev_id),
+ "aw_sar%u_irq", p_sar->dts_info.sar_num);
+
+ if (gpio_is_valid(p_sar->dts_info.irq_gpio)) {
+ p_sar->irq_init.to_irq = gpio_to_irq(p_sar->dts_info.irq_gpio);
+ ret = devm_gpio_request_one(p_sar->dev,
+ p_sar->dts_info.irq_gpio,
+ p_irq_init->flags,
+ p_sar->irq_init.label);
+ if (ret) {
+ dev_err(p_sar->dev,
+ "request irq gpio failed, ret = %d", ret);
+ return ret;
+ }
+ if (!thread_fn)
+ thread_fn = aw_sar_irq;
+ ret = devm_request_threaded_irq(p_sar->dev,
+ p_sar->irq_init.to_irq,
+ p_irq_init->handler,
+ thread_fn,
+ p_irq_init->irq_flags,
+ p_sar->irq_init.dev_id,
+ p_sar);
+ if (ret != 0) {
+ dev_err(p_sar->dev,
+ "failed to request IRQ %d: %d",
+ p_sar->irq_init.to_irq, ret);
+ return ret;
+ }
+ } else {
+ dev_err(p_sar->dev, "irq gpio invalid!");
+ return -EINVAL;
+ }
+
+ p_sar->irq_init.host_irq_stat = IRQ_DISABLE;
+ disable_irq(p_sar->irq_init.to_irq);
+
+ return 0;
+}
+
+static int32_t aw_sar_irq_init(struct aw_sar *p_sar)
+{
+
+ if (!p_sar->p_sar_para->p_platform_config->p_irq_init) {
+ dev_err(p_sar->dev, "AW_INVALID_PARA");
+ return -EINVAL;
+ }
+
+ if (p_sar->p_sar_para->p_platform_config->p_irq_init->p_irq_init_fn != NULL) {
+ dev_err(p_sar->dev, "p_irq_init_fn");
+ return p_sar->p_sar_para->p_platform_config->p_irq_init->p_irq_init_fn(p_sar);
+ }
+
+ return aw_sar_irq_init_comm(p_sar, p_sar->p_sar_para->p_platform_config->p_irq_init);
+}
+
+static void aw_sar_irq_free(struct aw_sar *p_sar)
+{
+ if ((p_sar->p_sar_para->p_platform_config != NULL) &&
+ (p_sar->p_sar_para->p_platform_config->p_irq_init != NULL) &&
+ (p_sar->p_sar_para->p_platform_config->p_irq_init->p_irq_deinit_fn != NULL)) {
+ p_sar->p_sar_para->p_platform_config->p_irq_init->p_irq_deinit_fn(p_sar);
+ dev_err(p_sar->dev, "AW_INVALID_PARA");
+ return;
+ }
+}
+
+static int32_t aw_sar_input_init_comm(struct aw_sar *p_sar)
+{
+ int32_t ret;
+ uint32_t i;
+
+ p_sar->channels_arr = devm_kzalloc(p_sar->dev,
+ sizeof(struct aw_channels_info) *
+ p_sar->p_sar_para->ch_num_max,
+ GFP_KERNEL);
+ if (!p_sar->channels_arr) {
+ dev_err(p_sar->dev, "devm_kzalloc err");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < p_sar->p_sar_para->ch_num_max; i++) {
+ snprintf(p_sar->channels_arr[i].name,
+ sizeof(p_sar->channels_arr->name),
+ "aw_sar%u_ch%ud",
+ p_sar->dts_info.sar_num, i);
+
+ p_sar->channels_arr[i].last_channel_info = 0;
+
+ if ((p_sar->dts_info.channel_use_flag >> i) & 0x01) {
+ p_sar->channels_arr[i].used = AW_TRUE;
+ p_sar->channels_arr[i].input = devm_input_allocate_device(p_sar->dev);
+ if (!p_sar->channels_arr[i].input)
+ return -EINVAL;
+ p_sar->channels_arr[i].input->name = p_sar->channels_arr[i].name;
+ input_set_abs_params(p_sar->channels_arr[i].input,
+ ABS_DISTANCE, -1, 100, 0, 0);
+ ret = input_register_device(p_sar->channels_arr[i].input);
+ if (ret) {
+ dev_err(p_sar->dev, "failed to register input device");
+ return ret;
+ }
+ } else {
+ p_sar->channels_arr[i].used = AW_FALSE;
+ p_sar->channels_arr[i].input = NULL;
+ }
+ }
+
+ return 0;
+}
+
+static int32_t aw_sar_input_init(struct aw_sar *p_sar)
+{
+ if (p_sar->p_sar_para->p_platform_config->p_input_init_fn != NULL)
+ return p_sar->p_sar_para->p_platform_config->p_input_init_fn(p_sar);
+
+ return aw_sar_input_init_comm(p_sar);
+}
+
+static void aw_sar_input_free(struct aw_sar *p_sar)
+{
+ if ((p_sar->p_sar_para->p_platform_config != NULL) &&
+ (p_sar->p_sar_para->p_platform_config->p_input_deinit_fn != NULL)) {
+ p_sar->p_sar_para->p_platform_config->p_input_deinit_fn(p_sar);
+ }
+}
+
+static int32_t aw_sar_check_init_over_irq_comm(struct aw_sar *p_sar)
+{
+ int32_t ret;
+
+ if (!p_sar->p_sar_para->p_init_over_irq)
+ return -EINVAL;
+
+ ret = aw_sar_check_init_over_irq_func(p_sar->i2c, p_sar->p_sar_para->p_init_over_irq);
+ if (ret == AW_ERR_IRQ_INIT_OVER) {
+ if (p_sar->p_sar_para->p_init_over_irq->p_get_err_type_fn != NULL) {
+ //Consider the abnormality reasonable
+ if (p_sar->p_sar_para->p_init_over_irq->p_get_err_type_fn(p_sar) == 0) {
+ p_sar->fw_fail_flag = AW_TRUE;
+ return 0;
+ }
+ }
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+//If there is no special operation on the chip, execute the common process
+int32_t aw_sar_check_init_over_irq(struct aw_sar *p_sar)
+{
+ if (!p_sar->p_sar_para->p_init_over_irq)
+ return -EINVAL;
+
+ if (p_sar->p_sar_para->p_init_over_irq->p_check_init_over_irq_fn != NULL)
+ return p_sar->p_sar_para->p_init_over_irq->p_check_init_over_irq_fn(p_sar);
+
+ return aw_sar_check_init_over_irq_comm(p_sar);
+}
+
+static int32_t aw_sar_chip_other_operation(struct aw_sar *p_sar)
+{
+ if (p_sar->p_sar_para->p_other_operation != NULL)
+ return p_sar->p_sar_para->p_other_operation(p_sar);
+
+ return 0;
+}
+
+static void aw_sar_chip_other_operation_free(struct aw_sar *p_sar)
+{
+ if (p_sar->p_sar_para->p_other_opera_free != NULL)
+ p_sar->p_sar_para->p_other_opera_free(p_sar);
+}
+
+int32_t aw_sar_soft_reset(struct aw_sar *p_sar)
+{
+ if (!p_sar->p_sar_para->p_soft_rst)
+ return -EINVAL;
+
+ //If a private interface is defined, the private interface is used
+ if (p_sar->p_sar_para->p_soft_rst->p_soft_reset_fn != NULL)
+ return p_sar->p_sar_para->p_soft_rst->p_soft_reset_fn(p_sar);
+
+ return aw_sar_soft_reset_func(p_sar->i2c, p_sar->p_sar_para->p_soft_rst);
+}
+
+static int32_t aw_sar_check_chipid(struct aw_sar *p_sar)
+{
+ if (!p_sar->p_sar_para)
+ return -EINVAL;
+
+ if (p_sar->p_sar_para->p_check_chipid != NULL) {
+ if (p_sar->p_sar_para->p_check_chipid->p_check_chipid_fn != NULL)
+ return p_sar->p_sar_para->p_check_chipid->p_check_chipid_fn(p_sar);
+ }
+
+ return -EINVAL;
+}
+
+int32_t aw_sar_load_def_reg_bin(struct aw_sar *p_sar)
+{
+ if ((!p_sar->p_sar_para->p_reg_bin) ||
+ (!p_sar->p_sar_para->p_reg_bin->bin_name)) {
+ dev_err(p_sar->dev, "p_reg_bin is NULL or bin_name is NULL error");
+ p_sar->ret_val = AW_BIN_PARA_INVALID;
+ return -EINVAL;
+ }
+
+ snprintf(p_sar->load_bin.bin_name, sizeof(p_sar->load_bin.bin_name),
+ "%s_%u.bin", p_sar->p_sar_para->p_reg_bin->bin_name,
+ p_sar->dts_info.sar_num);
+
+ p_sar->load_bin.bin_opera_func = p_sar->p_sar_para->p_reg_bin->bin_opera_func;
+
+ return aw_sar_load_bin_comm(p_sar);
+}
+
+static int32_t aw_sar_para_loaded(struct aw_sar *p_sar)
+{
+ if (!p_sar->p_sar_para->p_reg_arr)
+ return -EINVAL;
+
+ aw_sar_para_loaded_func(p_sar->i2c, p_sar->p_sar_para->p_reg_arr);
+
+ return 0;
+}
+
+static int32_t aw_sar_reg_update_boot_work(struct aw_sar *p_sar)
+{
+ if ((!p_sar->p_sar_para->p_boot_bin) || (!p_sar->p_sar_para->p_boot_bin->bin_name))
+ return -EINVAL;
+
+ snprintf(p_sar->load_bin.bin_name, sizeof(p_sar->load_bin.bin_name),
+ "%s_%u.bin", p_sar->p_sar_para->p_boot_bin->bin_name,
+ p_sar->dts_info.sar_num);
+
+ p_sar->load_bin.bin_opera_func = p_sar->p_sar_para->p_boot_bin->bin_opera_func;
+
+ return aw_sar_load_bin_comm(p_sar);
+}
+
+static int32_t aw_sar_update_fw_para(struct aw_sar *p_sar, const struct aw_sar_load_bin_t *p_bin)
+{
+ if ((!p_bin) || (!p_bin->bin_name))
+ return -EINVAL;
+
+ p_sar->load_bin.bin_opera_func = p_bin->bin_opera_func;
+ p_sar->load_bin.bin_load_fail_opera_func = p_bin->bin_load_fail_opera;
+
+ snprintf(p_sar->load_bin.bin_name, sizeof(p_sar->load_bin.bin_name),
+ "%s_%u.bin", p_bin->bin_name, p_sar->dts_info.sar_num);
+
+ return 0;
+}
+
+int32_t aw_sar_update_fw(struct aw_sar *p_sar)
+{
+ if (aw_sar_update_fw_para(p_sar, p_sar->p_sar_para->p_fw_bin) != 0)
+ return -EINVAL;
+
+ return aw_sar_load_bin_comm(p_sar);
+}
+
+static int32_t aw_sar_node_prox_update_fw(struct aw_sar *p_sar)
+{
+ if (aw_sar_update_fw_para(p_sar, p_sar->p_sar_para->p_prox_fw) != 0)
+ return -EINVAL;
+
+ return aw_sar_load_bin_comm(p_sar);
+}
+
+static int32_t aw_sar_node_reg_update_fw(struct aw_sar *p_sar)
+{
+ if (aw_sar_update_fw_para(p_sar, p_sar->p_sar_para->p_reg_fw))
+ return -EINVAL;
+
+ return aw_sar_load_bin_comm(p_sar);
+}
+
+static int32_t aw_sar_awrw_data_analysis(struct aw_sar *p_sar, const char *buf, uint8_t len)
+{
+ uint32_t theory_len = len * AW_SAR_AWRW_DATA_WIDTH + AW_SAR_AWRW_OffSET;
+ uint32_t actual_len = strlen(buf);
+ uint8_t data_temp[2] = { 0 };
+ uint32_t tranfar_data_temp;
+ uint8_t index = 0;
+ uint32_t i;
+
+ if (theory_len != actual_len) {
+ dev_err(p_sar->dev, "error theory_len = %d actual_len = %d",
+ theory_len, actual_len);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < len * AW_SAR_AWRW_DATA_WIDTH; i += AW_SAR_AWRW_DATA_WIDTH) {
+ data_temp[0] = buf[AW_SAR_AWRW_OffSET + i + AW_DATA_OffSET_2];
+ data_temp[1] = buf[AW_SAR_AWRW_OffSET + i + AW_DATA_OffSET_3];
+
+ if (sscanf(data_temp, "%02x", &tranfar_data_temp) == 1)
+ p_sar->awrw_info.p_i2c_tranfar_data[index] = (uint8_t)tranfar_data_temp;
+ index++;
+ }
+
+ return 0;
+}
+
+static int32_t aw_sar_awrw_write(struct aw_sar *p_sar, const char *buf)
+{
+ int32_t ret;
+
+ ret = aw_sar_awrw_data_analysis(p_sar, buf, p_sar->awrw_info.i2c_tranfar_data_len);
+ if (ret == 0)
+ aw_sar_i2c_write_seq(p_sar->i2c, p_sar->awrw_info.p_i2c_tranfar_data,
+ p_sar->awrw_info.i2c_tranfar_data_len);
+
+ return ret;
+}
+
+static int32_t aw_sar_awrw_read(struct aw_sar *p_sar, const char *buf)
+{
+ int32_t ret = 0;
+ uint8_t *p_buf = p_sar->awrw_info.p_i2c_tranfar_data + p_sar->awrw_info.addr_len;
+ uint32_t len = (uint16_t)(p_sar->awrw_info.data_len * p_sar->awrw_info.reg_num);
+
+ ret = aw_sar_awrw_data_analysis(p_sar, buf, p_sar->awrw_info.addr_len);
+ if (ret == 0) {
+ ret = aw_sar_i2c_read_seq(p_sar->i2c,
+ p_sar->awrw_info.p_i2c_tranfar_data,
+ p_sar->awrw_info.addr_len,
+ p_sar->awrw_info.p_i2c_tranfar_data + p_sar->awrw_info.addr_len,
+ (uint16_t)(p_sar->awrw_info.data_len * p_sar->awrw_info.reg_num));
+ if (ret != 0)
+ memset(p_buf, 0xff, len);
+ }
+
+ return ret;
+}
+
+static int32_t aw_sar_awrw_get_func(struct aw_sar *p_sar, char *buf)
+{
+ uint32_t len = 0;
+ uint32_t i;
+
+ if (!p_sar->awrw_info.p_i2c_tranfar_data) {
+ dev_err(p_sar->dev, "p_i2c_tranfar_data is NULL");
+ return len;
+ }
+
+ if (p_sar->awrw_info.rw_flag == AW_SAR_PACKAGE_RD) {
+ for (i = 0; i < p_sar->awrw_info.i2c_tranfar_data_len; i++) {
+ len += snprintf(buf + len, PAGE_SIZE - len, "0x%02x,",
+ p_sar->awrw_info.p_i2c_tranfar_data[i]);
+ }
+ } else {
+ for (i = 0; i < (p_sar->awrw_info.data_len) * (p_sar->awrw_info.reg_num); i++) {
+ len += snprintf(buf + len, PAGE_SIZE - len, "0x%02x,",
+ p_sar->awrw_info.p_i2c_tranfar_data[p_sar->awrw_info.addr_len + i]);
+ }
+ }
+ snprintf(buf + len - 1, PAGE_SIZE - len, "\n");
+
+ devm_kfree(p_sar->dev, p_sar->awrw_info.p_i2c_tranfar_data);
+ p_sar->awrw_info.p_i2c_tranfar_data = NULL;
+
+ return len;
+}
+
+//Function: continuous read register interface
+static ssize_t awrw_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct aw_sar *p_sar = dev_get_drvdata(dev);
+ ssize_t ret;
+
+ mutex_lock(&aw_sar_lock);
+ if ((p_sar->p_sar_para->p_aw_sar_awrw != NULL) &&
+ (p_sar->p_sar_para->p_aw_sar_awrw->p_get_awrw_node_fn != NULL)) {
+ ret = (ssize_t)p_sar->p_sar_para->p_aw_sar_awrw->p_get_awrw_node_fn(p_sar, buf);
+ mutex_unlock(&aw_sar_lock);
+ return ret;
+ }
+
+ ret = (ssize_t)aw_sar_awrw_get_func(p_sar, buf);
+
+ mutex_unlock(&aw_sar_lock);
+
+ return ret;
+}
+
+static int32_t aw_sar_awrw_handle(struct aw_sar *p_sar, const char *buf)
+{
+ int32_t ret;
+
+ p_sar->awrw_info.i2c_tranfar_data_len = p_sar->awrw_info.addr_len +
+ p_sar->awrw_info.data_len *
+ p_sar->awrw_info.reg_num;
+
+ if (p_sar->awrw_info.p_i2c_tranfar_data != NULL) {
+ devm_kfree(p_sar->dev, p_sar->awrw_info.p_i2c_tranfar_data);
+ p_sar->awrw_info.p_i2c_tranfar_data = NULL;
+ }
+
+ p_sar->awrw_info.p_i2c_tranfar_data = devm_kzalloc(p_sar->dev,
+ p_sar->awrw_info.i2c_tranfar_data_len, GFP_KERNEL);
+ if (!p_sar->awrw_info.p_i2c_tranfar_data)
+ return -ENOMEM;
+
+ if (p_sar->awrw_info.rw_flag == AW_SAR_I2C_WR) {
+ ret = aw_sar_awrw_write(p_sar, buf);
+ if (ret != 0)
+ dev_err(p_sar->dev, "awrw_write error");
+ if (p_sar->awrw_info.p_i2c_tranfar_data != NULL) {
+ devm_kfree(p_sar->dev, p_sar->awrw_info.p_i2c_tranfar_data);
+ p_sar->awrw_info.p_i2c_tranfar_data = NULL;
+ }
+ } else if (p_sar->awrw_info.rw_flag == AW_SAR_I2C_RD) {
+ ret = aw_sar_awrw_read(p_sar, buf);
+ if (ret != 0)
+ dev_err(p_sar->dev, "awrw_read error");
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int32_t aw_sar_awrw_set_func(struct aw_sar *p_sar, const char *buf)
+{
+ uint32_t rw_flag;
+ uint32_t addr_bytes;
+ uint32_t data_bytes;
+ uint32_t package_nums;
+ uint32_t addr_tmp;
+ uint32_t buf_index0;
+ uint32_t buf_index1;
+ uint32_t r_buf_len = 0;
+ uint32_t tr_offset = 0;
+ uint8_t addr[4] = { 0 };
+ uint32_t theory_len;
+ uint32_t actual_len;
+ uint32_t reg_num;
+ uint32_t i;
+ uint32_t j;
+
+ //step1: Parse frame header
+ if (sscanf(buf, "0x%02x 0x%02x 0x%02x ", &rw_flag, &addr_bytes, &data_bytes) != 3) {
+ dev_err(p_sar->dev, "sscanf0 parse error!");
+ return -EINVAL;
+ }
+ p_sar->awrw_info.rw_flag = (uint8_t)rw_flag;
+ p_sar->awrw_info.addr_len = (uint8_t)addr_bytes;
+ p_sar->awrw_info.data_len = (uint8_t)data_bytes;
+
+ if (addr_bytes > 4) {
+ dev_err(p_sar->dev, "para error!");
+ return -EINVAL;
+ }
+
+ if ((rw_flag == AW_SAR_I2C_WR) || (rw_flag == AW_SAR_I2C_RD)) {
+ if (sscanf(buf + AW_SAR_OFFSET_LEN, "0x%02x ", ®_num) != 1) {
+ dev_err(p_sar->dev, "sscanf1 parse error!");
+ return -EINVAL;
+ }
+ p_sar->awrw_info.reg_num = (uint8_t)reg_num;
+ aw_sar_awrw_handle(p_sar, buf);
+ } else if (rw_flag == AW_SAR_PACKAGE_RD) {
+ //step2: Get number of packages
+ if (sscanf(buf + AW_SAR_OFFSET_LEN, "0x%02x ", &package_nums) != 1) {
+ dev_err(p_sar->dev, "sscanf2 parse error!");
+ return -EINVAL;
+ }
+ theory_len = AW_SAR_OFFSET_LEN + AW_SAR_AWRW_DATA_WIDTH +
+ package_nums * (AW_SAR_AWRW_DATA_WIDTH +
+ AW_SAR_AWRW_DATA_WIDTH * addr_bytes);
+ actual_len = strlen(buf);
+ if (theory_len != actual_len) {
+ dev_err(p_sar->dev, "theory_len:%d, actual_len:%d error!",
+ theory_len, actual_len);
+ return -EINVAL;
+ }
+
+ //step3: Get the size of read data and apply for space
+ for (i = 0; i < package_nums; i++) {
+ buf_index0 = AW_SAR_OFFSET_LEN + AW_SAR_AWRW_DATA_WIDTH +
+ (AW_SAR_AWRW_DATA_WIDTH * addr_bytes +
+ AW_SAR_AWRW_DATA_WIDTH) * i;
+ if (sscanf(buf + buf_index0, "0x%02x", ®_num) != 1) {
+ dev_err(p_sar->dev, "sscanf3 parse error!");
+ return -EINVAL;
+ }
+ r_buf_len += reg_num * data_bytes;
+ }
+
+ p_sar->awrw_info.i2c_tranfar_data_len = r_buf_len;
+
+ if (p_sar->awrw_info.p_i2c_tranfar_data != NULL) {
+ devm_kfree(p_sar->dev, p_sar->awrw_info.p_i2c_tranfar_data);
+ p_sar->awrw_info.p_i2c_tranfar_data = NULL;
+ }
+ p_sar->awrw_info.p_i2c_tranfar_data = devm_kzalloc(p_sar->dev,
+ r_buf_len, GFP_KERNEL);
+ if (!p_sar->awrw_info.p_i2c_tranfar_data)
+ return -ENOMEM;
+
+ //step3: Resolve register address and read data in packets
+ for (i = 0; i < package_nums; i++) {
+ buf_index0 = AW_SAR_OFFSET_LEN + AW_SAR_AWRW_DATA_WIDTH +
+ (AW_SAR_AWRW_DATA_WIDTH * addr_bytes + AW_SAR_AWRW_DATA_WIDTH) * i;
+ if (sscanf(buf + buf_index0, "0x%02x", ®_num) != 1) {
+ dev_err(p_sar->dev, "sscanf4 parse error!");
+ return -EINVAL;
+ }
+
+ for (j = 0; j < addr_bytes; j += 1) {
+ buf_index1 = buf_index0 + AW_SAR_AWRW_DATA_WIDTH +
+ (j * AW_SAR_AWRW_DATA_WIDTH);
+ if (sscanf(buf + buf_index1, "0x%02x", &addr_tmp) == 1) {
+ addr[j] = (uint8_t)addr_tmp;
+ } else {
+ dev_err(p_sar->dev, "sscanf5 parse error!");
+ return -EINVAL;
+ }
+ }
+ aw_sar_i2c_read_seq(p_sar->i2c,
+ addr,
+ addr_bytes,
+ p_sar->awrw_info.p_i2c_tranfar_data + tr_offset,
+ (uint16_t)(data_bytes * reg_num));
+ tr_offset += data_bytes * reg_num;
+ }
+ }
+
+ return 0;
+}
+
+//Function: continuous write register interface
+static ssize_t
+awrw_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct aw_sar *p_sar = dev_get_drvdata(dev);
+
+ mutex_lock(&aw_sar_lock);
+
+ if ((p_sar->p_sar_para->p_aw_sar_awrw != NULL) &&
+ (p_sar->p_sar_para->p_aw_sar_awrw->p_set_awrw_node_fn != NULL)) {
+ p_sar->p_sar_para->p_aw_sar_awrw->p_set_awrw_node_fn(p_sar, buf, count);
+ mutex_unlock(&aw_sar_lock);
+ return count;
+ }
+
+ aw_sar_awrw_set_func(p_sar, buf);
+
+ mutex_unlock(&aw_sar_lock);
+
+ return count;
+}
+//Print all readable register values
+static ssize_t reg_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct aw_sar *p_sar = dev_get_drvdata(dev);
+ uint8_t reg_rd_access;
+ uint32_t reg_data;
+ ssize_t len = 0;
+ int32_t ret;
+ uint16_t i;
+
+ if (!p_sar->p_sar_para->p_reg_list)
+ return len;
+
+ reg_rd_access = p_sar->p_sar_para->p_reg_list->reg_rd_access;
+
+ for (i = 0; i < p_sar->p_sar_para->p_reg_list->reg_num; i++) {
+ if (p_sar->p_sar_para->p_reg_list->reg_perm[i].rw & reg_rd_access) {
+ ret = aw_sar_i2c_read(p_sar->i2c,
+ p_sar->p_sar_para->p_reg_list->reg_perm[i].reg, ®_data);
+ if (ret < 0)
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "i2c read error ret = %d\n", ret);
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "%x,%x\n",
+ p_sar->p_sar_para->p_reg_list->reg_perm[i].reg,
+ reg_data);
+ }
+ }
+
+ return len;
+}
+
+//Write register interface with write permission
+static ssize_t
+reg_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct aw_sar *p_sar = dev_get_drvdata(dev);
+ uint32_t databuf[2] = { 0, 0 };
+ uint8_t reg_wd_access;
+ uint16_t i;
+
+ if (!p_sar->p_sar_para->p_reg_list) {
+ dev_err(p_sar->dev, "AW_INVALID_PARA");
+ return count;
+ }
+
+ reg_wd_access = p_sar->p_sar_para->p_reg_list->reg_wd_access;
+
+ if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) != 2)
+ return count;
+
+ for (i = 0; i < p_sar->p_sar_para->p_reg_list->reg_num; i++) {
+ if ((uint16_t)databuf[0] == p_sar->p_sar_para->p_reg_list->reg_perm[i].reg) {
+ if (p_sar->p_sar_para->p_reg_list->reg_perm[i].rw & reg_wd_access) {
+ aw_sar_i2c_write(p_sar->i2c,
+ (uint16_t)databuf[0], (uint32_t)databuf[1]);
+ }
+ break;
+ }
+ }
+
+ return count;
+}
+
+//set chip Soft reset
+static ssize_t
+soft_rst_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct aw_sar *p_sar = dev_get_drvdata(dev);
+ uint32_t flag;
+
+ if (kstrtouint(buf, 0, &flag) != 0) {
+ dev_err(p_sar->dev, "kstrtouint parse err");
+ return count;
+ }
+
+ if (flag == AW_TRUE)
+ aw_sar_soft_reset(p_sar);
+
+ return count;
+}
+
+static int32_t aw_sar_aot(struct aw_sar *p_sar)
+{
+ if (!p_sar->p_sar_para->p_aot)
+ return -EINVAL;
+
+ if (p_sar->p_sar_para->p_aot->p_set_aot_node_fn != NULL)
+ return p_sar->p_sar_para->p_aot->p_set_aot_node_fn(p_sar);
+
+ return aw_sar_i2c_write_bits(p_sar->i2c, p_sar->p_sar_para->p_aot->aot_reg,
+ p_sar->p_sar_para->p_aot->aot_mask,
+ p_sar->p_sar_para->p_aot->aot_flag);
+}
+
+//Perform Auto-Offset-Tuning (AOT)
+static ssize_t
+aot_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct aw_sar *p_sar = dev_get_drvdata(dev);
+ uint32_t cali_flag;
+
+ if (kstrtouint(buf, 0, &cali_flag) != 0)
+ return count;
+
+ if (cali_flag == AW_TRUE)
+ aw_sar_aot(p_sar);
+ else
+ dev_err(p_sar->dev, "fail to set aot cali");
+
+ return count;
+}
+
+//update Register configuration and set the chip active mode
+int32_t aw_sar_update_reg_set_func(struct aw_sar *p_sar)
+{
+ aw_sar_load_def_reg_bin(p_sar);
+ aw_sar_mode_set(p_sar, p_sar->p_sar_para->p_chip_mode->active);
+
+ return 0;
+}
+
+//Update register configuration
+static ssize_t
+update_reg_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct aw_sar *p_sar = dev_get_drvdata(dev);
+ uint32_t flag;
+
+ if (kstrtouint(buf, 0, &flag) != 0) {
+ dev_err(p_sar->dev, "kstrtouint parse error");
+ return count;
+ }
+
+ if (flag == AW_TRUE) {
+ mutex_lock(&aw_sar_lock);
+ aw_sar_soft_reset(p_sar);
+ aw_sar_update_reg_set_func(p_sar);
+ mutex_unlock(&aw_sar_lock);
+ }
+
+ return count;
+}
+
+//get chip diff val
+static ssize_t aw_sar_get_diff(struct aw_sar *p_sar, char *buf)
+{
+ const struct aw_sar_diff_t *diff = p_sar->p_sar_para->p_diff;
+ int32_t diff_val;
+ ssize_t len = 0;
+ uint32_t data;
+ int32_t ret;
+ uint32_t i;
+
+ if (!p_sar->p_sar_para->p_diff)
+ return -EINVAL;
+
+ //If a private interface is defined, the private interface is used
+ if (p_sar->p_sar_para->p_diff->p_get_diff_node_fn != NULL)
+ return p_sar->p_sar_para->p_diff->p_get_diff_node_fn(p_sar, buf);
+
+ for (i = 0; i < p_sar->p_sar_para->ch_num_max; i++) {
+ ret = aw_sar_i2c_read(p_sar->i2c, diff->diff0_reg + i * diff->diff_step, &data);
+ if (ret != 0) {
+ dev_err(p_sar->dev, "read diff err: %d", ret);
+ return ret;
+ }
+ diff_val = (int32_t)data / (int32_t)diff->rm_float;
+ len += snprintf(buf + len, PAGE_SIZE - len, "DIFF_CH%u = %d\n", i, diff_val);
+ }
+
+ return len;
+}
+
+
+//Print diff values of all channels of the chip.
+static ssize_t diff_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct aw_sar *p_sar = dev_get_drvdata(dev);
+
+ return aw_sar_get_diff(p_sar, buf);
+}
+
+void aw_sar_mode_set(struct aw_sar *p_sar, uint8_t curr_mode)
+{
+ struct aw_sar_mode_set_t mode_set_para;
+
+ if (!p_sar->p_sar_para->p_mode)
+ return;
+
+ //If a private interface is defined, the private interface is used
+ if (p_sar->p_sar_para->p_mode->p_set_mode_node_fn != NULL) {
+ p_sar->p_sar_para->p_mode->p_set_mode_node_fn(p_sar, curr_mode);
+ return;
+ }
+
+ mode_set_para.chip_id = p_sar->chip_type;
+ mode_set_para.chip_mode.curr_mode = curr_mode;
+ mode_set_para.chip_mode.last_mode = p_sar->last_mode;
+
+ aw_sar_mode_set_func(p_sar->i2c, p_sar->irq_init.to_irq, &mode_set_para,
+ p_sar->p_sar_para->p_mode->mode_set_arr,
+ p_sar->p_sar_para->p_mode->mode_set_arr_len);
+ p_sar->last_mode = curr_mode;
+}
+
+//Set the chip to enter different modes
+static ssize_t mode_operation_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct aw_sar *p_sar = dev_get_drvdata(dev);
+ uint32_t mode;
+
+ if (kstrtouint(buf, 0, &mode) != 0) {
+ dev_err(p_sar->dev, "kstrtouint parse err");
+ return count;
+ }
+ aw_sar_mode_set(p_sar, mode);
+
+ return count;
+}
+
+//Get the current mode of the chip
+static ssize_t mode_operation_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct aw_sar *p_sar = dev_get_drvdata(dev);
+ ssize_t len = 0;
+
+ if (!p_sar->p_sar_para->p_mode)
+ return len;
+
+ if (p_sar->p_sar_para->p_mode->p_get_mode_node_fn != NULL)
+ len = p_sar->p_sar_para->p_mode->p_get_mode_node_fn(p_sar, buf);
+
+ return len;
+}
+
+//Print information related information
+static ssize_t chip_info_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct aw_sar *p_sar = dev_get_drvdata(dev);
+ ssize_t len = 0;
+
+ len += snprintf(buf + len, PAGE_SIZE - len, "reg_load_state: %d\n", p_sar->ret_val);
+
+ if ((p_sar->p_sar_para->p_get_chip_info != NULL) &&
+ (p_sar->p_sar_para->p_get_chip_info->p_get_chip_info_node_fn != NULL)) {
+ p_sar->p_sar_para->p_get_chip_info->p_get_chip_info_node_fn(p_sar, buf, &len);
+ }
+
+ return len;
+}
+
+static ssize_t offset_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct aw_sar *p_sar = dev_get_drvdata(dev);
+ ssize_t len = 0;
+
+ if ((p_sar->p_sar_para->p_offset != NULL) &&
+ (p_sar->p_sar_para->p_offset->p_get_offset_node_fn != NULL))
+ len = (ssize_t)p_sar->p_sar_para->p_offset->p_get_offset_node_fn(p_sar, buf);
+
+ return len;
+}
+
+static DEVICE_ATTR_RW(awrw);
+static DEVICE_ATTR_RW(reg);
+static DEVICE_ATTR_WO(soft_rst);
+static DEVICE_ATTR_WO(aot);
+static DEVICE_ATTR_WO(update_reg);
+static DEVICE_ATTR_RO(diff);
+static DEVICE_ATTR_RW(mode_operation);
+static DEVICE_ATTR_RO(chip_info);
+static DEVICE_ATTR_RO(offset);
+
+static struct attribute *aw_sar_attributes[] = {
+ &dev_attr_awrw.attr,
+ &dev_attr_reg.attr,
+ &dev_attr_soft_rst.attr,
+ &dev_attr_aot.attr,
+ &dev_attr_update_reg.attr,
+ &dev_attr_diff.attr,
+ &dev_attr_mode_operation.attr,
+ &dev_attr_chip_info.attr,
+ &dev_attr_offset.attr,
+ NULL
+};
+
+static const struct attribute_group aw_sar_attribute_group = {
+ .attrs = aw_sar_attributes,
+};
+
+//firmware upgrade through write register mode, and the operation is supported by flash chip
+static ssize_t prot_update_fw_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct aw_sar *p_sar = dev_get_drvdata(dev);
+
+
+ mutex_lock(&aw_sar_lock);
+ aw_sar_disable_irq(p_sar);
+
+ p_sar->prot_update_state = aw_sar_node_prox_update_fw(p_sar);
+
+ aw_sar_enable_irq(p_sar);
+ mutex_unlock(&aw_sar_lock);
+
+ return count;
+}
+
+//firmware upgrade throughr Write register mode,and the operation is supported by flash chip
+static ssize_t reg_update_fw_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct aw_sar *p_sar = dev_get_drvdata(dev);
+
+ mutex_lock(&aw_sar_lock);
+ aw_sar_disable_irq(p_sar);
+
+ aw_sar_node_reg_update_fw(p_sar);
+
+ aw_sar_enable_irq(p_sar);
+ mutex_unlock(&aw_sar_lock);
+
+ return count;
+}
+
+//boot upgrade throughr Write register mode,and the operation is supported by flash chip
+static ssize_t reg_update_boot_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct aw_sar *p_sar = dev_get_drvdata(dev);
+
+ if (!p_sar->p_sar_para->p_boot_bin)
+ return count;
+
+ mutex_lock(&aw_sar_lock);
+ aw_sar_disable_irq(p_sar);
+
+ aw_sar_reg_update_boot_work(p_sar);
+
+ aw_sar_enable_irq(p_sar);
+ mutex_unlock(&aw_sar_lock);
+
+ return count;
+}
+
+//Print the protocol upgrade status. 0 is success, Not 0 is failure
+//Print the current firmware version number
+static ssize_t prot_update_fw_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct aw_sar *p_sar = dev_get_drvdata(dev);
+ ssize_t len = 0;
+
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "protocol update state:%s!\n",
+ (p_sar->prot_update_state == 0) ? "ok" : "error");
+ if ((p_sar->p_sar_para->p_prox_fw != NULL) &&
+ (p_sar->p_sar_para->p_prox_fw->p_get_prot_update_fw_node_fn != NULL))
+ p_sar->p_sar_para->p_prox_fw->p_get_prot_update_fw_node_fn(p_sar, buf, &len);
+
+ return len;
+}
+
+static DEVICE_ATTR_RW(prot_update_fw);
+static DEVICE_ATTR_WO(reg_update_fw);
+static DEVICE_ATTR_WO(reg_update_boot);
+
+static struct attribute *aw_sar_update_attributes[] = {
+ &dev_attr_prot_update_fw.attr,
+ &dev_attr_reg_update_fw.attr,
+ &dev_attr_reg_update_boot.attr,
+ NULL
+};
+
+static const struct attribute_group aw_sar_update_attribute_group = {
+ .attrs = aw_sar_update_attributes,
+};
+
+static void aw_sar_update_work(struct work_struct *work)
+{
+ struct aw_sar *p_sar = container_of(work, struct aw_sar, update_work.work);
+ int32_t ret;
+
+ mutex_lock(&aw_sar_lock);
+
+ if (p_sar->dts_info.update_fw_flag == true) {
+ ret = aw_sar_update_fw(p_sar);
+ if (ret != 0) {
+ dev_err(p_sar->dev, "protocol upgrade firmware error!");
+ p_sar->ret_val = AW_PROT_UPDATE_ERR;
+ }
+ }
+
+ //2.Parse the bin file and load the register configuration
+ ret = aw_sar_load_def_reg_bin(p_sar);
+ if (ret != 0) {
+ p_sar->ret_val = AW_REG_LOAD_ERR;
+ dev_err(p_sar->dev, "reg_bin load err!");
+ aw_sar_para_loaded(p_sar);
+ }
+
+ //3.active chip
+ aw_sar_mode_set(p_sar, p_sar->p_sar_para->p_chip_mode->init_mode);
+ if (p_sar->irq_init.host_irq_stat == IRQ_DISABLE) {
+ enable_irq(p_sar->irq_init.to_irq);
+ p_sar->irq_init.host_irq_stat = IRQ_ENABLE;
+ }
+ p_sar->driver_code_initover_flag = 1;
+ mutex_unlock(&aw_sar_lock);
+}
+
+static void aw_sar_update(struct aw_sar *p_sar)
+{
+ if (!p_sar->p_sar_para->p_reg_bin)
+ return;
+
+ if (p_sar->p_sar_para->p_reg_bin->p_update_fn != NULL)
+ p_sar->p_sar_para->p_reg_bin->p_update_fn(p_sar);
+
+ if (p_sar->driver_code_initover_flag) {
+ schedule_delayed_work(&p_sar->update_work, msecs_to_jiffies(0));
+ } else {
+ INIT_DELAYED_WORK(&p_sar->update_work, aw_sar_update_work);
+ schedule_delayed_work(&p_sar->update_work,
+ msecs_to_jiffies(AW_POWER_ON_SYSFS_DELAY_MS));
+ }
+}
+
+static int32_t aw_sar_create_node(struct aw_sar *p_sar)
+{
+ int32_t ret;
+
+ i2c_set_clientdata(p_sar->i2c, p_sar);
+
+ ret = sysfs_create_group(&p_sar->i2c->dev.kobj, &aw_sar_attribute_group);
+
+ if (p_sar->dts_info.update_fw_flag == true)
+ ret |= sysfs_create_group(&p_sar->i2c->dev.kobj, &aw_sar_update_attribute_group);
+
+ //Special requirements of SAR chip
+ if (p_sar->p_sar_para->p_platform_config->p_add_node_create_fn != NULL)
+ ret |= p_sar->p_sar_para->p_platform_config->p_add_node_create_fn(p_sar);
+
+ return ret;
+}
+
+static void aw_sar_node_free(struct aw_sar *p_sar)
+{
+ sysfs_remove_group(&p_sar->i2c->dev.kobj, &aw_sar_attribute_group);
+
+ if (p_sar->dts_info.update_fw_flag == true)
+ sysfs_remove_group(&p_sar->i2c->dev.kobj, &aw_sar_update_attribute_group);
+
+ //Special requirements of SAR chip
+ if ((p_sar->p_sar_para->p_platform_config != NULL) &&
+ (p_sar->p_sar_para->p_platform_config->p_add_node_free_fn != NULL))
+ p_sar->p_sar_para->p_platform_config->p_add_node_free_fn(p_sar);
+}
+
+//The interrupt pin is set to internal pull-up start
+static void aw_sar_int_output(struct aw_sar *p_sar, int32_t level)
+{
+ if (level == 0) {
+ if (p_sar->pinctrl.pinctrl)
+ pinctrl_select_state(p_sar->pinctrl.pinctrl, p_sar->pinctrl.int_out_low);
+ else
+ dev_err(p_sar->dev, "Failed set int pin output low\n");
+ } else if (level == 1) {
+ if (p_sar->pinctrl.pinctrl)
+ pinctrl_select_state(p_sar->pinctrl.pinctrl, p_sar->pinctrl.int_out_high);
+ else
+ dev_err(p_sar->dev, "Failed set int pin output high\n");
+ }
+}
+
+static int32_t aw_sar_pinctrl_init(struct aw_sar *p_sar)
+{
+ struct aw_sar_pinctrl *pinctrl = &p_sar->pinctrl;
+ uint8_t pin_default_name[50] = { 0 };
+ uint8_t pin_output_low_name[50] = { 0 };
+ uint8_t pin_output_high_name[50] = { 0 };
+
+ pinctrl->pinctrl = devm_pinctrl_get(p_sar->dev);
+ if (IS_ERR_OR_NULL(pinctrl->pinctrl)) {
+ dev_err(p_sar->dev, "No pinctrl found\n");
+ pinctrl->pinctrl = NULL;
+ return -EINVAL;
+ }
+
+ snprintf(pin_default_name, sizeof(pin_default_name),
+ "aw_default_sar%u", p_sar->dts_info.sar_num);
+ pinctrl->default_sta = pinctrl_lookup_state(pinctrl->pinctrl, pin_default_name);
+ if (IS_ERR_OR_NULL(pinctrl->default_sta)) {
+ dev_err(p_sar->dev, "Failed get pinctrl state:default state");
+ goto exit_pinctrl_init;
+ }
+
+ snprintf(pin_output_high_name, sizeof(pin_output_high_name),
+ "aw_int_output_high_sar%u", p_sar->dts_info.sar_num);
+ pinctrl->int_out_high = pinctrl_lookup_state(pinctrl->pinctrl, pin_output_high_name);
+ if (IS_ERR_OR_NULL(pinctrl->int_out_high)) {
+ dev_err(p_sar->dev, "Failed get pinctrl state:output_high");
+ goto exit_pinctrl_init;
+ }
+
+ snprintf(pin_output_low_name, sizeof(pin_output_low_name),
+ "aw_int_output_low_sar%u", p_sar->dts_info.sar_num);
+ pinctrl->int_out_low = pinctrl_lookup_state(pinctrl->pinctrl, pin_output_low_name);
+ if (IS_ERR_OR_NULL(pinctrl->int_out_low)) {
+ dev_err(p_sar->dev, "Failed get pinctrl state:output_low");
+ goto exit_pinctrl_init;
+ }
+
+ return 0;
+
+exit_pinctrl_init:
+ devm_pinctrl_put(pinctrl->pinctrl);
+ pinctrl->pinctrl = NULL;
+
+ return -EINVAL;
+}
+
+static void aw_sar_pinctrl_deinit(struct aw_sar *p_sar)
+{
+ if (p_sar->pinctrl.pinctrl)
+ devm_pinctrl_put(p_sar->pinctrl.pinctrl);
+}
+//The interrupt pin is set to internal pull-up end
+
+//AW_SAR_REGULATOR_POWER_ON start
+static int32_t aw_sar_regulator_power_init(struct aw_sar *p_sar)
+{
+ uint8_t vcc_name[20] = { 0 };
+ int32_t rc;
+
+ snprintf(vcc_name, sizeof(vcc_name), "vcc%u", p_sar->dts_info.sar_num);
+ p_sar->vcc = regulator_get(p_sar->dev, vcc_name);
+ if (IS_ERR(p_sar->vcc)) {
+ rc = PTR_ERR(p_sar->vcc);
+ dev_err(p_sar->dev, "regulator get failed vcc rc = %d", rc);
+ return rc;
+ }
+
+ if (regulator_count_voltages(p_sar->vcc) > 0) {
+ rc = regulator_set_voltage(p_sar->vcc, AW_SAR_VCC_MIN_UV, AW_SAR_VCC_MAX_UV);
+ if (rc) {
+ dev_err(p_sar->dev, "regulator set vol failed rc = %d", rc);
+ goto reg_vcc_put;
+ }
+ }
+
+ return 0;
+
+reg_vcc_put:
+ regulator_put(p_sar->vcc);
+ return rc;
+}
+
+static void aw_sar_power_deinit(struct aw_sar *p_sar)
+{
+ if (p_sar->power_enable) {
+ //Turn off the power output. However,
+ //it may not be turned off immediately
+ //There are scenes where power sharing can exist
+ regulator_disable(p_sar->vcc);
+ regulator_put(p_sar->vcc);
+ }
+}
+
+static void aw_sar_power_enable(struct aw_sar *p_sar, bool on)
+{
+ int32_t rc;
+
+ if (on) {
+ rc = regulator_enable(p_sar->vcc);
+ if (rc) {
+ dev_err(p_sar->dev, "regulator_enable vol failed rc = %d", rc);
+ } else {
+ p_sar->power_enable = AW_TRUE;
+ msleep(20);
+ }
+ } else {
+ rc = regulator_disable(p_sar->vcc);
+ if (rc)
+ dev_err(p_sar->dev, "regulator_disable vol failed rc = %d", rc);
+ else
+ p_sar->power_enable = AW_FALSE;
+ }
+}
+
+static int32_t regulator_is_get_voltage(struct aw_sar *p_sar)
+{
+ uint32_t cnt = 10;
+ int32_t voltage_val;
+
+ while (cnt--) {
+ voltage_val = regulator_get_voltage(p_sar->vcc);
+ if (voltage_val >= AW_SAR_VCC_MIN_UV)
+ return 0;
+ mdelay(1);
+ }
+ //Ensure that the chip initialization is completed
+ msleep(20);
+
+ return -EINVAL;
+}
+//AW_SAR_REGULATOR_POWER_ON end
+
+static void aw_sar_init_lock(struct aw_sar *p_sar)
+{
+ //Initialize lock, To protect the thread safety of updating bin file
+ mutex_init(&aw_sar_lock);
+ //Required for mode setting
+ p_sar->last_mode = p_sar->p_sar_para->p_chip_mode->pre_init_mode;
+ p_sar->fw_fail_flag = AW_FALSE;
+ p_sar->ret_val = 0;
+}
+
+// AW_SAR_USB_PLUG_CAIL start
+static void aw_sar_ps_notify_callback_work(struct work_struct *work)
+{
+ struct aw_sar *p_sar = container_of(work, struct aw_sar, ps_notify_work);
+
+ aw_sar_aot(p_sar);
+}
+
+static int aw_sar_ps_get_state(struct aw_sar *p_sar, struct power_supply *psy, bool *present)
+{
+ union power_supply_propval pval = { 0 };
+ int retval;
+
+ retval = power_supply_get_property(psy, AW_USB_PROP_ONLINE, &pval);
+ if (retval) {
+ dev_err(p_sar->dev, "%s psy get property failed", psy->desc->name);
+ return retval;
+ }
+ *present = (pval.intval) ? true : false;
+
+ return 0;
+}
+
+static int aw_sar_ps_notify_callback(struct notifier_block *self,
+ unsigned long event, void *p)
+{
+ struct aw_sar *p_sar = container_of(self, struct aw_sar, ps_notif);
+ struct power_supply *psy = p;
+ bool present;
+ int retval;
+
+ if (event == PSY_EVENT_PROP_CHANGED
+ && psy && psy->desc->get_property && psy->desc->name &&
+ !strncmp(psy->desc->name, USB_POWER_SUPPLY_NAME,
+ sizeof(USB_POWER_SUPPLY_NAME))) {
+ retval = aw_sar_ps_get_state(p_sar, psy, &present);
+ if (retval) {
+ dev_err(p_sar->dev, "psy get property failed");
+ return retval;
+ }
+ if (event == PSY_EVENT_PROP_CHANGED) {
+ if (p_sar->ps_is_present == present)
+ return 0;
+ }
+ p_sar->ps_is_present = present;
+ schedule_work(&p_sar->ps_notify_work);
+ }
+ return 0;
+}
+
+static int aw_sar_ps_notify_init(struct aw_sar *p_sar)
+{
+ struct power_supply *psy;
+ int ret;
+
+ INIT_WORK(&p_sar->ps_notify_work, aw_sar_ps_notify_callback_work);
+ p_sar->ps_notif.notifier_call = (notifier_fn_t)aw_sar_ps_notify_callback;
+ ret = power_supply_reg_notifier(&p_sar->ps_notif);
+ if (ret) {
+ dev_err(p_sar->dev, "Unable to register ps_notifier: %d", ret);
+ return ret;
+ }
+ psy = power_supply_get_by_name(USB_POWER_SUPPLY_NAME);
+ if (!psy) {
+ dev_err(p_sar->dev, "Unable to get power_supply: %s", USB_POWER_SUPPLY_NAME);
+ goto free_ps_notifier;
+ }
+ ret = aw_sar_ps_get_state(p_sar, psy, &p_sar->ps_is_present);
+ if (ret) {
+ dev_err(p_sar->dev, "psy get property failed rc=%d", ret);
+ goto free_ps_notifier;
+ }
+ return 0;
+
+free_ps_notifier:
+ power_supply_unreg_notifier(&p_sar->ps_notif);
+
+ return -EINVAL;
+}
+// AW_SAR_USB_PLUG_CAIL end
+
+static int32_t aw_sar_platform_rsc_init(struct aw_sar *p_sar)
+{
+ int32_t ret;
+
+ if (!p_sar->p_sar_para->p_platform_config)
+ return -EINVAL;
+
+ //step 1.parsing dts data
+ ret = aw_sar_parse_dts(p_sar);
+ if (ret != 0) {
+ dev_err(p_sar->dev, "parse dts error!");
+ return ret;
+ }
+
+ //Initialization lock and some variables
+ aw_sar_init_lock(p_sar);
+
+ //Configure whether to use USB plug-in calibration in DTS according to customer requirements
+ if (p_sar->dts_info.use_plug_cail_flag == true) {
+ ret = aw_sar_ps_notify_init(p_sar);
+ if (ret < 0) {
+ dev_err(p_sar->dev, "error creating power supply notify");
+ goto err_ps_notify_init;
+ }
+ }
+
+ //The interrupt pin is set to internal pull-up and configured by DTS
+ if (p_sar->dts_info.use_inter_pull_up == true) {
+ ret = aw_sar_pinctrl_init(p_sar);
+ if (ret < 0) {
+ /* if define pinctrl must define the following state
+ * to let int-pin work normally: default, int_output_high,
+ * int_output_low, int_input
+ */
+ dev_err(p_sar->dev, "Failed get wanted pinctrl state");
+ goto err_pinctrl_init;
+ }
+ aw_sar_int_output(p_sar, 1);
+ }
+
+ //step 2.Create debug file node
+ ret = aw_sar_create_node(p_sar);
+ if (ret != 0) {
+ dev_err(p_sar->dev, "create node error!");
+ goto err_sysfs_nodes;
+ }
+
+ //step 3.Initialization interrupt
+ ret = aw_sar_irq_init(p_sar);
+ if (ret != 0) {
+ dev_err(p_sar->dev, "interrupt initialization error!");
+ goto err_irq_init;
+ }
+
+ //step 4.Initialization input Subsystem
+ ret = aw_sar_input_init(p_sar);
+ if (ret != 0) {
+ dev_err(p_sar->dev, "input_init error!");
+ goto err_input_init;
+ }
+
+ return 0;
+
+err_input_init:
+ aw_sar_input_free(p_sar);
+ aw_sar_irq_free(p_sar);
+err_irq_init:
+ aw_sar_node_free(p_sar);
+err_sysfs_nodes:
+ if (p_sar->dts_info.use_inter_pull_up == true)
+ aw_sar_pinctrl_deinit(p_sar);
+err_pinctrl_init:
+ if (p_sar->dts_info.use_plug_cail_flag == true)
+ power_supply_unreg_notifier(&p_sar->ps_notif);
+err_ps_notify_init:
+ mutex_destroy(&aw_sar_lock);
+
+ return ret;
+}
+
+/**
+ * @brief sar sensor initialization logic.
+ *
+ * @param p_sar data stored in type 'struct aw_sar *'.
+ * @return 0 if init succeeded. others if unpack error.
+ */
+static int32_t aw_sar_chip_init(struct aw_sar *p_sar)
+{
+ int32_t ret;
+
+ //step 1.check chipid,Verify whether the chip communication is successful
+ ret = aw_sar_check_chipid(p_sar);
+ if (ret != 0) {
+ dev_err(p_sar->dev, "check_chipid error!");
+ goto communication_fail;
+ }
+
+ //step 2.Check chip initialization completed,
+ ret = aw_sar_soft_reset(p_sar);
+ if (ret != 0) {
+ dev_err(p_sar->dev, "soft_reset error!");
+ goto communication_fail;
+ }
+
+ ret = aw_sar_check_init_over_irq(p_sar);
+ if (ret != 0) {
+ dev_err(p_sar->dev, "check_init_over_irqt error!");
+ goto communication_fail;
+ }
+
+ //step 3.chip other operation
+ ret = aw_sar_chip_other_operation(p_sar);
+ if (ret != 0) {
+ dev_err(p_sar->dev, "chip_other_operation error!");
+ goto free_other_opera;
+ }
+
+ //step 4.Parse the bin file, upgrade the firmware, and load the register prize
+ aw_sar_update(p_sar);
+
+ return 0;
+
+free_other_opera:
+ aw_sar_chip_other_operation_free(p_sar);
+communication_fail:
+
+ return ret;
+}
+
+static int32_t aw_sar_init(struct aw_sar *p_sar)
+{
+ int32_t ret;
+
+ //step 1: Platform resource initialization
+ ret = aw_sar_platform_rsc_init(p_sar);
+ if (ret != 0) {
+ dev_err(p_sar->dev, "platform_rsc_init error!");
+ return ret;
+ }
+
+ //step 2: Chip initialization
+ ret = aw_sar_chip_init(p_sar);
+ if (ret != 0) {
+ aw_sar_input_free(p_sar);
+ aw_sar_irq_free(p_sar);
+ aw_sar_node_free(p_sar);
+ if (p_sar->dts_info.use_inter_pull_up == true)
+ aw_sar_pinctrl_deinit(p_sar);
+ if (p_sar->dts_info.use_plug_cail_flag == true)
+ power_supply_unreg_notifier(&p_sar->ps_notif);
+ mutex_destroy(&aw_sar_lock);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int32_t aw_sar_regulator_power(struct aw_sar *p_sar)
+{
+ struct aw_sar_dts_info *p_dts_info = &p_sar->dts_info;
+ int32_t ret = 0;
+
+ p_dts_info->use_regulator_flag =
+ of_property_read_bool(p_sar->i2c->dev.of_node, "aw_sar,regulator-power-supply");
+
+ //Configure the use of regulator power supply in DTS
+ if (p_sar->dts_info.use_regulator_flag == true) {
+ ret = aw_sar_regulator_power_init(p_sar);
+ if (ret != 0) {
+ dev_err(p_sar->dev, "power init failed");
+ return ret;
+ }
+ aw_sar_power_enable(p_sar, AW_TRUE);
+ ret = regulator_is_get_voltage(p_sar);
+ if (ret != 0) {
+ dev_err(p_sar->dev, "get_voltage failed");
+ aw_sar_power_deinit(p_sar);
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * @brief Distinguish different chips by chip name and obtain relevant chip information
+ *
+ * @param p_sar Structure to be filled
+ * @return 0 if init succeeded.
+ */
+static int32_t aw_sar_get_chip_info(struct aw_sar *p_sar)
+{
+ int32_t ret;
+ uint8_t i;
+
+ for (i = 0; i < AW_SAR_DRIVER_MAX; i++) {
+ if (g_aw_sar_driver_list[i].p_who_am_i != NULL) {
+ ret = g_aw_sar_driver_list[i].p_who_am_i(p_sar);
+ if (ret == 0) {
+ p_sar->curr_use_driver_type = g_aw_sar_driver_list[i].driver_type;
+ if (!g_aw_sar_driver_list[i].p_chip_init) {
+ dev_err(p_sar->dev,
+ "drvier:%d p_chip_init is null error!", i);
+ continue;
+ }
+ g_aw_sar_driver_list[i].p_chip_init(p_sar);
+ dev_info(p_sar->dev, "current use drvier is :%d",
+ g_aw_sar_driver_list[i].driver_type);
+ return 0;
+ }
+ }
+ }
+
+ return -EINVAL;
+}
+
+static void aw_sar_monitor_work(struct work_struct *aw_work)
+{
+ struct aw_sar *p_sar = container_of(aw_work, struct aw_sar, monitor_work.work);
+ uint32_t data;
+ int32_t ret;
+
+ ret = aw_sar_i2c_read(p_sar->i2c, 0x0000, &data);
+ if (ret != 0) {
+ dev_err(p_sar->dev, "read 0x0000 err: %d", ret);
+ return;
+ }
+ if (data == 0 && p_sar->driver_code_initover_flag) {
+ dev_err(p_sar->dev, "aw_chip may reset");
+ aw_sar_disable_irq(p_sar);
+ ret = aw_sar_chip_init(p_sar);
+ if (ret != 0)
+ return;
+ }
+ queue_delayed_work(p_sar->monitor_wq, &p_sar->monitor_work,
+ msecs_to_jiffies(AW_SAR_MONITOR_ESD_DELAY_MS));
+}
+
+static int32_t aw_sar_monitor_esd_init(struct aw_sar *p_sar)
+{
+ p_sar->monitor_wq = create_singlethread_workqueue("aw_sar_workqueue");
+ if (!p_sar->monitor_wq) {
+ dev_err(&p_sar->i2c->dev, "aw_sar_workqueue error");
+ return -EINVAL;
+ }
+ INIT_DELAYED_WORK(&p_sar->monitor_work, aw_sar_monitor_work);
+ queue_delayed_work(p_sar->monitor_wq, &p_sar->monitor_work,
+ msecs_to_jiffies(AW_SAR_MONITOR_ESD_DELAY_MS));
+
+ return 0;
+}
+
+static void aw_sar_sensor_free(struct aw_sar *p_sar)
+{
+ if (g_aw_sar_driver_list[p_sar->curr_use_driver_type].p_chip_deinit != NULL)
+ g_aw_sar_driver_list[p_sar->curr_use_driver_type].p_chip_deinit(p_sar);
+}
+
+
+/**
+ * @brief Drive logic entry
+ *
+ */
+static int32_t aw_sar_i2c_probe(struct i2c_client *i2c)
+{
+ struct aw_sar *p_sar;
+ int32_t ret;
+
+ if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) {
+ pr_err("check_functionality failed!\n");
+ return -EIO;
+ }
+
+ p_sar = devm_kzalloc(&i2c->dev, sizeof(struct aw_sar), GFP_KERNEL);
+ if (!p_sar) {
+ ret = -ENOMEM;
+ goto err_malloc;
+ }
+
+ p_sar->dev = &i2c->dev;
+ p_sar->i2c = i2c;
+ i2c_set_clientdata(i2c, p_sar);
+
+ //1.Judge whether to use regular power supply. If yes, supply power
+ ret = aw_sar_regulator_power(p_sar);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "regulator_power error!");
+ goto err_malloc;
+ }
+
+ //2.Get chip initialization resources
+ ret = aw_sar_get_chip_info(p_sar);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "chip_init error!");
+ goto err_chip_init;
+ }
+
+ //3.Chip initialization process
+ ret = aw_sar_init(p_sar);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "sar_init error!");
+ goto err_sar_init;
+ }
+
+ if (p_sar->dts_info.monitor_esd_flag) {
+ ret = aw_sar_monitor_esd_init(p_sar);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "monitor_esd_init error!");
+ goto err_esd_init;
+ }
+ }
+
+ dev_dbg(&i2c->dev, "probe success!");
+
+ return 0;
+
+err_esd_init:
+ aw_sar_input_free(p_sar);
+ aw_sar_irq_free(p_sar);
+ aw_sar_node_free(p_sar);
+ if (p_sar->dts_info.use_inter_pull_up == true)
+ aw_sar_pinctrl_deinit(p_sar);
+ if (p_sar->dts_info.use_plug_cail_flag == true)
+ power_supply_unreg_notifier(&p_sar->ps_notif);
+ mutex_destroy(&aw_sar_lock);
+err_sar_init:
+ aw_sar_sensor_free(p_sar);
+err_chip_init:
+if (p_sar->dts_info.use_regulator_flag == true)
+ aw_sar_power_deinit(p_sar);
+err_malloc:
+ return ret;
+}
+
+static void aw_sar_i2c_remove(struct i2c_client *i2c)
+{
+ struct aw_sar *p_sar = i2c_get_clientdata(i2c);
+
+ aw_sar_chip_other_operation_free(p_sar);
+
+ aw_sar_node_free(p_sar);
+
+ aw_sar_irq_free(p_sar);
+
+ aw_sar_input_free(p_sar);
+
+ if (p_sar->dts_info.use_inter_pull_up == true)
+ aw_sar_pinctrl_deinit(p_sar);
+
+ if (p_sar->dts_info.use_regulator_flag == true)
+ aw_sar_power_deinit(p_sar);
+
+ if (p_sar->dts_info.use_plug_cail_flag == true)
+ power_supply_unreg_notifier(&p_sar->ps_notif);
+
+ aw_sar_sensor_free(p_sar);
+}
+
+static int aw_sar_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct aw_sar *p_sar = i2c_get_clientdata(client);
+
+ if (p_sar->dts_info.use_pm == true) {
+ if ((!p_sar->p_sar_para->p_platform_config) ||
+ (!p_sar->p_sar_para->p_platform_config->p_pm_chip_mode))
+ return 0;
+ if (p_sar->p_sar_para->p_platform_config->p_pm_chip_mode->p_suspend_fn) {
+ p_sar->p_sar_para->p_platform_config->p_pm_chip_mode->p_suspend_fn(p_sar);
+ return 0;
+ }
+ aw_sar_mode_set(p_sar,
+ p_sar->p_sar_para->p_platform_config->p_pm_chip_mode->suspend_set_mode);
+ }
+
+ if (p_sar->dts_info.monitor_esd_flag)
+ cancel_delayed_work_sync(&p_sar->monitor_work);
+
+ return 0;
+}
+
+static int aw_sar_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct aw_sar *p_sar = i2c_get_clientdata(client);
+
+ if (p_sar->dts_info.use_pm == true) {
+ if ((!p_sar->p_sar_para->p_platform_config) ||
+ (!p_sar->p_sar_para->p_platform_config->p_pm_chip_mode))
+ return 0;
+ if (p_sar->p_sar_para->p_platform_config->p_pm_chip_mode->p_resume_fn) {
+ p_sar->p_sar_para->p_platform_config->p_pm_chip_mode->p_resume_fn(p_sar);
+ return 0;
+ }
+ aw_sar_mode_set(p_sar,
+ p_sar->p_sar_para->p_platform_config->p_pm_chip_mode->resume_set_mode);
+ }
+
+ if (p_sar->dts_info.monitor_esd_flag)
+ queue_delayed_work(p_sar->monitor_wq, &p_sar->monitor_work,
+ msecs_to_jiffies(AW_SAR_MONITOR_ESD_DELAY_MS));
+
+ return 0;
+}
+
+static void aw_sar_i2c_shutdown(struct i2c_client *i2c)
+{
+ struct aw_sar *p_sar = i2c_get_clientdata(i2c);
+
+ if ((!p_sar->p_sar_para->p_platform_config) ||
+ (!p_sar->p_sar_para->p_platform_config->p_pm_chip_mode))
+ return;
+
+ if (p_sar->p_sar_para->p_platform_config->p_pm_chip_mode->p_shutdown_fn) {
+ p_sar->p_sar_para->p_platform_config->p_pm_chip_mode->p_shutdown_fn(p_sar);
+ return;
+ }
+
+ aw_sar_mode_set(p_sar,
+ p_sar->p_sar_para->p_platform_config->p_pm_chip_mode->shutdown_set_mode);
+}
+
+static const struct dev_pm_ops aw_sar_pm_ops = {
+ .suspend = aw_sar_suspend,
+ .resume = aw_sar_resume,
+};
+
+static const struct of_device_id aw_sar_dt_match[] = {
+ { .compatible = "awinic,aw96103" },
+ { .compatible = "awinic,aw96105" },
+ { .compatible = "awinic,aw96303" },
+ { .compatible = "awinic,aw96305" },
+ { .compatible = "awinic,aw96308" },
+};
+
+static const struct i2c_device_id aw_sar_i2c_id[] = {
+ { AW_SAR_I2C_NAME, 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, aw_sar_i2c_id);
+
+static struct i2c_driver aw_sar_i2c_driver = {
+ .driver = {
+ .name = AW_SAR_I2C_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(aw_sar_dt_match),
+ .pm = &aw_sar_pm_ops,
+ },
+ .probe = aw_sar_i2c_probe,
+ .remove = aw_sar_i2c_remove,
+ .shutdown = aw_sar_i2c_shutdown,
+ .id_table = aw_sar_i2c_id,
+};
+
+static int32_t __init aw_sar_i2c_init(void)
+{
+ int32_t ret;
+
+ ret = i2c_add_driver(&aw_sar_i2c_driver);
+ if (ret) {
+ pr_err("fail to add aw_sar device into i2c\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+module_init(aw_sar_i2c_init);
+static void __exit aw_sar_i2c_exit(void)
+{
+ i2c_del_driver(&aw_sar_i2c_driver);
+}
+module_exit(aw_sar_i2c_exit);
+MODULE_DESCRIPTION("AWINIC SAR Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/misc/aw_sar/aw_sar.h b/drivers/input/misc/aw_sar/aw_sar.h
new file mode 100644
index 000000000000..7a139f56e9c3
--- /dev/null
+++ b/drivers/input/misc/aw_sar/aw_sar.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef AW_SAR_H_
+#define AW_SAR_H_
+
+void aw_sar_disable_irq(struct aw_sar *p_sar);
+void aw_sar_enable_irq(struct aw_sar *p_sar);
+
+int32_t aw_sar_soft_reset(struct aw_sar *p_sar);
+int32_t aw_sar_check_init_over_irq(struct aw_sar *p_sar);
+int32_t aw_sar_update_fw(struct aw_sar *p_sar);
+int32_t aw_sar_load_def_reg_bin(struct aw_sar *p_sar);
+void aw_sar_mode_set(struct aw_sar *p_sar, uint8_t curr_mode);
+int32_t aw_sar_update_reg_set_func(struct aw_sar *p_sar);
+
+#endif
--
2.45.1
^ permalink raw reply related
* Re: [PATCH 2/2] HID: intel-ish-hid: fix endian-conversion
From: Arnd Bergmann @ 2024-05-29 13:18 UTC (permalink / raw)
To: Lixu Zhang, Arnd Bergmann, srinivas.pandruvada@linux.intel.com,
Jiri Kosina, Benjamin Tissoires
Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <DM4PR11MB59959F1AE3E9BDA36642000B93F22@DM4PR11MB5995.namprd11.prod.outlook.com>
On Wed, May 29, 2024, at 09:05, Zhang, Lixu wrote:
>>
>> for (i = 0; i < fragment->fragment_cnt && offset < ish_fw->size; i++) {
> You added a parameter fragment_count, but you didn't use it. Did you
> intend to use it here?
>
My mistake, that was again broken in my incorrect
rebase.
Arnd
^ permalink raw reply
* Re: [PATCH V1 1/5] dt-bindings: input: Add YAML to Awinic sar sensor.
From: Rob Herring (Arm) @ 2024-05-29 14:25 UTC (permalink / raw)
To: wangshuaijie
Cc: kangjiajun, dmitry.torokhov, jeff, linux-input, liweilei,
conor+dt, krzk+dt, devicetree, linux-kernel
In-Reply-To: <20240529130608.783624-2-wangshuaijie@awinic.com>
On Wed, 29 May 2024 13:06:04 +0000, wangshuaijie@awinic.com wrote:
> From: shuaijie wang <wangshuaijie@awinic.com>
>
> Add the awinic,aw_sar.yaml file to adapt to the awinic sar sensor driver.
>
> Signed-off-by: shuaijie wang <wangshuaijie@awinic.com>
> ---
> .../bindings/input/awinic,aw_sar.yaml | 110 ++++++++++++++++++
> 1 file changed, 110 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/input/awinic,aw_sar.yaml
>
My bot found errors running 'make dt_binding_check' on your patch:
yamllint warnings/errors:
dtschema/dtc warnings/errors:
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/input/awinic,aw_sar.example.dtb: awinic_sar@12: 'aw_sar,update_fw' does not match any of the regexes: '^#.*', '^(at25|bm|devbus|dmacap|dsa|exynos|fsi[ab]|gpio-fan|gpio-key|gpio|gpmc|hdmi|i2c-gpio),.*', '^(keypad|m25p|max8952|max8997|max8998|mpmc),.*', '^(pinctrl-single|#pinctrl-single|PowerPC),.*', '^(pl022|pxa-mmc|rcar_sound|rotary-encoder|s5m8767|sdhci),.*', '^(simple-audio-card|st-plgpio|st-spics|ts),.*', '^100ask,.*', '^70mai,.*', '^8dev,.*', '^GEFanuc,.*', '^IBM,.*', '^ORCL,.*', '^SUNW,.*', '^[a-zA-Z0-9#_][a-zA-Z0-9+\\-._@]{0,63}$', '^[a-zA-Z0-9+\\-._]*@[0-9a-zA-Z,]*$', '^abb,.*', '^abilis,.*', '^abracon,.*', '^abt,.*', '^acbel,.*', '^acelink,.*', '^acer,.*', '^acme,.*', '^actions,.*', '^active-semi,.*', '^ad,.*', '^adafruit,.*', '^adapteva,.*', '^adaptrum,.*', '^adh,.*', '^adi,.*', '^adieng,.*', '^admatec,.*', '^advantech,.*', '^aeroflexgaisler,.*', '^aesop,.*', '^airoha,.*', '^al,.*', '^alcatel,.*', '^aldec,.*', '^alfa-network,.*', '^allegro,.*', '^alliedvision,.*', '^allo,.*', '^allwinner,.*', '^alphascale,.*', '^alps,.*', '^alt,.*', '^altr,.*', '^amarula,.*', '^amazon,.*', '^amcc,.*', '^amd,.*', '^amediatech,.*', '^amlogic,.*', '^ampere,.*', '^amphenol,.*', '^ampire,.*', '^ams,.*', '^amstaos,.*', '^analogix,.*', '^anbernic,.*', '^andestech,.*', '^anvo,.*', '^aosong,.*', '^apm,.*', '^apple,.*', '^aptina,.*', '^arasan,.*', '^archermind,.*', '^arcom,.*', '^arctic,.*', '^arcx,.*', '^aries,.*', '^arm,.*', '^armadeus,.*', '^arrow,.*', '^artesyn,.*', '^asahi-kasei,.*', '^asc,.*', '^asix,.*', '^aspeed,.*', '^asrock,.*', '^asteralabs,.*', '^asus,.*', '^atheros,.*', '^atlas,.*', '^atmel,.*', '^auo,.*', '^auvidea,.*', '^avago,.*', '^avia,.*', '^avic,.*', '^avnet,.*', '^awinic,.*', '^axentia,.*', '^axis,.*', '^azoteq,.*', '^azw,.*', '^baikal,.*', '^bananapi,.*', '^beacon,.*', '^beagle,.*', '^belling,.*', '^bhf,.*', '^bigtreetech,.*', '^bitmain,.*', '^blutek,.*', '^boe,.*', '^bosch,.*', '^boundary,.*', '^brcm,.*', '^broadmobi,.*', '^bsh,.*', '^bticino,.*', '^buffalo,.*', '^bur,.*', '^bytedance,.*', '^calamp,.*', '^calao,.*', '^calaosystems,.*', '^calxeda,.*', '^canaan,.*', '^caninos,.*', '^capella,.*', '^cascoda,.*', '^catalyst,.*', '^cavium,.*', '^cdns,.*', '^cdtech,.*', '^cellwise,.*', '^ceva,.*', '^chargebyte,.*', '^checkpoint,.*', '^chefree,.*', '^chipidea,.*', '^chipone,.*', '^chipspark,.*', '^chongzhou,.*', '^chrontel,.*', '^chrp,.*', '^chunghwa,.*', '^chuwi,.*', '^ciaa,.*', '^cirrus,.*', '^cisco,.*', '^clockwork,.*', '^cloos,.*', '^cloudengines,.*', '^cnm,.*', '^cnxt,.*', '^colorfly,.*', '^compulab,.*', '^congatec,.*', '^coolpi,.*', '^coreriver,.*', '^corpro,.*', '^cortina,.*', '^cosmic,.*', '^crane,.*', '^creative,.*', '^crystalfontz,.*', '^csky,.*', '^csq,.*', '^ctera,.*', '^ctu,.*', '^cubietech,.*', '^cui,.*', '^cypress,.*', '^cyx,.*', '^cznic,.*', '^dallas,.*', '^dataimage,.*', '^davicom,.*', '^dell,.*', '^delta,.*', '^densitron,.*', '^denx,.*', '^devantech,.*', '^dfi,.*', '^dh,.*', '^difrnce,.*', '^digi,.*', '^digilent,.*', '^dimonoff,.*', '^diodes,.*', '^dioo,.*', '^dlc,.*', '^dlg,.*', '^dlink,.*', '^dmo,.*', '^domintech,.*', '^dongwoon,.*', '^dptechnics,.*', '^dragino,.*', '^ds,.*', '^dserve,.*', '^dynaimage,.*', '^ea,.*', '^ebang,.*', '^ebbg,.*', '^ebs-systart,.*', '^ebv,.*', '^eckelmann,.*', '^edgeble,.*', '^edimax,.*', '^edt,.*', '^ees,.*', '^eeti,.*', '^einfochips,.*', '^eink,.*', '^elan,.*', '^element14,.*', '^elgin,.*', '^elida,.*', '^elimo,.*', '^elpida,.*', '^embedfire,.*', '^embest,.*', '^emlid,.*', '^emmicro,.*', '^empire-electronix,.*', '^emtrion,.*', '^enclustra,.*', '^endless,.*', '^ene,.*', '^energymicro,.*', '^engicam,.*', '^engleder,.*', '^epcos,.*', '^epfl,.*', '^epson,.*', '^esp,.*', '^est,.*', '^ettus,.*', '^eukrea,.*', '^everest,.*', '^everspin,.*', '^evervision,.*', '^exar,.*', '^excito,.*', '^exegin,.*', '^ezchip,.*', '^facebook,.*', '^fairchild,.*', '^fairphone,.*', '^faraday,.*', '^fascontek,.*', '^fastrax,.*', '^fcs,.*', '^feixin,.*', '^feiyang,.*', '^fii,.*', '^firefly,.*', '^focaltech,.*', '^forlinx,.*', '^freebox,.*', '^freecom,.*', '^frida,.*', '^friendlyarm,.*', '^fsl,.*', '^fujitsu,.*', '^fxtec,.*', '^galaxycore,.*', '^gardena,.*', '^gateway,.*', '^gateworks,.*', '^gcw,.*', '^ge,.*', '^geekbuying,.*', '^gef,.*', '^gemei,.*', '^gemtek,.*', '^genesys,.*', '^geniatech,.*', '^giantec,.*', '^giantplus,.*', '^glinet,.*', '^globalscale,.*', '^globaltop,.*', '^gmt,.*', '^goldelico,.*', '^goodix,.*', '^google,.*', '^goramo,.*', '^gplus,.*', '^grinn,.*', '^grmn,.*', '^gumstix,.*', '^gw,.*', '^hannstar,.*', '^haochuangyi,.*', '^haoyu,.*', '^hardkernel,.*', '^hechuang,.*', '^hideep,.*', '^himax,.*', '^hirschmann,.*', '^hisi,.*', '^hisilicon,.*', '^hit,.*', '^hitex,.*', '^holt,.*', '^holtek,.*', '^honestar,.*', '^honeywell,.*', '^hoperf,.*', '^hoperun,.*', '^hp,.*', '^hpe,.*', '^hsg,.*', '^htc,.*', '^huawei,.*', '^hugsun,.*', '^hwacom,.*', '^hxt,.*', '^hycon,.*', '^hydis,.*', '^hynitron,.*', '^hynix,.*', '^hyundai,.*', '^i2se,.*', '^ibm,.*', '^icplus,.*', '^idt,.*', '^iei,.*', '^ifi,.*', '^ilitek,.*', '^imagis,.*', '^img,.*', '^imi,.*', '^inanbo,.*', '^incircuit,.*', '^indiedroid,.*', '^inet-tek,.*', '^infineon,.*', '^inforce,.*', '^ingenic,.*', '^ingrasys,.*', '^injoinic,.*', '^innocomm,.*', '^innolux,.*', '^inside-secure,.*', '^insignal,.*', '^inspur,.*', '^intel,.*', '^intercontrol,.*', '^invensense,.*', '^inventec,.*', '^inversepath,.*', '^iom,.*', '^irondevice,.*', '^isee,.*', '^isil,.*', '^issi,.*', '^ite,.*', '^itead,.*', '^itian,.*', '^ivo,.*', '^iwave,.*', '^jadard,.*', '^jasonic,.*', '^jdi,.*', '^jedec,.*', '^jesurun,.*', '^jethome,.*', '^jianda,.*', '^jide,.*', '^joz,.*', '^kam,.*', '^karo,.*', '^keithkoep,.*', '^keymile,.*', '^khadas,.*', '^kiebackpeter,.*', '^kinetic,.*', '^kingdisplay,.*', '^kingnovel,.*', '^kionix,.*', '^kobo,.*', '^kobol,.*', '^koe,.*', '^kontron,.*', '^kosagi,.*', '^kvg,.*', '^kyo,.*', '^lacie,.*', '^laird,.*', '^lamobo,.*', '^lantiq,.*', '^lattice,.*', '^lctech,.*', '^leadtek,.*', '^leez,.*', '^lego,.*', '^lemaker,.*', '^lenovo,.*', '^lg,.*', '^lgphilips,.*', '^libretech,.*', '^licheepi,.*', '^linaro,.*', '^lineartechnology,.*', '^linksprite,.*', '^linksys,.*', '^linutronix,.*', '^linux,.*', '^linx,.*', '^liteon,.*', '^litex,.*', '^lltc,.*', '^logicpd,.*', '^logictechno,.*', '^longcheer,.*', '^lontium,.*', '^loongmasses,.*', '^loongson,.*', '^lsi,.*', '^lunzn,.*', '^luxul,.*', '^lwn,.*', '^lxa,.*', '^m5stack,.*', '^macnica,.*', '^mantix,.*', '^mapleboard,.*', '^marantec,.*', '^marvell,.*', '^maxbotix,.*', '^maxim,.*', '^maxlinear,.*', '^mbvl,.*', '^mcube,.*', '^meas,.*', '^mecer,.*', '^mediatek,.*', '^megachips,.*', '^mele,.*', '^melexis,.*', '^melfas,.*', '^mellanox,.*', '^memsensing,.*', '^memsic,.*', '^menlo,.*', '^mentor,.*', '^meraki,.*', '^merrii,.*', '^methode,.*', '^micrel,.*', '^microchip,.*', '^microcrystal,.*', '^micron,.*', '^microsoft,.*', '^microsys,.*', '^mikroe,.*', '^mikrotik,.*', '^milkv,.*', '^miniand,.*', '^minix,.*', '^mips,.*', '^miramems,.*', '^mitsubishi,.*', '^mitsumi,.*', '^mixel,.*', '^miyoo,.*', '^mntre,.*', '^mobileye,.*', '^modtronix,.*', '^moortec,.*', '^mosaixtech,.*', '^motorcomm,.*', '^motorola,.*', '^moxa,.*', '^mpl,.*', '^mps,.*', '^mqmaker,.*', '^mrvl,.*', '^mscc,.*', '^msi,.*', '^mstar,.*', '^mti,.*', '^multi-inno,.*', '^mundoreader,.*', '^murata,.*', '^mxic,.*', '^mxicy,.*', '^myir,.*', '^national,.*', '^nec,.*', '^neonode,.*', '^netgear,.*', '^netlogic,.*', '^netron-dy,.*', '^netronix,.*', '^netxeon,.*', '^neweast,.*', '^newhaven,.*', '^newvision,.*', '^nexbox,.*', '^nextthing,.*', '^ni,.*', '^nintendo,.*', '^nlt,.*', '^nokia,.*', '^nordic,.*', '^novatek,.*', '^novtech,.*', '^numonyx,.*', '^nutsboard,.*', '^nuvoton,.*', '^nvd,.*', '^nvidia,.*', '^nxp,.*', '^oceanic,.*', '^ocs,.*', '^oct,.*', '^okaya,.*', '^oki,.*', '^olimex,.*', '^olpc,.*', '^oneplus,.*', '^onie,.*', '^onion,.*', '^onnn,.*', '^ontat,.*', '^opalkelly,.*', '^openailab,.*', '^opencores,.*', '^openembed,.*', '^openpandora,.*', '^openrisc,.*', '^option,.*', '^oranth,.*', '^orisetech,.*', '^ortustech,.*', '^osddisplays,.*', '^osmc,.*', '^ouya,.*', '^overkiz,.*', '^ovti,.*', '^oxsemi,.*', '^ozzmaker,.*', '^panasonic,.*', '^parade,.*', '^parallax,.*', '^pda,.*', '^pericom,.*', '^pervasive,.*', '^phicomm,.*', '^phytec,.*', '^picochip,.*', '^pine64,.*', '^pineriver,.*', '^pixcir,.*', '^plantower,.*', '^plathome,.*', '^plda,.*', '^plx,.*', '^ply,.*', '^pni,.*', '^pocketbook,.*', '^polaroid,.*', '^polyhex,.*', '^portwell,.*', '^poslab,.*', '^pov,.*', '^powertip,.*', '^powervr,.*', '^powkiddy,.*', '^primux,.*', '^probox2,.*', '^prt,.*', '^pulsedlight,.*', '^purism,.*', '^qca,.*', '^qcom,.*', '^qemu,.*', '^qi,.*', '^qiaodian,.*', '^qihua,.*', '^qishenglong,.*', '^qnap,.*', '^quanta,.*', '^radxa,.*', '^raidsonic,.*', '^ralink,.*', '^ramtron,.*', '^raspberrypi,.*', '^raydium,.*', '^rda,.*', '^realtek,.*', '^remarkable,.*', '^renesas,.*', '^rervision,.*', '^revotics,.*', '^rex,.*', '^richtek,.*', '^ricoh,.*', '^rikomagic,.*', '^riot,.*', '^riscv,.*', '^rockchip,.*', '^rocktech,.*', '^rohm,.*', '^ronbo,.*', '^roofull,.*', '^roseapplepi,.*', '^rve,.*', '^saef,.*', '^samsung,.*', '^samtec,.*', '^sancloud,.*', '^sandisk,.*', '^satoz,.*', '^sbs,.*', '^schindler,.*', '^seagate,.*', '^seeed,.*', '^seirobotics,.*', '^semtech,.*', '^senseair,.*', '^sensirion,.*', '^sensortek,.*', '^sercomm,.*', '^sff,.*', '^sgd,.*', '^sgmicro,.*', '^sgx,.*', '^sharp,.*', '^shift,.*', '^shimafuji,.*', '^shineworld,.*', '^shiratech,.*', '^si-en,.*', '^si-linux,.*', '^siemens,.*', '^sifive,.*', '^sigma,.*', '^sii,.*', '^sil,.*', '^silabs,.*', '^silan,.*', '^silead,.*', '^silergy,.*', '^silex-insight,.*', '^siliconfile,.*', '^siliconmitus,.*', '^silvaco,.*', '^simtek,.*', '^sinlinx,.*', '^sinovoip,.*', '^sinowealth,.*', '^sipeed,.*', '^sirf,.*', '^sis,.*', '^sitronix,.*', '^skov,.*', '^skyworks,.*', '^smartlabs,.*', '^smartrg,.*', '^smi,.*', '^smsc,.*', '^snps,.*', '^sochip,.*', '^socionext,.*', '^solidrun,.*', '^solomon,.*', '^sony,.*', '^sophgo,.*', '^sourceparts,.*', '^spansion,.*', '^sparkfun,.*', '^spinalhdl,.*', '^sprd,.*', '^square,.*', '^ssi,.*', '^sst,.*', '^sstar,.*', '^st,.*', '^st-ericsson,.*', '^starfive,.*', '^starry,.*', '^startek,.*', '^starterkit,.*', '^ste,.*', '^stericsson,.*', '^storlink,.*', '^storm,.*', '^storopack,.*', '^summit,.*', '^sunchip,.*', '^sundance,.*', '^sunplus,.*', '^supermicro,.*', '^swir,.*', '^syna,.*', '^synology,.*', '^synopsys,.*', '^tbs,.*', '^tbs-biometrics,.*', '^tcg,.*', '^tcl,.*', '^tcs,.*', '^tdo,.*', '^team-source-display,.*', '^technexion,.*', '^technologic,.*', '^techstar,.*', '^techwell,.*', '^teejet,.*', '^teltonika,.*', '^tempo,.*', '^terasic,.*', '^tesla,.*', '^tfc,.*', '^thead,.*', '^thine,.*', '^thingyjp,.*', '^thundercomm,.*', '^thwc,.*', '^ti,.*', '^tianma,.*', '^tlm,.*', '^tmt,.*', '^topeet,.*', '^topic,.*', '^toppoly,.*', '^topwise,.*', '^toradex,.*', '^toshiba,.*', '^toumaz,.*', '^tpk,.*', '^tplink,.*', '^tpo,.*', '^tq,.*', '^transpeed,.*', '^traverse,.*', '^tronfy,.*', '^tronsmart,.*', '^truly,.*', '^tsd,.*', '^turing,.*', '^tyan,.*', '^u-blox,.*', '^u-boot,.*', '^ubnt,.*', '^ucrobotics,.*', '^udoo,.*', '^ufispace,.*', '^ugoos,.*', '^uni-t,.*', '^uniwest,.*', '^upisemi,.*', '^urt,.*', '^usi,.*', '^usr,.*', '^utoo,.*', '^v3,.*', '^vaisala,.*', '^vamrs,.*', '^variscite,.*', '^vdl,.*', '^vertexcom,.*', '^via,.*', '^vialab,.*', '^vicor,.*', '^videostrong,.*', '^virtio,.*', '^virtual,.*', '^vishay,.*', '^visionox,.*', '^vitesse,.*', '^vivante,.*', '^vivax,.*', '^vocore,.*', '^voipac,.*', '^voltafield,.*', '^vot,.*', '^vscom,.*', '^vxt,.*', '^wacom,.*', '^wanchanglong,.*', '^wand,.*', '^waveshare,.*', '^wd,.*', '^we,.*', '^welltech,.*', '^wetek,.*', '^wexler,.*', '^whwave,.*', '^wi2wi,.*', '^widora,.*', '^wiligear,.*', '^willsemi,.*', '^winbond,.*', '^wingtech,.*', '^winlink,.*', '^winstar,.*', '^wirelesstag,.*', '^wits,.*', '^wlf,.*', '^wm,.*', '^wobo,.*', '^x-powers,.*', '^xen,.*', '^xes,.*', '^xiaomi,.*', '^xillybus,.*', '^xingbangda,.*', '^xinpeng,.*', '^xiphera,.*', '^xlnx,.*', '^xnano,.*', '^xunlong,.*', '^xylon,.*', '^yadro,.*', '^yamaha,.*', '^yes-optoelectronics,.*', '^yic,.*', '^yiming,.*', '^ylm,.*', '^yna,.*', '^yones-toptech,.*', '^ys,.*', '^ysoft,.*', '^zarlink,.*', '^zealz,.*', '^zeitec,.*', '^zidoo,.*', '^zii,.*', '^zinitix,.*', '^zkmagic,.*', '^zte,.*', '^zyxel,.*', 'pinctrl-[0-9]+'
from schema $id: http://devicetree.org/schemas/vendor-prefixes.yaml#
Documentation/devicetree/bindings/input/awinic,aw_sar.example.dtb: /example-0/i2c/awinic_sar@12: failed to match any schema with compatible: ['awinic,aw_sar']
doc reference errors (make refcheckdocs):
See https://patchwork.ozlabs.org/project/devicetree-bindings/patch/20240529130608.783624-2-wangshuaijie@awinic.com
The base for the series is generally the latest rc1. A different dependency
should be noted in *this* patch.
If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:
pip3 install dtschema --upgrade
Please check and re-submit after running the above command yourself. Note
that DT_SCHEMA_FILES can be set to your schema file to speed up checking
your schema. However, it must be unset to test all examples with your schema.
^ permalink raw reply
* Re: [PATCH v1 0/2] asus wmi and hid: use HID LED for brightness
From: Hans de Goede @ 2024-05-29 15:28 UTC (permalink / raw)
To: Luke D. Jones, Jiri Kosina, Benjamin Tissoires
Cc: ilpo.jarvinen, corentin.chary, platform-driver-x86, linux-kernel,
linux-input
In-Reply-To: <20240529012827.146005-1-luke@ljones.dev>
Hi all,
On 5/29/24 3:28 AM, Luke D. Jones wrote:
> Changelog:
> - v1
> - Split the patch in two
> - Move function body to asus-wmi and export
> - Use array of names and for loops
>
> History:
> - https://lore.kernel.org/linux-input/20240528013959.14661-1-luke@ljones.dev/T/#u
>
> Luke D. Jones (2):
> hid-asus: use hid for brightness control on keyboard
> hid-asus: change the report_id used for HID LED control
>
> drivers/hid/hid-asus.c | 32 +++++++++++++++++++-
> drivers/platform/x86/asus-wmi.c | 35 +++++++++++++++++++++-
> include/linux/platform_data/x86/asus-wmi.h | 10 +++++++
> 3 files changed, 75 insertions(+), 2 deletions(-)
Jiri, Benjamin since the first patch now also touches pdx86 files
we need to coordinate merging this.
There also is a long list of patches pending for
drivers/platform/x86/asus-wmi.c
So I would prefer to take this series (both patches) upstream through
the pdx86 tree to avoid conflicts.
May we have an ack from one of you for merging this through pdx86/for-next ?
Regards,
Hans
^ permalink raw reply
* [PATCH v3 1/2] input: Add event code for accessibility key
From: Aseda Aboagye @ 2024-05-29 18:19 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires, Dmitry Torokhov; +Cc: linux-input
HUTRR116 added support for a new usage titled "System Accessibility
Binding" which toggles a system-wide bound accessibility UI or command.
This commit simply adds a new event code for the usage.
Signed-off-by: Aseda Aboagye <aaboagye@chromium.org>
Acked-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
Changes from v2:
- None
Changes from v1:
- Modified formatting to match existing code, ignoring checkpatch.pl.
drivers/hid/hid-debug.c | 1 +
drivers/hid/hid-input.c | 1 +
include/uapi/linux/input-event-codes.h | 1 +
3 files changed, 3 insertions(+)
diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c
index e7ef1ea107c9..7749c81b6227 100644
--- a/drivers/hid/hid-debug.c
+++ b/drivers/hid/hid-debug.c
@@ -974,6 +974,7 @@ static const char *keys[KEY_MAX + 1] = {
[KEY_CAMERA_ACCESS_ENABLE] = "CameraAccessEnable",
[KEY_CAMERA_ACCESS_DISABLE] = "CameraAccessDisable",
[KEY_CAMERA_ACCESS_TOGGLE] = "CameraAccessToggle",
+ [KEY_ACCESSIBILITY] = "Accessibility",
[KEY_DICTATE] = "Dictate",
[KEY_MICMUTE] = "MicrophoneMute",
[KEY_BRIGHTNESS_MIN] = "BrightnessMin",
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index 851ee86eff32..1ecc5ad57b56 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -829,6 +829,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
if ((usage->hid & 0xf0) == 0xa0) { /* SystemControl */
switch (usage->hid & 0xf) {
case 0x9: map_key_clear(KEY_MICMUTE); break;
+ case 0xa: map_key_clear(KEY_ACCESSIBILITY); break;
default: goto ignore;
}
break;
diff --git a/include/uapi/linux/input-event-codes.h b/include/uapi/linux/input-event-codes.h
index 022a520e31fc..7ff6eeef1af0 100644
--- a/include/uapi/linux/input-event-codes.h
+++ b/include/uapi/linux/input-event-codes.h
@@ -617,6 +617,7 @@
#define KEY_CAMERA_ACCESS_ENABLE 0x24b /* Enables programmatic access to camera devices. (HUTRR72) */
#define KEY_CAMERA_ACCESS_DISABLE 0x24c /* Disables programmatic access to camera devices. (HUTRR72) */
#define KEY_CAMERA_ACCESS_TOGGLE 0x24d /* Toggles the current state of the camera access control. (HUTRR72) */
+#define KEY_ACCESSIBILITY 0x24e /* Toggles the system bound accessibility UI/command (HUTRR116) */
#define KEY_BRIGHTNESS_MIN 0x250 /* Set Brightness to Minimum */
#define KEY_BRIGHTNESS_MAX 0x251 /* Set Brightness to Maximum */
base-commit: 5128de84d8fc849400d00f7a6982711f129699ea
--
2.45.1.288.g0e0cd299f1-goog
^ permalink raw reply related
* [PATCH v3 2/2] input: Add support for "Do Not Disturb"
From: Aseda Aboagye @ 2024-05-29 18:28 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires, Dmitry Torokhov; +Cc: linux-input
In-Reply-To: <ZldxNQGXy4zWVPu1@google.com>
HUTRR94 added support for a new usage titled "System Do Not Disturb"
which toggles a system-wide Do Not Disturb setting. This commit simply
adds a new event code for the usage.
Signed-off-by: Aseda Aboagye <aaboagye@chromium.org>
---
Changes from v2:
- Added underscores to make `KEY_DO_NOT_DISTURB`.
Changes from v1:
- Modified formatting to match existing code, ignoring checkpatch.pl.
drivers/hid/hid-debug.c | 1 +
drivers/hid/hid-input.c | 8 ++++++++
include/uapi/linux/input-event-codes.h | 1 +
3 files changed, 10 insertions(+)
diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c
index 7749c81b6227..4ccfbd860bbe 100644
--- a/drivers/hid/hid-debug.c
+++ b/drivers/hid/hid-debug.c
@@ -975,6 +975,7 @@ static const char *keys[KEY_MAX + 1] = {
[KEY_CAMERA_ACCESS_DISABLE] = "CameraAccessDisable",
[KEY_CAMERA_ACCESS_TOGGLE] = "CameraAccessToggle",
[KEY_ACCESSIBILITY] = "Accessibility",
+ [KEY_DO_NOT_DISTURB] = "DoNotDisturb",
[KEY_DICTATE] = "Dictate",
[KEY_MICMUTE] = "MicrophoneMute",
[KEY_BRIGHTNESS_MIN] = "BrightnessMin",
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index 1ecc5ad57b56..31feb5e0714f 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -826,6 +826,14 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
break;
}
+ if ((usage->hid & 0xf0) == 0x90) { /* SystemControl*/
+ switch (usage->hid & 0xf) {
+ case 0xb: map_key_clear(KEY_DO_NOT_DISTURB); break;
+ default: goto ignore;
+ }
+ break;
+ }
+
if ((usage->hid & 0xf0) == 0xa0) { /* SystemControl */
switch (usage->hid & 0xf) {
case 0x9: map_key_clear(KEY_MICMUTE); break;
diff --git a/include/uapi/linux/input-event-codes.h b/include/uapi/linux/input-event-codes.h
index 7ff6eeef1af0..07038fd9682d 100644
--- a/include/uapi/linux/input-event-codes.h
+++ b/include/uapi/linux/input-event-codes.h
@@ -618,6 +618,7 @@
#define KEY_CAMERA_ACCESS_DISABLE 0x24c /* Disables programmatic access to camera devices. (HUTRR72) */
#define KEY_CAMERA_ACCESS_TOGGLE 0x24d /* Toggles the current state of the camera access control. (HUTRR72) */
#define KEY_ACCESSIBILITY 0x24e /* Toggles the system bound accessibility UI/command (HUTRR116) */
+#define KEY_DO_NOT_DISTURB 0x24f /* Toggles the system-wide "Do Not Disturb" control (HUTRR94)*/
#define KEY_BRIGHTNESS_MIN 0x250 /* Set Brightness to Minimum */
#define KEY_BRIGHTNESS_MAX 0x251 /* Set Brightness to Maximum */
--
2.45.1.288.g0e0cd299f1-goog
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox