* [PATCH RFC v4 05/10] net: qualcomm: prepare frame decoding for UART driver
From: Stefan Wahren @ 2017-03-27 13:37 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, David S. Miller
Cc: Greg Kroah-Hartman, Jiri Slaby, Marcel Holtmann,
Sebastian Reichel, netdev-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-serial-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, Stefan Wahren
In-Reply-To: <1490621848-24828-1-git-send-email-stefan.wahren-eS4NqCHxEME@public.gmane.org>
Unfortunately the frame format is not exactly identical between SPI
and UART. In case of SPI there is an additional HW length at the
beginning. So store the initial state to make the decoding state machine
more flexible and easy to extend for UART support.
Signed-off-by: Stefan Wahren <stefan.wahren-eS4NqCHxEME@public.gmane.org>
---
drivers/net/ethernet/qualcomm/qca_common.c | 12 ++++++------
drivers/net/ethernet/qualcomm/qca_common.h | 8 ++++++--
drivers/net/ethernet/qualcomm/qca_spi.c | 2 +-
3 files changed, 13 insertions(+), 9 deletions(-)
diff --git a/drivers/net/ethernet/qualcomm/qca_common.c b/drivers/net/ethernet/qualcomm/qca_common.c
index 26453a9..d930524 100644
--- a/drivers/net/ethernet/qualcomm/qca_common.c
+++ b/drivers/net/ethernet/qualcomm/qca_common.c
@@ -83,7 +83,7 @@ qcafrm_fsm_decode(struct qcafrm_handle *handle, u8 *buf, u16 buf_len, u8 recv_by
if (recv_byte != 0x00) {
/* first two bytes of length must be 0 */
- handle->state = QCAFRM_HW_LEN0;
+ handle->state = handle->init;
}
break;
case QCAFRM_HW_LEN2:
@@ -97,7 +97,7 @@ qcafrm_fsm_decode(struct qcafrm_handle *handle, u8 *buf, u16 buf_len, u8 recv_by
case QCAFRM_WAIT_AA4:
if (recv_byte != 0xAA) {
ret = QCAFRM_NOHEAD;
- handle->state = QCAFRM_HW_LEN0;
+ handle->state = handle->init;
} else {
handle->state--;
}
@@ -119,7 +119,7 @@ qcafrm_fsm_decode(struct qcafrm_handle *handle, u8 *buf, u16 buf_len, u8 recv_by
len = handle->offset;
if (len > buf_len || len < QCAFRM_ETHMINLEN) {
ret = QCAFRM_INVLEN;
- handle->state = QCAFRM_HW_LEN0;
+ handle->state = handle->init;
} else {
handle->state = (enum qcafrm_state)(len + 1);
/* Remaining number of bytes. */
@@ -135,7 +135,7 @@ qcafrm_fsm_decode(struct qcafrm_handle *handle, u8 *buf, u16 buf_len, u8 recv_by
case QCAFRM_WAIT_551:
if (recv_byte != 0x55) {
ret = QCAFRM_NOTAIL;
- handle->state = QCAFRM_HW_LEN0;
+ handle->state = handle->init;
} else {
handle->state = QCAFRM_WAIT_552;
}
@@ -143,11 +143,11 @@ qcafrm_fsm_decode(struct qcafrm_handle *handle, u8 *buf, u16 buf_len, u8 recv_by
case QCAFRM_WAIT_552:
if (recv_byte != 0x55) {
ret = QCAFRM_NOTAIL;
- handle->state = QCAFRM_HW_LEN0;
+ handle->state = handle->init;
} else {
ret = handle->offset;
/* Frame is fully received. */
- handle->state = QCAFRM_HW_LEN0;
+ handle->state = handle->init;
}
break;
}
diff --git a/drivers/net/ethernet/qualcomm/qca_common.h b/drivers/net/ethernet/qualcomm/qca_common.h
index d5e795d..431f99d 100644
--- a/drivers/net/ethernet/qualcomm/qca_common.h
+++ b/drivers/net/ethernet/qualcomm/qca_common.h
@@ -61,6 +61,7 @@
#define QCAFRM_ERR_BASE -1000
enum qcafrm_state {
+ /* HW length is only available on SPI */
QCAFRM_HW_LEN0 = 0x8000,
QCAFRM_HW_LEN1 = QCAFRM_HW_LEN0 - 1,
QCAFRM_HW_LEN2 = QCAFRM_HW_LEN1 - 1,
@@ -101,6 +102,8 @@ enum qcafrm_state {
struct qcafrm_handle {
/* Current decoding state */
enum qcafrm_state state;
+ /* Initial state depends on connection type */
+ enum qcafrm_state init;
/* Offset in buffer (borrowed for length too) */
s16 offset;
@@ -113,9 +116,10 @@ u16 qcafrm_create_header(u8 *buf, u16 len);
u16 qcafrm_create_footer(u8 *buf);
-static inline void qcafrm_fsm_init(struct qcafrm_handle *handle)
+static inline void qcafrm_fsm_init_spi(struct qcafrm_handle *handle)
{
- handle->state = QCAFRM_HW_LEN0;
+ handle->init = QCAFRM_HW_LEN0;
+ handle->state = handle->init;
}
/* Gather received bytes and try to extract a full Ethernet frame
diff --git a/drivers/net/ethernet/qualcomm/qca_spi.c b/drivers/net/ethernet/qualcomm/qca_spi.c
index 65adc10..3617bde 100644
--- a/drivers/net/ethernet/qualcomm/qca_spi.c
+++ b/drivers/net/ethernet/qualcomm/qca_spi.c
@@ -638,7 +638,7 @@ qcaspi_netdev_open(struct net_device *dev)
qca->intr_req = 1;
qca->intr_svc = 0;
qca->sync = QCASPI_SYNC_UNKNOWN;
- qcafrm_fsm_init(&qca->frm_handle);
+ qcafrm_fsm_init_spi(&qca->frm_handle);
qca->spi_thread = kthread_run((void *)qcaspi_spi_thread,
qca, "%s", dev->name);
--
2.1.4
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH RFC v4 04/10] net: qualcomm: rename qca_framing.c to qca_common.c
From: Stefan Wahren @ 2017-03-27 13:37 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, David S. Miller
Cc: Greg Kroah-Hartman, Jiri Slaby, Marcel Holtmann,
Sebastian Reichel, netdev, devicetree, linux-serial, linux-kernel,
Stefan Wahren
In-Reply-To: <1490621848-24828-1-git-send-email-stefan.wahren@i2se.com>
As preparation for the upcoming UART driver we need a module
which contains common functions for both interfaces. The module
qca_framing is a good candidate but renaming to qca_common would
make it clear.
Signed-off-by: Stefan Wahren <stefan.wahren@i2se.com>
---
drivers/net/ethernet/qualcomm/Makefile | 2 +-
drivers/net/ethernet/qualcomm/{qca_framing.c => qca_common.c} | 2 +-
drivers/net/ethernet/qualcomm/{qca_framing.h => qca_common.h} | 0
drivers/net/ethernet/qualcomm/qca_spi.c | 2 +-
drivers/net/ethernet/qualcomm/qca_spi.h | 2 +-
5 files changed, 4 insertions(+), 4 deletions(-)
rename drivers/net/ethernet/qualcomm/{qca_framing.c => qca_common.c} (99%)
rename drivers/net/ethernet/qualcomm/{qca_framing.h => qca_common.h} (100%)
diff --git a/drivers/net/ethernet/qualcomm/Makefile b/drivers/net/ethernet/qualcomm/Makefile
index aacb0a5..8080570 100644
--- a/drivers/net/ethernet/qualcomm/Makefile
+++ b/drivers/net/ethernet/qualcomm/Makefile
@@ -3,6 +3,6 @@
#
obj-$(CONFIG_QCA7000) += qcaspi.o
-qcaspi-objs := qca_spi.o qca_framing.o qca_7k.o qca_debug.o
+qcaspi-objs := qca_spi.o qca_common.o qca_7k.o qca_debug.o
obj-y += emac/
diff --git a/drivers/net/ethernet/qualcomm/qca_framing.c b/drivers/net/ethernet/qualcomm/qca_common.c
similarity index 99%
rename from drivers/net/ethernet/qualcomm/qca_framing.c
rename to drivers/net/ethernet/qualcomm/qca_common.c
index faa924c..26453a9 100644
--- a/drivers/net/ethernet/qualcomm/qca_framing.c
+++ b/drivers/net/ethernet/qualcomm/qca_common.c
@@ -23,7 +23,7 @@
#include <linux/kernel.h>
-#include "qca_framing.h"
+#include "qca_common.h"
u16
qcafrm_create_header(u8 *buf, u16 length)
diff --git a/drivers/net/ethernet/qualcomm/qca_framing.h b/drivers/net/ethernet/qualcomm/qca_common.h
similarity index 100%
rename from drivers/net/ethernet/qualcomm/qca_framing.h
rename to drivers/net/ethernet/qualcomm/qca_common.h
diff --git a/drivers/net/ethernet/qualcomm/qca_spi.c b/drivers/net/ethernet/qualcomm/qca_spi.c
index 4f431bc..65adc10 100644
--- a/drivers/net/ethernet/qualcomm/qca_spi.c
+++ b/drivers/net/ethernet/qualcomm/qca_spi.c
@@ -43,8 +43,8 @@
#include <linux/types.h>
#include "qca_7k.h"
+#include "qca_common.h"
#include "qca_debug.h"
-#include "qca_framing.h"
#include "qca_spi.h"
#define MAX_DMA_BURST_LEN 5000
diff --git a/drivers/net/ethernet/qualcomm/qca_spi.h b/drivers/net/ethernet/qualcomm/qca_spi.h
index 064853d..cce4802 100644
--- a/drivers/net/ethernet/qualcomm/qca_spi.h
+++ b/drivers/net/ethernet/qualcomm/qca_spi.h
@@ -32,7 +32,7 @@
#include <linux/spi/spi.h>
#include <linux/types.h>
-#include "qca_framing.h"
+#include "qca_common.h"
#define QCASPI_DRV_VERSION "0.2.7-i"
#define QCASPI_DRV_NAME "qcaspi"
--
2.1.4
^ permalink raw reply related
* [PATCH RFC v4 03/10] net: qualcomm: move qcaspi_tx_cmd to qca_spi.c
From: Stefan Wahren @ 2017-03-27 13:37 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, David S. Miller
Cc: Greg Kroah-Hartman, Jiri Slaby, Marcel Holtmann,
Sebastian Reichel, netdev, devicetree, linux-serial, linux-kernel,
Stefan Wahren
In-Reply-To: <1490621848-24828-1-git-send-email-stefan.wahren@i2se.com>
The function qcaspi_tx_cmd() is only called from qca_spi.c. So we better
move it there.
Signed-off-by: Stefan Wahren <stefan.wahren@i2se.com>
---
drivers/net/ethernet/qualcomm/qca_7k.c | 24 ------------------------
drivers/net/ethernet/qualcomm/qca_7k.h | 1 -
drivers/net/ethernet/qualcomm/qca_spi.c | 24 ++++++++++++++++++++++++
3 files changed, 24 insertions(+), 25 deletions(-)
diff --git a/drivers/net/ethernet/qualcomm/qca_7k.c b/drivers/net/ethernet/qualcomm/qca_7k.c
index 557d53c..aa90a1d 100644
--- a/drivers/net/ethernet/qualcomm/qca_7k.c
+++ b/drivers/net/ethernet/qualcomm/qca_7k.c
@@ -119,27 +119,3 @@ qcaspi_write_register(struct qcaspi *qca, u16 reg, u16 value)
return ret;
}
-
-int
-qcaspi_tx_cmd(struct qcaspi *qca, u16 cmd)
-{
- __be16 tx_data;
- struct spi_message *msg = &qca->spi_msg1;
- struct spi_transfer *transfer = &qca->spi_xfer1;
- int ret;
-
- tx_data = cpu_to_be16(cmd);
- transfer->len = sizeof(tx_data);
- transfer->tx_buf = &tx_data;
- transfer->rx_buf = NULL;
-
- ret = spi_sync(qca->spi_dev, msg);
-
- if (!ret)
- ret = msg->status;
-
- if (ret)
- qcaspi_spi_error(qca);
-
- return ret;
-}
diff --git a/drivers/net/ethernet/qualcomm/qca_7k.h b/drivers/net/ethernet/qualcomm/qca_7k.h
index 1cad851..b390b1f 100644
--- a/drivers/net/ethernet/qualcomm/qca_7k.h
+++ b/drivers/net/ethernet/qualcomm/qca_7k.h
@@ -67,6 +67,5 @@
void qcaspi_spi_error(struct qcaspi *qca);
int qcaspi_read_register(struct qcaspi *qca, u16 reg, u16 *result);
int qcaspi_write_register(struct qcaspi *qca, u16 reg, u16 value);
-int qcaspi_tx_cmd(struct qcaspi *qca, u16 cmd);
#endif /* _QCA_7K_H */
diff --git a/drivers/net/ethernet/qualcomm/qca_spi.c b/drivers/net/ethernet/qualcomm/qca_spi.c
index 513e6c7..4f431bc 100644
--- a/drivers/net/ethernet/qualcomm/qca_spi.c
+++ b/drivers/net/ethernet/qualcomm/qca_spi.c
@@ -193,6 +193,30 @@ qcaspi_read_legacy(struct qcaspi *qca, u8 *dst, u32 len)
}
static int
+qcaspi_tx_cmd(struct qcaspi *qca, u16 cmd)
+{
+ __be16 tx_data;
+ struct spi_message *msg = &qca->spi_msg1;
+ struct spi_transfer *transfer = &qca->spi_xfer1;
+ int ret;
+
+ tx_data = cpu_to_be16(cmd);
+ transfer->len = sizeof(tx_data);
+ transfer->tx_buf = &tx_data;
+ transfer->rx_buf = NULL;
+
+ ret = spi_sync(qca->spi_dev, msg);
+
+ if (!ret)
+ ret = msg->status;
+
+ if (ret)
+ qcaspi_spi_error(qca);
+
+ return ret;
+}
+
+static int
qcaspi_tx_frame(struct qcaspi *qca, struct sk_buff *skb)
{
u32 count;
--
2.1.4
^ permalink raw reply related
* [PATCH RFC v4 02/10] net: qca_debug: use net_device_ops instead of direct call
From: Stefan Wahren @ 2017-03-27 13:37 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, David S. Miller
Cc: Greg Kroah-Hartman, Jiri Slaby, Marcel Holtmann,
Sebastian Reichel, netdev, devicetree, linux-serial, linux-kernel,
Stefan Wahren
In-Reply-To: <1490621848-24828-1-git-send-email-stefan.wahren@i2se.com>
There is no need to export qcaspi_netdev_open and qcaspi_netdev_close
because they are also accessible via the net_device_ops.
Signed-off-by: Stefan Wahren <stefan.wahren@i2se.com>
---
drivers/net/ethernet/qualcomm/qca_debug.c | 5 +++--
drivers/net/ethernet/qualcomm/qca_spi.h | 3 ---
2 files changed, 3 insertions(+), 5 deletions(-)
diff --git a/drivers/net/ethernet/qualcomm/qca_debug.c b/drivers/net/ethernet/qualcomm/qca_debug.c
index d145df9..92b6be9 100644
--- a/drivers/net/ethernet/qualcomm/qca_debug.c
+++ b/drivers/net/ethernet/qualcomm/qca_debug.c
@@ -275,6 +275,7 @@ qcaspi_get_ringparam(struct net_device *dev, struct ethtool_ringparam *ring)
static int
qcaspi_set_ringparam(struct net_device *dev, struct ethtool_ringparam *ring)
{
+ const struct net_device_ops *ops = dev->netdev_ops;
struct qcaspi *qca = netdev_priv(dev);
if ((ring->rx_pending) ||
@@ -283,13 +284,13 @@ qcaspi_set_ringparam(struct net_device *dev, struct ethtool_ringparam *ring)
return -EINVAL;
if (netif_running(dev))
- qcaspi_netdev_close(dev);
+ ops->ndo_stop(dev);
qca->txr.count = max_t(u32, ring->tx_pending, TX_RING_MIN_LEN);
qca->txr.count = min_t(u16, qca->txr.count, TX_RING_MAX_LEN);
if (netif_running(dev))
- qcaspi_netdev_open(dev);
+ ops->ndo_open(dev);
return 0;
}
diff --git a/drivers/net/ethernet/qualcomm/qca_spi.h b/drivers/net/ethernet/qualcomm/qca_spi.h
index 6e31a0e..064853d 100644
--- a/drivers/net/ethernet/qualcomm/qca_spi.h
+++ b/drivers/net/ethernet/qualcomm/qca_spi.h
@@ -108,7 +108,4 @@ struct qcaspi {
u16 burst_len;
};
-int qcaspi_netdev_open(struct net_device *dev);
-int qcaspi_netdev_close(struct net_device *dev);
-
#endif /* _QCA_SPI_H */
--
2.1.4
^ permalink raw reply related
* [PATCH RFC v4 01/10] net: qualcomm: remove unnecessary includes
From: Stefan Wahren @ 2017-03-27 13:37 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, David S. Miller
Cc: Greg Kroah-Hartman, Jiri Slaby, Marcel Holtmann,
Sebastian Reichel, netdev-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-serial-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, Stefan Wahren
In-Reply-To: <1490621848-24828-1-git-send-email-stefan.wahren-eS4NqCHxEME@public.gmane.org>
Most of the includes in qca_7k.c are unnecessary so we better remove them.
Signed-off-by: Stefan Wahren <stefan.wahren-eS4NqCHxEME@public.gmane.org>
---
drivers/net/ethernet/qualcomm/qca_7k.c | 4 ----
1 file changed, 4 deletions(-)
diff --git a/drivers/net/ethernet/qualcomm/qca_7k.c b/drivers/net/ethernet/qualcomm/qca_7k.c
index f0066fb..557d53c 100644
--- a/drivers/net/ethernet/qualcomm/qca_7k.c
+++ b/drivers/net/ethernet/qualcomm/qca_7k.c
@@ -23,11 +23,7 @@
* kernel-based SPI device.
*/
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
#include <linux/spi/spi.h>
-#include <linux/version.h>
#include "qca_7k.h"
--
2.1.4
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH RFC v4 00/10] net: qualcomm: add QCA7000 UART driver
From: Stefan Wahren @ 2017-03-27 13:37 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, David S. Miller
Cc: Greg Kroah-Hartman, Jiri Slaby, Marcel Holtmann,
Sebastian Reichel, netdev, devicetree, linux-serial, linux-kernel,
Stefan Wahren
The Qualcomm QCA7000 HomePlug GreenPHY supports two interfaces:
UART and SPI. This patch series adds the missing support for UART.
This driver based on the Qualcomm code [1], but contains some changes:
* use random MAC address per default
* use net_device_stats from device
* share frame decoding between SPI and UART driver
* improve error handling
* reimplement tty_wakeup with work queue (based on slcan)
* use new serial device bus instead of ldisc
The patches 1 - 3 are just for clean up and are not related to
the UART support. Patches 4 - 7 prepare the existing QCA7000
code for UART support. Patch 8 contains the new driver. The last
two patches are suggested improvements for serial device bus.
The code itself has been tested on a Freescale i.MX28 board and
a Raspberry Pi Zero.
Changes in v4:
* rebase to current linux-next
* use parameter -M for git format-patch
* change order of local variables where possible
* implement basic serdev support (without hardware flow control)
Changes in v3:
* rebase to current net-next
Changes in v2:
* fix build issue by using netif_trans_update() and dev_trans_start()
[1] - https://github.com/IoE/qca7000
Stefan Wahren (10):
net: qualcomm: remove unnecessary includes
net: qca_debug: use net_device_ops instead of direct call
net: qualcomm: move qcaspi_tx_cmd to qca_spi.c
net: qualcomm: rename qca_framing.c to qca_common.c
net: qualcomm: prepare frame decoding for UART driver
net: qualcomm: make qca_common a separate kernel module
dt-bindings: net: add binding for QCA7000 UART
net: qualcomm: add QCA7000 UART driver
tty: serdev-ttyport: return actual baudrate from ttyport_set_baudrate
tty: serdev: add functions to retrieve common UART settings
.../devicetree/bindings/net/qca-qca7000-uart.txt | 31 ++
drivers/net/ethernet/qualcomm/Kconfig | 18 +-
drivers/net/ethernet/qualcomm/Makefile | 7 +-
drivers/net/ethernet/qualcomm/qca_7k.c | 28 --
drivers/net/ethernet/qualcomm/qca_7k.h | 1 -
.../qualcomm/{qca_framing.c => qca_common.c} | 24 +-
.../qualcomm/{qca_framing.h => qca_common.h} | 14 +-
drivers/net/ethernet/qualcomm/qca_debug.c | 5 +-
drivers/net/ethernet/qualcomm/qca_spi.c | 28 +-
drivers/net/ethernet/qualcomm/qca_spi.h | 5 +-
drivers/net/ethernet/qualcomm/qca_uart.c | 419 +++++++++++++++++++++
drivers/tty/serdev/core.c | 33 ++
drivers/tty/serdev/serdev-ttyport.c | 49 ++-
include/linux/serdev.h | 22 ++
14 files changed, 634 insertions(+), 50 deletions(-)
create mode 100644 Documentation/devicetree/bindings/net/qca-qca7000-uart.txt
rename drivers/net/ethernet/qualcomm/{qca_framing.c => qca_common.c} (86%)
rename drivers/net/ethernet/qualcomm/{qca_framing.h => qca_common.h} (90%)
create mode 100644 drivers/net/ethernet/qualcomm/qca_uart.c
--
2.1.4
^ permalink raw reply
* Re: [PATCH v1.1 3/3] device property: fwnode_property_read_string_array() returns nr of strings
From: Mika Westerberg @ 2017-03-27 13:36 UTC (permalink / raw)
To: Sakari Ailus
Cc: linux-acpi, devicetree, sudeep.holla, lorenzo.pieralisi, rafael,
mark.rutland, broonie, robh, ahs3
In-Reply-To: <1489584688-15624-1-git-send-email-sakari.ailus@linux.intel.com>
On Wed, Mar 15, 2017 at 03:31:28PM +0200, Sakari Ailus wrote:
> Functionally fwnode_property_read_string_array() should match
> of_property_read_string_array() and work as a drop-in substitute for the
> latter. of_property_read_string_array() returns the number of strings read
> if the target string pointer array is non-NULL. Make
> fwnode_property_read_string_array() do the same.
>
> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> ---
> This patch replaces v1 3/3 patch in this set.
>
> Instead of changing the return value of fwnode / device property API
> string array access on OF, change the behaviour on pset and ACPI instead.
> This makes them to return the number of strings read on success.
>
> I can merge this with patch 2/3 which is changing the same part of the
> file, however I'm sending this separately at least for now as I think it's
> easier to review this way, rather than making a bugfix and a change of the
> behaviour in the same patch.
>
> Regards,
> Sakari
>
> drivers/base/property.c | 64 +++++++++++++++++++++++++++++++++++--------------
> 1 file changed, 46 insertions(+), 18 deletions(-)
>
> diff --git a/drivers/base/property.c b/drivers/base/property.c
> index 8c98390..82187ac 100644
> --- a/drivers/base/property.c
> +++ b/drivers/base/property.c
> @@ -340,8 +340,8 @@ EXPORT_SYMBOL_GPL(device_property_read_u64_array);
> * Function reads an array of string properties with @propname from the device
> * firmware description and stores them to @val if found.
> *
> - * Return: number of values if @val was %NULL,
> - * %0 if the property was found (success),
> + * Return: number of values read on success if @val is non-NULL,
> + * number of values available on success if @val is NULL,
> * %-EINVAL if given arguments are not valid,
> * %-ENODATA if the property does not have a value,
> * %-EPROTO or %-EILSEQ if the property is not an array of strings,
> @@ -549,29 +549,57 @@ static int __fwnode_property_read_string_array(struct fwnode_handle *fwnode,
> of_property_read_string_array(to_of_node(fwnode),
> propname, val, nval) :
> of_property_count_strings(to_of_node(fwnode), propname);
> - else if (is_acpi_node(fwnode))
> - return acpi_node_prop_read(fwnode, propname, DEV_PROP_STRING,
> - val, nval);
> - else if (is_pset_node(fwnode)) {
> + else if (is_acpi_node(fwnode)) {
> + int array_len =
> + acpi_node_prop_read(fwnode, propname, DEV_PROP_STRING,
Why not change acpi_node_prop_read() instead? This way you don't need to
add the extra code dealing with the return value here.
Ditto for the pset counterpart.
^ permalink raw reply
* [PATCH 2/2] nvmem: imx-ocotp: add write support
From: Richard Leitner @ 2017-03-27 13:31 UTC (permalink / raw)
To: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A,
maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8
Cc: robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
devicetree-u79uwXL29TY76Z2rM5mHXA, dev-M/VWbR8SM2SsTnJN9+BGXg,
Richard Leitner
In-Reply-To: <1490621491-28247-1-git-send-email-richard.leitner-WcANXNA0UjBBDgjK7y7TUQ@public.gmane.org>
Implement write routine for OCOTP controller found in i.MX6 SoC's.
Furthermore add locking to the read function to prevent race conditions.
The write routine code is based on the fsl_otp driver from Freescale.
Signed-off-by: Richard Leitner <richard.leitner-WcANXNA0UjBBDgjK7y7TUQ@public.gmane.org>
---
.../devicetree/bindings/nvmem/imx-ocotp.txt | 4 +
drivers/nvmem/imx-ocotp.c | 210 ++++++++++++++++++++-
2 files changed, 212 insertions(+), 2 deletions(-)
diff --git a/Documentation/devicetree/bindings/nvmem/imx-ocotp.txt b/Documentation/devicetree/bindings/nvmem/imx-ocotp.txt
index 966a72e..4b04356 100644
--- a/Documentation/devicetree/bindings/nvmem/imx-ocotp.txt
+++ b/Documentation/devicetree/bindings/nvmem/imx-ocotp.txt
@@ -13,10 +13,14 @@ Required properties:
- reg: Should contain the register base and length.
- clocks: Should contain a phandle pointing to the gated peripheral clock.
+Optional properties:
+- read-only: disable write access
+
Example:
ocotp: ocotp@021bc000 {
compatible = "fsl,imx6q-ocotp", "syscon";
reg = <0x021bc000 0x4000>;
clocks = <&clks IMX6QDL_CLK_IIM>;
+ read-only;
};
diff --git a/drivers/nvmem/imx-ocotp.c b/drivers/nvmem/imx-ocotp.c
index e2b62b3..eb55b41 100644
--- a/drivers/nvmem/imx-ocotp.c
+++ b/drivers/nvmem/imx-ocotp.c
@@ -7,6 +7,9 @@
* Copyright (c) 2010 Baruch Siach <baruch-NswTu9S1W3P6gbPvEgmw2w@public.gmane.org>,
* Orex Computed Radiography
*
+ * Write support based on the fsl_otp driver,
+ * Copyright (C) 2010-2013 Freescale Semiconductor, Inc
+ *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
@@ -24,6 +27,7 @@
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
+#include <linux/delay.h>
#define IMX_OCOTP_OFFSET_B0W0 0x400 /* Offset from base address of the
* OTP Bank0 Word0
@@ -31,20 +35,69 @@
#define IMX_OCOTP_OFFSET_PER_WORD 0x10 /* Offset between the start addr
* of two consecutive OTP words.
*/
+
#define IMX_OCOTP_ADDR_CTRL 0x0000
+#define IMX_OCOTP_ADDR_CTRL_SET 0x0004
#define IMX_OCOTP_ADDR_CTRL_CLR 0x0008
+#define IMX_OCOTP_ADDR_TIMING 0x0010
+#define IMX_OCOTP_ADDR_DATA 0x0020
+#define IMX_OCOTP_BM_CTRL_ADDR 0x0000007F
+#define IMX_OCOTP_BM_CTRL_BUSY 0x00000100
#define IMX_OCOTP_BM_CTRL_ERROR 0x00000200
+#define IMX_OCOTP_BM_CTRL_REL_SHADOWS 0x00000400
+#define DEF_RELAX 20 /* > 16.5ns */
+#define IMX_OCOTP_WR_UNLOCK 0x3E770000
#define IMX_OCOTP_READ_LOCKED_VAL 0xBADABADA
+static DEFINE_MUTEX(ocotp_mutex);
+
struct ocotp_priv {
struct device *dev;
struct clk *clk;
void __iomem *base;
unsigned int nregs;
+ struct nvmem_config *config;
};
+static int imx_ocotp_wait_for_busy(void __iomem *base, u32 flags)
+{
+ int count;
+ u32 c, mask;
+
+ mask = IMX_OCOTP_BM_CTRL_BUSY | IMX_OCOTP_BM_CTRL_ERROR | flags;
+
+ for (count = 10000; count >= 0; count--) {
+ c = readl(base + IMX_OCOTP_ADDR_CTRL);
+ if (!(c & mask))
+ break;
+ cpu_relax();
+ }
+
+ if (count < 0) {
+ /* HW_OCOTP_CTRL[ERROR] will be set under the following
+ * conditions:
+ * - A write is performed to a shadow register during a shadow
+ * reload (essentially, while HW_OCOTP_CTRL[RELOAD_SHADOWS] is
+ * set. In addition, the contents of the shadow register shall
+ * not be updated.
+ * - A write is performed to a shadow register which has been
+ * locked.
+ * - A read is performed to from a shadow register which has
+ * been read locked.
+ * - A program is performed to a fuse word which has been locked
+ * - A read is performed to from a fuse word which has been read
+ * locked.
+ */
+ if (c & IMX_OCOTP_BM_CTRL_ERROR)
+ return -EPERM;
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
static void imx_ocotp_clr_err_if_set(void __iomem *base)
{
u32 c;
@@ -71,12 +124,21 @@ static int imx_ocotp_read(void *context, unsigned int offset,
if (count > (priv->nregs - index))
count = priv->nregs - index;
+ mutex_lock(&ocotp_mutex);
+
ret = clk_prepare_enable(priv->clk);
if (ret < 0) {
+ mutex_unlock(&ocotp_mutex);
dev_err(priv->dev, "failed to prepare/enable ocotp clk\n");
return ret;
}
+ ret = imx_ocotp_wait_for_busy(priv->base, 0);
+ if (ret < 0) {
+ dev_err(priv->dev, "timeout during read setup\n");
+ goto read_end;
+ }
+
for (i = index; i < (index + count); i++) {
*buf++ = readl(priv->base + IMX_OCOTP_OFFSET_B0W0 +
i * IMX_OCOTP_OFFSET_PER_WORD);
@@ -90,18 +152,160 @@ static int imx_ocotp_read(void *context, unsigned int offset,
if (*(buf - 1) == IMX_OCOTP_READ_LOCKED_VAL)
imx_ocotp_clr_err_if_set(priv->base);
}
+ ret = 0;
+read_end:
clk_disable_unprepare(priv->clk);
- return 0;
+ mutex_unlock(&ocotp_mutex);
+ return ret;
+}
+
+static int imx_ocotp_write(void *context, unsigned int offset, void *val,
+ size_t bytes)
+{
+ struct ocotp_priv *priv = context;
+ u32 *buf = val;
+ int ret;
+
+ unsigned long clk_rate = 0;
+ unsigned long strobe_read, relax, strobe_prog;
+ u32 timing = 0;
+ u32 ctrl;
+ u8 waddr;
+
+ /* allow only writing one complete OTP word at a time */
+ if ((bytes != priv->config->word_size) ||
+ (offset % priv->config->word_size))
+ return -EINVAL;
+
+ mutex_lock(&ocotp_mutex);
+
+ ret = clk_prepare_enable(priv->clk);
+ if (ret < 0) {
+ mutex_unlock(&ocotp_mutex);
+ dev_err(priv->dev, "failed to prepare/enable ocotp clk\n");
+ return ret;
+ }
+
+ /* 47.3.1.3.1
+ * Program HW_OCOTP_TIMING[STROBE_PROG] and HW_OCOTP_TIMING[RELAX]
+ * fields with timing values to match the current frequency of the
+ * ipg_clk. OTP writes will work at maximum bus frequencies as long
+ * as the HW_OCOTP_TIMING parameters are set correctly.
+ */
+ clk_rate = clk_get_rate(priv->clk);
+
+ relax = clk_rate / (1000000000 / DEF_RELAX) - 1;
+ strobe_prog = clk_rate / (1000000000 / 10000) + 2 * (DEF_RELAX + 1) - 1;
+ strobe_read = clk_rate / (1000000000 / 40) + 2 * (DEF_RELAX + 1) - 1;
+
+ timing = strobe_prog & 0x00000FFF;
+ timing |= (relax << 12) & 0x0000F000;
+ timing |= (strobe_read << 16) & 0x003F0000;
+
+ writel(timing, priv->base + IMX_OCOTP_ADDR_TIMING);
+
+ /* 47.3.1.3.2
+ * Check that HW_OCOTP_CTRL[BUSY] and HW_OCOTP_CTRL[ERROR] are clear.
+ * Overlapped accesses are not supported by the controller. Any pending
+ * write or reload must be completed before a write access can be
+ * requested.
+ */
+ ret = imx_ocotp_wait_for_busy(priv->base, 0);
+ if (ret < 0) {
+ dev_err(priv->dev, "timeout during timing setup\n");
+ goto write_end;
+ }
+
+ /* 47.3.1.3.3
+ * Write the requested address to HW_OCOTP_CTRL[ADDR] and program the
+ * unlock code into HW_OCOTP_CTRL[WR_UNLOCK]. This must be programmed
+ * for each write access. The lock code is documented in the register
+ * description. Both the unlock code and address can be written in the
+ * same operation.
+ */
+ /* OTP write/read address specifies one of 128 word address locations */
+ waddr = offset / 4;
+
+ ctrl = readl(priv->base + IMX_OCOTP_ADDR_CTRL);
+ ctrl &= ~IMX_OCOTP_BM_CTRL_ADDR;
+ ctrl |= waddr & IMX_OCOTP_BM_CTRL_ADDR;
+ ctrl |= IMX_OCOTP_WR_UNLOCK;
+
+ writel(ctrl, priv->base + IMX_OCOTP_ADDR_CTRL);
+
+ /* 47.3.1.3.4
+ * Write the data to the HW_OCOTP_DATA register. This will automatically
+ * set HW_OCOTP_CTRL[BUSY] and clear HW_OCOTP_CTRL[WR_UNLOCK]. To
+ * protect programming same OTP bit twice, before program OCOTP will
+ * automatically read fuse value in OTP and use read value to mask
+ * program data. The controller will use masked program data to program
+ * a 32-bit word in the OTP per the address in HW_OCOTP_CTRL[ADDR]. Bit
+ * fields with 1's will result in that OTP bit being programmed. Bit
+ * fields with 0's will be ignored. At the same time that the write is
+ * accepted, the controller makes an internal copy of
+ * HW_OCOTP_CTRL[ADDR] which cannot be updated until the next write
+ * sequence is initiated. This copy guarantees that erroneous writes to
+ * HW_OCOTP_CTRL[ADDR] will not affect an active write operation. It
+ * should also be noted that during the programming HW_OCOTP_DATA will
+ * shift right (with zero fill). This shifting is required to program
+ * the OTP serially. During the write operation, HW_OCOTP_DATA cannot be
+ * modified.
+ */
+ writel(*buf, priv->base + IMX_OCOTP_ADDR_DATA);
+
+ /* 47.4.1.4.5
+ * Once complete, the controller will clear BUSY. A write request to a
+ * protected or locked region will result in no OTP access and no
+ * setting of HW_OCOTP_CTRL[BUSY]. In addition HW_OCOTP_CTRL[ERROR] will
+ * be set. It must be cleared by software before any new write access
+ * can be issued.
+ */
+ ret = imx_ocotp_wait_for_busy(priv->base, 0);
+ if (ret < 0) {
+ if (ret == -EPERM) {
+ dev_err(priv->dev, "failed write to locked region");
+ imx_ocotp_clr_err_if_set(priv->base);
+ } else {
+ dev_err(priv->dev, "timeout during data write\n");
+ }
+ goto write_end;
+ }
+
+ /* 47.3.1.4
+ * Write Postamble: Due to internal electrical characteristics of the
+ * OTP during writes, all OTP operations following a write must be
+ * separated by 2 us after the clearing of HW_OCOTP_CTRL_BUSY following
+ * the write.
+ */
+ udelay(2);
+
+ /* reload all shadow registers */
+ writel(IMX_OCOTP_BM_CTRL_REL_SHADOWS,
+ priv->base + IMX_OCOTP_ADDR_CTRL_SET);
+ ret = imx_ocotp_wait_for_busy(priv->base,
+ IMX_OCOTP_BM_CTRL_REL_SHADOWS);
+ if (ret < 0) {
+ dev_err(priv->dev, "timeout during shadow register reload\n");
+ goto write_end;
+ }
+
+write_end:
+ clk_disable_unprepare(priv->clk);
+ mutex_unlock(&ocotp_mutex);
+ if (ret < 0)
+ return ret;
+ return bytes;
}
static struct nvmem_config imx_ocotp_nvmem_config = {
.name = "imx-ocotp",
- .read_only = true,
+ .read_only = false,
.word_size = 4,
.stride = 4,
.owner = THIS_MODULE,
.reg_read = imx_ocotp_read,
+ .reg_write = imx_ocotp_write,
};
static const struct of_device_id imx_ocotp_dt_ids[] = {
@@ -139,7 +343,9 @@ static int imx_ocotp_probe(struct platform_device *pdev)
imx_ocotp_nvmem_config.size = 4 * priv->nregs;
imx_ocotp_nvmem_config.dev = dev;
imx_ocotp_nvmem_config.priv = priv;
+ priv->config = &imx_ocotp_nvmem_config;
nvmem = nvmem_register(&imx_ocotp_nvmem_config);
+
if (IS_ERR(nvmem))
return PTR_ERR(nvmem);
--
2.1.4
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH 1/2] nvmem: imx-ocotp: clear error bit after reading locked values
From: Richard Leitner @ 2017-03-27 13:31 UTC (permalink / raw)
To: linux-kernel, srinivas.kandagatla, maxime.ripard
Cc: robh+dt, mark.rutland, devicetree, dev, Richard Leitner
When reading a "read locked" value from the OCOTP controller on i.MX6
SoC's an error bit is set. This bit has to be cleared by software before
any new write, read or reload access can be issued.
Therefore clear it after we detect such an "locked read".
Signed-off-by: Richard Leitner <richard.leitner@skidata.com>
---
drivers/nvmem/imx-ocotp.c | 41 ++++++++++++++++++++++++++++++++++++++---
1 file changed, 38 insertions(+), 3 deletions(-)
diff --git a/drivers/nvmem/imx-ocotp.c b/drivers/nvmem/imx-ocotp.c
index b8ca1e6..e2b62b3 100644
--- a/drivers/nvmem/imx-ocotp.c
+++ b/drivers/nvmem/imx-ocotp.c
@@ -25,6 +25,19 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
+#define IMX_OCOTP_OFFSET_B0W0 0x400 /* Offset from base address of the
+ * OTP Bank0 Word0
+ */
+#define IMX_OCOTP_OFFSET_PER_WORD 0x10 /* Offset between the start addr
+ * of two consecutive OTP words.
+ */
+#define IMX_OCOTP_ADDR_CTRL 0x0000
+#define IMX_OCOTP_ADDR_CTRL_CLR 0x0008
+
+#define IMX_OCOTP_BM_CTRL_ERROR 0x00000200
+
+#define IMX_OCOTP_READ_LOCKED_VAL 0xBADABADA
+
struct ocotp_priv {
struct device *dev;
struct clk *clk;
@@ -32,6 +45,17 @@ struct ocotp_priv {
unsigned int nregs;
};
+static void imx_ocotp_clr_err_if_set(void __iomem *base)
+{
+ u32 c;
+
+ c = readl(base + IMX_OCOTP_ADDR_CTRL);
+ if (!(c & IMX_OCOTP_BM_CTRL_ERROR))
+ return;
+
+ writel(IMX_OCOTP_BM_CTRL_ERROR, base + IMX_OCOTP_ADDR_CTRL_CLR);
+}
+
static int imx_ocotp_read(void *context, unsigned int offset,
void *val, size_t bytes)
{
@@ -52,11 +76,22 @@ static int imx_ocotp_read(void *context, unsigned int offset,
dev_err(priv->dev, "failed to prepare/enable ocotp clk\n");
return ret;
}
- for (i = index; i < (index + count); i++)
- *buf++ = readl(priv->base + 0x400 + i * 0x10);
- clk_disable_unprepare(priv->clk);
+ for (i = index; i < (index + count); i++) {
+ *buf++ = readl(priv->base + IMX_OCOTP_OFFSET_B0W0 +
+ i * IMX_OCOTP_OFFSET_PER_WORD);
+
+ /* 47.3.1.2
+ * For "read locked" registers 0xBADABADA will be returned and
+ * HW_OCOTP_CTRL[ERROR] will be set. It must be cleared by
+ * software before any new write, read or reload access can be
+ * issued
+ */
+ if (*(buf - 1) == IMX_OCOTP_READ_LOCKED_VAL)
+ imx_ocotp_clr_err_if_set(priv->base);
+ }
+ clk_disable_unprepare(priv->clk);
return 0;
}
--
2.1.4
^ permalink raw reply related
* Re: [PATCH v4 1/6] dt-bindings: iio: rockchip-saradc: add support for rk3328
From: Heiko Stuebner @ 2017-03-27 13:25 UTC (permalink / raw)
To: cl-TNX95d0MmH7DzftRWevZcw, jic23-DgEjT+Ai2ygdnm+yROfE0A
Cc: mark.rutland-5wv7dgnIgG8, wsa-z923LK4zBo2bacvFa/9K2g,
linux-iio-u79uwXL29TY76Z2rM5mHXA, catalin.marinas-5wv7dgnIgG8,
shawn.lin-TNX95d0MmH7DzftRWevZcw, will.deacon-5wv7dgnIgG8,
kever.yang-TNX95d0MmH7DzftRWevZcw,
dianders-F7+t8E8rja9g9hUCZPvPmw,
yamada.masahiro-uWyLwvC0a2jby3iVrkZq2A,
tony.xie-TNX95d0MmH7DzftRWevZcw, linux-i2c-u79uwXL29TY76Z2rM5mHXA,
pmeerw-jW+XmwGofnusTnJN9+BGXg, lars-Qo5EllUWu/uELgA04lAiVw,
zhengxing-TNX95d0MmH7DzftRWevZcw, khilman-rdvid1DuHRBWk0Htik3J/w,
linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
jay.xu-TNX95d0MmH7DzftRWevZcw, wxt-TNX95d0MmH7DzftRWevZcw,
huangtao-TNX95d0MmH7DzftRWevZcw,
devicetree-u79uwXL29TY76Z2rM5mHXA,
zhangqing-TNX95d0MmH7DzftRWevZcw,
paweljarosz3691-Re5JQEeQqe8AvxtiuMwx3w, arnd-r2nGTMty4D4,
yhx-TNX95d0MmH7DzftRWevZcw, knaack.h-Mmb7MZpHnFY,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
matthias.bgg-Re5JQEeQqe8AvxtiuMwx3w,
rocky.hao-TNX95d0MmH7DzftRWevZcw,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
david.wu-TNX95d0MmH7DzftRWevZcw, fabio.estevam-3arQi8VN3Tc,
andy.yan-TNX95d0MmH7DzftRWevZcw,
akpm-de/tnXTf+JLsfHDXvbKv3WD2FQJk+8+b,
shawnguo-DgEjT+Ai2ygdnm+yROfE0A, afaerber-l3A5Bk7waGM
In-Reply-To: <1490607650-18650-2-git-send-email-cl-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
Hi Jonathan,
Am Montag, 27. März 2017, 17:40:46 CEST schrieb cl-TNX95d0MmH7DzftRWevZcw@public.gmane.org:
> From: Liang Chen <cl-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
>
> The rk3328 saradc is the same as rk3399.
>
> Signed-off-by: Liang Chen <cl-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
> Reviewed-by: Heiko Stuebner <heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
> Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
just for planning purposes, do you plan on picking this up or do you
want me to queue it with the rest of the changes (Ack needed).
Heiko
^ permalink raw reply
* [PATCH v3 9/9] arm,arm64,drivers: add a prefix to drivers arch_topology interfaces
From: Juri Lelli @ 2017-03-27 13:18 UTC (permalink / raw)
To: linux-kernel
Cc: linux-pm, linux-arm-kernel, devicetree, peterz, vincent.guittot,
robh+dt, mark.rutland, linux, sudeep.holla, lorenzo.pieralisi,
catalin.marinas, will.deacon, morten.rasmussen, dietmar.eggemann,
juri.lelli, broonie, gregkh
In-Reply-To: <20170327131825.32134-1-juri.lelli@arm.com>
Now that some functions that deal with arch topology information live
under drivers, there is a clash of naming that might create confusion.
Tidy things up by creating a drivers namespace for interfaces used by
arch code; achieve this by prepending a 'atd_' (arch topology driver)
prefix to driver interfaces.
Signed-off-by: Juri Lelli <juri.lelli@arm.com>
---
arch/arm/kernel/topology.c | 8 ++++----
arch/arm64/kernel/topology.c | 4 ++--
drivers/base/arch_topology.c | 20 ++++++++++----------
include/linux/arch_topology.h | 8 ++++----
4 files changed, 20 insertions(+), 20 deletions(-)
diff --git a/arch/arm/kernel/topology.c b/arch/arm/kernel/topology.c
index 557be4f1d2d7..e53391026c1b 100644
--- a/arch/arm/kernel/topology.c
+++ b/arch/arm/kernel/topology.c
@@ -111,7 +111,7 @@ static void __init parse_dt_topology(void)
continue;
}
- if (parse_cpu_capacity(cn, cpu)) {
+ if (atd_parse_cpu_capacity(cn, cpu)) {
of_node_put(cn);
continue;
}
@@ -160,7 +160,7 @@ static void __init parse_dt_topology(void)
>> (SCHED_CAPACITY_SHIFT-1)) + 1;
if (cap_from_dt)
- normalize_cpu_capacity();
+ atd_normalize_cpu_capacity();
}
/*
@@ -173,10 +173,10 @@ static void update_cpu_capacity(unsigned int cpu)
if (!cpu_capacity(cpu) || cap_from_dt)
return;
- set_capacity_scale(cpu, cpu_capacity(cpu) / middle_capacity);
+ atd_set_capacity_scale(cpu, cpu_capacity(cpu) / middle_capacity);
pr_info("CPU%u: update cpu_capacity %lu\n",
- cpu, arch_scale_cpu_capacity(NULL, cpu));
+ cpu, atd_scale_cpu_capacity(NULL, cpu));
}
#else
diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c
index 255230c3e835..5f24faa09c05 100644
--- a/arch/arm64/kernel/topology.c
+++ b/arch/arm64/kernel/topology.c
@@ -39,7 +39,7 @@ static int __init get_cpu_for_node(struct device_node *node)
for_each_possible_cpu(cpu) {
if (of_get_cpu_node(cpu, NULL) == cpu_node) {
- parse_cpu_capacity(cpu_node, cpu);
+ atd_parse_cpu_capacity(cpu_node, cpu);
of_node_put(cpu_node);
return cpu;
}
@@ -191,7 +191,7 @@ static int __init parse_dt_topology(void)
if (ret != 0)
goto out_map;
- normalize_cpu_capacity();
+ atd_normalize_cpu_capacity();
/*
* Check that all cores are in the topology; the SMP code will
diff --git a/drivers/base/arch_topology.c b/drivers/base/arch_topology.c
index 0fc77fa900a9..87dca3320536 100644
--- a/drivers/base/arch_topology.c
+++ b/drivers/base/arch_topology.c
@@ -25,12 +25,12 @@
static DEFINE_MUTEX(cpu_scale_mutex);
static DEFINE_PER_CPU(unsigned long, cpu_scale) = SCHED_CAPACITY_SCALE;
-unsigned long arch_scale_cpu_capacity(struct sched_domain *sd, int cpu)
+unsigned long atd_scale_cpu_capacity(struct sched_domain *sd, int cpu)
{
return per_cpu(cpu_scale, cpu);
}
-void set_capacity_scale(unsigned int cpu, unsigned long capacity)
+void atd_set_capacity_scale(unsigned int cpu, unsigned long capacity)
{
per_cpu(cpu_scale, cpu) = capacity;
}
@@ -42,7 +42,7 @@ static ssize_t cpu_capacity_show(struct device *dev,
struct cpu *cpu = container_of(dev, struct cpu, dev);
return sprintf(buf, "%lu\n",
- arch_scale_cpu_capacity(NULL, cpu->dev.id));
+ atd_scale_cpu_capacity(NULL, cpu->dev.id));
}
static ssize_t cpu_capacity_store(struct device *dev,
@@ -67,7 +67,7 @@ static ssize_t cpu_capacity_store(struct device *dev,
mutex_lock(&cpu_scale_mutex);
for_each_cpu(i, &cpu_topology[this_cpu].core_sibling)
- set_capacity_scale(i, new_capacity);
+ atd_set_capacity_scale(i, new_capacity);
mutex_unlock(&cpu_scale_mutex);
return count;
@@ -96,7 +96,7 @@ static u32 capacity_scale;
static u32 *raw_capacity;
static bool cap_parsing_failed;
-void normalize_cpu_capacity(void)
+void atd_normalize_cpu_capacity(void)
{
u64 capacity;
int cpu;
@@ -111,14 +111,14 @@ void normalize_cpu_capacity(void)
cpu, raw_capacity[cpu]);
capacity = (raw_capacity[cpu] << SCHED_CAPACITY_SHIFT)
/ capacity_scale;
- set_capacity_scale(cpu, capacity);
+ atd_set_capacity_scale(cpu, capacity);
pr_debug("cpu_capacity: CPU%d cpu_capacity=%lu\n",
- cpu, arch_scale_cpu_capacity(NULL, cpu));
+ cpu, atd_scale_cpu_capacity(NULL, cpu));
}
mutex_unlock(&cpu_scale_mutex);
}
-int __init parse_cpu_capacity(struct device_node *cpu_node, int cpu)
+int __init atd_parse_cpu_capacity(struct device_node *cpu_node, int cpu)
{
int ret = 1;
u32 cpu_capacity;
@@ -183,12 +183,12 @@ init_cpu_capacity_callback(struct notifier_block *nb,
cpus_to_visit,
policy->related_cpus);
for_each_cpu(cpu, policy->related_cpus) {
- raw_capacity[cpu] = arch_scale_cpu_capacity(NULL, cpu) *
+ raw_capacity[cpu] = atd_scale_cpu_capacity(NULL, cpu) *
policy->cpuinfo.max_freq / 1000UL;
capacity_scale = max(raw_capacity[cpu], capacity_scale);
}
if (cpumask_empty(cpus_to_visit)) {
- normalize_cpu_capacity();
+ atd_normalize_cpu_capacity();
kfree(raw_capacity);
pr_debug("cpu_capacity: parsing done\n");
cap_parsing_done = true;
diff --git a/include/linux/arch_topology.h b/include/linux/arch_topology.h
index 4edae9fe8cdd..e25458d7ee9a 100644
--- a/include/linux/arch_topology.h
+++ b/include/linux/arch_topology.h
@@ -4,14 +4,14 @@
#ifndef _LINUX_ARCH_TOPOLOGY_H_
#define _LINUX_ARCH_TOPOLOGY_H_
-void normalize_cpu_capacity(void);
+void atd_normalize_cpu_capacity(void);
struct device_node;
-int parse_cpu_capacity(struct device_node *cpu_node, int cpu);
+int atd_parse_cpu_capacity(struct device_node *cpu_node, int cpu);
struct sched_domain;
-unsigned long arch_scale_cpu_capacity(struct sched_domain *sd, int cpu);
+unsigned long atd_scale_cpu_capacity(struct sched_domain *sd, int cpu);
-void set_capacity_scale(unsigned int cpu, unsigned long capacity);
+void atd_set_capacity_scale(unsigned int cpu, unsigned long capacity);
#endif /* _LINUX_ARCH_TOPOLOGY_H_ */
--
2.10.0
^ permalink raw reply related
* [PATCH v3 8/9] arm,arm64,drivers: move externs in a new header file
From: Juri Lelli @ 2017-03-27 13:18 UTC (permalink / raw)
To: linux-kernel
Cc: mark.rutland, devicetree, lorenzo.pieralisi, vincent.guittot,
juri.lelli, linux-pm, peterz, catalin.marinas, broonie,
will.deacon, gregkh, dietmar.eggemann, robh+dt, sudeep.holla,
linux, morten.rasmussen, linux-arm-kernel
In-Reply-To: <20170327131825.32134-1-juri.lelli@arm.com>
Create a new header file (include/linux/arch_topology.h) and put there
declarations of interfaces used by arm, arm64 and drivers code.
Signed-off-by: Juri Lelli <juri.lelli@arm.com>
---
arch/arm/kernel/topology.c | 7 +------
arch/arm64/kernel/topology.c | 4 +---
drivers/base/arch_topology.c | 1 +
include/linux/arch_topology.h | 17 +++++++++++++++++
4 files changed, 20 insertions(+), 9 deletions(-)
create mode 100644 include/linux/arch_topology.h
diff --git a/arch/arm/kernel/topology.c b/arch/arm/kernel/topology.c
index 1e35a3265ddf..557be4f1d2d7 100644
--- a/arch/arm/kernel/topology.c
+++ b/arch/arm/kernel/topology.c
@@ -11,6 +11,7 @@
* for more details.
*/
+#include <linux/arch_topology.h>
#include <linux/cpu.h>
#include <linux/cpufreq.h>
#include <linux/cpumask.h>
@@ -45,10 +46,6 @@
* updated during this sequence.
*/
-extern unsigned long
-arch_scale_cpu_capacity(struct sched_domain *sd, int cpu);
-extern void set_capacity_scale(unsigned int cpu, unsigned long capacity);
-
#ifdef CONFIG_OF
struct cpu_efficiency {
const char *compatible;
@@ -76,8 +73,6 @@ static unsigned long *__cpu_capacity;
static unsigned long middle_capacity = 1;
static bool cap_from_dt = true;
-extern void normalize_cpu_capacity(void);
-extern int __init parse_cpu_capacity(struct device_node *cpu_node, int cpu);
/*
* Iterate all CPUs' descriptor in DT and compute the efficiency
diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c
index 7e1f6f75185b..255230c3e835 100644
--- a/arch/arm64/kernel/topology.c
+++ b/arch/arm64/kernel/topology.c
@@ -11,6 +11,7 @@
* for more details.
*/
+#include <linux/arch_topology.h>
#include <linux/cpu.h>
#include <linux/cpumask.h>
#include <linux/init.h>
@@ -27,9 +28,6 @@
#include <asm/cputype.h>
#include <asm/topology.h>
-extern void normalize_cpu_capacity(void);
-extern int __init parse_cpu_capacity(struct device_node *cpu_node, int cpu);
-
static int __init get_cpu_for_node(struct device_node *node)
{
struct device_node *cpu_node;
diff --git a/drivers/base/arch_topology.c b/drivers/base/arch_topology.c
index 6543a032b332..0fc77fa900a9 100644
--- a/drivers/base/arch_topology.c
+++ b/drivers/base/arch_topology.c
@@ -13,6 +13,7 @@
*/
#include <linux/acpi.h>
+#include <linux/arch_topology.h>
#include <linux/cpu.h>
#include <linux/cpufreq.h>
#include <linux/device.h>
diff --git a/include/linux/arch_topology.h b/include/linux/arch_topology.h
new file mode 100644
index 000000000000..4edae9fe8cdd
--- /dev/null
+++ b/include/linux/arch_topology.h
@@ -0,0 +1,17 @@
+/*
+ * include/linux/arch_topology.h - arch specific cpu topology information
+ */
+#ifndef _LINUX_ARCH_TOPOLOGY_H_
+#define _LINUX_ARCH_TOPOLOGY_H_
+
+void normalize_cpu_capacity(void);
+
+struct device_node;
+int parse_cpu_capacity(struct device_node *cpu_node, int cpu);
+
+struct sched_domain;
+unsigned long arch_scale_cpu_capacity(struct sched_domain *sd, int cpu);
+
+void set_capacity_scale(unsigned int cpu, unsigned long capacity);
+
+#endif /* _LINUX_ARCH_TOPOLOGY_H_ */
--
2.10.0
^ permalink raw reply related
* [PATCH v3 7/9] arm,arm64,drivers: reduce scope of cap_parsing_failed
From: Juri Lelli @ 2017-03-27 13:18 UTC (permalink / raw)
To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
Cc: linux-pm-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA, peterz-wEGCiKHe2LqWVfeAwA7xHQ,
vincent.guittot-QSEj5FYQhm4dnm+yROfE0A,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
linux-lFZ/pmaqli7XmaaqVzeoHQ, sudeep.holla-5wv7dgnIgG8,
lorenzo.pieralisi-5wv7dgnIgG8, catalin.marinas-5wv7dgnIgG8,
will.deacon-5wv7dgnIgG8, morten.rasmussen-5wv7dgnIgG8,
dietmar.eggemann-5wv7dgnIgG8, juri.lelli-5wv7dgnIgG8,
broonie-DgEjT+Ai2ygdnm+yROfE0A,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r
In-Reply-To: <20170327131825.32134-1-juri.lelli-5wv7dgnIgG8@public.gmane.org>
Reduce the scope of cap_parsing_failed (making it static in
drivers/base/arch_topology.c) by slightly changing {arm,arm64} DT
parsing code.
For arm checking for !cap_parsing_failed before calling normalize_
cpu_capacity() is superfluous, as returning an error from parse_
cpu_capacity() (above) means cap_from _dt is set to false.
For arm64 we can simply check if raw_capacity points to something,
which is not if capacity parsing has failed.
Suggested-by: Morten Rasmussen <morten.rasmussen-5wv7dgnIgG8@public.gmane.org>
Signed-off-by: Juri Lelli <juri.lelli-5wv7dgnIgG8@public.gmane.org>
---
arch/arm/kernel/topology.c | 3 +--
arch/arm64/kernel/topology.c | 5 +----
drivers/base/arch_topology.c | 4 ++--
3 files changed, 4 insertions(+), 8 deletions(-)
diff --git a/arch/arm/kernel/topology.c b/arch/arm/kernel/topology.c
index 49ef025ffaa0..1e35a3265ddf 100644
--- a/arch/arm/kernel/topology.c
+++ b/arch/arm/kernel/topology.c
@@ -76,7 +76,6 @@ static unsigned long *__cpu_capacity;
static unsigned long middle_capacity = 1;
static bool cap_from_dt = true;
-extern bool cap_parsing_failed;
extern void normalize_cpu_capacity(void);
extern int __init parse_cpu_capacity(struct device_node *cpu_node, int cpu);
@@ -165,7 +164,7 @@ static void __init parse_dt_topology(void)
middle_capacity = ((max_capacity / 3)
>> (SCHED_CAPACITY_SHIFT-1)) + 1;
- if (cap_from_dt && !cap_parsing_failed)
+ if (cap_from_dt)
normalize_cpu_capacity();
}
diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c
index c5bc31eb97e8..7e1f6f75185b 100644
--- a/arch/arm64/kernel/topology.c
+++ b/arch/arm64/kernel/topology.c
@@ -27,7 +27,6 @@
#include <asm/cputype.h>
#include <asm/topology.h>
-extern bool cap_parsing_failed;
extern void normalize_cpu_capacity(void);
extern int __init parse_cpu_capacity(struct device_node *cpu_node, int cpu);
@@ -187,10 +186,8 @@ static int __init parse_dt_topology(void)
* cluster with restricted subnodes.
*/
map = of_get_child_by_name(cn, "cpu-map");
- if (!map) {
- cap_parsing_failed = true;
+ if (!map)
goto out;
- }
ret = parse_cluster(map, 0);
if (ret != 0)
diff --git a/drivers/base/arch_topology.c b/drivers/base/arch_topology.c
index b24d9a2af2c5..6543a032b332 100644
--- a/drivers/base/arch_topology.c
+++ b/drivers/base/arch_topology.c
@@ -93,7 +93,7 @@ subsys_initcall(register_cpu_capacity_sysctl);
static u32 capacity_scale;
static u32 *raw_capacity;
-bool cap_parsing_failed;
+static bool cap_parsing_failed;
void normalize_cpu_capacity(void)
{
@@ -208,7 +208,7 @@ static int __init register_cpufreq_notifier(void)
* until we have the necessary code to parse the cpu capacity, so
* skip registering cpufreq notifier.
*/
- if (!acpi_disabled || cap_parsing_failed)
+ if (!acpi_disabled || !raw_capacity)
return -EINVAL;
if (!alloc_cpumask_var(&cpus_to_visit, GFP_KERNEL)) {
--
2.10.0
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v3 6/9] drivers: remove useless comment from base/arch_topology.c
From: Juri Lelli @ 2017-03-27 13:18 UTC (permalink / raw)
To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
Cc: linux-pm-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA, peterz-wEGCiKHe2LqWVfeAwA7xHQ,
vincent.guittot-QSEj5FYQhm4dnm+yROfE0A,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
linux-lFZ/pmaqli7XmaaqVzeoHQ, sudeep.holla-5wv7dgnIgG8,
lorenzo.pieralisi-5wv7dgnIgG8, catalin.marinas-5wv7dgnIgG8,
will.deacon-5wv7dgnIgG8, morten.rasmussen-5wv7dgnIgG8,
dietmar.eggemann-5wv7dgnIgG8, juri.lelli-5wv7dgnIgG8,
broonie-DgEjT+Ai2ygdnm+yROfE0A,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r
In-Reply-To: <20170327131825.32134-1-juri.lelli-5wv7dgnIgG8@public.gmane.org>
Printing out an error message when we failed to get the cpu device is
not helping anyone. Remove it.
Signed-off-by: Juri Lelli <juri.lelli-5wv7dgnIgG8@public.gmane.org>
---
drivers/base/arch_topology.c | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/drivers/base/arch_topology.c b/drivers/base/arch_topology.c
index c33482121b7d..b24d9a2af2c5 100644
--- a/drivers/base/arch_topology.c
+++ b/drivers/base/arch_topology.c
@@ -81,11 +81,9 @@ static int register_cpu_capacity_sysctl(void)
for_each_possible_cpu(i) {
cpu = get_cpu_device(i);
- if (!cpu) {
- pr_err("%s: too early to get CPU%d device!\n",
- __func__, i);
+ if (!cpu)
continue;
- }
+
device_create_file(cpu, &dev_attr_cpu_capacity);
}
--
2.10.0
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v3 5/9] arm, arm64: factorize common cpu capacity default code
From: Juri Lelli @ 2017-03-27 13:18 UTC (permalink / raw)
To: linux-kernel
Cc: mark.rutland, devicetree, lorenzo.pieralisi, vincent.guittot,
juri.lelli, linux-pm, peterz, catalin.marinas, broonie,
will.deacon, gregkh, dietmar.eggemann, Russell King, robh+dt,
sudeep.holla, linux, morten.rasmussen, linux-arm-kernel
In-Reply-To: <20170327131825.32134-1-juri.lelli@arm.com>
arm and arm64 share lot of code relative to parsing CPU capacity
information from DT, using that information for appropriate scaling and
exposing a sysfs interface for chaging such values at runtime.
Factorize such code in a common place (driver/base/arch_topology.c) in
preparation for further additions.
Suggested-by: Will Deacon <will.deacon@arm.com>
Suggested-by: Mark Rutland <mark.rutland@arm.com>
Suggested-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Russell King <linux@armlinux.org.uk>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Juri Lelli <juri.lelli@arm.com>
---
Changes from v2:
- make capacity_scale and raw_capacity static
- added SPDX header
- improved indent
- misc. whitespaces/newlines fixes
Changes from v1:
- keep the original GPLv2 header
---
arch/arm/Kconfig | 1 +
arch/arm/kernel/topology.c | 213 ++-----------------------------------
arch/arm64/Kconfig | 1 +
arch/arm64/kernel/topology.c | 219 +--------------------------------------
drivers/base/Kconfig | 8 ++
drivers/base/Makefile | 1 +
drivers/base/arch_topology.c | 242 +++++++++++++++++++++++++++++++++++++++++++
7 files changed, 262 insertions(+), 423 deletions(-)
create mode 100644 drivers/base/arch_topology.c
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 0d4e71b42c77..cd61154bb6d0 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -25,6 +25,7 @@ config ARM
select EDAC_SUPPORT
select EDAC_ATOMIC_SCRUB
select GENERIC_ALLOCATOR
+ select GENERIC_ARCH_TOPOLOGY if ARM_CPU_TOPOLOGY
select GENERIC_ATOMIC64 if (CPU_V7M || CPU_V6 || !CPU_32v6K || !AEABI)
select GENERIC_CLOCKEVENTS_BROADCAST if SMP
select GENERIC_EARLY_IOREMAP
diff --git a/arch/arm/kernel/topology.c b/arch/arm/kernel/topology.c
index 162c82aeed96..49ef025ffaa0 100644
--- a/arch/arm/kernel/topology.c
+++ b/arch/arm/kernel/topology.c
@@ -44,75 +44,10 @@
* to run the rebalance_domains for all idle cores and the cpu_capacity can be
* updated during this sequence.
*/
-static DEFINE_PER_CPU(unsigned long, cpu_scale) = SCHED_CAPACITY_SCALE;
-static DEFINE_MUTEX(cpu_scale_mutex);
-unsigned long arch_scale_cpu_capacity(struct sched_domain *sd, int cpu)
-{
- return per_cpu(cpu_scale, cpu);
-}
-
-static void set_capacity_scale(unsigned int cpu, unsigned long capacity)
-{
- per_cpu(cpu_scale, cpu) = capacity;
-}
-
-static ssize_t cpu_capacity_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct cpu *cpu = container_of(dev, struct cpu, dev);
-
- return sprintf(buf, "%lu\n",
- arch_scale_cpu_capacity(NULL, cpu->dev.id));
-}
-
-static ssize_t cpu_capacity_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf,
- size_t count)
-{
- struct cpu *cpu = container_of(dev, struct cpu, dev);
- int this_cpu = cpu->dev.id, i;
- unsigned long new_capacity;
- ssize_t ret;
-
- if (count) {
- ret = kstrtoul(buf, 0, &new_capacity);
- if (ret)
- return ret;
- if (new_capacity > SCHED_CAPACITY_SCALE)
- return -EINVAL;
-
- mutex_lock(&cpu_scale_mutex);
- for_each_cpu(i, &cpu_topology[this_cpu].core_sibling)
- set_capacity_scale(i, new_capacity);
- mutex_unlock(&cpu_scale_mutex);
- }
-
- return count;
-}
-
-static DEVICE_ATTR_RW(cpu_capacity);
-
-static int register_cpu_capacity_sysctl(void)
-{
- int i;
- struct device *cpu;
-
- for_each_possible_cpu(i) {
- cpu = get_cpu_device(i);
- if (!cpu) {
- pr_err("%s: too early to get CPU%d device!\n",
- __func__, i);
- continue;
- }
- device_create_file(cpu, &dev_attr_cpu_capacity);
- }
-
- return 0;
-}
-subsys_initcall(register_cpu_capacity_sysctl);
+extern unsigned long
+arch_scale_cpu_capacity(struct sched_domain *sd, int cpu);
+extern void set_capacity_scale(unsigned int cpu, unsigned long capacity);
#ifdef CONFIG_OF
struct cpu_efficiency {
@@ -141,145 +76,9 @@ static unsigned long *__cpu_capacity;
static unsigned long middle_capacity = 1;
static bool cap_from_dt = true;
-static u32 *raw_capacity;
-static bool cap_parsing_failed;
-static u32 capacity_scale;
-
-static int __init parse_cpu_capacity(struct device_node *cpu_node, int cpu)
-{
- int ret = 1;
- u32 cpu_capacity;
-
- if (cap_parsing_failed)
- return !ret;
-
- ret = of_property_read_u32(cpu_node,
- "capacity-dmips-mhz",
- &cpu_capacity);
- if (!ret) {
- if (!raw_capacity) {
- raw_capacity = kcalloc(num_possible_cpus(),
- sizeof(*raw_capacity),
- GFP_KERNEL);
- if (!raw_capacity) {
- pr_err("cpu_capacity: failed to allocate memory for raw capacities\n");
- cap_parsing_failed = true;
- return ret;
- }
- }
- capacity_scale = max(cpu_capacity, capacity_scale);
- raw_capacity[cpu] = cpu_capacity;
- pr_debug("cpu_capacity: %s cpu_capacity=%u (raw)\n",
- cpu_node->full_name, raw_capacity[cpu]);
- } else {
- if (raw_capacity) {
- pr_err("cpu_capacity: missing %s raw capacity\n",
- cpu_node->full_name);
- pr_err("cpu_capacity: partial information: fallback to 1024 for all CPUs\n");
- }
- cap_parsing_failed = true;
- kfree(raw_capacity);
- }
-
- return !ret;
-}
-
-static void normalize_cpu_capacity(void)
-{
- u64 capacity;
- int cpu;
-
- if (!raw_capacity || cap_parsing_failed)
- return;
-
- pr_debug("cpu_capacity: capacity_scale=%u\n", capacity_scale);
- mutex_lock(&cpu_scale_mutex);
- for_each_possible_cpu(cpu) {
- capacity = (raw_capacity[cpu] << SCHED_CAPACITY_SHIFT)
- / capacity_scale;
- set_capacity_scale(cpu, capacity);
- pr_debug("cpu_capacity: CPU%d cpu_capacity=%lu\n",
- cpu, arch_scale_cpu_capacity(NULL, cpu));
- }
- mutex_unlock(&cpu_scale_mutex);
-}
-
-#ifdef CONFIG_CPU_FREQ
-static cpumask_var_t cpus_to_visit;
-static bool cap_parsing_done;
-static void parsing_done_workfn(struct work_struct *work);
-static DECLARE_WORK(parsing_done_work, parsing_done_workfn);
-
-static int
-init_cpu_capacity_callback(struct notifier_block *nb,
- unsigned long val,
- void *data)
-{
- struct cpufreq_policy *policy = data;
- int cpu;
-
- if (cap_parsing_failed || cap_parsing_done)
- return 0;
-
- switch (val) {
- case CPUFREQ_NOTIFY:
- pr_debug("cpu_capacity: init cpu capacity for CPUs [%*pbl] (to_visit=%*pbl)\n",
- cpumask_pr_args(policy->related_cpus),
- cpumask_pr_args(cpus_to_visit));
- cpumask_andnot(cpus_to_visit,
- cpus_to_visit,
- policy->related_cpus);
- for_each_cpu(cpu, policy->related_cpus) {
- raw_capacity[cpu] = arch_scale_cpu_capacity(NULL, cpu) *
- policy->cpuinfo.max_freq / 1000UL;
- capacity_scale = max(raw_capacity[cpu], capacity_scale);
- }
- if (cpumask_empty(cpus_to_visit)) {
- normalize_cpu_capacity();
- kfree(raw_capacity);
- pr_debug("cpu_capacity: parsing done\n");
- cap_parsing_done = true;
- schedule_work(&parsing_done_work);
- }
- }
- return 0;
-}
-
-static struct notifier_block init_cpu_capacity_notifier = {
- .notifier_call = init_cpu_capacity_callback,
-};
-
-static int __init register_cpufreq_notifier(void)
-{
- if (cap_parsing_failed)
- return -EINVAL;
-
- if (!alloc_cpumask_var(&cpus_to_visit, GFP_KERNEL)) {
- pr_err("cpu_capacity: failed to allocate memory for cpus_to_visit\n");
- return -ENOMEM;
- }
- cpumask_copy(cpus_to_visit, cpu_possible_mask);
-
- return cpufreq_register_notifier(&init_cpu_capacity_notifier,
- CPUFREQ_POLICY_NOTIFIER);
-}
-core_initcall(register_cpufreq_notifier);
-
-static void parsing_done_workfn(struct work_struct *work)
-{
- cpufreq_unregister_notifier(&init_cpu_capacity_notifier,
- CPUFREQ_POLICY_NOTIFIER);
-}
-
-#else
-static int __init free_raw_capacity(void)
-{
- kfree(raw_capacity);
-
- return 0;
-}
-core_initcall(free_raw_capacity);
-#endif
+extern bool cap_parsing_failed;
+extern void normalize_cpu_capacity(void);
+extern int __init parse_cpu_capacity(struct device_node *cpu_node, int cpu);
/*
* Iterate all CPUs' descriptor in DT and compute the efficiency
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 3741859765cf..e36fb12afad6 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -40,6 +40,7 @@ config ARM64
select EDAC_SUPPORT
select FRAME_POINTER
select GENERIC_ALLOCATOR
+ select GENERIC_ARCH_TOPOLOGY
select GENERIC_CLOCKEVENTS
select GENERIC_CLOCKEVENTS_BROADCAST
select GENERIC_CPU_AUTOPROBE
diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c
index 08243533e5ee..c5bc31eb97e8 100644
--- a/arch/arm64/kernel/topology.c
+++ b/arch/arm64/kernel/topology.c
@@ -11,7 +11,6 @@
* for more details.
*/
-#include <linux/acpi.h>
#include <linux/cpu.h>
#include <linux/cpumask.h>
#include <linux/init.h>
@@ -23,226 +22,14 @@
#include <linux/sched/topology.h>
#include <linux/slab.h>
#include <linux/string.h>
-#include <linux/cpufreq.h>
#include <asm/cpu.h>
#include <asm/cputype.h>
#include <asm/topology.h>
-static DEFINE_PER_CPU(unsigned long, cpu_scale) = SCHED_CAPACITY_SCALE;
-static DEFINE_MUTEX(cpu_scale_mutex);
-
-unsigned long arch_scale_cpu_capacity(struct sched_domain *sd, int cpu)
-{
- return per_cpu(cpu_scale, cpu);
-}
-
-static void set_capacity_scale(unsigned int cpu, unsigned long capacity)
-{
- per_cpu(cpu_scale, cpu) = capacity;
-}
-
-static ssize_t cpu_capacity_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct cpu *cpu = container_of(dev, struct cpu, dev);
-
- return sprintf(buf, "%lu\n",
- arch_scale_cpu_capacity(NULL, cpu->dev.id));
-}
-
-static ssize_t cpu_capacity_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf,
- size_t count)
-{
- struct cpu *cpu = container_of(dev, struct cpu, dev);
- int this_cpu = cpu->dev.id, i;
- unsigned long new_capacity;
- ssize_t ret;
-
- if (count) {
- ret = kstrtoul(buf, 0, &new_capacity);
- if (ret)
- return ret;
- if (new_capacity > SCHED_CAPACITY_SCALE)
- return -EINVAL;
-
- mutex_lock(&cpu_scale_mutex);
- for_each_cpu(i, &cpu_topology[this_cpu].core_sibling)
- set_capacity_scale(i, new_capacity);
- mutex_unlock(&cpu_scale_mutex);
- }
-
- return count;
-}
-
-static DEVICE_ATTR_RW(cpu_capacity);
-
-static int register_cpu_capacity_sysctl(void)
-{
- int i;
- struct device *cpu;
-
- for_each_possible_cpu(i) {
- cpu = get_cpu_device(i);
- if (!cpu) {
- pr_err("%s: too early to get CPU%d device!\n",
- __func__, i);
- continue;
- }
- device_create_file(cpu, &dev_attr_cpu_capacity);
- }
-
- return 0;
-}
-subsys_initcall(register_cpu_capacity_sysctl);
-
-static u32 capacity_scale;
-static u32 *raw_capacity;
-static bool cap_parsing_failed;
-
-static void __init parse_cpu_capacity(struct device_node *cpu_node, int cpu)
-{
- int ret;
- u32 cpu_capacity;
-
- if (cap_parsing_failed)
- return;
-
- ret = of_property_read_u32(cpu_node,
- "capacity-dmips-mhz",
- &cpu_capacity);
- if (!ret) {
- if (!raw_capacity) {
- raw_capacity = kcalloc(num_possible_cpus(),
- sizeof(*raw_capacity),
- GFP_KERNEL);
- if (!raw_capacity) {
- pr_err("cpu_capacity: failed to allocate memory for raw capacities\n");
- cap_parsing_failed = true;
- return;
- }
- }
- capacity_scale = max(cpu_capacity, capacity_scale);
- raw_capacity[cpu] = cpu_capacity;
- pr_debug("cpu_capacity: %s cpu_capacity=%u (raw)\n",
- cpu_node->full_name, raw_capacity[cpu]);
- } else {
- if (raw_capacity) {
- pr_err("cpu_capacity: missing %s raw capacity\n",
- cpu_node->full_name);
- pr_err("cpu_capacity: partial information: fallback to 1024 for all CPUs\n");
- }
- cap_parsing_failed = true;
- kfree(raw_capacity);
- }
-}
-
-static void normalize_cpu_capacity(void)
-{
- u64 capacity;
- int cpu;
-
- if (!raw_capacity || cap_parsing_failed)
- return;
-
- pr_debug("cpu_capacity: capacity_scale=%u\n", capacity_scale);
- mutex_lock(&cpu_scale_mutex);
- for_each_possible_cpu(cpu) {
- pr_debug("cpu_capacity: cpu=%d raw_capacity=%u\n",
- cpu, raw_capacity[cpu]);
- capacity = (raw_capacity[cpu] << SCHED_CAPACITY_SHIFT)
- / capacity_scale;
- set_capacity_scale(cpu, capacity);
- pr_debug("cpu_capacity: CPU%d cpu_capacity=%lu\n",
- cpu, arch_scale_cpu_capacity(NULL, cpu));
- }
- mutex_unlock(&cpu_scale_mutex);
-}
-
-#ifdef CONFIG_CPU_FREQ
-static cpumask_var_t cpus_to_visit;
-static bool cap_parsing_done;
-static void parsing_done_workfn(struct work_struct *work);
-static DECLARE_WORK(parsing_done_work, parsing_done_workfn);
-
-static int
-init_cpu_capacity_callback(struct notifier_block *nb,
- unsigned long val,
- void *data)
-{
- struct cpufreq_policy *policy = data;
- int cpu;
-
- if (cap_parsing_failed || cap_parsing_done)
- return 0;
-
- switch (val) {
- case CPUFREQ_NOTIFY:
- pr_debug("cpu_capacity: init cpu capacity for CPUs [%*pbl] (to_visit=%*pbl)\n",
- cpumask_pr_args(policy->related_cpus),
- cpumask_pr_args(cpus_to_visit));
- cpumask_andnot(cpus_to_visit,
- cpus_to_visit,
- policy->related_cpus);
- for_each_cpu(cpu, policy->related_cpus) {
- raw_capacity[cpu] = arch_scale_cpu_capacity(NULL, cpu) *
- policy->cpuinfo.max_freq / 1000UL;
- capacity_scale = max(raw_capacity[cpu], capacity_scale);
- }
- if (cpumask_empty(cpus_to_visit)) {
- normalize_cpu_capacity();
- kfree(raw_capacity);
- pr_debug("cpu_capacity: parsing done\n");
- cap_parsing_done = true;
- schedule_work(&parsing_done_work);
- }
- }
- return 0;
-}
-
-static struct notifier_block init_cpu_capacity_notifier = {
- .notifier_call = init_cpu_capacity_callback,
-};
-
-static int __init register_cpufreq_notifier(void)
-{
- /*
- * on ACPI-based systems we need to use the default cpu capacity
- * until we have the necessary code to parse the cpu capacity, so
- * skip registering cpufreq notifier.
- */
- if (!acpi_disabled || cap_parsing_failed)
- return -EINVAL;
-
- if (!alloc_cpumask_var(&cpus_to_visit, GFP_KERNEL)) {
- pr_err("cpu_capacity: failed to allocate memory for cpus_to_visit\n");
- return -ENOMEM;
- }
- cpumask_copy(cpus_to_visit, cpu_possible_mask);
-
- return cpufreq_register_notifier(&init_cpu_capacity_notifier,
- CPUFREQ_POLICY_NOTIFIER);
-}
-core_initcall(register_cpufreq_notifier);
-
-static void parsing_done_workfn(struct work_struct *work)
-{
- cpufreq_unregister_notifier(&init_cpu_capacity_notifier,
- CPUFREQ_POLICY_NOTIFIER);
-}
-
-#else
-static int __init free_raw_capacity(void)
-{
- kfree(raw_capacity);
-
- return 0;
-}
-core_initcall(free_raw_capacity);
-#endif
+extern bool cap_parsing_failed;
+extern void normalize_cpu_capacity(void);
+extern int __init parse_cpu_capacity(struct device_node *cpu_node, int cpu);
static int __init get_cpu_for_node(struct device_node *node)
{
diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
index d718ae4b907a..f046d21de57d 100644
--- a/drivers/base/Kconfig
+++ b/drivers/base/Kconfig
@@ -339,4 +339,12 @@ config CMA_ALIGNMENT
endif
+config GENERIC_ARCH_TOPOLOGY
+ bool
+ help
+ Enable support for architectures common topology code: e.g., parsing
+ CPU capacity information from DT, usage of such information for
+ appropriate scaling, sysfs interface for changing capacity values at
+ runtime.
+
endmenu
diff --git a/drivers/base/Makefile b/drivers/base/Makefile
index f2816f6ff76a..397e5c344e6a 100644
--- a/drivers/base/Makefile
+++ b/drivers/base/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_SOC_BUS) += soc.o
obj-$(CONFIG_PINCTRL) += pinctrl.o
obj-$(CONFIG_DEV_COREDUMP) += devcoredump.o
obj-$(CONFIG_GENERIC_MSI_IRQ_DOMAIN) += platform-msi.o
+obj-$(CONFIG_GENERIC_ARCH_TOPOLOGY) += arch_topology.o
obj-y += test/
diff --git a/drivers/base/arch_topology.c b/drivers/base/arch_topology.c
new file mode 100644
index 000000000000..c33482121b7d
--- /dev/null
+++ b/drivers/base/arch_topology.c
@@ -0,0 +1,242 @@
+/*
+ * Arch specific cpu topology information
+ *
+ * Copyright (C) 2016, ARM Ltd.
+ * Written by: Juri Lelli, ARM Ltd.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Released under the GPLv2 only.
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include <linux/acpi.h>
+#include <linux/cpu.h>
+#include <linux/cpufreq.h>
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/sched/topology.h>
+
+static DEFINE_MUTEX(cpu_scale_mutex);
+static DEFINE_PER_CPU(unsigned long, cpu_scale) = SCHED_CAPACITY_SCALE;
+
+unsigned long arch_scale_cpu_capacity(struct sched_domain *sd, int cpu)
+{
+ return per_cpu(cpu_scale, cpu);
+}
+
+void set_capacity_scale(unsigned int cpu, unsigned long capacity)
+{
+ per_cpu(cpu_scale, cpu) = capacity;
+}
+
+static ssize_t cpu_capacity_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct cpu *cpu = container_of(dev, struct cpu, dev);
+
+ return sprintf(buf, "%lu\n",
+ arch_scale_cpu_capacity(NULL, cpu->dev.id));
+}
+
+static ssize_t cpu_capacity_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct cpu *cpu = container_of(dev, struct cpu, dev);
+ int this_cpu = cpu->dev.id;
+ int i;
+ unsigned long new_capacity;
+ ssize_t ret;
+
+ if (!count)
+ return 0;
+
+ ret = kstrtoul(buf, 0, &new_capacity);
+ if (ret)
+ return ret;
+ if (new_capacity > SCHED_CAPACITY_SCALE)
+ return -EINVAL;
+
+ mutex_lock(&cpu_scale_mutex);
+ for_each_cpu(i, &cpu_topology[this_cpu].core_sibling)
+ set_capacity_scale(i, new_capacity);
+ mutex_unlock(&cpu_scale_mutex);
+
+ return count;
+}
+
+static DEVICE_ATTR_RW(cpu_capacity);
+
+static int register_cpu_capacity_sysctl(void)
+{
+ int i;
+ struct device *cpu;
+
+ for_each_possible_cpu(i) {
+ cpu = get_cpu_device(i);
+ if (!cpu) {
+ pr_err("%s: too early to get CPU%d device!\n",
+ __func__, i);
+ continue;
+ }
+ device_create_file(cpu, &dev_attr_cpu_capacity);
+ }
+
+ return 0;
+}
+subsys_initcall(register_cpu_capacity_sysctl);
+
+static u32 capacity_scale;
+static u32 *raw_capacity;
+bool cap_parsing_failed;
+
+void normalize_cpu_capacity(void)
+{
+ u64 capacity;
+ int cpu;
+
+ if (!raw_capacity || cap_parsing_failed)
+ return;
+
+ pr_debug("cpu_capacity: capacity_scale=%u\n", capacity_scale);
+ mutex_lock(&cpu_scale_mutex);
+ for_each_possible_cpu(cpu) {
+ pr_debug("cpu_capacity: cpu=%d raw_capacity=%u\n",
+ cpu, raw_capacity[cpu]);
+ capacity = (raw_capacity[cpu] << SCHED_CAPACITY_SHIFT)
+ / capacity_scale;
+ set_capacity_scale(cpu, capacity);
+ pr_debug("cpu_capacity: CPU%d cpu_capacity=%lu\n",
+ cpu, arch_scale_cpu_capacity(NULL, cpu));
+ }
+ mutex_unlock(&cpu_scale_mutex);
+}
+
+int __init parse_cpu_capacity(struct device_node *cpu_node, int cpu)
+{
+ int ret = 1;
+ u32 cpu_capacity;
+
+ if (cap_parsing_failed)
+ return !ret;
+
+ ret = of_property_read_u32(cpu_node,
+ "capacity-dmips-mhz",
+ &cpu_capacity);
+ if (!ret) {
+ if (!raw_capacity) {
+ raw_capacity = kcalloc(num_possible_cpus(),
+ sizeof(*raw_capacity),
+ GFP_KERNEL);
+ if (!raw_capacity) {
+ pr_err("cpu_capacity: failed to allocate memory for raw capacities\n");
+ cap_parsing_failed = true;
+ return ret;
+ }
+ }
+ capacity_scale = max(cpu_capacity, capacity_scale);
+ raw_capacity[cpu] = cpu_capacity;
+ pr_debug("cpu_capacity: %s cpu_capacity=%u (raw)\n",
+ cpu_node->full_name, raw_capacity[cpu]);
+ } else {
+ if (raw_capacity) {
+ pr_err("cpu_capacity: missing %s raw capacity\n",
+ cpu_node->full_name);
+ pr_err("cpu_capacity: partial information: fallback to 1024 for all CPUs\n");
+ }
+ cap_parsing_failed = true;
+ kfree(raw_capacity);
+ }
+
+ return !ret;
+}
+
+#ifdef CONFIG_CPU_FREQ
+static cpumask_var_t cpus_to_visit;
+static bool cap_parsing_done;
+static void parsing_done_workfn(struct work_struct *work);
+static DECLARE_WORK(parsing_done_work, parsing_done_workfn);
+
+static int
+init_cpu_capacity_callback(struct notifier_block *nb,
+ unsigned long val,
+ void *data)
+{
+ struct cpufreq_policy *policy = data;
+ int cpu;
+
+ if (cap_parsing_failed || cap_parsing_done)
+ return 0;
+
+ switch (val) {
+ case CPUFREQ_NOTIFY:
+ pr_debug("cpu_capacity: init cpu capacity for CPUs [%*pbl] (to_visit=%*pbl)\n",
+ cpumask_pr_args(policy->related_cpus),
+ cpumask_pr_args(cpus_to_visit));
+ cpumask_andnot(cpus_to_visit,
+ cpus_to_visit,
+ policy->related_cpus);
+ for_each_cpu(cpu, policy->related_cpus) {
+ raw_capacity[cpu] = arch_scale_cpu_capacity(NULL, cpu) *
+ policy->cpuinfo.max_freq / 1000UL;
+ capacity_scale = max(raw_capacity[cpu], capacity_scale);
+ }
+ if (cpumask_empty(cpus_to_visit)) {
+ normalize_cpu_capacity();
+ kfree(raw_capacity);
+ pr_debug("cpu_capacity: parsing done\n");
+ cap_parsing_done = true;
+ schedule_work(&parsing_done_work);
+ }
+ }
+ return 0;
+}
+
+static struct notifier_block init_cpu_capacity_notifier = {
+ .notifier_call = init_cpu_capacity_callback,
+};
+
+static int __init register_cpufreq_notifier(void)
+{
+ /*
+ * on ACPI-based systems we need to use the default cpu capacity
+ * until we have the necessary code to parse the cpu capacity, so
+ * skip registering cpufreq notifier.
+ */
+ if (!acpi_disabled || cap_parsing_failed)
+ return -EINVAL;
+
+ if (!alloc_cpumask_var(&cpus_to_visit, GFP_KERNEL)) {
+ pr_err("cpu_capacity: failed to allocate memory for cpus_to_visit\n");
+ return -ENOMEM;
+ }
+
+ cpumask_copy(cpus_to_visit, cpu_possible_mask);
+
+ return cpufreq_register_notifier(&init_cpu_capacity_notifier,
+ CPUFREQ_POLICY_NOTIFIER);
+}
+core_initcall(register_cpufreq_notifier);
+
+static void parsing_done_workfn(struct work_struct *work)
+{
+ cpufreq_unregister_notifier(&init_cpu_capacity_notifier,
+ CPUFREQ_POLICY_NOTIFIER);
+}
+
+#else
+static int __init free_raw_capacity(void)
+{
+ kfree(raw_capacity);
+
+ return 0;
+}
+core_initcall(free_raw_capacity);
+#endif
--
2.10.0
^ permalink raw reply related
* [PATCH v3 4/9] arm: remove wrong CONFIG_PROC_SYSCTL ifdef
From: Juri Lelli @ 2017-03-27 13:18 UTC (permalink / raw)
To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
Cc: linux-pm-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA, peterz-wEGCiKHe2LqWVfeAwA7xHQ,
vincent.guittot-QSEj5FYQhm4dnm+yROfE0A,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
linux-lFZ/pmaqli7XmaaqVzeoHQ, sudeep.holla-5wv7dgnIgG8,
lorenzo.pieralisi-5wv7dgnIgG8, catalin.marinas-5wv7dgnIgG8,
will.deacon-5wv7dgnIgG8, morten.rasmussen-5wv7dgnIgG8,
dietmar.eggemann-5wv7dgnIgG8, juri.lelli-5wv7dgnIgG8,
broonie-DgEjT+Ai2ygdnm+yROfE0A,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r
In-Reply-To: <20170327131825.32134-1-juri.lelli-5wv7dgnIgG8@public.gmane.org>
The sysfs cpu_capacity entry for each CPU has nothing to do with
PROC_FS, nor it's in /proc/sys path.
Remove such ifdef.
Cc: Russell King <linux-lFZ/pmaqli7XmaaqVzeoHQ@public.gmane.org>
Reported-and-suggested-by: Sudeep Holla <sudeep.holla-5wv7dgnIgG8@public.gmane.org>
Fixes: 7e5930aaef5d ('ARM: 8622/3: add sysfs cpu_capacity attribute')
Signed-off-by: Juri Lelli <juri.lelli-5wv7dgnIgG8@public.gmane.org>
---
arch/arm/kernel/topology.c | 2 --
1 file changed, 2 deletions(-)
diff --git a/arch/arm/kernel/topology.c b/arch/arm/kernel/topology.c
index 4e4af809606a..162c82aeed96 100644
--- a/arch/arm/kernel/topology.c
+++ b/arch/arm/kernel/topology.c
@@ -57,7 +57,6 @@ static void set_capacity_scale(unsigned int cpu, unsigned long capacity)
per_cpu(cpu_scale, cpu) = capacity;
}
-#ifdef CONFIG_PROC_SYSCTL
static ssize_t cpu_capacity_show(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -114,7 +113,6 @@ static int register_cpu_capacity_sysctl(void)
return 0;
}
subsys_initcall(register_cpu_capacity_sysctl);
-#endif
#ifdef CONFIG_OF
struct cpu_efficiency {
--
2.10.0
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v3 3/9] arm: fix return value of parse_cpu_capacity
From: Juri Lelli @ 2017-03-27 13:18 UTC (permalink / raw)
To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
Cc: linux-pm-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA, peterz-wEGCiKHe2LqWVfeAwA7xHQ,
vincent.guittot-QSEj5FYQhm4dnm+yROfE0A,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
linux-lFZ/pmaqli7XmaaqVzeoHQ, sudeep.holla-5wv7dgnIgG8,
lorenzo.pieralisi-5wv7dgnIgG8, catalin.marinas-5wv7dgnIgG8,
will.deacon-5wv7dgnIgG8, morten.rasmussen-5wv7dgnIgG8,
dietmar.eggemann-5wv7dgnIgG8, juri.lelli-5wv7dgnIgG8,
broonie-DgEjT+Ai2ygdnm+yROfE0A,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r
In-Reply-To: <20170327131825.32134-1-juri.lelli-5wv7dgnIgG8@public.gmane.org>
parse_cpu_capacity() has to return 0 on failure, but it currently returns
1 instead if raw_capacity kcalloc failed.
Fix it by removing the negation of the return value.
Cc: Russell King <linux-lFZ/pmaqli7XmaaqVzeoHQ@public.gmane.org>
Reported-by: Morten Rasmussen <morten.rasmussen-5wv7dgnIgG8@public.gmane.org>
Fixes: 06073ee26775 ('ARM: 8621/3: parse cpu capacity-dmips-mhz from DT')
Signed-off-by: Juri Lelli <juri.lelli-5wv7dgnIgG8@public.gmane.org>
---
arch/arm/kernel/topology.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm/kernel/topology.c b/arch/arm/kernel/topology.c
index f8a3ab82e77f..4e4af809606a 100644
--- a/arch/arm/kernel/topology.c
+++ b/arch/arm/kernel/topology.c
@@ -166,7 +166,7 @@ static int __init parse_cpu_capacity(struct device_node *cpu_node, int cpu)
if (!raw_capacity) {
pr_err("cpu_capacity: failed to allocate memory for raw capacities\n");
cap_parsing_failed = true;
- return !ret;
+ return ret;
}
}
capacity_scale = max(cpu_capacity, capacity_scale);
--
2.10.0
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v3 2/9] Documentation/ABI: add information about cpu_capacity
From: Juri Lelli @ 2017-03-27 13:18 UTC (permalink / raw)
To: linux-kernel
Cc: linux-pm, linux-arm-kernel, devicetree, peterz, vincent.guittot,
robh+dt, mark.rutland, linux, sudeep.holla, lorenzo.pieralisi,
catalin.marinas, will.deacon, morten.rasmussen, dietmar.eggemann,
juri.lelli, broonie, gregkh
In-Reply-To: <20170327131825.32134-1-juri.lelli@arm.com>
/sys/devices/system/cpu/cpu#/cpu_capacity describe information about
CPUs heterogeneity (ref. to Documentation/devicetree/bindings/arm/
cpu-capacity.txt).
Add such description.
Signed-off-by: Juri Lelli <juri.lelli@arm.com>
---
Documentation/ABI/testing/sysfs-devices-system-cpu | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu
index 2a4a423d08e0..f3d5817c4ef0 100644
--- a/Documentation/ABI/testing/sysfs-devices-system-cpu
+++ b/Documentation/ABI/testing/sysfs-devices-system-cpu
@@ -366,3 +366,10 @@ Contact: Linux ARM Kernel Mailing list <linux-arm-kernel@lists.infradead.org>
Description: AArch64 CPU registers
'identification' directory exposes the CPU ID registers for
identifying model and revision of the CPU.
+
+What: /sys/devices/system/cpu/cpu#/cpu_capacity
+Date: December 2016
+Contact: Linux kernel mailing list <linux-kernel@vger.kernel.org>
+Description: information about CPUs heterogeneity.
+
+ cpu_capacity: capacity of cpu#.
--
2.10.0
^ permalink raw reply related
* [PATCH v3 1/9] Documentation: arm: fix wrong reference number in DT definition
From: Juri Lelli @ 2017-03-27 13:18 UTC (permalink / raw)
To: linux-kernel
Cc: mark.rutland, devicetree, lorenzo.pieralisi, vincent.guittot,
juri.lelli, linux-pm, peterz, catalin.marinas, broonie,
will.deacon, gregkh, dietmar.eggemann, robh+dt, sudeep.holla,
linux, morten.rasmussen, linux-arm-kernel
In-Reply-To: <20170327131825.32134-1-juri.lelli@arm.com>
Reference to cpu capacity binding has a wrong number. Fix it.
Reported-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Signed-off-by: Juri Lelli <juri.lelli@arm.com>
Acked-by: Rob Herring <robh@kernel.org>
---
Documentation/devicetree/bindings/arm/cpus.txt | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Documentation/devicetree/bindings/arm/cpus.txt b/Documentation/devicetree/bindings/arm/cpus.txt
index 698ad1f097fa..83ec3fa0a05e 100644
--- a/Documentation/devicetree/bindings/arm/cpus.txt
+++ b/Documentation/devicetree/bindings/arm/cpus.txt
@@ -248,7 +248,7 @@ nodes to be present and contain the properties described below.
Usage: Optional
Value type: <u32>
Definition:
- # u32 value representing CPU capacity [3] in
+ # u32 value representing CPU capacity [4] in
DMIPS/MHz, relative to highest capacity-dmips-mhz
in the system.
@@ -475,5 +475,5 @@ cpus {
[2] arm/msm/qcom,kpss-acc.txt
[3] ARM Linux kernel documentation - idle states bindings
Documentation/devicetree/bindings/arm/idle-states.txt
-[3] ARM Linux kernel documentation - cpu capacity bindings
+[4] ARM Linux kernel documentation - cpu capacity bindings
Documentation/devicetree/bindings/arm/cpu-capacity.txt
--
2.10.0
^ permalink raw reply related
* [PATCH v3 0/9] Fix issues and factorize arm/arm64 capacity information code
From: Juri Lelli @ 2017-03-27 13:18 UTC (permalink / raw)
To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
Cc: linux-pm-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA, peterz-wEGCiKHe2LqWVfeAwA7xHQ,
vincent.guittot-QSEj5FYQhm4dnm+yROfE0A,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
linux-lFZ/pmaqli7XmaaqVzeoHQ, sudeep.holla-5wv7dgnIgG8,
lorenzo.pieralisi-5wv7dgnIgG8, catalin.marinas-5wv7dgnIgG8,
will.deacon-5wv7dgnIgG8, morten.rasmussen-5wv7dgnIgG8,
dietmar.eggemann-5wv7dgnIgG8, juri.lelli-5wv7dgnIgG8,
broonie-DgEjT+Ai2ygdnm+yROfE0A,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r
Hi,
arm and arm64 topology.c share a lot of code related to parsing of capacity
information. This is v3 of a solution [1] (based on Will's, Catalin's and
Mark's off-line suggestions) to move such common code in a single place:
drivers/base/arch_topology.c (by creating such file and conditionally compiling
it for arm and arm64 only).
First 4 patches are actually fixes for the current code.
Patch 5 is the actual refactoring.
Patch 6 is a minor change suggested by Greg and can be squashed as needed.
Patch 7 removes one of the extern symbols by changing a bit the now common
code.
Patch 8 removes the remaining externs (as required by Russell during v1 review)
by creating a new header file include/linux/arch_topology.h and including that
from arm, arm64 and drivers.
Last patch addresses Dietmar's comments to v1 and adds a 'atd_' prefix to
interfaces exported by drivers code and used by arch (and potentially others in
the future).
Changes from v2:
- rebase on top of 4.11-rc4
- fix various problems pointed out by Greg, thanks for the review!
(see patch 5 for details)
The set is based on top of linux/master (4.11-rc4 c02ed2e75ef4) and it is also
available from:
git://linux-arm.org/linux-jl.git upstream/default_caps_factorize-v3
Best,
- Juri
[1] v1 - https://marc.info/?l=linux-kernel&m=148483680119355&w=2
v2 - https://marc.info/?l=linux-kernel&m=148663344018205&w=2
Juri Lelli (9):
Documentation: arm: fix wrong reference number in DT definition
Documentation/ABI: add information about cpu_capacity
arm: fix return value of parse_cpu_capacity
arm: remove wrong CONFIG_PROC_SYSCTL ifdef
arm, arm64: factorize common cpu capacity default code
drivers: remove useless comment from base/arch_topology.c
arm,arm64,drivers: reduce scope of cap_parsing_failed
arm,arm64,drivers: move externs in a new header file
arm,arm64,drivers: add a prefix to drivers arch_topology interfaces
Documentation/ABI/testing/sysfs-devices-system-cpu | 7 +
Documentation/devicetree/bindings/arm/cpus.txt | 4 +-
arch/arm/Kconfig | 1 +
arch/arm/kernel/topology.c | 221 +------------------
arch/arm64/Kconfig | 1 +
arch/arm64/kernel/topology.c | 226 +------------------
drivers/base/Kconfig | 8 +
drivers/base/Makefile | 1 +
drivers/base/arch_topology.c | 241 +++++++++++++++++++++
include/linux/arch_topology.h | 17 ++
10 files changed, 288 insertions(+), 439 deletions(-)
create mode 100644 drivers/base/arch_topology.c
create mode 100644 include/linux/arch_topology.h
--
2.10.0
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v3 4/4] mvebu: wrt1900ac: Use pwm-fan rather than gpio-fan
From: Andrew Lunn @ 2017-03-27 13:17 UTC (permalink / raw)
To: Ralph Sennhauser
Cc: Mark Rutland, Alexandre Courbot, Jason Cooper, linux-pwm,
Linus Walleij, Russell King, Rob Herring, linux-kernel,
linux-gpio, devicetree, Thierry Reding, Gregory Clement,
Imre Kaloz, linux-arm-kernel, Sebastian Hesselbarth
In-Reply-To: <20170326230558.6b6c4ec6@gmail.com>
On Sun, Mar 26, 2017 at 11:05:58PM +0200, Ralph Sennhauser wrote:
> On Fri, 24 Mar 2017 15:35:05 +0100
> Andrew Lunn <andrew@lunn.ch> wrote:
>
> > > + pwm_fan {
> > > /* SUNON HA4010V4-0000-C99 */
> > > - compatible = "gpio-fan";
> > > - gpios = <&gpio0 24 0>;
> > >
> > > - gpio-fan,speed-map = <0 0
> > > - 4500 1>;
> > > + compatible = "pwm-fan";
> > > + pwms = <&gpio0 24 4000 0>;
> >
> > Hi Ralph
> >
> > I believe this last 0 is the flags parameter. Now that we have
> > #pwm-cells = 1, i think this last 0 should be dropped.
> >
> > Andrew
>
> Hi Andrew,
>
> isn't the 4000 (period?) pwm-cell #2 and 0 (flags?) pwm-cell #3? I
> actually expect "pwms = <&gpio0 24>;" here or "#pwm-cells = <2>;"
Hi Ralph
>From Documentation/devicetree/bindings/pwm/pwm.txt:
pwm-list ::= <single-pwm> [pwm-list]
single-pwm ::= <pwm-phandle> <pwm-specifier>
pwm-phandle : phandle to PWM controller node
pwm-specifier : array of #pwm-cells specifying the given PWM
(controller specific)
Our pwm-list has a single single-pwm.
phandle is &gpio0.
The remaining parts are the specifier, or which there should be #pwm-calls.
>From Documentation/devicetree/bindings/pwm/pwm.txt again:
pwm-specifier typically encodes the chip-relative PWM number and the PWM
period in nanoseconds.
Optionally, the pwm-specifier can encode a number of flags (defined in
<dt-bindings/pwm/pwm.h>) in a third cell:
- PWM_POLARITY_INVERTED: invert the PWM signal polarity
So we are using the 24th PWM and 4000 nanosecod period. We don't want
any flags.
So
pwms = <&gpio0 24 4000>;
has a phandle, and then 2 cells.
Andrew
^ permalink raw reply
* Re: [PATCH v3 1/2] drivers: pwm: pwm-atmel: switch to atomic PWM
From: Alexandre Belloni @ 2017-03-27 13:15 UTC (permalink / raw)
To: Boris Brezillon
Cc: Claudiu Beznea, thierry.reding, robh+dt, pawel.moll, mark.rutland,
ijc+devicetree, galak, linux-pwm, devicetree, linux-kernel,
linux-arm-kernel, nicolas.ferre, cristian.birsan,
andrei.pistirica, tudor.ambarus, eugen.hristev
In-Reply-To: <20170327150237.69d8523f@bbrezillon>
On 27/03/2017 at 15:02:37 +0200, Boris Brezillon wrote:
> Hi Claudiu,
>
> On Wed, 22 Mar 2017 15:29:34 +0200
> Claudiu Beznea <claudiu.beznea@microchip.com> wrote:
>
> > static const struct platform_device_id atmel_pwm_devtypes[] = {
> > {
> > .name = "at91sam9rl-pwm",
> > - .driver_data = (kernel_ulong_t)&atmel_pwm_data_v1,
> > + .driver_data = (kernel_ulong_t)&atmel_pwm_regs_v1,
> > }, {
> > .name = "sama5d3-pwm",
> > - .driver_data = (kernel_ulong_t)&atmel_pwm_data_v2,
> > + .driver_data = (kernel_ulong_t)&atmel_pwm_regs_v2,
> > }, {
> > /* sentinel */
> > },
>
> Unrelated to this series, but can you prepare a patch to get rid of
> this platform id table (AT91 platforms have completely switched to DT
> for quite some time now).
>
Please, don't until AVR32 is gone.
> You can also get rid of the "if (pdev->dev.of_node)" condition in
> atmel_pwm_probe() since it's guaranteed to be true, otherwise the
> ->probe() method wouldn't be called.
>
> Thanks,
>
> Boris
--
Alexandre Belloni, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
^ permalink raw reply
* Re: [PATCH v3 1/2] Input: add STMicroelectronics FingerTip touchscreen driver
From: Andi Shyti @ 2017-03-27 13:11 UTC (permalink / raw)
To: Dmitry Torokhov, Rob Herring
Cc: Javier Martinez Canillas, Andrzej Hajda, Chanwoo Choi,
linux-input-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, Andi Shyti, Andi Shyti
In-Reply-To: <20170327130743.27783-2-andi.shyti-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
Hi,
On Mon, Mar 27, 2017 at 10:07:42PM +0900, Andi Shyti wrote:
> Add binding for the STMicroelectronics FingerTip (stmfts)
> touchscreen driver.
>
> Signed-off-by: Andi Shyti <andi.shyti-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
> Reviewed-by: Javier Martinez Canillas <javier-JPH+aEBZ4P+UEJcrhfAQsw@public.gmane.org>
I'm sorry, I forgot to add:
Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
Andi
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH v3 2/2] Input: add support for the STMicroelectronics FingerTip touchscreen
From: Andi Shyti @ 2017-03-27 13:07 UTC (permalink / raw)
To: Dmitry Torokhov, Rob Herring
Cc: Javier Martinez Canillas, Andrzej Hajda, Chanwoo Choi,
linux-input-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, Andi Shyti, Andi Shyti
In-Reply-To: <20170327130743.27783-1-andi.shyti-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
The stmfts (ST-Microelectronics FingerTip S) touchscreen device
is a capacitive multi-touch controller mainly for mobile use.
It's connected through i2c bus at the address 0x49 and it
interfaces with userspace through input event interface.
At the current state it provides a touchscreen multitouch
functionality up to 10 fingers. Each finger is enumerated with a
distinctive id (from 0 to 9).
If enabled the device can support single "touch" hovering, by
providing three coordinates, x, y and distance.
It is possible to select the touchkey functionality which
provides a basic two keys interface for "home" and "back" menu,
typical in mobile phones.
Signed-off-by: Andi Shyti <andi.shyti-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
---
drivers/input/touchscreen/Kconfig | 12 +
drivers/input/touchscreen/Makefile | 1 +
drivers/input/touchscreen/stmfts.c | 805 +++++++++++++++++++++++++++++++++++++
3 files changed, 818 insertions(+)
create mode 100644 drivers/input/touchscreen/stmfts.c
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 33c62e5de4fa..f8631c64290d 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -1114,6 +1114,18 @@ config TOUCHSCREEN_ST1232
To compile this driver as a module, choose M here: the
module will be called st1232_ts.
+config TOUCHSCREEN_STMFTS
+ tristate "STMicroelectronics STMFTS touchscreen"
+ depends on I2C
+ depends on INPUT
+ depends on LEDS_CLASS
+ help
+ Say Y here if you want support for STMicroelectronics
+ STMFTS touchscreen.
+
+ To compile this driver as a module, choose M here: the
+ module will be called stmfts.
+
config TOUCHSCREEN_STMPE
tristate "STMicroelectronics STMPE touchscreens"
depends on MFD_STMPE
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 18e476948e44..6badce87037b 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -67,6 +67,7 @@ obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o
obj-$(CONFIG_TOUCHSCREEN_SILEAD) += silead.o
obj-$(CONFIG_TOUCHSCREEN_SIS_I2C) += sis_i2c.o
obj-$(CONFIG_TOUCHSCREEN_ST1232) += st1232.o
+obj-$(CONFIG_TOUCHSCREEN_STMFTS) += stmfts.o
obj-$(CONFIG_TOUCHSCREEN_STMPE) += stmpe-ts.o
obj-$(CONFIG_TOUCHSCREEN_SUN4I) += sun4i-ts.o
obj-$(CONFIG_TOUCHSCREEN_SUR40) += sur40.o
diff --git a/drivers/input/touchscreen/stmfts.c b/drivers/input/touchscreen/stmfts.c
new file mode 100644
index 000000000000..2e18b1456f42
--- /dev/null
+++ b/drivers/input/touchscreen/stmfts.c
@@ -0,0 +1,805 @@
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ * Author: Andi Shyti <andi.shyti-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * STMicroelectronics FTS Touchscreen device driver
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/input/mt.h>
+#include <linux/input/touchscreen.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+
+/* I2C commands */
+#define STMFTS_READ_INFO 0x80
+#define STMFTS_READ_STATUS 0x84
+#define STMFTS_READ_ONE_EVENT 0x85
+#define STMFTS_READ_ALL_EVENT 0x86
+#define STMFTS_LATEST_EVENT 0x87
+#define STMFTS_SLEEP_IN 0x90
+#define STMFTS_SLEEP_OUT 0x91
+#define STMFTS_MS_MT_SENSE_OFF 0x92
+#define STMFTS_MS_MT_SENSE_ON 0x93
+#define STMFTS_SS_HOVER_SENSE_OFF 0x94
+#define STMFTS_SS_HOVER_SENSE_ON 0x95
+#define STMFTS_MS_KEY_SENSE_OFF 0x9a
+#define STMFTS_MS_KEY_SENSE_ON 0x9b
+#define STMFTS_SYSTEM_RESET 0xa0
+#define STMFTS_CLEAR_EVENT_STACK 0xa1
+#define STMFTS_FULL_FORCE_CALIBRATION 0xa2
+#define STMFTS_MS_CX_TUNING 0xa3
+#define STMFTS_SS_CX_TUNING 0xa4
+
+/* events */
+#define STMFTS_EV_NO_EVENT 0x00
+#define STMFTS_EV_MULTI_TOUCH_DETECTED 0x02
+#define STMFTS_EV_MULTI_TOUCH_ENTER 0x03
+#define STMFTS_EV_MULTI_TOUCH_LEAVE 0x04
+#define STMFTS_EV_MULTI_TOUCH_MOTION 0x05
+#define STMFTS_EV_HOVER_ENTER 0x07
+#define STMFTS_EV_HOVER_LEAVE 0x08
+#define STMFTS_EV_HOVER_MOTION 0x09
+#define STMFTS_EV_KEY_STATUS 0x0e
+#define STMFTS_EV_ERROR 0x0f
+#define STMFTS_EV_CONTROLLER_READY 0x10
+#define STMFTS_EV_SLEEP_OUT_CONTROLLER_READY 0x11
+#define STMFTS_EV_STATUS 0x16
+#define STMFTS_EV_DEBUG 0xdb
+
+/* multi touch related event masks */
+#define STMFTS_MASK_EVENT_ID 0x0f
+#define STMFTS_MASK_TOUCH_ID 0xf0
+#define STMFTS_MASK_LEFT_EVENT 0x0f
+#define STMFTS_MASK_X_MSB 0x0f
+#define STMFTS_MASK_Y_LSB 0xf0
+
+/* key related event masks */
+#define STMFTS_MASK_KEY_NO_TOUCH 0x00
+#define STMFTS_MASK_KEY_MENU 0x01
+#define STMFTS_MASK_KEY_BACK 0x02
+
+#define STMFTS_EVENT_SIZE 8
+#define STMFTS_STACK_DEPTH 32
+#define STMFTS_DATA_MAX_SIZE (STMFTS_EVENT_SIZE * STMFTS_STACK_DEPTH)
+#define STMFTS_MAX_FINGERS 10
+#define STMFTS_DEV_NAME "stmfts"
+
+enum stmfts_regulators {
+ STMFTS_REGULATOR_VDD,
+ STMFTS_REGULATOR_AVDD,
+};
+
+struct stmfts_data {
+ struct i2c_client *client;
+ struct input_dev *input;
+ struct led_classdev led_cdev;
+ struct mutex mutex;
+
+ struct touchscreen_properties prop;
+
+ struct regulator_bulk_data regulators[2];
+
+ /* ledvdd will be used also to check
+ * whether the LED is supported
+ */
+ struct regulator *ledvdd;
+
+ u16 chip_id;
+ u8 chip_ver;
+ u16 fw_ver;
+ u8 config_id;
+ u8 config_ver;
+
+ u8 data[STMFTS_DATA_MAX_SIZE];
+
+ struct completion signal;
+
+ bool use_key;
+ bool led_status;
+ bool hover_enabled;
+ bool running;
+};
+
+static int stmfts_read_i2c_block_data(struct stmfts_data *sdata)
+{
+ struct i2c_msg msgs[2];
+ u8 cmd = STMFTS_READ_ALL_EVENT;
+
+ msgs[0].addr = sdata->client->addr;
+ msgs[0].flags = 0;
+ msgs[0].len = 1;
+ msgs[0].buf = &cmd;
+
+ msgs[1].addr = sdata->client->addr;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].len = STMFTS_DATA_MAX_SIZE - STMFTS_EVENT_SIZE;
+ msgs[1].buf = sdata->data + STMFTS_EVENT_SIZE;
+
+ return i2c_transfer(sdata->client->adapter, msgs, ARRAY_SIZE(msgs));
+}
+
+static void stmfts_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct stmfts_data *sdata = container_of(led_cdev,
+ struct stmfts_data, led_cdev);
+
+ if (value == sdata->led_status || !sdata->ledvdd)
+ return;
+
+ if (!value) {
+ regulator_disable(sdata->ledvdd);
+ } else {
+ int err = regulator_enable(sdata->ledvdd);
+
+ if (err)
+ dev_warn(&sdata->client->dev,
+ "failed to disable ledvdd regulator\n");
+ }
+
+ sdata->led_status = value;
+}
+
+static enum led_brightness stmfts_brightness_get(struct led_classdev *led_cdev)
+{
+ struct stmfts_data *sdata = container_of(led_cdev,
+ struct stmfts_data, led_cdev);
+
+ return !!regulator_is_enabled(sdata->ledvdd);
+}
+
+static void stmfts_parse_event(struct stmfts_data *sdata)
+{
+ u8 id, t_id;
+ u16 x, y, z, maj, min, orientation, area;
+ u8 *event;
+ int i;
+
+ for (i = 0; i < STMFTS_STACK_DEPTH; i++) {
+ event = &sdata->data[i*STMFTS_EVENT_SIZE];
+
+ id = event[0] & STMFTS_MASK_EVENT_ID;
+ t_id = (event[0] & STMFTS_MASK_TOUCH_ID) >> 4;
+
+ switch (id) {
+ case STMFTS_EV_NO_EVENT:
+ return;
+
+ case STMFTS_EV_MULTI_TOUCH_ENTER:
+ case STMFTS_EV_MULTI_TOUCH_LEAVE:
+ case STMFTS_EV_MULTI_TOUCH_MOTION:
+ if (id == STMFTS_EV_MULTI_TOUCH_ENTER)
+ input_mt_report_slot_state(sdata->input,
+ MT_TOOL_FINGER, true);
+ else if (id == STMFTS_EV_MULTI_TOUCH_LEAVE)
+ input_mt_report_slot_state(sdata->input,
+ MT_TOOL_FINGER, false);
+
+ x = event[1] | ((event[2] & STMFTS_MASK_X_MSB) << 8);
+ y = (event[2] >> 4) | (event[3] << 4);
+
+ maj = event[4];
+ min = event[5];
+ orientation = event[6];
+ area = event[7];
+
+ input_mt_slot(sdata->input, t_id);
+ input_report_abs(sdata->input, ABS_MT_POSITION_X, x);
+ input_report_abs(sdata->input, ABS_MT_POSITION_Y, y);
+ input_report_abs(sdata->input, ABS_MT_TOUCH_MAJOR, maj);
+ input_report_abs(sdata->input, ABS_MT_TOUCH_MINOR, min);
+ input_report_abs(sdata->input, ABS_MT_PRESSURE, area);
+ input_report_abs(sdata->input, ABS_MT_ORIENTATION,
+ orientation);
+ input_sync(sdata->input);
+
+ break;
+
+ case STMFTS_EV_HOVER_ENTER:
+ case STMFTS_EV_HOVER_LEAVE:
+ case STMFTS_EV_HOVER_MOTION:
+ x = (event[2] << 4) | (event[4] >> 4);
+ y = (event[3] << 4) | (event[4] & STMFTS_MASK_Y_LSB);
+ z = event[5];
+ orientation = event[6] & STMFTS_MASK_Y_LSB;
+
+ input_report_abs(sdata->input, ABS_X, x);
+ input_report_abs(sdata->input, ABS_Y, y);
+ input_report_abs(sdata->input, ABS_DISTANCE, z);
+ input_sync(sdata->input);
+
+ break;
+
+ case STMFTS_EV_KEY_STATUS:
+ switch (event[2]) {
+ case 0:
+ input_report_key(sdata->input, KEY_BACK, 0);
+ input_report_key(sdata->input, KEY_MENU, 0);
+ break;
+
+ case STMFTS_MASK_KEY_BACK:
+ input_report_key(sdata->input, KEY_BACK, 1);
+ break;
+
+ case STMFTS_MASK_KEY_MENU:
+ input_report_key(sdata->input, KEY_MENU, 1);
+ break;
+
+ default:
+ dev_warn(&sdata->client->dev,
+ "unknown key event\n");
+ }
+
+ input_sync(sdata->input);
+ break;
+
+ case STMFTS_EV_ERROR:
+ dev_warn(&sdata->client->dev,
+ "error code: 0x%x%x%x%x%x%x",
+ event[6], event[5], event[4],
+ event[3], event[2], event[1]);
+ break;
+
+ default:
+ dev_err(&sdata->client->dev,
+ "unknown event 0x%x\n", event[0]);
+ }
+ }
+}
+
+static irqreturn_t stmfts_irq_handler(int irq, void *dev)
+{
+ struct stmfts_data *sdata = dev;
+ int ret;
+
+ mutex_lock(&sdata->mutex);
+ ret = i2c_smbus_read_i2c_block_data(sdata->client,
+ STMFTS_READ_ONE_EVENT,
+ STMFTS_EVENT_SIZE, sdata->data);
+
+ if (ret < 0 || ret != STMFTS_EVENT_SIZE)
+ goto exit;
+
+ switch (sdata->data[0]) {
+ case STMFTS_EV_CONTROLLER_READY:
+ case STMFTS_EV_SLEEP_OUT_CONTROLLER_READY:
+ case STMFTS_EV_STATUS:
+ complete(&sdata->signal);
+ case STMFTS_EV_NO_EVENT:
+ case STMFTS_EV_DEBUG:
+ break;
+
+ default:
+ if (unlikely(!sdata->input))
+ goto exit;
+
+ ret = stmfts_read_i2c_block_data(sdata);
+ if (ret < 0)
+ goto exit;
+
+ stmfts_parse_event(sdata);
+ }
+
+exit:
+ mutex_unlock(&sdata->mutex);
+ return IRQ_HANDLED;
+}
+
+static int stmfts_write_and_wait(struct stmfts_data *sdata, const u8 cmd)
+{
+ int err;
+
+ err = i2c_smbus_write_byte(sdata->client, cmd);
+ if (err)
+ return err;
+
+ err = wait_for_completion_timeout(&sdata->signal,
+ msecs_to_jiffies(1000));
+
+ return !err ? -ETIMEDOUT : 0;
+}
+
+static int stmfts_input_open(struct input_dev *dev)
+{
+ int ret;
+ struct stmfts_data *sdata = input_get_drvdata(dev);
+
+ ret = pm_runtime_get_sync(&sdata->client->dev);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_write_byte(sdata->client, STMFTS_MS_MT_SENSE_ON);
+ if (ret)
+ return ret;
+
+ mutex_lock(&sdata->mutex);
+ sdata->running = true;
+
+ if (sdata->hover_enabled) {
+ ret = i2c_smbus_write_byte(sdata->client,
+ STMFTS_SS_HOVER_SENSE_ON);
+ if (ret)
+ dev_warn(&sdata->client->dev,
+ "failed to enable hover\n");
+ }
+ mutex_unlock(&sdata->mutex);
+
+ if (sdata->use_key) {
+ ret = i2c_smbus_write_byte(sdata->client,
+ STMFTS_MS_KEY_SENSE_ON);
+ if (ret)
+ /* I can still use only the touch screen */
+ dev_warn(&sdata->client->dev,
+ "failed to enable touchkey\n");
+ }
+
+ return 0;
+}
+
+static void stmfts_input_close(struct input_dev *dev)
+{
+ int ret;
+ struct stmfts_data *sdata = input_get_drvdata(dev);
+
+ ret = i2c_smbus_write_byte(sdata->client, STMFTS_MS_MT_SENSE_OFF);
+ if (ret)
+ dev_warn(&sdata->client->dev,
+ "failed to disable touchscreen\n");
+
+ mutex_lock(&sdata->mutex);
+ sdata->running = false;
+
+ if (sdata->hover_enabled) {
+ ret = i2c_smbus_write_byte(sdata->client,
+ STMFTS_SS_HOVER_SENSE_OFF);
+ if (ret)
+ dev_warn(&sdata->client->dev,
+ "failed to disable hover\n");
+ }
+ mutex_unlock(&sdata->mutex);
+
+ if (sdata->use_key) {
+ i2c_smbus_write_byte(sdata->client, STMFTS_MS_KEY_SENSE_OFF);
+ if (ret)
+ dev_warn(&sdata->client->dev,
+ "failed to disable touchkey\n");
+ }
+
+ pm_runtime_put_sync(&sdata->client->dev);
+}
+
+static ssize_t stmfts_sysfs_chip_id(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct stmfts_data *sdata = dev_get_drvdata(dev);
+
+ return sprintf(buf, "0x%x\n", sdata->chip_id);
+}
+
+static ssize_t stmfts_sysfs_chip_version(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct stmfts_data *sdata = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", sdata->chip_ver);
+}
+
+static ssize_t stmfts_sysfs_fw_ver(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct stmfts_data *sdata = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", sdata->fw_ver);
+}
+
+static ssize_t stmfts_sysfs_config_id(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct stmfts_data *sdata = dev_get_drvdata(dev);
+
+ return sprintf(buf, "0x%x\n", sdata->config_id);
+}
+
+static ssize_t stmfts_sysfs_config_version(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct stmfts_data *sdata = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", sdata->config_ver);
+}
+
+static ssize_t stmfts_sysfs_read_status(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct stmfts_data *sdata = dev_get_drvdata(dev);
+ u8 status[4];
+ int ret;
+
+ ret = i2c_smbus_read_i2c_block_data(sdata->client,
+ STMFTS_READ_STATUS, 4, status);
+
+ return sprintf(buf, "0x%x\n", status[0]);
+}
+
+static ssize_t stmfts_sysfs_hover_enable_read(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct stmfts_data *sdata = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", sdata->hover_enabled);
+}
+
+static ssize_t stmfts_sysfs_hover_enable_write(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ unsigned long value;
+ int err;
+ struct stmfts_data *sdata = dev_get_drvdata(dev);
+
+ if (kstrtoul(buf, 0, &value))
+ return -EINVAL;
+
+ mutex_lock(&sdata->mutex);
+
+ if (value & sdata->hover_enabled)
+ goto out;
+
+ if (!sdata->running) {
+ sdata->hover_enabled = !!value;
+ goto out;
+ }
+
+ if (value) {
+ err = i2c_smbus_write_byte(sdata->client,
+ STMFTS_SS_HOVER_SENSE_ON);
+ sdata->hover_enabled = !err;
+ } else {
+ err = i2c_smbus_write_byte(sdata->client,
+ STMFTS_SS_HOVER_SENSE_OFF);
+ sdata->hover_enabled = !!err;
+ }
+
+ if (err)
+ dev_warn(&sdata->client->dev, "failed to %s hover\n",
+ value ? "enable" : "disable");
+out:
+ mutex_unlock(&sdata->mutex);
+
+ return len;
+}
+
+static DEVICE_ATTR(chip_id, 0444, stmfts_sysfs_chip_id, NULL);
+static DEVICE_ATTR(chip_version, 0444, stmfts_sysfs_chip_version, NULL);
+static DEVICE_ATTR(fw_ver, 0444, stmfts_sysfs_fw_ver, NULL);
+static DEVICE_ATTR(config_id, 0444, stmfts_sysfs_config_id, NULL);
+static DEVICE_ATTR(config_version, 0444, stmfts_sysfs_config_version, NULL);
+static DEVICE_ATTR(status, 0444, stmfts_sysfs_read_status, NULL);
+static DEVICE_ATTR(hover_enable, 0644, stmfts_sysfs_hover_enable_read,
+ stmfts_sysfs_hover_enable_write);
+
+static struct attribute *stmfts_sysfs_attrs[] = {
+ &dev_attr_chip_id.attr,
+ &dev_attr_chip_version.attr,
+ &dev_attr_fw_ver.attr,
+ &dev_attr_config_id.attr,
+ &dev_attr_config_version.attr,
+ &dev_attr_status.attr,
+ &dev_attr_hover_enable.attr,
+ NULL
+};
+
+static struct attribute_group stmfts_attribute_group = {
+ .attrs = stmfts_sysfs_attrs
+};
+
+static int stmfts_power_on(struct stmfts_data *sdata)
+{
+ int err;
+ u8 reg[8];
+
+ err = regulator_bulk_enable(ARRAY_SIZE(sdata->regulators),
+ sdata->regulators);
+ if (err)
+ return err;
+
+ /*
+ * the datasheet does not specify the power on time, but considering
+ * that the reset time is < 10ms, I sleep 20ms to be sure
+ */
+ msleep(20);
+
+ err = i2c_smbus_read_i2c_block_data(sdata->client,
+ STMFTS_READ_INFO, 8, reg);
+ if (err < 0)
+ return err;
+ if (err != 8)
+ return -EIO;
+
+ sdata->chip_id = (reg[6] << 8) | reg[7];
+ sdata->chip_ver = reg[0];
+ sdata->fw_ver = (reg[2] << 8) | reg[3];
+ sdata->config_id = reg[4];
+ sdata->config_ver = reg[5];
+
+ reinit_completion(&sdata->signal);
+
+ enable_irq(sdata->client->irq);
+ err = stmfts_write_and_wait(sdata, STMFTS_SYSTEM_RESET);
+ if (err)
+ return err;
+
+ err = stmfts_write_and_wait(sdata, STMFTS_SLEEP_OUT);
+ if (err)
+ return err;
+
+ /* optional tuning */
+ err = stmfts_write_and_wait(sdata, STMFTS_MS_CX_TUNING);
+ if (err)
+ dev_warn(&sdata->client->dev, "failed to perform mutual auto tune\n");
+
+ /* optional tuning */
+ err = stmfts_write_and_wait(sdata, STMFTS_SS_CX_TUNING);
+ if (err)
+ dev_warn(&sdata->client->dev, "failed to perform self auto tune\n");
+
+ err = stmfts_write_and_wait(sdata, STMFTS_FULL_FORCE_CALIBRATION);
+ if (err)
+ return err;
+
+ /* at this point no one is using the touchscreen
+ * and I don't really care about the return value
+ */
+ i2c_smbus_write_byte(sdata->client, STMFTS_SLEEP_IN);
+
+ return 0;
+}
+
+static void stmfts_power_off(void *data)
+{
+ struct stmfts_data *sdata = data;
+
+ disable_irq(sdata->client->irq);
+ regulator_bulk_disable(ARRAY_SIZE(sdata->regulators),
+ sdata->regulators);
+}
+
+/* This function is void because I don't want to prevent using the touch key
+ * only because the LEDs don't get registered
+ */
+static int stmfts_enable_led(struct stmfts_data *sdata)
+{
+ int err;
+
+ /* get the regulator for powering the leds on */
+ sdata->ledvdd = devm_regulator_get(&sdata->client->dev, "ledvdd");
+ if (IS_ERR(sdata->ledvdd))
+ return PTR_ERR(sdata->ledvdd);
+
+ sdata->led_cdev.name = STMFTS_DEV_NAME;
+ sdata->led_cdev.max_brightness = LED_ON;
+ sdata->led_cdev.brightness = LED_OFF;
+ sdata->led_cdev.brightness_set = stmfts_brightness_set;
+ sdata->led_cdev.brightness_get = stmfts_brightness_get;
+
+ err = devm_led_classdev_register(&sdata->client->dev, &sdata->led_cdev);
+ if (err) {
+ devm_regulator_put(sdata->ledvdd);
+ return err;
+ }
+
+ return 0;
+}
+
+static int stmfts_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int err;
+ struct stmfts_data *sdata;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C |
+ I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_I2C_BLOCK))
+ return -ENODEV;
+
+ if (!client->dev.of_node)
+ return -ENOENT;
+
+ sdata = devm_kzalloc(&client->dev, sizeof(*sdata), GFP_KERNEL);
+ if (!sdata)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, sdata);
+
+ mutex_init(&sdata->mutex);
+
+ sdata->regulators[STMFTS_REGULATOR_VDD].supply = "vdd";
+ sdata->regulators[STMFTS_REGULATOR_AVDD].supply = "avdd";
+ err = devm_regulator_bulk_get(&client->dev,
+ ARRAY_SIZE(sdata->regulators), sdata->regulators);
+ if (err)
+ return err;
+
+ err = devm_add_action_or_reset(&client->dev, stmfts_power_off, sdata);
+ if (err)
+ return err;
+
+ sdata->client = client;
+
+ init_completion(&sdata->signal);
+
+ /*
+ * Do not enable interrupts by default.
+ * One possible case when an IRQ can be already rased is e.g. if the
+ * regulator is set as always on and the stmfts device sends an IRQ as
+ * soon as it gets powered, de-synchronizing the power on sequence.
+ * During power on, the device will be reset and all the initialization
+ * IRQ will be resent.
+ */
+ irq_set_status_flags(client->irq, IRQ_NOAUTOEN);
+ err = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL, stmfts_irq_handler,
+ IRQF_ONESHOT | IRQF_TRIGGER_LOW,
+ "stmfts_irq", sdata);
+ if (err)
+ return err;
+
+ dev_info(&client->dev, "initializing ST-Microelectronics FTS...\n");
+ err = stmfts_power_on(sdata);
+ if (err)
+ return err;
+
+ sdata->use_key = of_property_read_bool(client->dev.of_node,
+ "touch-key-connected");
+
+ sdata->input = devm_input_allocate_device(&client->dev);
+ if (!sdata->input)
+ return -ENOMEM;
+
+ sdata->input->name = STMFTS_DEV_NAME;
+ sdata->input->id.bustype = BUS_I2C;
+ sdata->input->open = stmfts_input_open;
+ sdata->input->close = stmfts_input_close;
+
+ touchscreen_parse_properties(sdata->input, true, &sdata->prop);
+
+ input_set_abs_params(sdata->input, ABS_MT_POSITION_X, 0,
+ sdata->prop.max_x, 0, 0);
+ input_set_abs_params(sdata->input, ABS_MT_POSITION_Y, 0,
+ sdata->prop.max_y, 0, 0);
+ input_set_abs_params(sdata->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
+ input_set_abs_params(sdata->input, ABS_MT_TOUCH_MINOR, 0, 255, 0, 0);
+ input_set_abs_params(sdata->input, ABS_MT_ORIENTATION, 0, 255, 0, 0);
+ input_set_abs_params(sdata->input, ABS_MT_PRESSURE, 0, 255, 0, 0);
+ input_set_abs_params(sdata->input, ABS_DISTANCE, 0, 255, 0, 0);
+
+ if (sdata->use_key) {
+ input_set_capability(sdata->input, EV_KEY, KEY_MENU);
+ input_set_capability(sdata->input, EV_KEY, KEY_BACK);
+ }
+
+ err = input_mt_init_slots(sdata->input,
+ STMFTS_MAX_FINGERS, INPUT_MT_DIRECT);
+ if (err)
+ return err;
+
+ input_set_drvdata(sdata->input, sdata);
+ err = input_register_device(sdata->input);
+ if (err)
+ return err;
+
+ if (sdata->use_key) {
+ err = stmfts_enable_led(sdata);
+ if (err) {
+ /* even if the LEDs have failed to be initialized and
+ * used in the driver, I can still use the device even
+ * without LEDs. The ledvdd regulator pointer will be
+ * used as a flag.
+ */
+ dev_warn(&client->dev,
+ "unable to use touchkey leds\n");
+ sdata->ledvdd = NULL;
+ }
+ }
+
+ err = sysfs_create_group(&sdata->client->dev.kobj,
+ &stmfts_attribute_group);
+ if (err)
+ return err;
+
+ pm_runtime_enable(&client->dev);
+
+ return 0;
+}
+
+static int stmfts_remove(struct i2c_client *client)
+{
+ pm_runtime_disable(&client->dev);
+ sysfs_remove_group(&client->dev.kobj, &stmfts_attribute_group);
+
+ return 0;
+}
+
+static int stmfts_runtime_suspend(struct device *dev)
+{
+ int ret;
+ struct stmfts_data *sdata = dev_get_drvdata(dev);
+
+ ret = i2c_smbus_write_byte(sdata->client, STMFTS_SLEEP_IN);
+ if (ret)
+ dev_warn(dev, "failed to suspend device\n");
+
+ return ret;
+}
+
+static int stmfts_runtime_resume(struct device *dev)
+{
+ int ret;
+ struct stmfts_data *sdata = dev_get_drvdata(dev);
+
+ ret = i2c_smbus_write_byte(sdata->client, STMFTS_SLEEP_OUT);
+ if (ret)
+ dev_err(dev, "failed to resume device\n");
+
+ return ret;
+}
+
+static int __maybe_unused stmfts_suspend(struct device *dev)
+{
+ struct stmfts_data *sdata = dev_get_drvdata(dev);
+
+ stmfts_power_off(sdata);
+
+ return 0;
+}
+
+static int __maybe_unused stmfts_resume(struct device *dev)
+{
+ struct stmfts_data *sdata = dev_get_drvdata(dev);
+
+ return stmfts_power_on(sdata);
+}
+
+static const struct dev_pm_ops stmfts_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(stmfts_suspend, stmfts_resume)
+ SET_RUNTIME_PM_OPS(stmfts_runtime_suspend, stmfts_runtime_resume, NULL)
+};
+
+static const struct of_device_id stmfts_of_match[] = {
+ { .compatible = "st,stmfts", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, stmfts_of_match);
+
+static const struct i2c_device_id stmfts_id[] = {
+ { "stmfts", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, stmfts_id);
+
+static struct i2c_driver stmfts_driver = {
+ .driver = {
+ .name = STMFTS_DEV_NAME,
+ .of_match_table = of_match_ptr(stmfts_of_match),
+ .pm = &stmfts_pm_ops,
+ },
+ .probe = stmfts_probe,
+ .remove = stmfts_remove,
+ .id_table = stmfts_id,
+};
+
+module_i2c_driver(stmfts_driver);
+
+MODULE_AUTHOR("Andi Shyti <andi.shyti-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>");
+MODULE_DESCRIPTION("STMicroelectronics FTS Touch Screen");
+MODULE_LICENSE("GPL v2");
--
2.11.0
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v3 1/2] Input: add STMicroelectronics FingerTip touchscreen driver
From: Andi Shyti @ 2017-03-27 13:07 UTC (permalink / raw)
To: Dmitry Torokhov, Rob Herring
Cc: Javier Martinez Canillas, Andrzej Hajda, Chanwoo Choi,
linux-input, devicetree, linux-kernel, Andi Shyti, Andi Shyti
In-Reply-To: <20170327130743.27783-1-andi.shyti@samsung.com>
Add binding for the STMicroelectronics FingerTip (stmfts)
touchscreen driver.
Signed-off-by: Andi Shyti <andi.shyti@samsung.com>
Reviewed-by: Javier Martinez Canillas <javier@osg.samsung.com>
---
.../bindings/input/touchscreen/st,stmfts.txt | 43 ++++++++++++++++++++++
1 file changed, 43 insertions(+)
create mode 100644 Documentation/devicetree/bindings/input/touchscreen/st,stmfts.txt
diff --git a/Documentation/devicetree/bindings/input/touchscreen/st,stmfts.txt b/Documentation/devicetree/bindings/input/touchscreen/st,stmfts.txt
new file mode 100644
index 000000000000..9683595cd0f5
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/touchscreen/st,stmfts.txt
@@ -0,0 +1,43 @@
+* ST-Microelectronics FingerTip touchscreen controller
+
+The ST-Microelectronics FingerTip device provides a basic touchscreen
+functionality. Along with it the user can enable the touchkey which can work as
+a basic HOME and BACK key for phones.
+
+The driver supports also hovering as an absolute single touch event with x, y, z
+coordinates.
+
+Required properties:
+- compatible : must be "st,stmfts"
+- reg : I2C slave address, (e.g. 0x49)
+- interrupt-parent : the phandle to the interrupt controller which provides
+ the interrupt
+- interrupts : interrupt specification
+- avdd-supply : analogic power supply
+- vdd-supply : power supply
+- touchscreen-size-x : see touchscreen.txt
+- touchscreen-size-y : see touchscreen.txt
+
+Optional properties:
+- touch-key-connected : specifies whether the touchkey feature is connected
+- ledvdd-supply : power supply to the touch key leds
+
+Example:
+
+i2c@00000000 {
+
+ /* ... */
+
+ touchscreen@49 {
+ compatible = "st,stmfts";
+ reg = <0x49>;
+ interrupt-parent = <&gpa1>;
+ interrupts = <1 IRQ_TYPE_NONE>;
+ touchscreen-size-x = <1599>;
+ touchscreen-size-y = <2559>;
+ touch-key-connected;
+ avdd-supply = <&ldo30_reg>;
+ vdd-supply = <&ldo31_reg>;
+ ledvdd-supply = <&ldo33_reg>;
+ };
+};
--
2.11.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox