From 0c0b7ea23aed0b55ef2f9803f13ddaae1943713d Mon Sep 17 00:00:00 2001 From: Nick Crews Date: Wed, 24 Apr 2019 10:56:50 -0600 Subject: platform/chrome: wilco_ec: Add property helper library A Property is typically a data item that is stored to NVRAM by the EC. Each of these data items has an index associated with it, known as the Property ID (PID). Properties may have variable lengths, up to a max of WILCO_EC_PROPERTY_MAX_SIZE bytes. Properties can be simple integers, or they may be more complex binary data. This patch adds support for getting and setting properties. This will be useful for setting the charge algorithm and charge schedules, which all use properties. Signed-off-by: Nick Crews Signed-off-by: Enric Balletbo i Serra --- include/linux/platform_data/wilco-ec.h | 71 ++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) (limited to 'include/linux/platform_data') diff --git a/include/linux/platform_data/wilco-ec.h b/include/linux/platform_data/wilco-ec.h index 1ff224793c99..50a21bd5fd44 100644 --- a/include/linux/platform_data/wilco-ec.h +++ b/include/linux/platform_data/wilco-ec.h @@ -123,4 +123,75 @@ struct wilco_ec_message { */ int wilco_ec_mailbox(struct wilco_ec_device *ec, struct wilco_ec_message *msg); +/* + * A Property is typically a data item that is stored to NVRAM + * by the EC. Each of these data items has an index associated + * with it, known as the Property ID (PID). Properties may have + * variable lengths, up to a max of WILCO_EC_PROPERTY_MAX_SIZE + * bytes. Properties can be simple integers, or they may be more + * complex binary data. + */ + +#define WILCO_EC_PROPERTY_MAX_SIZE 4 + +/** + * struct ec_property_set_msg - Message to get or set a property. + * @property_id: Which property to get or set. + * @length: Number of bytes of |data| that are used. + * @data: Actual property data. + */ +struct wilco_ec_property_msg { + u32 property_id; + int length; + u8 data[WILCO_EC_PROPERTY_MAX_SIZE]; +}; + +/** + * wilco_ec_get_property() - Retrieve a property from the EC. + * @ec: Embedded Controller device. + * @prop_msg: Message for request and response. + * + * The property_id field of |prop_msg| should be filled before calling this + * function. The result will be stored in the data and length fields. + * + * Return: 0 on success, negative error code on failure. + */ +int wilco_ec_get_property(struct wilco_ec_device *ec, + struct wilco_ec_property_msg *prop_msg); + +/** + * wilco_ec_set_property() - Store a property on the EC. + * @ec: Embedded Controller device. + * @prop_msg: Message for request and response. + * + * The property_id, length, and data fields of |prop_msg| should be + * filled before calling this function. + * + * Return: 0 on success, negative error code on failure. + */ +int wilco_ec_set_property(struct wilco_ec_device *ec, + struct wilco_ec_property_msg *prop_msg); + +/** + * wilco_ec_get_byte_property() - Retrieve a byte-size property from the EC. + * @ec: Embedded Controller device. + * @property_id: Which property to retrieve. + * @val: The result value, will be filled by this function. + * + * Return: 0 on success, negative error code on failure. + */ +int wilco_ec_get_byte_property(struct wilco_ec_device *ec, u32 property_id, + u8 *val); + +/** + * wilco_ec_get_byte_property() - Store a byte-size property on the EC. + * @ec: Embedded Controller device. + * @property_id: Which property to store. + * @val: Value to store. + * + * Return: 0 on success, negative error code on failure. + */ +int wilco_ec_set_byte_property(struct wilco_ec_device *ec, u32 property_id, + u8 val); + #endif /* WILCO_EC_H */ -- cgit v1.2.3 From 4c1ca625c622b7a9f04c2949fd1ffdc6effa86de Mon Sep 17 00:00:00 2001 From: Nick Crews Date: Tue, 16 Apr 2019 19:20:47 -0600 Subject: platform/chrome: wilco_ec: Add Boot on AC support Boot on AC is a policy which makes the device boot from S5 when AC power is connected. This is useful for users who want to run their device headless or with a dock. Signed-off-by: Nick Crews Signed-off-by: Enric Balletbo i Serra --- Documentation/ABI/testing/sysfs-platform-wilco-ec | 9 +++ drivers/platform/chrome/wilco_ec/Makefile | 2 +- drivers/platform/chrome/wilco_ec/core.c | 9 +++ drivers/platform/chrome/wilco_ec/sysfs.c | 77 +++++++++++++++++++++++ include/linux/platform_data/wilco-ec.h | 12 ++++ 5 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 Documentation/ABI/testing/sysfs-platform-wilco-ec create mode 100644 drivers/platform/chrome/wilco_ec/sysfs.c (limited to 'include/linux/platform_data') diff --git a/Documentation/ABI/testing/sysfs-platform-wilco-ec b/Documentation/ABI/testing/sysfs-platform-wilco-ec new file mode 100644 index 000000000000..8e5d6eee44db --- /dev/null +++ b/Documentation/ABI/testing/sysfs-platform-wilco-ec @@ -0,0 +1,9 @@ +What: /sys/bus/platform/devices/GOOG000C\:00/boot_on_ac +Date: April 2019 +KernelVersion: 5.3 +Description: + Boot on AC is a policy which makes the device boot from S5 + when AC power is connected. This is useful for users who + want to run their device headless or with a dock. + + Input should be parseable by kstrtou8() to 0 or 1. diff --git a/drivers/platform/chrome/wilco_ec/Makefile b/drivers/platform/chrome/wilco_ec/Makefile index 29b734137786..72df9b5e1983 100644 --- a/drivers/platform/chrome/wilco_ec/Makefile +++ b/drivers/platform/chrome/wilco_ec/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 -wilco_ec-objs := core.o mailbox.o properties.o +wilco_ec-objs := core.o mailbox.o properties.o sysfs.o obj-$(CONFIG_WILCO_EC) += wilco_ec.o wilco_ec_debugfs-objs := debugfs.o obj-$(CONFIG_WILCO_EC_DEBUGFS) += wilco_ec_debugfs.o diff --git a/drivers/platform/chrome/wilco_ec/core.c b/drivers/platform/chrome/wilco_ec/core.c index 05e1e2be1c91..abd15d04e57b 100644 --- a/drivers/platform/chrome/wilco_ec/core.c +++ b/drivers/platform/chrome/wilco_ec/core.c @@ -89,8 +89,16 @@ static int wilco_ec_probe(struct platform_device *pdev) goto unregister_debugfs; } + ret = wilco_ec_add_sysfs(ec); + if (ret < 0) { + dev_err(dev, "Failed to create sysfs entries: %d", ret); + goto unregister_rtc; + } + return 0; +unregister_rtc: + platform_device_unregister(ec->rtc_pdev); unregister_debugfs: if (ec->debugfs_pdev) platform_device_unregister(ec->debugfs_pdev); @@ -102,6 +110,7 @@ static int wilco_ec_remove(struct platform_device *pdev) { struct wilco_ec_device *ec = platform_get_drvdata(pdev); + wilco_ec_remove_sysfs(ec); platform_device_unregister(ec->rtc_pdev); if (ec->debugfs_pdev) platform_device_unregister(ec->debugfs_pdev); diff --git a/drivers/platform/chrome/wilco_ec/sysfs.c b/drivers/platform/chrome/wilco_ec/sysfs.c new file mode 100644 index 000000000000..f84f0480460a --- /dev/null +++ b/drivers/platform/chrome/wilco_ec/sysfs.c @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 Google LLC + * + * Sysfs properties to view and modify EC-controlled features on Wilco devices. + * The entries will appear under /sys/bus/platform/devices/GOOG000C:00/ + * + * See Documentation/ABI/testing/sysfs-platform-wilco-ec for more information. + */ + +#include +#include + +#define CMD_KB_CMOS 0x7C +#define SUB_CMD_KB_CMOS_AUTO_ON 0x03 + +struct boot_on_ac_request { + u8 cmd; /* Always CMD_KB_CMOS */ + u8 reserved1; + u8 sub_cmd; /* Always SUB_CMD_KB_CMOS_AUTO_ON */ + u8 reserved3to5[3]; + u8 val; /* Either 0 or 1 */ + u8 reserved7; +} __packed; + +static ssize_t boot_on_ac_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct wilco_ec_device *ec = dev_get_drvdata(dev); + struct boot_on_ac_request rq; + struct wilco_ec_message msg; + int ret; + u8 val; + + ret = kstrtou8(buf, 10, &val); + if (ret < 0) + return ret; + if (val > 1) + return -EINVAL; + + memset(&rq, 0, sizeof(rq)); + rq.cmd = CMD_KB_CMOS; + rq.sub_cmd = SUB_CMD_KB_CMOS_AUTO_ON; + rq.val = val; + + memset(&msg, 0, sizeof(msg)); + msg.type = WILCO_EC_MSG_LEGACY; + msg.request_data = &rq; + msg.request_size = sizeof(rq); + ret = wilco_ec_mailbox(ec, &msg); + if (ret < 0) + return ret; + + return count; +} + +static DEVICE_ATTR_WO(boot_on_ac); + +static struct attribute *wilco_dev_attrs[] = { + &dev_attr_boot_on_ac.attr, + NULL, +}; + +static struct attribute_group wilco_dev_attr_group = { + .attrs = wilco_dev_attrs, +}; + +int wilco_ec_add_sysfs(struct wilco_ec_device *ec) +{ + return sysfs_create_group(&ec->dev->kobj, &wilco_dev_attr_group); +} + +void wilco_ec_remove_sysfs(struct wilco_ec_device *ec) +{ + sysfs_remove_group(&ec->dev->kobj, &wilco_dev_attr_group); +} diff --git a/include/linux/platform_data/wilco-ec.h b/include/linux/platform_data/wilco-ec.h index 50a21bd5fd44..af68fc0563cc 100644 --- a/include/linux/platform_data/wilco-ec.h +++ b/include/linux/platform_data/wilco-ec.h @@ -194,4 +194,16 @@ int wilco_ec_get_byte_property(struct wilco_ec_device *ec, u32 property_id, int wilco_ec_set_byte_property(struct wilco_ec_device *ec, u32 property_id, u8 val); +/** + * wilco_ec_add_sysfs() - Create sysfs entries + * @ec: Wilco EC device + * + * wilco_ec_remove_sysfs() needs to be called afterwards + * to perform the necessary cleanup. + * + * Return: 0 on success or negative error code on failure. + */ +int wilco_ec_add_sysfs(struct wilco_ec_device *ec); +void wilco_ec_remove_sysfs(struct wilco_ec_device *ec); + #endif /* WILCO_EC_H */ -- cgit v1.2.3 From 2ad1f7a91449de48d4bd5d1ec361ba7bb9026505 Mon Sep 17 00:00:00 2001 From: Nick Crews Date: Wed, 8 May 2019 15:38:09 -0600 Subject: platform/chrome: wilco_ec: Remove 256 byte transfers The 0xF6 command, intended to send and receive 256 byte payloads to and from the EC, is not needed. The 0xF5 command for 32 byte payloads is sufficient. This patch removes support for the 0xF6 command and 256 byte payloads. Signed-off-by: Nick Crews Signed-off-by: Enric Balletbo i Serra --- Documentation/ABI/testing/debugfs-wilco-ec | 16 +++++++--------- drivers/platform/chrome/wilco_ec/core.c | 4 +--- drivers/platform/chrome/wilco_ec/debugfs.c | 10 ++-------- drivers/platform/chrome/wilco_ec/mailbox.c | 21 +++++---------------- include/linux/platform_data/wilco-ec.h | 9 ++------- 5 files changed, 17 insertions(+), 43 deletions(-) (limited to 'include/linux/platform_data') diff --git a/Documentation/ABI/testing/debugfs-wilco-ec b/Documentation/ABI/testing/debugfs-wilco-ec index 73a5a66ddca6..9d8d9d2def5b 100644 --- a/Documentation/ABI/testing/debugfs-wilco-ec +++ b/Documentation/ABI/testing/debugfs-wilco-ec @@ -23,11 +23,9 @@ Description: For writing, bytes 0-1 indicate the message type, one of enum wilco_ec_msg_type. Byte 2+ consist of the data passed in the - request, starting at MBOX[0] - - At least three bytes are required for writing, two for the type - and at least a single byte of data. Only the first - EC_MAILBOX_DATA_SIZE bytes of MBOX will be used. + request, starting at MBOX[0]. At least three bytes are required + for writing, two for the type and at least a single byte of + data. Example: // Request EC info type 3 (EC firmware build date) @@ -40,7 +38,7 @@ Description: $ cat /sys/kernel/debug/wilco_ec/raw 00 00 31 32 2f 32 31 2f 31 38 00 38 00 01 00 2f 00 ..12/21/18.8... - Note that the first 32 bytes of the received MBOX[] will be - printed, even if some of the data is junk. It is up to you to - know how many of the first bytes of data are the actual - response. + Note that the first 16 bytes of the received MBOX[] will be + printed, even if some of the data is junk, and skipping bytes + 17 to 32. It is up to you to know how many of the first bytes of + data are the actual response. diff --git a/drivers/platform/chrome/wilco_ec/core.c b/drivers/platform/chrome/wilco_ec/core.c index abd15d04e57b..45cf3a5ed062 100644 --- a/drivers/platform/chrome/wilco_ec/core.c +++ b/drivers/platform/chrome/wilco_ec/core.c @@ -52,9 +52,7 @@ static int wilco_ec_probe(struct platform_device *pdev) ec->dev = dev; mutex_init(&ec->mailbox_lock); - /* Largest data buffer size requirement is extended data response */ - ec->data_size = sizeof(struct wilco_ec_response) + - EC_MAILBOX_DATA_SIZE_EXTENDED; + ec->data_size = sizeof(struct wilco_ec_response) + EC_MAILBOX_DATA_SIZE; ec->data_buffer = devm_kzalloc(dev, ec->data_size, GFP_KERNEL); if (!ec->data_buffer) return -ENOMEM; diff --git a/drivers/platform/chrome/wilco_ec/debugfs.c b/drivers/platform/chrome/wilco_ec/debugfs.c index f163476d080d..281ec595e8e0 100644 --- a/drivers/platform/chrome/wilco_ec/debugfs.c +++ b/drivers/platform/chrome/wilco_ec/debugfs.c @@ -17,13 +17,13 @@ #define DRV_NAME "wilco-ec-debugfs" /* The 256 raw bytes will take up more space when represented as a hex string */ -#define FORMATTED_BUFFER_SIZE (EC_MAILBOX_DATA_SIZE_EXTENDED * 4) +#define FORMATTED_BUFFER_SIZE (EC_MAILBOX_DATA_SIZE * 4) struct wilco_ec_debugfs { struct wilco_ec_device *ec; struct dentry *dir; size_t response_size; - u8 raw_data[EC_MAILBOX_DATA_SIZE_EXTENDED]; + u8 raw_data[EC_MAILBOX_DATA_SIZE]; u8 formatted_data[FORMATTED_BUFFER_SIZE]; }; static struct wilco_ec_debugfs *debug_info; @@ -124,12 +124,6 @@ static ssize_t raw_write(struct file *file, const char __user *user_buf, msg.response_data = debug_info->raw_data; msg.response_size = EC_MAILBOX_DATA_SIZE; - /* Telemetry commands use extended response data */ - if (msg.type == WILCO_EC_MSG_TELEMETRY_LONG) { - msg.flags |= WILCO_EC_FLAG_EXTENDED_DATA; - msg.response_size = EC_MAILBOX_DATA_SIZE_EXTENDED; - } - ret = wilco_ec_mailbox(debug_info->ec, &msg); if (ret < 0) return ret; diff --git a/drivers/platform/chrome/wilco_ec/mailbox.c b/drivers/platform/chrome/wilco_ec/mailbox.c index 7fb58b487963..ced1f9f3dcee 100644 --- a/drivers/platform/chrome/wilco_ec/mailbox.c +++ b/drivers/platform/chrome/wilco_ec/mailbox.c @@ -119,7 +119,6 @@ static int wilco_ec_transfer(struct wilco_ec_device *ec, struct wilco_ec_response *rs; u8 checksum; u8 flag; - size_t size; /* Write request header, then data */ cros_ec_lpc_io_bytes_mec(MEC_IO_WRITE, 0, sizeof(*rq), (u8 *)rq); @@ -148,21 +147,11 @@ static int wilco_ec_transfer(struct wilco_ec_device *ec, return -EIO; } - /* - * The EC always returns either EC_MAILBOX_DATA_SIZE or - * EC_MAILBOX_DATA_SIZE_EXTENDED bytes of data, so we need to - * calculate the checksum on **all** of this data, even if we - * won't use all of it. - */ - if (msg->flags & WILCO_EC_FLAG_EXTENDED_DATA) - size = EC_MAILBOX_DATA_SIZE_EXTENDED; - else - size = EC_MAILBOX_DATA_SIZE; - /* Read back response */ rs = ec->data_buffer; checksum = cros_ec_lpc_io_bytes_mec(MEC_IO_READ, 0, - sizeof(*rs) + size, (u8 *)rs); + sizeof(*rs) + EC_MAILBOX_DATA_SIZE, + (u8 *)rs); if (checksum) { dev_dbg(ec->dev, "bad packet checksum 0x%02x\n", rs->checksum); return -EBADMSG; @@ -173,9 +162,9 @@ static int wilco_ec_transfer(struct wilco_ec_device *ec, return -EBADMSG; } - if (rs->data_size != size) { - dev_dbg(ec->dev, "unexpected packet size (%u != %zu)", - rs->data_size, size); + if (rs->data_size != EC_MAILBOX_DATA_SIZE) { + dev_dbg(ec->dev, "unexpected packet size (%u != %u)", + rs->data_size, EC_MAILBOX_DATA_SIZE); return -EMSGSIZE; } diff --git a/include/linux/platform_data/wilco-ec.h b/include/linux/platform_data/wilco-ec.h index af68fc0563cc..e3ce9ce49b11 100644 --- a/include/linux/platform_data/wilco-ec.h +++ b/include/linux/platform_data/wilco-ec.h @@ -13,12 +13,9 @@ /* Message flags for using the mailbox() interface */ #define WILCO_EC_FLAG_NO_RESPONSE BIT(0) /* EC does not respond */ -#define WILCO_EC_FLAG_EXTENDED_DATA BIT(1) /* EC returns 256 data bytes */ /* Normal commands have a maximum 32 bytes of data */ #define EC_MAILBOX_DATA_SIZE 32 -/* Extended commands have 256 bytes of response data */ -#define EC_MAILBOX_DATA_SIZE_EXTENDED 256 /** * struct wilco_ec_device - Wilco Embedded Controller handle. @@ -85,14 +82,12 @@ struct wilco_ec_response { * enum wilco_ec_msg_type - Message type to select a set of command codes. * @WILCO_EC_MSG_LEGACY: Legacy EC messages for standard EC behavior. * @WILCO_EC_MSG_PROPERTY: Get/Set/Sync EC controlled NVRAM property. - * @WILCO_EC_MSG_TELEMETRY_SHORT: 32 bytes of telemetry data provided by the EC. - * @WILCO_EC_MSG_TELEMETRY_LONG: 256 bytes of telemetry data provided by the EC. + * @WILCO_EC_MSG_TELEMETRY: Request telemetry data from the EC. */ enum wilco_ec_msg_type { WILCO_EC_MSG_LEGACY = 0x00f0, WILCO_EC_MSG_PROPERTY = 0x00f2, - WILCO_EC_MSG_TELEMETRY_SHORT = 0x00f5, - WILCO_EC_MSG_TELEMETRY_LONG = 0x00f6, + WILCO_EC_MSG_TELEMETRY = 0x00f5, }; /** -- cgit v1.2.3 From 1bd33bf0fe6d3012410db0302187199871b510a0 Mon Sep 17 00:00:00 2001 From: Esben Haabendal Date: Thu, 23 May 2019 14:02:20 +0200 Subject: net: ll_temac: Prepare indirect register access for multicast support With .ndo_set_rx_mode/temac_set_multicast_list() being called in atomic context (holding addr_list_lock), and temac_set_multicast_list() needing to access temac indirect registers, the mutex used to synchronize indirect register is a no-no. Replace it with a spinlock, and avoid sleeping in temac_indirect_busywait(). To avoid excessive holding of the lock, which is now a spinlock, the temac_device_reset() function is changed to only hold the lock for short periods. With timeouts, it could be holding the spinlock for more than 2 seconds. Signed-off-by: Esben Haabendal Signed-off-by: David S. Miller --- drivers/net/ethernet/xilinx/ll_temac.h | 5 +- drivers/net/ethernet/xilinx/ll_temac_main.c | 240 ++++++++++++++++++-------- drivers/net/ethernet/xilinx/ll_temac_mdio.c | 20 +-- include/linux/platform_data/xilinx-ll-temac.h | 3 +- 4 files changed, 179 insertions(+), 89 deletions(-) (limited to 'include/linux/platform_data') diff --git a/drivers/net/ethernet/xilinx/ll_temac.h b/drivers/net/ethernet/xilinx/ll_temac.h index 1aeda084b8f1..276292bca334 100644 --- a/drivers/net/ethernet/xilinx/ll_temac.h +++ b/drivers/net/ethernet/xilinx/ll_temac.h @@ -361,7 +361,7 @@ struct temac_local { /* For synchronization of indirect register access. Must be * shared mutex between interfaces in same TEMAC block. */ - struct mutex *indirect_mutex; + spinlock_t *indirect_lock; u32 options; /* Current options word */ int last_link; unsigned int temac_features; @@ -388,8 +388,9 @@ struct temac_local { /* xilinx_temac.c */ int temac_indirect_busywait(struct temac_local *lp); u32 temac_indirect_in32(struct temac_local *lp, int reg); +u32 temac_indirect_in32_locked(struct temac_local *lp, int reg); void temac_indirect_out32(struct temac_local *lp, int reg, u32 value); - +void temac_indirect_out32_locked(struct temac_local *lp, int reg, u32 value); /* xilinx_temac_mdio.c */ int temac_mdio_setup(struct temac_local *lp, struct platform_device *pdev); diff --git a/drivers/net/ethernet/xilinx/ll_temac_main.c b/drivers/net/ethernet/xilinx/ll_temac_main.c index 65fb549241b2..cc58bd8c12f6 100644 --- a/drivers/net/ethernet/xilinx/ll_temac_main.c +++ b/drivers/net/ethernet/xilinx/ll_temac_main.c @@ -53,6 +53,7 @@ #include #include #include +#include #include #include "ll_temac.h" @@ -84,51 +85,118 @@ static void _temac_iow_le(struct temac_local *lp, int offset, u32 value) return iowrite32(value, lp->regs + offset); } +static bool hard_acs_rdy(struct temac_local *lp) +{ + return temac_ior(lp, XTE_RDY0_OFFSET) & XTE_RDY0_HARD_ACS_RDY_MASK; +} + +static bool hard_acs_rdy_or_timeout(struct temac_local *lp, ktime_t timeout) +{ + ktime_t cur = ktime_get(); + + return hard_acs_rdy(lp) || ktime_after(cur, timeout); +} + +/* Poll for maximum 20 ms. This is similar to the 2 jiffies @ 100 Hz + * that was used before, and should cover MDIO bus speed down to 3200 + * Hz. + */ +#define HARD_ACS_RDY_POLL_NS (20 * NSEC_PER_MSEC) + +/** + * temac_indirect_busywait - Wait for current indirect register access + * to complete. + */ int temac_indirect_busywait(struct temac_local *lp) { - unsigned long end = jiffies + 2; + ktime_t timeout = ktime_add_ns(ktime_get(), HARD_ACS_RDY_POLL_NS); - while (!(temac_ior(lp, XTE_RDY0_OFFSET) & XTE_RDY0_HARD_ACS_RDY_MASK)) { - if (time_before_eq(end, jiffies)) { - WARN_ON(1); - return -ETIMEDOUT; - } - usleep_range(500, 1000); - } - return 0; + spin_until_cond(hard_acs_rdy_or_timeout(lp, timeout)); + if (WARN_ON(!hard_acs_rdy(lp))) + return -ETIMEDOUT; + else + return 0; } /** - * temac_indirect_in32 - * - * lp->indirect_mutex must be held when calling this function + * temac_indirect_in32 - Indirect register read access. This function + * must be called without lp->indirect_lock being held. */ u32 temac_indirect_in32(struct temac_local *lp, int reg) { - u32 val; + unsigned long flags; + int val; + + spin_lock_irqsave(lp->indirect_lock, flags); + val = temac_indirect_in32_locked(lp, reg); + spin_unlock_irqrestore(lp->indirect_lock, flags); + return val; +} - if (temac_indirect_busywait(lp)) +/** + * temac_indirect_in32_locked - Indirect register read access. This + * function must be called with lp->indirect_lock being held. Use + * this together with spin_lock_irqsave/spin_lock_irqrestore to avoid + * repeated lock/unlock and to ensure uninterrupted access to indirect + * registers. + */ +u32 temac_indirect_in32_locked(struct temac_local *lp, int reg) +{ + /* This initial wait should normally not spin, as we always + * try to wait for indirect access to complete before + * releasing the indirect_lock. + */ + if (WARN_ON(temac_indirect_busywait(lp))) return -ETIMEDOUT; + /* Initiate read from indirect register */ temac_iow(lp, XTE_CTL0_OFFSET, reg); - if (temac_indirect_busywait(lp)) + /* Wait for indirect register access to complete. We really + * should not see timeouts, and could even end up causing + * problem for following indirect access, so let's make a bit + * of WARN noise. + */ + if (WARN_ON(temac_indirect_busywait(lp))) return -ETIMEDOUT; - val = temac_ior(lp, XTE_LSW0_OFFSET); - - return val; + /* Value is ready now */ + return temac_ior(lp, XTE_LSW0_OFFSET); } /** - * temac_indirect_out32 - * - * lp->indirect_mutex must be held when calling this function + * temac_indirect_out32 - Indirect register write access. This function + * must be called without lp->indirect_lock being held. */ void temac_indirect_out32(struct temac_local *lp, int reg, u32 value) { - if (temac_indirect_busywait(lp)) + unsigned long flags; + + spin_lock_irqsave(lp->indirect_lock, flags); + temac_indirect_out32_locked(lp, reg, value); + spin_unlock_irqrestore(lp->indirect_lock, flags); +} + +/** + * temac_indirect_out32_locked - Indirect register write access. This + * function must be called with lp->indirect_lock being held. Use + * this together with spin_lock_irqsave/spin_lock_irqrestore to avoid + * repeated lock/unlock and to ensure uninterrupted access to indirect + * registers. + */ +void temac_indirect_out32_locked(struct temac_local *lp, int reg, u32 value) +{ + /* As in temac_indirect_in32_locked(), we should normally not + * spin here. And if it happens, we actually end up silently + * ignoring the write request. Ouch. + */ + if (WARN_ON(temac_indirect_busywait(lp))) return; + /* Initiate write to indirect register */ temac_iow(lp, XTE_LSW0_OFFSET, value); temac_iow(lp, XTE_CTL0_OFFSET, CNTLREG_WRITE_ENABLE_MASK | reg); - temac_indirect_busywait(lp); + /* As in temac_indirect_in32_locked(), we should not see timeouts + * here. And if it happens, we continue before the write has + * completed. Not good. + */ + WARN_ON(temac_indirect_busywait(lp)); } /** @@ -344,20 +412,21 @@ out: static void temac_do_set_mac_address(struct net_device *ndev) { struct temac_local *lp = netdev_priv(ndev); + unsigned long flags; /* set up unicast MAC address filter set its mac address */ - mutex_lock(lp->indirect_mutex); - temac_indirect_out32(lp, XTE_UAW0_OFFSET, - (ndev->dev_addr[0]) | - (ndev->dev_addr[1] << 8) | - (ndev->dev_addr[2] << 16) | - (ndev->dev_addr[3] << 24)); + spin_lock_irqsave(lp->indirect_lock, flags); + temac_indirect_out32_locked(lp, XTE_UAW0_OFFSET, + (ndev->dev_addr[0]) | + (ndev->dev_addr[1] << 8) | + (ndev->dev_addr[2] << 16) | + (ndev->dev_addr[3] << 24)); /* There are reserved bits in EUAW1 * so don't affect them Set MAC bits [47:32] in EUAW1 */ - temac_indirect_out32(lp, XTE_UAW1_OFFSET, - (ndev->dev_addr[4] & 0x000000ff) | - (ndev->dev_addr[5] << 8)); - mutex_unlock(lp->indirect_mutex); + temac_indirect_out32_locked(lp, XTE_UAW1_OFFSET, + (ndev->dev_addr[4] & 0x000000ff) | + (ndev->dev_addr[5] << 8)); + spin_unlock_irqrestore(lp->indirect_lock, flags); } static int temac_init_mac_address(struct net_device *ndev, const void *address) @@ -383,42 +452,56 @@ static int temac_set_mac_address(struct net_device *ndev, void *p) static void temac_set_multicast_list(struct net_device *ndev) { struct temac_local *lp = netdev_priv(ndev); - u32 multi_addr_msw, multi_addr_lsw, val; + u32 multi_addr_msw, multi_addr_lsw; int i; + unsigned long flags; + bool promisc_mode_disabled = false; - mutex_lock(lp->indirect_mutex); - if (ndev->flags & (IFF_ALLMULTI | IFF_PROMISC) || - netdev_mc_count(ndev) > MULTICAST_CAM_TABLE_NUM) { + if (ndev->flags & (IFF_PROMISC | IFF_ALLMULTI) || + (netdev_mc_count(ndev) > MULTICAST_CAM_TABLE_NUM)) { temac_indirect_out32(lp, XTE_AFM_OFFSET, XTE_AFM_EPPRM_MASK); dev_info(&ndev->dev, "Promiscuous mode enabled.\n"); - } else if (!netdev_mc_empty(ndev)) { + return; + } + + spin_lock_irqsave(lp->indirect_lock, flags); + + if (!netdev_mc_empty(ndev)) { struct netdev_hw_addr *ha; i = 0; netdev_for_each_mc_addr(ha, ndev) { - if (i >= MULTICAST_CAM_TABLE_NUM) + if (WARN_ON(i >= MULTICAST_CAM_TABLE_NUM)) break; multi_addr_msw = ((ha->addr[3] << 24) | (ha->addr[2] << 16) | (ha->addr[1] << 8) | (ha->addr[0])); - temac_indirect_out32(lp, XTE_MAW0_OFFSET, - multi_addr_msw); + temac_indirect_out32_locked(lp, XTE_MAW0_OFFSET, + multi_addr_msw); multi_addr_lsw = ((ha->addr[5] << 8) | (ha->addr[4]) | (i << 16)); - temac_indirect_out32(lp, XTE_MAW1_OFFSET, - multi_addr_lsw); + temac_indirect_out32_locked(lp, XTE_MAW1_OFFSET, + multi_addr_lsw); i++; } } else { - val = temac_indirect_in32(lp, XTE_AFM_OFFSET); - temac_indirect_out32(lp, XTE_AFM_OFFSET, - val & ~XTE_AFM_EPPRM_MASK); - temac_indirect_out32(lp, XTE_MAW0_OFFSET, 0); - temac_indirect_out32(lp, XTE_MAW1_OFFSET, 0); - dev_info(&ndev->dev, "Promiscuous mode disabled.\n"); + temac_indirect_out32_locked(lp, XTE_MAW0_OFFSET, 0); + temac_indirect_out32_locked(lp, XTE_MAW1_OFFSET, i << 16); + } } - mutex_unlock(lp->indirect_mutex); + + /* Enable address filter block if currently disabled */ + if (temac_indirect_in32_locked(lp, XTE_AFM_OFFSET) + & XTE_AFM_EPPRM_MASK) { + temac_indirect_out32_locked(lp, XTE_AFM_OFFSET, 0); + promisc_mode_disabled = true; + } + + spin_unlock_irqrestore(lp->indirect_lock, flags); + + if (promisc_mode_disabled) + dev_info(&ndev->dev, "Promiscuous mode disabled.\n"); } static struct temac_option { @@ -509,17 +592,19 @@ static u32 temac_setoptions(struct net_device *ndev, u32 options) struct temac_local *lp = netdev_priv(ndev); struct temac_option *tp = &temac_options[0]; int reg; + unsigned long flags; - mutex_lock(lp->indirect_mutex); + spin_lock_irqsave(lp->indirect_lock, flags); while (tp->opt) { - reg = temac_indirect_in32(lp, tp->reg) & ~tp->m_or; - if (options & tp->opt) + reg = temac_indirect_in32_locked(lp, tp->reg) & ~tp->m_or; + if (options & tp->opt) { reg |= tp->m_or; - temac_indirect_out32(lp, tp->reg, reg); + temac_indirect_out32_locked(lp, tp->reg, reg); + } tp++; } + spin_unlock_irqrestore(lp->indirect_lock, flags); lp->options |= options; - mutex_unlock(lp->indirect_mutex); return 0; } @@ -530,6 +615,7 @@ static void temac_device_reset(struct net_device *ndev) struct temac_local *lp = netdev_priv(ndev); u32 timeout; u32 val; + unsigned long flags; /* Perform a software reset */ @@ -538,7 +624,6 @@ static void temac_device_reset(struct net_device *ndev) dev_dbg(&ndev->dev, "%s()\n", __func__); - mutex_lock(lp->indirect_mutex); /* Reset the receiver and wait for it to finish reset */ temac_indirect_out32(lp, XTE_RXC1_OFFSET, XTE_RXC1_RXRST_MASK); timeout = 1000; @@ -564,8 +649,11 @@ static void temac_device_reset(struct net_device *ndev) } /* Disable the receiver */ - val = temac_indirect_in32(lp, XTE_RXC1_OFFSET); - temac_indirect_out32(lp, XTE_RXC1_OFFSET, val & ~XTE_RXC1_RXEN_MASK); + spin_lock_irqsave(lp->indirect_lock, flags); + val = temac_indirect_in32_locked(lp, XTE_RXC1_OFFSET); + temac_indirect_out32_locked(lp, XTE_RXC1_OFFSET, + val & ~XTE_RXC1_RXEN_MASK); + spin_unlock_irqrestore(lp->indirect_lock, flags); /* Reset Local Link (DMA) */ lp->dma_out(lp, DMA_CONTROL_REG, DMA_CONTROL_RST); @@ -585,12 +673,12 @@ static void temac_device_reset(struct net_device *ndev) "temac_device_reset descriptor allocation failed\n"); } - temac_indirect_out32(lp, XTE_RXC0_OFFSET, 0); - temac_indirect_out32(lp, XTE_RXC1_OFFSET, 0); - temac_indirect_out32(lp, XTE_TXC_OFFSET, 0); - temac_indirect_out32(lp, XTE_FCC_OFFSET, XTE_FCC_RXFLO_MASK); - - mutex_unlock(lp->indirect_mutex); + spin_lock_irqsave(lp->indirect_lock, flags); + temac_indirect_out32_locked(lp, XTE_RXC0_OFFSET, 0); + temac_indirect_out32_locked(lp, XTE_RXC1_OFFSET, 0); + temac_indirect_out32_locked(lp, XTE_TXC_OFFSET, 0); + temac_indirect_out32_locked(lp, XTE_FCC_OFFSET, XTE_FCC_RXFLO_MASK); + spin_unlock_irqrestore(lp->indirect_lock, flags); /* Sync default options with HW * but leave receiver and transmitter disabled. */ @@ -614,13 +702,14 @@ static void temac_adjust_link(struct net_device *ndev) struct phy_device *phy = ndev->phydev; u32 mii_speed; int link_state; + unsigned long flags; /* hash together the state values to decide if something has changed */ link_state = phy->speed | (phy->duplex << 1) | phy->link; - mutex_lock(lp->indirect_mutex); if (lp->last_link != link_state) { - mii_speed = temac_indirect_in32(lp, XTE_EMCFG_OFFSET); + spin_lock_irqsave(lp->indirect_lock, flags); + mii_speed = temac_indirect_in32_locked(lp, XTE_EMCFG_OFFSET); mii_speed &= ~XTE_EMCFG_LINKSPD_MASK; switch (phy->speed) { @@ -630,11 +719,12 @@ static void temac_adjust_link(struct net_device *ndev) } /* Write new speed setting out to TEMAC */ - temac_indirect_out32(lp, XTE_EMCFG_OFFSET, mii_speed); + temac_indirect_out32_locked(lp, XTE_EMCFG_OFFSET, mii_speed); + spin_unlock_irqrestore(lp->indirect_lock, flags); + lp->last_link = link_state; phy_print_status(phy); } - mutex_unlock(lp->indirect_mutex); } #ifdef CONFIG_64BIT @@ -1096,17 +1186,17 @@ static int temac_probe(struct platform_device *pdev) /* Setup mutex for synchronization of indirect register access */ if (pdata) { - if (!pdata->indirect_mutex) { + if (!pdata->indirect_lock) { dev_err(&pdev->dev, - "indirect_mutex missing in platform_data\n"); + "indirect_lock missing in platform_data\n"); return -EINVAL; } - lp->indirect_mutex = pdata->indirect_mutex; + lp->indirect_lock = pdata->indirect_lock; } else { - lp->indirect_mutex = devm_kmalloc(&pdev->dev, - sizeof(*lp->indirect_mutex), - GFP_KERNEL); - mutex_init(lp->indirect_mutex); + lp->indirect_lock = devm_kmalloc(&pdev->dev, + sizeof(*lp->indirect_lock), + GFP_KERNEL); + spin_lock_init(lp->indirect_lock); } /* map device registers */ diff --git a/drivers/net/ethernet/xilinx/ll_temac_mdio.c b/drivers/net/ethernet/xilinx/ll_temac_mdio.c index a4667326f745..6fd2dea4e60f 100644 --- a/drivers/net/ethernet/xilinx/ll_temac_mdio.c +++ b/drivers/net/ethernet/xilinx/ll_temac_mdio.c @@ -25,14 +25,15 @@ static int temac_mdio_read(struct mii_bus *bus, int phy_id, int reg) { struct temac_local *lp = bus->priv; u32 rc; + unsigned long flags; /* Write the PHY address to the MIIM Access Initiator register. * When the transfer completes, the PHY register value will appear * in the LSW0 register */ - mutex_lock(lp->indirect_mutex); + spin_lock_irqsave(lp->indirect_lock, flags); temac_iow(lp, XTE_LSW0_OFFSET, (phy_id << 5) | reg); - rc = temac_indirect_in32(lp, XTE_MIIMAI_OFFSET); - mutex_unlock(lp->indirect_mutex); + rc = temac_indirect_in32_locked(lp, XTE_MIIMAI_OFFSET); + spin_unlock_irqrestore(lp->indirect_lock, flags); dev_dbg(lp->dev, "temac_mdio_read(phy_id=%i, reg=%x) == %x\n", phy_id, reg, rc); @@ -43,6 +44,7 @@ static int temac_mdio_read(struct mii_bus *bus, int phy_id, int reg) static int temac_mdio_write(struct mii_bus *bus, int phy_id, int reg, u16 val) { struct temac_local *lp = bus->priv; + unsigned long flags; dev_dbg(lp->dev, "temac_mdio_write(phy_id=%i, reg=%x, val=%x)\n", phy_id, reg, val); @@ -50,10 +52,10 @@ static int temac_mdio_write(struct mii_bus *bus, int phy_id, int reg, u16 val) /* First write the desired value into the write data register * and then write the address into the access initiator register */ - mutex_lock(lp->indirect_mutex); - temac_indirect_out32(lp, XTE_MGTDR_OFFSET, val); - temac_indirect_out32(lp, XTE_MIIMAI_OFFSET, (phy_id << 5) | reg); - mutex_unlock(lp->indirect_mutex); + spin_lock_irqsave(lp->indirect_lock, flags); + temac_indirect_out32_locked(lp, XTE_MGTDR_OFFSET, val); + temac_indirect_out32_locked(lp, XTE_MIIMAI_OFFSET, (phy_id << 5) | reg); + spin_unlock_irqrestore(lp->indirect_lock, flags); return 0; } @@ -87,9 +89,7 @@ int temac_mdio_setup(struct temac_local *lp, struct platform_device *pdev) /* Enable the MDIO bus by asserting the enable bit and writing * in the clock config */ - mutex_lock(lp->indirect_mutex); temac_indirect_out32(lp, XTE_MC_OFFSET, 1 << 6 | clk_div); - mutex_unlock(lp->indirect_mutex); bus = devm_mdiobus_alloc(&pdev->dev); if (!bus) @@ -116,10 +116,8 @@ int temac_mdio_setup(struct temac_local *lp, struct platform_device *pdev) if (rc) return rc; - mutex_lock(lp->indirect_mutex); dev_dbg(lp->dev, "MDIO bus registered; MC:%x\n", temac_indirect_in32(lp, XTE_MC_OFFSET)); - mutex_unlock(lp->indirect_mutex); return 0; } diff --git a/include/linux/platform_data/xilinx-ll-temac.h b/include/linux/platform_data/xilinx-ll-temac.h index 368530f98176..f4a68136afa6 100644 --- a/include/linux/platform_data/xilinx-ll-temac.h +++ b/include/linux/platform_data/xilinx-ll-temac.h @@ -4,6 +4,7 @@ #include #include +#include struct ll_temac_platform_data { bool txcsum; /* Enable/disable TX checksum */ @@ -21,7 +22,7 @@ struct ll_temac_platform_data { * TEMAC IP block, the same mutex should be passed here, as * they share the same DCR bus bridge. */ - struct mutex *indirect_mutex; + spinlock_t *indirect_lock; /* DMA channel control setup */ u8 tx_irq_timeout; /* TX Interrupt Delay Time-out */ u8 tx_irq_count; /* TX Interrupt Coalescing Threshold Count */ -- cgit v1.2.3 From 990c0b53bf6599a9c9c7df1529dde681dee6cf64 Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Mon, 20 May 2019 19:32:16 +0800 Subject: dmaengine: imx-sdma: Let the core do the device node validation Let the DMA engine core do the device node validation instead of drivers. Signed-off-by: Baolin Wang Signed-off-by: Vinod Koul --- drivers/dma/imx-sdma.c | 9 ++------- include/linux/platform_data/dma-imx.h | 1 - 2 files changed, 2 insertions(+), 8 deletions(-) (limited to 'include/linux/platform_data') diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index 99d9f431ae2c..ca296f0849ef 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -1934,16 +1934,11 @@ disable_clk_ipg: static bool sdma_filter_fn(struct dma_chan *chan, void *fn_param) { struct sdma_channel *sdmac = to_sdma_chan(chan); - struct sdma_engine *sdma = sdmac->sdma; struct imx_dma_data *data = fn_param; if (!imx_dma_is_general_purpose(chan)) return false; - /* return false if it's not the right device */ - if (sdma->dev->of_node != data->of_node) - return false; - sdmac->data = *data; chan->private = &sdmac->data; @@ -1971,9 +1966,9 @@ static struct dma_chan *sdma_xlate(struct of_phandle_args *dma_spec, * be set to sdmac->event_id1. */ data.dma_request2 = 0; - data.of_node = ofdma->of_node; - return dma_request_channel(mask, sdma_filter_fn, &data); + return __dma_request_channel(&mask, sdma_filter_fn, &data, + ofdma->of_node); } static int sdma_probe(struct platform_device *pdev) diff --git a/include/linux/platform_data/dma-imx.h b/include/linux/platform_data/dma-imx.h index 9daea8d42a10..7d964e787299 100644 --- a/include/linux/platform_data/dma-imx.h +++ b/include/linux/platform_data/dma-imx.h @@ -55,7 +55,6 @@ struct imx_dma_data { int dma_request2; /* secondary DMA request line */ enum sdma_peripheral_type peripheral_type; int priority; - struct device_node *of_node; }; static inline int imx_dma_is_ipu(struct dma_chan *chan) -- cgit v1.2.3 From 153969fd952d81ab8f57574f9be1a90b0a0fa791 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 21 May 2019 03:38:25 +0200 Subject: ARM: versatile: Drop CLCD platform data The Versatile family no longer makes any use of the CLCD platform data, we have moved over all users to the DRM driver that has built-in handling of the displays. Delete the old auxdata. Signed-off-by: Linus Walleij --- arch/arm/mach-versatile/versatile_dt.c | 157 --------------------- include/linux/platform_data/video-clcd-versatile.h | 28 ---- 2 files changed, 185 deletions(-) delete mode 100644 include/linux/platform_data/video-clcd-versatile.h (limited to 'include/linux/platform_data') diff --git a/arch/arm/mach-versatile/versatile_dt.c b/arch/arm/mach-versatile/versatile_dt.c index 028463af726d..b5ff1ea5a944 100644 --- a/arch/arm/mach-versatile/versatile_dt.c +++ b/arch/arm/mach-versatile/versatile_dt.c @@ -29,8 +29,6 @@ #include #include #include -#include -#include #include #include #include @@ -47,14 +45,12 @@ */ #define VERSATILE_SYS_PCICTL_OFFSET 0x44 #define VERSATILE_SYS_MCI_OFFSET 0x48 -#define VERSATILE_SYS_CLCD_OFFSET 0x50 /* * VERSATILE peripheral addresses */ #define VERSATILE_MMCI0_BASE 0x10005000 /* MMC interface */ #define VERSATILE_MMCI1_BASE 0x1000B000 /* MMC Interface */ -#define VERSATILE_CLCD_BASE 0x10120000 /* CLCD */ #define VERSATILE_SCTL_BASE 0x101E0000 /* System controller */ #define VERSATILE_IB2_BASE 0x24000000 /* IB2 module */ #define VERSATILE_IB2_CTL_BASE (VERSATILE_IB2_BASE + 0x03000000) @@ -96,158 +92,6 @@ static struct mmci_platform_data mmc1_plat_data = { .status = mmc_status, }; -/* - * CLCD support. - */ -#define SYS_CLCD_MODE_MASK (3 << 0) -#define SYS_CLCD_MODE_888 (0 << 0) -#define SYS_CLCD_MODE_5551 (1 << 0) -#define SYS_CLCD_MODE_565_RLSB (2 << 0) -#define SYS_CLCD_MODE_565_BLSB (3 << 0) -#define SYS_CLCD_NLCDIOON (1 << 2) -#define SYS_CLCD_VDDPOSSWITCH (1 << 3) -#define SYS_CLCD_PWR3V5SWITCH (1 << 4) -#define SYS_CLCD_ID_MASK (0x1f << 8) -#define SYS_CLCD_ID_SANYO_3_8 (0x00 << 8) -#define SYS_CLCD_ID_UNKNOWN_8_4 (0x01 << 8) -#define SYS_CLCD_ID_EPSON_2_2 (0x02 << 8) -#define SYS_CLCD_ID_SANYO_2_5 (0x07 << 8) -#define SYS_CLCD_ID_VGA (0x1f << 8) - -static bool is_sanyo_2_5_lcd; - -/* - * Disable all display connectors on the interface module. - */ -static void versatile_clcd_disable(struct clcd_fb *fb) -{ - void __iomem *sys_clcd = versatile_sys_base + VERSATILE_SYS_CLCD_OFFSET; - u32 val; - - val = readl(sys_clcd); - val &= ~SYS_CLCD_NLCDIOON | SYS_CLCD_PWR3V5SWITCH; - writel(val, sys_clcd); - - /* - * If the LCD is Sanyo 2x5 in on the IB2 board, turn the back-light off - */ - if (of_machine_is_compatible("arm,versatile-ab") && is_sanyo_2_5_lcd) { - unsigned long ctrl; - - ctrl = readl(versatile_ib2_ctrl); - ctrl &= ~0x01; - writel(ctrl, versatile_ib2_ctrl); - } -} - -/* - * Enable the relevant connector on the interface module. - */ -static void versatile_clcd_enable(struct clcd_fb *fb) -{ - struct fb_var_screeninfo *var = &fb->fb.var; - void __iomem *sys_clcd = versatile_sys_base + VERSATILE_SYS_CLCD_OFFSET; - u32 val; - - val = readl(sys_clcd); - val &= ~SYS_CLCD_MODE_MASK; - - switch (var->green.length) { - case 5: - val |= SYS_CLCD_MODE_5551; - break; - case 6: - if (var->red.offset == 0) - val |= SYS_CLCD_MODE_565_RLSB; - else - val |= SYS_CLCD_MODE_565_BLSB; - break; - case 8: - val |= SYS_CLCD_MODE_888; - break; - } - - /* - * Set the MUX - */ - writel(val, sys_clcd); - - /* - * And now enable the PSUs - */ - val |= SYS_CLCD_NLCDIOON | SYS_CLCD_PWR3V5SWITCH; - writel(val, sys_clcd); - - /* - * If the LCD is Sanyo 2x5 in on the IB2 board, turn the back-light on - */ - if (of_machine_is_compatible("arm,versatile-ab") && is_sanyo_2_5_lcd) { - unsigned long ctrl; - - ctrl = readl(versatile_ib2_ctrl); - ctrl |= 0x01; - writel(ctrl, versatile_ib2_ctrl); - } -} - -/* - * Detect which LCD panel is connected, and return the appropriate - * clcd_panel structure. Note: we do not have any information on - * the required timings for the 8.4in panel, so we presently assume - * VGA timings. - */ -static int versatile_clcd_setup(struct clcd_fb *fb) -{ - void __iomem *sys_clcd = versatile_sys_base + VERSATILE_SYS_CLCD_OFFSET; - const char *panel_name; - u32 val; - - is_sanyo_2_5_lcd = false; - - val = readl(sys_clcd) & SYS_CLCD_ID_MASK; - if (val == SYS_CLCD_ID_SANYO_3_8) - panel_name = "Sanyo TM38QV67A02A"; - else if (val == SYS_CLCD_ID_SANYO_2_5) { - panel_name = "Sanyo QVGA Portrait"; - is_sanyo_2_5_lcd = true; - } else if (val == SYS_CLCD_ID_EPSON_2_2) - panel_name = "Epson L2F50113T00"; - else if (val == SYS_CLCD_ID_VGA) - panel_name = "VGA"; - else { - printk(KERN_ERR "CLCD: unknown LCD panel ID 0x%08x, using VGA\n", - val); - panel_name = "VGA"; - } - - fb->panel = versatile_clcd_get_panel(panel_name); - if (!fb->panel) - return -EINVAL; - - return versatile_clcd_setup_dma(fb, SZ_1M); -} - -static void versatile_clcd_decode(struct clcd_fb *fb, struct clcd_regs *regs) -{ - clcdfb_decode(fb, regs); - - /* Always clear BGR for RGB565: we do the routing externally */ - if (fb->fb.var.green.length == 6) - regs->cntl &= ~CNTL_BGR; -} - -static struct clcd_board clcd_plat_data = { - .name = "Versatile", - .caps = CLCD_CAP_5551 | CLCD_CAP_565 | CLCD_CAP_888, - .check = clcdfb_check, - .decode = versatile_clcd_decode, - .disable = versatile_clcd_disable, - .enable = versatile_clcd_enable, - .setup = versatile_clcd_setup, - .mmap = versatile_clcd_mmap_dma, - .remove = versatile_clcd_remove_dma, -}; - /* * Lookup table for attaching a specific name and platform_data pointer to * devices as they get created by of_platform_populate(). Ideally this table @@ -257,7 +101,6 @@ static struct clcd_board clcd_plat_data = { struct of_dev_auxdata versatile_auxdata_lookup[] __initdata = { OF_DEV_AUXDATA("arm,primecell", VERSATILE_MMCI0_BASE, "fpga:05", &mmc0_plat_data), OF_DEV_AUXDATA("arm,primecell", VERSATILE_MMCI1_BASE, "fpga:0b", &mmc1_plat_data), - OF_DEV_AUXDATA("arm,primecell", VERSATILE_CLCD_BASE, "dev:20", &clcd_plat_data), {} }; diff --git a/include/linux/platform_data/video-clcd-versatile.h b/include/linux/platform_data/video-clcd-versatile.h deleted file mode 100644 index 305ebaec3afd..000000000000 --- a/include/linux/platform_data/video-clcd-versatile.h +++ /dev/null @@ -1,28 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef PLAT_CLCD_H -#define PLAT_CLCD_H - -#ifdef CONFIG_PLAT_VERSATILE_CLCD -struct clcd_panel *versatile_clcd_get_panel(const char *); -int versatile_clcd_setup_dma(struct clcd_fb *, unsigned long); -int versatile_clcd_mmap_dma(struct clcd_fb *, struct vm_area_struct *); -void versatile_clcd_remove_dma(struct clcd_fb *); -#else -static inline struct clcd_panel *versatile_clcd_get_panel(const char *s) -{ - return NULL; -} -static inline int versatile_clcd_setup_dma(struct clcd_fb *fb, unsigned long framesize) -{ - return -ENODEV; -} -static inline int versatile_clcd_mmap_dma(struct clcd_fb *fb, struct vm_area_struct *vm) -{ - return -ENODEV; -} -static inline void versatile_clcd_remove_dma(struct clcd_fb *fb) -{ -} -#endif - -#endif -- cgit v1.2.3 From 2b2f7def058a5386838ef4dba70a860285f79e66 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Mon, 27 May 2019 04:51:53 -0700 Subject: bus: ti-sysc: Add support for missing clockdomain handling We need to let ti-sysc driver manage clockdomain autoidle for the duration of of reset, enable and idle. And we need to do it before we enable the clock and after we disable it. Currently we are still relying on platform callbacks indirectly managing clockdomain autoidle. But I noticed that for device tree only probed drivers it now happens only after we enabling the clocks and before we disable the clocks, while it should be the other way around. So far I have not noticed any issues with this though. Let's add new ti_sysc_clkdm_deny_idle() and ti_sysc_clkdm_allow_idle() functions for ti-sysc driver to use to manage clockdomains directly via platform data callbacks. Note that we can implement the clockdomain functions in pdata-quirks.c as for probing devices without "ti,hwmods" custom property we don't need to use the other platform data callbacks. Let's do this in one patch as there's is still an unlikely chance we may need to apply this as a fix for v5.2 for dropping legacy platform data for some devices. We also do have the option of adding back the platform data if needed in case of trouble. Tested-by: Keerthy Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/omap_hwmod.c | 39 ++--------- arch/arm/mach-omap2/pdata-quirks.c | 60 ++++++++++++++++ drivers/bus/ti-sysc.c | 127 +++++++++++++++++++++++++++------- include/linux/platform_data/ti-sysc.h | 8 +++ 4 files changed, 174 insertions(+), 60 deletions(-) (limited to 'include/linux/platform_data') diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c index 405ac24def05..932ba221e8e7 100644 --- a/arch/arm/mach-omap2/omap_hwmod.c +++ b/arch/arm/mach-omap2/omap_hwmod.c @@ -3445,6 +3445,7 @@ static int omap_hwmod_check_module(struct device *dev, * @dev: struct device * @oh: module * @sysc_fields: sysc register bits + * @clockdomain: clockdomain * @rev_offs: revision register offset * @sysc_offs: sysconfig register offset * @syss_offs: sysstatus register offset @@ -3456,6 +3457,7 @@ static int omap_hwmod_check_module(struct device *dev, static int omap_hwmod_allocate_module(struct device *dev, struct omap_hwmod *oh, const struct ti_sysc_module_data *data, struct sysc_regbits *sysc_fields, + struct clockdomain *clkdm, s32 rev_offs, s32 sysc_offs, s32 syss_offs, u32 sysc_flags, u32 idlemodes) @@ -3463,8 +3465,6 @@ static int omap_hwmod_allocate_module(struct device *dev, struct omap_hwmod *oh, struct omap_hwmod_class_sysconfig *sysc; struct omap_hwmod_class *class = NULL; struct omap_hwmod_ocp_if *oi = NULL; - struct clockdomain *clkdm = NULL; - struct clk *clk = NULL; void __iomem *regs = NULL; unsigned long flags; @@ -3511,36 +3511,6 @@ static int omap_hwmod_allocate_module(struct device *dev, struct omap_hwmod *oh, oi->user = OCP_USER_MPU | OCP_USER_SDMA; } - if (!oh->_clk) { - struct clk_hw_omap *hwclk; - - clk = of_clk_get_by_name(dev->of_node, "fck"); - if (!IS_ERR(clk)) - clk_prepare(clk); - else - clk = NULL; - - /* - * Populate clockdomain based on dts clock. It is needed for - * clkdm_deny_idle() and clkdm_allow_idle() until we have have - * interconnect driver and reset driver capable of blocking - * clockdomain idle during reset, enable and idle. - */ - if (clk) { - hwclk = to_clk_hw_omap(__clk_get_hw(clk)); - if (hwclk && hwclk->clkdm_name) - clkdm = clkdm_lookup(hwclk->clkdm_name); - } - - /* - * Note that we assume interconnect driver manages the clocks - * and do not need to populate oh->_clk for dynamically - * allocated modules. - */ - clk_unprepare(clk); - clk_put(clk); - } - spin_lock_irqsave(&oh->_lock, flags); if (regs) oh->_mpu_rt_va = regs; @@ -3626,7 +3596,7 @@ int omap_hwmod_init_module(struct device *dev, u32 sysc_flags, idlemodes; int error; - if (!dev || !data) + if (!dev || !data || !data->name || !cookie) return -EINVAL; oh = _lookup(data->name); @@ -3697,7 +3667,8 @@ int omap_hwmod_init_module(struct device *dev, return error; return omap_hwmod_allocate_module(dev, oh, data, sysc_fields, - rev_offs, sysc_offs, syss_offs, + cookie->clkdm, rev_offs, + sysc_offs, syss_offs, sysc_flags, idlemodes); } diff --git a/arch/arm/mach-omap2/pdata-quirks.c b/arch/arm/mach-omap2/pdata-quirks.c index a2ecc5e69abb..b09cc4e8d240 100644 --- a/arch/arm/mach-omap2/pdata-quirks.c +++ b/arch/arm/mach-omap2/pdata-quirks.c @@ -29,6 +29,7 @@ #include #include +#include "clockdomain.h" #include "common.h" #include "common-board-devices.h" #include "control.h" @@ -463,6 +464,62 @@ static void __init dra7x_evm_mmc_quirk(void) } #endif +static struct clockdomain *ti_sysc_find_one_clockdomain(struct clk *clk) +{ + struct clockdomain *clkdm = NULL; + struct clk_hw_omap *hwclk; + + hwclk = to_clk_hw_omap(__clk_get_hw(clk)); + if (hwclk && hwclk->clkdm_name) + clkdm = clkdm_lookup(hwclk->clkdm_name); + + return clkdm; +} + +/** + * ti_sysc_clkdm_init - find clockdomain based on clock + * @fck: device functional clock + * @ick: device interface clock + * @dev: struct device + * + * Populate clockdomain based on clock. It is needed for + * clkdm_deny_idle() and clkdm_allow_idle() for blocking clockdomain + * clockdomain idle during reset, enable and idle. + * + * Note that we assume interconnect driver manages the clocks + * and do not need to populate oh->_clk for dynamically + * allocated modules. + */ +static int ti_sysc_clkdm_init(struct device *dev, + struct clk *fck, struct clk *ick, + struct ti_sysc_cookie *cookie) +{ + if (fck) + cookie->clkdm = ti_sysc_find_one_clockdomain(fck); + if (cookie->clkdm) + return 0; + if (ick) + cookie->clkdm = ti_sysc_find_one_clockdomain(ick); + if (cookie->clkdm) + return 0; + + return -ENODEV; +} + +static void ti_sysc_clkdm_deny_idle(struct device *dev, + const struct ti_sysc_cookie *cookie) +{ + if (cookie->clkdm) + clkdm_deny_idle(cookie->clkdm); +} + +static void ti_sysc_clkdm_allow_idle(struct device *dev, + const struct ti_sysc_cookie *cookie) +{ + if (cookie->clkdm) + clkdm_allow_idle(cookie->clkdm); +} + static int ti_sysc_enable_module(struct device *dev, const struct ti_sysc_cookie *cookie) { @@ -494,6 +551,9 @@ static struct of_dev_auxdata omap_auxdata_lookup[]; static struct ti_sysc_platform_data ti_sysc_pdata = { .auxdata = omap_auxdata_lookup, + .init_clockdomain = ti_sysc_clkdm_init, + .clkdm_deny_idle = ti_sysc_clkdm_deny_idle, + .clkdm_allow_idle = ti_sysc_clkdm_allow_idle, .init_module = omap_hwmod_init_module, .enable_module = ti_sysc_enable_module, .idle_module = ti_sysc_idle_module, diff --git a/drivers/bus/ti-sysc.c b/drivers/bus/ti-sysc.c index b72741668c92..e86f7850206a 100644 --- a/drivers/bus/ti-sysc.c +++ b/drivers/bus/ti-sysc.c @@ -422,6 +422,30 @@ static void sysc_disable_opt_clocks(struct sysc *ddata) } } +static void sysc_clkdm_deny_idle(struct sysc *ddata) +{ + struct ti_sysc_platform_data *pdata; + + if (ddata->legacy_mode) + return; + + pdata = dev_get_platdata(ddata->dev); + if (pdata && pdata->clkdm_deny_idle) + pdata->clkdm_deny_idle(ddata->dev, &ddata->cookie); +} + +static void sysc_clkdm_allow_idle(struct sysc *ddata) +{ + struct ti_sysc_platform_data *pdata; + + if (ddata->legacy_mode) + return; + + pdata = dev_get_platdata(ddata->dev); + if (pdata && pdata->clkdm_allow_idle) + pdata->clkdm_allow_idle(ddata->dev, &ddata->cookie); +} + /** * sysc_init_resets - init rstctrl reset line if configured * @ddata: device driver data @@ -795,6 +819,7 @@ static void sysc_show_registers(struct sysc *ddata) #define SYSC_IDLE_MASK (SYSC_NR_IDLEMODES - 1) +/* Caller needs to manage sysc_clkdm_deny_idle() and sysc_clkdm_allow_idle() */ static int sysc_enable_module(struct device *dev) { struct sysc *ddata; @@ -805,11 +830,6 @@ static int sysc_enable_module(struct device *dev) if (ddata->offsets[SYSC_SYSCONFIG] == -ENODEV) return 0; - /* - * TODO: Need to prevent clockdomain autoidle? - * See clkdm_deny_idle() in arch/mach-omap2/omap_hwmod.c - */ - regbits = ddata->cap->regbits; reg = sysc_read(ddata, ddata->offsets[SYSC_SYSCONFIG]); @@ -861,6 +881,7 @@ static int sysc_best_idle_mode(u32 idlemodes, u32 *best_mode) return 0; } +/* Caller needs to manage sysc_clkdm_deny_idle() and sysc_clkdm_allow_idle() */ static int sysc_disable_module(struct device *dev) { struct sysc *ddata; @@ -872,11 +893,6 @@ static int sysc_disable_module(struct device *dev) if (ddata->offsets[SYSC_SYSCONFIG] == -ENODEV) return 0; - /* - * TODO: Need to prevent clockdomain autoidle? - * See clkdm_deny_idle() in arch/mach-omap2/omap_hwmod.c - */ - regbits = ddata->cap->regbits; reg = sysc_read(ddata, ddata->offsets[SYSC_SYSCONFIG]); @@ -966,14 +982,16 @@ static int __maybe_unused sysc_runtime_suspend(struct device *dev) if (!ddata->enabled) return 0; + sysc_clkdm_deny_idle(ddata); + if (ddata->legacy_mode) { error = sysc_runtime_suspend_legacy(dev, ddata); if (error) - return error; + goto err_allow_idle; } else { error = sysc_disable_module(dev); if (error) - return error; + goto err_allow_idle; } sysc_disable_main_clocks(ddata); @@ -983,6 +1001,9 @@ static int __maybe_unused sysc_runtime_suspend(struct device *dev) ddata->enabled = false; +err_allow_idle: + sysc_clkdm_allow_idle(ddata); + return error; } @@ -996,10 +1017,12 @@ static int __maybe_unused sysc_runtime_resume(struct device *dev) if (ddata->enabled) return 0; + sysc_clkdm_deny_idle(ddata); + if (sysc_opt_clks_needed(ddata)) { error = sysc_enable_opt_clocks(ddata); if (error) - return error; + goto err_allow_idle; } error = sysc_enable_main_clocks(ddata); @@ -1018,6 +1041,8 @@ static int __maybe_unused sysc_runtime_resume(struct device *dev) ddata->enabled = true; + sysc_clkdm_allow_idle(ddata); + return 0; err_main_clocks: @@ -1025,6 +1050,8 @@ err_main_clocks: err_opt_clocks: if (sysc_opt_clks_needed(ddata)) sysc_disable_opt_clocks(ddata); +err_allow_idle: + sysc_clkdm_allow_idle(ddata); return error; } @@ -1245,6 +1272,33 @@ static void sysc_init_revision_quirks(struct sysc *ddata) } } +static int sysc_clockdomain_init(struct sysc *ddata) +{ + struct ti_sysc_platform_data *pdata = dev_get_platdata(ddata->dev); + struct clk *fck = NULL, *ick = NULL; + int error; + + if (!pdata || !pdata->init_clockdomain) + return 0; + + switch (ddata->nr_clocks) { + case 2: + ick = ddata->clocks[SYSC_ICK]; + /* fallthrough */ + case 1: + fck = ddata->clocks[SYSC_FCK]; + break; + case 0: + return 0; + } + + error = pdata->init_clockdomain(ddata->dev, fck, ick, &ddata->cookie); + if (!error || error == -ENODEV) + return 0; + + return error; +} + /* * Note that pdata->init_module() typically does a reset first. After * pdata->init_module() is done, PM runtime can be used for the interconnect @@ -1255,7 +1309,7 @@ static int sysc_legacy_init(struct sysc *ddata) struct ti_sysc_platform_data *pdata = dev_get_platdata(ddata->dev); int error; - if (!ddata->legacy_mode || !pdata || !pdata->init_module) + if (!pdata || !pdata->init_module) return 0; error = pdata->init_module(ddata->dev, ddata->mdata, &ddata->cookie); @@ -1347,7 +1401,13 @@ static int sysc_init_module(struct sysc *ddata) (SYSC_QUIRK_NO_IDLE | SYSC_QUIRK_NO_IDLE_ON_INIT)) manage_clocks = false; + error = sysc_clockdomain_init(ddata); + if (error) + return error; + if (manage_clocks) { + sysc_clkdm_deny_idle(ddata); + error = sysc_enable_opt_clocks(ddata); if (error) return error; @@ -1360,20 +1420,33 @@ static int sysc_init_module(struct sysc *ddata) ddata->revision = sysc_read_revision(ddata); sysc_init_revision_quirks(ddata); - error = sysc_legacy_init(ddata); - if (error) - goto err_main_clocks; + if (ddata->legacy_mode) { + error = sysc_legacy_init(ddata); + if (error) + goto err_main_clocks; + } + + if (!ddata->legacy_mode && manage_clocks) { + error = sysc_enable_module(ddata->dev); + if (error) + goto err_main_clocks; + } error = sysc_reset(ddata); if (error) dev_err(ddata->dev, "Reset failed with %d\n", error); + if (!ddata->legacy_mode && manage_clocks) + sysc_disable_module(ddata->dev); + err_main_clocks: if (manage_clocks) sysc_disable_main_clocks(ddata); err_opt_clocks: - if (manage_clocks) + if (manage_clocks) { sysc_disable_opt_clocks(ddata); + sysc_clkdm_allow_idle(ddata); + } return error; } @@ -2012,20 +2085,22 @@ static int sysc_init_pdata(struct sysc *ddata) struct ti_sysc_platform_data *pdata = dev_get_platdata(ddata->dev); struct ti_sysc_module_data *mdata; - if (!pdata || !ddata->legacy_mode) + if (!pdata) return 0; mdata = devm_kzalloc(ddata->dev, sizeof(*mdata), GFP_KERNEL); if (!mdata) return -ENOMEM; - mdata->name = ddata->legacy_mode; - mdata->module_pa = ddata->module_pa; - mdata->module_size = ddata->module_size; - mdata->offsets = ddata->offsets; - mdata->nr_offsets = SYSC_MAX_REGS; - mdata->cap = ddata->cap; - mdata->cfg = &ddata->cfg; + if (ddata->legacy_mode) { + mdata->name = ddata->legacy_mode; + mdata->module_pa = ddata->module_pa; + mdata->module_size = ddata->module_size; + mdata->offsets = ddata->offsets; + mdata->nr_offsets = SYSC_MAX_REGS; + mdata->cap = ddata->cap; + mdata->cfg = &ddata->cfg; + } ddata->mdata = mdata; diff --git a/include/linux/platform_data/ti-sysc.h b/include/linux/platform_data/ti-sysc.h index 9256c0305968..6626fd31e309 100644 --- a/include/linux/platform_data/ti-sysc.h +++ b/include/linux/platform_data/ti-sysc.h @@ -19,6 +19,7 @@ enum ti_sysc_module_type { struct ti_sysc_cookie { void *data; + void *clkdm; }; /** @@ -125,9 +126,16 @@ struct ti_sysc_module_data { }; struct device; +struct clk; struct ti_sysc_platform_data { struct of_dev_auxdata *auxdata; + int (*init_clockdomain)(struct device *dev, struct clk *fck, + struct clk *ick, struct ti_sysc_cookie *cookie); + void (*clkdm_deny_idle)(struct device *dev, + const struct ti_sysc_cookie *cookie); + void (*clkdm_allow_idle)(struct device *dev, + const struct ti_sysc_cookie *cookie); int (*init_module)(struct device *dev, const struct ti_sysc_module_data *data, struct ti_sysc_cookie *cookie); -- cgit v1.2.3 From e0db94fe87dacd72be0699adcc29e321db7f1689 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Mon, 27 May 2019 04:51:53 -0700 Subject: bus: ti-sysc: Make OCP reset work for sysstatus and sysconfig reset bits We've had minimal OCP softreset support in ti-sysc interconnect target module driver only used for MCAN driver so far. But it turns out that MCAN has the sysstatus register resetdone bit inverted compared to most other modules. Let's make OCP softreset work for other typical cases with reset status in sysstatus or sysconfig register so we can use the new functions for sysc_enable_module() and sysc_disable_module() without "ti,hwmods" property in the following patches. Tested-by: Keerthy Signed-off-by: Tony Lindgren --- drivers/bus/ti-sysc.c | 72 ++++++++++++++++++++++++++--------- include/linux/platform_data/ti-sysc.h | 1 + 2 files changed, 55 insertions(+), 18 deletions(-) (limited to 'include/linux/platform_data') diff --git a/drivers/bus/ti-sysc.c b/drivers/bus/ti-sysc.c index f00997eea207..f4a048430cd1 100644 --- a/drivers/bus/ti-sysc.c +++ b/drivers/bus/ti-sysc.c @@ -153,6 +153,26 @@ static u32 sysc_read_revision(struct sysc *ddata) return sysc_read(ddata, offset); } +static u32 sysc_read_sysconfig(struct sysc *ddata) +{ + int offset = ddata->offsets[SYSC_SYSCONFIG]; + + if (offset < 0) + return 0; + + return sysc_read(ddata, offset); +} + +static u32 sysc_read_sysstatus(struct sysc *ddata) +{ + int offset = ddata->offsets[SYSC_SYSSTATUS]; + + if (offset < 0) + return 0; + + return sysc_read(ddata, offset); +} + static int sysc_add_named_clock_from_child(struct sysc *ddata, const char *name, const char *optfck_name) @@ -1369,34 +1389,49 @@ static int sysc_rstctrl_reset_deassert(struct sysc *ddata, bool reset) return reset_control_deassert(ddata->rsts); } +/* + * Note that the caller must ensure the interconnect target module is enabled + * before calling reset. Otherwise reset will not complete. + */ static int sysc_reset(struct sysc *ddata) { - int offset = ddata->offsets[SYSC_SYSCONFIG]; - int val; + int sysc_offset, syss_offset, sysc_val, rstval, quirks, error = 0; + u32 sysc_mask, syss_done; + + sysc_offset = ddata->offsets[SYSC_SYSCONFIG]; + syss_offset = ddata->offsets[SYSC_SYSSTATUS]; + quirks = ddata->cfg.quirks; - if (ddata->legacy_mode || offset < 0 || + if (ddata->legacy_mode || sysc_offset < 0 || + ddata->cap->regbits->srst_shift < 0 || ddata->cfg.quirks & SYSC_QUIRK_NO_RESET_ON_INIT) return 0; - /* - * Currently only support reset status in sysstatus. - * Warn and return error in all other cases - */ - if (!ddata->cfg.syss_mask) { - dev_err(ddata->dev, "No ti,syss-mask. Reset failed\n"); - return -EINVAL; - } + sysc_mask = BIT(ddata->cap->regbits->srst_shift); - val = sysc_read(ddata, offset); - val |= (0x1 << ddata->cap->regbits->srst_shift); - sysc_write(ddata, offset, val); + if (ddata->cfg.quirks & SYSS_QUIRK_RESETDONE_INVERTED) + syss_done = 0; + else + syss_done = ddata->cfg.syss_mask; + + sysc_val = sysc_read_sysconfig(ddata); + sysc_val |= sysc_mask; + sysc_write(ddata, sysc_offset, sysc_val); /* Poll on reset status */ - offset = ddata->offsets[SYSC_SYSSTATUS]; + if (syss_offset >= 0) { + error = readx_poll_timeout(sysc_read_sysstatus, ddata, rstval, + (rstval & ddata->cfg.syss_mask) == + syss_done, + 100, MAX_MODULE_SOFTRESET_WAIT); + + } else if (ddata->cfg.quirks & SYSC_QUIRK_RESET_STATUS) { + error = readx_poll_timeout(sysc_read_sysconfig, ddata, rstval, + !(rstval & sysc_mask), + 100, MAX_MODULE_SOFTRESET_WAIT); + } - return readl_poll_timeout(ddata->module_va + offset, val, - (val & ddata->cfg.syss_mask) == 0x0, - 100, MAX_MODULE_SOFTRESET_WAIT); + return error; } /* @@ -2099,6 +2134,7 @@ static const struct sysc_capabilities sysc_dra7_mcan = { .type = TI_SYSC_DRA7_MCAN, .sysc_mask = SYSC_DRA7_MCAN_ENAWAKEUP | SYSC_OMAP4_SOFTRESET, .regbits = &sysc_regbits_dra7_mcan, + .mod_quirks = SYSS_QUIRK_RESETDONE_INVERTED, }; static int sysc_init_pdata(struct sysc *ddata) diff --git a/include/linux/platform_data/ti-sysc.h b/include/linux/platform_data/ti-sysc.h index 6626fd31e309..8822e99ff813 100644 --- a/include/linux/platform_data/ti-sysc.h +++ b/include/linux/platform_data/ti-sysc.h @@ -47,6 +47,7 @@ struct sysc_regbits { s8 emufree_shift; }; +#define SYSS_QUIRK_RESETDONE_INVERTED BIT(14) #define SYSC_QUIRK_SWSUP_MSTANDBY BIT(13) #define SYSC_QUIRK_SWSUP_SIDLE_ACT BIT(12) #define SYSC_QUIRK_SWSUP_SIDLE BIT(11) -- cgit v1.2.3 From 1210d1e6bad1e7ccccb19627b880a50d7c15dd51 Mon Sep 17 00:00:00 2001 From: Nick Crews Date: Tue, 21 May 2019 13:20:45 -0600 Subject: platform/chrome: wilco_ec: Add telemetry char device interface The Wilco Embedded Controller is able to send telemetry data which is useful for enterprise applications. A daemon running on the OS sends a command to the EC via a write() to a char device, and can read the response with a read(). The write() request is verified by the driver to ensure that it is performing only one of the whitelisted commands, and that no extraneous data is being transmitted to the EC. The response is passed directly back to the reader with no modification. The character device will appear as /dev/wilco_telemN, where N is some small non-negative integer, starting with 0. Only one process may have the file descriptor open at a time. The calling userspace program needs to keep the device file descriptor open between the calls to write() and read() in order to preserve the response. Up to 32 bytes will be available for reading. For testing purposes, try requesting the EC's firmware build date, by sending the WILCO_EC_TELEM_GET_VERSION command with argument index=3. i.e. write [0x38, 0x00, 0x03] to the device node. An ASCII string of the build date is returned. Signed-off-by: Nick Crews Signed-off-by: Enric Balletbo i Serra --- drivers/platform/chrome/wilco_ec/Kconfig | 7 + drivers/platform/chrome/wilco_ec/Makefile | 2 + drivers/platform/chrome/wilco_ec/core.c | 13 + drivers/platform/chrome/wilco_ec/debugfs.c | 2 +- drivers/platform/chrome/wilco_ec/telemetry.c | 450 +++++++++++++++++++++++++++ include/linux/platform_data/wilco-ec.h | 2 + 6 files changed, 475 insertions(+), 1 deletion(-) create mode 100644 drivers/platform/chrome/wilco_ec/telemetry.c (limited to 'include/linux/platform_data') diff --git a/drivers/platform/chrome/wilco_ec/Kconfig b/drivers/platform/chrome/wilco_ec/Kconfig index 2c8f6f15f28f..90336874af59 100644 --- a/drivers/platform/chrome/wilco_ec/Kconfig +++ b/drivers/platform/chrome/wilco_ec/Kconfig @@ -27,3 +27,10 @@ config WILCO_EC_EVENTS (such as power state changes) to userspace. The EC sends the events over ACPI, and a driver queues up the events to be read by a userspace daemon from /dev/wilco_event using read() and poll(). + +config WILCO_EC_TELEMETRY + tristate "Enable querying telemetry data from EC" + depends on WILCO_EC + help + If you say Y here, you get support to query EC telemetry data from + /dev/wilco_telem0 using write() and then read(). diff --git a/drivers/platform/chrome/wilco_ec/Makefile b/drivers/platform/chrome/wilco_ec/Makefile index 4d8a5f068f8b..bc817164596e 100644 --- a/drivers/platform/chrome/wilco_ec/Makefile +++ b/drivers/platform/chrome/wilco_ec/Makefile @@ -6,3 +6,5 @@ wilco_ec_debugfs-objs := debugfs.o obj-$(CONFIG_WILCO_EC_DEBUGFS) += wilco_ec_debugfs.o wilco_ec_events-objs := event.o obj-$(CONFIG_WILCO_EC_EVENTS) += wilco_ec_events.o +wilco_ec_telem-objs := telemetry.o +obj-$(CONFIG_WILCO_EC_TELEMETRY) += wilco_ec_telem.o diff --git a/drivers/platform/chrome/wilco_ec/core.c b/drivers/platform/chrome/wilco_ec/core.c index 45cf3a5ed062..3724bf4b77c6 100644 --- a/drivers/platform/chrome/wilco_ec/core.c +++ b/drivers/platform/chrome/wilco_ec/core.c @@ -93,8 +93,20 @@ static int wilco_ec_probe(struct platform_device *pdev) goto unregister_rtc; } + /* Register child device that will be found by the telemetry driver. */ + ec->telem_pdev = platform_device_register_data(dev, "wilco_telem", + PLATFORM_DEVID_AUTO, + ec, sizeof(*ec)); + if (IS_ERR(ec->telem_pdev)) { + dev_err(dev, "Failed to create telemetry platform device\n"); + ret = PTR_ERR(ec->telem_pdev); + goto remove_sysfs; + } + return 0; +remove_sysfs: + wilco_ec_remove_sysfs(ec); unregister_rtc: platform_device_unregister(ec->rtc_pdev); unregister_debugfs: @@ -109,6 +121,7 @@ static int wilco_ec_remove(struct platform_device *pdev) struct wilco_ec_device *ec = platform_get_drvdata(pdev); wilco_ec_remove_sysfs(ec); + platform_device_unregister(ec->telem_pdev); platform_device_unregister(ec->rtc_pdev); if (ec->debugfs_pdev) platform_device_unregister(ec->debugfs_pdev); diff --git a/drivers/platform/chrome/wilco_ec/debugfs.c b/drivers/platform/chrome/wilco_ec/debugfs.c index 281ec595e8e0..8d65a1e2f1a3 100644 --- a/drivers/platform/chrome/wilco_ec/debugfs.c +++ b/drivers/platform/chrome/wilco_ec/debugfs.c @@ -16,7 +16,7 @@ #define DRV_NAME "wilco-ec-debugfs" -/* The 256 raw bytes will take up more space when represented as a hex string */ +/* The raw bytes will take up more space when represented as a hex string */ #define FORMATTED_BUFFER_SIZE (EC_MAILBOX_DATA_SIZE * 4) struct wilco_ec_debugfs { diff --git a/drivers/platform/chrome/wilco_ec/telemetry.c b/drivers/platform/chrome/wilco_ec/telemetry.c new file mode 100644 index 000000000000..94cdc166c840 --- /dev/null +++ b/drivers/platform/chrome/wilco_ec/telemetry.c @@ -0,0 +1,450 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Telemetry communication for Wilco EC + * + * Copyright 2019 Google LLC + * + * The Wilco Embedded Controller is able to send telemetry data + * which is useful for enterprise applications. A daemon running on + * the OS sends a command to the EC via a write() to a char device, + * and can read the response with a read(). The write() request is + * verified by the driver to ensure that it is performing only one + * of the whitelisted commands, and that no extraneous data is + * being transmitted to the EC. The response is passed directly + * back to the reader with no modification. + * + * The character device will appear as /dev/wilco_telemN, where N + * is some small non-negative integer, starting with 0. Only one + * process may have the file descriptor open at a time. The calling + * userspace program needs to keep the device file descriptor open + * between the calls to write() and read() in order to preserve the + * response. Up to 32 bytes will be available for reading. + * + * For testing purposes, try requesting the EC's firmware build + * date, by sending the WILCO_EC_TELEM_GET_VERSION command with + * argument index=3. i.e. write [0x38, 0x00, 0x03] + * to the device node. An ASCII string of the build date is + * returned. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TELEM_DEV_NAME "wilco_telem" +#define TELEM_CLASS_NAME TELEM_DEV_NAME +#define DRV_NAME TELEM_DEV_NAME +#define TELEM_DEV_NAME_FMT (TELEM_DEV_NAME "%d") +static struct class telem_class = { + .owner = THIS_MODULE, + .name = TELEM_CLASS_NAME, +}; + +/* Keep track of all the device numbers used. */ +#define TELEM_MAX_DEV 128 +static int telem_major; +static DEFINE_IDA(telem_ida); + +/* EC telemetry command codes */ +#define WILCO_EC_TELEM_GET_LOG 0x99 +#define WILCO_EC_TELEM_GET_VERSION 0x38 +#define WILCO_EC_TELEM_GET_FAN_INFO 0x2E +#define WILCO_EC_TELEM_GET_DIAG_INFO 0xFA +#define WILCO_EC_TELEM_GET_TEMP_INFO 0x95 +#define WILCO_EC_TELEM_GET_TEMP_READ 0x2C +#define WILCO_EC_TELEM_GET_BATT_EXT_INFO 0x07 + +#define TELEM_ARGS_SIZE_MAX 30 + +/** + * struct wilco_ec_telem_request - Telemetry command and arguments sent to EC. + * @command: One of WILCO_EC_TELEM_GET_* command codes. + * @reserved: Must be 0. + * @args: The first N bytes are one of telem_args_get_* structs, the rest is 0. + */ +struct wilco_ec_telem_request { + u8 command; + u8 reserved; + u8 args[TELEM_ARGS_SIZE_MAX]; +} __packed; + +/* + * The following telem_args_get_* structs are embedded within the |args| field + * of wilco_ec_telem_request. + */ + +struct telem_args_get_log { + u8 log_type; + u8 log_index; +} __packed; + +/* + * Get a piece of info about the EC firmware version: + * 0 = label + * 1 = svn_rev + * 2 = model_no + * 3 = build_date + * 4 = frio_version + */ +struct telem_args_get_version { + u8 index; +} __packed; + +struct telem_args_get_fan_info { + u8 command; + u8 fan_number; + u8 arg; +} __packed; + +struct telem_args_get_diag_info { + u8 type; + u8 sub_type; +} __packed; + +struct telem_args_get_temp_info { + u8 command; + u8 index; + u8 field; + u8 zone; +} __packed; + +struct telem_args_get_temp_read { + u8 sensor_index; +} __packed; + +struct telem_args_get_batt_ext_info { + u8 var_args[5]; +} __packed; + +/** + * check_telem_request() - Ensure that a request from userspace is valid. + * @rq: Request buffer copied from userspace. + * @size: Number of bytes copied from userspace. + * + * Return: 0 if valid, -EINVAL if bad command or reserved byte is non-zero, + * -EMSGSIZE if the request is too long. + * + * We do not want to allow userspace to send arbitrary telemetry commands to + * the EC. Therefore we check to ensure that + * 1. The request follows the format of struct wilco_ec_telem_request. + * 2. The supplied command code is one of the whitelisted commands. + * 3. The request only contains the necessary data for the header and arguments. + */ +static int check_telem_request(struct wilco_ec_telem_request *rq, + size_t size) +{ + size_t max_size = offsetof(struct wilco_ec_telem_request, args); + + if (rq->reserved) + return -EINVAL; + + switch (rq->command) { + case WILCO_EC_TELEM_GET_LOG: + max_size += sizeof(struct telem_args_get_log); + break; + case WILCO_EC_TELEM_GET_VERSION: + max_size += sizeof(struct telem_args_get_version); + break; + case WILCO_EC_TELEM_GET_FAN_INFO: + max_size += sizeof(struct telem_args_get_fan_info); + break; + case WILCO_EC_TELEM_GET_DIAG_INFO: + max_size += sizeof(struct telem_args_get_diag_info); + break; + case WILCO_EC_TELEM_GET_TEMP_INFO: + max_size += sizeof(struct telem_args_get_temp_info); + break; + case WILCO_EC_TELEM_GET_TEMP_READ: + max_size += sizeof(struct telem_args_get_temp_read); + break; + case WILCO_EC_TELEM_GET_BATT_EXT_INFO: + max_size += sizeof(struct telem_args_get_batt_ext_info); + break; + default: + return -EINVAL; + } + + return (size <= max_size) ? 0 : -EMSGSIZE; +} + +/** + * struct telem_device_data - Data for a Wilco EC device that queries telemetry. + * @cdev: Char dev that userspace reads and polls from. + * @dev: Device associated with the %cdev. + * @ec: Wilco EC that we will be communicating with using the mailbox interface. + * @available: Boolean of if the device can be opened. + */ +struct telem_device_data { + struct device dev; + struct cdev cdev; + struct wilco_ec_device *ec; + atomic_t available; +}; + +#define TELEM_RESPONSE_SIZE EC_MAILBOX_DATA_SIZE + +/** + * struct telem_session_data - Data that exists between open() and release(). + * @dev_data: Pointer to get back to the device data and EC. + * @request: Command and arguments sent to EC. + * @response: Response buffer of data from EC. + * @has_msg: Is there data available to read from a previous write? + */ +struct telem_session_data { + struct telem_device_data *dev_data; + struct wilco_ec_telem_request request; + u8 response[TELEM_RESPONSE_SIZE]; + bool has_msg; +}; + +/** + * telem_open() - Callback for when the device node is opened. + * @inode: inode for this char device node. + * @filp: file for this char device node. + * + * We need to ensure that after writing a command to the device, + * the same userspace process reads the corresponding result. + * Therefore, we increment a refcount on opening the device, so that + * only one process can communicate with the EC at a time. + * + * Return: 0 on success, or negative error code on failure. + */ +static int telem_open(struct inode *inode, struct file *filp) +{ + struct telem_device_data *dev_data; + struct telem_session_data *sess_data; + + /* Ensure device isn't already open */ + dev_data = container_of(inode->i_cdev, struct telem_device_data, cdev); + if (atomic_cmpxchg(&dev_data->available, 1, 0) == 0) + return -EBUSY; + + get_device(&dev_data->dev); + + sess_data = kzalloc(sizeof(*sess_data), GFP_KERNEL); + if (!sess_data) { + atomic_set(&dev_data->available, 1); + return -ENOMEM; + } + sess_data->dev_data = dev_data; + sess_data->has_msg = false; + + nonseekable_open(inode, filp); + filp->private_data = sess_data; + + return 0; +} + +static ssize_t telem_write(struct file *filp, const char __user *buf, + size_t count, loff_t *pos) +{ + struct telem_session_data *sess_data = filp->private_data; + struct wilco_ec_message msg = {}; + int ret; + + if (count > sizeof(sess_data->request)) + return -EMSGSIZE; + if (copy_from_user(&sess_data->request, buf, count)) + return -EFAULT; + ret = check_telem_request(&sess_data->request, count); + if (ret < 0) + return ret; + + memset(sess_data->response, 0, sizeof(sess_data->response)); + msg.type = WILCO_EC_MSG_TELEMETRY; + msg.request_data = &sess_data->request; + msg.request_size = sizeof(sess_data->request); + msg.response_data = sess_data->response; + msg.response_size = sizeof(sess_data->response); + + ret = wilco_ec_mailbox(sess_data->dev_data->ec, &msg); + if (ret < 0) + return ret; + if (ret != sizeof(sess_data->response)) + return -EMSGSIZE; + + sess_data->has_msg = true; + + return count; +} + +static ssize_t telem_read(struct file *filp, char __user *buf, size_t count, + loff_t *pos) +{ + struct telem_session_data *sess_data = filp->private_data; + + if (!sess_data->has_msg) + return -ENODATA; + if (count > sizeof(sess_data->response)) + return -EINVAL; + + if (copy_to_user(buf, sess_data->response, count)) + return -EFAULT; + + sess_data->has_msg = false; + + return count; +} + +static int telem_release(struct inode *inode, struct file *filp) +{ + struct telem_session_data *sess_data = filp->private_data; + + atomic_set(&sess_data->dev_data->available, 1); + put_device(&sess_data->dev_data->dev); + kfree(sess_data); + + return 0; +} + +static const struct file_operations telem_fops = { + .open = telem_open, + .write = telem_write, + .read = telem_read, + .release = telem_release, + .llseek = no_llseek, + .owner = THIS_MODULE, +}; + +/** + * telem_device_free() - Callback to free the telem_device_data structure. + * @d: The device embedded in our device data, which we have been ref counting. + * + * Once all open file descriptors are closed and the device has been removed, + * the refcount of the device will fall to 0 and this will be called. + */ +static void telem_device_free(struct device *d) +{ + struct telem_device_data *dev_data; + + dev_data = container_of(d, struct telem_device_data, dev); + kfree(dev_data); +} + +/** + * telem_device_probe() - Callback when creating a new device. + * @pdev: platform device that we will be receiving telems from. + * + * This finds a free minor number for the device, allocates and initializes + * some device data, and creates a new device and char dev node. + * + * Return: 0 on success, negative error code on failure. + */ +static int telem_device_probe(struct platform_device *pdev) +{ + struct telem_device_data *dev_data; + int error, minor; + + /* Get the next available device number */ + minor = ida_alloc_max(&telem_ida, TELEM_MAX_DEV-1, GFP_KERNEL); + if (minor < 0) { + error = minor; + dev_err(&pdev->dev, "Failed to find minor number: %d", error); + return error; + } + + dev_data = kzalloc(sizeof(*dev_data), GFP_KERNEL); + if (!dev_data) { + ida_simple_remove(&telem_ida, minor); + return -ENOMEM; + } + + /* Initialize the device data */ + dev_data->ec = dev_get_platdata(&pdev->dev); + atomic_set(&dev_data->available, 1); + platform_set_drvdata(pdev, dev_data); + + /* Initialize the device */ + dev_data->dev.devt = MKDEV(telem_major, minor); + dev_data->dev.class = &telem_class; + dev_data->dev.release = telem_device_free; + dev_set_name(&dev_data->dev, TELEM_DEV_NAME_FMT, minor); + device_initialize(&dev_data->dev); + + /* Initialize the character device and add it to userspace */; + cdev_init(&dev_data->cdev, &telem_fops); + error = cdev_device_add(&dev_data->cdev, &dev_data->dev); + if (error) { + put_device(&dev_data->dev); + ida_simple_remove(&telem_ida, minor); + return error; + } + + return 0; +} + +static int telem_device_remove(struct platform_device *pdev) +{ + struct telem_device_data *dev_data = platform_get_drvdata(pdev); + + cdev_device_del(&dev_data->cdev, &dev_data->dev); + put_device(&dev_data->dev); + ida_simple_remove(&telem_ida, MINOR(dev_data->dev.devt)); + + return 0; +} + +static struct platform_driver telem_driver = { + .probe = telem_device_probe, + .remove = telem_device_remove, + .driver = { + .name = DRV_NAME, + }, +}; + +static int __init telem_module_init(void) +{ + dev_t dev_num = 0; + int ret; + + ret = class_register(&telem_class); + if (ret) { + pr_err(DRV_NAME ": Failed registering class: %d", ret); + return ret; + } + + /* Request the kernel for device numbers, starting with minor=0 */ + ret = alloc_chrdev_region(&dev_num, 0, TELEM_MAX_DEV, TELEM_DEV_NAME); + if (ret) { + pr_err(DRV_NAME ": Failed allocating dev numbers: %d", ret); + goto destroy_class; + } + telem_major = MAJOR(dev_num); + + ret = platform_driver_register(&telem_driver); + if (ret < 0) { + pr_err(DRV_NAME ": Failed registering driver: %d\n", ret); + goto unregister_region; + } + + return 0; + +unregister_region: + unregister_chrdev_region(MKDEV(telem_major, 0), TELEM_MAX_DEV); +destroy_class: + class_unregister(&telem_class); + ida_destroy(&telem_ida); + return ret; +} + +static void __exit telem_module_exit(void) +{ + platform_driver_unregister(&telem_driver); + unregister_chrdev_region(MKDEV(telem_major, 0), TELEM_MAX_DEV); + class_unregister(&telem_class); + ida_destroy(&telem_ida); +} + +module_init(telem_module_init); +module_exit(telem_module_exit); + +MODULE_AUTHOR("Nick Crews "); +MODULE_DESCRIPTION("Wilco EC telemetry driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/include/linux/platform_data/wilco-ec.h b/include/linux/platform_data/wilco-ec.h index e3ce9ce49b11..ad03b586a095 100644 --- a/include/linux/platform_data/wilco-ec.h +++ b/include/linux/platform_data/wilco-ec.h @@ -29,6 +29,7 @@ * @data_size: Size of the data buffer used for EC communication. * @debugfs_pdev: The child platform_device used by the debugfs sub-driver. * @rtc_pdev: The child platform_device used by the RTC sub-driver. + * @telem_pdev: The child platform_device used by the telemetry sub-driver. */ struct wilco_ec_device { struct device *dev; @@ -40,6 +41,7 @@ struct wilco_ec_device { size_t data_size; struct platform_device *debugfs_pdev; struct platform_device *rtc_pdev; + struct platform_device *telem_pdev; }; /** -- cgit v1.2.3 From 3e582c6e911ffe6c90b9f90324bdf85fc728d0c3 Mon Sep 17 00:00:00 2001 From: Leilk Liu Date: Wed, 5 Jun 2019 11:07:04 +0800 Subject: spi: mediatek: add SPI_LSB_FIRST support this patch add SPI_LSB_FIRST feature support. Signed-off-by: Leilk Liu Signed-off-by: Mark Brown --- drivers/spi/spi-mt65xx.c | 15 ++++++--------- include/linux/platform_data/spi-mt65xx.h | 2 -- 2 files changed, 6 insertions(+), 11 deletions(-) (limited to 'include/linux/platform_data') diff --git a/drivers/spi/spi-mt65xx.c b/drivers/spi/spi-mt65xx.c index 0cce6f0ba824..7f4dc1844789 100644 --- a/drivers/spi/spi-mt65xx.c +++ b/drivers/spi/spi-mt65xx.c @@ -131,8 +131,6 @@ static const struct mtk_spi_compatible mt8183_compat = { * supplies it. */ static const struct mtk_chip_config mtk_default_chip_info = { - .rx_mlsb = 1, - .tx_mlsb = 1, .cs_pol = 0, .sample_sel = 0, }; @@ -203,14 +201,13 @@ static int mtk_spi_prepare_message(struct spi_master *master, reg_val &= ~SPI_CMD_CPOL; /* set the mlsbx and mlsbtx */ - if (chip_config->tx_mlsb) - reg_val |= SPI_CMD_TXMSBF; - else + if (spi->mode & SPI_LSB_FIRST) { reg_val &= ~SPI_CMD_TXMSBF; - if (chip_config->rx_mlsb) - reg_val |= SPI_CMD_RXMSBF; - else reg_val &= ~SPI_CMD_RXMSBF; + } else { + reg_val |= SPI_CMD_TXMSBF; + reg_val |= SPI_CMD_RXMSBF; + } /* set the tx/rx endian */ #ifdef __LITTLE_ENDIAN @@ -607,7 +604,7 @@ static int mtk_spi_probe(struct platform_device *pdev) master->auto_runtime_pm = true; master->dev.of_node = pdev->dev.of_node; - master->mode_bits = SPI_CPOL | SPI_CPHA; + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST; master->set_cs = mtk_spi_set_cs; master->prepare_message = mtk_spi_prepare_message; diff --git a/include/linux/platform_data/spi-mt65xx.h b/include/linux/platform_data/spi-mt65xx.h index ba4e4bb70262..8d5df58a13ef 100644 --- a/include/linux/platform_data/spi-mt65xx.h +++ b/include/linux/platform_data/spi-mt65xx.h @@ -14,8 +14,6 @@ /* Board specific platform_data */ struct mtk_chip_config { - u32 tx_mlsb; - u32 rx_mlsb; u32 cs_pol; u32 sample_sel; }; -- cgit v1.2.3 From 4e23be473e3063a9d3bc06bb0aee89885fffab0e Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Mon, 10 Jun 2019 04:48:05 -0700 Subject: bus: ti-sysc: Add support for module specific reset quirks Some older interconnect target modules need module internal clock toggling quirks to reset properly. We've been doing this in the platform code earlier, but need to be able to it directly in the ti-sysc driver when we no longer rely on on the platform code. Let's add reset handling for 1-wire, i2c and watchdog. Later on we can add more modules like msdi and dss as they get tested. For dra7 pcie, we should be able to just use the rstctrl reset driver when available. Signed-off-by: Tony Lindgren --- drivers/bus/ti-sysc.c | 129 ++++++++++++++++++++++++++++++++-- include/linux/platform_data/ti-sysc.h | 3 + 2 files changed, 127 insertions(+), 5 deletions(-) (limited to 'include/linux/platform_data') diff --git a/drivers/bus/ti-sysc.c b/drivers/bus/ti-sysc.c index a366ae548ec9..e6deabd8305d 100644 --- a/drivers/bus/ti-sysc.c +++ b/drivers/bus/ti-sysc.c @@ -71,6 +71,9 @@ static const char * const clock_names[SYSC_MAX_CLOCKS] = { * @name: name if available * @revision: interconnect target module revision * @needs_resume: runtime resume needed on resume from suspend + * @clk_enable_quirk: module specific clock enable quirk + * @clk_disable_quirk: module specific clock disable quirk + * @reset_done_quirk: module specific reset done quirk */ struct sysc { struct device *dev; @@ -94,6 +97,9 @@ struct sysc { unsigned int child_needs_resume:1; unsigned int disable_on_idle:1; struct delayed_work idle_work; + void (*clk_enable_quirk)(struct sysc *sysc); + void (*clk_disable_quirk)(struct sysc *sysc); + void (*reset_done_quirk)(struct sysc *sysc); }; static void sysc_parse_dts_quirks(struct sysc *ddata, struct device_node *np, @@ -760,8 +766,11 @@ static int sysc_ioremap(struct sysc *ddata) ddata->offsets[SYSC_SYSCONFIG], ddata->offsets[SYSC_SYSSTATUS]); + if (size < SZ_1K) + size = SZ_1K; + if ((size + sizeof(u32)) > ddata->module_size) - return -EINVAL; + size = ddata->module_size; } ddata->module_va = devm_ioremap(ddata->dev, @@ -1234,6 +1243,22 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = { SYSC_QUIRK_EXT_OPT_CLOCK | SYSC_QUIRK_NO_RESET_ON_INIT | SYSC_QUIRK_SWSUP_SIDLE), + /* Quirks that need to be set based on detected module */ + SYSC_QUIRK("hdq1w", 0, 0, 0x14, 0x18, 0x00000006, 0xffffffff, + SYSC_MODULE_QUIRK_HDQ1W), + SYSC_QUIRK("hdq1w", 0, 0, 0x14, 0x18, 0x0000000a, 0xffffffff, + SYSC_MODULE_QUIRK_HDQ1W), + SYSC_QUIRK("i2c", 0, 0, 0x20, 0x10, 0x00000036, 0x000000ff, + SYSC_MODULE_QUIRK_I2C), + SYSC_QUIRK("i2c", 0, 0, 0x20, 0x10, 0x0000003c, 0x000000ff, + SYSC_MODULE_QUIRK_I2C), + SYSC_QUIRK("i2c", 0, 0, 0x20, 0x10, 0x00000040, 0x000000ff, + SYSC_MODULE_QUIRK_I2C), + SYSC_QUIRK("i2c", 0, 0, 0x10, 0x90, 0x5040000a, 0xfffff0f0, + SYSC_MODULE_QUIRK_I2C), + SYSC_QUIRK("wdt", 0, 0, 0x10, 0x14, 0x502a0500, 0xfffff0f0, + SYSC_MODULE_QUIRK_WDT), + #ifdef DEBUG SYSC_QUIRK("adc", 0, 0, 0x10, -1, 0x47300001, 0xffffffff, 0), SYSC_QUIRK("atl", 0, 0, -1, -1, 0x0a070100, 0xffffffff, 0), @@ -1247,11 +1272,8 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = { SYSC_QUIRK("dwc3", 0, 0, 0x10, -1, 0x500a0200, 0xffffffff, 0), SYSC_QUIRK("epwmss", 0, 0, 0x4, -1, 0x47400001, 0xffffffff, 0), SYSC_QUIRK("gpu", 0, 0x1fc00, 0x1fc10, -1, 0, 0, 0), - SYSC_QUIRK("hdq1w", 0, 0, 0x14, 0x18, 0x00000006, 0xffffffff, 0), - SYSC_QUIRK("hdq1w", 0, 0, 0x14, 0x18, 0x0000000a, 0xffffffff, 0), SYSC_QUIRK("hsi", 0, 0, 0x10, 0x14, 0x50043101, 0xffffffff, 0), SYSC_QUIRK("iss", 0, 0, 0x10, -1, 0x40000101, 0xffffffff, 0), - SYSC_QUIRK("i2c", 0, 0, 0x10, 0x90, 0x5040000a, 0xfffff0f0, 0), SYSC_QUIRK("lcdc", 0, 0, 0x54, -1, 0x4f201000, 0xffffffff, 0), SYSC_QUIRK("mcasp", 0, 0, 0x4, -1, 0x44306302, 0xffffffff, 0), SYSC_QUIRK("mcasp", 0, 0, 0x4, -1, 0x44307b02, 0xffffffff, 0), @@ -1287,7 +1309,6 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = { SYSC_QUIRK("usb_host_hs", 0, 0, 0x10, -1, 0x50700101, 0xffffffff, 0), SYSC_QUIRK("usb_otg_hs", 0, 0x400, 0x404, 0x408, 0x00000050, 0xffffffff, 0), - SYSC_QUIRK("wdt", 0, 0, 0x10, 0x14, 0x502a0500, 0xfffff0f0, 0), SYSC_QUIRK("vfpe", 0, 0, 0x104, -1, 0x4d001200, 0xffffffff, 0), #endif }; @@ -1360,6 +1381,94 @@ static void sysc_init_revision_quirks(struct sysc *ddata) } } +/* 1-wire needs module's internal clocks enabled for reset */ +static void sysc_clk_enable_quirk_hdq1w(struct sysc *ddata) +{ + int offset = 0x0c; /* HDQ_CTRL_STATUS */ + u16 val; + + val = sysc_read(ddata, offset); + val |= BIT(5); + sysc_write(ddata, offset, val); +} + +/* I2C needs extra enable bit toggling for reset */ +static void sysc_clk_quirk_i2c(struct sysc *ddata, bool enable) +{ + int offset; + u16 val; + + /* I2C_CON, omap2/3 is different from omap4 and later */ + if ((ddata->revision & 0xffffff00) == 0x001f0000) + offset = 0x24; + else + offset = 0xa4; + + /* I2C_EN */ + val = sysc_read(ddata, offset); + if (enable) + val |= BIT(15); + else + val &= ~BIT(15); + sysc_write(ddata, offset, val); +} + +static void sysc_clk_enable_quirk_i2c(struct sysc *ddata) +{ + sysc_clk_quirk_i2c(ddata, true); +} + +static void sysc_clk_disable_quirk_i2c(struct sysc *ddata) +{ + sysc_clk_quirk_i2c(ddata, false); +} + +/* Watchdog timer needs a disable sequence after reset */ +static void sysc_reset_done_quirk_wdt(struct sysc *ddata) +{ + int wps, spr, error; + u32 val; + + wps = 0x34; + spr = 0x48; + + sysc_write(ddata, spr, 0xaaaa); + error = readl_poll_timeout(ddata->module_va + wps, val, + !(val & 0x10), 100, + MAX_MODULE_SOFTRESET_WAIT); + if (error) + dev_warn(ddata->dev, "wdt disable spr failed\n"); + + sysc_write(ddata, wps, 0x5555); + error = readl_poll_timeout(ddata->module_va + wps, val, + !(val & 0x10), 100, + MAX_MODULE_SOFTRESET_WAIT); + if (error) + dev_warn(ddata->dev, "wdt disable wps failed\n"); +} + +static void sysc_init_module_quirks(struct sysc *ddata) +{ + if (ddata->legacy_mode || !ddata->name) + return; + + if (ddata->cfg.quirks & SYSC_MODULE_QUIRK_HDQ1W) { + ddata->clk_enable_quirk = sysc_clk_enable_quirk_hdq1w; + + return; + } + + if (ddata->cfg.quirks & SYSC_MODULE_QUIRK_I2C) { + ddata->clk_enable_quirk = sysc_clk_enable_quirk_i2c; + ddata->clk_disable_quirk = sysc_clk_disable_quirk_i2c; + + return; + } + + if (ddata->cfg.quirks & SYSC_MODULE_QUIRK_WDT) + ddata->reset_done_quirk = sysc_reset_done_quirk_wdt; +} + static int sysc_clockdomain_init(struct sysc *ddata) { struct ti_sysc_platform_data *pdata = dev_get_platdata(ddata->dev); @@ -1468,10 +1577,16 @@ static int sysc_reset(struct sysc *ddata) else syss_done = ddata->cfg.syss_mask; + if (ddata->clk_disable_quirk) + ddata->clk_disable_quirk(ddata); + sysc_val = sysc_read_sysconfig(ddata); sysc_val |= sysc_mask; sysc_write(ddata, sysc_offset, sysc_val); + if (ddata->clk_enable_quirk) + ddata->clk_enable_quirk(ddata); + /* Poll on reset status */ if (syss_offset >= 0) { error = readx_poll_timeout(sysc_read_sysstatus, ddata, rstval, @@ -1485,6 +1600,9 @@ static int sysc_reset(struct sysc *ddata) 100, MAX_MODULE_SOFTRESET_WAIT); } + if (ddata->reset_done_quirk) + ddata->reset_done_quirk(ddata); + return error; } @@ -1531,6 +1649,7 @@ static int sysc_init_module(struct sysc *ddata) ddata->revision = sysc_read_revision(ddata); sysc_init_revision_quirks(ddata); + sysc_init_module_quirks(ddata); if (ddata->legacy_mode) { error = sysc_legacy_init(ddata); diff --git a/include/linux/platform_data/ti-sysc.h b/include/linux/platform_data/ti-sysc.h index 8822e99ff813..0c587d4fc718 100644 --- a/include/linux/platform_data/ti-sysc.h +++ b/include/linux/platform_data/ti-sysc.h @@ -47,6 +47,9 @@ struct sysc_regbits { s8 emufree_shift; }; +#define SYSC_MODULE_QUIRK_HDQ1W BIT(17) +#define SYSC_MODULE_QUIRK_I2C BIT(16) +#define SYSC_MODULE_QUIRK_WDT BIT(15) #define SYSS_QUIRK_RESETDONE_INVERTED BIT(14) #define SYSC_QUIRK_SWSUP_MSTANDBY BIT(13) #define SYSC_QUIRK_SWSUP_SIDLE_ACT BIT(12) -- cgit v1.2.3 From 18bd49c4c7c22a59634c8142d8618f5da8d29250 Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 10 Jun 2019 20:11:00 +0300 Subject: gpio: omap: constify register tables We must never alter the register tables; these are read-only as far as the driver is concerned. Constify these tables. Signed-off-by: Russell King Signed-off-by: Grygorii Strashko Tested-by: Tony Lindgren Signed-off-by: Linus Walleij --- drivers/gpio/gpio-omap.c | 12 ++++++------ include/linux/platform_data/gpio-omap.h | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'include/linux/platform_data') diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index 1c5fa12bcf9f..039bbb1ae6cb 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -48,6 +48,8 @@ struct gpio_regs { struct gpio_bank { void __iomem *base; + const struct omap_gpio_reg_offs *regs; + int irq; u32 non_wakeup_gpios; u32 enabled_non_wakeup_gpios; @@ -75,8 +77,6 @@ struct gpio_bank { void (*set_dataout)(struct gpio_bank *bank, unsigned gpio, int enable); int (*get_context_loss_count)(struct device *dev); - - struct omap_gpio_reg_offs *regs; }; #define GPIO_MOD_CTRL_BIT BIT(0) @@ -1075,7 +1075,7 @@ static int omap_gpio_chip_init(struct gpio_bank *bank, struct irq_chip *irqc) static void omap_gpio_init_context(struct gpio_bank *p) { - struct omap_gpio_reg_offs *regs = p->regs; + const struct omap_gpio_reg_offs *regs = p->regs; void __iomem *base = p->base; p->context.ctrl = readl_relaxed(base + regs->ctrl); @@ -1094,7 +1094,7 @@ static void omap_gpio_init_context(struct gpio_bank *p) static void omap_gpio_restore_context(struct gpio_bank *bank) { - struct omap_gpio_reg_offs *regs = bank->regs; + const struct omap_gpio_reg_offs *regs = bank->regs; void __iomem *base = bank->base; writel_relaxed(bank->context.wake_en, base + regs->wkup_en); @@ -1267,7 +1267,7 @@ static int gpio_omap_cpu_notifier(struct notifier_block *nb, return NOTIFY_OK; } -static struct omap_gpio_reg_offs omap2_gpio_regs = { +static const struct omap_gpio_reg_offs omap2_gpio_regs = { .revision = OMAP24XX_GPIO_REVISION, .direction = OMAP24XX_GPIO_OE, .datain = OMAP24XX_GPIO_DATAIN, @@ -1290,7 +1290,7 @@ static struct omap_gpio_reg_offs omap2_gpio_regs = { .fallingdetect = OMAP24XX_GPIO_FALLINGDETECT, }; -static struct omap_gpio_reg_offs omap4_gpio_regs = { +static const struct omap_gpio_reg_offs omap4_gpio_regs = { .revision = OMAP4_GPIO_REVISION, .direction = OMAP4_GPIO_OE, .datain = OMAP4_GPIO_DATAIN, diff --git a/include/linux/platform_data/gpio-omap.h b/include/linux/platform_data/gpio-omap.h index 7c36370c062e..1ca400005233 100644 --- a/include/linux/platform_data/gpio-omap.h +++ b/include/linux/platform_data/gpio-omap.h @@ -200,7 +200,7 @@ struct omap_gpio_platform_data { bool is_mpuio; /* whether the bank is of type MPUIO */ u32 non_wakeup_gpios; - struct omap_gpio_reg_offs *regs; + const struct omap_gpio_reg_offs *regs; /* Return context loss count due to PM states changing */ int (*get_context_loss_count)(struct device *dev); -- cgit v1.2.3 From e0668f28888184f6c633110a37386f2d4a6fa00e Mon Sep 17 00:00:00 2001 From: Yurii Pavlovskyi Date: Tue, 14 May 2019 21:00:31 +0200 Subject: platform/x86: asus-wmi: Improve DSTS WMI method ID detection The DSTS method detection mistakenly selects DCTS instead of DSTS if nothing is returned when the method ID is not defined in WMNB. As a result, the control of keyboard backlight is not functional for TUF Gaming series laptops. Implement detection based on _UID of the WMI device instead. There is evidence that DCTS is handled by ACPI WMI devices that have _UID ASUSWMI, whereas none of the devices without ASUSWMI respond to DCTS and DSTS is used instead [1]. DSDT examples: FX505GM (_UID ATK): Method (WMNB, 3, Serialized) { ... If ((Local0 == 0x53545344)) { ... Return (Zero) } ... // No return } K54C (_UID ATK): Method (WMNB, 3, Serialized) { ... If ((Local0 == 0x53545344)) { ... Return (0x02) } ... Return (0xFFFFFFFE) } [1] Link: https://lkml.org/lkml/2019/4/11/322 Signed-off-by: Yurii Pavlovskyi Suggested-by: Daniel Drake Signed-off-by: Andy Shevchenko --- drivers/hid/hid-asus.c | 2 +- drivers/platform/x86/asus-wmi.c | 23 ++++++++++++++++++++--- include/linux/platform_data/x86/asus-wmi.h | 4 ++-- 3 files changed, 23 insertions(+), 6 deletions(-) (limited to 'include/linux/platform_data') diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c index 336aeaed1159..1d01fe23ca0c 100644 --- a/drivers/hid/hid-asus.c +++ b/drivers/hid/hid-asus.c @@ -396,7 +396,7 @@ static bool asus_kbd_wmi_led_control_present(struct hid_device *hdev) if (!IS_ENABLED(CONFIG_ASUS_WMI)) return false; - ret = asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS2, + ret = asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS, ASUS_WMI_DEVID_KBD_BACKLIGHT, 0, &value); hid_dbg(hdev, "WMI backlight check: rc %d value %x", ret, value); if (ret) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index c67f11e0d6e7..ef526dcfeac5 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -83,6 +83,8 @@ MODULE_LICENSE("GPL"); #define USB_INTEL_XUSB2PR 0xD0 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31 +#define ASUS_ACPI_UID_ASUSWMI "ASUSWMI" + static const char * const ashs_ids[] = { "ATK4001", "ATK4002", NULL }; static bool ashs_present(void) @@ -1874,6 +1876,8 @@ static int asus_wmi_sysfs_init(struct platform_device *device) */ static int asus_wmi_platform_init(struct asus_wmi *asus) { + struct device *dev = &asus->platform_device->dev; + char *wmi_uid; int rv; /* INIT enable hotkeys on some models */ @@ -1903,11 +1907,24 @@ static int asus_wmi_platform_init(struct asus_wmi *asus) * Note, on most Eeepc, there is no way to check if a method exist * or note, while on notebooks, they returns 0xFFFFFFFE on failure, * but once again, SPEC may probably be used for that kind of things. + * + * Additionally at least TUF Gaming series laptops return nothing for + * unknown methods, so the detection in this way is not possible. + * + * There is strong indication that only ACPI WMI devices that have _UID + * equal to "ASUSWMI" use DCTS whereas those with "ATK" use DSTS. */ - if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS, 0, 0, NULL)) + wmi_uid = wmi_get_acpi_device_uid(ASUS_WMI_MGMT_GUID); + if (!wmi_uid) + return -ENODEV; + + if (!strcmp(wmi_uid, ASUS_ACPI_UID_ASUSWMI)) { + dev_info(dev, "Detected ASUSWMI, use DCTS\n"); + asus->dsts_id = ASUS_WMI_METHODID_DCTS; + } else { + dev_info(dev, "Detected %s, not ASUSWMI, use DSTS\n", wmi_uid); asus->dsts_id = ASUS_WMI_METHODID_DSTS; - else - asus->dsts_id = ASUS_WMI_METHODID_DSTS2; + } /* CWAP allow to define the behavior of the Fn+F2 key, * this method doesn't seems to be present on Eee PCs */ diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h index bfba245636a7..0668f76df921 100644 --- a/include/linux/platform_data/x86/asus-wmi.h +++ b/include/linux/platform_data/x86/asus-wmi.h @@ -18,8 +18,8 @@ #define ASUS_WMI_METHODID_GDSP 0x50534447 /* Get DiSPlay output */ #define ASUS_WMI_METHODID_DEVP 0x50564544 /* DEVice Policy */ #define ASUS_WMI_METHODID_OSVR 0x5256534F /* OS VeRsion */ -#define ASUS_WMI_METHODID_DSTS 0x53544344 /* Device STatuS */ -#define ASUS_WMI_METHODID_DSTS2 0x53545344 /* Device STatuS #2*/ +#define ASUS_WMI_METHODID_DCTS 0x53544344 /* Device status (DCTS) */ +#define ASUS_WMI_METHODID_DSTS 0x53545344 /* Device status (DSTS) */ #define ASUS_WMI_METHODID_BSTS 0x53545342 /* Bios STatuS ? */ #define ASUS_WMI_METHODID_DEVS 0x53564544 /* DEVice Set */ #define ASUS_WMI_METHODID_CFVS 0x53564643 /* CPU Frequency Volt Set */ -- cgit v1.2.3 From b096f626a6827ad2ced5ebdbdc04e62422d463f6 Mon Sep 17 00:00:00 2001 From: Yurii Pavlovskyi Date: Tue, 14 May 2019 21:07:05 +0200 Subject: platform/x86: asus-wmi: Switch fan boost mode The WMI exposes a write-only device ID where up to three fan modes can be switched on some laptops (TUF Gaming FX505GM). There is a hotkey combination Fn-F5 that does have a fan icon, which is designed to toggle between fan modes. The DSTS of the device ID returns information about the presence of this capability and the presence of each of the two additional fan modes as a bitmask (0x01 - overboost present, 0x02 - silent present) [1]. Add a SysFS entry that reads the last written value and updates value in WMI on write and a hotkey handler that toggles the modes taking into account their availability according to DSTS. Modes: * 0x00 - normal or balanced, * 0x01 - overboost, increased fan RPM, * 0x02 - silent, decreased fan RPM [1] Link: https://lkml.org/lkml/2019/4/12/110 Signed-off-by: Yurii Pavlovskyi Suggested-by: Daniel Drake Signed-off-by: Andy Shevchenko --- Documentation/ABI/testing/sysfs-platform-asus-wmi | 10 ++ drivers/platform/x86/asus-wmi.c | 151 ++++++++++++++++++++-- include/linux/platform_data/x86/asus-wmi.h | 1 + 3 files changed, 154 insertions(+), 8 deletions(-) (limited to 'include/linux/platform_data') diff --git a/Documentation/ABI/testing/sysfs-platform-asus-wmi b/Documentation/ABI/testing/sysfs-platform-asus-wmi index 019e1e29370e..87ae5cc983bf 100644 --- a/Documentation/ABI/testing/sysfs-platform-asus-wmi +++ b/Documentation/ABI/testing/sysfs-platform-asus-wmi @@ -36,3 +36,13 @@ KernelVersion: 3.5 Contact: "AceLan Kao" Description: Resume on lid open. 1 means on, 0 means off. + +What: /sys/devices/platform//fan_mode +Date: Apr 2019 +KernelVersion: 5.2 +Contact: "Yurii Pavlovskyi" +Description: + Fan boost mode: + * 0 - normal, + * 1 - overboost, + * 2 - silent diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index a1d85667383c..5712bc56fa10 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -70,6 +70,7 @@ MODULE_LICENSE("GPL"); #define NOTIFY_KBD_BRTUP 0xc4 #define NOTIFY_KBD_BRTDWN 0xc5 #define NOTIFY_KBD_BRTTOGGLE 0xc7 +#define NOTIFY_KBD_FBM 0x99 #define ASUS_WMI_FNLOCK_BIOS_DISABLED BIT(0) @@ -80,6 +81,13 @@ MODULE_LICENSE("GPL"); #define ASUS_FAN_CTRL_MANUAL 1 #define ASUS_FAN_CTRL_AUTO 2 +#define ASUS_FAN_MODE_NORMAL 0 +#define ASUS_FAN_MODE_OVERBOOST 1 +#define ASUS_FAN_MODE_OVERBOOST_MASK 0x01 +#define ASUS_FAN_MODE_SILENT 2 +#define ASUS_FAN_MODE_SILENT_MASK 0x02 +#define ASUS_FAN_MODES_MASK 0x03 + #define USB_INTEL_XUSB2PR 0xD0 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31 @@ -187,6 +195,10 @@ struct asus_wmi { int asus_hwmon_num_fans; int asus_hwmon_pwm; + bool fan_mode_available; + u8 fan_mode_mask; + u8 fan_mode; + struct hotplug_slot hotplug_slot; struct mutex hotplug_lock; struct mutex wmi_lock; @@ -1483,6 +1495,116 @@ static int asus_wmi_fan_init(struct asus_wmi *asus) return 0; } +/* Fan mode *******************************************************************/ + +static int fan_mode_check_present(struct asus_wmi *asus) +{ + u32 result; + int err; + + asus->fan_mode_available = false; + + err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FAN_MODE, &result); + if (err) { + if (err == -ENODEV) + return 0; + else + return err; + } + + if ((result & ASUS_WMI_DSTS_PRESENCE_BIT) && + (result & ASUS_FAN_MODES_MASK)) { + asus->fan_mode_available = true; + asus->fan_mode_mask = result & ASUS_FAN_MODES_MASK; + } + + return 0; +} + +static int fan_mode_write(struct asus_wmi *asus) +{ + int err; + u8 value; + u32 retval; + + value = asus->fan_mode; + + pr_info("Set fan mode: %u\n", value); + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_FAN_MODE, value, &retval); + + if (err) { + pr_warn("Failed to set fan mode: %d\n", err); + return err; + } + + if (retval != 1) { + pr_warn("Failed to set fan mode (retval): 0x%x\n", retval); + return -EIO; + } + + return 0; +} + +static int fan_mode_switch_next(struct asus_wmi *asus) +{ + if (asus->fan_mode == ASUS_FAN_MODE_NORMAL) { + if (asus->fan_mode_mask & ASUS_FAN_MODE_OVERBOOST_MASK) + asus->fan_mode = ASUS_FAN_MODE_OVERBOOST; + else if (asus->fan_mode_mask & ASUS_FAN_MODE_SILENT_MASK) + asus->fan_mode = ASUS_FAN_MODE_SILENT; + } else if (asus->fan_mode == ASUS_FAN_MODE_OVERBOOST) { + if (asus->fan_mode_mask & ASUS_FAN_MODE_SILENT_MASK) + asus->fan_mode = ASUS_FAN_MODE_SILENT; + else + asus->fan_mode = ASUS_FAN_MODE_NORMAL; + } else { + asus->fan_mode = ASUS_FAN_MODE_NORMAL; + } + + return fan_mode_write(asus); +} + +static ssize_t fan_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct asus_wmi *asus = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%d\n", asus->fan_mode); +} + +static ssize_t fan_mode_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int result; + u8 new_mode; + + struct asus_wmi *asus = dev_get_drvdata(dev); + + result = kstrtou8(buf, 10, &new_mode); + if (result < 0) { + pr_warn("Trying to store invalid value\n"); + return result; + } + + if (new_mode == ASUS_FAN_MODE_OVERBOOST) { + if (!(asus->fan_mode_mask & ASUS_FAN_MODE_OVERBOOST_MASK)) + return -EINVAL; + } else if (new_mode == ASUS_FAN_MODE_SILENT) { + if (!(asus->fan_mode_mask & ASUS_FAN_MODE_SILENT_MASK)) + return -EINVAL; + } else if (new_mode != ASUS_FAN_MODE_NORMAL) { + return -EINVAL; + } + + asus->fan_mode = new_mode; + fan_mode_write(asus); + + return result; +} + +// Fan mode: 0 - normal, 1 - overboost, 2 - silent +static DEVICE_ATTR_RW(fan_mode); + /* Backlight ******************************************************************/ static int read_backlight_power(struct asus_wmi *asus) @@ -1761,6 +1883,11 @@ static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus) return; } + if (asus->fan_mode_available && code == NOTIFY_KBD_FBM) { + fan_mode_switch_next(asus); + return; + } + if (is_display_toggle(code) && asus->driver->quirks->no_display_toggle) return; @@ -1917,6 +2044,7 @@ static struct attribute *platform_attributes[] = { &dev_attr_touchpad.attr, &dev_attr_lid_resume.attr, &dev_attr_als_enable.attr, + &dev_attr_fan_mode.attr, NULL }; @@ -1938,6 +2066,8 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj, devid = ASUS_WMI_DEVID_LID_RESUME; else if (attr == &dev_attr_als_enable.attr) devid = ASUS_WMI_DEVID_ALS_ENABLE; + else if (attr == &dev_attr_fan_mode.attr) + ok = asus->fan_mode_available; if (devid != -1) ok = !(asus_wmi_get_devstate_simple(asus, devid) < 0); @@ -2037,12 +2167,7 @@ static int asus_wmi_platform_init(struct asus_wmi *asus) asus_wmi_set_devstate(ASUS_WMI_DEVID_CWAP, asus->driver->quirks->wapf, NULL); - return asus_wmi_sysfs_init(asus->platform_device); -} - -static void asus_wmi_platform_exit(struct asus_wmi *asus) -{ - asus_wmi_sysfs_exit(asus->platform_device); + return 0; } /* debugfs ********************************************************************/ @@ -2200,6 +2325,14 @@ static int asus_wmi_add(struct platform_device *pdev) if (err) goto fail_platform; + err = fan_mode_check_present(asus); + if (err) + goto fail_fan_mode; + + err = asus_wmi_sysfs_init(asus->platform_device); + if (err) + goto fail_sysfs; + err = asus_wmi_input_init(asus); if (err) goto fail_input; @@ -2277,7 +2410,9 @@ fail_leds: fail_hwmon: asus_wmi_input_exit(asus); fail_input: - asus_wmi_platform_exit(asus); + asus_wmi_sysfs_exit(asus->platform_device); +fail_sysfs: +fail_fan_mode: fail_platform: kfree(asus); return err; @@ -2294,7 +2429,7 @@ static int asus_wmi_remove(struct platform_device *device) asus_wmi_led_exit(asus); asus_wmi_rfkill_exit(asus); asus_wmi_debugfs_exit(asus); - asus_wmi_platform_exit(asus); + asus_wmi_sysfs_exit(asus->platform_device); asus_hwmon_fan_set_auto(asus); kfree(asus); diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h index 0668f76df921..8551156b8dca 100644 --- a/include/linux/platform_data/x86/asus-wmi.h +++ b/include/linux/platform_data/x86/asus-wmi.h @@ -57,6 +57,7 @@ #define ASUS_WMI_DEVID_KBD_BACKLIGHT 0x00050021 #define ASUS_WMI_DEVID_LIGHT_SENSOR 0x00050022 /* ?? */ #define ASUS_WMI_DEVID_LIGHTBAR 0x00050025 +#define ASUS_WMI_DEVID_FAN_MODE 0x00110018 /* Misc */ #define ASUS_WMI_DEVID_CAMERA 0x00060013 -- cgit v1.2.3 From d308dfbf62eff897d71968d764f21a78678ee0a5 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 18 Jun 2019 12:58:33 +0200 Subject: i2c: mux/i801: Switch to use descriptor passing This switches the i801 GPIO mux to use GPIO descriptors for handling the GPIO lines. The previous hack which was reaching inside the GPIO chips etc cannot live on. We pass descriptors along with the GPIO mux device at creation instead. The GPIO mux was only used by way of platform data with a platform device from one place in the kernel: the i801 i2c bus driver. Let's just associate the GPIO descriptor table with the actual device like everyone else and dynamically create a descriptor table passed along with the GPIO i2c mux. This enables simplification of the GPIO i2c mux driver to use only the descriptor API and the OF probe path gets simplified in the process. The i801 driver was registering the GPIO i2c mux with PLATFORM_DEVID_AUTO which would make it hard to predict the device name and assign the descriptor table properly, but this seems to be a mistake to begin with: all of the GPIO mux devices are hardcoded to look up GPIO lines from the "gpio_ich" GPIO chip. If there are more than one mux, there is certainly more than one gpio chip as well, and then we have more serious problems. Switch to PLATFORM_DEVID_NONE instead. There can be only one. Cc: Mika Westerberg Cc: Andy Shevchenko Cc: Peter Rosin Cc: Jean Delvare Signed-off-by: Serge Semin Signed-off-by: Linus Walleij Reviewed-by: Andy Shevchenko Reviewed-by: Mika Westerberg [Removed a newline, suggested by Andy. /Peter] Signed-off-by: Peter Rosin --- drivers/i2c/busses/i2c-i801.c | 37 +++++++-- drivers/i2c/muxes/i2c-mux-gpio.c | 116 ++++++++--------------------- include/linux/platform_data/i2c-mux-gpio.h | 7 -- 3 files changed, 60 insertions(+), 100 deletions(-) (limited to 'include/linux/platform_data') diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 679c6c41f64b..bf484cd775ec 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -107,7 +107,7 @@ #include #if IS_ENABLED(CONFIG_I2C_MUX_GPIO) && defined CONFIG_DMI -#include +#include #include #endif @@ -274,6 +274,7 @@ struct i801_priv { #if IS_ENABLED(CONFIG_I2C_MUX_GPIO) && defined CONFIG_DMI const struct i801_mux_config *mux_drvdata; struct platform_device *mux_pdev; + struct gpiod_lookup_table *lookup; #endif struct platform_device *tco_pdev; @@ -1258,7 +1259,8 @@ static int i801_add_mux(struct i801_priv *priv) struct device *dev = &priv->adapter.dev; const struct i801_mux_config *mux_config; struct i2c_mux_gpio_platform_data gpio_data; - int err; + struct gpiod_lookup_table *lookup; + int err, i; if (!priv->mux_drvdata) return 0; @@ -1270,17 +1272,36 @@ static int i801_add_mux(struct i801_priv *priv) gpio_data.values = mux_config->values; gpio_data.n_values = mux_config->n_values; gpio_data.classes = mux_config->classes; - gpio_data.gpio_chip = mux_config->gpio_chip; - gpio_data.gpios = mux_config->gpios; - gpio_data.n_gpios = mux_config->n_gpios; gpio_data.idle = I2C_MUX_GPIO_NO_IDLE; - /* Register the mux device */ + /* Register GPIO descriptor lookup table */ + lookup = devm_kzalloc(dev, + struct_size(lookup, table, mux_config->n_gpios), + GFP_KERNEL); + if (!lookup) + return -ENOMEM; + lookup->dev_id = "i2c-mux-gpio"; + for (i = 0; i < mux_config->n_gpios; i++) { + lookup->table[i].chip_label = mux_config->gpio_chip; + lookup->table[i].chip_hwnum = mux_config->gpios[i]; + lookup->table[i].con_id = "mux"; + } + gpiod_add_lookup_table(lookup); + priv->lookup = lookup; + + /* + * Register the mux device, we use PLATFORM_DEVID_NONE here + * because since we are referring to the GPIO chip by name we are + * anyways in deep trouble if there is more than one of these + * devices, and there should likely only be one platform controller + * hub. + */ priv->mux_pdev = platform_device_register_data(dev, "i2c-mux-gpio", - PLATFORM_DEVID_AUTO, &gpio_data, + PLATFORM_DEVID_NONE, &gpio_data, sizeof(struct i2c_mux_gpio_platform_data)); if (IS_ERR(priv->mux_pdev)) { err = PTR_ERR(priv->mux_pdev); + gpiod_remove_lookup_table(lookup); priv->mux_pdev = NULL; dev_err(dev, "Failed to register i2c-mux-gpio device\n"); return err; @@ -1293,6 +1314,8 @@ static void i801_del_mux(struct i801_priv *priv) { if (priv->mux_pdev) platform_device_unregister(priv->mux_pdev); + if (priv->lookup) + gpiod_remove_lookup_table(priv->lookup); } static unsigned int i801_get_adapter_class(struct i801_priv *priv) diff --git a/drivers/i2c/muxes/i2c-mux-gpio.c b/drivers/i2c/muxes/i2c-mux-gpio.c index 13882a2a4f60..fd482feafb19 100644 --- a/drivers/i2c/muxes/i2c-mux-gpio.c +++ b/drivers/i2c/muxes/i2c-mux-gpio.c @@ -14,13 +14,14 @@ #include #include #include -#include +#include +#include +/* FIXME: stop poking around inside gpiolib */ #include "../../gpio/gpiolib.h" -#include struct gpiomux { struct i2c_mux_gpio_platform_data data; - unsigned gpio_base; + int ngpios; struct gpio_desc **gpios; }; @@ -30,8 +31,7 @@ static void i2c_mux_gpio_set(const struct gpiomux *mux, unsigned val) values[0] = val; - gpiod_set_array_value_cansleep(mux->data.n_gpios, mux->gpios, NULL, - values); + gpiod_set_array_value_cansleep(mux->ngpios, mux->gpios, NULL, values); } static int i2c_mux_gpio_select(struct i2c_mux_core *muxc, u32 chan) @@ -52,12 +52,6 @@ static int i2c_mux_gpio_deselect(struct i2c_mux_core *muxc, u32 chan) return 0; } -static int match_gpio_chip_by_label(struct gpio_chip *chip, - void *data) -{ - return !strcmp(chip->label, data); -} - #ifdef CONFIG_OF static int i2c_mux_gpio_probe_dt(struct gpiomux *mux, struct platform_device *pdev) @@ -65,8 +59,8 @@ static int i2c_mux_gpio_probe_dt(struct gpiomux *mux, struct device_node *np = pdev->dev.of_node; struct device_node *adapter_np, *child; struct i2c_adapter *adapter; - unsigned *values, *gpios; - int i = 0, ret; + unsigned *values; + int i = 0; if (!np) return -ENODEV; @@ -103,29 +97,6 @@ static int i2c_mux_gpio_probe_dt(struct gpiomux *mux, if (of_property_read_u32(np, "idle-state", &mux->data.idle)) mux->data.idle = I2C_MUX_GPIO_NO_IDLE; - mux->data.n_gpios = of_gpio_named_count(np, "mux-gpios"); - if (mux->data.n_gpios < 0) { - dev_err(&pdev->dev, "Missing mux-gpios property in the DT.\n"); - return -EINVAL; - } - - gpios = devm_kcalloc(&pdev->dev, - mux->data.n_gpios, sizeof(*mux->data.gpios), - GFP_KERNEL); - if (!gpios) { - dev_err(&pdev->dev, "Cannot allocate gpios array"); - return -ENOMEM; - } - - for (i = 0; i < mux->data.n_gpios; i++) { - ret = of_get_named_gpio(np, "mux-gpios", i); - if (ret < 0) - return ret; - gpios[i] = ret; - } - - mux->data.gpios = gpios; - return 0; } #else @@ -142,8 +113,8 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev) struct gpiomux *mux; struct i2c_adapter *parent; struct i2c_adapter *root; - unsigned initial_state, gpio_base; - int i, ret; + unsigned initial_state; + int i, ngpios, ret; mux = devm_kzalloc(&pdev->dev, sizeof(*mux), GFP_KERNEL); if (!mux) @@ -158,29 +129,19 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev) sizeof(mux->data)); } - /* - * If a GPIO chip name is provided, the GPIO pin numbers provided are - * relative to its base GPIO number. Otherwise they are absolute. - */ - if (mux->data.gpio_chip) { - struct gpio_chip *gpio; - - gpio = gpiochip_find(mux->data.gpio_chip, - match_gpio_chip_by_label); - if (!gpio) - return -EPROBE_DEFER; - - gpio_base = gpio->base; - } else { - gpio_base = 0; + ngpios = gpiod_count(&pdev->dev, "mux"); + if (ngpios <= 0) { + dev_err(&pdev->dev, "no valid gpios provided\n"); + return ngpios ?: -EINVAL; } + mux->ngpios = ngpios; parent = i2c_get_adapter(mux->data.parent); if (!parent) return -EPROBE_DEFER; muxc = i2c_mux_alloc(parent, &pdev->dev, mux->data.n_values, - mux->data.n_gpios * sizeof(*mux->gpios), 0, + ngpios * sizeof(*mux->gpios), 0, i2c_mux_gpio_select, NULL); if (!muxc) { ret = -ENOMEM; @@ -194,7 +155,6 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev) root = i2c_root_adapter(&parent->dev); muxc->mux_locked = true; - mux->gpio_base = gpio_base; if (mux->data.idle != I2C_MUX_GPIO_NO_IDLE) { initial_state = mux->data.idle; @@ -203,34 +163,28 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev) initial_state = mux->data.values[0]; } - for (i = 0; i < mux->data.n_gpios; i++) { + for (i = 0; i < ngpios; i++) { struct device *gpio_dev; - struct gpio_desc *gpio_desc; - - ret = gpio_request(gpio_base + mux->data.gpios[i], "i2c-mux-gpio"); - if (ret) { - dev_err(&pdev->dev, "Failed to request GPIO %d\n", - mux->data.gpios[i]); - goto err_request_gpio; + struct gpio_desc *gpiod; + enum gpiod_flags flag; + + if (initial_state & BIT(i)) + flag = GPIOD_OUT_HIGH; + else + flag = GPIOD_OUT_LOW; + gpiod = devm_gpiod_get_index(&pdev->dev, "mux", i, flag); + if (IS_ERR(gpiod)) { + ret = PTR_ERR(gpiod); + goto alloc_failed; } - ret = gpio_direction_output(gpio_base + mux->data.gpios[i], - initial_state & (1 << i)); - if (ret) { - dev_err(&pdev->dev, - "Failed to set direction of GPIO %d to output\n", - mux->data.gpios[i]); - i++; /* gpio_request above succeeded, so must free */ - goto err_request_gpio; - } - - gpio_desc = gpio_to_desc(gpio_base + mux->data.gpios[i]); - mux->gpios[i] = gpio_desc; + mux->gpios[i] = gpiod; if (!muxc->mux_locked) continue; - gpio_dev = &gpio_desc->gdev->dev; + /* FIXME: find a proper way to access the GPIO device */ + gpio_dev = &gpiod->gdev->dev; muxc->mux_locked = i2c_root_adapter(gpio_dev) == root; } @@ -253,10 +207,6 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev) add_adapter_failed: i2c_mux_del_adapters(muxc); - i = mux->data.n_gpios; -err_request_gpio: - for (; i > 0; i--) - gpio_free(gpio_base + mux->data.gpios[i - 1]); alloc_failed: i2c_put_adapter(parent); @@ -266,14 +216,8 @@ alloc_failed: static int i2c_mux_gpio_remove(struct platform_device *pdev) { struct i2c_mux_core *muxc = platform_get_drvdata(pdev); - struct gpiomux *mux = i2c_mux_priv(muxc); - int i; i2c_mux_del_adapters(muxc); - - for (i = 0; i < mux->data.n_gpios; i++) - gpio_free(mux->gpio_base + mux->data.gpios[i]); - i2c_put_adapter(muxc->parent); return 0; diff --git a/include/linux/platform_data/i2c-mux-gpio.h b/include/linux/platform_data/i2c-mux-gpio.h index 4406108201fe..28f288eed652 100644 --- a/include/linux/platform_data/i2c-mux-gpio.h +++ b/include/linux/platform_data/i2c-mux-gpio.h @@ -22,10 +22,6 @@ * position * @n_values: Number of multiplexer positions (busses to instantiate) * @classes: Optional I2C auto-detection classes - * @gpio_chip: Optional GPIO chip name; if set, GPIO pin numbers are given - * relative to the base GPIO number of that chip - * @gpios: Array of GPIO numbers used to control MUX - * @n_gpios: Number of GPIOs used to control MUX * @idle: Bitmask to write to MUX when idle or GPIO_I2CMUX_NO_IDLE if not used */ struct i2c_mux_gpio_platform_data { @@ -34,9 +30,6 @@ struct i2c_mux_gpio_platform_data { const unsigned *values; int n_values; const unsigned *classes; - char *gpio_chip; - const unsigned *gpios; - int n_gpios; unsigned idle; }; -- cgit v1.2.3 From fa49e1d37bbd6d25a11379891ece1e4d5d313036 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Tue, 28 May 2019 05:07:26 -0400 Subject: media: marvell-ccic: drop unused stuff Remove structure members and headers that are not actually used. Saves us from some noise in subsequent cleanup commits. Signed-off-by: Lubomir Rintel Acked-by: Pavel Machek Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/marvell-ccic/mcam-core.c | 1 - drivers/media/platform/marvell-ccic/mcam-core.h | 2 -- drivers/media/platform/marvell-ccic/mmp-driver.c | 2 -- include/linux/platform_data/media/mmp-camera.h | 1 - 4 files changed, 6 deletions(-) (limited to 'include/linux/platform_data') diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c index 2494a31de01b..76641d5211ab 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.c +++ b/drivers/media/platform/marvell-ccic/mcam-core.c @@ -1776,7 +1776,6 @@ int mccic_register(struct mcam_camera *cam) */ sensor_cfg.clock_speed = cam->clock_speed; sensor_cfg.use_smbus = cam->use_smbus; - cam->sensor_addr = ov7670_info.addr; cam->sensor = v4l2_i2c_new_subdev_board(&cam->v4l2_dev, cam->i2c_adapter, &ov7670_info, NULL); if (cam->sensor == NULL) { diff --git a/drivers/media/platform/marvell-ccic/mcam-core.h b/drivers/media/platform/marvell-ccic/mcam-core.h index a3a097a45e78..b828b1bb59d3 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.h +++ b/drivers/media/platform/marvell-ccic/mcam-core.h @@ -112,7 +112,6 @@ struct mcam_camera { short int use_smbus; /* SMBUS or straight I2c? */ enum mcam_buffer_mode buffer_mode; - int mclk_min; /* The minimal value of mclk */ int mclk_src; /* which clock source the mclk derives from */ int mclk_div; /* Clock Divider Value for MCLK */ @@ -152,7 +151,6 @@ struct mcam_camera { */ struct video_device vdev; struct v4l2_subdev *sensor; - unsigned short sensor_addr; /* Videobuf2 stuff */ struct vb2_queue vb_queue; diff --git a/drivers/media/platform/marvell-ccic/mmp-driver.c b/drivers/media/platform/marvell-ccic/mmp-driver.c index 9c4c7d37d0df..25a4e2b580f4 100644 --- a/drivers/media/platform/marvell-ccic/mmp-driver.c +++ b/drivers/media/platform/marvell-ccic/mmp-driver.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -330,7 +329,6 @@ static int mmpcam_probe(struct platform_device *pdev) mcam->calc_dphy = mmpcam_calc_dphy; mcam->dev = &pdev->dev; mcam->use_smbus = 0; - mcam->mclk_min = pdata->mclk_min; mcam->mclk_src = pdata->mclk_src; mcam->mclk_div = pdata->mclk_div; mcam->bus_type = pdata->bus_type; diff --git a/include/linux/platform_data/media/mmp-camera.h b/include/linux/platform_data/media/mmp-camera.h index d2d3a443eedf..4c3a80a45883 100644 --- a/include/linux/platform_data/media/mmp-camera.h +++ b/include/linux/platform_data/media/mmp-camera.h @@ -16,7 +16,6 @@ struct mmp_camera_platform_data { int sensor_power_gpio; int sensor_reset_gpio; enum v4l2_mbus_type bus_type; - int mclk_min; /* The minimal value of MCLK */ int mclk_src; /* which clock source the MCLK derives from */ int mclk_div; /* Clock Divider Value for MCLK */ /* -- cgit v1.2.3 From 3eefe36cc00c5391b1ca2a68c5f01e9aa127c2a6 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Tue, 28 May 2019 05:07:30 -0400 Subject: media: marvell-ccic: use async notifier to get the sensor An instance of a sensor on DT-based MMP2 platform is always going to be created asynchronously. Let's move the manual device creation away from the core to the Cafe driver (used on OLPC XO-1, not present in DT) and set up appropriate async matches: I2C on Cafe, FWNODE on MMP (OLPC XO-1.75). Signed-off-by: Lubomir Rintel Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/marvell-ccic/cafe-driver.c | 49 +++++-- drivers/media/platform/marvell-ccic/mcam-core.c | 157 +++++++++++++++------- drivers/media/platform/marvell-ccic/mcam-core.h | 5 +- drivers/media/platform/marvell-ccic/mmp-driver.c | 27 ++-- include/linux/platform_data/media/mmp-camera.h | 1 - 5 files changed, 162 insertions(+), 77 deletions(-) (limited to 'include/linux/platform_data') diff --git a/drivers/media/platform/marvell-ccic/cafe-driver.c b/drivers/media/platform/marvell-ccic/cafe-driver.c index cd108b14b715..fe85368675cb 100644 --- a/drivers/media/platform/marvell-ccic/cafe-driver.c +++ b/drivers/media/platform/marvell-ccic/cafe-driver.c @@ -9,6 +9,7 @@ * * Copyright 2006-11 One Laptop Per Child Association, Inc. * Copyright 2006-11 Jonathan Corbet + * Copyright 2018 Lubomir Rintel * * Written by Jonathan Corbet, corbet@lwn.net. * @@ -25,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -50,6 +52,7 @@ struct cafe_camera { int registered; /* Fully initialized? */ struct mcam_camera mcam; struct pci_dev *pdev; + struct i2c_adapter *i2c_adapter; wait_queue_head_t smbus_wait; /* Waiting on i2c events */ }; @@ -349,15 +352,15 @@ static int cafe_smbus_setup(struct cafe_camera *cam) return ret; } - cam->mcam.i2c_adapter = adap; + cam->i2c_adapter = adap; cafe_smbus_enable_irq(cam); return 0; } static void cafe_smbus_shutdown(struct cafe_camera *cam) { - i2c_del_adapter(cam->mcam.i2c_adapter); - kfree(cam->mcam.i2c_adapter); + i2c_del_adapter(cam->i2c_adapter); + kfree(cam->i2c_adapter); } @@ -450,6 +453,29 @@ static irqreturn_t cafe_irq(int irq, void *data) return IRQ_RETVAL(handled); } +/* -------------------------------------------------------------------------- */ + +static struct ov7670_config sensor_cfg = { + /* + * Exclude QCIF mode, because it only captures a tiny portion + * of the sensor FOV + */ + .min_width = 320, + .min_height = 240, + + /* + * Set the clock speed for the XO 1; I don't believe this + * driver has ever run anywhere else. + */ + .clock_speed = 45, + .use_smbus = 1, +}; + +struct i2c_board_info ov7670_info = { + .type = "ov7670", + .addr = 0x42 >> 1, + .platform_data = &sensor_cfg, +}; /* -------------------------------------------------------------------------- */ /* @@ -479,12 +505,6 @@ static int cafe_pci_probe(struct pci_dev *pdev, mcam->plat_power_down = cafe_ctlr_power_down; mcam->dev = &pdev->dev; snprintf(mcam->bus_info, sizeof(mcam->bus_info), "PCI:%s", pci_name(pdev)); - /* - * Set the clock speed for the XO 1; I don't believe this - * driver has ever run anywhere else. - */ - mcam->clock_speed = 45; - mcam->use_smbus = 1; /* * Vmalloc mode for buffers is traditional with this driver. * We *might* be able to run DMA_contig, especially on a system @@ -525,12 +545,21 @@ static int cafe_pci_probe(struct pci_dev *pdev, if (ret) goto out_pdown; + mcam->asd.match_type = V4L2_ASYNC_MATCH_I2C; + mcam->asd.match.i2c.adapter_id = i2c_adapter_id(cam->i2c_adapter); + mcam->asd.match.i2c.address = ov7670_info.addr; + ret = mccic_register(mcam); - if (ret == 0) { + if (ret) + goto out_smbus_shutdown; + + if (i2c_new_device(cam->i2c_adapter, &ov7670_info)) { cam->registered = 1; return 0; } + mccic_shutdown(mcam); +out_smbus_shutdown: cafe_smbus_shutdown(cam); out_pdown: cafe_ctlr_power_down(mcam); diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c index 76641d5211ab..7dc7d9d91782 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.c +++ b/drivers/media/platform/marvell-ccic/mcam-core.c @@ -4,6 +4,7 @@ * so it needs platform-specific support outside of the core. * * Copyright 2011 Jonathan Corbet corbet@lwn.net + * Copyright 2018 Lubomir Rintel */ #include #include @@ -26,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -93,6 +93,9 @@ MODULE_PARM_DESC(buffer_mode, #define sensor_call(cam, o, f, args...) \ v4l2_subdev_call(cam->sensor, o, f, ##args) +#define notifier_to_mcam(notifier) \ + container_of(notifier, struct mcam_camera, notifier) + static struct mcam_format_struct { __u8 *desc; __u32 pixelformat; @@ -1715,23 +1718,94 @@ EXPORT_SYMBOL_GPL(mccic_irq); /* * Registration and such. */ -static struct ov7670_config sensor_cfg = { + +static int mccic_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, struct v4l2_async_subdev *asd) +{ + struct mcam_camera *cam = notifier_to_mcam(notifier); + int ret; + + mutex_lock(&cam->s_mutex); + if (cam->sensor) { + cam_err(cam, "sensor already bound\n"); + ret = -EBUSY; + goto out; + } + + v4l2_set_subdev_hostdata(subdev, cam); + cam->sensor = subdev; + + ret = mcam_cam_init(cam); + if (ret) { + cam->sensor = NULL; + goto out; + } + + ret = mcam_setup_vb2(cam); + if (ret) { + cam->sensor = NULL; + goto out; + } + + cam->vdev = mcam_v4l_template; + cam->vdev.v4l2_dev = &cam->v4l2_dev; + cam->vdev.lock = &cam->s_mutex; + cam->vdev.queue = &cam->vb_queue; + video_set_drvdata(&cam->vdev, cam); + ret = video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1); + if (ret) { + cam->sensor = NULL; + goto out; + } + + cam_dbg(cam, "sensor %s bound\n", subdev->name); +out: + mutex_unlock(&cam->s_mutex); + return ret; +} + +static void mccic_notify_unbind(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, struct v4l2_async_subdev *asd) +{ + struct mcam_camera *cam = notifier_to_mcam(notifier); + + mutex_lock(&cam->s_mutex); + if (cam->sensor != subdev) { + cam_err(cam, "sensor %s not bound\n", subdev->name); + goto out; + } + + video_unregister_device(&cam->vdev); + cam->sensor = NULL; + cam_dbg(cam, "sensor %s unbound\n", subdev->name); + +out: + mutex_unlock(&cam->s_mutex); +} + +static int mccic_notify_complete(struct v4l2_async_notifier *notifier) +{ + struct mcam_camera *cam = notifier_to_mcam(notifier); + int ret; + /* - * Exclude QCIF mode, because it only captures a tiny portion - * of the sensor FOV + * Get the v4l2 setup done. */ - .min_width = 320, - .min_height = 240, -}; + ret = v4l2_ctrl_handler_init(&cam->ctrl_handler, 10); + if (!ret) + cam->v4l2_dev.ctrl_handler = &cam->ctrl_handler; + + return ret; +} +static const struct v4l2_async_notifier_operations mccic_notify_ops = { + .bound = mccic_notify_bound, + .unbind = mccic_notify_unbind, + .complete = mccic_notify_complete, +}; int mccic_register(struct mcam_camera *cam) { - struct i2c_board_info ov7670_info = { - .type = "ov7670", - .addr = 0x42 >> 1, - .platform_data = &sensor_cfg, - }; int ret; /* @@ -1744,17 +1818,20 @@ int mccic_register(struct mcam_camera *cam) printk(KERN_ERR "marvell-cam: Cafe can't do S/G I/O, attempting vmalloc mode instead\n"); cam->buffer_mode = B_vmalloc; } + if (!mcam_buffer_mode_supported(cam->buffer_mode)) { printk(KERN_ERR "marvell-cam: buffer mode %d unsupported\n", cam->buffer_mode); - return -EINVAL; + ret = -EINVAL; + goto out; } + /* * Register with V4L */ ret = v4l2_device_register(cam->dev, &cam->v4l2_dev); if (ret) - return ret; + goto out; mutex_init(&cam->s_mutex); cam->state = S_NOTREADY; @@ -1764,43 +1841,20 @@ int mccic_register(struct mcam_camera *cam) mcam_ctlr_init(cam); /* - * Get the v4l2 setup done. + * Register sensor notifier. */ - ret = v4l2_ctrl_handler_init(&cam->ctrl_handler, 10); - if (ret) - goto out_unregister; - cam->v4l2_dev.ctrl_handler = &cam->ctrl_handler; - - /* - * Try to find the sensor. - */ - sensor_cfg.clock_speed = cam->clock_speed; - sensor_cfg.use_smbus = cam->use_smbus; - cam->sensor = v4l2_i2c_new_subdev_board(&cam->v4l2_dev, - cam->i2c_adapter, &ov7670_info, NULL); - if (cam->sensor == NULL) { - ret = -ENODEV; - goto out_unregister; + v4l2_async_notifier_init(&cam->notifier); + ret = v4l2_async_notifier_add_subdev(&cam->notifier, &cam->asd); + if (ret) { + cam_warn(cam, "failed to add subdev to a notifier"); + goto out; } - ret = mcam_cam_init(cam); - if (ret) - goto out_unregister; - - ret = mcam_setup_vb2(cam); - if (ret) - goto out_unregister; - - mutex_lock(&cam->s_mutex); - cam->vdev = mcam_v4l_template; - cam->vdev.v4l2_dev = &cam->v4l2_dev; - cam->vdev.lock = &cam->s_mutex; - cam->vdev.queue = &cam->vb_queue; - video_set_drvdata(&cam->vdev, cam); - ret = video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1); - if (ret) { - mutex_unlock(&cam->s_mutex); - goto out_unregister; + cam->notifier.ops = &mccic_notify_ops; + ret = v4l2_async_notifier_register(&cam->v4l2_dev, &cam->notifier); + if (ret < 0) { + cam_warn(cam, "failed to register a sensor notifier"); + goto out; } /* @@ -1811,11 +1865,10 @@ int mccic_register(struct mcam_camera *cam) cam_warn(cam, "Unable to alloc DMA buffers at load will try again later."); } - mutex_unlock(&cam->s_mutex); return 0; -out_unregister: - v4l2_ctrl_handler_free(&cam->ctrl_handler); +out: + v4l2_async_notifier_unregister(&cam->notifier); v4l2_device_unregister(&cam->v4l2_dev); return ret; } @@ -1835,8 +1888,8 @@ void mccic_shutdown(struct mcam_camera *cam) } if (cam->buffer_mode == B_vmalloc) mcam_free_dma_bufs(cam); - video_unregister_device(&cam->vdev); v4l2_ctrl_handler_free(&cam->ctrl_handler); + v4l2_async_notifier_unregister(&cam->notifier); v4l2_device_unregister(&cam->v4l2_dev); } EXPORT_SYMBOL_GPL(mccic_shutdown); diff --git a/drivers/media/platform/marvell-ccic/mcam-core.h b/drivers/media/platform/marvell-ccic/mcam-core.h index b828b1bb59d3..4a72213aca1a 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.h +++ b/drivers/media/platform/marvell-ccic/mcam-core.h @@ -102,14 +102,11 @@ struct mcam_camera { * These fields should be set by the platform code prior to * calling mcam_register(). */ - struct i2c_adapter *i2c_adapter; unsigned char __iomem *regs; unsigned regs_size; /* size in bytes of the register space */ spinlock_t dev_lock; struct device *dev; /* For messages, dma alloc */ enum mcam_chip_id chip_id; - short int clock_speed; /* Sensor clock speed, default 30 */ - short int use_smbus; /* SMBUS or straight I2c? */ enum mcam_buffer_mode buffer_mode; int mclk_src; /* which clock source the mclk derives from */ @@ -150,6 +147,8 @@ struct mcam_camera { * Subsystem structures. */ struct video_device vdev; + struct v4l2_async_notifier notifier; + struct v4l2_async_subdev asd; struct v4l2_subdev *sensor; /* Videobuf2 stuff */ diff --git a/drivers/media/platform/marvell-ccic/mmp-driver.c b/drivers/media/platform/marvell-ccic/mmp-driver.c index 492663a8a29d..92061e4adbfd 100644 --- a/drivers/media/platform/marvell-ccic/mmp-driver.c +++ b/drivers/media/platform/marvell-ccic/mmp-driver.c @@ -4,12 +4,12 @@ * to work with the Armada 610 as used in the OLPC 1.75 system. * * Copyright 2011 Jonathan Corbet + * Copyright 2018 Lubomir Rintel */ #include #include #include -#include #include #include #include @@ -314,6 +314,7 @@ static int mmpcam_probe(struct platform_device *pdev) struct mmp_camera *cam; struct mcam_camera *mcam; struct resource *res; + struct fwnode_handle *ep; struct mmp_camera_platform_data *pdata; int ret; @@ -328,7 +329,6 @@ static int mmpcam_probe(struct platform_device *pdev) mcam->plat_power_down = mmpcam_power_down; mcam->calc_dphy = mmpcam_calc_dphy; mcam->dev = &pdev->dev; - mcam->use_smbus = 0; pdata = pdev->dev.platform_data; if (pdata) { mcam->mclk_src = pdata->mclk_src; @@ -372,15 +372,6 @@ static int mmpcam_probe(struct platform_device *pdev) cam->power_regs = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(cam->power_regs)) return PTR_ERR(cam->power_regs); - /* - * Find the i2c adapter. This assumes, of course, that the - * i2c bus is already up and functioning. - */ - mcam->i2c_adapter = platform_get_drvdata(pdata->i2c_device); - if (mcam->i2c_adapter == NULL) { - dev_err(&pdev->dev, "No i2c adapter\n"); - return -ENODEV; - } /* * Sensor GPIO pins. */ @@ -403,6 +394,19 @@ static int mmpcam_probe(struct platform_device *pdev) mcam_init_clk(mcam); + /* + * Create a match of the sensor against its OF node. + */ + ep = fwnode_graph_get_next_endpoint(of_fwnode_handle(pdev->dev.of_node), + NULL); + if (!ep) + return -ENODEV; + + mcam->asd.match_type = V4L2_ASYNC_MATCH_FWNODE; + mcam->asd.match.fwnode = fwnode_graph_get_remote_port_parent(ep); + + fwnode_handle_put(ep); + /* * Power the device up and hand it off to the core. */ @@ -412,6 +416,7 @@ static int mmpcam_probe(struct platform_device *pdev) ret = mccic_register(mcam); if (ret) goto out_power_down; + /* * Finally, set up our IRQ now that the core is ready to * deal with it. diff --git a/include/linux/platform_data/media/mmp-camera.h b/include/linux/platform_data/media/mmp-camera.h index 4c3a80a45883..c573ebc40035 100644 --- a/include/linux/platform_data/media/mmp-camera.h +++ b/include/linux/platform_data/media/mmp-camera.h @@ -12,7 +12,6 @@ enum dphy3_algo { }; struct mmp_camera_platform_data { - struct platform_device *i2c_device; int sensor_power_gpio; int sensor_reset_gpio; enum v4l2_mbus_type bus_type; -- cgit v1.2.3 From 81a409bfd5517d537097d3cfdfed7f8bf8ac469c Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Tue, 28 May 2019 05:07:31 -0400 Subject: media: marvell-ccic: provide a clock for the sensor The sensor needs the MCLK clock running when it's being probed. On platforms where the sensor is instantiated from a DT (MMP2) it is going to happen asynchronously. Therefore, the current modus operandi, where the bridge driver fiddles with the sensor power and clock itself is not going to fly. As the comments wisely note, this doesn't even belong there. Luckily, the ov7670 driver is already able to control its power and reset lines, we can just drop the MMP platform glue altogether. It also requests the clock via the standard clock subsystem. Good -- let's set up a clock instance so that the sensor can ask us to enable the clock. Note that this is pretty dumb at the moment: the clock is hardwired to a particular frequency and parent. It was always the case. Signed-off-by: Lubomir Rintel Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/marvell-ccic/Kconfig | 2 + drivers/media/platform/marvell-ccic/cafe-driver.c | 9 +- drivers/media/platform/marvell-ccic/mcam-core.c | 172 ++++++++++++++++------ drivers/media/platform/marvell-ccic/mcam-core.h | 3 + drivers/media/platform/marvell-ccic/mmp-driver.c | 152 ++----------------- include/linux/platform_data/media/mmp-camera.h | 2 - 6 files changed, 157 insertions(+), 183 deletions(-) (limited to 'include/linux/platform_data') diff --git a/drivers/media/platform/marvell-ccic/Kconfig b/drivers/media/platform/marvell-ccic/Kconfig index 86b84474dd8c..3e3f86264762 100644 --- a/drivers/media/platform/marvell-ccic/Kconfig +++ b/drivers/media/platform/marvell-ccic/Kconfig @@ -2,6 +2,7 @@ config VIDEO_CAFE_CCIC tristate "Marvell 88ALP01 (Cafe) CMOS Camera Controller support" depends on PCI && I2C && VIDEO_V4L2 + depends on COMMON_CLK select VIDEO_OV7670 select VIDEOBUF2_VMALLOC select VIDEOBUF2_DMA_CONTIG @@ -15,6 +16,7 @@ config VIDEO_MMP_CAMERA tristate "Marvell Armada 610 integrated camera controller support" depends on I2C && VIDEO_V4L2 depends on ARCH_MMP || COMPILE_TEST + depends on COMMON_CLK select VIDEO_OV7670 select I2C_GPIO select VIDEOBUF2_VMALLOC diff --git a/drivers/media/platform/marvell-ccic/cafe-driver.c b/drivers/media/platform/marvell-ccic/cafe-driver.c index fe85368675cb..16602628f895 100644 --- a/drivers/media/platform/marvell-ccic/cafe-driver.c +++ b/drivers/media/platform/marvell-ccic/cafe-driver.c @@ -31,6 +31,7 @@ #include #include #include +#include #include "mcam-core.h" @@ -531,11 +532,10 @@ static int cafe_pci_probe(struct pci_dev *pdev, goto out_iounmap; /* - * Initialize the controller and leave it powered up. It will - * stay that way until the sensor driver shows up. + * Initialize the controller. */ cafe_ctlr_init(mcam); - cafe_ctlr_power_up(mcam); + /* * Set up I2C/SMBUS communications. We have to drop the mutex here * because the sensor could attach in this call chain, leading to @@ -553,6 +553,9 @@ static int cafe_pci_probe(struct pci_dev *pdev, if (ret) goto out_smbus_shutdown; + clkdev_create(mcam->mclk, "xclk", "%d-%04x", + i2c_adapter_id(cam->i2c_adapter), ov7670_info.addr); + if (i2c_new_device(cam->i2c_adapter, &ov7670_info)) { cam->registered = 1; return 0; diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c index 7dc7d9d91782..f9ac1547d093 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.c +++ b/drivers/media/platform/marvell-ccic/mcam-core.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -303,9 +304,6 @@ static void mcam_enable_mipi(struct mcam_camera *mcam) */ mcam_reg_write(mcam, REG_CSI2_CTRL0, CSI2_C0_MIPI_EN | CSI2_C0_ACT_LANE(mcam->lane)); - mcam_reg_write(mcam, REG_CLKCTRL, - (mcam->mclk_src << 29) | mcam->mclk_div); - mcam->mipi_enabled = true; } } @@ -830,31 +828,6 @@ static void mcam_ctlr_irq_disable(struct mcam_camera *cam) mcam_reg_clear_bit(cam, REG_IRQMASK, FRAMEIRQS); } - - -static void mcam_ctlr_init(struct mcam_camera *cam) -{ - unsigned long flags; - - spin_lock_irqsave(&cam->dev_lock, flags); - /* - * Make sure it's not powered down. - */ - mcam_reg_clear_bit(cam, REG_CTRL1, C1_PWRDWN); - /* - * Turn off the enable bit. It sure should be off anyway, - * but it's good to be sure. - */ - mcam_reg_clear_bit(cam, REG_CTRL0, C0_ENABLE); - /* - * Clock the sensor appropriately. Controller clock should - * be 48MHz, sensor "typical" value is half that. - */ - mcam_reg_write_mask(cam, REG_CLKCTRL, 2, CLK_DIV_MASK); - spin_unlock_irqrestore(&cam->dev_lock, flags); -} - - /* * Stop the controller, and don't return until we're really sure that no * further DMA is going on. @@ -898,14 +871,15 @@ static int mcam_ctlr_power_up(struct mcam_camera *cam) int ret; spin_lock_irqsave(&cam->dev_lock, flags); - ret = cam->plat_power_up(cam); - if (ret) { - spin_unlock_irqrestore(&cam->dev_lock, flags); - return ret; + if (cam->plat_power_up) { + ret = cam->plat_power_up(cam); + if (ret) { + spin_unlock_irqrestore(&cam->dev_lock, flags); + return ret; + } } mcam_reg_clear_bit(cam, REG_CTRL1, C1_PWRDWN); spin_unlock_irqrestore(&cam->dev_lock, flags); - msleep(5); /* Just to be sure */ return 0; } @@ -920,10 +894,101 @@ static void mcam_ctlr_power_down(struct mcam_camera *cam) * power down routine. */ mcam_reg_set_bit(cam, REG_CTRL1, C1_PWRDWN); - cam->plat_power_down(cam); + if (cam->plat_power_down) + cam->plat_power_down(cam); spin_unlock_irqrestore(&cam->dev_lock, flags); } +/* ---------------------------------------------------------------------- */ +/* + * Controller clocks. + */ +static void mcam_clk_enable(struct mcam_camera *mcam) +{ + unsigned int i; + + for (i = 0; i < NR_MCAM_CLK; i++) { + if (!IS_ERR(mcam->clk[i])) + clk_prepare_enable(mcam->clk[i]); + } +} + +static void mcam_clk_disable(struct mcam_camera *mcam) +{ + int i; + + for (i = NR_MCAM_CLK - 1; i >= 0; i--) { + if (!IS_ERR(mcam->clk[i])) + clk_disable_unprepare(mcam->clk[i]); + } +} + +/* ---------------------------------------------------------------------- */ +/* + * Master sensor clock. + */ +static int mclk_prepare(struct clk_hw *hw) +{ + struct mcam_camera *cam = container_of(hw, struct mcam_camera, mclk_hw); + + clk_prepare(cam->clk[0]); + return 0; +} + +static void mclk_unprepare(struct clk_hw *hw) +{ + struct mcam_camera *cam = container_of(hw, struct mcam_camera, mclk_hw); + + clk_unprepare(cam->clk[0]); +} + +static int mclk_enable(struct clk_hw *hw) +{ + struct mcam_camera *cam = container_of(hw, struct mcam_camera, mclk_hw); + int mclk_src; + int mclk_div; + + /* + * Clock the sensor appropriately. Controller clock should + * be 48MHz, sensor "typical" value is half that. + */ + if (cam->bus_type == V4L2_MBUS_CSI2_DPHY) { + mclk_src = cam->mclk_src; + mclk_div = cam->mclk_div; + } else { + mclk_src = 3; + mclk_div = 2; + } + + clk_enable(cam->clk[0]); + mcam_reg_write(cam, REG_CLKCTRL, (mclk_src << 29) | mclk_div); + mcam_ctlr_power_up(cam); + + return 0; +} + +static void mclk_disable(struct clk_hw *hw) +{ + struct mcam_camera *cam = container_of(hw, struct mcam_camera, mclk_hw); + + mcam_ctlr_power_down(cam); + clk_disable(cam->clk[0]); +} + +static unsigned long mclk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return 48000000; +} + +static const struct clk_ops mclk_ops = { + .prepare = mclk_prepare, + .unprepare = mclk_unprepare, + .enable = mclk_enable, + .disable = mclk_disable, + .recalc_rate = mclk_recalc_rate, +}; + /* -------------------------------------------------------------------- */ /* * Communications with the sensor. @@ -948,7 +1013,6 @@ static int mcam_cam_init(struct mcam_camera *cam) ret = __mcam_cam_reset(cam); /* Get/set parameters? */ cam->state = S_IDLE; - mcam_ctlr_power_down(cam); return ret; } @@ -1584,9 +1648,10 @@ static int mcam_v4l_open(struct file *filp) if (ret) goto out; if (v4l2_fh_is_singular_file(filp)) { - ret = mcam_ctlr_power_up(cam); + ret = sensor_call(cam, core, s_power, 1); if (ret) goto out; + mcam_clk_enable(cam); __mcam_cam_reset(cam); mcam_set_config_needed(cam, 1); } @@ -1608,7 +1673,8 @@ static int mcam_v4l_release(struct file *filp) _vb2_fop_release(filp, NULL); if (last_open) { mcam_disable_mipi(cam); - mcam_ctlr_power_down(cam); + sensor_call(cam, core, s_power, 0); + mcam_clk_disable(cam); if (cam->buffer_mode == B_vmalloc && alloc_bufs_at_read) mcam_free_dma_bufs(cam); } @@ -1806,6 +1872,7 @@ static const struct v4l2_async_notifier_operations mccic_notify_ops = { int mccic_register(struct mcam_camera *cam) { + struct clk_init_data mclk_init = { }; int ret; /* @@ -1838,7 +1905,6 @@ int mccic_register(struct mcam_camera *cam) mcam_set_config_needed(cam, 1); cam->pix_format = mcam_def_pix_format; cam->mbus_code = mcam_def_mbus_code; - mcam_ctlr_init(cam); /* * Register sensor notifier. @@ -1857,6 +1923,26 @@ int mccic_register(struct mcam_camera *cam) goto out; } + /* + * Register sensor master clock. + */ + mclk_init.parent_names = NULL; + mclk_init.num_parents = 0; + mclk_init.ops = &mclk_ops; + mclk_init.name = "mclk"; + + of_property_read_string(cam->dev->of_node, "clock-output-names", + &mclk_init.name); + + cam->mclk_hw.init = &mclk_init; + + cam->mclk = devm_clk_register(cam->dev, &cam->mclk_hw); + if (IS_ERR(cam->mclk)) { + ret = PTR_ERR(cam->mclk); + dev_err(cam->dev, "can't register clock\n"); + goto out; + } + /* * If so requested, try to get our DMA buffers now. */ @@ -1884,7 +1970,7 @@ void mccic_shutdown(struct mcam_camera *cam) */ if (!list_empty(&cam->vdev.fh_list)) { cam_warn(cam, "Removing a device with users!\n"); - mcam_ctlr_power_down(cam); + sensor_call(cam, core, s_power, 0); } if (cam->buffer_mode == B_vmalloc) mcam_free_dma_bufs(cam); @@ -1906,7 +1992,8 @@ void mccic_suspend(struct mcam_camera *cam) enum mcam_state cstate = cam->state; mcam_ctlr_stop_dma(cam); - mcam_ctlr_power_down(cam); + sensor_call(cam, core, s_power, 0); + mcam_clk_disable(cam); cam->state = cstate; } mutex_unlock(&cam->s_mutex); @@ -1919,14 +2006,15 @@ int mccic_resume(struct mcam_camera *cam) mutex_lock(&cam->s_mutex); if (!list_empty(&cam->vdev.fh_list)) { - ret = mcam_ctlr_power_up(cam); + mcam_clk_enable(cam); + ret = sensor_call(cam, core, s_power, 1); if (ret) { mutex_unlock(&cam->s_mutex); return ret; } __mcam_cam_reset(cam); } else { - mcam_ctlr_power_down(cam); + sensor_call(cam, core, s_power, 0); } mutex_unlock(&cam->s_mutex); diff --git a/drivers/media/platform/marvell-ccic/mcam-core.h b/drivers/media/platform/marvell-ccic/mcam-core.h index 4a72213aca1a..2e3a7567a76a 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.h +++ b/drivers/media/platform/marvell-ccic/mcam-core.h @@ -8,6 +8,7 @@ #define _MCAM_CORE_H #include +#include #include #include #include @@ -125,6 +126,8 @@ struct mcam_camera { /* clock tree support */ struct clk *clk[NR_MCAM_CLK]; + struct clk_hw mclk_hw; + struct clk *mclk; /* * Callbacks from the core to the platform code. diff --git a/drivers/media/platform/marvell-ccic/mmp-driver.c b/drivers/media/platform/marvell-ccic/mmp-driver.c index 92061e4adbfd..450693e6657d 100644 --- a/drivers/media/platform/marvell-ccic/mmp-driver.c +++ b/drivers/media/platform/marvell-ccic/mmp-driver.c @@ -20,9 +20,7 @@ #include #include #include -#include #include -#include #include #include #include @@ -36,7 +34,6 @@ MODULE_LICENSE("GPL"); static char *mcam_clks[] = {"axi", "func", "phy"}; struct mmp_camera { - void __iomem *power_regs; struct platform_device *pdev; struct mcam_camera mcam; struct list_head devlist; @@ -92,94 +89,6 @@ static struct mmp_camera *mmpcam_find_device(struct platform_device *pdev) return NULL; } - - - -/* - * Power-related registers; this almost certainly belongs - * somewhere else. - * - * ARMADA 610 register manual, sec 7.2.1, p1842. - */ -#define CPU_SUBSYS_PMU_BASE 0xd4282800 -#define REG_CCIC_DCGCR 0x28 /* CCIC dyn clock gate ctrl reg */ -#define REG_CCIC_CRCR 0x50 /* CCIC clk reset ctrl reg */ - -static void mcam_clk_enable(struct mcam_camera *mcam) -{ - unsigned int i; - - for (i = 0; i < NR_MCAM_CLK; i++) { - if (!IS_ERR(mcam->clk[i])) - clk_prepare_enable(mcam->clk[i]); - } -} - -static void mcam_clk_disable(struct mcam_camera *mcam) -{ - int i; - - for (i = NR_MCAM_CLK - 1; i >= 0; i--) { - if (!IS_ERR(mcam->clk[i])) - clk_disable_unprepare(mcam->clk[i]); - } -} - -/* - * Power control. - */ -static void mmpcam_power_up_ctlr(struct mmp_camera *cam) -{ - iowrite32(0x3f, cam->power_regs + REG_CCIC_DCGCR); - iowrite32(0x3805b, cam->power_regs + REG_CCIC_CRCR); - mdelay(1); -} - -static int mmpcam_power_up(struct mcam_camera *mcam) -{ - struct mmp_camera *cam = mcam_to_cam(mcam); - struct mmp_camera_platform_data *pdata; - -/* - * Turn on power and clocks to the controller. - */ - mmpcam_power_up_ctlr(cam); - mcam_clk_enable(mcam); -/* - * Provide power to the sensor. - */ - mcam_reg_write(mcam, REG_CLKCTRL, 0x60000002); - pdata = cam->pdev->dev.platform_data; - gpio_set_value(pdata->sensor_power_gpio, 1); - mdelay(5); - mcam_reg_clear_bit(mcam, REG_CTRL1, 0x10000000); - gpio_set_value(pdata->sensor_reset_gpio, 0); /* reset is active low */ - mdelay(5); - gpio_set_value(pdata->sensor_reset_gpio, 1); /* reset is active low */ - mdelay(5); - - return 0; -} - -static void mmpcam_power_down(struct mcam_camera *mcam) -{ - struct mmp_camera *cam = mcam_to_cam(mcam); - struct mmp_camera_platform_data *pdata; -/* - * Turn off clocks and set reset lines - */ - iowrite32(0, cam->power_regs + REG_CCIC_DCGCR); - iowrite32(0, cam->power_regs + REG_CCIC_CRCR); -/* - * Shut down the sensor. - */ - pdata = cam->pdev->dev.platform_data; - gpio_set_value(pdata->sensor_power_gpio, 0); - gpio_set_value(pdata->sensor_reset_gpio, 0); - - mcam_clk_disable(mcam); -} - /* * calc the dphy register values * There are three dphy registers being used. @@ -325,8 +234,6 @@ static int mmpcam_probe(struct platform_device *pdev) INIT_LIST_HEAD(&cam->devlist); mcam = &cam->mcam; - mcam->plat_power_up = mmpcam_power_up; - mcam->plat_power_down = mmpcam_power_down; mcam->calc_dphy = mmpcam_calc_dphy; mcam->dev = &pdev->dev; pdata = pdev->dev.platform_data; @@ -364,33 +271,6 @@ static int mmpcam_probe(struct platform_device *pdev) if (IS_ERR(mcam->regs)) return PTR_ERR(mcam->regs); mcam->regs_size = resource_size(res); - /* - * Power/clock memory is elsewhere; get it too. Perhaps this - * should really be managed outside of this driver? - */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - cam->power_regs = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(cam->power_regs)) - return PTR_ERR(cam->power_regs); - /* - * Sensor GPIO pins. - */ - ret = devm_gpio_request(&pdev->dev, pdata->sensor_power_gpio, - "cam-power"); - if (ret) { - dev_err(&pdev->dev, "Can't get sensor power gpio %d", - pdata->sensor_power_gpio); - return ret; - } - gpio_direction_output(pdata->sensor_power_gpio, 0); - ret = devm_gpio_request(&pdev->dev, pdata->sensor_reset_gpio, - "cam-reset"); - if (ret) { - dev_err(&pdev->dev, "Can't get sensor reset gpio %d", - pdata->sensor_reset_gpio); - return ret; - } - gpio_direction_output(pdata->sensor_reset_gpio, 0); mcam_init_clk(mcam); @@ -408,14 +288,21 @@ static int mmpcam_probe(struct platform_device *pdev) fwnode_handle_put(ep); /* - * Power the device up and hand it off to the core. + * Register the device with the core. */ - ret = mmpcam_power_up(mcam); - if (ret) - return ret; ret = mccic_register(mcam); if (ret) - goto out_power_down; + return ret; + + /* + * Add OF clock provider. + */ + ret = of_clk_add_provider(pdev->dev.of_node, of_clk_src_simple_get, + mcam->mclk); + if (ret) { + dev_err(&pdev->dev, "can't add DT clock provider\n"); + goto out; + } /* * Finally, set up our IRQ now that the core is ready to @@ -424,7 +311,7 @@ static int mmpcam_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (res == NULL) { ret = -ENODEV; - goto out_unregister; + goto out; } cam->irq = res->start; ret = devm_request_irq(&pdev->dev, cam->irq, mmpcam_irq, IRQF_SHARED, @@ -434,10 +321,10 @@ static int mmpcam_probe(struct platform_device *pdev) return 0; } -out_unregister: +out: + fwnode_handle_put(mcam->asd.match.fwnode); mccic_shutdown(mcam); -out_power_down: - mmpcam_power_down(mcam); + return ret; } @@ -448,7 +335,6 @@ static int mmpcam_remove(struct mmp_camera *cam) mmpcam_remove_device(cam); mccic_shutdown(mcam); - mmpcam_power_down(mcam); return 0; } @@ -480,12 +366,6 @@ static int mmpcam_resume(struct platform_device *pdev) { struct mmp_camera *cam = mmpcam_find_device(pdev); - /* - * Power up unconditionally just in case the core tries to - * touch a register even if nothing was active before; trust - * me, it's better this way. - */ - mmpcam_power_up_ctlr(cam); return mccic_resume(&cam->mcam); } diff --git a/include/linux/platform_data/media/mmp-camera.h b/include/linux/platform_data/media/mmp-camera.h index c573ebc40035..53adaab64f28 100644 --- a/include/linux/platform_data/media/mmp-camera.h +++ b/include/linux/platform_data/media/mmp-camera.h @@ -12,8 +12,6 @@ enum dphy3_algo { }; struct mmp_camera_platform_data { - int sensor_power_gpio; - int sensor_reset_gpio; enum v4l2_mbus_type bus_type; int mclk_src; /* which clock source the MCLK derives from */ int mclk_div; /* Clock Divider Value for MCLK */ -- cgit v1.2.3 From f50dfaf772db187deb562764e7aa3b988d6bc538 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sun, 30 Jun 2019 16:03:02 +0200 Subject: misc: fsa9480: Delete this driver The FSA9480 has a new driver more appropriately located in the drivers/extcon subsystem. It is also more complete and includes device tree support. Delete the old misc driver. Cc: Arnd Bergmann Signed-off-by: Linus Walleij Reviewed-by: Chanwoo Choi Reviewed-by: Pawe Chmiel Link: https://lore.kernel.org/r/20190630140302.16245-1-linus.walleij@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/Kconfig | 9 - drivers/misc/Makefile | 1 - drivers/misc/fsa9480.c | 547 ---------------------------------- include/linux/platform_data/fsa9480.h | 24 -- 4 files changed, 581 deletions(-) delete mode 100644 drivers/misc/fsa9480.c delete mode 100644 include/linux/platform_data/fsa9480.h (limited to 'include/linux/platform_data') diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 8110d6a00c86..6abfc8e92fcc 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -422,15 +422,6 @@ config PCH_PHUB To compile this driver as a module, choose M here: the module will be called pch_phub. -config USB_SWITCH_FSA9480 - tristate "FSA9480 USB Switch" - depends on I2C - help - The FSA9480 is a USB port accessory detector and switch. - The FSA9480 is fully controlled using I2C and enables USB data, - stereo and mono audio, video, microphone and UART data to use - a common connector port. - config LATTICE_ECP3_CONFIG tristate "Lattice ECP3 FPGA bitstream configuration via SPI" depends on SPI && SYSFS diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 0cb35466c578..abd8ae249746 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -42,7 +42,6 @@ obj-$(CONFIG_VMWARE_BALLOON) += vmw_balloon.o obj-$(CONFIG_PCH_PHUB) += pch_phub.o obj-y += ti-st/ obj-y += lis3lv02d/ -obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o obj-$(CONFIG_ALTERA_STAPL) +=altera-stapl/ obj-$(CONFIG_INTEL_MEI) += mei/ obj-$(CONFIG_VMWARE_VMCI) += vmw_vmci/ diff --git a/drivers/misc/fsa9480.c b/drivers/misc/fsa9480.c deleted file mode 100644 index 4e11807040d3..000000000000 --- a/drivers/misc/fsa9480.c +++ /dev/null @@ -1,547 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * fsa9480.c - FSA9480 micro USB switch device driver - * - * Copyright (C) 2010 Samsung Electronics - * Minkyu Kang - * Wonguk Jeong - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* FSA9480 I2C registers */ -#define FSA9480_REG_DEVID 0x01 -#define FSA9480_REG_CTRL 0x02 -#define FSA9480_REG_INT1 0x03 -#define FSA9480_REG_INT2 0x04 -#define FSA9480_REG_INT1_MASK 0x05 -#define FSA9480_REG_INT2_MASK 0x06 -#define FSA9480_REG_ADC 0x07 -#define FSA9480_REG_TIMING1 0x08 -#define FSA9480_REG_TIMING2 0x09 -#define FSA9480_REG_DEV_T1 0x0a -#define FSA9480_REG_DEV_T2 0x0b -#define FSA9480_REG_BTN1 0x0c -#define FSA9480_REG_BTN2 0x0d -#define FSA9480_REG_CK 0x0e -#define FSA9480_REG_CK_INT1 0x0f -#define FSA9480_REG_CK_INT2 0x10 -#define FSA9480_REG_CK_INTMASK1 0x11 -#define FSA9480_REG_CK_INTMASK2 0x12 -#define FSA9480_REG_MANSW1 0x13 -#define FSA9480_REG_MANSW2 0x14 - -/* Control */ -#define CON_SWITCH_OPEN (1 << 4) -#define CON_RAW_DATA (1 << 3) -#define CON_MANUAL_SW (1 << 2) -#define CON_WAIT (1 << 1) -#define CON_INT_MASK (1 << 0) -#define CON_MASK (CON_SWITCH_OPEN | CON_RAW_DATA | \ - CON_MANUAL_SW | CON_WAIT) - -/* Device Type 1 */ -#define DEV_USB_OTG (1 << 7) -#define DEV_DEDICATED_CHG (1 << 6) -#define DEV_USB_CHG (1 << 5) -#define DEV_CAR_KIT (1 << 4) -#define DEV_UART (1 << 3) -#define DEV_USB (1 << 2) -#define DEV_AUDIO_2 (1 << 1) -#define DEV_AUDIO_1 (1 << 0) - -#define DEV_T1_USB_MASK (DEV_USB_OTG | DEV_USB) -#define DEV_T1_UART_MASK (DEV_UART) -#define DEV_T1_CHARGER_MASK (DEV_DEDICATED_CHG | DEV_USB_CHG) - -/* Device Type 2 */ -#define DEV_AV (1 << 6) -#define DEV_TTY (1 << 5) -#define DEV_PPD (1 << 4) -#define DEV_JIG_UART_OFF (1 << 3) -#define DEV_JIG_UART_ON (1 << 2) -#define DEV_JIG_USB_OFF (1 << 1) -#define DEV_JIG_USB_ON (1 << 0) - -#define DEV_T2_USB_MASK (DEV_JIG_USB_OFF | DEV_JIG_USB_ON) -#define DEV_T2_UART_MASK (DEV_JIG_UART_OFF | DEV_JIG_UART_ON) -#define DEV_T2_JIG_MASK (DEV_JIG_USB_OFF | DEV_JIG_USB_ON | \ - DEV_JIG_UART_OFF | DEV_JIG_UART_ON) - -/* - * Manual Switch - * D- [7:5] / D+ [4:2] - * 000: Open all / 001: USB / 010: AUDIO / 011: UART / 100: V_AUDIO - */ -#define SW_VAUDIO ((4 << 5) | (4 << 2)) -#define SW_UART ((3 << 5) | (3 << 2)) -#define SW_AUDIO ((2 << 5) | (2 << 2)) -#define SW_DHOST ((1 << 5) | (1 << 2)) -#define SW_AUTO ((0 << 5) | (0 << 2)) - -/* Interrupt 1 */ -#define INT_DETACH (1 << 1) -#define INT_ATTACH (1 << 0) - -struct fsa9480_usbsw { - struct i2c_client *client; - struct fsa9480_platform_data *pdata; - int dev1; - int dev2; - int mansw; -}; - -static struct fsa9480_usbsw *chip; - -static int fsa9480_write_reg(struct i2c_client *client, - int reg, int value) -{ - int ret; - - ret = i2c_smbus_write_byte_data(client, reg, value); - - if (ret < 0) - dev_err(&client->dev, "%s: err %d\n", __func__, ret); - - return ret; -} - -static int fsa9480_read_reg(struct i2c_client *client, int reg) -{ - int ret; - - ret = i2c_smbus_read_byte_data(client, reg); - - if (ret < 0) - dev_err(&client->dev, "%s: err %d\n", __func__, ret); - - return ret; -} - -static int fsa9480_read_irq(struct i2c_client *client, int *value) -{ - int ret; - - ret = i2c_smbus_read_i2c_block_data(client, - FSA9480_REG_INT1, 2, (u8 *)value); - *value &= 0xffff; - - if (ret < 0) - dev_err(&client->dev, "%s: err %d\n", __func__, ret); - - return ret; -} - -static void fsa9480_set_switch(const char *buf) -{ - struct fsa9480_usbsw *usbsw = chip; - struct i2c_client *client = usbsw->client; - unsigned int value; - unsigned int path = 0; - - value = fsa9480_read_reg(client, FSA9480_REG_CTRL); - - if (!strncmp(buf, "VAUDIO", 6)) { - path = SW_VAUDIO; - value &= ~CON_MANUAL_SW; - } else if (!strncmp(buf, "UART", 4)) { - path = SW_UART; - value &= ~CON_MANUAL_SW; - } else if (!strncmp(buf, "AUDIO", 5)) { - path = SW_AUDIO; - value &= ~CON_MANUAL_SW; - } else if (!strncmp(buf, "DHOST", 5)) { - path = SW_DHOST; - value &= ~CON_MANUAL_SW; - } else if (!strncmp(buf, "AUTO", 4)) { - path = SW_AUTO; - value |= CON_MANUAL_SW; - } else { - printk(KERN_ERR "Wrong command\n"); - return; - } - - usbsw->mansw = path; - fsa9480_write_reg(client, FSA9480_REG_MANSW1, path); - fsa9480_write_reg(client, FSA9480_REG_CTRL, value); -} - -static ssize_t fsa9480_get_switch(char *buf) -{ - struct fsa9480_usbsw *usbsw = chip; - struct i2c_client *client = usbsw->client; - unsigned int value; - - value = fsa9480_read_reg(client, FSA9480_REG_MANSW1); - - if (value == SW_VAUDIO) - return sprintf(buf, "VAUDIO\n"); - else if (value == SW_UART) - return sprintf(buf, "UART\n"); - else if (value == SW_AUDIO) - return sprintf(buf, "AUDIO\n"); - else if (value == SW_DHOST) - return sprintf(buf, "DHOST\n"); - else if (value == SW_AUTO) - return sprintf(buf, "AUTO\n"); - else - return sprintf(buf, "%x", value); -} - -static ssize_t fsa9480_show_device(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct fsa9480_usbsw *usbsw = dev_get_drvdata(dev); - struct i2c_client *client = usbsw->client; - int dev1, dev2; - - dev1 = fsa9480_read_reg(client, FSA9480_REG_DEV_T1); - dev2 = fsa9480_read_reg(client, FSA9480_REG_DEV_T2); - - if (!dev1 && !dev2) - return sprintf(buf, "NONE\n"); - - /* USB */ - if (dev1 & DEV_T1_USB_MASK || dev2 & DEV_T2_USB_MASK) - return sprintf(buf, "USB\n"); - - /* UART */ - if (dev1 & DEV_T1_UART_MASK || dev2 & DEV_T2_UART_MASK) - return sprintf(buf, "UART\n"); - - /* CHARGER */ - if (dev1 & DEV_T1_CHARGER_MASK) - return sprintf(buf, "CHARGER\n"); - - /* JIG */ - if (dev2 & DEV_T2_JIG_MASK) - return sprintf(buf, "JIG\n"); - - return sprintf(buf, "UNKNOWN\n"); -} - -static ssize_t fsa9480_show_manualsw(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return fsa9480_get_switch(buf); - -} - -static ssize_t fsa9480_set_manualsw(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - fsa9480_set_switch(buf); - - return count; -} - -static DEVICE_ATTR(device, S_IRUGO, fsa9480_show_device, NULL); -static DEVICE_ATTR(switch, S_IRUGO | S_IWUSR, - fsa9480_show_manualsw, fsa9480_set_manualsw); - -static struct attribute *fsa9480_attributes[] = { - &dev_attr_device.attr, - &dev_attr_switch.attr, - NULL -}; - -static const struct attribute_group fsa9480_group = { - .attrs = fsa9480_attributes, -}; - -static void fsa9480_detect_dev(struct fsa9480_usbsw *usbsw, int intr) -{ - int val1, val2, ctrl; - struct fsa9480_platform_data *pdata = usbsw->pdata; - struct i2c_client *client = usbsw->client; - - val1 = fsa9480_read_reg(client, FSA9480_REG_DEV_T1); - val2 = fsa9480_read_reg(client, FSA9480_REG_DEV_T2); - ctrl = fsa9480_read_reg(client, FSA9480_REG_CTRL); - - dev_info(&client->dev, "intr: 0x%x, dev1: 0x%x, dev2: 0x%x\n", - intr, val1, val2); - - if (!intr) - goto out; - - if (intr & INT_ATTACH) { /* Attached */ - /* USB */ - if (val1 & DEV_T1_USB_MASK || val2 & DEV_T2_USB_MASK) { - if (pdata->usb_cb) - pdata->usb_cb(FSA9480_ATTACHED); - - if (usbsw->mansw) { - fsa9480_write_reg(client, - FSA9480_REG_MANSW1, usbsw->mansw); - } - } - - /* UART */ - if (val1 & DEV_T1_UART_MASK || val2 & DEV_T2_UART_MASK) { - if (pdata->uart_cb) - pdata->uart_cb(FSA9480_ATTACHED); - - if (!(ctrl & CON_MANUAL_SW)) { - fsa9480_write_reg(client, - FSA9480_REG_MANSW1, SW_UART); - } - } - - /* CHARGER */ - if (val1 & DEV_T1_CHARGER_MASK) { - if (pdata->charger_cb) - pdata->charger_cb(FSA9480_ATTACHED); - } - - /* JIG */ - if (val2 & DEV_T2_JIG_MASK) { - if (pdata->jig_cb) - pdata->jig_cb(FSA9480_ATTACHED); - } - } else if (intr & INT_DETACH) { /* Detached */ - /* USB */ - if (usbsw->dev1 & DEV_T1_USB_MASK || - usbsw->dev2 & DEV_T2_USB_MASK) { - if (pdata->usb_cb) - pdata->usb_cb(FSA9480_DETACHED); - } - - /* UART */ - if (usbsw->dev1 & DEV_T1_UART_MASK || - usbsw->dev2 & DEV_T2_UART_MASK) { - if (pdata->uart_cb) - pdata->uart_cb(FSA9480_DETACHED); - } - - /* CHARGER */ - if (usbsw->dev1 & DEV_T1_CHARGER_MASK) { - if (pdata->charger_cb) - pdata->charger_cb(FSA9480_DETACHED); - } - - /* JIG */ - if (usbsw->dev2 & DEV_T2_JIG_MASK) { - if (pdata->jig_cb) - pdata->jig_cb(FSA9480_DETACHED); - } - } - - usbsw->dev1 = val1; - usbsw->dev2 = val2; - -out: - ctrl &= ~CON_INT_MASK; - fsa9480_write_reg(client, FSA9480_REG_CTRL, ctrl); -} - -static irqreturn_t fsa9480_irq_handler(int irq, void *data) -{ - struct fsa9480_usbsw *usbsw = data; - struct i2c_client *client = usbsw->client; - int intr; - - /* clear interrupt */ - fsa9480_read_irq(client, &intr); - - /* device detection */ - fsa9480_detect_dev(usbsw, intr); - - return IRQ_HANDLED; -} - -static int fsa9480_irq_init(struct fsa9480_usbsw *usbsw) -{ - struct fsa9480_platform_data *pdata = usbsw->pdata; - struct i2c_client *client = usbsw->client; - int ret; - int intr; - unsigned int ctrl = CON_MASK; - - /* clear interrupt */ - fsa9480_read_irq(client, &intr); - - /* unmask interrupt (attach/detach only) */ - fsa9480_write_reg(client, FSA9480_REG_INT1_MASK, 0xfc); - fsa9480_write_reg(client, FSA9480_REG_INT2_MASK, 0x1f); - - usbsw->mansw = fsa9480_read_reg(client, FSA9480_REG_MANSW1); - - if (usbsw->mansw) - ctrl &= ~CON_MANUAL_SW; /* Manual Switching Mode */ - - fsa9480_write_reg(client, FSA9480_REG_CTRL, ctrl); - - if (pdata && pdata->cfg_gpio) - pdata->cfg_gpio(); - - if (client->irq) { - ret = request_threaded_irq(client->irq, NULL, - fsa9480_irq_handler, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - "fsa9480 micro USB", usbsw); - if (ret) { - dev_err(&client->dev, "failed to request IRQ\n"); - return ret; - } - - if (pdata) - device_init_wakeup(&client->dev, pdata->wakeup); - } - - return 0; -} - -static int fsa9480_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct i2c_adapter *adapter = client->adapter; - struct fsa9480_usbsw *usbsw; - int ret = 0; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -EIO; - - usbsw = kzalloc(sizeof(struct fsa9480_usbsw), GFP_KERNEL); - if (!usbsw) { - dev_err(&client->dev, "failed to allocate driver data\n"); - return -ENOMEM; - } - - usbsw->client = client; - usbsw->pdata = client->dev.platform_data; - - chip = usbsw; - - i2c_set_clientdata(client, usbsw); - - ret = fsa9480_irq_init(usbsw); - if (ret) - goto fail1; - - ret = sysfs_create_group(&client->dev.kobj, &fsa9480_group); - if (ret) { - dev_err(&client->dev, - "failed to create fsa9480 attribute group\n"); - goto fail2; - } - - /* ADC Detect Time: 500ms */ - fsa9480_write_reg(client, FSA9480_REG_TIMING1, 0x6); - - if (chip->pdata->reset_cb) - chip->pdata->reset_cb(); - - /* device detection */ - fsa9480_detect_dev(usbsw, INT_ATTACH); - - pm_runtime_set_active(&client->dev); - - return 0; - -fail2: - if (client->irq) - free_irq(client->irq, usbsw); -fail1: - kfree(usbsw); - return ret; -} - -static int fsa9480_remove(struct i2c_client *client) -{ - struct fsa9480_usbsw *usbsw = i2c_get_clientdata(client); - - if (client->irq) - free_irq(client->irq, usbsw); - - sysfs_remove_group(&client->dev.kobj, &fsa9480_group); - device_init_wakeup(&client->dev, 0); - kfree(usbsw); - return 0; -} - -#ifdef CONFIG_PM_SLEEP - -static int fsa9480_suspend(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct fsa9480_usbsw *usbsw = i2c_get_clientdata(client); - struct fsa9480_platform_data *pdata = usbsw->pdata; - - if (device_may_wakeup(&client->dev) && client->irq) - enable_irq_wake(client->irq); - - if (pdata->usb_power) - pdata->usb_power(0); - - return 0; -} - -static int fsa9480_resume(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct fsa9480_usbsw *usbsw = i2c_get_clientdata(client); - int dev1, dev2; - - if (device_may_wakeup(&client->dev) && client->irq) - disable_irq_wake(client->irq); - - /* - * Clear Pending interrupt. Note that detect_dev does what - * the interrupt handler does. So, we don't miss pending and - * we reenable interrupt if there is one. - */ - fsa9480_read_reg(client, FSA9480_REG_INT1); - fsa9480_read_reg(client, FSA9480_REG_INT2); - - dev1 = fsa9480_read_reg(client, FSA9480_REG_DEV_T1); - dev2 = fsa9480_read_reg(client, FSA9480_REG_DEV_T2); - - /* device detection */ - fsa9480_detect_dev(usbsw, (dev1 || dev2) ? INT_ATTACH : INT_DETACH); - - return 0; -} - -static SIMPLE_DEV_PM_OPS(fsa9480_pm_ops, fsa9480_suspend, fsa9480_resume); -#define FSA9480_PM_OPS (&fsa9480_pm_ops) - -#else - -#define FSA9480_PM_OPS NULL - -#endif /* CONFIG_PM_SLEEP */ - -static const struct i2c_device_id fsa9480_id[] = { - {"fsa9480", 0}, - {} -}; -MODULE_DEVICE_TABLE(i2c, fsa9480_id); - -static struct i2c_driver fsa9480_i2c_driver = { - .driver = { - .name = "fsa9480", - .pm = FSA9480_PM_OPS, - }, - .probe = fsa9480_probe, - .remove = fsa9480_remove, - .id_table = fsa9480_id, -}; - -module_i2c_driver(fsa9480_i2c_driver); - -MODULE_AUTHOR("Minkyu Kang "); -MODULE_DESCRIPTION("FSA9480 USB Switch driver"); -MODULE_LICENSE("GPL"); diff --git a/include/linux/platform_data/fsa9480.h b/include/linux/platform_data/fsa9480.h deleted file mode 100644 index dea8d84448ec..000000000000 --- a/include/linux/platform_data/fsa9480.h +++ /dev/null @@ -1,24 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) 2010 Samsung Electronics - * Minkyu Kang - */ - -#ifndef _FSA9480_H_ -#define _FSA9480_H_ - -#define FSA9480_ATTACHED 1 -#define FSA9480_DETACHED 0 - -struct fsa9480_platform_data { - void (*cfg_gpio) (void); - void (*usb_cb) (u8 attached); - void (*uart_cb) (u8 attached); - void (*charger_cb) (u8 attached); - void (*jig_cb) (u8 attached); - void (*reset_cb) (void); - void (*usb_power) (u8 on); - int wakeup; -}; - -#endif /* _FSA9480_H_ */ -- cgit v1.2.3 From 9af93db9e140a4e6e79cdb098919bc928a72cd59 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Wed, 17 Jul 2019 13:10:58 +0800 Subject: platform/x86: asus: Rename "fan mode" to "fan boost mode" The Asus WMI spec indicates that the function being controlled here is called "Fan Boost Mode". The user-facing documentation also calls it this. The spec uses the term "fan mode" is used to refer to other things, including functionality expected to appear on future products. We missed this before as we are not dealing with the most readable of specs, and didn't forsee any confusion around shortening the name. Rename "fan mode" to "fan boost mode" to improve consistency with the spec and to avoid a future naming conflict. There is no interface breakage here since this has yet to be included in an official kernel release. I also updated the kernel version listed under ABI accordingly. Signed-off-by: Daniel Drake Acked-by: Yurii Pavlovskyi Signed-off-by: Andy Shevchenko --- Documentation/ABI/testing/sysfs-platform-asus-wmi | 6 +- drivers/platform/x86/asus-wmi.c | 118 ++++++++++++---------- include/linux/platform_data/x86/asus-wmi.h | 2 +- 3 files changed, 66 insertions(+), 60 deletions(-) (limited to 'include/linux/platform_data') diff --git a/Documentation/ABI/testing/sysfs-platform-asus-wmi b/Documentation/ABI/testing/sysfs-platform-asus-wmi index 87ae5cc983bf..9e99f2909612 100644 --- a/Documentation/ABI/testing/sysfs-platform-asus-wmi +++ b/Documentation/ABI/testing/sysfs-platform-asus-wmi @@ -37,9 +37,9 @@ Contact: "AceLan Kao" Description: Resume on lid open. 1 means on, 0 means off. -What: /sys/devices/platform//fan_mode -Date: Apr 2019 -KernelVersion: 5.2 +What: /sys/devices/platform//fan_boost_mode +Date: Sep 2019 +KernelVersion: 5.3 Contact: "Yurii Pavlovskyi" Description: Fan boost mode: diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 18f3a8bad52f..ca28d27dae63 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -68,12 +68,12 @@ MODULE_LICENSE("GPL"); #define ASUS_FAN_CTRL_MANUAL 1 #define ASUS_FAN_CTRL_AUTO 2 -#define ASUS_FAN_MODE_NORMAL 0 -#define ASUS_FAN_MODE_OVERBOOST 1 -#define ASUS_FAN_MODE_OVERBOOST_MASK 0x01 -#define ASUS_FAN_MODE_SILENT 2 -#define ASUS_FAN_MODE_SILENT_MASK 0x02 -#define ASUS_FAN_MODES_MASK 0x03 +#define ASUS_FAN_BOOST_MODE_NORMAL 0 +#define ASUS_FAN_BOOST_MODE_OVERBOOST 1 +#define ASUS_FAN_BOOST_MODE_OVERBOOST_MASK 0x01 +#define ASUS_FAN_BOOST_MODE_SILENT 2 +#define ASUS_FAN_BOOST_MODE_SILENT_MASK 0x02 +#define ASUS_FAN_BOOST_MODES_MASK 0x03 #define USB_INTEL_XUSB2PR 0xD0 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31 @@ -182,9 +182,9 @@ struct asus_wmi { int asus_hwmon_num_fans; int asus_hwmon_pwm; - bool fan_mode_available; - u8 fan_mode_mask; - u8 fan_mode; + bool fan_boost_mode_available; + u8 fan_boost_mode_mask; + u8 fan_boost_mode; struct hotplug_slot hotplug_slot; struct mutex hotplug_lock; @@ -1487,14 +1487,15 @@ static int asus_wmi_fan_init(struct asus_wmi *asus) /* Fan mode *******************************************************************/ -static int fan_mode_check_present(struct asus_wmi *asus) +static int fan_boost_mode_check_present(struct asus_wmi *asus) { u32 result; int err; - asus->fan_mode_available = false; + asus->fan_boost_mode_available = false; - err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FAN_MODE, &result); + err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FAN_BOOST_MODE, + &result); if (err) { if (err == -ENODEV) return 0; @@ -1503,72 +1504,77 @@ static int fan_mode_check_present(struct asus_wmi *asus) } if ((result & ASUS_WMI_DSTS_PRESENCE_BIT) && - (result & ASUS_FAN_MODES_MASK)) { - asus->fan_mode_available = true; - asus->fan_mode_mask = result & ASUS_FAN_MODES_MASK; + (result & ASUS_FAN_BOOST_MODES_MASK)) { + asus->fan_boost_mode_available = true; + asus->fan_boost_mode_mask = result & ASUS_FAN_BOOST_MODES_MASK; } return 0; } -static int fan_mode_write(struct asus_wmi *asus) +static int fan_boost_mode_write(struct asus_wmi *asus) { int err; u8 value; u32 retval; - value = asus->fan_mode; + value = asus->fan_boost_mode; - pr_info("Set fan mode: %u\n", value); - err = asus_wmi_set_devstate(ASUS_WMI_DEVID_FAN_MODE, value, &retval); + pr_info("Set fan boost mode: %u\n", value); + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_FAN_BOOST_MODE, value, + &retval); if (err) { - pr_warn("Failed to set fan mode: %d\n", err); + pr_warn("Failed to set fan boost mode: %d\n", err); return err; } if (retval != 1) { - pr_warn("Failed to set fan mode (retval): 0x%x\n", retval); + pr_warn("Failed to set fan boost mode (retval): 0x%x\n", + retval); return -EIO; } return 0; } -static int fan_mode_switch_next(struct asus_wmi *asus) +static int fan_boost_mode_switch_next(struct asus_wmi *asus) { - if (asus->fan_mode == ASUS_FAN_MODE_NORMAL) { - if (asus->fan_mode_mask & ASUS_FAN_MODE_OVERBOOST_MASK) - asus->fan_mode = ASUS_FAN_MODE_OVERBOOST; - else if (asus->fan_mode_mask & ASUS_FAN_MODE_SILENT_MASK) - asus->fan_mode = ASUS_FAN_MODE_SILENT; - } else if (asus->fan_mode == ASUS_FAN_MODE_OVERBOOST) { - if (asus->fan_mode_mask & ASUS_FAN_MODE_SILENT_MASK) - asus->fan_mode = ASUS_FAN_MODE_SILENT; + u8 mask = asus->fan_boost_mode_mask; + + if (asus->fan_boost_mode == ASUS_FAN_BOOST_MODE_NORMAL) { + if (mask & ASUS_FAN_BOOST_MODE_OVERBOOST_MASK) + asus->fan_boost_mode = ASUS_FAN_BOOST_MODE_OVERBOOST; + else if (mask & ASUS_FAN_BOOST_MODE_SILENT_MASK) + asus->fan_boost_mode = ASUS_FAN_BOOST_MODE_SILENT; + } else if (asus->fan_boost_mode == ASUS_FAN_BOOST_MODE_OVERBOOST) { + if (mask & ASUS_FAN_BOOST_MODE_SILENT_MASK) + asus->fan_boost_mode = ASUS_FAN_BOOST_MODE_SILENT; else - asus->fan_mode = ASUS_FAN_MODE_NORMAL; + asus->fan_boost_mode = ASUS_FAN_BOOST_MODE_NORMAL; } else { - asus->fan_mode = ASUS_FAN_MODE_NORMAL; + asus->fan_boost_mode = ASUS_FAN_BOOST_MODE_NORMAL; } - return fan_mode_write(asus); + return fan_boost_mode_write(asus); } -static ssize_t fan_mode_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t fan_boost_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) { struct asus_wmi *asus = dev_get_drvdata(dev); - return scnprintf(buf, PAGE_SIZE, "%d\n", asus->fan_mode); + return scnprintf(buf, PAGE_SIZE, "%d\n", asus->fan_boost_mode); } -static ssize_t fan_mode_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +static ssize_t fan_boost_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { int result; u8 new_mode; - struct asus_wmi *asus = dev_get_drvdata(dev); + u8 mask = asus->fan_boost_mode_mask; result = kstrtou8(buf, 10, &new_mode); if (result < 0) { @@ -1576,24 +1582,24 @@ static ssize_t fan_mode_store(struct device *dev, struct device_attribute *attr, return result; } - if (new_mode == ASUS_FAN_MODE_OVERBOOST) { - if (!(asus->fan_mode_mask & ASUS_FAN_MODE_OVERBOOST_MASK)) + if (new_mode == ASUS_FAN_BOOST_MODE_OVERBOOST) { + if (!(mask & ASUS_FAN_BOOST_MODE_OVERBOOST_MASK)) return -EINVAL; - } else if (new_mode == ASUS_FAN_MODE_SILENT) { - if (!(asus->fan_mode_mask & ASUS_FAN_MODE_SILENT_MASK)) + } else if (new_mode == ASUS_FAN_BOOST_MODE_SILENT) { + if (!(mask & ASUS_FAN_BOOST_MODE_SILENT_MASK)) return -EINVAL; - } else if (new_mode != ASUS_FAN_MODE_NORMAL) { + } else if (new_mode != ASUS_FAN_BOOST_MODE_NORMAL) { return -EINVAL; } - asus->fan_mode = new_mode; - fan_mode_write(asus); + asus->fan_boost_mode = new_mode; + fan_boost_mode_write(asus); return result; } -// Fan mode: 0 - normal, 1 - overboost, 2 - silent -static DEVICE_ATTR_RW(fan_mode); +// Fan boost mode: 0 - normal, 1 - overboost, 2 - silent +static DEVICE_ATTR_RW(fan_boost_mode); /* Backlight ******************************************************************/ @@ -1873,8 +1879,8 @@ static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus) return; } - if (asus->fan_mode_available && code == NOTIFY_KBD_FBM) { - fan_mode_switch_next(asus); + if (asus->fan_boost_mode_available && code == NOTIFY_KBD_FBM) { + fan_boost_mode_switch_next(asus); return; } @@ -2034,7 +2040,7 @@ static struct attribute *platform_attributes[] = { &dev_attr_touchpad.attr, &dev_attr_lid_resume.attr, &dev_attr_als_enable.attr, - &dev_attr_fan_mode.attr, + &dev_attr_fan_boost_mode.attr, NULL }; @@ -2056,8 +2062,8 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj, devid = ASUS_WMI_DEVID_LID_RESUME; else if (attr == &dev_attr_als_enable.attr) devid = ASUS_WMI_DEVID_ALS_ENABLE; - else if (attr == &dev_attr_fan_mode.attr) - ok = asus->fan_mode_available; + else if (attr == &dev_attr_fan_boost_mode.attr) + ok = asus->fan_boost_mode_available; if (devid != -1) ok = !(asus_wmi_get_devstate_simple(asus, devid) < 0); @@ -2315,9 +2321,9 @@ static int asus_wmi_add(struct platform_device *pdev) if (err) goto fail_platform; - err = fan_mode_check_present(asus); + err = fan_boost_mode_check_present(asus); if (err) - goto fail_fan_mode; + goto fail_fan_boost_mode; err = asus_wmi_sysfs_init(asus->platform_device); if (err) @@ -2402,7 +2408,7 @@ fail_hwmon: fail_input: asus_wmi_sysfs_exit(asus->platform_device); fail_sysfs: -fail_fan_mode: +fail_fan_boost_mode: fail_platform: kfree(asus); return err; diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h index 8551156b8dca..4802cd2c7309 100644 --- a/include/linux/platform_data/x86/asus-wmi.h +++ b/include/linux/platform_data/x86/asus-wmi.h @@ -57,7 +57,7 @@ #define ASUS_WMI_DEVID_KBD_BACKLIGHT 0x00050021 #define ASUS_WMI_DEVID_LIGHT_SENSOR 0x00050022 /* ?? */ #define ASUS_WMI_DEVID_LIGHTBAR 0x00050025 -#define ASUS_WMI_DEVID_FAN_MODE 0x00110018 +#define ASUS_WMI_DEVID_FAN_BOOST_MODE 0x00110018 /* Misc */ #define ASUS_WMI_DEVID_CAMERA 0x00060013 -- cgit v1.2.3 From 060157e1dbc133075a2e20786d6ff6d4b41909f9 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 9 Aug 2019 14:55:43 -0700 Subject: Input: remove w90x900 keyboard driver The ARM w90x900 platform is getting removed, so this driver is obsolete. Signed-off-by: Arnd Bergmann Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/Kconfig | 11 -- drivers/input/keyboard/Makefile | 1 - drivers/input/keyboard/w90p910_keypad.c | 264 --------------------------- include/linux/platform_data/keypad-w90p910.h | 16 -- 4 files changed, 292 deletions(-) delete mode 100644 drivers/input/keyboard/w90p910_keypad.c delete mode 100644 include/linux/platform_data/keypad-w90p910.h (limited to 'include/linux/platform_data') diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 8e9c3ea9d5e7..c1da129a4eb5 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -731,17 +731,6 @@ config KEYBOARD_XTKBD To compile this driver as a module, choose M here: the module will be called xtkbd. -config KEYBOARD_W90P910 - tristate "W90P910 Matrix Keypad support" - depends on ARCH_W90X900 - select INPUT_MATRIXKMAP - help - Say Y here to enable the matrix keypad on evaluation board - based on W90P910. - - To compile this driver as a module, choose M here: the - module will be called w90p910_keypad. - config KEYBOARD_CROS_EC tristate "ChromeOS EC keyboard" select INPUT_MATRIXKMAP diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 06a0af6efeae..9510325c0c5d 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -68,4 +68,3 @@ obj-$(CONFIG_KEYBOARD_TEGRA) += tegra-kbc.o obj-$(CONFIG_KEYBOARD_TM2_TOUCHKEY) += tm2-touchkey.o obj-$(CONFIG_KEYBOARD_TWL4030) += twl4030_keypad.o obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o -obj-$(CONFIG_KEYBOARD_W90P910) += w90p910_keypad.o diff --git a/drivers/input/keyboard/w90p910_keypad.c b/drivers/input/keyboard/w90p910_keypad.c deleted file mode 100644 index c88d05d6108a..000000000000 --- a/drivers/input/keyboard/w90p910_keypad.c +++ /dev/null @@ -1,264 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2008-2009 Nuvoton technology corporation. - * - * Wan ZongShun - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -/* Keypad Interface Control Registers */ -#define KPI_CONF 0x00 -#define KPI_3KCONF 0x04 -#define KPI_LPCONF 0x08 -#define KPI_STATUS 0x0C - -#define IS1KEY (0x01 << 16) -#define INTTR (0x01 << 21) -#define KEY0R (0x0f << 3) -#define KEY0C 0x07 -#define DEBOUNCE_BIT 0x08 -#define KSIZE0 (0x01 << 16) -#define KSIZE1 (0x01 << 17) -#define KPSEL (0x01 << 19) -#define ENKP (0x01 << 18) - -#define KGET_RAW(n) (((n) & KEY0R) >> 3) -#define KGET_COLUMN(n) ((n) & KEY0C) - -#define W90P910_NUM_ROWS 8 -#define W90P910_NUM_COLS 8 -#define W90P910_ROW_SHIFT 3 - -struct w90p910_keypad { - const struct w90p910_keypad_platform_data *pdata; - struct clk *clk; - struct input_dev *input_dev; - void __iomem *mmio_base; - int irq; - unsigned short keymap[W90P910_NUM_ROWS * W90P910_NUM_COLS]; -}; - -static void w90p910_keypad_scan_matrix(struct w90p910_keypad *keypad, - unsigned int status) -{ - struct input_dev *input_dev = keypad->input_dev; - unsigned int row = KGET_RAW(status); - unsigned int col = KGET_COLUMN(status); - unsigned int code = MATRIX_SCAN_CODE(row, col, W90P910_ROW_SHIFT); - unsigned int key = keypad->keymap[code]; - - input_event(input_dev, EV_MSC, MSC_SCAN, code); - input_report_key(input_dev, key, 1); - input_sync(input_dev); - - input_event(input_dev, EV_MSC, MSC_SCAN, code); - input_report_key(input_dev, key, 0); - input_sync(input_dev); -} - -static irqreturn_t w90p910_keypad_irq_handler(int irq, void *dev_id) -{ - struct w90p910_keypad *keypad = dev_id; - unsigned int kstatus, val; - - kstatus = __raw_readl(keypad->mmio_base + KPI_STATUS); - - val = INTTR | IS1KEY; - - if (kstatus & val) - w90p910_keypad_scan_matrix(keypad, kstatus); - - return IRQ_HANDLED; -} - -static int w90p910_keypad_open(struct input_dev *dev) -{ - struct w90p910_keypad *keypad = input_get_drvdata(dev); - const struct w90p910_keypad_platform_data *pdata = keypad->pdata; - unsigned int val, config; - - /* Enable unit clock */ - clk_enable(keypad->clk); - - val = __raw_readl(keypad->mmio_base + KPI_CONF); - val |= (KPSEL | ENKP); - val &= ~(KSIZE0 | KSIZE1); - - config = pdata->prescale | (pdata->debounce << DEBOUNCE_BIT); - - val |= config; - - __raw_writel(val, keypad->mmio_base + KPI_CONF); - - return 0; -} - -static void w90p910_keypad_close(struct input_dev *dev) -{ - struct w90p910_keypad *keypad = input_get_drvdata(dev); - - /* Disable clock unit */ - clk_disable(keypad->clk); -} - -static int w90p910_keypad_probe(struct platform_device *pdev) -{ - const struct w90p910_keypad_platform_data *pdata = - dev_get_platdata(&pdev->dev); - const struct matrix_keymap_data *keymap_data; - struct w90p910_keypad *keypad; - struct input_dev *input_dev; - struct resource *res; - int irq; - int error; - - if (!pdata) { - dev_err(&pdev->dev, "no platform data defined\n"); - return -EINVAL; - } - - keymap_data = pdata->keymap_data; - - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "failed to get keypad irq\n"); - return -ENXIO; - } - - keypad = kzalloc(sizeof(struct w90p910_keypad), GFP_KERNEL); - input_dev = input_allocate_device(); - if (!keypad || !input_dev) { - dev_err(&pdev->dev, "failed to allocate driver data\n"); - error = -ENOMEM; - goto failed_free; - } - - keypad->pdata = pdata; - keypad->input_dev = input_dev; - keypad->irq = irq; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { - dev_err(&pdev->dev, "failed to get I/O memory\n"); - error = -ENXIO; - goto failed_free; - } - - res = request_mem_region(res->start, resource_size(res), pdev->name); - if (res == NULL) { - dev_err(&pdev->dev, "failed to request I/O memory\n"); - error = -EBUSY; - goto failed_free; - } - - keypad->mmio_base = ioremap(res->start, resource_size(res)); - if (keypad->mmio_base == NULL) { - dev_err(&pdev->dev, "failed to remap I/O memory\n"); - error = -ENXIO; - goto failed_free_res; - } - - keypad->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(keypad->clk)) { - dev_err(&pdev->dev, "failed to get keypad clock\n"); - error = PTR_ERR(keypad->clk); - goto failed_free_io; - } - - /* set multi-function pin for w90p910 kpi. */ - mfp_set_groupi(&pdev->dev); - - input_dev->name = pdev->name; - input_dev->id.bustype = BUS_HOST; - input_dev->open = w90p910_keypad_open; - input_dev->close = w90p910_keypad_close; - input_dev->dev.parent = &pdev->dev; - - error = matrix_keypad_build_keymap(keymap_data, NULL, - W90P910_NUM_ROWS, W90P910_NUM_COLS, - keypad->keymap, input_dev); - if (error) { - dev_err(&pdev->dev, "failed to build keymap\n"); - goto failed_put_clk; - } - - error = request_irq(keypad->irq, w90p910_keypad_irq_handler, - 0, pdev->name, keypad); - if (error) { - dev_err(&pdev->dev, "failed to request IRQ\n"); - goto failed_put_clk; - } - - __set_bit(EV_REP, input_dev->evbit); - input_set_capability(input_dev, EV_MSC, MSC_SCAN); - input_set_drvdata(input_dev, keypad); - - /* Register the input device */ - error = input_register_device(input_dev); - if (error) { - dev_err(&pdev->dev, "failed to register input device\n"); - goto failed_free_irq; - } - - platform_set_drvdata(pdev, keypad); - return 0; - -failed_free_irq: - free_irq(irq, keypad); -failed_put_clk: - clk_put(keypad->clk); -failed_free_io: - iounmap(keypad->mmio_base); -failed_free_res: - release_mem_region(res->start, resource_size(res)); -failed_free: - input_free_device(input_dev); - kfree(keypad); - return error; -} - -static int w90p910_keypad_remove(struct platform_device *pdev) -{ - struct w90p910_keypad *keypad = platform_get_drvdata(pdev); - struct resource *res; - - free_irq(keypad->irq, keypad); - - clk_put(keypad->clk); - - input_unregister_device(keypad->input_dev); - - iounmap(keypad->mmio_base); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(res->start, resource_size(res)); - - kfree(keypad); - - return 0; -} - -static struct platform_driver w90p910_keypad_driver = { - .probe = w90p910_keypad_probe, - .remove = w90p910_keypad_remove, - .driver = { - .name = "nuc900-kpi", - }, -}; -module_platform_driver(w90p910_keypad_driver); - -MODULE_AUTHOR("Wan ZongShun "); -MODULE_DESCRIPTION("w90p910 keypad driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:nuc900-keypad"); diff --git a/include/linux/platform_data/keypad-w90p910.h b/include/linux/platform_data/keypad-w90p910.h deleted file mode 100644 index 206ca4ecd93f..000000000000 --- a/include/linux/platform_data/keypad-w90p910.h +++ /dev/null @@ -1,16 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __ASM_ARCH_W90P910_KEYPAD_H -#define __ASM_ARCH_W90P910_KEYPAD_H - -#include - -extern void mfp_set_groupi(struct device *dev); - -struct w90p910_keypad_platform_data { - const struct matrix_keymap_data *keymap_data; - - unsigned int prescale; - unsigned int debounce; -}; - -#endif /* __ASM_ARCH_W90P910_KEYPAD_H */ -- cgit v1.2.3