Linux Input/HID development
 help / color / mirror / Atom feed
* RE: [PATCH v3] HID: core: move Usage Page concatenation to Main item
From: Junge, Terry @ 2019-03-26 22:43 UTC (permalink / raw)
  To: Nicolas Saenz Julienne, Jiri Kosina, Benjamin Tissoires
  Cc: oneukum@suse.de, linux-input@vger.kernel.org,
	linux-kernel@vger.kernel.org
In-Reply-To: <20190326200331.25934-1-nsaenzjulienne@suse.de>

Hi Nicolas,

This patch looks good except for one comment/question below.

Thanks,
Terry

On Tuesday, March 26, 2019 1:04 PM Nicolas Saenz Julienne <nsaenzjulienne@suse.de> wrote:
>
>As seen on some USB wireless keyboards manufactured by Primax, the HID
>parser was using some assumptions that are not always true. In this case it's s
>the fact that, inside the scope of a main item, an Usage Page will always
>precede an Usage.
>
>The spec is not pretty clear as 6.2.2.7 states "Any usage that follows is
>interpreted as a Usage ID and concatenated with the Usage Page".
>While 6.2.2.8 states "When the parser encounters a main item it concatenates
>the last declared Usage Page with a Usage to form a complete usage value."
>Being somewhat contradictory it was decided to match Window's
>implementation, which follows 6.2.2.8.
>
>In summary, the patch moves the Usage Page concatenation from the local
>item parsing function to the main item parsing function.
>
>Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
>---
>
>v2->v3: - Update patch title
>
>v1->v2: - Add usage concatenation to hid_scan_main()
>	- Rework tests in hid-tools, making sure no-one is failing
>
> drivers/hid/hid-core.c | 40 ++++++++++++++++++++++++++++------------
> include/linux/hid.h    |  1 +
> 2 files changed, 29 insertions(+), 12 deletions(-)
>
>diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index
>9993b692598f..40c836ce3248 100644
>--- a/drivers/hid/hid-core.c
>+++ b/drivers/hid/hid-core.c
>@@ -218,13 +218,14 @@ static unsigned hid_lookup_collection(struct
>hid_parser *parser, unsigned type)
>  * Add a usage to the temporary parser table.
>  */
>
>-static int hid_add_usage(struct hid_parser *parser, unsigned usage)
>+static int hid_add_usage(struct hid_parser *parser, unsigned usage,
>+__u8 size)
> {
> 	if (parser->local.usage_index >= HID_MAX_USAGES) {
> 		hid_err(parser->device, "usage index exceeded\n");
> 		return -1;
> 	}
> 	parser->local.usage[parser->local.usage_index] = usage;
>+	parser->local.usage_size[parser->local.usage_index] = size;
> 	parser->local.collection_index[parser->local.usage_index] =
> 		parser->collection_stack_ptr ?
> 		parser->collection_stack[parser->collection_stack_ptr - 1] : 0;
>@@ -486,10 +487,7 @@ static int hid_parser_local(struct hid_parser *parser,
>struct hid_item *item)
> 			return 0;
> 		}
>
>-		if (item->size <= 2)
>-			data = (parser->global.usage_page << 16) + data;
>-
>-		return hid_add_usage(parser, data);
>+		return hid_add_usage(parser, data, item->size);
>
> 	case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM:
>
>@@ -498,9 +496,6 @@ static int hid_parser_local(struct hid_parser *parser,
>struct hid_item *item)
> 			return 0;
> 		}
>
>-		if (item->size <= 2)
>-			data = (parser->global.usage_page << 16) + data;
>-
> 		parser->local.usage_minimum = data;
> 		return 0;
>
>@@ -511,9 +506,6 @@ static int hid_parser_local(struct hid_parser *parser,
>struct hid_item *item)
> 			return 0;
> 		}
>
>-		if (item->size <= 2)
>-			data = (parser->global.usage_page << 16) + data;
>-
> 		count = data - parser->local.usage_minimum;
> 		if (count + parser->local.usage_index >= HID_MAX_USAGES) {
> 			/*
>@@ -533,7 +525,7 @@ static int hid_parser_local(struct hid_parser *parser,
>struct hid_item *item)
> 		}
>
> 		for (n = parser->local.usage_minimum; n <= data; n++)
>-			if (hid_add_usage(parser, n)) {
>+			if (hid_add_usage(parser, n, item->size)) {
> 				dbg_hid("hid_add_usage failed\n");
> 				return -1;
> 			}
>@@ -547,6 +539,26 @@ static int hid_parser_local(struct hid_parser *parser,
>struct hid_item *item)
> 	return 0;
> }
>
>+/*
>+ * Concatenate Usage Pages into Usages where relevant:
>+ * As per specification, 6.2.2.8: "When the parser encounters a main
>+item it
>+ * concatenates the last declared Usage Page with a Usage to form a
>+complete
>+ * usage value."
>+ */
>+
>+static void hid_concatenate_usage_page(struct hid_parser *parser) {
>+	unsigned usages;
>+	int i;
>+
>+	usages = max_t(unsigned, parser->local.usage_index,
>+				 parser->global.report_count);

I don't think we need to worry about global.report_count here,
just concatenate for the usages currently in the local queue so could
this be simplified by removing usages and just using local.usage_index?

        for (i = 0; i < local.usage_index; i++)

>+
>+	for (i = 0; i < usages; i++)
>+		if (parser->local.usage_size[i] <= 2)
>+			parser->local.usage[i] += parser->global.usage_page
><< 16; }
>+
> /*
>  * Process a main item.
>  */
>@@ -556,6 +568,8 @@ static int hid_parser_main(struct hid_parser *parser,
>struct hid_item *item)
> 	__u32 data;
> 	int ret;
>
>+	hid_concatenate_usage_page(parser);
>+
> 	data = item_udata(item);
>
> 	switch (item->tag) {
>@@ -765,6 +779,8 @@ static int hid_scan_main(struct hid_parser *parser,
>struct hid_item *item)
> 	__u32 data;
> 	int i;
>
>+	hid_concatenate_usage_page(parser);
>+
> 	data = item_udata(item);
>
> 	switch (item->tag) {
>diff --git a/include/linux/hid.h b/include/linux/hid.h index
>f9707d1dcb58..d1fb4b678873 100644
>--- a/include/linux/hid.h
>+++ b/include/linux/hid.h
>@@ -417,6 +417,7 @@ struct hid_global {
>
> struct hid_local {
> 	unsigned usage[HID_MAX_USAGES]; /* usage array */
>+	__u8 usage_size[HID_MAX_USAGES]; /* usage size array */
> 	unsigned collection_index[HID_MAX_USAGES]; /* collection index
>array */
> 	unsigned usage_index;
> 	unsigned usage_minimum;
>--
>2.21.0

^ permalink raw reply

* Re: [PATCH] HID: intel-ish-hid: ISH firmware loader client driver
From: Jett Rink @ 2019-03-26 21:15 UTC (permalink / raw)
  To: Srinivas Pandruvada
  Cc: Rushikesh S Kadam, benjamin.tissoires, jikos, ncrews,
	Gwendal Grignou, linux-kernel, linux-input
In-Reply-To: <31755c704928710da998353192157ddfd903080c.camel@linux.intel.com>

Tested-by: Jett Rink <jettrink@chromium.org>


On Sun, Mar 24, 2019 at 9:36 AM Srinivas Pandruvada
<srinivas.pandruvada@linux.intel.com> wrote:
>
> On Sat, 2019-03-23 at 16:46 +0530, Rushikesh S Kadam wrote:
> > This driver adds support for loading Intel Integrated
> > Sensor Hub (ISH) firmware from host file system to ISH
> > SRAM and start execution.
> >
> > At power-on, the ISH subsystem shall boot to an interim
> > Shim loader-firmware, which shall expose an ISHTP loader
> > device.
> >
> > The driver implements an ISHTP client that communicates
> > with the Shim ISHTP loader device over the intel-ish-hid
> > stack, to download the main ISH firmware.
> >
> > Signed-off-by: Rushikesh S Kadam <rushikesh.s.kadam@intel.com>
> Anybody in CC list here can add Reviewed-By/Tested-by tag?
>
> One comment below for copyright year.
>
> Thanks,
> Srinivas
> > ---
> > The patches are baselined to hid git tree, branch for-5.2/ish
> >
> https://git.kernel.org/pub/scm/linux/kernel/git/hid/hid.git/log/?h=for-5.2/ish
> >
> >  drivers/hid/Makefile                        |    1 +
> >  drivers/hid/intel-ish-hid/Kconfig           |   15 +
> >  drivers/hid/intel-ish-hid/Makefile          |    3 +
> >  drivers/hid/intel-ish-hid/ishtp-fw-loader.c | 1103
> > +++++++++++++++++++++++++++
> >  4 files changed, 1122 insertions(+)
> >  create mode 100644 drivers/hid/intel-ish-hid/ishtp-fw-loader.c
> >
> > diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
> > index 170163b..d8d393e 100644
> > --- a/drivers/hid/Makefile
> > +++ b/drivers/hid/Makefile
> > @@ -134,3 +134,4 @@ obj-$(CONFIG_USB_KBD)             += usbhid/
> >  obj-$(CONFIG_I2C_HID)                += i2c-hid/
> >
> >  obj-$(CONFIG_INTEL_ISH_HID)  += intel-ish-hid/
> > +obj-$(INTEL_ISH_FIRMWARE_DOWNLOADER) += intel-ish-hid/
> > diff --git a/drivers/hid/intel-ish-hid/Kconfig b/drivers/hid/intel-
> > ish-hid/Kconfig
> > index 519e4c8..786adbc 100644
> > --- a/drivers/hid/intel-ish-hid/Kconfig
> > +++ b/drivers/hid/intel-ish-hid/Kconfig
> > @@ -14,4 +14,19 @@ config INTEL_ISH_HID
> >         Broxton and Kaby Lake.
> >
> >         Say Y here if you want to support Intel ISH. If unsure, say
> > N.
> > +
> > +config INTEL_ISH_FIRMWARE_DOWNLOADER
> > +     tristate "Host Firmware Load feature for Intel ISH"
> > +     depends on INTEL_ISH_HID
> > +     depends on X86
> > +     help
> > +       The Integrated Sensor Hub (ISH) enables the kernel to offload
> > +       sensor polling and algorithm processing to a dedicated low
> > power
> > +       processor in the chipset.
> > +
> > +       The Host Firmware Load feature adds support to load the ISH
> > +       firmware from host file system at boot.
> > +
> > +       Say M here if you want to support Host Firmware Loading
> > feature
> > +       for Intel ISH. If unsure, say N.
> >  endmenu
> > diff --git a/drivers/hid/intel-ish-hid/Makefile b/drivers/hid/intel-
> > ish-hid/Makefile
> > index 825b70a..2de97e4 100644
> > --- a/drivers/hid/intel-ish-hid/Makefile
> > +++ b/drivers/hid/intel-ish-hid/Makefile
> > @@ -20,4 +20,7 @@ obj-$(CONFIG_INTEL_ISH_HID) += intel-ishtp-hid.o
> >  intel-ishtp-hid-objs := ishtp-hid.o
> >  intel-ishtp-hid-objs += ishtp-hid-client.o
> >
> > +obj-$(CONFIG_INTEL_ISH_FIRMWARE_DOWNLOADER) += intel-ishtp-loader.o
> > +intel-ishtp-loader-objs += ishtp-fw-loader.o
> > +
> >  ccflags-y += -Idrivers/hid/intel-ish-hid/ishtp
> > diff --git a/drivers/hid/intel-ish-hid/ishtp-fw-loader.c
> > b/drivers/hid/intel-ish-hid/ishtp-fw-loader.c
> > new file mode 100644
> > index 0000000..85d71d3
> > --- /dev/null
> > +++ b/drivers/hid/intel-ish-hid/ishtp-fw-loader.c
> > @@ -0,0 +1,1103 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * ISH-TP client driver for ISH firmware loading
> > + *
> > + * Copyright (c) 2018, Intel Corporation.
> Year 2019.
>
> > + */
> > +
> > +#include <linux/firmware.h>
> > +#include <linux/module.h>
> > +#include <linux/pci.h>
> > +#include <linux/intel-ish-client-if.h>
> > +#include <linux/property.h>
> > +#include <asm/cacheflush.h>
> > +
> > +/* ISH TX/RX ring buffer pool size */
> > +#define LOADER_CL_RX_RING_SIZE                       1
> > +#define LOADER_CL_TX_RING_SIZE                       1
> > +
> > +/*
> > + * ISH Shim firmware loader reserves 4 Kb buffer in SRAM. The buffer
> > is
> > + * used to temporarily hold the data transferred from host to Shim
> > firmware
> > + * loader. Reason for the odd size of 3968 bytes? Each IPC transfer
> > is 128
> > + * bytes (= 4 bytes header + 124 bytes payload). So the 4 Kb buffer
> > can
> > + * hold maximum of 32 IPC transfers, which means we can have a max
> > payload
> > + * of 3968 bytes (= 32 x 124 payload).
> > + */
> > +#define LOADER_SHIM_IPC_BUF_SIZE             3968
> > +
> > +/**
> > + * enum ish_loader_commands -        ISH loader host commands.
> > + * LOADER_CMD_XFER_QUERY     Query the Shim firmware loader for
> > capabilities
> > + * LOADER_CMD_XFER_FRAGMENT  Transfer one firmware image framgment
> > at a
> > + *                           time. The command may be executed
> > multiple
> > + *                           times until the entire firmware image
> > is
> > + *                           downloaded to SRAM.
> > + * LOADER_CMD_START          Start executing the main firmware.
> > + */
> > +enum ish_loader_commands {
> > +     LOADER_CMD_XFER_QUERY = 0,
> > +     LOADER_CMD_XFER_FRAGMENT,
> > +     LOADER_CMD_START,
> > +};
> > +
> > +/* Command bit mask */
> > +#define      CMD_MASK                                GENMASK(6, 0)
> > +#define      IS_RESPONSE                             BIT(7)
> > +
> > +/*
> > + * ISH firmware max delay for one transmit failure is 1 Hz,
> > + * and firmware will retry 2 times, so 3 Hz is used for timeout.
> > + */
> > +#define ISHTP_SEND_TIMEOUT                   (3 * HZ)
> > +
> > +/*
> > + * Loader transfer modes:
> > + *
> > + * LOADER_XFER_MODE_ISHTP mode uses the existing ISH-TP mechanims to
> > + * transfer data. This may use IPC or DMA if supported in firmware.
> > + * The buffer size is limited to 4 Kb by the IPC/ISH-TP protocol for
> > + * both IPC & DMA (legacy).
> > + *
> > + * LOADER_XFER_MODE_DIRECT_DMA - firmware loading is a bit different
> > + * from the sensor data streaming. Here we download a large (300+
> > Kb)
> > + * image directly to ISH SRAM memory. There is limited benefit of
> > + * DMA'ing 300 Kb image in 4 Kb chucks limit. Hence, we introduce
> > + * this "direct dma" mode, where we do not use ISH-TP for DMA, but
> > + * instead manage the DMA directly in kernel driver and Shim
> > firmware
> > + * loader (allocate buf, break in chucks and transfer). This allows
> > + * to overcome 4 Kb limit, and optimize the data flow path in
> > firmware.
> > + */
> > +#define LOADER_XFER_MODE_DIRECT_DMA          BIT(0)
> > +#define LOADER_XFER_MODE_ISHTP                       BIT(1)
> > +
> > +/* ISH Transport Loader client unique GUID */
> > +static const guid_t loader_ishtp_guid =
> > +     GUID_INIT(0xc804d06a, 0x55bd, 0x4ea7,
> > +               0xad, 0xed, 0x1e, 0x31, 0x22, 0x8c, 0x76, 0xdc);
> > +
> > +#define FILENAME_SIZE                                256
> > +
> > +/*
> > + * The firmware loading latency will be minimum if we can DMA the
> > + * entire ISH firmware image in one go. This requires that we
> > allocate
> > + * a large DMA buffer in kernel, which could be problematic on some
> > + * platforms. So here we limit the DMA buf size via a module_param.
> > + * We default to 4 pages, but a customer can set it to higher limit
> > if
> > + * deemed appropriate for his platform.
> > + */
> > +static int dma_buf_size_limit = 4 * PAGE_SIZE;
> > +
> > +/**
> > + * struct loader_msg_hdr - Header for ISH Loader commands.
> > + * @command:         LOADER_CMD* commands. Bit 7 is the response.
> > + * @status:          Command response status. Non 0, is error
> > condition.
> > + *
> > + * This structure is used as header for every command/data
> > sent/received
> > + * between Host driver and ISH Shim firmware loader.
> > + */
> > +struct loader_msg_hdr {
> > +     u8 command;
> > +     u8 reserved[2];
> > +     u8 status;
> > +} __packed;
> > +
> > +struct loader_xfer_query {
> > +     struct loader_msg_hdr hdr;
> > +     u32 image_size;
> > +} __packed;
> > +
> > +struct ish_fw_version {
> > +     u16 major;
> > +     u16 minor;
> > +     u16 hotfix;
> > +     u16 build;
> > +} __packed;
> > +
> > +union loader_version {
> > +     u32 value;
> > +     struct {
> > +             u8 major;
> > +             u8 minor;
> > +             u8 hotfix;
> > +             u8 build;
> > +     };
> > +} __packed;
> > +
> > +struct loader_capability {
> > +     u32 max_fw_image_size;
> > +     u32 xfer_mode;
> > +     u32 max_dma_buf_size; /* only for dma mode, multiples of
> > cacheline */
> > +} __packed;
> > +
> > +struct shim_fw_info {
> > +     struct ish_fw_version ish_fw_version;
> > +     u32 protocol_version;
> > +     union loader_version ldr_version;
> > +     struct loader_capability ldr_capability;
> > +} __packed;
> > +
> > +struct loader_xfer_query_response {
> > +     struct loader_msg_hdr hdr;
> > +     struct shim_fw_info fw_info;
> > +} __packed;
> > +
> > +struct loader_xfer_fragment {
> > +     struct loader_msg_hdr hdr;
> > +     u32 xfer_mode;
> > +     u32 offset;
> > +     u32 size;
> > +     u32 is_last;
> > +} __packed;
> > +
> > +struct loader_xfer_ipc_fragment {
> > +     struct loader_xfer_fragment fragment;
> > +     u8 data[] ____cacheline_aligned; /* variable length payload
> > here */
> > +} __packed;
> > +
> > +struct loader_xfer_dma_fragment {
> > +     struct loader_xfer_fragment fragment;
> > +     u64 ddr_phys_addr;
> > +} __packed;
> > +
> > +struct loader_start {
> > +     struct loader_msg_hdr hdr;
> > +} __packed;
> > +
> > +/**
> > + * struct ishtp_cl_data - Encapsulate per ISH-TP Client Data
> > + * @flag_response    Set true on receiving a firmware  response to
> > host
> > + *                   loader command
> > + * @cmd_resp_wait:   Wait queue for Host firmware loading, where the
> > + *                   client sends message to ISH firmware and wait
> > for
> > + *                   response
> > + * @work_ishtp_reset:        Work queue for reset handling
> > + * @work_fw_load:    Work queue for host firmware loading
> > + * @flag_retry               Flag for indicating host firmware
> > loading should be
> > + *                   retried
> > + * @bad_recv_cnt:    Running count of packets received with error
> > + *
> > + * This structure is used to store data per client
> > + */
> > +struct ishtp_cl_data {
> > +     struct ishtp_cl *loader_ishtp_cl;
> > +     struct ishtp_cl_device *cl_device;
> > +
> > +     /* Completion flags */
> > +     bool flag_response;
> > +
> > +     /* Copy buffer received in firmware "response" here */
> > +     void *response_data;
> > +     size_t response_size;
> > +
> > +     /* Wait queue for ISH firmware message event */
> > +     wait_queue_head_t cmd_resp_wait;
> > +
> > +     struct work_struct work_ishtp_reset;
> > +     struct work_struct work_fw_load;
> > +
> > +     /*
> > +      * In certain failure scenrios, it makes sense to reset the
> > +      * the ISH subsystem and retry Host firmware loading
> > +      * (e.g. bad message packet, ENOMEM, etc.)
> > +      * On the other hand, failures due to protocol mismatch, etc
> > +      * are not recoverable. We do not retry.
> > +      *
> > +      * If set, the flag indictes that we should re-try the
> > particular
> > +      * failure.
> > +      */
> > +     bool flag_retry;
> > +
> > +     /* Statistics */
> > +     unsigned int bad_recv_cnt;
> > +};
> > +
> > +#define IPC_FRAGMENT_DATA_PREAMBLE                           \
> > +     offsetof(struct loader_xfer_ipc_fragment, data)
> > +
> > +#define cl_data_to_dev(client_data) ishtp_device((client_data)-
> > >cl_device)
> > +
> > +/**
> > + * get_firmware_variant() - Gets the filename of firmware image to
> > be
> > + *                   loaded based on platform variant.
> > + * @client_data              Client data instance.
> > + * @filename         Returns firmware filename.
> > + *
> > + * Queries the firmware-name device property string.
> > + *
> > + * Return: 0 for success, negative error code for failure.
> > + */
> > +static int get_firmware_variant(struct ishtp_cl_data *client_data,
> > +                             char *filename)
> > +{
> > +     int rv;
> > +     const char *val;
> > +     struct device *devc = ishtp_get_pci_device(client_data-
> > >cl_device);
> > +
> > +     rv = device_property_read_string(devc, "firmware-name", &val);
> > +     if (rv < 0) {
> > +             dev_err(devc,
> > +                     "Error: ISH firmware-name device property
> > required\n");
> > +             return rv;
> > +     }
> > +     return snprintf(filename, FILENAME_SIZE, "intel/%s", val);
> > +}
> > +
> > +/**
> > + * report_bad_packets() Report bad packets
> > + * @loader_ishtp_cl: Client instance to get stats
> > + * @recv_buf:                Raw received host interface message
> > + *
> > + * Dumps error in case bad packet is received
> > + */
> > +static void report_bad_packet(struct ishtp_cl *loader_ishtp_cl,
> > +                           void *recv_buf)
> > +{
> > +     struct loader_msg_hdr *hdr = recv_buf;
> > +     struct ishtp_cl_data *client_data =
> > +             ishtp_get_client_data(loader_ishtp_cl);
> > +
> > +     client_data->bad_recv_cnt++;
> > +     dev_err(cl_data_to_dev(client_data),
> > +             "BAD packet: command=%02lx is_response=%u status=%02x
> > total_bad=%u\n",
> > +             hdr->command & CMD_MASK,
> > +             hdr->command & IS_RESPONSE ? 1 : 0,
> > +             hdr->status,
> > +             client_data->bad_recv_cnt);
> > +}
> > +
> > +/**
> > + * loader_ish_hw_reset() - Reset ISH HW in bad state
> > + * @loader_ishtp_cl  Client instance to reset
> > + *
> > + * This function resets ISH hardware, which shall reload
> > + * the Shim firmware loader, initiate ISH-TP interface reset,
> > + * re-attach kernel loader driver, and repeat Host
> > + * firmware load.
> > + */
> > +static inline void loader_ish_hw_reset(struct ishtp_cl
> > *loader_ishtp_cl)
> > +{
> > +     struct ishtp_cl_data *client_data =
> > +             ishtp_get_client_data(loader_ishtp_cl);
> > +
> > +     dev_warn(cl_data_to_dev(client_data), "Reset the ISH
> > subsystem\n");
> > +     ish_hw_reset(ishtp_get_ishtp_device(loader_ishtp_cl));
> > +}
> > +
> > +/**
> > + * loader_cl_send()  Send message from host to firmware
> > + * @client_data:     Client data instance
> > + * @msg                      Message buffer to send
> > + * @msg_size         Size of message
> > + *
> > + * Return: Received buffer size on success, negative error code on
> > failure.
> > + */
> > +static int loader_cl_send(struct ishtp_cl_data *client_data,
> > +                       u8 *msg, size_t msg_size)
> > +{
> > +     int rv;
> > +     size_t data_len;
> > +     struct loader_msg_hdr *in_hdr;
> > +     struct loader_msg_hdr *out_hdr = (struct loader_msg_hdr *)msg;
> > +     struct ishtp_cl *loader_ishtp_cl = client_data-
> > >loader_ishtp_cl;
> > +
> > +     dev_dbg(cl_data_to_dev(client_data),
> > +             "%s: command=%02lx is_response=%u status=%02x\n",
> > +             __func__,
> > +             out_hdr->command & CMD_MASK,
> > +             out_hdr->command & IS_RESPONSE ? 1 : 0,
> > +             out_hdr->status);
> > +
> > +     client_data->flag_response = false;
> > +     rv = ishtp_cl_send(loader_ishtp_cl, msg, msg_size);
> > +     if (rv < 0) {
> > +             dev_err(cl_data_to_dev(client_data),
> > +                     "ishtp_cl_send error %d\n", rv);
> > +             return rv;
> > +     }
> > +
> > +     wait_event_interruptible_timeout(client_data->cmd_resp_wait,
> > +                                      client_data->flag_response,
> > +                                      ISHTP_SEND_TIMEOUT);
> > +     if (!client_data->flag_response) {
> > +             dev_err(cl_data_to_dev(client_data),
> > +                     "Timed out for response to command=%02lx",
> > +                     out_hdr->command & CMD_MASK);
> > +             return -ETIMEDOUT;
> > +     }
> > +
> > +     /* All response messages will contain a header */
> > +     data_len = client_data->response_size;
> > +     in_hdr = (struct loader_msg_hdr *)client_data->response_data;
> > +
> > +     /* Sanity checks */
> > +     if (!(in_hdr->command & IS_RESPONSE)) {
> > +             dev_err(cl_data_to_dev(client_data),
> > +                     "Invalid response to command\n");
> > +             return -EIO;
> > +     }
> > +
> > +     if (in_hdr->status) {
> > +             dev_err(cl_data_to_dev(client_data),
> > +                     "Loader returned status %d\n",
> > +                     in_hdr->status);
> > +             return -EIO;
> > +     }
> > +
> > +     return data_len;
> > +}
> > +
> > +/**
> > + * process_recv() -  Receive and parse incoming packet
> > + * @loader_ishtp_cl: Client instance to get stats
> > + * @rb_in_proc:              ISH received message buffer
> > + *
> > + * Parse the incoming packet. If it is a response packet then it
> > will
> > + * update flag_response and wake up the caller waiting to for the
> > response.
> > + */
> > +static void process_recv(struct ishtp_cl *loader_ishtp_cl,
> > +                      struct ishtp_cl_rb *rb_in_proc)
> > +{
> > +     size_t data_len = rb_in_proc->buf_idx;
> > +     struct loader_msg_hdr *hdr =
> > +             (struct loader_msg_hdr *)rb_in_proc->buffer.data;
> > +     struct ishtp_cl_data *client_data =
> > +             ishtp_get_client_data(loader_ishtp_cl);
> > +
> > +     /*
> > +      * All firmware messages have a header. Check buffer size
> > +      * before accessing elements inside.
> > +      */
> > +     if (data_len < sizeof(struct loader_msg_hdr)) {
> > +             dev_err(cl_data_to_dev(client_data),
> > +                     "data size %zu is less than header %zu\n",
> > +                     data_len, sizeof(struct loader_msg_hdr));
> > +             report_bad_packet(client_data->loader_ishtp_cl, hdr);
> > +             goto end_error;
> > +     }
> > +
> > +     dev_dbg(cl_data_to_dev(client_data),
> > +             "%s: command=%02lx is_response=%u status=%02x\n",
> > +             __func__,
> > +             hdr->command & CMD_MASK,
> > +             hdr->command & IS_RESPONSE ? 1 : 0,
> > +             hdr->status);
> > +
> > +     switch (hdr->command & CMD_MASK) {
> > +     case LOADER_CMD_XFER_QUERY:
> > +     case LOADER_CMD_XFER_FRAGMENT:
> > +     case LOADER_CMD_START:
> > +             /* Sanity check */
> > +             if (client_data->response_data || client_data-
> > >flag_response) {
> > +                     dev_err(cl_data_to_dev(client_data),
> > +                             "Buffer overrun: previous firmware
> > message not yet processed\n");
> > +                     report_bad_packet(client_data->loader_ishtp_cl,
> > hdr);
> > +                     break;
> > +             }
> > +
> > +             /*
> > +              * Copy the buffer received in firmware response for
> > the
> > +              * calling thread.
> > +              */
> > +             client_data->response_data = kmalloc(data_len,
> > GFP_KERNEL);
> > +             if (!client_data->response_data)
> > +                     break;
> > +
> > +             memcpy(client_data->response_data,
> > +                    rb_in_proc->buffer.data, data_len);
> > +             client_data->response_size = data_len;
> > +
> > +             /* Free the buffer */
> > +             ishtp_cl_io_rb_recycle(rb_in_proc);
> > +             rb_in_proc = NULL;
> > +
> > +             /* Wake the calling thread */
> > +             client_data->flag_response = true;
> > +             wake_up_interruptible(&client_data->cmd_resp_wait);
> > +             break;
> > +
> > +     default:
> > +             dev_err(cl_data_to_dev(client_data),
> > +                     "Invalid command=%02lx\n",
> > +                     hdr->command & CMD_MASK);
> > +             report_bad_packet(client_data->loader_ishtp_cl, hdr);
> > +     }
> > +
> > +end_error:
> > +     /* Free the buffer if we did not do above */
> > +     if (rb_in_proc)
> > +             ishtp_cl_io_rb_recycle(rb_in_proc);
> > +}
> > +
> > +/**
> > + * loader_cl_event_cb() - bus driver callback for incoming message
> > + * @device:          Pointer to the the ishtp client device for
> > which
> > + *                   this message is targeted
> > + *
> > + * Remove the packet from the list and process the message by
> > calling
> > + * process_recv
> > + */
> > +static void loader_cl_event_cb(struct ishtp_cl_device *cl_device)
> > +{
> > +     struct ishtp_cl_rb *rb_in_proc;
> > +     struct ishtp_cl_data *client_data;
> > +     struct ishtp_cl *loader_ishtp_cl =
> > ishtp_get_drvdata(cl_device);
> > +
> > +     client_data = ishtp_get_client_data(loader_ishtp_cl);
> > +
> > +     while ((rb_in_proc = ishtp_cl_rx_get_rb(loader_ishtp_cl)) !=
> > NULL) {
> > +             if (!rb_in_proc->buffer.data) {
> > +                     dev_warn(cl_data_to_dev(client_data),
> > +                              "rb_in_proc->buffer.data returned
> > null");
> > +                     continue;
> > +             }
> > +
> > +             /* Process the data packet from firmware */
> > +             process_recv(loader_ishtp_cl, rb_in_proc);
> > +     }
> > +}
> > +
> > +/**
> > + * ish_query_loader_prop() -  Query ISH Shim firmware loader
> > + * @client_data:     Client data instance
> > + * @fw:                      Poiner to fw data struct in host memory
> > + *
> > + * This function queries the ISH Shim firmware loader for
> > capabilities.
> > + *
> > + * Return: 0 for success, negative error code for failure.
> > + */
> > +static int ish_query_loader_prop(struct ishtp_cl_data *client_data,
> > +                              const struct firmware *fw,
> > +                              struct shim_fw_info *fw_info)
> > +{
> > +     int rv;
> > +     size_t data_len;
> > +     struct loader_msg_hdr *hdr;
> > +     struct loader_xfer_query ldr_xfer_query;
> > +     struct loader_xfer_query_response *ldr_xfer_query_resp;
> > +
> > +     memset(&ldr_xfer_query, 0, sizeof(ldr_xfer_query));
> > +     ldr_xfer_query.hdr.command = LOADER_CMD_XFER_QUERY;
> > +     ldr_xfer_query.image_size = fw->size;
> > +     rv = loader_cl_send(client_data,
> > +                         (u8 *)&ldr_xfer_query,
> > +                         sizeof(ldr_xfer_query));
> > +     if (rv < 0) {
> > +             client_data->flag_retry = true;
> > +             goto end_error;
> > +     }
> > +
> > +     /* Check buffer size before accessing the elements */
> > +     data_len = client_data->response_size;
> > +     if (data_len != sizeof(struct loader_xfer_query_response)) {
> > +             dev_err(cl_data_to_dev(client_data),
> > +                     "data size %zu is not equal to size of
> > loader_xfer_query_response %zu\n",
> > +                     data_len, sizeof(struct
> > loader_xfer_query_response));
> > +             hdr = (struct loader_msg_hdr *)client_data-
> > >response_data;
> > +             report_bad_packet(client_data->loader_ishtp_cl, hdr);
> > +             client_data->flag_retry = true;
> > +             rv = -EMSGSIZE;
> > +             goto end_error;
> > +     }
> > +
> > +     /* Save fw_info for use outside this function */
> > +     ldr_xfer_query_resp =
> > +             (struct loader_xfer_query_response *)client_data-
> > >response_data;
> > +     *fw_info = ldr_xfer_query_resp->fw_info;
> > +
> > +     /* Loader firmware properties */
> > +     dev_dbg(cl_data_to_dev(client_data),
> > +             "ish_fw_version: major=%d minor=%d hotfix=%d build=%d
> > protocol_version=0x%x loader_version=%d\n",
> > +             fw_info->ish_fw_version.major,
> > +             fw_info->ish_fw_version.minor,
> > +             fw_info->ish_fw_version.hotfix,
> > +             fw_info->ish_fw_version.build,
> > +             fw_info->protocol_version,
> > +             fw_info->ldr_version.value);
> > +
> > +     dev_dbg(cl_data_to_dev(client_data),
> > +             "loader_capability: max_fw_image_size=0x%x xfer_mode=%d
> > max_dma_buf_size=0x%x dma_buf_size_limit=0x%x\n",
> > +             fw_info->ldr_capability.max_fw_image_size,
> > +             fw_info->ldr_capability.xfer_mode,
> > +             fw_info->ldr_capability.max_dma_buf_size,
> > +             dma_buf_size_limit);
> > +
> > +     /* Sanity checks */
> > +     if (fw_info->ldr_capability.max_fw_image_size < fw->size) {
> > +             dev_err(cl_data_to_dev(client_data),
> > +                     "ISH firmware size %zu is greater than Shim
> > firmware loader max supported %d\n",
> > +                     fw->size,
> > +                     fw_info->ldr_capability.max_fw_image_size);
> > +             rv = -ENOSPC;
> > +             goto end_error;
> > +     }
> > +
> > +     /* For DMA the buffer size should be multiple of cacheline size
> > */
> > +     if ((fw_info->ldr_capability.xfer_mode &
> > LOADER_XFER_MODE_DIRECT_DMA) &&
> > +         (fw_info->ldr_capability.max_dma_buf_size %
> > L1_CACHE_BYTES)) {
> > +             dev_err(cl_data_to_dev(client_data),
> > +                     "Shim firmware loader buffer size %d should be
> > multipe of cacheline\n",
> > +                     fw_info->ldr_capability.max_dma_buf_size);
> > +             rv = -EINVAL;
> > +             goto end_error;
> > +     }
> > +
> > +end_error:
> > +     /* Free ISH buffer if not done so in error case */
> > +     kfree(client_data->response_data);
> > +     client_data->response_data = NULL;
> > +     return rv;
> > +}
> > +
> > +/**
> > + * ish_fw_xfer_ishtp()       Loads ISH firmware using ishtp
> > interface
> > + * @client_data:     Client data instance
> > + * @fw:                      Pointer to fw data struct in host
> > memory
> > + *
> > + * This function uses ISH-TP to transfer ISH firmware from host to
> > + * ISH SRAM. Lower layers may use IPC or DMA depending on firmware
> > + * support.
> > + *
> > + * Return: 0 for success, negative error code for failure.
> > + */
> > +static int ish_fw_xfer_ishtp(struct ishtp_cl_data *client_data,
> > +                          const struct firmware *fw)
> > +{
> > +     int rv;
> > +     u32 fragment_offset, fragment_size, payload_max_size;
> > +     struct loader_xfer_ipc_fragment *ldr_xfer_ipc_frag;
> > +
> > +     payload_max_size =
> > +             LOADER_SHIM_IPC_BUF_SIZE - IPC_FRAGMENT_DATA_PREAMBLE;
> > +
> > +     ldr_xfer_ipc_frag = kzalloc(LOADER_SHIM_IPC_BUF_SIZE,
> > GFP_KERNEL);
> > +     if (!ldr_xfer_ipc_frag) {
> > +             client_data->flag_retry = true;
> > +             return -ENOMEM;
> > +     }
> > +
> > +     ldr_xfer_ipc_frag->fragment.hdr.command =
> > LOADER_CMD_XFER_FRAGMENT;
> > +     ldr_xfer_ipc_frag->fragment.xfer_mode = LOADER_XFER_MODE_ISHTP;
> > +
> > +     /* Break the firmware image into fragments and send as ISH-TP
> > payload */
> > +     fragment_offset = 0;
> > +     while (fragment_offset < fw->size) {
> > +             if (fragment_offset + payload_max_size < fw->size) {
> > +                     fragment_size = payload_max_size;
> > +                     ldr_xfer_ipc_frag->fragment.is_last = 0;
> > +             } else {
> > +                     fragment_size = fw->size - fragment_offset;
> > +                     ldr_xfer_ipc_frag->fragment.is_last = 1;
> > +             }
> > +
> > +             ldr_xfer_ipc_frag->fragment.offset = fragment_offset;
> > +             ldr_xfer_ipc_frag->fragment.size = fragment_size;
> > +             memcpy(ldr_xfer_ipc_frag->data,
> > +                    &fw->data[fragment_offset],
> > +                    fragment_size);
> > +
> > +             dev_dbg(cl_data_to_dev(client_data),
> > +                     "xfer_mode=ipc offset=0x%08x size=0x%08x
> > is_last=%d\n",
> > +                     ldr_xfer_ipc_frag->fragment.offset,
> > +                     ldr_xfer_ipc_frag->fragment.size,
> > +                     ldr_xfer_ipc_frag->fragment.is_last);
> > +
> > +             rv = loader_cl_send(client_data,
> > +                                 (u8 *)ldr_xfer_ipc_frag,
> > +                                 IPC_FRAGMENT_DATA_PREAMBLE +
> > fragment_size);
> > +             if (rv < 0) {
> > +                     client_data->flag_retry = true;
> > +                     goto end_err_resp_buf_release;
> > +             }
> > +
> > +             /* Free ISH buffer once response is processed */
> > +             kfree(client_data->response_data);
> > +             client_data->response_data = NULL;
> > +
> > +             fragment_offset += fragment_size;
> > +     }
> > +
> > +     kfree(ldr_xfer_ipc_frag);
> > +     return 0;
> > +
> > +end_err_resp_buf_release:
> > +     /* Free ISH buffer if not done already, in error case */
> > +     kfree(client_data->response_data);
> > +     client_data->response_data = NULL;
> > +     kfree(ldr_xfer_ipc_frag);
> > +     return rv;
> > +}
> > +
> > +/**
> > + * ish_fw_xfer_direct_dma() - Loads ISH firmware using direct dma
> > + * @client_data:     Client data instance
> > + * @fw:                      Poiner to fw data struct in host memory
> > + *
> > + * Host firmware load is a unique case where we need to download
> > + * a large firmware image (200+ Kb). This function implements
> > + * direct DMA transfer in kernel and ISH firmware. This allows
> > + * us to overcome the ISH-TP 4 Kb limit, and allows us to DMA
> > + * directly to ISH UMA at location of choice.
> > + * Function depends on corresponding support in ISH firmware.
> > + *
> > + * Return: 0 for success, negative error code for failure.
> > + */
> > +static int ish_fw_xfer_direct_dma(struct ishtp_cl_data *client_data,
> > +                               const struct firmware *fw,
> > +                               struct shim_fw_info fw_info)
> > +{
> > +     int rv;
> > +     void *dma_buf;
> > +     dma_addr_t dma_buf_phy;
> > +     u32 fragment_offset, fragment_size, payload_max_size;
> > +     struct loader_xfer_dma_fragment ldr_xfer_dma_frag;
> > +     struct device *devc = ishtp_get_pci_device(client_data-
> > >cl_device);
> > +     u32 shim_fw_buf_size =
> > +             fw_info.ldr_capability.max_dma_buf_size;
> > +
> > +     /*
> > +      * payload_max_size should be set to minimum of
> > +      *  (1) Size of firmware to be loaded,
> > +      *  (2) Max DMA buf size supported by Shim firmware,
> > +      *  (3) DMA buffer size limit set by boot_param
> > dma_buf_size_limit.
> > +      */
> > +     payload_max_size = min3(fw->size,
> > +                             (size_t)shim_fw_buf_size,
> > +                             (size_t)dma_buf_size_limit);
> > +
> > +     /*
> > +      * Buffer size should be multiple of cacheline size
> > +      * if it's not, select the previous cacheline boundary.
> > +      */
> > +     payload_max_size &= ~(L1_CACHE_BYTES - 1);
> > +
> > +     dma_buf = kmalloc(payload_max_size, GFP_KERNEL | GFP_DMA32);
> > +     if (!dma_buf) {
> > +             client_data->flag_retry = true;
> > +             return -ENOMEM;
> > +     }
> > +
> > +     dma_buf_phy = dma_map_single(devc, dma_buf, payload_max_size,
> > +                                  DMA_TO_DEVICE);
> > +     if (dma_mapping_error(devc, dma_buf_phy)) {
> > +             dev_err(cl_data_to_dev(client_data), "DMA map
> > failed\n");
> > +             client_data->flag_retry = true;
> > +             rv = -ENOMEM;
> > +             goto end_err_dma_buf_release;
> > +     }
> > +
> > +     ldr_xfer_dma_frag.fragment.hdr.command =
> > LOADER_CMD_XFER_FRAGMENT;
> > +     ldr_xfer_dma_frag.fragment.xfer_mode =
> > LOADER_XFER_MODE_DIRECT_DMA;
> > +     ldr_xfer_dma_frag.ddr_phys_addr = (u64)dma_buf_phy;
> > +
> > +     /* Send the firmware image in chucks of payload_max_size */
> > +     fragment_offset = 0;
> > +     while (fragment_offset < fw->size) {
> > +             if (fragment_offset + payload_max_size < fw->size) {
> > +                     fragment_size = payload_max_size;
> > +                     ldr_xfer_dma_frag.fragment.is_last = 0;
> > +             } else {
> > +                     fragment_size = fw->size - fragment_offset;
> > +                     ldr_xfer_dma_frag.fragment.is_last = 1;
> > +             }
> > +
> > +             ldr_xfer_dma_frag.fragment.offset = fragment_offset;
> > +             ldr_xfer_dma_frag.fragment.size = fragment_size;
> > +             memcpy(dma_buf, &fw->data[fragment_offset],
> > fragment_size);
> > +
> > +             dma_sync_single_for_device(devc, dma_buf_phy,
> > +                                        payload_max_size,
> > +                                        DMA_TO_DEVICE);
> > +
> > +             /*
> > +              * Flush cache here because the
> > dma_sync_single_for_device()
> > +              * does not do for x86.
> > +              */
> > +             clflush_cache_range(dma_buf, payload_max_size);
> > +
> > +             dev_dbg(cl_data_to_dev(client_data),
> > +                     "xfer_mode=dma offset=0x%08x size=0x%x
> > is_last=%d ddr_phys_addr=0x%016llx\n",
> > +                     ldr_xfer_dma_frag.fragment.offset,
> > +                     ldr_xfer_dma_frag.fragment.size,
> > +                     ldr_xfer_dma_frag.fragment.is_last,
> > +                     ldr_xfer_dma_frag.ddr_phys_addr);
> > +
> > +             rv = loader_cl_send(client_data,
> > +                                 (u8 *)&ldr_xfer_dma_frag,
> > +                                 sizeof(ldr_xfer_dma_frag));
> > +             if (rv < 0) {
> > +                     client_data->flag_retry = true;
> > +                     goto end_err_resp_buf_release;
> > +             }
> > +
> > +             /* Free ISH buffer once response is processed */
> > +             kfree(client_data->response_data);
> > +             client_data->response_data = NULL;
> > +
> > +             fragment_offset += fragment_size;
> > +     }
> > +
> > +     dma_unmap_single(devc, dma_buf_phy, payload_max_size,
> > DMA_TO_DEVICE);
> > +     kfree(dma_buf);
> > +     return 0;
> > +
> > +end_err_resp_buf_release:
> > +     /* Free ISH buffer if not done already, in error case */
> > +     kfree(client_data->response_data);
> > +     client_data->response_data = NULL;
> > +     dma_unmap_single(devc, dma_buf_phy, payload_max_size,
> > DMA_TO_DEVICE);
> > +end_err_dma_buf_release:
> > +     kfree(dma_buf);
> > +     return rv;
> > +}
> > +
> > +/**
> > + * ish_fw_start()    Start executing ISH main firmware
> > + * @client_data:     client data instance
> > + *
> > + * This function sends message to Shim firmware loader to start
> > + * the execution of ISH main firmware.
> > + *
> > + * Return: 0 for success, negative error code for failure.
> > + */
> > +static int ish_fw_start(struct ishtp_cl_data *client_data)
> > +{
> > +     int rv;
> > +     struct loader_start ldr_start;
> > +
> > +     memset(&ldr_start, 0, sizeof(ldr_start));
> > +     ldr_start.hdr.command = LOADER_CMD_START;
> > +     rv = loader_cl_send(client_data,
> > +                         (u8 *)&ldr_start,
> > +                         sizeof(ldr_start));
> > +
> > +     /* Free ISH buffer once response is processed */
> > +     kfree(client_data->response_data);
> > +     client_data->response_data = NULL;
> > +     return rv;
> > +}
> > +
> > +/**
> > + * load_fw_from_host()       Loads ISH firmware from host
> > + * @client_data:     Client data instance
> > + *
> > + * This function loads the ISH firmware to ISH sram and starts
> > execution
> > + *
> > + * Return: 0 for success, negative error code for failure.
> > + */
> > +static int load_fw_from_host(struct ishtp_cl_data *client_data)
> > +{
> > +     int rv;
> > +     u32 xfer_mode;
> > +     char *filename;
> > +     const struct firmware *fw;
> > +     struct shim_fw_info fw_info;
> > +
> > +     client_data->flag_retry = false;
> > +
> > +     filename = kzalloc(FILENAME_SIZE, GFP_KERNEL);
> > +     if (!filename) {
> > +             rv = -ENOMEM;
> > +             goto end_error;
> > +     }
> > +
> > +     /* Get filename of the ISH firmware to be loaded */
> > +     rv = get_firmware_variant(client_data, filename);
> > +     if (rv < 0)
> > +             goto end_err_filename_buf_release;
> > +
> > +     rv = request_firmware(&fw, filename,
> > cl_data_to_dev(client_data));
> > +     if (rv < 0)
> > +             goto end_err_filename_buf_release;
> > +
> > +     /* Step 1: Query Shim firmware loader properties */
> > +
> > +     rv = ish_query_loader_prop(client_data, fw, &fw_info);
> > +     if (rv < 0)
> > +             goto end_err_fw_release;
> > +
> > +     /* Step 2: Send the main firmware image to be loaded, to ISH
> > sram */
> > +
> > +     xfer_mode = fw_info.ldr_capability.xfer_mode;
> > +     if (xfer_mode & LOADER_XFER_MODE_DIRECT_DMA) {
> > +             rv = ish_fw_xfer_direct_dma(client_data, fw, fw_info);
> > +     } else if (xfer_mode & LOADER_XFER_MODE_ISHTP) {
> > +             rv = ish_fw_xfer_ishtp(client_data, fw);
> > +     } else {
> > +             dev_err(cl_data_to_dev(client_data),
> > +                     "No transfer mode selected in firmware\n");
> > +             rv = -EINVAL;
> > +     }
> > +     if (rv < 0)
> > +             goto end_err_fw_release;
> > +
> > +     /* Step 3: Start ISH main firmware exeuction */
> > +
> > +     rv = ish_fw_start(client_data);
> > +     if (rv < 0)
> > +             goto end_err_fw_release;
> > +
> > +     release_firmware(fw);
> > +     kfree(filename);
> > +     dev_info(cl_data_to_dev(client_data), "ISH firmware %s
> > loaded\n",
> > +              filename);
> > +     return 0;
> > +
> > +end_err_fw_release:
> > +     release_firmware(fw);
> > +end_err_filename_buf_release:
> > +     kfree(filename);
> > +end_error:
> > +     if (client_data->flag_retry) {
> > +             dev_warn(cl_data_to_dev(client_data),
> > +                      "ISH host firmware load failed %d. Reset ISH &
> > try again..\n",
> > +                      rv);
> > +             loader_ish_hw_reset(client_data->loader_ishtp_cl);
> > +     } else {
> > +             dev_err(cl_data_to_dev(client_data),
> > +                     "ISH host firmware load failed %d\n", rv);
> > +     }
> > +     return rv;
> > +}
> > +
> > +static void load_fw_from_host_handler(struct work_struct *work)
> > +{
> > +     struct ishtp_cl_data *client_data;
> > +
> > +     client_data = container_of(work, struct ishtp_cl_data,
> > +                                work_fw_load);
> > +     load_fw_from_host(client_data);
> > +}
> > +
> > +/**
> > + * loader_init() -   Init function for ISH-TP client
> > + * @loader_ishtp_cl: ISH-TP client instance
> > + * @reset:           true if called for init after reset
> > + *
> > + * Return: 0 for success, negative error code for failure
> > + */
> > +static int loader_init(struct ishtp_cl *loader_ishtp_cl, int reset)
> > +{
> > +     int rv;
> > +     struct ishtp_fw_client *fw_client;
> > +     struct ishtp_cl_data *client_data =
> > +             ishtp_get_client_data(loader_ishtp_cl);
> > +
> > +     dev_dbg(cl_data_to_dev(client_data), "reset flag: %d\n",
> > reset);
> > +
> > +     rv = ishtp_cl_link(loader_ishtp_cl);
> > +     if (rv < 0) {
> > +             dev_err(cl_data_to_dev(client_data), "ishtp_cl_link
> > failed\n");
> > +             return rv;
> > +     }
> > +
> > +     /* Connect to firmware client */
> > +     ishtp_set_tx_ring_size(loader_ishtp_cl,
> > LOADER_CL_TX_RING_SIZE);
> > +     ishtp_set_rx_ring_size(loader_ishtp_cl,
> > LOADER_CL_RX_RING_SIZE);
> > +
> > +     fw_client =
> > +             ishtp_fw_cl_get_client(ishtp_get_ishtp_device(loader_is
> > htp_cl),
> > +                                    &loader_ishtp_guid);
> > +     if (!fw_client) {
> > +             dev_err(cl_data_to_dev(client_data),
> > +                     "ISH client uuid not found\n");
> > +             rv = -ENOENT;
> > +             goto err_cl_unlink;
> > +     }
> > +
> > +     ishtp_cl_set_fw_client_id(loader_ishtp_cl,
> > +                               ishtp_get_fw_client_id(fw_client));
> > +     ishtp_set_connection_state(loader_ishtp_cl,
> > ISHTP_CL_CONNECTING);
> > +
> > +     rv = ishtp_cl_connect(loader_ishtp_cl);
> > +     if (rv < 0) {
> > +             dev_err(cl_data_to_dev(client_data), "Client connect
> > fail\n");
> > +             goto err_cl_unlink;
> > +     }
> > +
> > +     dev_dbg(cl_data_to_dev(client_data), "Client connected\n");
> > +
> > +     ishtp_register_event_cb(client_data->cl_device,
> > loader_cl_event_cb);
> > +
> > +     return 0;
> > +
> > +err_cl_unlink:
> > +     ishtp_cl_unlink(loader_ishtp_cl);
> > +     return rv;
> > +}
> > +
> > +static void loader_deinit(struct ishtp_cl *loader_ishtp_cl)
> > +{
> > +     ishtp_set_connection_state(loader_ishtp_cl,
> > ISHTP_CL_DISCONNECTING);
> > +     ishtp_cl_disconnect(loader_ishtp_cl);
> > +     ishtp_cl_unlink(loader_ishtp_cl);
> > +     ishtp_cl_flush_queues(loader_ishtp_cl);
> > +
> > +     /* Disband and free all Tx and Rx client-level rings */
> > +     ishtp_cl_free(loader_ishtp_cl);
> > +}
> > +
> > +static void reset_handler(struct work_struct *work)
> > +{
> > +     int rv;
> > +     struct ishtp_cl_data *client_data;
> > +     struct ishtp_cl *loader_ishtp_cl;
> > +     struct ishtp_cl_device *cl_device;
> > +
> > +     client_data = container_of(work, struct ishtp_cl_data,
> > +                                work_ishtp_reset);
> > +
> > +     loader_ishtp_cl = client_data->loader_ishtp_cl;
> > +     cl_device = client_data->cl_device;
> > +
> > +     /* Unlink, flush queues & start again */
> > +     ishtp_cl_unlink(loader_ishtp_cl);
> > +     ishtp_cl_flush_queues(loader_ishtp_cl);
> > +     ishtp_cl_free(loader_ishtp_cl);
> > +
> > +     loader_ishtp_cl = ishtp_cl_allocate(cl_device);
> > +     if (!loader_ishtp_cl)
> > +             return;
> > +
> > +     ishtp_set_drvdata(cl_device, loader_ishtp_cl);
> > +     ishtp_set_client_data(loader_ishtp_cl, client_data);
> > +     client_data->loader_ishtp_cl = loader_ishtp_cl;
> > +     client_data->cl_device = cl_device;
> > +
> > +     rv = loader_init(loader_ishtp_cl, 1);
> > +     if (rv < 0) {
> > +             dev_err(ishtp_device(cl_device), "Reset Failed\n");
> > +             return;
> > +     }
> > +
> > +     /* ISH firmware loading from host */
> > +     load_fw_from_host(client_data);
> > +}
> > +
> > +/**
> > + * loader_ishtp_cl_probe() - ISH-TP client driver probe
> > + * @cl_device:               ISH-TP client device instance
> > + *
> > + * This function gets called on device create on ISH-TP bus
> > + *
> > + * Return: 0 for success, negative error code for failure
> > + */
> > +static int loader_ishtp_cl_probe(struct ishtp_cl_device *cl_device)
> > +{
> > +     struct ishtp_cl *loader_ishtp_cl;
> > +     struct ishtp_cl_data *client_data;
> > +     int rv;
> > +
> > +     client_data = devm_kzalloc(ishtp_device(cl_device),
> > +                                sizeof(*client_data),
> > +                                GFP_KERNEL);
> > +     if (!client_data)
> > +             return -ENOMEM;
> > +
> > +     loader_ishtp_cl = ishtp_cl_allocate(cl_device);
> > +     if (!loader_ishtp_cl)
> > +             return -ENOMEM;
> > +
> > +     ishtp_set_drvdata(cl_device, loader_ishtp_cl);
> > +     ishtp_set_client_data(loader_ishtp_cl, client_data);
> > +     client_data->loader_ishtp_cl = loader_ishtp_cl;
> > +     client_data->cl_device = cl_device;
> > +
> > +     init_waitqueue_head(&client_data->cmd_resp_wait);
> > +
> > +     INIT_WORK(&client_data->work_ishtp_reset,
> > +               reset_handler);
> > +     INIT_WORK(&client_data->work_fw_load,
> > +               load_fw_from_host_handler);
> > +
> > +     rv = loader_init(loader_ishtp_cl, 0);
> > +     if (rv < 0) {
> > +             ishtp_cl_free(loader_ishtp_cl);
> > +             return rv;
> > +     }
> > +     ishtp_get_device(cl_device);
> > +
> > +     /* ISH firmware loading from host */
> > +     schedule_work(&client_data->work_fw_load);
> > +
> > +     return 0;
> > +}
> > +
> > +/**
> > + * loader_ishtp_cl_remove() - ISH-TP client driver remove
> > + * @cl_device:               ISH-TP client device instance
> > + *
> > + * This function gets called on device remove on ISH-TP bus
> > + *
> > + * Return: 0
> > + */
> > +static int loader_ishtp_cl_remove(struct ishtp_cl_device *cl_device)
> > +{
> > +     struct ishtp_cl_data *client_data;
> > +     struct ishtp_cl *loader_ishtp_cl =
> > ishtp_get_drvdata(cl_device);
> > +
> > +     client_data = ishtp_get_client_data(loader_ishtp_cl);
> > +
> > +     /*
> > +      * The sequence of the following two cancel_work_sync() is
> > +      * important. The work_fw_load can in turn schedue
> > +      * work_ishtp_reset, so first cancel work_fw_load then
> > +      * cancel work_ishtp_reset.
> > +      */
> > +     cancel_work_sync(&client_data->work_fw_load);
> > +     cancel_work_sync(&client_data->work_ishtp_reset);
> > +     loader_deinit(loader_ishtp_cl);
> > +     ishtp_put_device(cl_device);
> > +
> > +     return 0;
> > +}
> > +
> > +/**
> > + * loader_ishtp_cl_reset() - ISH-TP client driver reset
> > + * @cl_device:               ISH-TP client device instance
> > + *
> > + * This function gets called on device reset on ISH-TP bus
> > + *
> > + * Return: 0
> > + */
> > +static int loader_ishtp_cl_reset(struct ishtp_cl_device *cl_device)
> > +{
> > +     struct ishtp_cl_data *client_data;
> > +     struct ishtp_cl *loader_ishtp_cl =
> > ishtp_get_drvdata(cl_device);
> > +
> > +     client_data = ishtp_get_client_data(loader_ishtp_cl);
> > +
> > +     schedule_work(&client_data->work_ishtp_reset);
> > +
> > +     return 0;
> > +}
> > +
> > +static struct ishtp_cl_driver        loader_ishtp_cl_driver = {
> > +     .name = "ish-loader",
> > +     .guid = &loader_ishtp_guid,
> > +     .probe = loader_ishtp_cl_probe,
> > +     .remove = loader_ishtp_cl_remove,
> > +     .reset = loader_ishtp_cl_reset,
> > +};
> > +
> > +static int __init ish_loader_init(void)
> > +{
> > +     return ishtp_cl_driver_register(&loader_ishtp_cl_driver,
> > THIS_MODULE);
> > +}
> > +
> > +static void __exit ish_loader_exit(void)
> > +{
> > +     ishtp_cl_driver_unregister(&loader_ishtp_cl_driver);
> > +}
> > +
> > +late_initcall(ish_loader_init);
> > +module_exit(ish_loader_exit);
> > +
> > +module_param(dma_buf_size_limit, int, 0644);
> > +MODULE_PARM_DESC(dma_buf_size_limit, "Limit the DMA buf size to this
> > value in bytes");
> > +
> > +MODULE_DESCRIPTION("ISH ISH-TP Host firmware Loader Client Driver");
> > +MODULE_AUTHOR("Rushikesh S Kadam <rushikesh.s.kadam@intel.com>");
> > +
> > +MODULE_LICENSE("GPL v2");
> > +MODULE_ALIAS("ishtp:*");
>

^ permalink raw reply

* [PATCH v3] HID: core: move Usage Page concatenation to Main item
From: Nicolas Saenz Julienne @ 2019-03-26 20:03 UTC (permalink / raw)
  To: Jiri Kosina, Benjamin Tissoires
  Cc: oneukum, Terry.Junge, Nicolas Saenz Julienne, linux-input,
	linux-kernel

As seen on some USB wireless keyboards manufactured by Primax, the HID
parser was using some assumptions that are not always true. In this case
it's s the fact that, inside the scope of a main item, an Usage Page
will always precede an Usage.

The spec is not pretty clear as 6.2.2.7 states "Any usage that follows
is interpreted as a Usage ID and concatenated with the Usage Page".
While 6.2.2.8 states "When the parser encounters a main item it
concatenates the last declared Usage Page with a Usage to form a
complete usage value." Being somewhat contradictory it was decided to
match Window's implementation, which follows 6.2.2.8.

In summary, the patch moves the Usage Page concatenation from the local
item parsing function to the main item parsing function.

Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
---

v2->v3: - Update patch title

v1->v2: - Add usage concatenation to hid_scan_main()
	- Rework tests in hid-tools, making sure no-one is failing

 drivers/hid/hid-core.c | 40 ++++++++++++++++++++++++++++------------
 include/linux/hid.h    |  1 +
 2 files changed, 29 insertions(+), 12 deletions(-)

diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 9993b692598f..40c836ce3248 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -218,13 +218,14 @@ static unsigned hid_lookup_collection(struct hid_parser *parser, unsigned type)
  * Add a usage to the temporary parser table.
  */
 
-static int hid_add_usage(struct hid_parser *parser, unsigned usage)
+static int hid_add_usage(struct hid_parser *parser, unsigned usage, __u8 size)
 {
 	if (parser->local.usage_index >= HID_MAX_USAGES) {
 		hid_err(parser->device, "usage index exceeded\n");
 		return -1;
 	}
 	parser->local.usage[parser->local.usage_index] = usage;
+	parser->local.usage_size[parser->local.usage_index] = size;
 	parser->local.collection_index[parser->local.usage_index] =
 		parser->collection_stack_ptr ?
 		parser->collection_stack[parser->collection_stack_ptr - 1] : 0;
@@ -486,10 +487,7 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
 			return 0;
 		}
 
-		if (item->size <= 2)
-			data = (parser->global.usage_page << 16) + data;
-
-		return hid_add_usage(parser, data);
+		return hid_add_usage(parser, data, item->size);
 
 	case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM:
 
@@ -498,9 +496,6 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
 			return 0;
 		}
 
-		if (item->size <= 2)
-			data = (parser->global.usage_page << 16) + data;
-
 		parser->local.usage_minimum = data;
 		return 0;
 
@@ -511,9 +506,6 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
 			return 0;
 		}
 
-		if (item->size <= 2)
-			data = (parser->global.usage_page << 16) + data;
-
 		count = data - parser->local.usage_minimum;
 		if (count + parser->local.usage_index >= HID_MAX_USAGES) {
 			/*
@@ -533,7 +525,7 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
 		}
 
 		for (n = parser->local.usage_minimum; n <= data; n++)
-			if (hid_add_usage(parser, n)) {
+			if (hid_add_usage(parser, n, item->size)) {
 				dbg_hid("hid_add_usage failed\n");
 				return -1;
 			}
@@ -547,6 +539,26 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
 	return 0;
 }
 
+/*
+ * Concatenate Usage Pages into Usages where relevant:
+ * As per specification, 6.2.2.8: "When the parser encounters a main item it
+ * concatenates the last declared Usage Page with a Usage to form a complete
+ * usage value."
+ */
+
+static void hid_concatenate_usage_page(struct hid_parser *parser)
+{
+	unsigned usages;
+	int i;
+
+	usages = max_t(unsigned, parser->local.usage_index,
+				 parser->global.report_count);
+
+	for (i = 0; i < usages; i++)
+		if (parser->local.usage_size[i] <= 2)
+			parser->local.usage[i] += parser->global.usage_page << 16;
+}
+
 /*
  * Process a main item.
  */
@@ -556,6 +568,8 @@ static int hid_parser_main(struct hid_parser *parser, struct hid_item *item)
 	__u32 data;
 	int ret;
 
+	hid_concatenate_usage_page(parser);
+
 	data = item_udata(item);
 
 	switch (item->tag) {
@@ -765,6 +779,8 @@ static int hid_scan_main(struct hid_parser *parser, struct hid_item *item)
 	__u32 data;
 	int i;
 
+	hid_concatenate_usage_page(parser);
+
 	data = item_udata(item);
 
 	switch (item->tag) {
diff --git a/include/linux/hid.h b/include/linux/hid.h
index f9707d1dcb58..d1fb4b678873 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -417,6 +417,7 @@ struct hid_global {
 
 struct hid_local {
 	unsigned usage[HID_MAX_USAGES]; /* usage array */
+	__u8 usage_size[HID_MAX_USAGES]; /* usage size array */
 	unsigned collection_index[HID_MAX_USAGES]; /* collection index array */
 	unsigned usage_index;
 	unsigned usage_minimum;
-- 
2.21.0

^ permalink raw reply related

* [PATCH v2] HID: core: move Usage Page concatenation to hid_parser_main()
From: Nicolas Saenz Julienne @ 2019-03-26 19:59 UTC (permalink / raw)
  To: Jiri Kosina, Benjamin Tissoires
  Cc: oneukum, Terry.Junge, Nicolas Saenz Julienne, linux-input,
	linux-kernel

As seen on some USB wireless keyboards manufactured by Primax, the HID
parser was using some assumptions that are not always true. In this case
it's s the fact that, inside the scope of a main item, an Usage Page
will always precede an Usage.

The spec is not pretty clear as 6.2.2.7 states "Any usage that follows
is interpreted as a Usage ID and concatenated with the Usage Page".
While 6.2.2.8 states "When the parser encounters a main item it
concatenates the last declared Usage Page with a Usage to form a
complete usage value." Being somewhat contradictory it was decided to
match Window's implementation, which follows 6.2.2.8.

In summary, the patch moves the Usage Page concatenation from the local
item parsing function to the main item parsing function.

Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
---

v1->v2: - Add usage concatenation to hid_scan_main()
	- Rework tests in hid-tools, making sure no-one is failing

 drivers/hid/hid-core.c | 40 ++++++++++++++++++++++++++++------------
 include/linux/hid.h    |  1 +
 2 files changed, 29 insertions(+), 12 deletions(-)

diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 9993b692598f..40c836ce3248 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -218,13 +218,14 @@ static unsigned hid_lookup_collection(struct hid_parser *parser, unsigned type)
  * Add a usage to the temporary parser table.
  */
 
-static int hid_add_usage(struct hid_parser *parser, unsigned usage)
+static int hid_add_usage(struct hid_parser *parser, unsigned usage, __u8 size)
 {
 	if (parser->local.usage_index >= HID_MAX_USAGES) {
 		hid_err(parser->device, "usage index exceeded\n");
 		return -1;
 	}
 	parser->local.usage[parser->local.usage_index] = usage;
+	parser->local.usage_size[parser->local.usage_index] = size;
 	parser->local.collection_index[parser->local.usage_index] =
 		parser->collection_stack_ptr ?
 		parser->collection_stack[parser->collection_stack_ptr - 1] : 0;
@@ -486,10 +487,7 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
 			return 0;
 		}
 
-		if (item->size <= 2)
-			data = (parser->global.usage_page << 16) + data;
-
-		return hid_add_usage(parser, data);
+		return hid_add_usage(parser, data, item->size);
 
 	case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM:
 
@@ -498,9 +496,6 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
 			return 0;
 		}
 
-		if (item->size <= 2)
-			data = (parser->global.usage_page << 16) + data;
-
 		parser->local.usage_minimum = data;
 		return 0;
 
@@ -511,9 +506,6 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
 			return 0;
 		}
 
-		if (item->size <= 2)
-			data = (parser->global.usage_page << 16) + data;
-
 		count = data - parser->local.usage_minimum;
 		if (count + parser->local.usage_index >= HID_MAX_USAGES) {
 			/*
@@ -533,7 +525,7 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
 		}
 
 		for (n = parser->local.usage_minimum; n <= data; n++)
-			if (hid_add_usage(parser, n)) {
+			if (hid_add_usage(parser, n, item->size)) {
 				dbg_hid("hid_add_usage failed\n");
 				return -1;
 			}
@@ -547,6 +539,26 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
 	return 0;
 }
 
+/*
+ * Concatenate Usage Pages into Usages where relevant:
+ * As per specification, 6.2.2.8: "When the parser encounters a main item it
+ * concatenates the last declared Usage Page with a Usage to form a complete
+ * usage value."
+ */
+
+static void hid_concatenate_usage_page(struct hid_parser *parser)
+{
+	unsigned usages;
+	int i;
+
+	usages = max_t(unsigned, parser->local.usage_index,
+				 parser->global.report_count);
+
+	for (i = 0; i < usages; i++)
+		if (parser->local.usage_size[i] <= 2)
+			parser->local.usage[i] += parser->global.usage_page << 16;
+}
+
 /*
  * Process a main item.
  */
@@ -556,6 +568,8 @@ static int hid_parser_main(struct hid_parser *parser, struct hid_item *item)
 	__u32 data;
 	int ret;
 
+	hid_concatenate_usage_page(parser);
+
 	data = item_udata(item);
 
 	switch (item->tag) {
@@ -765,6 +779,8 @@ static int hid_scan_main(struct hid_parser *parser, struct hid_item *item)
 	__u32 data;
 	int i;
 
+	hid_concatenate_usage_page(parser);
+
 	data = item_udata(item);
 
 	switch (item->tag) {
diff --git a/include/linux/hid.h b/include/linux/hid.h
index f9707d1dcb58..d1fb4b678873 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -417,6 +417,7 @@ struct hid_global {
 
 struct hid_local {
 	unsigned usage[HID_MAX_USAGES]; /* usage array */
+	__u8 usage_size[HID_MAX_USAGES]; /* usage size array */
 	unsigned collection_index[HID_MAX_USAGES]; /* collection index array */
 	unsigned usage_index;
 	unsigned usage_minimum;
-- 
2.21.0

^ permalink raw reply related

* Re: [RESEND PATCH v6 02/11] dt-bindings: power: supply: add DT bindings for max77650
From: Bartosz Golaszewski @ 2019-03-26 17:34 UTC (permalink / raw)
  To: Pavel Machek
  Cc: Rob Herring, Mark Rutland, Linus Walleij, Dmitry Torokhov,
	Jacek Anaszewski, Lee Jones, Sebastian Reichel, Liam Girdwood,
	Greg Kroah-Hartman, Linux Kernel Mailing List,
	open list:GPIO SUBSYSTEM, devicetree, Linux Input,
	Linux LED Subsystem, Linux PM list, Bartosz Golaszewski
In-Reply-To: <20190322090038.GE27015@amd>

pt., 22 mar 2019 o 10:00 Pavel Machek <pavel@ucw.cz> napisał(a):
>
> On Mon 2019-03-18 18:40:31, Bartosz Golaszewski wrote:
> > From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
> >
> > Add the DT binding document for the battery charger module of max77650.
> >
> > Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
> > ---
> >  .../power/supply/max77650-charger.txt         | 27 +++++++++++++++++++
> >  1 file changed, 27 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/power/supply/max77650-charger.txt
> >
> > diff --git a/Documentation/devicetree/bindings/power/supply/max77650-charger.txt b/Documentation/devicetree/bindings/power/supply/max77650-charger.txt
> > new file mode 100644
> > index 000000000000..d25c95369616
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/power/supply/max77650-charger.txt
> > @@ -0,0 +1,27 @@
> > +Battery charger driver for MAX77650 PMIC from Maxim Integrated.
> > +
> > +This module is part of the MAX77650 MFD device. For more details
> > +see Documentation/devicetree/bindings/mfd/max77650.txt.
> > +
> > +The charger is represented as a sub-node of the PMIC node on the device tree.
> > +
> > +Required properties:
> > +--------------------
> > +- compatible:                Must be "maxim,max77650-charger"
> > +
> > +Optional properties:
> > +--------------------
> > +- min-microvolt:     Minimum CHGIN regulation voltage (in microvolts). Must be
> > +                     one of: 4000000, 4100000, 4200000, 4300000, 4400000,
> > +                     4500000, 4600000, 4700000.
>
> Probably needs "max," prefix. And .. what does this mean? Will charger
> shutdown if input is less than this?
>

The charger will enter the undervoltage lockout state and stop
charging, this is explained in the manual, so I don't think the
bindings are the right place to add this information.

Bart

> > +- curr-lim-microamp: CHGIN input current limit (in microamps). Must be one of:
> > +                     95000, 190000, 285000, 380000, 475000.
>
> "current-limit-microamp", I guess. And probably "max,current-limit-microamp".
>
> --
> (english) http://www.livejournal.com/~pavelmachek
> (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply

* [PATCH v7 11/11] MAINTAINERS: add an entry for max77650 mfd driver
From: Bartosz Golaszewski @ 2019-03-26 17:32 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Linus Walleij, Dmitry Torokhov,
	Jacek Anaszewski, Pavel Machek, Lee Jones, Sebastian Reichel,
	Liam Girdwood, Greg Kroah-Hartman
  Cc: linux-kernel, linux-gpio, devicetree, linux-input, linux-leds,
	linux-pm, Bartosz Golaszewski
In-Reply-To: <20190326173208.30614-1-brgl@bgdev.pl>

From: Bartosz Golaszewski <bgolaszewski@baylibre.com>

I plan on extending this set of drivers so add myself as maintainer.

Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
 MAINTAINERS | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 3e5a5d263f29..b32fe859c341 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9407,6 +9407,20 @@ S:	Maintained
 F:	Documentation/devicetree/bindings/sound/max9860.txt
 F:	sound/soc/codecs/max9860.*
 
+MAXIM MAX77650 PMIC MFD DRIVER
+M:	Bartosz Golaszewski <bgolaszewski@baylibre.com>
+L:	linux-kernel@vger.kernel.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/*/*max77650.txt
+F:	Documentation/devicetree/bindings/*/max77650*.txt
+F:	include/linux/mfd/max77650.h
+F:	drivers/mfd/max77650.c
+F:	drivers/regulator/max77650-regulator.c
+F:	drivers/power/supply/max77650-charger.c
+F:	drivers/input/misc/max77650-onkey.c
+F:	drivers/leds/leds-max77650.c
+F:	drivers/gpio/gpio-max77650.c
+
 MAXIM MAX77802 PMIC REGULATOR DEVICE DRIVER
 M:	Javier Martinez Canillas <javier@dowhile0.org>
 L:	linux-kernel@vger.kernel.org
-- 
2.20.1

^ permalink raw reply related

* [PATCH v7 10/11] input: max77650: add onkey support
From: Bartosz Golaszewski @ 2019-03-26 17:32 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Linus Walleij, Dmitry Torokhov,
	Jacek Anaszewski, Pavel Machek, Lee Jones, Sebastian Reichel,
	Liam Girdwood, Greg Kroah-Hartman
  Cc: linux-kernel, linux-gpio, devicetree, linux-input, linux-leds,
	linux-pm, Bartosz Golaszewski
In-Reply-To: <20190326173208.30614-1-brgl@bgdev.pl>

From: Bartosz Golaszewski <bgolaszewski@baylibre.com>

Add support for the push- and slide-button events for max77650.

Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Acked-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Acked-by: Pavel Machek <pavel@ucw.cz>
---
 drivers/input/misc/Kconfig          |   9 +++
 drivers/input/misc/Makefile         |   1 +
 drivers/input/misc/max77650-onkey.c | 121 ++++++++++++++++++++++++++++
 3 files changed, 131 insertions(+)
 create mode 100644 drivers/input/misc/max77650-onkey.c

diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index e15ed1bb8558..85bc675eecd3 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -190,6 +190,15 @@ config INPUT_M68K_BEEP
 	tristate "M68k Beeper support"
 	depends on M68K
 
+config INPUT_MAX77650_ONKEY
+	tristate "Maxim MAX77650 ONKEY support"
+	depends on MFD_MAX77650
+	help
+	  Support the ONKEY of the MAX77650 PMIC as an input device.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called max77650-onkey.
+
 config INPUT_MAX77693_HAPTIC
 	tristate "MAXIM MAX77693/MAX77843 haptic controller support"
 	depends on (MFD_MAX77693 || MFD_MAX77843) && PWM
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index b936c5b1d4ac..ffd72161c79b 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -43,6 +43,7 @@ obj-$(CONFIG_INPUT_IXP4XX_BEEPER)	+= ixp4xx-beeper.o
 obj-$(CONFIG_INPUT_KEYSPAN_REMOTE)	+= keyspan_remote.o
 obj-$(CONFIG_INPUT_KXTJ9)		+= kxtj9.o
 obj-$(CONFIG_INPUT_M68K_BEEP)		+= m68kspkr.o
+obj-$(CONFIG_INPUT_MAX77650_ONKEY)	+= max77650-onkey.o
 obj-$(CONFIG_INPUT_MAX77693_HAPTIC)	+= max77693-haptic.o
 obj-$(CONFIG_INPUT_MAX8925_ONKEY)	+= max8925_onkey.o
 obj-$(CONFIG_INPUT_MAX8997_HAPTIC)	+= max8997_haptic.o
diff --git a/drivers/input/misc/max77650-onkey.c b/drivers/input/misc/max77650-onkey.c
new file mode 100644
index 000000000000..fbf6caab7217
--- /dev/null
+++ b/drivers/input/misc/max77650-onkey.c
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (C) 2018 BayLibre SAS
+// Author: Bartosz Golaszewski <bgolaszewski@baylibre.com>
+//
+// ONKEY driver for MAXIM 77650/77651 charger/power-supply.
+
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/max77650.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define MAX77650_ONKEY_MODE_MASK	BIT(3)
+#define MAX77650_ONKEY_MODE_PUSH	0x00
+#define MAX77650_ONKEY_MODE_SLIDE	BIT(3)
+
+struct max77650_onkey {
+	struct input_dev *input;
+	unsigned int code;
+};
+
+static irqreturn_t max77650_onkey_falling(int irq, void *data)
+{
+	struct max77650_onkey *onkey = data;
+
+	input_report_key(onkey->input, onkey->code, 0);
+	input_sync(onkey->input);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t max77650_onkey_rising(int irq, void *data)
+{
+	struct max77650_onkey *onkey = data;
+
+	input_report_key(onkey->input, onkey->code, 1);
+	input_sync(onkey->input);
+
+	return IRQ_HANDLED;
+}
+
+static int max77650_onkey_probe(struct platform_device *pdev)
+{
+	int irq_r, irq_f, error, mode;
+	struct max77650_onkey *onkey;
+	struct device *dev, *parent;
+	struct regmap *map;
+	unsigned int type;
+
+	dev = &pdev->dev;
+	parent = dev->parent;
+
+	map = dev_get_regmap(parent, NULL);
+	if (!map)
+		return -ENODEV;
+
+	onkey = devm_kzalloc(dev, sizeof(*onkey), GFP_KERNEL);
+	if (!onkey)
+		return -ENOMEM;
+
+	error = device_property_read_u32(dev, "linux,code", &onkey->code);
+	if (error)
+		onkey->code = KEY_POWER;
+
+	if (device_property_read_bool(dev, "maxim,onkey-slide")) {
+		mode = MAX77650_ONKEY_MODE_SLIDE;
+		type = EV_SW;
+	} else {
+		mode = MAX77650_ONKEY_MODE_PUSH;
+		type = EV_KEY;
+	}
+
+	error = regmap_update_bits(map, MAX77650_REG_CNFG_GLBL,
+				   MAX77650_ONKEY_MODE_MASK, mode);
+	if (error)
+		return error;
+
+	irq_f = platform_get_irq_byname(pdev, "nEN_F");
+	if (irq_f < 0)
+		return irq_f;
+
+	irq_r = platform_get_irq_byname(pdev, "nEN_R");
+	if (irq_r < 0)
+		return irq_r;
+
+	onkey->input = devm_input_allocate_device(dev);
+	if (!onkey->input)
+		return -ENOMEM;
+
+	onkey->input->name = "max77650_onkey";
+	onkey->input->phys = "max77650_onkey/input0";
+	onkey->input->id.bustype = BUS_I2C;
+	input_set_capability(onkey->input, type, onkey->code);
+
+	error = devm_request_any_context_irq(dev, irq_f, max77650_onkey_falling,
+					     IRQF_ONESHOT, "onkey-down", onkey);
+	if (error < 0)
+		return error;
+
+	error = devm_request_any_context_irq(dev, irq_r, max77650_onkey_rising,
+					     IRQF_ONESHOT, "onkey-up", onkey);
+	if (error < 0)
+		return error;
+
+	return input_register_device(onkey->input);
+}
+
+static struct platform_driver max77650_onkey_driver = {
+	.driver = {
+		.name = "max77650-onkey",
+	},
+	.probe = max77650_onkey_probe,
+};
+module_platform_driver(max77650_onkey_driver);
+
+MODULE_DESCRIPTION("MAXIM 77650/77651 ONKEY driver");
+MODULE_AUTHOR("Bartosz Golaszewski <bgolaszewski@baylibre.com>");
+MODULE_LICENSE("GPL v2");
-- 
2.20.1

^ permalink raw reply related

* [PATCH v7 09/11] leds: max77650: add LEDs support
From: Bartosz Golaszewski @ 2019-03-26 17:32 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Linus Walleij, Dmitry Torokhov,
	Jacek Anaszewski, Pavel Machek, Lee Jones, Sebastian Reichel,
	Liam Girdwood, Greg Kroah-Hartman
  Cc: linux-kernel, linux-gpio, devicetree, linux-input, linux-leds,
	linux-pm, Bartosz Golaszewski
In-Reply-To: <20190326173208.30614-1-brgl@bgdev.pl>

From: Bartosz Golaszewski <bgolaszewski@baylibre.com>

This adds basic support for LEDs for the max77650 PMIC. The device has
three current sinks for driving LEDs.

Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Acked-by: Jacek Anaszewski <jacek.anaszewski@gmail.com>
---
 drivers/leds/Kconfig         |   6 ++
 drivers/leds/Makefile        |   1 +
 drivers/leds/leds-max77650.c | 147 +++++++++++++++++++++++++++++++++++
 3 files changed, 154 insertions(+)
 create mode 100644 drivers/leds/leds-max77650.c

diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index a72f97fca57b..d8c70cc6a714 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -608,6 +608,12 @@ config LEDS_TLC591XX
 	  This option enables support for Texas Instruments TLC59108
 	  and TLC59116 LED controllers.
 
+config LEDS_MAX77650
+	tristate "LED support for Maxim MAX77650 PMIC"
+	depends on LEDS_CLASS && MFD_MAX77650
+	help
+	  LEDs driver for MAX77650 family of PMICs from Maxim Integrated.
+
 config LEDS_MAX77693
 	tristate "LED support for MAX77693 Flash"
 	depends on LEDS_CLASS_FLASH
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 4c1b0054f379..f48b2404dbb7 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -61,6 +61,7 @@ obj-$(CONFIG_LEDS_MC13783)		+= leds-mc13783.o
 obj-$(CONFIG_LEDS_NS2)			+= leds-ns2.o
 obj-$(CONFIG_LEDS_NETXBIG)		+= leds-netxbig.o
 obj-$(CONFIG_LEDS_ASIC3)		+= leds-asic3.o
+obj-$(CONFIG_LEDS_MAX77650)		+= leds-max77650.o
 obj-$(CONFIG_LEDS_MAX77693)		+= leds-max77693.o
 obj-$(CONFIG_LEDS_MAX8997)		+= leds-max8997.o
 obj-$(CONFIG_LEDS_LM355x)		+= leds-lm355x.o
diff --git a/drivers/leds/leds-max77650.c b/drivers/leds/leds-max77650.c
new file mode 100644
index 000000000000..6b74ce9cac12
--- /dev/null
+++ b/drivers/leds/leds-max77650.c
@@ -0,0 +1,147 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (C) 2018 BayLibre SAS
+// Author: Bartosz Golaszewski <bgolaszewski@baylibre.com>
+//
+// LED driver for MAXIM 77650/77651 charger/power-supply.
+
+#include <linux/i2c.h>
+#include <linux/leds.h>
+#include <linux/mfd/max77650.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define MAX77650_LED_NUM_LEDS		3
+
+#define MAX77650_LED_A_BASE		0x40
+#define MAX77650_LED_B_BASE		0x43
+
+#define MAX77650_LED_BR_MASK		GENMASK(4, 0)
+#define MAX77650_LED_EN_MASK		GENMASK(7, 6)
+
+#define MAX77650_LED_MAX_BRIGHTNESS	MAX77650_LED_BR_MASK
+
+/* Enable EN_LED_MSTR. */
+#define MAX77650_LED_TOP_DEFAULT	BIT(0)
+
+#define MAX77650_LED_ENABLE		GENMASK(7, 6)
+#define MAX77650_LED_DISABLE		0x00
+
+#define MAX77650_LED_A_DEFAULT		MAX77650_LED_DISABLE
+/* 100% on duty */
+#define MAX77650_LED_B_DEFAULT		GENMASK(3, 0)
+
+struct max77650_led {
+	struct led_classdev cdev;
+	struct regmap *map;
+	unsigned int regA;
+	unsigned int regB;
+};
+
+static struct max77650_led *max77650_to_led(struct led_classdev *cdev)
+{
+	return container_of(cdev, struct max77650_led, cdev);
+}
+
+static int max77650_led_brightness_set(struct led_classdev *cdev,
+				       enum led_brightness brightness)
+{
+	struct max77650_led *led = max77650_to_led(cdev);
+	int val, mask;
+
+	mask = MAX77650_LED_BR_MASK | MAX77650_LED_EN_MASK;
+
+	if (brightness == LED_OFF)
+		val = MAX77650_LED_DISABLE;
+	else
+		val = MAX77650_LED_ENABLE | brightness;
+
+	return regmap_update_bits(led->map, led->regA, mask, val);
+}
+
+static int max77650_led_probe(struct platform_device *pdev)
+{
+	struct device_node *of_node, *child;
+	struct max77650_led *leds, *led;
+	struct device *parent;
+	struct device *dev;
+	struct regmap *map;
+	const char *label;
+	int rv, num_leds;
+	u32 reg;
+
+	dev = &pdev->dev;
+	parent = dev->parent;
+	of_node = dev->of_node;
+
+	if (!of_node)
+		return -ENODEV;
+
+	leds = devm_kcalloc(dev, sizeof(*leds),
+			    MAX77650_LED_NUM_LEDS, GFP_KERNEL);
+	if (!leds)
+		return -ENOMEM;
+
+	map = dev_get_regmap(dev->parent, NULL);
+	if (!map)
+		return -ENODEV;
+
+	num_leds = of_get_child_count(of_node);
+	if (!num_leds || num_leds > MAX77650_LED_NUM_LEDS)
+		return -ENODEV;
+
+	for_each_child_of_node(of_node, child) {
+		rv = of_property_read_u32(child, "reg", &reg);
+		if (rv || reg >= MAX77650_LED_NUM_LEDS)
+			return -EINVAL;
+
+		led = &leds[reg];
+		led->map = map;
+		led->regA = MAX77650_LED_A_BASE + reg;
+		led->regB = MAX77650_LED_B_BASE + reg;
+		led->cdev.brightness_set_blocking = max77650_led_brightness_set;
+		led->cdev.max_brightness = MAX77650_LED_MAX_BRIGHTNESS;
+
+		label = of_get_property(child, "label", NULL);
+		if (!label) {
+			led->cdev.name = "max77650::";
+		} else {
+			led->cdev.name = devm_kasprintf(dev, GFP_KERNEL,
+							"max77650:%s", label);
+			if (!led->cdev.name)
+				return -ENOMEM;
+		}
+
+		of_property_read_string(child, "linux,default-trigger",
+					&led->cdev.default_trigger);
+
+		rv = devm_of_led_classdev_register(dev, child, &led->cdev);
+		if (rv)
+			return rv;
+
+		rv = regmap_write(map, led->regA, MAX77650_LED_A_DEFAULT);
+		if (rv)
+			return rv;
+
+		rv = regmap_write(map, led->regB, MAX77650_LED_B_DEFAULT);
+		if (rv)
+			return rv;
+	}
+
+	return regmap_write(map,
+			    MAX77650_REG_CNFG_LED_TOP,
+			    MAX77650_LED_TOP_DEFAULT);
+}
+
+static struct platform_driver max77650_led_driver = {
+	.driver = {
+		.name = "max77650-led",
+	},
+	.probe = max77650_led_probe,
+};
+module_platform_driver(max77650_led_driver);
+
+MODULE_DESCRIPTION("MAXIM 77650/77651 LED driver");
+MODULE_AUTHOR("Bartosz Golaszewski <bgolaszewski@baylibre.com>");
+MODULE_LICENSE("GPL v2");
-- 
2.20.1

^ permalink raw reply related

* [PATCH v7 08/11] gpio: max77650: add GPIO support
From: Bartosz Golaszewski @ 2019-03-26 17:32 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Linus Walleij, Dmitry Torokhov,
	Jacek Anaszewski, Pavel Machek, Lee Jones, Sebastian Reichel,
	Liam Girdwood, Greg Kroah-Hartman
  Cc: linux-kernel, linux-gpio, devicetree, linux-input, linux-leds,
	linux-pm, Bartosz Golaszewski
In-Reply-To: <20190326173208.30614-1-brgl@bgdev.pl>

From: Bartosz Golaszewski <bgolaszewski@baylibre.com>

Add GPIO support for max77650 mfd device. This PMIC exposes a single
GPIO line.

Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
---
 drivers/gpio/Kconfig         |   7 ++
 drivers/gpio/Makefile        |   1 +
 drivers/gpio/gpio-max77650.c | 190 +++++++++++++++++++++++++++++++++++
 3 files changed, 198 insertions(+)
 create mode 100644 drivers/gpio/gpio-max77650.c

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 3f50526a771f..c4f912104440 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -1112,6 +1112,13 @@ config GPIO_MAX77620
 	  driver also provides interrupt support for each of the gpios.
 	  Say yes here to enable the max77620 to be used as gpio controller.
 
+config GPIO_MAX77650
+	tristate "Maxim MAX77650/77651 GPIO support"
+	depends on MFD_MAX77650
+	help
+	  GPIO driver for MAX77650/77651 PMIC from Maxim Semiconductor.
+	  These chips have a single pin that can be configured as GPIO.
+
 config GPIO_MSIC
 	bool "Intel MSIC mixed signal gpio support"
 	depends on (X86 || COMPILE_TEST) && MFD_INTEL_MSIC
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 54d55274b93a..075722d8317d 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -80,6 +80,7 @@ obj-$(CONFIG_GPIO_MAX7300)	+= gpio-max7300.o
 obj-$(CONFIG_GPIO_MAX7301)	+= gpio-max7301.o
 obj-$(CONFIG_GPIO_MAX732X)	+= gpio-max732x.o
 obj-$(CONFIG_GPIO_MAX77620)	+= gpio-max77620.o
+obj-$(CONFIG_GPIO_MAX77650)	+= gpio-max77650.o
 obj-$(CONFIG_GPIO_MB86S7X)	+= gpio-mb86s7x.o
 obj-$(CONFIG_GPIO_MENZ127)	+= gpio-menz127.o
 obj-$(CONFIG_GPIO_MERRIFIELD)	+= gpio-merrifield.o
diff --git a/drivers/gpio/gpio-max77650.c b/drivers/gpio/gpio-max77650.c
new file mode 100644
index 000000000000..3f03f4e8956c
--- /dev/null
+++ b/drivers/gpio/gpio-max77650.c
@@ -0,0 +1,190 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (C) 2018 BayLibre SAS
+// Author: Bartosz Golaszewski <bgolaszewski@baylibre.com>
+//
+// GPIO driver for MAXIM 77650/77651 charger/power-supply.
+
+#include <linux/gpio/driver.h>
+#include <linux/i2c.h>
+#include <linux/mfd/max77650.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define MAX77650_GPIO_DIR_MASK		BIT(0)
+#define MAX77650_GPIO_INVAL_MASK	BIT(1)
+#define MAX77650_GPIO_DRV_MASK		BIT(2)
+#define MAX77650_GPIO_OUTVAL_MASK	BIT(3)
+#define MAX77650_GPIO_DEBOUNCE_MASK	BIT(4)
+
+#define MAX77650_GPIO_DIR_OUT		0x00
+#define MAX77650_GPIO_DIR_IN		BIT(0)
+#define MAX77650_GPIO_OUT_LOW		0x00
+#define MAX77650_GPIO_OUT_HIGH		BIT(3)
+#define MAX77650_GPIO_DRV_OPEN_DRAIN	0x00
+#define MAX77650_GPIO_DRV_PUSH_PULL	BIT(2)
+#define MAX77650_GPIO_DEBOUNCE		BIT(4)
+
+#define MAX77650_GPIO_DIR_BITS(_reg) \
+		((_reg) & MAX77650_GPIO_DIR_MASK)
+#define MAX77650_GPIO_INVAL_BITS(_reg) \
+		(((_reg) & MAX77650_GPIO_INVAL_MASK) >> 1)
+
+struct max77650_gpio_chip {
+	struct regmap *map;
+	struct gpio_chip gc;
+	int irq;
+};
+
+static int max77650_gpio_direction_input(struct gpio_chip *gc,
+					 unsigned int offset)
+{
+	struct max77650_gpio_chip *chip = gpiochip_get_data(gc);
+
+	return regmap_update_bits(chip->map,
+				  MAX77650_REG_CNFG_GPIO,
+				  MAX77650_GPIO_DIR_MASK,
+				  MAX77650_GPIO_DIR_IN);
+}
+
+static int max77650_gpio_direction_output(struct gpio_chip *gc,
+					  unsigned int offset, int value)
+{
+	struct max77650_gpio_chip *chip = gpiochip_get_data(gc);
+	int mask, regval;
+
+	mask = MAX77650_GPIO_DIR_MASK | MAX77650_GPIO_OUTVAL_MASK;
+	regval = value ? MAX77650_GPIO_OUT_HIGH : MAX77650_GPIO_OUT_LOW;
+	regval |= MAX77650_GPIO_DIR_OUT;
+
+	return regmap_update_bits(chip->map,
+				  MAX77650_REG_CNFG_GPIO, mask, regval);
+}
+
+static void max77650_gpio_set_value(struct gpio_chip *gc,
+				    unsigned int offset, int value)
+{
+	struct max77650_gpio_chip *chip = gpiochip_get_data(gc);
+	int rv, regval;
+
+	regval = value ? MAX77650_GPIO_OUT_HIGH : MAX77650_GPIO_OUT_LOW;
+
+	rv = regmap_update_bits(chip->map, MAX77650_REG_CNFG_GPIO,
+				MAX77650_GPIO_OUTVAL_MASK, regval);
+	if (rv)
+		dev_err(gc->parent, "cannot set GPIO value: %d\n", rv);
+}
+
+static int max77650_gpio_get_value(struct gpio_chip *gc,
+				   unsigned int offset)
+{
+	struct max77650_gpio_chip *chip = gpiochip_get_data(gc);
+	unsigned int val;
+	int rv;
+
+	rv = regmap_read(chip->map, MAX77650_REG_CNFG_GPIO, &val);
+	if (rv)
+		return rv;
+
+	return MAX77650_GPIO_INVAL_BITS(val);
+}
+
+static int max77650_gpio_get_direction(struct gpio_chip *gc,
+				       unsigned int offset)
+{
+	struct max77650_gpio_chip *chip = gpiochip_get_data(gc);
+	unsigned int val;
+	int rv;
+
+	rv = regmap_read(chip->map, MAX77650_REG_CNFG_GPIO, &val);
+	if (rv)
+		return rv;
+
+	return MAX77650_GPIO_DIR_BITS(val);
+}
+
+static int max77650_gpio_set_config(struct gpio_chip *gc,
+				    unsigned int offset, unsigned long cfg)
+{
+	struct max77650_gpio_chip *chip = gpiochip_get_data(gc);
+
+	switch (pinconf_to_config_param(cfg)) {
+	case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+		return regmap_update_bits(chip->map,
+					  MAX77650_REG_CNFG_GPIO,
+					  MAX77650_GPIO_DRV_MASK,
+					  MAX77650_GPIO_DRV_OPEN_DRAIN);
+	case PIN_CONFIG_DRIVE_PUSH_PULL:
+		return regmap_update_bits(chip->map,
+					  MAX77650_REG_CNFG_GPIO,
+					  MAX77650_GPIO_DRV_MASK,
+					  MAX77650_GPIO_DRV_PUSH_PULL);
+	case PIN_CONFIG_INPUT_DEBOUNCE:
+		return regmap_update_bits(chip->map,
+					  MAX77650_REG_CNFG_GPIO,
+					  MAX77650_GPIO_DEBOUNCE_MASK,
+					  MAX77650_GPIO_DEBOUNCE);
+	default:
+		return -ENOTSUPP;
+	}
+}
+
+static int max77650_gpio_to_irq(struct gpio_chip *gc, unsigned int offset)
+{
+	struct max77650_gpio_chip *chip = gpiochip_get_data(gc);
+
+	return chip->irq;
+}
+
+static int max77650_gpio_probe(struct platform_device *pdev)
+{
+	struct max77650_gpio_chip *chip;
+	struct device *dev, *parent;
+	struct i2c_client *i2c;
+
+	dev = &pdev->dev;
+	parent = dev->parent;
+	i2c = to_i2c_client(parent);
+
+	chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
+	if (!chip)
+		return -ENOMEM;
+
+	chip->map = dev_get_regmap(parent, NULL);
+	if (!chip->map)
+		return -ENODEV;
+
+	chip->irq = platform_get_irq_byname(pdev, "GPI");
+	if (chip->irq < 0)
+		return chip->irq;
+
+	chip->gc.base = -1;
+	chip->gc.ngpio = 1;
+	chip->gc.label = i2c->name;
+	chip->gc.parent = dev;
+	chip->gc.owner = THIS_MODULE;
+	chip->gc.can_sleep = true;
+
+	chip->gc.direction_input = max77650_gpio_direction_input;
+	chip->gc.direction_output = max77650_gpio_direction_output;
+	chip->gc.set = max77650_gpio_set_value;
+	chip->gc.get = max77650_gpio_get_value;
+	chip->gc.get_direction = max77650_gpio_get_direction;
+	chip->gc.set_config = max77650_gpio_set_config;
+	chip->gc.to_irq = max77650_gpio_to_irq;
+
+	return devm_gpiochip_add_data(dev, &chip->gc, chip);
+}
+
+static struct platform_driver max77650_gpio_driver = {
+	.driver = {
+		.name = "max77650-gpio",
+	},
+	.probe = max77650_gpio_probe,
+};
+module_platform_driver(max77650_gpio_driver);
+
+MODULE_DESCRIPTION("MAXIM 77650/77651 GPIO driver");
+MODULE_AUTHOR("Bartosz Golaszewski <bgolaszewski@baylibre.com>");
+MODULE_LICENSE("GPL v2");
-- 
2.20.1

^ permalink raw reply related

* [PATCH v7 07/11] power: supply: max77650: add support for battery charger
From: Bartosz Golaszewski @ 2019-03-26 17:32 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Linus Walleij, Dmitry Torokhov,
	Jacek Anaszewski, Pavel Machek, Lee Jones, Sebastian Reichel,
	Liam Girdwood, Greg Kroah-Hartman
  Cc: linux-kernel, linux-gpio, devicetree, linux-input, linux-leds,
	linux-pm, Bartosz Golaszewski
In-Reply-To: <20190326173208.30614-1-brgl@bgdev.pl>

From: Bartosz Golaszewski <bgolaszewski@baylibre.com>

Add basic support for the battery charger for max77650 PMIC.

Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
 drivers/power/supply/Kconfig            |   7 +
 drivers/power/supply/Makefile           |   1 +
 drivers/power/supply/max77650-charger.c | 367 ++++++++++++++++++++++++
 3 files changed, 375 insertions(+)
 create mode 100644 drivers/power/supply/max77650-charger.c

diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig
index e901b9879e7e..0230c96fa94d 100644
--- a/drivers/power/supply/Kconfig
+++ b/drivers/power/supply/Kconfig
@@ -499,6 +499,13 @@ config CHARGER_DETECTOR_MAX14656
 	  Revision 1.2 and can be found e.g. in Kindle 4/5th generation
 	  readers and certain LG devices.
 
+config CHARGER_MAX77650
+	tristate "Maxim MAX77650 battery charger driver"
+	depends on MFD_MAX77650
+	help
+	  Say Y to enable support for the battery charger control of MAX77650
+	  PMICs.
+
 config CHARGER_MAX77693
 	tristate "Maxim MAX77693 battery charger driver"
 	depends on MFD_MAX77693
diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile
index b731c2a9b695..b73eb8c5c1a9 100644
--- a/drivers/power/supply/Makefile
+++ b/drivers/power/supply/Makefile
@@ -70,6 +70,7 @@ obj-$(CONFIG_CHARGER_MANAGER)	+= charger-manager.o
 obj-$(CONFIG_CHARGER_LTC3651)	+= ltc3651-charger.o
 obj-$(CONFIG_CHARGER_MAX14577)	+= max14577_charger.o
 obj-$(CONFIG_CHARGER_DETECTOR_MAX14656)	+= max14656_charger_detector.o
+obj-$(CONFIG_CHARGER_MAX77650)	+= max77650-charger.o
 obj-$(CONFIG_CHARGER_MAX77693)	+= max77693_charger.o
 obj-$(CONFIG_CHARGER_MAX8997)	+= max8997_charger.o
 obj-$(CONFIG_CHARGER_MAX8998)	+= max8998_charger.o
diff --git a/drivers/power/supply/max77650-charger.c b/drivers/power/supply/max77650-charger.c
new file mode 100644
index 000000000000..e7cca32944bd
--- /dev/null
+++ b/drivers/power/supply/max77650-charger.c
@@ -0,0 +1,367 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (C) 2018 BayLibre SAS
+// Author: Bartosz Golaszewski <bgolaszewski@baylibre.com>
+//
+// Battery charger driver for MAXIM 77650/77651 charger/power-supply.
+
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/max77650.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/regmap.h>
+
+#define MAX77650_CHARGER_ENABLED		BIT(0)
+#define MAX77650_CHARGER_DISABLED		0x00
+#define MAX77650_CHARGER_CHG_EN_MASK		BIT(0)
+
+#define MAX77650_CHG_DETAILS_MASK		GENMASK(7, 4)
+#define MAX77650_CHG_DETAILS_BITS(_reg) \
+		(((_reg) & MAX77650_CHG_DETAILS_MASK) >> 4)
+
+/* Charger is OFF. */
+#define MAX77650_CHG_OFF			0x00
+/* Charger is in prequalification mode. */
+#define MAX77650_CHG_PREQ			0x01
+/* Charger is in fast-charge constant current mode. */
+#define MAX77650_CHG_ON_CURR			0x02
+/* Charger is in JEITA modified fast-charge constant-current mode. */
+#define MAX77650_CHG_ON_CURR_JEITA		0x03
+/* Charger is in fast-charge constant-voltage mode. */
+#define MAX77650_CHG_ON_VOLT			0x04
+/* Charger is in JEITA modified fast-charge constant-voltage mode. */
+#define MAX77650_CHG_ON_VOLT_JEITA		0x05
+/* Charger is in top-off mode. */
+#define MAX77650_CHG_ON_TOPOFF			0x06
+/* Charger is in JEITA modified top-off mode. */
+#define MAX77650_CHG_ON_TOPOFF_JEITA		0x07
+/* Charger is done. */
+#define MAX77650_CHG_DONE			0x08
+/* Charger is JEITA modified done. */
+#define MAX77650_CHG_DONE_JEITA			0x09
+/* Charger is suspended due to a prequalification timer fault. */
+#define MAX77650_CHG_SUSP_PREQ_TIM_FAULT	0x0a
+/* Charger is suspended due to a fast-charge timer fault. */
+#define MAX77650_CHG_SUSP_FAST_CHG_TIM_FAULT	0x0b
+/* Charger is suspended due to a battery temperature fault. */
+#define MAX77650_CHG_SUSP_BATT_TEMP_FAULT	0x0c
+
+#define MAX77650_CHGIN_DETAILS_MASK		GENMASK(3, 2)
+#define MAX77650_CHGIN_DETAILS_BITS(_reg) \
+		(((_reg) & MAX77650_CHGIN_DETAILS_MASK) >> 2)
+
+#define MAX77650_CHGIN_UNDERVOLTAGE_LOCKOUT	0x00
+#define MAX77650_CHGIN_OVERVOLTAGE_LOCKOUT	0x01
+#define MAX77650_CHGIN_OKAY			0x11
+
+#define MAX77650_CHARGER_CHG_MASK	BIT(1)
+#define MAX77650_CHARGER_CHG_CHARGING(_reg) \
+		(((_reg) & MAX77650_CHARGER_CHG_MASK) > 1)
+
+#define MAX77650_CHARGER_VCHGIN_MIN_MASK	0xc0
+#define MAX77650_CHARGER_VCHGIN_MIN_SHIFT(_val)	((_val) << 5)
+
+#define MAX77650_CHARGER_ICHGIN_LIM_MASK	0x1c
+#define MAX77650_CHARGER_ICHGIN_LIM_SHIFT(_val)	((_val) << 2)
+
+struct max77650_charger_data {
+	struct regmap *map;
+	struct device *dev;
+};
+
+static enum power_supply_property max77650_charger_properties[] = {
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_ONLINE,
+	POWER_SUPPLY_PROP_CHARGE_TYPE
+};
+
+static const unsigned int max77650_charger_vchgin_min_table[] = {
+	4000000, 4100000, 4200000, 4300000, 4400000, 4500000, 4600000, 4700000
+};
+
+static const unsigned int max77650_charger_ichgin_lim_table[] = {
+	95000, 190000, 285000, 380000, 475000
+};
+
+static int max77650_charger_set_vchgin_min(struct max77650_charger_data *chg,
+					   unsigned int val)
+{
+	int i, rv;
+
+	for (i = 0; i < ARRAY_SIZE(max77650_charger_vchgin_min_table); i++) {
+		if (val == max77650_charger_vchgin_min_table[i]) {
+			rv = regmap_update_bits(chg->map,
+					MAX77650_REG_CNFG_CHG_B,
+					MAX77650_CHARGER_VCHGIN_MIN_MASK,
+					MAX77650_CHARGER_VCHGIN_MIN_SHIFT(i));
+			if (rv)
+				return rv;
+
+			return 0;
+		}
+	}
+
+	return -EINVAL;
+}
+
+static int max77650_charger_set_ichgin_lim(struct max77650_charger_data *chg,
+					   unsigned int val)
+{
+	int i, rv;
+
+	for (i = 0; i < ARRAY_SIZE(max77650_charger_ichgin_lim_table); i++) {
+		if (val == max77650_charger_ichgin_lim_table[i]) {
+			rv = regmap_update_bits(chg->map,
+					MAX77650_REG_CNFG_CHG_B,
+					MAX77650_CHARGER_ICHGIN_LIM_MASK,
+					MAX77650_CHARGER_ICHGIN_LIM_SHIFT(i));
+			if (rv)
+				return rv;
+
+			return 0;
+		}
+	}
+
+	return -EINVAL;
+}
+
+static int max77650_charger_enable(struct max77650_charger_data *chg)
+{
+	int rv;
+
+	rv = regmap_update_bits(chg->map,
+				MAX77650_REG_CNFG_CHG_B,
+				MAX77650_CHARGER_CHG_EN_MASK,
+				MAX77650_CHARGER_ENABLED);
+	if (rv)
+		dev_err(chg->dev, "unable to enable the charger: %d\n", rv);
+
+	return rv;
+}
+
+static int max77650_charger_disable(struct max77650_charger_data *chg)
+{
+	int rv;
+
+	rv = regmap_update_bits(chg->map,
+				MAX77650_REG_CNFG_CHG_B,
+				MAX77650_CHARGER_CHG_EN_MASK,
+				MAX77650_CHARGER_DISABLED);
+	if (rv)
+		dev_err(chg->dev, "unable to disable the charger: %d\n", rv);
+
+	return rv;
+}
+
+static irqreturn_t max77650_charger_check_status(int irq, void *data)
+{
+	struct max77650_charger_data *chg = data;
+	int rv, reg;
+
+	rv = regmap_read(chg->map, MAX77650_REG_STAT_CHG_B, &reg);
+	if (rv) {
+		dev_err(chg->dev,
+			"unable to read the charger status: %d\n", rv);
+		return IRQ_HANDLED;
+	}
+
+	switch (MAX77650_CHGIN_DETAILS_BITS(reg)) {
+	case MAX77650_CHGIN_UNDERVOLTAGE_LOCKOUT:
+		dev_err(chg->dev, "undervoltage lockout detected, disabling charger\n");
+		max77650_charger_disable(chg);
+		break;
+	case MAX77650_CHGIN_OVERVOLTAGE_LOCKOUT:
+		dev_err(chg->dev, "overvoltage lockout detected, disabling charger\n");
+		max77650_charger_disable(chg);
+		break;
+	case MAX77650_CHGIN_OKAY:
+		max77650_charger_enable(chg);
+		break;
+	default:
+		/* May be 0x10 - debouncing */
+		break;
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int max77650_charger_get_property(struct power_supply *psy,
+					 enum power_supply_property psp,
+					 union power_supply_propval *val)
+{
+	struct max77650_charger_data *chg = power_supply_get_drvdata(psy);
+	int rv, reg;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_STATUS:
+		rv = regmap_read(chg->map, MAX77650_REG_STAT_CHG_B, &reg);
+		if (rv)
+			return rv;
+
+		if (MAX77650_CHARGER_CHG_CHARGING(reg)) {
+			val->intval = POWER_SUPPLY_STATUS_CHARGING;
+			break;
+		}
+
+		switch (MAX77650_CHG_DETAILS_BITS(reg)) {
+		case MAX77650_CHG_OFF:
+		case MAX77650_CHG_SUSP_PREQ_TIM_FAULT:
+		case MAX77650_CHG_SUSP_FAST_CHG_TIM_FAULT:
+		case MAX77650_CHG_SUSP_BATT_TEMP_FAULT:
+			val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+			break;
+		case MAX77650_CHG_PREQ:
+		case MAX77650_CHG_ON_CURR:
+		case MAX77650_CHG_ON_CURR_JEITA:
+		case MAX77650_CHG_ON_VOLT:
+		case MAX77650_CHG_ON_VOLT_JEITA:
+		case MAX77650_CHG_ON_TOPOFF:
+		case MAX77650_CHG_ON_TOPOFF_JEITA:
+			val->intval = POWER_SUPPLY_STATUS_CHARGING;
+			break;
+		case MAX77650_CHG_DONE:
+			val->intval = POWER_SUPPLY_STATUS_FULL;
+			break;
+		default:
+			val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+		}
+		break;
+	case POWER_SUPPLY_PROP_ONLINE:
+		rv = regmap_read(chg->map, MAX77650_REG_STAT_CHG_B, &reg);
+		if (rv)
+			return rv;
+
+		val->intval = MAX77650_CHARGER_CHG_CHARGING(reg);
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_TYPE:
+		rv = regmap_read(chg->map, MAX77650_REG_STAT_CHG_B, &reg);
+		if (rv)
+			return rv;
+
+		if (!MAX77650_CHARGER_CHG_CHARGING(reg)) {
+			val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
+			break;
+		}
+
+		switch (MAX77650_CHG_DETAILS_BITS(reg)) {
+		case MAX77650_CHG_PREQ:
+		case MAX77650_CHG_ON_CURR:
+		case MAX77650_CHG_ON_CURR_JEITA:
+		case MAX77650_CHG_ON_VOLT:
+		case MAX77650_CHG_ON_VOLT_JEITA:
+			val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
+			break;
+		case MAX77650_CHG_ON_TOPOFF:
+		case MAX77650_CHG_ON_TOPOFF_JEITA:
+			val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+			break;
+		default:
+			val->intval = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static const struct power_supply_desc max77650_battery_desc = {
+	.name		= "max77650",
+	.type		= POWER_SUPPLY_TYPE_USB,
+	.get_property	= max77650_charger_get_property,
+	.properties	= max77650_charger_properties,
+	.num_properties	= ARRAY_SIZE(max77650_charger_properties),
+};
+
+static int max77650_charger_probe(struct platform_device *pdev)
+{
+	struct power_supply_config pscfg = {};
+	struct max77650_charger_data *chg;
+	struct power_supply *battery;
+	struct device *dev, *parent;
+	int rv, chg_irq, chgin_irq;
+	unsigned int prop;
+
+	dev = &pdev->dev;
+	parent = dev->parent;
+
+	chg = devm_kzalloc(dev, sizeof(*chg), GFP_KERNEL);
+	if (!chg)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, chg);
+
+	chg->map = dev_get_regmap(parent, NULL);
+	if (!chg->map)
+		return -ENODEV;
+
+	chg->dev = dev;
+
+	pscfg.of_node = dev->of_node;
+	pscfg.drv_data = chg;
+
+	chg_irq = platform_get_irq_byname(pdev, "CHG");
+	if (chg_irq < 0)
+		return chg_irq;
+
+	chgin_irq = platform_get_irq_byname(pdev, "CHGIN");
+	if (chgin_irq < 0)
+		return chgin_irq;
+
+	rv = devm_request_any_context_irq(dev, chg_irq,
+					  max77650_charger_check_status,
+					  IRQF_ONESHOT, "chg", chg);
+	if (rv < 0)
+		return rv;
+
+	rv = devm_request_any_context_irq(dev, chgin_irq,
+					  max77650_charger_check_status,
+					  IRQF_ONESHOT, "chgin", chg);
+	if (rv < 0)
+		return rv;
+
+	battery = devm_power_supply_register(dev,
+					     &max77650_battery_desc, &pscfg);
+	if (IS_ERR(battery))
+		return PTR_ERR(battery);
+
+	rv = of_property_read_u32(dev->of_node, "min-microvolt", &prop);
+	if (rv == 0) {
+		rv = max77650_charger_set_vchgin_min(chg, prop);
+		if (rv)
+			return rv;
+	}
+
+	rv = of_property_read_u32(dev->of_node,
+				  "current-limit-microamp", &prop);
+	if (rv == 0) {
+		rv = max77650_charger_set_ichgin_lim(chg, prop);
+		if (rv)
+			return rv;
+	}
+
+	return max77650_charger_enable(chg);
+}
+
+static int max77650_charger_remove(struct platform_device *pdev)
+{
+	struct max77650_charger_data *chg = platform_get_drvdata(pdev);
+
+	return max77650_charger_disable(chg);
+}
+
+static struct platform_driver max77650_charger_driver = {
+	.driver = {
+		.name = "max77650-charger",
+	},
+	.probe = max77650_charger_probe,
+	.remove = max77650_charger_remove,
+};
+module_platform_driver(max77650_charger_driver);
+
+MODULE_DESCRIPTION("MAXIM 77650/77651 charger driver");
+MODULE_AUTHOR("Bartosz Golaszewski <bgolaszewski@baylibre.com>");
+MODULE_LICENSE("GPL v2");
-- 
2.20.1

^ permalink raw reply related

* [PATCH v7 06/11] mfd: max77650: new core mfd driver
From: Bartosz Golaszewski @ 2019-03-26 17:32 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Linus Walleij, Dmitry Torokhov,
	Jacek Anaszewski, Pavel Machek, Lee Jones, Sebastian Reichel,
	Liam Girdwood, Greg Kroah-Hartman
  Cc: linux-kernel, linux-gpio, devicetree, linux-input, linux-leds,
	linux-pm, Bartosz Golaszewski
In-Reply-To: <20190326173208.30614-1-brgl@bgdev.pl>

From: Bartosz Golaszewski <bgolaszewski@baylibre.com>

Add the core mfd driver for max77650 PMIC. We define five sub-devices
for which the drivers will be added in subsequent patches.

Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
 drivers/mfd/Kconfig          |  14 +++
 drivers/mfd/Makefile         |   1 +
 drivers/mfd/max77650.c       | 234 +++++++++++++++++++++++++++++++++++
 include/linux/mfd/max77650.h |  59 +++++++++
 4 files changed, 308 insertions(+)
 create mode 100644 drivers/mfd/max77650.c
 create mode 100644 include/linux/mfd/max77650.h

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 0ce2d8dfc5f1..ade04e124aa0 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -733,6 +733,20 @@ config MFD_MAX77620
 	  provides common support for accessing the device; additional drivers
 	  must be enabled in order to use the functionality of the device.
 
+config MFD_MAX77650
+	tristate "Maxim MAX77650/77651 PMIC Support"
+	depends on I2C
+	depends on OF || COMPILE_TEST
+	select MFD_CORE
+	select REGMAP_I2C
+	help
+	  Say Y here to add support for Maxim Semiconductor MAX77650 and
+	  MAX77651 Power Management ICs. This is the core multifunction
+	  driver for interacting with the device. The module name is
+	  'max77650'. Additional drivers can be enabled in order to use
+	  the following functionalities of the device: GPIO, regulator,
+	  charger, LED, onkey.
+
 config MFD_MAX77686
 	tristate "Maxim Semiconductor MAX77686/802 PMIC Support"
 	depends on I2C
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index b4569ed7f3f3..5727d099c16f 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -155,6 +155,7 @@ obj-$(CONFIG_MFD_DA9150)	+= da9150-core.o
 
 obj-$(CONFIG_MFD_MAX14577)	+= max14577.o
 obj-$(CONFIG_MFD_MAX77620)	+= max77620.o
+obj-$(CONFIG_MFD_MAX77650)	+= max77650.o
 obj-$(CONFIG_MFD_MAX77686)	+= max77686.o
 obj-$(CONFIG_MFD_MAX77693)	+= max77693.o
 obj-$(CONFIG_MFD_MAX77843)	+= max77843.o
diff --git a/drivers/mfd/max77650.c b/drivers/mfd/max77650.c
new file mode 100644
index 000000000000..7a6c0a5cf602
--- /dev/null
+++ b/drivers/mfd/max77650.c
@@ -0,0 +1,234 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (C) 2018 BayLibre SAS
+// Author: Bartosz Golaszewski <bgolaszewski@baylibre.com>
+//
+// Core MFD driver for MAXIM 77650/77651 charger/power-supply.
+// Programming manual: https://pdfserv.maximintegrated.com/en/an/AN6428.pdf
+
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/max77650.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+
+#define MAX77650_INT_GPI_F_MSK		BIT(0)
+#define MAX77650_INT_GPI_R_MSK		BIT(1)
+#define MAX77650_INT_GPI_MSK \
+			(MAX77650_INT_GPI_F_MSK | MAX77650_INT_GPI_R_MSK)
+#define MAX77650_INT_nEN_F_MSK		BIT(2)
+#define MAX77650_INT_nEN_R_MSK		BIT(3)
+#define MAX77650_INT_TJAL1_R_MSK	BIT(4)
+#define MAX77650_INT_TJAL2_R_MSK	BIT(5)
+#define MAX77650_INT_DOD_R_MSK		BIT(6)
+
+#define MAX77650_INT_THM_MSK		BIT(0)
+#define MAX77650_INT_CHG_MSK		BIT(1)
+#define MAX77650_INT_CHGIN_MSK		BIT(2)
+#define MAX77650_INT_TJ_REG_MSK		BIT(3)
+#define MAX77650_INT_CHGIN_CTRL_MSK	BIT(4)
+#define MAX77650_INT_SYS_CTRL_MSK	BIT(5)
+#define MAX77650_INT_SYS_CNFG_MSK	BIT(6)
+
+#define MAX77650_INT_GLBL_OFFSET	0
+#define MAX77650_INT_CHG_OFFSET		1
+
+#define MAX77650_SBIA_LPM_MASK		BIT(5)
+#define MAX77650_SBIA_LPM_DISABLED	0x00
+
+enum {
+	MAX77650_INT_GPI,
+	MAX77650_INT_nEN_F,
+	MAX77650_INT_nEN_R,
+	MAX77650_INT_TJAL1_R,
+	MAX77650_INT_TJAL2_R,
+	MAX77650_INT_DOD_R,
+	MAX77650_INT_THM,
+	MAX77650_INT_CHG,
+	MAX77650_INT_CHGIN,
+	MAX77650_INT_TJ_REG,
+	MAX77650_INT_CHGIN_CTRL,
+	MAX77650_INT_SYS_CTRL,
+	MAX77650_INT_SYS_CNFG,
+};
+
+static const struct resource max77650_charger_resources[] = {
+	DEFINE_RES_IRQ_NAMED(MAX77650_INT_CHG, "CHG"),
+	DEFINE_RES_IRQ_NAMED(MAX77650_INT_CHGIN, "CHGIN"),
+};
+
+static const struct resource max77650_gpio_resources[] = {
+	DEFINE_RES_IRQ_NAMED(MAX77650_INT_GPI, "GPI"),
+};
+
+static const struct resource max77650_onkey_resources[] = {
+	DEFINE_RES_IRQ_NAMED(MAX77650_INT_nEN_F, "nEN_F"),
+	DEFINE_RES_IRQ_NAMED(MAX77650_INT_nEN_R, "nEN_R"),
+};
+
+static const struct mfd_cell max77650_cells[] = {
+	{
+		.name		= "max77650-regulator",
+		.of_compatible	= "maxim,max77650-regulator",
+	},
+	{
+		.name		= "max77650-charger",
+		.of_compatible	= "maxim,max77650-charger",
+		.resources	= max77650_charger_resources,
+		.num_resources	= ARRAY_SIZE(max77650_charger_resources),
+	},
+	{
+		.name		= "max77650-gpio",
+		.of_compatible	= "maxim,max77650-gpio",
+		.resources	= max77650_gpio_resources,
+		.num_resources	= ARRAY_SIZE(max77650_gpio_resources),
+	},
+	{
+		.name		= "max77650-led",
+		.of_compatible	= "maxim,max77650-led",
+	},
+	{
+		.name		= "max77650-onkey",
+		.of_compatible	= "maxim,max77650-onkey",
+		.resources	= max77650_onkey_resources,
+		.num_resources	= ARRAY_SIZE(max77650_onkey_resources),
+	},
+};
+
+static const struct regmap_irq max77650_irqs[] = {
+	[MAX77650_INT_GPI] = {
+		.reg_offset		= MAX77650_INT_GLBL_OFFSET,
+		.mask			= MAX77650_INT_GPI_MSK,
+		.type = {
+			.type_falling_val	= MAX77650_INT_GPI_F_MSK,
+			.type_rising_val	= MAX77650_INT_GPI_R_MSK,
+			.types_supported	= IRQ_TYPE_EDGE_BOTH,
+		},
+	},
+	REGMAP_IRQ_REG(MAX77650_INT_nEN_F,
+		       MAX77650_INT_GLBL_OFFSET, MAX77650_INT_nEN_F_MSK),
+	REGMAP_IRQ_REG(MAX77650_INT_nEN_R,
+		       MAX77650_INT_GLBL_OFFSET, MAX77650_INT_nEN_R_MSK),
+	REGMAP_IRQ_REG(MAX77650_INT_TJAL1_R,
+		       MAX77650_INT_GLBL_OFFSET, MAX77650_INT_TJAL1_R_MSK),
+	REGMAP_IRQ_REG(MAX77650_INT_TJAL2_R,
+		       MAX77650_INT_GLBL_OFFSET, MAX77650_INT_TJAL2_R_MSK),
+	REGMAP_IRQ_REG(MAX77650_INT_DOD_R,
+		       MAX77650_INT_GLBL_OFFSET, MAX77650_INT_DOD_R_MSK),
+	REGMAP_IRQ_REG(MAX77650_INT_THM,
+		       MAX77650_INT_CHG_OFFSET, MAX77650_INT_THM_MSK),
+	REGMAP_IRQ_REG(MAX77650_INT_CHG,
+		       MAX77650_INT_CHG_OFFSET, MAX77650_INT_CHG_MSK),
+	REGMAP_IRQ_REG(MAX77650_INT_CHGIN,
+		       MAX77650_INT_CHG_OFFSET, MAX77650_INT_CHGIN_MSK),
+	REGMAP_IRQ_REG(MAX77650_INT_TJ_REG,
+		       MAX77650_INT_CHG_OFFSET, MAX77650_INT_TJ_REG_MSK),
+	REGMAP_IRQ_REG(MAX77650_INT_CHGIN_CTRL,
+		       MAX77650_INT_CHG_OFFSET, MAX77650_INT_CHGIN_CTRL_MSK),
+	REGMAP_IRQ_REG(MAX77650_INT_SYS_CTRL,
+		       MAX77650_INT_CHG_OFFSET, MAX77650_INT_SYS_CTRL_MSK),
+	REGMAP_IRQ_REG(MAX77650_INT_SYS_CNFG,
+		       MAX77650_INT_CHG_OFFSET, MAX77650_INT_SYS_CNFG_MSK),
+};
+
+static const struct regmap_irq_chip max77650_irq_chip = {
+	.name			= "max77650-irq",
+	.irqs			= max77650_irqs,
+	.num_irqs		= ARRAY_SIZE(max77650_irqs),
+	.num_regs		= 2,
+	.status_base		= MAX77650_REG_INT_GLBL,
+	.mask_base		= MAX77650_REG_INTM_GLBL,
+	.type_in_mask		= true,
+	.type_invert		= true,
+	.init_ack_masked	= true,
+	.clear_on_unmask	= true,
+};
+
+static const struct regmap_config max77650_regmap_config = {
+	.name		= "max77650",
+	.reg_bits	= 8,
+	.val_bits	= 8,
+};
+
+static int max77650_i2c_probe(struct i2c_client *i2c)
+{
+	struct regmap_irq_chip_data *irq_data;
+	struct device *dev = &i2c->dev;
+	struct irq_domain *domain;
+	struct regmap *map;
+	unsigned int val;
+	int rv;
+
+	map = devm_regmap_init_i2c(i2c, &max77650_regmap_config);
+	if (IS_ERR(map)) {
+		dev_err(dev, "unable to initialize i2c regmap\n");
+		return PTR_ERR(map);
+	}
+
+	rv = regmap_read(map, MAX77650_REG_CID, &val);
+	if (rv) {
+		dev_err(dev, "unable to read the CID from device\n");
+		return rv;
+	}
+
+	switch (MAX77650_CID_BITS(val)) {
+	case MAX77650_CID_77650A:
+	case MAX77650_CID_77650C:
+	case MAX77650_CID_77651A:
+	case MAX77650_CID_77651B:
+		break;
+	default:
+		return -ENODEV;
+	}
+
+	/*
+	 * This IC has a low-power mode which reduces the quiescent current
+	 * consumption to ~5.6uA but is only suitable for systems consuming
+	 * less than ~2mA. Since this is not likely the case even on
+	 * linux-based wearables - keep the chip in normal power mode.
+	 */
+	rv = regmap_update_bits(map,
+				MAX77650_REG_CNFG_GLBL,
+				MAX77650_SBIA_LPM_MASK,
+				MAX77650_SBIA_LPM_DISABLED);
+	if (rv) {
+		dev_err(dev, "unable to change the power mode\n");
+		return rv;
+	}
+
+	rv = devm_regmap_add_irq_chip(dev, map, i2c->irq,
+				      IRQF_ONESHOT | IRQF_SHARED, 0,
+				      &max77650_irq_chip, &irq_data);
+	if (rv) {
+		dev_err(dev, "unable to add the regmap irq chip\n");
+		return rv;
+	}
+
+	domain = regmap_irq_get_domain(irq_data);
+
+	return devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE,
+				    max77650_cells, ARRAY_SIZE(max77650_cells),
+				    NULL, 0, domain);
+}
+
+static const struct of_device_id max77650_of_match[] = {
+	{ .compatible = "maxim,max77650" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, max77650_of_match);
+
+static struct i2c_driver max77650_i2c_driver = {
+	.driver = {
+		.name = "max77650",
+		.of_match_table = of_match_ptr(max77650_of_match),
+	},
+	.probe_new = max77650_i2c_probe,
+};
+module_i2c_driver(max77650_i2c_driver);
+
+MODULE_DESCRIPTION("MAXIM 77650/77651 multi-function core driver");
+MODULE_AUTHOR("Bartosz Golaszewski <bgolaszewski@baylibre.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mfd/max77650.h b/include/linux/mfd/max77650.h
new file mode 100644
index 000000000000..c809e211a8cd
--- /dev/null
+++ b/include/linux/mfd/max77650.h
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2018 BayLibre SAS
+ * Author: Bartosz Golaszewski <bgolaszewski@baylibre.com>
+ *
+ * Common definitions for MAXIM 77650/77651 charger/power-supply.
+ */
+
+#ifndef MAX77650_H
+#define MAX77650_H
+
+#include <linux/bits.h>
+
+#define MAX77650_REG_INT_GLBL		0x00
+#define MAX77650_REG_INT_CHG		0x01
+#define MAX77650_REG_STAT_CHG_A		0x02
+#define MAX77650_REG_STAT_CHG_B		0x03
+#define MAX77650_REG_ERCFLAG		0x04
+#define MAX77650_REG_STAT_GLBL		0x05
+#define MAX77650_REG_INTM_GLBL		0x06
+#define MAX77650_REG_INTM_CHG		0x07
+#define MAX77650_REG_CNFG_GLBL		0x10
+#define MAX77650_REG_CID		0x11
+#define MAX77650_REG_CNFG_GPIO		0x12
+#define MAX77650_REG_CNFG_CHG_A		0x18
+#define MAX77650_REG_CNFG_CHG_B		0x19
+#define MAX77650_REG_CNFG_CHG_C		0x1a
+#define MAX77650_REG_CNFG_CHG_D		0x1b
+#define MAX77650_REG_CNFG_CHG_E		0x1c
+#define MAX77650_REG_CNFG_CHG_F		0x1d
+#define MAX77650_REG_CNFG_CHG_G		0x1e
+#define MAX77650_REG_CNFG_CHG_H		0x1f
+#define MAX77650_REG_CNFG_CHG_I		0x20
+#define MAX77650_REG_CNFG_SBB_TOP	0x28
+#define MAX77650_REG_CNFG_SBB0_A	0x29
+#define MAX77650_REG_CNFG_SBB0_B	0x2a
+#define MAX77650_REG_CNFG_SBB1_A	0x2b
+#define MAX77650_REG_CNFG_SBB1_B	0x2c
+#define MAX77650_REG_CNFG_SBB2_A	0x2d
+#define MAX77650_REG_CNFG_SBB2_B	0x2e
+#define MAX77650_REG_CNFG_LDO_A		0x38
+#define MAX77650_REG_CNFG_LDO_B		0x39
+#define MAX77650_REG_CNFG_LED0_A	0x40
+#define MAX77650_REG_CNFG_LED1_A	0x41
+#define MAX77650_REG_CNFG_LED2_A	0x42
+#define MAX77650_REG_CNFG_LED0_B	0x43
+#define MAX77650_REG_CNFG_LED1_B	0x44
+#define MAX77650_REG_CNFG_LED2_B	0x45
+#define MAX77650_REG_CNFG_LED_TOP	0x46
+
+#define MAX77650_CID_MASK		GENMASK(3, 0)
+#define MAX77650_CID_BITS(_reg)		(_reg & MAX77650_CID_MASK)
+
+#define MAX77650_CID_77650A		0x03
+#define MAX77650_CID_77650C		0x0a
+#define MAX77650_CID_77651A		0x06
+#define MAX77650_CID_77651B		0x08
+
+#endif /* MAX77650_H */
-- 
2.20.1

^ permalink raw reply related

* [PATCH v7 05/11] mfd: core: document mfd_add_devices()
From: Bartosz Golaszewski @ 2019-03-26 17:32 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Linus Walleij, Dmitry Torokhov,
	Jacek Anaszewski, Pavel Machek, Lee Jones, Sebastian Reichel,
	Liam Girdwood, Greg Kroah-Hartman
  Cc: linux-kernel, linux-gpio, devicetree, linux-input, linux-leds,
	linux-pm, Bartosz Golaszewski
In-Reply-To: <20190326173208.30614-1-brgl@bgdev.pl>

From: Bartosz Golaszewski <bgolaszewski@baylibre.com>

Add a kernel doc for mfd_add_devices().

Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Acked-by: Pavel Machek <pavel@ucw.cz>
---
 drivers/mfd/mfd-core.c | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c
index 94e3f32ce935..0898a8db1747 100644
--- a/drivers/mfd/mfd-core.c
+++ b/drivers/mfd/mfd-core.c
@@ -269,6 +269,20 @@ static int mfd_add_device(struct device *parent, int id,
 	return ret;
 }
 
+/**
+ * mfd_add_devices - register a set of child devices
+ *
+ * @parent: Parent device for all sub-nodes.
+ * @id: Platform device id. If >= 0, each sub-device will have its cell_id
+ *      added to this number and use it as the platform device id.
+ * @cells: Array of mfd cells describing sub-devices.
+ * @n_devs: Number of sub-devices to register.
+ * @mem_base: Parent register range resource for sub-devices.
+ * @irq_base: Base of the range of virtual interrupt numbers allocated for
+ *            this MFD device. Unused if @domain is specified.
+ * @domain: Interrupt domain used to create mappings for HW interrupt numbers
+ *          specificed in sub-devices' IRQ resources.
+ */
 int mfd_add_devices(struct device *parent, int id,
 		    const struct mfd_cell *cells, int n_devs,
 		    struct resource *mem_base,
-- 
2.20.1

^ permalink raw reply related

* [PATCH v7 04/11] dt-bindings: input: add DT bindings for max77650
From: Bartosz Golaszewski @ 2019-03-26 17:32 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Linus Walleij, Dmitry Torokhov,
	Jacek Anaszewski, Pavel Machek, Lee Jones, Sebastian Reichel,
	Liam Girdwood, Greg Kroah-Hartman
  Cc: linux-kernel, linux-gpio, devicetree, linux-input, linux-leds,
	linux-pm, Bartosz Golaszewski, Rob Herring
In-Reply-To: <20190326173208.30614-1-brgl@bgdev.pl>

From: Bartosz Golaszewski <bgolaszewski@baylibre.com>

Add the DT binding document for the onkey module of max77650.

Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
 .../bindings/input/max77650-onkey.txt         | 26 +++++++++++++++++++
 1 file changed, 26 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/max77650-onkey.txt

diff --git a/Documentation/devicetree/bindings/input/max77650-onkey.txt b/Documentation/devicetree/bindings/input/max77650-onkey.txt
new file mode 100644
index 000000000000..477dc74f452a
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/max77650-onkey.txt
@@ -0,0 +1,26 @@
+Onkey driver for MAX77650 PMIC from Maxim Integrated.
+
+This module is part of the MAX77650 MFD device. For more details
+see Documentation/devicetree/bindings/mfd/max77650.txt.
+
+The onkey controller is represented as a sub-node of the PMIC node on
+the device tree.
+
+Required properties:
+--------------------
+- compatible:		Must be "maxim,max77650-onkey".
+
+Optional properties:
+- linux,code:		The key-code to be reported when the key is pressed.
+			Defaults to KEY_POWER.
+- maxim,onkey-slide:	The system's button is a slide switch, not the default
+			push button.
+
+Example:
+--------
+
+	onkey {
+		compatible = "maxim,max77650-onkey";
+		linux,code = <KEY_END>;
+		maxim,onkey-slide;
+	};
-- 
2.20.1

^ permalink raw reply related

* [PATCH v7 03/11] dt-bindings: leds: add DT bindings for max77650
From: Bartosz Golaszewski @ 2019-03-26 17:32 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Linus Walleij, Dmitry Torokhov,
	Jacek Anaszewski, Pavel Machek, Lee Jones, Sebastian Reichel,
	Liam Girdwood, Greg Kroah-Hartman
  Cc: linux-kernel, linux-gpio, devicetree, linux-input, linux-leds,
	linux-pm, Bartosz Golaszewski, Rob Herring
In-Reply-To: <20190326173208.30614-1-brgl@bgdev.pl>

From: Bartosz Golaszewski <bgolaszewski@baylibre.com>

Add the DT binding document for the LEDs module of max77650.

Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
 .../bindings/leds/leds-max77650.txt           | 57 +++++++++++++++++++
 1 file changed, 57 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/leds/leds-max77650.txt

diff --git a/Documentation/devicetree/bindings/leds/leds-max77650.txt b/Documentation/devicetree/bindings/leds/leds-max77650.txt
new file mode 100644
index 000000000000..3a67115cc1da
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/leds-max77650.txt
@@ -0,0 +1,57 @@
+LED driver for MAX77650 PMIC from Maxim Integrated.
+
+This module is part of the MAX77650 MFD device. For more details
+see Documentation/devicetree/bindings/mfd/max77650.txt.
+
+The LED controller is represented as a sub-node of the PMIC node on
+the device tree.
+
+This device has three current sinks.
+
+Required properties:
+--------------------
+- compatible:		Must be "maxim,max77650-led"
+- #address-cells:	Must be <1>.
+- #size-cells:		Must be <0>.
+
+Each LED is represented as a sub-node of the LED-controller node. Up to
+three sub-nodes can be defined.
+
+Required properties of the sub-node:
+------------------------------------
+
+- reg:			Must be <0>, <1> or <2>.
+
+Optional properties of the sub-node:
+------------------------------------
+
+- label:		See Documentation/devicetree/bindings/leds/common.txt
+- linux,default-trigger: See Documentation/devicetree/bindings/leds/common.txt
+
+For more details, please refer to the generic GPIO DT binding document
+<devicetree/bindings/gpio/gpio.txt>.
+
+Example:
+--------
+
+	leds {
+		compatible = "maxim,max77650-led";
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		led@0 {
+			reg = <0>;
+			label = "blue:usr0";
+		};
+
+		led@1 {
+			reg = <1>;
+			label = "red:usr1";
+			linux,default-trigger = "heartbeat";
+		};
+
+		led@2 {
+			reg = <2>;
+			label = "green:usr2";
+		};
+	};
-- 
2.20.1

^ permalink raw reply related

* [PATCH v7 02/11] dt-bindings: power: supply: add DT bindings for max77650
From: Bartosz Golaszewski @ 2019-03-26 17:31 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Linus Walleij, Dmitry Torokhov,
	Jacek Anaszewski, Pavel Machek, Lee Jones, Sebastian Reichel,
	Liam Girdwood, Greg Kroah-Hartman
  Cc: linux-kernel, linux-gpio, devicetree, linux-input, linux-leds,
	linux-pm, Bartosz Golaszewski
In-Reply-To: <20190326173208.30614-1-brgl@bgdev.pl>

From: Bartosz Golaszewski <bgolaszewski@baylibre.com>

Add the DT binding document for the battery charger module of max77650.

Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
 .../power/supply/max77650-charger.txt         | 27 +++++++++++++++++++
 1 file changed, 27 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/power/supply/max77650-charger.txt

diff --git a/Documentation/devicetree/bindings/power/supply/max77650-charger.txt b/Documentation/devicetree/bindings/power/supply/max77650-charger.txt
new file mode 100644
index 000000000000..fef188144386
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/supply/max77650-charger.txt
@@ -0,0 +1,27 @@
+Battery charger driver for MAX77650 PMIC from Maxim Integrated.
+
+This module is part of the MAX77650 MFD device. For more details
+see Documentation/devicetree/bindings/mfd/max77650.txt.
+
+The charger is represented as a sub-node of the PMIC node on the device tree.
+
+Required properties:
+--------------------
+- compatible:		Must be "maxim,max77650-charger"
+
+Optional properties:
+--------------------
+- min-microvolt:	Minimum CHGIN regulation voltage (in microvolts). Must be
+			one of: 4000000, 4100000, 4200000, 4300000, 4400000,
+			4500000, 4600000, 4700000.
+- current-limit-microamp:	CHGIN input current limit (in microamps). Must
+				be one of: 95000, 190000, 285000, 380000, 475000.
+
+Example:
+--------
+
+	charger {
+		compatible = "maxim,max77650-charger";
+		min-microvolt = <4200000>;
+		curr-lim-microamp = <285000>;
+	};
-- 
2.20.1

^ permalink raw reply related

* [PATCH v7 01/11] dt-bindings: mfd: add DT bindings for max77650
From: Bartosz Golaszewski @ 2019-03-26 17:31 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Linus Walleij, Dmitry Torokhov,
	Jacek Anaszewski, Pavel Machek, Lee Jones, Sebastian Reichel,
	Liam Girdwood, Greg Kroah-Hartman
  Cc: linux-kernel, linux-gpio, devicetree, linux-input, linux-leds,
	linux-pm, Bartosz Golaszewski, Rob Herring
In-Reply-To: <20190326173208.30614-1-brgl@bgdev.pl>

From: Bartosz Golaszewski <bgolaszewski@baylibre.com>

Add a DT binding document for max77650 ultra-low power PMIC. This
describes the core mfd device and the GPIO module.

Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Reviewed-by: Rob Herring <robh@kernel.org>
Acked-by: Pavel Machek <pavel@ucw.cz>
---
 .../devicetree/bindings/mfd/max77650.txt      | 46 +++++++++++++++++++
 1 file changed, 46 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mfd/max77650.txt

diff --git a/Documentation/devicetree/bindings/mfd/max77650.txt b/Documentation/devicetree/bindings/mfd/max77650.txt
new file mode 100644
index 000000000000..b529d8d19335
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/max77650.txt
@@ -0,0 +1,46 @@
+MAX77650 ultra low-power PMIC from Maxim Integrated.
+
+Required properties:
+-------------------
+- compatible:		Must be "maxim,max77650"
+- reg:			I2C device address.
+- interrupts:		The interrupt on the parent the controller is
+			connected to.
+- interrupt-controller: Marks the device node as an interrupt controller.
+- #interrupt-cells:	Must be <2>.
+
+- gpio-controller:	Marks the device node as a gpio controller.
+- #gpio-cells:		Must be <2>. The first cell is the pin number and
+			the second cell is used to specify the gpio active
+			state.
+
+Optional properties:
+--------------------
+gpio-line-names:	Single string containing the name of the GPIO line.
+
+The GPIO-controller module is represented as part of the top-level PMIC
+node. The device exposes a single GPIO line.
+
+For device-tree bindings of other sub-modules (regulator, power supply,
+LEDs and onkey) refer to the binding documents under the respective
+sub-system directories.
+
+For more details on GPIO bindings, please refer to the generic GPIO DT
+binding document <devicetree/bindings/gpio/gpio.txt>.
+
+Example:
+--------
+
+	pmic@48 {
+		compatible = "maxim,max77650";
+		reg = <0x48>;
+
+		interrupt-controller;
+		interrupt-parent = <&gpio2>;
+		#interrupt-cells = <2>;
+		interrupts = <3 IRQ_TYPE_LEVEL_LOW>;
+
+		gpio-controller;
+		#gpio-cells = <2>;
+		gpio-line-names = "max77650-charger";
+	};
-- 
2.20.1

^ permalink raw reply related

* [PATCH v7 00/11] mfd: add support for max77650 PMIC
From: Bartosz Golaszewski @ 2019-03-26 17:31 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Linus Walleij, Dmitry Torokhov,
	Jacek Anaszewski, Pavel Machek, Lee Jones, Sebastian Reichel,
	Liam Girdwood, Greg Kroah-Hartman
  Cc: linux-kernel, linux-gpio, devicetree, linux-input, linux-leds,
	linux-pm, Bartosz Golaszewski

From: Bartosz Golaszewski <bgolaszewski@baylibre.com>

This series adds support for max77650 ultra low-power PMIC. It provides
the core mfd driver and a set of five sub-drivers for the regulator,
power supply, gpio, leds and input subsystems.

Patches 1-4 add the DT binding documents. Patch 5 documents mfd_add_devices().
Patches 6-10 add all drivers. Last patch adds a MAINTAINERS entry for this
device.

The regulator part is already upstream.

v1 -> v2:
=========

General:
- use C++ style comments for the SPDX license identifier and the
  copyright header
- s/MODULE_LICENSE("GPL")/MODULE_LICENSE("GPL v2")/
- lookup the virtual interrupt numbers in the MFD driver, setup
  resources for child devices and use platform_get_irq_byname()
  in sub-drivers
- picked up review tags
- use devm_request_any_context_irq() for interrupt requests

LEDs:
- changed the max77650_leds_ prefix to max77650_led_
- drop the max77650_leds structure as the only field it held was the
  regmap pointer, move said pointer to struct max77650_led
- change the driver name to "max77650-led"
- drop the last return value check and return the result of
  regmap_write() directly
- change the labeling scheme to one consistent with other LED drivers

ONKEY:
- drop the key reporting helper and call the input functions directly
  from interrupt handlers
- rename the rv local variable to error
- drop parent device asignment

Regulator:
- drop the unnecessary init_data lookup from the driver code
- drop unnecessary include

Charger:
- disable the charger on driver remove
- change the power supply type to POWER_SUPPLY_TYPE_USB

GPIO:
- drop interrupt support until we have correct implementation of hierarchical
  irqs in gpiolib

v2 -> v3:
=========

General:
- dropped regulator patches as they're already in Mark Brown's branch

LED:
- fix the compatible string in the DT binding example
- use the max_brightness property
- use a common prefix ("MAX77650_LED") for all defines in the driver

MFD:
- add the MODULE_DEVICE_TABLE()
- add a sentinel to the of_device_id array
- constify the pointers to irq names
- use an enum instead of defines for interrupt indexes

v3 -> v4:
=========

GPIO:
- as discussed with Linus Walleij: the gpio-controller is now part of
  the core mfd module (we don't spawn a sub-node anymore), the binding
  document for GPIO has been dropped, the GPIO properties have been
  defined in the binding document for the mfd core, the interrupt
  functionality has been reintroduced with the irq directly passed from
  the mfd part
- due to the above changes the Reviewed-by tag from Linus was dropped

v4 -> v5:
=========

General:
- add a patch documenting mfd_add_devices()

MFD:
- pass the regmap irq_chip irq domain to mfd over mfd_add_devices so that
  the hw interrupts from resources can be correctly mapped to virtual irqs
- remove the enum listing cell indexes
- extend Kconfig help
- add a link to the programming manual
- use REGMAP_IRQ_REG() for regmap interrupts (except for GPI which has
  is composed of two hw interrupts for rising and falling edge)
- add error messages in probe
- use PLATFORM_DEVID_NONE constant in devm_mfd_add_devices()
- set irq_base to 0 in regmap_add_irq_chip() as other users to, it's only
  relevant if it's > 0

Charger:
- use non-maxim specific property names for minimum input voltage and current
  limit
- code shrink by using the enable/disable charger helpers everywhere
- use more descriptive names for constants

Onkey:
- use EV_SW event type for slide mode

LED:
- remove stray " from Kconfig help

v5 -> v6:
=========

MFD:
- remove stray spaces in the binding document
- rename the example dt node
- remove unnecessary interrupt-parent property from the bindings

LED:
- add a missing dependency on LEDS_CLASS to Kconfig

Onkey:
- use boolean for the slide button property

Charger:
- fix the property names in DT example
- make constants even more readable

v6 -> v7:
=========

Charger:
- rename the current limit property to current-limit-microamp

Bartosz Golaszewski (11):
  dt-bindings: mfd: add DT bindings for max77650
  dt-bindings: power: supply: add DT bindings for max77650
  dt-bindings: leds: add DT bindings for max77650
  dt-bindings: input: add DT bindings for max77650
  mfd: core: document mfd_add_devices()
  mfd: max77650: new core mfd driver
  power: supply: max77650: add support for battery charger
  gpio: max77650: add GPIO support
  leds: max77650: add LEDs support
  input: max77650: add onkey support
  MAINTAINERS: add an entry for max77650 mfd driver

 .../bindings/input/max77650-onkey.txt         |  26 ++
 .../bindings/leds/leds-max77650.txt           |  57 +++
 .../devicetree/bindings/mfd/max77650.txt      |  46 +++
 .../power/supply/max77650-charger.txt         |  27 ++
 MAINTAINERS                                   |  14 +
 drivers/gpio/Kconfig                          |   7 +
 drivers/gpio/Makefile                         |   1 +
 drivers/gpio/gpio-max77650.c                  | 190 +++++++++
 drivers/input/misc/Kconfig                    |   9 +
 drivers/input/misc/Makefile                   |   1 +
 drivers/input/misc/max77650-onkey.c           | 121 ++++++
 drivers/leds/Kconfig                          |   6 +
 drivers/leds/Makefile                         |   1 +
 drivers/leds/leds-max77650.c                  | 147 +++++++
 drivers/mfd/Kconfig                           |  14 +
 drivers/mfd/Makefile                          |   1 +
 drivers/mfd/max77650.c                        | 234 +++++++++++
 drivers/mfd/mfd-core.c                        |  14 +
 drivers/power/supply/Kconfig                  |   7 +
 drivers/power/supply/Makefile                 |   1 +
 drivers/power/supply/max77650-charger.c       | 367 ++++++++++++++++++
 include/linux/mfd/max77650.h                  |  59 +++
 22 files changed, 1350 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/max77650-onkey.txt
 create mode 100644 Documentation/devicetree/bindings/leds/leds-max77650.txt
 create mode 100644 Documentation/devicetree/bindings/mfd/max77650.txt
 create mode 100644 Documentation/devicetree/bindings/power/supply/max77650-charger.txt
 create mode 100644 drivers/gpio/gpio-max77650.c
 create mode 100644 drivers/input/misc/max77650-onkey.c
 create mode 100644 drivers/leds/leds-max77650.c
 create mode 100644 drivers/mfd/max77650.c
 create mode 100644 drivers/power/supply/max77650-charger.c
 create mode 100644 include/linux/mfd/max77650.h

-- 
2.20.1

^ permalink raw reply

* [PATCH] HID: quirks: Fix keyboard + touchpad on Lenovo Miix 630
From: Jeffrey Hugo @ 2019-03-26 16:55 UTC (permalink / raw)
  To: jikos, benjamin.tissoires
  Cc: linux-input, bjorn.andersson, lee.jones, linux-arm-msm,
	linux-kernel, Jeffrey Hugo

Similar to commit edfc3722cfef ("HID: quirks: Fix keyboard + touchpad on
Toshiba Click Mini not working"), the Lenovo Miix 630 has a combo
keyboard/touchpad device with vid:pid of 04F3:0400, which is shared with
Elan touchpads.  The combo on the Miix 630 has an ACPI id of QTEC0001,
which is not claimed by the elan_i2c driver, so key on that similar to
what was done for the Toshiba Click Mini.

Signed-off-by: Jeffrey Hugo <jeffrey.l.hugo@gmail.com>
---
 drivers/hid/hid-quirks.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c
index 1148d8c0816a..77ffba48cc73 100644
--- a/drivers/hid/hid-quirks.c
+++ b/drivers/hid/hid-quirks.c
@@ -715,7 +715,6 @@ static const struct hid_device_id hid_ignore_list[] = {
 	{ HID_USB_DEVICE(USB_VENDOR_ID_DEALEXTREAME, USB_DEVICE_ID_DEALEXTREAME_RADIO_SI4701) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EARTHMATE) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EM_LT20) },
-	{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, 0x0400) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_ETT, USB_DEVICE_ID_TC5UH) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_ETT, USB_DEVICE_ID_TC4UM) },
@@ -996,6 +995,10 @@ bool hid_ignore(struct hid_device *hdev)
 		if (hdev->product == 0x0401 &&
 		    strncmp(hdev->name, "ELAN0800", 8) != 0)
 			return true;
+		/* Same with product id 0x0400 */
+		if (hdev->product == 0x0400 &&
+		    strncmp(hdev->name, "QTEC0001", 8) != 0)
+			return true;
 		break;
 	}
 
-- 
2.17.1

^ permalink raw reply related

* Re: [RESEND PATCH v6 06/11] mfd: max77650: new core mfd driver
From: Bartosz Golaszewski @ 2019-03-26 13:49 UTC (permalink / raw)
  To: Pavel Machek
  Cc: Rob Herring, Mark Rutland, Linus Walleij, Dmitry Torokhov,
	Jacek Anaszewski, Lee Jones, Sebastian Reichel, Liam Girdwood,
	Greg Kroah-Hartman, Linux Kernel Mailing List,
	open list:GPIO SUBSYSTEM, devicetree, Linux Input,
	Linux LED Subsystem, Linux PM list, Bartosz Golaszewski
In-Reply-To: <20190322091223.GI27015@amd>

pt., 22 mar 2019 o 10:12 Pavel Machek <pavel@ucw.cz> napisał(a):
>
> On Mon 2019-03-18 18:42:23, Bartosz Golaszewski wrote:
> > From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
> >
> > Add the core mfd driver for max77650 PMIC. We define five sub-devices
> > for which the drivers will be added in subsequent patches.
> >
> > Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
> > ---
> >  drivers/mfd/Kconfig          |  14 +++
> >  drivers/mfd/Makefile         |   1 +
> >  drivers/mfd/max77650.c       | 234 +++++++++++++++++++++++++++++++++++
> >  include/linux/mfd/max77650.h |  59 +++++++++
> >  4 files changed, 308 insertions(+)
> >  create mode 100644 drivers/mfd/max77650.c
> >  create mode 100644 include/linux/mfd/max77650.h
> >
> > diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> > index 0ce2d8dfc5f1..ade04e124aa0 100644
> > --- a/drivers/mfd/Kconfig
> > +++ b/drivers/mfd/Kconfig
> > @@ -733,6 +733,20 @@ config MFD_MAX77620
> >         provides common support for accessing the device; additional drivers
> >         must be enabled in order to use the functionality of the device.
> >
> > +config MFD_MAX77650
> > +     tristate "Maxim MAX77650/77651 PMIC Support"
> > +     depends on I2C
> > +     depends on OF || COMPILE_TEST
>
> This says it will compile ok in !OF case. Will it?
>

It will compile because the stubs for OF functions will be provided -
it just won't work, because they won't be implemented.

Bart

> --
> (english) http://www.livejournal.com/~pavelmachek
> (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply

* RE: [PATCH 3/4] input: da9063_onkey: convert header to SPDX
From: Steve Twiss @ 2019-03-26  9:23 UTC (permalink / raw)
  To: Wolfram Sang
  Cc: LKML, linux-renesas-soc@vger.kernel.org, Lee Jones, Mark Brown,
	Support Opensource, Dmitry Torokhov, linux-input@vger.kernel.org
In-Reply-To: <20190318155728.28365-4-wsa+renesas@sang-engineering.com>

On 18 March 2019 15:57, Wolfram Sang wrote:

> Subject: [PATCH 3/4] input: da9063_onkey: convert header to SPDX
> Covnert the header of the source file to SPDX.

[...]

> +// SPDX-License-Identifier: GPL-2.0+
>  /*
>   * OnKey device driver for DA9063, DA9062 and DA9061 PMICs
>   * Copyright (C) 2015  Dialog Semiconductor Ltd.
[...]
>   */

Thanks!
Acked-by: Steve Twiss <stwiss.opensource@diasemi.com>

^ permalink raw reply

* Re: [RESEND PATCH v6 02/11] dt-bindings: power: supply: add DT bindings for max77650
From: Rob Herring @ 2019-03-25 20:46 UTC (permalink / raw)
  To: Bartosz Golaszewski
  Cc: Mark Rutland, Linus Walleij, Dmitry Torokhov, Jacek Anaszewski,
	Pavel Machek, Lee Jones, Sebastian Reichel, Liam Girdwood,
	Greg Kroah-Hartman, linux-kernel, linux-gpio, devicetree,
	linux-input, linux-leds, linux-pm, Bartosz Golaszewski
In-Reply-To: <20190318174228.18194-3-brgl@bgdev.pl>

On Mon, Mar 18, 2019 at 06:42:19PM +0100, Bartosz Golaszewski wrote:
> From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
> 
> Add the DT binding document for the battery charger module of max77650.
> 
> Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
> ---
>  .../power/supply/max77650-charger.txt         | 27 +++++++++++++++++++
>  1 file changed, 27 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/power/supply/max77650-charger.txt
> 
> diff --git a/Documentation/devicetree/bindings/power/supply/max77650-charger.txt b/Documentation/devicetree/bindings/power/supply/max77650-charger.txt
> new file mode 100644
> index 000000000000..d25c95369616
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/power/supply/max77650-charger.txt
> @@ -0,0 +1,27 @@
> +Battery charger driver for MAX77650 PMIC from Maxim Integrated.
> +
> +This module is part of the MAX77650 MFD device. For more details
> +see Documentation/devicetree/bindings/mfd/max77650.txt.
> +
> +The charger is represented as a sub-node of the PMIC node on the device tree.
> +
> +Required properties:
> +--------------------
> +- compatible:		Must be "maxim,max77650-charger"
> +
> +Optional properties:
> +--------------------
> +- min-microvolt:	Minimum CHGIN regulation voltage (in microvolts). Must be
> +			one of: 4000000, 4100000, 4200000, 4300000, 4400000,
> +			4500000, 4600000, 4700000.
> +- curr-lim-microamp:	CHGIN input current limit (in microamps). Must be one of:
> +			95000, 190000, 285000, 380000, 475000.

These should be common charger properties IMO.

Rob

^ permalink raw reply

* Re: [PATCH] ELAN touchpad i2c_hid bugs fix
From: Dmitry Torokhov @ 2019-03-25 18:30 UTC (permalink / raw)
  To: hotwater438
  Cc: Hans de Goede, Vladislav Dalechyn, Benjamin Tissoires,
	Jiri Kosina, Kai Heng Feng, Swboyd, Bigeasy,
	open list:HID CORE LAYER, lkml
In-Reply-To: <Laq4ykv--3-1@tutanota.com>

On Mon, Mar 25, 2019 at 11:23 AM <hotwater438@tutanota.com> wrote:
>
> Hi.
>
> Mar 25, 2019, 6:56 PM by dtor@chromium.org:
>
> If you want to keep on using edge you need to make sure that i2c-hid
> never loses edge, as replaying of previously disabled interrupts in
> not at all reliable. So you need to "kick" the device after
> enable_irq() by initiating read from it and be ready to not get any
> data or get valid data and process accordingly.
>
> I'm sorry, but how edge can be loosed? There is device ID and quirk's which are associated with that ID, and quirk will always trigger edge irq.

If edge arrives while interrupt is disabled and replay of the
interrupt does not work when it is reenabled (and I do not think it
ever works for GPIO, you can hunt for Thomas Gleixner emails to that
effect on LKML), then ISR in the driver will never be called, so
driver will never read from the device to reset the interrupt
condition and interrupt will never be reasserted again -> edge lost.

Thanks.

-- 
Dmitry

^ permalink raw reply

* Re: [PATCH] ELAN touchpad i2c_hid bugs fix
From: Dmitry Torokhov @ 2019-03-25 16:56 UTC (permalink / raw)
  To: Hans de Goede
  Cc: Vladislav Dalechyn, Benjamin Tissoires, Jiri Kosina,
	kai.heng.feng, swboyd, bigeasy, open list:HID CORE LAYER, lkml,
	hotwater438
In-Reply-To: <a2952bc4-0ad0-c8a0-5a5b-13a279023455@redhat.com>

Hi Hans,

On Mon, Mar 25, 2019 at 9:38 AM Hans de Goede <hdegoede@redhat.com> wrote:
>
> Hi Dmitry,
>
> On 25-03-19 17:02, Dmitry Torokhov wrote:
> > Hi Vladislav,
> >
> > On Mon, Mar 25, 2019 at 5:57 AM Vladislav Dalechyn
> > <vlad.dalechin@gmail.com> wrote:
> >>
> >> From: Vladislav Dalechyn <hotwater438@tutanota.com>
> >>
> >> Description: The ELAN1200:04F3:303E touchpad exposes several issues, all
> >> caused by an error setting the correct IRQ_TRIGGER flag:
> >> - i2c_hid incoplete error flood in journalctl;
> >> - Five finger tap kill's module so you have to restart it;
> >> - Two finger scoll is working incorrect and sometimes even when you
> >> raised one of two finger still thinks that you are scrolling.
> >>
> >> Fix all of these with a new quirk that corrects the trigger flag
> >> announced by the ACPI tables. (edge-falling).
> >
> > I do not believe this is right solution. The driver makes liberal use
> > of disable_irq() and enable_irq() which may lead to lost edges and
> > touchpad stopping working altogether.
> >
> > Usually the "extra" report is caused by GPIO controller clearing
> > interrupt condition at the wrong time (too early), or in unsafe or
> > racy fashion. You need to look there instead of adding quirk to
> > i2c-hid.
>
> The falling-edge solution was proposed by Elan themselves.
>
> Also if you look at: https://bugzilla.redhat.com/show_bug.cgi?id=1543769
>
> And esp. the "cat /proc/interrupts" output there, then you will see
> that the interrupt seems to be stuck at low level, which according
> to the ACPI tables is its active level.

So how does it generate a new edge if it is stuck at low?

Is it bad touchpad firmware that does not deassert interrupt quickly
enough? I scrolled through the bug but I do not see if it had been
confirmed that original windows installation actually uses edge (it
may very well be using it; Elan engineers pushed us to use edge in a
few cases, but they all boiled down to an issue with pin control/GPIO
implementation).

>
> As for this being a GPIO chip driver problem, this is using standard
> Intel pinctrl stuff, which is not showing this same issue with many
> other i2c-hid touchpads.

Well, there have been plenty of issues in intel drivers, coupled with
"interesting" things done by firmware and boards.

If you want to keep on using edge you need to make sure that i2c-hid
never loses edge, as replaying of previously disabled interrupts in
not at all reliable. So you need to "kick" the device after
enable_irq() by initiating read from it and be ready to not get any
data or get valid data and process accordingly.

Thanks.

-- 
Dmitry

^ permalink raw reply

* Re: [PATCH] ELAN touchpad i2c_hid bugs fix
From: Hans de Goede @ 2019-03-25 16:38 UTC (permalink / raw)
  To: Dmitry Torokhov, Vladislav Dalechyn
  Cc: Benjamin Tissoires, Jiri Kosina, kai.heng.feng, swboyd, bigeasy,
	open list:HID CORE LAYER, lkml, hotwater438
In-Reply-To: <CAE_wzQ8Aep1FOONeCUxvjBFbgKcVRWP5mUuwQnDxzfemUw-eaA@mail.gmail.com>

Hi Dmitry,

On 25-03-19 17:02, Dmitry Torokhov wrote:
> Hi Vladislav,
> 
> On Mon, Mar 25, 2019 at 5:57 AM Vladislav Dalechyn
> <vlad.dalechin@gmail.com> wrote:
>>
>> From: Vladislav Dalechyn <hotwater438@tutanota.com>
>>
>> Description: The ELAN1200:04F3:303E touchpad exposes several issues, all
>> caused by an error setting the correct IRQ_TRIGGER flag:
>> - i2c_hid incoplete error flood in journalctl;
>> - Five finger tap kill's module so you have to restart it;
>> - Two finger scoll is working incorrect and sometimes even when you
>> raised one of two finger still thinks that you are scrolling.
>>
>> Fix all of these with a new quirk that corrects the trigger flag
>> announced by the ACPI tables. (edge-falling).
> 
> I do not believe this is right solution. The driver makes liberal use
> of disable_irq() and enable_irq() which may lead to lost edges and
> touchpad stopping working altogether.
> 
> Usually the "extra" report is caused by GPIO controller clearing
> interrupt condition at the wrong time (too early), or in unsafe or
> racy fashion. You need to look there instead of adding quirk to
> i2c-hid.

The falling-edge solution was proposed by Elan themselves.

Also if you look at: https://bugzilla.redhat.com/show_bug.cgi?id=1543769

And esp. the "cat /proc/interrupts" output there, then you will see
that the interrupt seems to be stuck at low level, which according
to the ACPI tables is its active level.

As for this being a GPIO chip driver problem, this is using standard
Intel pinctrl stuff, which is not showing this same issue with many
other i2c-hid touchpads.

Regards,

Hans

^ permalink raw reply

* Re: [PATCH 1/1] hid: hid-sensor-custom: simplify getting .driver_data
From: Srinivas Pandruvada @ 2019-03-25 16:20 UTC (permalink / raw)
  To: Wolfram Sang, linux-kernel
  Cc: linux-renesas-soc, Jiri Kosina, Jonathan Cameron,
	Benjamin Tissoires, linux-input, linux-iio
In-Reply-To: <20190319163638.31003-1-wsa+renesas@sang-engineering.com>

On Tue, 2019-03-19 at 17:36 +0100, Wolfram Sang wrote:
> We should get 'driver_data' from 'struct device' directly. Going via
> platform_device is an unneeded step back and forth.
> 
> Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Acked-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>

> ---
> 
> Build tested only. buildbot is happy.
> 
>  drivers/hid/hid-sensor-custom.c | 12 ++++--------
>  1 file changed, 4 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/hid/hid-sensor-custom.c b/drivers/hid/hid-
> sensor-custom.c
> index bb012bc032e0..462e653a7bbb 100644
> --- a/drivers/hid/hid-sensor-custom.c
> +++ b/drivers/hid/hid-sensor-custom.c
> @@ -157,8 +157,7 @@ static int usage_id_cmp(const void *p1, const
> void *p2)
>  static ssize_t enable_sensor_show(struct device *dev,
>  				  struct device_attribute *attr, char
> *buf)
>  {
> -	struct platform_device *pdev = to_platform_device(dev);
> -	struct hid_sensor_custom *sensor_inst =
> platform_get_drvdata(pdev);
> +	struct hid_sensor_custom *sensor_inst = dev_get_drvdata(dev);
>  
>  	return sprintf(buf, "%d\n", sensor_inst->enable);
>  }
> @@ -237,8 +236,7 @@ static ssize_t enable_sensor_store(struct device
> *dev,
>  				   struct device_attribute *attr,
>  				   const char *buf, size_t count)
>  {
> -	struct platform_device *pdev = to_platform_device(dev);
> -	struct hid_sensor_custom *sensor_inst =
> platform_get_drvdata(pdev);
> +	struct hid_sensor_custom *sensor_inst = dev_get_drvdata(dev);
>  	int value;
>  	int ret = -EINVAL;
>  
> @@ -283,8 +281,7 @@ static const struct attribute_group
> enable_sensor_attr_group = {
>  static ssize_t show_value(struct device *dev, struct
> device_attribute *attr,
>  			  char *buf)
>  {
> -	struct platform_device *pdev = to_platform_device(dev);
> -	struct hid_sensor_custom *sensor_inst =
> platform_get_drvdata(pdev);
> +	struct hid_sensor_custom *sensor_inst = dev_get_drvdata(dev);
>  	struct hid_sensor_hub_attribute_info *attribute;
>  	int index, usage, field_index;
>  	char name[HID_CUSTOM_NAME_LENGTH];
> @@ -392,8 +389,7 @@ static ssize_t show_value(struct device *dev,
> struct device_attribute *attr,
>  static ssize_t store_value(struct device *dev, struct
> device_attribute *attr,
>  			   const char *buf, size_t count)
>  {
> -	struct platform_device *pdev = to_platform_device(dev);
> -	struct hid_sensor_custom *sensor_inst =
> platform_get_drvdata(pdev);
> +	struct hid_sensor_custom *sensor_inst = dev_get_drvdata(dev);
>  	int index, field_index, usage;
>  	char name[HID_CUSTOM_NAME_LENGTH];
>  	int value;

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox