* [PATCH v5 20/23] drivers/fsi/gpio: Add tracepoints for GPIO master
From: Christopher Bostic @ 2017-04-05 2:06 UTC (permalink / raw)
To: robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
linux-I+IVW8TIWO2tmTQ+vhA3Yw, rostedt-nx8X9YLhiw1AfugRpC6u6w,
mingo-H+wXaHxf7aLQT0dZR+AlfA,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
Cc: Jeremy Kerr, joel-U3u1mxZcP9KHXe+LvDLADg,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, andrew-zrmu5oMJ5Fs,
alistair-Y4h6yKqj69EXC2x5gXVKYQ,
benh-XVmvHMARGAS8U2dJNN8I7kB+6BGkLq7r, Chris Bostic
In-Reply-To: <20170405020607.79939-1-cbostic-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org>
From: Jeremy Kerr <jk-mnsaURCQ41sdnm+yROfE0A@public.gmane.org>
Add trace points for key GPIO operations of the GPIO based FSI
master.
Signed-off-by: Jeremy Kerr <jk-mnsaURCQ41sdnm+yROfE0A@public.gmane.org>
Signed-off-by: Chris Bostic <cbostic-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org>
---
drivers/fsi/fsi-master-gpio.c | 9 +++++
include/trace/events/fsi_master_gpio.h | 68 ++++++++++++++++++++++++++++++++++
2 files changed, 77 insertions(+)
create mode 100644 include/trace/events/fsi_master_gpio.h
diff --git a/drivers/fsi/fsi-master-gpio.c b/drivers/fsi/fsi-master-gpio.c
index 9fedfaf..5d9e0b0 100644
--- a/drivers/fsi/fsi-master-gpio.c
+++ b/drivers/fsi/fsi-master-gpio.c
@@ -61,6 +61,9 @@ struct fsi_master_gpio {
struct gpio_desc *gpio_mux; /* Mux control */
};
+#define CREATE_TRACE_POINTS
+#include <trace/events/fsi_master_gpio.h>
+
#define to_fsi_master_gpio(m) container_of(m, struct fsi_master_gpio, master)
struct fsi_gpio_msg {
@@ -128,6 +131,8 @@ static void serial_in(struct fsi_master_gpio *master, struct fsi_gpio_msg *msg,
msg->msg |= ~in_bit & 0x1; /* Data is negative active */
}
msg->bits += num_bits;
+
+ trace_fsi_master_gpio_in(master, num_bits, msg->msg);
}
static void serial_out(struct fsi_master_gpio *master,
@@ -139,6 +144,8 @@ static void serial_out(struct fsi_master_gpio *master,
uint64_t last_bit = ~0;
int next_bit;
+ trace_fsi_master_gpio_out(master, cmd->bits, cmd->msg);
+
if (!cmd->bits) {
dev_warn(master->dev, "trying to output 0 bits\n");
return;
@@ -464,6 +471,8 @@ static int fsi_master_gpio_break(struct fsi_master *_master, int link)
if (link != 0)
return -ENODEV;
+ trace_fsi_master_gpio_break(master);
+
set_sda_output(master, 1);
sda_out(master, 1);
clock_toggle(master, FSI_PRE_BREAK_CLOCKS);
diff --git a/include/trace/events/fsi_master_gpio.h b/include/trace/events/fsi_master_gpio.h
new file mode 100644
index 0000000..11b36c1
--- /dev/null
+++ b/include/trace/events/fsi_master_gpio.h
@@ -0,0 +1,68 @@
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM fsi_master_gpio
+
+#if !defined(_TRACE_FSI_MASTER_GPIO_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_FSI_MASTER_GPIO_H
+
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(fsi_master_gpio_in,
+ TP_PROTO(const struct fsi_master_gpio *master, int bits, uint64_t msg),
+ TP_ARGS(master, bits, msg),
+ TP_STRUCT__entry(
+ __field(int, master_idx)
+ __field(int, bits)
+ __field(uint64_t, msg)
+ ),
+ TP_fast_assign(
+ __entry->master_idx = master->master.idx;
+ __entry->bits = bits;
+ __entry->msg = msg & ((1ull<<bits) - 1);
+ ),
+ TP_printk("fsi-gpio%d => %0*llx[%d]",
+ __entry->master_idx,
+ (__entry->bits + 3) / 4,
+ __entry->msg,
+ __entry->bits
+ )
+);
+
+TRACE_EVENT(fsi_master_gpio_out,
+ TP_PROTO(const struct fsi_master_gpio *master, int bits, uint64_t msg),
+ TP_ARGS(master, bits, msg),
+ TP_STRUCT__entry(
+ __field(int, master_idx)
+ __field(int, bits)
+ __field(uint64_t, msg)
+ ),
+ TP_fast_assign(
+ __entry->master_idx = master->master.idx;
+ __entry->bits = bits;
+ __entry->msg = msg & ((1ull<<bits) - 1);
+ ),
+ TP_printk("fsi-gpio%d <= %0*llx[%d]",
+ __entry->master_idx,
+ (__entry->bits + 3) / 4,
+ __entry->msg,
+ __entry->bits
+ )
+);
+
+TRACE_EVENT(fsi_master_gpio_break,
+ TP_PROTO(const struct fsi_master_gpio *master),
+ TP_ARGS(master),
+ TP_STRUCT__entry(
+ __field(int, master_idx)
+ ),
+ TP_fast_assign(
+ __entry->master_idx = master->master.idx;
+ ),
+ TP_printk("fsi-gpio%d ----break---",
+ __entry->master_idx
+ )
+);
+
+#endif /* _TRACE_FSI_MASTER_GPIO_H */
+
+#include <trace/define_trace.h>
--
1.8.2.2
--
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 v5 21/23] drivers/fsi: Add SCOM FSI client device driver
From: Christopher Bostic @ 2017-04-05 2:06 UTC (permalink / raw)
To: robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
linux-I+IVW8TIWO2tmTQ+vhA3Yw, rostedt-nx8X9YLhiw1AfugRpC6u6w,
mingo-H+wXaHxf7aLQT0dZR+AlfA,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
Cc: Chris Bostic, joel-U3u1mxZcP9KHXe+LvDLADg,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, andrew-zrmu5oMJ5Fs,
alistair-Y4h6yKqj69EXC2x5gXVKYQ,
benh-XVmvHMARGAS8U2dJNN8I7kB+6BGkLq7r, Edward A . James,
Jeremy Kerr
In-Reply-To: <20170405020607.79939-1-cbostic-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org>
From: Chris Bostic <cbostic-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org>
Create a simple SCOM engine device driver that reads and writes
its control registers via an FSI bus.
Includes changes from Edward A. James <eajames-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>.
Signed-off-by: Chris Bostic <cbostic-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org>
Signed-off-by: Joel Stanley <joel-U3u1mxZcP9KHXe+LvDLADg@public.gmane.org>
Signed-off-by: Edward A. James <eajames-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
Signed-off-by: Jeremy Kerr <jk-mnsaURCQ41sdnm+yROfE0A@public.gmane.org>
---
drivers/fsi/Kconfig | 6 ++
drivers/fsi/Makefile | 1 +
drivers/fsi/fsi-scom.c | 263 +++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 270 insertions(+)
create mode 100644 drivers/fsi/fsi-scom.c
diff --git a/drivers/fsi/Kconfig b/drivers/fsi/Kconfig
index 9cf8345..0fa265c 100644
--- a/drivers/fsi/Kconfig
+++ b/drivers/fsi/Kconfig
@@ -18,6 +18,12 @@ config FSI_MASTER_GPIO
---help---
This option enables a FSI master driver using GPIO lines.
+config FSI_SCOM
+ tristate "SCOM FSI client device driver"
+ depends on FSI
+ ---help---
+ This option enables an FSI based SCOM device driver.
+
endif
endmenu
diff --git a/drivers/fsi/Makefile b/drivers/fsi/Makefile
index ed28ac0..3466f08 100644
--- a/drivers/fsi/Makefile
+++ b/drivers/fsi/Makefile
@@ -1,3 +1,4 @@
obj-$(CONFIG_FSI) += fsi-core.o
obj-$(CONFIG_FSI_MASTER_GPIO) += fsi-master-gpio.o
+obj-$(CONFIG_FSI_SCOM) += fsi-scom.o
diff --git a/drivers/fsi/fsi-scom.c b/drivers/fsi/fsi-scom.c
new file mode 100644
index 0000000..98d062f
--- /dev/null
+++ b/drivers/fsi/fsi-scom.c
@@ -0,0 +1,263 @@
+/*
+ * SCOM FSI Client device driver
+ *
+ * Copyright (C) IBM Corporation 2016
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERGCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/fsi.h>
+#include <linux/module.h>
+#include <linux/cdev.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/miscdevice.h>
+#include <linux/list.h>
+#include <linux/idr.h>
+
+#define FSI_ENGID_SCOM 0x5
+
+#define SCOM_FSI2PIB_DELAY 50
+
+/* SCOM engine register set */
+#define SCOM_DATA0_REG 0x00
+#define SCOM_DATA1_REG 0x04
+#define SCOM_CMD_REG 0x08
+#define SCOM_RESET_REG 0x1C
+
+#define SCOM_RESET_CMD 0x80000000
+#define SCOM_WRITE_CMD 0x80000000
+
+struct scom_device {
+ struct list_head link;
+ struct fsi_device *fsi_dev;
+ struct miscdevice mdev;
+ char name[32];
+ int idx;
+};
+
+#define to_scom_dev(x) container_of((x), struct scom_device, mdev)
+
+static struct list_head scom_devices;
+
+static DEFINE_IDA(scom_ida);
+
+static int put_scom(struct scom_device *scom_dev, uint64_t value,
+ uint32_t addr)
+{
+ int rc;
+ uint32_t data;
+
+ data = cpu_to_be32(SCOM_RESET_CMD);
+ rc = fsi_device_write(scom_dev->fsi_dev, SCOM_RESET_REG, &data,
+ sizeof(uint32_t));
+ if (rc)
+ return rc;
+
+ data = cpu_to_be32((value >> 32) & 0xffffffff);
+ rc = fsi_device_write(scom_dev->fsi_dev, SCOM_DATA0_REG, &data,
+ sizeof(uint32_t));
+ if (rc)
+ return rc;
+
+ data = cpu_to_be32(value & 0xffffffff);
+ rc = fsi_device_write(scom_dev->fsi_dev, SCOM_DATA1_REG, &data,
+ sizeof(uint32_t));
+ if (rc)
+ return rc;
+
+ data = cpu_to_be32(SCOM_WRITE_CMD | addr);
+ return fsi_device_write(scom_dev->fsi_dev, SCOM_CMD_REG, &data,
+ sizeof(uint32_t));
+}
+
+static int get_scom(struct scom_device *scom_dev, uint64_t *value,
+ uint32_t addr)
+{
+ uint32_t result, data;
+ int rc;
+
+ *value = 0ULL;
+ data = cpu_to_be32(addr);
+ rc = fsi_device_write(scom_dev->fsi_dev, SCOM_CMD_REG, &data,
+ sizeof(uint32_t));
+ if (rc)
+ return rc;
+
+ rc = fsi_device_read(scom_dev->fsi_dev, SCOM_DATA0_REG, &result,
+ sizeof(uint32_t));
+ if (rc)
+ return rc;
+
+ *value |= (uint64_t)cpu_to_be32(result) << 32;
+ rc = fsi_device_read(scom_dev->fsi_dev, SCOM_DATA1_REG, &result,
+ sizeof(uint32_t));
+ if (rc)
+ return rc;
+
+ *value |= cpu_to_be32(result);
+
+ return 0;
+}
+
+static ssize_t scom_read(struct file *filep, char __user *buf, size_t len,
+ loff_t *offset)
+{
+ int rc;
+ struct miscdevice *mdev =
+ (struct miscdevice *)filep->private_data;
+ struct scom_device *scom = to_scom_dev(mdev);
+ struct device *dev = &scom->fsi_dev->dev;
+ uint64_t val;
+
+ if (len != sizeof(uint64_t))
+ return -EINVAL;
+
+ rc = get_scom(scom, &val, *offset);
+ if (rc) {
+ dev_dbg(dev, "get_scom fail:%d\n", rc);
+ return rc;
+ }
+
+ rc = copy_to_user(buf, &val, len);
+ if (rc)
+ dev_dbg(dev, "copy to user failed:%d\n", rc);
+
+ return rc ? rc : len;
+}
+
+static ssize_t scom_write(struct file *filep, const char __user *buf,
+ size_t len, loff_t *offset)
+{
+ int rc;
+ struct miscdevice *mdev = filep->private_data;
+ struct scom_device *scom = to_scom_dev(mdev);
+ struct device *dev = &scom->fsi_dev->dev;
+ uint64_t val;
+
+ if (len != sizeof(uint64_t))
+ return -EINVAL;
+
+ rc = copy_from_user(&val, buf, len);
+ if (rc) {
+ dev_dbg(dev, "copy from user failed:%d\n", rc);
+ return -EINVAL;
+ }
+
+ rc = put_scom(scom, val, *offset);
+ if (rc) {
+ dev_dbg(dev, "put_scom failed with:%d\n", rc);
+ return rc;
+ }
+
+ return len;
+}
+
+static loff_t scom_llseek(struct file *file, loff_t offset, int whence)
+{
+ switch (whence) {
+ case SEEK_CUR:
+ break;
+ case SEEK_SET:
+ file->f_pos = offset;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return offset;
+}
+
+static const struct file_operations scom_fops = {
+ .owner = THIS_MODULE,
+ .llseek = scom_llseek,
+ .read = scom_read,
+ .write = scom_write,
+};
+
+static int scom_probe(struct device *dev)
+{
+ struct fsi_device *fsi_dev = to_fsi_dev(dev);
+ struct scom_device *scom;
+
+ scom = devm_kzalloc(dev, sizeof(*scom), GFP_KERNEL);
+ if (!scom)
+ return -ENOMEM;
+
+ scom->idx = ida_simple_get(&scom_ida, 1, INT_MAX, GFP_KERNEL);
+ snprintf(scom->name, sizeof(scom->name), "scom%d", scom->idx);
+ scom->fsi_dev = fsi_dev;
+ scom->mdev.minor = MISC_DYNAMIC_MINOR;
+ scom->mdev.fops = &scom_fops;
+ scom->mdev.name = scom->name;
+ scom->mdev.parent = dev;
+ list_add(&scom->link, &scom_devices);
+
+ return misc_register(&scom->mdev);
+}
+
+static int scom_remove(struct device *dev)
+{
+ struct scom_device *scom, *scom_tmp;
+ struct fsi_device *fsi_dev = to_fsi_dev(dev);
+
+ list_for_each_entry_safe(scom, scom_tmp, &scom_devices, link) {
+ if (scom->fsi_dev == fsi_dev) {
+ list_del(&scom->link);
+ ida_simple_remove(&scom_ida, scom->idx);
+ misc_deregister(&scom->mdev);
+ }
+ }
+
+ return 0;
+}
+
+static struct fsi_device_id scom_ids[] = {
+ {
+ .engine_type = FSI_ENGID_SCOM,
+ .version = FSI_VERSION_ANY,
+ },
+ { 0 }
+};
+
+static struct fsi_driver scom_drv = {
+ .id_table = scom_ids,
+ .drv = {
+ .name = "scom",
+ .bus = &fsi_bus_type,
+ .probe = scom_probe,
+ .remove = scom_remove,
+ }
+};
+
+static int scom_init(void)
+{
+ INIT_LIST_HEAD(&scom_devices);
+ return fsi_driver_register(&scom_drv);
+}
+
+static void scom_exit(void)
+{
+ struct list_head *pos;
+ struct scom_device *scom;
+
+ list_for_each(pos, &scom_devices) {
+ scom = list_entry(pos, struct scom_device, link);
+ misc_deregister(&scom->mdev);
+ devm_kfree(&scom->fsi_dev->dev, scom);
+ }
+ fsi_driver_unregister(&scom_drv);
+}
+
+module_init(scom_init);
+module_exit(scom_exit);
+MODULE_LICENSE("GPL");
--
1.8.2.2
--
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 v5 22/23] drivers/fsi: Add hub master support
From: Christopher Bostic @ 2017-04-05 2:06 UTC (permalink / raw)
To: robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
linux-I+IVW8TIWO2tmTQ+vhA3Yw, rostedt-nx8X9YLhiw1AfugRpC6u6w,
mingo-H+wXaHxf7aLQT0dZR+AlfA,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
Cc: Chris Bostic, joel-U3u1mxZcP9KHXe+LvDLADg,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, andrew-zrmu5oMJ5Fs,
alistair-Y4h6yKqj69EXC2x5gXVKYQ,
benh-XVmvHMARGAS8U2dJNN8I7kB+6BGkLq7r, Jeremy Kerr
In-Reply-To: <20170405020607.79939-1-cbostic-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org>
From: Chris Bostic <cbostic-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org>
Add an engine driver to expose a "hub" FSI master - which has a set of
control registers in the engine address space, and uses a chunk of the
slave address space for actual FSI communication.
Additional changes from Jeremy Kerr <jk-mnsaURCQ41sdnm+yROfE0A@public.gmane.org>.
Signed-off-by: Chris Bostic <cbostic-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org>
Signed-off-by: Jeremy Kerr <jk-mnsaURCQ41sdnm+yROfE0A@public.gmane.org>
Signed-off-by: Joel Stanley <joel-U3u1mxZcP9KHXe+LvDLADg@public.gmane.org>
---
drivers/fsi/Kconfig | 9 ++
drivers/fsi/Makefile | 1 +
drivers/fsi/fsi-master-hub.c | 327 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 337 insertions(+)
create mode 100644 drivers/fsi/fsi-master-hub.c
diff --git a/drivers/fsi/Kconfig b/drivers/fsi/Kconfig
index 0fa265c..e1156b4 100644
--- a/drivers/fsi/Kconfig
+++ b/drivers/fsi/Kconfig
@@ -18,6 +18,15 @@ config FSI_MASTER_GPIO
---help---
This option enables a FSI master driver using GPIO lines.
+config FSI_MASTER_HUB
+ tristate "FSI hub master"
+ depends on FSI
+ ---help---
+ This option enables a FSI hub master driver. Hub is a type of FSI
+ master that is connected to the upstream master via a slave. Hubs
+ allow chaining of FSI links to an arbitrary depth. This allows for
+ a high target device fanout.
+
config FSI_SCOM
tristate "SCOM FSI client device driver"
depends on FSI
diff --git a/drivers/fsi/Makefile b/drivers/fsi/Makefile
index 3466f08..65eb99d 100644
--- a/drivers/fsi/Makefile
+++ b/drivers/fsi/Makefile
@@ -1,4 +1,5 @@
obj-$(CONFIG_FSI) += fsi-core.o
+obj-$(CONFIG_FSI_MASTER_HUB) += fsi-master-hub.o
obj-$(CONFIG_FSI_MASTER_GPIO) += fsi-master-gpio.o
obj-$(CONFIG_FSI_SCOM) += fsi-scom.o
diff --git a/drivers/fsi/fsi-master-hub.c b/drivers/fsi/fsi-master-hub.c
new file mode 100644
index 0000000..133b9bf
--- /dev/null
+++ b/drivers/fsi/fsi-master-hub.c
@@ -0,0 +1,327 @@
+/*
+ * FSI hub master driver
+ *
+ * Copyright (C) IBM Corporation 2016
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/fsi.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include "fsi-master.h"
+
+/* Control Registers */
+#define FSI_MMODE 0x0 /* R/W: mode */
+#define FSI_MDLYR 0x4 /* R/W: delay */
+#define FSI_MCRSP 0x8 /* R/W: clock rate */
+#define FSI_MENP0 0x10 /* R/W: enable */
+#define FSI_MLEVP0 0x18 /* R: plug detect */
+#define FSI_MSENP0 0x18 /* S: Set enable */
+#define FSI_MCENP0 0x20 /* C: Clear enable */
+#define FSI_MAEB 0x70 /* R: Error address */
+#define FSI_MVER 0x74 /* R: master version/type */
+#define FSI_MRESP0 0xd0 /* W: Port reset */
+#define FSI_MESRB0 0x1d0 /* R: Master error status */
+#define FSI_MRESB0 0x1d0 /* W: Reset bridge */
+#define FSI_MECTRL 0x2e0 /* W: Error control */
+
+/* MMODE: Mode control */
+#define FSI_MMODE_EIP 0x80000000 /* Enable interrupt polling */
+#define FSI_MMODE_ECRC 0x40000000 /* Enable error recovery */
+#define FSI_MMODE_EPC 0x10000000 /* Enable parity checking */
+#define FSI_MMODE_P8_TO_LSB 0x00000010 /* Timeout value LSB */
+ /* MSB=1, LSB=0 is 0.8 ms */
+ /* MSB=0, LSB=1 is 0.9 ms */
+#define FSI_MMODE_CRS0SHFT 18 /* Clk rate selection 0 shift */
+#define FSI_MMODE_CRS0MASK 0x3ff /* Clk rate selection 0 mask */
+#define FSI_MMODE_CRS1SHFT 8 /* Clk rate selection 1 shift */
+#define FSI_MMODE_CRS1MASK 0x3ff /* Clk rate selection 1 mask */
+
+/* MRESB: Reset brindge */
+#define FSI_MRESB_RST_GEN 0x80000000 /* General reset */
+#define FSI_MRESB_RST_ERR 0x40000000 /* Error Reset */
+
+/* MRESB: Reset port */
+#define FSI_MRESP_RST_ALL_MASTER 0x20000000 /* Reset all FSI masters */
+#define FSI_MRESP_RST_ALL_LINK 0x10000000 /* Reset all FSI port contr. */
+#define FSI_MRESP_RST_MCR 0x08000000 /* Reset FSI master reg. */
+#define FSI_MRESP_RST_PYE 0x04000000 /* Reset FSI parity error */
+#define FSI_MRESP_RST_ALL 0xfc000000 /* Reset any error */
+
+/* MECTRL: Error control */
+#define FSI_MECTRL_EOAE 0x8000 /* Enable machine check when */
+ /* master 0 in error */
+#define FSI_MECTRL_P8_AUTO_TERM 0x4000 /* Auto terminate */
+
+#define FSI_ENGID_HUB_MASTER 0x1c
+#define FSI_HUB_LINK_OFFSET 0x80000
+#define FSI_HUB_LINK_SIZE 0x80000
+#define FSI_HUB_MASTER_MAX_LINKS 8
+
+#define FSI_LINK_ENABLE_SETUP_TIME 10 /* in mS */
+
+/*
+ * FSI hub master support
+ *
+ * A hub master increases the number of potential target devices that the
+ * primary FSI master can access. For each link a primary master supports,
+ * each of those links can in turn be chained to a hub master with multiple
+ * links of its own.
+ *
+ * The hub is controlled by a set of control registers exposed as a regular fsi
+ * device (the hub->upstream device), and provides access to the downstream FSI
+ * bus as through an address range on the slave itself (->addr and ->size).
+ *
+ * [This differs from "cascaded" masters, which expose the entire downstream
+ * bus entirely through the fsi device address range, and so have a smaller
+ * accessible address space.]
+ */
+struct fsi_master_hub {
+ struct fsi_master master;
+ struct fsi_device *upstream;
+ uint32_t addr, size; /* slave-relative addr of */
+ /* master address space */
+};
+
+#define to_fsi_master_hub(m) container_of(m, struct fsi_master_hub, master)
+
+static int hub_master_read(struct fsi_master *master, int link,
+ uint8_t id, uint32_t addr, void *val, size_t size)
+{
+ struct fsi_master_hub *hub = to_fsi_master_hub(master);
+
+ if (id != 0)
+ return -EINVAL;
+
+ addr += hub->addr + (link * FSI_HUB_LINK_SIZE);
+ return fsi_slave_read(hub->upstream->slave, addr, val, size);
+}
+
+static int hub_master_write(struct fsi_master *master, int link,
+ uint8_t id, uint32_t addr, const void *val, size_t size)
+{
+ struct fsi_master_hub *hub = to_fsi_master_hub(master);
+
+ if (id != 0)
+ return -EINVAL;
+
+ addr += hub->addr + (link * FSI_HUB_LINK_SIZE);
+ return fsi_slave_write(hub->upstream->slave, addr, val, size);
+}
+
+static int hub_master_break(struct fsi_master *master, int link)
+{
+ uint32_t addr, cmd;
+
+ addr = 0x4;
+ cmd = cpu_to_be32(0xc0de0000);
+
+ return hub_master_write(master, link, 0, addr, &cmd, sizeof(cmd));
+}
+
+static int hub_master_link_enable(struct fsi_master *master, int link)
+{
+ struct fsi_master_hub *hub = to_fsi_master_hub(master);
+ int idx, bit;
+ __be32 reg;
+ int rc;
+
+ idx = link / 32;
+ bit = link % 32;
+
+ reg = cpu_to_be32(0x80000000 >> bit);
+
+ rc = fsi_device_write(hub->upstream, FSI_MSENP0 + (4 * idx), ®, 4);
+
+ mdelay(FSI_LINK_ENABLE_SETUP_TIME);
+
+ fsi_device_read(hub->upstream, FSI_MENP0 + (4 * idx), ®, 4);
+
+ return rc;
+}
+
+static void hub_master_release(struct device *dev)
+{
+ struct fsi_master_hub *hub = to_fsi_master_hub(dev_to_fsi_master(dev));
+
+ kfree(hub);
+}
+
+/* mmode encoders */
+static inline u32 fsi_mmode_crs0(u32 x)
+{
+ return (x & FSI_MMODE_CRS0MASK) << FSI_MMODE_CRS0SHFT;
+}
+
+static inline u32 fsi_mmode_crs1(u32 x)
+{
+ return (x & FSI_MMODE_CRS1MASK) << FSI_MMODE_CRS1SHFT;
+}
+
+static int hub_master_init(struct fsi_master_hub *hub)
+{
+ struct fsi_device *dev = hub->upstream;
+ __be32 reg;
+ int rc;
+
+ reg = cpu_to_be32(FSI_MRESP_RST_ALL_MASTER | FSI_MRESP_RST_ALL_LINK
+ | FSI_MRESP_RST_MCR | FSI_MRESP_RST_PYE);
+ rc = fsi_device_write(dev, FSI_MRESP0, ®, sizeof(reg));
+ if (rc)
+ return rc;
+
+ /* Initialize the MFSI (hub master) engine */
+ reg = cpu_to_be32(FSI_MRESP_RST_ALL_MASTER | FSI_MRESP_RST_ALL_LINK
+ | FSI_MRESP_RST_MCR | FSI_MRESP_RST_PYE);
+ rc = fsi_device_write(dev, FSI_MRESP0, ®, sizeof(reg));
+ if (rc)
+ return rc;
+
+ reg = cpu_to_be32(FSI_MECTRL_EOAE | FSI_MECTRL_P8_AUTO_TERM);
+ rc = fsi_device_write(dev, FSI_MECTRL, ®, sizeof(reg));
+ if (rc)
+ return rc;
+
+ reg = cpu_to_be32(FSI_MMODE_EIP | FSI_MMODE_ECRC | FSI_MMODE_EPC
+ | fsi_mmode_crs0(1) | fsi_mmode_crs1(1)
+ | FSI_MMODE_P8_TO_LSB);
+ rc = fsi_device_write(dev, FSI_MMODE, ®, sizeof(reg));
+ if (rc)
+ return rc;
+
+ reg = cpu_to_be32(0xffff0000);
+ rc = fsi_device_write(dev, FSI_MDLYR, ®, sizeof(reg));
+ if (rc)
+ return rc;
+
+ reg = ~0;
+ rc = fsi_device_write(dev, FSI_MSENP0, ®, sizeof(reg));
+ if (rc)
+ return rc;
+
+ /* Leave enabled long enough for master logic to set up */
+ mdelay(FSI_LINK_ENABLE_SETUP_TIME);
+
+ rc = fsi_device_write(dev, FSI_MCENP0, ®, sizeof(reg));
+ if (rc)
+ return rc;
+
+ rc = fsi_device_read(dev, FSI_MAEB, ®, sizeof(reg));
+ if (rc)
+ return rc;
+
+ reg = cpu_to_be32(FSI_MRESP_RST_ALL_MASTER | FSI_MRESP_RST_ALL_LINK);
+ rc = fsi_device_write(dev, FSI_MRESP0, ®, sizeof(reg));
+ if (rc)
+ return rc;
+
+ rc = fsi_device_read(dev, FSI_MLEVP0, ®, sizeof(reg));
+ if (rc)
+ return rc;
+
+ /* Reset the master bridge */
+ reg = cpu_to_be32(FSI_MRESB_RST_GEN);
+ rc = fsi_device_write(dev, FSI_MRESB0, ®, sizeof(reg));
+ if (rc)
+ return rc;
+
+ reg = cpu_to_be32(FSI_MRESB_RST_ERR);
+ return fsi_device_write(dev, FSI_MRESB0, ®, sizeof(reg));
+}
+
+static int hub_master_probe(struct device *dev)
+{
+ struct fsi_device *fsi_dev = to_fsi_dev(dev);
+ struct fsi_master_hub *hub;
+ uint32_t reg, links;
+ __be32 __reg;
+ int rc;
+
+ rc = fsi_device_read(fsi_dev, FSI_MVER, &__reg, sizeof(__reg));
+ if (rc)
+ return rc;
+
+ reg = be32_to_cpu(__reg);
+ links = (reg >> 8) & 0xff;
+ dev_info(dev, "hub version %08x (%d links)\n", reg, links);
+
+ rc = fsi_slave_claim_range(fsi_dev->slave, FSI_HUB_LINK_OFFSET,
+ FSI_HUB_LINK_SIZE * links);
+ if (rc) {
+ dev_err(dev, "can't claim slave address range for links");
+ return rc;
+ }
+
+ hub = kzalloc(sizeof(*hub), GFP_KERNEL);
+ if (!hub) {
+ rc = -ENOMEM;
+ goto err_release;
+ }
+
+ hub->addr = FSI_HUB_LINK_OFFSET;
+ hub->size = FSI_HUB_LINK_SIZE * links;
+ hub->upstream = fsi_dev;
+
+ hub->master.dev.parent = dev;
+ hub->master.dev.release = hub_master_release;
+
+ hub->master.n_links = links;
+ hub->master.read = hub_master_read;
+ hub->master.write = hub_master_write;
+ hub->master.send_break = hub_master_break;
+ hub->master.link_enable = hub_master_link_enable;
+
+ dev_set_drvdata(dev, hub);
+
+ hub_master_init(hub);
+
+ rc = fsi_master_register(&hub->master);
+ if (!rc)
+ return 0;
+
+ kfree(hub);
+err_release:
+ fsi_slave_release_range(fsi_dev->slave, FSI_HUB_LINK_OFFSET,
+ FSI_HUB_LINK_SIZE * links);
+ return rc;
+}
+
+static int hub_master_remove(struct device *dev)
+{
+ struct fsi_master_hub *hub = dev_get_drvdata(dev);
+
+ fsi_master_unregister(&hub->master);
+ fsi_slave_release_range(hub->upstream->slave, hub->addr, hub->size);
+ return 0;
+}
+
+static struct fsi_device_id hub_master_ids[] = {
+ {
+ .engine_type = FSI_ENGID_HUB_MASTER,
+ .version = FSI_VERSION_ANY,
+ },
+ { 0 }
+};
+
+static struct fsi_driver hub_master_driver = {
+ .id_table = hub_master_ids,
+ .drv = {
+ .name = "fsi-master-hub",
+ .bus = &fsi_bus_type,
+ .probe = hub_master_probe,
+ .remove = hub_master_remove,
+ }
+};
+
+module_fsi_driver(hub_master_driver);
+MODULE_LICENSE("GPL");
--
1.8.2.2
--
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 v5 23/23] drivers/fsi: Use asynchronous slave mode
From: Christopher Bostic @ 2017-04-05 2:06 UTC (permalink / raw)
To: robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
linux-I+IVW8TIWO2tmTQ+vhA3Yw, rostedt-nx8X9YLhiw1AfugRpC6u6w,
mingo-H+wXaHxf7aLQT0dZR+AlfA,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
Cc: Jeremy Kerr, joel-U3u1mxZcP9KHXe+LvDLADg,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, andrew-zrmu5oMJ5Fs,
alistair-Y4h6yKqj69EXC2x5gXVKYQ,
benh-XVmvHMARGAS8U2dJNN8I7kB+6BGkLq7r, Chris Bostic
In-Reply-To: <20170405020607.79939-1-cbostic-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org>
From: Jeremy Kerr <jk-mnsaURCQ41sdnm+yROfE0A@public.gmane.org>
For slaves that are behind a software-clocked master, we want FSI CFAMs
to run asynchronously to the FSI clock, so set up our slaves to be in
async mode.
Signed-off-by: Jeremy Kerr <jk-mnsaURCQ41sdnm+yROfE0A@public.gmane.org>
Signed-off-by: Chris Bostic <cbostic-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org>
---
drivers/fsi/fsi-core.c | 22 +++++++++++++++++++++-
drivers/fsi/fsi-master-gpio.c | 1 +
drivers/fsi/fsi-master.h | 2 ++
3 files changed, 24 insertions(+), 1 deletion(-)
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 747d0e3..a6ed34f 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -47,6 +47,7 @@
#define FSI_SMODE 0x0 /* R/W: Mode register */
#define FSI_SISC 0x8 /* R/W: Interrupt condition */
#define FSI_SSTAT 0x14 /* R : Slave status */
+#define FSI_LLMODE 0x100 /* R/W: Link layer mode register */
/*
* SMODE fields
@@ -62,6 +63,11 @@
#define FSI_SMODE_LBCRR_SHIFT 8 /* Clk ratio shift */
#define FSI_SMODE_LBCRR_MASK 0xf /* Clk ratio mask */
+/*
+ * LLMODE fields
+ */
+#define FSI_LLMODE_ASYNC 0x1
+
#define FSI_SLAVE_SIZE_23b 0x800000
static DEFINE_IDA(master_ida);
@@ -560,8 +566,8 @@ static void fsi_slave_release(struct device *dev)
static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
{
+ uint32_t chip_id, llmode;
struct fsi_slave *slave;
- uint32_t chip_id;
uint8_t crc;
int rc;
@@ -597,6 +603,20 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
return -ENODEV;
}
+ /* If we're behind a master that doesn't provide a self-running bus
+ * clock, put the slave into async mode
+ */
+ if (master->flags & FSI_MASTER_FLAG_SWCLOCK) {
+ llmode = cpu_to_be32(FSI_LLMODE_ASYNC);
+ rc = fsi_master_write(master, link, id,
+ FSI_SLAVE_BASE + FSI_LLMODE,
+ &llmode, sizeof(llmode));
+ if (rc)
+ dev_warn(&master->dev,
+ "can't set llmode on slave:%02x:%02x %d\n",
+ link, id, rc);
+ }
+
/* We can communicate with a slave; create the slave device and
* register.
*/
diff --git a/drivers/fsi/fsi-master-gpio.c b/drivers/fsi/fsi-master-gpio.c
index 5d9e0b0..d5cce88 100644
--- a/drivers/fsi/fsi-master-gpio.c
+++ b/drivers/fsi/fsi-master-gpio.c
@@ -569,6 +569,7 @@ static int fsi_master_gpio_probe(struct platform_device *pdev)
master->gpio_mux = gpio;
master->master.n_links = 1;
+ master->master.flags = FSI_MASTER_FLAG_SWCLOCK;
master->master.read = fsi_master_gpio_read;
master->master.write = fsi_master_gpio_write;
master->master.term = fsi_master_gpio_term;
diff --git a/drivers/fsi/fsi-master.h b/drivers/fsi/fsi-master.h
index d6a4885..fd39924 100644
--- a/drivers/fsi/fsi-master.h
+++ b/drivers/fsi/fsi-master.h
@@ -19,6 +19,8 @@
#include <linux/device.h>
+#define FSI_MASTER_FLAG_SWCLOCK 0x1
+
struct fsi_master {
struct device dev;
int idx;
--
1.8.2.2
--
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
* Re: [PATCH v4 4/6] arm64: dts: rockchip: add core dtsi file for RK3328 SoCs
From: Elaine Zhang @ 2017-04-05 2:07 UTC (permalink / raw)
To: Heiko Stuebner, cl
Cc: robh+dt, mark.rutland, zhengxing, andy.yan, jay.xu, matthias.bgg,
paweljarosz3691, devicetree, linux-arm-kernel, linux-rockchip,
linux-kernel, wsa, linux-i2c, jic23, knaack.h, lars, pmeerw, wxt,
david.wu, linux-iio, shawn.lin, akpm, dianders, yamada.masahiro,
catalin.marinas, will.deacon, afaerber, shawnguo, khilman, arnd,
fabio.estevam, kever.yang, tony.xie
In-Reply-To: <6636047.jL6XHNkknt@phil>
On 04/05/2017 12:04 AM, Heiko Stuebner wrote:
> Am Montag, 27. März 2017, 17:40:48 CEST schrieb cl@rock-chips.com:
>> From: Liang Chen <cl@rock-chips.com>
>>
>> This patch adds core dtsi file for Rockchip RK3328 SoCs.
>>
>> Signed-off-by: Liang Chen <cl@rock-chips.com>
>
> applied for 4.12, with the following list of changes:
>
> - reorder some properties to bring them in alphabetical order
> - dropped the status-disabled from the power-controller
> power-domain control is a quite essential part of the system, so if
> boards really want to disable them, they should do it in their board file
> Having power-domains on all the time, is also our default in all other
> devicetrees.
> - removed #dma-cells from spi0 -> this is not a dma controller
> - reword the cru assigned-clocks comment a bit
> - fixed sdmmc1_bus4 pins, as indicated by Shawn and after looking up the
> correct pins in the manual
>
>
> And a final question, are you sure about SCLK_PDM becoming a child of the
> APLL in your cru assigned-clocks, as the APLL will vary later on with cpufreq
> active?
>
the NPLL will vary later on with cpufreq active.
The NPLL is better than APLL, so NPLL is for clk_core,and apll is for pdm.
please see the TRM in CRU:
1.4 Function Description
/........./
To maximize the flexibility, some of clocks can select divider source
from 5 PLLs. (Note: It’s
recommended to use NEW PLL instead of ARM PLL as arm clock source,
because NEW PLL is
near to ARM. And it’s jitter is better than ARM PLL).
>
> Heiko
>
>
>
^ permalink raw reply
* [PATCH v4 0/1] Fix memory leak in of_pci_get_host_bridge_resources
From: Jeffy Chen @ 2017-04-05 2:15 UTC (permalink / raw)
To: linux-pci
Cc: robh, toshi.kani, shawn.lin, briannorris, linux-kernel, dianders,
bhelgaas, dtor, Jeffy Chen, devicetree, Rob Herring, Frank Rowand
In of_pci_get_host_bridge_resources, we alloced some struct resource
variables, and they would cause memory leak since no where to free them.
Changes in v4:
Use IORESOURCE_AUTO to mark struct resources which could be copied.
Changes in v3:
Add some comments.
Changes in v2:
Don't change the resource_list_create_entry's behavior.
Jeffy Chen (1):
of/pci: Fix memory leak in of_pci_get_host_bridge_resources
drivers/of/of_pci.c | 51 ++++++++++++++++++---------------------------------
drivers/pci/bus.c | 7 ++++++-
2 files changed, 24 insertions(+), 34 deletions(-)
--
2.1.4
^ permalink raw reply
* [PATCH v4] of/pci: Fix memory leak in of_pci_get_host_bridge_resources
From: Jeffy Chen @ 2017-04-05 2:15 UTC (permalink / raw)
To: linux-pci-u79uwXL29TY76Z2rM5mHXA
Cc: robh-DgEjT+Ai2ygdnm+yROfE0A, toshi.kani-ZPxbGqLxI0U,
shawn.lin-TNX95d0MmH7DzftRWevZcw,
briannorris-F7+t8E8rja9g9hUCZPvPmw,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
dianders-F7+t8E8rja9g9hUCZPvPmw, bhelgaas-hpIqsD4AKlfQT0dZR+AlfA,
dtor-F7+t8E8rja9g9hUCZPvPmw, Jeffy Chen,
devicetree-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Frank Rowand
In-Reply-To: <1491358521-17834-1-git-send-email-jeffy.chen-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
Currently we only free the allocated resource struct when error.
This would cause memory leak after pci_free_resource_list.
Signed-off-by: Jeffy Chen <jeffy.chen-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
---
Changes in v4:
Use IORESOURCE_AUTO to mark struct resources which could be copied.
Changes in v3:
Add some comments.
Changes in v2:
Don't change the resource_list_create_entry's behavior.
drivers/of/of_pci.c | 51 ++++++++++++++++++---------------------------------
drivers/pci/bus.c | 7 ++++++-
2 files changed, 24 insertions(+), 34 deletions(-)
diff --git a/drivers/of/of_pci.c b/drivers/of/of_pci.c
index 0ee42c3..1ecbd79 100644
--- a/drivers/of/of_pci.c
+++ b/drivers/of/of_pci.c
@@ -189,9 +189,7 @@ int of_pci_get_host_bridge_resources(struct device_node *dev,
unsigned char busno, unsigned char bus_max,
struct list_head *resources, resource_size_t *io_base)
{
- struct resource_entry *window;
- struct resource *res;
- struct resource *bus_range;
+ struct resource res;
struct of_pci_range range;
struct of_pci_range_parser parser;
char range_type[4];
@@ -200,24 +198,21 @@ int of_pci_get_host_bridge_resources(struct device_node *dev,
if (io_base)
*io_base = (resource_size_t)OF_BAD_ADDR;
- bus_range = kzalloc(sizeof(*bus_range), GFP_KERNEL);
- if (!bus_range)
- return -ENOMEM;
-
pr_info("host bridge %s ranges:\n", dev->full_name);
- err = of_pci_parse_bus_range(dev, bus_range);
+ err = of_pci_parse_bus_range(dev, &res);
if (err) {
- bus_range->start = busno;
- bus_range->end = bus_max;
- bus_range->flags = IORESOURCE_BUS;
- pr_info(" No bus range found for %s, using %pR\n",
- dev->full_name, bus_range);
+ res.start = busno;
+ res.end = bus_max;
+ res.flags = IORESOURCE_BUS;
+ pr_info(" No bus range found for %s\n", dev->full_name);
} else {
- if (bus_range->end > bus_range->start + bus_max)
- bus_range->end = bus_range->start + bus_max;
+ if (res.end > res.start + bus_max)
+ res.end = res.start + bus_max;
}
- pci_add_resource(resources, bus_range);
+
+ res.flags |= IORESOURCE_AUTO;
+ pci_add_resource(resources, res);
/* Check for ranges property */
err = of_pci_range_parser_init(&parser, dev);
@@ -244,24 +239,16 @@ int of_pci_get_host_bridge_resources(struct device_node *dev,
if (range.cpu_addr == OF_BAD_ADDR || range.size == 0)
continue;
- res = kzalloc(sizeof(struct resource), GFP_KERNEL);
- if (!res) {
- err = -ENOMEM;
- goto parse_failed;
- }
-
- err = of_pci_range_to_resource(&range, dev, res);
- if (err) {
- kfree(res);
+ err = of_pci_range_to_resource(&range, dev, &res);
+ if (err)
continue;
- }
- if (resource_type(res) == IORESOURCE_IO) {
+ if (resource_type(&res) == IORESOURCE_IO) {
if (!io_base) {
pr_err("I/O range found for %s. Please provide an io_base pointer to save CPU base address\n",
dev->full_name);
err = -EINVAL;
- goto conversion_failed;
+ goto parse_failed;
}
if (*io_base != (resource_size_t)OF_BAD_ADDR)
pr_warn("More than one I/O resource converted for %s. CPU base address for old range lost!\n",
@@ -269,16 +256,14 @@ int of_pci_get_host_bridge_resources(struct device_node *dev,
*io_base = range.cpu_addr;
}
- pci_add_resource_offset(resources, res, res->start - range.pci_addr);
+ res.flags |= IORESOURCE_AUTO;
+ pci_add_resource_offset(resources, res,
+ res->start - range.pci_addr);
}
return 0;
-conversion_failed:
- kfree(res);
parse_failed:
- resource_list_for_each_entry(window, resources)
- kfree(window->res);
pci_free_resource_list(resources);
return err;
}
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
index bc56cf1..5f32bfd 100644
--- a/drivers/pci/bus.c
+++ b/drivers/pci/bus.c
@@ -22,12 +22,17 @@ void pci_add_resource_offset(struct list_head *resources, struct resource *res,
{
struct resource_entry *entry;
- entry = resource_list_create_entry(res, 0);
+ entry = resource_list_create_entry(NULL, 0);
if (!entry) {
printk(KERN_ERR "PCI: can't add host bridge window %pR\n", res);
return;
}
+ if (res->flags & IORESOURCE_AUTO)
+ *entry->res = *res;
+ else
+ entry->res = res
+
entry->offset = offset;
resource_list_add_tail(entry, resources);
}
--
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
* Re: [PATCH 2/2] ARM: dts: imx7: add USDHC NAND clock to SDHC instances
From: Fabio Estevam @ 2017-04-05 2:15 UTC (permalink / raw)
To: Stefan Agner
Cc: Dong Aisheng, Shawn Guo, Sascha Hauer, Stephen Boyd, Dong Aisheng,
Fabio Estevam, robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org,
Mark Rutland,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-clk-u79uwXL29TY76Z2rM5mHXA, linux-kernel
In-Reply-To: <CAOMZO5CprxqnqRXMFJLBAgYAZGGnXNs3ZciyVz_mSLRnnSQQRQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
On Sun, Apr 2, 2017 at 2:02 PM, Fabio Estevam <festevam-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
> On Sat, Apr 1, 2017 at 1:15 AM, Stefan Agner <stefan-XLVq0VzYD2Y@public.gmane.org> wrote:
>
>> IMX7D_IPG_ROOT_CLK is currently not a valid clock in upstream... So we
>> would have to add it to the clock driver first.
>>
>> I guess we could/should add it anyway at one point? But probably also as
>> init on, just to make sure Linux does not disable it since it is
>> currently used by several IPs implicitly.
>
> Yes, I made a previous attempt do add IMX7D_IPG_ROOT_CLK and it did
> not work as I did not put it in the init_on clock list.
>
> Will submit a new patch adding it to init_on, thanks.
I thought that adding IMX7D_IPG_ROOT_CLK would do the trick, but the
patch below also causes the kernel to not boot:
--- a/drivers/clk/imx/clk-imx7d.c
+++ b/drivers/clk/imx/clk-imx7d.c
@@ -386,7 +386,7 @@ static int const clks_init_on[] __initconst = {
IMX7D_PLL_SYS_MAIN_480M_CLK, IMX7D_NAND_USDHC_BUS_ROOT_CLK,
IMX7D_DRAM_PHYM_ROOT_CLK, IMX7D_DRAM_ROOT_CLK,
IMX7D_DRAM_PHYM_ALT_ROOT_CLK, IMX7D_DRAM_ALT_ROOT_CLK,
- IMX7D_AHB_CHANNEL_ROOT_CLK,
+ IMX7D_AHB_CHANNEL_ROOT_CLK, IMX7D_IPG_ROOT_CLK,
};
static struct clk_onecell_data clk_data;
@@ -788,7 +788,7 @@ static void __init imx7d_clocks_init(struct
device_node *ccm_node)
clks[IMX7D_WRCLK_ROOT_DIV] =
imx_clk_divider2("wrclk_post_div", "wrclk_pre_div", base + 0xbd00, 0,
6);
clks[IMX7D_CLKO1_ROOT_DIV] =
imx_clk_divider2("clko1_post_div", "clko1_pre_div", base + 0xbd80, 0,
6);
clks[IMX7D_CLKO2_ROOT_DIV] =
imx_clk_divider2("clko2_post_div", "clko2_pre_div", base + 0xbe00, 0,
6);
-
+ clks[IMX7D_IPG_ROOT_CLK] = imx_clk_divider2("ipg_root_clk",
"ahb_root_clk", base + 0x9080, 0, 2);
clks[IMX7D_ARM_A7_ROOT_CLK] = imx_clk_gate4("arm_a7_root_clk",
"arm_a7_div", base + 0x4000, 0);
clks[IMX7D_ARM_M4_ROOT_CLK] = imx_clk_gate4("arm_m4_root_clk",
"arm_m4_div", base + 0x4010, 0);
clks[IMX7D_ARM_M0_ROOT_CLK] = imx_clk_gate4("arm_m0_root_clk",
"arm_m0_div", base + 0x4020, 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 v2 2/2] of/pci: Fix memory leak in of_pci_get_host_bridge_resources
From: jeffy @ 2017-04-05 2:22 UTC (permalink / raw)
To: Bjorn Helgaas, Dmitry Torokhov
Cc: Rob Herring, linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
toshi.kani-ZPxbGqLxI0U, Shawn Lin, Brian Norris, Doug Anderson,
Frank Rowand, devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <CAErSpo6CZ1hoHyzEnWBi7VDr51bLurF4-4t=-v4jH27kOpzYrg-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
Hi Bjorn,
On 04/05/2017 03:18 AM, Bjorn Helgaas wrote:
> On Thu, Mar 23, 2017 at 5:58 PM, Dmitry Torokhov <dtor-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org> wrote:
>> On Thu, Mar 23, 2017 at 3:07 PM, Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org> wrote:
>>> On Thu, Mar 23, 2017 at 3:12 AM, Jeffy Chen <jeffy.chen-TNX95d0MmH7DzftRWevZcw@public.gmane.org> wrote:
>>>> Currently we only free the allocated resource struct when error.
>>>> This would cause memory leak after pci_free_resource_list.
>>>>
>>>> Signed-off-by: Jeffy Chen <jeffy.chen-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
>>>> ---
>>>>
>>>> Changes in v2:
>>>> Don't change the resource_list_create_entry's behavior.
>>>>
>>>> drivers/of/of_pci.c | 57 +++++++++++++++++++++++------------------------------
>>>> 1 file changed, 25 insertions(+), 32 deletions(-)
>>>>
>>>> diff --git a/drivers/of/of_pci.c b/drivers/of/of_pci.c
>>>> index 0ee42c3..a0ec246 100644
>>>> --- a/drivers/of/of_pci.c
>>>> +++ b/drivers/of/of_pci.c
>>>> @@ -190,8 +190,7 @@ int of_pci_get_host_bridge_resources(struct device_node *dev,
>>>> struct list_head *resources, resource_size_t *io_base)
>>>> {
>>>> struct resource_entry *window;
>>>> - struct resource *res;
>>>> - struct resource *bus_range;
>>>> + struct resource res;
>>>> struct of_pci_range range;
>>>> struct of_pci_range_parser parser;
>>>> char range_type[4];
>>>> @@ -200,24 +199,24 @@ int of_pci_get_host_bridge_resources(struct device_node *dev,
>>>> if (io_base)
>>>> *io_base = (resource_size_t)OF_BAD_ADDR;
>>>>
>>>> - bus_range = kzalloc(sizeof(*bus_range), GFP_KERNEL);
>>>> - if (!bus_range)
>>>> - return -ENOMEM;
>>>> -
>>>> pr_info("host bridge %s ranges:\n", dev->full_name);
>>>>
>>>> - err = of_pci_parse_bus_range(dev, bus_range);
>>>> + err = of_pci_parse_bus_range(dev, &res);
>>>> if (err) {
>>>> - bus_range->start = busno;
>>>> - bus_range->end = bus_max;
>>>> - bus_range->flags = IORESOURCE_BUS;
>>>> - pr_info(" No bus range found for %s, using %pR\n",
>>>> - dev->full_name, bus_range);
>>>> + res.start = busno;
>>>> + res.end = bus_max;
>>>> + res.flags = IORESOURCE_BUS;
>>>> + pr_info(" No bus range found for %s\n", dev->full_name);
>>>> } else {
>>>> - if (bus_range->end > bus_range->start + bus_max)
>>>> - bus_range->end = bus_range->start + bus_max;
>>>> + if (res.end > res.start + bus_max)
>>>> + res.end = res.start + bus_max;
>>>> + }
>>>> + window = pci_add_resource(resources, NULL);
>>>> + if (!window) {
>>>> + err = -ENOMEM;
>>>> + goto parse_failed;
>>>> }
>>>> - pci_add_resource(resources, bus_range);
>>>> + *window->res = res;
>>>
>>> Well, now this seems racy. You add a blank resource to the list first
>>> and then fill it in.
>>>
>>
>> Huh? There is absolutely no guarantees for concurrent access here.
>> pcI_add_resource_offset() first adds a resource and then modifies
>> offset. Here we add an empty resource and then fill it in.
>
> I don't really like this pattern either. Even if there's no actual
> racy behavior, it takes more analysis than necessary to figure that
> out.
>
> pci_add_resource_offset() allocates a resource list entry, sets the
> offset, then adds it to the list. It doesn't update a resource entry
> that might be visible to anybody else. Here we do update a resource
> that is already visible to others because it's already on the list.
i was following ./drivers/pnp/resource.c, but i'm agree this is not a
good way.
i'll upload a new version to fix this in another way. more ideas:
1/ pass a struct device to of_pci_get_host_bridge_resources and use
devm_kzalloc
2/ add a new type of flags(or reuse IORESOURCE_AUTO) to tell
pci_free_resource_list to kfree them)
3/ add new helpers of of_pci_add_resource[_offset] to alloc empty res,
fill it, add to list.
>
> Bjorn
>
> BTW, please CC linux-pci on the entire series so it's easier to
> review. I don't know where you envision having this applied, but I
> only apply things to the PCI tree after they appear on linux-pci.
>
oh, sorry, didn't notice that, will do in next version.
>
>
--
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: Re: [PATCH v3 04/11] drm/sun4i: abstract the layer type
From: Chen-Yu Tsai @ 2017-04-05 2:27 UTC (permalink / raw)
To: Icenowy Zheng
Cc: Sean Paul, Rob Herring, Maxime Ripard, Chen-Yu Tsai,
Jernej Skrabec, devicetree, linux-kernel, dri-devel, linux-sunxi,
linux-clk, linux-arm-kernel
In-Reply-To: <dbf0f478-ff53-0ced-24a3-e9de7077efd9-h8G6r0blFSE@public.gmane.org>
On Wed, Apr 5, 2017 at 3:53 AM, Icenowy Zheng <icenowy-h8G6r0blFSE@public.gmane.org> wrote:
>
>
> 在 2017年04月05日 03:28, Sean Paul 写道:
>>
>> On Thu, Mar 30, 2017 at 03:46:06AM +0800, Icenowy Zheng wrote:
>>>
>>> As we are going to add support for the Allwinner DE2 Mixer in sun4i-drm
>>> driver, we will finally have two types of layer.
>>>
>>> Abstract the layer type to void * and a ops struct, which contains the
>>> only function used by crtc -- get the drm_plane struct of the layer.
>>>
>>> Signed-off-by: Icenowy Zheng <icenowy-h8G6r0blFSE@public.gmane.org>
>>> ---
>>> Refactored patch in v3.
>>>
>>> drivers/gpu/drm/sun4i/sun4i_crtc.c | 19 +++++++++++--------
>>> drivers/gpu/drm/sun4i/sun4i_crtc.h | 3 ++-
>>> drivers/gpu/drm/sun4i/sun4i_layer.c | 19 ++++++++++++++++++-
>>> drivers/gpu/drm/sun4i/sun4i_layer.h | 2 +-
>>> drivers/gpu/drm/sun4i/sunxi_layer.h | 17 +++++++++++++++++
>>> 5 files changed, 49 insertions(+), 11 deletions(-)
>>> create mode 100644 drivers/gpu/drm/sun4i/sunxi_layer.h
>>>
>>> diff --git a/drivers/gpu/drm/sun4i/sun4i_crtc.c
>>> b/drivers/gpu/drm/sun4i/sun4i_crtc.c
>>> index 3c876c3a356a..33854ee7f636 100644
>>> --- a/drivers/gpu/drm/sun4i/sun4i_crtc.c
>>> +++ b/drivers/gpu/drm/sun4i/sun4i_crtc.c
>>> @@ -29,6 +29,7 @@
>>> #include "sun4i_crtc.h"
>>> #include "sun4i_drv.h"
>>> #include "sun4i_layer.h"
>>> +#include "sunxi_layer.h"
>>> #include "sun4i_tcon.h"
>>>
>>> static void sun4i_crtc_atomic_begin(struct drm_crtc *crtc,
>>> @@ -149,7 +150,7 @@ struct sun4i_crtc *sun4i_crtc_init(struct drm_device
>>> *drm,
>>> scrtc->tcon = tcon;
>>>
>>> /* Create our layers */
>>> - scrtc->layers = sun4i_layers_init(drm, scrtc->backend);
>>> + scrtc->layers = (void **)sun4i_layers_init(drm, scrtc);
>>> if (IS_ERR(scrtc->layers)) {
>>> dev_err(drm->dev, "Couldn't create the planes\n");
>>> return NULL;
>>> @@ -157,14 +158,15 @@ struct sun4i_crtc *sun4i_crtc_init(struct
>>> drm_device *drm,
>>>
>>> /* find primary and cursor planes for drm_crtc_init_with_planes
>>> */
>>> for (i = 0; scrtc->layers[i]; i++) {
>>> - struct sun4i_layer *layer = scrtc->layers[i];
>>> + void *layer = scrtc->layers[i];
>>> + struct drm_plane *plane =
>>> scrtc->layer_ops->get_plane(layer);
>>>
>>> - switch (layer->plane.type) {
>>> + switch (plane->type) {
>>> case DRM_PLANE_TYPE_PRIMARY:
>>> - primary = &layer->plane;
>>> + primary = plane;
>>> break;
>>> case DRM_PLANE_TYPE_CURSOR:
>>> - cursor = &layer->plane;
>>> + cursor = plane;
>>> break;
>>> default:
>>> break;
>>> @@ -190,10 +192,11 @@ struct sun4i_crtc *sun4i_crtc_init(struct
>>> drm_device *drm,
>>> /* Set possible_crtcs to this crtc for overlay planes */
>>> for (i = 0; scrtc->layers[i]; i++) {
>>> uint32_t possible_crtcs =
>>> BIT(drm_crtc_index(&scrtc->crtc));
>>> - struct sun4i_layer *layer = scrtc->layers[i];
>>> + void *layer = scrtc->layers[i];
>>> + struct drm_plane *plane =
>>> scrtc->layer_ops->get_plane(layer);
>>>
>>> - if (layer->plane.type == DRM_PLANE_TYPE_OVERLAY)
>>> - layer->plane.possible_crtcs = possible_crtcs;
>>> + if (plane->type == DRM_PLANE_TYPE_OVERLAY)
>>> + plane->possible_crtcs = possible_crtcs;
>>> }
>>>
>>> return scrtc;
>>> diff --git a/drivers/gpu/drm/sun4i/sun4i_crtc.h
>>> b/drivers/gpu/drm/sun4i/sun4i_crtc.h
>>> index 230cb8f0d601..a4036ee44cf8 100644
>>> --- a/drivers/gpu/drm/sun4i/sun4i_crtc.h
>>> +++ b/drivers/gpu/drm/sun4i/sun4i_crtc.h
>>> @@ -19,7 +19,8 @@ struct sun4i_crtc {
>>>
>>> struct sun4i_backend *backend;
>>> struct sun4i_tcon *tcon;
>>> - struct sun4i_layer **layers;
>>> + void **layers;
>>> + const struct sunxi_layer_ops *layer_ops;
>>
>>
>> I think you should probably take a different approach to abstract the
>> layer
>> type. How about creating
>>
>> struct sunxi_layer {
>> struct drm_plane plane;
>> }
>>
>> base and then subclassing that for sun4i and sun8i? By doing this you can
>> avoid
>> the nasty casting and you can also get rid of the get_plane() hook and
>> layer_ops.
>
>
> For the situation that using ** things are easily to get weird.
That code could be reworked, by initializing the layers directly within
the crtc init code. If you look at rockchip's drm driver, you'll see
they do this. There is a good reason to do it this way, as you need
to first create the primary and cursor layers, pass them in when you
create the crtc, then initialize any additional layers with the
possible_crtcs bitmap.
In our driver we are currently initializing all layers, then going
back and filling in possible_crtcs for the extra layers.
And as Maxime and I mentioned in the other thread, we don't really
need to keep a reference to **layers.
Regards
ChenYu
>
>>
>> Sean
>>
>>
>>
>>> };
>>>
>>> static inline struct sun4i_crtc *drm_crtc_to_sun4i_crtc(struct drm_crtc
>>> *crtc)
>>> diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.c
>>> b/drivers/gpu/drm/sun4i/sun4i_layer.c
>>> index f26bde5b9117..bc4a70d6968b 100644
>>> --- a/drivers/gpu/drm/sun4i/sun4i_layer.c
>>> +++ b/drivers/gpu/drm/sun4i/sun4i_layer.c
>>> @@ -16,7 +16,9 @@
>>> #include <drm/drmP.h>
>>>
>>> #include "sun4i_backend.h"
>>> +#include "sun4i_crtc.h"
>>> #include "sun4i_layer.h"
>>> +#include "sunxi_layer.h"
>>>
>>> struct sun4i_plane_desc {
>>> enum drm_plane_type type;
>>> @@ -100,6 +102,17 @@ static const struct sun4i_plane_desc
>>> sun4i_backend_planes[] = {
>>> },
>>> };
>>>
>>> +static struct drm_plane *sun4i_layer_get_plane(void *layer)
>>> +{
>>> + struct sun4i_layer *sun4i_layer = layer;
>>> +
>>> + return &sun4i_layer->plane;
>>> +}
>>> +
>>> +static const struct sunxi_layer_ops layer_ops = {
>>> + .get_plane = sun4i_layer_get_plane,
>>> +};
>>> +
>>> static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
>>> struct sun4i_backend
>>> *backend,
>>> const struct
>>> sun4i_plane_desc *plane)
>>> @@ -129,9 +142,10 @@ static struct sun4i_layer
>>> *sun4i_layer_init_one(struct drm_device *drm,
>>> }
>>>
>>> struct sun4i_layer **sun4i_layers_init(struct drm_device *drm,
>>> - struct sun4i_backend *backend)
>>> + struct sun4i_crtc *crtc)
>>> {
>>> struct sun4i_layer **layers;
>>> + struct sun4i_backend *backend = crtc->backend;
>>> int i;
>>>
>>> layers = devm_kcalloc(drm->dev, ARRAY_SIZE(sun4i_backend_planes)
>>> + 1,
>>> @@ -181,5 +195,8 @@ struct sun4i_layer **sun4i_layers_init(struct
>>> drm_device *drm,
>>> layers[i] = layer;
>>> };
>>>
>>> + /* Assign layer ops to the CRTC */
>>> + crtc->layer_ops = &layer_ops;
>>> +
>>> return layers;
>>> }
>>> diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.h
>>> b/drivers/gpu/drm/sun4i/sun4i_layer.h
>>> index 4be1f0919df2..425eea7b9e3b 100644
>>> --- a/drivers/gpu/drm/sun4i/sun4i_layer.h
>>> +++ b/drivers/gpu/drm/sun4i/sun4i_layer.h
>>> @@ -27,6 +27,6 @@ plane_to_sun4i_layer(struct drm_plane *plane)
>>> }
>>>
>>> struct sun4i_layer **sun4i_layers_init(struct drm_device *drm,
>>> - struct sun4i_backend *backend);
>>> + struct sun4i_crtc *crtc);
>>>
>>> #endif /* _SUN4I_LAYER_H_ */
>>> diff --git a/drivers/gpu/drm/sun4i/sunxi_layer.h
>>> b/drivers/gpu/drm/sun4i/sunxi_layer.h
>>> new file mode 100644
>>> index 000000000000..d8838ec39299
>>> --- /dev/null
>>> +++ b/drivers/gpu/drm/sun4i/sunxi_layer.h
>>> @@ -0,0 +1,17 @@
>>> +/*
>>> + * Copyright (C) 2017 Icenowy Zheng <icenowy-ymACFijhrKM@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 as
>>> + * published by the Free Software Foundation; either version 2 of
>>> + * the License, or (at your option) any later version.
>>> + */
>>> +
>>> +#ifndef _SUNXI_LAYER_H_
>>> +#define _SUNXI_LAYER_H_
>>> +
>>> +struct sunxi_layer_ops {
>>> + struct drm_plane *(*get_plane)(void *layer);
>>> +};
>>> +
>>> +#endif /* _SUNXI_LAYER_H_ */
>>> --
>>> 2.12.0
>>>
>>>
>>> _______________________________________________
>>> linux-arm-kernel mailing list
>>> linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
>>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>>
>>
>
> --
> You received this message because you are subscribed to the Google Groups
> "linux-sunxi" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
> For more options, visit https://groups.google.com/d/optout.
--
You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/d/optout.
^ permalink raw reply
* [PATCH v5 0/1] Fix memory leak in of_pci_get_host_bridge_resources
From: Jeffy Chen @ 2017-04-05 2:28 UTC (permalink / raw)
To: linux-pci
Cc: robh, toshi.kani, shawn.lin, briannorris, linux-kernel, dianders,
bhelgaas, dtor, Jeffy Chen, devicetree, Rob Herring, Frank Rowand
In of_pci_get_host_bridge_resources, we alloced some struct resource
variables, and they would cause memory leak since no where to free them.
Changes in v5:
Fix some careless compile errors.
Changes in v4:
Use IORESOURCE_AUTO to mark struct resources which could be copied.
Changes in v3:
Add some comments.
Changes in v2:
Don't change the resource_list_create_entry's behavior.
Jeffy Chen (1):
of/pci: Fix memory leak in of_pci_get_host_bridge_resources
drivers/of/of_pci.c | 51 ++++++++++++++++++---------------------------------
drivers/pci/bus.c | 7 ++++++-
2 files changed, 24 insertions(+), 34 deletions(-)
--
2.1.4
^ permalink raw reply
* [PATCH v5] of/pci: Fix memory leak in of_pci_get_host_bridge_resources
From: Jeffy Chen @ 2017-04-05 2:28 UTC (permalink / raw)
To: linux-pci
Cc: robh, toshi.kani, shawn.lin, briannorris, linux-kernel, dianders,
bhelgaas, dtor, Jeffy Chen, devicetree, Rob Herring, Frank Rowand
In-Reply-To: <1491359303-1385-1-git-send-email-jeffy.chen@rock-chips.com>
Currently we only free the allocated resource struct when error.
This would cause memory leak after pci_free_resource_list.
Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com>
---
Changes in v5:
Fix some careless compile errors.
Changes in v4:
Use IORESOURCE_AUTO to mark struct resources which could be copied.
Changes in v3:
Add some comments.
Changes in v2:
Don't change the resource_list_create_entry's behavior.
drivers/of/of_pci.c | 51 ++++++++++++++++++---------------------------------
drivers/pci/bus.c | 7 ++++++-
2 files changed, 24 insertions(+), 34 deletions(-)
diff --git a/drivers/of/of_pci.c b/drivers/of/of_pci.c
index 0ee42c3..49233bf 100644
--- a/drivers/of/of_pci.c
+++ b/drivers/of/of_pci.c
@@ -189,9 +189,7 @@ int of_pci_get_host_bridge_resources(struct device_node *dev,
unsigned char busno, unsigned char bus_max,
struct list_head *resources, resource_size_t *io_base)
{
- struct resource_entry *window;
- struct resource *res;
- struct resource *bus_range;
+ struct resource res;
struct of_pci_range range;
struct of_pci_range_parser parser;
char range_type[4];
@@ -200,24 +198,21 @@ int of_pci_get_host_bridge_resources(struct device_node *dev,
if (io_base)
*io_base = (resource_size_t)OF_BAD_ADDR;
- bus_range = kzalloc(sizeof(*bus_range), GFP_KERNEL);
- if (!bus_range)
- return -ENOMEM;
-
pr_info("host bridge %s ranges:\n", dev->full_name);
- err = of_pci_parse_bus_range(dev, bus_range);
+ err = of_pci_parse_bus_range(dev, &res);
if (err) {
- bus_range->start = busno;
- bus_range->end = bus_max;
- bus_range->flags = IORESOURCE_BUS;
- pr_info(" No bus range found for %s, using %pR\n",
- dev->full_name, bus_range);
+ res.start = busno;
+ res.end = bus_max;
+ res.flags = IORESOURCE_BUS;
+ pr_info(" No bus range found for %s\n", dev->full_name);
} else {
- if (bus_range->end > bus_range->start + bus_max)
- bus_range->end = bus_range->start + bus_max;
+ if (res.end > res.start + bus_max)
+ res.end = res.start + bus_max;
}
- pci_add_resource(resources, bus_range);
+
+ res.flags |= IORESOURCE_AUTO;
+ pci_add_resource(resources, &res);
/* Check for ranges property */
err = of_pci_range_parser_init(&parser, dev);
@@ -244,24 +239,16 @@ int of_pci_get_host_bridge_resources(struct device_node *dev,
if (range.cpu_addr == OF_BAD_ADDR || range.size == 0)
continue;
- res = kzalloc(sizeof(struct resource), GFP_KERNEL);
- if (!res) {
- err = -ENOMEM;
- goto parse_failed;
- }
-
- err = of_pci_range_to_resource(&range, dev, res);
- if (err) {
- kfree(res);
+ err = of_pci_range_to_resource(&range, dev, &res);
+ if (err)
continue;
- }
- if (resource_type(res) == IORESOURCE_IO) {
+ if (resource_type(&res) == IORESOURCE_IO) {
if (!io_base) {
pr_err("I/O range found for %s. Please provide an io_base pointer to save CPU base address\n",
dev->full_name);
err = -EINVAL;
- goto conversion_failed;
+ goto parse_failed;
}
if (*io_base != (resource_size_t)OF_BAD_ADDR)
pr_warn("More than one I/O resource converted for %s. CPU base address for old range lost!\n",
@@ -269,16 +256,14 @@ int of_pci_get_host_bridge_resources(struct device_node *dev,
*io_base = range.cpu_addr;
}
- pci_add_resource_offset(resources, res, res->start - range.pci_addr);
+ res.flags |= IORESOURCE_AUTO;
+ pci_add_resource_offset(resources, &res,
+ res.start - range.pci_addr);
}
return 0;
-conversion_failed:
- kfree(res);
parse_failed:
- resource_list_for_each_entry(window, resources)
- kfree(window->res);
pci_free_resource_list(resources);
return err;
}
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
index bc56cf1..3dbcb95 100644
--- a/drivers/pci/bus.c
+++ b/drivers/pci/bus.c
@@ -22,12 +22,17 @@ void pci_add_resource_offset(struct list_head *resources, struct resource *res,
{
struct resource_entry *entry;
- entry = resource_list_create_entry(res, 0);
+ entry = resource_list_create_entry(NULL, 0);
if (!entry) {
printk(KERN_ERR "PCI: can't add host bridge window %pR\n", res);
return;
}
+ if (res->flags & IORESOURCE_AUTO)
+ *entry->res = *res;
+ else
+ entry->res = res;
+
entry->offset = offset;
resource_list_add_tail(entry, resources);
}
--
2.1.4
^ permalink raw reply related
* Re: [PATCH 2/2] ARM: dts: imx7: add USDHC NAND clock to SDHC instances
From: Stefan Agner @ 2017-04-05 2:36 UTC (permalink / raw)
To: Fabio Estevam
Cc: Dong Aisheng, Shawn Guo, Sascha Hauer, Stephen Boyd, Dong Aisheng,
Fabio Estevam, robh+dt-DgEjT+Ai2ygdnm+yROfE0A, Mark Rutland,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-clk-u79uwXL29TY76Z2rM5mHXA, linux-kernel
In-Reply-To: <CAOMZO5A7QQEe5rgBbJBzo8GY41aYV9WA8e9teCUROyihg0kq_g-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
On 2017-04-04 19:15, Fabio Estevam wrote:
> On Sun, Apr 2, 2017 at 2:02 PM, Fabio Estevam <festevam-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
>> On Sat, Apr 1, 2017 at 1:15 AM, Stefan Agner <stefan-XLVq0VzYD2Y@public.gmane.org> wrote:
>>
>>> IMX7D_IPG_ROOT_CLK is currently not a valid clock in upstream... So we
>>> would have to add it to the clock driver first.
>>>
>>> I guess we could/should add it anyway at one point? But probably also as
>>> init on, just to make sure Linux does not disable it since it is
>>> currently used by several IPs implicitly.
>>
>> Yes, I made a previous attempt do add IMX7D_IPG_ROOT_CLK and it did
>> not work as I did not put it in the init_on clock list.
>>
>> Will submit a new patch adding it to init_on, thanks.
>
> I thought that adding IMX7D_IPG_ROOT_CLK would do the trick, but the
> patch below also causes the kernel to not boot:
>
> --- a/drivers/clk/imx/clk-imx7d.c
> +++ b/drivers/clk/imx/clk-imx7d.c
> @@ -386,7 +386,7 @@ static int const clks_init_on[] __initconst = {
> IMX7D_PLL_SYS_MAIN_480M_CLK, IMX7D_NAND_USDHC_BUS_ROOT_CLK,
> IMX7D_DRAM_PHYM_ROOT_CLK, IMX7D_DRAM_ROOT_CLK,
> IMX7D_DRAM_PHYM_ALT_ROOT_CLK, IMX7D_DRAM_ALT_ROOT_CLK,
> - IMX7D_AHB_CHANNEL_ROOT_CLK,
> + IMX7D_AHB_CHANNEL_ROOT_CLK, IMX7D_IPG_ROOT_CLK,
> };
>
> static struct clk_onecell_data clk_data;
> @@ -788,7 +788,7 @@ static void __init imx7d_clocks_init(struct
> device_node *ccm_node)
> clks[IMX7D_WRCLK_ROOT_DIV] =
> imx_clk_divider2("wrclk_post_div", "wrclk_pre_div", base + 0xbd00, 0,
> 6);
> clks[IMX7D_CLKO1_ROOT_DIV] =
> imx_clk_divider2("clko1_post_div", "clko1_pre_div", base + 0xbd80, 0,
> 6);
> clks[IMX7D_CLKO2_ROOT_DIV] =
> imx_clk_divider2("clko2_post_div", "clko2_pre_div", base + 0xbe00, 0,
> 6);
> -
> + clks[IMX7D_IPG_ROOT_CLK] = imx_clk_divider2("ipg_root_clk",
> "ahb_root_clk", base + 0x9080, 0, 2);
> clks[IMX7D_ARM_A7_ROOT_CLK] = imx_clk_gate4("arm_a7_root_clk",
> "arm_a7_div", base + 0x4000, 0);
> clks[IMX7D_ARM_M4_ROOT_CLK] = imx_clk_gate4("arm_m4_root_clk",
> "arm_m4_div", base + 0x4010, 0);
> clks[IMX7D_ARM_M0_ROOT_CLK] = imx_clk_gate4("arm_m0_root_clk",
> "arm_m0_div", base + 0x4020, 0);
Hm, imx_clk_divider2 sets CLK_SET_RATE_PARENT, maybe that influences the
parent?
I guess we actually don't want the clock framework to change that clock
rate, not sure whether we can freeze it or similar.
--
Stefan
--
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 V1 1/1] mtd: mtk-nor: set controller to 4B mode with large capacity flash
From: Guochun Mao @ 2017-04-05 3:34 UTC (permalink / raw)
To: Cyrille Pitchen
Cc: Marek Vasut, Boris Brezillon, Mark Rutland,
devicetree-u79uwXL29TY76Z2rM5mHXA, Richard Weinberger,
Russell King, linux-kernel-u79uwXL29TY76Z2rM5mHXA, Rob Herring,
linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Matthias Brugger,
linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Cyrille Pitchen,
David Woodhouse,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <873a3dac-7d3a-0723-4c7d-c786d3398278-yU5RGvR974pGWvitb5QawA@public.gmane.org>
Hi Cyrille,
Thank you for so detailed explanation.
I'll check nor->addr_width in both mt8173_nor_read() and
mt8173_nor_write().
Best regards,
Guochun
On Fri, 2017-03-31 at 10:56 +0200, Cyrille Pitchen wrote:
> Le 31/03/2017 à 04:26, Guochun Mao a écrit :
> > Hi Cyrille, Marek,
> >
> > Thanks for your suggestions.
> >
> > On Thu, 2017-03-30 at 19:40 +0200, Cyrille Pitchen wrote:
> >> Hi Guochun,
> >>
> >> Le 30/03/2017 à 10:23, Guochun Mao a écrit :
> >>> when nor's size larger than 16MByte, nor and controller should
> >>> enter 4Byte mode simultaneously.
> >>>
> >>> Signed-off-by: Guochun Mao <guochun.mao-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
> >>> ---
> >>> drivers/mtd/spi-nor/mtk-quadspi.c | 7 +++++++
> >>> 1 file changed, 7 insertions(+)
> >>>
> >>> diff --git a/drivers/mtd/spi-nor/mtk-quadspi.c b/drivers/mtd/spi-nor/mtk-quadspi.c
> >>> index e661877..05cd8a8 100644
> >>> --- a/drivers/mtd/spi-nor/mtk-quadspi.c
> >>> +++ b/drivers/mtd/spi-nor/mtk-quadspi.c
> >>> @@ -369,6 +369,13 @@ static int mt8173_nor_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf,
> >>> /* We only handle 1 byte */
> >>> ret = mt8173_nor_wr_sr(mt8173_nor, *buf);
> >>> break;
> >>> + case SPINOR_OP_EN4B:
> >>> + /* Set nor controller to 4-byte address mode,
> >>> + * and simultaneously set nor flash.
> >>> + * This case should cooperate with default operation.
> >>> + */
> >>> + writeb(readb(mt8173_nor->base + MTK_NOR_DUAL_REG) | 0x10,
> >>> + mt8173_nor->base + MTK_NOR_DUAL_REG);
> >>
> >> This is not good: you should check in both mt8173_nor_read() and
> >> mt8173_nor_write() whether nor->addr_width is either 3 or 4.
> >>
> >> from include/linux/mtd/spi-nor.h:
> >> * @addr_width: number of address bytes
> >>
> >> Besides SPI commands using an op code from 4-byte address instruction
> >> set always carry a 4-byte address. They can be used directly, without
> >> sending the SPINOR_OP_EN4B before. So you cannot assume that addresses
> >> will be 4-byte long only if your SPI controller driver has seen a
> >> SPINOR_OP_EN4B command before. This assumption is wrong.
> > Nor->addr_width is assigned in spi_nor_scan in spi-nor.c, and it's not
> > modified in later process.
> > Does it means that we will not switch nor between 3Byte address and
> > 4Byte?
> > So, is it better to check nor->addr_width when do nor initialization?
> >
>
> Currently yes, nor->addr_width, nor->read_opcode, nor->read_dummy, and
> nor->program_opcode are set once for all in spi_nor_scan().
>
> However, nor->read() is likely to be called soon from spi_nor_scan()
> with values of nor->addr_width, nor->read_opcode and nor->read_dummy
> different from those selected when exiting spi_nor_scan().
>
> So nor->read() / mt8173_nor_read should check nor->addr_width,
> nor->read_opcode and nor->read_dummy at each call.
>
> More precisely, I plan to use nor->read() from spi_nor_scan() to read
> SFDP (Serial Flash Discoverable Parameters) data.
>
> Whatever op code, numbers of address bytes and dummy cycles used for
> (Fast) Read commands, the Read SFDP command uses fixed settings
> standardized for all manufacturers and all memory parts:
> - op code: 5Ah
> - number of bytes for the address: 3 (even for memory > 128Mbits)
> - number of dummy clock cycles: 8 clocks
>
> https://patchwork.ozlabs.org/patch/742380/
>
> Please have a look at the spi_nor_read_sfdp().
>
> spi_nor_read_sfdp() is likely to be called from and only from
> spi_nor_scan().
>
> Best regards,
>
> Cyrille
>
> >>
> >> SPI controller driver should never check SPINOR_OP_* op codes like this.
> > I agree that SPI controller driver should not check SPINOR_OP_* op codes
> > like what I do.
> > I will correct it next version.
> >
> > Best Regards,
> > Guochun
> >>
> >> Then, testing SPINOR_OP_RDSR from mt8173_nor_read_reg() or
> >> SPINOR_OP_WRSR from mt8173_nor_write_reg() is not a good practice too:
> >> op codes may change depending on the memory manufacturer. So testing op
> >> code values like you do can work with some memories but maybe not all.
> >>
> >> Finally, don't use 0x10, please define a macro instead.
> >>
> >> Best regards,
> >>
> >> Cyrille
> >>
> >>> default:
> >>> ret = mt8173_nor_do_tx_rx(mt8173_nor, opcode, buf, len, NULL, 0);
> >>> if (ret)
> >>>
> >>
> >
> >
> >
>
--
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 01/11] arm64: allwinner: a64: enable RSB on A64
From: Chen-Yu Tsai @ 2017-04-05 3:48 UTC (permalink / raw)
To: Icenowy Zheng
Cc: Lee Jones, Rob Herring, Chen-Yu Tsai, Maxime Ripard,
Liam Girdwood, devicetree, linux-sunxi, linux-kernel,
linux-arm-kernel
In-Reply-To: <20170404180145.12897-2-icenowy-h8G6r0blFSE@public.gmane.org>
On Wed, Apr 5, 2017 at 2:01 AM, Icenowy Zheng <icenowy-h8G6r0blFSE@public.gmane.org> wrote:
> Allwinner A64 have a RSB controller like the one on A23/A33 SoCs.
>
> Add it and its pinmux.
>
> Signed-off-by: Icenowy Zheng <icenowy-h8G6r0blFSE@public.gmane.org>
> ---
> arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 21 +++++++++++++++++++++
> 1 file changed, 21 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
> index 6bc606b4d74d..9a75b1c7c91a 100644
> --- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
> +++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
> @@ -420,6 +420,27 @@
> #gpio-cells = <3>;
> interrupt-controller;
> #interrupt-cells = <3>;
> +
> + r_rsb_pins: rsb@0 {
> + pins = "PL0", "PL1";
> + function = "s_rsb";
> + drive-strength = <20>;
We don't need this. The hardware default should be enough.
> + bias-pull-up;
Boards should have external pull-ups for these pins, as the PMICs
start up in I2C mode. If any board actually doesn't have them and
needs the internal pull-ups, they should be set at the board level.
Otherwise,
Acked-by: Chen-Yu Tsai <wens-jdAy2FN1RRM@public.gmane.org>
> + };
> + };
> +
> + r_rsb: rsb@1f03400 {
> + compatible = "allwinner,sun8i-a23-rsb";
> + reg = <0x01f03400 0x400>;
> + interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>;
> + clocks = <&r_ccu 6>;
> + clock-frequency = <3000000>;
> + resets = <&r_ccu 2>;
> + pinctrl-names = "default";
> + pinctrl-0 = <&r_rsb_pins>;
> + status = "disabled";
> + #address-cells = <1>;
> + #size-cells = <0>;
> };
> };
> };
> --
> 2.12.2
>
> --
> You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
> For more options, visit https://groups.google.com/d/optout.
^ permalink raw reply
* Re: [PATCH 02/11] arm64: allwinner: a64: add NMI controller on A64
From: Chen-Yu Tsai @ 2017-04-05 3:51 UTC (permalink / raw)
To: Icenowy Zheng
Cc: Lee Jones, Rob Herring, Chen-Yu Tsai, Maxime Ripard,
Liam Girdwood, devicetree, linux-sunxi, linux-kernel,
linux-arm-kernel
In-Reply-To: <20170404180145.12897-3-icenowy-h8G6r0blFSE@public.gmane.org>
On Wed, Apr 5, 2017 at 2:01 AM, Icenowy Zheng <icenowy-h8G6r0blFSE@public.gmane.org> wrote:
> Allwinner A64 SoC features a NMI controller, which is usually connected
> to the AXP PMIC.
>
> Add support for it.
>
> Signed-off-by: Icenowy Zheng <icenowy-h8G6r0blFSE@public.gmane.org>
This might not be the best representation of the R_INTC block. Though
we'd need to change it for all SoCs if we want to be accurate. For now,
Acked-by: Chen-Yu Tsai <wens-jdAy2FN1RRM@public.gmane.org>
^ permalink raw reply
* Re: [linux-sunxi] [PATCH 03/11] dt-bindings: add device tree binding for X-Powers AXP803 PMIC
From: Chen-Yu Tsai @ 2017-04-05 3:52 UTC (permalink / raw)
To: Icenowy Zheng
Cc: Lee Jones, Rob Herring, Chen-Yu Tsai, Maxime Ripard,
Liam Girdwood, devicetree, linux-sunxi, linux-kernel,
linux-arm-kernel
In-Reply-To: <20170404180145.12897-4-icenowy-h8G6r0blFSE@public.gmane.org>
On Wed, Apr 5, 2017 at 2:01 AM, Icenowy Zheng <icenowy-h8G6r0blFSE@public.gmane.org> wrote:
> AXP803 is a PMIC produced by Shenzhen X-Powers, with either I2C or RSB
> bus.
>
> Add a compatible for it.
>
> Signed-off-by: Icenowy Zheng <icenowy-h8G6r0blFSE@public.gmane.org>
> ---
> Documentation/devicetree/bindings/mfd/axp20x.txt | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/devicetree/bindings/mfd/axp20x.txt b/Documentation/devicetree/bindings/mfd/axp20x.txt
> index b41d2601c6ba..31607631b0d8 100644
> --- a/Documentation/devicetree/bindings/mfd/axp20x.txt
> +++ b/Documentation/devicetree/bindings/mfd/axp20x.txt
> @@ -7,11 +7,12 @@ axp209 (X-Powers)
> axp221 (X-Powers)
> axp223 (X-Powers)
> axp809 (X-Powers)
> +axp803 (X-Powers)
>
> Required properties:
> - compatible: "x-powers,axp152", "x-powers,axp202", "x-powers,axp209",
> "x-powers,axp221", "x-powers,axp223", "x-powers,axp806",
> - "x-powers,axp809"
> + "x-powers,axp809", "x-powers,axp803"
Please sort them in ascending order. Otherwise,
Acked-by: Chen-Yu Tsai <wens-jdAy2FN1RRM@public.gmane.org>
> - reg: The I2C slave address or RSB hardware address for the AXP chip
> - interrupt-parent: The parent interrupt controller
> - interrupts: SoC NMI / GPIO interrupt connected to the PMIC's IRQ pin
> --
> 2.12.2
>
> --
> You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
> For more options, visit https://groups.google.com/d/optout.
--
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 v2 0/2] drivers: serial: Aspeed VUART driver
From: Joel Stanley @ 2017-04-05 4:03 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jiri Slaby, Mark Rutland, Rob Herring
Cc: linux-serial, linux-kernel, devicetree, openbmc, Andy Shevchenko,
Benjamin Herrenschmidt, Jeremy Kerr
This is v2 of a driver for the Aspeed VUART. This version addresses feedback
from Andy and Greg, and adds Rob's ack for the bindings change.
The VUART is a serial device on the BMC side of the LPC bus that connects a BMC
to it's host processor.
We add a flag to the serial core to allow the driver to skip probing of the
THRE irq behaviour, which could hang due to the host not reading bytes out of
the buffer.
We've been using this on systems for over a year, so it has seen a good amount
of testing.
Cheers,
Joel
Jeremy Kerr (1):
drivers/serial: Add driver for Aspeed virtual UART
Joel Stanley (1):
serial: 8250: Add flag so drivers can avoid THRE probe
Documentation/ABI/stable/sysfs-driver-aspeed-vuart | 15 +
Documentation/devicetree/bindings/serial/8250.txt | 2 +
drivers/tty/serial/8250/8250_port.c | 2 +-
drivers/tty/serial/8250/Kconfig | 10 +
drivers/tty/serial/8250/Makefile | 1 +
drivers/tty/serial/8250/aspeed-vuart.c | 341 +++++++++++++++++++++
include/linux/serial_core.h | 1 +
7 files changed, 371 insertions(+), 1 deletion(-)
create mode 100644 Documentation/ABI/stable/sysfs-driver-aspeed-vuart
create mode 100644 drivers/tty/serial/8250/aspeed-vuart.c
--
2.11.0
^ permalink raw reply
* [PATCH v2 1/2] serial: 8250: Add flag so drivers can avoid THRE probe
From: Joel Stanley @ 2017-04-05 4:03 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jiri Slaby, Mark Rutland, Rob Herring
Cc: linux-serial-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, Andy Shevchenko,
Benjamin Herrenschmidt, Jeremy Kerr,
openbmc-uLR06cmDAlY/bJ5BZ2RsiQ
In-Reply-To: <20170405040352.5661-1-joel-U3u1mxZcP9KHXe+LvDLADg@public.gmane.org>
The probing of THRE irq behaviour assumes the other end will be reading
bytes out of the buffer in order to probe the port at driver init. In
some cases the other end cannot be relied upon to read these bytes, so
provide a flag for them to skip this step.
Bit 16 was chosen as the flags are a int and the top bits are taken.
Acked-by: Benjamin Herrenschmidt <benh-XVmvHMARGAS8U2dJNN8I7kB+6BGkLq7r@public.gmane.org>
Signed-off-by: Joel Stanley <joel-U3u1mxZcP9KHXe+LvDLADg@public.gmane.org>
---
drivers/tty/serial/8250/8250_port.c | 2 +-
include/linux/serial_core.h | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index 6119516ef5fc..60a6c247340f 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -2229,7 +2229,7 @@ int serial8250_do_startup(struct uart_port *port)
}
}
- if (port->irq) {
+ if (port->irq && !(up->port.flags & UPF_NO_THRE_TEST)) {
unsigned char iir1;
/*
* Test for UARTs that do not reassert THRE when the
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index 58484fb35cc8..260245deec94 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -195,6 +195,7 @@ struct uart_port {
#define UPF_NO_TXEN_TEST ((__force upf_t) (1 << 15))
#define UPF_MAGIC_MULTIPLIER ((__force upf_t) ASYNC_MAGIC_MULTIPLIER /* 16 */ )
+#define UPF_NO_THRE_TEST ((__force upf_t) (1 << 19))
/* Port has hardware-assisted h/w flow control */
#define UPF_AUTO_CTS ((__force upf_t) (1 << 20))
#define UPF_AUTO_RTS ((__force upf_t) (1 << 21))
--
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 v2 2/2] drivers/serial: Add driver for Aspeed virtual UART
From: Joel Stanley @ 2017-04-05 4:03 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jiri Slaby, Mark Rutland, Rob Herring
Cc: Jeremy Kerr, linux-serial, linux-kernel, devicetree,
Andy Shevchenko, Benjamin Herrenschmidt, openbmc
In-Reply-To: <20170405040352.5661-1-joel@jms.id.au>
From: Jeremy Kerr <jk@ozlabs.org>
This change adds a driver for the 16550-based Aspeed virtual UART
device. We use a similar process to the of_serial driver for device
probe, but expose some VUART-specific functions through sysfs too.
The VUART is two UART 'front ends' connected by their FIFO (no actual
serial line in between). One is on the BMC side (management controller)
and one is on the host CPU side.
This driver is for the BMC side. The sysfs files allow the BMC
userspace, which owns the system configuration policy, to specify at
what IO port and interrupt number the host side will appear to the host
on the Host <-> BMC LPC bus. It could be different on a different system
(though most of them use 3f8/4).
OpenPOWER host firmware doesn't like it when the host-side of the
VUART's FIFO is not drained. This driver only disables host TX discard
mode when the port is in use. We set the VUART enabled bit when we bind
to the device, and clear it on unbind.
We don't want to do this on open/release, as the host may be using this
bit to configure serial output modes, which is independent of whether
the devices has been opened by BMC userspace.
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Signed-off-by: Joel Stanley <joel@jms.id.au>
Acked-by: Rob Herring <robh@kernel.org>
---
v2:
- Use attribute groups and DEVICE_ATTR_RW
- Use platform_get_resource/devm_ioremap_resource
- of_find_property -> of_property_read_bool
- Drop unncessary 0xff mask
- Fix comment style
- Use BIT and GENMASK where pssible
- Move to 8250 directory
- Rename ast -> aspeed to match other Aspeed drivers
- Add documentation of sysfs file
- Add detail to the commit message
- add Rob's ack for the binding change
---
Documentation/ABI/stable/sysfs-driver-aspeed-vuart | 15 +
Documentation/devicetree/bindings/serial/8250.txt | 2 +
drivers/tty/serial/8250/Kconfig | 10 +
drivers/tty/serial/8250/Makefile | 1 +
drivers/tty/serial/8250/aspeed-vuart.c | 341 +++++++++++++++++++++
5 files changed, 369 insertions(+)
create mode 100644 Documentation/ABI/stable/sysfs-driver-aspeed-vuart
create mode 100644 drivers/tty/serial/8250/aspeed-vuart.c
diff --git a/Documentation/ABI/stable/sysfs-driver-aspeed-vuart b/Documentation/ABI/stable/sysfs-driver-aspeed-vuart
new file mode 100644
index 000000000000..8062953ce77b
--- /dev/null
+++ b/Documentation/ABI/stable/sysfs-driver-aspeed-vuart
@@ -0,0 +1,15 @@
+What: /sys/bus/platform/drivers/aspeed-vuart/*/lpc_address
+Date: April 2017
+Contact: Jeremy Kerr <jk@ozlabs.org>
+Description: Configures which IO port the host side of the UART
+ will appear on the host <-> BMC LPC bus.
+Users: OpenBMC. Proposed changes should be mailed to
+ openbmc@lists.ozlabs.org
+
+What: /sys/bus/platform/drivers/aspeed-vuart*/sirq
+Date: April 2017
+Contact: Jeremy Kerr <jk@ozlabs.org>
+Description: Configures which interrupt number the host side of
+ the UART will appear on the host <-> BMC LPC bus.
+Users: OpenBMC. Proposed changes should be mailed to
+ openbmc@lists.ozlabs.org
diff --git a/Documentation/devicetree/bindings/serial/8250.txt b/Documentation/devicetree/bindings/serial/8250.txt
index 10276a46ecef..656733949309 100644
--- a/Documentation/devicetree/bindings/serial/8250.txt
+++ b/Documentation/devicetree/bindings/serial/8250.txt
@@ -20,6 +20,8 @@ Required properties:
- "fsl,16550-FIFO64"
- "fsl,ns16550"
- "ti,da830-uart"
+ - "aspeed,ast2400-vuart"
+ - "aspeed,ast2500-vuart"
- "serial" if the port type is unknown.
- reg : offset and length of the register set for the device.
- interrupts : should contain uart interrupt.
diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
index a65fb8197aec..fb217f02ec94 100644
--- a/drivers/tty/serial/8250/Kconfig
+++ b/drivers/tty/serial/8250/Kconfig
@@ -220,6 +220,16 @@ config SERIAL_8250_ACCENT
To compile this driver as a module, choose M here: the module
will be called 8250_accent.
+config SERIAL_8250_ASPEED_VUART
+ tristate "Aspeed Virtual UART"
+ depends on SERIAL_8250
+ depends on OF
+ help
+ If you want to use the virtual UART (VUART) device on Aspeed
+ BMC platforms, enable this option. This enables the 16550A-
+ compatible device on the local LPC bus, giving a UART device
+ with no physical RS232 connections.
+
config SERIAL_8250_BOCA
tristate "Support Boca cards"
depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS
diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile
index 2f30f9ecdb1b..405d720b51c7 100644
--- a/drivers/tty/serial/8250/Makefile
+++ b/drivers/tty/serial/8250/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_SERIAL_8250_EXAR) += 8250_exar.o
obj-$(CONFIG_SERIAL_8250_HP300) += 8250_hp300.o
obj-$(CONFIG_SERIAL_8250_CS) += serial_cs.o
obj-$(CONFIG_SERIAL_8250_ACORN) += 8250_acorn.o
+obj-$(CONFIG_SERIAL_8250_ASPEED_VUART) += aspeed-vuart.o
obj-$(CONFIG_SERIAL_8250_BCM2835AUX) += 8250_bcm2835aux.o
obj-$(CONFIG_SERIAL_8250_CONSOLE) += 8250_early.o
obj-$(CONFIG_SERIAL_8250_FOURPORT) += 8250_fourport.o
diff --git a/drivers/tty/serial/8250/aspeed-vuart.c b/drivers/tty/serial/8250/aspeed-vuart.c
new file mode 100644
index 000000000000..261b3dbaa7c1
--- /dev/null
+++ b/drivers/tty/serial/8250/aspeed-vuart.c
@@ -0,0 +1,341 @@
+/*
+ * Serial Port driver for Aspeed VUART device
+ *
+ * Copyright (C) 2016 Jeremy Kerr <jk@ozlabs.org>, IBM Corp.
+ * Copyright (C) 2006 Arnd Bergmann <arnd@arndb.de>, IBM Corp.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/clk.h>
+
+#include "8250.h"
+
+#define ASPEED_VUART_GCRA 0x20
+#define ASPEED_VUART_GCRA_VUART_EN BIT(0)
+#define ASPEED_VUART_GCRA_HOST_TX_DISCARD BIT(5)
+#define ASPEED_VUART_GCRB 0x24
+#define ASPEED_VUART_GCRB_HOST_SIRQ_MASK GENMASK(7, 4)
+#define ASPEED_VUART_GCRB_HOST_SIRQ_SHIFT 4
+#define ASPEED_VUART_ADDRL 0x28
+#define ASPEED_VUART_ADDRH 0x2c
+
+struct aspeed_vuart {
+ struct device *dev;
+ void __iomem *regs;
+ struct clk *clk;
+ int line;
+};
+
+/*
+ * The VUART is basically two UART 'front ends' connected by their FIFO
+ * (no actual serial line in between). One is on the BMC side (management
+ * controller) and one is on the host CPU side.
+ *
+ * It allows the BMC to provide to the host a "UART" that pipes into
+ * the BMC itself and can then be turned by the BMC into a network console
+ * of some sort for example.
+ *
+ * This driver is for the BMC side. The sysfs files allow the BMC
+ * userspace which owns the system configuration policy, to specify
+ * at what IO port and interrupt number the host side will appear
+ * to the host on the Host <-> BMC LPC bus. It could be different on a
+ * different system (though most of them use 3f8/4).
+ */
+
+static ssize_t lpc_address_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct aspeed_vuart *vuart = dev_get_drvdata(dev);
+ u16 addr;
+
+ addr = (readb(vuart->regs + ASPEED_VUART_ADDRH) << 8) |
+ (readb(vuart->regs + ASPEED_VUART_ADDRL));
+
+ return snprintf(buf, PAGE_SIZE - 1, "0x%x\n", addr);
+}
+
+static ssize_t lpc_address_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct aspeed_vuart *vuart = dev_get_drvdata(dev);
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 0, &val);
+ if (err)
+ return err;
+
+ writeb(val >> 8, vuart->regs + ASPEED_VUART_ADDRH);
+ writeb(val >> 0, vuart->regs + ASPEED_VUART_ADDRL);
+
+ return count;
+}
+
+static DEVICE_ATTR_RW(lpc_address);
+
+static ssize_t sirq_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct aspeed_vuart *vuart = dev_get_drvdata(dev);
+ u8 reg;
+
+ reg = readb(vuart->regs + ASPEED_VUART_GCRB);
+ reg &= ASPEED_VUART_GCRB_HOST_SIRQ_MASK;
+ reg >>= ASPEED_VUART_GCRB_HOST_SIRQ_SHIFT;
+
+ return snprintf(buf, PAGE_SIZE - 1, "%u\n", reg);
+}
+
+static ssize_t sirq_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct aspeed_vuart *vuart = dev_get_drvdata(dev);
+ unsigned long val;
+ int err;
+ u8 reg;
+
+ err = kstrtoul(buf, 0, &val);
+ if (err)
+ return err;
+
+ val <<= ASPEED_VUART_GCRB_HOST_SIRQ_SHIFT;
+ val &= ASPEED_VUART_GCRB_HOST_SIRQ_MASK;
+
+ reg = readb(vuart->regs + ASPEED_VUART_GCRB);
+ reg &= ~ASPEED_VUART_GCRB_HOST_SIRQ_MASK;
+ reg |= val;
+ writeb(reg, vuart->regs + ASPEED_VUART_GCRB);
+
+ return count;
+}
+
+static DEVICE_ATTR_RW(sirq);
+
+static struct attribute *aspeed_vuart_attrs[] = {
+ &dev_attr_sirq.attr,
+ &dev_attr_lpc_address.attr,
+ NULL,
+};
+
+static const struct attribute_group aspeed_vuart_attr_group = {
+ .attrs = aspeed_vuart_attrs,
+};
+
+static void aspeed_vuart_set_enabled(struct aspeed_vuart *vuart, bool enabled)
+{
+ u8 reg;
+
+ reg = readb(vuart->regs + ASPEED_VUART_GCRA);
+ reg &= ~ASPEED_VUART_GCRA_VUART_EN;
+ if (enabled)
+ reg |= ASPEED_VUART_GCRA_VUART_EN;
+ writeb(reg, vuart->regs + ASPEED_VUART_GCRA);
+}
+
+static void aspeed_vuart_set_host_tx_discard(struct aspeed_vuart *vuart,
+ bool discard)
+{
+ u8 reg;
+
+ reg = readb(vuart->regs + ASPEED_VUART_GCRA);
+
+ /* if the HOST_TX_DISCARD bit is set, discard is *disabled* */
+ reg &= ~ASPEED_VUART_GCRA_HOST_TX_DISCARD;
+ if (!discard)
+ reg |= ASPEED_VUART_GCRA_HOST_TX_DISCARD;
+
+ writeb(reg, vuart->regs + ASPEED_VUART_GCRA);
+}
+
+static int aspeed_vuart_startup(struct uart_port *uart_port)
+{
+ struct uart_8250_port *uart_8250_port = up_to_u8250p(uart_port);
+ struct aspeed_vuart *vuart = uart_8250_port->port.private_data;
+ int rc;
+
+ rc = serial8250_do_startup(uart_port);
+ if (rc)
+ return rc;
+
+ aspeed_vuart_set_host_tx_discard(vuart, false);
+
+ return 0;
+}
+
+static void aspeed_vuart_shutdown(struct uart_port *uart_port)
+{
+ struct uart_8250_port *uart_8250_port = up_to_u8250p(uart_port);
+ struct aspeed_vuart *vuart = uart_8250_port->port.private_data;
+
+ aspeed_vuart_set_host_tx_discard(vuart, true);
+
+ serial8250_do_shutdown(uart_port);
+}
+
+static int aspeed_vuart_probe(struct platform_device *pdev)
+{
+ struct uart_8250_port port;
+ struct resource *res;
+ struct aspeed_vuart *vuart;
+ struct device_node *np;
+ u32 clk, prop;
+ int rc;
+
+ np = pdev->dev.of_node;
+
+ vuart = devm_kzalloc(&pdev->dev, sizeof(*vuart), GFP_KERNEL);
+ if (!vuart)
+ return -ENOMEM;
+
+ vuart->dev = &pdev->dev;
+
+ /* The 8510 core creates the mapping, which we grab a reference to
+ * for VUART-specific registers */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ vuart->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(vuart->regs))
+ return PTR_ERR(vuart->regs);
+
+ memset(&port, 0, sizeof(port));
+ port.port.private_data = vuart;
+ port.port.membase = vuart->regs;
+ port.port.mapbase = res->start;
+ port.port.mapsize = resource_size(res);
+ port.port.startup = aspeed_vuart_startup;
+ port.port.shutdown = aspeed_vuart_shutdown;
+ port.port.dev = &pdev->dev;
+
+ rc = sysfs_create_group(&vuart->dev->kobj, &aspeed_vuart_attr_group);
+ if (rc < 0)
+ return rc;
+
+ if (of_property_read_u32(np, "clock-frequency", &clk)) {
+ vuart->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(vuart->clk)) {
+ dev_warn(&pdev->dev,
+ "clk or clock-frequency not defined\n");
+ return PTR_ERR(vuart->clk);
+ }
+
+ rc = clk_prepare_enable(vuart->clk);
+ if (rc < 0)
+ return rc;
+
+ clk = clk_get_rate(vuart->clk);
+ }
+
+ /* If current-speed was set, then try not to change it. */
+ if (of_property_read_u32(np, "current-speed", &prop) == 0)
+ port.port.custom_divisor = clk / (16 * prop);
+
+ /* Check for shifted address mapping */
+ if (of_property_read_u32(np, "reg-offset", &prop) == 0)
+ port.port.mapbase += prop;
+
+ /* Check for registers offset within the devices address range */
+ if (of_property_read_u32(np, "reg-shift", &prop) == 0)
+ port.port.regshift = prop;
+
+ /* Check for fifo size */
+ if (of_property_read_u32(np, "fifo-size", &prop) == 0)
+ port.port.fifosize = prop;
+
+ /* Check for a fixed line number */
+ rc = of_alias_get_id(np, "serial");
+ if (rc >= 0)
+ port.port.line = rc;
+
+ port.port.irq = irq_of_parse_and_map(np, 0);
+ port.port.irqflags = IRQF_SHARED;
+ port.port.iotype = UPIO_MEM;
+ if (of_property_read_u32(np, "reg-io-width", &prop) == 0) {
+ switch (prop) {
+ case 1:
+ port.port.iotype = UPIO_MEM;
+ break;
+ case 4:
+ port.port.iotype = of_device_is_big_endian(np) ?
+ UPIO_MEM32BE : UPIO_MEM32;
+ break;
+ default:
+ dev_warn(&pdev->dev, "unsupported reg-io-width (%d)\n",
+ prop);
+ rc = -EINVAL;
+ goto err_clk_disable;
+ }
+ }
+
+ port.port.type = PORT_16550A;
+ port.port.uartclk = clk;
+ port.port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF
+ | UPF_FIXED_PORT | UPF_FIXED_TYPE | UPF_NO_THRE_TEST;
+
+ if (of_property_read_bool(np, "no-loopback-test"))
+ port.port.flags |= UPF_SKIP_TEST;
+
+ if (port.port.fifosize)
+ port.capabilities = UART_CAP_FIFO;
+
+ if (of_property_read_bool(np, "auto-flow-control"))
+ port.capabilities |= UART_CAP_AFE;
+
+ rc = serial8250_register_8250_port(&port);
+ if (rc < 0)
+ goto err_clk_disable;
+
+ vuart->line = rc;
+
+ aspeed_vuart_set_enabled(vuart, true);
+ aspeed_vuart_set_host_tx_discard(vuart, true);
+ platform_set_drvdata(pdev, vuart);
+
+ return 0;
+
+err_clk_disable:
+ clk_disable_unprepare(vuart->clk);
+ irq_dispose_mapping(port.port.irq);
+ return rc;
+}
+
+static int aspeed_vuart_remove(struct platform_device *pdev)
+{
+ struct aspeed_vuart *vuart = platform_get_drvdata(pdev);
+
+ aspeed_vuart_set_enabled(vuart, false);
+ serial8250_unregister_port(vuart->line);
+ sysfs_remove_group(&vuart->dev->kobj, &aspeed_vuart_attr_group);
+ clk_disable_unprepare(vuart->clk);
+
+ return 0;
+}
+
+static const struct of_device_id aspeed_vuart_table[] = {
+ { .compatible = "aspeed,ast2400-vuart" },
+ { .compatible = "aspeed,ast2500-vuart" },
+ { },
+};
+
+static struct platform_driver aspeed_vuart_driver = {
+ .driver = {
+ .name = "aspeed-vuart",
+ .of_match_table = aspeed_vuart_table,
+ },
+ .probe = aspeed_vuart_probe,
+ .remove = aspeed_vuart_remove,
+};
+
+module_platform_driver(aspeed_vuart_driver);
+
+MODULE_AUTHOR("Jeremy Kerr <jk@ozlabs.org>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Driver for Aspeed VUART device");
--
2.11.0
^ permalink raw reply related
* Re: [PATCH] mmc: core: add mmc-card hardware reset enable support
From: Jaehoon Chung @ 2017-04-05 4:40 UTC (permalink / raw)
To: Richard Leitner, ulf.hansson-QSEj5FYQhm4dnm+yROfE0A,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8
Cc: shawn.lin-TNX95d0MmH7DzftRWevZcw,
adrian.hunter-ral2JQCrhuEAvxtiuMwx3w,
linus.walleij-QSEj5FYQhm4dnm+yROfE0A,
linux-mmc-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, dev-M/VWbR8SM2SsTnJN9+BGXg
In-Reply-To: <1491315394-7568-1-git-send-email-richard.leitner-WcANXNA0UjBBDgjK7y7TUQ@public.gmane.org>
Hi,
On 04/04/2017 11:16 PM, Richard Leitner wrote:
> Some eMMCs disable their hardware reset line (RST_N) by default. To enable
> it the host must set the corresponding bit in ECSD. An example for such
> a device is the Micron MTFCxGACAANA-4M.
>
> This patch adds a new mmc-card devicetree property to let the host enable
> this feature during card initialization.
>
> Signed-off-by: Richard Leitner <richard.leitner-WcANXNA0UjBBDgjK7y7TUQ@public.gmane.org>
> ---
> Documentation/devicetree/bindings/mmc/mmc-card.txt | 3 +++
> drivers/mmc/core/mmc.c | 21 +++++++++++++++++++++
> 2 files changed, 24 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/mmc/mmc-card.txt b/Documentation/devicetree/bindings/mmc/mmc-card.txt
> index a70fcd6..8590a40 100644
> --- a/Documentation/devicetree/bindings/mmc/mmc-card.txt
> +++ b/Documentation/devicetree/bindings/mmc/mmc-card.txt
> @@ -12,6 +12,9 @@ Required properties:
> Optional properties:
> -broken-hpi : Use this to indicate that the mmc-card has a broken hpi
> implementation, and that hpi should not be used
> +-enable-hw-reset : some eMMC devices have disabled the hw reset functionality
> + (RST_N_FUNCTION) by default. By adding this property the
> + host will enable it during initialization.
As i know, RST_N_FUNCTION is controlled bit[1:0]
0x0 : RST_n disabled (by default)
0x1 : permanently enabled
0x2 : permanently disabled
I think that it needs to add the description about these..
>
> Example:
>
> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
> index b502601..518d0e3 100644
> --- a/drivers/mmc/core/mmc.c
> +++ b/drivers/mmc/core/mmc.c
> @@ -1520,9 +1520,16 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
> int err;
> u32 cid[4];
> u32 rocr;
> + struct device_node *np;
> + bool enable_rst_n = false;
>
> WARN_ON(!host->claimed);
>
> + np = mmc_of_find_child_device(host, 0);
> + if (np && of_device_is_compatible(np, "mmc-card"))
> + enable_rst_n = of_property_read_bool(np, "enable-hw-reset");
> + of_node_put(np);
> +
> /* Set correct bus mode for MMC before attempting init */
> if (!mmc_host_is_spi(host))
> mmc_set_bus_mode(host, MMC_BUSMODE_OPENDRAIN);
> @@ -1810,6 +1817,20 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
> }
> }
>
> + /*
> + * try to enable RST_N if requested
> + * This is needed because some eMMC chips disable this function by
> + * default.
> + */
> + if (enable_rst_n) {
> + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
> + EXT_CSD_RST_N_FUNCTION, EXT_CSD_RST_N_ENABLED,
> + card->ext_csd.generic_cmd6_time);
> + if (err && err != -EBADMSG)
> + pr_warn("%s: Enabling RST_N feature failed\n",
> + mmc_hostname(card->host));
> + }
If enabled hw-reset, it doesn't need to re-enable this bit.
i didn't check the mmc-util..
If mmc-util provides the changing this, the using mmc-util is better than this.
Best Regards,
Jaehoon Chung
> +
> if (!oldcard)
> host->card = card;
>
>
--
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 0/6] clk: qcom: CPU clock driver for msm8996
From: Rajendra Nayak @ 2017-04-05 4:55 UTC (permalink / raw)
To: sboyd-sgV2jX0FEOL9JmXXK+q4OQ, mturquette-rdvid1DuHRBWk0Htik3J/w
Cc: linux-clk-u79uwXL29TY76Z2rM5mHXA,
linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, Rajendra Nayak
This series adds support for the CPU clocks on msm8996 devices.
A previous post of RFC can be found here
https://patchwork.kernel.org/patch/9355925/
Rajendra Nayak (5):
clk: qcom: Make clk_alpha_pll_configure available to modules
clk: qcom: Add CPU clock driver for msm8996
clk: qcom: cpu-8996: Add support to switch to alternate PLL
clk: qcom: cpu-8996: Add support to switch below 600Mhz
arm64: dts: msm8996: Add CPU clock controller node
Taniya Das (1):
clk: qcom: Fix .set_rate to handle alpha PLLs w/wo dynamic update
.../devicetree/bindings/clock/qcom,kryocc.txt | 17 +
arch/arm64/boot/dts/qcom/msm8996.dtsi | 6 +
drivers/clk/qcom/Kconfig | 8 +
drivers/clk/qcom/Makefile | 1 +
drivers/clk/qcom/clk-alpha-pll.c | 72 +++-
drivers/clk/qcom/clk-alpha-pll.h | 5 +
drivers/clk/qcom/clk-cpu-8996.c | 440 +++++++++++++++++++++
7 files changed, 538 insertions(+), 11 deletions(-)
create mode 100644 Documentation/devicetree/bindings/clock/qcom,kryocc.txt
create mode 100644 drivers/clk/qcom/clk-cpu-8996.c
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux Foundation
--
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 1/6] clk: qcom: Fix .set_rate to handle alpha PLLs w/wo dynamic update
From: Rajendra Nayak @ 2017-04-05 4:55 UTC (permalink / raw)
To: sboyd, mturquette
Cc: linux-clk, linux-arm-msm, devicetree, Taniya Das, Rajendra Nayak
In-Reply-To: <1491368129-24721-1-git-send-email-rnayak@codeaurora.org>
From: Taniya Das <tdas@codeaurora.org>
Alpha PLLs which do not support dynamic update feature
need to be explicitly disabled before a rate change.
The ones which do support dynamic update do so within a
single vco range, so add a min/max freq check for such
PLLs so they fall in the vco range.
Signed-off-by: Taniya Das <tdas@codeaurora.org>
Signed-off-by: Rajendra Nayak <rnayak@codeaurora.org>
---
drivers/clk/qcom/clk-alpha-pll.c | 71 +++++++++++++++++++++++++++++++++-------
drivers/clk/qcom/clk-alpha-pll.h | 5 +++
2 files changed, 65 insertions(+), 11 deletions(-)
diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c
index 47a1da3..ecb9e7f 100644
--- a/drivers/clk/qcom/clk-alpha-pll.c
+++ b/drivers/clk/qcom/clk-alpha-pll.c
@@ -376,19 +376,46 @@ static unsigned long alpha_pll_calc_rate(u64 prate, u32 l, u32 a)
return alpha_pll_calc_rate(prate, l, a);
}
-static int clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long prate)
+static int alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long prate,
+ int (*enable)(struct clk_hw *hw),
+ void (*disable)(struct clk_hw *hw))
{
+ bool enabled;
struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
const struct pll_vco *vco;
u32 l, off = pll->offset;
u64 a;
rate = alpha_pll_round_rate(rate, prate, &l, &a);
- vco = alpha_pll_find_vco(pll, rate);
- if (!vco) {
- pr_err("alpha pll not in a valid vco range\n");
- return -EINVAL;
+ enabled = clk_hw_is_enabled(hw);
+
+ if (pll->flags & SUPPORTS_DYNAMIC_UPDATE) {
+ /*
+ * PLLs which support dynamic updates support one single
+ * vco range, between min_rate and max_rate supported
+ */
+ if (rate < pll->min_rate || rate > pll->max_rate) {
+ pr_err("alpha pll rate outside supported min/max range\n");
+ return -EINVAL;
+ }
+ } else {
+ /*
+ * All alpha PLLs which do not support dynamic update,
+ * should be disabled before a vco update.
+ */
+ if (enabled)
+ disable(hw);
+
+ vco = alpha_pll_find_vco(pll, rate);
+ if (!vco) {
+ pr_err("alpha pll not in a valid vco range\n");
+ return -EINVAL;
+ }
+
+ regmap_update_bits(pll->clkr.regmap, off + PLL_USER_CTL,
+ PLL_VCO_MASK << PLL_VCO_SHIFT,
+ vco->val << PLL_VCO_SHIFT);
}
regmap_write(pll->clkr.regmap, off + PLL_L_VAL, l);
@@ -401,16 +428,29 @@ static int clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate,
regmap_write(pll->clkr.regmap, off + PLL_ALPHA_VAL_U, a >> 32);
}
- regmap_update_bits(pll->clkr.regmap, off + PLL_USER_CTL,
- PLL_VCO_MASK << PLL_VCO_SHIFT,
- vco->val << PLL_VCO_SHIFT);
-
regmap_update_bits(pll->clkr.regmap, off + PLL_USER_CTL, PLL_ALPHA_EN,
PLL_ALPHA_EN);
+ if (!(pll->flags & SUPPORTS_DYNAMIC_UPDATE) && enabled)
+ enable(hw);
+
return 0;
}
+static int clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long prate)
+{
+ return alpha_pll_set_rate(hw, rate, prate, clk_alpha_pll_enable,
+ clk_alpha_pll_disable);
+}
+
+static int clk_alpha_pll_hwfsm_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long prate)
+{
+ return alpha_pll_set_rate(hw, rate, prate, clk_alpha_pll_hwfsm_enable,
+ clk_alpha_pll_hwfsm_disable);
+}
+
static long clk_alpha_pll_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
{
@@ -420,6 +460,15 @@ static long clk_alpha_pll_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long min_freq, max_freq;
rate = alpha_pll_round_rate(rate, *prate, &l, &a);
+
+ if (pll->flags & SUPPORTS_DYNAMIC_UPDATE) {
+ if (rate < pll->min_rate)
+ rate = pll->min_rate;
+ else if (rate > pll->max_rate)
+ rate = pll->max_rate;
+ return rate;
+ }
+
if (alpha_pll_find_vco(pll, rate))
return rate;
@@ -445,7 +494,7 @@ static long clk_alpha_pll_round_rate(struct clk_hw *hw, unsigned long rate,
.is_enabled = clk_alpha_pll_hwfsm_is_enabled,
.recalc_rate = clk_alpha_pll_recalc_rate,
.round_rate = clk_alpha_pll_round_rate,
- .set_rate = clk_alpha_pll_set_rate,
+ .set_rate = clk_alpha_pll_hwfsm_set_rate,
};
EXPORT_SYMBOL_GPL(clk_alpha_pll_hwfsm_ops);
diff --git a/drivers/clk/qcom/clk-alpha-pll.h b/drivers/clk/qcom/clk-alpha-pll.h
index d6e1ee2..7aaa11c 100644
--- a/drivers/clk/qcom/clk-alpha-pll.h
+++ b/drivers/clk/qcom/clk-alpha-pll.h
@@ -27,6 +27,8 @@ struct pll_vco {
* struct clk_alpha_pll - phase locked loop (PLL)
* @offset: base address of registers
* @vco_table: array of VCO settings
+ * @min_rate: Minimim rate for PLLs with single VCO range
+ * @max_rate: Maximun rate for PLLs with single VCO range
* @clkr: regmap clock handle
*/
struct clk_alpha_pll {
@@ -37,8 +39,11 @@ struct clk_alpha_pll {
#define SUPPORTS_OFFLINE_REQ BIT(0)
#define SUPPORTS_16BIT_ALPHA BIT(1)
#define SUPPORTS_FSM_MODE BIT(2)
+#define SUPPORTS_DYNAMIC_UPDATE BIT(3)
u8 flags;
+ unsigned long min_rate;
+ unsigned long max_rate;
struct clk_regmap clkr;
};
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux Foundation
^ permalink raw reply related
* [PATCH 2/6] clk: qcom: Make clk_alpha_pll_configure available to modules
From: Rajendra Nayak @ 2017-04-05 4:55 UTC (permalink / raw)
To: sboyd-sgV2jX0FEOL9JmXXK+q4OQ, mturquette-rdvid1DuHRBWk0Htik3J/w
Cc: linux-clk-u79uwXL29TY76Z2rM5mHXA,
linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, Rajendra Nayak
In-Reply-To: <1491368129-24721-1-git-send-email-rnayak-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
Allow clk_alpha_pll_configure to be called from loadable
kernel modules.
Signed-off-by: Rajendra Nayak <rnayak-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
---
drivers/clk/qcom/clk-alpha-pll.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c
index ecb9e7f..7b06e21 100644
--- a/drivers/clk/qcom/clk-alpha-pll.c
+++ b/drivers/clk/qcom/clk-alpha-pll.c
@@ -141,6 +141,7 @@ void clk_alpha_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,
if (pll->flags & SUPPORTS_FSM_MODE)
qcom_pll_set_fsm_mode(regmap, off + PLL_MODE, 6, 0);
}
+EXPORT_SYMBOL_GPL(clk_alpha_pll_configure);
static int clk_alpha_pll_hwfsm_enable(struct clk_hw *hw)
{
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux Foundation
--
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 3/6] clk: qcom: Add CPU clock driver for msm8996
From: Rajendra Nayak @ 2017-04-05 4:55 UTC (permalink / raw)
To: sboyd, mturquette; +Cc: linux-clk, linux-arm-msm, devicetree, Rajendra Nayak
In-Reply-To: <1491368129-24721-1-git-send-email-rnayak@codeaurora.org>
Each of the CPU clusters (Power and Perf) on msm8996 are
clocked via 2 PLLs, a primary and alternate. There are also
2 Mux'es, a primary and secondary all connected together
as shown below
+-------+
XO | |
+------------------>0 |
| |
PLL/2 | SMUX +----+
+------->1 | |
| | | |
| +-------+ | +-------+
| +---->0 |
| | |
+---------------+ | +----------->1 | CPU clk
|Primary PLL +----+ PLL_EARLY | | +------>
| +------+-----------+ +------>2 PMUX |
+---------------+ | | | |
| +------+ | +-->3 |
+--^+ ACD +-----+ | +-------+
+---------------+ +------+ |
|Alt PLL | |
| +---------------------------+
+---------------+ PLL_EARLY
The primary PLL is what drives the CPU clk, except for times
when we are reprogramming the PLL itself (for rate changes) when
we temporarily switch to an alternate PLL. A subsequent patch adds
support to switch between primary and alternate PLL during rate
changes.
The primary PLL operates on a single VCO range, between 600Mhz
and 3Ghz. However the CPUs do support OPPs with frequencies
between 300Mhz and 600Mhz. In order to support running the CPUs
at those frequencies we end up having to lock the PLL at twice
the rate and drive the CPU clk via the PLL/2 output and SMUX.
So for frequencies above 600Mhz we follow the following path
Primary PLL --> PLL_EARLY --> PMUX(1) --> CPU clk
and for frequencies between 300Mhz and 600Mhz we follow
Primary PLL --> PLL/2 --> SMUX(1) --> PMUX(0) --> CPU clk
Support for this is added in a subsequent patch as well.
ACD stands for Adaptive Clock Distribution and is used to
detect voltage droops. We do not add support for ACD as yet.
This can be added at a later point as needed.
Signed-off-by: Rajendra Nayak <rnayak@codeaurora.org>
---
.../devicetree/bindings/clock/qcom,kryocc.txt | 17 +
drivers/clk/qcom/Kconfig | 8 +
drivers/clk/qcom/Makefile | 1 +
drivers/clk/qcom/clk-cpu-8996.c | 388 +++++++++++++++++++++
4 files changed, 414 insertions(+)
create mode 100644 Documentation/devicetree/bindings/clock/qcom,kryocc.txt
create mode 100644 drivers/clk/qcom/clk-cpu-8996.c
diff --git a/Documentation/devicetree/bindings/clock/qcom,kryocc.txt b/Documentation/devicetree/bindings/clock/qcom,kryocc.txt
new file mode 100644
index 0000000..c45de03
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/qcom,kryocc.txt
@@ -0,0 +1,17 @@
+Qualcomm CPUSS clock controller for Kryo CPUs
+----------------------------------------------------
+
+Required properties :
+- compatible : shall contain only one of the following:
+
+ "qcom,apcc-msm8996"
+
+- reg : shall contain base register location and length
+- #clock-cells : shall contain 1
+
+Example:
+ kryocc: clock-controller@6400000 {
+ compatible = "qcom,apcc-msm8996";
+ reg = <0x6400000 0x90000>;
+ #clock-cells = <1>;
+ };
diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig
index 5fb8d74..94d4a8f 100644
--- a/drivers/clk/qcom/Kconfig
+++ b/drivers/clk/qcom/Kconfig
@@ -187,3 +187,11 @@ config MSM_MMCC_8996
Support for the multimedia clock controller on msm8996 devices.
Say Y if you want to support multimedia devices such as display,
graphics, video encode/decode, camera, etc.
+
+config MSM_APCC_8996
+ tristate "MSM8996 CPU Clock Controller"
+ depends on COMMON_CLK_QCOM
+ help
+ Support for the CPU clock controller on msm8996 devices.
+ Say Y if you want to support CPU clock scaling using CPUfreq
+ drivers for dyanmic power management.
diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
index 1c3e222..bc452a6 100644
--- a/drivers/clk/qcom/Makefile
+++ b/drivers/clk/qcom/Makefile
@@ -30,5 +30,6 @@ obj-$(CONFIG_MSM_LCC_8960) += lcc-msm8960.o
obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o
obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o
obj-$(CONFIG_MSM_MMCC_8996) += mmcc-msm8996.o
+obj-$(CONFIG_MSM_APCC_8996) += clk-cpu-8996.o
obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o
obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o
diff --git a/drivers/clk/qcom/clk-cpu-8996.c b/drivers/clk/qcom/clk-cpu-8996.c
new file mode 100644
index 0000000..bc60111
--- /dev/null
+++ b/drivers/clk/qcom/clk-cpu-8996.c
@@ -0,0 +1,388 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include "clk-alpha-pll.h"
+
+#define VCO(a, b, c) { \
+ .val = a,\
+ .min_freq = b,\
+ .max_freq = c,\
+}
+
+#define DIV_2_INDEX 0
+#define PLL_INDEX 1
+#define ACD_INDEX 2
+#define ALT_INDEX 3
+
+/* PLLs */
+
+static const struct alpha_pll_config hfpll_config = {
+ .l = 60,
+ .config_ctl_val = 0x200d4828,
+ .config_ctl_hi_val = 0x006,
+ .pre_div_mask = BIT(12),
+ .post_div_mask = 0x3 << 8,
+ .main_output_mask = BIT(0),
+ .early_output_mask = BIT(3),
+};
+
+static struct clk_alpha_pll perfcl_pll = {
+ .offset = 0x80000,
+ .min_rate = 600000000,
+ .max_rate = 3000000000,
+ .flags = SUPPORTS_DYNAMIC_UPDATE | SUPPORTS_16BIT_ALPHA
+ | SUPPORTS_FSM_MODE,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "perfcl_pll",
+ .parent_names = (const char *[]){ "xo" },
+ .num_parents = 1,
+ .ops = &clk_alpha_pll_hwfsm_ops,
+ },
+};
+
+static struct clk_alpha_pll pwrcl_pll = {
+ .offset = 0x0,
+ .min_rate = 600000000,
+ .max_rate = 3000000000,
+ .flags = SUPPORTS_DYNAMIC_UPDATE | SUPPORTS_16BIT_ALPHA
+ | SUPPORTS_FSM_MODE,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "pwrcl_pll",
+ .parent_names = (const char *[]){ "xo" },
+ .num_parents = 1,
+ .ops = &clk_alpha_pll_hwfsm_ops,
+ },
+};
+
+static const struct pll_vco alt_pll_vco_modes[] = {
+ VCO(3, 250000000, 500000000),
+ VCO(2, 500000000, 750000000),
+ VCO(1, 750000000, 1000000000),
+ VCO(0, 1000000000, 2150400000),
+};
+
+static const struct alpha_pll_config altpll_config = {
+ .l = 16,
+ .vco_val = 0x3 << 20,
+ .vco_mask = 0x3 << 20,
+ .config_ctl_val = 0x4001051b,
+ .post_div_mask = 0x3 << 8,
+ .post_div_val = 0x1,
+ .main_output_mask = BIT(0),
+ .early_output_mask = BIT(3),
+};
+
+static struct clk_alpha_pll perfcl_alt_pll = {
+ .offset = 0x80100,
+ .vco_table = alt_pll_vco_modes,
+ .num_vco = ARRAY_SIZE(alt_pll_vco_modes),
+ .flags = SUPPORTS_OFFLINE_REQ | SUPPORTS_FSM_MODE,
+ .clkr.hw.init = &(struct clk_init_data) {
+ .name = "perfcl_alt_pll",
+ .parent_names = (const char *[]){ "xo" },
+ .num_parents = 1,
+ .ops = &clk_alpha_pll_hwfsm_ops,
+ },
+};
+
+static struct clk_alpha_pll pwrcl_alt_pll = {
+ .offset = 0x100,
+ .vco_table = alt_pll_vco_modes,
+ .num_vco = ARRAY_SIZE(alt_pll_vco_modes),
+ .flags = SUPPORTS_OFFLINE_REQ | SUPPORTS_FSM_MODE,
+ .clkr.hw.init = &(struct clk_init_data) {
+ .name = "pwrcl_alt_pll",
+ .parent_names = (const char *[]){ "xo" },
+ .num_parents = 1,
+ .ops = &clk_alpha_pll_hwfsm_ops,
+ },
+};
+
+/* Mux'es */
+
+struct clk_cpu_8996_mux {
+ u32 reg;
+ u32 shift;
+ u32 width;
+ struct clk_hw *pll;
+ struct clk_regmap clkr;
+};
+
+static inline
+struct clk_cpu_8996_mux *to_clk_cpu_8996_mux_hw(struct clk_hw *hw)
+{
+ return container_of(to_clk_regmap(hw), struct clk_cpu_8996_mux, clkr);
+}
+
+static u8 clk_cpu_8996_mux_get_parent(struct clk_hw *hw)
+{
+ unsigned int val;
+ struct clk_regmap *clkr = to_clk_regmap(hw);
+ struct clk_cpu_8996_mux *cpuclk = to_clk_cpu_8996_mux_hw(hw);
+ unsigned int mask = GENMASK(cpuclk->width - 1, 0);
+
+ regmap_read(clkr->regmap, cpuclk->reg, &val);
+
+ val >>= cpuclk->shift;
+ val &= mask;
+
+ return val;
+}
+
+static int clk_cpu_8996_mux_set_parent(struct clk_hw *hw, u8 index)
+{
+ unsigned int val;
+ struct clk_regmap *clkr = to_clk_regmap(hw);
+ struct clk_cpu_8996_mux *cpuclk = to_clk_cpu_8996_mux_hw(hw);
+ unsigned int mask = GENMASK(cpuclk->width + cpuclk->shift - 1,
+ cpuclk->shift);
+
+ val = index;
+ val = cpuclk->shift;
+
+ return regmap_update_bits(clkr->regmap, cpuclk->reg, mask, val);
+}
+
+static int
+clk_cpu_8996_mux_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
+{
+ struct clk_cpu_8996_mux *cpuclk = to_clk_cpu_8996_mux_hw(hw);
+ struct clk_hw *parent = cpuclk->pll;
+
+ if (!cpuclk->pll)
+ return -EINVAL;
+
+ req->best_parent_rate = clk_hw_round_rate(parent, req->rate);
+ req->best_parent_hw = parent;
+
+ return 0;
+}
+
+const struct clk_ops clk_cpu_8996_mux_ops = {
+ .set_parent = clk_cpu_8996_mux_set_parent,
+ .get_parent = clk_cpu_8996_mux_get_parent,
+ .determine_rate = clk_cpu_8996_mux_determine_rate,
+};
+
+static struct clk_cpu_8996_mux pwrcl_smux = {
+ .reg = 0x40,
+ .shift = 2,
+ .width = 2,
+ .clkr.hw.init = &(struct clk_init_data) {
+ .name = "pwrcl_smux",
+ .parent_names = (const char *[]){
+ "xo",
+ "pwrcl_pll_main",
+ },
+ .num_parents = 2,
+ .ops = &clk_cpu_8996_mux_ops,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_cpu_8996_mux perfcl_smux = {
+ .reg = 0x80040,
+ .shift = 2,
+ .width = 2,
+ .clkr.hw.init = &(struct clk_init_data) {
+ .name = "perfcl_smux",
+ .parent_names = (const char *[]){
+ "xo",
+ "perfcl_pll_main",
+ },
+ .num_parents = 2,
+ .ops = &clk_cpu_8996_mux_ops,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_cpu_8996_mux pwrcl_pmux = {
+ .reg = 0x40,
+ .shift = 0,
+ .width = 2,
+ .pll = &pwrcl_pll.clkr.hw,
+ .clkr.hw.init = &(struct clk_init_data) {
+ .name = "pwrcl_pmux",
+ .parent_names = (const char *[]){
+ "pwrcl_smux",
+ "pwrcl_pll",
+ "pwrcl_pll_acd",
+ "pwrcl_alt_pll",
+ },
+ .num_parents = 4,
+ .ops = &clk_cpu_8996_mux_ops,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_cpu_8996_mux perfcl_pmux = {
+ .reg = 0x80040,
+ .shift = 0,
+ .width = 2,
+ .pll = &perfcl_pll.clkr.hw,
+ .clkr.hw.init = &(struct clk_init_data) {
+ .name = "perfcl_pmux",
+ .parent_names = (const char *[]){
+ "perfcl_smux",
+ "perfcl_pll",
+ "pwrcl_pll_acd",
+ "perfcl_alt_pll",
+ },
+ .num_parents = 4,
+ .ops = &clk_cpu_8996_mux_ops,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static const struct regmap_config cpu_msm8996_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x80210,
+ .fast_io = true,
+ .val_format_endian = REGMAP_ENDIAN_LITTLE,
+};
+
+static const struct of_device_id match_table[] = {
+ { .compatible = "qcom,apcc-msm8996" },
+ {}
+};
+
+struct clk_regmap *clks[] = {
+ /* PLLs */
+ &perfcl_pll.clkr,
+ &pwrcl_pll.clkr,
+ &perfcl_alt_pll.clkr,
+ &pwrcl_alt_pll.clkr,
+ /* MUXs */
+ &perfcl_smux.clkr,
+ &pwrcl_smux.clkr,
+ &perfcl_pmux.clkr,
+ &pwrcl_pmux.clkr,
+};
+
+struct clk_hw_clks {
+ unsigned int num;
+ struct clk_hw *hws[];
+};
+
+static int
+qcom_cpu_clk_msm8996_register_clks(struct device *dev, struct clk_hw_clks *hws,
+ struct regmap *regmap)
+{
+ int i, ret;
+
+ hws->hws[0] = clk_hw_register_fixed_factor(dev, "perfcl_pll_main",
+ "perfcl_pll",
+ CLK_SET_RATE_PARENT, 1, 2);
+ perfcl_smux.pll = hws->hws[0];
+
+ hws->hws[1] = clk_hw_register_fixed_factor(dev, "pwrcl_pll_main",
+ "pwrcl_pll",
+ CLK_SET_RATE_PARENT, 1, 2);
+ pwrcl_smux.pll = hws->hws[1];
+
+ hws->num = 2;
+
+ for (i = 0; i < ARRAY_SIZE(clks); i++) {
+ ret = devm_clk_register_regmap(dev, clks[i]);
+ if (ret)
+ return ret;
+ }
+
+ clk_alpha_pll_configure(&perfcl_pll, regmap, &hfpll_config);
+ clk_alpha_pll_configure(&pwrcl_pll, regmap, &hfpll_config);
+ clk_alpha_pll_configure(&perfcl_alt_pll, regmap, &altpll_config);
+ clk_alpha_pll_configure(&pwrcl_alt_pll, regmap, &altpll_config);
+
+ return ret;
+}
+
+static int qcom_cpu_clk_msm8996_driver_probe(struct platform_device *pdev)
+{
+ int ret;
+ void __iomem *base;
+ struct resource *res;
+ struct regmap *regmap_cpu;
+ struct clk_hw_clks *hws;
+ struct clk_hw_onecell_data *data;
+ struct device *dev = &pdev->dev;
+ struct device_node *node = dev->of_node;
+
+ data = devm_kzalloc(dev, sizeof(*data) + 2 * sizeof(struct clk_hw *),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ hws = devm_kzalloc(dev, sizeof(*hws) + 2 * sizeof(struct clk_hw *),
+ GFP_KERNEL);
+ if (!hws)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ regmap_cpu = devm_regmap_init_mmio(dev, base,
+ &cpu_msm8996_regmap_config);
+ if (IS_ERR(regmap_cpu))
+ return PTR_ERR(regmap_cpu);
+
+ ret = qcom_cpu_clk_msm8996_register_clks(dev, hws, regmap_cpu);
+ if (ret)
+ return ret;
+
+ data->hws[0] = &pwrcl_pmux.clkr.hw;
+ data->hws[1] = &perfcl_pmux.clkr.hw;
+
+ data->num = 2;
+
+ platform_set_drvdata(pdev, hws);
+
+ return of_clk_add_hw_provider(node, of_clk_hw_onecell_get, data);
+}
+
+static int qcom_cpu_clk_msm8996_driver_remove(struct platform_device *pdev)
+{
+ int i;
+ struct device *dev = &pdev->dev;
+ struct clk_hw_clks *hws = platform_get_drvdata(pdev);
+
+ for (i = 0; i < hws->num; i++)
+ clk_hw_unregister_fixed_rate(hws->hws[i]);
+
+ of_clk_del_provider(dev->of_node);
+
+ return 0;
+}
+
+static struct platform_driver qcom_cpu_clk_msm8996_driver = {
+ .probe = qcom_cpu_clk_msm8996_driver_probe,
+ .remove = qcom_cpu_clk_msm8996_driver_remove,
+ .driver = {
+ .name = "qcom-apcc-msm8996",
+ .of_match_table = match_table,
+ },
+};
+
+module_platform_driver(qcom_cpu_clk_msm8996_driver);
+
+MODULE_ALIAS("platform:apcc-msm8996");
+MODULE_DESCRIPTION("QCOM MSM8996 CPU clock Driver");
+MODULE_LICENSE("GPL v2");
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux Foundation
^ 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