summaryrefslogtreecommitdiff
path: root/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0106-enable-AST2600-I3C.patch
diff options
context:
space:
mode:
Diffstat (limited to 'meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0106-enable-AST2600-I3C.patch')
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0106-enable-AST2600-I3C.patch910
1 files changed, 0 insertions, 910 deletions
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0106-enable-AST2600-I3C.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0106-enable-AST2600-I3C.patch
deleted file mode 100644
index 4cc88cbeb..000000000
--- a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0106-enable-AST2600-I3C.patch
+++ /dev/null
@@ -1,910 +0,0 @@
-From c3be31a18ef1755c86c169b9d0e54bdbbea99d8a Mon Sep 17 00:00:00 2001
-From: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
-Date: Wed, 6 May 2020 18:11:29 -0700
-Subject: [PATCH] enable AST2600 I3C
-
-This commit ports I3C related changes from Aspeed SDK v00.05.05.
-It also includes Vitor's I3C cdev implementation which isn't
-upstreamed yet so it should be refined later.
-
-Signed-off-by: Ryan Chen <ryan_chen@aspeedtech.com>
-Signed-off-by: Vitor Soares <soares@synopsys.com>
-Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
----
- arch/arm/boot/dts/aspeed-g6.dtsi | 19 +-
- drivers/clk/clk-ast2600.c | 30 ++-
- drivers/i3c/Kconfig | 15 ++
- drivers/i3c/Makefile | 1 +
- drivers/i3c/i3cdev.c | 428 ++++++++++++++++++++++++++++++
- drivers/i3c/internals.h | 2 +
- drivers/i3c/master.c | 39 ++-
- drivers/i3c/master/Kconfig | 5 +
- drivers/i3c/master/Makefile | 1 +
- drivers/i3c/master/aspeed-i3c-global.c | 77 ++++++
- include/dt-bindings/clock/ast2600-clock.h | 3 +-
- include/uapi/linux/i3c/i3cdev.h | 38 +++
- 12 files changed, 634 insertions(+), 24 deletions(-)
- create mode 100644 drivers/i3c/i3cdev.c
- create mode 100644 drivers/i3c/master/aspeed-i3c-global.c
- create mode 100644 include/uapi/linux/i3c/i3cdev.h
-
-diff --git a/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi
-index 91f431e419d9..33e1b0ef24f0 100644
---- a/arch/arm/boot/dts/aspeed-g6.dtsi
-+++ b/arch/arm/boot/dts/aspeed-g6.dtsi
-@@ -1091,13 +1091,20 @@
- };
-
- &i3c {
-+ i3cglobal: i3cg@0 {
-+ reg = <0x0 0x1000>;
-+ compatible = "aspeed,ast2600-i3c-global";
-+ resets = <&syscon ASPEED_RESET_I3C_DMA>;
-+ status = "disabled";
-+ };
-+
- i3c0: i3c0@2000 {
- #address-cells = <1>;
- #size-cells = <0>;
- #interrupt-cells = <1>;
- reg = <0x2000 0x1000>;
- compatible = "snps,dw-i3c-master-1.00a";
-- clocks = <&syscon ASPEED_CLK_APB2>;
-+ clocks = <&syscon ASPEED_CLK_GATE_I3C0CLK>;
- resets = <&syscon ASPEED_RESET_I3C0>;
- bus-frequency = <100000>;
- interrupts = <GIC_SPI 102 IRQ_TYPE_LEVEL_HIGH>;
-@@ -1110,7 +1117,7 @@
- #interrupt-cells = <1>;
- reg = <0x3000 0x1000>;
- compatible = "snps,dw-i3c-master-1.00a";
-- clocks = <&syscon ASPEED_CLK_APB2>;
-+ clocks = <&syscon ASPEED_CLK_GATE_I3C1CLK>;
- resets = <&syscon ASPEED_RESET_I3C1>;
- bus-frequency = <100000>;
- interrupts = <GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH>;
-@@ -1123,7 +1130,7 @@
- #interrupt-cells = <1>;
- reg = <0x4000 0x1000>;
- compatible = "snps,dw-i3c-master-1.00a";
-- clocks = <&syscon ASPEED_CLK_APB2>;
-+ clocks = <&syscon ASPEED_CLK_GATE_I3C2CLK>;
- resets = <&syscon ASPEED_RESET_I3C2>;
- bus-frequency = <100000>;
- interrupts = <GIC_SPI 104 IRQ_TYPE_LEVEL_HIGH>;
-@@ -1138,7 +1145,7 @@
- #interrupt-cells = <1>;
- reg = <0x5000 0x1000>;
- compatible = "snps,dw-i3c-master-1.00a";
-- clocks = <&syscon ASPEED_CLK_APB2>;
-+ clocks = <&syscon ASPEED_CLK_GATE_I3C3CLK>;
- resets = <&syscon ASPEED_RESET_I3C3>;
- bus-frequency = <100000>;
- interrupts = <GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH>;
-@@ -1153,7 +1160,7 @@
- #interrupt-cells = <1>;
- reg = <0x6000 0x1000>;
- compatible = "snps,dw-i3c-master-1.00a";
-- clocks = <&syscon ASPEED_CLK_APB2>;
-+ clocks = <&syscon ASPEED_CLK_GATE_I3C4CLK>;
- resets = <&syscon ASPEED_RESET_I3C4>;
- bus-frequency = <100000>;
- interrupts = <GIC_SPI 106 IRQ_TYPE_LEVEL_HIGH>;
-@@ -1168,7 +1175,7 @@
- #interrupt-cells = <1>;
- reg = <0x7000 0x1000>;
- compatible = "snps,dw-i3c-master-1.00a";
-- clocks = <&syscon ASPEED_CLK_APB2>;
-+ clocks = <&syscon ASPEED_CLK_GATE_I3C5CLK>;
- resets = <&syscon ASPEED_RESET_I3C5>;
- bus-frequency = <100000>;
- interrupts = <GIC_SPI 107 IRQ_TYPE_LEVEL_HIGH>;
-diff --git a/drivers/clk/clk-ast2600.c b/drivers/clk/clk-ast2600.c
-index e07326544fdc..a30dcbb1a3fd 100644
---- a/drivers/clk/clk-ast2600.c
-+++ b/drivers/clk/clk-ast2600.c
-@@ -112,14 +112,14 @@ static const struct aspeed_gate_data aspeed_g6_gates[] = {
- [ASPEED_CLK_GATE_LHCCLK] = { 37, -1, "lhclk-gate", "lhclk", 0 }, /* LPC master/LPC+ */
- /* Reserved 38 RSA: no longer used */
- /* Reserved 39 */
-- [ASPEED_CLK_GATE_I3C0CLK] = { 40, 40, "i3c0clk-gate", NULL, 0 }, /* I3C0 */
-- [ASPEED_CLK_GATE_I3C1CLK] = { 41, 41, "i3c1clk-gate", NULL, 0 }, /* I3C1 */
-- [ASPEED_CLK_GATE_I3C2CLK] = { 42, 42, "i3c2clk-gate", NULL, 0 }, /* I3C2 */
-- [ASPEED_CLK_GATE_I3C3CLK] = { 43, 43, "i3c3clk-gate", NULL, 0 }, /* I3C3 */
-- [ASPEED_CLK_GATE_I3C4CLK] = { 44, 44, "i3c4clk-gate", NULL, 0 }, /* I3C4 */
-- [ASPEED_CLK_GATE_I3C5CLK] = { 45, 45, "i3c5clk-gate", NULL, 0 }, /* I3C5 */
-- [ASPEED_CLK_GATE_I3C6CLK] = { 46, 46, "i3c6clk-gate", NULL, 0 }, /* I3C6 */
-- [ASPEED_CLK_GATE_I3C7CLK] = { 47, 47, "i3c7clk-gate", NULL, 0 }, /* I3C7 */
-+ [ASPEED_CLK_GATE_I3C0CLK] = { 40, 40, "i3c0clk-gate", "i3cclk", 0 }, /* I3C0 */
-+ [ASPEED_CLK_GATE_I3C1CLK] = { 41, 41, "i3c1clk-gate", "i3cclk", 0 }, /* I3C1 */
-+ [ASPEED_CLK_GATE_I3C2CLK] = { 42, 42, "i3c2clk-gate", "i3cclk", 0 }, /* I3C2 */
-+ [ASPEED_CLK_GATE_I3C3CLK] = { 43, 43, "i3c3clk-gate", "i3cclk", 0 }, /* I3C3 */
-+ [ASPEED_CLK_GATE_I3C4CLK] = { 44, 44, "i3c4clk-gate", "i3cclk", 0 }, /* I3C4 */
-+ [ASPEED_CLK_GATE_I3C5CLK] = { 45, 45, "i3c5clk-gate", "i3cclk", 0 }, /* I3C5 */
-+ [ASPEED_CLK_GATE_I3C6CLK] = { 46, 46, "i3c6clk-gate", "i3cclk", 0 }, /* I3C6 */
-+ [ASPEED_CLK_GATE_I3C7CLK] = { 47, 47, "i3c7clk-gate", "i3cclk", 0 }, /* I3C7 */
- [ASPEED_CLK_GATE_UART1CLK] = { 48, -1, "uart1clk-gate", "uart", 0 }, /* UART1 */
- [ASPEED_CLK_GATE_UART2CLK] = { 49, -1, "uart2clk-gate", "uart", 0 }, /* UART2 */
- [ASPEED_CLK_GATE_UART3CLK] = { 50, -1, "uart3clk-gate", "uart", 0 }, /* UART3 */
-@@ -749,6 +749,20 @@ static void __init aspeed_g6_cc(struct regmap *map)
- /* USB 2.0 port1 phy 40MHz clock */
- hw = clk_hw_register_fixed_rate(NULL, "usb-phy-40m", NULL, 0, 40000000);
- aspeed_g6_clk_data->hws[ASPEED_CLK_USBPHY_40M] = hw;
-+
-+ /* i3c clock source */
-+ regmap_read(map, ASPEED_G6_CLK_SELECTION5, &val);
-+ if(val & BIT(31)) {
-+ val = (val >> 28) & 0x7;
-+ if(val)
-+ div = val + 1;
-+ else
-+ div = val + 2;
-+ hw = clk_hw_register_fixed_factor(NULL, "i3cclk", "apll", 0, 1, div);
-+ } else {
-+ hw = clk_hw_register_fixed_factor(NULL, "i3cclk", "ahb", 0, 1, 1);
-+ }
-+ aspeed_g6_clk_data->hws[ASPEED_CLK_I3C] = hw;
- };
-
- static void __init aspeed_g6_cc_init(struct device_node *np)
-diff --git a/drivers/i3c/Kconfig b/drivers/i3c/Kconfig
-index 30a441506f61..01642768ab5f 100644
---- a/drivers/i3c/Kconfig
-+++ b/drivers/i3c/Kconfig
-@@ -20,5 +20,20 @@ menuconfig I3C
- will be called i3c.
-
- if I3C
-+
-+config I3CDEV
-+ tristate "I3C device interface"
-+ depends on I3C
-+ help
-+ Say Y here to use i3c-* device files, usually found in the /dev
-+ directory on your system. They make it possible to have user-space
-+ programs use the I3C devices.
-+
-+ This support is also available as a module. If so, the module
-+ will be called i3cdev.
-+
-+ Note that this application programming interface is EXPERIMENTAL
-+ and hence SUBJECT TO CHANGE WITHOUT NOTICE while it stabilizes.
-+
- source "drivers/i3c/master/Kconfig"
- endif # I3C
-diff --git a/drivers/i3c/Makefile b/drivers/i3c/Makefile
-index 11982efbc6d9..606d422841b2 100644
---- a/drivers/i3c/Makefile
-+++ b/drivers/i3c/Makefile
-@@ -1,4 +1,5 @@
- # SPDX-License-Identifier: GPL-2.0
- i3c-y := device.o master.o
- obj-$(CONFIG_I3C) += i3c.o
-+obj-$(CONFIG_I3CDEV) += i3cdev.o
- obj-$(CONFIG_I3C) += master/
-diff --git a/drivers/i3c/i3cdev.c b/drivers/i3c/i3cdev.c
-new file mode 100644
-index 000000000000..07f5641a902d
---- /dev/null
-+++ b/drivers/i3c/i3cdev.c
-@@ -0,0 +1,428 @@
-+// SPDX-License-Identifier: GPL-2.0
-+/*
-+ * Copyright (c) 2019 Synopsys, Inc. and/or its affiliates.
-+ *
-+ * Author: Vitor Soares <soares@synopsys.com>
-+ */
-+
-+#include <linux/cdev.h>
-+#include <linux/compat.h>
-+#include <linux/device.h>
-+#include <linux/fs.h>
-+#include <linux/init.h>
-+#include <linux/jiffies.h>
-+#include <linux/kernel.h>
-+#include <linux/list.h>
-+#include <linux/module.h>
-+#include <linux/notifier.h>
-+#include <linux/slab.h>
-+#include <linux/uaccess.h>
-+
-+#include <linux/i3c/i3cdev.h>
-+
-+#include "internals.h"
-+
-+struct i3cdev_data {
-+ struct list_head list;
-+ struct i3c_device *i3c;
-+ struct cdev cdev;
-+ struct device *dev;
-+ int id;
-+};
-+
-+static DEFINE_IDA(i3cdev_ida);
-+static dev_t i3cdev_number;
-+#define I3C_MINORS 16 /* 16 I3C devices supported for now */
-+
-+static LIST_HEAD(i3cdev_list);
-+static DEFINE_SPINLOCK(i3cdev_list_lock);
-+
-+static struct i3cdev_data *i3cdev_get_by_i3c(struct i3c_device *i3c)
-+{
-+ struct i3cdev_data *i3cdev;
-+
-+ spin_lock(&i3cdev_list_lock);
-+ list_for_each_entry(i3cdev, &i3cdev_list, list) {
-+ if (i3cdev->i3c == i3c)
-+ goto found;
-+ }
-+
-+ i3cdev = NULL;
-+
-+found:
-+ spin_unlock(&i3cdev_list_lock);
-+ return i3cdev;
-+}
-+
-+static struct i3cdev_data *get_free_i3cdev(struct i3c_device *i3c)
-+{
-+ struct i3cdev_data *i3cdev;
-+ int id;
-+
-+ id = ida_simple_get(&i3cdev_ida, 0, I3C_MINORS, GFP_KERNEL);
-+ if (id < 0) {
-+ pr_err("i3cdev: no minor number available!\n");
-+ return ERR_PTR(id);
-+ }
-+
-+ i3cdev = kzalloc(sizeof(*i3cdev), GFP_KERNEL);
-+ if (!i3cdev) {
-+ ida_simple_remove(&i3cdev_ida, id);
-+ return ERR_PTR(-ENOMEM);
-+ }
-+
-+ i3cdev->i3c = i3c;
-+ i3cdev->id = id;
-+
-+ spin_lock(&i3cdev_list_lock);
-+ list_add_tail(&i3cdev->list, &i3cdev_list);
-+ spin_unlock(&i3cdev_list_lock);
-+
-+ return i3cdev;
-+}
-+
-+static void put_i3cdev(struct i3cdev_data *i3cdev)
-+{
-+ spin_lock(&i3cdev_list_lock);
-+ list_del(&i3cdev->list);
-+ spin_unlock(&i3cdev_list_lock);
-+ kfree(i3cdev);
-+}
-+
-+static ssize_t
-+i3cdev_read(struct file *file, char __user *buf, size_t count, loff_t *f_pos)
-+{
-+ struct i3c_device *i3c = file->private_data;
-+ struct i3c_priv_xfer xfers = {
-+ .rnw = true,
-+ .len = count,
-+ };
-+ char *tmp;
-+ int ret;
-+
-+ tmp = kzalloc(count, GFP_KERNEL);
-+ if (!tmp)
-+ return -ENOMEM;
-+
-+ xfers.data.in = tmp;
-+
-+ dev_dbg(&i3c->dev, "Reading %zu bytes.\n", count);
-+
-+ ret = i3c_device_do_priv_xfers(i3c, &xfers, 1);
-+ if (!ret)
-+ ret = copy_to_user(buf, tmp, count) ? -EFAULT : ret;
-+
-+ kfree(tmp);
-+ return ret;
-+}
-+
-+static ssize_t
-+i3cdev_write(struct file *file, const char __user *buf, size_t count,
-+ loff_t *f_pos)
-+{
-+ struct i3c_device *i3c = file->private_data;
-+ struct i3c_priv_xfer xfers = {
-+ .rnw = false,
-+ .len = count,
-+ };
-+ char *tmp;
-+ int ret;
-+
-+ tmp = memdup_user(buf, count);
-+ if (IS_ERR(tmp))
-+ return PTR_ERR(tmp);
-+
-+ xfers.data.out = tmp;
-+
-+ dev_dbg(&i3c->dev, "Writing %zu bytes.\n", count);
-+
-+ ret = i3c_device_do_priv_xfers(i3c, &xfers, 1);
-+ kfree(tmp);
-+ return (!ret) ? count : ret;
-+}
-+
-+static int
-+i3cdev_do_priv_xfer(struct i3c_device *dev, struct i3c_ioc_priv_xfer *xfers,
-+ unsigned int nxfers)
-+{
-+ struct i3c_priv_xfer *k_xfers;
-+ u8 **data_ptrs;
-+ int i, ret = 0;
-+
-+ k_xfers = kcalloc(nxfers, sizeof(*k_xfers), GFP_KERNEL);
-+ if (!k_xfers)
-+ return -ENOMEM;
-+
-+ data_ptrs = kcalloc(nxfers, sizeof(*data_ptrs), GFP_KERNEL);
-+ if (!data_ptrs) {
-+ ret = -ENOMEM;
-+ goto err_free_k_xfer;
-+ }
-+
-+ for (i = 0; i < nxfers; i++) {
-+ data_ptrs[i] = memdup_user((const u8 __user *)
-+ (uintptr_t)xfers[i].data,
-+ xfers[i].len);
-+ if (IS_ERR(data_ptrs[i])) {
-+ ret = PTR_ERR(data_ptrs[i]);
-+ break;
-+ }
-+
-+ k_xfers[i].len = xfers[i].len;
-+ if (xfers[i].rnw) {
-+ k_xfers[i].rnw = true;
-+ k_xfers[i].data.in = data_ptrs[i];
-+ } else {
-+ k_xfers[i].rnw = false;
-+ k_xfers[i].data.out = data_ptrs[i];
-+ }
-+ }
-+
-+ if (ret < 0) {
-+ i--;
-+ goto err_free_mem;
-+ }
-+
-+ ret = i3c_device_do_priv_xfers(dev, k_xfers, nxfers);
-+ if (ret)
-+ goto err_free_mem;
-+
-+ for (i = 0; i < nxfers; i++) {
-+ if (xfers[i].rnw) {
-+ if (copy_to_user((void __user *)(uintptr_t)xfers[i].data,
-+ data_ptrs[i], xfers[i].len))
-+ ret = -EFAULT;
-+ }
-+ }
-+
-+err_free_mem:
-+ for (; i >= 0; i--)
-+ kfree(data_ptrs[i]);
-+ kfree(data_ptrs);
-+err_free_k_xfer:
-+ kfree(k_xfers);
-+ return ret;
-+}
-+
-+static struct i3c_ioc_priv_xfer *
-+i3cdev_get_ioc_priv_xfer(unsigned int cmd, struct i3c_ioc_priv_xfer *u_xfers,
-+ unsigned int *nxfers)
-+{
-+ u32 tmp = _IOC_SIZE(cmd);
-+
-+ if ((tmp % sizeof(struct i3c_ioc_priv_xfer)) != 0)
-+ return ERR_PTR(-EINVAL);
-+
-+ *nxfers = tmp / sizeof(struct i3c_ioc_priv_xfer);
-+ if (*nxfers == 0)
-+ return NULL;
-+
-+ return memdup_user(u_xfers, tmp);
-+}
-+
-+static int
-+i3cdev_ioc_priv_xfer(struct i3c_device *i3c, unsigned int cmd,
-+ struct i3c_ioc_priv_xfer *u_xfers)
-+{
-+ struct i3c_ioc_priv_xfer *k_xfers;
-+ unsigned int nxfers;
-+ int ret;
-+
-+ k_xfers = i3cdev_get_ioc_priv_xfer(cmd, u_xfers, &nxfers);
-+ if (IS_ERR_OR_NULL(k_xfers))
-+ return PTR_ERR(k_xfers);
-+
-+ ret = i3cdev_do_priv_xfer(i3c, k_xfers, nxfers);
-+
-+ kfree(k_xfers);
-+
-+ return ret;
-+}
-+
-+static long
-+i3cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-+{
-+ struct i3c_device *i3c = file->private_data;
-+
-+ dev_dbg(&i3c->dev, "ioctl, cmd=0x%02x, arg=0x%02lx\n", cmd, arg);
-+
-+ if (_IOC_TYPE(cmd) != I3C_DEV_IOC_MAGIC)
-+ return -ENOTTY;
-+
-+ /* Check command number and direction */
-+ if (_IOC_NR(cmd) == _IOC_NR(I3C_IOC_PRIV_XFER(0)) &&
-+ _IOC_DIR(cmd) == (_IOC_READ | _IOC_WRITE))
-+ return i3cdev_ioc_priv_xfer(i3c, cmd,
-+ (struct i3c_ioc_priv_xfer __user *)arg);
-+
-+ return 0;
-+}
-+
-+static int i3cdev_open(struct inode *inode, struct file *file)
-+{
-+ struct i3cdev_data *i3cdev = container_of(inode->i_cdev,
-+ struct i3cdev_data,
-+ cdev);
-+
-+ file->private_data = i3cdev->i3c;
-+
-+ return 0;
-+}
-+
-+static int i3cdev_release(struct inode *inode, struct file *file)
-+{
-+ file->private_data = NULL;
-+
-+ return 0;
-+}
-+
-+static const struct file_operations i3cdev_fops = {
-+ .owner = THIS_MODULE,
-+ .read = i3cdev_read,
-+ .write = i3cdev_write,
-+ .unlocked_ioctl = i3cdev_ioctl,
-+ .open = i3cdev_open,
-+ .release = i3cdev_release,
-+};
-+
-+/* ------------------------------------------------------------------------- */
-+
-+static struct class *i3cdev_class;
-+
-+static int i3cdev_attach(struct device *dev, void *dummy)
-+{
-+ struct i3cdev_data *i3cdev;
-+ struct i3c_device *i3c;
-+ int res;
-+
-+ if (dev->type == &i3c_masterdev_type || dev->driver)
-+ return 0;
-+
-+ i3c = dev_to_i3cdev(dev);
-+
-+ /* Get a device */
-+ i3cdev = get_free_i3cdev(i3c);
-+ if (IS_ERR(i3cdev))
-+ return PTR_ERR(i3cdev);
-+
-+ cdev_init(&i3cdev->cdev, &i3cdev_fops);
-+ i3cdev->cdev.owner = THIS_MODULE;
-+ res = cdev_add(&i3cdev->cdev,
-+ MKDEV(MAJOR(i3cdev_number), i3cdev->id), 1);
-+ if (res)
-+ goto error_cdev;
-+
-+ /* register this i3c device with the driver core */
-+ i3cdev->dev = device_create(i3cdev_class, &i3c->dev,
-+ MKDEV(MAJOR(i3cdev_number), i3cdev->id),
-+ NULL, "i3c-%s", dev_name(&i3c->dev));
-+ if (IS_ERR(i3cdev->dev)) {
-+ res = PTR_ERR(i3cdev->dev);
-+ goto error;
-+ }
-+ pr_debug("i3cdev: I3C device [%s] registered as minor %d\n",
-+ dev_name(&i3c->dev), i3cdev->id);
-+ return 0;
-+
-+error:
-+ cdev_del(&i3cdev->cdev);
-+error_cdev:
-+ put_i3cdev(i3cdev);
-+ return res;
-+}
-+
-+static int i3cdev_detach(struct device *dev, void *dummy)
-+{
-+ struct i3cdev_data *i3cdev;
-+ struct i3c_device *i3c;
-+
-+ if (dev->type == &i3c_masterdev_type)
-+ return 0;
-+
-+ i3c = dev_to_i3cdev(dev);
-+
-+ i3cdev = i3cdev_get_by_i3c(i3c);
-+ if (!i3cdev)
-+ return 0;
-+
-+ cdev_del(&i3cdev->cdev);
-+ device_destroy(i3cdev_class, MKDEV(MAJOR(i3cdev_number), i3cdev->id));
-+ ida_simple_remove(&i3cdev_ida, i3cdev->id);
-+ put_i3cdev(i3cdev);
-+
-+ pr_debug("i3cdev: device [%s] unregistered\n", dev_name(&i3c->dev));
-+
-+ return 0;
-+}
-+
-+static int i3cdev_notifier_call(struct notifier_block *nb,
-+ unsigned long action,
-+ void *data)
-+{
-+ struct device *dev = data;
-+
-+ switch (action) {
-+ case BUS_NOTIFY_ADD_DEVICE:
-+ case BUS_NOTIFY_UNBOUND_DRIVER:
-+ return i3cdev_attach(dev, NULL);
-+ case BUS_NOTIFY_DEL_DEVICE:
-+ case BUS_NOTIFY_BOUND_DRIVER:
-+ return i3cdev_detach(dev, NULL);
-+ }
-+
-+ return 0;
-+}
-+
-+static struct notifier_block i3c_notifier = {
-+ .notifier_call = i3cdev_notifier_call,
-+};
-+
-+static int __init i3cdev_init(void)
-+{
-+ int res;
-+
-+ /* Dynamically request unused major number */
-+ res = alloc_chrdev_region(&i3cdev_number, 0, I3C_MINORS, "i3c");
-+ if (res)
-+ goto out;
-+
-+ /* Create a classe to populate sysfs entries*/
-+ i3cdev_class = class_create(THIS_MODULE, "i3cdev");
-+ if (IS_ERR(i3cdev_class)) {
-+ res = PTR_ERR(i3cdev_class);
-+ goto out_unreg_chrdev;
-+ }
-+
-+ /* Keep track of busses which have devices to add or remove later */
-+ res = bus_register_notifier(&i3c_bus_type, &i3c_notifier);
-+ if (res)
-+ goto out_unreg_class;
-+
-+ /* Bind to already existing device without driver right away */
-+ i3c_for_each_dev(NULL, i3cdev_attach);
-+
-+ return 0;
-+
-+out_unreg_class:
-+ class_destroy(i3cdev_class);
-+out_unreg_chrdev:
-+ unregister_chrdev_region(i3cdev_number, I3C_MINORS);
-+out:
-+ pr_err("%s: Driver Initialisation failed\n", __FILE__);
-+ return res;
-+}
-+
-+static void __exit i3cdev_exit(void)
-+{
-+ bus_unregister_notifier(&i3c_bus_type, &i3c_notifier);
-+ i3c_for_each_dev(NULL, i3cdev_detach);
-+ class_destroy(i3cdev_class);
-+ unregister_chrdev_region(i3cdev_number, I3C_MINORS);
-+}
-+
-+MODULE_AUTHOR("Vitor Soares <soares@synopsys.com>");
-+MODULE_DESCRIPTION("I3C /dev entries driver");
-+MODULE_LICENSE("GPL");
-+
-+module_init(i3cdev_init);
-+module_exit(i3cdev_exit);
-diff --git a/drivers/i3c/internals.h b/drivers/i3c/internals.h
-index 86b7b44cfca2..a6deedf5ce06 100644
---- a/drivers/i3c/internals.h
-+++ b/drivers/i3c/internals.h
-@@ -11,6 +11,7 @@
- #include <linux/i3c/master.h>
-
- extern struct bus_type i3c_bus_type;
-+extern const struct device_type i3c_masterdev_type;
-
- void i3c_bus_normaluse_lock(struct i3c_bus *bus);
- void i3c_bus_normaluse_unlock(struct i3c_bus *bus);
-@@ -23,4 +24,5 @@ int i3c_dev_enable_ibi_locked(struct i3c_dev_desc *dev);
- int i3c_dev_request_ibi_locked(struct i3c_dev_desc *dev,
- const struct i3c_ibi_setup *req);
- void i3c_dev_free_ibi_locked(struct i3c_dev_desc *dev);
-+int i3c_for_each_dev(void *data, int (*fn)(struct device *, void *));
- #endif /* I3C_INTERNAL_H */
-diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
-index 5c051dba32a5..53910fb6702e 100644
---- a/drivers/i3c/master.c
-+++ b/drivers/i3c/master.c
-@@ -321,6 +321,7 @@ struct bus_type i3c_bus_type = {
- .probe = i3c_device_probe,
- .remove = i3c_device_remove,
- };
-+EXPORT_SYMBOL_GPL(i3c_bus_type);
-
- static enum i3c_addr_slot_status
- i3c_bus_get_addr_slot_status(struct i3c_bus *bus, u16 addr)
-@@ -523,11 +524,12 @@ static void i3c_masterdev_release(struct device *dev)
- of_node_put(dev->of_node);
- }
-
--static const struct device_type i3c_masterdev_type = {
-+const struct device_type i3c_masterdev_type = {
- .groups = i3c_masterdev_groups,
- };
-+EXPORT_SYMBOL_GPL(i3c_masterdev_type);
-
--int i3c_bus_set_mode(struct i3c_bus *i3cbus, enum i3c_bus_mode mode,
-+static int i3c_bus_set_mode(struct i3c_bus *i3cbus, enum i3c_bus_mode mode,
- unsigned long max_i2c_scl_rate)
- {
- struct i3c_master_controller *master = i3c_bus_to_i3c_master(i3cbus);
-@@ -1410,19 +1412,19 @@ static void i3c_master_detach_i2c_dev(struct i2c_dev_desc *dev)
- master->ops->detach_i2c_dev(dev);
- }
-
--static void i3c_master_pre_assign_dyn_addr(struct i3c_dev_desc *dev)
-+static int i3c_master_pre_assign_dyn_addr(struct i3c_dev_desc *dev)
- {
- struct i3c_master_controller *master = i3c_dev_get_master(dev);
- int ret;
-
- if (!dev->boardinfo || !dev->boardinfo->init_dyn_addr ||
- !dev->boardinfo->static_addr)
-- return;
-+ return -1;
-
- ret = i3c_master_setdasa_locked(master, dev->info.static_addr,
- dev->boardinfo->init_dyn_addr);
- if (ret)
-- return;
-+ return ret;
-
- dev->info.dyn_addr = dev->boardinfo->init_dyn_addr;
- ret = i3c_master_reattach_i3c_dev(dev, 0);
-@@ -1433,10 +1435,11 @@ static void i3c_master_pre_assign_dyn_addr(struct i3c_dev_desc *dev)
- if (ret)
- goto err_rstdaa;
-
-- return;
-+ return 0;
-
- err_rstdaa:
- i3c_master_rstdaa_locked(master, dev->boardinfo->init_dyn_addr);
-+ return ret;
- }
-
- static void
-@@ -1631,7 +1634,7 @@ static int i3c_master_bus_init(struct i3c_master_controller *master)
- enum i3c_addr_slot_status status;
- struct i2c_dev_boardinfo *i2cboardinfo;
- struct i3c_dev_boardinfo *i3cboardinfo;
-- struct i3c_dev_desc *i3cdev;
-+ struct i3c_dev_desc *i3cdev, *i3ctmp;
- struct i2c_dev_desc *i2cdev;
- int ret;
-
-@@ -1730,8 +1733,14 @@ static int i3c_master_bus_init(struct i3c_master_controller *master)
- * Pre-assign dynamic address and retrieve device information if
- * needed.
- */
-- i3c_bus_for_each_i3cdev(&master->bus, i3cdev)
-- i3c_master_pre_assign_dyn_addr(i3cdev);
-+ list_for_each_entry_safe(i3cdev, i3ctmp, &master->bus.devs.i3c,
-+ common.node) {
-+ ret = i3c_master_pre_assign_dyn_addr(i3cdev);
-+ if (ret) {
-+ i3c_master_detach_i3c_dev(i3cdev);
-+ i3c_master_free_i3c_dev(i3cdev);
-+ }
-+ }
-
- ret = i3c_master_do_daa(master);
- if (ret)
-@@ -2638,6 +2647,18 @@ void i3c_dev_free_ibi_locked(struct i3c_dev_desc *dev)
- dev->ibi = NULL;
- }
-
-+int i3c_for_each_dev(void *data, int (*fn)(struct device *, void *))
-+{
-+ int res;
-+
-+ mutex_lock(&i3c_core_lock);
-+ res = bus_for_each_dev(&i3c_bus_type, NULL, data, fn);
-+ mutex_unlock(&i3c_core_lock);
-+
-+ return res;
-+}
-+EXPORT_SYMBOL_GPL(i3c_for_each_dev);
-+
- static int __init i3c_init(void)
- {
- return bus_register(&i3c_bus_type);
-diff --git a/drivers/i3c/master/Kconfig b/drivers/i3c/master/Kconfig
-index 4e80a1fcbf91..693f9aba2b17 100644
---- a/drivers/i3c/master/Kconfig
-+++ b/drivers/i3c/master/Kconfig
-@@ -21,3 +21,8 @@ config DW_I3C_MASTER
-
- This driver can also be built as a module. If so, the module
- will be called dw-i3c-master.
-+
-+config ASPEED_I3C_GLOBAL
-+ tristate "ASPEED I3C global driver"
-+ depends on I3C
-+ depends on MACH_ASPEED_G6
-diff --git a/drivers/i3c/master/Makefile b/drivers/i3c/master/Makefile
-index 7eea9e086144..b5ec8e8dd622 100644
---- a/drivers/i3c/master/Makefile
-+++ b/drivers/i3c/master/Makefile
-@@ -1,3 +1,4 @@
- # SPDX-License-Identifier: GPL-2.0-only
-+obj-$(CONFIG_ASPEED_I3C_GLOBAL) += aspeed-i3c-global.o
- obj-$(CONFIG_CDNS_I3C_MASTER) += i3c-master-cdns.o
- obj-$(CONFIG_DW_I3C_MASTER) += dw-i3c-master.o
-diff --git a/drivers/i3c/master/aspeed-i3c-global.c b/drivers/i3c/master/aspeed-i3c-global.c
-new file mode 100644
-index 000000000000..8db6c1397a6c
---- /dev/null
-+++ b/drivers/i3c/master/aspeed-i3c-global.c
-@@ -0,0 +1,77 @@
-+// SPDX-License-Identifier: GPL-2.0
-+// Copyright (C) 2019 ASPEED Technology Inc.
-+
-+#include <linux/clk.h>
-+#include <linux/irq.h>
-+#include <linux/irqchip.h>
-+#include <linux/irqchip/chained_irq.h>
-+#include <linux/irqdomain.h>
-+#include <linux/module.h>
-+#include <linux/of_platform.h>
-+#include <linux/platform_device.h>
-+#include <linux/of_address.h>
-+#include <linux/of_irq.h>
-+#include <linux/io.h>
-+#include <linux/reset.h>
-+#include <linux/delay.h>
-+
-+#define ASPEED_I3CG_CTRL(x) (0x10 + (x*0x10))
-+#define ASPEED_I3CG_SET(x) (0x14 + (x*0x10))
-+
-+struct aspeed_i3c_global {
-+ void __iomem *base;
-+ struct reset_control *rst;
-+};
-+
-+static int aspeed_i3c_global_probe(struct platform_device *pdev)
-+{
-+ struct aspeed_i3c_global *i3c_global;
-+ struct device_node *node = pdev->dev.of_node;
-+ int i = 0;
-+
-+ i3c_global = kzalloc(sizeof(*i3c_global), GFP_KERNEL);
-+ if (!i3c_global)
-+ return -ENOMEM;
-+
-+ i3c_global->base = of_iomap(node, 0);
-+ if (!i3c_global->base) {
-+ return -ENOMEM;
-+ }
-+
-+ i3c_global->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL);
-+
-+ if (IS_ERR(i3c_global->rst)) {
-+ if (PTR_ERR(i3c_global->rst) != -EPROBE_DEFER)
-+ dev_err(&pdev->dev, "missing or invalid reset controller device tree entry\n");
-+ return PTR_ERR(i3c_global->rst);
-+ }
-+
-+ reset_control_assert(i3c_global->rst);
-+ udelay(3);
-+ reset_control_deassert(i3c_global->rst);
-+
-+ /* init */
-+ for(i = 0; i < 5; i++)
-+ writel(0x000474c4, i3c_global->base + ASPEED_I3CG_SET(i));
-+
-+ return 0;
-+}
-+
-+static const struct of_device_id aspeed_i3c_of_match[] = {
-+ { .compatible = "aspeed,ast2600-i3c-global", },
-+ { },
-+};
-+MODULE_DEVICE_TABLE(of, aspeed_i3c_of_match);
-+
-+static struct platform_driver aspeed_i3c_driver = {
-+ .probe = aspeed_i3c_global_probe,
-+ .driver = {
-+ .name = KBUILD_MODNAME,
-+ .of_match_table = aspeed_i3c_of_match,
-+ },
-+};
-+module_platform_driver(aspeed_i3c_driver);
-+
-+MODULE_AUTHOR("Ryan Chen");
-+MODULE_DESCRIPTION("ASPEED I3C Global Driver");
-+MODULE_LICENSE("GPL v2");
-diff --git a/include/dt-bindings/clock/ast2600-clock.h b/include/dt-bindings/clock/ast2600-clock.h
-index 26f84584a821..6cc47373cc97 100644
---- a/include/dt-bindings/clock/ast2600-clock.h
-+++ b/include/dt-bindings/clock/ast2600-clock.h
-@@ -88,7 +88,8 @@
- #define ASPEED_CLK_MAC3RCLK 69
- #define ASPEED_CLK_MAC4RCLK 70
- #define ASPEED_CLK_UART5 71
--#define ASPEED_CLK_MAX 72
-+#define ASPEED_CLK_I3C 72
-+#define ASPEED_CLK_MAX 73
-
- /* Only list resets here that are not part of a gate */
- #define ASPEED_RESET_ADC 55
-diff --git a/include/uapi/linux/i3c/i3cdev.h b/include/uapi/linux/i3c/i3cdev.h
-new file mode 100644
-index 000000000000..0897313f5516
---- /dev/null
-+++ b/include/uapi/linux/i3c/i3cdev.h
-@@ -0,0 +1,38 @@
-+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-+/*
-+ * Copyright (c) 2019 Synopsys, Inc. and/or its affiliates.
-+ *
-+ * Author: Vitor Soares <vitor.soares@synopsys.com>
-+ */
-+
-+#ifndef _UAPI_I3C_DEV_H_
-+#define _UAPI_I3C_DEV_H_
-+
-+#include <linux/types.h>
-+#include <linux/ioctl.h>
-+
-+/* IOCTL commands */
-+#define I3C_DEV_IOC_MAGIC 0x07
-+
-+/**
-+ * struct i3c_ioc_priv_xfer - I3C SDR ioctl private transfer
-+ * @data: Holds pointer to userspace buffer with transmit data.
-+ * @len: Length of data buffer buffers, in bytes.
-+ * @rnw: encodes the transfer direction. true for a read, false for a write
-+ */
-+struct i3c_ioc_priv_xfer {
-+ __u64 data;
-+ __u16 len;
-+ __u8 rnw;
-+ __u8 pad[5];
-+};
-+
-+
-+#define I3C_PRIV_XFER_SIZE(N) \
-+ ((((sizeof(struct i3c_ioc_priv_xfer)) * (N)) < (1 << _IOC_SIZEBITS)) \
-+ ? ((sizeof(struct i3c_ioc_priv_xfer)) * (N)) : 0)
-+
-+#define I3C_IOC_PRIV_XFER(N) \
-+ _IOC(_IOC_READ|_IOC_WRITE, I3C_DEV_IOC_MAGIC, 30, I3C_PRIV_XFER_SIZE(N))
-+
-+#endif
---
-2.7.4
-