diff options
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.patch | 910 |
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 - |