From e81b88932985c9134d410f4eaaaa9b81a3b4bd0c Mon Sep 17 00:00:00 2001 From: Yauhen Kharuzhy Date: Tue, 17 Sep 2019 00:15:36 +0300 Subject: [PATCH 001/198] extcon-intel-cht-wc: Don't reset USB data connection at probe Intel Cherry Trail Whiskey Cove extcon driver connect USB data lines to PMIC at driver probing for further charger detection. This causes reset of USB data sessions and removing all devices from bus. If system was booted from Live CD or USB dongle, this makes system unusable. Check if USB ID pin is floating and re-route data lines in this case only, don't touch otherwise. Reviewed-by: Hans de Goede Reviewed-by: Andy Shevchenko Signed-off-by: Yauhen Kharuzhy [cw00.choi: Clean-up the minor coding style] Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-intel-cht-wc.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/extcon/extcon-intel-cht-wc.c b/drivers/extcon/extcon-intel-cht-wc.c index 9d32150e68db..771f6f4cf92e 100644 --- a/drivers/extcon/extcon-intel-cht-wc.c +++ b/drivers/extcon/extcon-intel-cht-wc.c @@ -338,6 +338,7 @@ static int cht_wc_extcon_probe(struct platform_device *pdev) struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent); struct cht_wc_extcon_data *ext; unsigned long mask = ~(CHT_WC_PWRSRC_VBUS | CHT_WC_PWRSRC_USBID_MASK); + int pwrsrc_sts, id; int irq, ret; irq = platform_get_irq(pdev, 0); @@ -387,8 +388,19 @@ static int cht_wc_extcon_probe(struct platform_device *pdev) goto disable_sw_control; } - /* Route D+ and D- to PMIC for initial charger detection */ - cht_wc_extcon_set_phymux(ext, MUX_SEL_PMIC); + ret = regmap_read(ext->regmap, CHT_WC_PWRSRC_STS, &pwrsrc_sts); + if (ret) { + dev_err(ext->dev, "Error reading pwrsrc status: %d\n", ret); + goto disable_sw_control; + } + + /* + * If no USB host or device connected, route D+ and D- to PMIC for + * initial charger detection + */ + id = cht_wc_extcon_get_id(ext, pwrsrc_sts); + if (id != INTEL_USB_ID_GND) + cht_wc_extcon_set_phymux(ext, MUX_SEL_PMIC); /* Get initial state */ cht_wc_extcon_pwrsrc_event(ext); From 8b063441b7417a79b0c27efc401479748ccf8ad1 Mon Sep 17 00:00:00 2001 From: zhong jiang Date: Fri, 13 Sep 2019 00:52:27 +0800 Subject: [PATCH 002/198] drivers/misc: ti-st: Remove unneeded variable in st_tty_open st_tty_open do not need local variable to store different value, Hence just remove it. Signed-off-by: zhong jiang Link: https://lore.kernel.org/r/1568307147-43468-1-git-send-email-zhongjiang@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ti-st/st_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/misc/ti-st/st_core.c b/drivers/misc/ti-st/st_core.c index 7d9e23aa0b92..2ae9948a91e1 100644 --- a/drivers/misc/ti-st/st_core.c +++ b/drivers/misc/ti-st/st_core.c @@ -708,7 +708,6 @@ EXPORT_SYMBOL_GPL(st_unregister); */ static int st_tty_open(struct tty_struct *tty) { - int err = 0; struct st_data_s *st_gdata; pr_info("%s ", __func__); @@ -731,7 +730,8 @@ static int st_tty_open(struct tty_struct *tty) */ st_kim_complete(st_gdata->kim_data); pr_debug("done %s", __func__); - return err; + + return 0; } static void st_tty_close(struct tty_struct *tty) From 780ee709bdb49c0d3562890855d7ff7919e64075 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Fri, 4 Oct 2019 21:26:59 +0300 Subject: [PATCH 003/198] mei: buf: drop 'running hook' debug messages. Drop 'running hook' debug messages, as this info can be already retrieved via ftrace. Signed-off-by: Tomas Winkler Link: https://lore.kernel.org/r/20191004182659.2933-1-tomas.winkler@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/bus-fixup.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/misc/mei/bus-fixup.c b/drivers/misc/mei/bus-fixup.c index 32e9b1aed2ca..e52c8d02c234 100644 --- a/drivers/misc/mei/bus-fixup.c +++ b/drivers/misc/mei/bus-fixup.c @@ -46,8 +46,6 @@ static const uuid_le mei_nfc_info_guid = MEI_UUID_NFC_INFO; */ static void number_of_connections(struct mei_cl_device *cldev) { - dev_dbg(&cldev->dev, "running hook %s\n", __func__); - if (cldev->me_cl->props.max_number_of_connections > 1) cldev->do_match = 0; } @@ -59,8 +57,6 @@ static void number_of_connections(struct mei_cl_device *cldev) */ static void blacklist(struct mei_cl_device *cldev) { - dev_dbg(&cldev->dev, "running hook %s\n", __func__); - cldev->do_match = 0; } @@ -71,8 +67,6 @@ static void blacklist(struct mei_cl_device *cldev) */ static void whitelist(struct mei_cl_device *cldev) { - dev_dbg(&cldev->dev, "running hook %s\n", __func__); - cldev->do_match = 1; } @@ -248,7 +242,6 @@ static void mei_wd(struct mei_cl_device *cldev) { struct pci_dev *pdev = to_pci_dev(cldev->dev.parent); - dev_dbg(&cldev->dev, "running hook %s\n", __func__); if (pdev->device == MEI_DEV_ID_WPT_LP || pdev->device == MEI_DEV_ID_SPT || pdev->device == MEI_DEV_ID_SPT_H) @@ -402,8 +395,6 @@ static void mei_nfc(struct mei_cl_device *cldev) bus = cldev->bus; - dev_dbg(&cldev->dev, "running hook %s\n", __func__); - mutex_lock(&bus->device_lock); /* we need to connect to INFO GUID */ cl = mei_cl_alloc_linked(bus); From 3079b54aa9a0f0432b88a52e205cfa3486898330 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 2 Oct 2019 10:48:44 +0200 Subject: [PATCH 004/198] eeprom: Warn that the driver is deprecated Deprecating the driver in Kconfig is one thing, but we also need to let the users themselves know. Log a warning each time a device is bound to the deprecated eeprom driver. Signed-off-by: Jean Delvare Cc: Arnd Bergmann Cc: Greg Kroah-Hartman Link: https://lore.kernel.org/r/20191002104844.1dc4d8f3@endymion Signed-off-by: Greg Kroah-Hartman --- drivers/misc/eeprom/eeprom.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/misc/eeprom/eeprom.c b/drivers/misc/eeprom/eeprom.c index 2cfe3d4ae144..226b5efa6a77 100644 --- a/drivers/misc/eeprom/eeprom.c +++ b/drivers/misc/eeprom/eeprom.c @@ -175,6 +175,10 @@ static int eeprom_probe(struct i2c_client *client, } } + /* Let the users know they are using deprecated driver */ + dev_notice(&client->dev, + "eeprom driver is deprecated, please use at24 instead\n"); + /* create the sysfs eeprom file */ return sysfs_create_bin_file(&client->dev.kobj, &eeprom_attr); } From d20c0da8b2020acaa107b42041c8da2bafed1910 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 30 Jul 2019 11:15:14 -0700 Subject: [PATCH 005/198] fpga: Remove dev_err() usage after platform_get_irq() We don't need dev_err() messages when platform_get_irq() fails now that platform_get_irq() prints an error message itself when something goes wrong. Let's remove these prints with a simple semantic patch. // @@ expression ret; struct platform_device *E; @@ ret = ( platform_get_irq(E, ...) | platform_get_irq_byname(E, ...) ); if ( \( ret < 0 \| ret <= 0 \) ) { ( -if (ret != -EPROBE_DEFER) -{ ... -dev_err(...); -... } | ... -dev_err(...); ) ... } // While we're here, remove braces on if statements that only have one statement (manually). Cc: Moritz Fischer Cc: linux-fpga@vger.kernel.org Cc: Greg Kroah-Hartman Signed-off-by: Stephen Boyd Signed-off-by: Moritz Fischer --- drivers/fpga/zynq-fpga.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/fpga/zynq-fpga.c b/drivers/fpga/zynq-fpga.c index 31ef38e38537..ee7765049607 100644 --- a/drivers/fpga/zynq-fpga.c +++ b/drivers/fpga/zynq-fpga.c @@ -578,10 +578,8 @@ static int zynq_fpga_probe(struct platform_device *pdev) init_completion(&priv->dma_done); priv->irq = platform_get_irq(pdev, 0); - if (priv->irq < 0) { - dev_err(dev, "No IRQ available\n"); + if (priv->irq < 0) return priv->irq; - } priv->clk = devm_clk_get(dev, "ref_clk"); if (IS_ERR(priv->clk)) { From 73668309215285366c433489de70d31362987be9 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Tue, 8 Oct 2019 03:57:34 +0300 Subject: [PATCH 006/198] mei: fix modalias documentation mei client bus added the client protocol version to the device alias, but ABI documentation was not updated. Fixes: b26864cad1c9 (mei: bus: add client protocol version to the device alias) Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Link: https://lore.kernel.org/r/20191008005735.12707-1-tomas.winkler@intel.com Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-bus-mei | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/ABI/testing/sysfs-bus-mei b/Documentation/ABI/testing/sysfs-bus-mei index 6bd45346ac7e..3f8701e8fa24 100644 --- a/Documentation/ABI/testing/sysfs-bus-mei +++ b/Documentation/ABI/testing/sysfs-bus-mei @@ -4,7 +4,7 @@ KernelVersion: 3.10 Contact: Samuel Ortiz linux-mei@linux.intel.com Description: Stores the same MODALIAS value emitted by uevent - Format: mei::: + Format: mei::: What: /sys/bus/mei/devices/.../name Date: May 2015 From 3e917975b7cdef6cfe92931e04677d8cf1d3df98 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Tue, 8 Oct 2019 03:57:35 +0300 Subject: [PATCH 007/198] mei: me: fix me_intr_clear function name in KDoc Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Link: https://lore.kernel.org/r/20191008005735.12707-2-tomas.winkler@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/hw-me.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index abe1b1f4362f..47e6d173fa66 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -269,7 +269,7 @@ static inline void me_intr_disable(struct mei_device *dev, u32 hcsr) } /** - * mei_me_intr_clear - clear and stop interrupts + * me_intr_clear - clear and stop interrupts * * @dev: the device structure * @hcsr: supplied hcsr register value From b406357c572b29cdcf05f717c69ae0018fa6a146 Mon Sep 17 00:00:00 2001 From: Christian Kellner Date: Thu, 3 Oct 2019 19:32:40 +0200 Subject: [PATCH 008/198] thunderbolt: Add 'generation' attribute for devices The Thunderbolt standard went through several major iterations, here called generation. USB4, which will be based on Thunderbolt, will be generation 4. Let userspace know the generation of the controller in the devices in order to distinguish between Thunderbolt and USB4, so it can be shown in various user interfaces. Signed-off-by: Christian Kellner Signed-off-by: Mika Westerberg --- Documentation/ABI/testing/sysfs-bus-thunderbolt | 8 ++++++++ drivers/thunderbolt/switch.c | 10 ++++++++++ 2 files changed, 18 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-bus-thunderbolt b/Documentation/ABI/testing/sysfs-bus-thunderbolt index b21fba14689b..c31b4616181c 100644 --- a/Documentation/ABI/testing/sysfs-bus-thunderbolt +++ b/Documentation/ABI/testing/sysfs-bus-thunderbolt @@ -80,6 +80,14 @@ Contact: thunderbolt-software@lists.01.org Description: This attribute contains 1 if Thunderbolt device was already authorized on boot and 0 otherwise. +What: /sys/bus/thunderbolt/devices/.../generation +Date: Jan 2020 +KernelVersion: 5.5 +Contact: Christian Kellner +Description: This attribute contains the generation of the Thunderbolt + controller associated with the device. It will contain 4 + for USB4. + What: /sys/bus/thunderbolt/devices/.../key Date: Sep 2017 KernelVersion: 4.13 diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index 410bf1bceeee..ac9fa2740800 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -1120,6 +1120,15 @@ device_name_show(struct device *dev, struct device_attribute *attr, char *buf) } static DEVICE_ATTR_RO(device_name); +static ssize_t +generation_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct tb_switch *sw = tb_to_switch(dev); + + return sprintf(buf, "%u\n", sw->generation); +} +static DEVICE_ATTR_RO(generation); + static ssize_t key_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1325,6 +1334,7 @@ static struct attribute *switch_attrs[] = { &dev_attr_boot.attr, &dev_attr_device.attr, &dev_attr_device_name.attr, + &dev_attr_generation.attr, &dev_attr_key.attr, &dev_attr_nvm_authenticate.attr, &dev_attr_nvm_version.attr, From 71200fcbb91c1a6b6da7fe1c806c6dd27b42e14c Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 9 Oct 2019 22:44:35 +0800 Subject: [PATCH 009/198] w1: sgi_w1: use devm_platform_ioremap_resource() to simplify code Use devm_platform_ioremap_resource() to simplify the code a bit. This is detected by coccinelle. Signed-off-by: YueHaibing Link: https://lore.kernel.org/r/20191009144435.12656-1-yuehaibing@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/w1/masters/sgi_w1.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/w1/masters/sgi_w1.c b/drivers/w1/masters/sgi_w1.c index 1b2d96b945be..e8c7fa68d3cc 100644 --- a/drivers/w1/masters/sgi_w1.c +++ b/drivers/w1/masters/sgi_w1.c @@ -77,15 +77,13 @@ static int sgi_w1_probe(struct platform_device *pdev) { struct sgi_w1_device *sdev; struct sgi_w1_platform_data *pdata; - struct resource *res; sdev = devm_kzalloc(&pdev->dev, sizeof(struct sgi_w1_device), GFP_KERNEL); if (!sdev) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - sdev->mcr = devm_ioremap_resource(&pdev->dev, res); + sdev->mcr = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(sdev->mcr)) return PTR_ERR(sdev->mcr); From ad90ff6964d733ee59d1679f692c23fb1dab7f30 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 9 Oct 2019 22:37:52 +0800 Subject: [PATCH 010/198] misc: atmel_tclib: use devm_platform_ioremap_resource() to simplify code Use devm_platform_ioremap_resource() to simplify the code a bit. This is detected by coccinelle. Signed-off-by: YueHaibing Link: https://lore.kernel.org/r/20191009143752.11236-1-yuehaibing@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/atmel_tclib.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/misc/atmel_tclib.c b/drivers/misc/atmel_tclib.c index 08b5b639d77f..7de7840f613c 100644 --- a/drivers/misc/atmel_tclib.c +++ b/drivers/misc/atmel_tclib.c @@ -109,7 +109,6 @@ static int __init tc_probe(struct platform_device *pdev) struct atmel_tc *tc; struct clk *clk; int irq; - struct resource *r; unsigned int i; if (of_get_child_count(pdev->dev.of_node)) @@ -133,8 +132,7 @@ static int __init tc_probe(struct platform_device *pdev) if (IS_ERR(tc->slow_clk)) return PTR_ERR(tc->slow_clk); - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - tc->regs = devm_ioremap_resource(&pdev->dev, r); + tc->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(tc->regs)) return PTR_ERR(tc->regs); From fa6f90f349ac3c79bf085b2b4f5212ea102724d5 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 7 Oct 2019 11:30:46 -0700 Subject: [PATCH 011/198] sgi-gru: simplify procfs code some more Use seq_puts and simple string output and not seq_printf with formats and individual strings to reduce overall object size. $ size drivers/misc/sgi-gru/gruprocfs.o* (x86-64 defconfig with gru) text data bss dec hex filename 7006 8 0 7014 1b66 drivers/misc/sgi-gru/gruprocfs.o.new 7472 8 0 7480 1d38 drivers/misc/sgi-gru/gruprocfs.o.old Signed-off-by: Joe Perches Acked-by: Dimitri Sivanich Link: https://lore.kernel.org/r/cce61906a5f7f42f5b2b8b947fc61357bcb56e71.camel@perches.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/sgi-gru/gruprocfs.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/misc/sgi-gru/gruprocfs.c b/drivers/misc/sgi-gru/gruprocfs.c index 3a8d76d1ccae..2817f4751306 100644 --- a/drivers/misc/sgi-gru/gruprocfs.c +++ b/drivers/misc/sgi-gru/gruprocfs.c @@ -119,7 +119,7 @@ static int mcs_statistics_show(struct seq_file *s, void *p) "cch_interrupt_sync", "cch_deallocate", "tfh_write_only", "tfh_write_restart", "tgh_invalidate"}; - seq_printf(s, "%-20s%12s%12s%12s\n", "#id", "count", "aver-clks", "max-clks"); + seq_puts(s, "#id count aver-clks max-clks\n"); for (op = 0; op < mcsop_last; op++) { count = atomic_long_read(&mcs_op_statistics[op].count); total = atomic_long_read(&mcs_op_statistics[op].total); @@ -165,8 +165,7 @@ static int cch_seq_show(struct seq_file *file, void *data) const char *mode[] = { "??", "UPM", "INTR", "OS_POLL" }; if (gid == 0) - seq_printf(file, "#%5s%5s%6s%7s%9s%6s%8s%8s\n", "gid", "bid", - "ctx#", "asid", "pid", "cbrs", "dsbytes", "mode"); + seq_puts(file, "# gid bid ctx# asid pid cbrs dsbytes mode\n"); if (gru) for (i = 0; i < GRU_NUM_CCH; i++) { ts = gru->gs_gts[i]; @@ -191,10 +190,8 @@ static int gru_seq_show(struct seq_file *file, void *data) struct gru_state *gru = GID_TO_GRU(gid); if (gid == 0) { - seq_printf(file, "#%5s%5s%7s%6s%6s%8s%6s%6s\n", "gid", "nid", - "ctx", "cbr", "dsr", "ctx", "cbr", "dsr"); - seq_printf(file, "#%5s%5s%7s%6s%6s%8s%6s%6s\n", "", "", "busy", - "busy", "busy", "free", "free", "free"); + seq_puts(file, "# gid nid ctx cbr dsr ctx cbr dsr\n"); + seq_puts(file, "# busy busy busy free free free\n"); } if (gru) { ctxfree = GRU_NUM_CCH - gru->gs_active_contexts; From bb4d6e0ee83c91b34585d1ce2aceed1ae2d3729f Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 2 Oct 2019 14:56:58 -0700 Subject: [PATCH 012/198] lis3lv02d: switch to using input device polling mode Now that instances of input_dev support polling mode natively, we no longer need to create input_polled_dev instance. Signed-off-by: Dmitry Torokhov Link: https://lore.kernel.org/r/20191002215658.GA134561@dtor-ws Signed-off-by: Greg Kroah-Hartman --- drivers/misc/Kconfig | 1 - drivers/misc/lis3lv02d/lis3lv02d.c | 80 ++++++++++++++++-------------- drivers/misc/lis3lv02d/lis3lv02d.h | 4 +- 3 files changed, 46 insertions(+), 39 deletions(-) diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index c55b63750757..2fefecef6e06 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -8,7 +8,6 @@ menu "Misc devices" config SENSORS_LIS3LV02D tristate depends on INPUT - select INPUT_POLLDEV config AD525X_DPOT tristate "Analog Devices Digital Potentiometers" diff --git a/drivers/misc/lis3lv02d/lis3lv02d.c b/drivers/misc/lis3lv02d/lis3lv02d.c index 057d7bbde402..dd65cedf3b12 100644 --- a/drivers/misc/lis3lv02d/lis3lv02d.c +++ b/drivers/misc/lis3lv02d/lis3lv02d.c @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include #include @@ -434,23 +434,23 @@ int lis3lv02d_poweron(struct lis3lv02d *lis3) EXPORT_SYMBOL_GPL(lis3lv02d_poweron); -static void lis3lv02d_joystick_poll(struct input_polled_dev *pidev) +static void lis3lv02d_joystick_poll(struct input_dev *input) { - struct lis3lv02d *lis3 = pidev->private; + struct lis3lv02d *lis3 = input_get_drvdata(input); int x, y, z; mutex_lock(&lis3->mutex); lis3lv02d_get_xyz(lis3, &x, &y, &z); - input_report_abs(pidev->input, ABS_X, x); - input_report_abs(pidev->input, ABS_Y, y); - input_report_abs(pidev->input, ABS_Z, z); - input_sync(pidev->input); + input_report_abs(input, ABS_X, x); + input_report_abs(input, ABS_Y, y); + input_report_abs(input, ABS_Z, z); + input_sync(input); mutex_unlock(&lis3->mutex); } -static void lis3lv02d_joystick_open(struct input_polled_dev *pidev) +static int lis3lv02d_joystick_open(struct input_dev *input) { - struct lis3lv02d *lis3 = pidev->private; + struct lis3lv02d *lis3 = input_get_drvdata(input); if (lis3->pm_dev) pm_runtime_get_sync(lis3->pm_dev); @@ -461,12 +461,14 @@ static void lis3lv02d_joystick_open(struct input_polled_dev *pidev) * Update coordinates for the case where poll interval is 0 and * the chip in running purely under interrupt control */ - lis3lv02d_joystick_poll(pidev); + lis3lv02d_joystick_poll(input); + + return 0; } -static void lis3lv02d_joystick_close(struct input_polled_dev *pidev) +static void lis3lv02d_joystick_close(struct input_dev *input) { - struct lis3lv02d *lis3 = pidev->private; + struct lis3lv02d *lis3 = input_get_drvdata(input); atomic_set(&lis3->wake_thread, 0); if (lis3->pm_dev) @@ -497,7 +499,7 @@ out: static void lis302dl_interrupt_handle_click(struct lis3lv02d *lis3) { - struct input_dev *dev = lis3->idev->input; + struct input_dev *dev = lis3->idev; u8 click_src; mutex_lock(&lis3->mutex); @@ -677,26 +679,19 @@ int lis3lv02d_joystick_enable(struct lis3lv02d *lis3) if (lis3->idev) return -EINVAL; - lis3->idev = input_allocate_polled_device(); - if (!lis3->idev) + input_dev = input_allocate_device(); + if (!input_dev) return -ENOMEM; - lis3->idev->poll = lis3lv02d_joystick_poll; - lis3->idev->open = lis3lv02d_joystick_open; - lis3->idev->close = lis3lv02d_joystick_close; - lis3->idev->poll_interval = MDPS_POLL_INTERVAL; - lis3->idev->poll_interval_min = MDPS_POLL_MIN; - lis3->idev->poll_interval_max = MDPS_POLL_MAX; - lis3->idev->private = lis3; - input_dev = lis3->idev->input; - input_dev->name = "ST LIS3LV02DL Accelerometer"; input_dev->phys = DRIVER_NAME "/input0"; input_dev->id.bustype = BUS_HOST; input_dev->id.vendor = 0; input_dev->dev.parent = &lis3->pdev->dev; - set_bit(EV_ABS, input_dev->evbit); + input_dev->open = lis3lv02d_joystick_open; + input_dev->close = lis3lv02d_joystick_close; + max_val = (lis3->mdps_max_val * lis3->scale) / LIS3_ACCURACY; if (lis3->whoami == WAI_12B) { fuzz = LIS3_DEFAULT_FUZZ_12B; @@ -712,17 +707,32 @@ int lis3lv02d_joystick_enable(struct lis3lv02d *lis3) input_set_abs_params(input_dev, ABS_Y, -max_val, max_val, fuzz, flat); input_set_abs_params(input_dev, ABS_Z, -max_val, max_val, fuzz, flat); + input_set_drvdata(input_dev, lis3); + lis3->idev = input_dev; + + err = input_setup_polling(input_dev, lis3lv02d_joystick_poll); + if (err) + goto err_free_input; + + input_set_poll_interval(input_dev, MDPS_POLL_INTERVAL); + input_set_min_poll_interval(input_dev, MDPS_POLL_MIN); + input_set_max_poll_interval(input_dev, MDPS_POLL_MAX); + lis3->mapped_btns[0] = lis3lv02d_get_axis(abs(lis3->ac.x), btns); lis3->mapped_btns[1] = lis3lv02d_get_axis(abs(lis3->ac.y), btns); lis3->mapped_btns[2] = lis3lv02d_get_axis(abs(lis3->ac.z), btns); - err = input_register_polled_device(lis3->idev); - if (err) { - input_free_polled_device(lis3->idev); - lis3->idev = NULL; - } + err = input_register_device(lis3->idev); + if (err) + goto err_free_input; + return 0; + +err_free_input: + input_free_device(input_dev); + lis3->idev = NULL; return err; + } EXPORT_SYMBOL_GPL(lis3lv02d_joystick_enable); @@ -738,8 +748,7 @@ void lis3lv02d_joystick_disable(struct lis3lv02d *lis3) if (lis3->irq) misc_deregister(&lis3->miscdev); - input_unregister_polled_device(lis3->idev); - input_free_polled_device(lis3->idev); + input_unregister_device(lis3->idev); lis3->idev = NULL; } EXPORT_SYMBOL_GPL(lis3lv02d_joystick_disable); @@ -895,10 +904,9 @@ static void lis3lv02d_8b_configure(struct lis3lv02d *lis3, (p->click_thresh_y << 4)); if (lis3->idev) { - struct input_dev *input_dev = lis3->idev->input; - input_set_capability(input_dev, EV_KEY, BTN_X); - input_set_capability(input_dev, EV_KEY, BTN_Y); - input_set_capability(input_dev, EV_KEY, BTN_Z); + input_set_capability(lis3->idev, EV_KEY, BTN_X); + input_set_capability(lis3->idev, EV_KEY, BTN_Y); + input_set_capability(lis3->idev, EV_KEY, BTN_Z); } } diff --git a/drivers/misc/lis3lv02d/lis3lv02d.h b/drivers/misc/lis3lv02d/lis3lv02d.h index 1b0c99883c57..c394c0b08519 100644 --- a/drivers/misc/lis3lv02d/lis3lv02d.h +++ b/drivers/misc/lis3lv02d/lis3lv02d.h @@ -6,7 +6,7 @@ * Copyright (C) 2008-2009 Eric Piel */ #include -#include +#include #include #include @@ -281,7 +281,7 @@ struct lis3lv02d { * (1/1000th of earth gravity) */ - struct input_polled_dev *idev; /* input device */ + struct input_dev *idev; /* input device */ struct platform_device *pdev; /* platform device */ struct regulator_bulk_data regulators[2]; atomic_t count; /* interrupt count after last read */ From 32a738704d7fc20fcece0e14e4e44ec68689746f Mon Sep 17 00:00:00 2001 From: Denis Efremov Date: Thu, 26 Sep 2019 00:36:56 +0300 Subject: [PATCH 013/198] greybus: remove excessive check in gb_connection_hd_cport_quiesce() Function pointer "hd->driver->cport_quiesce" is already checked at the beginning of gb_connection_hd_cport_quiesce(). Thus, the second check can be removed. Signed-off-by: Denis Efremov Acked-by: Johan Hovold Link: https://lore.kernel.org/r/20190925213656.8950-1-efremov@linux.com Signed-off-by: Greg Kroah-Hartman --- drivers/greybus/connection.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/greybus/connection.c b/drivers/greybus/connection.c index fc8f57f97ce6..e3799a53a193 100644 --- a/drivers/greybus/connection.c +++ b/drivers/greybus/connection.c @@ -361,9 +361,6 @@ static int gb_connection_hd_cport_quiesce(struct gb_connection *connection) if (connection->mode_switch) peer_space += sizeof(struct gb_operation_msg_hdr); - if (!hd->driver->cport_quiesce) - return 0; - ret = hd->driver->cport_quiesce(hd, connection->hd_cport_id, peer_space, GB_CONNECTION_CPORT_QUIESCE_TIMEOUT); From d52accce0aadd577ab12b59a71a450b3765f5db1 Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Wed, 25 Sep 2019 06:37:56 +0000 Subject: [PATCH 014/198] platform: goldfish: Allow goldfish drivers for archs with IOMEM and DMA We don't need explicit dependency of Goldfish kconfig option on various architectures. Instead, the Goldfish kconfig option should only depend on HAS_IOMEM and HAS_DMA which is sufficient for all Goldfish devices. Signed-off-by: Anup Patel Link: https://lore.kernel.org/r/20190925063706.56175-2-anup.patel@wdc.com Signed-off-by: Greg Kroah-Hartman --- drivers/platform/goldfish/Kconfig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/platform/goldfish/Kconfig b/drivers/platform/goldfish/Kconfig index 77b35df3a801..f3d09b1631e3 100644 --- a/drivers/platform/goldfish/Kconfig +++ b/drivers/platform/goldfish/Kconfig @@ -1,8 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only menuconfig GOLDFISH bool "Platform support for Goldfish virtual devices" - depends on X86_32 || X86_64 || ARM || ARM64 || MIPS - depends on HAS_IOMEM + depends on HAS_IOMEM && HAS_DMA help Say Y here to get to see options for the Goldfish virtual platform. This option alone does not add any kernel code. From 9964f8c899fc66539f1fb57b9dc6c401b77b5cdc Mon Sep 17 00:00:00 2001 From: zhong jiang Date: Fri, 13 Sep 2019 01:04:01 +0800 Subject: [PATCH 015/198] misc: rtsx: Remove unneeded variable in rts5260_card_power_on rts5260_card_power_on do not need local variable to store different value, Hence just remove it. Signed-off-by: zhong jiang Link: https://lore.kernel.org/r/1568307841-44065-1-git-send-email-zhongjiang@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/cardreader/rts5260.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/misc/cardreader/rts5260.c b/drivers/misc/cardreader/rts5260.c index 40a6d199f2ea..4214f02a17fd 100644 --- a/drivers/misc/cardreader/rts5260.c +++ b/drivers/misc/cardreader/rts5260.c @@ -191,7 +191,6 @@ static int sd_set_sample_push_timing_sd30(struct rtsx_pcr *pcr) static int rts5260_card_power_on(struct rtsx_pcr *pcr, int card) { - int err = 0; struct rtsx_cr_option *option = &pcr->option; if (option->ocp_en) @@ -231,7 +230,7 @@ static int rts5260_card_power_on(struct rtsx_pcr *pcr, int card) rtsx_pci_write_register(pcr, REG_PRE_RW_MODE, EN_INFINITE_MODE, 0); - return err; + return 0; } static int rts5260_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) From 689e3557a2d1245475bb6ecfccf4ca5ab03e3a74 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 17 Sep 2019 17:58:56 -0700 Subject: [PATCH 016/198] misc: MIC: drop all 'comment' lines from its Kconfig The "comment" Kconfig lines for the Intel MIC drivers are redundant, and nowhere else do we use this kind of Kconfig style, so remove them. Signed-off-by: Randy Dunlap Cc: Sudeep Dutt Cc: Ashutosh Dixit Cc: Arnd Bergmann Cc: Greg Kroah-Hartman Link: https://lore.kernel.org/r/3aa90a0f-1576-d38b-8382-6ed623ed5466@infradead.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/Kconfig | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/drivers/misc/mic/Kconfig b/drivers/misc/mic/Kconfig index 948f45bbf135..b6841ba6d922 100644 --- a/drivers/misc/mic/Kconfig +++ b/drivers/misc/mic/Kconfig @@ -1,8 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only menu "Intel MIC & related support" -comment "Intel MIC Bus Driver" - config INTEL_MIC_BUS tristate "Intel MIC Bus Driver" depends on 64BIT && PCI && X86 @@ -18,8 +16,6 @@ config INTEL_MIC_BUS OS and tools for MIC to use with this driver are available from . -comment "SCIF Bus Driver" - config SCIF_BUS tristate "SCIF Bus Driver" depends on 64BIT && PCI && X86 @@ -35,8 +31,6 @@ config SCIF_BUS OS and tools for MIC to use with this driver are available from . -comment "VOP Bus Driver" - config VOP_BUS tristate "VOP Bus Driver" help @@ -51,8 +45,6 @@ config VOP_BUS OS and tools for MIC to use with this driver are available from . -comment "Intel MIC Host Driver" - config INTEL_MIC_HOST tristate "Intel MIC Host Driver" depends on 64BIT && PCI && X86 @@ -71,8 +63,6 @@ config INTEL_MIC_HOST OS and tools for MIC to use with this driver are available from . -comment "Intel MIC Card Driver" - config INTEL_MIC_CARD tristate "Intel MIC Card Driver" depends on 64BIT && X86 @@ -90,8 +80,6 @@ config INTEL_MIC_CARD For more information see . -comment "SCIF Driver" - config SCIF tristate "SCIF Driver" depends on 64BIT && PCI && X86 && SCIF_BUS && IOMMU_SUPPORT @@ -110,8 +98,6 @@ config SCIF OS and tools for MIC to use with this driver are available from . -comment "Intel MIC Coprocessor State Management (COSM) Drivers" - config MIC_COSM tristate "Intel MIC Coprocessor State Management (COSM) Drivers" depends on 64BIT && PCI && X86 && SCIF @@ -128,8 +114,6 @@ config MIC_COSM OS and tools for MIC to use with this driver are available from . -comment "VOP Driver" - config VOP tristate "VOP Driver" depends on VOP_BUS From 2419e55e532de14fdf336e09e453aa2831c73a25 Mon Sep 17 00:00:00 2001 From: Jorge Ramirez-Ortiz Date: Wed, 9 Oct 2019 15:41:19 +0100 Subject: [PATCH 017/198] misc: fastrpc: add mmap/unmap support Support the allocation/deallocation of buffers mapped to the DSP. When the memory mapped to the DSP at process creation is not enough, the fastrpc library can extend it at runtime. This avoids having to do large preallocations by default. Signed-off-by: Jorge Ramirez-Ortiz Reviewed-by: Srinivas Kandagatla Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20191009144123.24583-2-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/fastrpc.c | 181 ++++++++++++++++++++++++++++++++++++ include/uapi/misc/fastrpc.h | 15 +++ 2 files changed, 196 insertions(+) diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index 47ae84afac2e..aa1249bb581a 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -34,6 +34,7 @@ #define FASTRPC_CTXID_MASK (0xFF0) #define INIT_FILELEN_MAX (64 * 1024 * 1024) #define FASTRPC_DEVICE_NAME "fastrpc" +#define ADSP_MMAP_ADD_PAGES 0x1000 /* Retrives number of input buffers from the scalars parameter */ #define REMOTE_SCALARS_INBUFS(sc) (((sc) >> 16) & 0x0ff) @@ -66,6 +67,8 @@ /* Remote Method id table */ #define FASTRPC_RMID_INIT_ATTACH 0 #define FASTRPC_RMID_INIT_RELEASE 1 +#define FASTRPC_RMID_INIT_MMAP 4 +#define FASTRPC_RMID_INIT_MUNMAP 5 #define FASTRPC_RMID_INIT_CREATE 6 #define FASTRPC_RMID_INIT_CREATE_ATTR 7 #define FASTRPC_RMID_INIT_CREATE_STATIC 8 @@ -89,6 +92,23 @@ struct fastrpc_remote_arg { u64 len; }; +struct fastrpc_mmap_rsp_msg { + u64 vaddr; +}; + +struct fastrpc_mmap_req_msg { + s32 pgid; + u32 flags; + u64 vaddr; + s32 num; +}; + +struct fastrpc_munmap_req_msg { + s32 pgid; + u64 vaddr; + u64 size; +}; + struct fastrpc_msg { int pid; /* process group id */ int tid; /* thread id */ @@ -123,6 +143,9 @@ struct fastrpc_buf { /* Lock for dma buf attachments */ struct mutex lock; struct list_head attachments; + /* mmap support */ + struct list_head node; /* list of user requested mmaps */ + uintptr_t raddr; }; struct fastrpc_dma_buf_attachment { @@ -192,6 +215,7 @@ struct fastrpc_user { struct list_head user; struct list_head maps; struct list_head pending; + struct list_head mmaps; struct fastrpc_channel_ctx *cctx; struct fastrpc_session_ctx *sctx; @@ -269,6 +293,7 @@ static int fastrpc_buf_alloc(struct fastrpc_user *fl, struct device *dev, return -ENOMEM; INIT_LIST_HEAD(&buf->attachments); + INIT_LIST_HEAD(&buf->node); mutex_init(&buf->lock); buf->fl = fl; @@ -276,6 +301,7 @@ static int fastrpc_buf_alloc(struct fastrpc_user *fl, struct device *dev, buf->phys = 0; buf->size = size; buf->dev = dev; + buf->raddr = 0; buf->virt = dma_alloc_coherent(dev, buf->size, (dma_addr_t *)&buf->phys, GFP_KERNEL); @@ -1130,6 +1156,7 @@ static int fastrpc_device_release(struct inode *inode, struct file *file) struct fastrpc_channel_ctx *cctx = fl->cctx; struct fastrpc_invoke_ctx *ctx, *n; struct fastrpc_map *map, *m; + struct fastrpc_buf *buf, *b; unsigned long flags; fastrpc_release_current_dsp_process(fl); @@ -1151,6 +1178,11 @@ static int fastrpc_device_release(struct inode *inode, struct file *file) fastrpc_map_put(map); } + list_for_each_entry_safe(buf, b, &fl->mmaps, node) { + list_del(&buf->node); + fastrpc_buf_free(buf); + } + fastrpc_session_free(cctx, fl->sctx); fastrpc_channel_ctx_put(cctx); @@ -1179,6 +1211,7 @@ static int fastrpc_device_open(struct inode *inode, struct file *filp) mutex_init(&fl->mutex); INIT_LIST_HEAD(&fl->pending); INIT_LIST_HEAD(&fl->maps); + INIT_LIST_HEAD(&fl->mmaps); INIT_LIST_HEAD(&fl->user); fl->tgid = current->tgid; fl->cctx = cctx; @@ -1284,6 +1317,148 @@ static int fastrpc_invoke(struct fastrpc_user *fl, char __user *argp) return err; } +static int fastrpc_req_munmap_impl(struct fastrpc_user *fl, + struct fastrpc_req_munmap *req) +{ + struct fastrpc_invoke_args args[1] = { [0] = { 0 } }; + struct fastrpc_buf *buf, *b; + struct fastrpc_munmap_req_msg req_msg; + struct device *dev = fl->sctx->dev; + int err; + u32 sc; + + spin_lock(&fl->lock); + list_for_each_entry_safe(buf, b, &fl->mmaps, node) { + if ((buf->raddr == req->vaddrout) && (buf->size == req->size)) + break; + buf = NULL; + } + spin_unlock(&fl->lock); + + if (!buf) { + dev_err(dev, "mmap not in list\n"); + return -EINVAL; + } + + req_msg.pgid = fl->tgid; + req_msg.size = buf->size; + req_msg.vaddr = buf->raddr; + + args[0].ptr = (u64) (uintptr_t) &req_msg; + args[0].length = sizeof(req_msg); + + sc = FASTRPC_SCALARS(FASTRPC_RMID_INIT_MUNMAP, 1, 0); + err = fastrpc_internal_invoke(fl, true, FASTRPC_INIT_HANDLE, sc, + &args[0]); + if (!err) { + dev_dbg(dev, "unmmap\tpt 0x%09lx OK\n", buf->raddr); + spin_lock(&fl->lock); + list_del(&buf->node); + spin_unlock(&fl->lock); + fastrpc_buf_free(buf); + } else { + dev_err(dev, "unmmap\tpt 0x%09lx ERROR\n", buf->raddr); + } + + return err; +} + +static int fastrpc_req_munmap(struct fastrpc_user *fl, char __user *argp) +{ + struct fastrpc_req_munmap req; + + if (copy_from_user(&req, argp, sizeof(req))) + return -EFAULT; + + return fastrpc_req_munmap_impl(fl, &req); +} + +static int fastrpc_req_mmap(struct fastrpc_user *fl, char __user *argp) +{ + struct fastrpc_invoke_args args[3] = { [0 ... 2] = { 0 } }; + struct fastrpc_buf *buf = NULL; + struct fastrpc_mmap_req_msg req_msg; + struct fastrpc_mmap_rsp_msg rsp_msg; + struct fastrpc_req_munmap req_unmap; + struct fastrpc_phy_page pages; + struct fastrpc_req_mmap req; + struct device *dev = fl->sctx->dev; + int err; + u32 sc; + + if (copy_from_user(&req, argp, sizeof(req))) + return -EFAULT; + + if (req.flags != ADSP_MMAP_ADD_PAGES) { + dev_err(dev, "flag not supported 0x%x\n", req.flags); + return -EINVAL; + } + + if (req.vaddrin) { + dev_err(dev, "adding user allocated pages is not supported\n"); + return -EINVAL; + } + + err = fastrpc_buf_alloc(fl, fl->sctx->dev, req.size, &buf); + if (err) { + dev_err(dev, "failed to allocate buffer\n"); + return err; + } + + req_msg.pgid = fl->tgid; + req_msg.flags = req.flags; + req_msg.vaddr = req.vaddrin; + req_msg.num = sizeof(pages); + + args[0].ptr = (u64) (uintptr_t) &req_msg; + args[0].length = sizeof(req_msg); + + pages.addr = buf->phys; + pages.size = buf->size; + + args[1].ptr = (u64) (uintptr_t) &pages; + args[1].length = sizeof(pages); + + args[2].ptr = (u64) (uintptr_t) &rsp_msg; + args[2].length = sizeof(rsp_msg); + + sc = FASTRPC_SCALARS(FASTRPC_RMID_INIT_MMAP, 2, 1); + err = fastrpc_internal_invoke(fl, true, FASTRPC_INIT_HANDLE, sc, + &args[0]); + if (err) { + dev_err(dev, "mmap error (len 0x%08llx)\n", buf->size); + goto err_invoke; + } + + /* update the buffer to be able to deallocate the memory on the DSP */ + buf->raddr = (uintptr_t) rsp_msg.vaddr; + + /* let the client know the address to use */ + req.vaddrout = rsp_msg.vaddr; + + spin_lock(&fl->lock); + list_add_tail(&buf->node, &fl->mmaps); + spin_unlock(&fl->lock); + + if (copy_to_user((void __user *)argp, &req, sizeof(req))) { + /* unmap the memory and release the buffer */ + req_unmap.vaddrout = buf->raddr; + req_unmap.size = buf->size; + fastrpc_req_munmap_impl(fl, &req_unmap); + return -EFAULT; + } + + dev_dbg(dev, "mmap\t\tpt 0x%09lx OK [len 0x%08llx]\n", + buf->raddr, buf->size); + + return 0; + +err_invoke: + fastrpc_buf_free(buf); + + return err; +} + static long fastrpc_device_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { @@ -1304,6 +1479,12 @@ static long fastrpc_device_ioctl(struct file *file, unsigned int cmd, case FASTRPC_IOCTL_ALLOC_DMA_BUFF: err = fastrpc_dmabuf_alloc(fl, argp); break; + case FASTRPC_IOCTL_MMAP: + err = fastrpc_req_mmap(fl, argp); + break; + case FASTRPC_IOCTL_MUNMAP: + err = fastrpc_req_munmap(fl, argp); + break; default: err = -ENOTTY; break; diff --git a/include/uapi/misc/fastrpc.h b/include/uapi/misc/fastrpc.h index fb792e882cef..07de2b7aac85 100644 --- a/include/uapi/misc/fastrpc.h +++ b/include/uapi/misc/fastrpc.h @@ -10,6 +10,8 @@ #define FASTRPC_IOCTL_INVOKE _IOWR('R', 3, struct fastrpc_invoke) #define FASTRPC_IOCTL_INIT_ATTACH _IO('R', 4) #define FASTRPC_IOCTL_INIT_CREATE _IOWR('R', 5, struct fastrpc_init_create) +#define FASTRPC_IOCTL_MMAP _IOWR('R', 6, struct fastrpc_req_mmap) +#define FASTRPC_IOCTL_MUNMAP _IOWR('R', 7, struct fastrpc_req_munmap) struct fastrpc_invoke_args { __u64 ptr; @@ -38,4 +40,17 @@ struct fastrpc_alloc_dma_buf { __u64 size; /* size */ }; +struct fastrpc_req_mmap { + __s32 fd; + __u32 flags; /* flags for dsp to map with */ + __u64 vaddrin; /* optional virtual address */ + __u64 size; /* size */ + __u64 vaddrout; /* dsp virtual address */ +}; + +struct fastrpc_req_munmap { + __u64 vaddrout; /* address to unmap */ + __u64 size; /* size */ +}; + #endif /* __QCOM_FASTRPC_H__ */ From 2d10d2d170723e9278282458a6704552dcb77eac Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Wed, 9 Oct 2019 15:41:20 +0100 Subject: [PATCH 018/198] misc: fastrpc: fix memory leak from miscdev->name Fix a memory leak in miscdev->name by using devm_variant Orignally reported by kmemleak: [] kmemleak_alloc+0x50/0x84 [] __kmalloc_track_caller+0xe8/0x168 [] kvasprintf+0x78/0x100 [] kasprintf+0x50/0x74 [] fastrpc_rpmsg_probe+0xd8/0x20c [] rpmsg_dev_probe+0xa8/0x148 [] really_probe+0x208/0x248 [] driver_probe_device+0x98/0xc0 [] __device_attach_driver+0x9c/0xac [] bus_for_each_drv+0x60/0x8c [] __device_attach+0x8c/0x100 [] device_initial_probe+0x20/0x28 [] bus_probe_device+0x34/0x7c [] device_add+0x420/0x498 [] device_register+0x24/0x2c Signed-off-by: Srinivas Kandagatla Reviewed-by: Bjorn Andersson Link: https://lore.kernel.org/r/20191009144123.24583-3-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/fastrpc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index aa1249bb581a..c0fe3419c685 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -1610,8 +1610,8 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev) return -ENOMEM; data->miscdev.minor = MISC_DYNAMIC_MINOR; - data->miscdev.name = kasprintf(GFP_KERNEL, "fastrpc-%s", - domains[domain_id]); + data->miscdev.name = devm_kasprintf(rdev, GFP_KERNEL, "fastrpc-%s", + domains[domain_id]); data->miscdev.fops = &fastrpc_fops; err = misc_register(&data->miscdev); if (err) From 55bcda35584c995d2544902e77c2ad5bee6e729b Mon Sep 17 00:00:00 2001 From: Jorge Ramirez-Ortiz Date: Wed, 9 Oct 2019 15:41:21 +0100 Subject: [PATCH 019/198] misc: fastrpc: do not interrupt kernel calls the DSP firmware requires some calls to be held until processing has completed: this is to guarantee that memory continues to be accessible. Nevertheless, the fastrpc driver chooses not support the case were requests need to be held for unbounded amounts of time. If such a use-case becomes necessary, this timeout will need to be revisited. Signed-off-by: Jorge Ramirez-Ortiz Reviewed-by: Srinivas Kandagatla Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20191009144123.24583-4-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/fastrpc.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index c0fe3419c685..666c431380ce 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -959,8 +959,13 @@ static int fastrpc_internal_invoke(struct fastrpc_user *fl, u32 kernel, if (err) goto bail; - /* Wait for remote dsp to respond or time out */ - err = wait_for_completion_interruptible(&ctx->work); + if (kernel) { + if (!wait_for_completion_timeout(&ctx->work, 10 * HZ)) + err = -ETIMEDOUT; + } else { + err = wait_for_completion_interruptible(&ctx->work); + } + if (err) goto bail; From 387f625585d1a59e5dc7fbd6bd4002360cad78b0 Mon Sep 17 00:00:00 2001 From: Jorge Ramirez-Ortiz Date: Wed, 9 Oct 2019 15:41:22 +0100 Subject: [PATCH 020/198] misc: fastrpc: handle interrupted contexts Buffers owned by a context that has been interrupted either by a signal or a timeout might still be being accessed by the DSP. delegate returning the associated memory to a later time when the device is released. Signed-off-by: Jorge Ramirez-Ortiz Reviewed-by: Srinivas Kandagatla Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20191009144123.24583-5-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/fastrpc.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index 666c431380ce..eef2cdc00672 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -984,12 +984,13 @@ static int fastrpc_internal_invoke(struct fastrpc_user *fl, u32 kernel, } bail: - /* We are done with this compute context, remove it from pending list */ - spin_lock(&fl->lock); - list_del(&ctx->node); - spin_unlock(&fl->lock); - fastrpc_context_put(ctx); - + if (err != -ERESTARTSYS && err != -ETIMEDOUT) { + /* We are done with this compute context */ + spin_lock(&fl->lock); + list_del(&ctx->node); + spin_unlock(&fl->lock); + fastrpc_context_put(ctx); + } if (err) dev_dbg(fl->sctx->dev, "Error: Invoke Failed %d\n", err); From efcd2390f56ba979e0d1022c46f48d7beec0bdd6 Mon Sep 17 00:00:00 2001 From: Jorge Ramirez-Ortiz Date: Wed, 9 Oct 2019 15:41:23 +0100 Subject: [PATCH 021/198] misc: fastrpc: revert max init file size back to 2MB With the integration of the mmap/unmap functionality, it is no longer necessary to allow large memory allocations upfront since they can be handled during runtime. Tested on QCS404 with CDSP Neural Processing test suite. Signed-off-by: Jorge Ramirez-Ortiz Reviewed-by: Srinivas Kandagatla Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20191009144123.24583-6-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/fastrpc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index eef2cdc00672..b6420aae45b9 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -32,7 +32,7 @@ #define FASTRPC_CTX_MAX (256) #define FASTRPC_INIT_HANDLE 1 #define FASTRPC_CTXID_MASK (0xFF0) -#define INIT_FILELEN_MAX (64 * 1024 * 1024) +#define INIT_FILELEN_MAX (2 * 1024 * 1024) #define FASTRPC_DEVICE_NAME "fastrpc" #define ADSP_MMAP_ADD_PAGES 0x1000 From 7ed42113ce0379197c44232429e309bcc72424b0 Mon Sep 17 00:00:00 2001 From: Nishad Kamdar Date: Fri, 20 Sep 2019 21:48:30 +0530 Subject: [PATCH 022/198] ocxl: Use the correct style for SPDX License Identifier This patch corrects the SPDX License Identifier style in header files for Open Coherent Accelerator (OCXL) compatible device drivers. For C header files Documentation/process/license-rules.rst mandates C-like comments (opposed to C source files where C++ style should be used) Changes made by using a script provided by Joe Perches here: https://lkml.org/lkml/2019/2/7/46. Suggested-by: Joe Perches Signed-off-by: Nishad Kamdar Acked-by: Andrew Donnellan Link: https://lore.kernel.org/r/20190920161826.GA6894@nishad Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ocxl/ocxl_internal.h | 2 +- drivers/misc/ocxl/trace.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/misc/ocxl/ocxl_internal.h b/drivers/misc/ocxl/ocxl_internal.h index 97415afd79f3..345bf843a38e 100644 --- a/drivers/misc/ocxl/ocxl_internal.h +++ b/drivers/misc/ocxl/ocxl_internal.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +/* SPDX-License-Identifier: GPL-2.0+ */ // Copyright 2017 IBM Corp. #ifndef _OCXL_INTERNAL_H_ #define _OCXL_INTERNAL_H_ diff --git a/drivers/misc/ocxl/trace.h b/drivers/misc/ocxl/trace.h index 024f417e7e01..17e21cb2addd 100644 --- a/drivers/misc/ocxl/trace.h +++ b/drivers/misc/ocxl/trace.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +/* SPDX-License-Identifier: GPL-2.0+ */ // Copyright 2017 IBM Corp. #undef TRACE_SYSTEM #define TRACE_SYSTEM ocxl From 40688bd58df71269d146996e10e84ca3888fd330 Mon Sep 17 00:00:00 2001 From: Nishad Kamdar Date: Fri, 20 Sep 2019 21:29:34 +0530 Subject: [PATCH 023/198] misc: Use the correct style for SPDX License Identifier This patch corrects the SPDX License Identifier style in header files for Miscellaneous device drivers. For C header files Documentation/process/license-rules.rst mandates C-like comments (opposed to C source files where C++ style should be used) Changes made by using a script provided by Joe Perches here: https://lkml.org/lkml/2019/2/7/46. Suggested-by: Joe Perches Signed-off-by: Nishad Kamdar Link: https://lore.kernel.org/r/20190920155931.GA6251@nishad Signed-off-by: Greg Kroah-Hartman --- drivers/misc/hpilo.h | 2 +- drivers/misc/ibmvmc.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/misc/hpilo.h b/drivers/misc/hpilo.h index 94dfb9e40e29..1aa433a7f66c 100644 --- a/drivers/misc/hpilo.h +++ b/drivers/misc/hpilo.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +/* SPDX-License-Identifier: GPL-2.0 */ /* * linux/drivers/char/hpilo.h * diff --git a/drivers/misc/ibmvmc.h b/drivers/misc/ibmvmc.h index e140ada8fe2c..0e1756fffeae 100644 --- a/drivers/misc/ibmvmc.h +++ b/drivers/misc/ibmvmc.h @@ -1,5 +1,5 @@ -/* SPDX-License-Identifier: GPL-2.0+ - * +/* SPDX-License-Identifier: GPL-2.0+ */ +/* * linux/drivers/misc/ibmvmc.h * * IBM Power Systems Virtual Management Channel Support. From 6942635032cfd3e003e980d2dfa4e6323a3ce145 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Thu, 10 Oct 2019 17:47:20 +0200 Subject: [PATCH 024/198] extcon: sm5502: Reset registers during initialization On some devices (e.g. Samsung Galaxy A5 (2015)), the bootloader seems to keep interrupts enabled for SM5502 when booting Linux. Changing the cable state (i.e. plugging in a cable) - until the driver is loaded - will therefore produce an interrupt that is never read. In this situation, the cable state will be stuck forever on the initial state because SM5502 stops sending interrupts. This can be avoided by clearing those pending interrupts after the driver has been loaded. One way to do this is to reset all registers to default state by writing to SM5502_REG_RESET. This ensures that we start from a clean state, with all interrupts disabled. Suggested-by: Chanwoo Choi Signed-off-by: Stephan Gerhold Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-sm5502.c | 4 ++++ drivers/extcon/extcon-sm5502.h | 2 ++ 2 files changed, 6 insertions(+) diff --git a/drivers/extcon/extcon-sm5502.c b/drivers/extcon/extcon-sm5502.c index dc43847ad2b0..b3d93baf4fc5 100644 --- a/drivers/extcon/extcon-sm5502.c +++ b/drivers/extcon/extcon-sm5502.c @@ -65,6 +65,10 @@ struct sm5502_muic_info { /* Default value of SM5502 register to bring up MUIC device. */ static struct reg_data sm5502_reg_data[] = { { + .reg = SM5502_REG_RESET, + .val = SM5502_REG_RESET_MASK, + .invert = true, + }, { .reg = SM5502_REG_CONTROL, .val = SM5502_REG_CONTROL_MASK_INT_MASK, .invert = false, diff --git a/drivers/extcon/extcon-sm5502.h b/drivers/extcon/extcon-sm5502.h index 9dbb634d213b..ce1f1ec310c4 100644 --- a/drivers/extcon/extcon-sm5502.h +++ b/drivers/extcon/extcon-sm5502.h @@ -237,6 +237,8 @@ enum sm5502_reg { #define DM_DP_SWITCH_UART ((DM_DP_CON_SWITCH_UART < Date: Mon, 16 Sep 2019 13:57:38 -0500 Subject: [PATCH 025/198] soundwire: intel: add missing headers for cross-compilation readl/writel and ioread32 are used without the relevant headers, fix. Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20190916185739.32184-2-pierre-louis.bossart@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/intel.c | 1 + drivers/soundwire/intel_init.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index f1e38a293967..bcc5077b2814 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c index b74c2f144962..d488c44fcbae 100644 --- a/drivers/soundwire/intel_init.c +++ b/drivers/soundwire/intel_init.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include From 2948d192169356b9361331a62e09bae5219a37f4 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 16 Sep 2019 13:57:39 -0500 Subject: [PATCH 026/198] soundwire: intel: remove X86 dependency This is not needed and may generate unmet dependencies when COMPILE_TEST is used for SOF. ACPI is required, and should be tested when selecting this option. Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20190916185739.32184-3-pierre-louis.bossart@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soundwire/Kconfig b/drivers/soundwire/Kconfig index f518273cfbe3..26902023f4e7 100644 --- a/drivers/soundwire/Kconfig +++ b/drivers/soundwire/Kconfig @@ -23,7 +23,7 @@ config SOUNDWIRE_CADENCE config SOUNDWIRE_INTEL tristate "Intel SoundWire Master driver" select SOUNDWIRE_CADENCE - depends on X86 && ACPI && SND_SOC + depends on ACPI && SND_SOC help SoundWire Intel Master driver. If you have an Intel platform which has a SoundWire Master then From 214be7470e5fb0f3b1e228df376397c3da17638c Mon Sep 17 00:00:00 2001 From: Xu Yilun Date: Mon, 14 Oct 2019 13:42:01 +0800 Subject: [PATCH 027/198] Documentation: fpga: dfl: add descriptions for thermal/power management interfaces This patch add introductions to thermal/power interfaces. They are implemented as hwmon sysfs interfaces by thermal/power private feature drivers. Signed-off-by: Xu Yilun Signed-off-by: Wu Hao Acked-by: Guenter Roeck Signed-off-by: Moritz Fischer --- Documentation/fpga/dfl.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Documentation/fpga/dfl.rst b/Documentation/fpga/dfl.rst index 6fa483fc823e..094fc8aacd8e 100644 --- a/Documentation/fpga/dfl.rst +++ b/Documentation/fpga/dfl.rst @@ -108,6 +108,16 @@ More functions are exposed through sysfs error reporting sysfs interfaces allow user to read errors detected by the hardware, and clear the logged errors. + Power management (dfl_fme_power hwmon) + power management hwmon sysfs interfaces allow user to read power management + information (power consumption, thresholds, threshold status, limits, etc.) + and configure power thresholds for different throttling levels. + + Thermal management (dfl_fme_thermal hwmon) + thermal management hwmon sysfs interfaces allow user to read thermal + management information (current temperature, thresholds, threshold status, + etc.). + FIU - PORT ========== From 4284c65a9fda9d31f45209062f1b7f9bb438fc04 Mon Sep 17 00:00:00 2001 From: Wu Hao Date: Mon, 14 Oct 2019 13:42:02 +0800 Subject: [PATCH 028/198] fpga: dfl: fme: add thermal management support This patch adds support to thermal management private feature for DFL FPGA Management Engine (FME). This private feature driver registers a hwmon for thermal/temperature monitoring (hwmon temp1_input). If hardware automatic throttling is supported by this hardware, then driver also exposes sysfs interfaces under hwmon for thresholds (temp1_max/ crit/ emergency), threshold alarms (temp1_max_alarm/ temp1_crit_alarm) and throttling policy (temp1_max_policy). Signed-off-by: Luwei Kang Signed-off-by: Russ Weight Signed-off-by: Xu Yilun Signed-off-by: Wu Hao Acked-by: Guenter Roeck Reviewed-by: Moritz Fischer Signed-off-by: Moritz Fischer --- .../ABI/testing/sysfs-platform-dfl-fme | 64 +++++++ drivers/fpga/Kconfig | 2 +- drivers/fpga/dfl-fme-main.c | 178 ++++++++++++++++++ 3 files changed, 243 insertions(+), 1 deletion(-) diff --git a/Documentation/ABI/testing/sysfs-platform-dfl-fme b/Documentation/ABI/testing/sysfs-platform-dfl-fme index 72634d3ae4f4..8eb6d03e06e7 100644 --- a/Documentation/ABI/testing/sysfs-platform-dfl-fme +++ b/Documentation/ABI/testing/sysfs-platform-dfl-fme @@ -106,3 +106,67 @@ KernelVersion: 5.4 Contact: Wu Hao Description: Read-only. Read this file to get the second error detected by hardware. + +What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/name +Date: October 2019 +KernelVersion: 5.5 +Contact: Wu Hao +Description: Read-Only. Read this file to get the name of hwmon device, it + supports values: + 'dfl_fme_thermal' - thermal hwmon device name + +What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/temp1_input +Date: October 2019 +KernelVersion: 5.5 +Contact: Wu Hao +Description: Read-Only. It returns FPGA device temperature in millidegrees + Celsius. + +What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/temp1_max +Date: October 2019 +KernelVersion: 5.5 +Contact: Wu Hao +Description: Read-Only. It returns hardware threshold1 temperature in + millidegrees Celsius. If temperature rises at or above this + threshold, hardware starts 50% or 90% throttling (see + 'temp1_max_policy'). + +What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/temp1_crit +Date: October 2019 +KernelVersion: 5.5 +Contact: Wu Hao +Description: Read-Only. It returns hardware threshold2 temperature in + millidegrees Celsius. If temperature rises at or above this + threshold, hardware starts 100% throttling. + +What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/temp1_emergency +Date: October 2019 +KernelVersion: 5.5 +Contact: Wu Hao +Description: Read-Only. It returns hardware trip threshold temperature in + millidegrees Celsius. If temperature rises at or above this + threshold, a fatal event will be triggered to board management + controller (BMC) to shutdown FPGA. + +What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/temp1_max_alarm +Date: October 2019 +KernelVersion: 5.5 +Contact: Wu Hao +Description: Read-only. It returns 1 if temperature is currently at or above + hardware threshold1 (see 'temp1_max'), otherwise 0. + +What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/temp1_crit_alarm +Date: October 2019 +KernelVersion: 5.5 +Contact: Wu Hao +Description: Read-only. It returns 1 if temperature is currently at or above + hardware threshold2 (see 'temp1_crit'), otherwise 0. + +What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/temp1_max_policy +Date: October 2019 +KernelVersion: 5.5 +Contact: Wu Hao +Description: Read-Only. Read this file to get the policy of hardware threshold1 + (see 'temp1_max'). It only supports two values (policies): + 0 - AP2 state (90% throttling) + 1 - AP1 state (50% throttling) diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig index 73c779e920ed..72380e1d31c7 100644 --- a/drivers/fpga/Kconfig +++ b/drivers/fpga/Kconfig @@ -156,7 +156,7 @@ config FPGA_DFL config FPGA_DFL_FME tristate "FPGA DFL FME Driver" - depends on FPGA_DFL + depends on FPGA_DFL && HWMON help The FPGA Management Engine (FME) is a feature device implemented under Device Feature List (DFL) framework. Select this option to diff --git a/drivers/fpga/dfl-fme-main.c b/drivers/fpga/dfl-fme-main.c index 4d78e182878f..752d71c1748e 100644 --- a/drivers/fpga/dfl-fme-main.c +++ b/drivers/fpga/dfl-fme-main.c @@ -14,6 +14,8 @@ * Henry Mitchel */ +#include +#include #include #include #include @@ -181,6 +183,178 @@ static const struct dfl_feature_ops fme_hdr_ops = { .ioctl = fme_hdr_ioctl, }; +#define FME_THERM_THRESHOLD 0x8 +#define TEMP_THRESHOLD1 GENMASK_ULL(6, 0) +#define TEMP_THRESHOLD1_EN BIT_ULL(7) +#define TEMP_THRESHOLD2 GENMASK_ULL(14, 8) +#define TEMP_THRESHOLD2_EN BIT_ULL(15) +#define TRIP_THRESHOLD GENMASK_ULL(30, 24) +#define TEMP_THRESHOLD1_STATUS BIT_ULL(32) /* threshold1 reached */ +#define TEMP_THRESHOLD2_STATUS BIT_ULL(33) /* threshold2 reached */ +/* threshold1 policy: 0 - AP2 (90% throttle) / 1 - AP1 (50% throttle) */ +#define TEMP_THRESHOLD1_POLICY BIT_ULL(44) + +#define FME_THERM_RDSENSOR_FMT1 0x10 +#define FPGA_TEMPERATURE GENMASK_ULL(6, 0) + +#define FME_THERM_CAP 0x20 +#define THERM_NO_THROTTLE BIT_ULL(0) + +#define MD_PRE_DEG + +static bool fme_thermal_throttle_support(void __iomem *base) +{ + u64 v = readq(base + FME_THERM_CAP); + + return FIELD_GET(THERM_NO_THROTTLE, v) ? false : true; +} + +static umode_t thermal_hwmon_attrs_visible(const void *drvdata, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + const struct dfl_feature *feature = drvdata; + + /* temperature is always supported, and check hardware cap for others */ + if (attr == hwmon_temp_input) + return 0444; + + return fme_thermal_throttle_support(feature->ioaddr) ? 0444 : 0; +} + +static int thermal_hwmon_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct dfl_feature *feature = dev_get_drvdata(dev); + u64 v; + + switch (attr) { + case hwmon_temp_input: + v = readq(feature->ioaddr + FME_THERM_RDSENSOR_FMT1); + *val = (long)(FIELD_GET(FPGA_TEMPERATURE, v) * 1000); + break; + case hwmon_temp_max: + v = readq(feature->ioaddr + FME_THERM_THRESHOLD); + *val = (long)(FIELD_GET(TEMP_THRESHOLD1, v) * 1000); + break; + case hwmon_temp_crit: + v = readq(feature->ioaddr + FME_THERM_THRESHOLD); + *val = (long)(FIELD_GET(TEMP_THRESHOLD2, v) * 1000); + break; + case hwmon_temp_emergency: + v = readq(feature->ioaddr + FME_THERM_THRESHOLD); + *val = (long)(FIELD_GET(TRIP_THRESHOLD, v) * 1000); + break; + case hwmon_temp_max_alarm: + v = readq(feature->ioaddr + FME_THERM_THRESHOLD); + *val = (long)FIELD_GET(TEMP_THRESHOLD1_STATUS, v); + break; + case hwmon_temp_crit_alarm: + v = readq(feature->ioaddr + FME_THERM_THRESHOLD); + *val = (long)FIELD_GET(TEMP_THRESHOLD2_STATUS, v); + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static const struct hwmon_ops thermal_hwmon_ops = { + .is_visible = thermal_hwmon_attrs_visible, + .read = thermal_hwmon_read, +}; + +static const struct hwmon_channel_info *thermal_hwmon_info[] = { + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_EMERGENCY | + HWMON_T_MAX | HWMON_T_MAX_ALARM | + HWMON_T_CRIT | HWMON_T_CRIT_ALARM), + NULL +}; + +static const struct hwmon_chip_info thermal_hwmon_chip_info = { + .ops = &thermal_hwmon_ops, + .info = thermal_hwmon_info, +}; + +static ssize_t temp1_max_policy_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct dfl_feature *feature = dev_get_drvdata(dev); + u64 v; + + v = readq(feature->ioaddr + FME_THERM_THRESHOLD); + + return sprintf(buf, "%u\n", + (unsigned int)FIELD_GET(TEMP_THRESHOLD1_POLICY, v)); +} + +static DEVICE_ATTR_RO(temp1_max_policy); + +static struct attribute *thermal_extra_attrs[] = { + &dev_attr_temp1_max_policy.attr, + NULL, +}; + +static umode_t thermal_extra_attrs_visible(struct kobject *kobj, + struct attribute *attr, int index) +{ + struct device *dev = kobj_to_dev(kobj); + struct dfl_feature *feature = dev_get_drvdata(dev); + + return fme_thermal_throttle_support(feature->ioaddr) ? attr->mode : 0; +} + +static const struct attribute_group thermal_extra_group = { + .attrs = thermal_extra_attrs, + .is_visible = thermal_extra_attrs_visible, +}; +__ATTRIBUTE_GROUPS(thermal_extra); + +static int fme_thermal_mgmt_init(struct platform_device *pdev, + struct dfl_feature *feature) +{ + struct device *hwmon; + + /* + * create hwmon to allow userspace monitoring temperature and other + * threshold information. + * + * temp1_input -> FPGA device temperature + * temp1_max -> hardware threshold 1 -> 50% or 90% throttling + * temp1_crit -> hardware threshold 2 -> 100% throttling + * temp1_emergency -> hardware trip_threshold to shutdown FPGA + * temp1_max_alarm -> hardware threshold 1 alarm + * temp1_crit_alarm -> hardware threshold 2 alarm + * + * create device specific sysfs interfaces, e.g. read temp1_max_policy + * to understand the actual hardware throttling action (50% vs 90%). + * + * If hardware doesn't support automatic throttling per thresholds, + * then all above sysfs interfaces are not visible except temp1_input + * for temperature. + */ + hwmon = devm_hwmon_device_register_with_info(&pdev->dev, + "dfl_fme_thermal", feature, + &thermal_hwmon_chip_info, + thermal_extra_groups); + if (IS_ERR(hwmon)) { + dev_err(&pdev->dev, "Fail to register thermal hwmon\n"); + return PTR_ERR(hwmon); + } + + return 0; +} + +static const struct dfl_feature_id fme_thermal_mgmt_id_table[] = { + {.id = FME_FEATURE_ID_THERMAL_MGMT,}, + {0,} +}; + +static const struct dfl_feature_ops fme_thermal_mgmt_ops = { + .init = fme_thermal_mgmt_init, +}; + static struct dfl_feature_driver fme_feature_drvs[] = { { .id_table = fme_hdr_id_table, @@ -194,6 +368,10 @@ static struct dfl_feature_driver fme_feature_drvs[] = { .id_table = fme_global_err_id_table, .ops = &fme_global_err_ops, }, + { + .id_table = fme_thermal_mgmt_id_table, + .ops = &fme_thermal_mgmt_ops, + }, { .ops = NULL, }, From fddc9fcbe69a502d84adf0bad5e2c6c80823838c Mon Sep 17 00:00:00 2001 From: Wu Hao Date: Mon, 14 Oct 2019 13:42:03 +0800 Subject: [PATCH 029/198] fpga: dfl: fme: add power management support This patch adds support for power management private feature under FPGA Management Engine (FME). This private feature driver registers a hwmon for power (power1_input), thresholds information, e.g. (power1_max / crit / max_alarm / crit_alarm) and also read-only sysfs interfaces for other power management information. For configuration, user could write threshold values via above power1_max / crit sysfs interface under hwmon too. Signed-off-by: Luwei Kang Signed-off-by: Xu Yilun Signed-off-by: Wu Hao Acked-by: Guenter Roeck Reviewed-by: Moritz Fischer Signed-off-by: Moritz Fischer --- .../ABI/testing/sysfs-platform-dfl-fme | 68 ++++++ drivers/fpga/dfl-fme-main.c | 207 ++++++++++++++++++ 2 files changed, 275 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-platform-dfl-fme b/Documentation/ABI/testing/sysfs-platform-dfl-fme index 8eb6d03e06e7..3683cb1cdc3d 100644 --- a/Documentation/ABI/testing/sysfs-platform-dfl-fme +++ b/Documentation/ABI/testing/sysfs-platform-dfl-fme @@ -114,6 +114,7 @@ Contact: Wu Hao Description: Read-Only. Read this file to get the name of hwmon device, it supports values: 'dfl_fme_thermal' - thermal hwmon device name + 'dfl_fme_power' - power hwmon device name What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/temp1_input Date: October 2019 @@ -170,3 +171,70 @@ Description: Read-Only. Read this file to get the policy of hardware threshold1 (see 'temp1_max'). It only supports two values (policies): 0 - AP2 state (90% throttling) 1 - AP1 state (50% throttling) + +What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/power1_input +Date: October 2019 +KernelVersion: 5.5 +Contact: Wu Hao +Description: Read-Only. It returns current FPGA power consumption in uW. + +What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/power1_max +Date: October 2019 +KernelVersion: 5.5 +Contact: Wu Hao +Description: Read-Write. Read this file to get current hardware power + threshold1 in uW. If power consumption rises at or above + this threshold, hardware starts 50% throttling. + Write this file to set current hardware power threshold1 in uW. + As hardware only accepts values in Watts, so input value will + be round down per Watts (< 1 watts part will be discarded) and + clamped within the range from 0 to 127 Watts. Write fails with + -EINVAL if input parsing fails. + +What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/power1_crit +Date: October 2019 +KernelVersion: 5.5 +Contact: Wu Hao +Description: Read-Write. Read this file to get current hardware power + threshold2 in uW. If power consumption rises at or above + this threshold, hardware starts 90% throttling. + Write this file to set current hardware power threshold2 in uW. + As hardware only accepts values in Watts, so input value will + be round down per Watts (< 1 watts part will be discarded) and + clamped within the range from 0 to 127 Watts. Write fails with + -EINVAL if input parsing fails. + +What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/power1_max_alarm +Date: October 2019 +KernelVersion: 5.5 +Contact: Wu Hao +Description: Read-only. It returns 1 if power consumption is currently at or + above hardware threshold1 (see 'power1_max'), otherwise 0. + +What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/power1_crit_alarm +Date: October 2019 +KernelVersion: 5.5 +Contact: Wu Hao +Description: Read-only. It returns 1 if power consumption is currently at or + above hardware threshold2 (see 'power1_crit'), otherwise 0. + +What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/power1_xeon_limit +Date: October 2019 +KernelVersion: 5.5 +Contact: Wu Hao +Description: Read-Only. It returns power limit for XEON in uW. + +What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/power1_fpga_limit +Date: October 2019 +KernelVersion: 5.5 +Contact: Wu Hao +Description: Read-Only. It returns power limit for FPGA in uW. + +What: /sys/bus/platform/devices/dfl-fme.0/hwmon/hwmonX/power1_ltr +Date: October 2019 +KernelVersion: 5.5 +Contact: Wu Hao +Description: Read-only. Read this file to get current Latency Tolerance + Reporting (ltr) value. It returns 1 if all Accelerated + Function Units (AFUs) can tolerate latency >= 40us for memory + access or 0 if any AFU is latency sensitive (< 40us). diff --git a/drivers/fpga/dfl-fme-main.c b/drivers/fpga/dfl-fme-main.c index 752d71c1748e..7c930e6b314d 100644 --- a/drivers/fpga/dfl-fme-main.c +++ b/drivers/fpga/dfl-fme-main.c @@ -355,6 +355,209 @@ static const struct dfl_feature_ops fme_thermal_mgmt_ops = { .init = fme_thermal_mgmt_init, }; +#define FME_PWR_STATUS 0x8 +#define FME_LATENCY_TOLERANCE BIT_ULL(18) +#define PWR_CONSUMED GENMASK_ULL(17, 0) + +#define FME_PWR_THRESHOLD 0x10 +#define PWR_THRESHOLD1 GENMASK_ULL(6, 0) /* in Watts */ +#define PWR_THRESHOLD2 GENMASK_ULL(14, 8) /* in Watts */ +#define PWR_THRESHOLD_MAX 0x7f /* in Watts */ +#define PWR_THRESHOLD1_STATUS BIT_ULL(16) +#define PWR_THRESHOLD2_STATUS BIT_ULL(17) + +#define FME_PWR_XEON_LIMIT 0x18 +#define XEON_PWR_LIMIT GENMASK_ULL(14, 0) /* in 0.1 Watts */ +#define XEON_PWR_EN BIT_ULL(15) +#define FME_PWR_FPGA_LIMIT 0x20 +#define FPGA_PWR_LIMIT GENMASK_ULL(14, 0) /* in 0.1 Watts */ +#define FPGA_PWR_EN BIT_ULL(15) + +static int power_hwmon_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct dfl_feature *feature = dev_get_drvdata(dev); + u64 v; + + switch (attr) { + case hwmon_power_input: + v = readq(feature->ioaddr + FME_PWR_STATUS); + *val = (long)(FIELD_GET(PWR_CONSUMED, v) * 1000000); + break; + case hwmon_power_max: + v = readq(feature->ioaddr + FME_PWR_THRESHOLD); + *val = (long)(FIELD_GET(PWR_THRESHOLD1, v) * 1000000); + break; + case hwmon_power_crit: + v = readq(feature->ioaddr + FME_PWR_THRESHOLD); + *val = (long)(FIELD_GET(PWR_THRESHOLD2, v) * 1000000); + break; + case hwmon_power_max_alarm: + v = readq(feature->ioaddr + FME_PWR_THRESHOLD); + *val = (long)FIELD_GET(PWR_THRESHOLD1_STATUS, v); + break; + case hwmon_power_crit_alarm: + v = readq(feature->ioaddr + FME_PWR_THRESHOLD); + *val = (long)FIELD_GET(PWR_THRESHOLD2_STATUS, v); + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int power_hwmon_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + struct dfl_feature_platform_data *pdata = dev_get_platdata(dev->parent); + struct dfl_feature *feature = dev_get_drvdata(dev); + int ret = 0; + u64 v; + + val = clamp_val(val / 1000000, 0, PWR_THRESHOLD_MAX); + + mutex_lock(&pdata->lock); + + switch (attr) { + case hwmon_power_max: + v = readq(feature->ioaddr + FME_PWR_THRESHOLD); + v &= ~PWR_THRESHOLD1; + v |= FIELD_PREP(PWR_THRESHOLD1, val); + writeq(v, feature->ioaddr + FME_PWR_THRESHOLD); + break; + case hwmon_power_crit: + v = readq(feature->ioaddr + FME_PWR_THRESHOLD); + v &= ~PWR_THRESHOLD2; + v |= FIELD_PREP(PWR_THRESHOLD2, val); + writeq(v, feature->ioaddr + FME_PWR_THRESHOLD); + break; + default: + ret = -EOPNOTSUPP; + break; + } + + mutex_unlock(&pdata->lock); + + return ret; +} + +static umode_t power_hwmon_attrs_visible(const void *drvdata, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (attr) { + case hwmon_power_input: + case hwmon_power_max_alarm: + case hwmon_power_crit_alarm: + return 0444; + case hwmon_power_max: + case hwmon_power_crit: + return 0644; + } + + return 0; +} + +static const struct hwmon_ops power_hwmon_ops = { + .is_visible = power_hwmon_attrs_visible, + .read = power_hwmon_read, + .write = power_hwmon_write, +}; + +static const struct hwmon_channel_info *power_hwmon_info[] = { + HWMON_CHANNEL_INFO(power, HWMON_P_INPUT | + HWMON_P_MAX | HWMON_P_MAX_ALARM | + HWMON_P_CRIT | HWMON_P_CRIT_ALARM), + NULL +}; + +static const struct hwmon_chip_info power_hwmon_chip_info = { + .ops = &power_hwmon_ops, + .info = power_hwmon_info, +}; + +static ssize_t power1_xeon_limit_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct dfl_feature *feature = dev_get_drvdata(dev); + u16 xeon_limit = 0; + u64 v; + + v = readq(feature->ioaddr + FME_PWR_XEON_LIMIT); + + if (FIELD_GET(XEON_PWR_EN, v)) + xeon_limit = FIELD_GET(XEON_PWR_LIMIT, v); + + return sprintf(buf, "%u\n", xeon_limit * 100000); +} + +static ssize_t power1_fpga_limit_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct dfl_feature *feature = dev_get_drvdata(dev); + u16 fpga_limit = 0; + u64 v; + + v = readq(feature->ioaddr + FME_PWR_FPGA_LIMIT); + + if (FIELD_GET(FPGA_PWR_EN, v)) + fpga_limit = FIELD_GET(FPGA_PWR_LIMIT, v); + + return sprintf(buf, "%u\n", fpga_limit * 100000); +} + +static ssize_t power1_ltr_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct dfl_feature *feature = dev_get_drvdata(dev); + u64 v; + + v = readq(feature->ioaddr + FME_PWR_STATUS); + + return sprintf(buf, "%u\n", + (unsigned int)FIELD_GET(FME_LATENCY_TOLERANCE, v)); +} + +static DEVICE_ATTR_RO(power1_xeon_limit); +static DEVICE_ATTR_RO(power1_fpga_limit); +static DEVICE_ATTR_RO(power1_ltr); + +static struct attribute *power_extra_attrs[] = { + &dev_attr_power1_xeon_limit.attr, + &dev_attr_power1_fpga_limit.attr, + &dev_attr_power1_ltr.attr, + NULL +}; + +ATTRIBUTE_GROUPS(power_extra); + +static int fme_power_mgmt_init(struct platform_device *pdev, + struct dfl_feature *feature) +{ + struct device *hwmon; + + hwmon = devm_hwmon_device_register_with_info(&pdev->dev, + "dfl_fme_power", feature, + &power_hwmon_chip_info, + power_extra_groups); + if (IS_ERR(hwmon)) { + dev_err(&pdev->dev, "Fail to register power hwmon\n"); + return PTR_ERR(hwmon); + } + + return 0; +} + +static const struct dfl_feature_id fme_power_mgmt_id_table[] = { + {.id = FME_FEATURE_ID_POWER_MGMT,}, + {0,} +}; + +static const struct dfl_feature_ops fme_power_mgmt_ops = { + .init = fme_power_mgmt_init, +}; + static struct dfl_feature_driver fme_feature_drvs[] = { { .id_table = fme_hdr_id_table, @@ -372,6 +575,10 @@ static struct dfl_feature_driver fme_feature_drvs[] = { .id_table = fme_thermal_mgmt_id_table, .ops = &fme_thermal_mgmt_ops, }, + { + .id_table = fme_power_mgmt_id_table, + .ops = &fme_power_mgmt_ops, + }, { .ops = NULL, }, From 990be747648576184a65af0b20032126cb83a046 Mon Sep 17 00:00:00 2001 From: Jann Horn Date: Wed, 16 Oct 2019 17:01:19 +0200 Subject: [PATCH 030/198] binder: Use common definition of SZ_1K SZ_1K has been defined in include/linux/sizes.h since v3.6. Get rid of the duplicate definition. Signed-off-by: Jann Horn Acked-by: Christian Brauner Link: https://lore.kernel.org/r/20191016150119.154756-2-jannh@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 5b9ac2122e89..bef788058bc3 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -65,6 +65,7 @@ #include #include #include +#include #include #include @@ -92,11 +93,6 @@ static atomic_t binder_last_id; static int proc_show(struct seq_file *m, void *unused); DEFINE_SHOW_ATTRIBUTE(proc); -/* This is only defined in include/asm-arm/sizes.h */ -#ifndef SZ_1K -#define SZ_1K 0x400 -#endif - #ifndef SZ_4M #define SZ_4M 0x400000 #endif From 535bbe6a1f94dfc3e23cf1c9687459de7f3d2271 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 16 Sep 2019 14:23:44 -0500 Subject: [PATCH 031/198] soundwire: remove DAI_ID_RANGE definitions There is no reason to reserve a range of DAI IDs for SoundWire. This is not scalable and it's better to let the ASoC core allocate the dai->id when registering a component. Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20190916192348.467-3-pierre-louis.bossart@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/intel.c | 2 -- include/linux/soundwire/sdw.h | 3 --- 2 files changed, 5 deletions(-) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index bcc5077b2814..9d578dafa292 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -873,8 +873,6 @@ static int intel_create_dai(struct sdw_cdns *cdns, dais[i].capture.formats = SNDRV_PCM_FMTBIT_S16_LE; } - dais[i].id = SDW_DAI_ID_RANGE_START + i; - if (pcm) dais[i].ops = &intel_pcm_dai_ops; else diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index ea787201c3ac..688b40e65c89 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -40,9 +40,6 @@ struct sdw_slave; #define SDW_VALID_PORT_RANGE(n) ((n) <= 14 && (n) >= 1) -#define SDW_DAI_ID_RANGE_START 100 -#define SDW_DAI_ID_RANGE_END 200 - enum { SDW_PORT_DIRN_SINK = 0, SDW_PORT_DIRN_SOURCE, From 80464533e148b80f8fb7e08a044588ee44a4e5ea Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 16 Sep 2019 14:23:45 -0500 Subject: [PATCH 032/198] soundwire: intel: remove playback/capture stream_name We will create dai widget in SOF. Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20190916192348.467-4-pierre-louis.bossart@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/intel.c | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 9d578dafa292..a2cef9ba4289 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -843,14 +843,6 @@ static int intel_create_dai(struct sdw_cdns *cdns, return -ENOMEM; if (type == INTEL_PDI_BD || type == INTEL_PDI_OUT) { - dais[i].playback.stream_name = - kasprintf(GFP_KERNEL, "SDW%d Tx%d", - cdns->instance, i); - if (!dais[i].playback.stream_name) { - kfree(dais[i].name); - return -ENOMEM; - } - dais[i].playback.channels_min = 1; dais[i].playback.channels_max = max_ch; dais[i].playback.rates = SNDRV_PCM_RATE_48000; @@ -858,15 +850,6 @@ static int intel_create_dai(struct sdw_cdns *cdns, } if (type == INTEL_PDI_BD || type == INTEL_PDI_IN) { - dais[i].capture.stream_name = - kasprintf(GFP_KERNEL, "SDW%d Rx%d", - cdns->instance, i); - if (!dais[i].capture.stream_name) { - kfree(dais[i].name); - kfree(dais[i].playback.stream_name); - return -ENOMEM; - } - dais[i].capture.channels_min = 1; dais[i].capture.channels_max = max_ch; dais[i].capture.rates = SNDRV_PCM_RATE_48000; From 57a34790cd2cab02c3336fe96cfa33b9b65ed2ee Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 16 Sep 2019 14:23:46 -0500 Subject: [PATCH 033/198] soundwire: cadence/intel: simplify PDI/port mapping The existing Linux code uses a 1:1 mapping between ports and PDIs, but still has an independent allocation of ports and PDIs. Let's simplify the code and remove the port layer by only using PDIs. This patch does not change any functionality, just removes unnecessary code. This will also allow for further simplifications where the PDIs are not dynamically allocated but instead described in a topology file. Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20190916192348.467-5-pierre-louis.bossart@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/cadence_master.c | 110 ++++------------------- drivers/soundwire/cadence_master.h | 32 ++----- drivers/soundwire/intel.c | 135 +++++++---------------------- 3 files changed, 52 insertions(+), 225 deletions(-) diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 502ed4ec8f07..52ce380482b2 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -838,7 +838,8 @@ int sdw_cdns_pdi_init(struct sdw_cdns *cdns, struct sdw_cdns_stream_config config) { struct sdw_cdns_streams *stream; - int offset, i, ret; + int offset; + int ret; cdns->pcm.num_bd = config.pcm_bd; cdns->pcm.num_in = config.pcm_in; @@ -905,18 +906,6 @@ int sdw_cdns_pdi_init(struct sdw_cdns *cdns, stream->num_pdi = stream->num_bd + stream->num_in + stream->num_out; cdns->num_ports += stream->num_pdi; - cdns->ports = devm_kcalloc(cdns->dev, cdns->num_ports, - sizeof(*cdns->ports), GFP_KERNEL); - if (!cdns->ports) { - ret = -ENOMEM; - return ret; - } - - for (i = 0; i < cdns->num_ports; i++) { - cdns->ports[i].assigned = false; - cdns->ports[i].num = i + 1; /* Port 0 reserved for bulk */ - } - return 0; } EXPORT_SYMBOL(sdw_cdns_pdi_init); @@ -1207,13 +1196,11 @@ static struct sdw_cdns_pdi *cdns_find_pdi(struct sdw_cdns *cdns, * sdw_cdns_config_stream: Configure a stream * * @cdns: Cadence instance - * @port: Cadence data port * @ch: Channel count * @dir: Data direction * @pdi: PDI to be used */ void sdw_cdns_config_stream(struct sdw_cdns *cdns, - struct sdw_cdns_port *port, u32 ch, u32 dir, struct sdw_cdns_pdi *pdi) { u32 offset, val = 0; @@ -1221,89 +1208,26 @@ void sdw_cdns_config_stream(struct sdw_cdns *cdns, if (dir == SDW_DATA_DIR_RX) val = CDNS_PORTCTRL_DIRN; - offset = CDNS_PORTCTRL + port->num * CDNS_PORT_OFFSET; + offset = CDNS_PORTCTRL + pdi->num * CDNS_PORT_OFFSET; cdns_updatel(cdns, offset, CDNS_PORTCTRL_DIRN, val); - val = port->num; + val = pdi->num; val |= ((1 << ch) - 1) << SDW_REG_SHIFT(CDNS_PDI_CONFIG_CHANNEL); cdns_writel(cdns, CDNS_PDI_CONFIG(pdi->num), val); } EXPORT_SYMBOL(sdw_cdns_config_stream); /** - * cdns_get_num_pdi() - Get number of PDIs required - * - * @cdns: Cadence instance - * @pdi: PDI to be used - * @num: Number of PDIs - * @ch_count: Channel count - */ -static int cdns_get_num_pdi(struct sdw_cdns *cdns, - struct sdw_cdns_pdi *pdi, - unsigned int num, u32 ch_count) -{ - int i, pdis = 0; - - for (i = 0; i < num; i++) { - if (pdi[i].assigned) - continue; - - if (pdi[i].ch_count < ch_count) - ch_count -= pdi[i].ch_count; - else - ch_count = 0; - - pdis++; - - if (!ch_count) - break; - } - - if (ch_count) - return 0; - - return pdis; -} - -/** - * sdw_cdns_get_stream() - Get stream information + * sdw_cdns_alloc_pdi() - Allocate a PDI * * @cdns: Cadence instance * @stream: Stream to be allocated * @ch: Channel count * @dir: Data direction */ -int sdw_cdns_get_stream(struct sdw_cdns *cdns, - struct sdw_cdns_streams *stream, - u32 ch, u32 dir) -{ - int pdis = 0; - - if (dir == SDW_DATA_DIR_RX) - pdis = cdns_get_num_pdi(cdns, stream->in, stream->num_in, ch); - else - pdis = cdns_get_num_pdi(cdns, stream->out, stream->num_out, ch); - - /* check if we found PDI, else find in bi-directional */ - if (!pdis) - pdis = cdns_get_num_pdi(cdns, stream->bd, stream->num_bd, ch); - - return pdis; -} -EXPORT_SYMBOL(sdw_cdns_get_stream); - -/** - * sdw_cdns_alloc_stream() - Allocate a stream - * - * @cdns: Cadence instance - * @stream: Stream to be allocated - * @port: Cadence data port - * @ch: Channel count - * @dir: Data direction - */ -int sdw_cdns_alloc_stream(struct sdw_cdns *cdns, - struct sdw_cdns_streams *stream, - struct sdw_cdns_port *port, u32 ch, u32 dir) +struct sdw_cdns_pdi *sdw_cdns_alloc_pdi(struct sdw_cdns *cdns, + struct sdw_cdns_streams *stream, + u32 ch, u32 dir) { struct sdw_cdns_pdi *pdi = NULL; @@ -1316,18 +1240,16 @@ int sdw_cdns_alloc_stream(struct sdw_cdns *cdns, if (!pdi) pdi = cdns_find_pdi(cdns, stream->num_bd, stream->bd); - if (!pdi) - return -EIO; + if (pdi) { + pdi->l_ch_num = 0; + pdi->h_ch_num = ch - 1; + pdi->dir = dir; + pdi->ch_count = ch; + } - port->pdi = pdi; - pdi->l_ch_num = 0; - pdi->h_ch_num = ch - 1; - pdi->dir = dir; - pdi->ch_count = ch; - - return 0; + return pdi; } -EXPORT_SYMBOL(sdw_cdns_alloc_stream); +EXPORT_SYMBOL(sdw_cdns_alloc_pdi); MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("Cadence Soundwire Library"); diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h index 0b72b7094735..3634503f20b1 100644 --- a/drivers/soundwire/cadence_master.h +++ b/drivers/soundwire/cadence_master.h @@ -28,23 +28,6 @@ struct sdw_cdns_pdi { enum sdw_stream_type type; }; -/** - * struct sdw_cdns_port: Cadence port structure - * - * @num: port number - * @assigned: port assigned - * @ch: channel count - * @direction: data port direction - * @pdi: pdi for this port - */ -struct sdw_cdns_port { - unsigned int num; - bool assigned; - unsigned int ch; - enum sdw_data_direction direction; - struct sdw_cdns_pdi *pdi; -}; - /** * struct sdw_cdns_streams: Cadence stream data structure * @@ -95,8 +78,8 @@ struct sdw_cdns_stream_config { * struct sdw_cdns_dma_data: Cadence DMA data * * @name: SoundWire stream name - * @nr_ports: Number of ports - * @port: Ports + * @stream: stream runtime + * @pdi: PDI used for this dai * @bus: Bus handle * @stream_type: Stream type * @link_id: Master link id @@ -104,8 +87,7 @@ struct sdw_cdns_stream_config { struct sdw_cdns_dma_data { char *name; struct sdw_stream_runtime *stream; - int nr_ports; - struct sdw_cdns_port **port; + struct sdw_cdns_pdi *pdi; struct sdw_bus *bus; enum sdw_stream_type stream_type; int link_id; @@ -170,10 +152,10 @@ void sdw_cdns_debugfs_init(struct sdw_cdns *cdns, struct dentry *root); int sdw_cdns_get_stream(struct sdw_cdns *cdns, struct sdw_cdns_streams *stream, u32 ch, u32 dir); -int sdw_cdns_alloc_stream(struct sdw_cdns *cdns, - struct sdw_cdns_streams *stream, - struct sdw_cdns_port *port, u32 ch, u32 dir); -void sdw_cdns_config_stream(struct sdw_cdns *cdns, struct sdw_cdns_port *port, +struct sdw_cdns_pdi *sdw_cdns_alloc_pdi(struct sdw_cdns *cdns, + struct sdw_cdns_streams *stream, + u32 ch, u32 dir); +void sdw_cdns_config_stream(struct sdw_cdns *cdns, u32 ch, u32 dir, struct sdw_cdns_pdi *pdi); int sdw_cdns_pcm_set_stream(struct snd_soc_dai *dai, diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index a2cef9ba4289..f25695e95190 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -604,66 +604,6 @@ static int intel_post_bank_switch(struct sdw_bus *bus) * DAI routines */ -static struct sdw_cdns_port *intel_alloc_port(struct sdw_intel *sdw, - u32 ch, u32 dir, bool pcm) -{ - struct sdw_cdns *cdns = &sdw->cdns; - struct sdw_cdns_port *port = NULL; - int i, ret = 0; - - for (i = 0; i < cdns->num_ports; i++) { - if (cdns->ports[i].assigned) - continue; - - port = &cdns->ports[i]; - port->assigned = true; - port->direction = dir; - port->ch = ch; - break; - } - - if (!port) { - dev_err(cdns->dev, "Unable to find a free port\n"); - return NULL; - } - - if (pcm) { - ret = sdw_cdns_alloc_stream(cdns, &cdns->pcm, port, ch, dir); - if (ret) - goto out; - - intel_pdi_shim_configure(sdw, port->pdi); - sdw_cdns_config_stream(cdns, port, ch, dir, port->pdi); - - intel_pdi_alh_configure(sdw, port->pdi); - - } else { - ret = sdw_cdns_alloc_stream(cdns, &cdns->pdm, port, ch, dir); - } - -out: - if (ret) { - port->assigned = false; - port = NULL; - } - - return port; -} - -static void intel_port_cleanup(struct sdw_cdns_dma_data *dma) -{ - int i; - - for (i = 0; i < dma->nr_ports; i++) { - if (dma->port[i]) { - dma->port[i]->pdi->assigned = false; - dma->port[i]->pdi = NULL; - dma->port[i]->assigned = false; - dma->port[i] = NULL; - } - } -} - static int intel_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -671,9 +611,11 @@ static int intel_hw_params(struct snd_pcm_substream *substream, struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); struct sdw_intel *sdw = cdns_to_intel(cdns); struct sdw_cdns_dma_data *dma; + struct sdw_cdns_pdi *pdi; struct sdw_stream_config sconfig; struct sdw_port_config *pconfig; - int ret, i, ch, dir; + int ch, dir; + int ret; bool pcm = true; dma = snd_soc_dai_get_dma_data(dai, substream); @@ -686,38 +628,31 @@ static int intel_hw_params(struct snd_pcm_substream *substream, else dir = SDW_DATA_DIR_TX; - if (dma->stream_type == SDW_STREAM_PDM) { - /* TODO: Check whether PDM decimator is already in use */ - dma->nr_ports = sdw_cdns_get_stream(cdns, &cdns->pdm, ch, dir); + if (dma->stream_type == SDW_STREAM_PDM) pcm = false; - } else { - dma->nr_ports = sdw_cdns_get_stream(cdns, &cdns->pcm, ch, dir); + + /* FIXME: We would need to get PDI info from topology */ + if (pcm) + pdi = sdw_cdns_alloc_pdi(cdns, &cdns->pcm, ch, dir); + else + pdi = sdw_cdns_alloc_pdi(cdns, &cdns->pdm, ch, dir); + + if (!pdi) { + ret = -EINVAL; + goto error; } - if (!dma->nr_ports) { - dev_err(dai->dev, "ports/resources not available\n"); - return -EINVAL; - } + /* do run-time configurations for SHIM, ALH and PDI/PORT */ + intel_pdi_shim_configure(sdw, pdi); + intel_pdi_alh_configure(sdw, pdi); + sdw_cdns_config_stream(cdns, ch, dir, pdi); - dma->port = kcalloc(dma->nr_ports, sizeof(*dma->port), GFP_KERNEL); - if (!dma->port) - return -ENOMEM; - - for (i = 0; i < dma->nr_ports; i++) { - dma->port[i] = intel_alloc_port(sdw, ch, dir, pcm); - if (!dma->port[i]) { - ret = -EINVAL; - goto port_error; - } - } /* Inform DSP about PDI stream number */ - for (i = 0; i < dma->nr_ports; i++) { - ret = intel_config_stream(sdw, substream, dai, params, - dma->port[i]->pdi->intel_alh_id); - if (ret) - goto port_error; - } + ret = intel_config_stream(sdw, substream, dai, params, + pdi->intel_alh_id); + if (ret) + goto error; sconfig.direction = dir; sconfig.ch_count = ch; @@ -732,32 +667,22 @@ static int intel_hw_params(struct snd_pcm_substream *substream, } /* Port configuration */ - pconfig = kcalloc(dma->nr_ports, sizeof(*pconfig), GFP_KERNEL); + pconfig = kcalloc(1, sizeof(*pconfig), GFP_KERNEL); if (!pconfig) { ret = -ENOMEM; - goto port_error; + goto error; } - for (i = 0; i < dma->nr_ports; i++) { - pconfig[i].num = dma->port[i]->num; - pconfig[i].ch_mask = (1 << ch) - 1; - } + pconfig->num = pdi->num; + pconfig->ch_mask = (1 << ch) - 1; ret = sdw_stream_add_master(&cdns->bus, &sconfig, - pconfig, dma->nr_ports, dma->stream); - if (ret) { + pconfig, 1, dma->stream); + if (ret) dev_err(cdns->dev, "add master to stream failed:%d\n", ret); - goto stream_error; - } kfree(pconfig); - return ret; - -stream_error: - kfree(pconfig); -port_error: - intel_port_cleanup(dma); - kfree(dma->port); +error: return ret; } @@ -777,8 +702,6 @@ intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) dev_err(dai->dev, "remove master from stream %s failed: %d\n", dma->stream->name, ret); - intel_port_cleanup(dma); - kfree(dma->port); return ret; } From 807c15bc77871c695e254423f5e3839b2175db03 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 16 Sep 2019 14:23:47 -0500 Subject: [PATCH 034/198] soundwire: intel: don't filter out PDI0/1 PDI0/1 are reserved for Bulk and filtered out in the existing code. That leads to endless confusions on whether the index is the raw or corrected one. In addition we will need support for Bulk at some point so it's just simpler to expose those PDIs and not use it for now than try to be smart unless we have to remove the smarts. This patch requires a topology change to use PDIs starting at offset 2 explicitly. Note that there is a known discrepancy between hardware documentation and what ALH stream works in practice, future fixes are likely. Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20190916192348.467-6-pierre-louis.bossart@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/cadence_master.c | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 52ce380482b2..13ca305adf00 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -183,9 +183,6 @@ MODULE_PARM_DESC(cdns_mcp_int_mask, "Cadence MCP IntMask"); #define CDNS_DEFAULT_SSP_INTERVAL 0x18 #define CDNS_TX_TIMEOUT 2000 -#define CDNS_PCM_PDI_OFFSET 0x2 -#define CDNS_PDM_PDI_OFFSET 0x6 - #define CDNS_SCP_RX_FIFOLEVEL 0x2 /* @@ -279,11 +276,7 @@ static int cdns_reg_show(struct seq_file *s, void *data) ret += scnprintf(buf + ret, RD_BUF - ret, "\nDPn B0 Registers\n"); - /* - * in sdw_cdns_pdi_init() we filter out the Bulk PDIs, - * so the indices need to be corrected again - */ - num_ports = cdns->num_ports + CDNS_PCM_PDI_OFFSET; + num_ports = cdns->num_ports; for (i = 0; i < num_ports; i++) { ret += scnprintf(buf + ret, RD_BUF - ret, @@ -851,11 +844,8 @@ int sdw_cdns_pdi_init(struct sdw_cdns *cdns, /* Allocate PDIs for PCMs */ stream = &cdns->pcm; - /* First two PDIs are reserved for bulk transfers */ - if (stream->num_bd < CDNS_PCM_PDI_OFFSET) - return -EINVAL; - stream->num_bd -= CDNS_PCM_PDI_OFFSET; - offset = CDNS_PCM_PDI_OFFSET; + /* we allocate PDI0 and PDI1 which are used for Bulk */ + offset = 0; ret = cdns_allocate_pdi(cdns, &stream->bd, stream->num_bd, offset); @@ -882,7 +872,6 @@ int sdw_cdns_pdi_init(struct sdw_cdns *cdns, /* Allocate PDIs for PDMs */ stream = &cdns->pdm; - offset = CDNS_PDM_PDI_OFFSET; ret = cdns_allocate_pdi(cdns, &stream->bd, stream->num_bd, offset); if (ret) @@ -899,6 +888,9 @@ int sdw_cdns_pdi_init(struct sdw_cdns *cdns, ret = cdns_allocate_pdi(cdns, &stream->out, stream->num_out, offset); + + offset += stream->num_out; + if (ret) return ret; @@ -1177,12 +1169,13 @@ EXPORT_SYMBOL(cdns_set_sdw_stream); * Find and return a free PDI for a given PDI array */ static struct sdw_cdns_pdi *cdns_find_pdi(struct sdw_cdns *cdns, + unsigned int offset, unsigned int num, struct sdw_cdns_pdi *pdi) { int i; - for (i = 0; i < num; i++) { + for (i = offset; i < num; i++) { if (pdi[i].assigned) continue; pdi[i].assigned = true; @@ -1232,13 +1225,13 @@ struct sdw_cdns_pdi *sdw_cdns_alloc_pdi(struct sdw_cdns *cdns, struct sdw_cdns_pdi *pdi = NULL; if (dir == SDW_DATA_DIR_RX) - pdi = cdns_find_pdi(cdns, stream->num_in, stream->in); + pdi = cdns_find_pdi(cdns, 0, stream->num_in, stream->in); else - pdi = cdns_find_pdi(cdns, stream->num_out, stream->out); + pdi = cdns_find_pdi(cdns, 0, stream->num_out, stream->out); /* check if we found a PDI, else find in bi-directional */ if (!pdi) - pdi = cdns_find_pdi(cdns, stream->num_bd, stream->bd); + pdi = cdns_find_pdi(cdns, 2, stream->num_bd, stream->bd); if (pdi) { pdi->l_ch_num = 0; From 1b53385e7938d5a093e92044f9c89e4e76106f1b Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 16 Sep 2019 14:23:48 -0500 Subject: [PATCH 035/198] soundwire: cadence_master: improve PDI allocation PDI number should match dai->id, there is no need to track if a PDI is allocated or not. Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20190916192348.467-7-pierre-louis.bossart@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/cadence_master.c | 27 ++++++++++++++------------- drivers/soundwire/cadence_master.h | 4 +--- drivers/soundwire/intel.c | 5 ++--- 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 13ca305adf00..dbe386e179b4 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -814,7 +814,6 @@ static int cdns_allocate_pdi(struct sdw_cdns *cdns, for (i = 0; i < num; i++) { pdi[i].num = i + pdi_offset; - pdi[i].assigned = false; } *stream = pdi; @@ -1166,21 +1165,20 @@ EXPORT_SYMBOL(cdns_set_sdw_stream); * @num: Number of PDIs * @pdi: PDI instances * - * Find and return a free PDI for a given PDI array + * Find a PDI for a given PDI array. The PDI num and dai_id are + * expected to match, return NULL otherwise. */ static struct sdw_cdns_pdi *cdns_find_pdi(struct sdw_cdns *cdns, unsigned int offset, unsigned int num, - struct sdw_cdns_pdi *pdi) + struct sdw_cdns_pdi *pdi, + int dai_id) { int i; - for (i = offset; i < num; i++) { - if (pdi[i].assigned) - continue; - pdi[i].assigned = true; - return &pdi[i]; - } + for (i = offset; i < offset + num; i++) + if (pdi[i].num == dai_id) + return &pdi[i]; return NULL; } @@ -1220,18 +1218,21 @@ EXPORT_SYMBOL(sdw_cdns_config_stream); */ struct sdw_cdns_pdi *sdw_cdns_alloc_pdi(struct sdw_cdns *cdns, struct sdw_cdns_streams *stream, - u32 ch, u32 dir) + u32 ch, u32 dir, int dai_id) { struct sdw_cdns_pdi *pdi = NULL; if (dir == SDW_DATA_DIR_RX) - pdi = cdns_find_pdi(cdns, 0, stream->num_in, stream->in); + pdi = cdns_find_pdi(cdns, 0, stream->num_in, stream->in, + dai_id); else - pdi = cdns_find_pdi(cdns, 0, stream->num_out, stream->out); + pdi = cdns_find_pdi(cdns, 0, stream->num_out, stream->out, + dai_id); /* check if we found a PDI, else find in bi-directional */ if (!pdi) - pdi = cdns_find_pdi(cdns, 2, stream->num_bd, stream->bd); + pdi = cdns_find_pdi(cdns, 2, stream->num_bd, stream->bd, + dai_id); if (pdi) { pdi->l_ch_num = 0; diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h index 3634503f20b1..fbabd8afd3f5 100644 --- a/drivers/soundwire/cadence_master.h +++ b/drivers/soundwire/cadence_master.h @@ -8,7 +8,6 @@ /** * struct sdw_cdns_pdi: PDI (Physical Data Interface) instance * - * @assigned: pdi assigned * @num: pdi number * @intel_alh_id: link identifier * @l_ch_num: low channel for PDI @@ -18,7 +17,6 @@ * @type: stream type, PDM or PCM */ struct sdw_cdns_pdi { - bool assigned; int num; int intel_alh_id; int l_ch_num; @@ -154,7 +152,7 @@ int sdw_cdns_get_stream(struct sdw_cdns *cdns, u32 ch, u32 dir); struct sdw_cdns_pdi *sdw_cdns_alloc_pdi(struct sdw_cdns *cdns, struct sdw_cdns_streams *stream, - u32 ch, u32 dir); + u32 ch, u32 dir, int dai_id); void sdw_cdns_config_stream(struct sdw_cdns *cdns, u32 ch, u32 dir, struct sdw_cdns_pdi *pdi); diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index f25695e95190..acdec12d748d 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -631,11 +631,10 @@ static int intel_hw_params(struct snd_pcm_substream *substream, if (dma->stream_type == SDW_STREAM_PDM) pcm = false; - /* FIXME: We would need to get PDI info from topology */ if (pcm) - pdi = sdw_cdns_alloc_pdi(cdns, &cdns->pcm, ch, dir); + pdi = sdw_cdns_alloc_pdi(cdns, &cdns->pcm, ch, dir, dai->id); else - pdi = sdw_cdns_alloc_pdi(cdns, &cdns->pdm, ch, dir); + pdi = sdw_cdns_alloc_pdi(cdns, &cdns->pdm, ch, dir, dai->id); if (!pdi) { ret = -EINVAL; From 834c7360f92ad88155c5628eb10d60419ebaa0df Mon Sep 17 00:00:00 2001 From: Jann Horn Date: Fri, 18 Oct 2019 17:39:46 +0200 Subject: [PATCH 036/198] binder: Remove incorrect comment about vm_insert_page() behavior vm_insert_page() does increment the page refcount, and just to be sure, I've confirmed it by printing page_count(page[0].page_ptr) before and after vm_insert_page(). It's 1 before, 2 afterwards, as expected. Fixes: a145dd411eb2 ("VM: add "vm_insert_page()" function") Signed-off-by: Jann Horn Acked-by: Christian Brauner Link: https://lore.kernel.org/r/20191018153946.128584-1-jannh@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder_alloc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c index d42a8b2f636a..2faada3e97fd 100644 --- a/drivers/android/binder_alloc.c +++ b/drivers/android/binder_alloc.c @@ -267,7 +267,6 @@ static int binder_update_page_range(struct binder_alloc *alloc, int allocate, alloc->pages_high = index + 1; trace_binder_alloc_page_end(alloc, index); - /* vm_insert_page does not seem to increment the refcount */ } if (mm) { up_read(&mm->mmap_sem); From 6859eba4f6fbc515ea05f53b77ec55f49a6dcf0f Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Thu, 10 Oct 2019 16:27:10 +0300 Subject: [PATCH 037/198] samples: mei: use hostprogs kbuild constructs Use hostprogs kbuild constructs to compile mei sample program mei-amt-version Add CONFIG_SAMPLE_INTEL_MEI option to enable/disable the feature. Signed-off-by: Tomas Winkler Link: https://lore.kernel.org/r/20191010132710.4075-1-tomas.winkler@intel.com Signed-off-by: Greg Kroah-Hartman --- samples/Kconfig | 7 +++++++ samples/Makefile | 1 + samples/mei/Makefile | 12 ++++++------ 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/samples/Kconfig b/samples/Kconfig index c8dacb4dda80..b663d9d24114 100644 --- a/samples/Kconfig +++ b/samples/Kconfig @@ -169,4 +169,11 @@ config SAMPLE_VFS as mount API and statx(). Note that this is restricted to the x86 arch whilst it accesses system calls that aren't yet in all arches. +config SAMPLE_INTEL_MEI + bool "Build example program working with intel mei driver" + depends on INTEL_MEI + help + Build a sample program to work with mei device. + + endif # SAMPLES diff --git a/samples/Makefile b/samples/Makefile index 7d6e4ca28d69..d6062ab25347 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -20,3 +20,4 @@ obj-$(CONFIG_SAMPLE_TRACE_PRINTK) += trace_printk/ obj-$(CONFIG_VIDEO_PCI_SKELETON) += v4l/ obj-y += vfio-mdev/ subdir-$(CONFIG_SAMPLE_VFS) += vfs +obj-$(CONFIG_SAMPLE_INTEL_MEI) += mei/ diff --git a/samples/mei/Makefile b/samples/mei/Makefile index c7e52e9e92ca..27f37efdadb4 100644 --- a/samples/mei/Makefile +++ b/samples/mei/Makefile @@ -1,10 +1,10 @@ # SPDX-License-Identifier: GPL-2.0 -CC := $(CROSS_COMPILE)gcc -CFLAGS := -I../../usr/include +# Copyright (c) 2012-2019, Intel Corporation. All rights reserved. -PROGS := mei-amt-version +hostprogs-y := mei-amt-version -all: $(PROGS) +HOSTCFLAGS_mei-amt-version.o += -I$(objtree)/usr/include -clean: - rm -fr $(PROGS) +always := $(hostprogs-y) + +all: mei-amt-version From 3fc40449a06bc4510fd4eee4590af87917c47184 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Tue, 22 Oct 2019 10:03:08 +0530 Subject: [PATCH 038/198] soundwire: intel: use correct header for io calls Commit df72b7192119 ("soundwire: intel: add missing headers for cross-compilation") tried to fix cross compilation but erroneously used wrong header in one of the file. Fix it by using correct io.h header. Reported-by: kbuild test robot Fixes: df72b7192119 ("soundwire: intel: add missing headers for cross-compilation") Signed-off-by: Vinod Koul --- drivers/soundwire/intel_init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c index d488c44fcbae..2a2b4d8df462 100644 --- a/drivers/soundwire/intel_init.c +++ b/drivers/soundwire/intel_init.c @@ -9,7 +9,7 @@ #include #include -#include +#include #include #include #include From fbf6a7a4c976668ce5fd5de29e48772b146eabd1 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 6 Sep 2019 13:49:44 +0100 Subject: [PATCH 039/198] phy: xgene: make array serdes_reg static const, makes object smaller Don't populate the array serdes_reg on the stack but instead make it static const. Makes the object code smaller by 228 bytes. Before: text data bss dec hex filename 23875 6232 64 30171 75db drivers/phy/phy-xgene.o After: text data bss dec hex filename 23551 6328 64 29943 74f7 drivers/phy/phy-xgene.o (gcc version 9.2.1, amd64) Signed-off-by: Colin Ian King Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/phy-xgene.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/phy-xgene.c b/drivers/phy/phy-xgene.c index 3c9189473407..7a33ec12f71b 100644 --- a/drivers/phy/phy-xgene.c +++ b/drivers/phy/phy-xgene.c @@ -1342,7 +1342,7 @@ static int xgene_phy_hw_initialize(struct xgene_phy_ctx *ctx, static void xgene_phy_force_lat_summer_cal(struct xgene_phy_ctx *ctx, int lane) { int i; - struct { + static const struct { u32 reg; u32 val; } serdes_reg[] = { From f466de0208e3ef6e9d28ad0b2d6a30cdd9d5052f Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Wed, 9 Oct 2019 15:23:09 +0800 Subject: [PATCH 040/198] phy: tegra: xusb: remove unused variable The local variable @priv is set but not used, can be removed Signed-off-by: Chunfeng Yun Acked-by: Thierry Reding Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/tegra/xusb-tegra210.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/phy/tegra/xusb-tegra210.c b/drivers/phy/tegra/xusb-tegra210.c index 0c0df6897a3b..bc71c897298a 100644 --- a/drivers/phy/tegra/xusb-tegra210.c +++ b/drivers/phy/tegra/xusb-tegra210.c @@ -1225,13 +1225,10 @@ static int tegra210_hsic_phy_power_on(struct phy *phy) struct tegra_xusb_hsic_lane *hsic = to_hsic_lane(lane); struct tegra_xusb_hsic_pad *pad = to_hsic_pad(lane->pad); struct tegra_xusb_padctl *padctl = lane->pad->padctl; - struct tegra210_xusb_padctl *priv; unsigned int index = lane->index; u32 value; int err; - priv = to_tegra210_xusb_padctl(padctl); - err = regulator_enable(pad->supply); if (err) return err; From 265938bb1bc8f45141f59a62ac295c53946c4da0 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Thu, 26 Sep 2019 18:15:23 +0200 Subject: [PATCH 041/198] phy-mvebu-a3700-utmi: Use devm_platform_ioremap_resource() in mvebu_a3700_utmi_phy_probe() Simplify this function implementation by using a known wrapper function. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/marvell/phy-mvebu-a3700-utmi.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/phy/marvell/phy-mvebu-a3700-utmi.c b/drivers/phy/marvell/phy-mvebu-a3700-utmi.c index ded900b06f5a..23bc3bf5c4c0 100644 --- a/drivers/phy/marvell/phy-mvebu-a3700-utmi.c +++ b/drivers/phy/marvell/phy-mvebu-a3700-utmi.c @@ -216,20 +216,13 @@ static int mvebu_a3700_utmi_phy_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct mvebu_a3700_utmi *utmi; struct phy_provider *provider; - struct resource *res; utmi = devm_kzalloc(dev, sizeof(*utmi), GFP_KERNEL); if (!utmi) return -ENOMEM; /* Get UTMI memory region */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(dev, "Missing UTMI PHY memory resource\n"); - return -ENODEV; - } - - utmi->regs = devm_ioremap_resource(dev, res); + utmi->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(utmi->regs)) return PTR_ERR(utmi->regs); From c9dfadeee89d4c5d335969356c4e63e9b279d038 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 16 Oct 2019 16:27:33 +0200 Subject: [PATCH 042/198] phy: renesas: rcar-gen3-usb2: Use platform_get_irq_optional() for optional irq As platform_get_irq() now prints an error when the interrupt does not exist, a scary warning may be printed for an optional interrupt: phy_rcar_gen3_usb2 ee0a0200.usb-phy: IRQ index 0 not found Fix this by calling platform_get_irq_optional() instead. Fixes: 7723f4c5ecdb8d83 ("driver core: platform: Add an error message to platform_get_irq*()") Signed-off-by: Geert Uytterhoeven Reviewed-by: Yoshihiro Shimoda Reviewed-by: Stephen Boyd Tested-by: Yoshihiro Shimoda Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/renesas/phy-rcar-gen3-usb2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb2.c b/drivers/phy/renesas/phy-rcar-gen3-usb2.c index b7f6b1324395..49ec67d46ccc 100644 --- a/drivers/phy/renesas/phy-rcar-gen3-usb2.c +++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c @@ -614,7 +614,7 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) return PTR_ERR(channel->base); /* call request_irq for OTG */ - irq = platform_get_irq(pdev, 0); + irq = platform_get_irq_optional(pdev, 0); if (irq >= 0) { INIT_WORK(&channel->work, rcar_gen3_phy_usb2_work); irq = devm_request_irq(dev, irq, rcar_gen3_phy_usb2_irq, From fa093440142d3d25d619580373bb40e9653a203a Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 16 Oct 2019 21:57:35 +0800 Subject: [PATCH 043/198] phy: hisilicon: use devm_platform_ioremap_resource() to simplify code Use devm_platform_ioremap_resource() to simplify the code a bit. This is detected by coccinelle. Signed-off-by: YueHaibing Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/hisilicon/phy-hisi-inno-usb2.c | 4 +--- drivers/phy/hisilicon/phy-histb-combphy.c | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/phy/hisilicon/phy-hisi-inno-usb2.c b/drivers/phy/hisilicon/phy-hisi-inno-usb2.c index 9b16f13b5ab2..34a6a9a1ceb2 100644 --- a/drivers/phy/hisilicon/phy-hisi-inno-usb2.c +++ b/drivers/phy/hisilicon/phy-hisi-inno-usb2.c @@ -114,7 +114,6 @@ static int hisi_inno_phy_probe(struct platform_device *pdev) struct hisi_inno_phy_priv *priv; struct phy_provider *provider; struct device_node *child; - struct resource *res; int i = 0; int ret; @@ -122,8 +121,7 @@ static int hisi_inno_phy_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - priv->mmio = devm_ioremap_resource(dev, res); + priv->mmio = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(priv->mmio)) { ret = PTR_ERR(priv->mmio); return ret; diff --git a/drivers/phy/hisilicon/phy-histb-combphy.c b/drivers/phy/hisilicon/phy-histb-combphy.c index 62d10ef20296..f1cb3e4d2add 100644 --- a/drivers/phy/hisilicon/phy-histb-combphy.c +++ b/drivers/phy/hisilicon/phy-histb-combphy.c @@ -195,7 +195,6 @@ static int histb_combphy_probe(struct platform_device *pdev) struct histb_combphy_priv *priv; struct device_node *np = dev->of_node; struct histb_combphy_mode *mode; - struct resource *res; u32 vals[3]; int ret; @@ -203,8 +202,7 @@ static int histb_combphy_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - priv->mmio = devm_ioremap_resource(dev, res); + priv->mmio = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(priv->mmio)) { ret = PTR_ERR(priv->mmio); return ret; From e4b957d3a7c74749e2ccfb3dedb63b81e84b292c Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 15 Oct 2019 17:03:31 +0100 Subject: [PATCH 044/198] phy: phy-brcm-usb-init: fix __iomem annotations The register address should have __iomem attributes so fix this to remove the following sparse warnings: drivers/phy/broadcom/phy-brcm-usb-init.c:459:30: warning: cast removes address space '' of expression drivers/phy/broadcom/phy-brcm-usb-init.c:459:30: warning: incorrect type in argument 2 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:459:30: expected void [noderef] *addr drivers/phy/broadcom/phy-brcm-usb-init.c:459:30: got void * drivers/phy/broadcom/phy-brcm-usb-init.c:461:30: warning: cast removes address space '' of expression drivers/phy/broadcom/phy-brcm-usb-init.c:461:30: warning: incorrect type in argument 2 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:461:30: expected void [noderef] *addr drivers/phy/broadcom/phy-brcm-usb-init.c:461:30: got void * drivers/phy/broadcom/phy-brcm-usb-init.c:465:30: warning: cast removes address space '' of expression drivers/phy/broadcom/phy-brcm-usb-init.c:465:30: warning: incorrect type in argument 2 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:465:30: expected void [noderef] *addr drivers/phy/broadcom/phy-brcm-usb-init.c:465:30: got void * drivers/phy/broadcom/phy-brcm-usb-init.c:469:30: warning: cast removes address space '' of expression drivers/phy/broadcom/phy-brcm-usb-init.c:469:30: warning: incorrect type in argument 1 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:469:30: expected void [noderef] *addr drivers/phy/broadcom/phy-brcm-usb-init.c:469:30: got void * drivers/phy/broadcom/phy-brcm-usb-init.c:478:30: warning: cast removes address space '' of expression drivers/phy/broadcom/phy-brcm-usb-init.c:478:30: warning: incorrect type in argument 2 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:478:30: expected void [noderef] *addr drivers/phy/broadcom/phy-brcm-usb-init.c:478:30: got void * drivers/phy/broadcom/phy-brcm-usb-init.c:480:30: warning: cast removes address space '' of expression drivers/phy/broadcom/phy-brcm-usb-init.c:480:30: warning: incorrect type in argument 2 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:480:30: expected void [noderef] *addr drivers/phy/broadcom/phy-brcm-usb-init.c:480:30: got void * drivers/phy/broadcom/phy-brcm-usb-init.c:485:30: warning: cast removes address space '' of expression drivers/phy/broadcom/phy-brcm-usb-init.c:485:30: warning: incorrect type in argument 2 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:485:30: expected void [noderef] *addr drivers/phy/broadcom/phy-brcm-usb-init.c:485:30: got void * drivers/phy/broadcom/phy-brcm-usb-init.c:494:9: warning: cast removes address space '' of expression drivers/phy/broadcom/phy-brcm-usb-init.c:494:9: warning: incorrect type in argument 1 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:494:9: expected void [noderef] *reg drivers/phy/broadcom/phy-brcm-usb-init.c:494:9: got void * drivers/phy/broadcom/phy-brcm-usb-init.c:495:9: warning: cast removes address space '' of expression drivers/phy/broadcom/phy-brcm-usb-init.c:495:9: warning: incorrect type in argument 1 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:495:9: expected void [noderef] *reg drivers/phy/broadcom/phy-brcm-usb-init.c:495:9: got void * drivers/phy/broadcom/phy-brcm-usb-init.c:498:9: warning: cast removes address space '' of expression drivers/phy/broadcom/phy-brcm-usb-init.c:498:9: warning: incorrect type in argument 1 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:498:9: expected void [noderef] *reg drivers/phy/broadcom/phy-brcm-usb-init.c:498:9: got void * drivers/phy/broadcom/phy-brcm-usb-init.c:501:9: warning: cast removes address space '' of expression drivers/phy/broadcom/phy-brcm-usb-init.c:501:9: warning: incorrect type in argument 1 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:501:9: expected void [noderef] *reg drivers/phy/broadcom/phy-brcm-usb-init.c:501:9: got void * drivers/phy/broadcom/phy-brcm-usb-init.c:613:9: warning: cast removes address space '' of expression drivers/phy/broadcom/phy-brcm-usb-init.c:613:9: warning: incorrect type in argument 1 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:613:9: expected void [noderef] *reg drivers/phy/broadcom/phy-brcm-usb-init.c:613:9: got void * drivers/phy/broadcom/phy-brcm-usb-init.c:640:9: warning: cast removes address space '' of expression drivers/phy/broadcom/phy-brcm-usb-init.c:640:9: warning: incorrect type in argument 1 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:640:9: expected void [noderef] *reg drivers/phy/broadcom/phy-brcm-usb-init.c:640:9: got void * drivers/phy/broadcom/phy-brcm-usb-init.c:422:13: warning: incorrect type in assignment (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:422:13: expected void *reg drivers/phy/broadcom/phy-brcm-usb-init.c:422:13: got void [noderef] * drivers/phy/broadcom/phy-brcm-usb-init.c:423:38: warning: incorrect type in argument 1 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:423:38: expected void [noderef] *addr drivers/phy/broadcom/phy-brcm-usb-init.c:423:38: got void *reg drivers/phy/broadcom/phy-brcm-usb-init.c:423:52: warning: incorrect type in argument 2 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:423:52: expected void [noderef] *addr drivers/phy/broadcom/phy-brcm-usb-init.c:423:52: got void *reg drivers/phy/broadcom/phy-brcm-usb-init.c:710:64: warning: Using plain integer as NULL pointer drivers/phy/broadcom/phy-brcm-usb-init.c:712:32: warning: cast removes address space '' of expression drivers/phy/broadcom/phy-brcm-usb-init.c:712:32: warning: incorrect type in argument 2 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:712:32: expected void [noderef] *addr drivers/phy/broadcom/phy-brcm-usb-init.c:712:32: got void * drivers/phy/broadcom/phy-brcm-usb-init.c:713:29: warning: cast removes address space '' of expression drivers/phy/broadcom/phy-brcm-usb-init.c:713:29: warning: incorrect type in argument 1 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:713:29: expected void [noderef] *addr drivers/phy/broadcom/phy-brcm-usb-init.c:713:29: got void * drivers/phy/broadcom/phy-brcm-usb-init.c:717:29: warning: cast removes address space '' of expression drivers/phy/broadcom/phy-brcm-usb-init.c:717:29: warning: incorrect type in argument 2 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:717:29: expected void [noderef] *addr drivers/phy/broadcom/phy-brcm-usb-init.c:717:29: got void * drivers/phy/broadcom/phy-brcm-usb-init.c:720:9: warning: cast removes address space '' of expression drivers/phy/broadcom/phy-brcm-usb-init.c:720:9: warning: incorrect type in argument 1 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:720:9: expected void [noderef] *reg drivers/phy/broadcom/phy-brcm-usb-init.c:720:9: got void * drivers/phy/broadcom/phy-brcm-usb-init.c:721:9: warning: cast removes address space '' of expression drivers/phy/broadcom/phy-brcm-usb-init.c:721:9: warning: incorrect type in argument 1 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:721:9: expected void [noderef] *reg drivers/phy/broadcom/phy-brcm-usb-init.c:721:9: got void * drivers/phy/broadcom/phy-brcm-usb-init.c:422:13: warning: incorrect type in assignment (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:422:13: expected void *reg drivers/phy/broadcom/phy-brcm-usb-init.c:422:13: got void [noderef] * drivers/phy/broadcom/phy-brcm-usb-init.c:423:38: warning: incorrect type in argument 1 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:423:38: expected void [noderef] *addr drivers/phy/broadcom/phy-brcm-usb-init.c:423:38: got void *reg drivers/phy/broadcom/phy-brcm-usb-init.c:423:52: warning: incorrect type in argument 2 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:423:52: expected void [noderef] *addr drivers/phy/broadcom/phy-brcm-usb-init.c:423:52: got void *reg drivers/phy/broadcom/phy-brcm-usb-init.c:422:13: warning: incorrect type in assignment (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:422:13: expected void *reg drivers/phy/broadcom/phy-brcm-usb-init.c:422:13: got void [noderef] * drivers/phy/broadcom/phy-brcm-usb-init.c:423:38: warning: incorrect type in argument 1 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:423:38: expected void [noderef] *addr drivers/phy/broadcom/phy-brcm-usb-init.c:423:38: got void *reg drivers/phy/broadcom/phy-brcm-usb-init.c:423:52: warning: incorrect type in argument 2 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:423:52: expected void [noderef] *addr drivers/phy/broadcom/phy-brcm-usb-init.c:423:52: got void *reg drivers/phy/broadcom/phy-brcm-usb-init.c:434:13: warning: incorrect type in assignment (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:434:13: expected void *reg drivers/phy/broadcom/phy-brcm-usb-init.c:434:13: got void [noderef] * drivers/phy/broadcom/phy-brcm-usb-init.c:435:38: warning: incorrect type in argument 1 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:435:38: expected void [noderef] *addr drivers/phy/broadcom/phy-brcm-usb-init.c:435:38: got void *reg drivers/phy/broadcom/phy-brcm-usb-init.c:435:51: warning: incorrect type in argument 2 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:435:51: expected void [noderef] *addr drivers/phy/broadcom/phy-brcm-usb-init.c:435:51: got void *reg drivers/phy/broadcom/phy-brcm-usb-init.c:434:13: warning: incorrect type in assignment (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:434:13: expected void *reg drivers/phy/broadcom/phy-brcm-usb-init.c:434:13: got void [noderef] * drivers/phy/broadcom/phy-brcm-usb-init.c:435:38: warning: incorrect type in argument 1 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:435:38: expected void [noderef] *addr drivers/phy/broadcom/phy-brcm-usb-init.c:435:38: got void *reg drivers/phy/broadcom/phy-brcm-usb-init.c:435:51: warning: incorrect type in argument 2 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:435:51: expected void [noderef] *addr drivers/phy/broadcom/phy-brcm-usb-init.c:435:51: got void *reg drivers/phy/broadcom/phy-brcm-usb-init.c:434:13: warning: incorrect type in assignment (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:434:13: expected void *reg drivers/phy/broadcom/phy-brcm-usb-init.c:434:13: got void [noderef] * drivers/phy/broadcom/phy-brcm-usb-init.c:435:38: warning: incorrect type in argument 1 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:435:38: expected void [noderef] *addr drivers/phy/broadcom/phy-brcm-usb-init.c:435:38: got void *reg drivers/phy/broadcom/phy-brcm-usb-init.c:435:51: warning: incorrect type in argument 2 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:435:51: expected void [noderef] *addr drivers/phy/broadcom/phy-brcm-usb-init.c:435:51: got void *reg drivers/phy/broadcom/phy-brcm-usb-init.c:434:13: warning: incorrect type in assignment (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:434:13: expected void *reg drivers/phy/broadcom/phy-brcm-usb-init.c:434:13: got void [noderef] * drivers/phy/broadcom/phy-brcm-usb-init.c:435:38: warning: incorrect type in argument 1 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:435:38: expected void [noderef] *addr drivers/phy/broadcom/phy-brcm-usb-init.c:435:38: got void *reg drivers/phy/broadcom/phy-brcm-usb-init.c:435:51: warning: incorrect type in argument 2 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:435:51: expected void [noderef] *addr drivers/phy/broadcom/phy-brcm-usb-init.c:435:51: got void *reg drivers/phy/broadcom/phy-brcm-usb-init.c:794:29: warning: cast removes address space '' of expression drivers/phy/broadcom/phy-brcm-usb-init.c:794:29: warning: incorrect type in argument 1 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:794:29: expected void [noderef] *addr drivers/phy/broadcom/phy-brcm-usb-init.c:794:29: got void * drivers/phy/broadcom/phy-brcm-usb-init.c:813:29: warning: cast removes address space '' of expression drivers/phy/broadcom/phy-brcm-usb-init.c:813:29: warning: incorrect type in argument 2 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:813:29: expected void [noderef] *addr drivers/phy/broadcom/phy-brcm-usb-init.c:813:29: got void * drivers/phy/broadcom/phy-brcm-usb-init.c:829:37: warning: cast removes address space '' of expression drivers/phy/broadcom/phy-brcm-usb-init.c:829:37: warning: incorrect type in argument 1 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:829:37: expected void [noderef] *addr drivers/phy/broadcom/phy-brcm-usb-init.c:829:37: got void * drivers/phy/broadcom/phy-brcm-usb-init.c:843:37: warning: cast removes address space '' of expression drivers/phy/broadcom/phy-brcm-usb-init.c:843:37: warning: incorrect type in argument 1 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:843:37: expected void [noderef] *addr drivers/phy/broadcom/phy-brcm-usb-init.c:843:37: got void * drivers/phy/broadcom/phy-brcm-usb-init.c:847:37: warning: cast removes address space '' of expression drivers/phy/broadcom/phy-brcm-usb-init.c:847:37: warning: incorrect type in argument 2 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:847:37: expected void [noderef] *addr drivers/phy/broadcom/phy-brcm-usb-init.c:847:37: got void * drivers/phy/broadcom/phy-brcm-usb-init.c:422:13: warning: incorrect type in assignment (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:422:13: expected void *reg drivers/phy/broadcom/phy-brcm-usb-init.c:422:13: got void [noderef] * drivers/phy/broadcom/phy-brcm-usb-init.c:423:38: warning: incorrect type in argument 1 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:423:38: expected void [noderef] *addr drivers/phy/broadcom/phy-brcm-usb-init.c:423:38: got void *reg drivers/phy/broadcom/phy-brcm-usb-init.c:423:52: warning: incorrect type in argument 2 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:423:52: expected void [noderef] *addr drivers/phy/broadcom/phy-brcm-usb-init.c:423:52: got void *reg drivers/phy/broadcom/phy-brcm-usb-init.c:422:13: warning: incorrect type in assignment (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:422:13: expected void *reg drivers/phy/broadcom/phy-brcm-usb-init.c:422:13: got void [noderef] * drivers/phy/broadcom/phy-brcm-usb-init.c:423:38: warning: incorrect type in argument 1 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:423:38: expected void [noderef] *addr drivers/phy/broadcom/phy-brcm-usb-init.c:423:38: got void *reg drivers/phy/broadcom/phy-brcm-usb-init.c:423:52: warning: incorrect type in argument 2 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:423:52: expected void [noderef] *addr drivers/phy/broadcom/phy-brcm-usb-init.c:423:52: got void *reg drivers/phy/broadcom/phy-brcm-usb-init.c:434:13: warning: incorrect type in assignment (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:434:13: expected void *reg drivers/phy/broadcom/phy-brcm-usb-init.c:434:13: got void [noderef] * drivers/phy/broadcom/phy-brcm-usb-init.c:435:38: warning: incorrect type in argument 1 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:435:38: expected void [noderef] *addr drivers/phy/broadcom/phy-brcm-usb-init.c:435:38: got void *reg drivers/phy/broadcom/phy-brcm-usb-init.c:435:51: warning: incorrect type in argument 2 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:435:51: expected void [noderef] *addr drivers/phy/broadcom/phy-brcm-usb-init.c:435:51: got void *reg drivers/phy/broadcom/phy-brcm-usb-init.c:878:9: warning: cast removes address space '' of expression drivers/phy/broadcom/phy-brcm-usb-init.c:878:9: warning: incorrect type in argument 1 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:878:9: expected void [noderef] *reg drivers/phy/broadcom/phy-brcm-usb-init.c:878:9: got void * drivers/phy/broadcom/phy-brcm-usb-init.c:880:29: warning: cast removes address space '' of expression drivers/phy/broadcom/phy-brcm-usb-init.c:880:29: warning: incorrect type in argument 1 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:880:29: expected void [noderef] *addr drivers/phy/broadcom/phy-brcm-usb-init.c:880:29: got void * drivers/phy/broadcom/phy-brcm-usb-init.c:896:29: warning: cast removes address space '' of expression drivers/phy/broadcom/phy-brcm-usb-init.c:896:29: warning: incorrect type in argument 2 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:896:29: expected void [noderef] *addr drivers/phy/broadcom/phy-brcm-usb-init.c:896:29: got void * drivers/phy/broadcom/phy-brcm-usb-init.c:901:37: warning: cast removes address space '' of expression drivers/phy/broadcom/phy-brcm-usb-init.c:901:37: warning: incorrect type in argument 1 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:901:37: expected void [noderef] *addr drivers/phy/broadcom/phy-brcm-usb-init.c:901:37: got void * drivers/phy/broadcom/phy-brcm-usb-init.c:905:37: warning: cast removes address space '' of expression drivers/phy/broadcom/phy-brcm-usb-init.c:905:37: warning: incorrect type in argument 2 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:905:37: expected void [noderef] *addr drivers/phy/broadcom/phy-brcm-usb-init.c:905:37: got void * drivers/phy/broadcom/phy-brcm-usb-init.c:422:13: warning: incorrect type in assignment (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:422:13: expected void *reg drivers/phy/broadcom/phy-brcm-usb-init.c:422:13: got void [noderef] * drivers/phy/broadcom/phy-brcm-usb-init.c:423:38: warning: incorrect type in argument 1 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:423:38: expected void [noderef] *addr drivers/phy/broadcom/phy-brcm-usb-init.c:423:38: got void *reg drivers/phy/broadcom/phy-brcm-usb-init.c:423:52: warning: incorrect type in argument 2 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:423:52: expected void [noderef] *addr drivers/phy/broadcom/phy-brcm-usb-init.c:423:52: got void *reg drivers/phy/broadcom/phy-brcm-usb-init.c:422:13: warning: incorrect type in assignment (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:422:13: expected void *reg drivers/phy/broadcom/phy-brcm-usb-init.c:422:13: got void [noderef] * drivers/phy/broadcom/phy-brcm-usb-init.c:423:38: warning: incorrect type in argument 1 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:423:38: expected void [noderef] *addr drivers/phy/broadcom/phy-brcm-usb-init.c:423:38: got void *reg drivers/phy/broadcom/phy-brcm-usb-init.c:423:52: warning: incorrect type in argument 2 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:423:52: expected void [noderef] *addr drivers/phy/broadcom/phy-brcm-usb-init.c:423:52: got void *reg drivers/phy/broadcom/phy-brcm-usb-init.c:434:13: warning: incorrect type in assignment (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:434:13: expected void *reg drivers/phy/broadcom/phy-brcm-usb-init.c:434:13: got void [noderef] * drivers/phy/broadcom/phy-brcm-usb-init.c:435:38: warning: incorrect type in argument 1 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:435:38: expected void [noderef] *addr drivers/phy/broadcom/phy-brcm-usb-init.c:435:38: got void *reg drivers/phy/broadcom/phy-brcm-usb-init.c:435:51: warning: incorrect type in argument 2 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:435:51: expected void [noderef] *addr drivers/phy/broadcom/phy-brcm-usb-init.c:435:51: got void *reg drivers/phy/broadcom/phy-brcm-usb-init.c:434:13: warning: incorrect type in assignment (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:434:13: expected void *reg drivers/phy/broadcom/phy-brcm-usb-init.c:434:13: got void [noderef] * drivers/phy/broadcom/phy-brcm-usb-init.c:435:38: warning: incorrect type in argument 1 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:435:38: expected void [noderef] *addr drivers/phy/broadcom/phy-brcm-usb-init.c:435:38: got void *reg drivers/phy/broadcom/phy-brcm-usb-init.c:435:51: warning: incorrect type in argument 2 (different address spaces) drivers/phy/broadcom/phy-brcm-usb-init.c:435:51: expected void [noderef] *addr drivers/phy/broadcom/phy-brcm-usb-init.c:435:51: got void *reg drivers/phy/broadcom/phy-brcm-usb-init.c:422:13: warning: too many warnings Signed-off-by: Ben Dooks Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/broadcom/phy-brcm-usb-init.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/phy/broadcom/phy-brcm-usb-init.c b/drivers/phy/broadcom/phy-brcm-usb-init.c index 3c53625f8bc2..fa6dd117c40e 100644 --- a/drivers/phy/broadcom/phy-brcm-usb-init.c +++ b/drivers/phy/broadcom/phy-brcm-usb-init.c @@ -126,8 +126,8 @@ enum { USB_CTRL_SELECTOR_COUNT, }; -#define USB_CTRL_REG(base, reg) ((void *)base + USB_CTRL_##reg) -#define USB_XHCI_EC_REG(base, reg) ((void *)base + USB_XHCI_EC_##reg) +#define USB_CTRL_REG(base, reg) ((void __iomem *)base + USB_CTRL_##reg) +#define USB_XHCI_EC_REG(base, reg) ((void __iomem *)base + USB_XHCI_EC_##reg) #define USB_CTRL_MASK(reg, field) \ USB_CTRL_##reg##_##field##_MASK #define USB_CTRL_MASK_FAMILY(params, reg, field) \ @@ -416,7 +416,7 @@ void usb_ctrl_unset_family(struct brcm_usb_init_params *params, u32 reg_offset, u32 field) { u32 mask; - void *reg; + void __iomem *reg; mask = params->usb_reg_bits_map[field]; reg = params->ctrl_regs + reg_offset; @@ -428,7 +428,7 @@ void usb_ctrl_set_family(struct brcm_usb_init_params *params, u32 reg_offset, u32 field) { u32 mask; - void *reg; + void __iomem *reg; mask = params->usb_reg_bits_map[field]; reg = params->ctrl_regs + reg_offset; From 1025cb924bd517f3c458f36973582d4c2adedd6a Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 15 Oct 2019 17:03:32 +0100 Subject: [PATCH 045/198] phy: phy-brcm-usb-init: fix use of integer as pointer The xhci_ec_base variable is a pointer, so don't compare it with an integer. Signed-off-by: Ben Dooks Reviewed-by: Andrew Murray Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/broadcom/phy-brcm-usb-init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/broadcom/phy-brcm-usb-init.c b/drivers/phy/broadcom/phy-brcm-usb-init.c index fa6dd117c40e..91b5b09589d6 100644 --- a/drivers/phy/broadcom/phy-brcm-usb-init.c +++ b/drivers/phy/broadcom/phy-brcm-usb-init.c @@ -707,7 +707,7 @@ static void brcmusb_usb3_otp_fix(struct brcm_usb_init_params *params) void __iomem *xhci_ec_base = params->xhci_ec_regs; u32 val; - if (params->family_id != 0x74371000 || xhci_ec_base == 0) + if (params->family_id != 0x74371000 || !xhci_ec_base) return; brcmusb_writel(0xa20c, USB_XHCI_EC_REG(xhci_ec_base, IRAADR)); val = brcmusb_readl(USB_XHCI_EC_REG(xhci_ec_base, IRADAT)); From 700d3802427d0550a2e0947254491edb284347b5 Mon Sep 17 00:00:00 2001 From: Fabrizio Castro Date: Tue, 8 Oct 2019 11:38:43 +0100 Subject: [PATCH 046/198] dt-bindings: rcar-gen3-phy-usb2: Add r8a774b1 support Document RZ/G2N (R8A774B1) SoC bindings. Signed-off-by: Fabrizio Castro Reviewed-by: Geert Uytterhoeven Reviewed-by: Yoshihiro Shimoda Signed-off-by: Kishon Vijay Abraham I --- Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt b/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt index 503a8cfb3184..7734b219d9aa 100644 --- a/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt +++ b/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt @@ -10,6 +10,8 @@ Required properties: SoC. "renesas,usb2-phy-r8a774a1" if the device is a part of an R8A774A1 SoC. + "renesas,usb2-phy-r8a774b1" if the device is a part of an R8A774B1 + SoC. "renesas,usb2-phy-r8a774c0" if the device is a part of an R8A774C0 SoC. "renesas,usb2-phy-r8a7795" if the device is a part of an R8A7795 From 3f6d43a66e3e7cc3f0026153d54a2197f62276e4 Mon Sep 17 00:00:00 2001 From: Fabrizio Castro Date: Tue, 8 Oct 2019 11:38:46 +0100 Subject: [PATCH 047/198] dt-bindings: rcar-gen3-phy-usb3: Add r8a774b1 support Document RZ/G2N (R8A774B1) SoC bindings. Signed-off-by: Fabrizio Castro Reviewed-by: Geert Uytterhoeven Reviewed-by: Yoshihiro Shimoda Signed-off-by: Kishon Vijay Abraham I --- Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb3.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb3.txt b/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb3.txt index 9d9826609c2f..0fe433b9a592 100644 --- a/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb3.txt +++ b/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb3.txt @@ -8,6 +8,8 @@ need this driver. Required properties: - compatible: "renesas,r8a774a1-usb3-phy" if the device is a part of an R8A774A1 + SoC. + "renesas,r8a774b1-usb3-phy" if the device is a part of an R8A774B1 SoC. "renesas,r8a7795-usb3-phy" if the device is a part of an R8A7795 SoC. From ac25b6e9f8d26b90694c1150b2e677aeac46ec71 Mon Sep 17 00:00:00 2001 From: Nagarjuna Kristam Date: Fri, 18 Oct 2019 15:08:06 +0530 Subject: [PATCH 048/198] phy: tegra: xusb: Add XUSB dual mode support on Tegra210 Configure the port capabilities based on usb_dr_mode settings. Based on work by JC Kuo . Signed-off-by: Nagarjuna Kristam Reviewed-by: JC Kuo Acked-by: Thierry Reding Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/tegra/xusb-tegra210.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/drivers/phy/tegra/xusb-tegra210.c b/drivers/phy/tegra/xusb-tegra210.c index bc71c897298a..5d15478ef2a6 100644 --- a/drivers/phy/tegra/xusb-tegra210.c +++ b/drivers/phy/tegra/xusb-tegra210.c @@ -39,7 +39,10 @@ #define XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_XUSB 0x1 #define XUSB_PADCTL_USB2_PORT_CAP 0x008 +#define XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_DISABLED(x) (0x0 << ((x) * 4)) #define XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_HOST(x) (0x1 << ((x) * 4)) +#define XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_DEVICE(x) (0x2 << ((x) * 4)) +#define XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_OTG(x) (0x3 << ((x) * 4)) #define XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_MASK(x) (0x3 << ((x) * 4)) #define XUSB_PADCTL_SS_PORT_MAP 0x014 @@ -64,6 +67,7 @@ #define XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPADX_CTL1(x) (0x084 + (x) * 0x40) #define XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_LEV_SHIFT 7 #define XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_LEV_MASK 0x3 +#define XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_LEV_VAL 0x1 #define XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_FIX18 (1 << 6) #define XUSB_PADCTL_USB2_OTG_PADX_CTL0(x) (0x088 + (x) * 0x40) @@ -957,7 +961,14 @@ static int tegra210_usb2_phy_power_on(struct phy *phy) value = padctl_readl(padctl, XUSB_PADCTL_USB2_PORT_CAP); value &= ~XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_MASK(index); - value |= XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_HOST(index); + if (port->mode == USB_DR_MODE_UNKNOWN) + value |= XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_DISABLED(index); + else if (port->mode == USB_DR_MODE_PERIPHERAL) + value |= XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_DEVICE(index); + else if (port->mode == USB_DR_MODE_HOST) + value |= XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_HOST(index); + else if (port->mode == USB_DR_MODE_OTG) + value |= XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_OTG(index); padctl_writel(padctl, value, XUSB_PADCTL_USB2_PORT_CAP); value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index)); @@ -989,7 +1000,12 @@ static int tegra210_usb2_phy_power_on(struct phy *phy) XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPADX_CTL1(index)); value &= ~(XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_LEV_MASK << XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_LEV_SHIFT); - value |= XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_FIX18; + if (port->mode == USB_DR_MODE_HOST) + value |= XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_FIX18; + else + value |= + XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_LEV_VAL << + XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_LEV_SHIFT; padctl_writel(padctl, value, XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPADX_CTL1(index)); From a5be28c3656af71f1c9d75381f7b86d5056da9f3 Mon Sep 17 00:00:00 2001 From: Nagarjuna Kristam Date: Fri, 18 Oct 2019 15:08:07 +0530 Subject: [PATCH 049/198] phy: tegra: xusb: Add usb3 port fake support on Tegra210 On Tegra210, usb2 only otg/peripheral ports dont work in device mode. They need an assosciated usb3 port to work in device mode. Identify an unused usb3 port and assign it as a fake USB3 port to USB2 only port whose mode is otg/peripheral. Based on work by BH Hsieh . Signed-off-by: Nagarjuna Kristam Acked-by: Thierry Reding Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/tegra/xusb-tegra210.c | 56 ++++++++++++++++++++++++++ drivers/phy/tegra/xusb.c | 65 +++++++++++++++++++++++++++++++ drivers/phy/tegra/xusb.h | 2 + 3 files changed, 123 insertions(+) diff --git a/drivers/phy/tegra/xusb-tegra210.c b/drivers/phy/tegra/xusb-tegra210.c index 5d15478ef2a6..12e5a46c1de3 100644 --- a/drivers/phy/tegra/xusb-tegra210.c +++ b/drivers/phy/tegra/xusb-tegra210.c @@ -50,6 +50,7 @@ #define XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_SHIFT(x) ((x) * 5) #define XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK(x) (0x7 << ((x) * 5)) #define XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP(x, v) (((v) & 0x7) << ((x) * 5)) +#define XUSB_PADCTL_SS_PORT_MAP_PORT_DISABLED 0x7 #define XUSB_PADCTL_ELPG_PROGRAM1 0x024 #define XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN (1 << 31) @@ -944,6 +945,34 @@ static int tegra210_usb2_phy_power_on(struct phy *phy) priv = to_tegra210_xusb_padctl(padctl); + if (port->usb3_port_fake != -1) { + value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP); + value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK( + port->usb3_port_fake); + value |= XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP( + port->usb3_port_fake, index); + padctl_writel(padctl, value, XUSB_PADCTL_SS_PORT_MAP); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); + value &= ~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_VCORE_DOWN( + port->usb3_port_fake); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); + + usleep_range(100, 200); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); + value &= ~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN_EARLY( + port->usb3_port_fake); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); + + usleep_range(100, 200); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); + value &= ~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN( + port->usb3_port_fake); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); + } + value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL0); value &= ~((XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_MASK << XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_SHIFT) | @@ -1078,6 +1107,32 @@ static int tegra210_usb2_phy_power_off(struct phy *phy) mutex_lock(&padctl->lock); + if (port->usb3_port_fake != -1) { + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); + value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN_EARLY( + port->usb3_port_fake); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); + + usleep_range(100, 200); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); + value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN( + port->usb3_port_fake); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); + + usleep_range(250, 350); + + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1); + value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_VCORE_DOWN( + port->usb3_port_fake); + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1); + + value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP); + value |= XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP(port->usb3_port_fake, + XUSB_PADCTL_SS_PORT_MAP_PORT_DISABLED); + padctl_writel(padctl, value, XUSB_PADCTL_SS_PORT_MAP); + } + if (WARN_ON(pad->enable == 0)) goto out; @@ -2049,6 +2104,7 @@ const struct tegra_xusb_padctl_soc tegra210_xusb_padctl_soc = { .ops = &tegra210_xusb_padctl_ops, .supply_names = tegra210_xusb_padctl_supply_names, .num_supplies = ARRAY_SIZE(tegra210_xusb_padctl_supply_names), + .need_fake_usb3_port = true, }; EXPORT_SYMBOL_GPL(tegra210_xusb_padctl_soc); diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c index 2ea8497af82a..b4b217e2ad49 100644 --- a/drivers/phy/tegra/xusb.c +++ b/drivers/phy/tegra/xusb.c @@ -800,9 +800,62 @@ static void __tegra_xusb_remove_ports(struct tegra_xusb_padctl *padctl) } } +static int tegra_xusb_find_unused_usb3_port(struct tegra_xusb_padctl *padctl) +{ + struct device_node *np; + unsigned int i; + + for (i = 0; i < padctl->soc->ports.usb3.count; i++) { + np = tegra_xusb_find_port_node(padctl, "usb3", i); + if (!np || !of_device_is_available(np)) + return i; + } + + return -ENODEV; +} + +static bool tegra_xusb_port_is_companion(struct tegra_xusb_usb2_port *usb2) +{ + unsigned int i; + struct tegra_xusb_usb3_port *usb3; + struct tegra_xusb_padctl *padctl = usb2->base.padctl; + + for (i = 0; i < padctl->soc->ports.usb3.count; i++) { + usb3 = tegra_xusb_find_usb3_port(padctl, i); + if (usb3 && usb3->port == usb2->base.index) + return true; + } + + return false; +} + +static int tegra_xusb_update_usb3_fake_port(struct tegra_xusb_usb2_port *usb2) +{ + int fake; + + /* Disable usb3_port_fake usage by default and assign if needed */ + usb2->usb3_port_fake = -1; + + if ((usb2->mode == USB_DR_MODE_OTG || + usb2->mode == USB_DR_MODE_PERIPHERAL) && + !tegra_xusb_port_is_companion(usb2)) { + fake = tegra_xusb_find_unused_usb3_port(usb2->base.padctl); + if (fake < 0) { + dev_err(&usb2->base.dev, "no unused USB3 ports available\n"); + return -ENODEV; + } + + dev_dbg(&usb2->base.dev, "Found unused usb3 port: %d\n", fake); + usb2->usb3_port_fake = fake; + } + + return 0; +} + static int tegra_xusb_setup_ports(struct tegra_xusb_padctl *padctl) { struct tegra_xusb_port *port; + struct tegra_xusb_usb2_port *usb2; unsigned int i; int err = 0; @@ -832,6 +885,18 @@ static int tegra_xusb_setup_ports(struct tegra_xusb_padctl *padctl) goto remove_ports; } + if (padctl->soc->need_fake_usb3_port) { + for (i = 0; i < padctl->soc->ports.usb2.count; i++) { + usb2 = tegra_xusb_find_usb2_port(padctl, i); + if (!usb2) + continue; + + err = tegra_xusb_update_usb3_fake_port(usb2); + if (err < 0) + goto remove_ports; + } + } + list_for_each_entry(port, &padctl->ports, list) { err = port->ops->enable(port); if (err < 0) diff --git a/drivers/phy/tegra/xusb.h b/drivers/phy/tegra/xusb.h index 093076ca27fd..bd91832a0843 100644 --- a/drivers/phy/tegra/xusb.h +++ b/drivers/phy/tegra/xusb.h @@ -291,6 +291,7 @@ struct tegra_xusb_usb2_port { struct regulator *supply; enum usb_dr_mode mode; bool internal; + int usb3_port_fake; }; static inline struct tegra_xusb_usb2_port * @@ -389,6 +390,7 @@ struct tegra_xusb_padctl_soc { const char * const *supply_names; unsigned int num_supplies; + bool need_fake_usb3_port; }; struct tegra_xusb_padctl { From 90767cdfea89c3ac7012035d66633b9ba839091a Mon Sep 17 00:00:00 2001 From: Nagarjuna Kristam Date: Fri, 18 Oct 2019 15:08:08 +0530 Subject: [PATCH 050/198] phy: tegra: xusb: Add vbus override support on Tegra210 Tegra XUSB device control driver needs to control vbus override during its operations, add API for the support. Signed-off-by: Nagarjuna Kristam Acked-by: Thierry Reding Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/tegra/xusb-tegra210.c | 58 +++++++++++++++++++++++++++++++ drivers/phy/tegra/xusb.c | 22 ++++++++++++ drivers/phy/tegra/xusb.h | 2 ++ include/linux/phy/tegra/xusb.h | 4 ++- 4 files changed, 85 insertions(+), 1 deletion(-) diff --git a/drivers/phy/tegra/xusb-tegra210.c b/drivers/phy/tegra/xusb-tegra210.c index 12e5a46c1de3..394913bb2f20 100644 --- a/drivers/phy/tegra/xusb-tegra210.c +++ b/drivers/phy/tegra/xusb-tegra210.c @@ -65,6 +65,10 @@ #define XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(x) (1 << (1 + (x))) #define XUSB_PADCTL_USB3_PAD_MUX_SATA_IDDQ_DISABLE(x) (1 << (8 + (x))) +#define XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPADX_CTL0(x) (0x080 + (x) * 0x40) +#define XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL0_ZIP (1 << 18) +#define XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL0_ZIN (1 << 22) + #define XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPADX_CTL1(x) (0x084 + (x) * 0x40) #define XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_LEV_SHIFT 7 #define XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_LEV_MASK 0x3 @@ -227,6 +231,12 @@ #define XUSB_PADCTL_UPHY_USB3_PADX_ECTL6(x) (0xa74 + (x) * 0x40) #define XUSB_PADCTL_UPHY_USB3_PAD_ECTL6_RX_EQ_CTRL_H_VAL 0xfcf01368 +#define XUSB_PADCTL_USB2_VBUS_ID 0xc60 +#define XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_VBUS_ON (1 << 14) +#define XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT 18 +#define XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_MASK 0xf +#define XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_FLOATING 8 + struct tegra210_xusb_fuse_calibration { u32 hs_curr_level[4]; u32 hs_term_range_adj; @@ -2013,6 +2023,52 @@ static const struct tegra_xusb_port_ops tegra210_usb3_port_ops = { .map = tegra210_usb3_port_map, }; +static int tegra210_xusb_padctl_vbus_override(struct tegra_xusb_padctl *padctl, + bool status) +{ + u32 value; + + dev_dbg(padctl->dev, "%s vbus override\n", status ? "set" : "clear"); + + value = padctl_readl(padctl, XUSB_PADCTL_USB2_VBUS_ID); + + if (status) { + value |= XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_VBUS_ON; + value &= ~(XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_MASK << + XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT); + value |= XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_FLOATING << + XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT; + } else { + value &= ~XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_VBUS_ON; + } + + padctl_writel(padctl, value, XUSB_PADCTL_USB2_VBUS_ID); + + return 0; +} + +static int tegra210_utmi_port_reset(struct phy *phy) +{ + struct tegra_xusb_padctl *padctl; + struct tegra_xusb_lane *lane; + u32 value; + + lane = phy_get_drvdata(phy); + padctl = lane->pad->padctl; + + value = padctl_readl(padctl, + XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPADX_CTL0(lane->index)); + + if ((value & XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL0_ZIP) || + (value & XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL0_ZIN)) { + tegra210_xusb_padctl_vbus_override(padctl, false); + tegra210_xusb_padctl_vbus_override(padctl, true); + return 1; + } + + return 0; +} + static int tegra210_xusb_read_fuse_calibration(struct tegra210_xusb_fuse_calibration *fuse) { @@ -2075,6 +2131,8 @@ static const struct tegra_xusb_padctl_ops tegra210_xusb_padctl_ops = { .remove = tegra210_xusb_padctl_remove, .usb3_set_lfps_detect = tegra210_usb3_set_lfps_detect, .hsic_set_idle = tegra210_hsic_set_idle, + .vbus_override = tegra210_xusb_padctl_vbus_override, + .utmi_port_reset = tegra210_utmi_port_reset, }; static const char * const tegra210_xusb_padctl_supply_names[] = { diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c index b4b217e2ad49..bf4b00853438 100644 --- a/drivers/phy/tegra/xusb.c +++ b/drivers/phy/tegra/xusb.c @@ -1121,6 +1121,28 @@ int tegra_xusb_padctl_usb3_set_lfps_detect(struct tegra_xusb_padctl *padctl, } EXPORT_SYMBOL_GPL(tegra_xusb_padctl_usb3_set_lfps_detect); +int tegra_xusb_padctl_set_vbus_override(struct tegra_xusb_padctl *padctl, + bool val) +{ + if (padctl->soc->ops->vbus_override) + return padctl->soc->ops->vbus_override(padctl, val); + + return -ENOTSUPP; +} +EXPORT_SYMBOL_GPL(tegra_xusb_padctl_set_vbus_override); + +int tegra_phy_xusb_utmi_port_reset(struct phy *phy) +{ + struct tegra_xusb_lane *lane = phy_get_drvdata(phy); + struct tegra_xusb_padctl *padctl = lane->pad->padctl; + + if (padctl->soc->ops->utmi_port_reset) + return padctl->soc->ops->utmi_port_reset(phy); + + return -ENOTSUPP; +} +EXPORT_SYMBOL_GPL(tegra_phy_xusb_utmi_port_reset); + MODULE_AUTHOR("Thierry Reding "); MODULE_DESCRIPTION("Tegra XUSB Pad Controller driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/tegra/xusb.h b/drivers/phy/tegra/xusb.h index bd91832a0843..da94fcce6307 100644 --- a/drivers/phy/tegra/xusb.h +++ b/drivers/phy/tegra/xusb.h @@ -373,6 +373,8 @@ struct tegra_xusb_padctl_ops { unsigned int index, bool idle); int (*usb3_set_lfps_detect)(struct tegra_xusb_padctl *padctl, unsigned int index, bool enable); + int (*vbus_override)(struct tegra_xusb_padctl *padctl, bool set); + int (*utmi_port_reset)(struct phy *phy); }; struct tegra_xusb_padctl_soc { diff --git a/include/linux/phy/tegra/xusb.h b/include/linux/phy/tegra/xusb.h index ee59562c8354..1235865e7e2c 100644 --- a/include/linux/phy/tegra/xusb.h +++ b/include/linux/phy/tegra/xusb.h @@ -18,5 +18,7 @@ int tegra_xusb_padctl_hsic_set_idle(struct tegra_xusb_padctl *padctl, unsigned int port, bool idle); int tegra_xusb_padctl_usb3_set_lfps_detect(struct tegra_xusb_padctl *padctl, unsigned int port, bool enable); - +int tegra_xusb_padctl_set_vbus_override(struct tegra_xusb_padctl *padctl, + bool val); +int tegra_phy_xusb_utmi_port_reset(struct phy *phy); #endif /* PHY_TEGRA_XUSB_H */ From a8a24429dd1d799f1c6737779af88c1d39290a77 Mon Sep 17 00:00:00 2001 From: Nagarjuna Kristam Date: Fri, 18 Oct 2019 15:08:09 +0530 Subject: [PATCH 051/198] phy: tegra: xusb: Add vbus override support on Tegra186 Tegra XUSB device control driver needs to control vbus override during its operations, add API for the support. Signed-off-by: Nagarjuna Kristam Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/tegra/xusb-tegra186.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/phy/tegra/xusb-tegra186.c b/drivers/phy/tegra/xusb-tegra186.c index 6f3afaf9398f..84c27394c181 100644 --- a/drivers/phy/tegra/xusb-tegra186.c +++ b/drivers/phy/tegra/xusb-tegra186.c @@ -857,9 +857,32 @@ static void tegra186_xusb_padctl_remove(struct tegra_xusb_padctl *padctl) { } +static int tegra186_xusb_padctl_vbus_override(struct tegra_xusb_padctl *padctl, + bool status) +{ + u32 value; + + dev_dbg(padctl->dev, "%s vbus override\n", status ? "set" : "clear"); + + value = padctl_readl(padctl, USB2_VBUS_ID); + + if (status) { + value |= VBUS_OVERRIDE; + value &= ~ID_OVERRIDE(~0); + value |= ID_OVERRIDE_FLOATING; + } else { + value &= ~VBUS_OVERRIDE; + } + + padctl_writel(padctl, value, USB2_VBUS_ID); + + return 0; +} + static const struct tegra_xusb_padctl_ops tegra186_xusb_padctl_ops = { .probe = tegra186_xusb_padctl_probe, .remove = tegra186_xusb_padctl_remove, + .vbus_override = tegra186_xusb_padctl_vbus_override, }; static const char * const tegra186_xusb_padctl_supply_names[] = { From 4bd5ead82d4b877ebe41daf95f28cda53205b039 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Mon, 7 Oct 2019 16:55:10 +0900 Subject: [PATCH 052/198] phy: renesas: rcar-gen3-usb2: Fix sysfs interface of "role" Since the role_store() uses strncmp(), it's possible to refer out-of-memory if the sysfs data size is smaller than strlen("host"). This patch fixes it by using sysfs_streq() instead of strncmp(). Reported-by: Pavel Machek Fixes: 9bb86777fb71 ("phy: rcar-gen3-usb2: add sysfs for usb role swap") Cc: # v4.10+ Signed-off-by: Yoshihiro Shimoda Reviewed-by: Geert Uytterhoeven Acked-by: Pavel Machek Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/renesas/phy-rcar-gen3-usb2.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb2.c b/drivers/phy/renesas/phy-rcar-gen3-usb2.c index 49ec67d46ccc..bfb22f868857 100644 --- a/drivers/phy/renesas/phy-rcar-gen3-usb2.c +++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -320,9 +321,9 @@ static ssize_t role_store(struct device *dev, struct device_attribute *attr, if (!ch->is_otg_channel || !rcar_gen3_is_any_rphy_initialized(ch)) return -EIO; - if (!strncmp(buf, "host", strlen("host"))) + if (sysfs_streq(buf, "host")) new_mode = PHY_MODE_USB_HOST; - else if (!strncmp(buf, "peripheral", strlen("peripheral"))) + else if (sysfs_streq(buf, "peripheral")) new_mode = PHY_MODE_USB_DEVICE; else return -EINVAL; From 49ea07d33d9a32c17e18b322e789507280ceb2a3 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 22 Oct 2019 18:54:44 -0500 Subject: [PATCH 053/198] soundwire: intel/cadence: fix startup sequence Multiple changes squashed in single patch to avoid tick-tock effect and avoid breaking compilation/bisect 1. Per the hardware documentation, all changes to MCP_CONFIG, MCP_CONTROL, MCP_CMDCTRL and MCP_PHYCTRL need to be validated with a self-clearing write to MCP_CONFIG_UPDATE. Add a helper and do the update when the CONFIG is changed. 2. Move interrupt enable after interrupt handler registration 3. Add a new helper to start the hardware bus reset with maximum duration to make sure the Slave(s) correctly detect the reset pattern and to ensure electrical conflicts can be resolved. 4. flush command FIFOs Better error handling will be provided after interrupt disable is provided in follow-up patches. Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20191022235448.17586-2-pierre-louis.bossart@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/cadence_master.c | 80 +++++++++++++++++++++--------- drivers/soundwire/cadence_master.h | 1 + drivers/soundwire/intel.c | 14 +++++- 3 files changed, 69 insertions(+), 26 deletions(-) diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index dbe386e179b4..5337ccacccd1 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -228,6 +228,22 @@ static int cdns_clear_bit(struct sdw_cdns *cdns, int offset, u32 value) return -EAGAIN; } +/* + * all changes to the MCP_CONFIG, MCP_CONTROL, MCP_CMDCTRL and MCP_PHYCTRL + * need to be confirmed with a write to MCP_CONFIG_UPDATE + */ +static int cdns_update_config(struct sdw_cdns *cdns) +{ + int ret; + + ret = cdns_clear_bit(cdns, CDNS_MCP_CONFIG_UPDATE, + CDNS_MCP_CONFIG_UPDATE_BIT); + if (ret < 0) + dev_err(cdns->dev, "Config update timedout\n"); + + return ret; +} + /* * debugfs */ @@ -745,7 +761,38 @@ EXPORT_SYMBOL(sdw_cdns_thread); /* * init routines */ -static int _cdns_enable_interrupt(struct sdw_cdns *cdns) + +/** + * sdw_cdns_exit_reset() - Program reset parameters and start bus operations + * @cdns: Cadence instance + */ +int sdw_cdns_exit_reset(struct sdw_cdns *cdns) +{ + /* program maximum length reset to be safe */ + cdns_updatel(cdns, CDNS_MCP_CONTROL, + CDNS_MCP_CONTROL_RST_DELAY, + CDNS_MCP_CONTROL_RST_DELAY); + + /* use hardware generated reset */ + cdns_updatel(cdns, CDNS_MCP_CONTROL, + CDNS_MCP_CONTROL_HW_RST, + CDNS_MCP_CONTROL_HW_RST); + + /* enable bus operations with clock and data */ + cdns_updatel(cdns, CDNS_MCP_CONFIG, + CDNS_MCP_CONFIG_OP, + CDNS_MCP_CONFIG_OP_NORMAL); + + /* commit changes */ + return cdns_update_config(cdns); +} +EXPORT_SYMBOL(sdw_cdns_exit_reset); + +/** + * sdw_cdns_enable_interrupt() - Enable SDW interrupts and update config + * @cdns: Cadence instance + */ +int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns) { u32 mask; @@ -777,24 +824,8 @@ static int _cdns_enable_interrupt(struct sdw_cdns *cdns) cdns_writel(cdns, CDNS_MCP_INTMASK, mask); - return 0; -} - -/** - * sdw_cdns_enable_interrupt() - Enable SDW interrupts and update config - * @cdns: Cadence instance - */ -int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns) -{ - int ret; - - _cdns_enable_interrupt(cdns); - ret = cdns_clear_bit(cdns, CDNS_MCP_CONFIG_UPDATE, - CDNS_MCP_CONFIG_UPDATE_BIT); - if (ret < 0) - dev_err(cdns->dev, "Config update timedout\n"); - - return ret; + /* commit changes */ + return cdns_update_config(cdns); } EXPORT_SYMBOL(sdw_cdns_enable_interrupt); @@ -955,6 +986,10 @@ int sdw_cdns_init(struct sdw_cdns *cdns) cdns_writel(cdns, CDNS_MCP_SSP_CTRL0, CDNS_DEFAULT_SSP_INTERVAL); cdns_writel(cdns, CDNS_MCP_SSP_CTRL1, CDNS_DEFAULT_SSP_INTERVAL); + /* flush command FIFOs */ + cdns_updatel(cdns, CDNS_MCP_CONTROL, CDNS_MCP_CONTROL_CMD_RST, + CDNS_MCP_CONTROL_CMD_RST); + /* Set cmd accept mode */ cdns_updatel(cdns, CDNS_MCP_CONTROL, CDNS_MCP_CONTROL_CMD_ACCEPT, CDNS_MCP_CONTROL_CMD_ACCEPT); @@ -977,13 +1012,10 @@ int sdw_cdns_init(struct sdw_cdns *cdns) /* Set cmd mode for Tx and Rx cmds */ val &= ~CDNS_MCP_CONFIG_CMD; - /* Set operation to normal */ - val &= ~CDNS_MCP_CONFIG_OP; - val |= CDNS_MCP_CONFIG_OP_NORMAL; - cdns_writel(cdns, CDNS_MCP_CONFIG, val); - return 0; + /* commit changes */ + return cdns_update_config(cdns); } EXPORT_SYMBOL(sdw_cdns_init); diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h index fbabd8afd3f5..6199e71edeab 100644 --- a/drivers/soundwire/cadence_master.h +++ b/drivers/soundwire/cadence_master.h @@ -141,6 +141,7 @@ irqreturn_t sdw_cdns_thread(int irq, void *dev_id); int sdw_cdns_init(struct sdw_cdns *cdns); int sdw_cdns_pdi_init(struct sdw_cdns *cdns, struct sdw_cdns_stream_config config); +int sdw_cdns_exit_reset(struct sdw_cdns *cdns); int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns); #ifdef CONFIG_DEBUG_FS diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index acdec12d748d..7b5947840ee6 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -947,8 +947,6 @@ static int intel_probe(struct platform_device *pdev) if (ret) goto err_init; - ret = sdw_cdns_enable_interrupt(&sdw->cdns); - /* Read the PDI config and initialize cadence PDI */ intel_pdi_init(sdw, &config); ret = sdw_cdns_pdi_init(&sdw->cdns, config); @@ -966,6 +964,18 @@ static int intel_probe(struct platform_device *pdev) goto err_init; } + ret = sdw_cdns_enable_interrupt(&sdw->cdns); + if (ret < 0) { + dev_err(sdw->cdns.dev, "cannot enable interrupts\n"); + goto err_init; + } + + ret = sdw_cdns_exit_reset(&sdw->cdns); + if (ret < 0) { + dev_err(sdw->cdns.dev, "unable to exit bus reset sequence\n"); + goto err_init; + } + /* Register DAIs */ ret = intel_register_dai(sdw); if (ret) { From 675d4c9aba86cf3345bc61880e2edfd6762ef4ae Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 22 Oct 2019 18:54:45 -0500 Subject: [PATCH 054/198] soundwire: cadence_master: add hw_reset capability in debugfs Provide debugfs capability to kick link and devices into hard-reset (as defined by MIPI). This capability is really useful when some devices are no longer responsive and/or to check the software handling of resynchronization. Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20191022235448.17586-3-pierre-louis.bossart@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/cadence_master.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 5337ccacccd1..7476e867468d 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -333,6 +333,26 @@ static int cdns_reg_show(struct seq_file *s, void *data) } DEFINE_SHOW_ATTRIBUTE(cdns_reg); +static int cdns_hw_reset(void *data, u64 value) +{ + struct sdw_cdns *cdns = data; + int ret; + + if (value != 1) + return -EINVAL; + + /* Userspace changed the hardware state behind the kernel's back */ + add_taint(TAINT_USER, LOCKDEP_STILL_OK); + + ret = sdw_cdns_exit_reset(cdns); + + dev_dbg(cdns->dev, "link hw_reset done: %d\n", ret); + + return ret; +} + +DEFINE_DEBUGFS_ATTRIBUTE(cdns_hw_reset_fops, NULL, cdns_hw_reset, "%llu\n"); + /** * sdw_cdns_debugfs_init() - Cadence debugfs init * @cdns: Cadence instance @@ -341,6 +361,9 @@ DEFINE_SHOW_ATTRIBUTE(cdns_reg); void sdw_cdns_debugfs_init(struct sdw_cdns *cdns, struct dentry *root) { debugfs_create_file("cdns-registers", 0400, root, cdns, &cdns_reg_fops); + + debugfs_create_file("cdns-hw-reset", 0200, root, cdns, + &cdns_hw_reset_fops); } EXPORT_SYMBOL_GPL(sdw_cdns_debugfs_init); From dfbe642d1ed9018271962a08602027aeb4f3f525 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 22 Oct 2019 18:54:46 -0500 Subject: [PATCH 055/198] soundwire: intel: add helper for initialization Move code to helper for reuse in power management routines Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20191022235448.17586-4-pierre-louis.bossart@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/intel.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 7b5947840ee6..cf514cdf195f 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -897,6 +897,15 @@ static struct sdw_master_ops sdw_intel_ops = { .post_bank_switch = intel_post_bank_switch, }; +static int intel_init(struct sdw_intel *sdw) +{ + /* Initialize shim and controller */ + intel_link_power_up(sdw); + intel_shim_init(sdw); + + return sdw_cdns_init(&sdw->cdns); +} + /* * probe and init */ @@ -939,11 +948,8 @@ static int intel_probe(struct platform_device *pdev) return 0; } - /* Initialize shim and controller */ - intel_link_power_up(sdw); - intel_shim_init(sdw); - - ret = sdw_cdns_init(&sdw->cdns); + /* Initialize shim, controller and Cadence IP */ + ret = intel_init(sdw); if (ret) goto err_init; From 9e3d47fb2bdc203c8cb63cbebdff99adcee76f5f Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 22 Oct 2019 18:54:47 -0500 Subject: [PATCH 056/198] soundwire: intel/cadence: add flag for interrupt enable Prepare for future PM support and fix error handling by disabling interrupts as needed. Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20191022235448.17586-5-pierre-louis.bossart@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/cadence_master.c | 18 ++++++++++++------ drivers/soundwire/cadence_master.h | 2 +- drivers/soundwire/intel.c | 13 +++++++------ 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 7476e867468d..d2c44bfff53a 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -815,14 +815,17 @@ EXPORT_SYMBOL(sdw_cdns_exit_reset); * sdw_cdns_enable_interrupt() - Enable SDW interrupts and update config * @cdns: Cadence instance */ -int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns) +int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns, bool state) { - u32 mask; + u32 slave_intmask0 = 0; + u32 slave_intmask1 = 0; + u32 mask = 0; - cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK0, - CDNS_MCP_SLAVE_INTMASK0_MASK); - cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK1, - CDNS_MCP_SLAVE_INTMASK1_MASK); + if (!state) + goto update_masks; + + slave_intmask0 = CDNS_MCP_SLAVE_INTMASK0_MASK; + slave_intmask1 = CDNS_MCP_SLAVE_INTMASK1_MASK; /* enable detection of all slave state changes */ mask = CDNS_MCP_INT_SLAVE_MASK; @@ -845,6 +848,9 @@ int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns) if (interrupt_mask) /* parameter override */ mask = interrupt_mask; +update_masks: + cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK0, slave_intmask0); + cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK1, slave_intmask1); cdns_writel(cdns, CDNS_MCP_INTMASK, mask); /* commit changes */ diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h index 6199e71edeab..a106aea6fe53 100644 --- a/drivers/soundwire/cadence_master.h +++ b/drivers/soundwire/cadence_master.h @@ -142,7 +142,7 @@ int sdw_cdns_init(struct sdw_cdns *cdns); int sdw_cdns_pdi_init(struct sdw_cdns *cdns, struct sdw_cdns_stream_config config); int sdw_cdns_exit_reset(struct sdw_cdns *cdns); -int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns); +int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns, bool state); #ifdef CONFIG_DEBUG_FS void sdw_cdns_debugfs_init(struct sdw_cdns *cdns, struct dentry *root); diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index cf514cdf195f..8d32d8eaff1c 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -939,7 +939,7 @@ static int intel_probe(struct platform_device *pdev) ret = sdw_add_bus_master(&sdw->cdns.bus); if (ret) { dev_err(&pdev->dev, "sdw_add_bus_master fail: %d\n", ret); - goto err_master_reg; + return ret; } if (sdw->cdns.bus.prop.hw_disabled) { @@ -970,7 +970,7 @@ static int intel_probe(struct platform_device *pdev) goto err_init; } - ret = sdw_cdns_enable_interrupt(&sdw->cdns); + ret = sdw_cdns_enable_interrupt(&sdw->cdns, true); if (ret < 0) { dev_err(sdw->cdns.dev, "cannot enable interrupts\n"); goto err_init; @@ -979,7 +979,7 @@ static int intel_probe(struct platform_device *pdev) ret = sdw_cdns_exit_reset(&sdw->cdns); if (ret < 0) { dev_err(sdw->cdns.dev, "unable to exit bus reset sequence\n"); - goto err_init; + goto err_interrupt; } /* Register DAIs */ @@ -987,18 +987,18 @@ static int intel_probe(struct platform_device *pdev) if (ret) { dev_err(sdw->cdns.dev, "DAI registration failed: %d\n", ret); snd_soc_unregister_component(sdw->cdns.dev); - goto err_dai; + goto err_interrupt; } intel_debugfs_init(sdw); return 0; -err_dai: +err_interrupt: + sdw_cdns_enable_interrupt(&sdw->cdns, false); free_irq(sdw->res->irq, sdw); err_init: sdw_delete_bus_master(&sdw->cdns.bus); -err_master_reg: return ret; } @@ -1010,6 +1010,7 @@ static int intel_remove(struct platform_device *pdev) if (!sdw->cdns.bus.prop.hw_disabled) { intel_debugfs_exit(sdw); + sdw_cdns_enable_interrupt(&sdw->cdns, false); free_irq(sdw->res->irq, sdw); snd_soc_unregister_component(sdw->cdns.dev); } From 3ccb8551f52edfcf16291cde07a8d49fc3976b7a Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 22 Oct 2019 18:54:48 -0500 Subject: [PATCH 057/198] soundwire: cadence_master: make clock stop exit configurable on init The use of clock stop is not a requirement, the IP can e.g. be completely power gated and not detect any wakes while in s2idle/deep sleep. For now clock-stop is not supported anyways so the control parameter is always false. This will be revisited when we add clock stop. Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20191022235448.17586-6-pierre-louis.bossart@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/cadence_master.c | 15 ++++++++------- drivers/soundwire/cadence_master.h | 2 +- drivers/soundwire/intel.c | 2 +- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index d2c44bfff53a..fed21e2b2277 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -979,7 +979,7 @@ static u32 cdns_set_initial_frame_shape(int n_rows, int n_cols) * sdw_cdns_init() - Cadence initialization * @cdns: Cadence instance */ -int sdw_cdns_init(struct sdw_cdns *cdns) +int sdw_cdns_init(struct sdw_cdns *cdns, bool clock_stop_exit) { struct sdw_bus *bus = &cdns->bus; struct sdw_master_prop *prop = &bus->prop; @@ -987,12 +987,13 @@ int sdw_cdns_init(struct sdw_cdns *cdns) int divider; int ret; - /* Exit clock stop */ - ret = cdns_clear_bit(cdns, CDNS_MCP_CONTROL, - CDNS_MCP_CONTROL_CLK_STOP_CLR); - if (ret < 0) { - dev_err(cdns->dev, "Couldn't exit from clock stop\n"); - return ret; + if (clock_stop_exit) { + ret = cdns_clear_bit(cdns, CDNS_MCP_CONTROL, + CDNS_MCP_CONTROL_CLK_STOP_CLR); + if (ret < 0) { + dev_err(cdns->dev, "Couldn't exit from clock stop\n"); + return ret; + } } /* Set clock divider */ diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h index a106aea6fe53..001457cbe5ad 100644 --- a/drivers/soundwire/cadence_master.h +++ b/drivers/soundwire/cadence_master.h @@ -138,7 +138,7 @@ extern struct sdw_master_ops sdw_cdns_master_ops; irqreturn_t sdw_cdns_irq(int irq, void *dev_id); irqreturn_t sdw_cdns_thread(int irq, void *dev_id); -int sdw_cdns_init(struct sdw_cdns *cdns); +int sdw_cdns_init(struct sdw_cdns *cdns, bool clock_stop_exit); int sdw_cdns_pdi_init(struct sdw_cdns *cdns, struct sdw_cdns_stream_config config); int sdw_cdns_exit_reset(struct sdw_cdns *cdns); diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 8d32d8eaff1c..3a6bbf0ff04e 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -903,7 +903,7 @@ static int intel_init(struct sdw_intel *sdw) intel_link_power_up(sdw); intel_shim_init(sdw); - return sdw_cdns_init(&sdw->cdns); + return sdw_cdns_init(&sdw->cdns, false); } /* From 54fad40d3f01c5d14e2ba123b2753029dd6a4cbd Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 23 Oct 2019 15:45:23 +0800 Subject: [PATCH 058/198] phy: ti: dm816x: remove set but not used variable 'phy_data' Fixes gcc '-Wunused-but-set-variable' warning: drivers/phy/ti/phy-dm816x-usb.c:192:29: warning: variable phy_data set but not used [-Wunused-but-set-variable] It is never used, so can be removed. Signed-off-by: YueHaibing Reviewed-by: Roger Quadros Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/ti/phy-dm816x-usb.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/phy/ti/phy-dm816x-usb.c b/drivers/phy/ti/phy-dm816x-usb.c index cbcce7cf0028..26f194779064 100644 --- a/drivers/phy/ti/phy-dm816x-usb.c +++ b/drivers/phy/ti/phy-dm816x-usb.c @@ -189,7 +189,6 @@ static int dm816x_usb_phy_probe(struct platform_device *pdev) struct phy_provider *phy_provider; struct usb_otg *otg; const struct of_device_id *of_id; - const struct usb_phy_data *phy_data; int error; of_id = of_match_device(of_match_ptr(dm816x_usb_phy_id_table), @@ -220,8 +219,6 @@ static int dm816x_usb_phy_probe(struct platform_device *pdev) if (phy->usbphy_ctrl == 0x2c) phy->instance = 1; - phy_data = of_id->data; - otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL); if (!otg) return -ENOMEM; From 64d5989c1ae52407e63c6387863a75036a03e2f8 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 1 Oct 2019 15:23:32 +0200 Subject: [PATCH 059/198] phy: tegra: use regulator_bulk_set_supply_names() Use the new regulator helper instead of a for loop. Signed-off-by: Bartosz Golaszewski Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/tegra/xusb.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c index bf4b00853438..f98ec3922c02 100644 --- a/drivers/phy/tegra/xusb.c +++ b/drivers/phy/tegra/xusb.c @@ -927,7 +927,6 @@ static int tegra_xusb_padctl_probe(struct platform_device *pdev) struct tegra_xusb_padctl *padctl; const struct of_device_id *match; struct resource *res; - unsigned int i; int err; /* for backwards compatibility with old device trees */ @@ -972,8 +971,9 @@ static int tegra_xusb_padctl_probe(struct platform_device *pdev) goto remove; } - for (i = 0; i < padctl->soc->num_supplies; i++) - padctl->supplies[i].supply = padctl->soc->supply_names[i]; + regulator_bulk_set_supply_names(padctl->supplies, + padctl->soc->supply_names, + padctl->soc->num_supplies); err = devm_regulator_bulk_get(&pdev->dev, padctl->soc->num_supplies, padctl->supplies); From 316b429459066215abb50060873ec0832efc4044 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Wed, 23 Oct 2019 17:47:44 +0300 Subject: [PATCH 060/198] phy: ti: gmii-sel: fix mac tx internal delay for rgmii-rxid Now phy-gmii-sel will disable MAC TX internal delay for PHY interface mode "rgmii-rxid" which is incorrect. Hence, fix it by enabling MAC TX internal delay in the case of "rgmii-rxid" mode. Fixes: 92b58b34741f ("phy: ti: introduce phy-gmii-sel driver") Signed-off-by: Grygorii Strashko Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/ti/phy-gmii-sel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/ti/phy-gmii-sel.c b/drivers/phy/ti/phy-gmii-sel.c index a52c5bb35033..a28bd15297f5 100644 --- a/drivers/phy/ti/phy-gmii-sel.c +++ b/drivers/phy/ti/phy-gmii-sel.c @@ -69,11 +69,11 @@ static int phy_gmii_sel_mode(struct phy *phy, enum phy_mode mode, int submode) break; case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_RXID: gmii_sel_mode = AM33XX_GMII_SEL_MODE_RGMII; break; case PHY_INTERFACE_MODE_RGMII_ID: - case PHY_INTERFACE_MODE_RGMII_RXID: case PHY_INTERFACE_MODE_RGMII_TXID: gmii_sel_mode = AM33XX_GMII_SEL_MODE_RGMII; rgmii_id = 1; From 76126f5bd3fbec84d30b2a327287835666751d47 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 24 Oct 2019 13:18:01 +0530 Subject: [PATCH 061/198] dt-bindings: phy-qcom-qmp: Add sm8150 UFS phy compatible string Document "qcom,sdm845-qmp-ufs-phy" compatible string for QMP UFS PHY found on SM8150. Signed-off-by: Vinod Koul Reviewed-by: Bjorn Andersson Reviewed-by: Stephen Boyd Reviewed-by: Rob Herring Signed-off-by: Kishon Vijay Abraham I --- Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt b/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt index 085fbd676cfc..eac9ad3cbbc8 100644 --- a/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt +++ b/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt @@ -14,7 +14,8 @@ Required properties: "qcom,msm8998-qmp-pcie-phy" for PCIe QMP phy on msm8998, "qcom,sdm845-qmp-usb3-phy" for USB3 QMP V3 phy on sdm845, "qcom,sdm845-qmp-usb3-uni-phy" for USB3 QMP V3 UNI phy on sdm845, - "qcom,sdm845-qmp-ufs-phy" for UFS QMP phy on sdm845. + "qcom,sdm845-qmp-ufs-phy" for UFS QMP phy on sdm845, + "qcom,sm8150-qmp-ufs-phy" for UFS QMP phy on sm8150. - reg: - index 0: address and length of register set for PHY's common @@ -57,6 +58,8 @@ Required properties: "aux", "cfg_ahb", "ref", "com_aux". For "qcom,sdm845-qmp-ufs-phy" must contain: "ref", "ref_aux". + For "qcom,sm8150-qmp-ufs-phy" must contain: + "ref", "ref_aux". - resets: a list of phandles and reset controller specifier pairs, one for each entry in reset-names. @@ -83,6 +86,8 @@ Required properties: "phy", "common". For "qcom,sdm845-qmp-ufs-phy": must contain: "ufsphy". + For "qcom,sm8150-qmp-ufs-phy": must contain: + "ufsphy". - vdda-phy-supply: Phandle to a regulator supply to PHY core block. - vdda-pll-supply: Phandle to 1.8V regulator supply to PHY refclk pll block. From a88c85ee2ea30d3c3a8210f793b2b1e1301b76e6 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 24 Oct 2019 13:18:02 +0530 Subject: [PATCH 062/198] phy: qcom-qmp: Add SM8150 QMP UFS PHY support SM8150 UFS PHY is v4 of QMP phy. Add support for V4 QMP phy register defines and support for SM8150 QMP UFS PHY. Signed-off-by: Vinod Koul Reviewed-by: Bjorn Andersson Reviewed-by: Stephen Boyd Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/qualcomm/phy-qcom-qmp.c | 120 ++++++++++++++++++++++++++++ drivers/phy/qualcomm/phy-qcom-qmp.h | 96 ++++++++++++++++++++++ 2 files changed, 216 insertions(+) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c index 39e8deb8001e..091e20303a14 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp.c @@ -165,6 +165,11 @@ static const unsigned int sdm845_ufsphy_regs_layout[] = { [QPHY_PCS_READY_STATUS] = 0x160, }; +static const unsigned int sm8150_ufsphy_regs_layout[] = { + [QPHY_START_CTRL] = 0x00, + [QPHY_PCS_READY_STATUS] = 0x180, +}; + static const struct qmp_phy_init_tbl msm8996_pcie_serdes_tbl[] = { QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x1c), QMP_PHY_INIT_CFG(QSERDES_COM_CLK_ENABLE1, 0x10), @@ -879,6 +884,93 @@ static const struct qmp_phy_init_tbl msm8998_usb3_pcs_tbl[] = { QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_RUN_TIME, 0x13), }; +static const struct qmp_phy_init_tbl sm8150_ufsphy_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_POWER_DOWN_CONTROL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_EN_SEL, 0xd9), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x11), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_HS_SWITCH_SEL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_EN, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_MAP, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_INITVAL2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_HSCLK_SEL, 0x11), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE0, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE0, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE0, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xac), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1e), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE1, 0x98), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE1, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE1, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE1, 0x32), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE1, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0xdd), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x23), + + /* Rate B */ + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_MAP, 0x06), +}; + +static const struct qmp_phy_init_tbl sm8150_ufsphy_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_TX_PWM_GEAR_1_DIVIDER_BAND0_1, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_PWM_GEAR_2_DIVIDER_BAND0_1, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_PWM_GEAR_3_DIVIDER_BAND0_1, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_PWM_GEAR_4_DIVIDER_BAND0_1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_TRAN_DRVR_EMP_EN, 0x0c), +}; + +static const struct qmp_phy_init_tbl sm8150_ufsphy_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_LVL, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x1e), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_BAND, 0x18), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_FO_GAIN, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CONTROLS, 0xf1), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_LOW, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CTRL2, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FO_GAIN, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_GAIN, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_TERM_BW, 0x1b), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x1d), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_MEASURE_TIME, 0x10), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0xc0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_LOW, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0xf6), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0x3b), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0x3d), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0xe0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0xc8), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0xc8), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x3b), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0xb1), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_LOW, 0xe0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH, 0xc8), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH2, 0xc8), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH3, 0x3b), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH4, 0xb1), + +}; + +static const struct qmp_phy_init_tbl sm8150_ufsphy_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_RX_SIGDET_CTRL2, 0x6d), + QMP_PHY_INIT_CFG(QPHY_V4_TX_LARGE_AMP_DRV_LVL, 0x0a), + QMP_PHY_INIT_CFG(QPHY_V4_TX_SMALL_AMP_DRV_LVL, 0x02), + QMP_PHY_INIT_CFG(QPHY_V4_TX_MID_TERM_CTRL1, 0x43), + QMP_PHY_INIT_CFG(QPHY_V4_DEBUG_BUS_CLKSEL, 0x1f), + QMP_PHY_INIT_CFG(QPHY_V4_RX_MIN_HIBERN8_TIME, 0xff), + QMP_PHY_INIT_CFG(QPHY_V4_MULTI_LANE_CTRL1, 0x02), +}; /* struct qmp_phy_cfg - per-PHY initialization config */ struct qmp_phy_cfg { @@ -1276,6 +1368,31 @@ static const struct qmp_phy_cfg msm8998_usb3phy_cfg = { .is_dual_lane_phy = true, }; +static const struct qmp_phy_cfg sm8150_ufsphy_cfg = { + .type = PHY_TYPE_UFS, + .nlanes = 2, + + .serdes_tbl = sm8150_ufsphy_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sm8150_ufsphy_serdes_tbl), + .tx_tbl = sm8150_ufsphy_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sm8150_ufsphy_tx_tbl), + .rx_tbl = sm8150_ufsphy_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sm8150_ufsphy_rx_tbl), + .pcs_tbl = sm8150_ufsphy_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sm8150_ufsphy_pcs_tbl), + .clk_list = sdm845_ufs_phy_clk_l, + .num_clks = ARRAY_SIZE(sdm845_ufs_phy_clk_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = sm8150_ufsphy_regs_layout, + + .start_ctrl = SERDES_START, + .pwrdn_ctrl = SW_PWRDN, + + .is_dual_lane_phy = true, + .no_pcs_sw_reset = true, +}; + static void qcom_qmp_phy_configure(void __iomem *base, const unsigned int *regs, const struct qmp_phy_init_tbl tbl[], @@ -1998,6 +2115,9 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = { }, { .compatible = "qcom,msm8998-qmp-usb3-phy", .data = &msm8998_usb3phy_cfg, + }, { + .compatible = "qcom,sm8150-qmp-ufs-phy", + .data = &sm8150_ufsphy_cfg, }, { }, }; diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.h b/drivers/phy/qualcomm/phy-qcom-qmp.h index 335ea5d7ef40..ab6ff9b45a32 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.h +++ b/drivers/phy/qualcomm/phy-qcom-qmp.h @@ -313,4 +313,100 @@ #define QPHY_V3_PCS_MISC_OSC_DTCT_MODE2_CONFIG4 0x5c #define QPHY_V3_PCS_MISC_OSC_DTCT_MODE2_CONFIG5 0x60 +/* Only for QMP V4 PHY - QSERDES COM registers */ +#define QSERDES_V4_COM_PLL_IVCO 0x058 +#define QSERDES_V4_COM_CMN_IPTRIM 0x060 +#define QSERDES_V4_COM_CP_CTRL_MODE0 0x074 +#define QSERDES_V4_COM_CP_CTRL_MODE1 0x078 +#define QSERDES_V4_COM_PLL_RCTRL_MODE0 0x07c +#define QSERDES_V4_COM_PLL_RCTRL_MODE1 0x080 +#define QSERDES_V4_COM_PLL_CCTRL_MODE0 0x084 +#define QSERDES_V4_COM_PLL_CCTRL_MODE1 0x088 +#define QSERDES_V4_COM_SYSCLK_EN_SEL 0x094 +#define QSERDES_V4_COM_LOCK_CMP_EN 0x0a4 +#define QSERDES_V4_COM_LOCK_CMP1_MODE0 0x0ac +#define QSERDES_V4_COM_LOCK_CMP2_MODE0 0x0b0 +#define QSERDES_V4_COM_LOCK_CMP1_MODE1 0x0b4 +#define QSERDES_V4_COM_DEC_START_MODE0 0x0bc +#define QSERDES_V4_COM_LOCK_CMP2_MODE1 0x0b8 +#define QSERDES_V4_COM_DEC_START_MODE1 0x0c4 +#define QSERDES_V4_COM_VCO_TUNE_MAP 0x10c +#define QSERDES_V4_COM_VCO_TUNE_INITVAL2 0x124 +#define QSERDES_V4_COM_HSCLK_SEL 0x158 +#define QSERDES_V4_COM_HSCLK_HS_SWITCH_SEL 0x15c +#define QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE0 0x1ac +#define QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE0 0x1b0 +#define QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE1 0x1b4 +#define QSERDES_V4_COM_BIN_VCOCAL_HSCLK_SEL 0x1bc +#define QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE1 0x1b8 + +/* Only for QMP V4 PHY - TX registers */ +#define QSERDES_V4_TX_LANE_MODE_1 0x84 +#define QSERDES_V4_TX_PWM_GEAR_1_DIVIDER_BAND0_1 0xd8 +#define QSERDES_V4_TX_PWM_GEAR_2_DIVIDER_BAND0_1 0xdC +#define QSERDES_V4_TX_PWM_GEAR_3_DIVIDER_BAND0_1 0xe0 +#define QSERDES_V4_TX_PWM_GEAR_4_DIVIDER_BAND0_1 0xe4 +#define QSERDES_V4_TX_TRAN_DRVR_EMP_EN 0xb8 + +/* Only for QMP V4 PHY - RX registers */ +#define QSERDES_V4_RX_UCDR_FO_GAIN 0x008 +#define QSERDES_V4_RX_UCDR_SO_GAIN 0x014 +#define QSERDES_V4_RX_UCDR_FASTLOCK_FO_GAIN 0x030 +#define QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE 0x034 +#define QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_LOW 0x03c +#define QSERDES_V4_RX_UCDR_PI_CONTROLS 0x044 +#define QSERDES_V4_RX_UCDR_PI_CTRL2 0x048 +#define QSERDES_V4_RX_AC_JTAG_ENABLE 0x068 +#define QSERDES_V4_RX_AC_JTAG_MODE 0x078 +#define QSERDES_V4_RX_RX_TERM_BW 0x080 +#define QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2 0x0ec +#define QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3 0x0f0 +#define QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4 0x0f4 +#define QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW 0x0f8 +#define QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH 0x0fc +#define QSERDES_V4_RX_RX_IDAC_MEASURE_TIME 0x100 +#define QSERDES_V4_RX_RX_OFFSET_ADAPTOR_CNTRL2 0x114 +#define QSERDES_V4_RX_SIGDET_CNTRL 0x11c +#define QSERDES_V4_RX_SIGDET_LVL 0x120 +#define QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL 0x124 +#define QSERDES_V4_RX_RX_BAND 0x128 +#define QSERDES_V4_RX_RX_MODE_00_LOW 0x170 +#define QSERDES_V4_RX_RX_MODE_00_HIGH 0x174 +#define QSERDES_V4_RX_RX_MODE_00_HIGH2 0x178 +#define QSERDES_V4_RX_RX_MODE_00_HIGH3 0x17c +#define QSERDES_V4_RX_RX_MODE_00_HIGH4 0x180 +#define QSERDES_V4_RX_RX_MODE_01_LOW 0x184 +#define QSERDES_V4_RX_RX_MODE_01_HIGH 0x188 +#define QSERDES_V4_RX_RX_MODE_01_HIGH2 0x18c +#define QSERDES_V4_RX_RX_MODE_01_HIGH3 0x190 +#define QSERDES_V4_RX_RX_MODE_01_HIGH4 0x194 +#define QSERDES_V4_RX_RX_MODE_10_LOW 0x198 +#define QSERDES_V4_RX_RX_MODE_10_HIGH 0x19c +#define QSERDES_V4_RX_RX_MODE_10_HIGH2 0x1a0 +#define QSERDES_V4_RX_RX_MODE_10_HIGH3 0x1a4 +#define QSERDES_V4_RX_RX_MODE_10_HIGH4 0x1a8 +#define QSERDES_V4_RX_DCC_CTRL1 0x1bc + +/* Only for QMP V4 PHY - PCS registers */ +#define QPHY_V4_PHY_START 0x000 +#define QPHY_V4_POWER_DOWN_CONTROL 0x004 +#define QPHY_V4_SW_RESET 0x008 +#define QPHY_V4_TIMER_20US_CORECLK_STEPS_MSB 0x00c +#define QPHY_V4_TIMER_20US_CORECLK_STEPS_LSB 0x010 +#define QPHY_V4_PLL_CNTL 0x02c +#define QPHY_V4_TX_LARGE_AMP_DRV_LVL 0x030 +#define QPHY_V4_TX_SMALL_AMP_DRV_LVL 0x038 +#define QPHY_V4_BIST_FIXED_PAT_CTRL 0x060 +#define QPHY_V4_TX_HSGEAR_CAPABILITY 0x074 +#define QPHY_V4_RX_HSGEAR_CAPABILITY 0x0b4 +#define QPHY_V4_DEBUG_BUS_CLKSEL 0x124 +#define QPHY_V4_LINECFG_DISABLE 0x148 +#define QPHY_V4_RX_MIN_HIBERN8_TIME 0x150 +#define QPHY_V4_RX_SIGDET_CTRL2 0x158 +#define QPHY_V4_TX_PWM_GEAR_BAND 0x160 +#define QPHY_V4_TX_HS_GEAR_BAND 0x168 +#define QPHY_V4_PCS_READY_STATUS 0x180 +#define QPHY_V4_TX_MID_TERM_CTRL1 0x1d8 +#define QPHY_V4_MULTI_LANE_CTRL1 0x1e0 + #endif From ddd1bbbae486ff5913c8fc72c853dcea60713236 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 25 Oct 2019 14:12:27 +0100 Subject: [PATCH 063/198] extcon: sm5502: remove redundant assignment to variable cable_type The variable cable_type is being initialized with a value that is never read and is being re-assigned a little later on. The assignment is redundant and hence can be removed. Addresses-Coverity: ("Unused value") Signed-off-by: Colin Ian King Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-sm5502.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/extcon/extcon-sm5502.c b/drivers/extcon/extcon-sm5502.c index b3d93baf4fc5..bcf65aaca5d2 100644 --- a/drivers/extcon/extcon-sm5502.c +++ b/drivers/extcon/extcon-sm5502.c @@ -276,7 +276,7 @@ static int sm5502_muic_set_path(struct sm5502_muic_info *info, /* Return cable type of attached or detached accessories */ static unsigned int sm5502_muic_get_cable_type(struct sm5502_muic_info *info) { - unsigned int cable_type = -1, adc, dev_type1; + unsigned int cable_type, adc, dev_type1; int ret; /* Read ADC value according to external cable or button */ From 6bd03e7113694d5d5cbffe40acdea0db772331e0 Mon Sep 17 00:00:00 2001 From: Ondrej Jirman Date: Tue, 29 Oct 2019 21:17:38 +0100 Subject: [PATCH 064/198] dt-bindings: Add bindings for USB3 phy on Allwinner H6 The new Allwinner H6 SoC contains a USB3 PHY that is wired to the external USB3 pins of the SoC. Add a device tree binding for the PHY. Signed-off-by: Ondrej Jirman Acked-by: Maxime Ripard Reviewed-by: Rob Herring Signed-off-by: Kishon Vijay Abraham I --- .../phy/allwinner,sun50i-h6-usb3-phy.yaml | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/allwinner,sun50i-h6-usb3-phy.yaml diff --git a/Documentation/devicetree/bindings/phy/allwinner,sun50i-h6-usb3-phy.yaml b/Documentation/devicetree/bindings/phy/allwinner,sun50i-h6-usb3-phy.yaml new file mode 100644 index 000000000000..e5922b427342 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/allwinner,sun50i-h6-usb3-phy.yaml @@ -0,0 +1,47 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright 2019 Ondrej Jirman +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/phy/allwinner,sun50i-h6-usb3-phy.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Allwinner H6 USB3 PHY + +maintainers: + - Ondrej Jirman + +properties: + compatible: + enum: + - allwinner,sun50i-h6-usb3-phy + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + resets: + maxItems: 1 + + "#phy-cells": + const: 0 + +required: + - compatible + - reg + - clocks + - resets + - "#phy-cells" + +examples: + - | + #include + #include + phy@5210000 { + compatible = "allwinner,sun50i-h6-usb3-phy"; + reg = <0x5210000 0x10000>; + clocks = <&ccu CLK_USB_PHY1>; + resets = <&ccu RST_USB_PHY1>; + #phy-cells = <0>; + }; From a228890f94586c2f8417831c228ac8ed955ef856 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Tue, 29 Oct 2019 21:17:39 +0100 Subject: [PATCH 065/198] phy: allwinner: add phy driver for USB3 PHY on Allwinner H6 SoC Allwinner H6 SoC contains a USB3 PHY (with USB2 DP/DM lines also controlled). Add a driver for it. The register operations in this driver is mainly extracted from the BSP USB3 driver. Signed-off-by: Ondrej Jirman Signed-off-by: Icenowy Zheng Reviewed-by: Chen-Yu Tsai Acked-by: Maxime Ripard Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/allwinner/Kconfig | 11 ++ drivers/phy/allwinner/Makefile | 1 + drivers/phy/allwinner/phy-sun50i-usb3.c | 190 ++++++++++++++++++++++++ 3 files changed, 202 insertions(+) create mode 100644 drivers/phy/allwinner/phy-sun50i-usb3.c diff --git a/drivers/phy/allwinner/Kconfig b/drivers/phy/allwinner/Kconfig index 215425296c77..3dab79e9d52b 100644 --- a/drivers/phy/allwinner/Kconfig +++ b/drivers/phy/allwinner/Kconfig @@ -45,3 +45,14 @@ config PHY_SUN9I_USB sun9i SoCs. This driver controls each individual USB 2 host PHY. + +config PHY_SUN50I_USB3 + tristate "Allwinner H6 SoC USB3 PHY driver" + depends on ARCH_SUNXI && HAS_IOMEM && OF + depends on RESET_CONTROLLER + select GENERIC_PHY + help + Enable this to support the USB3.0-capable transceiver that is + part of Allwinner H6 SoC. + + This driver controls each individual USB 2+3 host PHY combo. diff --git a/drivers/phy/allwinner/Makefile b/drivers/phy/allwinner/Makefile index 799a65c0b58d..bd74901a1255 100644 --- a/drivers/phy/allwinner/Makefile +++ b/drivers/phy/allwinner/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_PHY_SUN4I_USB) += phy-sun4i-usb.o obj-$(CONFIG_PHY_SUN6I_MIPI_DPHY) += phy-sun6i-mipi-dphy.o obj-$(CONFIG_PHY_SUN9I_USB) += phy-sun9i-usb.o +obj-$(CONFIG_PHY_SUN50I_USB3) += phy-sun50i-usb3.o diff --git a/drivers/phy/allwinner/phy-sun50i-usb3.c b/drivers/phy/allwinner/phy-sun50i-usb3.c new file mode 100644 index 000000000000..1169f3e83a6f --- /dev/null +++ b/drivers/phy/allwinner/phy-sun50i-usb3.c @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Allwinner sun50i(H6) USB 3.0 phy driver + * + * Copyright (C) 2017 Icenowy Zheng + * + * Based on phy-sun9i-usb.c, which is: + * + * Copyright (C) 2014-2015 Chen-Yu Tsai + * + * Based on code from Allwinner BSP, which is: + * + * Copyright (c) 2010-2015 Allwinner Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* Interface Status and Control Registers */ +#define SUNXI_ISCR 0x00 +#define SUNXI_PIPE_CLOCK_CONTROL 0x14 +#define SUNXI_PHY_TUNE_LOW 0x18 +#define SUNXI_PHY_TUNE_HIGH 0x1c +#define SUNXI_PHY_EXTERNAL_CONTROL 0x20 + +/* USB2.0 Interface Status and Control Register */ +#define SUNXI_ISCR_FORCE_VBUS (3 << 12) + +/* PIPE Clock Control Register */ +#define SUNXI_PCC_PIPE_CLK_OPEN (1 << 6) + +/* PHY External Control Register */ +#define SUNXI_PEC_EXTERN_VBUS (3 << 1) +#define SUNXI_PEC_SSC_EN (1 << 24) +#define SUNXI_PEC_REF_SSP_EN (1 << 26) + +/* PHY Tune High Register */ +#define SUNXI_TX_DEEMPH_3P5DB(n) ((n) << 19) +#define SUNXI_TX_DEEMPH_3P5DB_MASK GENMASK(24, 19) +#define SUNXI_TX_DEEMPH_6DB(n) ((n) << 13) +#define SUNXI_TX_DEEMPH_6GB_MASK GENMASK(18, 13) +#define SUNXI_TX_SWING_FULL(n) ((n) << 6) +#define SUNXI_TX_SWING_FULL_MASK GENMASK(12, 6) +#define SUNXI_LOS_BIAS(n) ((n) << 3) +#define SUNXI_LOS_BIAS_MASK GENMASK(5, 3) +#define SUNXI_TXVBOOSTLVL(n) ((n) << 0) +#define SUNXI_TXVBOOSTLVL_MASK GENMASK(0, 2) + +struct sun50i_usb3_phy { + struct phy *phy; + void __iomem *regs; + struct reset_control *reset; + struct clk *clk; +}; + +static void sun50i_usb3_phy_open(struct sun50i_usb3_phy *phy) +{ + u32 val; + + val = readl(phy->regs + SUNXI_PHY_EXTERNAL_CONTROL); + val |= SUNXI_PEC_EXTERN_VBUS; + val |= SUNXI_PEC_SSC_EN | SUNXI_PEC_REF_SSP_EN; + writel(val, phy->regs + SUNXI_PHY_EXTERNAL_CONTROL); + + val = readl(phy->regs + SUNXI_PIPE_CLOCK_CONTROL); + val |= SUNXI_PCC_PIPE_CLK_OPEN; + writel(val, phy->regs + SUNXI_PIPE_CLOCK_CONTROL); + + val = readl(phy->regs + SUNXI_ISCR); + val |= SUNXI_ISCR_FORCE_VBUS; + writel(val, phy->regs + SUNXI_ISCR); + + /* + * All the magic numbers written to the PHY_TUNE_{LOW_HIGH} + * registers are directly taken from the BSP USB3 driver from + * Allwiner. + */ + writel(0x0047fc87, phy->regs + SUNXI_PHY_TUNE_LOW); + + val = readl(phy->regs + SUNXI_PHY_TUNE_HIGH); + val &= ~(SUNXI_TXVBOOSTLVL_MASK | SUNXI_LOS_BIAS_MASK | + SUNXI_TX_SWING_FULL_MASK | SUNXI_TX_DEEMPH_6GB_MASK | + SUNXI_TX_DEEMPH_3P5DB_MASK); + val |= SUNXI_TXVBOOSTLVL(0x7); + val |= SUNXI_LOS_BIAS(0x7); + val |= SUNXI_TX_SWING_FULL(0x55); + val |= SUNXI_TX_DEEMPH_6DB(0x20); + val |= SUNXI_TX_DEEMPH_3P5DB(0x15); + writel(val, phy->regs + SUNXI_PHY_TUNE_HIGH); +} + +static int sun50i_usb3_phy_init(struct phy *_phy) +{ + struct sun50i_usb3_phy *phy = phy_get_drvdata(_phy); + int ret; + + ret = clk_prepare_enable(phy->clk); + if (ret) + return ret; + + ret = reset_control_deassert(phy->reset); + if (ret) { + clk_disable_unprepare(phy->clk); + return ret; + } + + sun50i_usb3_phy_open(phy); + return 0; +} + +static int sun50i_usb3_phy_exit(struct phy *_phy) +{ + struct sun50i_usb3_phy *phy = phy_get_drvdata(_phy); + + reset_control_assert(phy->reset); + clk_disable_unprepare(phy->clk); + + return 0; +} + +static const struct phy_ops sun50i_usb3_phy_ops = { + .init = sun50i_usb3_phy_init, + .exit = sun50i_usb3_phy_exit, + .owner = THIS_MODULE, +}; + +static int sun50i_usb3_phy_probe(struct platform_device *pdev) +{ + struct sun50i_usb3_phy *phy; + struct device *dev = &pdev->dev; + struct phy_provider *phy_provider; + struct resource *res; + + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) + return -ENOMEM; + + phy->clk = devm_clk_get(dev, NULL); + if (IS_ERR(phy->clk)) { + if (PTR_ERR(phy->clk) != -EPROBE_DEFER) + dev_err(dev, "failed to get phy clock\n"); + return PTR_ERR(phy->clk); + } + + phy->reset = devm_reset_control_get(dev, NULL); + if (IS_ERR(phy->reset)) { + dev_err(dev, "failed to get reset control\n"); + return PTR_ERR(phy->reset); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + phy->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(phy->regs)) + return PTR_ERR(phy->regs); + + phy->phy = devm_phy_create(dev, NULL, &sun50i_usb3_phy_ops); + if (IS_ERR(phy->phy)) { + dev_err(dev, "failed to create PHY\n"); + return PTR_ERR(phy->phy); + } + + phy_set_drvdata(phy->phy, phy); + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id sun50i_usb3_phy_of_match[] = { + { .compatible = "allwinner,sun50i-h6-usb3-phy" }, + { }, +}; +MODULE_DEVICE_TABLE(of, sun50i_usb3_phy_of_match); + +static struct platform_driver sun50i_usb3_phy_driver = { + .probe = sun50i_usb3_phy_probe, + .driver = { + .of_match_table = sun50i_usb3_phy_of_match, + .name = "sun50i-usb3-phy", + } +}; +module_platform_driver(sun50i_usb3_phy_driver); + +MODULE_DESCRIPTION("Allwinner H6 USB 3.0 phy driver"); +MODULE_AUTHOR("Icenowy Zheng "); +MODULE_LICENSE("GPL"); From 711b2bfba748b5adf8ad837b490b393197279203 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Thu, 24 Oct 2019 00:38:49 +0200 Subject: [PATCH 066/198] phy: add PHY_MODE_LVDS There are combo phys out there that can be switched between doing dsi and lvds. So add a mode definition for it. Signed-off-by: Heiko Stuebner Signed-off-by: Kishon Vijay Abraham I --- include/linux/phy/phy.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h index 15032f145063..56d3a100006a 100644 --- a/include/linux/phy/phy.h +++ b/include/linux/phy/phy.h @@ -38,7 +38,8 @@ enum phy_mode { PHY_MODE_PCIE, PHY_MODE_ETHERNET, PHY_MODE_MIPI_DPHY, - PHY_MODE_SATA + PHY_MODE_SATA, + PHY_MODE_LVDS, }; /** From b7535a3bc0bac70e9644fb35cdeffabb59f578ee Mon Sep 17 00:00:00 2001 From: Wyon Bi Date: Thu, 24 Oct 2019 00:38:51 +0200 Subject: [PATCH 067/198] phy/rockchip: Add support for Innosilicon MIPI/LVDS/TTL PHY The Innosilicon Video Combo PHY not only supports MIPI DSI, but also LVDS and TTL functions with small die size and low pin count. Customers can choose according to their own applications. Signed-off-by: Wyon Bi [removed TTL mode for now, as it required a hook back into the dsi host] Signed-off-by: Heiko Stuebner Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/rockchip/Kconfig | 8 + drivers/phy/rockchip/Makefile | 1 + .../phy/rockchip/phy-rockchip-inno-dsidphy.c | 805 ++++++++++++++++++ 3 files changed, 814 insertions(+) create mode 100644 drivers/phy/rockchip/phy-rockchip-inno-dsidphy.c diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig index c454c90cd99e..dbd2de4d28b1 100644 --- a/drivers/phy/rockchip/Kconfig +++ b/drivers/phy/rockchip/Kconfig @@ -35,6 +35,14 @@ config PHY_ROCKCHIP_INNO_USB2 help Support for Rockchip USB2.0 PHY with Innosilicon IP block. +config PHY_ROCKCHIP_INNO_DSIDPHY + tristate "Rockchip Innosilicon MIPI/LVDS/TTL PHY driver" + depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF + select GENERIC_PHY + help + Enable this to support the Rockchip MIPI/LVDS/TTL PHY with + Innosilicon IP block. + config PHY_ROCKCHIP_PCIE tristate "Rockchip PCIe PHY Driver" depends on (ARCH_ROCKCHIP && OF) || COMPILE_TEST diff --git a/drivers/phy/rockchip/Makefile b/drivers/phy/rockchip/Makefile index fd21cbaf40dd..9f59a81e4e0d 100644 --- a/drivers/phy/rockchip/Makefile +++ b/drivers/phy/rockchip/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_PHY_ROCKCHIP_DP) += phy-rockchip-dp.o obj-$(CONFIG_PHY_ROCKCHIP_EMMC) += phy-rockchip-emmc.o +obj-$(CONFIG_PHY_ROCKCHIP_INNO_DSIDPHY) += phy-rockchip-inno-dsidphy.o obj-$(CONFIG_PHY_ROCKCHIP_INNO_HDMI) += phy-rockchip-inno-hdmi.o obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2) += phy-rockchip-inno-usb2.o obj-$(CONFIG_PHY_ROCKCHIP_PCIE) += phy-rockchip-pcie.o diff --git a/drivers/phy/rockchip/phy-rockchip-inno-dsidphy.c b/drivers/phy/rockchip/phy-rockchip-inno-dsidphy.c new file mode 100644 index 000000000000..fc729ecd3fe9 --- /dev/null +++ b/drivers/phy/rockchip/phy-rockchip-inno-dsidphy.c @@ -0,0 +1,805 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018 Rockchip Electronics Co. Ltd. + * + * Author: Wyon Bi + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PSEC_PER_SEC 1000000000000LL + +#define UPDATE(x, h, l) (((x) << (l)) & GENMASK((h), (l))) + +/* + * The offset address[7:0] is distributed two parts, one from the bit7 to bit5 + * is the first address, the other from the bit4 to bit0 is the second address. + * when you configure the registers, you must set both of them. The Clock Lane + * and Data Lane use the same registers with the same second address, but the + * first address is different. + */ +#define FIRST_ADDRESS(x) (((x) & 0x7) << 5) +#define SECOND_ADDRESS(x) (((x) & 0x1f) << 0) +#define PHY_REG(first, second) (FIRST_ADDRESS(first) | \ + SECOND_ADDRESS(second)) + +/* Analog Register Part: reg00 */ +#define BANDGAP_POWER_MASK BIT(7) +#define BANDGAP_POWER_DOWN BIT(7) +#define BANDGAP_POWER_ON 0 +#define LANE_EN_MASK GENMASK(6, 2) +#define LANE_EN_CK BIT(6) +#define LANE_EN_3 BIT(5) +#define LANE_EN_2 BIT(4) +#define LANE_EN_1 BIT(3) +#define LANE_EN_0 BIT(2) +#define POWER_WORK_MASK GENMASK(1, 0) +#define POWER_WORK_ENABLE UPDATE(1, 1, 0) +#define POWER_WORK_DISABLE UPDATE(2, 1, 0) +/* Analog Register Part: reg01 */ +#define REG_SYNCRST_MASK BIT(2) +#define REG_SYNCRST_RESET BIT(2) +#define REG_SYNCRST_NORMAL 0 +#define REG_LDOPD_MASK BIT(1) +#define REG_LDOPD_POWER_DOWN BIT(1) +#define REG_LDOPD_POWER_ON 0 +#define REG_PLLPD_MASK BIT(0) +#define REG_PLLPD_POWER_DOWN BIT(0) +#define REG_PLLPD_POWER_ON 0 +/* Analog Register Part: reg03 */ +#define REG_FBDIV_HI_MASK BIT(5) +#define REG_FBDIV_HI(x) UPDATE((x >> 8), 5, 5) +#define REG_PREDIV_MASK GENMASK(4, 0) +#define REG_PREDIV(x) UPDATE(x, 4, 0) +/* Analog Register Part: reg04 */ +#define REG_FBDIV_LO_MASK GENMASK(7, 0) +#define REG_FBDIV_LO(x) UPDATE(x, 7, 0) +/* Analog Register Part: reg05 */ +#define SAMPLE_CLOCK_PHASE_MASK GENMASK(6, 4) +#define SAMPLE_CLOCK_PHASE(x) UPDATE(x, 6, 4) +#define CLOCK_LANE_SKEW_PHASE_MASK GENMASK(2, 0) +#define CLOCK_LANE_SKEW_PHASE(x) UPDATE(x, 2, 0) +/* Analog Register Part: reg06 */ +#define DATA_LANE_3_SKEW_PHASE_MASK GENMASK(6, 4) +#define DATA_LANE_3_SKEW_PHASE(x) UPDATE(x, 6, 4) +#define DATA_LANE_2_SKEW_PHASE_MASK GENMASK(2, 0) +#define DATA_LANE_2_SKEW_PHASE(x) UPDATE(x, 2, 0) +/* Analog Register Part: reg07 */ +#define DATA_LANE_1_SKEW_PHASE_MASK GENMASK(6, 4) +#define DATA_LANE_1_SKEW_PHASE(x) UPDATE(x, 6, 4) +#define DATA_LANE_0_SKEW_PHASE_MASK GENMASK(2, 0) +#define DATA_LANE_0_SKEW_PHASE(x) UPDATE(x, 2, 0) +/* Analog Register Part: reg08 */ +#define SAMPLE_CLOCK_DIRECTION_MASK BIT(4) +#define SAMPLE_CLOCK_DIRECTION_REVERSE BIT(4) +#define SAMPLE_CLOCK_DIRECTION_FORWARD 0 +/* Digital Register Part: reg00 */ +#define REG_DIG_RSTN_MASK BIT(0) +#define REG_DIG_RSTN_NORMAL BIT(0) +#define REG_DIG_RSTN_RESET 0 +/* Digital Register Part: reg01 */ +#define INVERT_TXCLKESC_MASK BIT(1) +#define INVERT_TXCLKESC_ENABLE BIT(1) +#define INVERT_TXCLKESC_DISABLE 0 +#define INVERT_TXBYTECLKHS_MASK BIT(0) +#define INVERT_TXBYTECLKHS_ENABLE BIT(0) +#define INVERT_TXBYTECLKHS_DISABLE 0 +/* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg05 */ +#define T_LPX_CNT_MASK GENMASK(5, 0) +#define T_LPX_CNT(x) UPDATE(x, 5, 0) +/* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg06 */ +#define T_HS_PREPARE_CNT_MASK GENMASK(6, 0) +#define T_HS_PREPARE_CNT(x) UPDATE(x, 6, 0) +/* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg07 */ +#define T_HS_ZERO_CNT_MASK GENMASK(5, 0) +#define T_HS_ZERO_CNT(x) UPDATE(x, 5, 0) +/* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg08 */ +#define T_HS_TRAIL_CNT_MASK GENMASK(6, 0) +#define T_HS_TRAIL_CNT(x) UPDATE(x, 6, 0) +/* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg09 */ +#define T_HS_EXIT_CNT_MASK GENMASK(4, 0) +#define T_HS_EXIT_CNT(x) UPDATE(x, 4, 0) +/* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg0a */ +#define T_CLK_POST_CNT_MASK GENMASK(3, 0) +#define T_CLK_POST_CNT(x) UPDATE(x, 3, 0) +/* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg0c */ +#define LPDT_TX_PPI_SYNC_MASK BIT(2) +#define LPDT_TX_PPI_SYNC_ENABLE BIT(2) +#define LPDT_TX_PPI_SYNC_DISABLE 0 +#define T_WAKEUP_CNT_HI_MASK GENMASK(1, 0) +#define T_WAKEUP_CNT_HI(x) UPDATE(x, 1, 0) +/* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg0d */ +#define T_WAKEUP_CNT_LO_MASK GENMASK(7, 0) +#define T_WAKEUP_CNT_LO(x) UPDATE(x, 7, 0) +/* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg0e */ +#define T_CLK_PRE_CNT_MASK GENMASK(3, 0) +#define T_CLK_PRE_CNT(x) UPDATE(x, 3, 0) +/* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg10 */ +#define T_TA_GO_CNT_MASK GENMASK(5, 0) +#define T_TA_GO_CNT(x) UPDATE(x, 5, 0) +/* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg11 */ +#define T_TA_SURE_CNT_MASK GENMASK(5, 0) +#define T_TA_SURE_CNT(x) UPDATE(x, 5, 0) +/* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg12 */ +#define T_TA_WAIT_CNT_MASK GENMASK(5, 0) +#define T_TA_WAIT_CNT(x) UPDATE(x, 5, 0) +/* LVDS Register Part: reg00 */ +#define LVDS_DIGITAL_INTERNAL_RESET_MASK BIT(2) +#define LVDS_DIGITAL_INTERNAL_RESET_DISABLE BIT(2) +#define LVDS_DIGITAL_INTERNAL_RESET_ENABLE 0 +/* LVDS Register Part: reg01 */ +#define LVDS_DIGITAL_INTERNAL_ENABLE_MASK BIT(7) +#define LVDS_DIGITAL_INTERNAL_ENABLE BIT(7) +#define LVDS_DIGITAL_INTERNAL_DISABLE 0 +/* LVDS Register Part: reg03 */ +#define MODE_ENABLE_MASK GENMASK(2, 0) +#define TTL_MODE_ENABLE BIT(2) +#define LVDS_MODE_ENABLE BIT(1) +#define MIPI_MODE_ENABLE BIT(0) +/* LVDS Register Part: reg0b */ +#define LVDS_LANE_EN_MASK GENMASK(7, 3) +#define LVDS_DATA_LANE0_EN BIT(7) +#define LVDS_DATA_LANE1_EN BIT(6) +#define LVDS_DATA_LANE2_EN BIT(5) +#define LVDS_DATA_LANE3_EN BIT(4) +#define LVDS_CLK_LANE_EN BIT(3) +#define LVDS_PLL_POWER_MASK BIT(2) +#define LVDS_PLL_POWER_OFF BIT(2) +#define LVDS_PLL_POWER_ON 0 +#define LVDS_BANDGAP_POWER_MASK BIT(0) +#define LVDS_BANDGAP_POWER_DOWN BIT(0) +#define LVDS_BANDGAP_POWER_ON 0 + +#define DSI_PHY_RSTZ 0xa0 +#define PHY_ENABLECLK BIT(2) +#define DSI_PHY_STATUS 0xb0 +#define PHY_LOCK BIT(0) + +struct mipi_dphy_timing { + unsigned int clkmiss; + unsigned int clkpost; + unsigned int clkpre; + unsigned int clkprepare; + unsigned int clksettle; + unsigned int clktermen; + unsigned int clktrail; + unsigned int clkzero; + unsigned int dtermen; + unsigned int eot; + unsigned int hsexit; + unsigned int hsprepare; + unsigned int hszero; + unsigned int hssettle; + unsigned int hsskip; + unsigned int hstrail; + unsigned int init; + unsigned int lpx; + unsigned int taget; + unsigned int tago; + unsigned int tasure; + unsigned int wakeup; +}; + +struct inno_dsidphy { + struct device *dev; + struct clk *ref_clk; + struct clk *pclk_phy; + struct clk *pclk_host; + void __iomem *phy_base; + void __iomem *host_base; + struct reset_control *rst; + enum phy_mode mode; + + struct { + struct clk_hw hw; + u8 prediv; + u16 fbdiv; + unsigned long rate; + } pll; +}; + +enum { + REGISTER_PART_ANALOG, + REGISTER_PART_DIGITAL, + REGISTER_PART_CLOCK_LANE, + REGISTER_PART_DATA0_LANE, + REGISTER_PART_DATA1_LANE, + REGISTER_PART_DATA2_LANE, + REGISTER_PART_DATA3_LANE, + REGISTER_PART_LVDS, +}; + +static inline struct inno_dsidphy *hw_to_inno(struct clk_hw *hw) +{ + return container_of(hw, struct inno_dsidphy, pll.hw); +} + +static void phy_update_bits(struct inno_dsidphy *inno, + u8 first, u8 second, u8 mask, u8 val) +{ + u32 reg = PHY_REG(first, second) << 2; + unsigned int tmp, orig; + + orig = readl(inno->phy_base + reg); + tmp = orig & ~mask; + tmp |= val & mask; + writel(tmp, inno->phy_base + reg); +} + +static void mipi_dphy_timing_get_default(struct mipi_dphy_timing *timing, + unsigned long period) +{ + /* Global Operation Timing Parameters */ + timing->clkmiss = 0; + timing->clkpost = 70000 + 52 * period; + timing->clkpre = 8 * period; + timing->clkprepare = 65000; + timing->clksettle = 95000; + timing->clktermen = 0; + timing->clktrail = 80000; + timing->clkzero = 260000; + timing->dtermen = 0; + timing->eot = 0; + timing->hsexit = 120000; + timing->hsprepare = 65000 + 4 * period; + timing->hszero = 145000 + 6 * period; + timing->hssettle = 85000 + 6 * period; + timing->hsskip = 40000; + timing->hstrail = max(8 * period, 60000 + 4 * period); + timing->init = 100000000; + timing->lpx = 60000; + timing->taget = 5 * timing->lpx; + timing->tago = 4 * timing->lpx; + timing->tasure = 2 * timing->lpx; + timing->wakeup = 1000000000; +} + +static void inno_dsidphy_mipi_mode_enable(struct inno_dsidphy *inno) +{ + struct mipi_dphy_timing gotp; + const struct { + unsigned long rate; + u8 hs_prepare; + u8 clk_lane_hs_zero; + u8 data_lane_hs_zero; + u8 hs_trail; + } timings[] = { + { 110000000, 0x20, 0x16, 0x02, 0x22}, + { 150000000, 0x06, 0x16, 0x03, 0x45}, + { 200000000, 0x18, 0x17, 0x04, 0x0b}, + { 250000000, 0x05, 0x17, 0x05, 0x16}, + { 300000000, 0x51, 0x18, 0x06, 0x2c}, + { 400000000, 0x64, 0x19, 0x07, 0x33}, + { 500000000, 0x20, 0x1b, 0x07, 0x4e}, + { 600000000, 0x6a, 0x1d, 0x08, 0x3a}, + { 700000000, 0x3e, 0x1e, 0x08, 0x6a}, + { 800000000, 0x21, 0x1f, 0x09, 0x29}, + {1000000000, 0x09, 0x20, 0x09, 0x27}, + }; + u32 t_txbyteclkhs, t_txclkesc, ui; + u32 txbyteclkhs, txclkesc, esc_clk_div; + u32 hs_exit, clk_post, clk_pre, wakeup, lpx, ta_go, ta_sure, ta_wait; + u32 hs_prepare, hs_trail, hs_zero, clk_lane_hs_zero, data_lane_hs_zero; + unsigned int i; + + /* Select MIPI mode */ + phy_update_bits(inno, REGISTER_PART_LVDS, 0x03, + MODE_ENABLE_MASK, MIPI_MODE_ENABLE); + /* Configure PLL */ + phy_update_bits(inno, REGISTER_PART_ANALOG, 0x03, + REG_PREDIV_MASK, REG_PREDIV(inno->pll.prediv)); + phy_update_bits(inno, REGISTER_PART_ANALOG, 0x03, + REG_FBDIV_HI_MASK, REG_FBDIV_HI(inno->pll.fbdiv)); + phy_update_bits(inno, REGISTER_PART_ANALOG, 0x04, + REG_FBDIV_LO_MASK, REG_FBDIV_LO(inno->pll.fbdiv)); + /* Enable PLL and LDO */ + phy_update_bits(inno, REGISTER_PART_ANALOG, 0x01, + REG_LDOPD_MASK | REG_PLLPD_MASK, + REG_LDOPD_POWER_ON | REG_PLLPD_POWER_ON); + /* Reset analog */ + phy_update_bits(inno, REGISTER_PART_ANALOG, 0x01, + REG_SYNCRST_MASK, REG_SYNCRST_RESET); + udelay(1); + phy_update_bits(inno, REGISTER_PART_ANALOG, 0x01, + REG_SYNCRST_MASK, REG_SYNCRST_NORMAL); + /* Reset digital */ + phy_update_bits(inno, REGISTER_PART_DIGITAL, 0x00, + REG_DIG_RSTN_MASK, REG_DIG_RSTN_RESET); + udelay(1); + phy_update_bits(inno, REGISTER_PART_DIGITAL, 0x00, + REG_DIG_RSTN_MASK, REG_DIG_RSTN_NORMAL); + + txbyteclkhs = inno->pll.rate / 8; + t_txbyteclkhs = div_u64(PSEC_PER_SEC, txbyteclkhs); + + esc_clk_div = DIV_ROUND_UP(txbyteclkhs, 20000000); + txclkesc = txbyteclkhs / esc_clk_div; + t_txclkesc = div_u64(PSEC_PER_SEC, txclkesc); + + ui = div_u64(PSEC_PER_SEC, inno->pll.rate); + + memset(&gotp, 0, sizeof(gotp)); + mipi_dphy_timing_get_default(&gotp, ui); + + /* + * The value of counter for HS Ths-exit + * Ths-exit = Tpin_txbyteclkhs * value + */ + hs_exit = DIV_ROUND_UP(gotp.hsexit, t_txbyteclkhs); + /* + * The value of counter for HS Tclk-post + * Tclk-post = Tpin_txbyteclkhs * value + */ + clk_post = DIV_ROUND_UP(gotp.clkpost, t_txbyteclkhs); + /* + * The value of counter for HS Tclk-pre + * Tclk-pre = Tpin_txbyteclkhs * value + */ + clk_pre = DIV_ROUND_UP(gotp.clkpre, t_txbyteclkhs); + + /* + * The value of counter for HS Tlpx Time + * Tlpx = Tpin_txbyteclkhs * (2 + value) + */ + lpx = DIV_ROUND_UP(gotp.lpx, t_txbyteclkhs); + if (lpx >= 2) + lpx -= 2; + + /* + * The value of counter for HS Tta-go + * Tta-go for turnaround + * Tta-go = Ttxclkesc * value + */ + ta_go = DIV_ROUND_UP(gotp.tago, t_txclkesc); + /* + * The value of counter for HS Tta-sure + * Tta-sure for turnaround + * Tta-sure = Ttxclkesc * value + */ + ta_sure = DIV_ROUND_UP(gotp.tasure, t_txclkesc); + /* + * The value of counter for HS Tta-wait + * Tta-wait for turnaround + * Tta-wait = Ttxclkesc * value + */ + ta_wait = DIV_ROUND_UP(gotp.taget, t_txclkesc); + + for (i = 0; i < ARRAY_SIZE(timings); i++) + if (inno->pll.rate <= timings[i].rate) + break; + + if (i == ARRAY_SIZE(timings)) + --i; + + hs_prepare = timings[i].hs_prepare; + hs_trail = timings[i].hs_trail; + clk_lane_hs_zero = timings[i].clk_lane_hs_zero; + data_lane_hs_zero = timings[i].data_lane_hs_zero; + wakeup = 0x3ff; + + for (i = REGISTER_PART_CLOCK_LANE; i <= REGISTER_PART_DATA3_LANE; i++) { + if (i == REGISTER_PART_CLOCK_LANE) + hs_zero = clk_lane_hs_zero; + else + hs_zero = data_lane_hs_zero; + + phy_update_bits(inno, i, 0x05, T_LPX_CNT_MASK, + T_LPX_CNT(lpx)); + phy_update_bits(inno, i, 0x06, T_HS_PREPARE_CNT_MASK, + T_HS_PREPARE_CNT(hs_prepare)); + phy_update_bits(inno, i, 0x07, T_HS_ZERO_CNT_MASK, + T_HS_ZERO_CNT(hs_zero)); + phy_update_bits(inno, i, 0x08, T_HS_TRAIL_CNT_MASK, + T_HS_TRAIL_CNT(hs_trail)); + phy_update_bits(inno, i, 0x09, T_HS_EXIT_CNT_MASK, + T_HS_EXIT_CNT(hs_exit)); + phy_update_bits(inno, i, 0x0a, T_CLK_POST_CNT_MASK, + T_CLK_POST_CNT(clk_post)); + phy_update_bits(inno, i, 0x0e, T_CLK_PRE_CNT_MASK, + T_CLK_PRE_CNT(clk_pre)); + phy_update_bits(inno, i, 0x0c, T_WAKEUP_CNT_HI_MASK, + T_WAKEUP_CNT_HI(wakeup >> 8)); + phy_update_bits(inno, i, 0x0d, T_WAKEUP_CNT_LO_MASK, + T_WAKEUP_CNT_LO(wakeup)); + phy_update_bits(inno, i, 0x10, T_TA_GO_CNT_MASK, + T_TA_GO_CNT(ta_go)); + phy_update_bits(inno, i, 0x11, T_TA_SURE_CNT_MASK, + T_TA_SURE_CNT(ta_sure)); + phy_update_bits(inno, i, 0x12, T_TA_WAIT_CNT_MASK, + T_TA_WAIT_CNT(ta_wait)); + } + + /* Enable all lanes on analog part */ + phy_update_bits(inno, REGISTER_PART_ANALOG, 0x00, + LANE_EN_MASK, LANE_EN_CK | LANE_EN_3 | LANE_EN_2 | + LANE_EN_1 | LANE_EN_0); +} + +static void inno_dsidphy_lvds_mode_enable(struct inno_dsidphy *inno) +{ + u8 prediv = 2; + u16 fbdiv = 28; + + /* Sample clock reverse direction */ + phy_update_bits(inno, REGISTER_PART_ANALOG, 0x08, + SAMPLE_CLOCK_DIRECTION_MASK, + SAMPLE_CLOCK_DIRECTION_REVERSE); + + /* Select LVDS mode */ + phy_update_bits(inno, REGISTER_PART_LVDS, 0x03, + MODE_ENABLE_MASK, LVDS_MODE_ENABLE); + /* Configure PLL */ + phy_update_bits(inno, REGISTER_PART_ANALOG, 0x03, + REG_PREDIV_MASK, REG_PREDIV(prediv)); + phy_update_bits(inno, REGISTER_PART_ANALOG, 0x03, + REG_FBDIV_HI_MASK, REG_FBDIV_HI(fbdiv)); + phy_update_bits(inno, REGISTER_PART_ANALOG, 0x04, + REG_FBDIV_LO_MASK, REG_FBDIV_LO(fbdiv)); + phy_update_bits(inno, REGISTER_PART_LVDS, 0x08, 0xff, 0xfc); + /* Enable PLL and Bandgap */ + phy_update_bits(inno, REGISTER_PART_LVDS, 0x0b, + LVDS_PLL_POWER_MASK | LVDS_BANDGAP_POWER_MASK, + LVDS_PLL_POWER_ON | LVDS_BANDGAP_POWER_ON); + + msleep(20); + + /* Reset LVDS digital logic */ + phy_update_bits(inno, REGISTER_PART_LVDS, 0x00, + LVDS_DIGITAL_INTERNAL_RESET_MASK, + LVDS_DIGITAL_INTERNAL_RESET_ENABLE); + udelay(1); + phy_update_bits(inno, REGISTER_PART_LVDS, 0x00, + LVDS_DIGITAL_INTERNAL_RESET_MASK, + LVDS_DIGITAL_INTERNAL_RESET_DISABLE); + /* Enable LVDS digital logic */ + phy_update_bits(inno, REGISTER_PART_LVDS, 0x01, + LVDS_DIGITAL_INTERNAL_ENABLE_MASK, + LVDS_DIGITAL_INTERNAL_ENABLE); + /* Enable LVDS analog driver */ + phy_update_bits(inno, REGISTER_PART_LVDS, 0x0b, + LVDS_LANE_EN_MASK, LVDS_CLK_LANE_EN | + LVDS_DATA_LANE0_EN | LVDS_DATA_LANE1_EN | + LVDS_DATA_LANE2_EN | LVDS_DATA_LANE3_EN); +} + +static int inno_dsidphy_power_on(struct phy *phy) +{ + struct inno_dsidphy *inno = phy_get_drvdata(phy); + + clk_prepare_enable(inno->pclk_phy); + pm_runtime_get_sync(inno->dev); + + /* Bandgap power on */ + phy_update_bits(inno, REGISTER_PART_ANALOG, 0x00, + BANDGAP_POWER_MASK, BANDGAP_POWER_ON); + /* Enable power work */ + phy_update_bits(inno, REGISTER_PART_ANALOG, 0x00, + POWER_WORK_MASK, POWER_WORK_ENABLE); + + switch (inno->mode) { + case PHY_MODE_MIPI_DPHY: + inno_dsidphy_mipi_mode_enable(inno); + break; + case PHY_MODE_LVDS: + inno_dsidphy_lvds_mode_enable(inno); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int inno_dsidphy_power_off(struct phy *phy) +{ + struct inno_dsidphy *inno = phy_get_drvdata(phy); + + phy_update_bits(inno, REGISTER_PART_ANALOG, 0x00, LANE_EN_MASK, 0); + phy_update_bits(inno, REGISTER_PART_ANALOG, 0x01, + REG_LDOPD_MASK | REG_PLLPD_MASK, + REG_LDOPD_POWER_DOWN | REG_PLLPD_POWER_DOWN); + phy_update_bits(inno, REGISTER_PART_ANALOG, 0x00, + POWER_WORK_MASK, POWER_WORK_DISABLE); + phy_update_bits(inno, REGISTER_PART_ANALOG, 0x00, + BANDGAP_POWER_MASK, BANDGAP_POWER_DOWN); + + phy_update_bits(inno, REGISTER_PART_LVDS, 0x0b, LVDS_LANE_EN_MASK, 0); + phy_update_bits(inno, REGISTER_PART_LVDS, 0x01, + LVDS_DIGITAL_INTERNAL_ENABLE_MASK, + LVDS_DIGITAL_INTERNAL_DISABLE); + phy_update_bits(inno, REGISTER_PART_LVDS, 0x0b, + LVDS_PLL_POWER_MASK | LVDS_BANDGAP_POWER_MASK, + LVDS_PLL_POWER_OFF | LVDS_BANDGAP_POWER_DOWN); + + pm_runtime_put(inno->dev); + clk_disable_unprepare(inno->pclk_phy); + + return 0; +} + +static int inno_dsidphy_set_mode(struct phy *phy, enum phy_mode mode, + int submode) +{ + struct inno_dsidphy *inno = phy_get_drvdata(phy); + + switch (mode) { + case PHY_MODE_MIPI_DPHY: + case PHY_MODE_LVDS: + inno->mode = mode; + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct phy_ops inno_dsidphy_ops = { + .set_mode = inno_dsidphy_set_mode, + .power_on = inno_dsidphy_power_on, + .power_off = inno_dsidphy_power_off, + .owner = THIS_MODULE, +}; + +static unsigned long inno_dsidphy_pll_round_rate(struct inno_dsidphy *inno, + unsigned long prate, + unsigned long rate, + u8 *prediv, u16 *fbdiv) +{ + unsigned long best_freq = 0; + unsigned long fref, fout; + u8 min_prediv, max_prediv; + u8 _prediv, best_prediv = 1; + u16 _fbdiv, best_fbdiv = 1; + u32 min_delta = UINT_MAX; + + /* + * The PLL output frequency can be calculated using a simple formula: + * PLL_Output_Frequency = (FREF / PREDIV * FBDIV) / 2 + * PLL_Output_Frequency: it is equal to DDR-Clock-Frequency * 2 + */ + fref = prate / 2; + if (rate > 1000000000UL) + fout = 1000000000UL; + else + fout = rate; + + /* 5Mhz < Fref / prediv < 40MHz */ + min_prediv = DIV_ROUND_UP(fref, 40000000); + max_prediv = fref / 5000000; + + for (_prediv = min_prediv; _prediv <= max_prediv; _prediv++) { + u64 tmp; + u32 delta; + + tmp = (u64)fout * _prediv; + do_div(tmp, fref); + _fbdiv = tmp; + + /* + * The possible settings of feedback divider are + * 12, 13, 14, 16, ~ 511 + */ + if (_fbdiv == 15) + continue; + + if (_fbdiv < 12 || _fbdiv > 511) + continue; + + tmp = (u64)_fbdiv * fref; + do_div(tmp, _prediv); + + delta = abs(fout - tmp); + if (!delta) { + best_prediv = _prediv; + best_fbdiv = _fbdiv; + best_freq = tmp; + break; + } else if (delta < min_delta) { + best_prediv = _prediv; + best_fbdiv = _fbdiv; + best_freq = tmp; + min_delta = delta; + } + } + + if (best_freq) { + *prediv = best_prediv; + *fbdiv = best_fbdiv; + } + + return best_freq; +} + +static long inno_dsidphy_pll_clk_round_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long *prate) +{ + struct inno_dsidphy *inno = hw_to_inno(hw); + unsigned long fout; + u16 fbdiv = 1; + u8 prediv = 1; + + fout = inno_dsidphy_pll_round_rate(inno, *prate, rate, + &prediv, &fbdiv); + + return fout; +} + +static int inno_dsidphy_pll_clk_set_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate) +{ + struct inno_dsidphy *inno = hw_to_inno(hw); + unsigned long fout; + u16 fbdiv = 1; + u8 prediv = 1; + + fout = inno_dsidphy_pll_round_rate(inno, parent_rate, rate, + &prediv, &fbdiv); + + dev_dbg(inno->dev, "fin=%lu, fout=%lu, prediv=%u, fbdiv=%u\n", + parent_rate, fout, prediv, fbdiv); + + inno->pll.prediv = prediv; + inno->pll.fbdiv = fbdiv; + inno->pll.rate = fout; + + return 0; +} + +static unsigned long +inno_dsidphy_pll_clk_recalc_rate(struct clk_hw *hw, unsigned long prate) +{ + struct inno_dsidphy *inno = hw_to_inno(hw); + + /* PLL_Output_Frequency = (FREF / PREDIV * FBDIV) / 2 */ + return (prate / inno->pll.prediv * inno->pll.fbdiv) / 2; +} + +static const struct clk_ops inno_dsidphy_pll_clk_ops = { + .round_rate = inno_dsidphy_pll_clk_round_rate, + .set_rate = inno_dsidphy_pll_clk_set_rate, + .recalc_rate = inno_dsidphy_pll_clk_recalc_rate, +}; + +static int inno_dsidphy_pll_register(struct inno_dsidphy *inno) +{ + struct device *dev = inno->dev; + struct clk *clk; + const char *parent_name; + struct clk_init_data init; + int ret; + + parent_name = __clk_get_name(inno->ref_clk); + + init.name = "mipi_dphy_pll"; + ret = of_property_read_string(dev->of_node, "clock-output-names", + &init.name); + if (ret < 0) + dev_dbg(dev, "phy should set clock-output-names property\n"); + + init.ops = &inno_dsidphy_pll_clk_ops; + init.parent_names = &parent_name; + init.num_parents = 1; + init.flags = 0; + + inno->pll.hw.init = &init; + clk = devm_clk_register(dev, &inno->pll.hw); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + dev_err(dev, "failed to register PLL: %d\n", ret); + return ret; + } + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, + &inno->pll.hw); +} + +static int inno_dsidphy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct inno_dsidphy *inno; + struct phy_provider *phy_provider; + struct phy *phy; + int ret; + + inno = devm_kzalloc(dev, sizeof(*inno), GFP_KERNEL); + if (!inno) + return -ENOMEM; + + inno->dev = dev; + platform_set_drvdata(pdev, inno); + + inno->phy_base = devm_platform_ioremap_resource(pdev, 0); + if (!inno->phy_base) + return -ENOMEM; + + inno->ref_clk = devm_clk_get(dev, "ref"); + if (IS_ERR(inno->ref_clk)) { + ret = PTR_ERR(inno->ref_clk); + dev_err(dev, "failed to get ref clock: %d\n", ret); + return ret; + } + + inno->pclk_phy = devm_clk_get(dev, "pclk"); + if (IS_ERR(inno->pclk_phy)) { + ret = PTR_ERR(inno->pclk_phy); + dev_err(dev, "failed to get phy pclk: %d\n", ret); + return ret; + } + + inno->rst = devm_reset_control_get(dev, "apb"); + if (IS_ERR(inno->rst)) { + ret = PTR_ERR(inno->rst); + dev_err(dev, "failed to get system reset control: %d\n", ret); + return ret; + } + + phy = devm_phy_create(dev, NULL, &inno_dsidphy_ops); + if (IS_ERR(phy)) { + ret = PTR_ERR(phy); + dev_err(dev, "failed to create phy: %d\n", ret); + return ret; + } + + phy_set_drvdata(phy, inno); + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(phy_provider)) { + ret = PTR_ERR(phy_provider); + dev_err(dev, "failed to register phy provider: %d\n", ret); + return ret; + } + + ret = inno_dsidphy_pll_register(inno); + if (ret) + return ret; + + pm_runtime_enable(dev); + + return 0; +} + +static int inno_dsidphy_remove(struct platform_device *pdev) +{ + struct inno_dsidphy *inno = platform_get_drvdata(pdev); + + pm_runtime_disable(inno->dev); + + return 0; +} + +static const struct of_device_id inno_dsidphy_of_match[] = { + { .compatible = "rockchip,px30-dsi-dphy", }, + { .compatible = "rockchip,rk3128-dsi-dphy", }, + { .compatible = "rockchip,rk3368-dsi-dphy", }, + {} +}; +MODULE_DEVICE_TABLE(of, inno_dsidphy_of_match); + +static struct platform_driver inno_dsidphy_driver = { + .driver = { + .name = "inno-dsidphy", + .of_match_table = of_match_ptr(inno_dsidphy_of_match), + }, + .probe = inno_dsidphy_probe, + .remove = inno_dsidphy_remove, +}; +module_platform_driver(inno_dsidphy_driver); + +MODULE_AUTHOR("Wyon Bi "); +MODULE_DESCRIPTION("Innosilicon MIPI/LVDS/TTL Video Combo PHY driver"); +MODULE_LICENSE("GPL v2"); From 3817c79611792333a33f6548a2aaf701a70fc593 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Thu, 24 Oct 2019 00:38:50 +0200 Subject: [PATCH 068/198] dt-bindings: phy: add yaml binding for rockchip,px30-dsi-dphy This adds a yaml binding for the external dsi phy found on Rockchip socs of the px30, rk3128 and rk3368 variants. Signed-off-by: Heiko Stuebner Reviewed-by: Rob Herring Signed-off-by: Kishon Vijay Abraham I --- .../bindings/phy/rockchip,px30-dsi-dphy.yaml | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/rockchip,px30-dsi-dphy.yaml diff --git a/Documentation/devicetree/bindings/phy/rockchip,px30-dsi-dphy.yaml b/Documentation/devicetree/bindings/phy/rockchip,px30-dsi-dphy.yaml new file mode 100644 index 000000000000..bb0da87bcd84 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/rockchip,px30-dsi-dphy.yaml @@ -0,0 +1,75 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/rockchip,px30-dsi-dphy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Rockchip MIPI DPHY with additional LVDS/TTL modes + +maintainers: + - Heiko Stuebner + +properties: + "#phy-cells": + const: 0 + + "#clock-cells": + const: 0 + + compatible: + enum: + - rockchip,px30-dsi-dphy + - rockchip,rk3128-dsi-dphy + - rockchip,rk3368-dsi-dphy + + reg: + maxItems: 1 + + clocks: + items: + - description: PLL reference clock + - description: Module clock + + clock-names: + items: + - const: ref + - const: pclk + + power-domains: + maxItems: 1 + description: phandle to the associated power domain + + resets: + items: + - description: exclusive PHY reset line + + reset-names: + items: + - const: apb + +required: + - "#phy-cells" + - "#clock-cells" + - compatible + - reg + - clocks + - clock-names + - resets + - reset-names + +additionalProperties: false + +examples: + - | + dsi_dphy: phy@ff2e0000 { + compatible = "rockchip,px30-video-phy"; + reg = <0x0 0xff2e0000 0x0 0x10000>; + clocks = <&pmucru 13>, <&cru 12>; + clock-names = "ref", "pclk"; + #clock-cells = <0>; + resets = <&cru 12>; + reset-names = "apb"; + #phy-cells = <0>; + }; + +... From 82b5d164415549e74cfa1f9156ffd4463d0a76e2 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 4 Sep 2019 11:40:14 +0000 Subject: [PATCH 069/198] phy: lantiq: vrx200-pcie: fix error return code in ltq_vrx200_pcie_phy_power_on() Fix to return a negative error code from the error handling case instead of 0, as done elsewhere in this function. Fixes: e52a632195bf ("phy: lantiq: vrx200-pcie: add a driver for the Lantiq VRX200 PCIe PHY") Signed-off-by: Wei Yongjun Reviewed-by: Martin Blumenstingl Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/lantiq/phy-lantiq-vrx200-pcie.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/phy/lantiq/phy-lantiq-vrx200-pcie.c b/drivers/phy/lantiq/phy-lantiq-vrx200-pcie.c index 544d64a84cc0..6e457967653e 100644 --- a/drivers/phy/lantiq/phy-lantiq-vrx200-pcie.c +++ b/drivers/phy/lantiq/phy-lantiq-vrx200-pcie.c @@ -323,7 +323,8 @@ static int ltq_vrx200_pcie_phy_power_on(struct phy *phy) goto err_disable_pdi_clk; /* Check if we are in "startup ready" status */ - if (ltq_vrx200_pcie_phy_wait_for_pll(phy) != 0) + ret = ltq_vrx200_pcie_phy_wait_for_pll(phy); + if (ret) goto err_disable_phy_clk; ltq_vrx200_pcie_phy_apply_workarounds(phy); From c9baab38fe0e28762d0d67611cbe2aef0fb3fc72 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Wed, 9 Oct 2019 17:12:49 +0100 Subject: [PATCH 070/198] phy: renesas: phy-rcar-gen2: Fix the array off by one warning Fix the below smatch warning by adding variable check rather than the hardcoded value. warn: array off by one? 'data->select_value[channel_num]' Reported-by: Dan Carpenter Signed-off-by: Biju Das Reviewed-by: Geert Uytterhoeven Reviewed-by: Wolfram Sang Reviewed-by: Ulrich Hecht Reviewed-by: Yoshihiro Shimoda Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/renesas/phy-rcar-gen2.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/phy/renesas/phy-rcar-gen2.c b/drivers/phy/renesas/phy-rcar-gen2.c index 2926e4937301..2e279ac0fa4d 100644 --- a/drivers/phy/renesas/phy-rcar-gen2.c +++ b/drivers/phy/renesas/phy-rcar-gen2.c @@ -71,6 +71,7 @@ struct rcar_gen2_phy_driver { struct rcar_gen2_phy_data { const struct phy_ops *gen2_phy_ops; const u32 (*select_value)[PHYS_PER_CHANNEL]; + const u32 num_channels; }; static int rcar_gen2_phy_init(struct phy *p) @@ -271,11 +272,13 @@ static const u32 usb20_select_value[][PHYS_PER_CHANNEL] = { static const struct rcar_gen2_phy_data rcar_gen2_usb_phy_data = { .gen2_phy_ops = &rcar_gen2_phy_ops, .select_value = pci_select_value, + .num_channels = ARRAY_SIZE(pci_select_value), }; static const struct rcar_gen2_phy_data rz_g1c_usb_phy_data = { .gen2_phy_ops = &rz_g1c_phy_ops, .select_value = usb20_select_value, + .num_channels = ARRAY_SIZE(usb20_select_value), }; static const struct of_device_id rcar_gen2_phy_match_table[] = { @@ -389,7 +392,7 @@ static int rcar_gen2_phy_probe(struct platform_device *pdev) channel->selected_phy = -1; error = of_property_read_u32(np, "reg", &channel_num); - if (error || channel_num > 2) { + if (error || channel_num >= data->num_channels) { dev_err(dev, "Invalid \"reg\" property\n"); of_node_put(np); return error; From 64f86b9978449ff05bfa6c64b4c5439e21e9c80b Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Tue, 8 Oct 2019 13:52:08 +0200 Subject: [PATCH 071/198] phy: qcom-usb-hs: Fix extcon double register after power cycle Commit f0b5c2c96370 ("phy: qcom-usb-hs: Replace the extcon API") switched from extcon_register_notifier() to the resource-managed API, i.e. devm_extcon_register_notifier(). This is problematic in this case, because the extcon notifier is dynamically registered/unregistered whenever the PHY is powered on/off. The resource-managed API does not unregister the notifier until the driver is removed, so as soon as the PHY is power cycled, attempting to register the notifier again results in: double register detected WARNING: CPU: 1 PID: 182 at kernel/notifier.c:26 notifier_chain_register+0x74/0xa0 Call trace: ... extcon_register_notifier+0x74/0xb8 devm_extcon_register_notifier+0x54/0xb8 qcom_usb_hs_phy_power_on+0x1fc/0x208 ... ... and USB stops working after plugging the cable out and in another time. The easiest way to fix this is to make a partial revert of commit f0b5c2c96370 ("phy: qcom-usb-hs: Replace the extcon API") and avoid using the resource-managed API in this case. Fixes: f0b5c2c96370 ("phy: qcom-usb-hs: Replace the extcon API") Signed-off-by: Stephan Gerhold Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/qualcomm/phy-qcom-usb-hs.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/phy/qualcomm/phy-qcom-usb-hs.c b/drivers/phy/qualcomm/phy-qcom-usb-hs.c index b163b3a1558d..61054272a7c8 100644 --- a/drivers/phy/qualcomm/phy-qcom-usb-hs.c +++ b/drivers/phy/qualcomm/phy-qcom-usb-hs.c @@ -158,8 +158,8 @@ static int qcom_usb_hs_phy_power_on(struct phy *phy) /* setup initial state */ qcom_usb_hs_phy_vbus_notifier(&uphy->vbus_notify, state, uphy->vbus_edev); - ret = devm_extcon_register_notifier(&ulpi->dev, uphy->vbus_edev, - EXTCON_USB, &uphy->vbus_notify); + ret = extcon_register_notifier(uphy->vbus_edev, EXTCON_USB, + &uphy->vbus_notify); if (ret) goto err_ulpi; } @@ -180,6 +180,9 @@ static int qcom_usb_hs_phy_power_off(struct phy *phy) { struct qcom_usb_hs_phy *uphy = phy_get_drvdata(phy); + if (uphy->vbus_edev) + extcon_unregister_notifier(uphy->vbus_edev, EXTCON_USB, + &uphy->vbus_notify); regulator_disable(uphy->v3p3); regulator_disable(uphy->v1p8); clk_disable_unprepare(uphy->sleep_clk); From 4569e64ab6a590dec418f2cd98fbe907a08fd452 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Tue, 17 Sep 2019 10:25:32 +0200 Subject: [PATCH 072/198] phy: phy-rockchip-inno-usb2: add phy description for px30 The px30 soc from Rockchip shares the same register description as the rk3328, so can re-use its definitions. Signed-off-by: Heiko Stuebner Acked-by: Rob Herring Signed-off-by: Kishon Vijay Abraham I --- Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt | 1 + drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 1 + 2 files changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt b/Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt index 00639baae74a..541f5298827c 100644 --- a/Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt +++ b/Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt @@ -2,6 +2,7 @@ ROCKCHIP USB2.0 PHY WITH INNO IP BLOCK Required properties (phy (parent) node): - compatible : should be one of the listed compatibles: + * "rockchip,px30-usb2phy" * "rockchip,rk3228-usb2phy" * "rockchip,rk3328-usb2phy" * "rockchip,rk3366-usb2phy" diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c index eae865ff312c..680cc0c8825c 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c @@ -1423,6 +1423,7 @@ static const struct rockchip_usb2phy_cfg rv1108_phy_cfgs[] = { }; static const struct of_device_id rockchip_usb2phy_dt_match[] = { + { .compatible = "rockchip,px30-usb2phy", .data = &rk3328_phy_cfgs }, { .compatible = "rockchip,rk3228-usb2phy", .data = &rk3228_phy_cfgs }, { .compatible = "rockchip,rk3328-usb2phy", .data = &rk3328_phy_cfgs }, { .compatible = "rockchip,rk3366-usb2phy", .data = &rk3366_phy_cfgs }, From f07a360813f6c551380dca8817d8eb5e7ab40a21 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 25 Jun 2019 15:10:01 +0300 Subject: [PATCH 073/198] thunderbolt: Introduce tb_switch_is_icm() We currently differentiate between SW CM (Software Connection Manager, sometimes also called External Connection Manager) and ICM (Firmware based Connection Manager, Internal Connection Manager) by looking directly at the sw->config.enabled field which may be rather hard to understand for the casual reader. For this reason introduce a wrapper function with documentation that should make the intention more clear. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/lc.c | 4 ++-- drivers/thunderbolt/switch.c | 4 ++-- drivers/thunderbolt/tb.h | 14 ++++++++++++++ 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/drivers/thunderbolt/lc.c b/drivers/thunderbolt/lc.c index ae1e92611c3e..af38076088f6 100644 --- a/drivers/thunderbolt/lc.c +++ b/drivers/thunderbolt/lc.c @@ -94,7 +94,7 @@ int tb_lc_configure_link(struct tb_switch *sw) struct tb_port *up, *down; int ret; - if (!sw->config.enabled || !tb_route(sw)) + if (!tb_route(sw) || tb_switch_is_icm(sw)) return 0; up = tb_upstream_port(sw); @@ -124,7 +124,7 @@ void tb_lc_unconfigure_link(struct tb_switch *sw) { struct tb_port *up, *down; - if (sw->is_unplugged || !sw->config.enabled || !tb_route(sw)) + if (sw->is_unplugged || !tb_route(sw) || tb_switch_is_icm(sw)) return; up = tb_upstream_port(sw); diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index c498464875cf..d42fc0f8b060 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -986,7 +986,7 @@ static int tb_plug_events_active(struct tb_switch *sw, bool active) u32 data; int res; - if (!sw->config.enabled) + if (tb_switch_is_icm(sw)) return 0; sw->config.plug_events_delay = 0xff; @@ -1720,7 +1720,7 @@ static int tb_switch_add_dma_port(struct tb_switch *sw) } /* Root switch DMA port requires running firmware */ - if (!tb_route(sw) && sw->config.enabled) + if (!tb_route(sw) && !tb_switch_is_icm(sw)) return 0; sw->dma_port = dma_port_alloc(sw); diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 6407d529871d..1565af2e48cb 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -591,6 +591,20 @@ static inline bool tb_switch_is_fr(const struct tb_switch *sw) } } +/** + * tb_switch_is_icm() - Is the switch handled by ICM firmware + * @sw: Switch to check + * + * In case there is a need to differentiate whether ICM firmware or SW CM + * is handling @sw this function can be called. It is valid to call this + * after tb_switch_alloc() and tb_switch_configure() has been called + * (latter only for SW CM case). + */ +static inline bool tb_switch_is_icm(const struct tb_switch *sw) +{ + return !sw->config.enabled; +} + int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged); int tb_port_add_nfc_credits(struct tb_port *port, int credits); int tb_port_set_initial_credits(struct tb_port *port, u32 credits); From 68b91293c837c859e841b5bedf2274687bbd53de Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 22 Mar 2019 14:28:11 +0200 Subject: [PATCH 074/198] thunderbolt: Log switch route string on config read/write timeout This helps to point out which switch config read/write triggered the timeout. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/ctl.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/thunderbolt/ctl.c b/drivers/thunderbolt/ctl.c index 2ec1af8f7968..d97813e80e5f 100644 --- a/drivers/thunderbolt/ctl.c +++ b/drivers/thunderbolt/ctl.c @@ -962,8 +962,8 @@ int tb_cfg_read(struct tb_ctl *ctl, void *buffer, u64 route, u32 port, return tb_cfg_get_error(ctl, space, &res); case -ETIMEDOUT: - tb_ctl_warn(ctl, "timeout reading config space %u from %#x\n", - space, offset); + tb_ctl_warn(ctl, "%llx: timeout reading config space %u from %#x\n", + route, space, offset); break; default: @@ -988,8 +988,8 @@ int tb_cfg_write(struct tb_ctl *ctl, const void *buffer, u64 route, u32 port, return tb_cfg_get_error(ctl, space, &res); case -ETIMEDOUT: - tb_ctl_warn(ctl, "timeout writing config space %u to %#x\n", - space, offset); + tb_ctl_warn(ctl, "%llx: timeout writing config space %u to %#x\n", + route, space, offset); break; default: From af99f696b5c57e5e7465750f3a7b3ae5e69d6c7d Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 27 Aug 2019 15:18:20 +0300 Subject: [PATCH 075/198] thunderbolt: Log error if adding switch fails If we fail to add a switch for some reason log an error instead of keeping silent. This is useful for debugging. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/switch.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index d42fc0f8b060..dea50fd91e68 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -1785,21 +1785,25 @@ int tb_switch_add(struct tb_switch *sw) * configuration based mailbox. */ ret = tb_switch_add_dma_port(sw); - if (ret) + if (ret) { + dev_err(&sw->dev, "failed to add DMA port\n"); return ret; + } if (!sw->safe_mode) { /* read drom */ ret = tb_drom_read(sw); if (ret) { - tb_sw_warn(sw, "tb_eeprom_read_rom failed\n"); + dev_err(&sw->dev, "reading DROM failed\n"); return ret; } tb_sw_dbg(sw, "uid: %#llx\n", sw->uid); ret = tb_switch_set_uuid(sw); - if (ret) + if (ret) { + dev_err(&sw->dev, "failed to set UUID\n"); return ret; + } for (i = 0; i <= sw->config.max_port_number; i++) { if (sw->ports[i].disabled) { @@ -1807,14 +1811,18 @@ int tb_switch_add(struct tb_switch *sw) continue; } ret = tb_init_port(&sw->ports[i]); - if (ret) + if (ret) { + dev_err(&sw->dev, "failed to initialize port %d\n", i); return ret; + } } } ret = device_add(&sw->dev); - if (ret) + if (ret) { + dev_err(&sw->dev, "failed to add device: %d\n", ret); return ret; + } if (tb_route(sw)) { dev_info(&sw->dev, "new device found, vendor=%#x device=%#x\n", @@ -1826,6 +1834,7 @@ int tb_switch_add(struct tb_switch *sw) ret = tb_switch_nvm_add(sw); if (ret) { + dev_err(&sw->dev, "failed to add NVM devices\n"); device_del(&sw->dev); return ret; } From 8f57d47806664d9b2e618ea8086adcf76752daaf Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 6 Sep 2019 11:59:00 +0300 Subject: [PATCH 076/198] thunderbolt: Convert basic adapter register names to follow the USB4 spec Now that USB4 spec has names for these basic registers we can use them instead. This makes it easier to match certain register to the spec. No functional changes. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/switch.c | 18 +++++++++--------- drivers/thunderbolt/tb_regs.h | 15 ++++++++------- drivers/thunderbolt/tunnel.c | 10 +++++----- 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index dea50fd91e68..9c2e739c79f3 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -553,17 +553,17 @@ int tb_port_add_nfc_credits(struct tb_port *port, int credits) if (credits == 0 || port->sw->is_unplugged) return 0; - nfc_credits = port->config.nfc_credits & TB_PORT_NFC_CREDITS_MASK; + nfc_credits = port->config.nfc_credits & ADP_CS_4_NFC_BUFFERS_MASK; nfc_credits += credits; - tb_port_dbg(port, "adding %d NFC credits to %lu", - credits, port->config.nfc_credits & TB_PORT_NFC_CREDITS_MASK); + tb_port_dbg(port, "adding %d NFC credits to %lu", credits, + port->config.nfc_credits & ADP_CS_4_NFC_BUFFERS_MASK); - port->config.nfc_credits &= ~TB_PORT_NFC_CREDITS_MASK; + port->config.nfc_credits &= ~ADP_CS_4_NFC_BUFFERS_MASK; port->config.nfc_credits |= nfc_credits; return tb_port_write(port, &port->config.nfc_credits, - TB_CFG_PORT, 4, 1); + TB_CFG_PORT, ADP_CS_4, 1); } /** @@ -578,14 +578,14 @@ int tb_port_set_initial_credits(struct tb_port *port, u32 credits) u32 data; int ret; - ret = tb_port_read(port, &data, TB_CFG_PORT, 5, 1); + ret = tb_port_read(port, &data, TB_CFG_PORT, ADP_CS_5, 1); if (ret) return ret; - data &= ~TB_PORT_LCA_MASK; - data |= (credits << TB_PORT_LCA_SHIFT) & TB_PORT_LCA_MASK; + data &= ~ADP_CS_5_LCA_MASK; + data |= (credits << ADP_CS_5_LCA_SHIFT) & ADP_CS_5_LCA_MASK; - return tb_port_write(port, &data, TB_CFG_PORT, 5, 1); + return tb_port_write(port, &data, TB_CFG_PORT, ADP_CS_5, 1); } /** diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h index deb9d4a977b9..d0858db9f904 100644 --- a/drivers/thunderbolt/tb_regs.h +++ b/drivers/thunderbolt/tb_regs.h @@ -211,13 +211,14 @@ struct tb_regs_port_header { } __packed; -/* DWORD 4 */ -#define TB_PORT_NFC_CREDITS_MASK GENMASK(19, 0) -#define TB_PORT_MAX_CREDITS_SHIFT 20 -#define TB_PORT_MAX_CREDITS_MASK GENMASK(26, 20) -/* DWORD 5 */ -#define TB_PORT_LCA_SHIFT 22 -#define TB_PORT_LCA_MASK GENMASK(28, 22) +/* Basic adapter configuration registers */ +#define ADP_CS_4 0x04 +#define ADP_CS_4_NFC_BUFFERS_MASK GENMASK(9, 0) +#define ADP_CS_4_TOTAL_BUFFERS_MASK GENMASK(29, 20) +#define ADP_CS_4_TOTAL_BUFFERS_SHIFT 20 +#define ADP_CS_5 0x05 +#define ADP_CS_5_LCA_MASK GENMASK(28, 22) +#define ADP_CS_5_LCA_SHIFT 22 /* Display Port adapter registers */ diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c index 5a99234826e7..2f728029c12d 100644 --- a/drivers/thunderbolt/tunnel.c +++ b/drivers/thunderbolt/tunnel.c @@ -324,12 +324,12 @@ static void tb_dp_init_video_path(struct tb_path *path, bool discover) path->weight = 1; if (discover) { - path->nfc_credits = nfc_credits & TB_PORT_NFC_CREDITS_MASK; + path->nfc_credits = nfc_credits & ADP_CS_4_NFC_BUFFERS_MASK; } else { u32 max_credits; - max_credits = (nfc_credits & TB_PORT_MAX_CREDITS_MASK) >> - TB_PORT_MAX_CREDITS_SHIFT; + max_credits = (nfc_credits & ADP_CS_4_TOTAL_BUFFERS_MASK) >> + ADP_CS_4_TOTAL_BUFFERS_SHIFT; /* Leave some credits for AUX path */ path->nfc_credits = min(max_credits - 2, 12U); } @@ -478,8 +478,8 @@ static u32 tb_dma_credits(struct tb_port *nhi) { u32 max_credits; - max_credits = (nhi->config.nfc_credits & TB_PORT_MAX_CREDITS_MASK) >> - TB_PORT_MAX_CREDITS_SHIFT; + max_credits = (nhi->config.nfc_credits & ADP_CS_4_TOTAL_BUFFERS_MASK) >> + ADP_CS_4_TOTAL_BUFFERS_SHIFT; return min(max_credits, 13U); } From 778bfca3d14aa93d1e3062835061401b08c258f7 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 6 Sep 2019 12:05:24 +0300 Subject: [PATCH 077/198] thunderbolt: Convert PCIe adapter register names to follow the USB4 spec Now that USB4 spec has names for these PCIe adapter registers we can use them instead. This makes it easier to match certain register to the spec. No functional changes. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/switch.c | 10 ++++++---- drivers/thunderbolt/tb_regs.h | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index 9c2e739c79f3..7d05c858423b 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -803,10 +803,11 @@ bool tb_pci_port_is_enabled(struct tb_port *port) { u32 data; - if (tb_port_read(port, &data, TB_CFG_PORT, port->cap_adap, 1)) + if (tb_port_read(port, &data, TB_CFG_PORT, + port->cap_adap + ADP_PCIE_CS_0, 1)) return false; - return !!(data & TB_PCI_EN); + return !!(data & ADP_PCIE_CS_0_PE); } /** @@ -816,10 +817,11 @@ bool tb_pci_port_is_enabled(struct tb_port *port) */ int tb_pci_port_enable(struct tb_port *port, bool enable) { - u32 word = enable ? TB_PCI_EN : 0x0; + u32 word = enable ? ADP_PCIE_CS_0_PE : 0x0; if (!port->cap_adap) return -ENXIO; - return tb_port_write(port, &word, TB_CFG_PORT, port->cap_adap, 1); + return tb_port_write(port, &word, TB_CFG_PORT, + port->cap_adap + ADP_PCIE_CS_0, 1); } /** diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h index d0858db9f904..4be9df354527 100644 --- a/drivers/thunderbolt/tb_regs.h +++ b/drivers/thunderbolt/tb_regs.h @@ -241,8 +241,8 @@ struct tb_regs_port_header { #define TB_DP_REMOTE_CAP 0x5 /* PCIe adapter registers */ - -#define TB_PCI_EN BIT(31) +#define ADP_PCIE_CS_0 0x00 +#define ADP_PCIE_CS_0_PE BIT(31) /* Hop register from TB_CFG_HOPS. 8 byte per entry. */ struct tb_regs_hop { From 98176380cbe5e7747ccd82ed982ce5dfd5cc8b65 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 6 Sep 2019 11:32:15 +0300 Subject: [PATCH 078/198] thunderbolt: Convert DP adapter register names to follow the USB4 spec Now that USB4 spec has names for these DP adapter registers we can use them instead. This makes it easier to match certain register to the spec. No functional changes. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/switch.c | 50 ++++++++++++++++++++--------------- drivers/thunderbolt/tb_regs.h | 32 ++++++++++------------ drivers/thunderbolt/tunnel.c | 8 +++--- 3 files changed, 46 insertions(+), 44 deletions(-) diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index 7d05c858423b..c6bfdcaf7973 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -835,11 +835,12 @@ int tb_dp_port_hpd_is_active(struct tb_port *port) u32 data; int ret; - ret = tb_port_read(port, &data, TB_CFG_PORT, port->cap_adap + 2, 1); + ret = tb_port_read(port, &data, TB_CFG_PORT, + port->cap_adap + ADP_DP_CS_2, 1); if (ret) return ret; - return !!(data & TB_DP_HDP); + return !!(data & ADP_DP_CS_2_HDP); } /** @@ -853,12 +854,14 @@ int tb_dp_port_hpd_clear(struct tb_port *port) u32 data; int ret; - ret = tb_port_read(port, &data, TB_CFG_PORT, port->cap_adap + 3, 1); + ret = tb_port_read(port, &data, TB_CFG_PORT, + port->cap_adap + ADP_DP_CS_3, 1); if (ret) return ret; - data |= TB_DP_HPDC; - return tb_port_write(port, &data, TB_CFG_PORT, port->cap_adap + 3, 1); + data |= ADP_DP_CS_3_HDPC; + return tb_port_write(port, &data, TB_CFG_PORT, + port->cap_adap + ADP_DP_CS_3, 1); } /** @@ -876,20 +879,23 @@ int tb_dp_port_set_hops(struct tb_port *port, unsigned int video, u32 data[2]; int ret; - ret = tb_port_read(port, data, TB_CFG_PORT, port->cap_adap, - ARRAY_SIZE(data)); + ret = tb_port_read(port, data, TB_CFG_PORT, + port->cap_adap + ADP_DP_CS_0, ARRAY_SIZE(data)); if (ret) return ret; - data[0] &= ~TB_DP_VIDEO_HOPID_MASK; - data[1] &= ~(TB_DP_AUX_RX_HOPID_MASK | TB_DP_AUX_TX_HOPID_MASK); + data[0] &= ~ADP_DP_CS_0_VIDEO_HOPID_MASK; + data[1] &= ~ADP_DP_CS_1_AUX_RX_HOPID_MASK; + data[1] &= ~ADP_DP_CS_1_AUX_RX_HOPID_MASK; - data[0] |= (video << TB_DP_VIDEO_HOPID_SHIFT) & TB_DP_VIDEO_HOPID_MASK; - data[1] |= aux_tx & TB_DP_AUX_TX_HOPID_MASK; - data[1] |= (aux_rx << TB_DP_AUX_RX_HOPID_SHIFT) & TB_DP_AUX_RX_HOPID_MASK; + data[0] |= (video << ADP_DP_CS_0_VIDEO_HOPID_SHIFT) & + ADP_DP_CS_0_VIDEO_HOPID_MASK; + data[1] |= aux_tx & ADP_DP_CS_1_AUX_TX_HOPID_MASK; + data[1] |= (aux_rx << ADP_DP_CS_1_AUX_RX_HOPID_SHIFT) & + ADP_DP_CS_1_AUX_RX_HOPID_MASK; - return tb_port_write(port, data, TB_CFG_PORT, port->cap_adap, - ARRAY_SIZE(data)); + return tb_port_write(port, data, TB_CFG_PORT, + port->cap_adap + ADP_DP_CS_0, ARRAY_SIZE(data)); } /** @@ -900,11 +906,11 @@ bool tb_dp_port_is_enabled(struct tb_port *port) { u32 data[2]; - if (tb_port_read(port, data, TB_CFG_PORT, port->cap_adap, + if (tb_port_read(port, data, TB_CFG_PORT, port->cap_adap + ADP_DP_CS_0, ARRAY_SIZE(data))) return false; - return !!(data[0] & (TB_DP_VIDEO_EN | TB_DP_AUX_EN)); + return !!(data[0] & (ADP_DP_CS_0_VE | ADP_DP_CS_0_AE)); } /** @@ -920,18 +926,18 @@ int tb_dp_port_enable(struct tb_port *port, bool enable) u32 data[2]; int ret; - ret = tb_port_read(port, data, TB_CFG_PORT, port->cap_adap, - ARRAY_SIZE(data)); + ret = tb_port_read(port, data, TB_CFG_PORT, + port->cap_adap + ADP_DP_CS_0, ARRAY_SIZE(data)); if (ret) return ret; if (enable) - data[0] |= TB_DP_VIDEO_EN | TB_DP_AUX_EN; + data[0] |= ADP_DP_CS_0_VE | ADP_DP_CS_0_AE; else - data[0] &= ~(TB_DP_VIDEO_EN | TB_DP_AUX_EN); + data[0] &= ~(ADP_DP_CS_0_VE | ADP_DP_CS_0_AE); - return tb_port_write(port, data, TB_CFG_PORT, port->cap_adap, - ARRAY_SIZE(data)); + return tb_port_write(port, data, TB_CFG_PORT, + port->cap_adap + ADP_DP_CS_0, ARRAY_SIZE(data)); } /* switch utility functions */ diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h index 4be9df354527..faa14b3df83c 100644 --- a/drivers/thunderbolt/tb_regs.h +++ b/drivers/thunderbolt/tb_regs.h @@ -221,24 +221,20 @@ struct tb_regs_port_header { #define ADP_CS_5_LCA_SHIFT 22 /* Display Port adapter registers */ - -/* DWORD 0 */ -#define TB_DP_VIDEO_HOPID_SHIFT 16 -#define TB_DP_VIDEO_HOPID_MASK GENMASK(26, 16) -#define TB_DP_AUX_EN BIT(30) -#define TB_DP_VIDEO_EN BIT(31) -/* DWORD 1 */ -#define TB_DP_AUX_TX_HOPID_MASK GENMASK(10, 0) -#define TB_DP_AUX_RX_HOPID_SHIFT 11 -#define TB_DP_AUX_RX_HOPID_MASK GENMASK(21, 11) -/* DWORD 2 */ -#define TB_DP_HDP BIT(6) -/* DWORD 3 */ -#define TB_DP_HPDC BIT(9) -/* DWORD 4 */ -#define TB_DP_LOCAL_CAP 0x4 -/* DWORD 5 */ -#define TB_DP_REMOTE_CAP 0x5 +#define ADP_DP_CS_0 0x00 +#define ADP_DP_CS_0_VIDEO_HOPID_MASK GENMASK(26, 16) +#define ADP_DP_CS_0_VIDEO_HOPID_SHIFT 16 +#define ADP_DP_CS_0_AE BIT(30) +#define ADP_DP_CS_0_VE BIT(31) +#define ADP_DP_CS_1_AUX_TX_HOPID_MASK GENMASK(10, 0) +#define ADP_DP_CS_1_AUX_RX_HOPID_MASK GENMASK(21, 11) +#define ADP_DP_CS_1_AUX_RX_HOPID_SHIFT 11 +#define ADP_DP_CS_2 0x02 +#define ADP_DP_CS_2_HDP BIT(6) +#define ADP_DP_CS_3 0x03 +#define ADP_DP_CS_3_HDPC BIT(9) +#define DP_LOCAL_CAP 0x04 +#define DP_REMOTE_CAP 0x05 /* PCIe adapter registers */ #define ADP_PCIE_CS_0 0x00 diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c index 2f728029c12d..382331d71c28 100644 --- a/drivers/thunderbolt/tunnel.c +++ b/drivers/thunderbolt/tunnel.c @@ -241,23 +241,23 @@ static int tb_dp_xchg_caps(struct tb_tunnel *tunnel) /* Read both DP_LOCAL_CAP registers */ ret = tb_port_read(in, &in_dp_cap, TB_CFG_PORT, - in->cap_adap + TB_DP_LOCAL_CAP, 1); + in->cap_adap + DP_LOCAL_CAP, 1); if (ret) return ret; ret = tb_port_read(out, &out_dp_cap, TB_CFG_PORT, - out->cap_adap + TB_DP_LOCAL_CAP, 1); + out->cap_adap + DP_LOCAL_CAP, 1); if (ret) return ret; /* Write IN local caps to OUT remote caps */ ret = tb_port_write(out, &in_dp_cap, TB_CFG_PORT, - out->cap_adap + TB_DP_REMOTE_CAP, 1); + out->cap_adap + DP_REMOTE_CAP, 1); if (ret) return ret; return tb_port_write(in, &out_dp_cap, TB_CFG_PORT, - in->cap_adap + TB_DP_REMOTE_CAP, 1); + in->cap_adap + DP_REMOTE_CAP, 1); } static int tb_dp_activate(struct tb_tunnel *tunnel, bool active) From 826c6a1773084c737abf09dccc591f9a59b8b812 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 1 Jul 2019 18:41:51 +0300 Subject: [PATCH 079/198] thunderbolt: Make tb_sw_write() take const parameter The function does not modify the argument in any way so make it const. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tb.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 1565af2e48cb..455ca490ea87 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -399,7 +399,7 @@ static inline int tb_sw_read(struct tb_switch *sw, void *buffer, length); } -static inline int tb_sw_write(struct tb_switch *sw, void *buffer, +static inline int tb_sw_write(struct tb_switch *sw, const void *buffer, enum tb_cfg_space space, u32 offset, u32 length) { if (sw->is_unplugged) From b433d0100562233b21beb13c0139feeff350bc68 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 30 Sep 2019 14:07:22 +0300 Subject: [PATCH 080/198] thunderbolt: Add helper macro to iterate over switch ports There are quite many places in the driver where we iterate over each port in the switch. To make it bit more convenient, add a macro that can be used to iterate over each port and convert existing call sites to use it. This is based on code by Lukas Wunner. No functional changes. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/icm.c | 12 +++----- drivers/thunderbolt/switch.c | 57 ++++++++++++++++++----------------- drivers/thunderbolt/tb.c | 54 ++++++++++++++++----------------- drivers/thunderbolt/tb.h | 11 +++++++ drivers/thunderbolt/xdomain.c | 5 ++- 5 files changed, 74 insertions(+), 65 deletions(-) diff --git a/drivers/thunderbolt/icm.c b/drivers/thunderbolt/icm.c index 245588f691e7..24625880692e 100644 --- a/drivers/thunderbolt/icm.c +++ b/drivers/thunderbolt/icm.c @@ -1893,14 +1893,12 @@ static int icm_suspend(struct tb *tb) */ static void icm_unplug_children(struct tb_switch *sw) { - unsigned int i; + struct tb_port *port; if (tb_route(sw)) sw->is_unplugged = true; - for (i = 1; i <= sw->config.max_port_number; i++) { - struct tb_port *port = &sw->ports[i]; - + tb_switch_for_each_port(sw, port) { if (port->xdomain) port->xdomain->is_unplugged = true; else if (tb_port_has_remote(port)) @@ -1936,11 +1934,9 @@ static void remove_unplugged_switch(struct tb_switch *sw) static void icm_free_unplugged_children(struct tb_switch *sw) { - unsigned int i; - - for (i = 1; i <= sw->config.max_port_number; i++) { - struct tb_port *port = &sw->ports[i]; + struct tb_port *port; + tb_switch_for_each_port(sw, port) { if (port->xdomain && port->xdomain->is_unplugged) { tb_xdomain_remove(port->xdomain); port->xdomain = NULL; diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index c6bfdcaf7973..849681500daa 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -1400,14 +1400,14 @@ static const struct attribute_group *switch_groups[] = { static void tb_switch_release(struct device *dev) { struct tb_switch *sw = tb_to_switch(dev); - int i; + struct tb_port *port; dma_port_free(sw->dma_port); - for (i = 1; i <= sw->config.max_port_number; i++) { - if (!sw->ports[i].disabled) { - ida_destroy(&sw->ports[i].in_hopids); - ida_destroy(&sw->ports[i].out_hopids); + tb_switch_for_each_port(sw, port) { + if (!port->disabled) { + ida_destroy(&port->in_hopids); + ida_destroy(&port->out_hopids); } } @@ -1869,7 +1869,7 @@ int tb_switch_add(struct tb_switch *sw) */ void tb_switch_remove(struct tb_switch *sw) { - int i; + struct tb_port *port; if (sw->rpm) { pm_runtime_get_sync(&sw->dev); @@ -1877,13 +1877,13 @@ void tb_switch_remove(struct tb_switch *sw) } /* port 0 is the switch itself and never has a remote */ - for (i = 1; i <= sw->config.max_port_number; i++) { - if (tb_port_has_remote(&sw->ports[i])) { - tb_switch_remove(sw->ports[i].remote->sw); - sw->ports[i].remote = NULL; - } else if (sw->ports[i].xdomain) { - tb_xdomain_remove(sw->ports[i].xdomain); - sw->ports[i].xdomain = NULL; + tb_switch_for_each_port(sw, port) { + if (tb_port_has_remote(port)) { + tb_switch_remove(port->remote->sw); + port->remote = NULL; + } else if (port->xdomain) { + tb_xdomain_remove(port->xdomain); + port->xdomain = NULL; } } @@ -1903,7 +1903,8 @@ void tb_switch_remove(struct tb_switch *sw) */ void tb_sw_set_unplugged(struct tb_switch *sw) { - int i; + struct tb_port *port; + if (sw == sw->tb->root_switch) { tb_sw_WARN(sw, "cannot unplug root switch\n"); return; @@ -1913,17 +1914,19 @@ void tb_sw_set_unplugged(struct tb_switch *sw) return; } sw->is_unplugged = true; - for (i = 0; i <= sw->config.max_port_number; i++) { - if (tb_port_has_remote(&sw->ports[i])) - tb_sw_set_unplugged(sw->ports[i].remote->sw); - else if (sw->ports[i].xdomain) - sw->ports[i].xdomain->is_unplugged = true; + tb_switch_for_each_port(sw, port) { + if (tb_port_has_remote(port)) + tb_sw_set_unplugged(port->remote->sw); + else if (port->xdomain) + port->xdomain->is_unplugged = true; } } int tb_switch_resume(struct tb_switch *sw) { - int i, err; + struct tb_port *port; + int err; + tb_sw_dbg(sw, "resuming switch\n"); /* @@ -1971,9 +1974,7 @@ int tb_switch_resume(struct tb_switch *sw) return err; /* check for surviving downstream switches */ - for (i = 1; i <= sw->config.max_port_number; i++) { - struct tb_port *port = &sw->ports[i]; - + tb_switch_for_each_port(sw, port) { if (!tb_port_has_remote(port) && !port->xdomain) continue; @@ -1997,14 +1998,16 @@ int tb_switch_resume(struct tb_switch *sw) void tb_switch_suspend(struct tb_switch *sw) { - int i, err; + struct tb_port *port; + int err; + err = tb_plug_events_active(sw, false); if (err) return; - for (i = 1; i <= sw->config.max_port_number; i++) { - if (tb_port_has_remote(&sw->ports[i])) - tb_switch_suspend(sw->ports[i].remote->sw); + tb_switch_for_each_port(sw, port) { + if (tb_port_has_remote(port)) + tb_switch_suspend(port->remote->sw); } tb_lc_set_sleep(sw); diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index 1f7a9e1cc09c..49589b38ff12 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -61,12 +61,10 @@ static void tb_discover_tunnels(struct tb_switch *sw) struct tb *tb = sw->tb; struct tb_cm *tcm = tb_priv(tb); struct tb_port *port; - int i; - for (i = 1; i <= sw->config.max_port_number; i++) { + tb_switch_for_each_port(sw, port) { struct tb_tunnel *tunnel = NULL; - port = &sw->ports[i]; switch (port->config.type) { case TB_TYPE_DP_HDMI_IN: tunnel = tb_tunnel_discover_dp(tb, port); @@ -95,9 +93,9 @@ static void tb_discover_tunnels(struct tb_switch *sw) list_add_tail(&tunnel->list, &tcm->tunnel_list); } - for (i = 1; i <= sw->config.max_port_number; i++) { - if (tb_port_has_remote(&sw->ports[i])) - tb_discover_tunnels(sw->ports[i].remote->sw); + tb_switch_for_each_port(sw, port) { + if (tb_port_has_remote(port)) + tb_discover_tunnels(port->remote->sw); } } @@ -130,9 +128,10 @@ static void tb_scan_port(struct tb_port *port); */ static void tb_scan_switch(struct tb_switch *sw) { - int i; - for (i = 1; i <= sw->config.max_port_number; i++) - tb_scan_port(&sw->ports[i]); + struct tb_port *port; + + tb_switch_for_each_port(sw, port) + tb_scan_port(port); } /** @@ -263,10 +262,9 @@ static void tb_free_invalid_tunnels(struct tb *tb) */ static void tb_free_unplugged_children(struct tb_switch *sw) { - int i; - for (i = 1; i <= sw->config.max_port_number; i++) { - struct tb_port *port = &sw->ports[i]; + struct tb_port *port; + tb_switch_for_each_port(sw, port) { if (!tb_port_has_remote(port)) continue; @@ -289,10 +287,13 @@ static void tb_free_unplugged_children(struct tb_switch *sw) static struct tb_port *tb_find_port(struct tb_switch *sw, enum tb_port_type type) { - int i; - for (i = 1; i <= sw->config.max_port_number; i++) - if (sw->ports[i].config.type == type) - return &sw->ports[i]; + struct tb_port *port; + + tb_switch_for_each_port(sw, port) { + if (port->config.type == type) + return port; + } + return NULL; } @@ -304,18 +305,18 @@ static struct tb_port *tb_find_port(struct tb_switch *sw, static struct tb_port *tb_find_unused_port(struct tb_switch *sw, enum tb_port_type type) { - int i; + struct tb_port *port; - for (i = 1; i <= sw->config.max_port_number; i++) { - if (tb_is_upstream_port(&sw->ports[i])) + tb_switch_for_each_port(sw, port) { + if (tb_is_upstream_port(port)) continue; - if (sw->ports[i].config.type != type) + if (port->config.type != type) continue; - if (!sw->ports[i].cap_adap) + if (port->cap_adap) continue; - if (tb_port_is_enabled(&sw->ports[i])) + if (tb_port_is_enabled(port)) continue; - return &sw->ports[i]; + return port; } return NULL; } @@ -734,11 +735,10 @@ static int tb_resume_noirq(struct tb *tb) static int tb_free_unplugged_xdomains(struct tb_switch *sw) { - int i, ret = 0; - - for (i = 1; i <= sw->config.max_port_number; i++) { - struct tb_port *port = &sw->ports[i]; + struct tb_port *port; + int ret = 0; + tb_switch_for_each_port(sw, port) { if (tb_is_upstream_port(port)) continue; if (port->xdomain && port->xdomain->is_unplugged) { diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 455ca490ea87..4c77d5264660 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -530,6 +530,17 @@ struct tb_switch *tb_switch_find_by_link_depth(struct tb *tb, u8 link, struct tb_switch *tb_switch_find_by_uuid(struct tb *tb, const uuid_t *uuid); struct tb_switch *tb_switch_find_by_route(struct tb *tb, u64 route); +/** + * tb_switch_for_each_port() - Iterate over each switch port + * @sw: Switch whose ports to iterate + * @p: Port used as iterator + * + * Iterates over each switch port skipping the control port (port %0). + */ +#define tb_switch_for_each_port(sw, p) \ + for ((p) = &(sw)->ports[1]; \ + (p) <= &(sw)->ports[(sw)->config.max_port_number]; (p)++) + static inline struct tb_switch *tb_switch_get(struct tb_switch *sw) { if (sw) diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c index 4e17a7c7bf0a..880d784398a3 100644 --- a/drivers/thunderbolt/xdomain.c +++ b/drivers/thunderbolt/xdomain.c @@ -1404,10 +1404,9 @@ struct tb_xdomain_lookup { static struct tb_xdomain *switch_find_xdomain(struct tb_switch *sw, const struct tb_xdomain_lookup *lookup) { - int i; + struct tb_port *port; - for (i = 1; i <= sw->config.max_port_number; i++) { - struct tb_port *port = &sw->ports[i]; + tb_switch_for_each_port(sw, port) { struct tb_xdomain *xd; if (port->xdomain) { From b5db76dba0642ea6f2391374f3b2b479014e5bf0 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 8 Oct 2019 19:03:34 +0300 Subject: [PATCH 081/198] thunderbolt: Refactor add_switch() into two functions Currently add_switch() takes a huge amount of parameters that makes it hard to maintain. Instead of passing all those parameters we can split the function into two parts (alloc and add) and fill the additional switch fields directly in the functions calling those. While there remove redundant error logging in case kmemdup() fails. No functional changes. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/icm.c | 112 +++++++++++++++++++++++--------------- 1 file changed, 68 insertions(+), 44 deletions(-) diff --git a/drivers/thunderbolt/icm.c b/drivers/thunderbolt/icm.c index 24625880692e..d9caac77e08c 100644 --- a/drivers/thunderbolt/icm.c +++ b/drivers/thunderbolt/icm.c @@ -147,6 +147,17 @@ static const struct intel_vss *parse_intel_vss(const void *ep_name, size_t size) return NULL; } +static bool intel_vss_is_rtd3(const void *ep_name, size_t size) +{ + const struct intel_vss *vss; + + vss = parse_intel_vss(ep_name, size); + if (vss) + return !!(vss->flags & INTEL_VSS_FLAGS_RTD3); + + return false; +} + static inline struct tb *icm_to_tb(struct icm *icm) { return ((void *)icm - sizeof(struct tb)); @@ -562,58 +573,42 @@ static int icm_fr_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd) return 0; } -static struct tb_switch *add_switch(struct tb_switch *parent_sw, u64 route, - const uuid_t *uuid, const u8 *ep_name, - size_t ep_name_size, u8 connection_id, - u8 connection_key, u8 link, u8 depth, - enum tb_security_level security_level, - bool authorized, bool boot) +static struct tb_switch *alloc_switch(struct tb_switch *parent_sw, u64 route, + const uuid_t *uuid) { - const struct intel_vss *vss; + struct tb *tb = parent_sw->tb; struct tb_switch *sw; - int ret; - pm_runtime_get_sync(&parent_sw->dev); - - sw = tb_switch_alloc(parent_sw->tb, &parent_sw->dev, route); - if (IS_ERR(sw)) - goto out; + sw = tb_switch_alloc(tb, &parent_sw->dev, route); + if (IS_ERR(sw)) { + tb_warn(tb, "failed to allocate switch at %llx\n", route); + return sw; + } sw->uuid = kmemdup(uuid, sizeof(*uuid), GFP_KERNEL); if (!sw->uuid) { - tb_sw_warn(sw, "cannot allocate memory for switch\n"); tb_switch_put(sw); - goto out; + return ERR_PTR(-ENOMEM); } - sw->connection_id = connection_id; - sw->connection_key = connection_key; - sw->link = link; - sw->depth = depth; - sw->authorized = authorized; - sw->security_level = security_level; - sw->boot = boot; - init_completion(&sw->rpm_complete); - vss = parse_intel_vss(ep_name, ep_name_size); - if (vss) - sw->rpm = !!(vss->flags & INTEL_VSS_FLAGS_RTD3); + init_completion(&sw->rpm_complete); + return sw; +} + +static int add_switch(struct tb_switch *parent_sw, struct tb_switch *sw) +{ + u64 route = tb_route(sw); + int ret; /* Link the two switches now */ tb_port_at(route, parent_sw)->remote = tb_upstream_port(sw); tb_upstream_port(sw)->remote = tb_port_at(route, parent_sw); ret = tb_switch_add(sw); - if (ret) { + if (ret) tb_port_at(tb_route(sw), parent_sw)->remote = NULL; - tb_switch_put(sw); - sw = ERR_PTR(ret); - } -out: - pm_runtime_mark_last_busy(&parent_sw->dev); - pm_runtime_put_autosuspend(&parent_sw->dev); - - return sw; + return ret; } static void update_switch(struct tb_switch *parent_sw, struct tb_switch *sw, @@ -811,10 +806,25 @@ icm_fr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr) return; } - add_switch(parent_sw, route, &pkg->ep_uuid, (const u8 *)pkg->ep_name, - sizeof(pkg->ep_name), pkg->connection_id, - pkg->connection_key, link, depth, security_level, - authorized, boot); + pm_runtime_get_sync(&parent_sw->dev); + + sw = alloc_switch(parent_sw, route, &pkg->ep_uuid); + if (!IS_ERR(sw)) { + sw->connection_id = pkg->connection_id; + sw->connection_key = pkg->connection_key; + sw->link = link; + sw->depth = depth; + sw->authorized = authorized; + sw->security_level = security_level; + sw->boot = boot; + sw->rpm = intel_vss_is_rtd3(pkg->ep_name, sizeof(pkg->ep_name)); + + if (add_switch(parent_sw, sw)) + tb_switch_put(sw); + } + + pm_runtime_mark_last_busy(&parent_sw->dev); + pm_runtime_put_autosuspend(&parent_sw->dev); tb_switch_put(parent_sw); } @@ -1205,11 +1215,25 @@ __icm_tr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr, return; } - sw = add_switch(parent_sw, route, &pkg->ep_uuid, (const u8 *)pkg->ep_name, - sizeof(pkg->ep_name), pkg->connection_id, 0, 0, 0, - security_level, authorized, boot); - if (!IS_ERR(sw) && force_rtd3) - sw->rpm = true; + pm_runtime_get_sync(&parent_sw->dev); + + sw = alloc_switch(parent_sw, route, &pkg->ep_uuid); + if (!IS_ERR(sw)) { + sw->connection_id = pkg->connection_id; + sw->authorized = authorized; + sw->security_level = security_level; + sw->boot = boot; + sw->rpm = force_rtd3; + if (!sw->rpm) + sw->rpm = intel_vss_is_rtd3(pkg->ep_name, + sizeof(pkg->ep_name)); + + if (add_switch(parent_sw, sw)) + tb_switch_put(sw); + } + + pm_runtime_mark_last_busy(&parent_sw->dev); + pm_runtime_put_autosuspend(&parent_sw->dev); tb_switch_put(parent_sw); } From 91c0c12080d0f40ee7275485221b06b4e1e289e1 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 21 Mar 2019 19:03:00 +0200 Subject: [PATCH 082/198] thunderbolt: Add support for lane bonding Lane bonding allows aggregating two 10/20 Gb/s (depending on the generation) lanes into a single 20/40 Gb/s bonded link. This allows sharing the full bandwidth more efficiently. In order to establish lane bonding we need to check that lane bonding is possible through link controller and that both ends of the link actually supports 2x widths. This also means that all the paths should be established through the primary port so update tb_path_alloc() to handle this as well. Lane bonding is supported starting from Falcon Ridge (2nd generation) controllers. We also expose the current speed and number of lanes under each device except the host router following similar attribute naming than USB bus. Expose speed and number of lanes for both directions to allow possibility of asymmetric link in the future. Signed-off-by: Mika Westerberg --- .../ABI/testing/sysfs-bus-thunderbolt | 28 ++ drivers/thunderbolt/icm.c | 12 +- drivers/thunderbolt/lc.c | 28 ++ drivers/thunderbolt/path.c | 30 +- drivers/thunderbolt/switch.c | 288 ++++++++++++++++++ drivers/thunderbolt/tb.c | 22 ++ drivers/thunderbolt/tb.h | 10 + drivers/thunderbolt/tb_msgs.h | 2 + drivers/thunderbolt/tb_regs.h | 20 ++ drivers/thunderbolt/tunnel.c | 19 +- 10 files changed, 452 insertions(+), 7 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-bus-thunderbolt b/Documentation/ABI/testing/sysfs-bus-thunderbolt index c31b4616181c..82e80de78dd0 100644 --- a/Documentation/ABI/testing/sysfs-bus-thunderbolt +++ b/Documentation/ABI/testing/sysfs-bus-thunderbolt @@ -112,6 +112,34 @@ Contact: thunderbolt-software@lists.01.org Description: This attribute contains name of this device extracted from the device DROM. +What: /sys/bus/thunderbolt/devices/.../rx_speed +Date: Jan 2020 +KernelVersion: 5.5 +Contact: Mika Westerberg +Description: This attribute reports the device RX speed per lane. + All RX lanes run at the same speed. + +What: /sys/bus/thunderbolt/devices/.../rx_lanes +Date: Jan 2020 +KernelVersion: 5.5 +Contact: Mika Westerberg +Description: This attribute reports number of RX lanes the device is + using simultaneusly through its upstream port. + +What: /sys/bus/thunderbolt/devices/.../tx_speed +Date: Jan 2020 +KernelVersion: 5.5 +Contact: Mika Westerberg +Description: This attribute reports the TX speed per lane. + All TX lanes run at the same speed. + +What: /sys/bus/thunderbolt/devices/.../tx_lanes +Date: Jan 2020 +KernelVersion: 5.5 +Contact: Mika Westerberg +Description: This attribute reports number of TX lanes the device is + using simultaneusly through its upstream port. + What: /sys/bus/thunderbolt/devices/.../vendor Date: Sep 2017 KernelVersion: 4.13 diff --git a/drivers/thunderbolt/icm.c b/drivers/thunderbolt/icm.c index d9caac77e08c..78480b782045 100644 --- a/drivers/thunderbolt/icm.c +++ b/drivers/thunderbolt/icm.c @@ -692,11 +692,11 @@ icm_fr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr) (const struct icm_fr_event_device_connected *)hdr; enum tb_security_level security_level; struct tb_switch *sw, *parent_sw; + bool boot, dual_lane, speed_gen3; struct icm *icm = tb_priv(tb); bool authorized = false; struct tb_xdomain *xd; u8 link, depth; - bool boot; u64 route; int ret; @@ -709,6 +709,8 @@ icm_fr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr) security_level = (pkg->hdr.flags & ICM_FLAGS_SLEVEL_MASK) >> ICM_FLAGS_SLEVEL_SHIFT; boot = pkg->link_info & ICM_LINK_INFO_BOOT; + dual_lane = pkg->hdr.flags & ICM_FLAGS_DUAL_LANE; + speed_gen3 = pkg->hdr.flags & ICM_FLAGS_SPEED_GEN3; if (pkg->link_info & ICM_LINK_INFO_REJECTED) { tb_info(tb, "switch at %u.%u was rejected by ICM firmware because topology limit exceeded\n", @@ -817,6 +819,8 @@ icm_fr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr) sw->authorized = authorized; sw->security_level = security_level; sw->boot = boot; + sw->link_speed = speed_gen3 ? 20 : 10; + sw->link_width = dual_lane ? 2 : 1; sw->rpm = intel_vss_is_rtd3(pkg->ep_name, sizeof(pkg->ep_name)); if (add_switch(parent_sw, sw)) @@ -1152,10 +1156,10 @@ __icm_tr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr, { const struct icm_tr_event_device_connected *pkg = (const struct icm_tr_event_device_connected *)hdr; + bool authorized, boot, dual_lane, speed_gen3; enum tb_security_level security_level; struct tb_switch *sw, *parent_sw; struct tb_xdomain *xd; - bool authorized, boot; u64 route; icm_postpone_rescan(tb); @@ -1173,6 +1177,8 @@ __icm_tr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr, security_level = (pkg->hdr.flags & ICM_FLAGS_SLEVEL_MASK) >> ICM_FLAGS_SLEVEL_SHIFT; boot = pkg->link_info & ICM_LINK_INFO_BOOT; + dual_lane = pkg->hdr.flags & ICM_FLAGS_DUAL_LANE; + speed_gen3 = pkg->hdr.flags & ICM_FLAGS_SPEED_GEN3; if (pkg->link_info & ICM_LINK_INFO_REJECTED) { tb_info(tb, "switch at %llx was rejected by ICM firmware because topology limit exceeded\n", @@ -1223,6 +1229,8 @@ __icm_tr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr, sw->authorized = authorized; sw->security_level = security_level; sw->boot = boot; + sw->link_speed = speed_gen3 ? 20 : 10; + sw->link_width = dual_lane ? 2 : 1; sw->rpm = force_rtd3; if (!sw->rpm) sw->rpm = intel_vss_is_rtd3(pkg->ep_name, diff --git a/drivers/thunderbolt/lc.c b/drivers/thunderbolt/lc.c index af38076088f6..df56523eb822 100644 --- a/drivers/thunderbolt/lc.c +++ b/drivers/thunderbolt/lc.c @@ -177,3 +177,31 @@ int tb_lc_set_sleep(struct tb_switch *sw) return 0; } + +/** + * tb_lc_lane_bonding_possible() - Is lane bonding possible towards switch + * @sw: Switch to check + * + * Checks whether conditions for lane bonding from parent to @sw are + * possible. + */ +bool tb_lc_lane_bonding_possible(struct tb_switch *sw) +{ + struct tb_port *up; + int cap, ret; + u32 val; + + if (sw->generation < 2) + return false; + + up = tb_upstream_port(sw); + cap = find_port_lc_cap(up); + if (cap < 0) + return false; + + ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, cap + TB_LC_PORT_ATTR, 1); + if (ret) + return false; + + return !!(val & TB_LC_PORT_ATTR_BE); +} diff --git a/drivers/thunderbolt/path.c b/drivers/thunderbolt/path.c index afe5f8391ebf..6cf66597d5d8 100644 --- a/drivers/thunderbolt/path.c +++ b/drivers/thunderbolt/path.c @@ -220,7 +220,8 @@ err: * Creates path between two ports starting with given @src_hopid. Reserves * HopIDs for each port (they can be different from @src_hopid depending on * how many HopIDs each port already have reserved). If there are dual - * links on the path, prioritizes using @link_nr. + * links on the path, prioritizes using @link_nr but takes into account + * that the lanes may be bonded. * * Return: Returns a tb_path on success or NULL on failure. */ @@ -259,7 +260,9 @@ struct tb_path *tb_path_alloc(struct tb *tb, struct tb_port *src, int src_hopid, if (!in_port) goto err; - if (in_port->dual_link_port && in_port->link_nr != link_nr) + /* When lanes are bonded primary link must be used */ + if (!in_port->bonded && in_port->dual_link_port && + in_port->link_nr != link_nr) in_port = in_port->dual_link_port; ret = tb_port_alloc_in_hopid(in_port, in_hopid, in_hopid); @@ -271,8 +274,27 @@ struct tb_path *tb_path_alloc(struct tb *tb, struct tb_port *src, int src_hopid, if (!out_port) goto err; - if (out_port->dual_link_port && out_port->link_nr != link_nr) - out_port = out_port->dual_link_port; + /* + * Pick up right port when going from non-bonded to + * bonded or from bonded to non-bonded. + */ + if (out_port->dual_link_port) { + if (!in_port->bonded && out_port->bonded && + out_port->link_nr) { + /* + * Use primary link when going from + * non-bonded to bonded. + */ + out_port = out_port->dual_link_port; + } else if (!out_port->bonded && + out_port->link_nr != link_nr) { + /* + * If out port is not bonded follow + * link_nr. + */ + out_port = out_port->dual_link_port; + } + } if (i == num_hops - 1) ret = tb_port_alloc_out_hopid(out_port, dst_hopid, diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index 849681500daa..12a63f86820e 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -775,6 +775,132 @@ struct tb_port *tb_next_port_on_path(struct tb_port *start, struct tb_port *end, return next; } +static int tb_port_get_link_speed(struct tb_port *port) +{ + u32 val, speed; + int ret; + + if (!port->cap_phy) + return -EINVAL; + + ret = tb_port_read(port, &val, TB_CFG_PORT, + port->cap_phy + LANE_ADP_CS_1, 1); + if (ret) + return ret; + + speed = (val & LANE_ADP_CS_1_CURRENT_SPEED_MASK) >> + LANE_ADP_CS_1_CURRENT_SPEED_SHIFT; + return speed == LANE_ADP_CS_1_CURRENT_SPEED_GEN3 ? 20 : 10; +} + +static int tb_port_get_link_width(struct tb_port *port) +{ + u32 val; + int ret; + + if (!port->cap_phy) + return -EINVAL; + + ret = tb_port_read(port, &val, TB_CFG_PORT, + port->cap_phy + LANE_ADP_CS_1, 1); + if (ret) + return ret; + + return (val & LANE_ADP_CS_1_CURRENT_WIDTH_MASK) >> + LANE_ADP_CS_1_CURRENT_WIDTH_SHIFT; +} + +static bool tb_port_is_width_supported(struct tb_port *port, int width) +{ + u32 phy, widths; + int ret; + + if (!port->cap_phy) + return false; + + ret = tb_port_read(port, &phy, TB_CFG_PORT, + port->cap_phy + LANE_ADP_CS_0, 1); + if (ret) + return ret; + + widths = (phy & LANE_ADP_CS_0_SUPPORTED_WIDTH_MASK) >> + LANE_ADP_CS_0_SUPPORTED_WIDTH_SHIFT; + + return !!(widths & width); +} + +static int tb_port_set_link_width(struct tb_port *port, unsigned int width) +{ + u32 val; + int ret; + + if (!port->cap_phy) + return -EINVAL; + + ret = tb_port_read(port, &val, TB_CFG_PORT, + port->cap_phy + LANE_ADP_CS_1, 1); + if (ret) + return ret; + + val &= ~LANE_ADP_CS_1_TARGET_WIDTH_MASK; + switch (width) { + case 1: + val |= LANE_ADP_CS_1_TARGET_WIDTH_SINGLE << + LANE_ADP_CS_1_TARGET_WIDTH_SHIFT; + break; + case 2: + val |= LANE_ADP_CS_1_TARGET_WIDTH_DUAL << + LANE_ADP_CS_1_TARGET_WIDTH_SHIFT; + break; + default: + return -EINVAL; + } + + val |= LANE_ADP_CS_1_LB; + + return tb_port_write(port, &val, TB_CFG_PORT, + port->cap_phy + LANE_ADP_CS_1, 1); +} + +static int tb_port_lane_bonding_enable(struct tb_port *port) +{ + int ret; + + /* + * Enable lane bonding for both links if not already enabled by + * for example the boot firmware. + */ + ret = tb_port_get_link_width(port); + if (ret == 1) { + ret = tb_port_set_link_width(port, 2); + if (ret) + return ret; + } + + ret = tb_port_get_link_width(port->dual_link_port); + if (ret == 1) { + ret = tb_port_set_link_width(port->dual_link_port, 2); + if (ret) { + tb_port_set_link_width(port, 1); + return ret; + } + } + + port->bonded = true; + port->dual_link_port->bonded = true; + + return 0; +} + +static void tb_port_lane_bonding_disable(struct tb_port *port) +{ + port->dual_link_port->bonded = false; + port->bonded = false; + + tb_port_set_link_width(port->dual_link_port, 1); + tb_port_set_link_width(port, 1); +} + /** * tb_port_is_enabled() - Is the adapter port enabled * @port: Port to check @@ -1183,6 +1309,36 @@ static ssize_t key_store(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR(key, 0600, key_show, key_store); +static ssize_t speed_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct tb_switch *sw = tb_to_switch(dev); + + return sprintf(buf, "%u.0 Gb/s\n", sw->link_speed); +} + +/* + * Currently all lanes must run at the same speed but we expose here + * both directions to allow possible asymmetric links in the future. + */ +static DEVICE_ATTR(rx_speed, 0444, speed_show, NULL); +static DEVICE_ATTR(tx_speed, 0444, speed_show, NULL); + +static ssize_t lanes_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct tb_switch *sw = tb_to_switch(dev); + + return sprintf(buf, "%u\n", sw->link_width); +} + +/* + * Currently link has same amount of lanes both directions (1 or 2) but + * expose them separately to allow possible asymmetric links in the future. + */ +static DEVICE_ATTR(rx_lanes, 0444, lanes_show, NULL); +static DEVICE_ATTR(tx_lanes, 0444, lanes_show, NULL); + static void nvm_authenticate_start(struct tb_switch *sw) { struct pci_dev *root_port; @@ -1340,6 +1496,10 @@ static struct attribute *switch_attrs[] = { &dev_attr_key.attr, &dev_attr_nvm_authenticate.attr, &dev_attr_nvm_version.attr, + &dev_attr_rx_speed.attr, + &dev_attr_rx_lanes.attr, + &dev_attr_tx_speed.attr, + &dev_attr_tx_lanes.attr, &dev_attr_vendor.attr, &dev_attr_vendor_name.attr, &dev_attr_unique_id.attr, @@ -1370,6 +1530,13 @@ static umode_t switch_attr_is_visible(struct kobject *kobj, sw->security_level == TB_SECURITY_SECURE) return attr->mode; return 0; + } else if (attr == &dev_attr_rx_speed.attr || + attr == &dev_attr_rx_lanes.attr || + attr == &dev_attr_tx_speed.attr || + attr == &dev_attr_tx_lanes.attr) { + if (tb_route(sw)) + return attr->mode; + return 0; } else if (attr == &dev_attr_nvm_authenticate.attr) { if (sw->dma_port && !sw->no_nvm_upgrade) return attr->mode; @@ -1769,6 +1936,123 @@ static int tb_switch_add_dma_port(struct tb_switch *sw) return -ESHUTDOWN; } +static bool tb_switch_lane_bonding_possible(struct tb_switch *sw) +{ + const struct tb_port *up = tb_upstream_port(sw); + + if (!up->dual_link_port || !up->dual_link_port->remote) + return false; + + return tb_lc_lane_bonding_possible(sw); +} + +static int tb_switch_update_link_attributes(struct tb_switch *sw) +{ + struct tb_port *up; + bool change = false; + int ret; + + if (!tb_route(sw) || tb_switch_is_icm(sw)) + return 0; + + up = tb_upstream_port(sw); + + ret = tb_port_get_link_speed(up); + if (ret < 0) + return ret; + if (sw->link_speed != ret) + change = true; + sw->link_speed = ret; + + ret = tb_port_get_link_width(up); + if (ret < 0) + return ret; + if (sw->link_width != ret) + change = true; + sw->link_width = ret; + + /* Notify userspace that there is possible link attribute change */ + if (device_is_registered(&sw->dev) && change) + kobject_uevent(&sw->dev.kobj, KOBJ_CHANGE); + + return 0; +} + +/** + * tb_switch_lane_bonding_enable() - Enable lane bonding + * @sw: Switch to enable lane bonding + * + * Connection manager can call this function to enable lane bonding of a + * switch. If conditions are correct and both switches support the feature, + * lanes are bonded. It is safe to call this to any switch. + */ +int tb_switch_lane_bonding_enable(struct tb_switch *sw) +{ + struct tb_switch *parent = tb_to_switch(sw->dev.parent); + struct tb_port *up, *down; + u64 route = tb_route(sw); + int ret; + + if (!route) + return 0; + + if (!tb_switch_lane_bonding_possible(sw)) + return 0; + + up = tb_upstream_port(sw); + down = tb_port_at(route, parent); + + if (!tb_port_is_width_supported(up, 2) || + !tb_port_is_width_supported(down, 2)) + return 0; + + ret = tb_port_lane_bonding_enable(up); + if (ret) { + tb_port_warn(up, "failed to enable lane bonding\n"); + return ret; + } + + ret = tb_port_lane_bonding_enable(down); + if (ret) { + tb_port_warn(down, "failed to enable lane bonding\n"); + tb_port_lane_bonding_disable(up); + return ret; + } + + tb_switch_update_link_attributes(sw); + + tb_sw_dbg(sw, "lane bonding enabled\n"); + return ret; +} + +/** + * tb_switch_lane_bonding_disable() - Disable lane bonding + * @sw: Switch whose lane bonding to disable + * + * Disables lane bonding between @sw and parent. This can be called even + * if lanes were not bonded originally. + */ +void tb_switch_lane_bonding_disable(struct tb_switch *sw) +{ + struct tb_switch *parent = tb_to_switch(sw->dev.parent); + struct tb_port *up, *down; + + if (!tb_route(sw)) + return; + + up = tb_upstream_port(sw); + if (!up->bonded) + return; + + down = tb_port_at(tb_route(sw), parent); + + tb_port_lane_bonding_disable(up); + tb_port_lane_bonding_disable(down); + + tb_switch_update_link_attributes(sw); + tb_sw_dbg(sw, "lane bonding disabled\n"); +} + /** * tb_switch_add() - Add a switch to the domain * @sw: Switch to add @@ -1824,6 +2108,10 @@ int tb_switch_add(struct tb_switch *sw) return ret; } } + + ret = tb_switch_update_link_attributes(sw); + if (ret) + return ret; } ret = device_add(&sw->dev); diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index 49589b38ff12..f2ce6adc1f48 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -216,6 +216,10 @@ static void tb_scan_port(struct tb_port *port) upstream_port->dual_link_port->remote = port->dual_link_port; } + /* Enable lane bonding if supported */ + if (tb_switch_lane_bonding_enable(sw)) + tb_sw_warn(sw, "failed to enable lane bonding\n"); + tb_scan_switch(sw); } @@ -269,6 +273,7 @@ static void tb_free_unplugged_children(struct tb_switch *sw) continue; if (port->remote->sw->is_unplugged) { + tb_switch_lane_bonding_disable(port->remote->sw); tb_switch_remove(port->remote->sw); port->remote = NULL; if (port->dual_link_port) @@ -534,6 +539,7 @@ static void tb_handle_hotplug(struct work_struct *work) tb_port_dbg(port, "switch unplugged\n"); tb_sw_set_unplugged(port->remote->sw); tb_free_invalid_tunnels(tb); + tb_switch_lane_bonding_disable(port->remote->sw); tb_switch_remove(port->remote->sw); port->remote = NULL; if (port->dual_link_port) @@ -703,6 +709,21 @@ static int tb_suspend_noirq(struct tb *tb) return 0; } +static void tb_restore_children(struct tb_switch *sw) +{ + struct tb_port *port; + + tb_switch_for_each_port(sw, port) { + if (!tb_port_has_remote(port)) + continue; + + if (tb_switch_lane_bonding_enable(port->remote->sw)) + dev_warn(&sw->dev, "failed to restore lane bonding\n"); + + tb_restore_children(port->remote->sw); + } +} + static int tb_resume_noirq(struct tb *tb) { struct tb_cm *tcm = tb_priv(tb); @@ -716,6 +737,7 @@ static int tb_resume_noirq(struct tb *tb) tb_switch_resume(tb->root_switch); tb_free_invalid_tunnels(tb); tb_free_unplugged_children(tb->root_switch); + tb_restore_children(tb->root_switch); list_for_each_entry_safe(tunnel, n, &tcm->tunnel_list, list) tb_tunnel_restart(tunnel); if (!list_empty(&tcm->tunnel_list)) { diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 4c77d5264660..16d529983004 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -61,6 +61,8 @@ struct tb_switch_nvm { * @device: Device ID of the switch * @vendor_name: Name of the vendor (or %NULL if not known) * @device_name: Name of the device (or %NULL if not known) + * @link_speed: Speed of the link in Gb/s + * @link_width: Width of the link (1 or 2) * @generation: Switch Thunderbolt generation * @cap_plug_events: Offset to the plug events capability (%0 if not found) * @cap_lc: Offset to the link controller capability (%0 if not found) @@ -97,6 +99,8 @@ struct tb_switch { u16 device; const char *vendor_name; const char *device_name; + unsigned int link_speed; + unsigned int link_width; unsigned int generation; int cap_plug_events; int cap_lc; @@ -127,6 +131,7 @@ struct tb_switch { * @cap_adap: Offset of the adapter specific capability (%0 if not present) * @port: Port number on switch * @disabled: Disabled by eeprom + * @bonded: true if the port is bonded (two lanes combined as one) * @dual_link_port: If the switch is connected using two ports, points * to the other port. * @link_nr: Is this primary or secondary port on the dual_link. @@ -142,6 +147,7 @@ struct tb_port { int cap_adap; u8 port; bool disabled; + bool bonded; struct tb_port *dual_link_port; u8 link_nr:1; struct ida in_hopids; @@ -616,6 +622,9 @@ static inline bool tb_switch_is_icm(const struct tb_switch *sw) return !sw->config.enabled; } +int tb_switch_lane_bonding_enable(struct tb_switch *sw); +void tb_switch_lane_bonding_disable(struct tb_switch *sw); + int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged); int tb_port_add_nfc_credits(struct tb_port *port, int credits); int tb_port_set_initial_credits(struct tb_port *port, u32 credits); @@ -659,6 +668,7 @@ int tb_lc_read_uuid(struct tb_switch *sw, u32 *uuid); int tb_lc_configure_link(struct tb_switch *sw); void tb_lc_unconfigure_link(struct tb_switch *sw); int tb_lc_set_sleep(struct tb_switch *sw); +bool tb_lc_lane_bonding_possible(struct tb_switch *sw); static inline int tb_route_length(u64 route) { diff --git a/drivers/thunderbolt/tb_msgs.h b/drivers/thunderbolt/tb_msgs.h index 4b641e4ee0c5..3705057723b6 100644 --- a/drivers/thunderbolt/tb_msgs.h +++ b/drivers/thunderbolt/tb_msgs.h @@ -122,6 +122,8 @@ struct icm_pkg_header { #define ICM_FLAGS_NO_KEY BIT(1) #define ICM_FLAGS_SLEVEL_SHIFT 3 #define ICM_FLAGS_SLEVEL_MASK GENMASK(4, 3) +#define ICM_FLAGS_DUAL_LANE BIT(5) +#define ICM_FLAGS_SPEED_GEN3 BIT(7) #define ICM_FLAGS_WRITE BIT(7) struct icm_pkg_driver_ready { diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h index faa14b3df83c..3a39490a954b 100644 --- a/drivers/thunderbolt/tb_regs.h +++ b/drivers/thunderbolt/tb_regs.h @@ -220,6 +220,23 @@ struct tb_regs_port_header { #define ADP_CS_5_LCA_MASK GENMASK(28, 22) #define ADP_CS_5_LCA_SHIFT 22 +/* Lane adapter registers */ +#define LANE_ADP_CS_0 0x00 +#define LANE_ADP_CS_0_SUPPORTED_WIDTH_MASK GENMASK(25, 20) +#define LANE_ADP_CS_0_SUPPORTED_WIDTH_SHIFT 20 +#define LANE_ADP_CS_1 0x01 +#define LANE_ADP_CS_1_TARGET_WIDTH_MASK GENMASK(9, 4) +#define LANE_ADP_CS_1_TARGET_WIDTH_SHIFT 4 +#define LANE_ADP_CS_1_TARGET_WIDTH_SINGLE 0x1 +#define LANE_ADP_CS_1_TARGET_WIDTH_DUAL 0x3 +#define LANE_ADP_CS_1_LB BIT(15) +#define LANE_ADP_CS_1_CURRENT_SPEED_MASK GENMASK(19, 16) +#define LANE_ADP_CS_1_CURRENT_SPEED_SHIFT 16 +#define LANE_ADP_CS_1_CURRENT_SPEED_GEN2 0x8 +#define LANE_ADP_CS_1_CURRENT_SPEED_GEN3 0x4 +#define LANE_ADP_CS_1_CURRENT_WIDTH_MASK GENMASK(25, 20) +#define LANE_ADP_CS_1_CURRENT_WIDTH_SHIFT 20 + /* Display Port adapter registers */ #define ADP_DP_CS_0 0x00 #define ADP_DP_CS_0_VIDEO_HOPID_MASK GENMASK(26, 16) @@ -277,6 +294,9 @@ struct tb_regs_hop { #define TB_LC_FUSE 0x03 /* Link controller registers */ +#define TB_LC_PORT_ATTR 0x8d +#define TB_LC_PORT_ATTR_BE BIT(12) + #define TB_LC_SX_CTRL 0x96 #define TB_LC_SX_CTRL_L1C BIT(16) #define TB_LC_SX_CTRL_L2C BIT(20) diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c index 382331d71c28..3353396e0806 100644 --- a/drivers/thunderbolt/tunnel.c +++ b/drivers/thunderbolt/tunnel.c @@ -90,6 +90,22 @@ static int tb_pci_activate(struct tb_tunnel *tunnel, bool activate) return 0; } +static int tb_initial_credits(const struct tb_switch *sw) +{ + /* If the path is complete sw is not NULL */ + if (sw) { + /* More credits for faster link */ + switch (sw->link_speed * sw->link_width) { + case 40: + return 32; + case 20: + return 24; + } + } + + return 16; +} + static void tb_pci_init_path(struct tb_path *path) { path->egress_fc_enable = TB_PATH_SOURCE | TB_PATH_INTERNAL; @@ -101,7 +117,8 @@ static void tb_pci_init_path(struct tb_path *path) path->drop_packages = 0; path->nfc_credits = 0; path->hops[0].initial_credits = 7; - path->hops[1].initial_credits = 16; + path->hops[1].initial_credits = + tb_initial_credits(path->hops[1].in_port->sw); } /** From 0d46c08d1ed4f7bb283c7315824f2bfe2c5e0fa9 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 26 Aug 2019 18:19:33 +0300 Subject: [PATCH 083/198] thunderbolt: Add default linking between lane adapters if not provided by DROM We currently read how sibling lane adapter ports relate each other from DROM (Device ROM). If the two lane adapter ports go through the same physical connector these lanes can then be bonded together. However, some cases DROM does not provide this information or it is missing completely (host routers typically do not have DROM). In this case we have hard-coded the relationship. Expand this to work with both legacy devices where lane adapter ports 1 and 2, and 3 and 4 are always linked together, and with USB4 devices where lane adapter 1 is always following lane adapter 0 or is disabled completely (see USB4 section 5.2.1 for more information). Signed-off-by: Mika Westerberg --- drivers/thunderbolt/eeprom.c | 11 ----------- drivers/thunderbolt/switch.c | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/drivers/thunderbolt/eeprom.c b/drivers/thunderbolt/eeprom.c index ee5196479854..8dd7de0cc826 100644 --- a/drivers/thunderbolt/eeprom.c +++ b/drivers/thunderbolt/eeprom.c @@ -514,17 +514,6 @@ int tb_drom_read(struct tb_switch *sw) * no entries). Hardcode the configuration here. */ tb_drom_read_uid_only(sw, &sw->uid); - - sw->ports[1].link_nr = 0; - sw->ports[2].link_nr = 1; - sw->ports[1].dual_link_port = &sw->ports[2]; - sw->ports[2].dual_link_port = &sw->ports[1]; - - sw->ports[3].link_nr = 0; - sw->ports[4].link_nr = 1; - sw->ports[3].dual_link_port = &sw->ports[4]; - sw->ports[4].dual_link_port = &sw->ports[3]; - return 0; } diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index 12a63f86820e..205045caabdc 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -1936,6 +1936,36 @@ static int tb_switch_add_dma_port(struct tb_switch *sw) return -ESHUTDOWN; } +static void tb_switch_default_link_ports(struct tb_switch *sw) +{ + int i; + + for (i = 1; i <= sw->config.max_port_number; i += 2) { + struct tb_port *port = &sw->ports[i]; + struct tb_port *subordinate; + + if (!tb_port_is_null(port)) + continue; + + /* Check for the subordinate port */ + if (i == sw->config.max_port_number || + !tb_port_is_null(&sw->ports[i + 1])) + continue; + + /* Link them if not already done so (by DROM) */ + subordinate = &sw->ports[i + 1]; + if (!port->dual_link_port && !subordinate->dual_link_port) { + port->link_nr = 0; + port->dual_link_port = subordinate; + subordinate->link_nr = 1; + subordinate->dual_link_port = port; + + tb_sw_dbg(sw, "linked ports %d <-> %d\n", + port->port, subordinate->port); + } + } +} + static bool tb_switch_lane_bonding_possible(struct tb_switch *sw) { const struct tb_port *up = tb_upstream_port(sw); @@ -2109,6 +2139,8 @@ int tb_switch_add(struct tb_switch *sw) } } + tb_switch_default_link_ports(sw); + ret = tb_switch_update_link_attributes(sw); if (ret) return ret; From 17a8f815a0df1e164979222ba7ab796b294c1748 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 8 Oct 2019 16:42:47 +0300 Subject: [PATCH 084/198] thunderbolt: Expand controller name in tb_switch_is_xy() For a casual reader tb_switch_is_cr() does not tell much so instead spell out the full controller name in the function name. For example tb_switch_is_cr() becomes tb_switch_is_cactus_ridge() which is easier to understand. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/cap.c | 6 +++--- drivers/thunderbolt/tb.c | 4 ++-- drivers/thunderbolt/tb.h | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/thunderbolt/cap.c b/drivers/thunderbolt/cap.c index 8bf8e031f0bc..fdd77bb4628d 100644 --- a/drivers/thunderbolt/cap.c +++ b/drivers/thunderbolt/cap.c @@ -33,9 +33,9 @@ static int tb_port_enable_tmu(struct tb_port *port, bool enable) * Legacy devices need to have TMU access enabled before port * space can be fully accessed. */ - if (tb_switch_is_lr(sw)) + if (tb_switch_is_light_ridge(sw)) offset = 0x26; - else if (tb_switch_is_er(sw)) + else if (tb_switch_is_eagle_ridge(sw)) offset = 0x2a; else return 0; @@ -60,7 +60,7 @@ static void tb_port_dummy_read(struct tb_port *port) * reading stale data on next read perform one dummy read after * port capabilities are walked. */ - if (tb_switch_is_lr(port->sw)) { + if (tb_switch_is_light_ridge(port->sw)) { u32 dummy; tb_port_read(port, &dummy, TB_CFG_PORT, 0, 1); diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index f2ce6adc1f48..e8e2d20cf4c6 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -342,9 +342,9 @@ static struct tb_port *tb_find_pcie_down(struct tb_switch *sw, * Hard-coded Thunderbolt port to PCIe down port mapping * per controller. */ - if (tb_switch_is_cr(sw)) + if (tb_switch_is_cactus_ridge(sw)) index = !phy_port ? 6 : 7; - else if (tb_switch_is_fr(sw)) + else if (tb_switch_is_falcon_ridge(sw)) index = !phy_port ? 6 : 8; else goto out; diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 16d529983004..a6f1fa0d4771 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -576,17 +576,17 @@ static inline struct tb_switch *tb_switch_parent(struct tb_switch *sw) return tb_to_switch(sw->dev.parent); } -static inline bool tb_switch_is_lr(const struct tb_switch *sw) +static inline bool tb_switch_is_light_ridge(const struct tb_switch *sw) { return sw->config.device_id == PCI_DEVICE_ID_INTEL_LIGHT_RIDGE; } -static inline bool tb_switch_is_er(const struct tb_switch *sw) +static inline bool tb_switch_is_eagle_ridge(const struct tb_switch *sw) { return sw->config.device_id == PCI_DEVICE_ID_INTEL_EAGLE_RIDGE; } -static inline bool tb_switch_is_cr(const struct tb_switch *sw) +static inline bool tb_switch_is_cactus_ridge(const struct tb_switch *sw) { switch (sw->config.device_id) { case PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_2C: @@ -597,7 +597,7 @@ static inline bool tb_switch_is_cr(const struct tb_switch *sw) } } -static inline bool tb_switch_is_fr(const struct tb_switch *sw) +static inline bool tb_switch_is_falcon_ridge(const struct tb_switch *sw) { switch (sw->config.device_id) { case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_BRIDGE: From 7bffd97eb7ab8a67de718bdd626e9fad27ee61b9 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 22 Mar 2019 15:16:53 +0200 Subject: [PATCH 085/198] thunderbolt: Add downstream PCIe port mappings for Alpine and Titan Ridge In order to keep PCIe hierarchies consistent across hotplugs, add hard-coded PCIe downstream port to Thunderbolt port for Alpine Ridge and Titan Ridge as well. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tb.c | 5 ++++- drivers/thunderbolt/tb.h | 25 +++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index e8e2d20cf4c6..c24b577e049e 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -342,10 +342,13 @@ static struct tb_port *tb_find_pcie_down(struct tb_switch *sw, * Hard-coded Thunderbolt port to PCIe down port mapping * per controller. */ - if (tb_switch_is_cactus_ridge(sw)) + if (tb_switch_is_cactus_ridge(sw) || + tb_switch_is_alpine_ridge(sw)) index = !phy_port ? 6 : 7; else if (tb_switch_is_falcon_ridge(sw)) index = !phy_port ? 6 : 8; + else if (tb_switch_is_titan_ridge(sw)) + index = !phy_port ? 8 : 9; else goto out; diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index a6f1fa0d4771..3d7b2202d248 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -608,6 +608,31 @@ static inline bool tb_switch_is_falcon_ridge(const struct tb_switch *sw) } } +static inline bool tb_switch_is_alpine_ridge(const struct tb_switch *sw) +{ + switch (sw->config.device_id) { + case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_2C_BRIDGE: + case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_LP_BRIDGE: + case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_4C_BRIDGE: + case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_BRIDGE: + return true; + default: + return false; + } +} + +static inline bool tb_switch_is_titan_ridge(const struct tb_switch *sw) +{ + switch (sw->config.device_id) { + case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_BRIDGE: + case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_BRIDGE: + case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_DD_BRIDGE: + return true; + default: + return false; + } +} + /** * tb_switch_is_icm() - Is the switch handled by ICM firmware * @sw: Switch to check From de718ac7b6aefa594d5d95881882bc68ec3b83b6 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 15 Feb 2019 18:18:47 +0200 Subject: [PATCH 086/198] thunderbolt: Add Display Port CM handshake for Titan Ridge devices Titan Ridge needs an additional connection manager handshake in order to do proper Display Port tunneling so implement it here. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tb_regs.h | 3 +++ drivers/thunderbolt/tunnel.c | 45 +++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h index 3a39490a954b..8d11b4a2d552 100644 --- a/drivers/thunderbolt/tb_regs.h +++ b/drivers/thunderbolt/tb_regs.h @@ -252,6 +252,9 @@ struct tb_regs_port_header { #define ADP_DP_CS_3_HDPC BIT(9) #define DP_LOCAL_CAP 0x04 #define DP_REMOTE_CAP 0x05 +#define DP_STATUS_CTRL 0x06 +#define DP_STATUS_CTRL_CMHS BIT(25) +#define DP_STATUS_CTRL_UF BIT(26) /* PCIe adapter registers */ #define ADP_PCIE_CS_0 0x00 diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c index 3353396e0806..009c2683a386 100644 --- a/drivers/thunderbolt/tunnel.c +++ b/drivers/thunderbolt/tunnel.c @@ -6,6 +6,7 @@ * Copyright (C) 2019, Intel Corporation */ +#include #include #include @@ -242,6 +243,42 @@ struct tb_tunnel *tb_tunnel_alloc_pci(struct tb *tb, struct tb_port *up, return tunnel; } +static int tb_dp_cm_handshake(struct tb_port *in, struct tb_port *out) +{ + int timeout = 10; + u32 val; + int ret; + + /* Both ends need to support this */ + if (!tb_switch_is_titan_ridge(in->sw) || + !tb_switch_is_titan_ridge(out->sw)) + return 0; + + ret = tb_port_read(out, &val, TB_CFG_PORT, + out->cap_adap + DP_STATUS_CTRL, 1); + if (ret) + return ret; + + val |= DP_STATUS_CTRL_UF | DP_STATUS_CTRL_CMHS; + + ret = tb_port_write(out, &val, TB_CFG_PORT, + out->cap_adap + DP_STATUS_CTRL, 1); + if (ret) + return ret; + + do { + ret = tb_port_read(out, &val, TB_CFG_PORT, + out->cap_adap + DP_STATUS_CTRL, 1); + if (ret) + return ret; + if (!(val & DP_STATUS_CTRL_CMHS)) + return 0; + usleep_range(10, 100); + } while (timeout--); + + return -ETIMEDOUT; +} + static int tb_dp_xchg_caps(struct tb_tunnel *tunnel) { struct tb_port *out = tunnel->dst_port; @@ -256,6 +293,14 @@ static int tb_dp_xchg_caps(struct tb_tunnel *tunnel) if (in->sw->generation < 2 || out->sw->generation < 2) return 0; + /* + * Perform connection manager handshake between IN and OUT ports + * before capabilities exchange can take place. + */ + ret = tb_dp_cm_handshake(in, out); + if (ret) + return ret; + /* Read both DP_LOCAL_CAP registers */ ret = tb_port_read(in, &in_dp_cap, TB_CFG_PORT, in->cap_adap + DP_LOCAL_CAP, 1); From 8afe909b78e16ee4baecf78fd4e404aabf425f8c Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 26 Mar 2019 15:52:30 +0300 Subject: [PATCH 087/198] thunderbolt: Add Display Port adapter pairing and resource management To perform proper Display Port tunneling for Thunderbolt 3 devices we need to allocate DP resources for DP IN port before they can be used. The reason for this is that the user can also connect a monitor directly to the Type-C ports in which case the Thunderbolt controller acts as re-driver for Display Port (no tunneling takes place) taking the DP sinks away from the connection manager. This allocation is done using special sink allocation registers available through the link controller. We can pair DP IN to DP OUT only if * DP IN has sink allocated via link controller * DP OUT port receives hotplug event For DP IN adapters (only for the host router) we first query whether there is DP resource available (it may be the previous instance of the driver for example already allocated it) and if it is we add it to the list. We then update the list when after each plug/unplug event to a DP IN/OUT adapter. Each time the list is updated we try to find additional DP IN <-> DP OUT pairs for tunnel establishment. This strategy also makes it possible to establish another tunnel in case there are 3 monitors connected and one gets unplugged releasing the DP IN adapter for the new tunnel. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/lc.c | 161 +++++++++++++++++++++++++++ drivers/thunderbolt/switch.c | 44 ++++++++ drivers/thunderbolt/tb.c | 201 ++++++++++++++++++++++++++++------ drivers/thunderbolt/tb.h | 9 ++ drivers/thunderbolt/tb_regs.h | 6 + 5 files changed, 387 insertions(+), 34 deletions(-) diff --git a/drivers/thunderbolt/lc.c b/drivers/thunderbolt/lc.c index df56523eb822..bd44d50246d2 100644 --- a/drivers/thunderbolt/lc.c +++ b/drivers/thunderbolt/lc.c @@ -205,3 +205,164 @@ bool tb_lc_lane_bonding_possible(struct tb_switch *sw) return !!(val & TB_LC_PORT_ATTR_BE); } + +static int tb_lc_dp_sink_from_port(const struct tb_switch *sw, + struct tb_port *in) +{ + struct tb_port *port; + + /* The first DP IN port is sink 0 and second is sink 1 */ + tb_switch_for_each_port(sw, port) { + if (tb_port_is_dpin(port)) + return in != port; + } + + return -EINVAL; +} + +static int tb_lc_dp_sink_available(struct tb_switch *sw, int sink) +{ + u32 val, alloc; + int ret; + + ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, + sw->cap_lc + TB_LC_SNK_ALLOCATION, 1); + if (ret) + return ret; + + /* + * Sink is available for CM/SW to use if the allocation valie is + * either 0 or 1. + */ + if (!sink) { + alloc = val & TB_LC_SNK_ALLOCATION_SNK0_MASK; + if (!alloc || alloc == TB_LC_SNK_ALLOCATION_SNK0_CM) + return 0; + } else { + alloc = (val & TB_LC_SNK_ALLOCATION_SNK1_MASK) >> + TB_LC_SNK_ALLOCATION_SNK1_SHIFT; + if (!alloc || alloc == TB_LC_SNK_ALLOCATION_SNK1_CM) + return 0; + } + + return -EBUSY; +} + +/** + * tb_lc_dp_sink_query() - Is DP sink available for DP IN port + * @sw: Switch whose DP sink is queried + * @in: DP IN port to check + * + * Queries through LC SNK_ALLOCATION registers whether DP sink is available + * for the given DP IN port or not. + */ +bool tb_lc_dp_sink_query(struct tb_switch *sw, struct tb_port *in) +{ + int sink; + + /* + * For older generations sink is always available as there is no + * allocation mechanism. + */ + if (sw->generation < 3) + return true; + + sink = tb_lc_dp_sink_from_port(sw, in); + if (sink < 0) + return false; + + return !tb_lc_dp_sink_available(sw, sink); +} + +/** + * tb_lc_dp_sink_alloc() - Allocate DP sink + * @sw: Switch whose DP sink is allocated + * @in: DP IN port the DP sink is allocated for + * + * Allocate DP sink for @in via LC SNK_ALLOCATION registers. If the + * resource is available and allocation is successful returns %0. In all + * other cases returs negative errno. In particular %-EBUSY is returned if + * the resource was not available. + */ +int tb_lc_dp_sink_alloc(struct tb_switch *sw, struct tb_port *in) +{ + int ret, sink; + u32 val; + + if (sw->generation < 3) + return 0; + + sink = tb_lc_dp_sink_from_port(sw, in); + if (sink < 0) + return sink; + + ret = tb_lc_dp_sink_available(sw, sink); + if (ret) + return ret; + + ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, + sw->cap_lc + TB_LC_SNK_ALLOCATION, 1); + if (ret) + return ret; + + if (!sink) { + val &= ~TB_LC_SNK_ALLOCATION_SNK0_MASK; + val |= TB_LC_SNK_ALLOCATION_SNK0_CM; + } else { + val &= ~TB_LC_SNK_ALLOCATION_SNK1_MASK; + val |= TB_LC_SNK_ALLOCATION_SNK1_CM << + TB_LC_SNK_ALLOCATION_SNK1_SHIFT; + } + + ret = tb_sw_write(sw, &val, TB_CFG_SWITCH, + sw->cap_lc + TB_LC_SNK_ALLOCATION, 1); + + if (ret) + return ret; + + tb_port_dbg(in, "sink %d allocated\n", sink); + return 0; +} + +/** + * tb_lc_dp_sink_dealloc() - De-allocate DP sink + * @sw: Switch whose DP sink is de-allocated + * @in: DP IN port whose DP sink is de-allocated + * + * De-allocate DP sink from @in using LC SNK_ALLOCATION registers. + */ +int tb_lc_dp_sink_dealloc(struct tb_switch *sw, struct tb_port *in) +{ + int ret, sink; + u32 val; + + if (sw->generation < 3) + return 0; + + sink = tb_lc_dp_sink_from_port(sw, in); + if (sink < 0) + return sink; + + /* Needs to be owned by CM/SW */ + ret = tb_lc_dp_sink_available(sw, sink); + if (ret) + return ret; + + ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, + sw->cap_lc + TB_LC_SNK_ALLOCATION, 1); + if (ret) + return ret; + + if (!sink) + val &= ~TB_LC_SNK_ALLOCATION_SNK0_MASK; + else + val &= ~TB_LC_SNK_ALLOCATION_SNK1_MASK; + + ret = tb_sw_write(sw, &val, TB_CFG_SWITCH, + sw->cap_lc + TB_LC_SNK_ALLOCATION, 1); + if (ret) + return ret; + + tb_port_dbg(in, "sink %d de-allocated\n", sink); + return 0; +} diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index 205045caabdc..3f477df2730a 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -645,6 +645,7 @@ static int tb_init_port(struct tb_port *port) ida_init(&port->out_hopids); } + INIT_LIST_HEAD(&port->list); return 0; } @@ -2333,6 +2334,49 @@ void tb_switch_suspend(struct tb_switch *sw) tb_lc_set_sleep(sw); } +/** + * tb_switch_query_dp_resource() - Query availability of DP resource + * @sw: Switch whose DP resource is queried + * @in: DP IN port + * + * Queries availability of DP resource for DP tunneling using switch + * specific means. Returns %true if resource is available. + */ +bool tb_switch_query_dp_resource(struct tb_switch *sw, struct tb_port *in) +{ + return tb_lc_dp_sink_query(sw, in); +} + +/** + * tb_switch_alloc_dp_resource() - Allocate available DP resource + * @sw: Switch whose DP resource is allocated + * @in: DP IN port + * + * Allocates DP resource for DP tunneling. The resource must be + * available for this to succeed (see tb_switch_query_dp_resource()). + * Returns %0 in success and negative errno otherwise. + */ +int tb_switch_alloc_dp_resource(struct tb_switch *sw, struct tb_port *in) +{ + return tb_lc_dp_sink_alloc(sw, in); +} + +/** + * tb_switch_dealloc_dp_resource() - De-allocate DP resource + * @sw: Switch whose DP resource is de-allocated + * @in: DP IN port + * + * De-allocates DP resource that was previously allocated for DP + * tunneling. + */ +void tb_switch_dealloc_dp_resource(struct tb_switch *sw, struct tb_port *in) +{ + if (tb_lc_dp_sink_dealloc(sw, in)) { + tb_sw_warn(sw, "failed to de-allocate DP resource for port %d\n", + in->port); + } +} + struct tb_sw_lookup { struct tb *tb; u8 link; diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index c24b577e049e..8f58b9c3ef07 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -18,6 +18,7 @@ /** * struct tb_cm - Simple Thunderbolt connection manager * @tunnel_list: List of active tunnels + * @dp_resources: List of available DP resources for DP tunneling * @hotplug_active: tb_handle_hotplug will stop progressing plug * events and exit if this is not set (it needs to * acquire the lock one more time). Used to drain wq @@ -25,6 +26,7 @@ */ struct tb_cm { struct list_head tunnel_list; + struct list_head dp_resources; bool hotplug_active; }; @@ -56,6 +58,42 @@ static void tb_queue_hotplug(struct tb *tb, u64 route, u8 port, bool unplug) /* enumeration & hot plug handling */ +static void tb_add_dp_resources(struct tb_switch *sw) +{ + struct tb_cm *tcm = tb_priv(sw->tb); + struct tb_port *port; + + tb_switch_for_each_port(sw, port) { + if (!tb_port_is_dpin(port)) + continue; + + if (!tb_switch_query_dp_resource(sw, port)) + continue; + + list_add_tail(&port->list, &tcm->dp_resources); + tb_port_dbg(port, "DP IN resource available\n"); + } +} + +static void tb_remove_dp_resources(struct tb_switch *sw) +{ + struct tb_cm *tcm = tb_priv(sw->tb); + struct tb_port *port, *tmp; + + /* Clear children resources first */ + tb_switch_for_each_port(sw, port) { + if (tb_port_has_remote(port)) + tb_remove_dp_resources(port->remote->sw); + } + + list_for_each_entry_safe(port, tmp, &tcm->dp_resources, list) { + if (port->sw == sw) { + tb_port_dbg(port, "DP OUT resource unavailable\n"); + list_del_init(&port->list); + } + } +} + static void tb_discover_tunnels(struct tb_switch *sw) { struct tb *tb = sw->tb; @@ -223,8 +261,9 @@ static void tb_scan_port(struct tb_port *port) tb_scan_switch(sw); } -static int tb_free_tunnel(struct tb *tb, enum tb_tunnel_type type, - struct tb_port *src_port, struct tb_port *dst_port) +static struct tb_tunnel *tb_find_tunnel(struct tb *tb, enum tb_tunnel_type type, + struct tb_port *src_port, + struct tb_port *dst_port) { struct tb_cm *tcm = tb_priv(tb); struct tb_tunnel *tunnel; @@ -233,14 +272,32 @@ static int tb_free_tunnel(struct tb *tb, enum tb_tunnel_type type, if (tunnel->type == type && ((src_port && src_port == tunnel->src_port) || (dst_port && dst_port == tunnel->dst_port))) { - tb_tunnel_deactivate(tunnel); - list_del(&tunnel->list); - tb_tunnel_free(tunnel); - return 0; + return tunnel; } } - return -ENODEV; + return NULL; +} + +static void tb_deactivate_and_free_tunnel(struct tb_tunnel *tunnel) +{ + if (!tunnel) + return; + + tb_tunnel_deactivate(tunnel); + list_del(&tunnel->list); + + /* + * In case of DP tunnel make sure the DP IN resource is deallocated + * properly. + */ + if (tb_tunnel_is_dp(tunnel)) { + struct tb_port *in = tunnel->src_port; + + tb_switch_dealloc_dp_resource(in->sw, in); + } + + tb_tunnel_free(tunnel); } /** @@ -253,11 +310,8 @@ static void tb_free_invalid_tunnels(struct tb *tb) struct tb_tunnel *n; list_for_each_entry_safe(tunnel, n, &tcm->tunnel_list, list) { - if (tb_tunnel_is_invalid(tunnel)) { - tb_tunnel_deactivate(tunnel); - list_del(&tunnel->list); - tb_tunnel_free(tunnel); - } + if (tb_tunnel_is_invalid(tunnel)) + tb_deactivate_and_free_tunnel(tunnel); } } @@ -273,6 +327,7 @@ static void tb_free_unplugged_children(struct tb_switch *sw) continue; if (port->remote->sw->is_unplugged) { + tb_remove_dp_resources(port->remote->sw); tb_switch_lane_bonding_disable(port->remote->sw); tb_switch_remove(port->remote->sw); port->remote = NULL; @@ -367,42 +422,112 @@ out: return tb_find_unused_port(sw, TB_TYPE_PCIE_DOWN); } -static int tb_tunnel_dp(struct tb *tb, struct tb_port *out) +static void tb_tunnel_dp(struct tb *tb) { struct tb_cm *tcm = tb_priv(tb); - struct tb_switch *sw = out->sw; + struct tb_port *port, *in, *out; struct tb_tunnel *tunnel; - struct tb_port *in; - if (tb_port_is_enabled(out)) - return 0; + /* + * Find pair of inactive DP IN and DP OUT adapters and then + * establish a DP tunnel between them. + */ + tb_dbg(tb, "looking for DP IN <-> DP OUT pairs:\n"); - do { - sw = tb_to_switch(sw->dev.parent); - if (!sw) - return 0; - in = tb_find_unused_port(sw, TB_TYPE_DP_HDMI_IN); - } while (!in); + in = NULL; + out = NULL; + list_for_each_entry(port, &tcm->dp_resources, list) { + if (tb_port_is_enabled(port)) { + tb_port_dbg(port, "in use\n"); + continue; + } + + tb_port_dbg(port, "available\n"); + + if (!in && tb_port_is_dpin(port)) + in = port; + else if (!out && tb_port_is_dpout(port)) + out = port; + } + + if (!in) { + tb_dbg(tb, "no suitable DP IN adapter available, not tunneling\n"); + return; + } + if (!out) { + tb_dbg(tb, "no suitable DP OUT adapter available, not tunneling\n"); + return; + } + + if (tb_switch_alloc_dp_resource(in->sw, in)) { + tb_port_dbg(in, "no resource available for DP IN, not tunneling\n"); + return; + } tunnel = tb_tunnel_alloc_dp(tb, in, out); if (!tunnel) { - tb_port_dbg(out, "DP tunnel allocation failed\n"); - return -ENOMEM; + tb_port_dbg(out, "could not allocate DP tunnel\n"); + goto dealloc_dp; } if (tb_tunnel_activate(tunnel)) { tb_port_info(out, "DP tunnel activation failed, aborting\n"); tb_tunnel_free(tunnel); - return -EIO; + goto dealloc_dp; } list_add_tail(&tunnel->list, &tcm->tunnel_list); - return 0; + return; + +dealloc_dp: + tb_switch_dealloc_dp_resource(in->sw, in); } -static void tb_teardown_dp(struct tb *tb, struct tb_port *out) +static void tb_dp_resource_unavailable(struct tb *tb, struct tb_port *port) { - tb_free_tunnel(tb, TB_TUNNEL_DP, NULL, out); + struct tb_port *in, *out; + struct tb_tunnel *tunnel; + + if (tb_port_is_dpin(port)) { + tb_port_dbg(port, "DP IN resource unavailable\n"); + in = port; + out = NULL; + } else { + tb_port_dbg(port, "DP OUT resource unavailable\n"); + in = NULL; + out = port; + } + + tunnel = tb_find_tunnel(tb, TB_TUNNEL_DP, in, out); + tb_deactivate_and_free_tunnel(tunnel); + list_del_init(&port->list); + + /* + * See if there is another DP OUT port that can be used for + * to create another tunnel. + */ + tb_tunnel_dp(tb); +} + +static void tb_dp_resource_available(struct tb *tb, struct tb_port *port) +{ + struct tb_cm *tcm = tb_priv(tb); + struct tb_port *p; + + if (tb_port_is_enabled(port)) + return; + + list_for_each_entry(p, &tcm->dp_resources, list) { + if (p == port) + return; + } + + tb_port_dbg(port, "DP %s resource available\n", + tb_port_is_dpin(port) ? "IN" : "OUT"); + list_add_tail(&port->list, &tcm->dp_resources); + + /* Look for suitable DP IN <-> DP OUT pairs now */ + tb_tunnel_dp(tb); } static int tb_tunnel_pci(struct tb *tb, struct tb_switch *sw) @@ -477,6 +602,7 @@ static int tb_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd) static void __tb_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd) { struct tb_port *dst_port; + struct tb_tunnel *tunnel; struct tb_switch *sw; sw = tb_to_switch(xd->dev.parent); @@ -487,7 +613,8 @@ static void __tb_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd) * case of cable disconnect) so it is fine if we cannot find it * here anymore. */ - tb_free_tunnel(tb, TB_TUNNEL_DMA, NULL, dst_port); + tunnel = tb_find_tunnel(tb, TB_TUNNEL_DMA, NULL, dst_port); + tb_deactivate_and_free_tunnel(tunnel); } static int tb_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd) @@ -542,11 +669,14 @@ static void tb_handle_hotplug(struct work_struct *work) tb_port_dbg(port, "switch unplugged\n"); tb_sw_set_unplugged(port->remote->sw); tb_free_invalid_tunnels(tb); + tb_remove_dp_resources(port->remote->sw); tb_switch_lane_bonding_disable(port->remote->sw); tb_switch_remove(port->remote->sw); port->remote = NULL; if (port->dual_link_port) port->dual_link_port->remote = NULL; + /* Maybe we can create another DP tunnel */ + tb_tunnel_dp(tb); } else if (port->xdomain) { struct tb_xdomain *xd = tb_xdomain_get(port->xdomain); @@ -563,8 +693,8 @@ static void tb_handle_hotplug(struct work_struct *work) port->xdomain = NULL; __tb_disconnect_xdomain_paths(tb, xd); tb_xdomain_put(xd); - } else if (tb_port_is_dpout(port)) { - tb_teardown_dp(tb, port); + } else if (tb_port_is_dpout(port) || tb_port_is_dpin(port)) { + tb_dp_resource_unavailable(tb, port); } else { tb_port_dbg(port, "got unplug event for disconnected port, ignoring\n"); @@ -577,8 +707,8 @@ static void tb_handle_hotplug(struct work_struct *work) tb_scan_port(port); if (!port->remote) tb_port_dbg(port, "hotplug: no switch found\n"); - } else if (tb_port_is_dpout(port)) { - tb_tunnel_dp(tb, port); + } else if (tb_port_is_dpout(port) || tb_port_is_dpin(port)) { + tb_dp_resource_available(tb, port); } } @@ -691,6 +821,8 @@ static int tb_start(struct tb *tb) tb_scan_switch(tb->root_switch); /* Find out tunnels created by the boot firmware */ tb_discover_tunnels(tb->root_switch); + /* Add DP IN resources for the root switch */ + tb_add_dp_resources(tb->root_switch); /* Make the discovered switches available to the userspace */ device_for_each_child(&tb->root_switch->dev, NULL, tb_scan_finalize_switch); @@ -820,6 +952,7 @@ struct tb *tb_probe(struct tb_nhi *nhi) tcm = tb_priv(tb); INIT_LIST_HEAD(&tcm->tunnel_list); + INIT_LIST_HEAD(&tcm->dp_resources); return tb; } diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 3d7b2202d248..5311e6e3f3df 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -137,6 +137,7 @@ struct tb_switch { * @link_nr: Is this primary or secondary port on the dual_link. * @in_hopids: Currently allocated input HopIDs * @out_hopids: Currently allocated output HopIDs + * @list: Used to link ports to DP resources list */ struct tb_port { struct tb_regs_port_header config; @@ -152,6 +153,7 @@ struct tb_port { u8 link_nr:1; struct ida in_hopids; struct ida out_hopids; + struct list_head list; }; /** @@ -650,6 +652,10 @@ static inline bool tb_switch_is_icm(const struct tb_switch *sw) int tb_switch_lane_bonding_enable(struct tb_switch *sw); void tb_switch_lane_bonding_disable(struct tb_switch *sw); +bool tb_switch_query_dp_resource(struct tb_switch *sw, struct tb_port *in); +int tb_switch_alloc_dp_resource(struct tb_switch *sw, struct tb_port *in); +void tb_switch_dealloc_dp_resource(struct tb_switch *sw, struct tb_port *in); + int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged); int tb_port_add_nfc_credits(struct tb_port *port, int credits); int tb_port_set_initial_credits(struct tb_port *port, u32 credits); @@ -694,6 +700,9 @@ int tb_lc_configure_link(struct tb_switch *sw); void tb_lc_unconfigure_link(struct tb_switch *sw); int tb_lc_set_sleep(struct tb_switch *sw); bool tb_lc_lane_bonding_possible(struct tb_switch *sw); +bool tb_lc_dp_sink_query(struct tb_switch *sw, struct tb_port *in); +int tb_lc_dp_sink_alloc(struct tb_switch *sw, struct tb_port *in); +int tb_lc_dp_sink_dealloc(struct tb_switch *sw, struct tb_port *in); static inline int tb_route_length(u64 route) { diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h index 8d11b4a2d552..aec35e61cc14 100644 --- a/drivers/thunderbolt/tb_regs.h +++ b/drivers/thunderbolt/tb_regs.h @@ -295,6 +295,12 @@ struct tb_regs_hop { #define TB_LC_DESC_PORT_SIZE_SHIFT 16 #define TB_LC_DESC_PORT_SIZE_MASK GENMASK(27, 16) #define TB_LC_FUSE 0x03 +#define TB_LC_SNK_ALLOCATION 0x10 +#define TB_LC_SNK_ALLOCATION_SNK0_MASK GENMASK(3, 0) +#define TB_LC_SNK_ALLOCATION_SNK0_CM 0x1 +#define TB_LC_SNK_ALLOCATION_SNK1_SHIFT 4 +#define TB_LC_SNK_ALLOCATION_SNK1_MASK GENMASK(7, 4) +#define TB_LC_SNK_ALLOCATION_SNK1_CM 0x1 /* Link controller registers */ #define TB_LC_PORT_ATTR 0x8d From a11b88add4401d006ab593c525c0dddc8ace7655 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 26 Mar 2019 16:03:48 +0300 Subject: [PATCH 088/198] thunderbolt: Add bandwidth management for Display Port tunnels Titan Ridge supports Display Port 1.4 which adds HBR3 (High Bit Rate) rates that may be up to 8.1 Gb/s over 4 lanes. This translates to effective data bandwidth of 25.92 Gb/s (as 8/10 encoding is removed by the DP adapters when going over Thunderbolt fabric). If another high rate monitor is connected we may need to reduce the bandwidth it consumes so that it fits into the total 40 Gb/s available on the Thunderbolt fabric. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/path.c | 22 +++ drivers/thunderbolt/tb.c | 52 ++++++- drivers/thunderbolt/tb.h | 2 + drivers/thunderbolt/tb_regs.h | 17 ++ drivers/thunderbolt/tunnel.c | 282 +++++++++++++++++++++++++++++++++- drivers/thunderbolt/tunnel.h | 10 +- 6 files changed, 381 insertions(+), 4 deletions(-) diff --git a/drivers/thunderbolt/path.c b/drivers/thunderbolt/path.c index 6cf66597d5d8..ad58559ea88e 100644 --- a/drivers/thunderbolt/path.c +++ b/drivers/thunderbolt/path.c @@ -557,3 +557,25 @@ bool tb_path_is_invalid(struct tb_path *path) } return false; } + +/** + * tb_path_switch_on_path() - Does the path go through certain switch + * @path: Path to check + * @sw: Switch to check + * + * Goes over all hops on path and checks if @sw is any of them. + * Direction does not matter. + */ +bool tb_path_switch_on_path(const struct tb_path *path, + const struct tb_switch *sw) +{ + int i; + + for (i = 0; i < path->path_length; i++) { + if (path->hops[i].in_port->sw == sw || + path->hops[i].out_port->sw == sw) + return true; + } + + return false; +} diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index 8f58b9c3ef07..bb763a5cf103 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -422,11 +422,51 @@ out: return tb_find_unused_port(sw, TB_TYPE_PCIE_DOWN); } +static int tb_available_bw(struct tb_cm *tcm, struct tb_port *in, + struct tb_port *out) +{ + struct tb_switch *sw = out->sw; + struct tb_tunnel *tunnel; + int bw, available_bw = 40000; + + while (sw && sw != in->sw) { + bw = sw->link_speed * sw->link_width * 1000; /* Mb/s */ + /* Leave 10% guard band */ + bw -= bw / 10; + + /* + * Check for any active DP tunnels that go through this + * switch and reduce their consumed bandwidth from + * available. + */ + list_for_each_entry(tunnel, &tcm->tunnel_list, list) { + int consumed_bw; + + if (!tb_tunnel_switch_on_path(tunnel, sw)) + continue; + + consumed_bw = tb_tunnel_consumed_bandwidth(tunnel); + if (consumed_bw < 0) + return consumed_bw; + + bw -= consumed_bw; + } + + if (bw < available_bw) + available_bw = bw; + + sw = tb_switch_parent(sw); + } + + return available_bw; +} + static void tb_tunnel_dp(struct tb *tb) { struct tb_cm *tcm = tb_priv(tb); struct tb_port *port, *in, *out; struct tb_tunnel *tunnel; + int available_bw; /* * Find pair of inactive DP IN and DP OUT adapters and then @@ -464,7 +504,17 @@ static void tb_tunnel_dp(struct tb *tb) return; } - tunnel = tb_tunnel_alloc_dp(tb, in, out); + /* Calculate available bandwidth between in and out */ + available_bw = tb_available_bw(tcm, in, out); + if (available_bw < 0) { + tb_warn(tb, "failed to determine available bandwidth\n"); + return; + } + + tb_dbg(tb, "available bandwidth for new DP tunnel %u Mb/s\n", + available_bw); + + tunnel = tb_tunnel_alloc_dp(tb, in, out, available_bw); if (!tunnel) { tb_port_dbg(out, "could not allocate DP tunnel\n"); goto dealloc_dp; diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 5311e6e3f3df..ec851f20c571 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -691,6 +691,8 @@ void tb_path_free(struct tb_path *path); int tb_path_activate(struct tb_path *path); void tb_path_deactivate(struct tb_path *path); bool tb_path_is_invalid(struct tb_path *path); +bool tb_path_switch_on_path(const struct tb_path *path, + const struct tb_switch *sw); int tb_drom_read(struct tb_switch *sw); int tb_drom_read_uid_only(struct tb_switch *sw, u64 *uid); diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h index aec35e61cc14..7ee45b73c7f7 100644 --- a/drivers/thunderbolt/tb_regs.h +++ b/drivers/thunderbolt/tb_regs.h @@ -255,6 +255,23 @@ struct tb_regs_port_header { #define DP_STATUS_CTRL 0x06 #define DP_STATUS_CTRL_CMHS BIT(25) #define DP_STATUS_CTRL_UF BIT(26) +#define DP_COMMON_CAP 0x07 +/* + * DP_COMMON_CAP offsets work also for DP_LOCAL_CAP and DP_REMOTE_CAP + * with exception of DPRX done. + */ +#define DP_COMMON_CAP_RATE_MASK GENMASK(11, 8) +#define DP_COMMON_CAP_RATE_SHIFT 8 +#define DP_COMMON_CAP_RATE_RBR 0x0 +#define DP_COMMON_CAP_RATE_HBR 0x1 +#define DP_COMMON_CAP_RATE_HBR2 0x2 +#define DP_COMMON_CAP_RATE_HBR3 0x3 +#define DP_COMMON_CAP_LANES_MASK GENMASK(14, 12) +#define DP_COMMON_CAP_LANES_SHIFT 12 +#define DP_COMMON_CAP_1_LANE 0x0 +#define DP_COMMON_CAP_2_LANES 0x1 +#define DP_COMMON_CAP_4_LANES 0x2 +#define DP_COMMON_CAP_DPRX_DONE BIT(31) /* PCIe adapter registers */ #define ADP_PCIE_CS_0 0x00 diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c index 009c2683a386..0d3463c4e24a 100644 --- a/drivers/thunderbolt/tunnel.c +++ b/drivers/thunderbolt/tunnel.c @@ -279,11 +279,138 @@ static int tb_dp_cm_handshake(struct tb_port *in, struct tb_port *out) return -ETIMEDOUT; } +static inline u32 tb_dp_cap_get_rate(u32 val) +{ + u32 rate = (val & DP_COMMON_CAP_RATE_MASK) >> DP_COMMON_CAP_RATE_SHIFT; + + switch (rate) { + case DP_COMMON_CAP_RATE_RBR: + return 1620; + case DP_COMMON_CAP_RATE_HBR: + return 2700; + case DP_COMMON_CAP_RATE_HBR2: + return 5400; + case DP_COMMON_CAP_RATE_HBR3: + return 8100; + default: + return 0; + } +} + +static inline u32 tb_dp_cap_set_rate(u32 val, u32 rate) +{ + val &= ~DP_COMMON_CAP_RATE_MASK; + switch (rate) { + default: + WARN(1, "invalid rate %u passed, defaulting to 1620 MB/s\n", rate); + /* Fallthrough */ + case 1620: + val |= DP_COMMON_CAP_RATE_RBR << DP_COMMON_CAP_RATE_SHIFT; + break; + case 2700: + val |= DP_COMMON_CAP_RATE_HBR << DP_COMMON_CAP_RATE_SHIFT; + break; + case 5400: + val |= DP_COMMON_CAP_RATE_HBR2 << DP_COMMON_CAP_RATE_SHIFT; + break; + case 8100: + val |= DP_COMMON_CAP_RATE_HBR3 << DP_COMMON_CAP_RATE_SHIFT; + break; + } + return val; +} + +static inline u32 tb_dp_cap_get_lanes(u32 val) +{ + u32 lanes = (val & DP_COMMON_CAP_LANES_MASK) >> DP_COMMON_CAP_LANES_SHIFT; + + switch (lanes) { + case DP_COMMON_CAP_1_LANE: + return 1; + case DP_COMMON_CAP_2_LANES: + return 2; + case DP_COMMON_CAP_4_LANES: + return 4; + default: + return 0; + } +} + +static inline u32 tb_dp_cap_set_lanes(u32 val, u32 lanes) +{ + val &= ~DP_COMMON_CAP_LANES_MASK; + switch (lanes) { + default: + WARN(1, "invalid number of lanes %u passed, defaulting to 1\n", + lanes); + /* Fallthrough */ + case 1: + val |= DP_COMMON_CAP_1_LANE << DP_COMMON_CAP_LANES_SHIFT; + break; + case 2: + val |= DP_COMMON_CAP_2_LANES << DP_COMMON_CAP_LANES_SHIFT; + break; + case 4: + val |= DP_COMMON_CAP_4_LANES << DP_COMMON_CAP_LANES_SHIFT; + break; + } + return val; +} + +static unsigned int tb_dp_bandwidth(unsigned int rate, unsigned int lanes) +{ + /* Tunneling removes the DP 8b/10b encoding */ + return rate * lanes * 8 / 10; +} + +static int tb_dp_reduce_bandwidth(int max_bw, u32 in_rate, u32 in_lanes, + u32 out_rate, u32 out_lanes, u32 *new_rate, + u32 *new_lanes) +{ + static const u32 dp_bw[][2] = { + /* Mb/s, lanes */ + { 8100, 4 }, /* 25920 Mb/s */ + { 5400, 4 }, /* 17280 Mb/s */ + { 8100, 2 }, /* 12960 Mb/s */ + { 2700, 4 }, /* 8640 Mb/s */ + { 5400, 2 }, /* 8640 Mb/s */ + { 8100, 1 }, /* 6480 Mb/s */ + { 1620, 4 }, /* 5184 Mb/s */ + { 5400, 1 }, /* 4320 Mb/s */ + { 2700, 2 }, /* 4320 Mb/s */ + { 1620, 2 }, /* 2592 Mb/s */ + { 2700, 1 }, /* 2160 Mb/s */ + { 1620, 1 }, /* 1296 Mb/s */ + }; + unsigned int i; + + /* + * Find a combination that can fit into max_bw and does not + * exceed the maximum rate and lanes supported by the DP OUT and + * DP IN adapters. + */ + for (i = 0; i < ARRAY_SIZE(dp_bw); i++) { + if (dp_bw[i][0] > out_rate || dp_bw[i][1] > out_lanes) + continue; + + if (dp_bw[i][0] > in_rate || dp_bw[i][1] > in_lanes) + continue; + + if (tb_dp_bandwidth(dp_bw[i][0], dp_bw[i][1]) <= max_bw) { + *new_rate = dp_bw[i][0]; + *new_lanes = dp_bw[i][1]; + return 0; + } + } + + return -ENOSR; +} + static int tb_dp_xchg_caps(struct tb_tunnel *tunnel) { + u32 out_dp_cap, out_rate, out_lanes, in_dp_cap, in_rate, in_lanes, bw; struct tb_port *out = tunnel->dst_port; struct tb_port *in = tunnel->src_port; - u32 in_dp_cap, out_dp_cap; int ret; /* @@ -318,6 +445,44 @@ static int tb_dp_xchg_caps(struct tb_tunnel *tunnel) if (ret) return ret; + in_rate = tb_dp_cap_get_rate(in_dp_cap); + in_lanes = tb_dp_cap_get_lanes(in_dp_cap); + tb_port_dbg(in, "maximum supported bandwidth %u Mb/s x%u = %u Mb/s\n", + in_rate, in_lanes, tb_dp_bandwidth(in_rate, in_lanes)); + + /* + * If the tunnel bandwidth is limited (max_bw is set) then see + * if we need to reduce bandwidth to fit there. + */ + out_rate = tb_dp_cap_get_rate(out_dp_cap); + out_lanes = tb_dp_cap_get_lanes(out_dp_cap); + bw = tb_dp_bandwidth(out_rate, out_lanes); + tb_port_dbg(out, "maximum supported bandwidth %u Mb/s x%u = %u Mb/s\n", + out_rate, out_lanes, bw); + + if (tunnel->max_bw && bw > tunnel->max_bw) { + u32 new_rate, new_lanes, new_bw; + + ret = tb_dp_reduce_bandwidth(tunnel->max_bw, in_rate, in_lanes, + out_rate, out_lanes, &new_rate, + &new_lanes); + if (ret) { + tb_port_info(out, "not enough bandwidth for DP tunnel\n"); + return ret; + } + + new_bw = tb_dp_bandwidth(new_rate, new_lanes); + tb_port_dbg(out, "bandwidth reduced to %u Mb/s x%u = %u Mb/s\n", + new_rate, new_lanes, new_bw); + + /* + * Set new rate and number of lanes before writing it to + * the IN port remote caps. + */ + out_dp_cap = tb_dp_cap_set_rate(out_dp_cap, new_rate); + out_dp_cap = tb_dp_cap_set_lanes(out_dp_cap, new_lanes); + } + return tb_port_write(in, &out_dp_cap, TB_CFG_PORT, in->cap_adap + DP_REMOTE_CAP, 1); } @@ -359,6 +524,56 @@ static int tb_dp_activate(struct tb_tunnel *tunnel, bool active) return 0; } +static int tb_dp_consumed_bandwidth(struct tb_tunnel *tunnel) +{ + struct tb_port *in = tunnel->src_port; + const struct tb_switch *sw = in->sw; + u32 val, rate = 0, lanes = 0; + int ret; + + if (tb_switch_is_titan_ridge(sw)) { + int timeout = 10; + + /* + * Wait for DPRX done. Normally it should be already set + * for active tunnel. + */ + do { + ret = tb_port_read(in, &val, TB_CFG_PORT, + in->cap_adap + DP_COMMON_CAP, 1); + if (ret) + return ret; + + if (val & DP_COMMON_CAP_DPRX_DONE) { + rate = tb_dp_cap_get_rate(val); + lanes = tb_dp_cap_get_lanes(val); + break; + } + msleep(250); + } while (timeout--); + + if (!timeout) + return -ETIMEDOUT; + } else if (sw->generation >= 2) { + /* + * Read from the copied remote cap so that we take into + * account if capabilities were reduced during exchange. + */ + ret = tb_port_read(in, &val, TB_CFG_PORT, + in->cap_adap + DP_REMOTE_CAP, 1); + if (ret) + return ret; + + rate = tb_dp_cap_get_rate(val); + lanes = tb_dp_cap_get_lanes(val); + } else { + /* No bandwidth management for legacy devices */ + return 0; + } + + return tb_dp_bandwidth(rate, lanes); +} + static void tb_dp_init_aux_path(struct tb_path *path) { int i; @@ -423,6 +638,7 @@ struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in) tunnel->init = tb_dp_xchg_caps; tunnel->activate = tb_dp_activate; + tunnel->consumed_bandwidth = tb_dp_consumed_bandwidth; tunnel->src_port = in; path = tb_path_discover(in, TB_DP_VIDEO_HOPID, NULL, -1, @@ -481,6 +697,7 @@ err_free: * @tb: Pointer to the domain structure * @in: DP in adapter port * @out: DP out adapter port + * @max_bw: Maximum available bandwidth for the DP tunnel (%0 if not limited) * * Allocates a tunnel between @in and @out that is capable of tunneling * Display Port traffic. @@ -488,7 +705,7 @@ err_free: * Return: Returns a tb_tunnel on success or NULL on failure. */ struct tb_tunnel *tb_tunnel_alloc_dp(struct tb *tb, struct tb_port *in, - struct tb_port *out) + struct tb_port *out, int max_bw) { struct tb_tunnel *tunnel; struct tb_path **paths; @@ -503,8 +720,10 @@ struct tb_tunnel *tb_tunnel_alloc_dp(struct tb *tb, struct tb_port *in, tunnel->init = tb_dp_xchg_caps; tunnel->activate = tb_dp_activate; + tunnel->consumed_bandwidth = tb_dp_consumed_bandwidth; tunnel->src_port = in; tunnel->dst_port = out; + tunnel->max_bw = max_bw; paths = tunnel->paths; @@ -751,3 +970,62 @@ void tb_tunnel_deactivate(struct tb_tunnel *tunnel) tb_path_deactivate(tunnel->paths[i]); } } + +/** + * tb_tunnel_switch_on_path() - Does the tunnel go through switch + * @tunnel: Tunnel to check + * @sw: Switch to check + * + * Returns true if @tunnel goes through @sw (direction does not matter), + * false otherwise. + */ +bool tb_tunnel_switch_on_path(const struct tb_tunnel *tunnel, + const struct tb_switch *sw) +{ + int i; + + for (i = 0; i < tunnel->npaths; i++) { + if (!tunnel->paths[i]) + continue; + if (tb_path_switch_on_path(tunnel->paths[i], sw)) + return true; + } + + return false; +} + +static bool tb_tunnel_is_active(const struct tb_tunnel *tunnel) +{ + int i; + + for (i = 0; i < tunnel->npaths; i++) { + if (!tunnel->paths[i]) + return false; + if (!tunnel->paths[i]->activated) + return false; + } + + return true; +} + +/** + * tb_tunnel_consumed_bandwidth() - Return bandwidth consumed by the tunnel + * @tunnel: Tunnel to check + * + * Returns bandwidth currently consumed by @tunnel and %0 if the @tunnel + * is not active or does consume bandwidth. + */ +int tb_tunnel_consumed_bandwidth(struct tb_tunnel *tunnel) +{ + if (!tb_tunnel_is_active(tunnel)) + return 0; + + if (tunnel->consumed_bandwidth) { + int ret = tunnel->consumed_bandwidth(tunnel); + + tb_tunnel_dbg(tunnel, "consumed bandwidth %d Mb/s\n", ret); + return ret; + } + + return 0; +} diff --git a/drivers/thunderbolt/tunnel.h b/drivers/thunderbolt/tunnel.h index c68bbcd3a62c..ba888da005f5 100644 --- a/drivers/thunderbolt/tunnel.h +++ b/drivers/thunderbolt/tunnel.h @@ -27,8 +27,11 @@ enum tb_tunnel_type { * @npaths: Number of paths in @paths * @init: Optional tunnel specific initialization * @activate: Optional tunnel specific activation/deactivation + * @consumed_bandwidth: Return how much bandwidth the tunnel consumes * @list: Tunnels are linked using this field * @type: Type of the tunnel + * @max_bw: Maximum bandwidth (Mb/s) available for the tunnel (only for DP). + * Only set if the bandwidth needs to be limited. */ struct tb_tunnel { struct tb *tb; @@ -38,8 +41,10 @@ struct tb_tunnel { size_t npaths; int (*init)(struct tb_tunnel *tunnel); int (*activate)(struct tb_tunnel *tunnel, bool activate); + int (*consumed_bandwidth)(struct tb_tunnel *tunnel); struct list_head list; enum tb_tunnel_type type; + unsigned int max_bw; }; struct tb_tunnel *tb_tunnel_discover_pci(struct tb *tb, struct tb_port *down); @@ -47,7 +52,7 @@ struct tb_tunnel *tb_tunnel_alloc_pci(struct tb *tb, struct tb_port *up, struct tb_port *down); struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in); struct tb_tunnel *tb_tunnel_alloc_dp(struct tb *tb, struct tb_port *in, - struct tb_port *out); + struct tb_port *out, int max_bw); struct tb_tunnel *tb_tunnel_alloc_dma(struct tb *tb, struct tb_port *nhi, struct tb_port *dst, int transmit_ring, int transmit_path, int receive_ring, @@ -58,6 +63,9 @@ int tb_tunnel_activate(struct tb_tunnel *tunnel); int tb_tunnel_restart(struct tb_tunnel *tunnel); void tb_tunnel_deactivate(struct tb_tunnel *tunnel); bool tb_tunnel_is_invalid(struct tb_tunnel *tunnel); +bool tb_tunnel_switch_on_path(const struct tb_tunnel *tunnel, + const struct tb_switch *sw); +int tb_tunnel_consumed_bandwidth(struct tb_tunnel *tunnel); static inline bool tb_tunnel_is_pci(const struct tb_tunnel *tunnel) { From 354a7a7716edb377953a324421915d7788e0bca9 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 21 Mar 2019 15:31:14 +0200 Subject: [PATCH 089/198] thunderbolt: Do not start firmware unless asked by the user Since now we can do pretty much the same thing in the software connection manager than the firmware would do, there is no point starting it by default. Instead we can just continue using the software connection manager. Make it possible for user to switch between the two by adding a module pararameter (start_icm) which is by default false. Having this ability to enable the firmware may be useful at least when debugging possible issues with the software connection manager implementation. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/icm.c | 23 +++++++++++++++++++---- drivers/thunderbolt/tb.c | 4 ---- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/drivers/thunderbolt/icm.c b/drivers/thunderbolt/icm.c index 78480b782045..13e88109742e 100644 --- a/drivers/thunderbolt/icm.c +++ b/drivers/thunderbolt/icm.c @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -43,6 +44,10 @@ #define ICM_APPROVE_TIMEOUT 10000 /* ms */ #define ICM_MAX_LINK 4 +static bool start_icm; +module_param(start_icm, bool, 0444); +MODULE_PARM_DESC(start_icm, "start ICM firmware if it is not running (default: false)"); + /** * struct icm - Internal connection manager private data * @request_lock: Makes sure only one message is send to ICM at time @@ -350,6 +355,14 @@ static void icm_veto_end(struct tb *tb) } } +static bool icm_firmware_running(const struct tb_nhi *nhi) +{ + u32 val; + + val = ioread32(nhi->iobase + REG_FW_STS); + return !!(val & REG_FW_STS_ICM_EN); +} + static bool icm_fr_is_supported(struct tb *tb) { return !x86_apple_machine; @@ -1381,9 +1394,12 @@ static bool icm_ar_is_supported(struct tb *tb) /* * Starting from Alpine Ridge we can use ICM on Apple machines * as well. We just need to reset and re-enable it first. + * However, only start it if explicitly asked by the user. */ - if (!x86_apple_machine) + if (icm_firmware_running(tb->nhi)) return true; + if (!start_icm) + return false; /* * Find the upstream PCIe port in case we need to do reset @@ -1736,8 +1752,7 @@ static int icm_firmware_start(struct tb *tb, struct tb_nhi *nhi) u32 val; /* Check if the ICM firmware is already running */ - val = ioread32(nhi->iobase + REG_FW_STS); - if (val & REG_FW_STS_ICM_EN) + if (icm_firmware_running(nhi)) return 0; dev_dbg(&nhi->pdev->dev, "starting ICM firmware\n"); @@ -2244,7 +2259,7 @@ struct tb *icm_probe(struct tb_nhi *nhi) case PCI_DEVICE_ID_INTEL_ICL_NHI0: case PCI_DEVICE_ID_INTEL_ICL_NHI1: - icm->is_supported = icm_ar_is_supported; + icm->is_supported = icm_fr_is_supported; icm->driver_ready = icm_icl_driver_ready; icm->set_uuid = icm_icl_set_uuid; icm->device_connected = icm_icl_device_connected; diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index bb763a5cf103..ea8727f769d6 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -9,7 +9,6 @@ #include #include #include -#include #include "tb.h" #include "tb_regs.h" @@ -990,9 +989,6 @@ struct tb *tb_probe(struct tb_nhi *nhi) struct tb_cm *tcm; struct tb *tb; - if (!x86_apple_machine) - return NULL; - tb = tb_domain_alloc(nhi, sizeof(*tcm)); if (!tb) return NULL; From 891e60368ba1840c1b8bbc72beb1da0dae289430 Mon Sep 17 00:00:00 2001 From: Johannes Thumshirn Date: Wed, 16 Oct 2019 12:01:58 +0200 Subject: [PATCH 090/198] drivers: mcb: use symbol namespaces Now that we have symbol namespaces, use them in MCB to not pollute the default namespace with MCB internals. Signed-off-by: Johannes Thumshirn Reviewed-by: Jessica Yu Reviewed-by: Michael Moese Link: https://lore.kernel.org/r/20191016100158.1400-1-jthumshirn@suse.de Signed-off-by: Greg Kroah-Hartman --- drivers/gpio/gpio-menz127.c | 1 + drivers/iio/adc/men_z188_adc.c | 1 + drivers/mcb/mcb-core.c | 28 +++++++++++++------------- drivers/mcb/mcb-lpc.c | 1 + drivers/mcb/mcb-parse.c | 2 +- drivers/mcb/mcb-pci.c | 1 + drivers/tty/serial/8250/8250_men_mcb.c | 1 + drivers/tty/serial/men_z135_uart.c | 1 + drivers/watchdog/menz69_wdt.c | 1 + 9 files changed, 22 insertions(+), 15 deletions(-) diff --git a/drivers/gpio/gpio-menz127.c b/drivers/gpio/gpio-menz127.c index 70fdb42a8e88..1e21c661d79d 100644 --- a/drivers/gpio/gpio-menz127.c +++ b/drivers/gpio/gpio-menz127.c @@ -211,3 +211,4 @@ MODULE_AUTHOR("Andreas Werner "); MODULE_DESCRIPTION("MEN 16z127 GPIO Controller"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("mcb:16z127"); +MODULE_IMPORT_NS(MCB); diff --git a/drivers/iio/adc/men_z188_adc.c b/drivers/iio/adc/men_z188_adc.c index 3b2fbb7ce431..196c8226381e 100644 --- a/drivers/iio/adc/men_z188_adc.c +++ b/drivers/iio/adc/men_z188_adc.c @@ -167,3 +167,4 @@ MODULE_AUTHOR("Johannes Thumshirn "); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("IIO ADC driver for MEN 16z188 ADC Core"); MODULE_ALIAS("mcb:16z188"); +MODULE_IMPORT_NS(MCB); diff --git a/drivers/mcb/mcb-core.c b/drivers/mcb/mcb-core.c index b72e82efaee5..38fbb3b59873 100644 --- a/drivers/mcb/mcb-core.c +++ b/drivers/mcb/mcb-core.c @@ -191,7 +191,7 @@ int __mcb_register_driver(struct mcb_driver *drv, struct module *owner, return driver_register(&drv->driver); } -EXPORT_SYMBOL_GPL(__mcb_register_driver); +EXPORT_SYMBOL_NS_GPL(__mcb_register_driver, MCB); /** * mcb_unregister_driver() - Unregister a @mcb_driver from the system @@ -203,7 +203,7 @@ void mcb_unregister_driver(struct mcb_driver *drv) { driver_unregister(&drv->driver); } -EXPORT_SYMBOL_GPL(mcb_unregister_driver); +EXPORT_SYMBOL_NS_GPL(mcb_unregister_driver, MCB); static void mcb_release_dev(struct device *dev) { @@ -249,7 +249,7 @@ out: return ret; } -EXPORT_SYMBOL_GPL(mcb_device_register); +EXPORT_SYMBOL_NS_GPL(mcb_device_register, MCB); static void mcb_free_bus(struct device *dev) { @@ -301,7 +301,7 @@ err_free: kfree(bus); return ERR_PTR(rc); } -EXPORT_SYMBOL_GPL(mcb_alloc_bus); +EXPORT_SYMBOL_NS_GPL(mcb_alloc_bus, MCB); static int __mcb_devices_unregister(struct device *dev, void *data) { @@ -323,7 +323,7 @@ void mcb_release_bus(struct mcb_bus *bus) { mcb_devices_unregister(bus); } -EXPORT_SYMBOL_GPL(mcb_release_bus); +EXPORT_SYMBOL_NS_GPL(mcb_release_bus, MCB); /** * mcb_bus_put() - Increment refcnt @@ -338,7 +338,7 @@ struct mcb_bus *mcb_bus_get(struct mcb_bus *bus) return bus; } -EXPORT_SYMBOL_GPL(mcb_bus_get); +EXPORT_SYMBOL_NS_GPL(mcb_bus_get, MCB); /** * mcb_bus_put() - Decrement refcnt @@ -351,7 +351,7 @@ void mcb_bus_put(struct mcb_bus *bus) if (bus) put_device(&bus->dev); } -EXPORT_SYMBOL_GPL(mcb_bus_put); +EXPORT_SYMBOL_NS_GPL(mcb_bus_put, MCB); /** * mcb_alloc_dev() - Allocate a device @@ -371,7 +371,7 @@ struct mcb_device *mcb_alloc_dev(struct mcb_bus *bus) return dev; } -EXPORT_SYMBOL_GPL(mcb_alloc_dev); +EXPORT_SYMBOL_NS_GPL(mcb_alloc_dev, MCB); /** * mcb_free_dev() - Free @mcb_device @@ -383,7 +383,7 @@ void mcb_free_dev(struct mcb_device *dev) { kfree(dev); } -EXPORT_SYMBOL_GPL(mcb_free_dev); +EXPORT_SYMBOL_NS_GPL(mcb_free_dev, MCB); static int __mcb_bus_add_devices(struct device *dev, void *data) { @@ -412,7 +412,7 @@ void mcb_bus_add_devices(const struct mcb_bus *bus) { bus_for_each_dev(&mcb_bus_type, NULL, NULL, __mcb_bus_add_devices); } -EXPORT_SYMBOL_GPL(mcb_bus_add_devices); +EXPORT_SYMBOL_NS_GPL(mcb_bus_add_devices, MCB); /** * mcb_get_resource() - get a resource for a mcb device @@ -428,7 +428,7 @@ struct resource *mcb_get_resource(struct mcb_device *dev, unsigned int type) else return NULL; } -EXPORT_SYMBOL_GPL(mcb_get_resource); +EXPORT_SYMBOL_NS_GPL(mcb_get_resource, MCB); /** * mcb_request_mem() - Request memory @@ -454,7 +454,7 @@ struct resource *mcb_request_mem(struct mcb_device *dev, const char *name) return mem; } -EXPORT_SYMBOL_GPL(mcb_request_mem); +EXPORT_SYMBOL_NS_GPL(mcb_request_mem, MCB); /** * mcb_release_mem() - Release memory requested by device @@ -469,7 +469,7 @@ void mcb_release_mem(struct resource *mem) size = resource_size(mem); release_mem_region(mem->start, size); } -EXPORT_SYMBOL_GPL(mcb_release_mem); +EXPORT_SYMBOL_NS_GPL(mcb_release_mem, MCB); static int __mcb_get_irq(struct mcb_device *dev) { @@ -495,7 +495,7 @@ int mcb_get_irq(struct mcb_device *dev) return __mcb_get_irq(dev); } -EXPORT_SYMBOL_GPL(mcb_get_irq); +EXPORT_SYMBOL_NS_GPL(mcb_get_irq, MCB); static int mcb_init(void) { diff --git a/drivers/mcb/mcb-lpc.c b/drivers/mcb/mcb-lpc.c index 8f1bde437a7e..506676754538 100644 --- a/drivers/mcb/mcb-lpc.c +++ b/drivers/mcb/mcb-lpc.c @@ -168,3 +168,4 @@ module_exit(mcb_lpc_exit); MODULE_AUTHOR("Andreas Werner "); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("MCB over LPC support"); +MODULE_IMPORT_NS(MCB); diff --git a/drivers/mcb/mcb-parse.c b/drivers/mcb/mcb-parse.c index 3b69e6aa3d88..0266bfddfbe2 100644 --- a/drivers/mcb/mcb-parse.c +++ b/drivers/mcb/mcb-parse.c @@ -253,4 +253,4 @@ free_header: return ret; } -EXPORT_SYMBOL_GPL(chameleon_parse_cells); +EXPORT_SYMBOL_NS_GPL(chameleon_parse_cells, MCB); diff --git a/drivers/mcb/mcb-pci.c b/drivers/mcb/mcb-pci.c index 14866aa22f75..dc88232d9af8 100644 --- a/drivers/mcb/mcb-pci.c +++ b/drivers/mcb/mcb-pci.c @@ -131,3 +131,4 @@ module_pci_driver(mcb_pci_driver); MODULE_AUTHOR("Johannes Thumshirn "); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("MCB over PCI support"); +MODULE_IMPORT_NS(MCB); diff --git a/drivers/tty/serial/8250/8250_men_mcb.c b/drivers/tty/serial/8250/8250_men_mcb.c index 8df89e9cd254..e985f344b2dd 100644 --- a/drivers/tty/serial/8250/8250_men_mcb.c +++ b/drivers/tty/serial/8250/8250_men_mcb.c @@ -174,3 +174,4 @@ MODULE_AUTHOR("Michael Moese "); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("MEN 16z135 High Speed UART"); MODULE_ALIAS("mcb:16z135"); +MODULE_IMPORT_NS(MCB); diff --git a/drivers/watchdog/menz69_wdt.c b/drivers/watchdog/menz69_wdt.c index ed18238c5407..8973f98bc6a5 100644 --- a/drivers/watchdog/menz69_wdt.c +++ b/drivers/watchdog/menz69_wdt.c @@ -168,3 +168,4 @@ module_mcb_driver(men_z069_driver); MODULE_AUTHOR("Johannes Thumshirn "); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("mcb:16z069"); +MODULE_IMPORT_NS(MCB); From f188b5e76aae9f713c73708d2ba57b65953ce207 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 4 Nov 2019 11:12:38 -0700 Subject: [PATCH 091/198] coresight: etm4x: Save/restore state across CPU low power states Some hardware will ignore bit TRCPDCR.PU which is used to signal to hardware that power should not be removed from the trace unit. Let's mitigate against this by conditionally saving and restoring the trace unit state when the CPU enters low power states. This patchset introduces a firmware property named 'arm,coresight-loses-context-with-cpu' - when this is present the hardware state will be conditionally saved and restored. A module parameter 'pm_save_enable' is also introduced which can be configured to override the firmware property. This can be set to never allow save/restore or to conditionally allow it (only for self-hosted). The default value is determined by firmware. We avoid saving the hardware state when self-hosted coresight isn't in use to reduce PM latency - we can't determine this by reading the claim tags (TRCCLAIMCLR) as these are 'trace' registers which need power and clocking, something we can't easily provide in the PM context. Therefore we rely on the existing drvdata->mode internal state that is set when self-hosted coresight is used (and powered). Signed-off-by: Andrew Murray Reviewed-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20191104181251.26732-2-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm4x.c | 318 ++++++++++++++++++ drivers/hwtracing/coresight/coresight-etm4x.h | 64 ++++ drivers/hwtracing/coresight/coresight.c | 6 + include/linux/coresight.h | 6 + 4 files changed, 394 insertions(+) diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index a128b5063f46..4cecabdd051b 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -26,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -37,6 +39,15 @@ static int boot_enable; module_param(boot_enable, int, 0444); MODULE_PARM_DESC(boot_enable, "Enable tracing on boot"); +#define PARAM_PM_SAVE_FIRMWARE 0 /* save self-hosted state as per firmware */ +#define PARAM_PM_SAVE_NEVER 1 /* never save any state */ +#define PARAM_PM_SAVE_SELF_HOSTED 2 /* save self-hosted state only */ + +static int pm_save_enable = PARAM_PM_SAVE_FIRMWARE; +module_param(pm_save_enable, int, 0444); +MODULE_PARM_DESC(pm_save_enable, + "Save/restore state on power down: 1 = never, 2 = self-hosted"); + /* The number of ETMv4 currently registered */ static int etm4_count; static struct etmv4_drvdata *etmdrvdata[NR_CPUS]; @@ -54,6 +65,14 @@ static void etm4_os_unlock(struct etmv4_drvdata *drvdata) isb(); } +static void etm4_os_lock(struct etmv4_drvdata *drvdata) +{ + /* Writing 0x1 to TRCOSLAR locks the trace registers */ + writel_relaxed(0x1, drvdata->base + TRCOSLAR); + drvdata->os_unlock = false; + isb(); +} + static bool etm4_arch_supported(u8 arch) { /* Mask out the minor version number */ @@ -1085,6 +1104,288 @@ static void etm4_init_trace_id(struct etmv4_drvdata *drvdata) drvdata->trcid = coresight_get_trace_id(drvdata->cpu); } +#ifdef CONFIG_CPU_PM +static int etm4_cpu_save(struct etmv4_drvdata *drvdata) +{ + int i, ret = 0; + struct etmv4_save_state *state; + struct device *etm_dev = &drvdata->csdev->dev; + + /* + * As recommended by 3.4.1 ("The procedure when powering down the PE") + * of ARM IHI 0064D + */ + dsb(sy); + isb(); + + CS_UNLOCK(drvdata->base); + + /* Lock the OS lock to disable trace and external debugger access */ + etm4_os_lock(drvdata); + + /* wait for TRCSTATR.PMSTABLE to go up */ + if (coresight_timeout(drvdata->base, TRCSTATR, + TRCSTATR_PMSTABLE_BIT, 1)) { + dev_err(etm_dev, + "timeout while waiting for PM Stable Status\n"); + etm4_os_unlock(drvdata); + ret = -EBUSY; + goto out; + } + + state = drvdata->save_state; + + state->trcprgctlr = readl(drvdata->base + TRCPRGCTLR); + state->trcprocselr = readl(drvdata->base + TRCPROCSELR); + state->trcconfigr = readl(drvdata->base + TRCCONFIGR); + state->trcauxctlr = readl(drvdata->base + TRCAUXCTLR); + state->trceventctl0r = readl(drvdata->base + TRCEVENTCTL0R); + state->trceventctl1r = readl(drvdata->base + TRCEVENTCTL1R); + state->trcstallctlr = readl(drvdata->base + TRCSTALLCTLR); + state->trctsctlr = readl(drvdata->base + TRCTSCTLR); + state->trcsyncpr = readl(drvdata->base + TRCSYNCPR); + state->trcccctlr = readl(drvdata->base + TRCCCCTLR); + state->trcbbctlr = readl(drvdata->base + TRCBBCTLR); + state->trctraceidr = readl(drvdata->base + TRCTRACEIDR); + state->trcqctlr = readl(drvdata->base + TRCQCTLR); + + state->trcvictlr = readl(drvdata->base + TRCVICTLR); + state->trcviiectlr = readl(drvdata->base + TRCVIIECTLR); + state->trcvissctlr = readl(drvdata->base + TRCVISSCTLR); + state->trcvipcssctlr = readl(drvdata->base + TRCVIPCSSCTLR); + state->trcvdctlr = readl(drvdata->base + TRCVDCTLR); + state->trcvdsacctlr = readl(drvdata->base + TRCVDSACCTLR); + state->trcvdarcctlr = readl(drvdata->base + TRCVDARCCTLR); + + for (i = 0; i < drvdata->nrseqstate; i++) + state->trcseqevr[i] = readl(drvdata->base + TRCSEQEVRn(i)); + + state->trcseqrstevr = readl(drvdata->base + TRCSEQRSTEVR); + state->trcseqstr = readl(drvdata->base + TRCSEQSTR); + state->trcextinselr = readl(drvdata->base + TRCEXTINSELR); + + for (i = 0; i < drvdata->nr_cntr; i++) { + state->trccntrldvr[i] = readl(drvdata->base + TRCCNTRLDVRn(i)); + state->trccntctlr[i] = readl(drvdata->base + TRCCNTCTLRn(i)); + state->trccntvr[i] = readl(drvdata->base + TRCCNTVRn(i)); + } + + for (i = 0; i < drvdata->nr_resource * 2; i++) + state->trcrsctlr[i] = readl(drvdata->base + TRCRSCTLRn(i)); + + for (i = 0; i < drvdata->nr_ss_cmp; i++) { + state->trcssccr[i] = readl(drvdata->base + TRCSSCCRn(i)); + state->trcsscsr[i] = readl(drvdata->base + TRCSSCSRn(i)); + state->trcsspcicr[i] = readl(drvdata->base + TRCSSPCICRn(i)); + } + + for (i = 0; i < drvdata->nr_addr_cmp * 2; i++) { + state->trcacvr[i] = readl(drvdata->base + TRCACVRn(i)); + state->trcacatr[i] = readl(drvdata->base + TRCACATRn(i)); + } + + /* + * Data trace stream is architecturally prohibited for A profile cores + * so we don't save (or later restore) trcdvcvr and trcdvcmr - As per + * section 1.3.4 ("Possible functional configurations of an ETMv4 trace + * unit") of ARM IHI 0064D. + */ + + for (i = 0; i < drvdata->numcidc; i++) + state->trccidcvr[i] = readl(drvdata->base + TRCCIDCVRn(i)); + + for (i = 0; i < drvdata->numvmidc; i++) + state->trcvmidcvr[i] = readl(drvdata->base + TRCVMIDCVRn(i)); + + state->trccidcctlr0 = readl(drvdata->base + TRCCIDCCTLR0); + state->trccidcctlr1 = readl(drvdata->base + TRCCIDCCTLR1); + + state->trcvmidcctlr0 = readl(drvdata->base + TRCVMIDCCTLR0); + state->trcvmidcctlr0 = readl(drvdata->base + TRCVMIDCCTLR1); + + state->trcclaimset = readl(drvdata->base + TRCCLAIMCLR); + + state->trcpdcr = readl(drvdata->base + TRCPDCR); + + /* wait for TRCSTATR.IDLE to go up */ + if (coresight_timeout(drvdata->base, TRCSTATR, TRCSTATR_IDLE_BIT, 1)) { + dev_err(etm_dev, + "timeout while waiting for Idle Trace Status\n"); + etm4_os_unlock(drvdata); + ret = -EBUSY; + goto out; + } + + drvdata->state_needs_restore = true; + + /* + * Power can be removed from the trace unit now. We do this to + * potentially save power on systems that respect the TRCPDCR_PU + * despite requesting software to save/restore state. + */ + writel_relaxed((state->trcpdcr & ~TRCPDCR_PU), + drvdata->base + TRCPDCR); + +out: + CS_LOCK(drvdata->base); + return ret; +} + +static void etm4_cpu_restore(struct etmv4_drvdata *drvdata) +{ + int i; + struct etmv4_save_state *state = drvdata->save_state; + + CS_UNLOCK(drvdata->base); + + writel_relaxed(state->trcclaimset, drvdata->base + TRCCLAIMSET); + + writel_relaxed(state->trcprgctlr, drvdata->base + TRCPRGCTLR); + writel_relaxed(state->trcprocselr, drvdata->base + TRCPROCSELR); + writel_relaxed(state->trcconfigr, drvdata->base + TRCCONFIGR); + writel_relaxed(state->trcauxctlr, drvdata->base + TRCAUXCTLR); + writel_relaxed(state->trceventctl0r, drvdata->base + TRCEVENTCTL0R); + writel_relaxed(state->trceventctl1r, drvdata->base + TRCEVENTCTL1R); + writel_relaxed(state->trcstallctlr, drvdata->base + TRCSTALLCTLR); + writel_relaxed(state->trctsctlr, drvdata->base + TRCTSCTLR); + writel_relaxed(state->trcsyncpr, drvdata->base + TRCSYNCPR); + writel_relaxed(state->trcccctlr, drvdata->base + TRCCCCTLR); + writel_relaxed(state->trcbbctlr, drvdata->base + TRCBBCTLR); + writel_relaxed(state->trctraceidr, drvdata->base + TRCTRACEIDR); + writel_relaxed(state->trcqctlr, drvdata->base + TRCQCTLR); + + writel_relaxed(state->trcvictlr, drvdata->base + TRCVICTLR); + writel_relaxed(state->trcviiectlr, drvdata->base + TRCVIIECTLR); + writel_relaxed(state->trcvissctlr, drvdata->base + TRCVISSCTLR); + writel_relaxed(state->trcvipcssctlr, drvdata->base + TRCVIPCSSCTLR); + writel_relaxed(state->trcvdctlr, drvdata->base + TRCVDCTLR); + writel_relaxed(state->trcvdsacctlr, drvdata->base + TRCVDSACCTLR); + writel_relaxed(state->trcvdarcctlr, drvdata->base + TRCVDARCCTLR); + + for (i = 0; i < drvdata->nrseqstate; i++) + writel_relaxed(state->trcseqevr[i], + drvdata->base + TRCSEQEVRn(i)); + + writel_relaxed(state->trcseqrstevr, drvdata->base + TRCSEQRSTEVR); + writel_relaxed(state->trcseqstr, drvdata->base + TRCSEQSTR); + writel_relaxed(state->trcextinselr, drvdata->base + TRCEXTINSELR); + + for (i = 0; i < drvdata->nr_cntr; i++) { + writel_relaxed(state->trccntrldvr[i], + drvdata->base + TRCCNTRLDVRn(i)); + writel_relaxed(state->trccntctlr[i], + drvdata->base + TRCCNTCTLRn(i)); + writel_relaxed(state->trccntvr[i], + drvdata->base + TRCCNTVRn(i)); + } + + for (i = 0; i < drvdata->nr_resource * 2; i++) + writel_relaxed(state->trcrsctlr[i], + drvdata->base + TRCRSCTLRn(i)); + + for (i = 0; i < drvdata->nr_ss_cmp; i++) { + writel_relaxed(state->trcssccr[i], + drvdata->base + TRCSSCCRn(i)); + writel_relaxed(state->trcsscsr[i], + drvdata->base + TRCSSCSRn(i)); + writel_relaxed(state->trcsspcicr[i], + drvdata->base + TRCSSPCICRn(i)); + } + + for (i = 0; i < drvdata->nr_addr_cmp * 2; i++) { + writel_relaxed(state->trcacvr[i], + drvdata->base + TRCACVRn(i)); + writel_relaxed(state->trcacatr[i], + drvdata->base + TRCACATRn(i)); + } + + for (i = 0; i < drvdata->numcidc; i++) + writel_relaxed(state->trccidcvr[i], + drvdata->base + TRCCIDCVRn(i)); + + for (i = 0; i < drvdata->numvmidc; i++) + writel_relaxed(state->trcvmidcvr[i], + drvdata->base + TRCVMIDCVRn(i)); + + writel_relaxed(state->trccidcctlr0, drvdata->base + TRCCIDCCTLR0); + writel_relaxed(state->trccidcctlr1, drvdata->base + TRCCIDCCTLR1); + + writel_relaxed(state->trcvmidcctlr0, drvdata->base + TRCVMIDCCTLR0); + writel_relaxed(state->trcvmidcctlr0, drvdata->base + TRCVMIDCCTLR1); + + writel_relaxed(state->trcclaimset, drvdata->base + TRCCLAIMSET); + + writel_relaxed(state->trcpdcr, drvdata->base + TRCPDCR); + + drvdata->state_needs_restore = false; + + /* + * As recommended by section 4.3.7 ("Synchronization when using the + * memory-mapped interface") of ARM IHI 0064D + */ + dsb(sy); + isb(); + + /* Unlock the OS lock to re-enable trace and external debug access */ + etm4_os_unlock(drvdata); + CS_LOCK(drvdata->base); +} + +static int etm4_cpu_pm_notify(struct notifier_block *nb, unsigned long cmd, + void *v) +{ + struct etmv4_drvdata *drvdata; + unsigned int cpu = smp_processor_id(); + + if (!etmdrvdata[cpu]) + return NOTIFY_OK; + + drvdata = etmdrvdata[cpu]; + + if (!drvdata->save_state) + return NOTIFY_OK; + + if (WARN_ON_ONCE(drvdata->cpu != cpu)) + return NOTIFY_BAD; + + switch (cmd) { + case CPU_PM_ENTER: + /* save the state if self-hosted coresight is in use */ + if (local_read(&drvdata->mode)) + if (etm4_cpu_save(drvdata)) + return NOTIFY_BAD; + break; + case CPU_PM_EXIT: + /* fallthrough */ + case CPU_PM_ENTER_FAILED: + if (drvdata->state_needs_restore) + etm4_cpu_restore(drvdata); + break; + default: + return NOTIFY_DONE; + } + + return NOTIFY_OK; +} + +static struct notifier_block etm4_cpu_pm_nb = { + .notifier_call = etm4_cpu_pm_notify, +}; + +static int etm4_cpu_pm_register(void) +{ + return cpu_pm_register_notifier(&etm4_cpu_pm_nb); +} + +static void etm4_cpu_pm_unregister(void) +{ + cpu_pm_unregister_notifier(&etm4_cpu_pm_nb); +} +#else +static int etm4_cpu_pm_register(void) { return 0; } +static void etm4_cpu_pm_unregister(void) { } +#endif + static int etm4_probe(struct amba_device *adev, const struct amba_id *id) { int ret; @@ -1101,6 +1402,17 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id) dev_set_drvdata(dev, drvdata); + if (pm_save_enable == PARAM_PM_SAVE_FIRMWARE) + pm_save_enable = coresight_loses_context_with_cpu(dev) ? + PARAM_PM_SAVE_SELF_HOSTED : PARAM_PM_SAVE_NEVER; + + if (pm_save_enable != PARAM_PM_SAVE_NEVER) { + drvdata->save_state = devm_kmalloc(dev, + sizeof(struct etmv4_save_state), GFP_KERNEL); + if (!drvdata->save_state) + return -ENOMEM; + } + /* Validity for the resource is already checked by the AMBA core */ base = devm_ioremap_resource(dev, res); if (IS_ERR(base)) @@ -1135,6 +1447,10 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id) if (ret < 0) goto err_arch_supported; hp_online = ret; + + ret = etm4_cpu_pm_register(); + if (ret) + goto err_arch_supported; } cpus_read_unlock(); @@ -1185,6 +1501,8 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id) err_arch_supported: if (--etm4_count == 0) { + etm4_cpu_pm_unregister(); + cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING); if (hp_online) cpuhp_remove_state_nocalls(hp_online); diff --git a/drivers/hwtracing/coresight/coresight-etm4x.h b/drivers/hwtracing/coresight/coresight-etm4x.h index 4523f10ddd0f..546d790cb01b 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.h +++ b/drivers/hwtracing/coresight/coresight-etm4x.h @@ -175,6 +175,7 @@ ETM_MODE_EXCL_USER) #define TRCSTATR_IDLE_BIT 0 +#define TRCSTATR_PMSTABLE_BIT 1 #define ETM_DEFAULT_ADDR_COMP 0 /* PowerDown Control Register bits */ @@ -281,6 +282,65 @@ struct etmv4_config { u32 ext_inp; }; +/** + * struct etm4_save_state - state to be preserved when ETM is without power + */ +struct etmv4_save_state { + u32 trcprgctlr; + u32 trcprocselr; + u32 trcconfigr; + u32 trcauxctlr; + u32 trceventctl0r; + u32 trceventctl1r; + u32 trcstallctlr; + u32 trctsctlr; + u32 trcsyncpr; + u32 trcccctlr; + u32 trcbbctlr; + u32 trctraceidr; + u32 trcqctlr; + + u32 trcvictlr; + u32 trcviiectlr; + u32 trcvissctlr; + u32 trcvipcssctlr; + u32 trcvdctlr; + u32 trcvdsacctlr; + u32 trcvdarcctlr; + + u32 trcseqevr[ETM_MAX_SEQ_STATES]; + u32 trcseqrstevr; + u32 trcseqstr; + u32 trcextinselr; + u32 trccntrldvr[ETMv4_MAX_CNTR]; + u32 trccntctlr[ETMv4_MAX_CNTR]; + u32 trccntvr[ETMv4_MAX_CNTR]; + + u32 trcrsctlr[ETM_MAX_RES_SEL * 2]; + + u32 trcssccr[ETM_MAX_SS_CMP]; + u32 trcsscsr[ETM_MAX_SS_CMP]; + u32 trcsspcicr[ETM_MAX_SS_CMP]; + + u64 trcacvr[ETM_MAX_SINGLE_ADDR_CMP]; + u64 trcacatr[ETM_MAX_SINGLE_ADDR_CMP]; + u64 trccidcvr[ETMv4_MAX_CTXID_CMP]; + u32 trcvmidcvr[ETM_MAX_VMID_CMP]; + u32 trccidcctlr0; + u32 trccidcctlr1; + u32 trcvmidcctlr0; + u32 trcvmidcctlr1; + + u32 trcclaimset; + + u32 cntr_val[ETMv4_MAX_CNTR]; + u32 seq_state; + u32 vinst_ctrl; + u32 ss_status[ETM_MAX_SS_CMP]; + + u32 trcpdcr; +}; + /** * struct etm4_drvdata - specifics associated to an ETM component * @base: Memory mapped base address for this component. @@ -336,6 +396,8 @@ struct etmv4_config { * @atbtrig: If the implementation can support ATB triggers * @lpoverride: If the implementation can support low-power state over. * @config: structure holding configuration parameters. + * @save_state: State to be preserved across power loss + * @state_needs_restore: True when there is context to restore after PM exit */ struct etmv4_drvdata { void __iomem *base; @@ -381,6 +443,8 @@ struct etmv4_drvdata { bool atbtrig; bool lpoverride; struct etmv4_config config; + struct etmv4_save_state *save_state; + bool state_needs_restore; }; /* Address comparator access types */ diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 6453c67a4d01..e6ca899fea4e 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -1308,6 +1308,12 @@ static inline int coresight_search_device_idx(struct coresight_dev_list *dict, return -ENOENT; } +bool coresight_loses_context_with_cpu(struct device *dev) +{ + return fwnode_property_present(dev_fwnode(dev), + "arm,coresight-loses-context-with-cpu"); +} + /* * coresight_alloc_device_name - Get an index for a given device in the * device index list specific to a driver. An index is allocated for a diff --git a/include/linux/coresight.h b/include/linux/coresight.h index a2b68823717b..44e552de419c 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -285,6 +285,8 @@ extern void coresight_disclaim_device(void __iomem *base); extern void coresight_disclaim_device_unlocked(void __iomem *base); extern char *coresight_alloc_device_name(struct coresight_dev_list *devs, struct device *dev); + +extern bool coresight_loses_context_with_cpu(struct device *dev); #else static inline struct coresight_device * coresight_register(struct coresight_desc *desc) { return NULL; } @@ -307,6 +309,10 @@ static inline int coresight_claim_device(void __iomem *base) static inline void coresight_disclaim_device(void __iomem *base) {} static inline void coresight_disclaim_device_unlocked(void __iomem *base) {} +static inline bool coresight_loses_context_with_cpu(struct device *dev) +{ + return false; +} #endif extern int coresight_get_cpu(struct device *dev); From b7909065e8ebaffd80375d01f5d151ff59b59ce6 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 4 Nov 2019 11:12:39 -0700 Subject: [PATCH 092/198] dt-bindings: arm: coresight: Add support for coresight-loses-context-with-cpu Some coresight components, because of choices made during hardware integration, require their state to be saved and restored across CPU low power states. The software has no reliable method of detecting when save/restore is required thus let's add a binding to inform the kernel. Signed-off-by: Andrew Murray Reviewed-by: Mathieu Poirier Reviewed-by: Suzuki K Poulose Reviewed-by: Rob Herring Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20191104181251.26732-3-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/arm/coresight.txt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Documentation/devicetree/bindings/arm/coresight.txt b/Documentation/devicetree/bindings/arm/coresight.txt index fcc3bacfd8bc..d02c42d21f2f 100644 --- a/Documentation/devicetree/bindings/arm/coresight.txt +++ b/Documentation/devicetree/bindings/arm/coresight.txt @@ -87,6 +87,15 @@ its hardware characteristcs. * port or ports: see "Graph bindings for Coresight" below. +* Optional properties for all components: + + * arm,coresight-loses-context-with-cpu : boolean. Indicates that the + hardware will lose register context on CPU power down (e.g. CPUIdle). + An example of where this may be needed are systems which contain a + coresight component and CPU in the same power domain. When the CPU + powers down the coresight component also powers down and loses its + context. This property is currently only used for the ETM 4.x driver. + * Optional properties for ETM/PTMs: * arm,cp14: must be present if the system accesses ETM/PTM management From 0373d90639ef45c419d14131c3037436b05c4243 Mon Sep 17 00:00:00 2001 From: Tanmay Vilas Kumar Jagdale Date: Mon, 4 Nov 2019 11:12:40 -0700 Subject: [PATCH 093/198] coresight: etm4x: Add support for ThunderX2 Add ETMv4 periperhal ID for Marvell's ThunderX2 chip. This chip contains ETMv4.1 version. Signed-off-by: Tanmay Vilas Kumar Jagdale Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20191104181251.26732-4-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm4x.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 4cecabdd051b..8f98701cadc5 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -1529,6 +1529,7 @@ static const struct amba_id etm4_ids[] = { CS_AMBA_UCI_ID(0x000f0211, uci_id_etm4),/* Qualcomm Kryo */ CS_AMBA_ID(0x000bb802), /* Qualcomm Kryo 385 Cortex-A55 */ CS_AMBA_ID(0x000bb803), /* Qualcomm Kryo 385 Cortex-A75 */ + CS_AMBA_UCI_ID(0x000cc0af, uci_id_etm4),/* Marvell ThunderX2 */ {}, }; From 057f2c57b08d0a7e87b022d05b1d4f7173bd4735 Mon Sep 17 00:00:00 2001 From: Mike Leach Date: Mon, 4 Nov 2019 11:12:41 -0700 Subject: [PATCH 094/198] coresight: etm4x: Fixes for ETM v4.4 architecture updates. ETMv4.4 adds in support for tracing secure EL2 (per arch 8.x updates). Patch accounts for this new capability. Signed-off-by: Mike Leach Reviewed-by: Leo Yan Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20191104181251.26732-5-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- .../hwtracing/coresight/coresight-etm4x-sysfs.c | 12 ++++++------ drivers/hwtracing/coresight/coresight-etm4x.c | 5 ++++- drivers/hwtracing/coresight/coresight-etm4x.h | 15 +++++++++++---- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c index 219c10eb752c..b6984be0c515 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c @@ -738,7 +738,7 @@ static ssize_t s_exlevel_vinst_show(struct device *dev, struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); struct etmv4_config *config = &drvdata->config; - val = BMVAL(config->vinst_ctrl, 16, 19); + val = (config->vinst_ctrl & ETM_EXLEVEL_S_VICTLR_MASK) >> 16; return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -754,8 +754,8 @@ static ssize_t s_exlevel_vinst_store(struct device *dev, return -EINVAL; spin_lock(&drvdata->spinlock); - /* clear all EXLEVEL_S bits (bit[18] is never implemented) */ - config->vinst_ctrl &= ~(BIT(16) | BIT(17) | BIT(19)); + /* clear all EXLEVEL_S bits */ + config->vinst_ctrl &= ~(ETM_EXLEVEL_S_VICTLR_MASK); /* enable instruction tracing for corresponding exception level */ val &= drvdata->s_ex_level; config->vinst_ctrl |= (val << 16); @@ -773,7 +773,7 @@ static ssize_t ns_exlevel_vinst_show(struct device *dev, struct etmv4_config *config = &drvdata->config; /* EXLEVEL_NS, bits[23:20] */ - val = BMVAL(config->vinst_ctrl, 20, 23); + val = (config->vinst_ctrl & ETM_EXLEVEL_NS_VICTLR_MASK) >> 20; return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -789,8 +789,8 @@ static ssize_t ns_exlevel_vinst_store(struct device *dev, return -EINVAL; spin_lock(&drvdata->spinlock); - /* clear EXLEVEL_NS bits (bit[23] is never implemented */ - config->vinst_ctrl &= ~(BIT(20) | BIT(21) | BIT(22)); + /* clear EXLEVEL_NS bits */ + config->vinst_ctrl &= ~(ETM_EXLEVEL_NS_VICTLR_MASK); /* enable instruction tracing for corresponding exception level */ val &= drvdata->ns_ex_level; config->vinst_ctrl |= (val << 20); diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 8f98701cadc5..efe120925f9d 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -648,6 +648,7 @@ static void etm4_init_arch_data(void *info) * TRCARCHMAJ, bits[11:8] architecture major versin number */ drvdata->arch = BMVAL(etmidr1, 4, 11); + drvdata->config.arch = drvdata->arch; /* maximum size of resources */ etmidr2 = readl_relaxed(drvdata->base + TRCIDR2); @@ -799,6 +800,7 @@ static u64 etm4_get_ns_access_type(struct etmv4_config *config) static u64 etm4_get_access_type(struct etmv4_config *config) { u64 access_type = etm4_get_ns_access_type(config); + u64 s_hyp = (config->arch & 0x0f) >= 0x4 ? ETM_EXLEVEL_S_HYP : 0; /* * EXLEVEL_S, bits[11:8], don't trace anything happening @@ -806,7 +808,8 @@ static u64 etm4_get_access_type(struct etmv4_config *config) */ access_type |= (ETM_EXLEVEL_S_APP | ETM_EXLEVEL_S_OS | - ETM_EXLEVEL_S_HYP); + s_hyp | + ETM_EXLEVEL_S_MON); return access_type; } diff --git a/drivers/hwtracing/coresight/coresight-etm4x.h b/drivers/hwtracing/coresight/coresight-etm4x.h index 546d790cb01b..b873df38e7d8 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.h +++ b/drivers/hwtracing/coresight/coresight-etm4x.h @@ -181,17 +181,22 @@ /* PowerDown Control Register bits */ #define TRCPDCR_PU BIT(3) -/* secure state access levels */ +/* secure state access levels - TRCACATRn */ #define ETM_EXLEVEL_S_APP BIT(8) #define ETM_EXLEVEL_S_OS BIT(9) -#define ETM_EXLEVEL_S_NA BIT(10) -#define ETM_EXLEVEL_S_HYP BIT(11) -/* non-secure state access levels */ +#define ETM_EXLEVEL_S_HYP BIT(10) +#define ETM_EXLEVEL_S_MON BIT(11) +/* non-secure state access levels - TRCACATRn */ #define ETM_EXLEVEL_NS_APP BIT(12) #define ETM_EXLEVEL_NS_OS BIT(13) #define ETM_EXLEVEL_NS_HYP BIT(14) #define ETM_EXLEVEL_NS_NA BIT(15) +/* secure / non secure masks - TRCVICTLR, IDR3 */ +#define ETM_EXLEVEL_S_VICTLR_MASK GENMASK(19, 16) +/* NS MON (EL3) mode never implemented */ +#define ETM_EXLEVEL_NS_VICTLR_MASK GENMASK(22, 20) + /** * struct etmv4_config - configuration information related to an ETMv4 * @mode: Controls various modes supported by this ETM. @@ -238,6 +243,7 @@ * @vmid_mask0: VM ID comparator mask for comparator 0-3. * @vmid_mask1: VM ID comparator mask for comparator 4-7. * @ext_inp: External input selection. + * @arch: ETM architecture version (for arch dependent config). */ struct etmv4_config { u32 mode; @@ -280,6 +286,7 @@ struct etmv4_config { u32 vmid_mask0; u32 vmid_mask1; u32 ext_inp; + u8 arch; }; /** From 2fe6899e36aa174abefd017887f9cfe0cb60c43a Mon Sep 17 00:00:00 2001 From: Mike Leach Date: Mon, 4 Nov 2019 11:12:42 -0700 Subject: [PATCH 095/198] coresight: etm4x: Fix input validation for sysfs. A number of issues are fixed relating to sysfs input validation:- 1) bb_ctrl_store() - incorrect compare of bit select field to absolute value. Reworked per ETMv4 specification. 2) seq_event_store() - incorrect mask value - register has two event values. 3) cyc_threshold_store() - must mask with max before checking min otherwise wrapped values can set illegal value below min. 4) res_ctrl_store() - update to mask off all res0 bits. Reviewed-by: Leo Yan Reviewed-by: Mathieu Poirier Signed-off-by: Mike Leach Fixes: a77de2637c9eb ("coresight: etm4x: moving sysFS entries to a dedicated file") Cc: stable # 4.9+ Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20191104181251.26732-6-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- .../coresight/coresight-etm4x-sysfs.c | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c index b6984be0c515..cc8156318018 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c @@ -652,10 +652,13 @@ static ssize_t cyc_threshold_store(struct device *dev, if (kstrtoul(buf, 16, &val)) return -EINVAL; + + /* mask off max threshold before checking min value */ + val &= ETM_CYC_THRESHOLD_MASK; if (val < drvdata->ccitmin) return -EINVAL; - config->ccctlr = val & ETM_CYC_THRESHOLD_MASK; + config->ccctlr = val; return size; } static DEVICE_ATTR_RW(cyc_threshold); @@ -686,14 +689,16 @@ static ssize_t bb_ctrl_store(struct device *dev, return -EINVAL; if (!drvdata->nr_addr_cmp) return -EINVAL; + /* - * Bit[7:0] selects which address range comparator is used for - * branch broadcast control. + * Bit[8] controls include(1) / exclude(0), bits[0-7] select + * individual range comparators. If include then at least 1 + * range must be selected. */ - if (BMVAL(val, 0, 7) > drvdata->nr_addr_cmp) + if ((val & BIT(8)) && (BMVAL(val, 0, 7) == 0)) return -EINVAL; - config->bb_ctrl = val; + config->bb_ctrl = val & GENMASK(8, 0); return size; } static DEVICE_ATTR_RW(bb_ctrl); @@ -1324,8 +1329,8 @@ static ssize_t seq_event_store(struct device *dev, spin_lock(&drvdata->spinlock); idx = config->seq_idx; - /* RST, bits[7:0] */ - config->seq_ctrl[idx] = val & 0xFF; + /* Seq control has two masks B[15:8] F[7:0] */ + config->seq_ctrl[idx] = val & 0xFFFF; spin_unlock(&drvdata->spinlock); return size; } @@ -1580,7 +1585,7 @@ static ssize_t res_ctrl_store(struct device *dev, if (idx % 2 != 0) /* PAIRINV, bit[21] */ val &= ~BIT(21); - config->res_ctrl[idx] = val; + config->res_ctrl[idx] = val & GENMASK(21, 0); spin_unlock(&drvdata->spinlock); return size; } From 75198a7d4c0c3ee75f81ee2dbe908d9082ab4206 Mon Sep 17 00:00:00 2001 From: Mike Leach Date: Mon, 4 Nov 2019 11:12:43 -0700 Subject: [PATCH 096/198] coresight: etm4x: Add missing API to set EL match on address filters TRCACATRn registers have match bits for secure and non-secure exception levels which are not accessible by the sysfs API. This adds a new sysfs parameter to enable this - addr_exlevel_s_ns. Signed-off-by: Mike Leach Reviewed-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20191104181251.26732-7-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- .../coresight/coresight-etm4x-sysfs.c | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c index cc8156318018..97a33cf98797 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c @@ -1233,6 +1233,47 @@ static ssize_t addr_context_store(struct device *dev, } static DEVICE_ATTR_RW(addr_context); +static ssize_t addr_exlevel_s_ns_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u8 idx; + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; + + spin_lock(&drvdata->spinlock); + idx = config->addr_idx; + val = BMVAL(config->addr_acc[idx], 14, 8); + spin_unlock(&drvdata->spinlock); + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t addr_exlevel_s_ns_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 idx; + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; + + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + + if (val & ~((GENMASK(14, 8) >> 8))) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + idx = config->addr_idx; + /* clear Exlevel_ns & Exlevel_s bits[14:12, 11:8], bit[15] is res0 */ + config->addr_acc[idx] &= ~(GENMASK(14, 8)); + config->addr_acc[idx] |= (val << 8); + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(addr_exlevel_s_ns); + static ssize_t seq_idx_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -2038,6 +2079,7 @@ static struct attribute *coresight_etmv4_attrs[] = { &dev_attr_addr_stop.attr, &dev_attr_addr_ctxtype.attr, &dev_attr_addr_context.attr, + &dev_attr_addr_exlevel_s_ns.attr, &dev_attr_seq_idx.attr, &dev_attr_seq_state.attr, &dev_attr_seq_event.attr, From 1b6b0e087fc7c63ebaf9f78c69c7fdecb5fa8cd0 Mon Sep 17 00:00:00 2001 From: Mike Leach Date: Mon, 4 Nov 2019 11:12:44 -0700 Subject: [PATCH 097/198] coresight: etm4x: Fix issues with start-stop logic. Fixes the following issues when using the ETMv4 start-stop logic. 1) Setting a start or a stop address should not automatically set the start-stop status to 'on'. The value set by the user in 'mode' must be respected or start instances could be missed. 2) Missing API for controlling TRCVIPCSSCTLR - start stop control by PE comparators. 3) Default ETM configuration sets a trace all range, and correctly sets the start-stop status bit. This was not being correctly reflected in the 'mode' parameter. Signed-off-by: Mike Leach Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20191104181251.26732-8-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- .../coresight/coresight-etm4x-sysfs.c | 39 +++++++++++++++++-- drivers/hwtracing/coresight/coresight-etm4x.c | 1 + 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c index 97a33cf98797..ea1e034809a0 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c @@ -217,6 +217,7 @@ static ssize_t reset_store(struct device *dev, /* No start-stop filtering for ViewInst */ config->vissctlr = 0x0; + config->vipcssctlr = 0x0; /* Disable seq events */ for (i = 0; i < drvdata->nrseqstate-1; i++) @@ -1059,8 +1060,6 @@ static ssize_t addr_start_store(struct device *dev, config->addr_val[idx] = (u64)val; config->addr_type[idx] = ETM_ADDR_TYPE_START; config->vissctlr |= BIT(idx); - /* SSSTATUS, bit[9] - turn on start/stop logic */ - config->vinst_ctrl |= BIT(9); spin_unlock(&drvdata->spinlock); return size; } @@ -1116,8 +1115,6 @@ static ssize_t addr_stop_store(struct device *dev, config->addr_val[idx] = (u64)val; config->addr_type[idx] = ETM_ADDR_TYPE_STOP; config->vissctlr |= BIT(idx + 16); - /* SSSTATUS, bit[9] - turn on start/stop logic */ - config->vinst_ctrl |= BIT(9); spin_unlock(&drvdata->spinlock); return size; } @@ -1274,6 +1271,39 @@ static ssize_t addr_exlevel_s_ns_store(struct device *dev, } static DEVICE_ATTR_RW(addr_exlevel_s_ns); +static ssize_t vinst_pe_cmp_start_stop_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; + + if (!drvdata->nr_pe_cmp) + return -EINVAL; + val = config->vipcssctlr; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} +static ssize_t vinst_pe_cmp_start_stop_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + if (!drvdata->nr_pe_cmp) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + config->vipcssctlr = val; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(vinst_pe_cmp_start_stop); + static ssize_t seq_idx_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -2080,6 +2110,7 @@ static struct attribute *coresight_etmv4_attrs[] = { &dev_attr_addr_ctxtype.attr, &dev_attr_addr_context.attr, &dev_attr_addr_exlevel_s_ns.attr, + &dev_attr_vinst_pe_cmp_start_stop.attr, &dev_attr_seq_idx.attr, &dev_attr_seq_state.attr, &dev_attr_seq_event.attr, diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index efe120925f9d..d5148afdbe80 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -887,6 +887,7 @@ static void etm4_set_default_filter(struct etmv4_config *config) * in the started state */ config->vinst_ctrl |= BIT(9); + config->mode |= ETM_MODE_VIEWINST_STARTSTOP; /* No start-stop filtering for ViewInst */ config->vissctlr = 0x0; From c2431fed288a4a5771dc6470366c07fb42c691ef Mon Sep 17 00:00:00 2001 From: Mike Leach Date: Mon, 4 Nov 2019 11:12:45 -0700 Subject: [PATCH 098/198] coresight: etm4x: Improve usability of sysfs - include/exclude addr. Setting include / exclude on a range had to be done by setting the bit in 'mode' before setting the range. However, setting this bit also had the effect of altering the current range as well. Changed to only set include / exclude setting of a range at the point of setting that range. Either use a 3rd input parameter as the include exclude value, or if not present use the current value of 'mode'. Do not change current range when 'mode' changes. Signed-off-by: Mike Leach Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20191104181251.26732-9-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- .../hwtracing/coresight/coresight-etm4x-sysfs.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c index ea1e034809a0..8c056dd1a55e 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c @@ -297,8 +297,6 @@ static ssize_t mode_store(struct device *dev, spin_lock(&drvdata->spinlock); config->mode = val & ETMv4_MODE_ALL; - etm4_set_mode_exclude(drvdata, - config->mode & ETM_MODE_EXCLUDE ? true : false); if (drvdata->instrp0 == true) { /* start by clearing instruction P0 field */ @@ -972,8 +970,12 @@ static ssize_t addr_range_store(struct device *dev, unsigned long val1, val2; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); struct etmv4_config *config = &drvdata->config; + int elements, exclude; - if (sscanf(buf, "%lx %lx", &val1, &val2) != 2) + elements = sscanf(buf, "%lx %lx %x", &val1, &val2, &exclude); + + /* exclude is optional, but need at least two parameter */ + if (elements < 2) return -EINVAL; /* lower address comparator cannot have a higher address value */ if (val1 > val2) @@ -1001,9 +1003,11 @@ static ssize_t addr_range_store(struct device *dev, /* * Program include or exclude control bits for vinst or vdata * whenever we change addr comparators to ETM_ADDR_TYPE_RANGE + * use supplied value, or default to bit set in 'mode' */ - etm4_set_mode_exclude(drvdata, - config->mode & ETM_MODE_EXCLUDE ? true : false); + if (elements != 3) + exclude = config->mode & ETM_MODE_EXCLUDE; + etm4_set_mode_exclude(drvdata, exclude ? true : false); spin_unlock(&drvdata->spinlock); return size; From 3e12d3b01324a077a6313327d414a698584ef8c3 Mon Sep 17 00:00:00 2001 From: Mike Leach Date: Mon, 4 Nov 2019 11:12:46 -0700 Subject: [PATCH 099/198] coresight: etm4x: Improve usability of sysfs - CID and VMID masks. Context ID and VM ID masks required 2 value inputs, even when the second value is ignored as insufficient CID / VMID comparators are implemented. Permit a single value to be used if that is sufficient to cover all implemented comparators. Signed-off-by: Mike Leach Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20191104181251.26732-10-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm4x-sysfs.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c index 8c056dd1a55e..1cfbddda0b4d 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c @@ -1794,6 +1794,7 @@ static ssize_t ctxid_masks_store(struct device *dev, unsigned long val1, val2, mask; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); struct etmv4_config *config = &drvdata->config; + int nr_inputs; /* * Don't use contextID tracing if coming from a PID namespace. See @@ -1809,7 +1810,9 @@ static ssize_t ctxid_masks_store(struct device *dev, */ if (!drvdata->ctxid_size || !drvdata->numcidc) return -EINVAL; - if (sscanf(buf, "%lx %lx", &val1, &val2) != 2) + /* one mask if <= 4 comparators, two for up to 8 */ + nr_inputs = sscanf(buf, "%lx %lx", &val1, &val2); + if ((drvdata->numcidc > 4) && (nr_inputs != 2)) return -EINVAL; spin_lock(&drvdata->spinlock); @@ -1983,6 +1986,7 @@ static ssize_t vmid_masks_store(struct device *dev, unsigned long val1, val2, mask; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); struct etmv4_config *config = &drvdata->config; + int nr_inputs; /* * only implemented when vmid tracing is enabled, i.e. at least one @@ -1990,7 +1994,9 @@ static ssize_t vmid_masks_store(struct device *dev, */ if (!drvdata->vmid_size || !drvdata->numvmidc) return -EINVAL; - if (sscanf(buf, "%lx %lx", &val1, &val2) != 2) + /* one mask if <= 4 comparators, two for up to 8 */ + nr_inputs = sscanf(buf, "%lx %lx", &val1, &val2); + if ((drvdata->numvmidc > 4) && (nr_inputs != 2)) return -EINVAL; spin_lock(&drvdata->spinlock); From a578427de5b2bb7ac52f807505a6bea7fff93d4b Mon Sep 17 00:00:00 2001 From: Mike Leach Date: Mon, 4 Nov 2019 11:12:47 -0700 Subject: [PATCH 100/198] coresight: etm4x: Add view comparator settings API to sysfs. Currently it is not possible to view the current settings of a given address comparator without knowing what type it is set to. For example, if a comparator is set as an addr_start comparator, attempting to read addr_stop for the same index will result in an error. addr_cmp_view is added to allow the user to see the current settings of the indexed address comparator without resorting to trial and error when the set type is not known. Signed-off-by: Mike Leach Reviewed-by: Leo Yan Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20191104181251.26732-11-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- .../coresight/coresight-etm4x-sysfs.c | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c index 1cfbddda0b4d..1768e7286a9e 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c @@ -1275,6 +1275,57 @@ static ssize_t addr_exlevel_s_ns_store(struct device *dev, } static DEVICE_ATTR_RW(addr_exlevel_s_ns); +static const char * const addr_type_names[] = { + "unused", + "single", + "range", + "start", + "stop" +}; + +static ssize_t addr_cmp_view_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 idx, addr_type; + unsigned long addr_v, addr_v2, addr_ctrl; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; + int size = 0; + bool exclude = false; + + spin_lock(&drvdata->spinlock); + idx = config->addr_idx; + addr_v = config->addr_val[idx]; + addr_ctrl = config->addr_acc[idx]; + addr_type = config->addr_type[idx]; + if (addr_type == ETM_ADDR_TYPE_RANGE) { + if (idx & 0x1) { + idx -= 1; + addr_v2 = addr_v; + addr_v = config->addr_val[idx]; + } else { + addr_v2 = config->addr_val[idx + 1]; + } + exclude = config->viiectlr & BIT(idx / 2 + 16); + } + spin_unlock(&drvdata->spinlock); + if (addr_type) { + size = scnprintf(buf, PAGE_SIZE, "addr_cmp[%i] %s %#lx", idx, + addr_type_names[addr_type], addr_v); + if (addr_type == ETM_ADDR_TYPE_RANGE) { + size += scnprintf(buf + size, PAGE_SIZE - size, + " %#lx %s", addr_v2, + exclude ? "exclude" : "include"); + } + size += scnprintf(buf + size, PAGE_SIZE - size, + " ctrl(%#lx)\n", addr_ctrl); + } else { + size = scnprintf(buf, PAGE_SIZE, "addr_cmp[%i] unused\n", idx); + } + return size; +} +static DEVICE_ATTR_RO(addr_cmp_view); + static ssize_t vinst_pe_cmp_start_stop_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -2120,6 +2171,7 @@ static struct attribute *coresight_etmv4_attrs[] = { &dev_attr_addr_ctxtype.attr, &dev_attr_addr_context.attr, &dev_attr_addr_exlevel_s_ns.attr, + &dev_attr_addr_cmp_view.attr, &dev_attr_vinst_pe_cmp_start_stop.attr, &dev_attr_seq_idx.attr, &dev_attr_seq_state.attr, From ebddaad09e1067408c921c6919a1f6ad7eb3e646 Mon Sep 17 00:00:00 2001 From: Mike Leach Date: Mon, 4 Nov 2019 11:12:48 -0700 Subject: [PATCH 101/198] coresight: etm4x: Add missing single-shot control API to sysfs An API to control single-shot comparator operation was missing from sysfs. This adds the parameters to sysfs to allow programming of this feature. Signed-off-by: Mike Leach Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20191104181251.26732-12-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- .../coresight/coresight-etm4x-sysfs.c | 122 ++++++++++++++++++ drivers/hwtracing/coresight/coresight-etm4x.c | 26 +++- drivers/hwtracing/coresight/coresight-etm4x.h | 2 + 3 files changed, 149 insertions(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c index 1768e7286a9e..3fc12ac44270 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c @@ -239,6 +239,7 @@ static ssize_t reset_store(struct device *dev, for (i = 0; i < drvdata->nr_resource; i++) config->res_ctrl[i] = 0x0; + config->ss_idx = 0x0; for (i = 0; i < drvdata->nr_ss_cmp; i++) { config->ss_ctrl[i] = 0x0; config->ss_pe_cmp[i] = 0x0; @@ -1717,6 +1718,123 @@ static ssize_t res_ctrl_store(struct device *dev, } static DEVICE_ATTR_RW(res_ctrl); +static ssize_t sshot_idx_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; + + val = config->ss_idx; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t sshot_idx_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + if (val >= drvdata->nr_ss_cmp) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + config->ss_idx = val; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(sshot_idx); + +static ssize_t sshot_ctrl_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; + + spin_lock(&drvdata->spinlock); + val = config->ss_ctrl[config->ss_idx]; + spin_unlock(&drvdata->spinlock); + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t sshot_ctrl_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 idx; + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + idx = config->ss_idx; + config->ss_ctrl[idx] = val & GENMASK(24, 0); + /* must clear bit 31 in related status register on programming */ + config->ss_status[idx] &= ~BIT(31); + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(sshot_ctrl); + +static ssize_t sshot_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; + + spin_lock(&drvdata->spinlock); + val = config->ss_status[config->ss_idx]; + spin_unlock(&drvdata->spinlock); + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} +static DEVICE_ATTR_RO(sshot_status); + +static ssize_t sshot_pe_ctrl_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; + + spin_lock(&drvdata->spinlock); + val = config->ss_pe_cmp[config->ss_idx]; + spin_unlock(&drvdata->spinlock); + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t sshot_pe_ctrl_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 idx; + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + idx = config->ss_idx; + config->ss_pe_cmp[idx] = val & GENMASK(7, 0); + /* must clear bit 31 in related status register on programming */ + config->ss_status[idx] &= ~BIT(31); + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(sshot_pe_ctrl); + static ssize_t ctxid_idx_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -2173,6 +2291,10 @@ static struct attribute *coresight_etmv4_attrs[] = { &dev_attr_addr_exlevel_s_ns.attr, &dev_attr_addr_cmp_view.attr, &dev_attr_vinst_pe_cmp_start_stop.attr, + &dev_attr_sshot_idx.attr, + &dev_attr_sshot_ctrl.attr, + &dev_attr_sshot_pe_ctrl.attr, + &dev_attr_sshot_status.attr, &dev_attr_seq_idx.attr, &dev_attr_seq_state.attr, &dev_attr_seq_event.attr, diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index d5148afdbe80..dc3f507e7562 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -168,6 +168,9 @@ static int etm4_enable_hw(struct etmv4_drvdata *drvdata) drvdata->base + TRCRSCTLRn(i)); for (i = 0; i < drvdata->nr_ss_cmp; i++) { + /* always clear status bit on restart if using single-shot */ + if (config->ss_ctrl[i] || config->ss_pe_cmp[i]) + config->ss_status[i] &= ~BIT(31); writel_relaxed(config->ss_ctrl[i], drvdata->base + TRCSSCCRn(i)); writel_relaxed(config->ss_status[i], @@ -467,6 +470,9 @@ static void etm4_disable_hw(void *info) { u32 control; struct etmv4_drvdata *drvdata = info; + struct etmv4_config *config = &drvdata->config; + struct device *etm_dev = &drvdata->csdev->dev; + int i; CS_UNLOCK(drvdata->base); @@ -489,6 +495,18 @@ static void etm4_disable_hw(void *info) isb(); writel_relaxed(control, drvdata->base + TRCPRGCTLR); + /* wait for TRCSTATR.PMSTABLE to go to '1' */ + if (coresight_timeout(drvdata->base, TRCSTATR, + TRCSTATR_PMSTABLE_BIT, 1)) + dev_err(etm_dev, + "timeout while waiting for PM stable Trace Status\n"); + + /* read the status of the single shot comparators */ + for (i = 0; i < drvdata->nr_ss_cmp; i++) { + config->ss_status[i] = + readl_relaxed(drvdata->base + TRCSSCSRn(i)); + } + coresight_disclaim_device_unlocked(drvdata->base); CS_LOCK(drvdata->base); @@ -595,6 +613,7 @@ static void etm4_init_arch_data(void *info) u32 etmidr4; u32 etmidr5; struct etmv4_drvdata *drvdata = info; + int i; /* Make sure all registers are accessible */ etm4_os_unlock(drvdata); @@ -718,9 +737,14 @@ static void etm4_init_arch_data(void *info) drvdata->nr_resource = BMVAL(etmidr4, 16, 19) + 1; /* * NUMSSCC, bits[23:20] the number of single-shot - * comparator control for tracing + * comparator control for tracing. Read any status regs as these + * also contain RO capability data. */ drvdata->nr_ss_cmp = BMVAL(etmidr4, 20, 23); + for (i = 0; i < drvdata->nr_ss_cmp; i++) { + drvdata->config.ss_status[i] = + readl_relaxed(drvdata->base + TRCSSCSRn(i)); + } /* NUMCIDC, bits[27:24] number of Context ID comparators for tracing */ drvdata->numcidc = BMVAL(etmidr4, 24, 27); /* NUMVMIDC, bits[31:28] number of VMID comparators for tracing */ diff --git a/drivers/hwtracing/coresight/coresight-etm4x.h b/drivers/hwtracing/coresight/coresight-etm4x.h index b873df38e7d8..4a695bf90582 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.h +++ b/drivers/hwtracing/coresight/coresight-etm4x.h @@ -227,6 +227,7 @@ * @cntr_val: Sets or returns the value for a counter. * @res_idx: Resource index selector. * @res_ctrl: Controls the selection of the resources in the trace unit. + * @ss_idx: Single-shot index selector. * @ss_ctrl: Controls the corresponding single-shot comparator resource. * @ss_status: The status of the corresponding single-shot comparator. * @ss_pe_cmp: Selects the PE comparator inputs for Single-shot control. @@ -270,6 +271,7 @@ struct etmv4_config { u32 cntr_val[ETMv4_MAX_CNTR]; u8 res_idx; u32 res_ctrl[ETM_MAX_RES_SEL]; + u8 ss_idx; u32 ss_ctrl[ETM_MAX_SS_CMP]; u32 ss_status[ETM_MAX_SS_CMP]; u32 ss_pe_cmp[ETM_MAX_SS_CMP]; From f08d688223b04e16d259fea547ae15ecc6c44293 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 4 Nov 2019 11:12:49 -0700 Subject: [PATCH 102/198] coresight: Add explicit architecture dependency Coresight hardware is only likely to appear on Arm systems and currently the core code has Arm-specific barrier operations in it so can't be built anywhere else so add an explicit dependency saying so. This will make no practical difference currently due to the way subsystems are referenced, the subsystem is only pulled in on arm and arm64, so mainly serves as documentation in case someone wants to increase build coverage. Signed-off-by: Mark Brown Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20191104181251.26732-13-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig index 7a9f5fb08330..6ff30e25af55 100644 --- a/drivers/hwtracing/coresight/Kconfig +++ b/drivers/hwtracing/coresight/Kconfig @@ -4,6 +4,7 @@ # menuconfig CORESIGHT bool "CoreSight Tracing Support" + depends on ARM || ARM64 depends on OF || ACPI select ARM_AMBA select PERF_EVENTS From edda32dabedb01f98b9d7b9a4492c13357834bbe Mon Sep 17 00:00:00 2001 From: Yabin Cui Date: Mon, 4 Nov 2019 11:12:50 -0700 Subject: [PATCH 103/198] coresight: Serialize enabling/disabling a link device. When tracing etm data of multiple threads on multiple cpus through perf interface, some link devices are shared between paths of different cpus. It creates race conditions when different cpus wants to enable/disable the same link device at the same time. Example 1: Two cpus want to enable different ports of a coresight funnel, thus calling the funnel enable operation at the same time. But the funnel enable operation isn't reentrantable. Example 2: For an enabled coresight dynamic replicator with refcnt=1, one cpu wants to disable it, while another cpu wants to enable it. Ideally we still have an enabled replicator with refcnt=1 at the end. But in reality the result is uncertain. Since coresight devices claim themselves when enabled for self-hosted usage, the race conditions above usually make the link devices not usable after many cycles. To fix the race conditions, this patch uses spinlocks to serialize enabling/disabling link devices. Fixes: a06ae8609b3d ("coresight: add CoreSight core layer framework") Signed-off-by: Yabin Cui Signed-off-by: Mathieu Poirier Cc: stable # 5.3 Link: https://lore.kernel.org/r/20191104181251.26732-14-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- .../hwtracing/coresight/coresight-funnel.c | 32 ++++++++++--- .../coresight/coresight-replicator.c | 33 +++++++++++--- .../hwtracing/coresight/coresight-tmc-etf.c | 26 ++++++++--- drivers/hwtracing/coresight/coresight.c | 45 ++++++------------- 4 files changed, 87 insertions(+), 49 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c index 05f7896c3a01..b605889b507a 100644 --- a/drivers/hwtracing/coresight/coresight-funnel.c +++ b/drivers/hwtracing/coresight/coresight-funnel.c @@ -38,12 +38,14 @@ DEFINE_CORESIGHT_DEVLIST(funnel_devs, "funnel"); * @atclk: optional clock for the core parts of the funnel. * @csdev: component vitals needed by the framework. * @priority: port selection order. + * @spinlock: serialize enable/disable operations. */ struct funnel_drvdata { void __iomem *base; struct clk *atclk; struct coresight_device *csdev; unsigned long priority; + spinlock_t spinlock; }; static int dynamic_funnel_enable_hw(struct funnel_drvdata *drvdata, int port) @@ -76,11 +78,21 @@ static int funnel_enable(struct coresight_device *csdev, int inport, { int rc = 0; struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + unsigned long flags; + bool first_enable = false; - if (drvdata->base) - rc = dynamic_funnel_enable_hw(drvdata, inport); - + spin_lock_irqsave(&drvdata->spinlock, flags); + if (atomic_read(&csdev->refcnt[inport]) == 0) { + if (drvdata->base) + rc = dynamic_funnel_enable_hw(drvdata, inport); + if (!rc) + first_enable = true; + } if (!rc) + atomic_inc(&csdev->refcnt[inport]); + spin_unlock_irqrestore(&drvdata->spinlock, flags); + + if (first_enable) dev_dbg(&csdev->dev, "FUNNEL inport %d enabled\n", inport); return rc; } @@ -107,11 +119,19 @@ static void funnel_disable(struct coresight_device *csdev, int inport, int outport) { struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + unsigned long flags; + bool last_disable = false; - if (drvdata->base) - dynamic_funnel_disable_hw(drvdata, inport); + spin_lock_irqsave(&drvdata->spinlock, flags); + if (atomic_dec_return(&csdev->refcnt[inport]) == 0) { + if (drvdata->base) + dynamic_funnel_disable_hw(drvdata, inport); + last_disable = true; + } + spin_unlock_irqrestore(&drvdata->spinlock, flags); - dev_dbg(&csdev->dev, "FUNNEL inport %d disabled\n", inport); + if (last_disable) + dev_dbg(&csdev->dev, "FUNNEL inport %d disabled\n", inport); } static const struct coresight_ops_link funnel_link_ops = { diff --git a/drivers/hwtracing/coresight/coresight-replicator.c b/drivers/hwtracing/coresight/coresight-replicator.c index b29ba640eb25..43304196a1a6 100644 --- a/drivers/hwtracing/coresight/coresight-replicator.c +++ b/drivers/hwtracing/coresight/coresight-replicator.c @@ -31,11 +31,13 @@ DEFINE_CORESIGHT_DEVLIST(replicator_devs, "replicator"); * whether this one is programmable or not. * @atclk: optional clock for the core parts of the replicator. * @csdev: component vitals needed by the framework + * @spinlock: serialize enable/disable operations. */ struct replicator_drvdata { void __iomem *base; struct clk *atclk; struct coresight_device *csdev; + spinlock_t spinlock; }; static void dynamic_replicator_reset(struct replicator_drvdata *drvdata) @@ -97,10 +99,22 @@ static int replicator_enable(struct coresight_device *csdev, int inport, { int rc = 0; struct replicator_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + unsigned long flags; + bool first_enable = false; - if (drvdata->base) - rc = dynamic_replicator_enable(drvdata, inport, outport); + spin_lock_irqsave(&drvdata->spinlock, flags); + if (atomic_read(&csdev->refcnt[outport]) == 0) { + if (drvdata->base) + rc = dynamic_replicator_enable(drvdata, inport, + outport); + if (!rc) + first_enable = true; + } if (!rc) + atomic_inc(&csdev->refcnt[outport]); + spin_unlock_irqrestore(&drvdata->spinlock, flags); + + if (first_enable) dev_dbg(&csdev->dev, "REPLICATOR enabled\n"); return rc; } @@ -137,10 +151,19 @@ static void replicator_disable(struct coresight_device *csdev, int inport, int outport) { struct replicator_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + unsigned long flags; + bool last_disable = false; - if (drvdata->base) - dynamic_replicator_disable(drvdata, inport, outport); - dev_dbg(&csdev->dev, "REPLICATOR disabled\n"); + spin_lock_irqsave(&drvdata->spinlock, flags); + if (atomic_dec_return(&csdev->refcnt[outport]) == 0) { + if (drvdata->base) + dynamic_replicator_disable(drvdata, inport, outport); + last_disable = true; + } + spin_unlock_irqrestore(&drvdata->spinlock, flags); + + if (last_disable) + dev_dbg(&csdev->dev, "REPLICATOR disabled\n"); } static const struct coresight_ops_link replicator_link_ops = { diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index 807416b75ecc..d0cc3985b72a 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -334,9 +334,10 @@ static int tmc_disable_etf_sink(struct coresight_device *csdev) static int tmc_enable_etf_link(struct coresight_device *csdev, int inport, int outport) { - int ret; + int ret = 0; unsigned long flags; struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + bool first_enable = false; spin_lock_irqsave(&drvdata->spinlock, flags); if (drvdata->reading) { @@ -344,12 +345,18 @@ static int tmc_enable_etf_link(struct coresight_device *csdev, return -EBUSY; } - ret = tmc_etf_enable_hw(drvdata); + if (atomic_read(&csdev->refcnt[0]) == 0) { + ret = tmc_etf_enable_hw(drvdata); + if (!ret) { + drvdata->mode = CS_MODE_SYSFS; + first_enable = true; + } + } if (!ret) - drvdata->mode = CS_MODE_SYSFS; + atomic_inc(&csdev->refcnt[0]); spin_unlock_irqrestore(&drvdata->spinlock, flags); - if (!ret) + if (first_enable) dev_dbg(&csdev->dev, "TMC-ETF enabled\n"); return ret; } @@ -359,6 +366,7 @@ static void tmc_disable_etf_link(struct coresight_device *csdev, { unsigned long flags; struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + bool last_disable = false; spin_lock_irqsave(&drvdata->spinlock, flags); if (drvdata->reading) { @@ -366,11 +374,15 @@ static void tmc_disable_etf_link(struct coresight_device *csdev, return; } - tmc_etf_disable_hw(drvdata); - drvdata->mode = CS_MODE_DISABLED; + if (atomic_dec_return(&csdev->refcnt[0]) == 0) { + tmc_etf_disable_hw(drvdata); + drvdata->mode = CS_MODE_DISABLED; + last_disable = true; + } spin_unlock_irqrestore(&drvdata->spinlock, flags); - dev_dbg(&csdev->dev, "TMC-ETF disabled\n"); + if (last_disable) + dev_dbg(&csdev->dev, "TMC-ETF disabled\n"); } static void *tmc_alloc_etf_buffer(struct coresight_device *csdev, diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index e6ca899fea4e..ef20f74c85fa 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -253,9 +253,9 @@ static int coresight_enable_link(struct coresight_device *csdev, struct coresight_device *parent, struct coresight_device *child) { - int ret; + int ret = 0; int link_subtype; - int refport, inport, outport; + int inport, outport; if (!parent || !child) return -EINVAL; @@ -264,29 +264,17 @@ static int coresight_enable_link(struct coresight_device *csdev, outport = coresight_find_link_outport(csdev, child); link_subtype = csdev->subtype.link_subtype; - if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG) - refport = inport; - else if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_SPLIT) - refport = outport; - else - refport = 0; + if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG && inport < 0) + return inport; + if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_SPLIT && outport < 0) + return outport; - if (refport < 0) - return refport; + if (link_ops(csdev)->enable) + ret = link_ops(csdev)->enable(csdev, inport, outport); + if (!ret) + csdev->enable = true; - if (atomic_inc_return(&csdev->refcnt[refport]) == 1) { - if (link_ops(csdev)->enable) { - ret = link_ops(csdev)->enable(csdev, inport, outport); - if (ret) { - atomic_dec(&csdev->refcnt[refport]); - return ret; - } - } - } - - csdev->enable = true; - - return 0; + return ret; } static void coresight_disable_link(struct coresight_device *csdev, @@ -295,7 +283,7 @@ static void coresight_disable_link(struct coresight_device *csdev, { int i, nr_conns; int link_subtype; - int refport, inport, outport; + int inport, outport; if (!parent || !child) return; @@ -305,20 +293,15 @@ static void coresight_disable_link(struct coresight_device *csdev, link_subtype = csdev->subtype.link_subtype; if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG) { - refport = inport; nr_conns = csdev->pdata->nr_inport; } else if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_SPLIT) { - refport = outport; nr_conns = csdev->pdata->nr_outport; } else { - refport = 0; nr_conns = 1; } - if (atomic_dec_return(&csdev->refcnt[refport]) == 0) { - if (link_ops(csdev)->disable) - link_ops(csdev)->disable(csdev, inport, outport); - } + if (link_ops(csdev)->disable) + link_ops(csdev)->disable(csdev, inport, outport); for (i = 0; i < nr_conns; i++) if (atomic_read(&csdev->refcnt[i]) != 0) From 9c7db4947506eed0e2346d8ada701d50a400fbbd Mon Sep 17 00:00:00 2001 From: Rikard Falkeborn Date: Mon, 4 Nov 2019 11:12:51 -0700 Subject: [PATCH 104/198] coresight: etm4x: Fix BMVAL misuse The second argument should be the lsb and the third argument should be the msb. Signed-off-by: Rikard Falkeborn Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20191104181251.26732-15-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm4x-sysfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c index 3fc12ac44270..ce41482431f9 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c @@ -1246,7 +1246,7 @@ static ssize_t addr_exlevel_s_ns_show(struct device *dev, spin_lock(&drvdata->spinlock); idx = config->addr_idx; - val = BMVAL(config->addr_acc[idx], 14, 8); + val = BMVAL(config->addr_acc[idx], 8, 14); spin_unlock(&drvdata->spinlock); return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } From 7a2b9e6ec84588b0be65cc0ae45a65bac431496b Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Tue, 5 Nov 2019 17:05:13 +0200 Subject: [PATCH 105/198] mei: bus: prefix device names on bus with the bus name Add parent device name to the name of devices on bus to avoid device names collisions for same client UUID available from different MEI heads. Namely this prevents sysfs collision under /sys/bus/mei/device/ In the device part leave just UUID other parameters that are required for device matching are not required here and are just bloating the name. Cc: Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Link: https://lore.kernel.org/r/20191105150514.14010-1-tomas.winkler@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/bus.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 985bd4fd3328..53bb394ccba6 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -873,15 +873,16 @@ static const struct device_type mei_cl_device_type = { /** * mei_cl_bus_set_name - set device name for me client device + * - + * Example: 0000:00:16.0-55213584-9a29-4916-badf-0fb7ed682aeb * * @cldev: me client device */ static inline void mei_cl_bus_set_name(struct mei_cl_device *cldev) { - dev_set_name(&cldev->dev, "mei:%s:%pUl:%02X", - cldev->name, - mei_me_cl_uuid(cldev->me_cl), - mei_me_cl_ver(cldev->me_cl)); + dev_set_name(&cldev->dev, "%s-%pUl", + dev_name(cldev->bus->dev), + mei_me_cl_uuid(cldev->me_cl)); } /** From 82b29b9f72afdccb40ea5f3c13c6a3cb65a597bc Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Tue, 5 Nov 2019 17:05:14 +0200 Subject: [PATCH 106/198] mei: me: add comet point V device id Comet Point (Comet Lake) V device id. Cc: Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Link: https://lore.kernel.org/r/20191105150514.14010-2-tomas.winkler@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/hw-me-regs.h | 1 + drivers/misc/mei/pci-me.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h index c09f8bb49495..b359f06f05e7 100644 --- a/drivers/misc/mei/hw-me-regs.h +++ b/drivers/misc/mei/hw-me-regs.h @@ -81,6 +81,7 @@ #define MEI_DEV_ID_CMP_LP 0x02e0 /* Comet Point LP */ #define MEI_DEV_ID_CMP_LP_3 0x02e4 /* Comet Point LP 3 (iTouch) */ +#define MEI_DEV_ID_CMP_V 0xA3BA /* Comet Point Lake V */ #define MEI_DEV_ID_ICP_LP 0x34E0 /* Ice Lake Point LP */ diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index 3dca63eddaa0..ce43415a536c 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -98,6 +98,7 @@ static const struct pci_device_id mei_me_pci_tbl[] = { {MEI_PCI_DEVICE(MEI_DEV_ID_CMP_LP, MEI_ME_PCH12_CFG)}, {MEI_PCI_DEVICE(MEI_DEV_ID_CMP_LP_3, MEI_ME_PCH8_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_CMP_V, MEI_ME_PCH12_CFG)}, {MEI_PCI_DEVICE(MEI_DEV_ID_ICP_LP, MEI_ME_PCH12_CFG)}, From 8670b2b8b029a6650d133486be9d2ace146fd29a Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 24 Oct 2019 19:40:42 +0200 Subject: [PATCH 107/198] rfkill: allocate static minor udev has a feature of creating /dev/ device-nodes if it finds a devnode: modalias. This allows for auto-loading of modules that provide the node. This requires to use a statically allocated minor number for misc character devices. However, rfkill uses dynamic minor numbers and prevents auto-loading of the module. So allocate the next static misc minor number and use it for rfkill. Signed-off-by: Marcel Holtmann Link: https://lore.kernel.org/r/20191024174042.19851-1-marcel@holtmann.org Signed-off-by: Greg Kroah-Hartman --- include/linux/miscdevice.h | 1 + net/rfkill/core.c | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h index 3247a3dc7934..b06b75776a32 100644 --- a/include/linux/miscdevice.h +++ b/include/linux/miscdevice.h @@ -57,6 +57,7 @@ #define UHID_MINOR 239 #define USERIO_MINOR 240 #define VHOST_VSOCK_MINOR 241 +#define RFKILL_MINOR 242 #define MISC_DYNAMIC_MINOR 255 struct device; diff --git a/net/rfkill/core.c b/net/rfkill/core.c index f9b08a6d8dbe..0bf9bf1ceb8f 100644 --- a/net/rfkill/core.c +++ b/net/rfkill/core.c @@ -1316,10 +1316,12 @@ static const struct file_operations rfkill_fops = { .llseek = no_llseek, }; +#define RFKILL_NAME "rfkill" + static struct miscdevice rfkill_miscdev = { - .name = "rfkill", .fops = &rfkill_fops, - .minor = MISC_DYNAMIC_MINOR, + .name = RFKILL_NAME, + .minor = RFKILL_MINOR, }; static int __init rfkill_init(void) @@ -1371,3 +1373,6 @@ static void __exit rfkill_exit(void) class_unregister(&rfkill_class); } module_exit(rfkill_exit); + +MODULE_ALIAS_MISCDEV(RFKILL_MINOR); +MODULE_ALIAS("devname:" RFKILL_NAME); From 482c86cc37b7fa884843ec37edd1847c5463e3a9 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 16 Oct 2019 17:25:46 +0800 Subject: [PATCH 108/198] char: xillybus: use devm_platform_ioremap_resource() to simplify code Use devm_platform_ioremap_resource() to simplify the code a bit. This is detected by coccinelle. Signed-off-by: YueHaibing Acked-by: Eli Billauer Link: https://lore.kernel.org/r/20191016092546.26332-1-yuehaibing@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/char/xillybus/xillybus_of.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/char/xillybus/xillybus_of.c b/drivers/char/xillybus/xillybus_of.c index bfafd8f5e826..96b6de8a30e5 100644 --- a/drivers/char/xillybus/xillybus_of.c +++ b/drivers/char/xillybus/xillybus_of.c @@ -116,7 +116,6 @@ static int xilly_drv_probe(struct platform_device *op) struct xilly_endpoint *endpoint; int rc; int irq; - struct resource *res; struct xilly_endpoint_hardware *ephw = &of_hw; if (of_property_read_bool(dev->of_node, "dma-coherent")) @@ -129,9 +128,7 @@ static int xilly_drv_probe(struct platform_device *op) dev_set_drvdata(dev, endpoint); - res = platform_get_resource(op, IORESOURCE_MEM, 0); - endpoint->registers = devm_ioremap_resource(dev, res); - + endpoint->registers = devm_platform_ioremap_resource(op, 0); if (IS_ERR(endpoint->registers)) return PTR_ERR(endpoint->registers); From 9b8303fc6efa724bd6a90656434fbde2cc6ceb2c Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Tue, 29 Oct 2019 11:42:31 +0000 Subject: [PATCH 109/198] nvmem: core: fix nvmem_cell_write inline function nvmem_cell_write's buf argument uses different types based on the configuration of CONFIG_NVMEM. The function prototype for enabled NVMEM uses 'void *' type, but the static dummy function for disabled NVMEM uses 'const char *' instead. Fix the different behaviour by always expecting a 'void *' typed buf argument. Fixes: 7a78a7f7695b ("power: reset: nvmem-reboot-mode: use NVMEM as reboot mode write interface") Reported-by: kbuild test robot Cc: Han Nandor Cc: Srinivas Kandagatla Cc: linux-kernel@vger.kernel.org Signed-off-by: Sebastian Reichel Reviewed-By: Han Nandor Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20191029114240.14905-2-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- include/linux/nvmem-consumer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/nvmem-consumer.h b/include/linux/nvmem-consumer.h index 8f8be5b00060..5c17cb733224 100644 --- a/include/linux/nvmem-consumer.h +++ b/include/linux/nvmem-consumer.h @@ -118,7 +118,7 @@ static inline void *nvmem_cell_read(struct nvmem_cell *cell, size_t *len) } static inline int nvmem_cell_write(struct nvmem_cell *cell, - const char *buf, size_t len) + void *buf, size_t len) { return -EOPNOTSUPP; } From 1e6d8e5f44414de1bb6d11b7ff69e8385b5007d0 Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Tue, 29 Oct 2019 11:42:32 +0000 Subject: [PATCH 110/198] nvmem: sc27xx: Change to use devm_hwspin_lock_request_specific() to request one hwlock Change to use devm_hwspin_lock_request_specific() to help to simplify the cleanup code for drivers requesting one hwlock. Thus we can remove the redundant sc27xx_efuse_remove() and platform_set_drvdata(). Signed-off-by: Baolin Wang Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20191029114240.14905-3-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/sc27xx-efuse.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/nvmem/sc27xx-efuse.c b/drivers/nvmem/sc27xx-efuse.c index c6ee21018d80..ab5e7e0bc3d8 100644 --- a/drivers/nvmem/sc27xx-efuse.c +++ b/drivers/nvmem/sc27xx-efuse.c @@ -211,7 +211,7 @@ static int sc27xx_efuse_probe(struct platform_device *pdev) return ret; } - efuse->hwlock = hwspin_lock_request_specific(ret); + efuse->hwlock = devm_hwspin_lock_request_specific(&pdev->dev, ret); if (!efuse->hwlock) { dev_err(&pdev->dev, "failed to request hwspinlock\n"); return -ENXIO; @@ -219,7 +219,6 @@ static int sc27xx_efuse_probe(struct platform_device *pdev) mutex_init(&efuse->mutex); efuse->dev = &pdev->dev; - platform_set_drvdata(pdev, efuse); econfig.stride = 1; econfig.word_size = 1; @@ -232,21 +231,12 @@ static int sc27xx_efuse_probe(struct platform_device *pdev) nvmem = devm_nvmem_register(&pdev->dev, &econfig); if (IS_ERR(nvmem)) { dev_err(&pdev->dev, "failed to register nvmem config\n"); - hwspin_lock_free(efuse->hwlock); return PTR_ERR(nvmem); } return 0; } -static int sc27xx_efuse_remove(struct platform_device *pdev) -{ - struct sc27xx_efuse *efuse = platform_get_drvdata(pdev); - - hwspin_lock_free(efuse->hwlock); - return 0; -} - static const struct of_device_id sc27xx_efuse_of_match[] = { { .compatible = "sprd,sc2731-efuse" }, { } @@ -254,7 +244,6 @@ static const struct of_device_id sc27xx_efuse_of_match[] = { static struct platform_driver sc27xx_efuse_driver = { .probe = sc27xx_efuse_probe, - .remove = sc27xx_efuse_remove, .driver = { .name = "sc27xx-efuse", .of_match_table = sc27xx_efuse_of_match, From f8017bfc14146d3f392aad08d060dfde5fd563b0 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Tue, 29 Oct 2019 11:42:33 +0000 Subject: [PATCH 111/198] nvmem: imx: scu: support hole region check Introduce HOLE/ECC_REGION flag and in_hole helper to ease the check of hole region. The ECC_REGION is also introduced here which is preparing for programming support. ECC_REGION could only be programmed once, so need take care. Signed-off-by: Peng Fan Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20191029114240.14905-4-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/imx-ocotp-scu.c | 47 +++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 5 deletions(-) diff --git a/drivers/nvmem/imx-ocotp-scu.c b/drivers/nvmem/imx-ocotp-scu.c index 61a17f943f47..030e27ba4dfb 100644 --- a/drivers/nvmem/imx-ocotp-scu.c +++ b/drivers/nvmem/imx-ocotp-scu.c @@ -19,9 +19,20 @@ enum ocotp_devtype { IMX8QM, }; +#define ECC_REGION BIT(0) +#define HOLE_REGION BIT(1) + +struct ocotp_region { + u32 start; + u32 end; + u32 flag; +}; + struct ocotp_devtype_data { int devtype; int nregs; + u32 num_region; + struct ocotp_region region[]; }; struct ocotp_priv { @@ -38,13 +49,41 @@ struct imx_sc_msg_misc_fuse_read { static struct ocotp_devtype_data imx8qxp_data = { .devtype = IMX8QXP, .nregs = 800, + .num_region = 3, + .region = { + {0x10, 0x10f, ECC_REGION}, + {0x110, 0x21F, HOLE_REGION}, + {0x220, 0x31F, ECC_REGION}, + }, }; static struct ocotp_devtype_data imx8qm_data = { .devtype = IMX8QM, .nregs = 800, + .num_region = 2, + .region = { + {0x10, 0x10f, ECC_REGION}, + {0x1a0, 0x1ff, ECC_REGION}, + }, }; +static bool in_hole(void *context, u32 index) +{ + struct ocotp_priv *priv = context; + const struct ocotp_devtype_data *data = priv->data; + int i; + + for (i = 0; i < data->num_region; i++) { + if (data->region[i].flag & HOLE_REGION) { + if ((index >= data->region[i].start) && + (index <= data->region[i].end)) + return true; + } + } + + return false; +} + static int imx_sc_misc_otp_fuse_read(struct imx_sc_ipc *ipc, u32 word, u32 *val) { @@ -91,11 +130,9 @@ static int imx_scu_ocotp_read(void *context, unsigned int offset, buf = p; for (i = index; i < (index + count); i++) { - if (priv->data->devtype == IMX8QXP) { - if ((i > 271) && (i < 544)) { - *buf++ = 0; - continue; - } + if (in_hole(context, i)) { + *buf++ = 0; + continue; } ret = imx_sc_misc_otp_fuse_read(priv->nvmem_ipc, i, buf); From 885ce72a09d072a3d75231c68e27f21c956c9f46 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Tue, 29 Oct 2019 11:42:34 +0000 Subject: [PATCH 112/198] nvmem: imx: scu: support write The fuse programming from non-secure world is blocked, so we could only use Arm Trusted Firmware SIP call to let ATF program fuse. Because there is ECC region that could only be programmed once, so add a heler in_ecc to check the ecc region. Signed-off-by: Peng Fan Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20191029114240.14905-5-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/imx-ocotp-scu.c | 73 ++++++++++++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/drivers/nvmem/imx-ocotp-scu.c b/drivers/nvmem/imx-ocotp-scu.c index 030e27ba4dfb..03f1ab23ad51 100644 --- a/drivers/nvmem/imx-ocotp-scu.c +++ b/drivers/nvmem/imx-ocotp-scu.c @@ -7,6 +7,7 @@ * Peng Fan */ +#include #include #include #include @@ -14,6 +15,9 @@ #include #include +#define IMX_SIP_OTP 0xC200000A +#define IMX_SIP_OTP_WRITE 0x2 + enum ocotp_devtype { IMX8QXP, IMX8QM, @@ -46,6 +50,8 @@ struct imx_sc_msg_misc_fuse_read { u32 word; } __packed; +static DEFINE_MUTEX(scu_ocotp_mutex); + static struct ocotp_devtype_data imx8qxp_data = { .devtype = IMX8QXP, .nregs = 800, @@ -84,6 +90,23 @@ static bool in_hole(void *context, u32 index) return false; } +static bool in_ecc(void *context, u32 index) +{ + struct ocotp_priv *priv = context; + const struct ocotp_devtype_data *data = priv->data; + int i; + + for (i = 0; i < data->num_region; i++) { + if (data->region[i].flag & ECC_REGION) { + if ((index >= data->region[i].start) && + (index <= data->region[i].end)) + return true; + } + } + + return false; +} + static int imx_sc_misc_otp_fuse_read(struct imx_sc_ipc *ipc, u32 word, u32 *val) { @@ -127,6 +150,8 @@ static int imx_scu_ocotp_read(void *context, unsigned int offset, if (!p) return -ENOMEM; + mutex_lock(&scu_ocotp_mutex); + buf = p; for (i = index; i < (index + count); i++) { @@ -137,6 +162,7 @@ static int imx_scu_ocotp_read(void *context, unsigned int offset, ret = imx_sc_misc_otp_fuse_read(priv->nvmem_ipc, i, buf); if (ret) { + mutex_unlock(&scu_ocotp_mutex); kfree(p); return ret; } @@ -145,18 +171,63 @@ static int imx_scu_ocotp_read(void *context, unsigned int offset, memcpy(val, (u8 *)p + offset % 4, bytes); + mutex_unlock(&scu_ocotp_mutex); + kfree(p); return 0; } +static int imx_scu_ocotp_write(void *context, unsigned int offset, + void *val, size_t bytes) +{ + struct ocotp_priv *priv = context; + struct arm_smccc_res res; + u32 *buf = val; + u32 tmp; + u32 index; + int ret; + + /* allow only writing one complete OTP word at a time */ + if ((bytes != 4) || (offset % 4)) + return -EINVAL; + + index = offset >> 2; + + if (in_hole(context, index)) + return -EINVAL; + + if (in_ecc(context, index)) { + pr_warn("ECC region, only program once\n"); + mutex_lock(&scu_ocotp_mutex); + ret = imx_sc_misc_otp_fuse_read(priv->nvmem_ipc, index, &tmp); + mutex_unlock(&scu_ocotp_mutex); + if (ret) + return ret; + if (tmp) { + pr_warn("ECC region, already has value: %x\n", tmp); + return -EIO; + } + } + + mutex_lock(&scu_ocotp_mutex); + + arm_smccc_smc(IMX_SIP_OTP, IMX_SIP_OTP_WRITE, index, *buf, + 0, 0, 0, 0, &res); + + mutex_unlock(&scu_ocotp_mutex); + + return res.a0; +} + static struct nvmem_config imx_scu_ocotp_nvmem_config = { .name = "imx-scu-ocotp", - .read_only = true, + .read_only = false, .word_size = 4, .stride = 1, .owner = THIS_MODULE, .reg_read = imx_scu_ocotp_read, + .reg_write = imx_scu_ocotp_write, }; static const struct of_device_id imx_scu_ocotp_dt_ids[] = { From c33c585f1b3a99d53920bdac614aca461d8db06f Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Tue, 29 Oct 2019 11:42:35 +0000 Subject: [PATCH 113/198] nvmem: imx-ocotp: reset error status on probe If software running before the OCOTP driver is loaded left the controller with the error status pending, the driver will never be able to complete the read timing setup. Reset the error status on probe to make sure the controller is in usable state. Signed-off-by: Lucas Stach Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20191029114240.14905-6-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/imx-ocotp.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/nvmem/imx-ocotp.c b/drivers/nvmem/imx-ocotp.c index dff2f3c357f5..fc40555ca4cd 100644 --- a/drivers/nvmem/imx-ocotp.c +++ b/drivers/nvmem/imx-ocotp.c @@ -521,6 +521,10 @@ static int imx_ocotp_probe(struct platform_device *pdev) if (IS_ERR(priv->clk)) return PTR_ERR(priv->clk); + clk_prepare_enable(priv->clk); + imx_ocotp_clr_err_if_set(priv->base); + clk_disable_unprepare(priv->clk); + priv->params = of_device_get_match_data(&pdev->dev); imx_ocotp_nvmem_config.size = 4 * priv->params->nregs; imx_ocotp_nvmem_config.dev = dev; From 95d25206eb1dd03677b87f54792816ef0ef6ce08 Mon Sep 17 00:00:00 2001 From: Freeman Liu Date: Tue, 29 Oct 2019 11:42:36 +0000 Subject: [PATCH 114/198] dt-bindings: nvmem: Add Spreadtrum eFuse controller documentation This patch adds the binding documentation for Spreadtrum eFuse controller. Signed-off-by: Freeman Liu Signed-off-by: Baolin Wang Reviewed-by: Rob Herring Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20191029114240.14905-7-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/nvmem/sprd-efuse.txt | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 Documentation/devicetree/bindings/nvmem/sprd-efuse.txt diff --git a/Documentation/devicetree/bindings/nvmem/sprd-efuse.txt b/Documentation/devicetree/bindings/nvmem/sprd-efuse.txt new file mode 100644 index 000000000000..96b6feec27f0 --- /dev/null +++ b/Documentation/devicetree/bindings/nvmem/sprd-efuse.txt @@ -0,0 +1,39 @@ += Spreadtrum eFuse device tree bindings = + +Required properties: +- compatible: Should be "sprd,ums312-efuse". +- reg: Specify the address offset of efuse controller. +- clock-names: Should be "enable". +- clocks: The phandle and specifier referencing the controller's clock. +- hwlocks: Reference to a phandle of a hwlock provider node. + += Data cells = +Are child nodes of eFuse, bindings of which as described in +bindings/nvmem/nvmem.txt + +Example: + + ap_efuse: efuse@32240000 { + compatible = "sprd,ums312-efuse"; + reg = <0 0x32240000 0 0x10000>; + clock-names = "enable"; + hwlocks = <&hwlock 8>; + clocks = <&aonapb_gate CLK_EFUSE_EB>; + + /* Data cells */ + thermal_calib: calib@10 { + reg = <0x10 0x2>; + }; + }; + += Data consumers = +Are device nodes which consume nvmem data cells. + +Example: + + thermal { + ... + + nvmem-cells = <&thermal_calib>; + nvmem-cell-names = "calibration"; + }; From 096030e7f449caac81ee140d10d9b4b360b723fd Mon Sep 17 00:00:00 2001 From: Freeman Liu Date: Tue, 29 Oct 2019 11:42:37 +0000 Subject: [PATCH 115/198] nvmem: sprd: Add Spreadtrum SoCs eFuse support The Spreadtrum eFuse controller is widely used to dump chip ID, configuration setting, function select and so on, as well as supporting one-time programming. Signed-off-by: Freeman Liu Signed-off-by: Baolin Wang Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20191029114240.14905-8-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/Kconfig | 11 + drivers/nvmem/Makefile | 2 + drivers/nvmem/sprd-efuse.c | 424 +++++++++++++++++++++++++++++++++++++ 3 files changed, 437 insertions(+) create mode 100644 drivers/nvmem/sprd-efuse.c diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index c2ec750cae6e..8fd425d38d97 100644 --- a/drivers/nvmem/Kconfig +++ b/drivers/nvmem/Kconfig @@ -230,4 +230,15 @@ config NVMEM_ZYNQMP If sure, say yes. If unsure, say no. +config SPRD_EFUSE + tristate "Spreadtrum SoC eFuse Support" + depends on ARCH_SPRD || COMPILE_TEST + depends on HAS_IOMEM + help + This is a simple driver to dump specified values of Spreadtrum + SoCs from eFuse. + + This driver can also be built as a module. If so, the module + will be called nvmem-sprd-efuse. + endif diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile index e5c153d99a67..7c19870a6bbe 100644 --- a/drivers/nvmem/Makefile +++ b/drivers/nvmem/Makefile @@ -50,3 +50,5 @@ obj-$(CONFIG_SC27XX_EFUSE) += nvmem-sc27xx-efuse.o nvmem-sc27xx-efuse-y := sc27xx-efuse.o obj-$(CONFIG_NVMEM_ZYNQMP) += nvmem_zynqmp_nvmem.o nvmem_zynqmp_nvmem-y := zynqmp_nvmem.o +obj-$(CONFIG_SPRD_EFUSE) += nvmem_sprd_efuse.o +nvmem_sprd_efuse-y := sprd-efuse.o diff --git a/drivers/nvmem/sprd-efuse.c b/drivers/nvmem/sprd-efuse.c new file mode 100644 index 000000000000..2f1e0fbd1901 --- /dev/null +++ b/drivers/nvmem/sprd-efuse.c @@ -0,0 +1,424 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2019 Spreadtrum Communications Inc. + +#include +#include +#include +#include +#include +#include +#include +#include + +#define SPRD_EFUSE_ENABLE 0x20 +#define SPRD_EFUSE_ERR_FLAG 0x24 +#define SPRD_EFUSE_ERR_CLR 0x28 +#define SPRD_EFUSE_MAGIC_NUM 0x2c +#define SPRD_EFUSE_FW_CFG 0x50 +#define SPRD_EFUSE_PW_SWT 0x54 +#define SPRD_EFUSE_MEM(val) (0x1000 + ((val) << 2)) + +#define SPRD_EFUSE_VDD_EN BIT(0) +#define SPRD_EFUSE_AUTO_CHECK_EN BIT(1) +#define SPRD_EFUSE_DOUBLE_EN BIT(2) +#define SPRD_EFUSE_MARGIN_RD_EN BIT(3) +#define SPRD_EFUSE_LOCK_WR_EN BIT(4) + +#define SPRD_EFUSE_ERR_CLR_MASK GENMASK(13, 0) + +#define SPRD_EFUSE_ENK1_ON BIT(0) +#define SPRD_EFUSE_ENK2_ON BIT(1) +#define SPRD_EFUSE_PROG_EN BIT(2) + +#define SPRD_EFUSE_MAGIC_NUMBER 0x8810 + +/* Block width (bytes) definitions */ +#define SPRD_EFUSE_BLOCK_WIDTH 4 + +/* + * The Spreadtrum AP efuse contains 2 parts: normal efuse and secure efuse, + * and we can only access the normal efuse in kernel. So define the normal + * block offset index and normal block numbers. + */ +#define SPRD_EFUSE_NORMAL_BLOCK_NUMS 24 +#define SPRD_EFUSE_NORMAL_BLOCK_OFFSET 72 + +/* Timeout (ms) for the trylock of hardware spinlocks */ +#define SPRD_EFUSE_HWLOCK_TIMEOUT 5000 + +/* + * Since different Spreadtrum SoC chip can have different normal block numbers + * and offset. And some SoC can support block double feature, which means + * when reading or writing data to efuse memory, the controller can save double + * data in case one data become incorrect after a long period. + * + * Thus we should save them in the device data structure. + */ +struct sprd_efuse_variant_data { + u32 blk_nums; + u32 blk_offset; + bool blk_double; +}; + +struct sprd_efuse { + struct device *dev; + struct clk *clk; + struct hwspinlock *hwlock; + struct mutex mutex; + void __iomem *base; + const struct sprd_efuse_variant_data *data; +}; + +static const struct sprd_efuse_variant_data ums312_data = { + .blk_nums = SPRD_EFUSE_NORMAL_BLOCK_NUMS, + .blk_offset = SPRD_EFUSE_NORMAL_BLOCK_OFFSET, + .blk_double = false, +}; + +/* + * On Spreadtrum platform, we have multi-subsystems will access the unique + * efuse controller, so we need one hardware spinlock to synchronize between + * the multiple subsystems. + */ +static int sprd_efuse_lock(struct sprd_efuse *efuse) +{ + int ret; + + mutex_lock(&efuse->mutex); + + ret = hwspin_lock_timeout_raw(efuse->hwlock, + SPRD_EFUSE_HWLOCK_TIMEOUT); + if (ret) { + dev_err(efuse->dev, "timeout get the hwspinlock\n"); + mutex_unlock(&efuse->mutex); + return ret; + } + + return 0; +} + +static void sprd_efuse_unlock(struct sprd_efuse *efuse) +{ + hwspin_unlock_raw(efuse->hwlock); + mutex_unlock(&efuse->mutex); +} + +static void sprd_efuse_set_prog_power(struct sprd_efuse *efuse, bool en) +{ + u32 val = readl(efuse->base + SPRD_EFUSE_PW_SWT); + + if (en) + val &= ~SPRD_EFUSE_ENK2_ON; + else + val &= ~SPRD_EFUSE_ENK1_ON; + + writel(val, efuse->base + SPRD_EFUSE_PW_SWT); + + /* Open or close efuse power need wait 1000us to make power stable. */ + usleep_range(1000, 1200); + + if (en) + val |= SPRD_EFUSE_ENK1_ON; + else + val |= SPRD_EFUSE_ENK2_ON; + + writel(val, efuse->base + SPRD_EFUSE_PW_SWT); + + /* Open or close efuse power need wait 1000us to make power stable. */ + usleep_range(1000, 1200); +} + +static void sprd_efuse_set_read_power(struct sprd_efuse *efuse, bool en) +{ + u32 val = readl(efuse->base + SPRD_EFUSE_ENABLE); + + if (en) + val |= SPRD_EFUSE_VDD_EN; + else + val &= ~SPRD_EFUSE_VDD_EN; + + writel(val, efuse->base + SPRD_EFUSE_ENABLE); + + /* Open or close efuse power need wait 1000us to make power stable. */ + usleep_range(1000, 1200); +} + +static void sprd_efuse_set_prog_lock(struct sprd_efuse *efuse, bool en) +{ + u32 val = readl(efuse->base + SPRD_EFUSE_ENABLE); + + if (en) + val |= SPRD_EFUSE_LOCK_WR_EN; + else + val &= ~SPRD_EFUSE_LOCK_WR_EN; + + writel(val, efuse->base + SPRD_EFUSE_ENABLE); +} + +static void sprd_efuse_set_auto_check(struct sprd_efuse *efuse, bool en) +{ + u32 val = readl(efuse->base + SPRD_EFUSE_ENABLE); + + if (en) + val |= SPRD_EFUSE_AUTO_CHECK_EN; + else + val &= ~SPRD_EFUSE_AUTO_CHECK_EN; + + writel(val, efuse->base + SPRD_EFUSE_ENABLE); +} + +static void sprd_efuse_set_data_double(struct sprd_efuse *efuse, bool en) +{ + u32 val = readl(efuse->base + SPRD_EFUSE_ENABLE); + + if (en) + val |= SPRD_EFUSE_DOUBLE_EN; + else + val &= ~SPRD_EFUSE_DOUBLE_EN; + + writel(val, efuse->base + SPRD_EFUSE_ENABLE); +} + +static void sprd_efuse_set_prog_en(struct sprd_efuse *efuse, bool en) +{ + u32 val = readl(efuse->base + SPRD_EFUSE_PW_SWT); + + if (en) + val |= SPRD_EFUSE_PROG_EN; + else + val &= ~SPRD_EFUSE_PROG_EN; + + writel(val, efuse->base + SPRD_EFUSE_PW_SWT); +} + +static int sprd_efuse_raw_prog(struct sprd_efuse *efuse, u32 blk, bool doub, + bool lock, u32 *data) +{ + u32 status; + int ret = 0; + + /* + * We need set the correct magic number before writing the efuse to + * allow programming, and block other programming until we clear the + * magic number. + */ + writel(SPRD_EFUSE_MAGIC_NUMBER, + efuse->base + SPRD_EFUSE_MAGIC_NUM); + + /* + * Power on the efuse, enable programme and enable double data + * if asked. + */ + sprd_efuse_set_prog_power(efuse, true); + sprd_efuse_set_prog_en(efuse, true); + sprd_efuse_set_data_double(efuse, doub); + + /* + * Enable the auto-check function to validate if the programming is + * successful. + */ + sprd_efuse_set_auto_check(efuse, true); + + writel(*data, efuse->base + SPRD_EFUSE_MEM(blk)); + + /* Disable auto-check and data double after programming */ + sprd_efuse_set_auto_check(efuse, false); + sprd_efuse_set_data_double(efuse, false); + + /* + * Check the efuse error status, if the programming is successful, + * we should lock this efuse block to avoid programming again. + */ + status = readl(efuse->base + SPRD_EFUSE_ERR_FLAG); + if (status) { + dev_err(efuse->dev, + "write error status %d of block %d\n", ret, blk); + + writel(SPRD_EFUSE_ERR_CLR_MASK, + efuse->base + SPRD_EFUSE_ERR_CLR); + ret = -EBUSY; + } else { + sprd_efuse_set_prog_lock(efuse, lock); + writel(*data, efuse->base + SPRD_EFUSE_MEM(blk)); + sprd_efuse_set_prog_lock(efuse, false); + } + + sprd_efuse_set_prog_power(efuse, false); + writel(0, efuse->base + SPRD_EFUSE_MAGIC_NUM); + + return ret; +} + +static int sprd_efuse_raw_read(struct sprd_efuse *efuse, int blk, u32 *val, + bool doub) +{ + u32 status; + + /* + * Need power on the efuse before reading data from efuse, and will + * power off the efuse after reading process. + */ + sprd_efuse_set_read_power(efuse, true); + + /* Enable double data if asked */ + sprd_efuse_set_data_double(efuse, doub); + + /* Start to read data from efuse block */ + *val = readl(efuse->base + SPRD_EFUSE_MEM(blk)); + + /* Disable double data */ + sprd_efuse_set_data_double(efuse, false); + + /* Power off the efuse */ + sprd_efuse_set_read_power(efuse, false); + + /* + * Check the efuse error status and clear them if there are some + * errors occurred. + */ + status = readl(efuse->base + SPRD_EFUSE_ERR_FLAG); + if (status) { + dev_err(efuse->dev, + "read error status %d of block %d\n", status, blk); + + writel(SPRD_EFUSE_ERR_CLR_MASK, + efuse->base + SPRD_EFUSE_ERR_CLR); + return -EBUSY; + } + + return 0; +} + +static int sprd_efuse_read(void *context, u32 offset, void *val, size_t bytes) +{ + struct sprd_efuse *efuse = context; + bool blk_double = efuse->data->blk_double; + u32 index = offset / SPRD_EFUSE_BLOCK_WIDTH + efuse->data->blk_offset; + u32 blk_offset = (offset % SPRD_EFUSE_BLOCK_WIDTH) * BITS_PER_BYTE; + u32 data; + int ret; + + ret = sprd_efuse_lock(efuse); + if (ret) + return ret; + + ret = clk_prepare_enable(efuse->clk); + if (ret) + goto unlock; + + ret = sprd_efuse_raw_read(efuse, index, &data, blk_double); + if (!ret) { + data >>= blk_offset; + memcpy(val, &data, bytes); + } + + clk_disable_unprepare(efuse->clk); + +unlock: + sprd_efuse_unlock(efuse); + return ret; +} + +static int sprd_efuse_write(void *context, u32 offset, void *val, size_t bytes) +{ + struct sprd_efuse *efuse = context; + int ret; + + ret = sprd_efuse_lock(efuse); + if (ret) + return ret; + + ret = clk_prepare_enable(efuse->clk); + if (ret) + goto unlock; + + ret = sprd_efuse_raw_prog(efuse, offset, false, false, val); + + clk_disable_unprepare(efuse->clk); + +unlock: + sprd_efuse_unlock(efuse); + return ret; +} + +static int sprd_efuse_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct nvmem_device *nvmem; + struct nvmem_config econfig = { }; + struct sprd_efuse *efuse; + const struct sprd_efuse_variant_data *pdata; + int ret; + + pdata = of_device_get_match_data(&pdev->dev); + if (!pdata) { + dev_err(&pdev->dev, "No matching driver data found\n"); + return -EINVAL; + } + + efuse = devm_kzalloc(&pdev->dev, sizeof(*efuse), GFP_KERNEL); + if (!efuse) + return -ENOMEM; + + efuse->base = devm_platform_ioremap_resource(pdev, 0); + if (!efuse->base) + return -ENOMEM; + + ret = of_hwspin_lock_get_id(np, 0); + if (ret < 0) { + dev_err(&pdev->dev, "failed to get hwlock id\n"); + return ret; + } + + efuse->hwlock = devm_hwspin_lock_request_specific(&pdev->dev, ret); + if (!efuse->hwlock) { + dev_err(&pdev->dev, "failed to request hwlock\n"); + return -ENXIO; + } + + efuse->clk = devm_clk_get(&pdev->dev, "enable"); + if (IS_ERR(efuse->clk)) { + dev_err(&pdev->dev, "failed to get enable clock\n"); + return PTR_ERR(efuse->clk); + } + + mutex_init(&efuse->mutex); + efuse->dev = &pdev->dev; + efuse->data = pdata; + + econfig.stride = 1; + econfig.word_size = 1; + econfig.read_only = false; + econfig.name = "sprd-efuse"; + econfig.size = efuse->data->blk_nums * SPRD_EFUSE_BLOCK_WIDTH; + econfig.reg_read = sprd_efuse_read; + econfig.reg_write = sprd_efuse_write; + econfig.priv = efuse; + econfig.dev = &pdev->dev; + nvmem = devm_nvmem_register(&pdev->dev, &econfig); + if (IS_ERR(nvmem)) { + dev_err(&pdev->dev, "failed to register nvmem\n"); + return PTR_ERR(nvmem); + } + + return 0; +} + +static const struct of_device_id sprd_efuse_of_match[] = { + { .compatible = "sprd,ums312-efuse", .data = &ums312_data }, + { } +}; + +static struct platform_driver sprd_efuse_driver = { + .probe = sprd_efuse_probe, + .driver = { + .name = "sprd-efuse", + .of_match_table = sprd_efuse_of_match, + }, +}; + +module_platform_driver(sprd_efuse_driver); + +MODULE_AUTHOR("Freeman Liu "); +MODULE_DESCRIPTION("Spreadtrum AP efuse driver"); +MODULE_LICENSE("GPL v2"); From b210fac7f636a1859cb3773a245452213fa32c59 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Tue, 29 Oct 2019 11:42:38 +0000 Subject: [PATCH 116/198] nvmem: imx: scu: fix dependency in Kconfig Fix below error by adding HAVE_ARM_SMCCC dependency in Kconfig ERROR: "__arm_smccc_smc" [drivers/nvmem/nvmem-imx-ocotp-scu.ko] undefined! Reported-by: kbuild test robot Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20191029114240.14905-9-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index 8fd425d38d97..fd0716818881 100644 --- a/drivers/nvmem/Kconfig +++ b/drivers/nvmem/Kconfig @@ -50,6 +50,7 @@ config NVMEM_IMX_OCOTP config NVMEM_IMX_OCOTP_SCU tristate "i.MX8 SCU On-Chip OTP Controller support" depends on IMX_SCU + depends on HAVE_ARM_SMCCC help This is a driver for the SCU On-Chip OTP Controller (OCOTP) available on i.MX8 SoCs. From de02fc40fc63aa6950435a18e99843603b1bda01 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Tue, 29 Oct 2019 11:42:39 +0000 Subject: [PATCH 117/198] dt-bindings: nvmem: add binding for Rockchip OTP controller Newer Rockchip SoCs use a different IP for accessing special one- time-programmable memory, so add a binding for these controllers. Signed-off-by: Heiko Stuebner Reviewed-by: Rob Herring Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20191029114240.14905-10-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- .../bindings/nvmem/rockchip-otp.txt | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 Documentation/devicetree/bindings/nvmem/rockchip-otp.txt diff --git a/Documentation/devicetree/bindings/nvmem/rockchip-otp.txt b/Documentation/devicetree/bindings/nvmem/rockchip-otp.txt new file mode 100644 index 000000000000..40f649f7c2e5 --- /dev/null +++ b/Documentation/devicetree/bindings/nvmem/rockchip-otp.txt @@ -0,0 +1,25 @@ +Rockchip internal OTP (One Time Programmable) memory device tree bindings + +Required properties: +- compatible: Should be one of the following. + - "rockchip,px30-otp" - for PX30 SoCs. + - "rockchip,rk3308-otp" - for RK3308 SoCs. +- reg: Should contain the registers location and size +- clocks: Must contain an entry for each entry in clock-names. +- clock-names: Should be "otp", "apb_pclk" and "phy". +- resets: Must contain an entry for each entry in reset-names. + See ../../reset/reset.txt for details. +- reset-names: Should be "phy". + +See nvmem.txt for more information. + +Example: + otp: otp@ff290000 { + compatible = "rockchip,px30-otp"; + reg = <0x0 0xff290000 0x0 0x4000>; + #address-cells = <1>; + #size-cells = <1>; + clocks = <&cru SCLK_OTP_USR>, <&cru PCLK_OTP_NS>, + <&cru PCLK_OTP_PHY>; + clock-names = "otp", "apb_pclk", "phy"; + }; From 755864feb729a791e1fd0f3338ddc5aae32c8ac1 Mon Sep 17 00:00:00 2001 From: Finley Xiao Date: Tue, 29 Oct 2019 11:42:40 +0000 Subject: [PATCH 118/198] nvmem: add Rockchip OTP driver Newer Rockchip socs like the px30 use a different one-time-programmable memory controller for things like cpu-id and leakage information, so add the necessary driver for it. Signed-off-by: Finley Xiao [ported from vendor 4.4, converted to clock-bulk API and cleanups] Signed-off-by: Heiko Stuebner Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20191029114240.14905-11-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/Kconfig | 11 ++ drivers/nvmem/Makefile | 2 + drivers/nvmem/rockchip-otp.c | 268 +++++++++++++++++++++++++++++++++++ 3 files changed, 281 insertions(+) create mode 100644 drivers/nvmem/rockchip-otp.c diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index fd0716818881..73567e922491 100644 --- a/drivers/nvmem/Kconfig +++ b/drivers/nvmem/Kconfig @@ -120,6 +120,17 @@ config ROCKCHIP_EFUSE This driver can also be built as a module. If so, the module will be called nvmem_rockchip_efuse. +config ROCKCHIP_OTP + tristate "Rockchip OTP controller support" + depends on ARCH_ROCKCHIP || COMPILE_TEST + depends on HAS_IOMEM + help + This is a simple drive to dump specified values of Rockchip SoC + from otp, such as cpu-leakage. + + This driver can also be built as a module. If so, the module + will be called nvmem_rockchip_otp. + config NVMEM_BCM_OCOTP tristate "Broadcom On-Chip OTP Controller support" depends on ARCH_BCM_IPROC || COMPILE_TEST diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile index 7c19870a6bbe..9e667823edb3 100644 --- a/drivers/nvmem/Makefile +++ b/drivers/nvmem/Makefile @@ -30,6 +30,8 @@ obj-$(CONFIG_QCOM_QFPROM) += nvmem_qfprom.o nvmem_qfprom-y := qfprom.o obj-$(CONFIG_ROCKCHIP_EFUSE) += nvmem_rockchip_efuse.o nvmem_rockchip_efuse-y := rockchip-efuse.o +obj-$(CONFIG_ROCKCHIP_OTP) += nvmem-rockchip-otp.o +nvmem-rockchip-otp-y := rockchip-otp.o obj-$(CONFIG_NVMEM_SUNXI_SID) += nvmem_sunxi_sid.o nvmem_stm32_romem-y := stm32-romem.o obj-$(CONFIG_NVMEM_STM32_ROMEM) += nvmem_stm32_romem.o diff --git a/drivers/nvmem/rockchip-otp.c b/drivers/nvmem/rockchip-otp.c new file mode 100644 index 000000000000..9f53bcce2f87 --- /dev/null +++ b/drivers/nvmem/rockchip-otp.c @@ -0,0 +1,268 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Rockchip OTP Driver + * + * Copyright (c) 2018 Rockchip Electronics Co. Ltd. + * Author: Finley Xiao + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* OTP Register Offsets */ +#define OTPC_SBPI_CTRL 0x0020 +#define OTPC_SBPI_CMD_VALID_PRE 0x0024 +#define OTPC_SBPI_CS_VALID_PRE 0x0028 +#define OTPC_SBPI_STATUS 0x002C +#define OTPC_USER_CTRL 0x0100 +#define OTPC_USER_ADDR 0x0104 +#define OTPC_USER_ENABLE 0x0108 +#define OTPC_USER_Q 0x0124 +#define OTPC_INT_STATUS 0x0304 +#define OTPC_SBPI_CMD0_OFFSET 0x1000 +#define OTPC_SBPI_CMD1_OFFSET 0x1004 + +/* OTP Register bits and masks */ +#define OTPC_USER_ADDR_MASK GENMASK(31, 16) +#define OTPC_USE_USER BIT(0) +#define OTPC_USE_USER_MASK GENMASK(16, 16) +#define OTPC_USER_FSM_ENABLE BIT(0) +#define OTPC_USER_FSM_ENABLE_MASK GENMASK(16, 16) +#define OTPC_SBPI_DONE BIT(1) +#define OTPC_USER_DONE BIT(2) + +#define SBPI_DAP_ADDR 0x02 +#define SBPI_DAP_ADDR_SHIFT 8 +#define SBPI_DAP_ADDR_MASK GENMASK(31, 24) +#define SBPI_CMD_VALID_MASK GENMASK(31, 16) +#define SBPI_DAP_CMD_WRF 0xC0 +#define SBPI_DAP_REG_ECC 0x3A +#define SBPI_ECC_ENABLE 0x00 +#define SBPI_ECC_DISABLE 0x09 +#define SBPI_ENABLE BIT(0) +#define SBPI_ENABLE_MASK GENMASK(16, 16) + +#define OTPC_TIMEOUT 10000 + +struct rockchip_otp { + struct device *dev; + void __iomem *base; + struct clk_bulk_data *clks; + int num_clks; + struct reset_control *rst; +}; + +/* list of required clocks */ +static const char * const rockchip_otp_clocks[] = { + "otp", "apb_pclk", "phy", +}; + +struct rockchip_data { + int size; +}; + +static int rockchip_otp_reset(struct rockchip_otp *otp) +{ + int ret; + + ret = reset_control_assert(otp->rst); + if (ret) { + dev_err(otp->dev, "failed to assert otp phy %d\n", ret); + return ret; + } + + udelay(2); + + ret = reset_control_deassert(otp->rst); + if (ret) { + dev_err(otp->dev, "failed to deassert otp phy %d\n", ret); + return ret; + } + + return 0; +} + +static int rockchip_otp_wait_status(struct rockchip_otp *otp, u32 flag) +{ + u32 status = 0; + int ret; + + ret = readl_poll_timeout_atomic(otp->base + OTPC_INT_STATUS, status, + (status & flag), 1, OTPC_TIMEOUT); + if (ret) + return ret; + + /* clean int status */ + writel(flag, otp->base + OTPC_INT_STATUS); + + return 0; +} + +static int rockchip_otp_ecc_enable(struct rockchip_otp *otp, bool enable) +{ + int ret = 0; + + writel(SBPI_DAP_ADDR_MASK | (SBPI_DAP_ADDR << SBPI_DAP_ADDR_SHIFT), + otp->base + OTPC_SBPI_CTRL); + + writel(SBPI_CMD_VALID_MASK | 0x1, otp->base + OTPC_SBPI_CMD_VALID_PRE); + writel(SBPI_DAP_CMD_WRF | SBPI_DAP_REG_ECC, + otp->base + OTPC_SBPI_CMD0_OFFSET); + if (enable) + writel(SBPI_ECC_ENABLE, otp->base + OTPC_SBPI_CMD1_OFFSET); + else + writel(SBPI_ECC_DISABLE, otp->base + OTPC_SBPI_CMD1_OFFSET); + + writel(SBPI_ENABLE_MASK | SBPI_ENABLE, otp->base + OTPC_SBPI_CTRL); + + ret = rockchip_otp_wait_status(otp, OTPC_SBPI_DONE); + if (ret < 0) + dev_err(otp->dev, "timeout during ecc_enable\n"); + + return ret; +} + +static int rockchip_otp_read(void *context, unsigned int offset, + void *val, size_t bytes) +{ + struct rockchip_otp *otp = context; + u8 *buf = val; + int ret = 0; + + ret = clk_bulk_prepare_enable(otp->num_clks, otp->clks); + if (ret < 0) { + dev_err(otp->dev, "failed to prepare/enable clks\n"); + return ret; + } + + ret = rockchip_otp_reset(otp); + if (ret) { + dev_err(otp->dev, "failed to reset otp phy\n"); + goto disable_clks; + } + + ret = rockchip_otp_ecc_enable(otp, false); + if (ret < 0) { + dev_err(otp->dev, "rockchip_otp_ecc_enable err\n"); + goto disable_clks; + } + + writel(OTPC_USE_USER | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL); + udelay(5); + while (bytes--) { + writel(offset++ | OTPC_USER_ADDR_MASK, + otp->base + OTPC_USER_ADDR); + writel(OTPC_USER_FSM_ENABLE | OTPC_USER_FSM_ENABLE_MASK, + otp->base + OTPC_USER_ENABLE); + ret = rockchip_otp_wait_status(otp, OTPC_USER_DONE); + if (ret < 0) { + dev_err(otp->dev, "timeout during read setup\n"); + goto read_end; + } + *buf++ = readb(otp->base + OTPC_USER_Q); + } + +read_end: + writel(0x0 | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL); +disable_clks: + clk_bulk_disable_unprepare(otp->num_clks, otp->clks); + + return ret; +} + +static struct nvmem_config otp_config = { + .name = "rockchip-otp", + .owner = THIS_MODULE, + .read_only = true, + .stride = 1, + .word_size = 1, + .reg_read = rockchip_otp_read, +}; + +static const struct rockchip_data px30_data = { + .size = 0x40, +}; + +static const struct of_device_id rockchip_otp_match[] = { + { + .compatible = "rockchip,px30-otp", + .data = (void *)&px30_data, + }, + { + .compatible = "rockchip,rk3308-otp", + .data = (void *)&px30_data, + }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, rockchip_otp_match); + +static int rockchip_otp_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rockchip_otp *otp; + const struct rockchip_data *data; + struct nvmem_device *nvmem; + int ret, i; + + data = of_device_get_match_data(dev); + if (!data) { + dev_err(dev, "failed to get match data\n"); + return -EINVAL; + } + + otp = devm_kzalloc(&pdev->dev, sizeof(struct rockchip_otp), + GFP_KERNEL); + if (!otp) + return -ENOMEM; + + otp->dev = dev; + otp->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(otp->base)) + return PTR_ERR(otp->base); + + otp->num_clks = ARRAY_SIZE(rockchip_otp_clocks); + otp->clks = devm_kcalloc(dev, otp->num_clks, + sizeof(*otp->clks), GFP_KERNEL); + if (!otp->clks) + return -ENOMEM; + + for (i = 0; i < otp->num_clks; ++i) + otp->clks[i].id = rockchip_otp_clocks[i]; + + ret = devm_clk_bulk_get(dev, otp->num_clks, otp->clks); + if (ret) + return ret; + + otp->rst = devm_reset_control_get(dev, "phy"); + if (IS_ERR(otp->rst)) + return PTR_ERR(otp->rst); + + otp_config.size = data->size; + otp_config.priv = otp; + otp_config.dev = dev; + nvmem = devm_nvmem_register(dev, &otp_config); + + return PTR_ERR_OR_ZERO(nvmem); +} + +static struct platform_driver rockchip_otp_driver = { + .probe = rockchip_otp_probe, + .driver = { + .name = "rockchip-otp", + .of_match_table = rockchip_otp_match, + }, +}; + +module_platform_driver(rockchip_otp_driver); +MODULE_DESCRIPTION("Rockchip OTP driver"); +MODULE_LICENSE("GPL v2"); From 907b471ca228a5fc95f7ee8b3d189e64ade7ce9b Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Thu, 7 Nov 2019 00:38:39 +0200 Subject: [PATCH 119/198] mei: me: mei_me_dev_init() use struct device instead of struct pci_dev. It's enough to bind mei_device with associated 'struct device' instead of actual 'struct pci_dev'. This is to allow working with mei devices embedded within another pci device, usually via MFD framework, where mei device is represented as a platform device. Bump copyright year to 2019 on effected files. Signed-off-by: Tomas Winkler Link: https://lore.kernel.org/r/20191106223841.15802-2-tomas.winkler@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/hw-me.c | 10 +++++----- drivers/misc/mei/hw-me.h | 4 ++-- drivers/misc/mei/pci-me.c | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index 5ef30c7c92b3..7d241c70e3e0 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2003-2018, Intel Corporation. All rights reserved. + * Copyright (c) 2003-2019, Intel Corporation. All rights reserved. * Intel Management Engine Interface (Intel MEI) Linux driver */ @@ -1461,19 +1461,19 @@ const struct mei_cfg *mei_me_get_cfg(kernel_ulong_t idx) /** * mei_me_dev_init - allocates and initializes the mei device structure * - * @pdev: The pci device structure + * @parent: device associated with physical device (pci/platform) * @cfg: per device generation config * * Return: The mei_device pointer on success, NULL on failure. */ -struct mei_device *mei_me_dev_init(struct pci_dev *pdev, +struct mei_device *mei_me_dev_init(struct device *parent, const struct mei_cfg *cfg) { struct mei_device *dev; struct mei_me_hw *hw; int i; - dev = devm_kzalloc(&pdev->dev, sizeof(struct mei_device) + + dev = devm_kzalloc(parent, sizeof(struct mei_device) + sizeof(struct mei_me_hw), GFP_KERNEL); if (!dev) return NULL; @@ -1483,7 +1483,7 @@ struct mei_device *mei_me_dev_init(struct pci_dev *pdev, for (i = 0; i < DMA_DSCR_NUM; i++) dev->dr_dscr[i].size = cfg->dma_size[i]; - mei_device_init(dev, &pdev->dev, &mei_me_hw_ops); + mei_device_init(dev, parent, &mei_me_hw_ops); hw->cfg = cfg; dev->fw_f_fw_ver_supported = cfg->fw_ver_supported; diff --git a/drivers/misc/mei/hw-me.h b/drivers/misc/mei/hw-me.h index 1d8794828cbc..b39347faadf5 100644 --- a/drivers/misc/mei/hw-me.h +++ b/drivers/misc/mei/hw-me.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (c) 2012-2018, Intel Corporation. All rights reserved. + * Copyright (c) 2012-2019, Intel Corporation. All rights reserved. * Intel Management Engine Interface (Intel MEI) Linux driver */ @@ -91,7 +91,7 @@ enum mei_cfg_idx { const struct mei_cfg *mei_me_get_cfg(kernel_ulong_t idx); -struct mei_device *mei_me_dev_init(struct pci_dev *pdev, +struct mei_device *mei_me_dev_init(struct device *parent, const struct mei_cfg *cfg); int mei_me_pg_enter_sync(struct mei_device *dev); diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index ce43415a536c..e382ecca96d7 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -192,7 +192,7 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } /* allocates and initializes the mei dev structure */ - dev = mei_me_dev_init(pdev, cfg); + dev = mei_me_dev_init(&pdev->dev, cfg); if (!dev) { err = -ENOMEM; goto end; From 261b3e1f2a01c72b1882cf5bccfbd4bf40ea62e8 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Thu, 7 Nov 2019 00:38:40 +0200 Subject: [PATCH 120/198] mei: me: store irq number in the hw struct. Store irq number in hw struct to by used by synchronize_irq(). This is to allow working with mei devices embedded within another pci devices, via MFD framework, where mei device is represented as a platform device. Bump the copyright year to 2019 on hw-me.c and hw-me.h Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Link: https://lore.kernel.org/r/20191106223841.15802-3-tomas.winkler@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/hw-me.c | 4 ++-- drivers/misc/mei/hw-me.h | 2 ++ drivers/misc/mei/pci-me.c | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index 7d241c70e3e0..23606d0ddcd6 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -323,9 +323,9 @@ static void mei_me_intr_disable(struct mei_device *dev) */ static void mei_me_synchronize_irq(struct mei_device *dev) { - struct pci_dev *pdev = to_pci_dev(dev->dev); + struct mei_me_hw *hw = to_me_hw(dev); - synchronize_irq(pdev->irq); + synchronize_irq(hw->irq); } /** diff --git a/drivers/misc/mei/hw-me.h b/drivers/misc/mei/hw-me.h index b39347faadf5..c45b32a7cc46 100644 --- a/drivers/misc/mei/hw-me.h +++ b/drivers/misc/mei/hw-me.h @@ -42,6 +42,7 @@ struct mei_cfg { * * @cfg: per device generation config and ops * @mem_addr: io memory address + * @irq: irq number * @pg_state: power gating state * @d0i3_supported: di03 support * @hbuf_depth: depth of hardware host/write buffer in slots @@ -49,6 +50,7 @@ struct mei_cfg { struct mei_me_hw { const struct mei_cfg *cfg; void __iomem *mem_addr; + int irq; enum mei_pg_state pg_state; bool d0i3_supported; u8 hbuf_depth; diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index e382ecca96d7..6233b3ca1c1d 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -199,6 +199,7 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } hw = to_me_hw(dev); hw->mem_addr = pcim_iomap_table(pdev)[0]; + hw->irq = pdev->irq; pci_enable_msi(pdev); From 261e071acd9bcbcfbc30652640385615ced27f4f Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Thu, 7 Nov 2019 00:38:41 +0200 Subject: [PATCH 121/198] mei: abstract fw status register read. This is to allow working with mei devices embedded within another pci device, where mei device is represented as a platform child device and fw status registers are not necessarily resident in the device pci config space. Bump the copyright year to 2019 on the modified files. Signed-off-by: Tomas Winkler Link: https://lore.kernel.org/r/20191106223841.15802-4-tomas.winkler@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/hw-me.c | 24 ++++++++++++++++-------- drivers/misc/mei/hw-me.h | 2 ++ drivers/misc/mei/hw-txe.c | 10 +++++++--- drivers/misc/mei/init.c | 6 ++++-- drivers/misc/mei/mei_dev.h | 8 ++++---- drivers/misc/mei/pci-me.c | 8 ++++++++ 6 files changed, 41 insertions(+), 17 deletions(-) diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index 23606d0ddcd6..0ec55431e26b 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -183,20 +183,19 @@ static inline void mei_me_d0i3c_write(struct mei_device *dev, u32 reg) static int mei_me_fw_status(struct mei_device *dev, struct mei_fw_status *fw_status) { - struct pci_dev *pdev = to_pci_dev(dev->dev); struct mei_me_hw *hw = to_me_hw(dev); const struct mei_fw_status *fw_src = &hw->cfg->fw_status; int ret; int i; - if (!fw_status) + if (!fw_status || !hw->read_fws) return -EINVAL; fw_status->count = fw_src->count; for (i = 0; i < fw_src->count && i < MEI_FW_STATUS_MAX; i++) { - ret = pci_read_config_dword(pdev, fw_src->status[i], - &fw_status->status[i]); - trace_mei_pci_cfg_read(dev->dev, "PCI_CFG_HSF_X", + ret = hw->read_fws(dev, fw_src->status[i], + &fw_status->status[i]); + trace_mei_pci_cfg_read(dev->dev, "PCI_CFG_HFS_X", fw_src->status[i], fw_status->status[i]); if (ret) @@ -210,19 +209,26 @@ static int mei_me_fw_status(struct mei_device *dev, * mei_me_hw_config - configure hw dependent settings * * @dev: mei device + * + * Return: + * * -EINVAL when read_fws is not set + * * 0 on success + * */ -static void mei_me_hw_config(struct mei_device *dev) +static int mei_me_hw_config(struct mei_device *dev) { - struct pci_dev *pdev = to_pci_dev(dev->dev); struct mei_me_hw *hw = to_me_hw(dev); u32 hcsr, reg; + if (WARN_ON(!hw->read_fws)) + return -EINVAL; + /* Doesn't change in runtime */ hcsr = mei_hcsr_read(dev); hw->hbuf_depth = (hcsr & H_CBD) >> 24; reg = 0; - pci_read_config_dword(pdev, PCI_CFG_HFS_1, ®); + hw->read_fws(dev, PCI_CFG_HFS_1, ®); trace_mei_pci_cfg_read(dev->dev, "PCI_CFG_HFS_1", PCI_CFG_HFS_1, reg); hw->d0i3_supported = ((reg & PCI_CFG_HFS_1_D0I3_MSK) == PCI_CFG_HFS_1_D0I3_MSK); @@ -233,6 +239,8 @@ static void mei_me_hw_config(struct mei_device *dev) if (reg & H_D0I3C_I3) hw->pg_state = MEI_PG_ON; } + + return 0; } /** diff --git a/drivers/misc/mei/hw-me.h b/drivers/misc/mei/hw-me.h index c45b32a7cc46..3352d19b8e85 100644 --- a/drivers/misc/mei/hw-me.h +++ b/drivers/misc/mei/hw-me.h @@ -46,6 +46,7 @@ struct mei_cfg { * @pg_state: power gating state * @d0i3_supported: di03 support * @hbuf_depth: depth of hardware host/write buffer in slots + * @read_fws: read FW status register handler */ struct mei_me_hw { const struct mei_cfg *cfg; @@ -54,6 +55,7 @@ struct mei_me_hw { enum mei_pg_state pg_state; bool d0i3_supported; u8 hbuf_depth; + int (*read_fws)(const struct mei_device *dev, int where, u32 *val); }; #define to_me_hw(dev) (struct mei_me_hw *)((dev)->hw) diff --git a/drivers/misc/mei/hw-txe.c b/drivers/misc/mei/hw-txe.c index 5e58656b8e19..785b260b3ae9 100644 --- a/drivers/misc/mei/hw-txe.c +++ b/drivers/misc/mei/hw-txe.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2013-2014, Intel Corporation. All rights reserved. + * Copyright (c) 2013-2019, Intel Corporation. All rights reserved. * Intel Management Engine Interface (Intel MEI) Linux driver */ @@ -660,14 +660,16 @@ static int mei_txe_fw_status(struct mei_device *dev, } /** - * mei_txe_hw_config - configure hardware at the start of the devices + * mei_txe_hw_config - configure hardware at the start of the devices * * @dev: the device structure * * Configure hardware at the start of the device should be done only * once at the device probe time + * + * Return: always 0 */ -static void mei_txe_hw_config(struct mei_device *dev) +static int mei_txe_hw_config(struct mei_device *dev) { struct mei_txe_hw *hw = to_txe_hw(dev); @@ -677,6 +679,8 @@ static void mei_txe_hw_config(struct mei_device *dev) dev_dbg(dev->dev, "aliveness_resp = 0x%08x, readiness = 0x%08x.\n", hw->aliveness, hw->readiness); + + return 0; } /** diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index b9fef773e71b..bcee77768b91 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2012-2018, Intel Corporation. All rights reserved. + * Copyright (c) 2012-2019, Intel Corporation. All rights reserved. * Intel Management Engine Interface (Intel MEI) Linux driver */ @@ -190,7 +190,9 @@ int mei_start(struct mei_device *dev) /* acknowledge interrupt and stop interrupts */ mei_clear_interrupts(dev); - mei_hw_config(dev); + ret = mei_hw_config(dev); + if (ret) + goto err; dev_dbg(dev->dev, "reset in start the mei device.\n"); diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 0f2141178299..e0ac660c96e7 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (c) 2003-2018, Intel Corporation. All rights reserved. + * Copyright (c) 2003-2019, Intel Corporation. All rights reserved. * Intel Management Engine Interface (Intel MEI) Linux driver */ @@ -287,7 +287,7 @@ struct mei_hw_ops { bool (*hw_is_ready)(struct mei_device *dev); int (*hw_reset)(struct mei_device *dev, bool enable); int (*hw_start)(struct mei_device *dev); - void (*hw_config)(struct mei_device *dev); + int (*hw_config)(struct mei_device *dev); int (*fw_status)(struct mei_device *dev, struct mei_fw_status *fw_sts); enum mei_pg_state (*pg_state)(struct mei_device *dev); @@ -614,9 +614,9 @@ void mei_irq_compl_handler(struct mei_device *dev, struct list_head *cmpl_list); */ -static inline void mei_hw_config(struct mei_device *dev) +static inline int mei_hw_config(struct mei_device *dev) { - dev->ops->hw_config(dev); + return dev->ops->hw_config(dev); } static inline enum mei_pg_state mei_pg_state(struct mei_device *dev) diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index 6233b3ca1c1d..1de6daf38602 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -121,6 +121,13 @@ static inline void mei_me_set_pm_domain(struct mei_device *dev) {} static inline void mei_me_unset_pm_domain(struct mei_device *dev) {} #endif /* CONFIG_PM */ +static int mei_me_read_fws(const struct mei_device *dev, int where, u32 *val) +{ + struct pci_dev *pdev = to_pci_dev(dev->dev); + + return pci_read_config_dword(pdev, where, val); +} + /** * mei_me_quirk_probe - probe for devices that doesn't valid ME interface * @@ -200,6 +207,7 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent) hw = to_me_hw(dev); hw->mem_addr = pcim_iomap_table(pdev)[0]; hw->irq = pdev->irq; + hw->read_fws = mei_me_read_fws; pci_enable_msi(pdev); From 52f6efdf80924449023c559c3134258c2c6da43b Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Thu, 7 Nov 2019 12:44:45 +0200 Subject: [PATCH 122/198] mei: add trc detection register to sysfs The glitch detection HW (TRC) save it status information into TRC status register. Make it available to user-space via read-only sysfs file. The TRC register is availab for PCH15 gen and newer, for older platforms reading the sysfs file will fail with EOPNOTSUPP. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Link: https://lore.kernel.org/r/20191107104445.19101-1-tomas.winkler@intel.com Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-class-mei | 10 +++++++ drivers/misc/mei/hw-me-regs.h | 3 +- drivers/misc/mei/hw-me.c | 34 +++++++++++++++++++++++ drivers/misc/mei/hw-me.h | 4 +++ drivers/misc/mei/main.c | 24 ++++++++++++++++ drivers/misc/mei/mei_dev.h | 10 +++++++ drivers/misc/mei/pci-me.c | 4 +-- 7 files changed, 86 insertions(+), 3 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-class-mei b/Documentation/ABI/testing/sysfs-class-mei index a92d844f806e..e9dc110650ae 100644 --- a/Documentation/ABI/testing/sysfs-class-mei +++ b/Documentation/ABI/testing/sysfs-class-mei @@ -80,3 +80,13 @@ Description: Display the ME device state. DISABLED POWER_DOWN POWER_UP + +What: /sys/class/mei/meiN/trc +Date: Nov 2019 +KernelVersion: 5.5 +Contact: Tomas Winkler +Description: Display trc status register content + + The ME FW writes Glitch Detection HW (TRC) + status information into trc status register + for BIOS and OS to monitor fw health. diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h index b359f06f05e7..7cd67fb2365d 100644 --- a/drivers/misc/mei/hw-me-regs.h +++ b/drivers/misc/mei/hw-me-regs.h @@ -163,7 +163,8 @@ access to ME_CBD */ #define ME_IS_HRA 0x00000002 /* ME Interrupt Enable HRA - host read only access to ME_IE */ #define ME_IE_HRA 0x00000001 - +/* TRC control shadow register */ +#define ME_TRC 0x00000030 /* H_HPG_CSR register bits */ #define H_HPG_CSR_PGIHEXR 0x00000001 diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index 0ec55431e26b..668418d7ea77 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -172,6 +172,27 @@ static inline void mei_me_d0i3c_write(struct mei_device *dev, u32 reg) mei_me_reg_write(to_me_hw(dev), H_D0I3C, reg); } +/** + * mei_me_trc_status - read trc status register + * + * @dev: mei device + * @trc: trc status register value + * + * Return: 0 on success, error otherwise + */ +static int mei_me_trc_status(struct mei_device *dev, u32 *trc) +{ + struct mei_me_hw *hw = to_me_hw(dev); + + if (!hw->cfg->hw_trc_supported) + return -EOPNOTSUPP; + + *trc = mei_me_reg_read(hw, ME_TRC); + trace_mei_reg_read(dev->dev, "ME_TRC", ME_TRC, *trc); + + return 0; +} + /** * mei_me_fw_status - read fw status register from pci config space * @@ -1302,6 +1323,7 @@ end: static const struct mei_hw_ops mei_me_hw_ops = { + .trc_status = mei_me_trc_status, .fw_status = mei_me_fw_status, .pg_state = mei_me_pg_state, @@ -1392,6 +1414,9 @@ static bool mei_me_fw_type_sps(struct pci_dev *pdev) .dma_size[DMA_DSCR_DEVICE] = SZ_128K, \ .dma_size[DMA_DSCR_CTRL] = PAGE_SIZE +#define MEI_CFG_TRC \ + .hw_trc_supported = 1 + /* ICH Legacy devices */ static const struct mei_cfg mei_me_ich_cfg = { MEI_CFG_ICH_HFS, @@ -1440,6 +1465,14 @@ static const struct mei_cfg mei_me_pch12_cfg = { MEI_CFG_DMA_128, }; +/* Tiger Lake and newer devices */ +static const struct mei_cfg mei_me_pch15_cfg = { + MEI_CFG_PCH8_HFS, + MEI_CFG_FW_VER_SUPP, + MEI_CFG_DMA_128, + MEI_CFG_TRC, +}; + /* * mei_cfg_list - A list of platform platform specific configurations. * Note: has to be synchronized with enum mei_cfg_idx. @@ -1454,6 +1487,7 @@ static const struct mei_cfg *const mei_cfg_list[] = { [MEI_ME_PCH8_CFG] = &mei_me_pch8_cfg, [MEI_ME_PCH8_SPS_CFG] = &mei_me_pch8_sps_cfg, [MEI_ME_PCH12_CFG] = &mei_me_pch12_cfg, + [MEI_ME_PCH15_CFG] = &mei_me_pch15_cfg, }; const struct mei_cfg *mei_me_get_cfg(kernel_ulong_t idx) diff --git a/drivers/misc/mei/hw-me.h b/drivers/misc/mei/hw-me.h index 3352d19b8e85..4a8d4dcd5a91 100644 --- a/drivers/misc/mei/hw-me.h +++ b/drivers/misc/mei/hw-me.h @@ -21,12 +21,14 @@ * @quirk_probe: device exclusion quirk * @dma_size: device DMA buffers size * @fw_ver_supported: is fw version retrievable from FW + * @hw_trc_supported: does the hw support trc register */ struct mei_cfg { const struct mei_fw_status fw_status; bool (*quirk_probe)(struct pci_dev *pdev); size_t dma_size[DMA_DSCR_NUM]; u32 fw_ver_supported:1; + u32 hw_trc_supported:1; }; @@ -78,6 +80,7 @@ struct mei_me_hw { * servers platforms with quirk for * SPS firmware exclusion. * @MEI_ME_PCH12_CFG: Platform Controller Hub Gen12 and newer + * @MEI_ME_PCH15_CFG: Platform Controller Hub Gen15 and newer * @MEI_ME_NUM_CFG: Upper Sentinel. */ enum mei_cfg_idx { @@ -90,6 +93,7 @@ enum mei_cfg_idx { MEI_ME_PCH8_CFG, MEI_ME_PCH8_SPS_CFG, MEI_ME_PCH12_CFG, + MEI_ME_PCH15_CFG, MEI_ME_NUM_CFG, }; diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 7310b476323c..4ef6e37caafc 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -700,6 +700,29 @@ static int mei_fasync(int fd, struct file *file, int band) return fasync_helper(fd, file, band, &cl->ev_async); } +/** + * trc_show - mei device trc attribute show method + * + * @device: device pointer + * @attr: attribute pointer + * @buf: char out buffer + * + * Return: number of the bytes printed into buf or error + */ +static ssize_t trc_show(struct device *device, + struct device_attribute *attr, char *buf) +{ + struct mei_device *dev = dev_get_drvdata(device); + u32 trc; + int ret; + + ret = mei_trc_status(dev, &trc); + if (ret) + return ret; + return sprintf(buf, "%08X\n", trc); +} +static DEVICE_ATTR_RO(trc); + /** * fw_status_show - mei device fw_status attribute show method * @@ -887,6 +910,7 @@ static struct attribute *mei_attrs[] = { &dev_attr_tx_queue_limit.attr, &dev_attr_fw_ver.attr, &dev_attr_dev_state.attr, + &dev_attr_trc.attr, NULL }; ATTRIBUTE_GROUPS(mei); diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index e0ac660c96e7..76f8ff5ff974 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -260,6 +260,7 @@ struct mei_cl { * @hw_config : configure hw * * @fw_status : get fw status registers + * @trc_status : get trc status register * @pg_state : power gating state of the device * @pg_in_transition : is device now in pg transition * @pg_is_enabled : is power gating enabled @@ -290,6 +291,8 @@ struct mei_hw_ops { int (*hw_config)(struct mei_device *dev); int (*fw_status)(struct mei_device *dev, struct mei_fw_status *fw_sts); + int (*trc_status)(struct mei_device *dev, u32 *trc); + enum mei_pg_state (*pg_state)(struct mei_device *dev); bool (*pg_in_transition)(struct mei_device *dev); bool (*pg_is_enabled)(struct mei_device *dev); @@ -711,6 +714,13 @@ static inline int mei_count_full_read_slots(struct mei_device *dev) return dev->ops->rdbuf_full_slots(dev); } +static inline int mei_trc_status(struct mei_device *dev, u32 *trc) +{ + if (dev->ops->trc_status) + return dev->ops->trc_status(dev, trc); + return -EOPNOTSUPP; +} + static inline int mei_fw_status(struct mei_device *dev, struct mei_fw_status *fw_status) { diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index 1de6daf38602..c845b7e40f26 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -102,9 +102,9 @@ static const struct pci_device_id mei_me_pci_tbl[] = { {MEI_PCI_DEVICE(MEI_DEV_ID_ICP_LP, MEI_ME_PCH12_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_TGP_LP, MEI_ME_PCH12_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_TGP_LP, MEI_ME_PCH15_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_MCC, MEI_ME_PCH12_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_MCC, MEI_ME_PCH15_CFG)}, {MEI_PCI_DEVICE(MEI_DEV_ID_MCC_4, MEI_ME_PCH8_CFG)}, /* required last entry */ From 2e32c2d675ac19730ec656322414649f04eddaa8 Mon Sep 17 00:00:00 2001 From: Jeremy Kerr Date: Fri, 8 Nov 2019 15:49:35 +1030 Subject: [PATCH 123/198] fsi: Add fsi-master class This change adds a device class for FSI masters, allowing access under /sys/class/fsi-master/, and easier udev rules. Signed-off-by: Jeremy Kerr Signed-off-by: Joel Stanley Acked-by: Alistair Popple Link: https://lore.kernel.org/r/20191108051945.7109-2-joel@jms.id.au Signed-off-by: Greg Kroah-Hartman --- drivers/fsi/fsi-core.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c index 1f76740f33b6..0861f6097b33 100644 --- a/drivers/fsi/fsi-core.c +++ b/drivers/fsi/fsi-core.c @@ -1241,6 +1241,10 @@ static ssize_t master_break_store(struct device *dev, static DEVICE_ATTR(break, 0200, NULL, master_break_store); +struct class fsi_master_class = { + .name = "fsi-master", +}; + int fsi_master_register(struct fsi_master *master) { int rc; @@ -1249,6 +1253,7 @@ int fsi_master_register(struct fsi_master *master) mutex_init(&master->scan_lock); master->idx = ida_simple_get(&master_ida, 0, INT_MAX, GFP_KERNEL); dev_set_name(&master->dev, "fsi%d", master->idx); + master->dev.class = &fsi_master_class; rc = device_register(&master->dev); if (rc) { @@ -1350,8 +1355,15 @@ static int __init fsi_init(void) rc = bus_register(&fsi_bus_type); if (rc) goto fail_bus; + + rc = class_register(&fsi_master_class); + if (rc) + goto fail_class; + return 0; + fail_class: + bus_unregister(&fsi_bus_type); fail_bus: unregister_chrdev_region(fsi_base_dev, FSI_CHAR_MAX_DEVICES); return rc; @@ -1360,6 +1372,7 @@ postcore_initcall(fsi_init); static void fsi_exit(void) { + class_unregister(&fsi_master_class); bus_unregister(&fsi_bus_type); unregister_chrdev_region(fsi_base_dev, FSI_CHAR_MAX_DEVICES); ida_destroy(&fsi_minor_ida); From cf700ba035dce78f17f6ce9405a485e490656666 Mon Sep 17 00:00:00 2001 From: Jeremy Kerr Date: Fri, 8 Nov 2019 15:49:36 +1030 Subject: [PATCH 124/198] fsi: Move master attributes to fsi-master class Populate fsi_master_class->dev_attrs with the existing attribute definitions, so we don't need to explicitly register. Signed-off-by: Jeremy Kerr Signed-off-by: Joel Stanley Acked-by: Alistair Popple Link: https://lore.kernel.org/r/20191108051945.7109-3-joel@jms.id.au Signed-off-by: Greg Kroah-Hartman --- drivers/fsi/fsi-core.c | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c index 0861f6097b33..c773c65a5058 100644 --- a/drivers/fsi/fsi-core.c +++ b/drivers/fsi/fsi-core.c @@ -1241,8 +1241,17 @@ static ssize_t master_break_store(struct device *dev, static DEVICE_ATTR(break, 0200, NULL, master_break_store); +static struct attribute *master_attrs[] = { + &dev_attr_break.attr, + &dev_attr_rescan.attr, + NULL +}; + +ATTRIBUTE_GROUPS(master); + struct class fsi_master_class = { .name = "fsi-master", + .dev_groups = master_groups, }; int fsi_master_register(struct fsi_master *master) @@ -1261,20 +1270,6 @@ int fsi_master_register(struct fsi_master *master) return rc; } - rc = device_create_file(&master->dev, &dev_attr_rescan); - if (rc) { - device_del(&master->dev); - ida_simple_remove(&master_ida, master->idx); - return rc; - } - - rc = device_create_file(&master->dev, &dev_attr_break); - if (rc) { - device_del(&master->dev); - ida_simple_remove(&master_ida, master->idx); - return rc; - } - np = dev_of_node(&master->dev); if (!of_property_read_bool(np, "no-scan-on-init")) { mutex_lock(&master->scan_lock); From 2fc95e04131cc9cf1d89a5829ada5e6ce5390c03 Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Fri, 8 Nov 2019 15:49:37 +1030 Subject: [PATCH 125/198] ABI: Update FSI path documentation The paths added back in 4.13 weren't quite correct. The in reality the files documented lived under /sys/devices/../fsi0/rescan /sys/devices/../fsi0/break /sys/devices/../fsi0/slave@00:00/term /sys/devices/../fsi0/slave@00:00/raw In 5.5 with the addition of the FSI class they move to /sys/devices/../fsi-master/fsi0/rescan /sys/devices/../fsi-master/fsi0/break /sys/devices/../fsi-master/fsi0/slave@00:00/term /sys/devices/../fsi-master/fsi0/slave@00:00/raw This is closer to how the (incorrect) documentation described them. Signed-off-by: Joel Stanley Acked-by: Alistair Popple Link: https://lore.kernel.org/r/20191108051945.7109-4-joel@jms.id.au Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-bus-fsi | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-bus-fsi b/Documentation/ABI/testing/sysfs-bus-fsi index 57c806350d6c..320697bdf41d 100644 --- a/Documentation/ABI/testing/sysfs-bus-fsi +++ b/Documentation/ABI/testing/sysfs-bus-fsi @@ -1,25 +1,25 @@ -What: /sys/bus/platform/devices/fsi-master/rescan +What: /sys/bus/platform/devices/../fsi-master/fsi0/rescan Date: May 2017 KernelVersion: 4.12 -Contact: cbostic@linux.vnet.ibm.com +Contact: linux-fsi@lists.ozlabs.org Description: Initiates a FSI master scan for all connected slave devices on its links. -What: /sys/bus/platform/devices/fsi-master/break +What: /sys/bus/platform/devices/../fsi-master/fsi0/break Date: May 2017 KernelVersion: 4.12 -Contact: cbostic@linux.vnet.ibm.com +Contact: linux-fsi@lists.ozlabs.org Description: Sends an FSI BREAK command on a master's communication link to any connnected slaves. A BREAK resets connected device's logic and preps it to receive further commands from the master. -What: /sys/bus/platform/devices/fsi-master/slave@00:00/term +What: /sys/bus/platform/devices/../fsi-master/fsi0/slave@00:00/term Date: May 2017 KernelVersion: 4.12 -Contact: cbostic@linux.vnet.ibm.com +Contact: linux-fsi@lists.ozlabs.org Description: Sends an FSI terminate command from the master to its connected slave. A terminate resets the slave's state machines @@ -29,10 +29,10 @@ Description: ongoing operation in case of an expired 'Master Time Out' timer. -What: /sys/bus/platform/devices/fsi-master/slave@00:00/raw +What: /sys/bus/platform/devices/../fsi-master/fsi0/slave@00:00/raw Date: May 2017 KernelVersion: 4.12 -Contact: cbostic@linux.vnet.ibm.com +Contact: linux-fsi@lists.ozlabs.org Description: Provides a means of reading/writing a 32 bit value from/to a specified FSI bus address. From ae77481601596096939e1eac9b6e0c8e709b37c9 Mon Sep 17 00:00:00 2001 From: Andrew Jeffery Date: Fri, 8 Nov 2019 15:49:38 +1030 Subject: [PATCH 126/198] trace: fsi: Print transfer size unsigned Due to other bugs I observed a spurious -1 transfer size. Signed-off-by: Andrew Jeffery Signed-off-by: Joel Stanley Acked-by: Alistair Popple Link: https://lore.kernel.org/r/20191108051945.7109-5-joel@jms.id.au Signed-off-by: Greg Kroah-Hartman --- include/trace/events/fsi.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/trace/events/fsi.h b/include/trace/events/fsi.h index 92e5e89e52ed..9832cb8e0eb0 100644 --- a/include/trace/events/fsi.h +++ b/include/trace/events/fsi.h @@ -26,7 +26,7 @@ TRACE_EVENT(fsi_master_read, __entry->addr = addr; __entry->size = size; ), - TP_printk("fsi%d:%02d:%02d %08x[%zd]", + TP_printk("fsi%d:%02d:%02d %08x[%zu]", __entry->master_idx, __entry->link, __entry->id, @@ -56,7 +56,7 @@ TRACE_EVENT(fsi_master_write, __entry->data = 0; memcpy(&__entry->data, data, size); ), - TP_printk("fsi%d:%02d:%02d %08x[%zd] <= {%*ph}", + TP_printk("fsi%d:%02d:%02d %08x[%zu] <= {%*ph}", __entry->master_idx, __entry->link, __entry->id, @@ -93,7 +93,7 @@ TRACE_EVENT(fsi_master_rw_result, if (__entry->write || !__entry->ret) memcpy(&__entry->data, data, size); ), - TP_printk("fsi%d:%02d:%02d %08x[%zd] %s {%*ph} ret %d", + TP_printk("fsi%d:%02d:%02d %08x[%zu] %s {%*ph} ret %d", __entry->master_idx, __entry->link, __entry->id, From 9f4c2b516b4f031e3cd0e45957f4150b3c1a083d Mon Sep 17 00:00:00 2001 From: Andrew Jeffery Date: Fri, 8 Nov 2019 15:49:39 +1030 Subject: [PATCH 127/198] fsi: core: Fix small accesses and unaligned offsets via sysfs Subtracting the offset delta from four-byte alignment lead to wrapping of the requested length where `count` is less than `off`. Generalise the length handling to enable and optimise aligned access sizes for all offset and size combinations. The new formula produces the following results for given offset and count values: offset count | length --------------+------- 0 1 | 1 0 2 | 2 0 3 | 2 0 4 | 4 0 5 | 4 1 1 | 1 1 2 | 1 1 3 | 1 1 4 | 1 1 5 | 1 2 1 | 1 2 2 | 2 2 3 | 2 2 4 | 2 2 5 | 2 3 1 | 1 3 2 | 1 3 3 | 1 3 4 | 1 3 5 | 1 We might need something like this for the cfam chardevs as well, for example we don't currently implement any alignment restrictions / handling in the hardware master driver. Signed-off-by: Andrew Jeffery Signed-off-by: Joel Stanley Link: https://lore.kernel.org/r/20191108051945.7109-6-joel@jms.id.au Signed-off-by: Greg Kroah-Hartman --- drivers/fsi/fsi-core.c | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c index c773c65a5058..e02ebcb0c9e6 100644 --- a/drivers/fsi/fsi-core.c +++ b/drivers/fsi/fsi-core.c @@ -544,6 +544,31 @@ static int fsi_slave_scan(struct fsi_slave *slave) return 0; } +static unsigned long aligned_access_size(size_t offset, size_t count) +{ + unsigned long offset_unit, count_unit; + + /* Criteria: + * + * 1. Access size must be less than or equal to the maximum access + * width or the highest power-of-two factor of offset + * 2. Access size must be less than or equal to the amount specified by + * count + * + * The access width is optimal if we can calculate 1 to be strictly + * equal while still satisfying 2. + */ + + /* Find 1 by the bottom bit of offset (with a 4 byte access cap) */ + offset_unit = BIT(__builtin_ctzl(offset | 4)); + + /* Find 2 by the top bit of count */ + count_unit = BIT(8 * sizeof(unsigned long) - 1 - __builtin_clzl(count)); + + /* Constrain the maximum access width to the minimum of both criteria */ + return BIT(__builtin_ctzl(offset_unit | count_unit)); +} + static ssize_t fsi_slave_sysfs_raw_read(struct file *file, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t off, size_t count) @@ -559,8 +584,7 @@ static ssize_t fsi_slave_sysfs_raw_read(struct file *file, return -EINVAL; for (total_len = 0; total_len < count; total_len += read_len) { - read_len = min_t(size_t, count, 4); - read_len -= off & 0x3; + read_len = aligned_access_size(off, count - total_len); rc = fsi_slave_read(slave, off, buf + total_len, read_len); if (rc) @@ -587,8 +611,7 @@ static ssize_t fsi_slave_sysfs_raw_write(struct file *file, return -EINVAL; for (total_len = 0; total_len < count; total_len += write_len) { - write_len = min_t(size_t, count, 4); - write_len -= off & 0x3; + write_len = aligned_access_size(off, count - total_len); rc = fsi_slave_write(slave, off, buf + total_len, write_len); if (rc) From 56ec311fddacd0e18795322e1d25048179787b4b Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Fri, 8 Nov 2019 15:49:40 +1030 Subject: [PATCH 128/198] fsi: fsi_master_class can be static There are no users outside of this file. Fixes: 0604d53d4da8 ("fsi: Add fsi-master class") Signed-off-by: kbuild test robot Signed-off-by: Joel Stanley Link: https://lore.kernel.org/r/20191108051945.7109-7-joel@jms.id.au Signed-off-by: Greg Kroah-Hartman --- drivers/fsi/fsi-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c index e02ebcb0c9e6..8244da8a7241 100644 --- a/drivers/fsi/fsi-core.c +++ b/drivers/fsi/fsi-core.c @@ -1272,7 +1272,7 @@ static struct attribute *master_attrs[] = { ATTRIBUTE_GROUPS(master); -struct class fsi_master_class = { +static struct class fsi_master_class = { .name = "fsi-master", .dev_groups = master_groups, }; From 8bcd06d0cb3d991b45231cf22c467759b11f18aa Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Fri, 8 Nov 2019 15:49:41 +1030 Subject: [PATCH 129/198] fsi: Move defines to common header The FSI master registers are common to the hub and AST2600 master (and the FSP2, if someone was to upstream a driver for that). Add defines to the fsi-master.h header, and introduce headings to delineate the existing low level details. Acked-by: Andrew Jeffery Acked-by: Jeremy Kerr Signed-off-by: Joel Stanley Link: https://lore.kernel.org/r/20191108051945.7109-8-joel@jms.id.au Signed-off-by: Greg Kroah-Hartman --- drivers/fsi/fsi-master-hub.c | 46 ----------------------- drivers/fsi/fsi-master.h | 71 ++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 46 deletions(-) diff --git a/drivers/fsi/fsi-master-hub.c b/drivers/fsi/fsi-master-hub.c index f158b1a88286..def35cf92571 100644 --- a/drivers/fsi/fsi-master-hub.c +++ b/drivers/fsi/fsi-master-hub.c @@ -13,53 +13,7 @@ #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 */ diff --git a/drivers/fsi/fsi-master.h b/drivers/fsi/fsi-master.h index c7174237e864..6e8d4d4d5149 100644 --- a/drivers/fsi/fsi-master.h +++ b/drivers/fsi/fsi-master.h @@ -12,6 +12,71 @@ #include #include +/* + * Master registers + * + * These are used by hardware masters, such as the one in the FSP2, AST2600 and + * the hub master in POWER processors. + */ + +/* 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_MSTAP0 0xd0 /* R: Port status */ +#define FSI_MRESP0 0xd0 /* W: Port reset */ +#define FSI_MESRB0 0x1d0 /* R: Master error status */ +#define FSI_MRESB0 0x1d0 /* W: Reset bridge */ +#define FSI_MSCSB0 0x1d4 /* R: Master sub command stack */ +#define FSI_MATRB0 0x1d8 /* R: Master address trace */ +#define FSI_MDTRB0 0x1dc /* R: Master data trace */ +#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_RELA 0x20000000 /* Enable relative address commands */ +#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 */ + +/* MRESP: 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_HUB_LINK_OFFSET 0x80000 +#define FSI_HUB_LINK_SIZE 0x80000 +#define FSI_HUB_MASTER_MAX_LINKS 8 + +/* + * Protocol definitions + * + * These are used by low level masters that bit-bang out the protocol + */ + /* Various protocol delays */ #define FSI_ECHO_DELAY_CLOCKS 16 /* Number clocks for echo delay */ #define FSI_SEND_DELAY_CLOCKS 16 /* Number clocks for send delay */ @@ -47,6 +112,12 @@ /* fsi-master definition and flags */ #define FSI_MASTER_FLAG_SWCLOCK 0x1 +/* + * Structures and function prototypes + * + * These are common to all masters + */ + struct fsi_master { struct device dev; int idx; From cccaa160c37639ea7d71c59a56a276264110ed62 Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Fri, 8 Nov 2019 15:49:42 +1030 Subject: [PATCH 130/198] dt-bindings: fsi: Add description of FSI master This describes the FSI master present in the AST2600. Signed-off-by: Joel Stanley Acked-by: Alistair Popple Link: https://lore.kernel.org/r/20191108051945.7109-9-joel@jms.id.au Signed-off-by: Greg Kroah-Hartman --- .../bindings/fsi/fsi-master-aspeed.txt | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 Documentation/devicetree/bindings/fsi/fsi-master-aspeed.txt diff --git a/Documentation/devicetree/bindings/fsi/fsi-master-aspeed.txt b/Documentation/devicetree/bindings/fsi/fsi-master-aspeed.txt new file mode 100644 index 000000000000..b758f91914f7 --- /dev/null +++ b/Documentation/devicetree/bindings/fsi/fsi-master-aspeed.txt @@ -0,0 +1,24 @@ +Device-tree bindings for AST2600 FSI master +------------------------------------------- + +The AST2600 contains two identical FSI masters. They share a clock and have a +separate interrupt line and output pins. + +Required properties: + - compatible: "aspeed,ast2600-fsi-master" + - reg: base address and length + - clocks: phandle and clock number + - interrupts: platform dependent interrupt description + - pinctrl-0: phandle to pinctrl node + - pinctrl-names: pinctrl state + +Examples: + + fsi-master { + compatible = "aspeed,ast2600-fsi-master", "fsi-master"; + reg = <0x1e79b000 0x94>; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_fsi1_default>; + clocks = <&syscon ASPEED_CLK_GATE_FSICLK>; + }; From 606397d67f4184a40732537be72e7e8658c26717 Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Fri, 8 Nov 2019 15:49:43 +1030 Subject: [PATCH 131/198] fsi: Add ast2600 master driver The ast2600 BMC has a pair of FSI masters in it, behind an AHB to OPB bridge. The master driver supports reads and writes of full words, half word and byte accesses to remote CFAMs. It can perform very basic error recovery through resetting of the FSI port when an error is detected, and the issuing of breaks and terms. Signed-off-by: Joel Stanley Acked-by: Alistair Popple -- v2: - remove debugging - squash in fixes Link: https://lore.kernel.org/r/20191108051945.7109-10-joel@jms.id.au Signed-off-by: Greg Kroah-Hartman --- drivers/fsi/Kconfig | 8 + drivers/fsi/Makefile | 1 + drivers/fsi/fsi-master-aspeed.c | 522 ++++++++++++++++++++++++++++++++ 3 files changed, 531 insertions(+) create mode 100644 drivers/fsi/fsi-master-aspeed.c diff --git a/drivers/fsi/Kconfig b/drivers/fsi/Kconfig index c612db7a914a..92ce6d85802c 100644 --- a/drivers/fsi/Kconfig +++ b/drivers/fsi/Kconfig @@ -53,6 +53,14 @@ config FSI_MASTER_AST_CF lines driven by the internal ColdFire coprocessor. This requires the corresponding machine specific ColdFire firmware to be available. +config FSI_MASTER_ASPEED + tristate "FSI ASPEED master" + help + This option enables a FSI master that is present behind an OPB bridge + in the AST2600. + + Enable it for your BMC kernel in an OpenPower or IBM Power system. + config FSI_SCOM tristate "SCOM FSI client device driver" ---help--- diff --git a/drivers/fsi/Makefile b/drivers/fsi/Makefile index e4a2ff043c32..da218a1ad8e1 100644 --- a/drivers/fsi/Makefile +++ b/drivers/fsi/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_FSI) += fsi-core.o obj-$(CONFIG_FSI_MASTER_HUB) += fsi-master-hub.o +obj-$(CONFIG_FSI_MASTER_ASPEED) += fsi-master-aspeed.o obj-$(CONFIG_FSI_MASTER_GPIO) += fsi-master-gpio.o obj-$(CONFIG_FSI_MASTER_AST_CF) += fsi-master-ast-cf.o obj-$(CONFIG_FSI_SCOM) += fsi-scom.o diff --git a/drivers/fsi/fsi-master-aspeed.c b/drivers/fsi/fsi-master-aspeed.c new file mode 100644 index 000000000000..d1b83f035483 --- /dev/null +++ b/drivers/fsi/fsi-master-aspeed.c @@ -0,0 +1,522 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// Copyright (C) IBM Corporation 2018 +// FSI master driver for AST2600 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fsi-master.h" + +struct fsi_master_aspeed { + struct fsi_master master; + struct device *dev; + void __iomem *base; + struct clk *clk; +}; + +#define to_fsi_master_aspeed(m) \ + container_of(m, struct fsi_master_aspeed, master) + +/* Control register (size 0x400) */ +static const u32 ctrl_base = 0x80000000; + +static const u32 fsi_base = 0xa0000000; + +#define OPB_FSI_VER 0x00 +#define OPB_TRIGGER 0x04 +#define OPB_CTRL_BASE 0x08 +#define OPB_FSI_BASE 0x0c +#define OPB_CLK_SYNC 0x3c +#define OPB_IRQ_CLEAR 0x40 +#define OPB_IRQ_MASK 0x44 +#define OPB_IRQ_STATUS 0x48 + +#define OPB0_SELECT 0x10 +#define OPB0_RW 0x14 +#define OPB0_XFER_SIZE 0x18 +#define OPB0_FSI_ADDR 0x1c +#define OPB0_FSI_DATA_W 0x20 +#define OPB0_STATUS 0x80 +#define OPB0_FSI_DATA_R 0x84 + +#define OPB0_WRITE_ORDER1 0x4c +#define OPB0_WRITE_ORDER2 0x50 +#define OPB1_WRITE_ORDER1 0x54 +#define OPB1_WRITE_ORDER2 0x58 +#define OPB0_READ_ORDER1 0x5c +#define OPB1_READ_ORDER2 0x60 + +#define OPB_RETRY_COUNTER 0x64 + +/* OPBn_STATUS */ +#define STATUS_HALFWORD_ACK BIT(0) +#define STATUS_FULLWORD_ACK BIT(1) +#define STATUS_ERR_ACK BIT(2) +#define STATUS_RETRY BIT(3) +#define STATUS_TIMEOUT BIT(4) + +/* OPB_IRQ_MASK */ +#define OPB1_XFER_ACK_EN BIT(17) +#define OPB0_XFER_ACK_EN BIT(16) + +/* OPB_RW */ +#define CMD_READ BIT(0) +#define CMD_WRITE 0 + +/* OPBx_XFER_SIZE */ +#define XFER_FULLWORD (BIT(1) | BIT(0)) +#define XFER_HALFWORD (BIT(0)) +#define XFER_BYTE (0) + +#define FSI_LINK_ENABLE_SETUP_TIME 10 /* in mS */ + +#define DEFAULT_DIVISOR 14 +#define OPB_POLL_TIMEOUT 10000 + +static int __opb_write(struct fsi_master_aspeed *aspeed, u32 addr, + u32 val, u32 transfer_size) +{ + void __iomem *base = aspeed->base; + u32 reg, status; + int ret; + + writel(CMD_WRITE, base + OPB0_RW); + writel(transfer_size, base + OPB0_XFER_SIZE); + writel(addr, base + OPB0_FSI_ADDR); + writel(val, base + OPB0_FSI_DATA_W); + writel(0x1, base + OPB_IRQ_CLEAR); + writel(0x1, base + OPB_TRIGGER); + + ret = readl_poll_timeout(base + OPB_IRQ_STATUS, reg, + (reg & OPB0_XFER_ACK_EN) != 0, + 0, OPB_POLL_TIMEOUT); + + status = readl(base + OPB0_STATUS); + + /* Return error when poll timed out */ + if (ret) + return ret; + + /* Command failed, master will reset */ + if (status & STATUS_ERR_ACK) + return -EIO; + + return 0; +} + +static int opb_writeb(struct fsi_master_aspeed *aspeed, u32 addr, u8 val) +{ + return __opb_write(aspeed, addr, val, XFER_BYTE); +} + +static int opb_writew(struct fsi_master_aspeed *aspeed, u32 addr, __be16 val) +{ + return __opb_write(aspeed, addr, (__force u16)val, XFER_HALFWORD); +} + +static int opb_writel(struct fsi_master_aspeed *aspeed, u32 addr, __be32 val) +{ + return __opb_write(aspeed, addr, (__force u32)val, XFER_FULLWORD); +} + +static int __opb_read(struct fsi_master_aspeed *aspeed, uint32_t addr, + u32 transfer_size, void *out) +{ + void __iomem *base = aspeed->base; + u32 result, reg; + int status, ret; + + writel(CMD_READ, base + OPB0_RW); + writel(transfer_size, base + OPB0_XFER_SIZE); + writel(addr, base + OPB0_FSI_ADDR); + writel(0x1, base + OPB_IRQ_CLEAR); + writel(0x1, base + OPB_TRIGGER); + + ret = readl_poll_timeout(base + OPB_IRQ_STATUS, reg, + (reg & OPB0_XFER_ACK_EN) != 0, + 0, OPB_POLL_TIMEOUT); + + status = readl(base + OPB0_STATUS); + + result = readl(base + OPB0_FSI_DATA_R); + + /* Return error when poll timed out */ + if (ret) + return ret; + + /* Command failed, master will reset */ + if (status & STATUS_ERR_ACK) + return -EIO; + + if (out) { + switch (transfer_size) { + case XFER_BYTE: + *(u8 *)out = result; + break; + case XFER_HALFWORD: + *(u16 *)out = result; + break; + case XFER_FULLWORD: + *(u32 *)out = result; + break; + default: + return -EINVAL; + } + + } + + return 0; +} + +static int opb_readl(struct fsi_master_aspeed *aspeed, uint32_t addr, __be32 *out) +{ + return __opb_read(aspeed, addr, XFER_FULLWORD, out); +} + +static int opb_readw(struct fsi_master_aspeed *aspeed, uint32_t addr, __be16 *out) +{ + return __opb_read(aspeed, addr, XFER_HALFWORD, (void *)out); +} + +static int opb_readb(struct fsi_master_aspeed *aspeed, uint32_t addr, u8 *out) +{ + return __opb_read(aspeed, addr, XFER_BYTE, (void *)out); +} + +static int check_errors(struct fsi_master_aspeed *aspeed, int err) +{ + int ret; + + if (err == -EIO) { + /* Check MAEB (0x70) ? */ + + /* Then clear errors in master */ + ret = opb_writel(aspeed, ctrl_base + FSI_MRESP0, + cpu_to_be32(FSI_MRESP_RST_ALL_MASTER)); + if (ret) { + /* TODO: log? return different code? */ + return ret; + } + /* TODO: confirm that 0x70 was okay */ + } + + /* This will pass through timeout errors */ + return err; +} + +static int aspeed_master_read(struct fsi_master *master, int link, + uint8_t id, uint32_t addr, void *val, size_t size) +{ + struct fsi_master_aspeed *aspeed = to_fsi_master_aspeed(master); + int ret; + + if (id != 0) + return -EINVAL; + + addr += link * FSI_HUB_LINK_SIZE; + + switch (size) { + case 1: + ret = opb_readb(aspeed, fsi_base + addr, val); + break; + case 2: + ret = opb_readw(aspeed, fsi_base + addr, val); + break; + case 4: + ret = opb_readl(aspeed, fsi_base + addr, val); + break; + default: + return -EINVAL; + } + + ret = check_errors(aspeed, ret); + if (ret) + return ret; + + return 0; +} + +static int aspeed_master_write(struct fsi_master *master, int link, + uint8_t id, uint32_t addr, const void *val, size_t size) +{ + struct fsi_master_aspeed *aspeed = to_fsi_master_aspeed(master); + int ret; + + if (id != 0) + return -EINVAL; + + addr += link * FSI_HUB_LINK_SIZE; + + switch (size) { + case 1: + ret = opb_writeb(aspeed, fsi_base + addr, *(u8 *)val); + break; + case 2: + ret = opb_writew(aspeed, fsi_base + addr, *(__be16 *)val); + break; + case 4: + ret = opb_writel(aspeed, fsi_base + addr, *(__be32 *)val); + break; + default: + return -EINVAL; + } + + ret = check_errors(aspeed, ret); + if (ret) + return ret; + + return 0; +} + +static int aspeed_master_link_enable(struct fsi_master *master, int link) +{ + struct fsi_master_aspeed *aspeed = to_fsi_master_aspeed(master); + int idx, bit, ret; + __be32 reg, result; + + idx = link / 32; + bit = link % 32; + + reg = cpu_to_be32(0x80000000 >> bit); + + ret = opb_writel(aspeed, ctrl_base + FSI_MSENP0 + (4 * idx), reg); + if (ret) + return ret; + + mdelay(FSI_LINK_ENABLE_SETUP_TIME); + + ret = opb_readl(aspeed, ctrl_base + FSI_MENP0 + (4 * idx), &result); + if (ret) + return ret; + + if (result != reg) { + dev_err(aspeed->dev, "%s failed: %08x\n", __func__, result); + return -EIO; + } + + return 0; +} + +static int aspeed_master_term(struct fsi_master *master, int link, uint8_t id) +{ + uint32_t addr; + __be32 cmd; + + addr = 0x4; + cmd = cpu_to_be32(0xecc00000); + + return aspeed_master_write(master, link, id, addr, &cmd, 4); +} + +static int aspeed_master_break(struct fsi_master *master, int link) +{ + uint32_t addr; + __be32 cmd; + + addr = 0x0; + cmd = cpu_to_be32(0xc0de0000); + + return aspeed_master_write(master, link, 0, addr, &cmd, 4); +} + +static void aspeed_master_release(struct device *dev) +{ + struct fsi_master_aspeed *aspeed = + to_fsi_master_aspeed(dev_to_fsi_master(dev)); + + kfree(aspeed); +} + +/* 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 aspeed_master_init(struct fsi_master_aspeed *aspeed) +{ + __be32 reg; + + reg = cpu_to_be32(FSI_MRESP_RST_ALL_MASTER | FSI_MRESP_RST_ALL_LINK + | FSI_MRESP_RST_MCR | FSI_MRESP_RST_PYE); + opb_writel(aspeed, ctrl_base + FSI_MRESP0, reg); + + /* 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); + opb_writel(aspeed, ctrl_base + FSI_MRESP0, reg); + + reg = cpu_to_be32(FSI_MECTRL_EOAE | FSI_MECTRL_P8_AUTO_TERM); + opb_writel(aspeed, ctrl_base + FSI_MECTRL, reg); + + reg = cpu_to_be32(FSI_MMODE_ECRC | FSI_MMODE_EPC | FSI_MMODE_RELA + | fsi_mmode_crs0(DEFAULT_DIVISOR) + | fsi_mmode_crs1(DEFAULT_DIVISOR) + | FSI_MMODE_P8_TO_LSB); + opb_writel(aspeed, ctrl_base + FSI_MMODE, reg); + + reg = cpu_to_be32(0xffff0000); + opb_writel(aspeed, ctrl_base + FSI_MDLYR, reg); + + reg = cpu_to_be32(~0); + opb_writel(aspeed, ctrl_base + FSI_MSENP0, reg); + + /* Leave enabled long enough for master logic to set up */ + mdelay(FSI_LINK_ENABLE_SETUP_TIME); + + opb_writel(aspeed, ctrl_base + FSI_MCENP0, reg); + + opb_readl(aspeed, ctrl_base + FSI_MAEB, NULL); + + reg = cpu_to_be32(FSI_MRESP_RST_ALL_MASTER | FSI_MRESP_RST_ALL_LINK); + opb_writel(aspeed, ctrl_base + FSI_MRESP0, reg); + + opb_readl(aspeed, ctrl_base + FSI_MLEVP0, NULL); + + /* Reset the master bridge */ + reg = cpu_to_be32(FSI_MRESB_RST_GEN); + opb_writel(aspeed, ctrl_base + FSI_MRESB0, reg); + + reg = cpu_to_be32(FSI_MRESB_RST_ERR); + opb_writel(aspeed, ctrl_base + FSI_MRESB0, reg); + + return 0; +} + +static int fsi_master_aspeed_probe(struct platform_device *pdev) +{ + struct fsi_master_aspeed *aspeed; + struct resource *res; + int rc, links, reg; + __be32 raw; + + aspeed = devm_kzalloc(&pdev->dev, sizeof(*aspeed), GFP_KERNEL); + if (!aspeed) + return -ENOMEM; + + aspeed->dev = &pdev->dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + aspeed->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(aspeed->base)) + return PTR_ERR(aspeed->base); + + aspeed->clk = devm_clk_get(aspeed->dev, NULL); + if (IS_ERR(aspeed->clk)) { + dev_err(aspeed->dev, "couldn't get clock\n"); + return PTR_ERR(aspeed->clk); + } + rc = clk_prepare_enable(aspeed->clk); + if (rc) { + dev_err(aspeed->dev, "couldn't enable clock\n"); + return rc; + } + + writel(0x1, aspeed->base + OPB_CLK_SYNC); + writel(OPB1_XFER_ACK_EN | OPB0_XFER_ACK_EN, + aspeed->base + OPB_IRQ_MASK); + + /* TODO: determine an appropriate value */ + writel(0x10, aspeed->base + OPB_RETRY_COUNTER); + + writel(ctrl_base, aspeed->base + OPB_CTRL_BASE); + writel(fsi_base, aspeed->base + OPB_FSI_BASE); + + /* Set read data order */ + writel(0x0011bb1b, aspeed->base + OPB0_READ_ORDER1); + + /* Set write data order */ + writel(0x0011bb1b, aspeed->base + OPB0_WRITE_ORDER1); + writel(0xffaa5500, aspeed->base + OPB0_WRITE_ORDER2); + + /* + * Select OPB0 for all operations. + * Will need to be reworked when enabling DMA or anything that uses + * OPB1. + */ + writel(0x1, aspeed->base + OPB0_SELECT); + + rc = opb_readl(aspeed, ctrl_base + FSI_MVER, &raw); + if (rc) { + dev_err(&pdev->dev, "failed to read hub version\n"); + return rc; + } + + reg = be32_to_cpu(raw); + links = (reg >> 8) & 0xff; + dev_info(&pdev->dev, "hub version %08x (%d links)\n", reg, links); + + aspeed->master.dev.parent = &pdev->dev; + aspeed->master.dev.release = aspeed_master_release; + aspeed->master.dev.of_node = of_node_get(dev_of_node(&pdev->dev)); + + aspeed->master.n_links = links; + aspeed->master.read = aspeed_master_read; + aspeed->master.write = aspeed_master_write; + aspeed->master.send_break = aspeed_master_break; + aspeed->master.term = aspeed_master_term; + aspeed->master.link_enable = aspeed_master_link_enable; + + dev_set_drvdata(&pdev->dev, aspeed); + + aspeed_master_init(aspeed); + + rc = fsi_master_register(&aspeed->master); + if (rc) + goto err_release; + + /* At this point, fsi_master_register performs the device_initialize(), + * and holds the sole reference on master.dev. This means the device + * will be freed (via ->release) during any subsequent call to + * fsi_master_unregister. We add our own reference to it here, so we + * can perform cleanup (in _remove()) without it being freed before + * we're ready. + */ + get_device(&aspeed->master.dev); + return 0; + +err_release: + clk_disable_unprepare(aspeed->clk); + return rc; +} + +static int fsi_master_aspeed_remove(struct platform_device *pdev) +{ + struct fsi_master_aspeed *aspeed = platform_get_drvdata(pdev); + + fsi_master_unregister(&aspeed->master); + clk_disable_unprepare(aspeed->clk); + + return 0; +} + +static const struct of_device_id fsi_master_aspeed_match[] = { + { .compatible = "aspeed,ast2600-fsi-master" }, + { }, +}; + +static struct platform_driver fsi_master_aspeed_driver = { + .driver = { + .name = "fsi-master-aspeed", + .of_match_table = fsi_master_aspeed_match, + }, + .probe = fsi_master_aspeed_probe, + .remove = fsi_master_aspeed_remove, +}; + +module_platform_driver(fsi_master_aspeed_driver); +MODULE_LICENSE("GPL"); From 913b73730e15eebbe7182834a3a923ca0c1255e8 Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Fri, 8 Nov 2019 15:49:44 +1030 Subject: [PATCH 132/198] fsi: aspeed: Add trace points These trace points help with debugging the FSI master. They show the low level reads, writes and error states of the master. Signed-off-by: Joel Stanley Link: https://lore.kernel.org/r/20191108051945.7109-11-joel@jms.id.au Signed-off-by: Greg Kroah-Hartman --- drivers/fsi/fsi-master-aspeed.c | 22 +++++++ include/trace/events/fsi_master_aspeed.h | 77 ++++++++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 include/trace/events/fsi_master_aspeed.h diff --git a/drivers/fsi/fsi-master-aspeed.c b/drivers/fsi/fsi-master-aspeed.c index d1b83f035483..95e226ac78b9 100644 --- a/drivers/fsi/fsi-master-aspeed.c +++ b/drivers/fsi/fsi-master-aspeed.c @@ -77,6 +77,9 @@ static const u32 fsi_base = 0xa0000000; #define XFER_HALFWORD (BIT(0)) #define XFER_BYTE (0) +#define CREATE_TRACE_POINTS +#include + #define FSI_LINK_ENABLE_SETUP_TIME 10 /* in mS */ #define DEFAULT_DIVISOR 14 @@ -102,6 +105,8 @@ static int __opb_write(struct fsi_master_aspeed *aspeed, u32 addr, status = readl(base + OPB0_STATUS); + trace_fsi_master_aspeed_opb_write(addr, val, transfer_size, status, reg); + /* Return error when poll timed out */ if (ret) return ret; @@ -149,6 +154,10 @@ static int __opb_read(struct fsi_master_aspeed *aspeed, uint32_t addr, result = readl(base + OPB0_FSI_DATA_R); + trace_fsi_master_aspeed_opb_read(addr, transfer_size, result, + readl(base + OPB0_STATUS), + reg); + /* Return error when poll timed out */ if (ret) return ret; @@ -196,6 +205,19 @@ static int check_errors(struct fsi_master_aspeed *aspeed, int err) { int ret; + if (trace_fsi_master_aspeed_opb_error_enabled()) { + __be32 mresp0, mstap0, mesrb0; + + opb_readl(aspeed, ctrl_base + FSI_MRESP0, &mresp0); + opb_readl(aspeed, ctrl_base + FSI_MSTAP0, &mstap0); + opb_readl(aspeed, ctrl_base + FSI_MESRB0, &mesrb0); + + trace_fsi_master_aspeed_opb_error( + be32_to_cpu(mresp0), + be32_to_cpu(mstap0), + be32_to_cpu(mesrb0)); + } + if (err == -EIO) { /* Check MAEB (0x70) ? */ diff --git a/include/trace/events/fsi_master_aspeed.h b/include/trace/events/fsi_master_aspeed.h new file mode 100644 index 000000000000..a355ceacc33f --- /dev/null +++ b/include/trace/events/fsi_master_aspeed.h @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM fsi_master_aspeed + +#if !defined(_TRACE_FSI_MASTER_ASPEED_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_FSI_MASTER_ASPEED_H + +#include + +TRACE_EVENT(fsi_master_aspeed_opb_read, + TP_PROTO(uint32_t addr, size_t size, uint32_t result, uint32_t status, uint32_t irq_status), + TP_ARGS(addr, size, result, status, irq_status), + TP_STRUCT__entry( + __field(uint32_t, addr) + __field(size_t, size) + __field(uint32_t, result) + __field(uint32_t, status) + __field(uint32_t, irq_status) + ), + TP_fast_assign( + __entry->addr = addr; + __entry->size = size; + __entry->result = result; + __entry->status = status; + __entry->irq_status = irq_status; + ), + TP_printk("addr %08x size %zu: result %08x sts: %08x irq_sts: %08x", + __entry->addr, __entry->size, __entry->result, + __entry->status, __entry->irq_status + ) +); + +TRACE_EVENT(fsi_master_aspeed_opb_write, + TP_PROTO(uint32_t addr, uint32_t val, size_t size, uint32_t status, uint32_t irq_status), + TP_ARGS(addr, val, size, status, irq_status), + TP_STRUCT__entry( + __field(uint32_t, addr) + __field(uint32_t, val) + __field(size_t, size) + __field(uint32_t, status) + __field(uint32_t, irq_status) + ), + TP_fast_assign( + __entry->addr = addr; + __entry->val = val; + __entry->size = size; + __entry->status = status; + __entry->irq_status = irq_status; + ), + TP_printk("addr %08x val %08x size %zu status: %08x irq_sts: %08x", + __entry->addr, __entry->val, __entry->size, + __entry->status, __entry->irq_status + ) + ); + +TRACE_EVENT(fsi_master_aspeed_opb_error, + TP_PROTO(uint32_t mresp0, uint32_t mstap0, uint32_t mesrb0), + TP_ARGS(mresp0, mstap0, mesrb0), + TP_STRUCT__entry( + __field(uint32_t, mresp0) + __field(uint32_t, mstap0) + __field(uint32_t, mesrb0) + ), + TP_fast_assign( + __entry->mresp0 = mresp0; + __entry->mstap0 = mstap0; + __entry->mesrb0 = mesrb0; + ), + TP_printk("mresp0 %08x mstap0 %08x mesrb0 %08x", + __entry->mresp0, __entry->mstap0, __entry->mesrb0 + ) + ); + +#endif + +#include From 5e5022995683539609b3904213b7d72ab71188b7 Mon Sep 17 00:00:00 2001 From: Andrew Jeffery Date: Fri, 8 Nov 2019 15:49:45 +1030 Subject: [PATCH 133/198] fsi: aspeed: Fix OPB0 byte order register values The data byte order selection registers in the APB2OPB primarily expose some internal plumbing necessary to get correct write accesses onto the OPB. OPB write cycles require "data mirroring" across the 32-bit data bus to support variable data width slaves that don't implement "byte enables". For slaves that do implement byte enables the master can signal which bytes on the data bus the slave should consider valid. The data mirroring behaviour is specified by the following table: +-----------------+----------+-----------------------------------+ | | | 32-bit Data Bus | +---------+-------+----------+---------+---------+-------+-------+ | | | | | | | | | ABus | Mn_BE | Request | Dbus | Dbus | Dbus | Dbus | | (30:31) | (0:3) | Transfer | 0:7 | 8:15 | 16:23 | 24:31 | | | | Size | byte0 | byte1 | byte2 | byte3 | +---------+-------+----------+---------+---------+-------+-------+ | 00 | 1111 | fullword | byte0 | byte1 | byte2 | byte3 | +---------+-------+----------+---------+---------+-------+-------+ | 00 | 1110 | halfword | byte0 | byte1 | byte2 | | +---------+-------+----------+---------+---------+-------+-------+ | 01 | 0111 | byte | _byte1_ | byte1 | byte2 | byte3 | +---------+-------+----------+---------+---------+-------+-------+ | 00 | 1100 | halfword | byte0 | byte1 | | | +---------+-------+----------+---------+---------+-------+-------+ | 01 | 0110 | byte | _byte1_ | byte1 | byte2 | | +---------+-------+----------+---------+---------+-------+-------+ | 10 | 0011 | halfword | _byte2_ | _byte3_ | byte2 | byte3 | +---------+-------+----------+---------+---------+-------+-------+ | 00 | 1000 | byte | byte0 | | | | +---------+-------+----------+---------+---------+-------+-------+ | 01 | 0100 | byte | _byte1_ | byte1 | | | +---------+-------+----------+---------+---------+-------+-------+ | 10 | 0010 | byte | _byte2_ | | byte2 | | +---------+-------+----------+---------+---------+-------+-------+ | 11 | 0001 | byte | _byte3_ | _byte3_ | | byte3 | +---------+-------+----------+---------+---------+-------+-------+ Mirrored data values are highlighted by underscores in the Dbus columns. The values in the ABus and Request Transfer Size columns correspond to values in the field names listed in the write data order select register descriptions. Similar configuration registers are exposed for reads which enables the secondary purpose of configuring hardware endian conversions. It appears the data bus byte order is switched around in hardware so set the registers such that we can access the correct values for all widths. The values were determined by experimentation on hardware against fixed CFAM register values to configure the read data order, then in combination with the table above and the register layout documentation in the AST2600 datasheet performing write/read cycles to configure the write data order registers. Signed-off-by: Andrew Jeffery Signed-off-by: Joel Stanley Acked-by: Alistair Popple Link: https://lore.kernel.org/r/20191108051945.7109-12-joel@jms.id.au Signed-off-by: Greg Kroah-Hartman --- drivers/fsi/fsi-master-aspeed.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/fsi/fsi-master-aspeed.c b/drivers/fsi/fsi-master-aspeed.c index 95e226ac78b9..f49742b310c2 100644 --- a/drivers/fsi/fsi-master-aspeed.c +++ b/drivers/fsi/fsi-master-aspeed.c @@ -459,11 +459,11 @@ static int fsi_master_aspeed_probe(struct platform_device *pdev) writel(fsi_base, aspeed->base + OPB_FSI_BASE); /* Set read data order */ - writel(0x0011bb1b, aspeed->base + OPB0_READ_ORDER1); + writel(0x00030b1b, aspeed->base + OPB0_READ_ORDER1); /* Set write data order */ - writel(0x0011bb1b, aspeed->base + OPB0_WRITE_ORDER1); - writel(0xffaa5500, aspeed->base + OPB0_WRITE_ORDER2); + writel(0x0011101b, aspeed->base + OPB0_WRITE_ORDER1); + writel(0x0c330f3f, aspeed->base + OPB0_WRITE_ORDER2); /* * Select OPB0 for all operations. From 6120e5d821c0e3104ddbc2ad5dd126e0c9eb20f2 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Fri, 8 Nov 2019 14:53:48 +0200 Subject: [PATCH 134/198] dt-bindings: interconnect: qcom: add msm8974 bindings Add device tree bindings for the Qualcomm MSM8974 interconnect providers that support setting system bandwidth requirements between various network-on-chip fabrics. Signed-off-by: Brian Masney Reviewed-by: Rob Herring Reviewed-by: Bjorn Andersson Link: https://lore.kernel.org/r/20191024103054.9770-2-masneyb@onstation.org Signed-off-by: Georgi Djakov Link: https://lore.kernel.org/r/20191108125349.24191-2-georgi.djakov@linaro.org Signed-off-by: Greg Kroah-Hartman --- .../bindings/interconnect/qcom,msm8974.yaml | 62 ++++++++ .../dt-bindings/interconnect/qcom,msm8974.h | 146 ++++++++++++++++++ 2 files changed, 208 insertions(+) create mode 100644 Documentation/devicetree/bindings/interconnect/qcom,msm8974.yaml create mode 100644 include/dt-bindings/interconnect/qcom,msm8974.h diff --git a/Documentation/devicetree/bindings/interconnect/qcom,msm8974.yaml b/Documentation/devicetree/bindings/interconnect/qcom,msm8974.yaml new file mode 100644 index 000000000000..9af3c6e59cff --- /dev/null +++ b/Documentation/devicetree/bindings/interconnect/qcom,msm8974.yaml @@ -0,0 +1,62 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/interconnect/qcom,msm8974.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm MSM8974 Network-On-Chip Interconnect + +maintainers: + - Brian Masney + +description: | + The Qualcomm MSM8974 interconnect providers support setting system + bandwidth requirements between various network-on-chip fabrics. + +properties: + reg: + maxItems: 1 + + compatible: + enum: + - qcom,msm8974-bimc + - qcom,msm8974-cnoc + - qcom,msm8974-mmssnoc + - qcom,msm8974-ocmemnoc + - qcom,msm8974-pnoc + - qcom,msm8974-snoc + + '#interconnect-cells': + const: 1 + + clock-names: + items: + - const: bus + - const: bus_a + + clocks: + items: + - description: Bus Clock + - description: Bus A Clock + +required: + - compatible + - reg + - '#interconnect-cells' + - clock-names + - clocks + +additionalProperties: false + +examples: + - | + #include + + bimc: interconnect@fc380000 { + reg = <0xfc380000 0x6a000>; + compatible = "qcom,msm8974-bimc"; + #interconnect-cells = <1>; + clock-names = "bus", "bus_a"; + clocks = <&rpmcc RPM_SMD_BIMC_CLK>, + <&rpmcc RPM_SMD_BIMC_A_CLK>; + }; diff --git a/include/dt-bindings/interconnect/qcom,msm8974.h b/include/dt-bindings/interconnect/qcom,msm8974.h new file mode 100644 index 000000000000..e65ae27ffff2 --- /dev/null +++ b/include/dt-bindings/interconnect/qcom,msm8974.h @@ -0,0 +1,146 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) */ +/* + * Qualcomm msm8974 interconnect IDs + * + * Copyright (c) 2019 Brian Masney + */ + +#ifndef __DT_BINDINGS_INTERCONNECT_QCOM_MSM8974_H +#define __DT_BINDINGS_INTERCONNECT_QCOM_MSM8974_H + +#define BIMC_MAS_AMPSS_M0 0 +#define BIMC_MAS_AMPSS_M1 1 +#define BIMC_MAS_MSS_PROC 2 +#define BIMC_TO_MNOC 3 +#define BIMC_TO_SNOC 4 +#define BIMC_SLV_EBI_CH0 5 +#define BIMC_SLV_AMPSS_L2 6 + +#define CNOC_MAS_RPM_INST 0 +#define CNOC_MAS_RPM_DATA 1 +#define CNOC_MAS_RPM_SYS 2 +#define CNOC_MAS_DEHR 3 +#define CNOC_MAS_QDSS_DAP 4 +#define CNOC_MAS_SPDM 5 +#define CNOC_MAS_TIC 6 +#define CNOC_SLV_CLK_CTL 7 +#define CNOC_SLV_CNOC_MSS 8 +#define CNOC_SLV_SECURITY 9 +#define CNOC_SLV_TCSR 10 +#define CNOC_SLV_TLMM 11 +#define CNOC_SLV_CRYPTO_0_CFG 12 +#define CNOC_SLV_CRYPTO_1_CFG 13 +#define CNOC_SLV_IMEM_CFG 14 +#define CNOC_SLV_MESSAGE_RAM 15 +#define CNOC_SLV_BIMC_CFG 16 +#define CNOC_SLV_BOOT_ROM 17 +#define CNOC_SLV_PMIC_ARB 18 +#define CNOC_SLV_SPDM_WRAPPER 19 +#define CNOC_SLV_DEHR_CFG 20 +#define CNOC_SLV_MPM 21 +#define CNOC_SLV_QDSS_CFG 22 +#define CNOC_SLV_RBCPR_CFG 23 +#define CNOC_SLV_RBCPR_QDSS_APU_CFG 24 +#define CNOC_TO_SNOC 25 +#define CNOC_SLV_CNOC_ONOC_CFG 26 +#define CNOC_SLV_CNOC_MNOC_MMSS_CFG 27 +#define CNOC_SLV_CNOC_MNOC_CFG 28 +#define CNOC_SLV_PNOC_CFG 29 +#define CNOC_SLV_SNOC_MPU_CFG 30 +#define CNOC_SLV_SNOC_CFG 31 +#define CNOC_SLV_EBI1_DLL_CFG 32 +#define CNOC_SLV_PHY_APU_CFG 33 +#define CNOC_SLV_EBI1_PHY_CFG 34 +#define CNOC_SLV_RPM 35 +#define CNOC_SLV_SERVICE_CNOC 36 + +#define MNOC_MAS_GRAPHICS_3D 0 +#define MNOC_MAS_JPEG 1 +#define MNOC_MAS_MDP_PORT0 2 +#define MNOC_MAS_VIDEO_P0 3 +#define MNOC_MAS_VIDEO_P1 4 +#define MNOC_MAS_VFE 5 +#define MNOC_TO_CNOC 6 +#define MNOC_TO_BIMC 7 +#define MNOC_SLV_CAMERA_CFG 8 +#define MNOC_SLV_DISPLAY_CFG 9 +#define MNOC_SLV_OCMEM_CFG 10 +#define MNOC_SLV_CPR_CFG 11 +#define MNOC_SLV_CPR_XPU_CFG 12 +#define MNOC_SLV_MISC_CFG 13 +#define MNOC_SLV_MISC_XPU_CFG 14 +#define MNOC_SLV_VENUS_CFG 15 +#define MNOC_SLV_GRAPHICS_3D_CFG 16 +#define MNOC_SLV_MMSS_CLK_CFG 17 +#define MNOC_SLV_MMSS_CLK_XPU_CFG 18 +#define MNOC_SLV_MNOC_MPU_CFG 19 +#define MNOC_SLV_ONOC_MPU_CFG 20 +#define MNOC_SLV_SERVICE_MNOC 21 + +#define OCMEM_NOC_TO_OCMEM_VNOC 0 +#define OCMEM_MAS_JPEG_OCMEM 1 +#define OCMEM_MAS_MDP_OCMEM 2 +#define OCMEM_MAS_VIDEO_P0_OCMEM 3 +#define OCMEM_MAS_VIDEO_P1_OCMEM 4 +#define OCMEM_MAS_VFE_OCMEM 5 +#define OCMEM_MAS_CNOC_ONOC_CFG 6 +#define OCMEM_SLV_SERVICE_ONOC 7 +#define OCMEM_VNOC_TO_SNOC 8 +#define OCMEM_VNOC_TO_OCMEM_NOC 9 +#define OCMEM_VNOC_MAS_GFX3D 10 +#define OCMEM_SLV_OCMEM 11 + +#define PNOC_MAS_PNOC_CFG 0 +#define PNOC_MAS_SDCC_1 1 +#define PNOC_MAS_SDCC_3 2 +#define PNOC_MAS_SDCC_4 3 +#define PNOC_MAS_SDCC_2 4 +#define PNOC_MAS_TSIF 5 +#define PNOC_MAS_BAM_DMA 6 +#define PNOC_MAS_BLSP_2 7 +#define PNOC_MAS_USB_HSIC 8 +#define PNOC_MAS_BLSP_1 9 +#define PNOC_MAS_USB_HS 10 +#define PNOC_TO_SNOC 11 +#define PNOC_SLV_SDCC_1 12 +#define PNOC_SLV_SDCC_3 13 +#define PNOC_SLV_SDCC_2 14 +#define PNOC_SLV_SDCC_4 15 +#define PNOC_SLV_TSIF 16 +#define PNOC_SLV_BAM_DMA 17 +#define PNOC_SLV_BLSP_2 18 +#define PNOC_SLV_USB_HSIC 19 +#define PNOC_SLV_BLSP_1 20 +#define PNOC_SLV_USB_HS 21 +#define PNOC_SLV_PDM 22 +#define PNOC_SLV_PERIPH_APU_CFG 23 +#define PNOC_SLV_PNOC_MPU_CFG 24 +#define PNOC_SLV_PRNG 25 +#define PNOC_SLV_SERVICE_PNOC 26 + +#define SNOC_MAS_LPASS_AHB 0 +#define SNOC_MAS_QDSS_BAM 1 +#define SNOC_MAS_SNOC_CFG 2 +#define SNOC_TO_BIMC 3 +#define SNOC_TO_CNOC 4 +#define SNOC_TO_PNOC 5 +#define SNOC_TO_OCMEM_VNOC 6 +#define SNOC_MAS_CRYPTO_CORE0 7 +#define SNOC_MAS_CRYPTO_CORE1 8 +#define SNOC_MAS_LPASS_PROC 9 +#define SNOC_MAS_MSS 10 +#define SNOC_MAS_MSS_NAV 11 +#define SNOC_MAS_OCMEM_DMA 12 +#define SNOC_MAS_WCSS 13 +#define SNOC_MAS_QDSS_ETR 14 +#define SNOC_MAS_USB3 15 +#define SNOC_SLV_AMPSS 16 +#define SNOC_SLV_LPASS 17 +#define SNOC_SLV_USB3 18 +#define SNOC_SLV_WCSS 19 +#define SNOC_SLV_OCIMEM 20 +#define SNOC_SLV_SNOC_OCMEM 21 +#define SNOC_SLV_SERVICE_SNOC 22 +#define SNOC_SLV_QDSS_STM 23 + +#endif From 4e60a9568dc6f411d4f631fe33b5553d080b7e8c Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Fri, 8 Nov 2019 14:53:49 +0200 Subject: [PATCH 135/198] interconnect: qcom: add msm8974 driver Add driver for the Qualcomm MSM8974 interconnect providers that support setting system bandwidth requirements between various network-on-chip fabrics. Signed-off-by: Brian Masney Reviewed-by: Bjorn Andersson Link: https://lore.kernel.org/r/20191024103054.9770-3-masneyb@onstation.org Signed-off-by: Georgi Djakov Link: https://lore.kernel.org/r/20191108125349.24191-3-georgi.djakov@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/interconnect/qcom/Kconfig | 9 + drivers/interconnect/qcom/Makefile | 2 + drivers/interconnect/qcom/msm8974.c | 784 ++++++++++++++++++++++++++++ 3 files changed, 795 insertions(+) create mode 100644 drivers/interconnect/qcom/msm8974.c diff --git a/drivers/interconnect/qcom/Kconfig b/drivers/interconnect/qcom/Kconfig index 6ab4012a059a..c49afbea3458 100644 --- a/drivers/interconnect/qcom/Kconfig +++ b/drivers/interconnect/qcom/Kconfig @@ -5,6 +5,15 @@ config INTERCONNECT_QCOM help Support for Qualcomm's Network-on-Chip interconnect hardware. +config INTERCONNECT_QCOM_MSM8974 + tristate "Qualcomm MSM8974 interconnect driver" + depends on INTERCONNECT_QCOM + depends on QCOM_SMD_RPM + select INTERCONNECT_QCOM_SMD_RPM + help + This is a driver for the Qualcomm Network-on-Chip on msm8974-based + platforms. + config INTERCONNECT_QCOM_QCS404 tristate "Qualcomm QCS404 interconnect driver" depends on INTERCONNECT_QCOM diff --git a/drivers/interconnect/qcom/Makefile b/drivers/interconnect/qcom/Makefile index 67dafb783dec..9adf9e380545 100644 --- a/drivers/interconnect/qcom/Makefile +++ b/drivers/interconnect/qcom/Makefile @@ -1,9 +1,11 @@ # SPDX-License-Identifier: GPL-2.0 +qnoc-msm8974-objs := msm8974.o qnoc-qcs404-objs := qcs404.o qnoc-sdm845-objs := sdm845.o icc-smd-rpm-objs := smd-rpm.o +obj-$(CONFIG_INTERCONNECT_QCOM_MSM8974) += qnoc-msm8974.o obj-$(CONFIG_INTERCONNECT_QCOM_QCS404) += qnoc-qcs404.o obj-$(CONFIG_INTERCONNECT_QCOM_SDM845) += qnoc-sdm845.o obj-$(CONFIG_INTERCONNECT_QCOM_SMD_RPM) += icc-smd-rpm.o diff --git a/drivers/interconnect/qcom/msm8974.c b/drivers/interconnect/qcom/msm8974.c new file mode 100644 index 000000000000..ce599a0c83d9 --- /dev/null +++ b/drivers/interconnect/qcom/msm8974.c @@ -0,0 +1,784 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Brian Masney + * + * Based on MSM bus code from downstream MSM kernel sources. + * Copyright (c) 2012-2013 The Linux Foundation. All rights reserved. + * + * Based on qcs404.c + * Copyright (C) 2019 Linaro Ltd + * + * Here's a rough representation that shows the various buses that form the + * Network On Chip (NOC) for the msm8974: + * + * Multimedia Subsystem (MMSS) + * |----------+-----------------------------------+-----------| + * | | + * | | + * Config | Bus Interface | Memory Controller + * |------------+-+-----------| |------------+-+-----------| + * | | + * | | + * | System | + * |--------------+-+---------------------------------+-+-------------| + * | | + * | | + * Peripheral | On Chip | Memory (OCMEM) + * |------------+-------------| |------------+-------------| + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "smd-rpm.h" + +enum { + MSM8974_BIMC_MAS_AMPSS_M0 = 1, + MSM8974_BIMC_MAS_AMPSS_M1, + MSM8974_BIMC_MAS_MSS_PROC, + MSM8974_BIMC_TO_MNOC, + MSM8974_BIMC_TO_SNOC, + MSM8974_BIMC_SLV_EBI_CH0, + MSM8974_BIMC_SLV_AMPSS_L2, + MSM8974_CNOC_MAS_RPM_INST, + MSM8974_CNOC_MAS_RPM_DATA, + MSM8974_CNOC_MAS_RPM_SYS, + MSM8974_CNOC_MAS_DEHR, + MSM8974_CNOC_MAS_QDSS_DAP, + MSM8974_CNOC_MAS_SPDM, + MSM8974_CNOC_MAS_TIC, + MSM8974_CNOC_SLV_CLK_CTL, + MSM8974_CNOC_SLV_CNOC_MSS, + MSM8974_CNOC_SLV_SECURITY, + MSM8974_CNOC_SLV_TCSR, + MSM8974_CNOC_SLV_TLMM, + MSM8974_CNOC_SLV_CRYPTO_0_CFG, + MSM8974_CNOC_SLV_CRYPTO_1_CFG, + MSM8974_CNOC_SLV_IMEM_CFG, + MSM8974_CNOC_SLV_MESSAGE_RAM, + MSM8974_CNOC_SLV_BIMC_CFG, + MSM8974_CNOC_SLV_BOOT_ROM, + MSM8974_CNOC_SLV_PMIC_ARB, + MSM8974_CNOC_SLV_SPDM_WRAPPER, + MSM8974_CNOC_SLV_DEHR_CFG, + MSM8974_CNOC_SLV_MPM, + MSM8974_CNOC_SLV_QDSS_CFG, + MSM8974_CNOC_SLV_RBCPR_CFG, + MSM8974_CNOC_SLV_RBCPR_QDSS_APU_CFG, + MSM8974_CNOC_TO_SNOC, + MSM8974_CNOC_SLV_CNOC_ONOC_CFG, + MSM8974_CNOC_SLV_CNOC_MNOC_MMSS_CFG, + MSM8974_CNOC_SLV_CNOC_MNOC_CFG, + MSM8974_CNOC_SLV_PNOC_CFG, + MSM8974_CNOC_SLV_SNOC_MPU_CFG, + MSM8974_CNOC_SLV_SNOC_CFG, + MSM8974_CNOC_SLV_EBI1_DLL_CFG, + MSM8974_CNOC_SLV_PHY_APU_CFG, + MSM8974_CNOC_SLV_EBI1_PHY_CFG, + MSM8974_CNOC_SLV_RPM, + MSM8974_CNOC_SLV_SERVICE_CNOC, + MSM8974_MNOC_MAS_GRAPHICS_3D, + MSM8974_MNOC_MAS_JPEG, + MSM8974_MNOC_MAS_MDP_PORT0, + MSM8974_MNOC_MAS_VIDEO_P0, + MSM8974_MNOC_MAS_VIDEO_P1, + MSM8974_MNOC_MAS_VFE, + MSM8974_MNOC_TO_CNOC, + MSM8974_MNOC_TO_BIMC, + MSM8974_MNOC_SLV_CAMERA_CFG, + MSM8974_MNOC_SLV_DISPLAY_CFG, + MSM8974_MNOC_SLV_OCMEM_CFG, + MSM8974_MNOC_SLV_CPR_CFG, + MSM8974_MNOC_SLV_CPR_XPU_CFG, + MSM8974_MNOC_SLV_MISC_CFG, + MSM8974_MNOC_SLV_MISC_XPU_CFG, + MSM8974_MNOC_SLV_VENUS_CFG, + MSM8974_MNOC_SLV_GRAPHICS_3D_CFG, + MSM8974_MNOC_SLV_MMSS_CLK_CFG, + MSM8974_MNOC_SLV_MMSS_CLK_XPU_CFG, + MSM8974_MNOC_SLV_MNOC_MPU_CFG, + MSM8974_MNOC_SLV_ONOC_MPU_CFG, + MSM8974_MNOC_SLV_SERVICE_MNOC, + MSM8974_OCMEM_NOC_TO_OCMEM_VNOC, + MSM8974_OCMEM_MAS_JPEG_OCMEM, + MSM8974_OCMEM_MAS_MDP_OCMEM, + MSM8974_OCMEM_MAS_VIDEO_P0_OCMEM, + MSM8974_OCMEM_MAS_VIDEO_P1_OCMEM, + MSM8974_OCMEM_MAS_VFE_OCMEM, + MSM8974_OCMEM_MAS_CNOC_ONOC_CFG, + MSM8974_OCMEM_SLV_SERVICE_ONOC, + MSM8974_OCMEM_VNOC_TO_SNOC, + MSM8974_OCMEM_VNOC_TO_OCMEM_NOC, + MSM8974_OCMEM_VNOC_MAS_GFX3D, + MSM8974_OCMEM_SLV_OCMEM, + MSM8974_PNOC_MAS_PNOC_CFG, + MSM8974_PNOC_MAS_SDCC_1, + MSM8974_PNOC_MAS_SDCC_3, + MSM8974_PNOC_MAS_SDCC_4, + MSM8974_PNOC_MAS_SDCC_2, + MSM8974_PNOC_MAS_TSIF, + MSM8974_PNOC_MAS_BAM_DMA, + MSM8974_PNOC_MAS_BLSP_2, + MSM8974_PNOC_MAS_USB_HSIC, + MSM8974_PNOC_MAS_BLSP_1, + MSM8974_PNOC_MAS_USB_HS, + MSM8974_PNOC_TO_SNOC, + MSM8974_PNOC_SLV_SDCC_1, + MSM8974_PNOC_SLV_SDCC_3, + MSM8974_PNOC_SLV_SDCC_2, + MSM8974_PNOC_SLV_SDCC_4, + MSM8974_PNOC_SLV_TSIF, + MSM8974_PNOC_SLV_BAM_DMA, + MSM8974_PNOC_SLV_BLSP_2, + MSM8974_PNOC_SLV_USB_HSIC, + MSM8974_PNOC_SLV_BLSP_1, + MSM8974_PNOC_SLV_USB_HS, + MSM8974_PNOC_SLV_PDM, + MSM8974_PNOC_SLV_PERIPH_APU_CFG, + MSM8974_PNOC_SLV_PNOC_MPU_CFG, + MSM8974_PNOC_SLV_PRNG, + MSM8974_PNOC_SLV_SERVICE_PNOC, + MSM8974_SNOC_MAS_LPASS_AHB, + MSM8974_SNOC_MAS_QDSS_BAM, + MSM8974_SNOC_MAS_SNOC_CFG, + MSM8974_SNOC_TO_BIMC, + MSM8974_SNOC_TO_CNOC, + MSM8974_SNOC_TO_PNOC, + MSM8974_SNOC_TO_OCMEM_VNOC, + MSM8974_SNOC_MAS_CRYPTO_CORE0, + MSM8974_SNOC_MAS_CRYPTO_CORE1, + MSM8974_SNOC_MAS_LPASS_PROC, + MSM8974_SNOC_MAS_MSS, + MSM8974_SNOC_MAS_MSS_NAV, + MSM8974_SNOC_MAS_OCMEM_DMA, + MSM8974_SNOC_MAS_WCSS, + MSM8974_SNOC_MAS_QDSS_ETR, + MSM8974_SNOC_MAS_USB3, + MSM8974_SNOC_SLV_AMPSS, + MSM8974_SNOC_SLV_LPASS, + MSM8974_SNOC_SLV_USB3, + MSM8974_SNOC_SLV_WCSS, + MSM8974_SNOC_SLV_OCIMEM, + MSM8974_SNOC_SLV_SNOC_OCMEM, + MSM8974_SNOC_SLV_SERVICE_SNOC, + MSM8974_SNOC_SLV_QDSS_STM, +}; + +#define RPM_BUS_MASTER_REQ 0x73616d62 +#define RPM_BUS_SLAVE_REQ 0x766c7362 + +#define to_msm8974_icc_provider(_provider) \ + container_of(_provider, struct msm8974_icc_provider, provider) + +static const struct clk_bulk_data msm8974_icc_bus_clocks[] = { + { .id = "bus" }, + { .id = "bus_a" }, +}; + +/** + * struct msm8974_icc_provider - Qualcomm specific interconnect provider + * @provider: generic interconnect provider + * @bus_clks: the clk_bulk_data table of bus clocks + * @num_clks: the total number of clk_bulk_data entries + */ +struct msm8974_icc_provider { + struct icc_provider provider; + struct clk_bulk_data *bus_clks; + int num_clks; +}; + +#define MSM8974_ICC_MAX_LINKS 3 + +/** + * struct msm8974_icc_node - Qualcomm specific interconnect nodes + * @name: the node name used in debugfs + * @id: a unique node identifier + * @links: an array of nodes where we can go next while traversing + * @num_links: the total number of @links + * @buswidth: width of the interconnect between a node and the bus (bytes) + * @mas_rpm_id: RPM ID for devices that are bus masters + * @slv_rpm_id: RPM ID for devices that are bus slaves + * @rate: current bus clock rate in Hz + */ +struct msm8974_icc_node { + unsigned char *name; + u16 id; + u16 links[MSM8974_ICC_MAX_LINKS]; + u16 num_links; + u16 buswidth; + int mas_rpm_id; + int slv_rpm_id; + u64 rate; +}; + +struct msm8974_icc_desc { + struct msm8974_icc_node **nodes; + size_t num_nodes; +}; + +#define DEFINE_QNODE(_name, _id, _buswidth, _mas_rpm_id, _slv_rpm_id, \ + ...) \ + static struct msm8974_icc_node _name = { \ + .name = #_name, \ + .id = _id, \ + .buswidth = _buswidth, \ + .mas_rpm_id = _mas_rpm_id, \ + .slv_rpm_id = _slv_rpm_id, \ + .num_links = ARRAY_SIZE(((int[]){ __VA_ARGS__ })), \ + .links = { __VA_ARGS__ }, \ + } + +DEFINE_QNODE(mas_ampss_m0, MSM8974_BIMC_MAS_AMPSS_M0, 8, 0, -1); +DEFINE_QNODE(mas_ampss_m1, MSM8974_BIMC_MAS_AMPSS_M1, 8, 0, -1); +DEFINE_QNODE(mas_mss_proc, MSM8974_BIMC_MAS_MSS_PROC, 8, 1, -1); +DEFINE_QNODE(bimc_to_mnoc, MSM8974_BIMC_TO_MNOC, 8, 2, -1, MSM8974_BIMC_SLV_EBI_CH0); +DEFINE_QNODE(bimc_to_snoc, MSM8974_BIMC_TO_SNOC, 8, 3, 2, MSM8974_SNOC_TO_BIMC, MSM8974_BIMC_SLV_EBI_CH0, MSM8974_BIMC_MAS_AMPSS_M0); +DEFINE_QNODE(slv_ebi_ch0, MSM8974_BIMC_SLV_EBI_CH0, 8, -1, 0); +DEFINE_QNODE(slv_ampss_l2, MSM8974_BIMC_SLV_AMPSS_L2, 8, -1, 1); + +static struct msm8974_icc_node *msm8974_bimc_nodes[] = { + [BIMC_MAS_AMPSS_M0] = &mas_ampss_m0, + [BIMC_MAS_AMPSS_M1] = &mas_ampss_m1, + [BIMC_MAS_MSS_PROC] = &mas_mss_proc, + [BIMC_TO_MNOC] = &bimc_to_mnoc, + [BIMC_TO_SNOC] = &bimc_to_snoc, + [BIMC_SLV_EBI_CH0] = &slv_ebi_ch0, + [BIMC_SLV_AMPSS_L2] = &slv_ampss_l2, +}; + +static struct msm8974_icc_desc msm8974_bimc = { + .nodes = msm8974_bimc_nodes, + .num_nodes = ARRAY_SIZE(msm8974_bimc_nodes), +}; + +DEFINE_QNODE(mas_rpm_inst, MSM8974_CNOC_MAS_RPM_INST, 8, 45, -1); +DEFINE_QNODE(mas_rpm_data, MSM8974_CNOC_MAS_RPM_DATA, 8, 46, -1); +DEFINE_QNODE(mas_rpm_sys, MSM8974_CNOC_MAS_RPM_SYS, 8, 47, -1); +DEFINE_QNODE(mas_dehr, MSM8974_CNOC_MAS_DEHR, 8, 48, -1); +DEFINE_QNODE(mas_qdss_dap, MSM8974_CNOC_MAS_QDSS_DAP, 8, 49, -1); +DEFINE_QNODE(mas_spdm, MSM8974_CNOC_MAS_SPDM, 8, 50, -1); +DEFINE_QNODE(mas_tic, MSM8974_CNOC_MAS_TIC, 8, 51, -1); +DEFINE_QNODE(slv_clk_ctl, MSM8974_CNOC_SLV_CLK_CTL, 8, -1, 47); +DEFINE_QNODE(slv_cnoc_mss, MSM8974_CNOC_SLV_CNOC_MSS, 8, -1, 48); +DEFINE_QNODE(slv_security, MSM8974_CNOC_SLV_SECURITY, 8, -1, 49); +DEFINE_QNODE(slv_tcsr, MSM8974_CNOC_SLV_TCSR, 8, -1, 50); +DEFINE_QNODE(slv_tlmm, MSM8974_CNOC_SLV_TLMM, 8, -1, 51); +DEFINE_QNODE(slv_crypto_0_cfg, MSM8974_CNOC_SLV_CRYPTO_0_CFG, 8, -1, 52); +DEFINE_QNODE(slv_crypto_1_cfg, MSM8974_CNOC_SLV_CRYPTO_1_CFG, 8, -1, 53); +DEFINE_QNODE(slv_imem_cfg, MSM8974_CNOC_SLV_IMEM_CFG, 8, -1, 54); +DEFINE_QNODE(slv_message_ram, MSM8974_CNOC_SLV_MESSAGE_RAM, 8, -1, 55); +DEFINE_QNODE(slv_bimc_cfg, MSM8974_CNOC_SLV_BIMC_CFG, 8, -1, 56); +DEFINE_QNODE(slv_boot_rom, MSM8974_CNOC_SLV_BOOT_ROM, 8, -1, 57); +DEFINE_QNODE(slv_pmic_arb, MSM8974_CNOC_SLV_PMIC_ARB, 8, -1, 59); +DEFINE_QNODE(slv_spdm_wrapper, MSM8974_CNOC_SLV_SPDM_WRAPPER, 8, -1, 60); +DEFINE_QNODE(slv_dehr_cfg, MSM8974_CNOC_SLV_DEHR_CFG, 8, -1, 61); +DEFINE_QNODE(slv_mpm, MSM8974_CNOC_SLV_MPM, 8, -1, 62); +DEFINE_QNODE(slv_qdss_cfg, MSM8974_CNOC_SLV_QDSS_CFG, 8, -1, 63); +DEFINE_QNODE(slv_rbcpr_cfg, MSM8974_CNOC_SLV_RBCPR_CFG, 8, -1, 64); +DEFINE_QNODE(slv_rbcpr_qdss_apu_cfg, MSM8974_CNOC_SLV_RBCPR_QDSS_APU_CFG, 8, -1, 65); +DEFINE_QNODE(cnoc_to_snoc, MSM8974_CNOC_TO_SNOC, 8, 52, 75); +DEFINE_QNODE(slv_cnoc_onoc_cfg, MSM8974_CNOC_SLV_CNOC_ONOC_CFG, 8, -1, 68); +DEFINE_QNODE(slv_cnoc_mnoc_mmss_cfg, MSM8974_CNOC_SLV_CNOC_MNOC_MMSS_CFG, 8, -1, 58); +DEFINE_QNODE(slv_cnoc_mnoc_cfg, MSM8974_CNOC_SLV_CNOC_MNOC_CFG, 8, -1, 66); +DEFINE_QNODE(slv_pnoc_cfg, MSM8974_CNOC_SLV_PNOC_CFG, 8, -1, 69); +DEFINE_QNODE(slv_snoc_mpu_cfg, MSM8974_CNOC_SLV_SNOC_MPU_CFG, 8, -1, 67); +DEFINE_QNODE(slv_snoc_cfg, MSM8974_CNOC_SLV_SNOC_CFG, 8, -1, 70); +DEFINE_QNODE(slv_ebi1_dll_cfg, MSM8974_CNOC_SLV_EBI1_DLL_CFG, 8, -1, 71); +DEFINE_QNODE(slv_phy_apu_cfg, MSM8974_CNOC_SLV_PHY_APU_CFG, 8, -1, 72); +DEFINE_QNODE(slv_ebi1_phy_cfg, MSM8974_CNOC_SLV_EBI1_PHY_CFG, 8, -1, 73); +DEFINE_QNODE(slv_rpm, MSM8974_CNOC_SLV_RPM, 8, -1, 74); +DEFINE_QNODE(slv_service_cnoc, MSM8974_CNOC_SLV_SERVICE_CNOC, 8, -1, 76); + +static struct msm8974_icc_node *msm8974_cnoc_nodes[] = { + [CNOC_MAS_RPM_INST] = &mas_rpm_inst, + [CNOC_MAS_RPM_DATA] = &mas_rpm_data, + [CNOC_MAS_RPM_SYS] = &mas_rpm_sys, + [CNOC_MAS_DEHR] = &mas_dehr, + [CNOC_MAS_QDSS_DAP] = &mas_qdss_dap, + [CNOC_MAS_SPDM] = &mas_spdm, + [CNOC_MAS_TIC] = &mas_tic, + [CNOC_SLV_CLK_CTL] = &slv_clk_ctl, + [CNOC_SLV_CNOC_MSS] = &slv_cnoc_mss, + [CNOC_SLV_SECURITY] = &slv_security, + [CNOC_SLV_TCSR] = &slv_tcsr, + [CNOC_SLV_TLMM] = &slv_tlmm, + [CNOC_SLV_CRYPTO_0_CFG] = &slv_crypto_0_cfg, + [CNOC_SLV_CRYPTO_1_CFG] = &slv_crypto_1_cfg, + [CNOC_SLV_IMEM_CFG] = &slv_imem_cfg, + [CNOC_SLV_MESSAGE_RAM] = &slv_message_ram, + [CNOC_SLV_BIMC_CFG] = &slv_bimc_cfg, + [CNOC_SLV_BOOT_ROM] = &slv_boot_rom, + [CNOC_SLV_PMIC_ARB] = &slv_pmic_arb, + [CNOC_SLV_SPDM_WRAPPER] = &slv_spdm_wrapper, + [CNOC_SLV_DEHR_CFG] = &slv_dehr_cfg, + [CNOC_SLV_MPM] = &slv_mpm, + [CNOC_SLV_QDSS_CFG] = &slv_qdss_cfg, + [CNOC_SLV_RBCPR_CFG] = &slv_rbcpr_cfg, + [CNOC_SLV_RBCPR_QDSS_APU_CFG] = &slv_rbcpr_qdss_apu_cfg, + [CNOC_TO_SNOC] = &cnoc_to_snoc, + [CNOC_SLV_CNOC_ONOC_CFG] = &slv_cnoc_onoc_cfg, + [CNOC_SLV_CNOC_MNOC_MMSS_CFG] = &slv_cnoc_mnoc_mmss_cfg, + [CNOC_SLV_CNOC_MNOC_CFG] = &slv_cnoc_mnoc_cfg, + [CNOC_SLV_PNOC_CFG] = &slv_pnoc_cfg, + [CNOC_SLV_SNOC_MPU_CFG] = &slv_snoc_mpu_cfg, + [CNOC_SLV_SNOC_CFG] = &slv_snoc_cfg, + [CNOC_SLV_EBI1_DLL_CFG] = &slv_ebi1_dll_cfg, + [CNOC_SLV_PHY_APU_CFG] = &slv_phy_apu_cfg, + [CNOC_SLV_EBI1_PHY_CFG] = &slv_ebi1_phy_cfg, + [CNOC_SLV_RPM] = &slv_rpm, + [CNOC_SLV_SERVICE_CNOC] = &slv_service_cnoc, +}; + +static struct msm8974_icc_desc msm8974_cnoc = { + .nodes = msm8974_cnoc_nodes, + .num_nodes = ARRAY_SIZE(msm8974_cnoc_nodes), +}; + +DEFINE_QNODE(mas_graphics_3d, MSM8974_MNOC_MAS_GRAPHICS_3D, 16, 6, -1, MSM8974_MNOC_TO_BIMC); +DEFINE_QNODE(mas_jpeg, MSM8974_MNOC_MAS_JPEG, 16, 7, -1, MSM8974_MNOC_TO_BIMC); +DEFINE_QNODE(mas_mdp_port0, MSM8974_MNOC_MAS_MDP_PORT0, 16, 8, -1, MSM8974_MNOC_TO_BIMC); +DEFINE_QNODE(mas_video_p0, MSM8974_MNOC_MAS_VIDEO_P0, 16, 9, -1); +DEFINE_QNODE(mas_video_p1, MSM8974_MNOC_MAS_VIDEO_P1, 16, 10, -1); +DEFINE_QNODE(mas_vfe, MSM8974_MNOC_MAS_VFE, 16, 11, -1, MSM8974_MNOC_TO_BIMC); +DEFINE_QNODE(mnoc_to_cnoc, MSM8974_MNOC_TO_CNOC, 16, 4, -1); +DEFINE_QNODE(mnoc_to_bimc, MSM8974_MNOC_TO_BIMC, 16, -1, 16, MSM8974_BIMC_TO_MNOC); +DEFINE_QNODE(slv_camera_cfg, MSM8974_MNOC_SLV_CAMERA_CFG, 16, -1, 3); +DEFINE_QNODE(slv_display_cfg, MSM8974_MNOC_SLV_DISPLAY_CFG, 16, -1, 4); +DEFINE_QNODE(slv_ocmem_cfg, MSM8974_MNOC_SLV_OCMEM_CFG, 16, -1, 5); +DEFINE_QNODE(slv_cpr_cfg, MSM8974_MNOC_SLV_CPR_CFG, 16, -1, 6); +DEFINE_QNODE(slv_cpr_xpu_cfg, MSM8974_MNOC_SLV_CPR_XPU_CFG, 16, -1, 7); +DEFINE_QNODE(slv_misc_cfg, MSM8974_MNOC_SLV_MISC_CFG, 16, -1, 8); +DEFINE_QNODE(slv_misc_xpu_cfg, MSM8974_MNOC_SLV_MISC_XPU_CFG, 16, -1, 9); +DEFINE_QNODE(slv_venus_cfg, MSM8974_MNOC_SLV_VENUS_CFG, 16, -1, 10); +DEFINE_QNODE(slv_graphics_3d_cfg, MSM8974_MNOC_SLV_GRAPHICS_3D_CFG, 16, -1, 11); +DEFINE_QNODE(slv_mmss_clk_cfg, MSM8974_MNOC_SLV_MMSS_CLK_CFG, 16, -1, 12); +DEFINE_QNODE(slv_mmss_clk_xpu_cfg, MSM8974_MNOC_SLV_MMSS_CLK_XPU_CFG, 16, -1, 13); +DEFINE_QNODE(slv_mnoc_mpu_cfg, MSM8974_MNOC_SLV_MNOC_MPU_CFG, 16, -1, 14); +DEFINE_QNODE(slv_onoc_mpu_cfg, MSM8974_MNOC_SLV_ONOC_MPU_CFG, 16, -1, 15); +DEFINE_QNODE(slv_service_mnoc, MSM8974_MNOC_SLV_SERVICE_MNOC, 16, -1, 17); + +static struct msm8974_icc_node *msm8974_mnoc_nodes[] = { + [MNOC_MAS_GRAPHICS_3D] = &mas_graphics_3d, + [MNOC_MAS_JPEG] = &mas_jpeg, + [MNOC_MAS_MDP_PORT0] = &mas_mdp_port0, + [MNOC_MAS_VIDEO_P0] = &mas_video_p0, + [MNOC_MAS_VIDEO_P1] = &mas_video_p1, + [MNOC_MAS_VFE] = &mas_vfe, + [MNOC_TO_CNOC] = &mnoc_to_cnoc, + [MNOC_TO_BIMC] = &mnoc_to_bimc, + [MNOC_SLV_CAMERA_CFG] = &slv_camera_cfg, + [MNOC_SLV_DISPLAY_CFG] = &slv_display_cfg, + [MNOC_SLV_OCMEM_CFG] = &slv_ocmem_cfg, + [MNOC_SLV_CPR_CFG] = &slv_cpr_cfg, + [MNOC_SLV_CPR_XPU_CFG] = &slv_cpr_xpu_cfg, + [MNOC_SLV_MISC_CFG] = &slv_misc_cfg, + [MNOC_SLV_MISC_XPU_CFG] = &slv_misc_xpu_cfg, + [MNOC_SLV_VENUS_CFG] = &slv_venus_cfg, + [MNOC_SLV_GRAPHICS_3D_CFG] = &slv_graphics_3d_cfg, + [MNOC_SLV_MMSS_CLK_CFG] = &slv_mmss_clk_cfg, + [MNOC_SLV_MMSS_CLK_XPU_CFG] = &slv_mmss_clk_xpu_cfg, + [MNOC_SLV_MNOC_MPU_CFG] = &slv_mnoc_mpu_cfg, + [MNOC_SLV_ONOC_MPU_CFG] = &slv_onoc_mpu_cfg, + [MNOC_SLV_SERVICE_MNOC] = &slv_service_mnoc, +}; + +static struct msm8974_icc_desc msm8974_mnoc = { + .nodes = msm8974_mnoc_nodes, + .num_nodes = ARRAY_SIZE(msm8974_mnoc_nodes), +}; + +DEFINE_QNODE(ocmem_noc_to_ocmem_vnoc, MSM8974_OCMEM_NOC_TO_OCMEM_VNOC, 16, 54, 78, MSM8974_OCMEM_SLV_OCMEM); +DEFINE_QNODE(mas_jpeg_ocmem, MSM8974_OCMEM_MAS_JPEG_OCMEM, 16, 13, -1); +DEFINE_QNODE(mas_mdp_ocmem, MSM8974_OCMEM_MAS_MDP_OCMEM, 16, 14, -1); +DEFINE_QNODE(mas_video_p0_ocmem, MSM8974_OCMEM_MAS_VIDEO_P0_OCMEM, 16, 15, -1); +DEFINE_QNODE(mas_video_p1_ocmem, MSM8974_OCMEM_MAS_VIDEO_P1_OCMEM, 16, 16, -1); +DEFINE_QNODE(mas_vfe_ocmem, MSM8974_OCMEM_MAS_VFE_OCMEM, 16, 17, -1); +DEFINE_QNODE(mas_cnoc_onoc_cfg, MSM8974_OCMEM_MAS_CNOC_ONOC_CFG, 16, 12, -1); +DEFINE_QNODE(slv_service_onoc, MSM8974_OCMEM_SLV_SERVICE_ONOC, 16, -1, 19); +DEFINE_QNODE(slv_ocmem, MSM8974_OCMEM_SLV_OCMEM, 16, -1, 18); + +/* Virtual NoC is needed for connection to OCMEM */ +DEFINE_QNODE(ocmem_vnoc_to_onoc, MSM8974_OCMEM_VNOC_TO_OCMEM_NOC, 16, 56, 79, MSM8974_OCMEM_NOC_TO_OCMEM_VNOC); +DEFINE_QNODE(ocmem_vnoc_to_snoc, MSM8974_OCMEM_VNOC_TO_SNOC, 8, 57, 80); +DEFINE_QNODE(mas_v_ocmem_gfx3d, MSM8974_OCMEM_VNOC_MAS_GFX3D, 8, 55, -1, MSM8974_OCMEM_VNOC_TO_OCMEM_NOC); + +static struct msm8974_icc_node *msm8974_onoc_nodes[] = { + [OCMEM_NOC_TO_OCMEM_VNOC] = &ocmem_noc_to_ocmem_vnoc, + [OCMEM_MAS_JPEG_OCMEM] = &mas_jpeg_ocmem, + [OCMEM_MAS_MDP_OCMEM] = &mas_mdp_ocmem, + [OCMEM_MAS_VIDEO_P0_OCMEM] = &mas_video_p0_ocmem, + [OCMEM_MAS_VIDEO_P1_OCMEM] = &mas_video_p1_ocmem, + [OCMEM_MAS_VFE_OCMEM] = &mas_vfe_ocmem, + [OCMEM_MAS_CNOC_ONOC_CFG] = &mas_cnoc_onoc_cfg, + [OCMEM_SLV_SERVICE_ONOC] = &slv_service_onoc, + [OCMEM_VNOC_TO_SNOC] = &ocmem_vnoc_to_snoc, + [OCMEM_VNOC_TO_OCMEM_NOC] = &ocmem_vnoc_to_onoc, + [OCMEM_VNOC_MAS_GFX3D] = &mas_v_ocmem_gfx3d, + [OCMEM_SLV_OCMEM] = &slv_ocmem, +}; + +static struct msm8974_icc_desc msm8974_onoc = { + .nodes = msm8974_onoc_nodes, + .num_nodes = ARRAY_SIZE(msm8974_onoc_nodes), +}; + +DEFINE_QNODE(mas_pnoc_cfg, MSM8974_PNOC_MAS_PNOC_CFG, 8, 43, -1); +DEFINE_QNODE(mas_sdcc_1, MSM8974_PNOC_MAS_SDCC_1, 8, 33, -1, MSM8974_PNOC_TO_SNOC); +DEFINE_QNODE(mas_sdcc_3, MSM8974_PNOC_MAS_SDCC_3, 8, 34, -1, MSM8974_PNOC_TO_SNOC); +DEFINE_QNODE(mas_sdcc_4, MSM8974_PNOC_MAS_SDCC_4, 8, 36, -1, MSM8974_PNOC_TO_SNOC); +DEFINE_QNODE(mas_sdcc_2, MSM8974_PNOC_MAS_SDCC_2, 8, 35, -1, MSM8974_PNOC_TO_SNOC); +DEFINE_QNODE(mas_tsif, MSM8974_PNOC_MAS_TSIF, 8, 37, -1, MSM8974_PNOC_TO_SNOC); +DEFINE_QNODE(mas_bam_dma, MSM8974_PNOC_MAS_BAM_DMA, 8, 38, -1); +DEFINE_QNODE(mas_blsp_2, MSM8974_PNOC_MAS_BLSP_2, 8, 39, -1, MSM8974_PNOC_TO_SNOC); +DEFINE_QNODE(mas_usb_hsic, MSM8974_PNOC_MAS_USB_HSIC, 8, 40, -1, MSM8974_PNOC_TO_SNOC); +DEFINE_QNODE(mas_blsp_1, MSM8974_PNOC_MAS_BLSP_1, 8, 41, -1, MSM8974_PNOC_TO_SNOC); +DEFINE_QNODE(mas_usb_hs, MSM8974_PNOC_MAS_USB_HS, 8, 42, -1, MSM8974_PNOC_TO_SNOC); +DEFINE_QNODE(pnoc_to_snoc, MSM8974_PNOC_TO_SNOC, 8, 44, 45, MSM8974_SNOC_TO_PNOC, MSM8974_PNOC_SLV_PRNG); +DEFINE_QNODE(slv_sdcc_1, MSM8974_PNOC_SLV_SDCC_1, 8, -1, 31); +DEFINE_QNODE(slv_sdcc_3, MSM8974_PNOC_SLV_SDCC_3, 8, -1, 32); +DEFINE_QNODE(slv_sdcc_2, MSM8974_PNOC_SLV_SDCC_2, 8, -1, 33); +DEFINE_QNODE(slv_sdcc_4, MSM8974_PNOC_SLV_SDCC_4, 8, -1, 34); +DEFINE_QNODE(slv_tsif, MSM8974_PNOC_SLV_TSIF, 8, -1, 35); +DEFINE_QNODE(slv_bam_dma, MSM8974_PNOC_SLV_BAM_DMA, 8, -1, 36); +DEFINE_QNODE(slv_blsp_2, MSM8974_PNOC_SLV_BLSP_2, 8, -1, 37); +DEFINE_QNODE(slv_usb_hsic, MSM8974_PNOC_SLV_USB_HSIC, 8, -1, 38); +DEFINE_QNODE(slv_blsp_1, MSM8974_PNOC_SLV_BLSP_1, 8, -1, 39); +DEFINE_QNODE(slv_usb_hs, MSM8974_PNOC_SLV_USB_HS, 8, -1, 40); +DEFINE_QNODE(slv_pdm, MSM8974_PNOC_SLV_PDM, 8, -1, 41); +DEFINE_QNODE(slv_periph_apu_cfg, MSM8974_PNOC_SLV_PERIPH_APU_CFG, 8, -1, 42); +DEFINE_QNODE(slv_pnoc_mpu_cfg, MSM8974_PNOC_SLV_PNOC_MPU_CFG, 8, -1, 43); +DEFINE_QNODE(slv_prng, MSM8974_PNOC_SLV_PRNG, 8, -1, 44, MSM8974_PNOC_TO_SNOC); +DEFINE_QNODE(slv_service_pnoc, MSM8974_PNOC_SLV_SERVICE_PNOC, 8, -1, 46); + +static struct msm8974_icc_node *msm8974_pnoc_nodes[] = { + [PNOC_MAS_PNOC_CFG] = &mas_pnoc_cfg, + [PNOC_MAS_SDCC_1] = &mas_sdcc_1, + [PNOC_MAS_SDCC_3] = &mas_sdcc_3, + [PNOC_MAS_SDCC_4] = &mas_sdcc_4, + [PNOC_MAS_SDCC_2] = &mas_sdcc_2, + [PNOC_MAS_TSIF] = &mas_tsif, + [PNOC_MAS_BAM_DMA] = &mas_bam_dma, + [PNOC_MAS_BLSP_2] = &mas_blsp_2, + [PNOC_MAS_USB_HSIC] = &mas_usb_hsic, + [PNOC_MAS_BLSP_1] = &mas_blsp_1, + [PNOC_MAS_USB_HS] = &mas_usb_hs, + [PNOC_TO_SNOC] = &pnoc_to_snoc, + [PNOC_SLV_SDCC_1] = &slv_sdcc_1, + [PNOC_SLV_SDCC_3] = &slv_sdcc_3, + [PNOC_SLV_SDCC_2] = &slv_sdcc_2, + [PNOC_SLV_SDCC_4] = &slv_sdcc_4, + [PNOC_SLV_TSIF] = &slv_tsif, + [PNOC_SLV_BAM_DMA] = &slv_bam_dma, + [PNOC_SLV_BLSP_2] = &slv_blsp_2, + [PNOC_SLV_USB_HSIC] = &slv_usb_hsic, + [PNOC_SLV_BLSP_1] = &slv_blsp_1, + [PNOC_SLV_USB_HS] = &slv_usb_hs, + [PNOC_SLV_PDM] = &slv_pdm, + [PNOC_SLV_PERIPH_APU_CFG] = &slv_periph_apu_cfg, + [PNOC_SLV_PNOC_MPU_CFG] = &slv_pnoc_mpu_cfg, + [PNOC_SLV_PRNG] = &slv_prng, + [PNOC_SLV_SERVICE_PNOC] = &slv_service_pnoc, +}; + +static struct msm8974_icc_desc msm8974_pnoc = { + .nodes = msm8974_pnoc_nodes, + .num_nodes = ARRAY_SIZE(msm8974_pnoc_nodes), +}; + +DEFINE_QNODE(mas_lpass_ahb, MSM8974_SNOC_MAS_LPASS_AHB, 8, 18, -1); +DEFINE_QNODE(mas_qdss_bam, MSM8974_SNOC_MAS_QDSS_BAM, 8, 19, -1); +DEFINE_QNODE(mas_snoc_cfg, MSM8974_SNOC_MAS_SNOC_CFG, 8, 20, -1); +DEFINE_QNODE(snoc_to_bimc, MSM8974_SNOC_TO_BIMC, 8, 21, 24, MSM8974_BIMC_TO_SNOC); +DEFINE_QNODE(snoc_to_cnoc, MSM8974_SNOC_TO_CNOC, 8, 22, 25); +DEFINE_QNODE(snoc_to_pnoc, MSM8974_SNOC_TO_PNOC, 8, 29, 28, MSM8974_PNOC_TO_SNOC); +DEFINE_QNODE(snoc_to_ocmem_vnoc, MSM8974_SNOC_TO_OCMEM_VNOC, 8, 53, 77, MSM8974_OCMEM_VNOC_TO_OCMEM_NOC); +DEFINE_QNODE(mas_crypto_core0, MSM8974_SNOC_MAS_CRYPTO_CORE0, 8, 23, -1, MSM8974_SNOC_TO_BIMC); +DEFINE_QNODE(mas_crypto_core1, MSM8974_SNOC_MAS_CRYPTO_CORE1, 8, 24, -1); +DEFINE_QNODE(mas_lpass_proc, MSM8974_SNOC_MAS_LPASS_PROC, 8, 25, -1, MSM8974_SNOC_TO_OCMEM_VNOC); +DEFINE_QNODE(mas_mss, MSM8974_SNOC_MAS_MSS, 8, 26, -1); +DEFINE_QNODE(mas_mss_nav, MSM8974_SNOC_MAS_MSS_NAV, 8, 27, -1); +DEFINE_QNODE(mas_ocmem_dma, MSM8974_SNOC_MAS_OCMEM_DMA, 8, 28, -1); +DEFINE_QNODE(mas_wcss, MSM8974_SNOC_MAS_WCSS, 8, 30, -1); +DEFINE_QNODE(mas_qdss_etr, MSM8974_SNOC_MAS_QDSS_ETR, 8, 31, -1); +DEFINE_QNODE(mas_usb3, MSM8974_SNOC_MAS_USB3, 8, 32, -1, MSM8974_SNOC_TO_BIMC); +DEFINE_QNODE(slv_ampss, MSM8974_SNOC_SLV_AMPSS, 8, -1, 20); +DEFINE_QNODE(slv_lpass, MSM8974_SNOC_SLV_LPASS, 8, -1, 21); +DEFINE_QNODE(slv_usb3, MSM8974_SNOC_SLV_USB3, 8, -1, 22); +DEFINE_QNODE(slv_wcss, MSM8974_SNOC_SLV_WCSS, 8, -1, 23); +DEFINE_QNODE(slv_ocimem, MSM8974_SNOC_SLV_OCIMEM, 8, -1, 26); +DEFINE_QNODE(slv_snoc_ocmem, MSM8974_SNOC_SLV_SNOC_OCMEM, 8, -1, 27); +DEFINE_QNODE(slv_service_snoc, MSM8974_SNOC_SLV_SERVICE_SNOC, 8, -1, 29); +DEFINE_QNODE(slv_qdss_stm, MSM8974_SNOC_SLV_QDSS_STM, 8, -1, 30); + +static struct msm8974_icc_node *msm8974_snoc_nodes[] = { + [SNOC_MAS_LPASS_AHB] = &mas_lpass_ahb, + [SNOC_MAS_QDSS_BAM] = &mas_qdss_bam, + [SNOC_MAS_SNOC_CFG] = &mas_snoc_cfg, + [SNOC_TO_BIMC] = &snoc_to_bimc, + [SNOC_TO_CNOC] = &snoc_to_cnoc, + [SNOC_TO_PNOC] = &snoc_to_pnoc, + [SNOC_TO_OCMEM_VNOC] = &snoc_to_ocmem_vnoc, + [SNOC_MAS_CRYPTO_CORE0] = &mas_crypto_core0, + [SNOC_MAS_CRYPTO_CORE1] = &mas_crypto_core1, + [SNOC_MAS_LPASS_PROC] = &mas_lpass_proc, + [SNOC_MAS_MSS] = &mas_mss, + [SNOC_MAS_MSS_NAV] = &mas_mss_nav, + [SNOC_MAS_OCMEM_DMA] = &mas_ocmem_dma, + [SNOC_MAS_WCSS] = &mas_wcss, + [SNOC_MAS_QDSS_ETR] = &mas_qdss_etr, + [SNOC_MAS_USB3] = &mas_usb3, + [SNOC_SLV_AMPSS] = &slv_ampss, + [SNOC_SLV_LPASS] = &slv_lpass, + [SNOC_SLV_USB3] = &slv_usb3, + [SNOC_SLV_WCSS] = &slv_wcss, + [SNOC_SLV_OCIMEM] = &slv_ocimem, + [SNOC_SLV_SNOC_OCMEM] = &slv_snoc_ocmem, + [SNOC_SLV_SERVICE_SNOC] = &slv_service_snoc, + [SNOC_SLV_QDSS_STM] = &slv_qdss_stm, +}; + +static struct msm8974_icc_desc msm8974_snoc = { + .nodes = msm8974_snoc_nodes, + .num_nodes = ARRAY_SIZE(msm8974_snoc_nodes), +}; + +static int msm8974_icc_aggregate(struct icc_node *node, u32 tag, u32 avg_bw, + u32 peak_bw, u32 *agg_avg, u32 *agg_peak) +{ + *agg_avg += avg_bw; + *agg_peak = max(*agg_peak, peak_bw); + + return 0; +} + +static void msm8974_icc_rpm_smd_send(struct device *dev, int rsc_type, + char *name, int id, u64 val) +{ + int ret; + + if (id == -1) + return; + + /* + * Setting the bandwidth requests for some nodes fails and this same + * behavior occurs on the downstream MSM 3.4 kernel sources based on + * errors like this in that kernel: + * + * msm_rpm_get_error_from_ack(): RPM NACK Unsupported resource + * AXI: msm_bus_rpm_req(): RPM: Ack failed + * AXI: msm_bus_rpm_commit_arb(): RPM: Req fail: mas:32, bw:240000000 + * + * Since there's no publicly available documentation for this hardware, + * and the bandwidth for some nodes in the path can be set properly, + * let's not return an error. + */ + ret = qcom_icc_rpm_smd_send(QCOM_SMD_RPM_ACTIVE_STATE, rsc_type, id, + val); + if (ret) + dev_dbg(dev, "Cannot set bandwidth for node %s (%d): %d\n", + name, id, ret); +} + +static int msm8974_icc_set(struct icc_node *src, struct icc_node *dst) +{ + struct msm8974_icc_node *src_qn, *dst_qn; + struct msm8974_icc_provider *qp; + u64 sum_bw, max_peak_bw, rate; + u32 agg_avg = 0, agg_peak = 0; + struct icc_provider *provider; + struct icc_node *n; + int ret, i; + + src_qn = src->data; + dst_qn = dst->data; + provider = src->provider; + qp = to_msm8974_icc_provider(provider); + + list_for_each_entry(n, &provider->nodes, node_list) + msm8974_icc_aggregate(n, 0, n->avg_bw, n->peak_bw, + &agg_avg, &agg_peak); + + sum_bw = icc_units_to_bps(agg_avg); + max_peak_bw = icc_units_to_bps(agg_peak); + + /* Set bandwidth on source node */ + msm8974_icc_rpm_smd_send(provider->dev, RPM_BUS_MASTER_REQ, + src_qn->name, src_qn->mas_rpm_id, sum_bw); + + msm8974_icc_rpm_smd_send(provider->dev, RPM_BUS_SLAVE_REQ, + src_qn->name, src_qn->slv_rpm_id, sum_bw); + + /* Set bandwidth on destination node */ + msm8974_icc_rpm_smd_send(provider->dev, RPM_BUS_MASTER_REQ, + dst_qn->name, dst_qn->mas_rpm_id, sum_bw); + + msm8974_icc_rpm_smd_send(provider->dev, RPM_BUS_SLAVE_REQ, + dst_qn->name, dst_qn->slv_rpm_id, sum_bw); + + rate = max(sum_bw, max_peak_bw); + + do_div(rate, src_qn->buswidth); + + if (src_qn->rate == rate) + return 0; + + for (i = 0; i < qp->num_clks; i++) { + ret = clk_set_rate(qp->bus_clks[i].clk, rate); + if (ret) { + dev_err(provider->dev, "%s clk_set_rate error: %d\n", + qp->bus_clks[i].id, ret); + ret = 0; + } + } + + src_qn->rate = rate; + + return 0; +} + +static int msm8974_icc_probe(struct platform_device *pdev) +{ + const struct msm8974_icc_desc *desc; + struct msm8974_icc_node **qnodes; + struct msm8974_icc_provider *qp; + struct device *dev = &pdev->dev; + struct icc_onecell_data *data; + struct icc_provider *provider; + struct icc_node *node; + size_t num_nodes, i; + int ret; + + /* wait for the RPM proxy */ + if (!qcom_icc_rpm_smd_available()) + return -EPROBE_DEFER; + + desc = of_device_get_match_data(dev); + if (!desc) + return -EINVAL; + + qnodes = desc->nodes; + num_nodes = desc->num_nodes; + + qp = devm_kzalloc(dev, sizeof(*qp), GFP_KERNEL); + if (!qp) + return -ENOMEM; + + data = devm_kzalloc(dev, struct_size(data, nodes, num_nodes), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + qp->bus_clks = devm_kmemdup(dev, msm8974_icc_bus_clocks, + sizeof(msm8974_icc_bus_clocks), GFP_KERNEL); + if (!qp->bus_clks) + return -ENOMEM; + + qp->num_clks = ARRAY_SIZE(msm8974_icc_bus_clocks); + ret = devm_clk_bulk_get(dev, qp->num_clks, qp->bus_clks); + if (ret) + return ret; + + ret = clk_bulk_prepare_enable(qp->num_clks, qp->bus_clks); + if (ret) + return ret; + + provider = &qp->provider; + INIT_LIST_HEAD(&provider->nodes); + provider->dev = dev; + provider->set = msm8974_icc_set; + provider->aggregate = msm8974_icc_aggregate; + provider->xlate = of_icc_xlate_onecell; + provider->data = data; + + ret = icc_provider_add(provider); + if (ret) { + dev_err(dev, "error adding interconnect provider: %d\n", ret); + goto err_disable_clks; + } + + for (i = 0; i < num_nodes; i++) { + size_t j; + + node = icc_node_create(qnodes[i]->id); + if (IS_ERR(node)) { + ret = PTR_ERR(node); + goto err_del_icc; + } + + node->name = qnodes[i]->name; + node->data = qnodes[i]; + icc_node_add(node, provider); + + dev_dbg(dev, "registered node %s\n", node->name); + + /* populate links */ + for (j = 0; j < qnodes[i]->num_links; j++) + icc_link_create(node, qnodes[i]->links[j]); + + data->nodes[i] = node; + } + data->num_nodes = num_nodes; + + platform_set_drvdata(pdev, qp); + + return 0; + +err_del_icc: + list_for_each_entry(node, &provider->nodes, node_list) { + icc_node_del(node); + icc_node_destroy(node->id); + } + icc_provider_del(provider); + +err_disable_clks: + clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks); + + return ret; +} + +static int msm8974_icc_remove(struct platform_device *pdev) +{ + struct msm8974_icc_provider *qp = platform_get_drvdata(pdev); + struct icc_provider *provider = &qp->provider; + struct icc_node *n; + + list_for_each_entry(n, &provider->nodes, node_list) { + icc_node_del(n); + icc_node_destroy(n->id); + } + clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks); + + return icc_provider_del(provider); +} + +static const struct of_device_id msm8974_noc_of_match[] = { + { .compatible = "qcom,msm8974-bimc", .data = &msm8974_bimc}, + { .compatible = "qcom,msm8974-cnoc", .data = &msm8974_cnoc}, + { .compatible = "qcom,msm8974-mmssnoc", .data = &msm8974_mnoc}, + { .compatible = "qcom,msm8974-ocmemnoc", .data = &msm8974_onoc}, + { .compatible = "qcom,msm8974-pnoc", .data = &msm8974_pnoc}, + { .compatible = "qcom,msm8974-snoc", .data = &msm8974_snoc}, + { }, +}; +MODULE_DEVICE_TABLE(of, msm8974_noc_of_match); + +static struct platform_driver msm8974_noc_driver = { + .probe = msm8974_icc_probe, + .remove = msm8974_icc_remove, + .driver = { + .name = "qnoc-msm8974", + .of_match_table = msm8974_noc_of_match, + }, +}; +module_platform_driver(msm8974_noc_driver); +MODULE_DESCRIPTION("Qualcomm MSM8974 NoC driver"); +MODULE_AUTHOR("Brian Masney "); +MODULE_LICENSE("GPL v2"); From c134f914e9f55b7817e2bae625ec0e5f1379f7cd Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 22 Oct 2019 18:29:48 -0500 Subject: [PATCH 136/198] soundwire: intel: fix PDI/stream mapping for Bulk The previous formula is incorrect for PDI0/1, the mapping is not linear but has a discontinuity between PDI1 and PDI2. This change has no effect on PCM PDIs (same mapping). Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20191022232948.17156-1-pierre-louis.bossart@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/intel.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 3a6bbf0ff04e..96f75bc32386 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -480,7 +480,10 @@ intel_pdi_shim_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi) unsigned int link_id = sdw->instance; int pdi_conf = 0; - pdi->intel_alh_id = (link_id * 16) + pdi->num + 5; + /* the Bulk and PCM streams are not contiguous */ + pdi->intel_alh_id = (link_id * 16) + pdi->num + 3; + if (pdi->num >= 2) + pdi->intel_alh_id += 2; /* * Program stream parameters to stream SHIM register @@ -509,7 +512,10 @@ intel_pdi_alh_configure(struct sdw_intel *sdw, struct sdw_cdns_pdi *pdi) unsigned int link_id = sdw->instance; unsigned int conf; - pdi->intel_alh_id = (link_id * 16) + pdi->num + 5; + /* the Bulk and PCM streams are not contiguous */ + pdi->intel_alh_id = (link_id * 16) + pdi->num + 3; + if (pdi->num >= 2) + pdi->intel_alh_id += 2; /* Program Stream config ALH register */ conf = intel_readl(alh, SDW_ALH_STRMZCFG(pdi->intel_alh_id)); From 5bd54539788b3b3a415e84204cc89f918658d56d Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 22 Oct 2019 18:48:06 -0500 Subject: [PATCH 137/198] soundwire: remove bitfield for unique_id, use u8 There is no good reason why the unique_id needs to be stored as 4 bits. The code will work without changes with a u8 since all values are already filtered while parsing the ACPI tables and Slave devID registers. Use u8 representation. This will allow us to encode a "IGNORE_UNIQUE_ID" value to account for firmware/BIOS creativity. Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20191022234808.17432-2-pierre-louis.bossart@linux.intel.com Signed-off-by: Vinod Koul --- include/linux/soundwire/sdw.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 688b40e65c89..28745b9ba279 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -403,6 +403,8 @@ int sdw_slave_read_prop(struct sdw_slave *slave); * SDW Slave Structures and APIs */ +#define SDW_IGNORED_UNIQUE_ID 0xFF + /** * struct sdw_slave_id - Slave ID * @mfg_id: MIPI Manufacturer ID @@ -418,7 +420,7 @@ struct sdw_slave_id { __u16 mfg_id; __u16 part_id; __u8 class_id; - __u8 unique_id:4; + __u8 unique_id; __u8 sdw_version:4; }; From de5b174b3bc8b8fa4921a7d6bd4b2e646120640d Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 22 Oct 2019 18:48:07 -0500 Subject: [PATCH 138/198] soundwire: slave: add helper to extract slave ID Simplify the loop with a helper. The only functionality change is that we continue the loop even with an ACPI error. Follow-up patches will build on this change. Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20191022234808.17432-3-pierre-louis.bossart@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/slave.c | 50 ++++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/drivers/soundwire/slave.c b/drivers/soundwire/slave.c index 48a63ca130d2..e4e4505b5111 100644 --- a/drivers/soundwire/slave.c +++ b/drivers/soundwire/slave.c @@ -64,6 +64,36 @@ static int sdw_slave_add(struct sdw_bus *bus, } #if IS_ENABLED(CONFIG_ACPI) + +static bool find_slave(struct sdw_bus *bus, + struct acpi_device *adev, + struct sdw_slave_id *id) +{ + unsigned long long addr; + unsigned int link_id; + acpi_status status; + + status = acpi_evaluate_integer(adev->handle, + METHOD_NAME__ADR, NULL, &addr); + + if (ACPI_FAILURE(status)) { + dev_err(bus->dev, "_ADR resolution failed: %x\n", + status); + return false; + } + + /* Extract link id from ADR, Bit 51 to 48 (included) */ + link_id = (addr >> 48) & GENMASK(3, 0); + + /* Check for link_id match */ + if (link_id != bus->link_id) + return false; + + sdw_extract_slave_id(bus, addr, id); + + return true; +} + /* * sdw_acpi_find_slaves() - Find Slave devices in Master ACPI node * @bus: SDW bus instance @@ -81,29 +111,11 @@ int sdw_acpi_find_slaves(struct sdw_bus *bus) } list_for_each_entry(adev, &parent->children, node) { - unsigned long long addr; struct sdw_slave_id id; - unsigned int link_id; - acpi_status status; - status = acpi_evaluate_integer(adev->handle, - METHOD_NAME__ADR, NULL, &addr); - - if (ACPI_FAILURE(status)) { - dev_err(bus->dev, "_ADR resolution failed: %x\n", - status); - return status; - } - - /* Extract link id from ADR, Bit 51 to 48 (included) */ - link_id = (addr >> 48) & GENMASK(3, 0); - - /* Check for link_id match */ - if (link_id != bus->link_id) + if (!find_slave(bus, adev, &id)) continue; - sdw_extract_slave_id(bus, addr, &id); - /* * don't error check for sdw_slave_add as we want to continue * adding Slaves From 2e8c4ad1f04413a4a67ef10746a7566007d2ed55 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 22 Oct 2019 18:48:08 -0500 Subject: [PATCH 139/198] soundwire: ignore uniqueID when irrelevant The uniqueID is useful when there are two or more devices of the same type (identical manufacturer ID, part ID) on the same link. When there is a single device of a given type on a link, its uniqueID is irrelevant. It's not uncommon on actual platforms to see variations of the uniqueID, or differences between devID registers and ACPI _ADR fields. This patch suggests a filter on startup to identify 'single' devices and tag them accordingly. The uniqueID is then not used for the probe, and the device name omits the uniqueID as well. Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20191022234808.17432-4-pierre-louis.bossart@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/bus.c | 7 +++--- drivers/soundwire/slave.c | 52 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index fc53dbe57f85..be5d437058ed 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -422,10 +422,11 @@ static struct sdw_slave *sdw_get_slave(struct sdw_bus *bus, int i) static int sdw_compare_devid(struct sdw_slave *slave, struct sdw_slave_id id) { - if (slave->id.unique_id != id.unique_id || - slave->id.mfg_id != id.mfg_id || + if (slave->id.mfg_id != id.mfg_id || slave->id.part_id != id.part_id || - slave->id.class_id != id.class_id) + slave->id.class_id != id.class_id || + (slave->id.unique_id != SDW_IGNORED_UNIQUE_ID && + slave->id.unique_id != id.unique_id)) return -ENODEV; return 0; diff --git a/drivers/soundwire/slave.c b/drivers/soundwire/slave.c index e4e4505b5111..e656ff57fc04 100644 --- a/drivers/soundwire/slave.c +++ b/drivers/soundwire/slave.c @@ -29,10 +29,17 @@ static int sdw_slave_add(struct sdw_bus *bus, slave->dev.parent = bus->dev; slave->dev.fwnode = fwnode; - /* name shall be sdw:link:mfg:part:class:unique */ - dev_set_name(&slave->dev, "sdw:%x:%x:%x:%x:%x", - bus->link_id, id->mfg_id, id->part_id, - id->class_id, id->unique_id); + if (id->unique_id == SDW_IGNORED_UNIQUE_ID) { + /* name shall be sdw:link:mfg:part:class */ + dev_set_name(&slave->dev, "sdw:%x:%x:%x:%x", + bus->link_id, id->mfg_id, id->part_id, + id->class_id); + } else { + /* name shall be sdw:link:mfg:part:class:unique */ + dev_set_name(&slave->dev, "sdw:%x:%x:%x:%x:%x", + bus->link_id, id->mfg_id, id->part_id, + id->class_id, id->unique_id); + } slave->dev.release = sdw_slave_release; slave->dev.bus = &sdw_bus_type; @@ -103,6 +110,7 @@ static bool find_slave(struct sdw_bus *bus, int sdw_acpi_find_slaves(struct sdw_bus *bus) { struct acpi_device *adev, *parent; + struct acpi_device *adev2, *parent2; parent = ACPI_COMPANION(bus->dev); if (!parent) { @@ -112,10 +120,46 @@ int sdw_acpi_find_slaves(struct sdw_bus *bus) list_for_each_entry(adev, &parent->children, node) { struct sdw_slave_id id; + struct sdw_slave_id id2; + bool ignore_unique_id = true; if (!find_slave(bus, adev, &id)) continue; + /* brute-force O(N^2) search for duplicates */ + parent2 = parent; + list_for_each_entry(adev2, &parent2->children, node) { + + if (adev == adev2) + continue; + + if (!find_slave(bus, adev2, &id2)) + continue; + + if (id.sdw_version != id2.sdw_version || + id.mfg_id != id2.mfg_id || + id.part_id != id2.part_id || + id.class_id != id2.class_id) + continue; + + if (id.unique_id != id2.unique_id) { + dev_dbg(bus->dev, + "Valid unique IDs %x %x for Slave mfg %x part %d\n", + id.unique_id, id2.unique_id, + id.mfg_id, id.part_id); + ignore_unique_id = false; + } else { + dev_err(bus->dev, + "Invalid unique IDs %x %x for Slave mfg %x part %d\n", + id.unique_id, id2.unique_id, + id.mfg_id, id.part_id); + return -ENODEV; + } + } + + if (ignore_unique_id) + id.unique_id = SDW_IGNORED_UNIQUE_ID; + /* * don't error check for sdw_slave_add as we want to continue * adding Slaves From 998174042da229e2cf5841f574aba4a743e69650 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 8 Nov 2019 21:34:30 +0100 Subject: [PATCH 140/198] ppdev: fix PPGETTIME/PPSETTIME ioctls Going through the uses of timeval in the user space API, I noticed two bugs in ppdev that were introduced in the y2038 conversion: * The range check was accidentally moved from ppsettime to ppgettime * On sparc64, the microseconds are in the other half of the 64-bit word. Fix both, and mark the fix for stable backports. Cc: stable@vger.kernel.org Fixes: 3b9ab374a1e6 ("ppdev: convert to y2038 safe") Signed-off-by: Arnd Bergmann Link: https://lore.kernel.org/r/20191108203435.112759-8-arnd@arndb.de Signed-off-by: Greg Kroah-Hartman --- drivers/char/ppdev.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c index c86f18aa8985..34bb88fe0b0a 100644 --- a/drivers/char/ppdev.c +++ b/drivers/char/ppdev.c @@ -619,20 +619,27 @@ static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (copy_from_user(time32, argp, sizeof(time32))) return -EFAULT; + if ((time32[0] < 0) || (time32[1] < 0)) + return -EINVAL; + return pp_set_timeout(pp->pdev, time32[0], time32[1]); case PPSETTIME64: if (copy_from_user(time64, argp, sizeof(time64))) return -EFAULT; + if ((time64[0] < 0) || (time64[1] < 0)) + return -EINVAL; + + if (IS_ENABLED(CONFIG_SPARC64) && !in_compat_syscall()) + time64[1] >>= 32; + return pp_set_timeout(pp->pdev, time64[0], time64[1]); case PPGETTIME32: jiffies_to_timespec64(pp->pdev->timeout, &ts); time32[0] = ts.tv_sec; time32[1] = ts.tv_nsec / NSEC_PER_USEC; - if ((time32[0] < 0) || (time32[1] < 0)) - return -EINVAL; if (copy_to_user(argp, time32, sizeof(time32))) return -EFAULT; @@ -643,8 +650,9 @@ static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg) jiffies_to_timespec64(pp->pdev->timeout, &ts); time64[0] = ts.tv_sec; time64[1] = ts.tv_nsec / NSEC_PER_USEC; - if ((time64[0] < 0) || (time64[1] < 0)) - return -EINVAL; + + if (IS_ENABLED(CONFIG_SPARC64) && !in_compat_syscall()) + time64[1] <<= 32; if (copy_to_user(argp, time64, sizeof(time64))) return -EFAULT; From 45a2d64696b11913bcf1087b041740edbade3e21 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 8 Nov 2019 21:34:29 +0100 Subject: [PATCH 141/198] lp: fix sparc64 LPSETTIMEOUT ioctl The layout of struct timeval is different on sparc64 from anything else, and the patch I did long ago failed to take this into account. Change it now to handle sparc64 user space correctly again. Quite likely nobody cares about parallel ports on sparc64, but there is no reason not to fix it. Cc: stable@vger.kernel.org Fixes: 9a450484089d ("lp: support 64-bit time_t user space") Signed-off-by: Arnd Bergmann Link: https://lore.kernel.org/r/20191108203435.112759-7-arnd@arndb.de Signed-off-by: Greg Kroah-Hartman --- drivers/char/lp.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/char/lp.c b/drivers/char/lp.c index 7c9269e3477a..bd95aba1f9fe 100644 --- a/drivers/char/lp.c +++ b/drivers/char/lp.c @@ -713,6 +713,10 @@ static int lp_set_timeout64(unsigned int minor, void __user *arg) if (copy_from_user(karg, arg, sizeof(karg))) return -EFAULT; + /* sparc64 suseconds_t is 32-bit only */ + if (IS_ENABLED(CONFIG_SPARC64) && !in_compat_syscall()) + karg[1] >>= 32; + return lp_set_timeout(minor, karg[0], karg[1]); } From c059d579961d62c1a675cb89ef799902e6bce815 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Wed, 16 Oct 2019 15:45:37 +0100 Subject: [PATCH 142/198] parport: daisy: avoid hardcoded name The daisy device name is hardcoded, define it in the header file and use it in the code. Signed-off-by: Sudip Mukherjee Link: https://lore.kernel.org/r/20191016144540.18810-1-sudipm.mukherjee@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/parport/probe.c | 2 +- include/linux/parport.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/parport/probe.c b/drivers/parport/probe.c index e035174ba205..e5e6a463a941 100644 --- a/drivers/parport/probe.c +++ b/drivers/parport/probe.c @@ -257,7 +257,7 @@ static ssize_t parport_read_device_id (struct parport *port, char *buffer, ssize_t parport_device_id (int devnum, char *buffer, size_t count) { ssize_t retval = -ENXIO; - struct pardevice *dev = parport_open (devnum, "Device ID probe"); + struct pardevice *dev = parport_open(devnum, daisy_dev_name); if (!dev) return -ENXIO; diff --git a/include/linux/parport.h b/include/linux/parport.h index 397607a0c0eb..13932ce8b37b 100644 --- a/include/linux/parport.h +++ b/include/linux/parport.h @@ -460,6 +460,7 @@ extern size_t parport_ieee1284_epp_read_addr (struct parport *, void *, size_t, int); /* IEEE1284.3 functions */ +#define daisy_dev_name "Device ID probe" extern int parport_daisy_init (struct parport *port); extern void parport_daisy_fini (struct parport *port); extern struct pardevice *parport_open (int devnum, const char *name); From e962cd9cf9cfb1f5db6bbad6a6470e5ccf56e290 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Wed, 16 Oct 2019 15:45:38 +0100 Subject: [PATCH 143/198] parport: do not check portlist when using device-model We do not need to maintain a list of ports when we are using the device-model. The base layer is going to maintain the list for us and we can get the list of ports just using bus_for_each_dev(). Signed-off-by: Sudip Mukherjee Link: https://lore.kernel.org/r/20191016144540.18810-2-sudipm.mukherjee@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/parport/share.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/parport/share.c b/drivers/parport/share.c index 7b4ee33c1935..96538b7975e5 100644 --- a/drivers/parport/share.c +++ b/drivers/parport/share.c @@ -266,9 +266,6 @@ static int port_check(struct device *dev, void *dev_drv) int __parport_register_driver(struct parport_driver *drv, struct module *owner, const char *mod_name) { - if (list_empty(&portlist)) - get_lowlevel_driver(); - if (drv->devmodel) { /* using device model */ int ret; @@ -292,6 +289,8 @@ int __parport_register_driver(struct parport_driver *drv, struct module *owner, drv->devmodel = false; + if (list_empty(&portlist)) + get_lowlevel_driver(); mutex_lock(®istration_lock); list_for_each_entry(port, &portlist, list) drv->attach(port); From 231ec2f24dad18d021b361045bbd618ba62a274e Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Wed, 16 Oct 2019 15:45:39 +0100 Subject: [PATCH 144/198] parport: load lowlevel driver if ports not found Usually all the distro will load the parport low level driver as part of their initialization. But we can get into a situation where all the parallel port drivers are built as module and we unload all the modules at a later time. Then if we just do "modprobe parport" it will only load the parport module and will not load the low level driver which will actually register the ports. So, check the bus if there is any parport registered, if not, load the low level driver. We can get into the above situation with all distro but only Suse has setup the alias for "parport_lowlevel" and so it only works in Suse. Users of Debian based distro will need to load the lowlevel module manually. Signed-off-by: Sudip Mukherjee Link: https://lore.kernel.org/r/20191016144540.18810-3-sudipm.mukherjee@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/parport/share.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/drivers/parport/share.c b/drivers/parport/share.c index 96538b7975e5..d6920ebeabcd 100644 --- a/drivers/parport/share.c +++ b/drivers/parport/share.c @@ -230,6 +230,18 @@ static int port_check(struct device *dev, void *dev_drv) return 0; } +/* + * Iterates through all the devices connected to the bus and return 1 + * if the device is a parallel port. + */ + +static int port_detect(struct device *dev, void *dev_drv) +{ + if (is_parport(dev)) + return 1; + return 0; +} + /** * parport_register_driver - register a parallel port device driver * @drv: structure describing the driver @@ -279,6 +291,15 @@ int __parport_register_driver(struct parport_driver *drv, struct module *owner, if (ret) return ret; + /* + * check if bus has any parallel port registered, if + * none is found then load the lowlevel driver. + */ + ret = bus_for_each_dev(&parport_bus_type, NULL, NULL, + port_detect); + if (!ret) + get_lowlevel_driver(); + mutex_lock(®istration_lock); if (drv->match_port) bus_for_each_dev(&parport_bus_type, NULL, drv, From 60f8a59ddcdc7fb7c17180ba10d9c49bc91156c7 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Wed, 16 Oct 2019 15:45:40 +0100 Subject: [PATCH 145/198] parport: daisy: use new parport device model Modify parport daisy driver to use the new parallel port device model. Last attempt was '1aec4211204d ("parport: daisy: use new parport device model")' which failed as daisy was also trying to load the low level driver and that resulted in a deadlock. Cc: Michal Kubecek Signed-off-by: Sudip Mukherjee Tested-by: Steven Rostedt (VMware) Link: https://lore.kernel.org/r/20191016144540.18810-4-sudipm.mukherjee@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/parport/daisy.c | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/drivers/parport/daisy.c b/drivers/parport/daisy.c index 5484a46dafda..3b00e2c8e2e9 100644 --- a/drivers/parport/daisy.c +++ b/drivers/parport/daisy.c @@ -45,6 +45,7 @@ static struct daisydev { static DEFINE_SPINLOCK(topology_lock); static int numdevs; +static bool daisy_init_done; /* Forward-declaration of lower-level functions. */ static int mux_present(struct parport *port); @@ -87,6 +88,24 @@ static struct parport *clone_parport(struct parport *real, int muxport) return extra; } +static int daisy_drv_probe(struct pardevice *par_dev) +{ + struct device_driver *drv = par_dev->dev.driver; + + if (strcmp(drv->name, "daisy_drv")) + return -ENODEV; + if (strcmp(par_dev->name, daisy_dev_name)) + return -ENODEV; + + return 0; +} + +static struct parport_driver daisy_driver = { + .name = "daisy_drv", + .probe = daisy_drv_probe, + .devmodel = true, +}; + /* Discover the IEEE1284.3 topology on a port -- muxes and daisy chains. * Return value is number of devices actually detected. */ int parport_daisy_init(struct parport *port) @@ -98,6 +117,23 @@ int parport_daisy_init(struct parport *port) int i; int last_try = 0; + if (!daisy_init_done) { + /* + * flag should be marked true first as + * parport_register_driver() might try to load the low + * level driver which will lead to announcing new ports + * and which will again come back here at + * parport_daisy_init() + */ + daisy_init_done = true; + i = parport_register_driver(&daisy_driver); + if (i) { + pr_err("daisy registration failed\n"); + daisy_init_done = false; + return i; + } + } + again: /* Because this is called before any other devices exist, * we don't have to claim exclusive access. */ @@ -213,10 +249,12 @@ void parport_daisy_fini(struct parport *port) struct pardevice *parport_open(int devnum, const char *name) { struct daisydev *p = topology; + struct pardev_cb par_cb; struct parport *port; struct pardevice *dev; int daisy; + memset(&par_cb, 0, sizeof(par_cb)); spin_lock(&topology_lock); while (p && p->devnum != devnum) p = p->next; @@ -230,7 +268,7 @@ struct pardevice *parport_open(int devnum, const char *name) port = parport_get_port(p->port); spin_unlock(&topology_lock); - dev = parport_register_device(port, name, NULL, NULL, NULL, 0, NULL); + dev = parport_register_dev_model(port, name, &par_cb, devnum); parport_put_port(port); if (!dev) return NULL; From 8eb52a1ee37aafd9b796713aa0b3ab9cbc455be3 Mon Sep 17 00:00:00 2001 From: Jann Horn Date: Fri, 18 Oct 2019 22:56:29 +0200 Subject: [PATCH 146/198] binder: Fix race between mmap() and binder_alloc_print_pages() binder_alloc_print_pages() iterates over alloc->pages[0..alloc->buffer_size-1] under alloc->mutex. binder_alloc_mmap_handler() writes alloc->pages and alloc->buffer_size without holding that lock, and even writes them before the last bailout point. Unfortunately we can't take the alloc->mutex in the ->mmap() handler because mmap_sem can be taken while alloc->mutex is held. So instead, we have to locklessly check whether the binder_alloc has been fully initialized with binder_alloc_get_vma(), like in binder_alloc_new_buf_locked(). Fixes: 8ef4665aa129 ("android: binder: Add page usage in binder stats") Cc: stable@vger.kernel.org Signed-off-by: Jann Horn Acked-by: Christian Brauner Link: https://lore.kernel.org/r/20191018205631.248274-1-jannh@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder_alloc.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c index 1cbe37823de2..d718ed0537be 100644 --- a/drivers/android/binder_alloc.c +++ b/drivers/android/binder_alloc.c @@ -840,14 +840,20 @@ void binder_alloc_print_pages(struct seq_file *m, int free = 0; mutex_lock(&alloc->mutex); - for (i = 0; i < alloc->buffer_size / PAGE_SIZE; i++) { - page = &alloc->pages[i]; - if (!page->page_ptr) - free++; - else if (list_empty(&page->lru)) - active++; - else - lru++; + /* + * Make sure the binder_alloc is fully initialized, otherwise we might + * read inconsistent state. + */ + if (binder_alloc_get_vma(alloc) != NULL) { + for (i = 0; i < alloc->buffer_size / PAGE_SIZE; i++) { + page = &alloc->pages[i]; + if (!page->page_ptr) + free++; + else if (list_empty(&page->lru)) + active++; + else + lru++; + } } mutex_unlock(&alloc->mutex); seq_printf(m, " pages: %d:%d:%d\n", active, lru, free); From a7a74d7ff55a0c657bc46238b050460b9eacea95 Mon Sep 17 00:00:00 2001 From: Jann Horn Date: Fri, 18 Oct 2019 22:56:30 +0200 Subject: [PATCH 147/198] binder: Prevent repeated use of ->mmap() via NULL mapping binder_alloc_mmap_handler() attempts to detect the use of ->mmap() on a binder_proc whose binder_alloc has already been initialized by checking whether alloc->buffer is non-zero. Before commit 880211667b20 ("binder: remove kernel vm_area for buffer space"), alloc->buffer was a kernel mapping address, which is always non-zero, but since that commit, it is a userspace mapping address. A sufficiently privileged user can map /dev/binder at NULL, tricking binder_alloc_mmap_handler() into assuming that the binder_proc has not been mapped yet. This leads to memory unsafety. Luckily, no context on Android has such privileges, and on a typical Linux desktop system, you need to be root to do that. Fix it by using the mapping size instead of the mapping address to distinguish the mapped case. A valid VMA can't have size zero. Fixes: 880211667b20 ("binder: remove kernel vm_area for buffer space") Cc: stable@vger.kernel.org Signed-off-by: Jann Horn Acked-by: Christian Brauner Link: https://lore.kernel.org/r/20191018205631.248274-2-jannh@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder_alloc.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c index d718ed0537be..1f73d12409e3 100644 --- a/drivers/android/binder_alloc.c +++ b/drivers/android/binder_alloc.c @@ -680,17 +680,17 @@ int binder_alloc_mmap_handler(struct binder_alloc *alloc, struct binder_buffer *buffer; mutex_lock(&binder_alloc_mmap_lock); - if (alloc->buffer) { + if (alloc->buffer_size) { ret = -EBUSY; failure_string = "already mapped"; goto err_already_mapped; } - - alloc->buffer = (void __user *)vma->vm_start; - mutex_unlock(&binder_alloc_mmap_lock); - alloc->buffer_size = min_t(unsigned long, vma->vm_end - vma->vm_start, SZ_4M); + mutex_unlock(&binder_alloc_mmap_lock); + + alloc->buffer = (void __user *)vma->vm_start; + alloc->pages = kcalloc(alloc->buffer_size / PAGE_SIZE, sizeof(alloc->pages[0]), GFP_KERNEL); @@ -721,8 +721,9 @@ err_alloc_buf_struct_failed: kfree(alloc->pages); alloc->pages = NULL; err_alloc_pages_failed: - mutex_lock(&binder_alloc_mmap_lock); alloc->buffer = NULL; + mutex_lock(&binder_alloc_mmap_lock); + alloc->buffer_size = 0; err_already_mapped: mutex_unlock(&binder_alloc_mmap_lock); binder_alloc_debug(BINDER_DEBUG_USER_ERROR, From 2a9edd056ed4fbf9d2e797c3fc06335af35bccc4 Mon Sep 17 00:00:00 2001 From: Jann Horn Date: Fri, 18 Oct 2019 22:56:31 +0200 Subject: [PATCH 148/198] binder: Handle start==NULL in binder_update_page_range() The old loop wouldn't stop when reaching `start` if `start==NULL`, instead continuing backwards to index -1 and crashing. Luckily you need to be highly privileged to map things at NULL, so it's not a big problem. Fix it by adjusting the loop so that the loop variable is always in bounds. This patch is deliberately minimal to simplify backporting, but IMO this function could use a refactor. The jump labels in the second loop body are horrible (the error gotos should be jumping to free_range instead), and both loops would look nicer if they just iterated upwards through indices. And the up_read()+mmput() shouldn't be duplicated like that. Cc: stable@vger.kernel.org Fixes: 457b9a6f09f0 ("Staging: android: add binder driver") Signed-off-by: Jann Horn Acked-by: Christian Brauner Link: https://lore.kernel.org/r/20191018205631.248274-3-jannh@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder_alloc.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c index 1f73d12409e3..2d8b9b91dee0 100644 --- a/drivers/android/binder_alloc.c +++ b/drivers/android/binder_alloc.c @@ -276,8 +276,7 @@ static int binder_update_page_range(struct binder_alloc *alloc, int allocate, return 0; free_range: - for (page_addr = end - PAGE_SIZE; page_addr >= start; - page_addr -= PAGE_SIZE) { + for (page_addr = end - PAGE_SIZE; 1; page_addr -= PAGE_SIZE) { bool ret; size_t index; @@ -290,6 +289,8 @@ free_range: WARN_ON(!ret); trace_binder_free_lru_end(alloc, index); + if (page_addr == start) + break; continue; err_vm_insert_page_failed: @@ -297,7 +298,8 @@ err_vm_insert_page_failed: page->page_ptr = NULL; err_alloc_page_failed: err_page_ptr_cleared: - ; + if (page_addr == start) + break; } err_no_vma: if (mm) { From 3ec1bd7693ee5bba69eaa1a2b74b14d37971769e Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Tue, 5 Nov 2019 09:32:12 +0200 Subject: [PATCH 149/198] uio: fix irq init with dt support & irq not defined This change also does a bit of a unification for the IRQ init code. But the actual problem is that UIO_IRQ_NONE == 0, so for the DT case where UIO_IRQ_NONE gets assigned to `uioinfo->irq`, a 2nd initialization will get triggered (for the IRQ) and this one will exit via `goto bad1`. As far as things seem to go, the only case where UIO_IRQ_NONE seems valid, is when using a device-tree. The driver has some legacy support for old platform_data structures. It looks like, for platform_data a non-existent IRQ is an invalid case (or was considered an invalid case). Which is why -ENXIO is treated only when a DT is used. Signed-off-by: Dragos Bogdan Signed-off-by: Alexandru Ardelean Acked-by: Damian Hobson-Garcia Link: https://lore.kernel.org/r/20191105073212.16719-1-alexandru.ardelean@analog.com Signed-off-by: Greg Kroah-Hartman --- drivers/uio/uio_dmem_genirq.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/uio/uio_dmem_genirq.c b/drivers/uio/uio_dmem_genirq.c index ebcf1434e296..81c88f7bbbcb 100644 --- a/drivers/uio/uio_dmem_genirq.c +++ b/drivers/uio/uio_dmem_genirq.c @@ -151,8 +151,6 @@ static int uio_dmem_genirq_probe(struct platform_device *pdev) int i; if (pdev->dev.of_node) { - int irq; - /* alloc uioinfo for one device */ uioinfo = kzalloc(sizeof(*uioinfo), GFP_KERNEL); if (!uioinfo) { @@ -163,13 +161,6 @@ static int uio_dmem_genirq_probe(struct platform_device *pdev) uioinfo->name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%pOFn", pdev->dev.of_node); uioinfo->version = "devicetree"; - - /* Multiple IRQs are not supported */ - irq = platform_get_irq(pdev, 0); - if (irq == -ENXIO) - uioinfo->irq = UIO_IRQ_NONE; - else - uioinfo->irq = irq; } if (!uioinfo || !uioinfo->name || !uioinfo->version) { @@ -199,8 +190,11 @@ static int uio_dmem_genirq_probe(struct platform_device *pdev) mutex_init(&priv->alloc_lock); if (!uioinfo->irq) { + /* Multiple IRQs are not supported */ ret = platform_get_irq(pdev, 0); - if (ret < 0) + if (ret == -ENXIO && pdev->dev.of_node) + ret = UIO_IRQ_NONE; + else if (ret < 0) goto bad1; uioinfo->irq = ret; } From 46549769d0512e44c5a08ae2dd05cebe55b3db16 Mon Sep 17 00:00:00 2001 From: Dhanuka Warusadura Date: Mon, 28 Oct 2019 16:37:44 +0530 Subject: [PATCH 150/198] w1: Fix documentation warning. This patch fixes - warning: Function parameter or member 'of_match_table' not described in 'w1_family' Signed-off-by: Dhanuka Warusadura Link: https://lore.kernel.org/r/20191028110744.6523-1-csx@disroot.org Signed-off-by: Greg Kroah-Hartman --- include/linux/w1.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/w1.h b/include/linux/w1.h index 7da0c7588e04..cebf3464bc03 100644 --- a/include/linux/w1.h +++ b/include/linux/w1.h @@ -262,6 +262,7 @@ struct w1_family_ops { * @family_entry: family linked list * @fid: 8 bit family identifier * @fops: operations for this family + * @of_match_table: open firmware match table * @refcnt: reference counter */ struct w1_family { From b0bbc882e7b1b22e1a5aa4ccfaebf7ee445b7639 Mon Sep 17 00:00:00 2001 From: "Ben Dooks (Codethink)" Date: Thu, 17 Oct 2019 12:44:47 +0100 Subject: [PATCH 151/198] pcmcia: include for pcmcia_parse_tuple Include for pcmcia_parse_tuple declaration to fix the following sparse warning: drivers/pcmcia/cistpl.c:1287:5: warning: symbol 'pcmcia_parse_tuple' was not declared. Should it be static? Signed-off-by: Ben Dooks Link: https://lore.kernel.org/r/20191017114447.20455-1-ben.dooks@codethink.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/pcmcia/cistpl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pcmcia/cistpl.c b/drivers/pcmcia/cistpl.c index 629359fe3513..cf109d9a1112 100644 --- a/drivers/pcmcia/cistpl.c +++ b/drivers/pcmcia/cistpl.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "cs_internal.h" static const u_char mantissa[] = { From e0c31fdda2647f8313274c49506628151b81b289 Mon Sep 17 00:00:00 2001 From: "Ben Dooks (Codethink)" Date: Thu, 17 Oct 2019 12:40:59 +0100 Subject: [PATCH 152/198] pcmcia: include cs_internal.h for missing declarations Include cs_internal.h (and pcmcia/cistpl.h as required by cs_internal.h) for the declearions of cb_alloc and cb_free to silence the following sparse warnings; drivers/pcmcia/cardbus.c:64:11: warning: symbol 'cb_alloc' was not declared. Should it be static? drivers/pcmcia/cardbus.c:103:6: warning: symbol 'cb_free' was not declared. Should it be static? Signed-off-by: Ben Dooks Link: https://lore.kernel.org/r/20191017114059.10989-1-ben.dooks@codethink.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/pcmcia/cardbus.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pcmcia/cardbus.c b/drivers/pcmcia/cardbus.c index c502dfbf66e3..45c8252c8edc 100644 --- a/drivers/pcmcia/cardbus.c +++ b/drivers/pcmcia/cardbus.c @@ -22,7 +22,9 @@ #include #include +#include +#include "cs_internal.h" static void cardbus_config_irq_and_cls(struct pci_bus *bus, int irq) { From c6bf3842a34abe3ec2f5bc81754883689aea6c0d Mon Sep 17 00:00:00 2001 From: Angelo Dureghello Date: Sat, 19 Oct 2019 22:40:15 +0200 Subject: [PATCH 153/198] w1: new driver. DS2430 chip add support for ds2430, 1 page, 256bit (32bytes) eeprom (family 0x14). Tests done: 32 bytes dump: x@y:~# hexdump -C -n 32 /sys/bus/w1/devices/14-00000158556e/eeprom 00000000 39 39 0a 00 00 36 0a ff ff ff ff ff ff ff ff ff 00000010 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 00000020 34 bytes dump: 32 only displayed x@y:~# hexdump -C -n 34 /sys/bus/w1/devices/14-00000158556e/eeprom 00000000 39 39 0a 00 00 36 0a ff ff ff ff ff ff ff ff ff 00000010 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 00000020 pattern write: x@y:~# echo 123456789 > /sys/bus/w1/devices/14-00000158556e/eeprom x@y:~# hexdump -C -n 54 /sys/bus/w1/devices/14-00000158556e/eeprom 00000000 31 32 33 34 35 36 37 38 39 0a ff ff ff ff ff ff 00000010 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 00000020 specific address 1-byte write x@y:~# dd if=/dev/zero of=/sys/bus/w1/devices/14-00000158556e/eeprom \ count=1 bs=1 seek=4 1+0 records in 1+0 records out x@y:~# hexdump -C -n 54 /sys/bus/w1/devices/14-00000158556e/eeprom 00000000 31 32 33 34 00 36 37 38 39 0a ff ff ff ff ff ff 00000010 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 00000020 writing binary block x@y:~# cat dump-128bytes.bin > /sys/bus/w1/devices/14-00000158556e/eeprom cat: write error: File too large x@y:~# cat dump-32bytes.bin > /sys/bus/w1/devices/14-00000158556e/eeprom x@y:~# hexdump -C -n 54 /sys/bus/w1/devices/14-00000158556e/eeprom 00000000 10 0b 5b ff ff ff ff ff ff ff ff ff ff ff ff ff 00000010 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 40 00000020 Signed-off-by: Angelo Dureghello Link: https://lore.kernel.org/r/20191019204015.61474-1-angelo.dureghello@timesys.com Signed-off-by: Greg Kroah-Hartman --- drivers/w1/slaves/Kconfig | 8 + drivers/w1/slaves/Makefile | 1 + drivers/w1/slaves/w1_ds2430.c | 295 ++++++++++++++++++++++++++++++++++ 3 files changed, 304 insertions(+) create mode 100644 drivers/w1/slaves/w1_ds2430.c diff --git a/drivers/w1/slaves/Kconfig b/drivers/w1/slaves/Kconfig index b7847636501d..687753889c34 100644 --- a/drivers/w1/slaves/Kconfig +++ b/drivers/w1/slaves/Kconfig @@ -74,6 +74,14 @@ config W1_SLAVE_DS2805 organized as 7 pages of 16 bytes each with 64bit unique number. Requires OverDrive Speed to talk to. +config W1_SLAVE_DS2430 + tristate "256b EEPROM family support (DS2430)" + help + Say Y here if you want to use a 1-wire 256bit EEPROM + family device (DS2430). + This EEPROM is organized as one page of 32 bytes for random + access. + config W1_SLAVE_DS2431 tristate "1kb EEPROM family support (DS2431)" help diff --git a/drivers/w1/slaves/Makefile b/drivers/w1/slaves/Makefile index 8e9655eaa478..278bcf2a9bfd 100644 --- a/drivers/w1/slaves/Makefile +++ b/drivers/w1/slaves/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_W1_SLAVE_DS2408) += w1_ds2408.o obj-$(CONFIG_W1_SLAVE_DS2413) += w1_ds2413.o obj-$(CONFIG_W1_SLAVE_DS2406) += w1_ds2406.o obj-$(CONFIG_W1_SLAVE_DS2423) += w1_ds2423.o +obj-$(CONFIG_W1_SLAVE_DS2430) += w1_ds2430.o obj-$(CONFIG_W1_SLAVE_DS2431) += w1_ds2431.o obj-$(CONFIG_W1_SLAVE_DS2805) += w1_ds2805.o obj-$(CONFIG_W1_SLAVE_DS2433) += w1_ds2433.o diff --git a/drivers/w1/slaves/w1_ds2430.c b/drivers/w1/slaves/w1_ds2430.c new file mode 100644 index 000000000000..6fb0563fb2ae --- /dev/null +++ b/drivers/w1/slaves/w1_ds2430.c @@ -0,0 +1,295 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * w1_ds2430.c - w1 family 14 (DS2430) driver + ** + * Copyright (c) 2019 Angelo Dureghello + * + * Cloned and modified from ds2431 + * Copyright (c) 2008 Bernhard Weirich + * + */ + +#include +#include +#include +#include +#include +#include + +#include + +#define W1_EEPROM_DS2430 0x14 + +#define W1_F14_EEPROM_SIZE 32 +#define W1_F14_PAGE_COUNT 1 +#define W1_F14_PAGE_BITS 5 +#define W1_F14_PAGE_SIZE (1 << W1_F14_PAGE_BITS) +#define W1_F14_PAGE_MASK 0x1F + +#define W1_F14_SCRATCH_BITS 5 +#define W1_F14_SCRATCH_SIZE (1 << W1_F14_SCRATCH_BITS) +#define W1_F14_SCRATCH_MASK (W1_F14_SCRATCH_SIZE-1) + +#define W1_F14_READ_EEPROM 0xF0 +#define W1_F14_WRITE_SCRATCH 0x0F +#define W1_F14_READ_SCRATCH 0xAA +#define W1_F14_COPY_SCRATCH 0x55 +#define W1_F14_VALIDATION_KEY 0xa5 + +#define W1_F14_TPROG_MS 11 +#define W1_F14_READ_RETRIES 10 +#define W1_F14_READ_MAXLEN W1_F14_SCRATCH_SIZE + +/* + * Check the file size bounds and adjusts count as needed. + * This would not be needed if the file size didn't reset to 0 after a write. + */ +static inline size_t w1_f14_fix_count(loff_t off, size_t count, size_t size) +{ + if (off > size) + return 0; + + if ((off + count) > size) + return size - off; + + return count; +} + +/* + * Read a block from W1 ROM two times and compares the results. + * If they are equal they are returned, otherwise the read + * is repeated W1_F14_READ_RETRIES times. + * + * count must not exceed W1_F14_READ_MAXLEN. + */ +static int w1_f14_readblock(struct w1_slave *sl, int off, int count, char *buf) +{ + u8 wrbuf[2]; + u8 cmp[W1_F14_READ_MAXLEN]; + int tries = W1_F14_READ_RETRIES; + + do { + wrbuf[0] = W1_F14_READ_EEPROM; + wrbuf[1] = off & 0xff; + + if (w1_reset_select_slave(sl)) + return -1; + + w1_write_block(sl->master, wrbuf, 2); + w1_read_block(sl->master, buf, count); + + if (w1_reset_select_slave(sl)) + return -1; + + w1_write_block(sl->master, wrbuf, 2); + w1_read_block(sl->master, cmp, count); + + if (!memcmp(cmp, buf, count)) + return 0; + } while (--tries); + + dev_err(&sl->dev, "proof reading failed %d times\n", + W1_F14_READ_RETRIES); + + return -1; +} + +static ssize_t eeprom_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) +{ + struct w1_slave *sl = kobj_to_w1_slave(kobj); + int todo = count; + + count = w1_f14_fix_count(off, count, W1_F14_EEPROM_SIZE); + if (count == 0) + return 0; + + mutex_lock(&sl->master->bus_mutex); + + /* read directly from the EEPROM in chunks of W1_F14_READ_MAXLEN */ + while (todo > 0) { + int block_read; + + if (todo >= W1_F14_READ_MAXLEN) + block_read = W1_F14_READ_MAXLEN; + else + block_read = todo; + + if (w1_f14_readblock(sl, off, block_read, buf) < 0) + count = -EIO; + + todo -= W1_F14_READ_MAXLEN; + buf += W1_F14_READ_MAXLEN; + off += W1_F14_READ_MAXLEN; + } + + mutex_unlock(&sl->master->bus_mutex); + + return count; +} + +/* + * Writes to the scratchpad and reads it back for verification. + * Then copies the scratchpad to EEPROM. + * The data must be aligned at W1_F14_SCRATCH_SIZE bytes and + * must be W1_F14_SCRATCH_SIZE bytes long. + * The master must be locked. + * + * @param sl The slave structure + * @param addr Address for the write + * @param len length must be <= (W1_F14_PAGE_SIZE - (addr & W1_F14_PAGE_MASK)) + * @param data The data to write + * @return 0=Success -1=failure + */ +static int w1_f14_write(struct w1_slave *sl, int addr, int len, const u8 *data) +{ + int tries = W1_F14_READ_RETRIES; + u8 wrbuf[2]; + u8 rdbuf[W1_F14_SCRATCH_SIZE + 3]; + +retry: + + /* Write the data to the scratchpad */ + if (w1_reset_select_slave(sl)) + return -1; + + wrbuf[0] = W1_F14_WRITE_SCRATCH; + wrbuf[1] = addr & 0xff; + + w1_write_block(sl->master, wrbuf, 2); + w1_write_block(sl->master, data, len); + + /* Read the scratchpad and verify */ + if (w1_reset_select_slave(sl)) + return -1; + + w1_write_8(sl->master, W1_F14_READ_SCRATCH); + w1_read_block(sl->master, rdbuf, len + 2); + + /* + * Compare what was read against the data written + * Note: on read scratchpad, device returns 2 bulk 0xff bytes, + * to be discarded. + */ + if ((memcmp(data, &rdbuf[2], len) != 0)) { + + if (--tries) + goto retry; + + dev_err(&sl->dev, + "could not write to eeprom, scratchpad compare failed %d times\n", + W1_F14_READ_RETRIES); + + return -1; + } + + /* Copy the scratchpad to EEPROM */ + if (w1_reset_select_slave(sl)) + return -1; + + wrbuf[0] = W1_F14_COPY_SCRATCH; + wrbuf[1] = W1_F14_VALIDATION_KEY; + w1_write_block(sl->master, wrbuf, 2); + + /* Sleep for tprog ms to wait for the write to complete */ + msleep(W1_F14_TPROG_MS); + + /* Reset the bus to wake up the EEPROM */ + w1_reset_bus(sl->master); + + return 0; +} + +static ssize_t eeprom_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) +{ + struct w1_slave *sl = kobj_to_w1_slave(kobj); + int addr, len; + int copy; + + count = w1_f14_fix_count(off, count, W1_F14_EEPROM_SIZE); + if (count == 0) + return 0; + + mutex_lock(&sl->master->bus_mutex); + + /* Can only write data in blocks of the size of the scratchpad */ + addr = off; + len = count; + while (len > 0) { + + /* if len too short or addr not aligned */ + if (len < W1_F14_SCRATCH_SIZE || addr & W1_F14_SCRATCH_MASK) { + char tmp[W1_F14_SCRATCH_SIZE]; + + /* read the block and update the parts to be written */ + if (w1_f14_readblock(sl, addr & ~W1_F14_SCRATCH_MASK, + W1_F14_SCRATCH_SIZE, tmp)) { + count = -EIO; + goto out_up; + } + + /* copy at most to the boundary of the PAGE or len */ + copy = W1_F14_SCRATCH_SIZE - + (addr & W1_F14_SCRATCH_MASK); + + if (copy > len) + copy = len; + + memcpy(&tmp[addr & W1_F14_SCRATCH_MASK], buf, copy); + if (w1_f14_write(sl, addr & ~W1_F14_SCRATCH_MASK, + W1_F14_SCRATCH_SIZE, tmp) < 0) { + count = -EIO; + goto out_up; + } + } else { + + copy = W1_F14_SCRATCH_SIZE; + if (w1_f14_write(sl, addr, copy, buf) < 0) { + count = -EIO; + goto out_up; + } + } + buf += copy; + addr += copy; + len -= copy; + } + +out_up: + mutex_unlock(&sl->master->bus_mutex); + + return count; +} + +static BIN_ATTR_RW(eeprom, W1_F14_EEPROM_SIZE); + +static struct bin_attribute *w1_f14_bin_attrs[] = { + &bin_attr_eeprom, + NULL, +}; + +static const struct attribute_group w1_f14_group = { + .bin_attrs = w1_f14_bin_attrs, +}; + +static const struct attribute_group *w1_f14_groups[] = { + &w1_f14_group, + NULL, +}; + +static struct w1_family_ops w1_f14_fops = { + .groups = w1_f14_groups, +}; + +static struct w1_family w1_family_14 = { + .fid = W1_EEPROM_DS2430, + .fops = &w1_f14_fops, +}; +module_w1_family(w1_family_14); + +MODULE_AUTHOR("Angelo Dureghello "); +MODULE_DESCRIPTION("w1 family 14 driver for DS2430, 256kb EEPROM"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("w1-family-" __stringify(W1_EEPROM_DS2430)); From c0e5f4e73a7148e18b763067d181661987cb4c09 Mon Sep 17 00:00:00 2001 From: Rui Feng Date: Mon, 21 Oct 2019 16:05:05 +0800 Subject: [PATCH 154/198] misc: rtsx: Add support for RTS5261 Add support for new chip rts5261. In order to support rts5261, the definitions of some internal registers and workflow have to be modified and are different from its predecessors. So we need this patch to ensure RTS5261 can work. Signed-off-by: Rui Feng Link: https://lore.kernel.org/r/1571645105-5028-1-git-send-email-rui_feng@realsil.com.cn Signed-off-by: Greg Kroah-Hartman --- drivers/misc/cardreader/Makefile | 2 +- drivers/misc/cardreader/rts5261.c | 792 +++++++++++++++++++++++++++++ drivers/misc/cardreader/rts5261.h | 233 +++++++++ drivers/misc/cardreader/rtsx_pcr.c | 43 +- drivers/misc/cardreader/rtsx_pcr.h | 1 + include/linux/rtsx_pci.h | 1 + 6 files changed, 1064 insertions(+), 8 deletions(-) create mode 100644 drivers/misc/cardreader/rts5261.c create mode 100644 drivers/misc/cardreader/rts5261.h diff --git a/drivers/misc/cardreader/Makefile b/drivers/misc/cardreader/Makefile index d9bff5a2217e..1f56267ed2f4 100644 --- a/drivers/misc/cardreader/Makefile +++ b/drivers/misc/cardreader/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_MISC_ALCOR_PCI) += alcor_pci.o obj-$(CONFIG_MISC_RTSX_PCI) += rtsx_pci.o -rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o rts5249.o rts5260.o +rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o rts5249.o rts5260.o rts5261.o obj-$(CONFIG_MISC_RTSX_USB) += rtsx_usb.o diff --git a/drivers/misc/cardreader/rts5261.c b/drivers/misc/cardreader/rts5261.c new file mode 100644 index 000000000000..32dcec2e9dfd --- /dev/null +++ b/drivers/misc/cardreader/rts5261.c @@ -0,0 +1,792 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Driver for Realtek PCI-Express card reader + * + * Copyright(c) 2018-2019 Realtek Semiconductor Corp. All rights reserved. + * + * Author: + * Rui FENG + * Wei WANG + */ + +#include +#include +#include + +#include "rts5261.h" +#include "rtsx_pcr.h" + +static u8 rts5261_get_ic_version(struct rtsx_pcr *pcr) +{ + u8 val; + + rtsx_pci_read_register(pcr, DUMMY_REG_RESET_0, &val); + return val & IC_VERSION_MASK; +} + +static void rts5261_fill_driving(struct rtsx_pcr *pcr, u8 voltage) +{ + u8 driving_3v3[4][3] = { + {0x13, 0x13, 0x13}, + {0x96, 0x96, 0x96}, + {0x7F, 0x7F, 0x7F}, + {0x96, 0x96, 0x96}, + }; + u8 driving_1v8[4][3] = { + {0x99, 0x99, 0x99}, + {0x3A, 0x3A, 0x3A}, + {0xE6, 0xE6, 0xE6}, + {0xB3, 0xB3, 0xB3}, + }; + u8 (*driving)[3], drive_sel; + + if (voltage == OUTPUT_3V3) { + driving = driving_3v3; + drive_sel = pcr->sd30_drive_sel_3v3; + } else { + driving = driving_1v8; + drive_sel = pcr->sd30_drive_sel_1v8; + } + + rtsx_pci_write_register(pcr, SD30_CLK_DRIVE_SEL, + 0xFF, driving[drive_sel][0]); + + rtsx_pci_write_register(pcr, SD30_CMD_DRIVE_SEL, + 0xFF, driving[drive_sel][1]); + + rtsx_pci_write_register(pcr, SD30_DAT_DRIVE_SEL, + 0xFF, driving[drive_sel][2]); +} + +static void rtsx5261_fetch_vendor_settings(struct rtsx_pcr *pcr) +{ + u32 reg; + /* 0x814~0x817 */ + rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG2, ®); + pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG2, reg); + + if (!rts5261_vendor_setting_valid(reg)) { + pcr_dbg(pcr, "skip fetch vendor setting\n"); + return; + } + + pcr->card_drive_sel &= 0x3F; + pcr->card_drive_sel |= rts5261_reg_to_card_drive_sel(reg); + + if (rts5261_reg_check_reverse_socket(reg)) + pcr->flags |= PCR_REVERSE_SOCKET; + + /* 0x724~0x727 */ + rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG1, ®); + pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG1, reg); + + pcr->aspm_en = rts5261_reg_to_aspm(reg); + pcr->sd30_drive_sel_1v8 = rts5261_reg_to_sd30_drive_sel_1v8(reg); + pcr->sd30_drive_sel_3v3 = rts5261_reg_to_sd30_drive_sel_3v3(reg); +} + +static void rts5261_force_power_down(struct rtsx_pcr *pcr, u8 pm_state) +{ + /* Set relink_time to 0 */ + rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 1, MASK_8_BIT_DEF, 0); + rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 2, MASK_8_BIT_DEF, 0); + rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 3, + RELINK_TIME_MASK, 0); + + if (pm_state == HOST_ENTER_S3) + rtsx_pci_write_register(pcr, pcr->reg_pm_ctrl3, + D3_DELINK_MODE_EN, D3_DELINK_MODE_EN); + + rtsx_pci_write_register(pcr, RTS5261_REG_FPDCTL, + SSC_POWER_DOWN, SSC_POWER_DOWN); +} + +static int rts5261_enable_auto_blink(struct rtsx_pcr *pcr) +{ + return rtsx_pci_write_register(pcr, OLT_LED_CTL, + LED_SHINE_MASK, LED_SHINE_EN); +} + +static int rts5261_disable_auto_blink(struct rtsx_pcr *pcr) +{ + return rtsx_pci_write_register(pcr, OLT_LED_CTL, + LED_SHINE_MASK, LED_SHINE_DISABLE); +} + +static int rts5261_turn_on_led(struct rtsx_pcr *pcr) +{ + return rtsx_pci_write_register(pcr, GPIO_CTL, + 0x02, 0x02); +} + +static int rts5261_turn_off_led(struct rtsx_pcr *pcr) +{ + return rtsx_pci_write_register(pcr, GPIO_CTL, + 0x02, 0x00); +} + +/* SD Pull Control Enable: + * SD_DAT[3:0] ==> pull up + * SD_CD ==> pull up + * SD_WP ==> pull up + * SD_CMD ==> pull up + * SD_CLK ==> pull down + */ +static const u32 rts5261_sd_pull_ctl_enable_tbl[] = { + RTSX_REG_PAIR(CARD_PULL_CTL2, 0xAA), + RTSX_REG_PAIR(CARD_PULL_CTL3, 0xE9), + 0, +}; + +/* SD Pull Control Disable: + * SD_DAT[3:0] ==> pull down + * SD_CD ==> pull up + * SD_WP ==> pull down + * SD_CMD ==> pull down + * SD_CLK ==> pull down + */ +static const u32 rts5261_sd_pull_ctl_disable_tbl[] = { + RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55), + RTSX_REG_PAIR(CARD_PULL_CTL3, 0xD5), + 0, +}; + +static int rts5261_sd_set_sample_push_timing_sd30(struct rtsx_pcr *pcr) +{ + rtsx_pci_write_register(pcr, SD_CFG1, SD_MODE_SELECT_MASK + | SD_ASYNC_FIFO_NOT_RST, SD_30_MODE | SD_ASYNC_FIFO_NOT_RST); + rtsx_pci_write_register(pcr, CLK_CTL, CLK_LOW_FREQ, CLK_LOW_FREQ); + rtsx_pci_write_register(pcr, CARD_CLK_SOURCE, 0xFF, + CRC_VAR_CLK0 | SD30_FIX_CLK | SAMPLE_VAR_CLK1); + rtsx_pci_write_register(pcr, CLK_CTL, CLK_LOW_FREQ, 0); + + return 0; +} + +static int rts5261_card_power_on(struct rtsx_pcr *pcr, int card) +{ + struct rtsx_cr_option *option = &pcr->option; + + if (option->ocp_en) + rtsx_pci_enable_ocp(pcr); + + + rtsx_pci_write_register(pcr, RTS5261_LDO1_CFG1, + RTS5261_LDO1_TUNE_MASK, RTS5261_LDO1_33); + rtsx_pci_write_register(pcr, RTS5261_LDO1233318_POW_CTL, + RTS5261_LDO1_POWERON, RTS5261_LDO1_POWERON); + + rtsx_pci_write_register(pcr, RTS5261_LDO1233318_POW_CTL, + RTS5261_LDO3318_POWERON, RTS5261_LDO3318_POWERON); + + msleep(20); + + rtsx_pci_write_register(pcr, CARD_OE, SD_OUTPUT_EN, SD_OUTPUT_EN); + + /* Initialize SD_CFG1 register */ + rtsx_pci_write_register(pcr, SD_CFG1, 0xFF, + SD_CLK_DIVIDE_128 | SD_20_MODE | SD_BUS_WIDTH_1BIT); + + rtsx_pci_write_register(pcr, SD_SAMPLE_POINT_CTL, + 0xFF, SD20_RX_POS_EDGE); + rtsx_pci_write_register(pcr, SD_PUSH_POINT_CTL, 0xFF, 0); + rtsx_pci_write_register(pcr, CARD_STOP, SD_STOP | SD_CLR_ERR, + SD_STOP | SD_CLR_ERR); + + /* Reset SD_CFG3 register */ + rtsx_pci_write_register(pcr, SD_CFG3, SD30_CLK_END_EN, 0); + rtsx_pci_write_register(pcr, REG_SD_STOP_SDCLK_CFG, + SD30_CLK_STOP_CFG_EN | SD30_CLK_STOP_CFG1 | + SD30_CLK_STOP_CFG0, 0); + + if (pcr->extra_caps & EXTRA_CAPS_SD_SDR50 || + pcr->extra_caps & EXTRA_CAPS_SD_SDR104) + rts5261_sd_set_sample_push_timing_sd30(pcr); + + return 0; +} + +static int rts5261_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) +{ + int err; + u16 val = 0; + + rtsx_pci_write_register(pcr, RTS5261_CARD_PWR_CTL, + RTS5261_PUPDC, RTS5261_PUPDC); + + switch (voltage) { + case OUTPUT_3V3: + rtsx_pci_read_phy_register(pcr, PHY_TUNE, &val); + val |= PHY_TUNE_SDBUS_33; + err = rtsx_pci_write_phy_register(pcr, PHY_TUNE, val); + if (err < 0) + return err; + + rtsx_pci_write_register(pcr, RTS5261_DV3318_CFG, + RTS5261_DV3318_TUNE_MASK, RTS5261_DV3318_33); + rtsx_pci_write_register(pcr, SD_PAD_CTL, + SD_IO_USING_1V8, 0); + break; + case OUTPUT_1V8: + rtsx_pci_read_phy_register(pcr, PHY_TUNE, &val); + val &= ~PHY_TUNE_SDBUS_33; + err = rtsx_pci_write_phy_register(pcr, PHY_TUNE, val); + if (err < 0) + return err; + + rtsx_pci_write_register(pcr, RTS5261_DV3318_CFG, + RTS5261_DV3318_TUNE_MASK, RTS5261_DV3318_18); + rtsx_pci_write_register(pcr, SD_PAD_CTL, + SD_IO_USING_1V8, SD_IO_USING_1V8); + break; + default: + return -EINVAL; + } + + /* set pad drive */ + rts5261_fill_driving(pcr, voltage); + + return 0; +} + +static void rts5261_stop_cmd(struct rtsx_pcr *pcr) +{ + rtsx_pci_writel(pcr, RTSX_HCBCTLR, STOP_CMD); + rtsx_pci_writel(pcr, RTSX_HDBCTLR, STOP_DMA); + rtsx_pci_write_register(pcr, RTS5260_DMA_RST_CTL_0, + RTS5260_DMA_RST | RTS5260_ADMA3_RST, + RTS5260_DMA_RST | RTS5260_ADMA3_RST); + rtsx_pci_write_register(pcr, RBCTL, RB_FLUSH, RB_FLUSH); +} + +static void rts5261_card_before_power_off(struct rtsx_pcr *pcr) +{ + rts5261_stop_cmd(pcr); + rts5261_switch_output_voltage(pcr, OUTPUT_3V3); + +} + +static void rts5261_enable_ocp(struct rtsx_pcr *pcr) +{ + u8 val = 0; + + val = SD_OCP_INT_EN | SD_DETECT_EN; + rtsx_pci_write_register(pcr, REG_OCPCTL, 0xFF, val); + +} + +static void rts5261_disable_ocp(struct rtsx_pcr *pcr) +{ + u8 mask = 0; + + mask = SD_OCP_INT_EN | SD_DETECT_EN; + rtsx_pci_write_register(pcr, REG_OCPCTL, mask, 0); + rtsx_pci_write_register(pcr, RTS5261_LDO1_CFG0, + RTS5261_LDO1_OCP_EN | RTS5261_LDO1_OCP_LMT_EN, 0); + +} + +static int rts5261_card_power_off(struct rtsx_pcr *pcr, int card) +{ + int err = 0; + + rts5261_card_before_power_off(pcr); + err = rtsx_pci_write_register(pcr, RTS5261_LDO1233318_POW_CTL, + RTS5261_LDO_POWERON_MASK, 0); + + if (pcr->option.ocp_en) + rtsx_pci_disable_ocp(pcr); + + return err; +} + +static void rts5261_init_ocp(struct rtsx_pcr *pcr) +{ + struct rtsx_cr_option *option = &pcr->option; + + if (option->ocp_en) { + u8 mask, val; + + rtsx_pci_write_register(pcr, RTS5261_LDO1_CFG0, + RTS5261_LDO1_OCP_EN | RTS5261_LDO1_OCP_LMT_EN, + RTS5261_LDO1_OCP_EN | RTS5261_LDO1_OCP_LMT_EN); + + rtsx_pci_write_register(pcr, RTS5261_LDO1_CFG0, + RTS5261_LDO1_OCP_THD_MASK, option->sd_800mA_ocp_thd); + + rtsx_pci_write_register(pcr, RTS5261_LDO1_CFG0, + RTS5261_LDO1_OCP_LMT_THD_MASK, + RTS5261_LDO1_LMT_THD_2000); + + mask = SD_OCP_GLITCH_MASK; + val = pcr->hw_param.ocp_glitch; + rtsx_pci_write_register(pcr, REG_OCPGLITCH, mask, val); + + rts5261_enable_ocp(pcr); + } else { + rtsx_pci_write_register(pcr, RTS5261_LDO1_CFG0, + RTS5261_LDO1_OCP_EN | RTS5261_LDO1_OCP_LMT_EN, 0); + } +} + +static void rts5261_clear_ocpstat(struct rtsx_pcr *pcr) +{ + u8 mask = 0; + u8 val = 0; + + mask = SD_OCP_INT_CLR | SD_OC_CLR; + val = SD_OCP_INT_CLR | SD_OC_CLR; + + rtsx_pci_write_register(pcr, REG_OCPCTL, mask, val); + + udelay(10); + rtsx_pci_write_register(pcr, REG_OCPCTL, mask, 0); + +} + +static void rts5261_process_ocp(struct rtsx_pcr *pcr) +{ + if (!pcr->option.ocp_en) + return; + + rtsx_pci_get_ocpstat(pcr, &pcr->ocp_stat); + + if (pcr->ocp_stat & (SD_OC_NOW | SD_OC_EVER)) { + rts5261_card_power_off(pcr, RTSX_SD_CARD); + rtsx_pci_write_register(pcr, CARD_OE, SD_OUTPUT_EN, 0); + rts5261_clear_ocpstat(pcr); + pcr->ocp_stat = 0; + } + +} + +static int rts5261_init_from_hw(struct rtsx_pcr *pcr) +{ + int retval; + u32 lval, i; + u8 valid, efuse_valid, tmp; + + rtsx_pci_write_register(pcr, RTS5261_REG_PME_FORCE_CTL, + REG_EFUSE_POR | REG_EFUSE_POWER_MASK, + REG_EFUSE_POR | REG_EFUSE_POWERON); + udelay(1); + rtsx_pci_write_register(pcr, RTS5261_EFUSE_ADDR, + RTS5261_EFUSE_ADDR_MASK, 0x00); + rtsx_pci_write_register(pcr, RTS5261_EFUSE_CTL, + RTS5261_EFUSE_ENABLE | RTS5261_EFUSE_MODE_MASK, + RTS5261_EFUSE_ENABLE); + + /* Wait transfer end */ + for (i = 0; i < MAX_RW_REG_CNT; i++) { + rtsx_pci_read_register(pcr, RTS5261_EFUSE_CTL, &tmp); + if ((tmp & 0x80) == 0) + break; + } + rtsx_pci_read_register(pcr, RTS5261_EFUSE_READ_DATA, &tmp); + efuse_valid = ((tmp & 0x0C) >> 2); + pcr_dbg(pcr, "Load efuse valid: 0x%x\n", efuse_valid); + + if (efuse_valid == 0) { + retval = rtsx_pci_read_config_dword(pcr, + PCR_SETTING_REG2, &lval); + if (retval != 0) + pcr_dbg(pcr, "read 0x814 DW fail\n"); + pcr_dbg(pcr, "DW from 0x814: 0x%x\n", lval); + /* 0x816 */ + valid = (u8)((lval >> 16) & 0x03); + pcr_dbg(pcr, "0x816: %d\n", valid); + } + rtsx_pci_write_register(pcr, RTS5261_REG_PME_FORCE_CTL, + REG_EFUSE_POR, 0); + pcr_dbg(pcr, "Disable efuse por!\n"); + + rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG2, &lval); + lval = lval & 0x00FFFFFF; + retval = rtsx_pci_write_config_dword(pcr, PCR_SETTING_REG2, lval); + if (retval != 0) + pcr_dbg(pcr, "write config fail\n"); + + return retval; +} + +static void rts5261_init_from_cfg(struct rtsx_pcr *pcr) +{ + u32 lval; + struct rtsx_cr_option *option = &pcr->option; + + rtsx_pci_read_config_dword(pcr, PCR_ASPM_SETTING_REG1, &lval); + + if (lval & ASPM_L1_1_EN_MASK) + rtsx_set_dev_flag(pcr, ASPM_L1_1_EN); + else + rtsx_clear_dev_flag(pcr, ASPM_L1_1_EN); + + if (lval & ASPM_L1_2_EN_MASK) + rtsx_set_dev_flag(pcr, ASPM_L1_2_EN); + else + rtsx_clear_dev_flag(pcr, ASPM_L1_2_EN); + + if (lval & PM_L1_1_EN_MASK) + rtsx_set_dev_flag(pcr, PM_L1_1_EN); + else + rtsx_clear_dev_flag(pcr, PM_L1_1_EN); + + if (lval & PM_L1_2_EN_MASK) + rtsx_set_dev_flag(pcr, PM_L1_2_EN); + else + rtsx_clear_dev_flag(pcr, PM_L1_2_EN); + + rtsx_pci_write_register(pcr, ASPM_FORCE_CTL, 0xFF, 0); + if (option->ltr_en) { + u16 val; + + pcie_capability_read_word(pcr->pci, PCI_EXP_DEVCTL2, &val); + if (val & PCI_EXP_DEVCTL2_LTR_EN) { + option->ltr_enabled = true; + option->ltr_active = true; + rtsx_set_ltr_latency(pcr, option->ltr_active_latency); + } else { + option->ltr_enabled = false; + } + } + + if (rtsx_check_dev_flag(pcr, ASPM_L1_1_EN | ASPM_L1_2_EN + | PM_L1_1_EN | PM_L1_2_EN)) + option->force_clkreq_0 = false; + else + option->force_clkreq_0 = true; +} + +static int rts5261_extra_init_hw(struct rtsx_pcr *pcr) +{ + struct rtsx_cr_option *option = &pcr->option; + + rtsx_pci_write_register(pcr, RTS5261_AUTOLOAD_CFG1, + CD_RESUME_EN_MASK, CD_RESUME_EN_MASK); + + rts5261_init_from_cfg(pcr); + rts5261_init_from_hw(pcr); + + /* power off efuse */ + rtsx_pci_write_register(pcr, RTS5261_REG_PME_FORCE_CTL, + REG_EFUSE_POWER_MASK, REG_EFUSE_POWEROFF); + rtsx_pci_write_register(pcr, L1SUB_CONFIG1, + AUX_CLK_ACTIVE_SEL_MASK, MAC_CKSW_DONE); + rtsx_pci_write_register(pcr, L1SUB_CONFIG3, 0xFF, 0); + + rtsx_pci_write_register(pcr, RTS5261_AUTOLOAD_CFG4, + RTS5261_AUX_CLK_16M_EN, 0); + + /* Release PRSNT# */ + rtsx_pci_write_register(pcr, RTS5261_AUTOLOAD_CFG4, + RTS5261_FORCE_PRSNT_LOW, 0); + rtsx_pci_write_register(pcr, FUNC_FORCE_CTL, + FUNC_FORCE_UPME_XMT_DBG, FUNC_FORCE_UPME_XMT_DBG); + + rtsx_pci_write_register(pcr, PCLK_CTL, + PCLK_MODE_SEL, PCLK_MODE_SEL); + + rtsx_pci_write_register(pcr, PM_EVENT_DEBUG, PME_DEBUG_0, PME_DEBUG_0); + rtsx_pci_write_register(pcr, PM_CLK_FORCE_CTL, CLK_PM_EN, CLK_PM_EN); + + /* LED shine disabled, set initial shine cycle period */ + rtsx_pci_write_register(pcr, OLT_LED_CTL, 0x0F, 0x02); + + /* Configure driving */ + rts5261_fill_driving(pcr, OUTPUT_3V3); + + /* + * If u_force_clkreq_0 is enabled, CLKREQ# PIN will be forced + * to drive low, and we forcibly request clock. + */ + if (option->force_clkreq_0) + rtsx_pci_write_register(pcr, PETXCFG, + FORCE_CLKREQ_DELINK_MASK, FORCE_CLKREQ_LOW); + else + rtsx_pci_write_register(pcr, PETXCFG, + FORCE_CLKREQ_DELINK_MASK, FORCE_CLKREQ_HIGH); + + rtsx_pci_write_register(pcr, pcr->reg_pm_ctrl3, 0x10, 0x00); + rtsx_pci_write_register(pcr, RTS5261_REG_PME_FORCE_CTL, + FORCE_PM_CONTROL | FORCE_PM_VALUE, FORCE_PM_CONTROL); + + /* Clear Enter RTD3_cold Information*/ + rtsx_pci_write_register(pcr, RTS5261_FW_CTL, + RTS5261_INFORM_RTD3_COLD, 0); + + return 0; +} + +static void rts5261_enable_aspm(struct rtsx_pcr *pcr, bool enable) +{ + struct rtsx_cr_option *option = &pcr->option; + u8 val = 0; + + if (pcr->aspm_enabled == enable) + return; + + if (option->dev_aspm_mode == DEV_ASPM_DYNAMIC) { + val = pcr->aspm_en; + rtsx_pci_update_cfg_byte(pcr, pcr->pcie_cap + PCI_EXP_LNKCTL, + ASPM_MASK_NEG, val); + } else if (option->dev_aspm_mode == DEV_ASPM_BACKDOOR) { + u8 mask = FORCE_ASPM_VAL_MASK | FORCE_ASPM_CTL0; + + val = FORCE_ASPM_CTL0; + val |= (pcr->aspm_en & 0x02); + rtsx_pci_write_register(pcr, ASPM_FORCE_CTL, mask, val); + val = pcr->aspm_en; + rtsx_pci_update_cfg_byte(pcr, pcr->pcie_cap + PCI_EXP_LNKCTL, + ASPM_MASK_NEG, val); + } + pcr->aspm_enabled = enable; + +} + +static void rts5261_disable_aspm(struct rtsx_pcr *pcr, bool enable) +{ + struct rtsx_cr_option *option = &pcr->option; + u8 val = 0; + + if (pcr->aspm_enabled == enable) + return; + + if (option->dev_aspm_mode == DEV_ASPM_DYNAMIC) { + val = 0; + rtsx_pci_update_cfg_byte(pcr, pcr->pcie_cap + PCI_EXP_LNKCTL, + ASPM_MASK_NEG, val); + } else if (option->dev_aspm_mode == DEV_ASPM_BACKDOOR) { + u8 mask = FORCE_ASPM_VAL_MASK | FORCE_ASPM_CTL0; + + val = 0; + rtsx_pci_update_cfg_byte(pcr, pcr->pcie_cap + PCI_EXP_LNKCTL, + ASPM_MASK_NEG, val); + val = FORCE_ASPM_CTL0; + rtsx_pci_write_register(pcr, ASPM_FORCE_CTL, mask, val); + } + rtsx_pci_write_register(pcr, SD_CFG1, SD_ASYNC_FIFO_NOT_RST, 0); + udelay(10); + pcr->aspm_enabled = enable; +} + +static void rts5261_set_aspm(struct rtsx_pcr *pcr, bool enable) +{ + if (enable) + rts5261_enable_aspm(pcr, true); + else + rts5261_disable_aspm(pcr, false); +} + +static void rts5261_set_l1off_cfg_sub_d0(struct rtsx_pcr *pcr, int active) +{ + struct rtsx_cr_option *option = &pcr->option; + int aspm_L1_1, aspm_L1_2; + u8 val = 0; + + aspm_L1_1 = rtsx_check_dev_flag(pcr, ASPM_L1_1_EN); + aspm_L1_2 = rtsx_check_dev_flag(pcr, ASPM_L1_2_EN); + + if (active) { + /* run, latency: 60us */ + if (aspm_L1_1) + val = option->ltr_l1off_snooze_sspwrgate; + } else { + /* l1off, latency: 300us */ + if (aspm_L1_2) + val = option->ltr_l1off_sspwrgate; + } + + rtsx_set_l1off_sub(pcr, val); +} + +static const struct pcr_ops rts5261_pcr_ops = { + .fetch_vendor_settings = rtsx5261_fetch_vendor_settings, + .turn_on_led = rts5261_turn_on_led, + .turn_off_led = rts5261_turn_off_led, + .extra_init_hw = rts5261_extra_init_hw, + .enable_auto_blink = rts5261_enable_auto_blink, + .disable_auto_blink = rts5261_disable_auto_blink, + .card_power_on = rts5261_card_power_on, + .card_power_off = rts5261_card_power_off, + .switch_output_voltage = rts5261_switch_output_voltage, + .force_power_down = rts5261_force_power_down, + .stop_cmd = rts5261_stop_cmd, + .set_aspm = rts5261_set_aspm, + .set_l1off_cfg_sub_d0 = rts5261_set_l1off_cfg_sub_d0, + .enable_ocp = rts5261_enable_ocp, + .disable_ocp = rts5261_disable_ocp, + .init_ocp = rts5261_init_ocp, + .process_ocp = rts5261_process_ocp, + .clear_ocpstat = rts5261_clear_ocpstat, +}; + +static inline u8 double_ssc_depth(u8 depth) +{ + return ((depth > 1) ? (depth - 1) : depth); +} + +int rts5261_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock, + u8 ssc_depth, bool initial_mode, bool double_clk, bool vpclk) +{ + int err, clk; + u8 n, clk_divider, mcu_cnt, div; + static const u8 depth[] = { + [RTSX_SSC_DEPTH_4M] = RTS5261_SSC_DEPTH_4M, + [RTSX_SSC_DEPTH_2M] = RTS5261_SSC_DEPTH_2M, + [RTSX_SSC_DEPTH_1M] = RTS5261_SSC_DEPTH_1M, + [RTSX_SSC_DEPTH_500K] = RTS5261_SSC_DEPTH_512K, + }; + + if (initial_mode) { + /* We use 250k(around) here, in initial stage */ + clk_divider = SD_CLK_DIVIDE_128; + card_clock = 30000000; + } else { + clk_divider = SD_CLK_DIVIDE_0; + } + err = rtsx_pci_write_register(pcr, SD_CFG1, + SD_CLK_DIVIDE_MASK, clk_divider); + if (err < 0) + return err; + + card_clock /= 1000000; + pcr_dbg(pcr, "Switch card clock to %dMHz\n", card_clock); + + clk = card_clock; + if (!initial_mode && double_clk) + clk = card_clock * 2; + pcr_dbg(pcr, "Internal SSC clock: %dMHz (cur_clock = %d)\n", + clk, pcr->cur_clock); + + if (clk == pcr->cur_clock) + return 0; + + if (pcr->ops->conv_clk_and_div_n) + n = (u8)pcr->ops->conv_clk_and_div_n(clk, CLK_TO_DIV_N); + else + n = (u8)(clk - 4); + if ((clk <= 4) || (n > 396)) + return -EINVAL; + + mcu_cnt = (u8)(125/clk + 3); + if (mcu_cnt > 15) + mcu_cnt = 15; + + div = CLK_DIV_1; + while ((n < MIN_DIV_N_PCR - 4) && (div < CLK_DIV_8)) { + if (pcr->ops->conv_clk_and_div_n) { + int dbl_clk = pcr->ops->conv_clk_and_div_n(n, + DIV_N_TO_CLK) * 2; + n = (u8)pcr->ops->conv_clk_and_div_n(dbl_clk, + CLK_TO_DIV_N); + } else { + n = (n + 4) * 2 - 4; + } + div++; + } + + n = (n / 2); + pcr_dbg(pcr, "n = %d, div = %d\n", n, div); + + ssc_depth = depth[ssc_depth]; + if (double_clk) + ssc_depth = double_ssc_depth(ssc_depth); + + if (ssc_depth) { + if (div == CLK_DIV_2) { + if (ssc_depth > 1) + ssc_depth -= 1; + else + ssc_depth = RTS5261_SSC_DEPTH_8M; + } else if (div == CLK_DIV_4) { + if (ssc_depth > 2) + ssc_depth -= 2; + else + ssc_depth = RTS5261_SSC_DEPTH_8M; + } else if (div == CLK_DIV_8) { + if (ssc_depth > 3) + ssc_depth -= 3; + else + ssc_depth = RTS5261_SSC_DEPTH_8M; + } + } else { + ssc_depth = 0; + } + pcr_dbg(pcr, "ssc_depth = %d\n", ssc_depth); + + rtsx_pci_init_cmd(pcr); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL, + CLK_LOW_FREQ, CLK_LOW_FREQ); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_DIV, + 0xFF, (div << 4) | mcu_cnt); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, 0); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL2, + SSC_DEPTH_MASK, ssc_depth); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_DIV_N_0, 0xFF, n); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, SSC_RSTB); + if (vpclk) { + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_VPCLK0_CTL, + PHASE_NOT_RESET, 0); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_VPCLK1_CTL, + PHASE_NOT_RESET, 0); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_VPCLK0_CTL, + PHASE_NOT_RESET, PHASE_NOT_RESET); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_VPCLK1_CTL, + PHASE_NOT_RESET, PHASE_NOT_RESET); + } + + err = rtsx_pci_send_cmd(pcr, 2000); + if (err < 0) + return err; + + /* Wait SSC clock stable */ + udelay(SSC_CLOCK_STABLE_WAIT); + err = rtsx_pci_write_register(pcr, CLK_CTL, CLK_LOW_FREQ, 0); + if (err < 0) + return err; + + pcr->cur_clock = clk; + return 0; + +} + +void rts5261_init_params(struct rtsx_pcr *pcr) +{ + struct rtsx_cr_option *option = &pcr->option; + struct rtsx_hw_param *hw_param = &pcr->hw_param; + + pcr->extra_caps = EXTRA_CAPS_SD_SDR50 | EXTRA_CAPS_SD_SDR104; + pcr->num_slots = 1; + pcr->ops = &rts5261_pcr_ops; + + pcr->flags = 0; + pcr->card_drive_sel = RTSX_CARD_DRIVE_DEFAULT; + pcr->sd30_drive_sel_1v8 = CFG_DRIVER_TYPE_B; + pcr->sd30_drive_sel_3v3 = CFG_DRIVER_TYPE_B; + pcr->aspm_en = ASPM_L1_EN; + pcr->tx_initial_phase = SET_CLOCK_PHASE(20, 27, 16); + pcr->rx_initial_phase = SET_CLOCK_PHASE(24, 6, 5); + + pcr->ic_version = rts5261_get_ic_version(pcr); + pcr->sd_pull_ctl_enable_tbl = rts5261_sd_pull_ctl_enable_tbl; + pcr->sd_pull_ctl_disable_tbl = rts5261_sd_pull_ctl_disable_tbl; + + pcr->reg_pm_ctrl3 = RTS5261_AUTOLOAD_CFG3; + + option->dev_flags = (LTR_L1SS_PWR_GATE_CHECK_CARD_EN + | LTR_L1SS_PWR_GATE_EN); + option->ltr_en = true; + + /* init latency of active, idle, L1OFF to 60us, 300us, 3ms */ + option->ltr_active_latency = LTR_ACTIVE_LATENCY_DEF; + option->ltr_idle_latency = LTR_IDLE_LATENCY_DEF; + option->ltr_l1off_latency = LTR_L1OFF_LATENCY_DEF; + option->l1_snooze_delay = L1_SNOOZE_DELAY_DEF; + option->ltr_l1off_sspwrgate = 0x7F; + option->ltr_l1off_snooze_sspwrgate = 0x78; + option->dev_aspm_mode = DEV_ASPM_DYNAMIC; + + option->ocp_en = 1; + hw_param->interrupt_en |= SD_OC_INT_EN; + hw_param->ocp_glitch = SD_OCP_GLITCH_800U; + option->sd_800mA_ocp_thd = RTS5261_LDO1_OCP_THD_1040; +} diff --git a/drivers/misc/cardreader/rts5261.h b/drivers/misc/cardreader/rts5261.h new file mode 100644 index 000000000000..ebfdd236a553 --- /dev/null +++ b/drivers/misc/cardreader/rts5261.h @@ -0,0 +1,233 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Driver for Realtek PCI-Express card reader + * + * Copyright(c) 2018-2019 Realtek Semiconductor Corp. All rights reserved. + * + * Author: + * Rui FENG + * Wei WANG + */ +#ifndef RTS5261_H +#define RTS5261_H + +/*New add*/ +#define rts5261_vendor_setting_valid(reg) ((reg) & 0x010000) +#define rts5261_reg_to_aspm(reg) (((reg) >> 28) ^ 0x03) +#define rts5261_reg_check_reverse_socket(reg) ((reg) & 0x04) +#define rts5261_reg_to_card_drive_sel(reg) ((((reg) >> 6) & 0x01) << 6) +#define rts5261_reg_to_sd30_drive_sel_1v8(reg) (((reg) >> 22) ^ 0x03) +#define rts5261_reg_to_sd30_drive_sel_3v3(reg) (((reg) >> 16) ^ 0x03) + + +#define RTS5261_AUTOLOAD_CFG0 0xFF7B +#define RTS5261_AUTOLOAD_CFG1 0xFF7C +#define RTS5261_AUTOLOAD_CFG2 0xFF7D +#define RTS5261_AUTOLOAD_CFG3 0xFF7E +#define RTS5261_AUTOLOAD_CFG4 0xFF7F +#define RTS5261_FORCE_PRSNT_LOW (1 << 6) +#define RTS5261_AUX_CLK_16M_EN (1 << 5) + +#define RTS5261_REG_VREF 0xFE97 +#define RTS5261_PWD_SUSPND_EN (1 << 4) + +#define RTS5261_PAD_H3L1 0xFF79 +#define PAD_GPIO_H3L1 (1 << 3) + +/* SSC_CTL2 0xFC12 */ +#define RTS5261_SSC_DEPTH_MASK 0x07 +#define RTS5261_SSC_DEPTH_DISALBE 0x00 +#define RTS5261_SSC_DEPTH_8M 0x01 +#define RTS5261_SSC_DEPTH_4M 0x02 +#define RTS5261_SSC_DEPTH_2M 0x03 +#define RTS5261_SSC_DEPTH_1M 0x04 +#define RTS5261_SSC_DEPTH_512K 0x05 +#define RTS5261_SSC_DEPTH_256K 0x06 +#define RTS5261_SSC_DEPTH_128K 0x07 + +/* efuse control register*/ +#define RTS5261_EFUSE_CTL 0xFC30 +#define RTS5261_EFUSE_ENABLE 0x80 +/* EFUSE_MODE: 0=READ 1=PROGRAM */ +#define RTS5261_EFUSE_MODE_MASK 0x40 +#define RTS5261_EFUSE_PROGRAM 0x40 + +#define RTS5261_EFUSE_ADDR 0xFC31 +#define RTS5261_EFUSE_ADDR_MASK 0x3F + +#define RTS5261_EFUSE_WRITE_DATA 0xFC32 +#define RTS5261_EFUSE_READ_DATA 0xFC34 + +/* DMACTL 0xFE2C */ +#define RTS5261_DMA_PACK_SIZE_MASK 0xF0 + +/* FW config info register */ +#define RTS5261_FW_CFG_INFO0 0xFF50 +#define RTS5261_FW_EXPRESS_TEST_MASK (0x01<<0) +#define RTS5261_FW_EA_MODE_MASK (0x01<<5) + +/* FW config register */ +#define RTS5261_FW_CFG0 0xFF54 +#define RTS5261_FW_ENTER_EXPRESS (0x01<<0) + +#define RTS5261_FW_CFG1 0xFF55 +#define RTS5261_SYS_CLK_SEL_MCU_CLK (0x01<<7) +#define RTS5261_CRC_CLK_SEL_MCU_CLK (0x01<<6) +#define RTS5261_FAKE_MCU_CLOCK_GATING (0x01<<5) +/*MCU_bus_mode_sel: 0=real 8051 1=fake mcu*/ +#define RTS5261_MCU_BUS_SEL_MASK (0x01<<4) +/*MCU_clock_sel:VerA 00=aux16M 01=aux400K 1x=REFCLK100M*/ +/*MCU_clock_sel:VerB 00=aux400K 01=aux16M 10=REFCLK100M*/ +#define RTS5261_MCU_CLOCK_SEL_MASK (0x03<<2) +#define RTS5261_MCU_CLOCK_SEL_16M (0x01<<2) +#define RTS5261_MCU_CLOCK_GATING (0x01<<1) +#define RTS5261_DRIVER_ENABLE_FW (0x01<<0) + +/* FW status register */ +#define RTS5261_FW_STATUS 0xFF56 +#define RTS5261_EXPRESS_LINK_FAIL_MASK (0x01<<7) + +/* FW control register */ +#define RTS5261_FW_CTL 0xFF5F +#define RTS5261_INFORM_RTD3_COLD (0x01<<5) + +#define RTS5261_REG_FPDCTL 0xFF60 + +#define RTS5261_REG_LDO12_CFG 0xFF6E +#define RTS5261_LDO12_VO_TUNE_MASK (0x07<<1) +#define RTS5261_LDO12_115 (0x03<<1) +#define RTS5261_LDO12_120 (0x04<<1) +#define RTS5261_LDO12_125 (0x05<<1) +#define RTS5261_LDO12_130 (0x06<<1) +#define RTS5261_LDO12_135 (0x07<<1) + +/* LDO control register */ +#define RTS5261_CARD_PWR_CTL 0xFD50 +#define RTS5261_SD_CLK_ISO (0x01<<7) +#define RTS5261_PAD_SD_DAT_FW_CTRL (0x01<<6) +#define RTS5261_PUPDC (0x01<<5) +#define RTS5261_SD_CMD_ISO (0x01<<4) +#define RTS5261_SD_DAT_ISO_MASK (0x0F<<0) + +#define RTS5261_LDO1233318_POW_CTL 0xFF70 +#define RTS5261_LDO3318_POWERON (0x01<<3) +#define RTS5261_LDO3_POWERON (0x01<<2) +#define RTS5261_LDO2_POWERON (0x01<<1) +#define RTS5261_LDO1_POWERON (0x01<<0) +#define RTS5261_LDO_POWERON_MASK (0x0F<<0) + +#define RTS5261_DV3318_CFG 0xFF71 +#define RTS5261_DV3318_TUNE_MASK (0x07<<4) +#define RTS5261_DV3318_18 (0x02<<4) +#define RTS5261_DV3318_19 (0x04<<4) +#define RTS5261_DV3318_33 (0x07<<4) + +#define RTS5261_LDO1_CFG0 0xFF72 +#define RTS5261_LDO1_OCP_THD_MASK (0x07<<5) +#define RTS5261_LDO1_OCP_EN (0x01<<4) +#define RTS5261_LDO1_OCP_LMT_THD_MASK (0x03<<2) +#define RTS5261_LDO1_OCP_LMT_EN (0x01<<1) + +/* CRD6603-433 190319 request changed */ +#define RTS5261_LDO1_OCP_THD_740 (0x00<<5) +#define RTS5261_LDO1_OCP_THD_800 (0x01<<5) +#define RTS5261_LDO1_OCP_THD_860 (0x02<<5) +#define RTS5261_LDO1_OCP_THD_920 (0x03<<5) +#define RTS5261_LDO1_OCP_THD_980 (0x04<<5) +#define RTS5261_LDO1_OCP_THD_1040 (0x05<<5) +#define RTS5261_LDO1_OCP_THD_1100 (0x06<<5) +#define RTS5261_LDO1_OCP_THD_1160 (0x07<<5) + +#define RTS5261_LDO1_LMT_THD_450 (0x00<<2) +#define RTS5261_LDO1_LMT_THD_1000 (0x01<<2) +#define RTS5261_LDO1_LMT_THD_1500 (0x02<<2) +#define RTS5261_LDO1_LMT_THD_2000 (0x03<<2) + +#define RTS5261_LDO1_CFG1 0xFF73 +#define RTS5261_LDO1_TUNE_MASK (0x07<<1) +#define RTS5261_LDO1_18 (0x05<<1) +#define RTS5261_LDO1_33 (0x07<<1) +#define RTS5261_LDO1_PWD_MASK (0x01<<0) + +#define RTS5261_LDO2_CFG0 0xFF74 +#define RTS5261_LDO2_OCP_THD_MASK (0x07<<5) +#define RTS5261_LDO2_OCP_EN (0x01<<4) +#define RTS5261_LDO2_OCP_LMT_THD_MASK (0x03<<2) +#define RTS5261_LDO2_OCP_LMT_EN (0x01<<1) + +#define RTS5261_LDO2_OCP_THD_620 (0x00<<5) +#define RTS5261_LDO2_OCP_THD_650 (0x01<<5) +#define RTS5261_LDO2_OCP_THD_680 (0x02<<5) +#define RTS5261_LDO2_OCP_THD_720 (0x03<<5) +#define RTS5261_LDO2_OCP_THD_750 (0x04<<5) +#define RTS5261_LDO2_OCP_THD_780 (0x05<<5) +#define RTS5261_LDO2_OCP_THD_810 (0x06<<5) +#define RTS5261_LDO2_OCP_THD_840 (0x07<<5) + +#define RTS5261_LDO2_CFG1 0xFF75 +#define RTS5261_LDO2_TUNE_MASK (0x07<<1) +#define RTS5261_LDO2_18 (0x05<<1) +#define RTS5261_LDO2_33 (0x07<<1) +#define RTS5261_LDO2_PWD_MASK (0x01<<0) + +#define RTS5261_LDO3_CFG0 0xFF76 +#define RTS5261_LDO3_OCP_THD_MASK (0x07<<5) +#define RTS5261_LDO3_OCP_EN (0x01<<4) +#define RTS5261_LDO3_OCP_LMT_THD_MASK (0x03<<2) +#define RTS5261_LDO3_OCP_LMT_EN (0x01<<1) + +#define RTS5261_LDO3_OCP_THD_620 (0x00<<5) +#define RTS5261_LDO3_OCP_THD_650 (0x01<<5) +#define RTS5261_LDO3_OCP_THD_680 (0x02<<5) +#define RTS5261_LDO3_OCP_THD_720 (0x03<<5) +#define RTS5261_LDO3_OCP_THD_750 (0x04<<5) +#define RTS5261_LDO3_OCP_THD_780 (0x05<<5) +#define RTS5261_LDO3_OCP_THD_810 (0x06<<5) +#define RTS5261_LDO3_OCP_THD_840 (0x07<<5) + +#define RTS5261_LDO3_CFG1 0xFF77 +#define RTS5261_LDO3_TUNE_MASK (0x07<<1) +#define RTS5261_LDO3_18 (0x05<<1) +#define RTS5261_LDO3_33 (0x07<<1) +#define RTS5261_LDO3_PWD_MASK (0x01<<0) + +#define RTS5261_REG_PME_FORCE_CTL 0xFF78 +#define FORCE_PM_CONTROL 0x20 +#define FORCE_PM_VALUE 0x10 +#define REG_EFUSE_BYPASS 0x08 +#define REG_EFUSE_POR 0x04 +#define REG_EFUSE_POWER_MASK 0x03 +#define REG_EFUSE_POWERON 0x03 +#define REG_EFUSE_POWEROFF 0x00 + + +/* Single LUN, support SD/SD EXPRESS */ +#define DEFAULT_SINGLE 0 +#define SD_LUN 1 +#define SD_EXPRESS_LUN 2 + +/* For Change_FPGA_SSCClock Function */ +#define MULTIPLY_BY_1 0x00 +#define MULTIPLY_BY_2 0x01 +#define MULTIPLY_BY_3 0x02 +#define MULTIPLY_BY_4 0x03 +#define MULTIPLY_BY_5 0x04 +#define MULTIPLY_BY_6 0x05 +#define MULTIPLY_BY_7 0x06 +#define MULTIPLY_BY_8 0x07 +#define MULTIPLY_BY_9 0x08 +#define MULTIPLY_BY_10 0x09 + +#define DIVIDE_BY_2 0x01 +#define DIVIDE_BY_3 0x02 +#define DIVIDE_BY_4 0x03 +#define DIVIDE_BY_5 0x04 +#define DIVIDE_BY_6 0x05 +#define DIVIDE_BY_7 0x06 +#define DIVIDE_BY_8 0x07 +#define DIVIDE_BY_9 0x08 +#define DIVIDE_BY_10 0x09 + +int rts5261_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock, + u8 ssc_depth, bool initial_mode, bool double_clk, bool vpclk); + +#endif /* RTS5261_H */ diff --git a/drivers/misc/cardreader/rtsx_pcr.c b/drivers/misc/cardreader/rtsx_pcr.c index b4a66b64f742..fd7b2167103d 100644 --- a/drivers/misc/cardreader/rtsx_pcr.c +++ b/drivers/misc/cardreader/rtsx_pcr.c @@ -22,6 +22,7 @@ #include #include "rtsx_pcr.h" +#include "rts5261.h" static bool msi_en = true; module_param(msi_en, bool, S_IRUGO | S_IWUSR); @@ -34,9 +35,6 @@ static struct mfd_cell rtsx_pcr_cells[] = { [RTSX_SD_CARD] = { .name = DRV_NAME_RTSX_PCI_SDMMC, }, - [RTSX_MS_CARD] = { - .name = DRV_NAME_RTSX_PCI_MS, - }, }; static const struct pci_device_id rtsx_pci_ids[] = { @@ -51,6 +49,7 @@ static const struct pci_device_id rtsx_pci_ids[] = { { PCI_DEVICE(0x10EC, 0x524A), PCI_CLASS_OTHERS << 16, 0xFF0000 }, { PCI_DEVICE(0x10EC, 0x525A), PCI_CLASS_OTHERS << 16, 0xFF0000 }, { PCI_DEVICE(0x10EC, 0x5260), PCI_CLASS_OTHERS << 16, 0xFF0000 }, + { PCI_DEVICE(0x10EC, 0x5261), PCI_CLASS_OTHERS << 16, 0xFF0000 }, { 0, } }; @@ -438,8 +437,16 @@ static void rtsx_pci_add_sg_tbl(struct rtsx_pcr *pcr, if (end) option |= RTSX_SG_END; - val = ((u64)addr << 32) | ((u64)len << 12) | option; + if (PCI_PID(pcr) == PID_5261) { + if (len > 0xFFFF) + val = ((u64)addr << 32) | (((u64)len & 0xFFFF) << 16) + | (((u64)len >> 16) << 6) | option; + else + val = ((u64)addr << 32) | ((u64)len << 16) | option; + } else { + val = ((u64)addr << 32) | ((u64)len << 12) | option; + } put_unaligned_le64(val, ptr); pcr->sgi++; } @@ -684,7 +691,6 @@ int rtsx_pci_card_pull_ctl_disable(struct rtsx_pcr *pcr, int card) else return -EINVAL; - return rtsx_pci_set_pull_ctl(pcr, tbl); } EXPORT_SYMBOL_GPL(rtsx_pci_card_pull_ctl_disable); @@ -735,6 +741,10 @@ int rtsx_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock, [RTSX_SSC_DEPTH_250K] = SSC_DEPTH_250K, }; + if (PCI_PID(pcr) == PID_5261) + return rts5261_pci_switch_clock(pcr, card_clock, + ssc_depth, initial_mode, double_clk, vpclk); + if (initial_mode) { /* We use 250k(around) here, in initial stage */ clk_divider = SD_CLK_DIVIDE_128; @@ -1253,7 +1263,15 @@ static int rtsx_pci_init_hw(struct rtsx_pcr *pcr) rtsx_pci_enable_bus_int(pcr); /* Power on SSC */ - err = rtsx_pci_write_register(pcr, FPDCTL, SSC_POWER_DOWN, 0); + if (PCI_PID(pcr) == PID_5261) { + /* Gating real mcu clock */ + err = rtsx_pci_write_register(pcr, RTS5261_FW_CFG1, + RTS5261_MCU_CLOCK_GATING, 0); + err = rtsx_pci_write_register(pcr, RTS5261_REG_FPDCTL, + SSC_POWER_DOWN, 0); + } else { + err = rtsx_pci_write_register(pcr, FPDCTL, SSC_POWER_DOWN, 0); + } if (err < 0) return err; @@ -1283,7 +1301,12 @@ static int rtsx_pci_init_hw(struct rtsx_pcr *pcr) /* Enable SSC Clock */ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL1, 0xFF, SSC_8X_EN | SSC_SEL_4M); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL2, 0xFF, 0x12); + if (PCI_PID(pcr) == PID_5261) + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL2, 0xFF, + RTS5261_SSC_DEPTH_2M); + else + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL2, 0xFF, 0x12); + /* Disable cd_pwr_save */ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CHANGE_LINK_STATE, 0x16, 0x10); /* Clear Link Ready Interrupt */ @@ -1314,6 +1337,7 @@ static int rtsx_pci_init_hw(struct rtsx_pcr *pcr) case PID_524A: case PID_525A: case PID_5260: + case PID_5261: rtsx_pci_write_register(pcr, PM_CLK_FORCE_CTL, 1, 1); break; default: @@ -1393,9 +1417,14 @@ static int rtsx_pci_init_chip(struct rtsx_pcr *pcr) case 0x5286: rtl8402_init_params(pcr); break; + case 0x5260: rts5260_init_params(pcr); break; + + case 0x5261: + rts5261_init_params(pcr); + break; } pcr_dbg(pcr, "PID: 0x%04x, IC version: 0x%02x\n", diff --git a/drivers/misc/cardreader/rtsx_pcr.h b/drivers/misc/cardreader/rtsx_pcr.h index 98f729263dc1..ed391df52f4f 100644 --- a/drivers/misc/cardreader/rtsx_pcr.h +++ b/drivers/misc/cardreader/rtsx_pcr.h @@ -53,6 +53,7 @@ void rts524a_init_params(struct rtsx_pcr *pcr); void rts525a_init_params(struct rtsx_pcr *pcr); void rtl8411b_init_params(struct rtsx_pcr *pcr); void rts5260_init_params(struct rtsx_pcr *pcr); +void rts5261_init_params(struct rtsx_pcr *pcr); static inline u8 map_sd_drive(int idx) { diff --git a/include/linux/rtsx_pci.h b/include/linux/rtsx_pci.h index f87da30a58b1..65b8142a7fed 100644 --- a/include/linux/rtsx_pci.h +++ b/include/linux/rtsx_pci.h @@ -1262,6 +1262,7 @@ struct rtsx_pcr { #define PID_5250 0x5250 #define PID_525A 0x525A #define PID_5260 0x5260 +#define PID_5261 0x5261 #define CHK_PCI_PID(pcr, pid) ((pcr)->pci->device == (pid)) #define PCI_VID(pcr) ((pcr)->pci->vendor) From e9cb0497b1c801a66f9abc907b2c55241099e6ac Mon Sep 17 00:00:00 2001 From: Richard Gong Date: Mon, 4 Nov 2019 10:24:36 -0600 Subject: [PATCH 155/198] firmware: Fix incompatible function behavior for RSU driver The older versions of remote system update (RSU) firmware don't support retry and notify features then the kernel module dies when it queries the RSU retry counter or performs notify operation. Update the Intel service layer and RSU drivers to be compatible with all versions of RSU firmware. Reported-by: Radu Barcau Reported-by: kbuild test robot Signed-off-by: Richard Gong Link: https://lore.kernel.org/r/1572884676-1385-1-git-send-email-richard.gong@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/firmware/stratix10-rsu.c | 42 ++++++++----------- drivers/firmware/stratix10-svc.c | 18 +++++++- .../firmware/intel/stratix10-svc-client.h | 8 ++++ 3 files changed, 43 insertions(+), 25 deletions(-) diff --git a/drivers/firmware/stratix10-rsu.c b/drivers/firmware/stratix10-rsu.c index bb008c019920..f8533338b018 100644 --- a/drivers/firmware/stratix10-rsu.c +++ b/drivers/firmware/stratix10-rsu.c @@ -20,7 +20,6 @@ #define RSU_VERSION_MASK GENMASK_ULL(63, 32) #define RSU_ERROR_LOCATION_MASK GENMASK_ULL(31, 0) #define RSU_ERROR_DETAIL_MASK GENMASK_ULL(63, 32) -#define RSU_FW_VERSION_MASK GENMASK_ULL(15, 0) #define RSU_TIMEOUT (msecs_to_jiffies(SVC_RSU_REQUEST_TIMEOUT_MS)) @@ -109,9 +108,12 @@ static void rsu_command_callback(struct stratix10_svc_client *client, { struct stratix10_rsu_priv *priv = client->priv; - if (data->status != BIT(SVC_STATUS_RSU_OK)) - dev_err(client->dev, "RSU returned status is %i\n", - data->status); + if (data->status == BIT(SVC_STATUS_RSU_NO_SUPPORT)) + dev_warn(client->dev, "Secure FW doesn't support notify\n"); + else if (data->status == BIT(SVC_STATUS_RSU_ERROR)) + dev_err(client->dev, "Failure, returned status is %lu\n", + BIT(data->status)); + complete(&priv->completion); } @@ -133,9 +135,11 @@ static void rsu_retry_callback(struct stratix10_svc_client *client, if (data->status == BIT(SVC_STATUS_RSU_OK)) priv->retry_counter = *counter; + else if (data->status == BIT(SVC_STATUS_RSU_NO_SUPPORT)) + dev_warn(client->dev, "Secure FW doesn't support retry\n"); else - dev_err(client->dev, "Failed to get retry counter %i\n", - data->status); + dev_err(client->dev, "Failed to get retry counter %lu\n", + BIT(data->status)); complete(&priv->completion); } @@ -333,15 +337,10 @@ static ssize_t notify_store(struct device *dev, return ret; } - /* only 19.3 or late version FW supports retry counter feature */ - if (FIELD_GET(RSU_FW_VERSION_MASK, priv->status.version)) { - ret = rsu_send_msg(priv, COMMAND_RSU_RETRY, - 0, rsu_retry_callback); - if (ret) { - dev_err(dev, - "Error, getting RSU retry %i\n", ret); - return ret; - } + ret = rsu_send_msg(priv, COMMAND_RSU_RETRY, 0, rsu_retry_callback); + if (ret) { + dev_err(dev, "Error, getting RSU retry %i\n", ret); + return ret; } return count; @@ -413,15 +412,10 @@ static int stratix10_rsu_probe(struct platform_device *pdev) stratix10_svc_free_channel(priv->chan); } - /* only 19.3 or late version FW supports retry counter feature */ - if (FIELD_GET(RSU_FW_VERSION_MASK, priv->status.version)) { - ret = rsu_send_msg(priv, COMMAND_RSU_RETRY, 0, - rsu_retry_callback); - if (ret) { - dev_err(dev, - "Error, getting RSU retry %i\n", ret); - stratix10_svc_free_channel(priv->chan); - } + ret = rsu_send_msg(priv, COMMAND_RSU_RETRY, 0, rsu_retry_callback); + if (ret) { + dev_err(dev, "Error, getting RSU retry %i\n", ret); + stratix10_svc_free_channel(priv->chan); } return ret; diff --git a/drivers/firmware/stratix10-svc.c b/drivers/firmware/stratix10-svc.c index b485321189e1..c6c31402848d 100644 --- a/drivers/firmware/stratix10-svc.c +++ b/drivers/firmware/stratix10-svc.c @@ -493,8 +493,24 @@ static int svc_normal_to_secure_thread(void *data) pdata->chan->scl->receive_cb(pdata->chan->scl, cbdata); break; default: - pr_warn("it shouldn't happen\n"); + pr_warn("Secure firmware doesn't support...\n"); + + /* + * be compatible with older version firmware which + * doesn't support RSU notify or retry + */ + if ((pdata->command == COMMAND_RSU_RETRY) || + (pdata->command == COMMAND_RSU_NOTIFY)) { + cbdata->status = + BIT(SVC_STATUS_RSU_NO_SUPPORT); + cbdata->kaddr1 = NULL; + cbdata->kaddr2 = NULL; + cbdata->kaddr3 = NULL; + pdata->chan->scl->receive_cb( + pdata->chan->scl, cbdata); + } break; + } }; diff --git a/include/linux/firmware/intel/stratix10-svc-client.h b/include/linux/firmware/intel/stratix10-svc-client.h index b6c4302a39e0..59bc6e2af693 100644 --- a/include/linux/firmware/intel/stratix10-svc-client.h +++ b/include/linux/firmware/intel/stratix10-svc-client.h @@ -41,6 +41,12 @@ * * SVC_STATUS_RSU_OK: * Secure firmware accepts the request of remote status update (RSU). + * + * SVC_STATUS_RSU_ERROR: + * Error encountered during remote system update. + * + * SVC_STATUS_RSU_NO_SUPPORT: + * Secure firmware doesn't support RSU retry or notify feature. */ #define SVC_STATUS_RECONFIG_REQUEST_OK 0 #define SVC_STATUS_RECONFIG_BUFFER_SUBMITTED 1 @@ -50,6 +56,8 @@ #define SVC_STATUS_RECONFIG_ERROR 5 #define SVC_STATUS_RSU_OK 6 #define SVC_STATUS_RSU_ERROR 7 +#define SVC_STATUS_RSU_NO_SUPPORT 8 + /** * Flag bit for COMMAND_RECONFIG * From 0a8f72fafb3f72a08df4ee491fcbeaafd6de85fd Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Thu, 14 Nov 2019 08:42:00 +0200 Subject: [PATCH 156/198] stm class: Lose the protocol driver when dropping its reference Commit c7fd62bc69d02 ("stm class: Introduce framing protocol drivers") forgot to tear down the link between an stm device and its protocol driver when policy is removed. This leads to an invalid pointer reference if one tries to write to an stm device after the policy has been removed and the protocol driver module unloaded, leading to the below splat: > BUG: unable to handle page fault for address: ffffffffc0737068 > #PF: supervisor read access in kernel mode > #PF: error_code(0x0000) - not-present page > PGD 3d780f067 P4D 3d780f067 PUD 3d7811067 PMD 492781067 PTE 0 > Oops: 0000 [#1] SMP NOPTI > CPU: 1 PID: 26122 Comm: cat Not tainted 5.4.0-rc5+ #1 > RIP: 0010:stm_output_free+0x40/0xc0 [stm_core] > Call Trace: > stm_char_release+0x3e/0x70 [stm_core] > __fput+0xc6/0x260 > ____fput+0xe/0x10 > task_work_run+0x9d/0xc0 > exit_to_usermode_loop+0x103/0x110 > do_syscall_64+0x19d/0x1e0 > entry_SYSCALL_64_after_hwframe+0x44/0xa9 Fix this by tearing down the link from an stm device to its protocol driver when the policy involving that driver is removed. Signed-off-by: Alexander Shishkin Fixes: c7fd62bc69d02 ("stm class: Introduce framing protocol drivers") Reported-by: Ammy Yi Tested-by: Ammy Yi CC: stable@vger.kernel.org # v4.20+ Link: https://lore.kernel.org/r/20191114064201.43089-2-alexander.shishkin@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/stm/policy.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/hwtracing/stm/policy.c b/drivers/hwtracing/stm/policy.c index 4b9e44b227d8..4f932a419752 100644 --- a/drivers/hwtracing/stm/policy.c +++ b/drivers/hwtracing/stm/policy.c @@ -345,7 +345,11 @@ void stp_policy_unbind(struct stp_policy *policy) stm->policy = NULL; policy->stm = NULL; + /* + * Drop the reference on the protocol driver and lose the link. + */ stm_put_protocol(stm->pdrv); + stm->pdrv = NULL; stm_put_device(stm); } From 87ff16007cff64a36ef31f50d45b69211d88da1d Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Thu, 14 Nov 2019 08:42:01 +0200 Subject: [PATCH 157/198] intel_th: Document software sinks Add documentation for the software sinks API of the MSU driver and the msu-sink module in particular. Signed-off-by: Alexander Shishkin Link: https://lore.kernel.org/r/20191114064201.43089-3-alexander.shishkin@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- Documentation/trace/intel_th.rst | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/Documentation/trace/intel_th.rst b/Documentation/trace/intel_th.rst index baa12eb09ef4..70b7126eaeeb 100644 --- a/Documentation/trace/intel_th.rst +++ b/Documentation/trace/intel_th.rst @@ -44,7 +44,8 @@ Documentation/trace/stm.rst for more information on that. MSU can be configured to collect trace data into a system memory buffer, which can later on be read from its device nodes via read() or -mmap() interface. +mmap() interface and directed to a "software sink" driver that will +consume the data and/or relay it further. On the whole, Intel(R) Trace Hub does not require any special userspace software to function; everything can be configured, started @@ -122,3 +123,28 @@ In order to enable the host mode, set the 'host_mode' parameter of the will show up on the intel_th bus. Also, trace configuration and capture controlling attribute groups of the 'gth' device will not be exposed. The 'sth' device will operate as usual. + +Software Sinks +-------------- + +The Memory Storage Unit (MSU) driver provides an in-kernel API for +drivers to register themselves as software sinks for the trace data. +Such drivers can further export the data via other devices, such as +USB device controllers or network cards. + +The API has two main parts:: + - notifying the software sink that a particular window is full, and + "locking" that window, that is, making it unavailable for the trace + collection; when this happens, the MSU driver will automatically + switch to the next window in the buffer if it is unlocked, or stop + the trace capture if it's not; + - tracking the "locked" state of windows and providing a way for the + software sink driver to notify the MSU driver when a window is + unlocked and can be used again to collect trace data. + +An example sink driver, msu-sink illustrates the implementation of a +software sink. Functionally, it simply unlocks windows as soon as they +are full, keeping the MSU running in a circular buffer mode. Unlike the +"multi" mode, it will fill out all the windows in the buffer as opposed +to just the first one. It can be enabled by writing "sink" to the "mode" +file (assuming msu-sink.ko is loaded). From 64498695dd800bcb494d96b94f99356ece91ea90 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sat, 16 Nov 2019 16:21:36 +0200 Subject: [PATCH 158/198] mei: bus: add more client attributes to sysfs Export more client attributes via sysfs that are usually obtained upon connection. In some cases, for example a monitoring application may wish to know the attributes without actually performing the connection. Added attributes: max number of connections, fixed address, max message length. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Link: https://lore.kernel.org/r/20191116142136.17535-1-tomas.winkler@intel.com Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-bus-mei | 21 +++++++++++++++ drivers/misc/mei/bus.c | 33 +++++++++++++++++++++++ drivers/misc/mei/client.h | 36 +++++++++++++++++++++++++ 3 files changed, 90 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-bus-mei b/Documentation/ABI/testing/sysfs-bus-mei index 3f8701e8fa24..3d37e2796d5a 100644 --- a/Documentation/ABI/testing/sysfs-bus-mei +++ b/Documentation/ABI/testing/sysfs-bus-mei @@ -26,3 +26,24 @@ KernelVersion: 4.3 Contact: Tomas Winkler Description: Stores mei client protocol version Format: %d + +What: /sys/bus/mei/devices/.../max_conn +Date: Nov 2019 +KernelVersion: 5.5 +Contact: Tomas Winkler +Description: Stores mei client maximum number of connections + Format: %d + +What: /sys/bus/mei/devices/.../fixed +Date: Nov 2019 +KernelVersion: 5.5 +Contact: Tomas Winkler +Description: Stores mei client fixed address, if any + Format: %d + +What: /sys/bus/mei/devices/.../max_len +Date: Nov 2019 +KernelVersion: 5.5 +Contact: Tomas Winkler +Description: Stores mei client maximum message length + Format: %d diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 53bb394ccba6..a0a495c95e3c 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -791,11 +791,44 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *a, } static DEVICE_ATTR_RO(modalias); +static ssize_t max_conn_show(struct device *dev, struct device_attribute *a, + char *buf) +{ + struct mei_cl_device *cldev = to_mei_cl_device(dev); + u8 maxconn = mei_me_cl_max_conn(cldev->me_cl); + + return scnprintf(buf, PAGE_SIZE, "%d", maxconn); +} +static DEVICE_ATTR_RO(max_conn); + +static ssize_t fixed_show(struct device *dev, struct device_attribute *a, + char *buf) +{ + struct mei_cl_device *cldev = to_mei_cl_device(dev); + u8 fixed = mei_me_cl_fixed(cldev->me_cl); + + return scnprintf(buf, PAGE_SIZE, "%d", fixed); +} +static DEVICE_ATTR_RO(fixed); + +static ssize_t max_len_show(struct device *dev, struct device_attribute *a, + char *buf) +{ + struct mei_cl_device *cldev = to_mei_cl_device(dev); + u32 maxlen = mei_me_cl_max_len(cldev->me_cl); + + return scnprintf(buf, PAGE_SIZE, "%u", maxlen); +} +static DEVICE_ATTR_RO(max_len); + static struct attribute *mei_cldev_attrs[] = { &dev_attr_name.attr, &dev_attr_uuid.attr, &dev_attr_version.attr, &dev_attr_modalias.attr, + &dev_attr_max_conn.attr, + &dev_attr_fixed.attr, + &dev_attr_max_len.attr, NULL, }; ATTRIBUTE_GROUPS(mei_cldev); diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index c1f9e810cf81..2f8954def591 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -69,6 +69,42 @@ static inline u8 mei_me_cl_ver(const struct mei_me_client *me_cl) return me_cl->props.protocol_version; } +/** + * mei_me_cl_max_conn - return me client max number of connections + * + * @me_cl: me client + * + * Return: me client max number of connections + */ +static inline u8 mei_me_cl_max_conn(const struct mei_me_client *me_cl) +{ + return me_cl->props.max_number_of_connections; +} + +/** + * mei_me_cl_fixed - return me client fixed address, if any + * + * @me_cl: me client + * + * Return: me client fixed address + */ +static inline u8 mei_me_cl_fixed(const struct mei_me_client *me_cl) +{ + return me_cl->props.fixed_address; +} + +/** + * mei_me_cl_max_len - return me client max msg length + * + * @me_cl: me client + * + * Return: me client max msg length + */ +static inline u32 mei_me_cl_max_len(const struct mei_me_client *me_cl) +{ + return me_cl->props.max_msg_length; +} + /* * MEI IO Functions */ From 7a7ebfa85f4fac349f3ab219538c44efe18b0cf6 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 11 Nov 2019 13:25:44 +0300 Subject: [PATCH 159/198] thunderbolt: Power cycle the router if NVM authentication fails On zang's Dell XPS 13 9370 after Thunderbolt NVM firmware upgrade the Thunderbolt controller did not come back as expected. Only after the system was rebooted it became available again. It is not entirely clear what happened but I suspect the new NVM firmware image authentication failed for some reason. Regardless of this the router needs to be power cycled if NVM authentication fails in order to get it fully functional again. This modifies the driver to issue a power cycle in case the NVM authentication fails immediately when dma_port_flash_update_auth() returns. We also need to call tb_switch_set_uuid() earlier to be able to fetch possible NVM authentication failure when DMA port is added. Link: https://bugzilla.kernel.org/show_bug.cgi?id=205457 Reported-by: zang Cc: stable Signed-off-by: Mika Westerberg Signed-off-by: Greg Kroah-Hartman --- drivers/thunderbolt/switch.c | 54 ++++++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index 3f477df2730a..ca86a8e09c77 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -168,7 +168,7 @@ static int nvm_validate_and_write(struct tb_switch *sw) static int nvm_authenticate_host(struct tb_switch *sw) { - int ret; + int ret = 0; /* * Root switch NVM upgrade requires that we disconnect the @@ -176,6 +176,8 @@ static int nvm_authenticate_host(struct tb_switch *sw) * already). */ if (!sw->safe_mode) { + u32 status; + ret = tb_domain_disconnect_all_paths(sw->tb); if (ret) return ret; @@ -184,7 +186,16 @@ static int nvm_authenticate_host(struct tb_switch *sw) * everything goes well so getting timeout is expected. */ ret = dma_port_flash_update_auth(sw->dma_port); - return ret == -ETIMEDOUT ? 0 : ret; + if (!ret || ret == -ETIMEDOUT) + return 0; + + /* + * Any error from update auth operation requires power + * cycling of the host router. + */ + tb_sw_warn(sw, "failed to authenticate NVM, power cycling\n"); + if (dma_port_flash_update_auth_status(sw->dma_port, &status) > 0) + nvm_set_auth_status(sw, status); } /* @@ -192,7 +203,7 @@ static int nvm_authenticate_host(struct tb_switch *sw) * switch. */ dma_port_power_cycle(sw->dma_port); - return 0; + return ret; } static int nvm_authenticate_device(struct tb_switch *sw) @@ -200,8 +211,16 @@ static int nvm_authenticate_device(struct tb_switch *sw) int ret, retries = 10; ret = dma_port_flash_update_auth(sw->dma_port); - if (ret && ret != -ETIMEDOUT) + switch (ret) { + case 0: + case -ETIMEDOUT: + case -EACCES: + case -EINVAL: + /* Power cycle is required */ + break; + default: return ret; + } /* * Poll here for the authentication status. It takes some time @@ -1420,8 +1439,6 @@ static ssize_t nvm_authenticate_store(struct device *dev, */ nvm_authenticate_start(sw); ret = nvm_authenticate_host(sw); - if (ret) - nvm_authenticate_complete(sw); } else { ret = nvm_authenticate_device(sw); } @@ -1876,13 +1893,16 @@ static int tb_switch_add_dma_port(struct tb_switch *sw) int ret; switch (sw->generation) { - case 3: - break; - case 2: /* Only root switch can be upgraded */ if (tb_route(sw)) return 0; + + /* fallthrough */ + case 3: + ret = tb_switch_set_uuid(sw); + if (ret) + return ret; break; default: @@ -1906,6 +1926,19 @@ static int tb_switch_add_dma_port(struct tb_switch *sw) if (sw->no_nvm_upgrade) return 0; + /* + * If there is status already set then authentication failed + * when the dma_port_flash_update_auth() returned. Power cycling + * is not needed (it was done already) so only thing we do here + * is to unblock runtime PM of the root port. + */ + nvm_get_auth_status(sw, &status); + if (status) { + if (!tb_route(sw)) + nvm_authenticate_complete(sw); + return 0; + } + /* * Check status of the previous flash authentication. If there * is one we need to power cycle the switch in any case to make @@ -1921,9 +1954,6 @@ static int tb_switch_add_dma_port(struct tb_switch *sw) if (status) { tb_sw_info(sw, "switch flash authentication failed\n"); - ret = tb_switch_set_uuid(sw); - if (ret) - return ret; nvm_set_auth_status(sw, status); } From 9def5dc10d3e21cf7673731be21224b6cc0eb16a Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Mon, 18 Nov 2019 11:52:06 -0700 Subject: [PATCH 160/198] coresight: funnel: Fix missing spin_lock_init() The driver allocates the spinlock but not initialize it. Use spin_lock_init() on it to initialize it correctly. This is detected by Coccinelle semantic patch. Signed-off-by: Wei Yongjun Tested-by: Yabin Cui Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20191118185207.30441-2-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-funnel.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwtracing/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c index b605889b507a..900690a9f7f0 100644 --- a/drivers/hwtracing/coresight/coresight-funnel.c +++ b/drivers/hwtracing/coresight/coresight-funnel.c @@ -253,6 +253,7 @@ static int funnel_probe(struct device *dev, struct resource *res) } dev->platform_data = pdata; + spin_lock_init(&drvdata->spinlock); desc.type = CORESIGHT_DEV_TYPE_LINK; desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_MERG; desc.ops = &funnel_cs_ops; From 9a5d847c0bdadb67892b7d176a028578aaed0c3a Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Mon, 18 Nov 2019 11:52:07 -0700 Subject: [PATCH 161/198] coresight: replicator: Fix missing spin_lock_init() The driver allocates the spinlock but not initialize it. Use spin_lock_init() on it to initialize it correctly. This is detected by Coccinelle semantic patch. Signed-off-by: Wei Yongjun Tested-by: Yabin Cui Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20191118185207.30441-3-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-replicator.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwtracing/coresight/coresight-replicator.c b/drivers/hwtracing/coresight/coresight-replicator.c index 43304196a1a6..e7dc1c31d20d 100644 --- a/drivers/hwtracing/coresight/coresight-replicator.c +++ b/drivers/hwtracing/coresight/coresight-replicator.c @@ -248,6 +248,7 @@ static int replicator_probe(struct device *dev, struct resource *res) } dev->platform_data = pdata; + spin_lock_init(&drvdata->spinlock); desc.type = CORESIGHT_DEV_TYPE_LINK; desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_SPLIT; desc.ops = &replicator_cs_ops; From 512592779a337feb5905d8fcf9498dbf33672d4a Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Wed, 20 Nov 2019 15:08:04 +0200 Subject: [PATCH 162/198] intel_th: Fix a double put_device() in error path Commit a753bfcfdb1f ("intel_th: Make the switch allocate its subdevices") factored out intel_th_subdevice_alloc() from intel_th_populate(), but got the error path wrong, resulting in two instances of a double put_device() on a freshly initialized, but not 'added' device. Fix this by only doing one put_device() in the error path. Signed-off-by: Alexander Shishkin Fixes: a753bfcfdb1f ("intel_th: Make the switch allocate its subdevices") Reported-by: Wen Yang Reviewed-by: Andy Shevchenko Cc: stable@vger.kernel.org # v4.14+ Link: https://lore.kernel.org/r/20191120130806.44028-2-alexander.shishkin@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/intel_th/core.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/hwtracing/intel_th/core.c b/drivers/hwtracing/intel_th/core.c index d5c1821b31c6..0dfd97bbde9e 100644 --- a/drivers/hwtracing/intel_th/core.c +++ b/drivers/hwtracing/intel_th/core.c @@ -649,10 +649,8 @@ intel_th_subdevice_alloc(struct intel_th *th, } err = intel_th_device_add_resources(thdev, res, subdev->nres); - if (err) { - put_device(&thdev->dev); + if (err) goto fail_put_device; - } if (subdev->type == INTEL_TH_OUTPUT) { if (subdev->mknode) @@ -667,10 +665,8 @@ intel_th_subdevice_alloc(struct intel_th *th, } err = device_add(&thdev->dev); - if (err) { - put_device(&thdev->dev); + if (err) goto fail_free_res; - } /* need switch driver to be loaded to enumerate the rest */ if (subdev->type == INTEL_TH_SWITCH && !req) { From 6a1743422a7c0fda26764a544136cac13e5ae486 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Wed, 20 Nov 2019 15:08:05 +0200 Subject: [PATCH 163/198] intel_th: pci: Add Ice Lake CPU support This adds support for the Trace Hub in Ice Lake CPU. Signed-off-by: Alexander Shishkin Reviewed-by: Andy Shevchenko Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20191120130806.44028-3-alexander.shishkin@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/intel_th/pci.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/hwtracing/intel_th/pci.c b/drivers/hwtracing/intel_th/pci.c index 03ca5b1bef9f..845b13d8ad6d 100644 --- a/drivers/hwtracing/intel_th/pci.c +++ b/drivers/hwtracing/intel_th/pci.c @@ -209,6 +209,11 @@ static const struct pci_device_id intel_th_pci_id_table[] = { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x45c5), .driver_data = (kernel_ulong_t)&intel_th_2x, }, + { + /* Ice Lake CPU */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x8a29), + .driver_data = (kernel_ulong_t)&intel_th_2x, + }, { /* Tiger Lake PCH */ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xa0a6), From 6e6c18bcb78c0dc0601ebe216bed12c844492d0c Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Wed, 20 Nov 2019 15:08:06 +0200 Subject: [PATCH 164/198] intel_th: pci: Add Tiger Lake CPU support This adds support for the Trace Hub in Tiger Lake CPU. Signed-off-by: Alexander Shishkin Reviewed-by: Andy Shevchenko Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20191120130806.44028-4-alexander.shishkin@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/intel_th/pci.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/hwtracing/intel_th/pci.c b/drivers/hwtracing/intel_th/pci.c index 845b13d8ad6d..ebf3e30e989a 100644 --- a/drivers/hwtracing/intel_th/pci.c +++ b/drivers/hwtracing/intel_th/pci.c @@ -214,6 +214,11 @@ static const struct pci_device_id intel_th_pci_id_table[] = { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x8a29), .driver_data = (kernel_ulong_t)&intel_th_2x, }, + { + /* Tiger Lake CPU */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x9a33), + .driver_data = (kernel_ulong_t)&intel_th_2x, + }, { /* Tiger Lake PCH */ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xa0a6), From c8175bd155c5c8e20008787148f8303ab20885b8 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 20 Nov 2019 21:42:47 +0800 Subject: [PATCH 165/198] char: Fix Kconfig indentation Adjust indentation from spaces to tab (+optional two spaces) as in coding style with command like: $ sed -e 's/^ /\t/' -i */Kconfig Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20191120134247.16073-1-krzk@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/char/Kconfig | 6 +++--- drivers/char/agp/Kconfig | 2 +- drivers/char/hw_random/Kconfig | 10 +++++----- drivers/char/ipmi/Kconfig | 20 ++++++++++---------- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index df0fc997dc3e..26956c006987 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -439,8 +439,8 @@ config RAW_DRIVER Once bound, I/O against /dev/raw/rawN uses efficient zero-copy I/O. See the raw(8) manpage for more details. - Applications should preferably open the device (eg /dev/hda1) - with the O_DIRECT flag. + Applications should preferably open the device (eg /dev/hda1) + with the O_DIRECT flag. config MAX_RAW_DEVS int "Maximum number of RAW devices to support (1-65536)" @@ -559,4 +559,4 @@ config RANDOM_TRUST_BOOTLOADER device randomness. Say Y here to assume the entropy provided by the booloader is trustworthy so it will be added to the kernel's entropy pool. Otherwise, say N here so it will be regarded as device input that - only mixes the entropy pool. \ No newline at end of file + only mixes the entropy pool. diff --git a/drivers/char/agp/Kconfig b/drivers/char/agp/Kconfig index 812d6aa6e013..bc54235a7022 100644 --- a/drivers/char/agp/Kconfig +++ b/drivers/char/agp/Kconfig @@ -63,7 +63,7 @@ config AGP_AMD64 This option gives you AGP support for the GLX component of X using the on-CPU northbridge of the AMD Athlon64/Opteron CPUs. You still need an external AGP bridge like the AMD 8151, VIA - K8T400M, SiS755. It may also support other AGP bridges when loaded + K8T400M, SiS755. It may also support other AGP bridges when loaded with agp_try_unsupported=1. config AGP_INTEL diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig index 59f25286befe..5011eb673ab4 100644 --- a/drivers/char/hw_random/Kconfig +++ b/drivers/char/hw_random/Kconfig @@ -377,13 +377,13 @@ config HW_RANDOM_CAVIUM depends on HW_RANDOM && PCI && (ARM64 || (COMPILE_TEST && 64BIT)) default HW_RANDOM ---help--- - This driver provides kernel-side support for the Random Number - Generator hardware found on Cavium SoCs. + This driver provides kernel-side support for the Random Number + Generator hardware found on Cavium SoCs. - To compile this driver as a module, choose M here: the - module will be called cavium_rng. + To compile this driver as a module, choose M here: the + module will be called cavium_rng. - If unsure, say Y. + If unsure, say Y. config HW_RANDOM_MTK tristate "Mediatek Random Number Generator support" diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig index 4bad0614109b..cc4bea773ded 100644 --- a/drivers/char/ipmi/Kconfig +++ b/drivers/char/ipmi/Kconfig @@ -8,13 +8,13 @@ menuconfig IPMI_HANDLER depends on HAS_IOMEM select IPMI_DMI_DECODE if DMI help - This enables the central IPMI message handler, required for IPMI + This enables the central IPMI message handler, required for IPMI to work. - IPMI is a standard for managing sensors (temperature, - voltage, etc.) in a system. + IPMI is a standard for managing sensors (temperature, + voltage, etc.) in a system. - See for more details on the driver. + See for more details on the driver. If unsure, say N. @@ -56,14 +56,14 @@ config IPMI_PANIC_STRING config IPMI_DEVICE_INTERFACE tristate 'Device interface for IPMI' help - This provides an IOCTL interface to the IPMI message handler so + This provides an IOCTL interface to the IPMI message handler so userland processes may use IPMI. It supports poll() and select(). config IPMI_SI tristate 'IPMI System Interface handler' select IPMI_PLAT_DATA help - Provides a driver for System Interfaces (KCS, SMIC, BT). + Provides a driver for System Interfaces (KCS, SMIC, BT). Currently, only KCS and SMIC are supported. If you are using IPMI, you should probably say "y" here. @@ -71,7 +71,7 @@ config IPMI_SSIF tristate 'IPMI SMBus handler (SSIF)' select I2C help - Provides a driver for a SMBus interface to a BMC, meaning that you + Provides a driver for a SMBus interface to a BMC, meaning that you have a driver that must be accessed over an I2C bus instead of a standard interface. This module requires I2C support. @@ -79,17 +79,17 @@ config IPMI_POWERNV depends on PPC_POWERNV tristate 'POWERNV (OPAL firmware) IPMI interface' help - Provides a driver for OPAL firmware-based IPMI interfaces. + Provides a driver for OPAL firmware-based IPMI interfaces. config IPMI_WATCHDOG tristate 'IPMI Watchdog Timer' help - This enables the IPMI watchdog timer. + This enables the IPMI watchdog timer. config IPMI_POWEROFF tristate 'IPMI Poweroff' help - This enables a function to power off the system with IPMI if + This enables a function to power off the system with IPMI if the IPMI management controller is capable of this. endif # IPMI_HANDLER From ab64ec1db25e0cceab0bad15b03fd57e2b461b15 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 20 Nov 2019 21:40:56 +0800 Subject: [PATCH 166/198] misc: Fix Kconfig indentation Adjust indentation from spaces to tab (+optional two spaces) as in coding style with command like: $ sed -e 's/^ /\t/' -i */Kconfig Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20191120134056.14677-1-krzk@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/Kconfig | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 2fefecef6e06..7f0d48f406e3 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -325,14 +325,14 @@ config SENSORS_TSL2550 will be called tsl2550. config SENSORS_BH1770 - tristate "BH1770GLC / SFH7770 combined ALS - Proximity sensor" - depends on I2C - ---help--- - Say Y here if you want to build a driver for BH1770GLC (ROHM) or + tristate "BH1770GLC / SFH7770 combined ALS - Proximity sensor" + depends on I2C + ---help--- + Say Y here if you want to build a driver for BH1770GLC (ROHM) or SFH7770 (Osram) combined ambient light and proximity sensor chip. - To compile this driver as a module, choose M here: the - module will be called bh1770glc. If unsure, say N here. + To compile this driver as a module, choose M here: the + module will be called bh1770glc. If unsure, say N here. config SENSORS_APDS990X tristate "APDS990X combined als and proximity sensors" @@ -437,8 +437,8 @@ config PCI_ENDPOINT_TEST select CRC32 tristate "PCI Endpoint Test driver" ---help--- - Enable this configuration option to enable the host side test driver - for PCI Endpoint. + Enable this configuration option to enable the host side test driver + for PCI Endpoint. config XILINX_SDFEC tristate "Xilinx SDFEC 16" From abb7e16fb6dfc32d0c0e63787409fdb4c348915c Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Mon, 16 Sep 2019 09:16:29 +0300 Subject: [PATCH 167/198] habanalabs: handle F/W failure for sensor initialization In case the F/W fails to initialize the thermal sensors, print an appropriate error message to kernel log and fail the device initialization. Reviewed-by: Tomer Tayar Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/goya/goya.c | 5 +++++ drivers/misc/habanalabs/include/hl_boot_if.h | 2 ++ 2 files changed, 7 insertions(+) diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index 6fba14b81f90..09caef7642fd 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -2328,6 +2328,11 @@ static int goya_init_cpu(struct hl_device *hdev, u32 cpu_timeout) "ARM status %d - u-boot stopped by user\n", status); break; + case CPU_BOOT_STATUS_TS_INIT_FAIL: + dev_err(hdev->dev, + "ARM status %d - Thermal Sensor initialization failed\n", + status); + break; default: dev_err(hdev->dev, "ARM status %d - Invalid status code\n", diff --git a/drivers/misc/habanalabs/include/hl_boot_if.h b/drivers/misc/habanalabs/include/hl_boot_if.h index 4cd04c090285..2853a2de8cf6 100644 --- a/drivers/misc/habanalabs/include/hl_boot_if.h +++ b/drivers/misc/habanalabs/include/hl_boot_if.h @@ -20,6 +20,8 @@ enum cpu_boot_status { CPU_BOOT_STATUS_DRAM_INIT_FAIL, CPU_BOOT_STATUS_FIT_CORRUPTED, CPU_BOOT_STATUS_UBOOT_NOT_READY, + CPU_BOOT_STATUS_RESERVED, + CPU_BOOT_STATUS_TS_INIT_FAIL, }; enum kmd_msg { From 1e295d4dd5b2e3d9dab68bfcab1f5666cde3804d Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Sat, 28 Sep 2019 12:18:04 +0800 Subject: [PATCH 168/198] habanalabs: remove set but not used variable 'ctx' Fixes gcc '-Wunused-but-set-variable' warning: drivers/misc/habanalabs/device.c: In function hpriv_release: drivers/misc/habanalabs/device.c:45:17: warning: variable ctx set but not used [-Wunused-but-set-variable] It is never used since commit eb7caf84b029 ("habanalabs: maintain a list of file private data objects") Reported-by: Hulk Robot Signed-off-by: YueHaibing Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/device.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/misc/habanalabs/device.c b/drivers/misc/habanalabs/device.c index 459fee70a597..2f5a4da707e7 100644 --- a/drivers/misc/habanalabs/device.c +++ b/drivers/misc/habanalabs/device.c @@ -42,12 +42,10 @@ static void hpriv_release(struct kref *ref) { struct hl_fpriv *hpriv; struct hl_device *hdev; - struct hl_ctx *ctx; hpriv = container_of(ref, struct hl_fpriv, refcount); hdev = hpriv->hdev; - ctx = hpriv->ctx; put_pid(hpriv->taskpid); From f435614ff55c6783919028cb914ffd7422e0b03b Mon Sep 17 00:00:00 2001 From: Tomer Tayar Date: Wed, 2 Oct 2019 13:53:52 +0000 Subject: [PATCH 169/198] habanalabs: Fix typos s/paerser/parser/ s/requeusted/requested/ s/an JOB/a JOB/ Signed-off-by: Tomer Tayar Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/habanalabs.h | 2 +- drivers/misc/habanalabs/hw_queue.c | 4 ++-- include/uapi/misc/habanalabs.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/misc/habanalabs/habanalabs.h b/drivers/misc/habanalabs/habanalabs.h index 75862be53c60..c3d24ffad9fa 100644 --- a/drivers/misc/habanalabs/habanalabs.h +++ b/drivers/misc/habanalabs/habanalabs.h @@ -774,7 +774,7 @@ struct hl_cs_job { }; /** - * struct hl_cs_parser - command submission paerser properties. + * struct hl_cs_parser - command submission parser properties. * @user_cb: the CB we got from the user. * @patched_cb: in case of patching, this is internal CB which is submitted on * the queue instead of the CB we got from the IOCTL. diff --git a/drivers/misc/habanalabs/hw_queue.c b/drivers/misc/habanalabs/hw_queue.c index 55b383b2a116..f733b534f738 100644 --- a/drivers/misc/habanalabs/hw_queue.c +++ b/drivers/misc/habanalabs/hw_queue.c @@ -220,7 +220,7 @@ out: } /* - * ext_hw_queue_schedule_job - submit an JOB to an external queue + * ext_hw_queue_schedule_job - submit a JOB to an external queue * * @job: pointer to the job that needs to be submitted to the queue * @@ -278,7 +278,7 @@ static void ext_hw_queue_schedule_job(struct hl_cs_job *job) } /* - * int_hw_queue_schedule_job - submit an JOB to an internal queue + * int_hw_queue_schedule_job - submit a JOB to an internal queue * * @job: pointer to the job that needs to be submitted to the queue * diff --git a/include/uapi/misc/habanalabs.h b/include/uapi/misc/habanalabs.h index 39c4ea51a719..53e4ff73578e 100644 --- a/include/uapi/misc/habanalabs.h +++ b/include/uapi/misc/habanalabs.h @@ -589,7 +589,7 @@ struct hl_debug_args { * * The user can call this IOCTL with a handle it received from the CS IOCTL * to wait until the handle's CS has finished executing. The user will wait - * inside the kernel until the CS has finished or until the user-requeusted + * inside the kernel until the CS has finished or until the user-requested * timeout has expired. * * The return value of the IOCTL is a standard Linux error code. The possible From df762375f17e1765bc3a0b345378e1726d85ca75 Mon Sep 17 00:00:00 2001 From: Tomer Tayar Date: Thu, 3 Oct 2019 15:22:35 +0000 Subject: [PATCH 170/198] habanalabs: Mark queue as expecting CB handle or address Jobs on some queues must be provided with a handle to a driver command buffer object, while for other queues, jobs must be provided with an address to a command buffer. Currently the distinction is done based on the queue type, which is less flexible if the same queue type behaves differently on different types of ASICs. This patch adds a new queue property for this target, which is configured per queue type per ASIC type. Signed-off-by: Tomer Tayar Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/command_submission.c | 4 +++- drivers/misc/habanalabs/goya/goya.c | 3 +++ drivers/misc/habanalabs/habanalabs.h | 3 +++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/misc/habanalabs/command_submission.c b/drivers/misc/habanalabs/command_submission.c index a9ac045dcfde..f44205540520 100644 --- a/drivers/misc/habanalabs/command_submission.c +++ b/drivers/misc/habanalabs/command_submission.c @@ -414,7 +414,9 @@ static struct hl_cb *validate_queue_index(struct hl_device *hdev, "Queue index %d is restricted for the kernel driver\n", chunk->queue_index); return NULL; - } else if (hw_queue_prop->type == QUEUE_TYPE_INT) { + } + + if (!hw_queue_prop->requires_kernel_cb) { *ext_queue = false; return (struct hl_cb *) (uintptr_t) chunk->cb_handle; } diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index 09caef7642fd..71693fcffb16 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -337,17 +337,20 @@ void goya_get_fixed_properties(struct hl_device *hdev) for (i = 0 ; i < NUMBER_OF_EXT_HW_QUEUES ; i++) { prop->hw_queues_props[i].type = QUEUE_TYPE_EXT; prop->hw_queues_props[i].driver_only = 0; + prop->hw_queues_props[i].requires_kernel_cb = 1; } for (; i < NUMBER_OF_EXT_HW_QUEUES + NUMBER_OF_CPU_HW_QUEUES ; i++) { prop->hw_queues_props[i].type = QUEUE_TYPE_CPU; prop->hw_queues_props[i].driver_only = 1; + prop->hw_queues_props[i].requires_kernel_cb = 0; } for (; i < NUMBER_OF_EXT_HW_QUEUES + NUMBER_OF_CPU_HW_QUEUES + NUMBER_OF_INT_HW_QUEUES; i++) { prop->hw_queues_props[i].type = QUEUE_TYPE_INT; prop->hw_queues_props[i].driver_only = 0; + prop->hw_queues_props[i].requires_kernel_cb = 0; } for (; i < HL_MAX_QUEUES; i++) diff --git a/drivers/misc/habanalabs/habanalabs.h b/drivers/misc/habanalabs/habanalabs.h index c3d24ffad9fa..f47f4b22cb6b 100644 --- a/drivers/misc/habanalabs/habanalabs.h +++ b/drivers/misc/habanalabs/habanalabs.h @@ -98,10 +98,13 @@ enum hl_queue_type { * @type: queue type. * @driver_only: true if only the driver is allowed to send a job to this queue, * false otherwise. + * @requires_kernel_cb: true if a CB handle must be provided for jobs on this + * queue, false otherwise (a CB address must be provided). */ struct hw_queue_properties { enum hl_queue_type type; u8 driver_only; + u8 requires_kernel_cb; }; /** From cb596aee8842c87605ea1a9062af2ab435a742d4 Mon Sep 17 00:00:00 2001 From: Tomer Tayar Date: Thu, 3 Oct 2019 15:22:36 +0000 Subject: [PATCH 171/198] habanalabs: Add a new H/W queue type This patch adds a support for a new H/W queue type. This type of queue is for DMA and compute engines jobs, for which completion notification are sent by H/W. Command buffer for this queue can be created either through the CB IOCTL and using the retrieved CB handle, or by preparing a buffer on the host or device SRAM/DRAM, and using the device address to that buffer. The patch includes the handling of the 2 options, as well as the initialization of the H/W queue and its jobs scheduling. Signed-off-by: Tomer Tayar Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/command_submission.c | 120 ++++++--- drivers/misc/habanalabs/goya/goya.c | 4 +- drivers/misc/habanalabs/habanalabs.h | 24 +- drivers/misc/habanalabs/hw_queue.c | 249 +++++++++++++++---- drivers/misc/habanalabs/include/qman_if.h | 12 + 5 files changed, 308 insertions(+), 101 deletions(-) diff --git a/drivers/misc/habanalabs/command_submission.c b/drivers/misc/habanalabs/command_submission.c index f44205540520..776ddafc47fb 100644 --- a/drivers/misc/habanalabs/command_submission.c +++ b/drivers/misc/habanalabs/command_submission.c @@ -65,6 +65,18 @@ static void cs_put(struct hl_cs *cs) kref_put(&cs->refcount, cs_do_release); } +static bool is_cb_patched(struct hl_device *hdev, struct hl_cs_job *job) +{ + /* + * Patched CB is created for external queues jobs, and for H/W queues + * jobs if the user CB was allocated by driver and MMU is disabled. + */ + return (job->queue_type == QUEUE_TYPE_EXT || + (job->queue_type == QUEUE_TYPE_HW && + job->is_kernel_allocated_cb && + !hdev->mmu_enable)); +} + /* * cs_parser - parse the user command submission * @@ -91,11 +103,13 @@ static int cs_parser(struct hl_fpriv *hpriv, struct hl_cs_job *job) parser.patched_cb = NULL; parser.user_cb = job->user_cb; parser.user_cb_size = job->user_cb_size; - parser.ext_queue = job->ext_queue; + parser.queue_type = job->queue_type; + parser.is_kernel_allocated_cb = job->is_kernel_allocated_cb; job->patched_cb = NULL; rc = hdev->asic_funcs->cs_parser(hdev, &parser); - if (job->ext_queue) { + + if (is_cb_patched(hdev, job)) { if (!rc) { job->patched_cb = parser.patched_cb; job->job_cb_size = parser.patched_cb_size; @@ -124,7 +138,7 @@ static void free_job(struct hl_device *hdev, struct hl_cs_job *job) { struct hl_cs *cs = job->cs; - if (job->ext_queue) { + if (is_cb_patched(hdev, job)) { hl_userptr_delete_list(hdev, &job->userptr_list); /* @@ -140,6 +154,19 @@ static void free_job(struct hl_device *hdev, struct hl_cs_job *job) } } + /* For H/W queue jobs, if a user CB was allocated by driver and MMU is + * enabled, the user CB isn't released in cs_parser() and thus should be + * released here. + */ + if (job->queue_type == QUEUE_TYPE_HW && + job->is_kernel_allocated_cb && hdev->mmu_enable) { + spin_lock(&job->user_cb->lock); + job->user_cb->cs_cnt--; + spin_unlock(&job->user_cb->lock); + + hl_cb_put(job->user_cb); + } + /* * This is the only place where there can be multiple threads * modifying the list at the same time @@ -150,7 +177,8 @@ static void free_job(struct hl_device *hdev, struct hl_cs_job *job) hl_debugfs_remove_job(hdev, job); - if (job->ext_queue) + if (job->queue_type == QUEUE_TYPE_EXT || + job->queue_type == QUEUE_TYPE_HW) cs_put(cs); kfree(job); @@ -387,18 +415,13 @@ static void job_wq_completion(struct work_struct *work) free_job(hdev, job); } -static struct hl_cb *validate_queue_index(struct hl_device *hdev, - struct hl_cb_mgr *cb_mgr, - struct hl_cs_chunk *chunk, - bool *ext_queue) +static int validate_queue_index(struct hl_device *hdev, + struct hl_cs_chunk *chunk, + enum hl_queue_type *queue_type, + bool *is_kernel_allocated_cb) { struct asic_fixed_properties *asic = &hdev->asic_prop; struct hw_queue_properties *hw_queue_prop; - u32 cb_handle; - struct hl_cb *cb; - - /* Assume external queue */ - *ext_queue = true; hw_queue_prop = &asic->hw_queues_props[chunk->queue_index]; @@ -406,22 +429,29 @@ static struct hl_cb *validate_queue_index(struct hl_device *hdev, (hw_queue_prop->type == QUEUE_TYPE_NA)) { dev_err(hdev->dev, "Queue index %d is invalid\n", chunk->queue_index); - return NULL; + return -EINVAL; } if (hw_queue_prop->driver_only) { dev_err(hdev->dev, "Queue index %d is restricted for the kernel driver\n", chunk->queue_index); - return NULL; + return -EINVAL; } - if (!hw_queue_prop->requires_kernel_cb) { - *ext_queue = false; - return (struct hl_cb *) (uintptr_t) chunk->cb_handle; - } + *queue_type = hw_queue_prop->type; + *is_kernel_allocated_cb = !!hw_queue_prop->requires_kernel_cb; + + return 0; +} + +static struct hl_cb *get_cb_from_cs_chunk(struct hl_device *hdev, + struct hl_cb_mgr *cb_mgr, + struct hl_cs_chunk *chunk) +{ + struct hl_cb *cb; + u32 cb_handle; - /* Retrieve CB object */ cb_handle = (u32) (chunk->cb_handle >> PAGE_SHIFT); cb = hl_cb_get(hdev, cb_mgr, cb_handle); @@ -446,7 +476,8 @@ release_cb: return NULL; } -struct hl_cs_job *hl_cs_allocate_job(struct hl_device *hdev, bool ext_queue) +struct hl_cs_job *hl_cs_allocate_job(struct hl_device *hdev, + enum hl_queue_type queue_type, bool is_kernel_allocated_cb) { struct hl_cs_job *job; @@ -454,12 +485,14 @@ struct hl_cs_job *hl_cs_allocate_job(struct hl_device *hdev, bool ext_queue) if (!job) return NULL; - job->ext_queue = ext_queue; + job->queue_type = queue_type; + job->is_kernel_allocated_cb = is_kernel_allocated_cb; - if (job->ext_queue) { + if (is_cb_patched(hdev, job)) INIT_LIST_HEAD(&job->userptr_list); + + if (job->queue_type == QUEUE_TYPE_EXT) INIT_WORK(&job->finish_work, job_wq_completion); - } return job; } @@ -472,7 +505,7 @@ static int _hl_cs_ioctl(struct hl_fpriv *hpriv, void __user *chunks, struct hl_cs_job *job; struct hl_cs *cs; struct hl_cb *cb; - bool ext_queue_present = false; + bool int_queues_only = true; u32 size_to_copy; int rc, i, parse_cnt; @@ -516,23 +549,33 @@ static int _hl_cs_ioctl(struct hl_fpriv *hpriv, void __user *chunks, /* Validate ALL the CS chunks before submitting the CS */ for (i = 0, parse_cnt = 0 ; i < num_chunks ; i++, parse_cnt++) { struct hl_cs_chunk *chunk = &cs_chunk_array[i]; - bool ext_queue; + enum hl_queue_type queue_type; + bool is_kernel_allocated_cb; - cb = validate_queue_index(hdev, &hpriv->cb_mgr, chunk, - &ext_queue); - if (ext_queue) { - ext_queue_present = true; + rc = validate_queue_index(hdev, chunk, &queue_type, + &is_kernel_allocated_cb); + if (rc) + goto free_cs_object; + + if (is_kernel_allocated_cb) { + cb = get_cb_from_cs_chunk(hdev, &hpriv->cb_mgr, chunk); if (!cb) { rc = -EINVAL; goto free_cs_object; } + } else { + cb = (struct hl_cb *) (uintptr_t) chunk->cb_handle; } - job = hl_cs_allocate_job(hdev, ext_queue); + if (queue_type == QUEUE_TYPE_EXT || queue_type == QUEUE_TYPE_HW) + int_queues_only = false; + + job = hl_cs_allocate_job(hdev, queue_type, + is_kernel_allocated_cb); if (!job) { dev_err(hdev->dev, "Failed to allocate a new job\n"); rc = -ENOMEM; - if (ext_queue) + if (is_kernel_allocated_cb) goto release_cb; else goto free_cs_object; @@ -542,7 +585,7 @@ static int _hl_cs_ioctl(struct hl_fpriv *hpriv, void __user *chunks, job->cs = cs; job->user_cb = cb; job->user_cb_size = chunk->cb_size; - if (job->ext_queue) + if (is_kernel_allocated_cb) job->job_cb_size = cb->size; else job->job_cb_size = chunk->cb_size; @@ -555,10 +598,11 @@ static int _hl_cs_ioctl(struct hl_fpriv *hpriv, void __user *chunks, /* * Increment CS reference. When CS reference is 0, CS is * done and can be signaled to user and free all its resources - * Only increment for JOB on external queues, because only - * for those JOBs we get completion + * Only increment for JOB on external or H/W queues, because + * only for those JOBs we get completion */ - if (job->ext_queue) + if (job->queue_type == QUEUE_TYPE_EXT || + job->queue_type == QUEUE_TYPE_HW) cs_get(cs); hl_debugfs_add_job(hdev, job); @@ -572,9 +616,9 @@ static int _hl_cs_ioctl(struct hl_fpriv *hpriv, void __user *chunks, } } - if (!ext_queue_present) { + if (int_queues_only) { dev_err(hdev->dev, - "Reject CS %d.%llu because no external queues jobs\n", + "Reject CS %d.%llu because only internal queues jobs are present\n", cs->ctx->asid, cs->sequence); rc = -EINVAL; goto free_cs_object; diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index 71693fcffb16..0b40915bede2 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -3943,7 +3943,7 @@ int goya_cs_parser(struct hl_device *hdev, struct hl_cs_parser *parser) { struct goya_device *goya = hdev->asic_specific; - if (!parser->ext_queue) + if (parser->queue_type == QUEUE_TYPE_INT) return goya_parse_cb_no_ext_queue(hdev, parser); if (goya->hw_cap_initialized & HW_CAP_MMU) @@ -4614,7 +4614,7 @@ static int goya_memset_device_memory(struct hl_device *hdev, u64 addr, u64 size, lin_dma_pkt++; } while (--lin_dma_pkts_cnt); - job = hl_cs_allocate_job(hdev, true); + job = hl_cs_allocate_job(hdev, QUEUE_TYPE_EXT, true); if (!job) { dev_err(hdev->dev, "Failed to allocate a new job\n"); rc = -ENOMEM; diff --git a/drivers/misc/habanalabs/habanalabs.h b/drivers/misc/habanalabs/habanalabs.h index f47f4b22cb6b..371d1ec15697 100644 --- a/drivers/misc/habanalabs/habanalabs.h +++ b/drivers/misc/habanalabs/habanalabs.h @@ -85,12 +85,15 @@ struct hl_fpriv; * @QUEUE_TYPE_INT: internal queue that performs DMA inside the device's * memories and/or operates the compute engines. * @QUEUE_TYPE_CPU: S/W queue for communication with the device's CPU. + * @QUEUE_TYPE_HW: queue of DMA and compute engines jobs, for which completion + * notifications are sent by H/W. */ enum hl_queue_type { QUEUE_TYPE_NA, QUEUE_TYPE_EXT, QUEUE_TYPE_INT, - QUEUE_TYPE_CPU + QUEUE_TYPE_CPU, + QUEUE_TYPE_HW }; /** @@ -755,11 +758,14 @@ struct hl_cs { * @userptr_list: linked-list of userptr mappings that belong to this job and * wait for completion. * @debugfs_list: node in debugfs list of command submission jobs. + * @queue_type: the type of the H/W queue this job is submitted to. * @id: the id of this job inside a CS. * @hw_queue_id: the id of the H/W queue this job is submitted to. * @user_cb_size: the actual size of the CB we got from the user. * @job_cb_size: the actual size of the CB that we put on the queue. - * @ext_queue: whether the job is for external queue or internal queue. + * @is_kernel_allocated_cb: true if the CB handle we got from the user holds a + * handle to a kernel-allocated CB object, false + * otherwise (SRAM/DRAM/host address). */ struct hl_cs_job { struct list_head cs_node; @@ -769,11 +775,12 @@ struct hl_cs_job { struct work_struct finish_work; struct list_head userptr_list; struct list_head debugfs_list; + enum hl_queue_type queue_type; u32 id; u32 hw_queue_id; u32 user_cb_size; u32 job_cb_size; - u8 ext_queue; + u8 is_kernel_allocated_cb; }; /** @@ -784,24 +791,28 @@ struct hl_cs_job { * @job_userptr_list: linked-list of userptr mappings that belong to the related * job and wait for completion. * @cs_sequence: the sequence number of the related CS. + * @queue_type: the type of the H/W queue this job is submitted to. * @ctx_id: the ID of the context the related CS belongs to. * @hw_queue_id: the id of the H/W queue this job is submitted to. * @user_cb_size: the actual size of the CB we got from the user. * @patched_cb_size: the size of the CB after parsing. - * @ext_queue: whether the job is for external queue or internal queue. * @job_id: the id of the related job inside the related CS. + * @is_kernel_allocated_cb: true if the CB handle we got from the user holds a + * handle to a kernel-allocated CB object, false + * otherwise (SRAM/DRAM/host address). */ struct hl_cs_parser { struct hl_cb *user_cb; struct hl_cb *patched_cb; struct list_head *job_userptr_list; u64 cs_sequence; + enum hl_queue_type queue_type; u32 ctx_id; u32 hw_queue_id; u32 user_cb_size; u32 patched_cb_size; - u8 ext_queue; u8 job_id; + u8 is_kernel_allocated_cb; }; @@ -1504,7 +1515,8 @@ int hl_cb_pool_init(struct hl_device *hdev); int hl_cb_pool_fini(struct hl_device *hdev); void hl_cs_rollback_all(struct hl_device *hdev); -struct hl_cs_job *hl_cs_allocate_job(struct hl_device *hdev, bool ext_queue); +struct hl_cs_job *hl_cs_allocate_job(struct hl_device *hdev, + enum hl_queue_type queue_type, bool is_kernel_allocated_cb); void goya_set_asic_funcs(struct hl_device *hdev); diff --git a/drivers/misc/habanalabs/hw_queue.c b/drivers/misc/habanalabs/hw_queue.c index f733b534f738..91579dde9262 100644 --- a/drivers/misc/habanalabs/hw_queue.c +++ b/drivers/misc/habanalabs/hw_queue.c @@ -58,8 +58,8 @@ out: } /* - * ext_queue_submit_bd - Submit a buffer descriptor to an external queue - * + * ext_and_hw_queue_submit_bd() - Submit a buffer descriptor to an external or a + * H/W queue. * @hdev: pointer to habanalabs device structure * @q: pointer to habanalabs queue structure * @ctl: BD's control word @@ -73,8 +73,8 @@ out: * This function must be called when the scheduler mutex is taken * */ -static void ext_queue_submit_bd(struct hl_device *hdev, struct hl_hw_queue *q, - u32 ctl, u32 len, u64 ptr) +static void ext_and_hw_queue_submit_bd(struct hl_device *hdev, + struct hl_hw_queue *q, u32 ctl, u32 len, u64 ptr) { struct hl_bd *bd; @@ -173,6 +173,45 @@ static int int_queue_sanity_checks(struct hl_device *hdev, return 0; } +/* + * hw_queue_sanity_checks() - Perform some sanity checks on a H/W queue. + * @hdev: Pointer to hl_device structure. + * @q: Pointer to hl_hw_queue structure. + * @num_of_entries: How many entries to check for space. + * + * Perform the following: + * - Make sure we have enough space in the completion queue. + * This check also ensures that there is enough space in the h/w queue, as + * both queues are of the same size. + * - Reserve space in the completion queue (needs to be reversed if there + * is a failure down the road before the actual submission of work). + * + * Both operations are done using the "free_slots_cnt" field of the completion + * queue. The CI counters of the queue and the completion queue are not + * needed/used for the H/W queue type. + */ +static int hw_queue_sanity_checks(struct hl_device *hdev, struct hl_hw_queue *q, + int num_of_entries) +{ + atomic_t *free_slots = + &hdev->completion_queue[q->hw_queue_id].free_slots_cnt; + + /* + * Check we have enough space in the completion queue. + * Add -1 to counter (decrement) unless counter was already 0. + * In that case, CQ is full so we can't submit a new CB. + * atomic_add_unless will return 0 if counter was already 0. + */ + if (atomic_add_negative(num_of_entries * -1, free_slots)) { + dev_dbg(hdev->dev, "No space for %d entries on CQ %d\n", + num_of_entries, q->hw_queue_id); + atomic_add(num_of_entries, free_slots); + return -EAGAIN; + } + + return 0; +} + /* * hl_hw_queue_send_cb_no_cmpl - send a single CB (not a JOB) without completion * @@ -188,7 +227,7 @@ int hl_hw_queue_send_cb_no_cmpl(struct hl_device *hdev, u32 hw_queue_id, u32 cb_size, u64 cb_ptr) { struct hl_hw_queue *q = &hdev->kernel_queues[hw_queue_id]; - int rc; + int rc = 0; /* * The CPU queue is a synchronous queue with an effective depth of @@ -206,11 +245,18 @@ int hl_hw_queue_send_cb_no_cmpl(struct hl_device *hdev, u32 hw_queue_id, goto out; } - rc = ext_queue_sanity_checks(hdev, q, 1, false); - if (rc) - goto out; + /* + * hl_hw_queue_send_cb_no_cmpl() is called for queues of a H/W queue + * type only on init phase, when the queues are empty and being tested, + * so there is no need for sanity checks. + */ + if (q->queue_type != QUEUE_TYPE_HW) { + rc = ext_queue_sanity_checks(hdev, q, 1, false); + if (rc) + goto out; + } - ext_queue_submit_bd(hdev, q, 0, cb_size, cb_ptr); + ext_and_hw_queue_submit_bd(hdev, q, 0, cb_size, cb_ptr); out: if (q->queue_type != QUEUE_TYPE_CPU) @@ -220,14 +266,14 @@ out: } /* - * ext_hw_queue_schedule_job - submit a JOB to an external queue + * ext_queue_schedule_job - submit a JOB to an external queue * * @job: pointer to the job that needs to be submitted to the queue * * This function must be called when the scheduler mutex is taken * */ -static void ext_hw_queue_schedule_job(struct hl_cs_job *job) +static void ext_queue_schedule_job(struct hl_cs_job *job) { struct hl_device *hdev = job->cs->ctx->hdev; struct hl_hw_queue *q = &hdev->kernel_queues[job->hw_queue_id]; @@ -260,7 +306,7 @@ static void ext_hw_queue_schedule_job(struct hl_cs_job *job) * H/W queues is done under the scheduler mutex * * No need to check if CQ is full because it was already - * checked in hl_queue_sanity_checks + * checked in ext_queue_sanity_checks */ cq = &hdev->completion_queue[q->hw_queue_id]; cq_addr = cq->bus_address + cq->pi * sizeof(struct hl_cq_entry); @@ -274,18 +320,18 @@ static void ext_hw_queue_schedule_job(struct hl_cs_job *job) cq->pi = hl_cq_inc_ptr(cq->pi); - ext_queue_submit_bd(hdev, q, ctl, len, ptr); + ext_and_hw_queue_submit_bd(hdev, q, ctl, len, ptr); } /* - * int_hw_queue_schedule_job - submit a JOB to an internal queue + * int_queue_schedule_job - submit a JOB to an internal queue * * @job: pointer to the job that needs to be submitted to the queue * * This function must be called when the scheduler mutex is taken * */ -static void int_hw_queue_schedule_job(struct hl_cs_job *job) +static void int_queue_schedule_job(struct hl_cs_job *job) { struct hl_device *hdev = job->cs->ctx->hdev; struct hl_hw_queue *q = &hdev->kernel_queues[job->hw_queue_id]; @@ -307,6 +353,60 @@ static void int_hw_queue_schedule_job(struct hl_cs_job *job) hdev->asic_funcs->ring_doorbell(hdev, q->hw_queue_id, q->pi); } +/* + * hw_queue_schedule_job - submit a JOB to a H/W queue + * + * @job: pointer to the job that needs to be submitted to the queue + * + * This function must be called when the scheduler mutex is taken + * + */ +static void hw_queue_schedule_job(struct hl_cs_job *job) +{ + struct hl_device *hdev = job->cs->ctx->hdev; + struct hl_hw_queue *q = &hdev->kernel_queues[job->hw_queue_id]; + struct hl_cq *cq; + u64 ptr; + u32 offset, ctl, len; + + /* + * Upon PQE completion, COMP_DATA is used as the write data to the + * completion queue (QMAN HBW message), and COMP_OFFSET is used as the + * write address offset in the SM block (QMAN LBW message). + * The write address offset is calculated as "COMP_OFFSET << 2". + */ + offset = job->cs->sequence & (HL_MAX_PENDING_CS - 1); + ctl = ((offset << BD_CTL_COMP_OFFSET_SHIFT) & BD_CTL_COMP_OFFSET_MASK) | + ((q->pi << BD_CTL_COMP_DATA_SHIFT) & BD_CTL_COMP_DATA_MASK); + + len = job->job_cb_size; + + /* + * A patched CB is created only if a user CB was allocated by driver and + * MMU is disabled. If MMU is enabled, the user CB should be used + * instead. If the user CB wasn't allocated by driver, assume that it + * holds an address. + */ + if (job->patched_cb) + ptr = job->patched_cb->bus_address; + else if (job->is_kernel_allocated_cb) + ptr = job->user_cb->bus_address; + else + ptr = (u64) (uintptr_t) job->user_cb; + + /* + * No need to protect pi_offset because scheduling to the + * H/W queues is done under the scheduler mutex + * + * No need to check if CQ is full because it was already + * checked in hw_queue_sanity_checks + */ + cq = &hdev->completion_queue[q->hw_queue_id]; + cq->pi = hl_cq_inc_ptr(cq->pi); + + ext_and_hw_queue_submit_bd(hdev, q, ctl, len, ptr); +} + /* * hl_hw_queue_schedule_cs - schedule a command submission * @@ -330,23 +430,34 @@ int hl_hw_queue_schedule_cs(struct hl_cs *cs) } q = &hdev->kernel_queues[0]; - /* This loop assumes all external queues are consecutive */ for (i = 0, cq_cnt = 0 ; i < HL_MAX_QUEUES ; i++, q++) { - if (q->queue_type == QUEUE_TYPE_EXT) { - if (cs->jobs_in_queue_cnt[i]) { + if (cs->jobs_in_queue_cnt[i]) { + switch (q->queue_type) { + case QUEUE_TYPE_EXT: rc = ext_queue_sanity_checks(hdev, q, - cs->jobs_in_queue_cnt[i], true); - if (rc) - goto unroll_cq_resv; - cq_cnt++; - } - } else if (q->queue_type == QUEUE_TYPE_INT) { - if (cs->jobs_in_queue_cnt[i]) { + cs->jobs_in_queue_cnt[i], true); + break; + case QUEUE_TYPE_INT: rc = int_queue_sanity_checks(hdev, q, - cs->jobs_in_queue_cnt[i]); - if (rc) - goto unroll_cq_resv; + cs->jobs_in_queue_cnt[i]); + break; + case QUEUE_TYPE_HW: + rc = hw_queue_sanity_checks(hdev, q, + cs->jobs_in_queue_cnt[i]); + break; + default: + dev_err(hdev->dev, "Queue type %d is invalid\n", + q->queue_type); + rc = -EINVAL; + break; } + + if (rc) + goto unroll_cq_resv; + + if (q->queue_type == QUEUE_TYPE_EXT || + q->queue_type == QUEUE_TYPE_HW) + cq_cnt++; } } @@ -373,21 +484,30 @@ int hl_hw_queue_schedule_cs(struct hl_cs *cs) } list_for_each_entry_safe(job, tmp, &cs->job_list, cs_node) - if (job->ext_queue) - ext_hw_queue_schedule_job(job); - else - int_hw_queue_schedule_job(job); + switch (job->queue_type) { + case QUEUE_TYPE_EXT: + ext_queue_schedule_job(job); + break; + case QUEUE_TYPE_INT: + int_queue_schedule_job(job); + break; + case QUEUE_TYPE_HW: + hw_queue_schedule_job(job); + break; + default: + break; + } cs->submitted = true; goto out; unroll_cq_resv: - /* This loop assumes all external queues are consecutive */ q = &hdev->kernel_queues[0]; for (i = 0 ; (i < HL_MAX_QUEUES) && (cq_cnt > 0) ; i++, q++) { - if ((q->queue_type == QUEUE_TYPE_EXT) && - (cs->jobs_in_queue_cnt[i])) { + if ((q->queue_type == QUEUE_TYPE_EXT || + q->queue_type == QUEUE_TYPE_HW) && + cs->jobs_in_queue_cnt[i]) { atomic_t *free_slots = &hdev->completion_queue[i].free_slots_cnt; atomic_add(cs->jobs_in_queue_cnt[i], free_slots); @@ -414,8 +534,8 @@ void hl_hw_queue_inc_ci_kernel(struct hl_device *hdev, u32 hw_queue_id) q->ci = hl_queue_inc_ptr(q->ci); } -static int ext_and_cpu_hw_queue_init(struct hl_device *hdev, - struct hl_hw_queue *q, bool is_cpu_queue) +static int ext_and_cpu_queue_init(struct hl_device *hdev, struct hl_hw_queue *q, + bool is_cpu_queue) { void *p; int rc; @@ -465,7 +585,7 @@ free_queue: return rc; } -static int int_hw_queue_init(struct hl_device *hdev, struct hl_hw_queue *q) +static int int_queue_init(struct hl_device *hdev, struct hl_hw_queue *q) { void *p; @@ -485,18 +605,38 @@ static int int_hw_queue_init(struct hl_device *hdev, struct hl_hw_queue *q) return 0; } -static int cpu_hw_queue_init(struct hl_device *hdev, struct hl_hw_queue *q) +static int cpu_queue_init(struct hl_device *hdev, struct hl_hw_queue *q) { - return ext_and_cpu_hw_queue_init(hdev, q, true); + return ext_and_cpu_queue_init(hdev, q, true); } -static int ext_hw_queue_init(struct hl_device *hdev, struct hl_hw_queue *q) +static int ext_queue_init(struct hl_device *hdev, struct hl_hw_queue *q) { - return ext_and_cpu_hw_queue_init(hdev, q, false); + return ext_and_cpu_queue_init(hdev, q, false); +} + +static int hw_queue_init(struct hl_device *hdev, struct hl_hw_queue *q) +{ + void *p; + + p = hdev->asic_funcs->asic_dma_alloc_coherent(hdev, + HL_QUEUE_SIZE_IN_BYTES, + &q->bus_address, + GFP_KERNEL | __GFP_ZERO); + if (!p) + return -ENOMEM; + + q->kernel_address = (u64) (uintptr_t) p; + + /* Make sure read/write pointers are initialized to start of queue */ + q->ci = 0; + q->pi = 0; + + return 0; } /* - * hw_queue_init - main initialization function for H/W queue object + * queue_init - main initialization function for H/W queue object * * @hdev: pointer to hl_device device structure * @q: pointer to hl_hw_queue queue structure @@ -505,7 +645,7 @@ static int ext_hw_queue_init(struct hl_device *hdev, struct hl_hw_queue *q) * Allocate dma-able memory for the queue and initialize fields * Returns 0 on success */ -static int hw_queue_init(struct hl_device *hdev, struct hl_hw_queue *q, +static int queue_init(struct hl_device *hdev, struct hl_hw_queue *q, u32 hw_queue_id) { int rc; @@ -516,21 +656,20 @@ static int hw_queue_init(struct hl_device *hdev, struct hl_hw_queue *q, switch (q->queue_type) { case QUEUE_TYPE_EXT: - rc = ext_hw_queue_init(hdev, q); + rc = ext_queue_init(hdev, q); break; - case QUEUE_TYPE_INT: - rc = int_hw_queue_init(hdev, q); + rc = int_queue_init(hdev, q); break; - case QUEUE_TYPE_CPU: - rc = cpu_hw_queue_init(hdev, q); + rc = cpu_queue_init(hdev, q); + break; + case QUEUE_TYPE_HW: + rc = hw_queue_init(hdev, q); break; - case QUEUE_TYPE_NA: q->valid = 0; return 0; - default: dev_crit(hdev->dev, "wrong queue type %d during init\n", q->queue_type); @@ -554,7 +693,7 @@ static int hw_queue_init(struct hl_device *hdev, struct hl_hw_queue *q, * * Free the queue memory */ -static void hw_queue_fini(struct hl_device *hdev, struct hl_hw_queue *q) +static void queue_fini(struct hl_device *hdev, struct hl_hw_queue *q) { if (!q->valid) return; @@ -612,7 +751,7 @@ int hl_hw_queues_create(struct hl_device *hdev) i < HL_MAX_QUEUES ; i++, q_ready_cnt++, q++) { q->queue_type = asic->hw_queues_props[i].type; - rc = hw_queue_init(hdev, q, i); + rc = queue_init(hdev, q, i); if (rc) { dev_err(hdev->dev, "failed to initialize queue %d\n", i); @@ -624,7 +763,7 @@ int hl_hw_queues_create(struct hl_device *hdev) release_queues: for (i = 0, q = hdev->kernel_queues ; i < q_ready_cnt ; i++, q++) - hw_queue_fini(hdev, q); + queue_fini(hdev, q); kfree(hdev->kernel_queues); @@ -637,7 +776,7 @@ void hl_hw_queues_destroy(struct hl_device *hdev) int i; for (i = 0, q = hdev->kernel_queues ; i < HL_MAX_QUEUES ; i++, q++) - hw_queue_fini(hdev, q); + queue_fini(hdev, q); kfree(hdev->kernel_queues); } diff --git a/drivers/misc/habanalabs/include/qman_if.h b/drivers/misc/habanalabs/include/qman_if.h index bf59bbe27fdc..0fdb49188ed7 100644 --- a/drivers/misc/habanalabs/include/qman_if.h +++ b/drivers/misc/habanalabs/include/qman_if.h @@ -23,6 +23,8 @@ struct hl_bd { #define HL_BD_SIZE sizeof(struct hl_bd) /* + * S/W CTL FIELDS. + * * BD_CTL_REPEAT_VALID tells the CP whether the repeat field in the BD CTL is * valid. 1 means the repeat field is valid, 0 means not-valid, * i.e. repeat == 1 @@ -33,6 +35,16 @@ struct hl_bd { #define BD_CTL_SHADOW_INDEX_SHIFT 0 #define BD_CTL_SHADOW_INDEX_MASK 0x00000FFF +/* + * H/W CTL FIELDS + */ + +#define BD_CTL_COMP_OFFSET_SHIFT 16 +#define BD_CTL_COMP_OFFSET_MASK 0x00FF0000 + +#define BD_CTL_COMP_DATA_SHIFT 0 +#define BD_CTL_COMP_DATA_MASK 0x0000FFFF + /* * COMPLETION QUEUE */ From 8fdacf2a530f36f6f0621a95ef0e37d8db2d2f89 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Wed, 2 Oct 2019 14:14:08 +0300 Subject: [PATCH 172/198] habanalabs: set TPC Icache to 16 cache lines Reduce latency to memory during TPC kernel execution. Signed-off-by: Oded Gabbay Reviewed-by: Tomer Tayar --- drivers/misc/habanalabs/goya/goya.c | 3 +++ drivers/misc/habanalabs/habanalabs.h | 7 ++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index 0b40915bede2..d49f5ecd903b 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -1457,6 +1457,9 @@ static void goya_init_golden_registers(struct hl_device *hdev) 1 << TPC0_NRTR_SCRAMB_EN_VAL_SHIFT); WREG32(mmTPC0_NRTR_NON_LIN_SCRAMB + offset, 1 << TPC0_NRTR_NON_LIN_SCRAMB_EN_SHIFT); + + WREG32_FIELD(TPC0_CFG_MSS_CONFIG, offset, + ICACHE_FETCH_LINE_NUM, 2); } WREG32(mmDMA_NRTR_SCRAMB_EN, 1 << DMA_NRTR_SCRAMB_EN_VAL_SHIFT); diff --git a/drivers/misc/habanalabs/habanalabs.h b/drivers/misc/habanalabs/habanalabs.h index 371d1ec15697..91445371b08b 100644 --- a/drivers/misc/habanalabs/habanalabs.h +++ b/drivers/misc/habanalabs/habanalabs.h @@ -1062,9 +1062,10 @@ void hl_wreg(struct hl_device *hdev, u32 reg, u32 val); #define REG_FIELD_SHIFT(reg, field) reg##_##field##_SHIFT #define REG_FIELD_MASK(reg, field) reg##_##field##_MASK -#define WREG32_FIELD(reg, field, val) \ - WREG32(mm##reg, (RREG32(mm##reg) & ~REG_FIELD_MASK(reg, field)) | \ - (val) << REG_FIELD_SHIFT(reg, field)) +#define WREG32_FIELD(reg, offset, field, val) \ + WREG32(mm##reg + offset, (RREG32(mm##reg + offset) & \ + ~REG_FIELD_MASK(reg, field)) | \ + (val) << REG_FIELD_SHIFT(reg, field)) /* Timeout should be longer when working with simulator but cap the * increased timeout to some maximum From 62c1e124a9e03ccb8bb39efe1d092c2376967528 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Thu, 10 Oct 2019 15:48:59 +0300 Subject: [PATCH 173/198] habanalabs: add opcode to INFO IOCTL to return clock rate Add a new opcode to the INFO IOCTL to allow the user application to retrieve the ASIC's current and maximum clock rate. The rate is returned in MHz. Signed-off-by: Oded Gabbay Reviewed-by: Tomer Tayar --- drivers/misc/habanalabs/goya/goya.c | 3 ++- drivers/misc/habanalabs/goya/goyaP.h | 2 ++ drivers/misc/habanalabs/goya/goya_hwmgr.c | 31 ++++++++++++++++++++++ drivers/misc/habanalabs/habanalabs.h | 2 ++ drivers/misc/habanalabs/habanalabs_ioctl.c | 23 ++++++++++++++++ include/uapi/misc/habanalabs.h | 19 +++++++++---- 6 files changed, 74 insertions(+), 6 deletions(-) diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index d49f5ecd903b..ac574d18c139 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -5148,7 +5148,8 @@ static const struct hl_asic_funcs goya_funcs = { .init_iatu = goya_init_iatu, .rreg = hl_rreg, .wreg = hl_wreg, - .halt_coresight = goya_halt_coresight + .halt_coresight = goya_halt_coresight, + .get_clk_rate = goya_get_clk_rate }; /* diff --git a/drivers/misc/habanalabs/goya/goyaP.h b/drivers/misc/habanalabs/goya/goyaP.h index 89b6574f8e4f..c3230cb6e25c 100644 --- a/drivers/misc/habanalabs/goya/goyaP.h +++ b/drivers/misc/habanalabs/goya/goyaP.h @@ -233,4 +233,6 @@ void goya_cpu_accessible_dma_pool_free(struct hl_device *hdev, size_t size, void *vaddr); void goya_mmu_remove_device_cpu_mappings(struct hl_device *hdev); +int goya_get_clk_rate(struct hl_device *hdev, u32 *cur_clk, u32 *max_clk); + #endif /* GOYAP_H_ */ diff --git a/drivers/misc/habanalabs/goya/goya_hwmgr.c b/drivers/misc/habanalabs/goya/goya_hwmgr.c index a2a700c3d597..b2ebc01e27f4 100644 --- a/drivers/misc/habanalabs/goya/goya_hwmgr.c +++ b/drivers/misc/habanalabs/goya/goya_hwmgr.c @@ -32,6 +32,37 @@ void goya_set_pll_profile(struct hl_device *hdev, enum hl_pll_frequency freq) } } +int goya_get_clk_rate(struct hl_device *hdev, u32 *cur_clk, u32 *max_clk) +{ + long value; + + if (hl_device_disabled_or_in_reset(hdev)) + return -ENODEV; + + value = hl_get_frequency(hdev, MME_PLL, false); + + if (value < 0) { + dev_err(hdev->dev, "Failed to retrieve device max clock %ld\n", + value); + return value; + } + + *max_clk = (value / 1000 / 1000); + + value = hl_get_frequency(hdev, MME_PLL, true); + + if (value < 0) { + dev_err(hdev->dev, + "Failed to retrieve device current clock %ld\n", + value); + return value; + } + + *cur_clk = (value / 1000 / 1000); + + return 0; +} + static ssize_t mme_clk_show(struct device *dev, struct device_attribute *attr, char *buf) { diff --git a/drivers/misc/habanalabs/habanalabs.h b/drivers/misc/habanalabs/habanalabs.h index 91445371b08b..4ff2da859653 100644 --- a/drivers/misc/habanalabs/habanalabs.h +++ b/drivers/misc/habanalabs/habanalabs.h @@ -508,6 +508,7 @@ enum hl_pll_frequency { * @rreg: Read a register. Needed for simulator support. * @wreg: Write a register. Needed for simulator support. * @halt_coresight: stop the ETF and ETR traces. + * @get_clk_rate: Retrieve the ASIC current and maximum clock rate in MHz */ struct hl_asic_funcs { int (*early_init)(struct hl_device *hdev); @@ -590,6 +591,7 @@ struct hl_asic_funcs { u32 (*rreg)(struct hl_device *hdev, u32 reg); void (*wreg)(struct hl_device *hdev, u32 reg, u32 val); void (*halt_coresight)(struct hl_device *hdev); + int (*get_clk_rate)(struct hl_device *hdev, u32 *cur_clk, u32 *max_clk); }; diff --git a/drivers/misc/habanalabs/habanalabs_ioctl.c b/drivers/misc/habanalabs/habanalabs_ioctl.c index 66d9c710073c..cd4b5a9ceac1 100644 --- a/drivers/misc/habanalabs/habanalabs_ioctl.c +++ b/drivers/misc/habanalabs/habanalabs_ioctl.c @@ -221,6 +221,25 @@ static int device_utilization(struct hl_device *hdev, struct hl_info_args *args) min((size_t) max_size, sizeof(device_util))) ? -EFAULT : 0; } +static int get_clk_rate(struct hl_device *hdev, struct hl_info_args *args) +{ + struct hl_info_clk_rate clk_rate = {0}; + u32 max_size = args->return_size; + void __user *out = (void __user *) (uintptr_t) args->return_pointer; + int rc; + + if ((!max_size) || (!out)) + return -EINVAL; + + rc = hdev->asic_funcs->get_clk_rate(hdev, &clk_rate.cur_clk_rate_mhz, + &clk_rate.max_clk_rate_mhz); + if (rc) + return rc; + + return copy_to_user(out, &clk_rate, + min((size_t) max_size, sizeof(clk_rate))) ? -EFAULT : 0; +} + static int _hl_info_ioctl(struct hl_fpriv *hpriv, void *data, struct device *dev) { @@ -271,6 +290,10 @@ static int _hl_info_ioctl(struct hl_fpriv *hpriv, void *data, rc = hw_events_info(hdev, true, args); break; + case HL_INFO_CLK_RATE: + rc = get_clk_rate(hdev, args); + break; + default: dev_err(dev, "Invalid request %d\n", args->op); rc = -ENOTTY; diff --git a/include/uapi/misc/habanalabs.h b/include/uapi/misc/habanalabs.h index 53e4ff73578e..783793c8be1c 100644 --- a/include/uapi/misc/habanalabs.h +++ b/include/uapi/misc/habanalabs.h @@ -88,13 +88,16 @@ enum hl_device_status { * internal engine. * HL_INFO_DEVICE_STATUS - Retrieve the device's status. This opcode doesn't * require an open context. - * HL_INFO_DEVICE_UTILIZATION - Retrieve the total utilization of the device - * over the last period specified by the user. - * The period can be between 100ms to 1s, in - * resolution of 100ms. The return value is a - * percentage of the utilization rate. + * HL_INFO_DEVICE_UTILIZATION - Retrieve the total utilization of the device + * over the last period specified by the user. + * The period can be between 100ms to 1s, in + * resolution of 100ms. The return value is a + * percentage of the utilization rate. * HL_INFO_HW_EVENTS_AGGREGATE - Receive an array describing how many times each * event occurred since the driver was loaded. + * HL_INFO_CLK_RATE - Retrieve the current and maximum clock rate + * of the device in MHz. The maximum clock rate is + * configurable via sysfs parameter */ #define HL_INFO_HW_IP_INFO 0 #define HL_INFO_HW_EVENTS 1 @@ -103,6 +106,7 @@ enum hl_device_status { #define HL_INFO_DEVICE_STATUS 4 #define HL_INFO_DEVICE_UTILIZATION 6 #define HL_INFO_HW_EVENTS_AGGREGATE 7 +#define HL_INFO_CLK_RATE 8 #define HL_INFO_VERSION_MAX_LEN 128 @@ -149,6 +153,11 @@ struct hl_info_device_utilization { __u32 pad; }; +struct hl_info_clk_rate { + __u32 cur_clk_rate_mhz; + __u32 max_clk_rate_mhz; +}; + struct hl_info_args { /* Location of relevant struct in userspace */ __u64 return_pointer; From 8d6de52866dcf1d6cbdec9aa10f722dd43b2431f Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 16 Oct 2019 16:46:32 +0800 Subject: [PATCH 174/198] habanalabs: remove set but not used variable 'qman_base_addr' Fixes gcc '-Wunused-but-set-variable' warning: drivers/misc/habanalabs/goya/goya.c: In function 'goya_init_mme_cmdq': drivers/misc/habanalabs/goya/goya.c:1536:6: warning: variable 'qman_base_addr' set but not used [-Wunused-but-set-variable] It is never used, so can be removed. Reported-by: Hulk Robot Signed-off-by: YueHaibing Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/goya/goya.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index ac574d18c139..e8812154343f 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -1539,7 +1539,6 @@ static void goya_init_mme_cmdq(struct hl_device *hdev) u32 mtr_base_lo, mtr_base_hi; u32 so_base_lo, so_base_hi; u32 gic_base_lo, gic_base_hi; - u64 qman_base_addr; mtr_base_lo = lower_32_bits(CFG_BASE + mmSYNC_MNGR_MON_PAY_ADDRL_0); mtr_base_hi = upper_32_bits(CFG_BASE + mmSYNC_MNGR_MON_PAY_ADDRL_0); @@ -1551,9 +1550,6 @@ static void goya_init_mme_cmdq(struct hl_device *hdev) gic_base_hi = upper_32_bits(CFG_BASE + mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR); - qman_base_addr = hdev->asic_prop.sram_base_address + - MME_QMAN_BASE_OFFSET; - WREG32(mmMME_CMDQ_CP_MSG_BASE0_ADDR_LO, mtr_base_lo); WREG32(mmMME_CMDQ_CP_MSG_BASE0_ADDR_HI, mtr_base_hi); WREG32(mmMME_CMDQ_CP_MSG_BASE1_ADDR_LO, so_base_lo); From 91edbf2cf8f0416b854674e891d7a5274f4b1702 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Wed, 16 Oct 2019 11:53:52 +0300 Subject: [PATCH 175/198] habanalabs: expose card name in INFO IOCTL To enable userspace processes, e.g. management utilities, to display the card name to the user, add the card name property to the HW_IP structure that is copied to the user in the INFO IOCTL. Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/goya/goya.c | 3 +++ drivers/misc/habanalabs/habanalabs_ioctl.c | 9 +++++++-- include/uapi/misc/habanalabs.h | 2 ++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index e8812154343f..d3ee9e2aa57e 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -396,6 +396,9 @@ void goya_get_fixed_properties(struct hl_device *hdev) prop->tpc_enabled_mask = TPC_ENABLED_MASK; prop->pcie_dbi_base_address = mmPCIE_DBI_BASE; prop->pcie_aux_dbi_reg_addr = CFG_BASE + mmPCIE_AUX_DBI; + + strncpy(prop->armcp_info.card_name, GOYA_DEFAULT_CARD_NAME, + CARD_NAME_MAX_LEN); } /* diff --git a/drivers/misc/habanalabs/habanalabs_ioctl.c b/drivers/misc/habanalabs/habanalabs_ioctl.c index cd4b5a9ceac1..02d7491fa28f 100644 --- a/drivers/misc/habanalabs/habanalabs_ioctl.c +++ b/drivers/misc/habanalabs/habanalabs_ioctl.c @@ -63,8 +63,13 @@ static int hw_ip_info(struct hl_device *hdev, struct hl_info_args *args) if (hw_ip.dram_size > 0) hw_ip.dram_enabled = 1; hw_ip.num_of_events = prop->num_of_events; - memcpy(hw_ip.armcp_version, - prop->armcp_info.armcp_version, VERSION_MAX_LEN); + + memcpy(hw_ip.armcp_version, prop->armcp_info.armcp_version, + min(VERSION_MAX_LEN, HL_INFO_VERSION_MAX_LEN)); + + memcpy(hw_ip.card_name, prop->armcp_info.card_name, + min(CARD_NAME_MAX_LEN, HL_INFO_CARD_NAME_MAX_LEN)); + hw_ip.armcp_cpld_version = le32_to_cpu(prop->armcp_info.cpld_version); hw_ip.psoc_pci_pll_nr = prop->psoc_pci_pll_nr; hw_ip.psoc_pci_pll_nf = prop->psoc_pci_pll_nf; diff --git a/include/uapi/misc/habanalabs.h b/include/uapi/misc/habanalabs.h index 783793c8be1c..e387d9e560b3 100644 --- a/include/uapi/misc/habanalabs.h +++ b/include/uapi/misc/habanalabs.h @@ -109,6 +109,7 @@ enum hl_device_status { #define HL_INFO_CLK_RATE 8 #define HL_INFO_VERSION_MAX_LEN 128 +#define HL_INFO_CARD_NAME_MAX_LEN 16 struct hl_info_hw_ip_info { __u64 sram_base_address; @@ -127,6 +128,7 @@ struct hl_info_hw_ip_info { __u8 dram_enabled; __u8 pad[2]; __u8 armcp_version[HL_INFO_VERSION_MAX_LEN]; + __u8 card_name[HL_INFO_CARD_NAME_MAX_LEN]; }; struct hl_info_dram_usage { From f05912d8f16bf303e293d4add2caecb8a9231c41 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Sun, 20 Oct 2019 11:07:11 +0300 Subject: [PATCH 176/198] habanalabs: read F/W versions before failure Move the read of the F/W boot versions before exiting on possible failures of the F/W boot. This will help debug boot failures as we will be able to know the F/W boot version. Signed-off-by: Oded Gabbay Reviewed-by: Omer Shpigelman --- drivers/misc/habanalabs/goya/goya.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index d3ee9e2aa57e..4e767e1d78e4 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -2296,6 +2296,10 @@ static int goya_init_cpu(struct hl_device *hdev, u32 cpu_timeout) 10000, cpu_timeout); + /* Read U-Boot version now in case we will later fail */ + goya_read_device_fw_version(hdev, FW_COMP_UBOOT); + goya_read_device_fw_version(hdev, FW_COMP_PREBOOT); + if (rc) { dev_err(hdev->dev, "Error in ARM u-boot!"); switch (status) { @@ -2347,10 +2351,6 @@ static int goya_init_cpu(struct hl_device *hdev, u32 cpu_timeout) return -EIO; } - /* Read U-Boot version now in case we will later fail */ - goya_read_device_fw_version(hdev, FW_COMP_UBOOT); - goya_read_device_fw_version(hdev, FW_COMP_PREBOOT); - if (!hdev->fw_loading) { dev_info(hdev->dev, "Skip loading FW\n"); goto out; From e1a84d56fcb92d4551692cbec4bada1cec00e620 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Thu, 24 Oct 2019 09:52:25 +0300 Subject: [PATCH 177/198] habanalabs: use registers name defines for ETR block We have a single ETR block in the SOC, so use explicit register name defines for initializing this block. This makes it more readable and maintainable. Signed-off-by: Oded Gabbay Reviewed-by: Omer Shpigelman --- drivers/misc/habanalabs/goya/goya_coresight.c | 51 ++++---- .../include/goya/asic_reg/goya_regs.h | 1 + .../include/goya/asic_reg/psoc_etr_regs.h | 114 ++++++++++++++++++ 3 files changed, 140 insertions(+), 26 deletions(-) create mode 100644 drivers/misc/habanalabs/include/goya/asic_reg/psoc_etr_regs.h diff --git a/drivers/misc/habanalabs/goya/goya_coresight.c b/drivers/misc/habanalabs/goya/goya_coresight.c index b4d406af1bed..16bcd60b111f 100644 --- a/drivers/misc/habanalabs/goya/goya_coresight.c +++ b/drivers/misc/habanalabs/goya/goya_coresight.c @@ -377,33 +377,32 @@ static int goya_config_etr(struct hl_device *hdev, struct hl_debug_params *params) { struct hl_debug_params_etr *input; - u64 base_reg = mmPSOC_ETR_BASE - CFG_BASE; u32 val; int rc; - WREG32(base_reg + 0xFB0, CORESIGHT_UNLOCK); + WREG32(mmPSOC_ETR_LAR, CORESIGHT_UNLOCK); - val = RREG32(base_reg + 0x304); + val = RREG32(mmPSOC_ETR_FFCR); val |= 0x1000; - WREG32(base_reg + 0x304, val); + WREG32(mmPSOC_ETR_FFCR, val); val |= 0x40; - WREG32(base_reg + 0x304, val); + WREG32(mmPSOC_ETR_FFCR, val); - rc = goya_coresight_timeout(hdev, base_reg + 0x304, 6, false); + rc = goya_coresight_timeout(hdev, mmPSOC_ETR_FFCR, 6, false); if (rc) { dev_err(hdev->dev, "Failed to %s ETR on timeout, error %d\n", params->enable ? "enable" : "disable", rc); return rc; } - rc = goya_coresight_timeout(hdev, base_reg + 0xC, 2, true); + rc = goya_coresight_timeout(hdev, mmPSOC_ETR_STS, 2, true); if (rc) { dev_err(hdev->dev, "Failed to %s ETR on timeout, error %d\n", params->enable ? "enable" : "disable", rc); return rc; } - WREG32(base_reg + 0x20, 0); + WREG32(mmPSOC_ETR_CTL, 0); if (params->enable) { input = params->input; @@ -423,25 +422,25 @@ static int goya_config_etr(struct hl_device *hdev, return -EINVAL; } - WREG32(base_reg + 0x34, 0x3FFC); - WREG32(base_reg + 0x4, input->buffer_size); - WREG32(base_reg + 0x28, input->sink_mode); - WREG32(base_reg + 0x110, 0x700); - WREG32(base_reg + 0x118, + WREG32(mmPSOC_ETR_BUFWM, 0x3FFC); + WREG32(mmPSOC_ETR_RSZ, input->buffer_size); + WREG32(mmPSOC_ETR_MODE, input->sink_mode); + WREG32(mmPSOC_ETR_AXICTL, 0x700); + WREG32(mmPSOC_ETR_DBALO, lower_32_bits(input->buffer_address)); - WREG32(base_reg + 0x11C, + WREG32(mmPSOC_ETR_DBAHI, upper_32_bits(input->buffer_address)); - WREG32(base_reg + 0x304, 3); - WREG32(base_reg + 0x308, 0xA); - WREG32(base_reg + 0x20, 1); + WREG32(mmPSOC_ETR_FFCR, 3); + WREG32(mmPSOC_ETR_PSCR, 0xA); + WREG32(mmPSOC_ETR_CTL, 1); } else { - WREG32(base_reg + 0x34, 0); - WREG32(base_reg + 0x4, 0x400); - WREG32(base_reg + 0x118, 0); - WREG32(base_reg + 0x11C, 0); - WREG32(base_reg + 0x308, 0); - WREG32(base_reg + 0x28, 0); - WREG32(base_reg + 0x304, 0); + WREG32(mmPSOC_ETR_BUFWM, 0); + WREG32(mmPSOC_ETR_RSZ, 0x400); + WREG32(mmPSOC_ETR_DBALO, 0); + WREG32(mmPSOC_ETR_DBAHI, 0); + WREG32(mmPSOC_ETR_PSCR, 0); + WREG32(mmPSOC_ETR_MODE, 0); + WREG32(mmPSOC_ETR_FFCR, 0); if (params->output_size >= sizeof(u64)) { u32 rwp, rwphi; @@ -451,8 +450,8 @@ static int goya_config_etr(struct hl_device *hdev, * the buffer is set in the RWP register (lower 32 * bits), and in the RWPHI register (upper 8 bits). */ - rwp = RREG32(base_reg + 0x18); - rwphi = RREG32(base_reg + 0x3c) & 0xff; + rwp = RREG32(mmPSOC_ETR_RWP); + rwphi = RREG32(mmPSOC_ETR_RWPHI) & 0xff; *(u64 *) params->output = ((u64) rwphi << 32) | rwp; } } diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/goya_regs.h b/drivers/misc/habanalabs/include/goya/asic_reg/goya_regs.h index 19b0f0ef1d0b..fce490e6a231 100644 --- a/drivers/misc/habanalabs/include/goya/asic_reg/goya_regs.h +++ b/drivers/misc/habanalabs/include/goya/asic_reg/goya_regs.h @@ -84,6 +84,7 @@ #include "tpc6_rtr_regs.h" #include "tpc7_nrtr_regs.h" #include "tpc0_eml_cfg_regs.h" +#include "psoc_etr_regs.h" #include "psoc_global_conf_masks.h" #include "dma_macro_masks.h" diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/psoc_etr_regs.h b/drivers/misc/habanalabs/include/goya/asic_reg/psoc_etr_regs.h new file mode 100644 index 000000000000..b7c33e025db5 --- /dev/null +++ b/drivers/misc/habanalabs/include/goya/asic_reg/psoc_etr_regs.h @@ -0,0 +1,114 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright 2016-2018 HabanaLabs, Ltd. + * All Rights Reserved. + * + */ + +/************************************ + ** This is an auto-generated file ** + ** DO NOT EDIT BELOW ** + ************************************/ + +#ifndef ASIC_REG_PSOC_ETR_REGS_H_ +#define ASIC_REG_PSOC_ETR_REGS_H_ + +/* + ***************************************** + * PSOC_ETR (Prototype: ETR) + ***************************************** + */ + +#define mmPSOC_ETR_RSZ 0x2C43004 + +#define mmPSOC_ETR_STS 0x2C4300C + +#define mmPSOC_ETR_RRD 0x2C43010 + +#define mmPSOC_ETR_RRP 0x2C43014 + +#define mmPSOC_ETR_RWP 0x2C43018 + +#define mmPSOC_ETR_TRG 0x2C4301C + +#define mmPSOC_ETR_CTL 0x2C43020 + +#define mmPSOC_ETR_RWD 0x2C43024 + +#define mmPSOC_ETR_MODE 0x2C43028 + +#define mmPSOC_ETR_LBUFLEVEL 0x2C4302C + +#define mmPSOC_ETR_CBUFLEVEL 0x2C43030 + +#define mmPSOC_ETR_BUFWM 0x2C43034 + +#define mmPSOC_ETR_RRPHI 0x2C43038 + +#define mmPSOC_ETR_RWPHI 0x2C4303C + +#define mmPSOC_ETR_AXICTL 0x2C43110 + +#define mmPSOC_ETR_DBALO 0x2C43118 + +#define mmPSOC_ETR_DBAHI 0x2C4311C + +#define mmPSOC_ETR_FFSR 0x2C43300 + +#define mmPSOC_ETR_FFCR 0x2C43304 + +#define mmPSOC_ETR_PSCR 0x2C43308 + +#define mmPSOC_ETR_ITMISCOP0 0x2C43EE0 + +#define mmPSOC_ETR_ITTRFLIN 0x2C43EE8 + +#define mmPSOC_ETR_ITATBDATA0 0x2C43EEC + +#define mmPSOC_ETR_ITATBCTR2 0x2C43EF0 + +#define mmPSOC_ETR_ITATBCTR1 0x2C43EF4 + +#define mmPSOC_ETR_ITATBCTR0 0x2C43EF8 + +#define mmPSOC_ETR_ITCTRL 0x2C43F00 + +#define mmPSOC_ETR_CLAIMSET 0x2C43FA0 + +#define mmPSOC_ETR_CLAIMCLR 0x2C43FA4 + +#define mmPSOC_ETR_LAR 0x2C43FB0 + +#define mmPSOC_ETR_LSR 0x2C43FB4 + +#define mmPSOC_ETR_AUTHSTATUS 0x2C43FB8 + +#define mmPSOC_ETR_DEVID 0x2C43FC8 + +#define mmPSOC_ETR_DEVTYPE 0x2C43FCC + +#define mmPSOC_ETR_PERIPHID4 0x2C43FD0 + +#define mmPSOC_ETR_PERIPHID5 0x2C43FD4 + +#define mmPSOC_ETR_PERIPHID6 0x2C43FD8 + +#define mmPSOC_ETR_PERIPHID7 0x2C43FDC + +#define mmPSOC_ETR_PERIPHID0 0x2C43FE0 + +#define mmPSOC_ETR_PERIPHID1 0x2C43FE4 + +#define mmPSOC_ETR_PERIPHID2 0x2C43FE8 + +#define mmPSOC_ETR_PERIPHID3 0x2C43FEC + +#define mmPSOC_ETR_COMPID0 0x2C43FF0 + +#define mmPSOC_ETR_COMPID1 0x2C43FF4 + +#define mmPSOC_ETR_COMPID2 0x2C43FF8 + +#define mmPSOC_ETR_COMPID3 0x2C43FFC + +#endif /* ASIC_REG_PSOC_ETR_REGS_H_ */ From 6476b472437de2c41dc8873134c60d2928f806ce Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Thu, 24 Oct 2019 10:12:35 +0300 Subject: [PATCH 178/198] habanalabs: set ETR as non-secured ETR should always be non-secured as it is used by the users to record profiling/trace data. Signed-off-by: Oded Gabbay Reviewed-by: Omer Shpigelman --- drivers/misc/habanalabs/goya/goya_coresight.c | 4 +++- drivers/misc/habanalabs/include/goya/asic_reg/goya_masks.h | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/misc/habanalabs/goya/goya_coresight.c b/drivers/misc/habanalabs/goya/goya_coresight.c index 16bcd60b111f..c1ee6e2b5dff 100644 --- a/drivers/misc/habanalabs/goya/goya_coresight.c +++ b/drivers/misc/habanalabs/goya/goya_coresight.c @@ -8,6 +8,7 @@ #include "goyaP.h" #include "include/goya/goya_coresight.h" #include "include/goya/asic_reg/goya_regs.h" +#include "include/goya/asic_reg/goya_masks.h" #include @@ -425,7 +426,8 @@ static int goya_config_etr(struct hl_device *hdev, WREG32(mmPSOC_ETR_BUFWM, 0x3FFC); WREG32(mmPSOC_ETR_RSZ, input->buffer_size); WREG32(mmPSOC_ETR_MODE, input->sink_mode); - WREG32(mmPSOC_ETR_AXICTL, 0x700); + WREG32(mmPSOC_ETR_AXICTL, + 0x700 | PSOC_ETR_AXICTL_PROTCTRLBIT1_SHIFT); WREG32(mmPSOC_ETR_DBALO, lower_32_bits(input->buffer_address)); WREG32(mmPSOC_ETR_DBAHI, diff --git a/drivers/misc/habanalabs/include/goya/asic_reg/goya_masks.h b/drivers/misc/habanalabs/include/goya/asic_reg/goya_masks.h index 8618891d5afa..3c44ef3a23ed 100644 --- a/drivers/misc/habanalabs/include/goya/asic_reg/goya_masks.h +++ b/drivers/misc/habanalabs/include/goya/asic_reg/goya_masks.h @@ -260,4 +260,6 @@ #define DMA_QM_3_GLBL_CFG1_DMA_STOP_SHIFT DMA_QM_0_GLBL_CFG1_DMA_STOP_SHIFT #define DMA_QM_4_GLBL_CFG1_DMA_STOP_SHIFT DMA_QM_0_GLBL_CFG1_DMA_STOP_SHIFT +#define PSOC_ETR_AXICTL_PROTCTRLBIT1_SHIFT 1 + #endif /* ASIC_REG_GOYA_MASKS_H_ */ From bd4c8cb17d4e8f9e01ce48e3f2009307a58e60d2 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Sat, 9 Nov 2019 23:16:33 +0200 Subject: [PATCH 179/198] habanalabs: increase max jobs number to 512 In training, there is a need for a large amount of patching to the recipe. This results in many command buffers contains a lot of DMA packets. The number of command buffers per CS is larger than the current maximum of 64, which is an arbitrary number that is enough for inference, but it has no real affect on the code and/or resources of the host machine. Signed-off-by: Oded Gabbay Reviewed-by: Omer Shpigelman --- drivers/misc/habanalabs/habanalabs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/habanalabs/habanalabs.h b/drivers/misc/habanalabs/habanalabs.h index 4ff2da859653..0813041f669a 100644 --- a/drivers/misc/habanalabs/habanalabs.h +++ b/drivers/misc/habanalabs/habanalabs.h @@ -40,7 +40,7 @@ #define HL_MAX_QUEUES 128 -#define HL_MAX_JOBS_PER_CS 64 +#define HL_MAX_JOBS_PER_CS 512 /* MUST BE POWER OF 2 and larger than 1 */ #define HL_MAX_PENDING_CS 64 From eda58bf7860a028f207e8d4201d86191b898bbee Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Sun, 10 Nov 2019 18:48:06 +0200 Subject: [PATCH 180/198] habanalabs: don't print error when queues are full If the queues are full and we return -EAGAIN to the user, there is no need to print an error, as that case isn't an error and the user is expected to re-submit the work. Signed-off-by: Oded Gabbay Reviewed-by: Omer Shpigelman --- drivers/misc/habanalabs/command_submission.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/misc/habanalabs/command_submission.c b/drivers/misc/habanalabs/command_submission.c index 776ddafc47fb..8850f475a413 100644 --- a/drivers/misc/habanalabs/command_submission.c +++ b/drivers/misc/habanalabs/command_submission.c @@ -626,9 +626,10 @@ static int _hl_cs_ioctl(struct hl_fpriv *hpriv, void __user *chunks, rc = hl_hw_queue_schedule_cs(cs); if (rc) { - dev_err(hdev->dev, - "Failed to submit CS %d.%llu to H/W queues, error %d\n", - cs->ctx->asid, cs->sequence, rc); + if (rc != -EAGAIN) + dev_err(hdev->dev, + "Failed to submit CS %d.%llu to H/W queues, error %d\n", + cs->ctx->asid, cs->sequence, rc); goto free_cs_object; } From 5d1012576d20dd7cb70e00ea1b4c2af11a6c9156 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Sun, 10 Nov 2019 16:08:26 +0200 Subject: [PATCH 181/198] habanalabs: export uapi defines to user-space The two defines that control the maximum size of a command buffer and the maximum number of JOBS per CS need to be exported to the user as they are part of the API towards user-space. Signed-off-by: Oded Gabbay Reviewed-by: Omer Shpigelman --- drivers/misc/habanalabs/habanalabs.h | 4 ---- include/uapi/misc/habanalabs.h | 16 ++++++++++++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/drivers/misc/habanalabs/habanalabs.h b/drivers/misc/habanalabs/habanalabs.h index 0813041f669a..2a5344cc1a60 100644 --- a/drivers/misc/habanalabs/habanalabs.h +++ b/drivers/misc/habanalabs/habanalabs.h @@ -40,8 +40,6 @@ #define HL_MAX_QUEUES 128 -#define HL_MAX_JOBS_PER_CS 512 - /* MUST BE POWER OF 2 and larger than 1 */ #define HL_MAX_PENDING_CS 64 @@ -242,8 +240,6 @@ struct hl_dma_fence { * Command Buffers */ -#define HL_MAX_CB_SIZE 0x200000 /* 2MB */ - /** * struct hl_cb_mgr - describes a Command Buffer Manager. * @cb_lock: protects cb_handles. diff --git a/include/uapi/misc/habanalabs.h b/include/uapi/misc/habanalabs.h index e387d9e560b3..716e70750f23 100644 --- a/include/uapi/misc/habanalabs.h +++ b/include/uapi/misc/habanalabs.h @@ -192,13 +192,15 @@ struct hl_info_args { /* Opcode to destroy previously created command buffer */ #define HL_CB_OP_DESTROY 1 +#define HL_MAX_CB_SIZE 0x200000 /* 2MB */ + struct hl_cb_in { /* Handle of CB or 0 if we want to create one */ __u64 cb_handle; /* HL_CB_OP_* */ __u32 op; - /* Size of CB. Maximum size is 2MB. The minimum size that will be - * allocated, regardless of this parameter's value, is PAGE_SIZE + /* Size of CB. Maximum size is HL_MAX_CB_SIZE. The minimum size that + * will be allocated, regardless of this parameter's value, is PAGE_SIZE */ __u32 cb_size; /* Context ID - Currently not in use */ @@ -244,6 +246,8 @@ struct hl_cs_chunk { #define HL_CS_STATUS_SUCCESS 0 +#define HL_MAX_JOBS_PER_CS 512 + struct hl_cs_in { /* this holds address of array of hl_cs_chunk for restore phase */ __u64 chunks_restore; @@ -253,9 +257,13 @@ struct hl_cs_in { * Currently not in use */ __u64 chunks_store; - /* Number of chunks in restore phase array */ + /* Number of chunks in restore phase array. Maximum number is + * HL_MAX_JOBS_PER_CS + */ __u32 num_chunks_restore; - /* Number of chunks in execution array */ + /* Number of chunks in execution array. Maximum number is + * HL_MAX_JOBS_PER_CS + */ __u32 num_chunks_execute; /* Number of chunks in restore phase array - Currently not in use */ __u32 num_chunks_store; From 7f74d4d335f1bdcb51fca584d5ad065c4ff996ac Mon Sep 17 00:00:00 2001 From: Omer Shpigelman Date: Mon, 12 Aug 2019 11:48:46 +0300 Subject: [PATCH 182/198] habanalabs: re-factor memory module code Some of the functions in the memory module code were too long and/or contained multiple operations that are not always done together. Re-factor the code by dividing those functions to smaller functions which are more readable and maintainable. Signed-off-by: Omer Shpigelman Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/goya/goya.c | 2 +- drivers/misc/habanalabs/habanalabs.h | 4 +- drivers/misc/habanalabs/memory.c | 283 +++++++++++++++------------ 3 files changed, 159 insertions(+), 130 deletions(-) diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index 4e767e1d78e4..9712122d6cb1 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -3935,7 +3935,7 @@ static int goya_parse_cb_no_ext_queue(struct hl_device *hdev, return 0; dev_err(hdev->dev, - "Internal CB address %px + 0x%x is not in SRAM nor in DRAM\n", + "Internal CB address 0x%px + 0x%x is not in SRAM nor in DRAM\n", parser->user_cb, parser->user_cb_size); return -EFAULT; diff --git a/drivers/misc/habanalabs/habanalabs.h b/drivers/misc/habanalabs/habanalabs.h index 2a5344cc1a60..78aef59e690b 100644 --- a/drivers/misc/habanalabs/habanalabs.h +++ b/drivers/misc/habanalabs/habanalabs.h @@ -692,7 +692,7 @@ struct hl_ctx_mgr { * @sgt: pointer to the scatter-gather table that holds the pages. * @dir: for DMA unmapping, the direction must be supplied, so save it. * @debugfs_list: node in debugfs list of command submissions. - * @addr: user-space virtual pointer to the start of the memory area. + * @addr: user-space virtual address of the start of the memory area. * @size: size of the memory area to pin & map. * @dma_mapped: true if the SG was mapped to DMA addresses, false otherwise. */ @@ -1527,7 +1527,7 @@ void hl_vm_fini(struct hl_device *hdev); int hl_pin_host_memory(struct hl_device *hdev, u64 addr, u64 size, struct hl_userptr *userptr); -int hl_unpin_host_memory(struct hl_device *hdev, struct hl_userptr *userptr); +void hl_unpin_host_memory(struct hl_device *hdev, struct hl_userptr *userptr); void hl_userptr_delete_list(struct hl_device *hdev, struct list_head *userptr_list); bool hl_userptr_is_pinned(struct hl_device *hdev, u64 addr, u32 size, diff --git a/drivers/misc/habanalabs/memory.c b/drivers/misc/habanalabs/memory.c index 365fb0cb8dff..8ade9886a5a7 100644 --- a/drivers/misc/habanalabs/memory.c +++ b/drivers/misc/habanalabs/memory.c @@ -159,20 +159,19 @@ pages_pack_err: } /* - * get_userptr_from_host_va - initialize userptr structure from given host - * virtual address - * - * @hdev : habanalabs device structure - * @args : parameters containing the virtual address and size - * @p_userptr : pointer to result userptr structure + * dma_map_host_va - DMA mapping of the given host virtual address. + * @hdev: habanalabs device structure + * @addr: the host virtual address of the memory area + * @size: the size of the memory area + * @p_userptr: pointer to result userptr structure * * This function does the following: * - Allocate userptr structure * - Pin the given host memory using the userptr structure * - Perform DMA mapping to have the DMA addresses of the pages */ -static int get_userptr_from_host_va(struct hl_device *hdev, - struct hl_mem_in *args, struct hl_userptr **p_userptr) +static int dma_map_host_va(struct hl_device *hdev, u64 addr, u64 size, + struct hl_userptr **p_userptr) { struct hl_userptr *userptr; int rc; @@ -183,8 +182,7 @@ static int get_userptr_from_host_va(struct hl_device *hdev, goto userptr_err; } - rc = hl_pin_host_memory(hdev, args->map_host.host_virt_addr, - args->map_host.mem_size, userptr); + rc = hl_pin_host_memory(hdev, addr, size, userptr); if (rc) { dev_err(hdev->dev, "Failed to pin host memory\n"); goto pin_err; @@ -215,16 +213,16 @@ userptr_err: } /* - * free_userptr - free userptr structure - * - * @hdev : habanalabs device structure - * @userptr : userptr to free + * dma_unmap_host_va - DMA unmapping of the given host virtual address. + * @hdev: habanalabs device structure + * @userptr: userptr to free * * This function does the following: * - Unpins the physical pages * - Frees the userptr structure */ -static void free_userptr(struct hl_device *hdev, struct hl_userptr *userptr) +static void dma_unmap_host_va(struct hl_device *hdev, + struct hl_userptr *userptr) { hl_unpin_host_memory(hdev, userptr); kfree(userptr); @@ -253,10 +251,9 @@ static void dram_pg_pool_do_release(struct kref *ref) } /* - * free_phys_pg_pack - free physical page pack - * - * @hdev : habanalabs device structure - * @phys_pg_pack : physical page pack to free + * free_phys_pg_pack - free physical page pack + * @hdev: habanalabs device structure + * @phys_pg_pack: physical page pack to free * * This function does the following: * - For DRAM memory only, iterate over the pack and free each physical block @@ -264,7 +261,7 @@ static void dram_pg_pool_do_release(struct kref *ref) * - Free the hl_vm_phys_pg_pack structure */ static void free_phys_pg_pack(struct hl_device *hdev, - struct hl_vm_phys_pg_pack *phys_pg_pack) + struct hl_vm_phys_pg_pack *phys_pg_pack) { struct hl_vm *vm = &hdev->vm; u64 i; @@ -631,20 +628,18 @@ static u32 get_sg_info(struct scatterlist *sg, dma_addr_t *dma_addr) /* * init_phys_pg_pack_from_userptr - initialize physical page pack from host - * memory - * - * @ctx : current context - * @userptr : userptr to initialize from - * @pphys_pg_pack : res pointer + * memory + * @asid: current context ASID + * @userptr: userptr to initialize from + * @pphys_pg_pack: result pointer * * This function does the following: * - Pin the physical pages related to the given virtual block * - Create a physical page pack from the physical pages related to the given * virtual block */ -static int init_phys_pg_pack_from_userptr(struct hl_ctx *ctx, - struct hl_userptr *userptr, - struct hl_vm_phys_pg_pack **pphys_pg_pack) +static int init_phys_pg_pack_from_userptr(u32 asid, struct hl_userptr *userptr, + struct hl_vm_phys_pg_pack **pphys_pg_pack) { struct hl_vm_phys_pg_pack *phys_pg_pack; struct scatterlist *sg; @@ -660,7 +655,7 @@ static int init_phys_pg_pack_from_userptr(struct hl_ctx *ctx, phys_pg_pack->vm_type = userptr->vm_type; phys_pg_pack->created_from_userptr = true; - phys_pg_pack->asid = ctx->asid; + phys_pg_pack->asid = asid; atomic_set(&phys_pg_pack->mapping_cnt, 1); /* Only if all dma_addrs are aligned to 2MB and their @@ -731,19 +726,18 @@ page_pack_arr_mem_err: } /* - * map_phys_page_pack - maps the physical page pack - * - * @ctx : current context - * @vaddr : start address of the virtual area to map from - * @phys_pg_pack : the pack of physical pages to map to + * map_phys_pg_pack - maps the physical page pack. + * @ctx: current context + * @vaddr: start address of the virtual area to map from + * @phys_pg_pack: the pack of physical pages to map to * * This function does the following: * - Maps each chunk of virtual memory to matching physical chunk * - Stores number of successful mappings in the given argument - * - Returns 0 on success, error code otherwise. + * - Returns 0 on success, error code otherwise */ -static int map_phys_page_pack(struct hl_ctx *ctx, u64 vaddr, - struct hl_vm_phys_pg_pack *phys_pg_pack) +static int map_phys_pg_pack(struct hl_ctx *ctx, u64 vaddr, + struct hl_vm_phys_pg_pack *phys_pg_pack) { struct hl_device *hdev = ctx->hdev; u64 next_vaddr = vaddr, paddr, mapped_pg_cnt = 0, i; @@ -783,6 +777,36 @@ err: return rc; } +/* + * unmap_phys_pg_pack - unmaps the physical page pack + * @ctx: current context + * @vaddr: start address of the virtual area to unmap + * @phys_pg_pack: the pack of physical pages to unmap + */ +static void unmap_phys_pg_pack(struct hl_ctx *ctx, u64 vaddr, + struct hl_vm_phys_pg_pack *phys_pg_pack) +{ + struct hl_device *hdev = ctx->hdev; + u64 next_vaddr, i; + u32 page_size; + + page_size = phys_pg_pack->page_size; + next_vaddr = vaddr; + + for (i = 0 ; i < phys_pg_pack->npages ; i++, next_vaddr += page_size) { + if (hl_mmu_unmap(ctx, next_vaddr, page_size)) + dev_warn_ratelimited(hdev->dev, + "unmap failed for vaddr: 0x%llx\n", next_vaddr); + + /* + * unmapping on Palladium can be really long, so avoid a CPU + * soft lockup bug by sleeping a little between unmapping pages + */ + if (hdev->pldm) + usleep_range(500, 1000); + } +} + static int get_paddr_from_handle(struct hl_ctx *ctx, struct hl_mem_in *args, u64 *paddr) { @@ -839,18 +863,21 @@ static int map_device_va(struct hl_ctx *ctx, struct hl_mem_in *args, *device_addr = 0; if (is_userptr) { - rc = get_userptr_from_host_va(hdev, args, &userptr); + u64 addr = args->map_host.host_virt_addr, + size = args->map_host.mem_size; + + rc = dma_map_host_va(hdev, addr, size, &userptr); if (rc) { dev_err(hdev->dev, "failed to get userptr from va\n"); return rc; } - rc = init_phys_pg_pack_from_userptr(ctx, userptr, + rc = init_phys_pg_pack_from_userptr(ctx->asid, userptr, &phys_pg_pack); if (rc) { dev_err(hdev->dev, "unable to init page pack for vaddr 0x%llx\n", - args->map_host.host_virt_addr); + addr); goto init_page_pack_err; } @@ -909,7 +936,7 @@ static int map_device_va(struct hl_ctx *ctx, struct hl_mem_in *args, mutex_lock(&ctx->mmu_lock); - rc = map_phys_page_pack(ctx, ret_vaddr, phys_pg_pack); + rc = map_phys_pg_pack(ctx, ret_vaddr, phys_pg_pack); if (rc) { mutex_unlock(&ctx->mmu_lock); dev_err(hdev->dev, "mapping page pack failed for handle %u\n", @@ -955,7 +982,7 @@ shared_err: free_phys_pg_pack(hdev, phys_pg_pack); init_page_pack_err: if (is_userptr) - free_userptr(hdev, userptr); + dma_unmap_host_va(hdev, userptr); return rc; } @@ -977,8 +1004,6 @@ static int unmap_device_va(struct hl_ctx *ctx, u64 vaddr) struct hl_vm_hash_node *hnode = NULL; struct hl_userptr *userptr = NULL; enum vm_type_t *vm_type; - u64 next_vaddr, i; - u32 page_size; bool is_userptr; int rc; @@ -1004,8 +1029,8 @@ static int unmap_device_va(struct hl_ctx *ctx, u64 vaddr) if (*vm_type == VM_TYPE_USERPTR) { is_userptr = true; userptr = hnode->ptr; - rc = init_phys_pg_pack_from_userptr(ctx, userptr, - &phys_pg_pack); + rc = init_phys_pg_pack_from_userptr(ctx->asid, userptr, + &phys_pg_pack); if (rc) { dev_err(hdev->dev, "unable to init page pack for vaddr 0x%llx\n", @@ -1029,24 +1054,11 @@ static int unmap_device_va(struct hl_ctx *ctx, u64 vaddr) goto mapping_cnt_err; } - page_size = phys_pg_pack->page_size; - vaddr &= ~(((u64) page_size) - 1); - - next_vaddr = vaddr; + vaddr &= ~(((u64) phys_pg_pack->page_size) - 1); mutex_lock(&ctx->mmu_lock); - for (i = 0 ; i < phys_pg_pack->npages ; i++, next_vaddr += page_size) { - if (hl_mmu_unmap(ctx, next_vaddr, page_size)) - dev_warn_ratelimited(hdev->dev, - "unmap failed for vaddr: 0x%llx\n", next_vaddr); - - /* unmapping on Palladium can be really long, so avoid a CPU - * soft lockup bug by sleeping a little between unmapping pages - */ - if (hdev->pldm) - usleep_range(500, 1000); - } + unmap_phys_pg_pack(ctx, vaddr, phys_pg_pack); hdev->asic_funcs->mmu_invalidate_cache(hdev, true); @@ -1064,7 +1076,7 @@ static int unmap_device_va(struct hl_ctx *ctx, u64 vaddr) if (is_userptr) { free_phys_pg_pack(hdev, phys_pg_pack); - free_userptr(hdev, userptr); + dma_unmap_host_va(hdev, userptr); } return 0; @@ -1203,57 +1215,17 @@ out: return rc; } -/* - * hl_pin_host_memory - pins a chunk of host memory - * - * @hdev : pointer to the habanalabs device structure - * @addr : the user-space virtual address of the memory area - * @size : the size of the memory area - * @userptr : pointer to hl_userptr structure - * - * This function does the following: - * - Pins the physical pages - * - Create a SG list from those pages - */ -int hl_pin_host_memory(struct hl_device *hdev, u64 addr, u64 size, - struct hl_userptr *userptr) +static int get_user_memory(struct hl_device *hdev, u64 addr, u64 size, + u32 npages, u64 start, u32 offset, + struct hl_userptr *userptr) { - u64 start, end; - u32 npages, offset; int rc; - if (!size) { - dev_err(hdev->dev, "size to pin is invalid - %llu\n", size); - return -EINVAL; - } - if (!access_ok((void __user *) (uintptr_t) addr, size)) { dev_err(hdev->dev, "user pointer is invalid - 0x%llx\n", addr); return -EFAULT; } - /* - * If the combination of the address and size requested for this memory - * region causes an integer overflow, return error. - */ - if (((addr + size) < addr) || - PAGE_ALIGN(addr + size) < (addr + size)) { - dev_err(hdev->dev, - "user pointer 0x%llx + %llu causes integer overflow\n", - addr, size); - return -EINVAL; - } - - start = addr & PAGE_MASK; - offset = addr & ~PAGE_MASK; - end = PAGE_ALIGN(addr + size); - npages = (end - start) >> PAGE_SHIFT; - - userptr->size = size; - userptr->addr = addr; - userptr->dma_mapped = false; - INIT_LIST_HEAD(&userptr->job_node); - userptr->vec = frame_vector_create(npages); if (!userptr->vec) { dev_err(hdev->dev, "Failed to create frame vector\n"); @@ -1279,17 +1251,82 @@ int hl_pin_host_memory(struct hl_device *hdev, u64 addr, u64 size, goto put_framevec; } - userptr->sgt = kzalloc(sizeof(*userptr->sgt), GFP_ATOMIC); - if (!userptr->sgt) { - rc = -ENOMEM; - goto put_framevec; - } - rc = sg_alloc_table_from_pages(userptr->sgt, frame_vector_pages(userptr->vec), npages, offset, size, GFP_ATOMIC); if (rc < 0) { dev_err(hdev->dev, "failed to create SG table from pages\n"); + goto put_framevec; + } + + return 0; + +put_framevec: + put_vaddr_frames(userptr->vec); +destroy_framevec: + frame_vector_destroy(userptr->vec); + return rc; +} + +/* + * hl_pin_host_memory - pins a chunk of host memory. + * @hdev: pointer to the habanalabs device structure + * @addr: the host virtual address of the memory area + * @size: the size of the memory area + * @userptr: pointer to hl_userptr structure + * + * This function does the following: + * - Pins the physical pages + * - Create an SG list from those pages + */ +int hl_pin_host_memory(struct hl_device *hdev, u64 addr, u64 size, + struct hl_userptr *userptr) +{ + u64 start, end; + u32 npages, offset; + int rc; + + if (!size) { + dev_err(hdev->dev, "size to pin is invalid - %llu\n", size); + return -EINVAL; + } + + /* + * If the combination of the address and size requested for this memory + * region causes an integer overflow, return error. + */ + if (((addr + size) < addr) || + PAGE_ALIGN(addr + size) < (addr + size)) { + dev_err(hdev->dev, + "user pointer 0x%llx + %llu causes integer overflow\n", + addr, size); + return -EINVAL; + } + + /* + * This function can be called also from data path, hence use atomic + * always as it is not a big allocation. + */ + userptr->sgt = kzalloc(sizeof(*userptr->sgt), GFP_ATOMIC); + if (!userptr->sgt) + return -ENOMEM; + + start = addr & PAGE_MASK; + offset = addr & ~PAGE_MASK; + end = PAGE_ALIGN(addr + size); + npages = (end - start) >> PAGE_SHIFT; + + userptr->size = size; + userptr->addr = addr; + userptr->dma_mapped = false; + INIT_LIST_HEAD(&userptr->job_node); + + rc = get_user_memory(hdev, addr, size, npages, start, offset, + userptr); + if (rc) { + dev_err(hdev->dev, + "failed to get user memory for address 0x%llx\n", + addr); goto free_sgt; } @@ -1299,34 +1336,28 @@ int hl_pin_host_memory(struct hl_device *hdev, u64 addr, u64 size, free_sgt: kfree(userptr->sgt); -put_framevec: - put_vaddr_frames(userptr->vec); -destroy_framevec: - frame_vector_destroy(userptr->vec); return rc; } /* - * hl_unpin_host_memory - unpins a chunk of host memory - * - * @hdev : pointer to the habanalabs device structure - * @userptr : pointer to hl_userptr structure + * hl_unpin_host_memory - unpins a chunk of host memory. + * @hdev: pointer to the habanalabs device structure + * @userptr: pointer to hl_userptr structure * * This function does the following: * - Unpins the physical pages related to the host memory * - Free the SG list */ -int hl_unpin_host_memory(struct hl_device *hdev, struct hl_userptr *userptr) +void hl_unpin_host_memory(struct hl_device *hdev, struct hl_userptr *userptr) { struct page **pages; hl_debugfs_remove_userptr(hdev, userptr); if (userptr->dma_mapped) - hdev->asic_funcs->hl_dma_unmap_sg(hdev, - userptr->sgt->sgl, - userptr->sgt->nents, - userptr->dir); + hdev->asic_funcs->hl_dma_unmap_sg(hdev, userptr->sgt->sgl, + userptr->sgt->nents, + userptr->dir); pages = frame_vector_pages(userptr->vec); if (!IS_ERR(pages)) { @@ -1342,8 +1373,6 @@ int hl_unpin_host_memory(struct hl_device *hdev, struct hl_userptr *userptr) sg_free_table(userptr->sgt); kfree(userptr->sgt); - - return 0; } /* @@ -1627,7 +1656,7 @@ void hl_vm_ctx_fini(struct hl_ctx *ctx) idr_for_each_entry(&vm->phys_pg_pack_handles, phys_pg_list, i) if (phys_pg_list->asid == ctx->asid) { dev_dbg(hdev->dev, - "page list 0x%p of asid %d is still alive\n", + "page list 0x%px of asid %d is still alive\n", phys_pg_list, ctx->asid); atomic64_sub(phys_pg_list->total_size, &hdev->dram_used_mem); From 7b6e4ea0f7b16ab292df5e67f5d847929f8e4d3e Mon Sep 17 00:00:00 2001 From: Omer Shpigelman Date: Thu, 14 Nov 2019 18:23:53 +0000 Subject: [PATCH 183/198] habanalabs: type specific MMU cache invalidation Add the ability to invalidate the necessary MMU cache only. This ability is a prerequisite for future ASICs support. Note that in Goya ASIC, a single cache is used for both host/DRAM mappings and hence this patch should not have any effect on current behavior. Signed-off-by: Omer Shpigelman Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/goya/goya.c | 6 ++++-- drivers/misc/habanalabs/habanalabs.h | 11 ++++++----- drivers/misc/habanalabs/memory.c | 4 ++-- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index 9712122d6cb1..3c22fb96a26f 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -2463,7 +2463,8 @@ int goya_mmu_init(struct hl_device *hdev) WREG32_AND(mmSTLB_STLB_FEATURE_EN, (~STLB_STLB_FEATURE_EN_FOLLOWER_EN_MASK)); - hdev->asic_funcs->mmu_invalidate_cache(hdev, true); + hdev->asic_funcs->mmu_invalidate_cache(hdev, true, + VM_TYPE_USERPTR | VM_TYPE_PHYS_PACK); WREG32(mmMMU_MMU_ENABLE, 1); WREG32(mmMMU_SPI_MASK, 0xF); @@ -4845,7 +4846,8 @@ static void goya_mmu_prepare(struct hl_device *hdev, u32 asid) goya_mmu_prepare_reg(hdev, goya_mmu_regs[i], asid); } -static void goya_mmu_invalidate_cache(struct hl_device *hdev, bool is_hard) +static void goya_mmu_invalidate_cache(struct hl_device *hdev, bool is_hard, + u32 flags) { struct goya_device *goya = hdev->asic_specific; u32 status, timeout_usec; diff --git a/drivers/misc/habanalabs/habanalabs.h b/drivers/misc/habanalabs/habanalabs.h index 78aef59e690b..36d05c32f7ec 100644 --- a/drivers/misc/habanalabs/habanalabs.h +++ b/drivers/misc/habanalabs/habanalabs.h @@ -114,8 +114,8 @@ struct hw_queue_properties { * @VM_TYPE_PHYS_PACK: mapping of DRAM memory to device virtual address. */ enum vm_type_t { - VM_TYPE_USERPTR, - VM_TYPE_PHYS_PACK + VM_TYPE_USERPTR = 0x1, + VM_TYPE_PHYS_PACK = 0x2 }; /** @@ -483,8 +483,8 @@ enum hl_pll_frequency { * @get_events_stat: retrieve event queue entries histogram. * @read_pte: read MMU page table entry from DRAM. * @write_pte: write MMU page table entry to DRAM. - * @mmu_invalidate_cache: flush MMU STLB cache, either with soft (L1 only) or - * hard (L0 & L1) flush. + * @mmu_invalidate_cache: flush MMU STLB host/DRAM cache, either with soft + * (L1 only) or hard (L0 & L1) flush. * @mmu_invalidate_cache_range: flush specific MMU STLB cache lines with * ASID-VA-size mask. * @send_heartbeat: send is-alive packet to ArmCP and verify response. @@ -565,7 +565,8 @@ struct hl_asic_funcs { u32 *size); u64 (*read_pte)(struct hl_device *hdev, u64 addr); void (*write_pte)(struct hl_device *hdev, u64 addr, u64 val); - void (*mmu_invalidate_cache)(struct hl_device *hdev, bool is_hard); + void (*mmu_invalidate_cache)(struct hl_device *hdev, bool is_hard, + u32 flags); void (*mmu_invalidate_cache_range)(struct hl_device *hdev, bool is_hard, u32 asid, u64 va, u64 size); int (*send_heartbeat)(struct hl_device *hdev); diff --git a/drivers/misc/habanalabs/memory.c b/drivers/misc/habanalabs/memory.c index 8ade9886a5a7..12db6609da27 100644 --- a/drivers/misc/habanalabs/memory.c +++ b/drivers/misc/habanalabs/memory.c @@ -944,7 +944,7 @@ static int map_device_va(struct hl_ctx *ctx, struct hl_mem_in *args, goto map_err; } - hdev->asic_funcs->mmu_invalidate_cache(hdev, false); + hdev->asic_funcs->mmu_invalidate_cache(hdev, false, *vm_type); mutex_unlock(&ctx->mmu_lock); @@ -1060,7 +1060,7 @@ static int unmap_device_va(struct hl_ctx *ctx, u64 vaddr) unmap_phys_pg_pack(ctx, vaddr, phys_pg_pack); - hdev->asic_funcs->mmu_invalidate_cache(hdev, true); + hdev->asic_funcs->mmu_invalidate_cache(hdev, true, *vm_type); mutex_unlock(&ctx->mmu_lock); From 30919edef243e9dc91a3c65e5b1059d481e597e9 Mon Sep 17 00:00:00 2001 From: Omer Shpigelman Date: Thu, 14 Nov 2019 18:23:54 +0000 Subject: [PATCH 184/198] habanalabs: re-factor MMU masks and documentation Some cosmetics around the MMU code to make it more self-explanatory. Signed-off-by: Omer Shpigelman Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/debugfs.c | 6 ++-- .../include/hw_ip/mmu/mmu_general.h | 6 ++-- drivers/misc/habanalabs/mmu.c | 36 +++++++++---------- 3 files changed, 23 insertions(+), 25 deletions(-) diff --git a/drivers/misc/habanalabs/debugfs.c b/drivers/misc/habanalabs/debugfs.c index 87f37ac31ccd..1e1fa619a225 100644 --- a/drivers/misc/habanalabs/debugfs.c +++ b/drivers/misc/habanalabs/debugfs.c @@ -345,7 +345,7 @@ static inline u64 get_hop4_pte_addr(struct hl_ctx *ctx, u64 hop_addr, static inline u64 get_next_hop_addr(u64 curr_pte) { if (curr_pte & PAGE_PRESENT_MASK) - return curr_pte & PHYS_ADDR_MASK; + return curr_pte & HOP_PHYS_ADDR_MASK; else return ULLONG_MAX; } @@ -535,7 +535,7 @@ static int device_va_to_pa(struct hl_device *hdev, u64 virt_addr, { struct hl_ctx *ctx = hdev->compute_ctx; u64 hop_addr, hop_pte_addr, hop_pte; - u64 offset_mask = HOP4_MASK | OFFSET_MASK; + u64 offset_mask = HOP4_MASK | FLAGS_MASK; int rc = 0; if (!ctx) { @@ -579,7 +579,7 @@ static int device_va_to_pa(struct hl_device *hdev, u64 virt_addr, hop_pte_addr = get_hop4_pte_addr(ctx, hop_addr, virt_addr); hop_pte = hdev->asic_funcs->read_pte(hdev, hop_pte_addr); - offset_mask = OFFSET_MASK; + offset_mask = FLAGS_MASK; } if (!(hop_pte & PAGE_PRESENT_MASK)) diff --git a/drivers/misc/habanalabs/include/hw_ip/mmu/mmu_general.h b/drivers/misc/habanalabs/include/hw_ip/mmu/mmu_general.h index 71ea3c3e8ba3..74a5502b8c4e 100644 --- a/drivers/misc/habanalabs/include/hw_ip/mmu/mmu_general.h +++ b/drivers/misc/habanalabs/include/hw_ip/mmu/mmu_general.h @@ -17,13 +17,12 @@ #define PAGE_PRESENT_MASK 0x0000000000001ull #define SWAP_OUT_MASK 0x0000000000004ull #define LAST_MASK 0x0000000000800ull -#define PHYS_ADDR_MASK 0xFFFFFFFFFFFFF000ull #define HOP0_MASK 0x3000000000000ull #define HOP1_MASK 0x0FF8000000000ull #define HOP2_MASK 0x0007FC0000000ull #define HOP3_MASK 0x000003FE00000ull #define HOP4_MASK 0x00000001FF000ull -#define OFFSET_MASK 0x0000000000FFFull +#define FLAGS_MASK 0x0000000000FFFull #define HOP0_SHIFT 48 #define HOP1_SHIFT 39 @@ -31,8 +30,7 @@ #define HOP3_SHIFT 21 #define HOP4_SHIFT 12 -#define PTE_PHYS_ADDR_SHIFT 12 -#define PTE_PHYS_ADDR_MASK ~OFFSET_MASK +#define HOP_PHYS_ADDR_MASK (~FLAGS_MASK) #define HL_PTE_SIZE sizeof(u64) #define HOP_TABLE_SIZE PAGE_SIZE_4KB diff --git a/drivers/misc/habanalabs/mmu.c b/drivers/misc/habanalabs/mmu.c index 176c315836f1..21b4e3281b3e 100644 --- a/drivers/misc/habanalabs/mmu.c +++ b/drivers/misc/habanalabs/mmu.c @@ -105,8 +105,8 @@ static inline void write_pte(struct hl_ctx *ctx, u64 shadow_pte_addr, u64 val) * clear the 12 LSBs and translate the shadow hop to its associated * physical hop, and add back the original 12 LSBs. */ - u64 phys_val = get_phys_addr(ctx, val & PTE_PHYS_ADDR_MASK) | - (val & OFFSET_MASK); + u64 phys_val = get_phys_addr(ctx, val & HOP_PHYS_ADDR_MASK) | + (val & FLAGS_MASK); ctx->hdev->asic_funcs->write_pte(ctx->hdev, get_phys_addr(ctx, shadow_pte_addr), @@ -199,7 +199,7 @@ static inline u64 get_hop4_pte_addr(struct hl_ctx *ctx, u64 hop_addr, u64 vaddr) static inline u64 get_next_hop_addr(struct hl_ctx *ctx, u64 curr_pte) { if (curr_pte & PAGE_PRESENT_MASK) - return curr_pte & PHYS_ADDR_MASK; + return curr_pte & HOP_PHYS_ADDR_MASK; else return ULLONG_MAX; } @@ -288,23 +288,23 @@ static int dram_default_mapping_init(struct hl_ctx *ctx) } /* need only pte 0 in hops 0 and 1 */ - pte_val = (hop1_addr & PTE_PHYS_ADDR_MASK) | PAGE_PRESENT_MASK; + pte_val = (hop1_addr & HOP_PHYS_ADDR_MASK) | PAGE_PRESENT_MASK; write_pte(ctx, hop0_addr, pte_val); - pte_val = (hop2_addr & PTE_PHYS_ADDR_MASK) | PAGE_PRESENT_MASK; + pte_val = (hop2_addr & HOP_PHYS_ADDR_MASK) | PAGE_PRESENT_MASK; write_pte(ctx, hop1_addr, pte_val); get_pte(ctx, hop1_addr); hop2_pte_addr = hop2_addr; for (i = 0 ; i < num_of_hop3 ; i++) { - pte_val = (ctx->dram_default_hops[i] & PTE_PHYS_ADDR_MASK) | + pte_val = (ctx->dram_default_hops[i] & HOP_PHYS_ADDR_MASK) | PAGE_PRESENT_MASK; write_pte(ctx, hop2_pte_addr, pte_val); get_pte(ctx, hop2_addr); hop2_pte_addr += HL_PTE_SIZE; } - pte_val = (prop->mmu_dram_default_page_addr & PTE_PHYS_ADDR_MASK) | + pte_val = (prop->mmu_dram_default_page_addr & HOP_PHYS_ADDR_MASK) | LAST_MASK | PAGE_PRESENT_MASK; for (i = 0 ; i < num_of_hop3 ; i++) { @@ -400,8 +400,6 @@ int hl_mmu_init(struct hl_device *hdev) if (!hdev->mmu_enable) return 0; - /* MMU H/W init was already done in device hw_init() */ - hdev->mmu_pgt_pool = gen_pool_create(__ffs(prop->mmu_hop_table_size), -1); @@ -427,6 +425,8 @@ int hl_mmu_init(struct hl_device *hdev) goto err_pool_add; } + /* MMU H/W init will be done in device hw_init() */ + return 0; err_pool_add: @@ -450,10 +450,10 @@ void hl_mmu_fini(struct hl_device *hdev) if (!hdev->mmu_enable) return; + /* MMU H/W fini was already done in device hw_fini() */ + kvfree(hdev->mmu_shadow_hop0); gen_pool_destroy(hdev->mmu_pgt_pool); - - /* MMU H/W fini will be done in device hw_fini() */ } /** @@ -584,7 +584,7 @@ static int _hl_mmu_unmap(struct hl_ctx *ctx, u64 virt_addr) if (hdev->dram_default_page_mapping && is_dram_addr) { u64 default_pte = (prop->mmu_dram_default_page_addr & - PTE_PHYS_ADDR_MASK) | LAST_MASK | + HOP_PHYS_ADDR_MASK) | LAST_MASK | PAGE_PRESENT_MASK; if (curr_pte == default_pte) { dev_err(hdev->dev, @@ -773,7 +773,7 @@ static int _hl_mmu_map(struct hl_ctx *ctx, u64 virt_addr, u64 phys_addr, if (hdev->dram_default_page_mapping && is_dram_addr) { u64 default_pte = (prop->mmu_dram_default_page_addr & - PTE_PHYS_ADDR_MASK) | LAST_MASK | + HOP_PHYS_ADDR_MASK) | LAST_MASK | PAGE_PRESENT_MASK; if (curr_pte != default_pte) { @@ -813,7 +813,7 @@ static int _hl_mmu_map(struct hl_ctx *ctx, u64 virt_addr, u64 phys_addr, goto err; } - curr_pte = (phys_addr & PTE_PHYS_ADDR_MASK) | LAST_MASK + curr_pte = (phys_addr & HOP_PHYS_ADDR_MASK) | LAST_MASK | PAGE_PRESENT_MASK; if (is_huge) @@ -823,25 +823,25 @@ static int _hl_mmu_map(struct hl_ctx *ctx, u64 virt_addr, u64 phys_addr, if (hop1_new) { curr_pte = - (hop1_addr & PTE_PHYS_ADDR_MASK) | PAGE_PRESENT_MASK; + (hop1_addr & HOP_PHYS_ADDR_MASK) | PAGE_PRESENT_MASK; write_pte(ctx, hop0_pte_addr, curr_pte); } if (hop2_new) { curr_pte = - (hop2_addr & PTE_PHYS_ADDR_MASK) | PAGE_PRESENT_MASK; + (hop2_addr & HOP_PHYS_ADDR_MASK) | PAGE_PRESENT_MASK; write_pte(ctx, hop1_pte_addr, curr_pte); get_pte(ctx, hop1_addr); } if (hop3_new) { curr_pte = - (hop3_addr & PTE_PHYS_ADDR_MASK) | PAGE_PRESENT_MASK; + (hop3_addr & HOP_PHYS_ADDR_MASK) | PAGE_PRESENT_MASK; write_pte(ctx, hop2_pte_addr, curr_pte); get_pte(ctx, hop2_addr); } if (!is_huge) { if (hop4_new) { - curr_pte = (hop4_addr & PTE_PHYS_ADDR_MASK) | + curr_pte = (hop4_addr & HOP_PHYS_ADDR_MASK) | PAGE_PRESENT_MASK; write_pte(ctx, hop3_pte_addr, curr_pte); get_pte(ctx, hop3_addr); From 54bb67444ea3f388756c5955db52ef62eb4ba3b9 Mon Sep 17 00:00:00 2001 From: Omer Shpigelman Date: Thu, 14 Nov 2019 18:23:55 +0000 Subject: [PATCH 185/198] habanalabs: split MMU properties to PCI/DRAM Split the properties used for MMU mappings to DRAM and PCI (host) types. This is a prerequisite for future ASICs support. Note that in Goya ASIC, the PMMU and DMMU are the same (except of page sizes) as only one MMU mechanism is used for both of the mapping types. Hence this patch should not have any effect on current behavior. Signed-off-by: Omer Shpigelman Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/debugfs.c | 90 +++++++---- drivers/misc/habanalabs/goya/goya.c | 17 ++ drivers/misc/habanalabs/habanalabs.h | 114 ++++++++----- .../include/hw_ip/mmu/mmu_general.h | 1 - drivers/misc/habanalabs/memory.c | 45 +++--- drivers/misc/habanalabs/mmu.c | 151 +++++++++++------- 6 files changed, 269 insertions(+), 149 deletions(-) diff --git a/drivers/misc/habanalabs/debugfs.c b/drivers/misc/habanalabs/debugfs.c index 1e1fa619a225..1cf75010a379 100644 --- a/drivers/misc/habanalabs/debugfs.c +++ b/drivers/misc/habanalabs/debugfs.c @@ -307,39 +307,51 @@ static inline u64 get_hop0_addr(struct hl_ctx *ctx) (ctx->asid * ctx->hdev->asic_prop.mmu_hop_table_size); } -static inline u64 get_hop0_pte_addr(struct hl_ctx *ctx, u64 hop_addr, - u64 virt_addr) +static inline u64 get_hopN_pte_addr(struct hl_ctx *ctx, u64 hop_addr, + u64 virt_addr, u64 mask, u64 shift) { return hop_addr + ctx->hdev->asic_prop.mmu_pte_size * - ((virt_addr & HOP0_MASK) >> HOP0_SHIFT); + ((virt_addr & mask) >> shift); } -static inline u64 get_hop1_pte_addr(struct hl_ctx *ctx, u64 hop_addr, - u64 virt_addr) +static inline u64 get_hop0_pte_addr(struct hl_ctx *ctx, + struct hl_mmu_properties *mmu_specs, + u64 hop_addr, u64 vaddr) { - return hop_addr + ctx->hdev->asic_prop.mmu_pte_size * - ((virt_addr & HOP1_MASK) >> HOP1_SHIFT); + return get_hopN_pte_addr(ctx, hop_addr, vaddr, mmu_specs->hop0_mask, + mmu_specs->hop0_shift); } -static inline u64 get_hop2_pte_addr(struct hl_ctx *ctx, u64 hop_addr, - u64 virt_addr) +static inline u64 get_hop1_pte_addr(struct hl_ctx *ctx, + struct hl_mmu_properties *mmu_specs, + u64 hop_addr, u64 vaddr) { - return hop_addr + ctx->hdev->asic_prop.mmu_pte_size * - ((virt_addr & HOP2_MASK) >> HOP2_SHIFT); + return get_hopN_pte_addr(ctx, hop_addr, vaddr, mmu_specs->hop1_mask, + mmu_specs->hop1_shift); } -static inline u64 get_hop3_pte_addr(struct hl_ctx *ctx, u64 hop_addr, - u64 virt_addr) +static inline u64 get_hop2_pte_addr(struct hl_ctx *ctx, + struct hl_mmu_properties *mmu_specs, + u64 hop_addr, u64 vaddr) { - return hop_addr + ctx->hdev->asic_prop.mmu_pte_size * - ((virt_addr & HOP3_MASK) >> HOP3_SHIFT); + return get_hopN_pte_addr(ctx, hop_addr, vaddr, mmu_specs->hop2_mask, + mmu_specs->hop2_shift); } -static inline u64 get_hop4_pte_addr(struct hl_ctx *ctx, u64 hop_addr, - u64 virt_addr) +static inline u64 get_hop3_pte_addr(struct hl_ctx *ctx, + struct hl_mmu_properties *mmu_specs, + u64 hop_addr, u64 vaddr) { - return hop_addr + ctx->hdev->asic_prop.mmu_pte_size * - ((virt_addr & HOP4_MASK) >> HOP4_SHIFT); + return get_hopN_pte_addr(ctx, hop_addr, vaddr, mmu_specs->hop3_mask, + mmu_specs->hop3_shift); +} + +static inline u64 get_hop4_pte_addr(struct hl_ctx *ctx, + struct hl_mmu_properties *mmu_specs, + u64 hop_addr, u64 vaddr) +{ + return get_hopN_pte_addr(ctx, hop_addr, vaddr, mmu_specs->hop4_mask, + mmu_specs->hop4_shift); } static inline u64 get_next_hop_addr(u64 curr_pte) @@ -355,7 +367,10 @@ static int mmu_show(struct seq_file *s, void *data) struct hl_debugfs_entry *entry = s->private; struct hl_dbg_device_entry *dev_entry = entry->dev_entry; struct hl_device *hdev = dev_entry->hdev; + struct asic_fixed_properties *prop = &hdev->asic_prop; + struct hl_mmu_properties *mmu_prop; struct hl_ctx *ctx; + bool is_dram_addr; u64 hop0_addr = 0, hop0_pte_addr = 0, hop0_pte = 0, hop1_addr = 0, hop1_pte_addr = 0, hop1_pte = 0, @@ -377,33 +392,39 @@ static int mmu_show(struct seq_file *s, void *data) return 0; } + is_dram_addr = hl_mem_area_inside_range(virt_addr, prop->dmmu.page_size, + prop->va_space_dram_start_address, + prop->va_space_dram_end_address); + + mmu_prop = is_dram_addr ? &prop->dmmu : &prop->pmmu; + mutex_lock(&ctx->mmu_lock); /* the following lookup is copied from unmap() in mmu.c */ hop0_addr = get_hop0_addr(ctx); - hop0_pte_addr = get_hop0_pte_addr(ctx, hop0_addr, virt_addr); + hop0_pte_addr = get_hop0_pte_addr(ctx, mmu_prop, hop0_addr, virt_addr); hop0_pte = hdev->asic_funcs->read_pte(hdev, hop0_pte_addr); hop1_addr = get_next_hop_addr(hop0_pte); if (hop1_addr == ULLONG_MAX) goto not_mapped; - hop1_pte_addr = get_hop1_pte_addr(ctx, hop1_addr, virt_addr); + hop1_pte_addr = get_hop1_pte_addr(ctx, mmu_prop, hop1_addr, virt_addr); hop1_pte = hdev->asic_funcs->read_pte(hdev, hop1_pte_addr); hop2_addr = get_next_hop_addr(hop1_pte); if (hop2_addr == ULLONG_MAX) goto not_mapped; - hop2_pte_addr = get_hop2_pte_addr(ctx, hop2_addr, virt_addr); + hop2_pte_addr = get_hop2_pte_addr(ctx, mmu_prop, hop2_addr, virt_addr); hop2_pte = hdev->asic_funcs->read_pte(hdev, hop2_pte_addr); hop3_addr = get_next_hop_addr(hop2_pte); if (hop3_addr == ULLONG_MAX) goto not_mapped; - hop3_pte_addr = get_hop3_pte_addr(ctx, hop3_addr, virt_addr); + hop3_pte_addr = get_hop3_pte_addr(ctx, mmu_prop, hop3_addr, virt_addr); hop3_pte = hdev->asic_funcs->read_pte(hdev, hop3_pte_addr); if (!(hop3_pte & LAST_MASK)) { @@ -412,7 +433,8 @@ static int mmu_show(struct seq_file *s, void *data) if (hop4_addr == ULLONG_MAX) goto not_mapped; - hop4_pte_addr = get_hop4_pte_addr(ctx, hop4_addr, virt_addr); + hop4_pte_addr = get_hop4_pte_addr(ctx, mmu_prop, hop4_addr, + virt_addr); hop4_pte = hdev->asic_funcs->read_pte(hdev, hop4_pte_addr); if (!(hop4_pte & PAGE_PRESENT_MASK)) goto not_mapped; @@ -534,41 +556,50 @@ static int device_va_to_pa(struct hl_device *hdev, u64 virt_addr, u64 *phys_addr) { struct hl_ctx *ctx = hdev->compute_ctx; + struct asic_fixed_properties *prop = &hdev->asic_prop; + struct hl_mmu_properties *mmu_prop; u64 hop_addr, hop_pte_addr, hop_pte; u64 offset_mask = HOP4_MASK | FLAGS_MASK; int rc = 0; + bool is_dram_addr; if (!ctx) { dev_err(hdev->dev, "no ctx available\n"); return -EINVAL; } + is_dram_addr = hl_mem_area_inside_range(virt_addr, prop->dmmu.page_size, + prop->va_space_dram_start_address, + prop->va_space_dram_end_address); + + mmu_prop = is_dram_addr ? &prop->dmmu : &prop->pmmu; + mutex_lock(&ctx->mmu_lock); /* hop 0 */ hop_addr = get_hop0_addr(ctx); - hop_pte_addr = get_hop0_pte_addr(ctx, hop_addr, virt_addr); + hop_pte_addr = get_hop0_pte_addr(ctx, mmu_prop, hop_addr, virt_addr); hop_pte = hdev->asic_funcs->read_pte(hdev, hop_pte_addr); /* hop 1 */ hop_addr = get_next_hop_addr(hop_pte); if (hop_addr == ULLONG_MAX) goto not_mapped; - hop_pte_addr = get_hop1_pte_addr(ctx, hop_addr, virt_addr); + hop_pte_addr = get_hop1_pte_addr(ctx, mmu_prop, hop_addr, virt_addr); hop_pte = hdev->asic_funcs->read_pte(hdev, hop_pte_addr); /* hop 2 */ hop_addr = get_next_hop_addr(hop_pte); if (hop_addr == ULLONG_MAX) goto not_mapped; - hop_pte_addr = get_hop2_pte_addr(ctx, hop_addr, virt_addr); + hop_pte_addr = get_hop2_pte_addr(ctx, mmu_prop, hop_addr, virt_addr); hop_pte = hdev->asic_funcs->read_pte(hdev, hop_pte_addr); /* hop 3 */ hop_addr = get_next_hop_addr(hop_pte); if (hop_addr == ULLONG_MAX) goto not_mapped; - hop_pte_addr = get_hop3_pte_addr(ctx, hop_addr, virt_addr); + hop_pte_addr = get_hop3_pte_addr(ctx, mmu_prop, hop_addr, virt_addr); hop_pte = hdev->asic_funcs->read_pte(hdev, hop_pte_addr); if (!(hop_pte & LAST_MASK)) { @@ -576,7 +607,8 @@ static int device_va_to_pa(struct hl_device *hdev, u64 virt_addr, hop_addr = get_next_hop_addr(hop_pte); if (hop_addr == ULLONG_MAX) goto not_mapped; - hop_pte_addr = get_hop4_pte_addr(ctx, hop_addr, virt_addr); + hop_pte_addr = get_hop4_pte_addr(ctx, mmu_prop, hop_addr, + virt_addr); hop_pte = hdev->asic_funcs->read_pte(hdev, hop_pte_addr); offset_mask = FLAGS_MASK; diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index 3c22fb96a26f..3294a6a92f75 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -380,6 +380,23 @@ void goya_get_fixed_properties(struct hl_device *hdev) prop->mmu_hop0_tables_total_size = HOP0_TABLES_TOTAL_SIZE; prop->dram_page_size = PAGE_SIZE_2MB; + prop->dmmu.hop0_shift = HOP0_SHIFT; + prop->dmmu.hop1_shift = HOP1_SHIFT; + prop->dmmu.hop2_shift = HOP2_SHIFT; + prop->dmmu.hop3_shift = HOP3_SHIFT; + prop->dmmu.hop4_shift = HOP4_SHIFT; + prop->dmmu.hop0_mask = HOP0_MASK; + prop->dmmu.hop1_mask = HOP1_MASK; + prop->dmmu.hop2_mask = HOP2_MASK; + prop->dmmu.hop3_mask = HOP3_MASK; + prop->dmmu.hop4_mask = HOP4_MASK; + prop->dmmu.huge_page_size = PAGE_SIZE_2MB; + + /* No difference between PMMU and DMMU except of page size */ + memcpy(&prop->pmmu, &prop->dmmu, sizeof(prop->dmmu)); + prop->dmmu.page_size = PAGE_SIZE_2MB; + prop->pmmu.page_size = PAGE_SIZE_4KB; + prop->va_space_host_start_address = VA_HOST_SPACE_START; prop->va_space_host_end_address = VA_HOST_SPACE_END; prop->va_space_dram_start_address = VA_DDR_SPACE_START; diff --git a/drivers/misc/habanalabs/habanalabs.h b/drivers/misc/habanalabs/habanalabs.h index 36d05c32f7ec..00c949f4ccd1 100644 --- a/drivers/misc/habanalabs/habanalabs.h +++ b/drivers/misc/habanalabs/habanalabs.h @@ -130,6 +130,36 @@ enum hl_device_hw_state { HL_DEVICE_HW_STATE_DIRTY }; +/** + * struct hl_mmu_properties - ASIC specific MMU address translation properties. + * @hop0_shift: shift of hop 0 mask. + * @hop1_shift: shift of hop 1 mask. + * @hop2_shift: shift of hop 2 mask. + * @hop3_shift: shift of hop 3 mask. + * @hop4_shift: shift of hop 4 mask. + * @hop0_mask: mask to get the PTE address in hop 0. + * @hop1_mask: mask to get the PTE address in hop 1. + * @hop2_mask: mask to get the PTE address in hop 2. + * @hop3_mask: mask to get the PTE address in hop 3. + * @hop4_mask: mask to get the PTE address in hop 4. + * @page_size: default page size used to allocate memory. + * @huge_page_size: page size used to allocate memory with huge pages. + */ +struct hl_mmu_properties { + u64 hop0_shift; + u64 hop1_shift; + u64 hop2_shift; + u64 hop3_shift; + u64 hop4_shift; + u64 hop0_mask; + u64 hop1_mask; + u64 hop2_mask; + u64 hop3_mask; + u64 hop4_mask; + u32 page_size; + u32 huge_page_size; +}; + /** * struct asic_fixed_properties - ASIC specific immutable properties. * @hw_queues_props: H/W queues properties. @@ -137,6 +167,8 @@ enum hl_device_hw_state { * available sensors. * @uboot_ver: F/W U-boot version. * @preboot_ver: F/W Preboot version. + * @dmmu: DRAM MMU address translation properties. + * @pmmu: PCI (host) MMU address translation properties. * @sram_base_address: SRAM physical start address. * @sram_end_address: SRAM physical end address. * @sram_user_base_address - SRAM physical start address for user access. @@ -173,53 +205,55 @@ enum hl_device_hw_state { * @psoc_pci_pll_nf: PCI PLL NF value. * @psoc_pci_pll_od: PCI PLL OD value. * @psoc_pci_pll_div_factor: PCI PLL DIV FACTOR 1 value. - * @completion_queues_count: number of completion queues. * @high_pll: high PLL frequency used by the device. * @cb_pool_cb_cnt: number of CBs in the CB pool. * @cb_pool_cb_size: size of each CB in the CB pool. * @tpc_enabled_mask: which TPCs are enabled. + * @completion_queues_count: number of completion queues. */ struct asic_fixed_properties { struct hw_queue_properties hw_queues_props[HL_MAX_QUEUES]; - struct armcp_info armcp_info; - char uboot_ver[VERSION_MAX_LEN]; - char preboot_ver[VERSION_MAX_LEN]; - u64 sram_base_address; - u64 sram_end_address; - u64 sram_user_base_address; - u64 dram_base_address; - u64 dram_end_address; - u64 dram_user_base_address; - u64 dram_size; - u64 dram_pci_bar_size; - u64 max_power_default; - u64 va_space_host_start_address; - u64 va_space_host_end_address; - u64 va_space_dram_start_address; - u64 va_space_dram_end_address; - u64 dram_size_for_default_page_mapping; - u64 pcie_dbi_base_address; - u64 pcie_aux_dbi_reg_addr; - u64 mmu_pgt_addr; - u64 mmu_dram_default_page_addr; - u32 mmu_pgt_size; - u32 mmu_pte_size; - u32 mmu_hop_table_size; - u32 mmu_hop0_tables_total_size; - u32 dram_page_size; - u32 cfg_size; - u32 sram_size; - u32 max_asid; - u32 num_of_events; - u32 psoc_pci_pll_nr; - u32 psoc_pci_pll_nf; - u32 psoc_pci_pll_od; - u32 psoc_pci_pll_div_factor; - u32 high_pll; - u32 cb_pool_cb_cnt; - u32 cb_pool_cb_size; - u8 completion_queues_count; - u8 tpc_enabled_mask; + struct armcp_info armcp_info; + char uboot_ver[VERSION_MAX_LEN]; + char preboot_ver[VERSION_MAX_LEN]; + struct hl_mmu_properties dmmu; + struct hl_mmu_properties pmmu; + u64 sram_base_address; + u64 sram_end_address; + u64 sram_user_base_address; + u64 dram_base_address; + u64 dram_end_address; + u64 dram_user_base_address; + u64 dram_size; + u64 dram_pci_bar_size; + u64 max_power_default; + u64 va_space_host_start_address; + u64 va_space_host_end_address; + u64 va_space_dram_start_address; + u64 va_space_dram_end_address; + u64 dram_size_for_default_page_mapping; + u64 pcie_dbi_base_address; + u64 pcie_aux_dbi_reg_addr; + u64 mmu_pgt_addr; + u64 mmu_dram_default_page_addr; + u32 mmu_pgt_size; + u32 mmu_pte_size; + u32 mmu_hop_table_size; + u32 mmu_hop0_tables_total_size; + u32 dram_page_size; + u32 cfg_size; + u32 sram_size; + u32 max_asid; + u32 num_of_events; + u32 psoc_pci_pll_nr; + u32 psoc_pci_pll_nf; + u32 psoc_pci_pll_od; + u32 psoc_pci_pll_div_factor; + u32 high_pll; + u32 cb_pool_cb_cnt; + u32 cb_pool_cb_size; + u8 tpc_enabled_mask; + u8 completion_queues_count; }; /** diff --git a/drivers/misc/habanalabs/include/hw_ip/mmu/mmu_general.h b/drivers/misc/habanalabs/include/hw_ip/mmu/mmu_general.h index 74a5502b8c4e..a6851a9d3f03 100644 --- a/drivers/misc/habanalabs/include/hw_ip/mmu/mmu_general.h +++ b/drivers/misc/habanalabs/include/hw_ip/mmu/mmu_general.h @@ -12,7 +12,6 @@ #define PAGE_SHIFT_2MB 21 #define PAGE_SIZE_2MB (_AC(1, UL) << PAGE_SHIFT_2MB) #define PAGE_SIZE_4KB (_AC(1, UL) << PAGE_SHIFT_4KB) -#define PAGE_MASK_2MB (~(PAGE_SIZE_2MB - 1)) #define PAGE_PRESENT_MASK 0x0000000000001ull #define SWAP_OUT_MASK 0x0000000000004ull diff --git a/drivers/misc/habanalabs/memory.c b/drivers/misc/habanalabs/memory.c index 12db6609da27..cce6bdb6e655 100644 --- a/drivers/misc/habanalabs/memory.c +++ b/drivers/misc/habanalabs/memory.c @@ -13,7 +13,6 @@ #include #include -#define PGS_IN_2MB_PAGE (PAGE_SIZE_2MB >> PAGE_SHIFT) #define HL_MMU_DEBUG 0 /* @@ -516,8 +515,8 @@ static inline int add_va_block(struct hl_device *hdev, * - Return the start address of the virtual block */ static u64 get_va_block(struct hl_device *hdev, - struct hl_va_range *va_range, u64 size, u64 hint_addr, - bool is_userptr) + struct hl_va_range *va_range, u64 size, u64 hint_addr, + bool is_userptr) { struct hl_vm_va_block *va_block, *new_va_block = NULL; u64 valid_start, valid_size, prev_start, prev_end, page_mask, @@ -525,18 +524,17 @@ static u64 get_va_block(struct hl_device *hdev, u32 page_size; bool add_prev = false; - if (is_userptr) { + if (is_userptr) /* * We cannot know if the user allocated memory with huge pages * or not, hence we continue with the biggest possible * granularity. */ - page_size = PAGE_SIZE_2MB; - page_mask = PAGE_MASK_2MB; - } else { - page_size = hdev->asic_prop.dram_page_size; - page_mask = ~((u64)page_size - 1); - } + page_size = hdev->asic_prop.pmmu.huge_page_size; + else + page_size = hdev->asic_prop.dmmu.page_size; + + page_mask = ~((u64)page_size - 1); mutex_lock(&va_range->lock); @@ -558,7 +556,6 @@ static u64 get_va_block(struct hl_device *hdev, if (valid_size >= size && (!new_va_block || valid_size < res_valid_size)) { - new_va_block = va_block; res_valid_start = valid_start; res_valid_size = valid_size; @@ -629,7 +626,7 @@ static u32 get_sg_info(struct scatterlist *sg, dma_addr_t *dma_addr) /* * init_phys_pg_pack_from_userptr - initialize physical page pack from host * memory - * @asid: current context ASID + * @ctx: current context * @userptr: userptr to initialize from * @pphys_pg_pack: result pointer * @@ -638,16 +635,20 @@ static u32 get_sg_info(struct scatterlist *sg, dma_addr_t *dma_addr) * - Create a physical page pack from the physical pages related to the given * virtual block */ -static int init_phys_pg_pack_from_userptr(u32 asid, struct hl_userptr *userptr, +static int init_phys_pg_pack_from_userptr(struct hl_ctx *ctx, + struct hl_userptr *userptr, struct hl_vm_phys_pg_pack **pphys_pg_pack) { + struct hl_mmu_properties *mmu_prop = &ctx->hdev->asic_prop.pmmu; struct hl_vm_phys_pg_pack *phys_pg_pack; struct scatterlist *sg; dma_addr_t dma_addr; u64 page_mask, total_npages; - u32 npages, page_size = PAGE_SIZE; + u32 npages, page_size = PAGE_SIZE, + huge_page_size = mmu_prop->huge_page_size; bool first = true, is_huge_page_opt = true; int rc, i, j; + u32 pgs_in_huge_page = huge_page_size >> __ffs(page_size); phys_pg_pack = kzalloc(sizeof(*phys_pg_pack), GFP_KERNEL); if (!phys_pg_pack) @@ -655,7 +656,7 @@ static int init_phys_pg_pack_from_userptr(u32 asid, struct hl_userptr *userptr, phys_pg_pack->vm_type = userptr->vm_type; phys_pg_pack->created_from_userptr = true; - phys_pg_pack->asid = asid; + phys_pg_pack->asid = ctx->asid; atomic_set(&phys_pg_pack->mapping_cnt, 1); /* Only if all dma_addrs are aligned to 2MB and their @@ -670,14 +671,14 @@ static int init_phys_pg_pack_from_userptr(u32 asid, struct hl_userptr *userptr, total_npages += npages; - if ((npages % PGS_IN_2MB_PAGE) || - (dma_addr & (PAGE_SIZE_2MB - 1))) + if ((npages % pgs_in_huge_page) || + (dma_addr & (huge_page_size - 1))) is_huge_page_opt = false; } if (is_huge_page_opt) { - page_size = PAGE_SIZE_2MB; - total_npages /= PGS_IN_2MB_PAGE; + page_size = huge_page_size; + do_div(total_npages, pgs_in_huge_page); } page_mask = ~(((u64) page_size) - 1); @@ -709,7 +710,7 @@ static int init_phys_pg_pack_from_userptr(u32 asid, struct hl_userptr *userptr, dma_addr += page_size; if (is_huge_page_opt) - npages -= PGS_IN_2MB_PAGE; + npages -= pgs_in_huge_page; else npages--; } @@ -872,7 +873,7 @@ static int map_device_va(struct hl_ctx *ctx, struct hl_mem_in *args, return rc; } - rc = init_phys_pg_pack_from_userptr(ctx->asid, userptr, + rc = init_phys_pg_pack_from_userptr(ctx, userptr, &phys_pg_pack); if (rc) { dev_err(hdev->dev, @@ -1029,7 +1030,7 @@ static int unmap_device_va(struct hl_ctx *ctx, u64 vaddr) if (*vm_type == VM_TYPE_USERPTR) { is_userptr = true; userptr = hnode->ptr; - rc = init_phys_pg_pack_from_userptr(ctx->asid, userptr, + rc = init_phys_pg_pack_from_userptr(ctx, userptr, &phys_pg_pack); if (rc) { dev_err(hdev->dev, diff --git a/drivers/misc/habanalabs/mmu.c b/drivers/misc/habanalabs/mmu.c index 21b4e3281b3e..3a7f8ff19eb2 100644 --- a/drivers/misc/habanalabs/mmu.c +++ b/drivers/misc/habanalabs/mmu.c @@ -171,29 +171,44 @@ static inline u64 get_hopN_pte_addr(struct hl_ctx *ctx, u64 hop_addr, ((virt_addr & mask) >> shift); } -static inline u64 get_hop0_pte_addr(struct hl_ctx *ctx, u64 hop_addr, u64 vaddr) +static inline u64 get_hop0_pte_addr(struct hl_ctx *ctx, + struct hl_mmu_properties *mmu_prop, + u64 hop_addr, u64 vaddr) { - return get_hopN_pte_addr(ctx, hop_addr, vaddr, HOP0_MASK, HOP0_SHIFT); + return get_hopN_pte_addr(ctx, hop_addr, vaddr, mmu_prop->hop0_mask, + mmu_prop->hop0_shift); } -static inline u64 get_hop1_pte_addr(struct hl_ctx *ctx, u64 hop_addr, u64 vaddr) +static inline u64 get_hop1_pte_addr(struct hl_ctx *ctx, + struct hl_mmu_properties *mmu_prop, + u64 hop_addr, u64 vaddr) { - return get_hopN_pte_addr(ctx, hop_addr, vaddr, HOP1_MASK, HOP1_SHIFT); + return get_hopN_pte_addr(ctx, hop_addr, vaddr, mmu_prop->hop1_mask, + mmu_prop->hop1_shift); } -static inline u64 get_hop2_pte_addr(struct hl_ctx *ctx, u64 hop_addr, u64 vaddr) +static inline u64 get_hop2_pte_addr(struct hl_ctx *ctx, + struct hl_mmu_properties *mmu_prop, + u64 hop_addr, u64 vaddr) { - return get_hopN_pte_addr(ctx, hop_addr, vaddr, HOP2_MASK, HOP2_SHIFT); + return get_hopN_pte_addr(ctx, hop_addr, vaddr, mmu_prop->hop2_mask, + mmu_prop->hop2_shift); } -static inline u64 get_hop3_pte_addr(struct hl_ctx *ctx, u64 hop_addr, u64 vaddr) +static inline u64 get_hop3_pte_addr(struct hl_ctx *ctx, + struct hl_mmu_properties *mmu_prop, + u64 hop_addr, u64 vaddr) { - return get_hopN_pte_addr(ctx, hop_addr, vaddr, HOP3_MASK, HOP3_SHIFT); + return get_hopN_pte_addr(ctx, hop_addr, vaddr, mmu_prop->hop3_mask, + mmu_prop->hop3_shift); } -static inline u64 get_hop4_pte_addr(struct hl_ctx *ctx, u64 hop_addr, u64 vaddr) +static inline u64 get_hop4_pte_addr(struct hl_ctx *ctx, + struct hl_mmu_properties *mmu_prop, + u64 hop_addr, u64 vaddr) { - return get_hopN_pte_addr(ctx, hop_addr, vaddr, HOP4_MASK, HOP4_SHIFT); + return get_hopN_pte_addr(ctx, hop_addr, vaddr, mmu_prop->hop4_mask, + mmu_prop->hop4_shift); } static inline u64 get_next_hop_addr(struct hl_ctx *ctx, u64 curr_pte) @@ -513,24 +528,23 @@ void hl_mmu_ctx_fini(struct hl_ctx *ctx) mutex_destroy(&ctx->mmu_lock); } -static int _hl_mmu_unmap(struct hl_ctx *ctx, u64 virt_addr) +static int _hl_mmu_unmap(struct hl_ctx *ctx, u64 virt_addr, bool is_dram_addr) { struct hl_device *hdev = ctx->hdev; struct asic_fixed_properties *prop = &hdev->asic_prop; + struct hl_mmu_properties *mmu_prop; u64 hop0_addr = 0, hop0_pte_addr = 0, hop1_addr = 0, hop1_pte_addr = 0, hop2_addr = 0, hop2_pte_addr = 0, hop3_addr = 0, hop3_pte_addr = 0, hop4_addr = 0, hop4_pte_addr = 0, curr_pte; - bool is_dram_addr, is_huge, clear_hop3 = true; + bool is_huge, clear_hop3 = true; - is_dram_addr = hl_mem_area_inside_range(virt_addr, PAGE_SIZE_2MB, - prop->va_space_dram_start_address, - prop->va_space_dram_end_address); + mmu_prop = is_dram_addr ? &prop->dmmu : &prop->pmmu; hop0_addr = get_hop0_addr(ctx); - hop0_pte_addr = get_hop0_pte_addr(ctx, hop0_addr, virt_addr); + hop0_pte_addr = get_hop0_pte_addr(ctx, mmu_prop, hop0_addr, virt_addr); curr_pte = *(u64 *) (uintptr_t) hop0_pte_addr; @@ -539,7 +553,7 @@ static int _hl_mmu_unmap(struct hl_ctx *ctx, u64 virt_addr) if (hop1_addr == ULLONG_MAX) goto not_mapped; - hop1_pte_addr = get_hop1_pte_addr(ctx, hop1_addr, virt_addr); + hop1_pte_addr = get_hop1_pte_addr(ctx, mmu_prop, hop1_addr, virt_addr); curr_pte = *(u64 *) (uintptr_t) hop1_pte_addr; @@ -548,7 +562,7 @@ static int _hl_mmu_unmap(struct hl_ctx *ctx, u64 virt_addr) if (hop2_addr == ULLONG_MAX) goto not_mapped; - hop2_pte_addr = get_hop2_pte_addr(ctx, hop2_addr, virt_addr); + hop2_pte_addr = get_hop2_pte_addr(ctx, mmu_prop, hop2_addr, virt_addr); curr_pte = *(u64 *) (uintptr_t) hop2_pte_addr; @@ -557,7 +571,7 @@ static int _hl_mmu_unmap(struct hl_ctx *ctx, u64 virt_addr) if (hop3_addr == ULLONG_MAX) goto not_mapped; - hop3_pte_addr = get_hop3_pte_addr(ctx, hop3_addr, virt_addr); + hop3_pte_addr = get_hop3_pte_addr(ctx, mmu_prop, hop3_addr, virt_addr); curr_pte = *(u64 *) (uintptr_t) hop3_pte_addr; @@ -575,7 +589,8 @@ static int _hl_mmu_unmap(struct hl_ctx *ctx, u64 virt_addr) if (hop4_addr == ULLONG_MAX) goto not_mapped; - hop4_pte_addr = get_hop4_pte_addr(ctx, hop4_addr, virt_addr); + hop4_pte_addr = get_hop4_pte_addr(ctx, mmu_prop, hop4_addr, + virt_addr); curr_pte = *(u64 *) (uintptr_t) hop4_pte_addr; @@ -667,25 +682,36 @@ not_mapped: int hl_mmu_unmap(struct hl_ctx *ctx, u64 virt_addr, u32 page_size) { struct hl_device *hdev = ctx->hdev; + struct asic_fixed_properties *prop = &hdev->asic_prop; + struct hl_mmu_properties *mmu_prop; u64 real_virt_addr; u32 real_page_size, npages; int i, rc; + bool is_dram_addr; if (!hdev->mmu_enable) return 0; + is_dram_addr = hl_mem_area_inside_range(virt_addr, prop->dmmu.page_size, + prop->va_space_dram_start_address, + prop->va_space_dram_end_address); + + mmu_prop = is_dram_addr ? &prop->dmmu : &prop->pmmu; + /* - * The H/W handles mapping of 4KB/2MB page. Hence if the host page size - * is bigger, we break it to sub-pages and unmap them separately. + * The H/W handles mapping of specific page sizes. Hence if the page + * size is bigger, we break it to sub-pages and unmap them separately. */ - if ((page_size % PAGE_SIZE_2MB) == 0) { - real_page_size = PAGE_SIZE_2MB; - } else if ((page_size % PAGE_SIZE_4KB) == 0) { - real_page_size = PAGE_SIZE_4KB; + if ((page_size % mmu_prop->huge_page_size) == 0) { + real_page_size = mmu_prop->huge_page_size; + } else if ((page_size % mmu_prop->page_size) == 0) { + real_page_size = mmu_prop->page_size; } else { dev_err(hdev->dev, - "page size of %u is not 4KB nor 2MB aligned, can't unmap\n", - page_size); + "page size of %u is not %uKB nor %uMB aligned, can't unmap\n", + page_size, + mmu_prop->page_size >> 10, + mmu_prop->huge_page_size >> 20); return -EFAULT; } @@ -694,7 +720,7 @@ int hl_mmu_unmap(struct hl_ctx *ctx, u64 virt_addr, u32 page_size) real_virt_addr = virt_addr; for (i = 0 ; i < npages ; i++) { - rc = _hl_mmu_unmap(ctx, real_virt_addr); + rc = _hl_mmu_unmap(ctx, real_virt_addr, is_dram_addr); if (rc) return rc; @@ -705,10 +731,11 @@ int hl_mmu_unmap(struct hl_ctx *ctx, u64 virt_addr, u32 page_size) } static int _hl_mmu_map(struct hl_ctx *ctx, u64 virt_addr, u64 phys_addr, - u32 page_size) + u32 page_size, bool is_dram_addr) { struct hl_device *hdev = ctx->hdev; struct asic_fixed_properties *prop = &hdev->asic_prop; + struct hl_mmu_properties *mmu_prop; u64 hop0_addr = 0, hop0_pte_addr = 0, hop1_addr = 0, hop1_pte_addr = 0, hop2_addr = 0, hop2_pte_addr = 0, @@ -716,21 +743,19 @@ static int _hl_mmu_map(struct hl_ctx *ctx, u64 virt_addr, u64 phys_addr, hop4_addr = 0, hop4_pte_addr = 0, curr_pte = 0; bool hop1_new = false, hop2_new = false, hop3_new = false, - hop4_new = false, is_huge, is_dram_addr; + hop4_new = false, is_huge; int rc = -ENOMEM; - /* - * This mapping function can map a 4KB/2MB page. For 2MB page there are - * only 3 hops rather than 4. Currently the DRAM allocation uses 2MB - * pages only but user memory could have been allocated with one of the - * two page sizes. Since this is a common code for all the three cases, - * we need this hugs page check. - */ - is_huge = page_size == PAGE_SIZE_2MB; + mmu_prop = is_dram_addr ? &prop->dmmu : &prop->pmmu; - is_dram_addr = hl_mem_area_inside_range(virt_addr, page_size, - prop->va_space_dram_start_address, - prop->va_space_dram_end_address); + /* + * This mapping function can map a page or a huge page. For huge page + * there are only 3 hops rather than 4. Currently the DRAM allocation + * uses huge pages only but user memory could have been allocated with + * one of the two page sizes. Since this is a common code for all the + * three cases, we need this hugs page check. + */ + is_huge = page_size == mmu_prop->huge_page_size; if (is_dram_addr && !is_huge) { dev_err(hdev->dev, "DRAM mapping should use huge pages only\n"); @@ -738,28 +763,28 @@ static int _hl_mmu_map(struct hl_ctx *ctx, u64 virt_addr, u64 phys_addr, } hop0_addr = get_hop0_addr(ctx); - hop0_pte_addr = get_hop0_pte_addr(ctx, hop0_addr, virt_addr); + hop0_pte_addr = get_hop0_pte_addr(ctx, mmu_prop, hop0_addr, virt_addr); curr_pte = *(u64 *) (uintptr_t) hop0_pte_addr; hop1_addr = get_alloc_next_hop_addr(ctx, curr_pte, &hop1_new); if (hop1_addr == ULLONG_MAX) goto err; - hop1_pte_addr = get_hop1_pte_addr(ctx, hop1_addr, virt_addr); + hop1_pte_addr = get_hop1_pte_addr(ctx, mmu_prop, hop1_addr, virt_addr); curr_pte = *(u64 *) (uintptr_t) hop1_pte_addr; hop2_addr = get_alloc_next_hop_addr(ctx, curr_pte, &hop2_new); if (hop2_addr == ULLONG_MAX) goto err; - hop2_pte_addr = get_hop2_pte_addr(ctx, hop2_addr, virt_addr); + hop2_pte_addr = get_hop2_pte_addr(ctx, mmu_prop, hop2_addr, virt_addr); curr_pte = *(u64 *) (uintptr_t) hop2_pte_addr; hop3_addr = get_alloc_next_hop_addr(ctx, curr_pte, &hop3_new); if (hop3_addr == ULLONG_MAX) goto err; - hop3_pte_addr = get_hop3_pte_addr(ctx, hop3_addr, virt_addr); + hop3_pte_addr = get_hop3_pte_addr(ctx, mmu_prop, hop3_addr, virt_addr); curr_pte = *(u64 *) (uintptr_t) hop3_pte_addr; if (!is_huge) { @@ -767,7 +792,8 @@ static int _hl_mmu_map(struct hl_ctx *ctx, u64 virt_addr, u64 phys_addr, if (hop4_addr == ULLONG_MAX) goto err; - hop4_pte_addr = get_hop4_pte_addr(ctx, hop4_addr, virt_addr); + hop4_pte_addr = get_hop4_pte_addr(ctx, mmu_prop, hop4_addr, + virt_addr); curr_pte = *(u64 *) (uintptr_t) hop4_pte_addr; } @@ -890,25 +916,36 @@ err: int hl_mmu_map(struct hl_ctx *ctx, u64 virt_addr, u64 phys_addr, u32 page_size) { struct hl_device *hdev = ctx->hdev; + struct asic_fixed_properties *prop = &hdev->asic_prop; + struct hl_mmu_properties *mmu_prop; u64 real_virt_addr, real_phys_addr; u32 real_page_size, npages; int i, rc, mapped_cnt = 0; + bool is_dram_addr; if (!hdev->mmu_enable) return 0; + is_dram_addr = hl_mem_area_inside_range(virt_addr, prop->dmmu.page_size, + prop->va_space_dram_start_address, + prop->va_space_dram_end_address); + + mmu_prop = is_dram_addr ? &prop->dmmu : &prop->pmmu; + /* - * The H/W handles mapping of 4KB/2MB page. Hence if the host page size - * is bigger, we break it to sub-pages and map them separately. + * The H/W handles mapping of specific page sizes. Hence if the page + * size is bigger, we break it to sub-pages and map them separately. */ - if ((page_size % PAGE_SIZE_2MB) == 0) { - real_page_size = PAGE_SIZE_2MB; - } else if ((page_size % PAGE_SIZE_4KB) == 0) { - real_page_size = PAGE_SIZE_4KB; + if ((page_size % mmu_prop->huge_page_size) == 0) { + real_page_size = mmu_prop->huge_page_size; + } else if ((page_size % mmu_prop->page_size) == 0) { + real_page_size = mmu_prop->page_size; } else { dev_err(hdev->dev, - "page size of %u is not 4KB nor 2MB aligned, can't map\n", - page_size); + "page size of %u is not %dKB nor %dMB aligned, can't unmap\n", + page_size, + mmu_prop->page_size >> 10, + mmu_prop->huge_page_size >> 20); return -EFAULT; } @@ -923,7 +960,7 @@ int hl_mmu_map(struct hl_ctx *ctx, u64 virt_addr, u64 phys_addr, u32 page_size) for (i = 0 ; i < npages ; i++) { rc = _hl_mmu_map(ctx, real_virt_addr, real_phys_addr, - real_page_size); + real_page_size, is_dram_addr); if (rc) goto err; @@ -937,7 +974,7 @@ int hl_mmu_map(struct hl_ctx *ctx, u64 virt_addr, u64 phys_addr, u32 page_size) err: real_virt_addr = virt_addr; for (i = 0 ; i < mapped_cnt ; i++) { - if (_hl_mmu_unmap(ctx, real_virt_addr)) + if (_hl_mmu_unmap(ctx, real_virt_addr, is_dram_addr)) dev_warn_ratelimited(hdev->dev, "failed to unmap va: 0x%llx\n", real_virt_addr); From bc75d799f9531fe112f7d78a5547ad549293fb7e Mon Sep 17 00:00:00 2001 From: Omer Shpigelman Date: Thu, 14 Nov 2019 18:23:56 +0000 Subject: [PATCH 186/198] habanalabs: prevent read/write from/to the device during hard reset During hard reset we should not access the device except of necessary reset operations because the device might be stuck or unresponsive. Signed-off-by: Omer Shpigelman Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/goya/goya.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index 3294a6a92f75..2935e84fe7d8 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -4870,7 +4870,8 @@ static void goya_mmu_invalidate_cache(struct hl_device *hdev, bool is_hard, u32 status, timeout_usec; int rc; - if (!(goya->hw_cap_initialized & HW_CAP_MMU)) + if (!(goya->hw_cap_initialized & HW_CAP_MMU) || + hdev->hard_reset_pending) return; /* no need in L1 only invalidation in Goya */ @@ -4909,7 +4910,8 @@ static void goya_mmu_invalidate_cache_range(struct hl_device *hdev, u32 status, timeout_usec, inv_data, pi; int rc; - if (!(goya->hw_cap_initialized & HW_CAP_MMU)) + if (!(goya->hw_cap_initialized & HW_CAP_MMU) || + hdev->hard_reset_pending) return; /* no need in L1 only invalidation in Goya */ From 1b98d8b23f29e5ff82fe84ca0c8acffaa971b9d6 Mon Sep 17 00:00:00 2001 From: Omer Shpigelman Date: Thu, 14 Nov 2019 18:23:57 +0000 Subject: [PATCH 187/198] habanalabs: optimize MMU unmap Reduce context close time by skipping hash table lookup if possible in order to avoid hard reset with open contexts. Reset with open contexts can potentially lead to a kernel crash as the generic pool of the MMU hops is destroyed while it is not empty because some unmap operations are not done. This commit affect mainly when running on simulator. Signed-off-by: Omer Shpigelman Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/mmu.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/misc/habanalabs/mmu.c b/drivers/misc/habanalabs/mmu.c index 3a7f8ff19eb2..6262b26e2086 100644 --- a/drivers/misc/habanalabs/mmu.c +++ b/drivers/misc/habanalabs/mmu.c @@ -25,10 +25,9 @@ static struct pgt_info *get_pgt_info(struct hl_ctx *ctx, u64 hop_addr) return pgt_info; } -static void free_hop(struct hl_ctx *ctx, u64 hop_addr) +static void _free_hop(struct hl_ctx *ctx, struct pgt_info *pgt_info) { struct hl_device *hdev = ctx->hdev; - struct pgt_info *pgt_info = get_pgt_info(ctx, hop_addr); gen_pool_free(hdev->mmu_pgt_pool, pgt_info->phys_addr, hdev->asic_prop.mmu_hop_table_size); @@ -37,6 +36,13 @@ static void free_hop(struct hl_ctx *ctx, u64 hop_addr) kfree(pgt_info); } +static void free_hop(struct hl_ctx *ctx, u64 hop_addr) +{ + struct pgt_info *pgt_info = get_pgt_info(ctx, hop_addr); + + _free_hop(ctx, pgt_info); +} + static u64 alloc_hop(struct hl_ctx *ctx) { struct hl_device *hdev = ctx->hdev; @@ -159,7 +165,7 @@ static inline int put_pte(struct hl_ctx *ctx, u64 hop_addr) */ num_of_ptes_left = pgt_info->num_of_ptes; if (!num_of_ptes_left) - free_hop(ctx, hop_addr); + _free_hop(ctx, pgt_info); return num_of_ptes_left; } @@ -516,13 +522,14 @@ void hl_mmu_ctx_fini(struct hl_ctx *ctx) dram_default_mapping_fini(ctx); if (!hash_empty(ctx->mmu_shadow_hash)) - dev_err(hdev->dev, "ctx is freed while it has pgts in use\n"); + dev_err(hdev->dev, "ctx %d is freed while it has pgts in use\n", + ctx->asid); hash_for_each_safe(ctx->mmu_shadow_hash, i, tmp, pgt_info, node) { - dev_err(hdev->dev, + dev_err_ratelimited(hdev->dev, "pgt_info of addr 0x%llx of asid %d was not destroyed, num_ptes: %d\n", pgt_info->phys_addr, ctx->asid, pgt_info->num_of_ptes); - free_hop(ctx, pgt_info->shadow_addr); + _free_hop(ctx, pgt_info); } mutex_destroy(&ctx->mmu_lock); From 71c5e55e7c077fa17c42fbda91a8d14322825c44 Mon Sep 17 00:00:00 2001 From: Omer Shpigelman Date: Thu, 14 Nov 2019 18:23:57 +0000 Subject: [PATCH 188/198] habanalabs: skip VA block list update in reset flow Reduce context close time by skipping the VA block free list update in order to avoid hard reset with open contexts. Reset with open contexts can potentially lead to a kernel crash as the generic pool of the MMU hops is destroyed while it is not empty because some unmap operations are not done. The commit affect mainly when running on simulator. Signed-off-by: Omer Shpigelman Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/memory.c | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/drivers/misc/habanalabs/memory.c b/drivers/misc/habanalabs/memory.c index cce6bdb6e655..e6412e13145e 100644 --- a/drivers/misc/habanalabs/memory.c +++ b/drivers/misc/habanalabs/memory.c @@ -993,17 +993,19 @@ init_page_pack_err: * * @ctx : current context * @vaddr : device virtual address to unmap + * @ctx_free : true if in context free flow, false otherwise. * * This function does the following: * - Unmap the physical pages related to the given virtual address * - return the device virtual block to the virtual block list */ -static int unmap_device_va(struct hl_ctx *ctx, u64 vaddr) +static int unmap_device_va(struct hl_ctx *ctx, u64 vaddr, bool ctx_free) { struct hl_device *hdev = ctx->hdev; struct hl_vm_phys_pg_pack *phys_pg_pack = NULL; struct hl_vm_hash_node *hnode = NULL; struct hl_userptr *userptr = NULL; + struct hl_va_range *va_range; enum vm_type_t *vm_type; bool is_userptr; int rc; @@ -1029,6 +1031,7 @@ static int unmap_device_va(struct hl_ctx *ctx, u64 vaddr) if (*vm_type == VM_TYPE_USERPTR) { is_userptr = true; + va_range = &ctx->host_va_range; userptr = hnode->ptr; rc = init_phys_pg_pack_from_userptr(ctx, userptr, &phys_pg_pack); @@ -1040,6 +1043,7 @@ static int unmap_device_va(struct hl_ctx *ctx, u64 vaddr) } } else if (*vm_type == VM_TYPE_PHYS_PACK) { is_userptr = false; + va_range = &ctx->dram_va_range; phys_pg_pack = hnode->ptr; } else { dev_warn(hdev->dev, @@ -1065,12 +1069,18 @@ static int unmap_device_va(struct hl_ctx *ctx, u64 vaddr) mutex_unlock(&ctx->mmu_lock); - if (add_va_block(hdev, - is_userptr ? &ctx->host_va_range : &ctx->dram_va_range, - vaddr, - vaddr + phys_pg_pack->total_size - 1)) - dev_warn(hdev->dev, "add va block failed for vaddr: 0x%llx\n", - vaddr); + /* + * No point in maintaining the free VA block list if the context is + * closing as the list will be freed anyway + */ + if (!ctx_free) { + rc = add_va_block(hdev, va_range, vaddr, + vaddr + phys_pg_pack->total_size - 1); + if (rc) + dev_warn(hdev->dev, + "add va block failed for vaddr: 0x%llx\n", + vaddr); + } atomic_dec(&phys_pg_pack->mapping_cnt); kfree(hnode); @@ -1202,8 +1212,8 @@ int hl_mem_ioctl(struct hl_fpriv *hpriv, void *data) break; case HL_MEM_OP_UNMAP: - rc = unmap_device_va(ctx, - args->in.unmap.device_virt_addr); + rc = unmap_device_va(ctx, args->in.unmap.device_virt_addr, + false); break; default: @@ -1650,7 +1660,7 @@ void hl_vm_ctx_fini(struct hl_ctx *ctx) dev_dbg(hdev->dev, "hl_mem_hash_node of vaddr 0x%llx of asid %d is still alive\n", hnode->vaddr, ctx->asid); - unmap_device_va(ctx, hnode->vaddr); + unmap_device_va(ctx, hnode->vaddr, true); } spin_lock(&vm->idr_lock); From bea84c4d67e5efd1078ef234ef1304a4d1788008 Mon Sep 17 00:00:00 2001 From: Omer Shpigelman Date: Thu, 14 Nov 2019 18:23:58 +0000 Subject: [PATCH 189/198] habanalabs: invalidate MMU cache only once Reduce context close time by performing MMU cache invalidation once at the end of the unmap loop rather in each iteration, in order to avoid hard reset with open contexts. Reset with open contexts can potentially lead to a kernel crash as the generic pool of the MMU hops is destroyed while it is not empty because some unmap operations are not done. The commit affect mainly when running on simulator. Signed-off-by: Omer Shpigelman Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/memory.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/misc/habanalabs/memory.c b/drivers/misc/habanalabs/memory.c index e6412e13145e..47e38c6f2d64 100644 --- a/drivers/misc/habanalabs/memory.c +++ b/drivers/misc/habanalabs/memory.c @@ -1065,7 +1065,13 @@ static int unmap_device_va(struct hl_ctx *ctx, u64 vaddr, bool ctx_free) unmap_phys_pg_pack(ctx, vaddr, phys_pg_pack); - hdev->asic_funcs->mmu_invalidate_cache(hdev, true, *vm_type); + /* + * During context free this function is called in a loop to clean all + * the context mappings. Hence the cache invalidation can be called once + * at the loop end rather than for each iteration + */ + if (!ctx_free) + hdev->asic_funcs->mmu_invalidate_cache(hdev, true, *vm_type); mutex_unlock(&ctx->mmu_lock); @@ -1663,6 +1669,10 @@ void hl_vm_ctx_fini(struct hl_ctx *ctx) unmap_device_va(ctx, hnode->vaddr, true); } + /* invalidate the cache once after the unmapping loop */ + hdev->asic_funcs->mmu_invalidate_cache(hdev, true, VM_TYPE_USERPTR); + hdev->asic_funcs->mmu_invalidate_cache(hdev, true, VM_TYPE_PHYS_PACK); + spin_lock(&vm->idr_lock); idr_for_each_entry(&vm->phys_pg_pack_handles, phys_pg_list, i) if (phys_pg_list->asid == ctx->asid) { From e604f551cdce07e45b6ca34eab58648185b3fba0 Mon Sep 17 00:00:00 2001 From: Omer Shpigelman Date: Thu, 14 Nov 2019 18:23:59 +0000 Subject: [PATCH 190/198] habanalabs: remove unnecessary checks Now that the VA block free list is not updated on context close in order to optimize this flow, no need in the sanity checks of the list contents as these will fail for sure. In addition, remove the "context closing with VA in use" print during hard reset as this situation is a side effect of the failure that caused the hard reset. Signed-off-by: Omer Shpigelman Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/memory.c | 40 +++++++------------------------- 1 file changed, 9 insertions(+), 31 deletions(-) diff --git a/drivers/misc/habanalabs/memory.c b/drivers/misc/habanalabs/memory.c index 47e38c6f2d64..6c72cb4eff54 100644 --- a/drivers/misc/habanalabs/memory.c +++ b/drivers/misc/habanalabs/memory.c @@ -544,7 +544,6 @@ static u64 get_va_block(struct hl_device *hdev, /* calc the first possible aligned addr */ valid_start = va_block->start; - if (valid_start & (page_size - 1)) { valid_start &= page_mask; valid_start += page_size; @@ -1588,43 +1587,16 @@ int hl_vm_ctx_init(struct hl_ctx *ctx) * @hdev : pointer to the habanalabs structure * va_range : pointer to virtual addresses range * - * This function initializes the following: - * - Checks that the given range contains the whole initial range + * This function does the following: * - Frees the virtual addresses block list and its lock */ static void hl_va_range_fini(struct hl_device *hdev, struct hl_va_range *va_range) { - struct hl_vm_va_block *va_block; - - if (list_empty(&va_range->list)) { - dev_warn(hdev->dev, - "va list should not be empty on cleanup!\n"); - goto out; - } - - if (!list_is_singular(&va_range->list)) { - dev_warn(hdev->dev, - "va list should not contain multiple blocks on cleanup!\n"); - goto free_va_list; - } - - va_block = list_first_entry(&va_range->list, typeof(*va_block), node); - - if (va_block->start != va_range->start_addr || - va_block->end != va_range->end_addr) { - dev_warn(hdev->dev, - "wrong va block on cleanup, from 0x%llx to 0x%llx\n", - va_block->start, va_block->end); - goto free_va_list; - } - -free_va_list: mutex_lock(&va_range->lock); clear_va_list_locked(hdev, &va_range->list); mutex_unlock(&va_range->lock); -out: mutex_destroy(&va_range->lock); } @@ -1659,8 +1631,14 @@ void hl_vm_ctx_fini(struct hl_ctx *ctx) hl_debugfs_remove_ctx_mem_hash(hdev, ctx); - if (!hash_empty(ctx->mem_hash)) - dev_notice(hdev->dev, "ctx is freed while it has va in use\n"); + /* + * Clearly something went wrong on hard reset so no point in printing + * another side effect error + */ + if (!hdev->hard_reset_pending && !hash_empty(ctx->mem_hash)) + dev_notice(hdev->dev, + "ctx %d is freed while it has va in use\n", + ctx->asid); hash_for_each_safe(ctx->mem_hash, i, tmp_node, hnode, node) { dev_dbg(hdev->dev, From 7fbdc12b91110d21e15b84d5acf8402ae608d8c1 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Sat, 16 Nov 2019 12:24:19 +0200 Subject: [PATCH 191/198] habanalabs: remove prints on successful device initialization Successful device initialization is mentioned in kernel log with the message "Successfully added device to habanalabs driver". There is no point of spamming the log with additional messages about successful queue testing, which are implied by the above mentioned message. Signed-off-by: Oded Gabbay Reviewed-by: Omer Shpigelman --- drivers/misc/habanalabs/firmware_if.c | 5 +---- drivers/misc/habanalabs/goya/goya.c | 3 --- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/misc/habanalabs/firmware_if.c b/drivers/misc/habanalabs/firmware_if.c index ea2ca67fbfbf..f5bd03171dac 100644 --- a/drivers/misc/habanalabs/firmware_if.c +++ b/drivers/misc/habanalabs/firmware_if.c @@ -143,10 +143,7 @@ int hl_fw_test_cpu_queue(struct hl_device *hdev) sizeof(test_pkt), HL_DEVICE_TIMEOUT_USEC, &result); if (!rc) { - if (result == ARMCP_PACKET_FENCE_VAL) - dev_info(hdev->dev, - "queue test on CPU queue succeeded\n"); - else + if (result != ARMCP_PACKET_FENCE_VAL) dev_err(hdev->dev, "CPU queue test failed (0x%08lX)\n", result); } else { diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index 2935e84fe7d8..70bdaeffb6ce 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -3006,9 +3006,6 @@ int goya_test_queue(struct hl_device *hdev, u32 hw_queue_id) "H/W queue %d test failed (scratch(0x%08llX) == 0x%08X)\n", hw_queue_id, (unsigned long long) fence_dma_addr, tmp); rc = -EIO; - } else { - dev_info(hdev->dev, "queue test on H/W queue %d succeeded\n", - hw_queue_id); } free_pkt: From da1342a0eec038dc466742e662218f6be349d1b7 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Sat, 16 Nov 2019 12:26:30 +0200 Subject: [PATCH 192/198] habanalabs: use defines for F/W files Make the code more concise and maintainable by using defines for the F/W files. Signed-off-by: Oded Gabbay Reviewed-by: Omer Shpigelman --- drivers/misc/habanalabs/goya/goya.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index 70bdaeffb6ce..c8d16aa4382c 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -72,6 +72,9 @@ * */ +#define GOYA_UBOOT_FW_FILE "habanalabs/goya/goya-u-boot.bin" +#define GOYA_LINUX_FW_FILE "habanalabs/goya/goya-fit.itb" + #define GOYA_MMU_REGS_NUM 63 #define GOYA_DMA_POOL_BLK_SIZE 0x100 /* 256 bytes */ @@ -2163,13 +2166,11 @@ static void goya_halt_engines(struct hl_device *hdev, bool hard_reset) */ static int goya_push_uboot_to_device(struct hl_device *hdev) { - char fw_name[200]; void __iomem *dst; - snprintf(fw_name, sizeof(fw_name), "habanalabs/goya/goya-u-boot.bin"); dst = hdev->pcie_bar[SRAM_CFG_BAR_ID] + UBOOT_FW_OFFSET; - return hl_fw_push_fw_to_device(hdev, fw_name, dst); + return hl_fw_push_fw_to_device(hdev, GOYA_UBOOT_FW_FILE, dst); } /* @@ -2182,13 +2183,11 @@ static int goya_push_uboot_to_device(struct hl_device *hdev) */ static int goya_push_linux_to_device(struct hl_device *hdev) { - char fw_name[200]; void __iomem *dst; - snprintf(fw_name, sizeof(fw_name), "habanalabs/goya/goya-fit.itb"); dst = hdev->pcie_bar[DDR_BAR_ID] + LINUX_FW_OFFSET; - return hl_fw_push_fw_to_device(hdev, fw_name, dst); + return hl_fw_push_fw_to_device(hdev, GOYA_LINUX_FW_FILE, dst); } static int goya_pldm_init_cpu(struct hl_device *hdev) From e16ee4103770acf365372886eac7c750017c918e Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Sat, 16 Nov 2019 12:29:50 +0200 Subject: [PATCH 193/198] habanalabs: make code more concise Instead of doing if inside if, just write them with && operator. Signed-off-by: Oded Gabbay Reviewed-by: Omer Shpigelman --- drivers/misc/habanalabs/habanalabs_ioctl.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/drivers/misc/habanalabs/habanalabs_ioctl.c b/drivers/misc/habanalabs/habanalabs_ioctl.c index 02d7491fa28f..5d9c269d99db 100644 --- a/drivers/misc/habanalabs/habanalabs_ioctl.c +++ b/drivers/misc/habanalabs/habanalabs_ioctl.c @@ -60,7 +60,7 @@ static int hw_ip_info(struct hl_device *hdev, struct hl_info_args *args) hw_ip.tpc_enabled_mask = prop->tpc_enabled_mask; hw_ip.sram_size = prop->sram_size - sram_kmd_size; hw_ip.dram_size = prop->dram_size - dram_kmd_size; - if (hw_ip.dram_size > 0) + if (hw_ip.dram_size > PAGE_SIZE) hw_ip.dram_enabled = 1; hw_ip.num_of_events = prop->num_of_events; @@ -184,17 +184,14 @@ static int debug_coresight(struct hl_device *hdev, struct hl_debug_args *args) goto out; } - if (output) { - if (copy_to_user((void __user *) (uintptr_t) args->output_ptr, - output, - args->output_size)) { - dev_err(hdev->dev, - "copy to user failed in debug ioctl\n"); - rc = -EFAULT; - goto out; - } + if (output && copy_to_user((void __user *) (uintptr_t) args->output_ptr, + output, args->output_size)) { + dev_err(hdev->dev, "copy to user failed in debug ioctl\n"); + rc = -EFAULT; + goto out; } + out: kfree(params); kfree(output); @@ -434,9 +431,8 @@ static long _hl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg, retcode = func(hpriv, kdata); - if (cmd & IOC_OUT) - if (copy_to_user((void __user *)arg, kdata, usize)) - retcode = -EFAULT; + if ((cmd & IOC_OUT) && copy_to_user((void __user *)arg, kdata, usize)) + retcode = -EFAULT; out_err: if (retcode) From 52c01b0137193ab0c9282ec8d09c6338446e6e9f Mon Sep 17 00:00:00 2001 From: Moti Haimovski Date: Sun, 3 Nov 2019 16:26:44 +0200 Subject: [PATCH 194/198] habanalabs: expose reset counters via existing INFO IOCTL Expose both soft and hard reset counts via INFO IOCTL. This will allow system management applications to easily check if the device has undergone reset. Signed-off-by: Moti Haimovski Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/habanalabs_ioctl.c | 19 +++++++++++++++++++ include/uapi/misc/habanalabs.h | 9 +++++++++ 2 files changed, 28 insertions(+) diff --git a/drivers/misc/habanalabs/habanalabs_ioctl.c b/drivers/misc/habanalabs/habanalabs_ioctl.c index 5d9c269d99db..6474b868ef27 100644 --- a/drivers/misc/habanalabs/habanalabs_ioctl.c +++ b/drivers/misc/habanalabs/habanalabs_ioctl.c @@ -242,6 +242,22 @@ static int get_clk_rate(struct hl_device *hdev, struct hl_info_args *args) min((size_t) max_size, sizeof(clk_rate))) ? -EFAULT : 0; } +static int get_reset_count(struct hl_device *hdev, struct hl_info_args *args) +{ + struct hl_info_reset_count reset_count = {0}; + u32 max_size = args->return_size; + void __user *out = (void __user *) (uintptr_t) args->return_pointer; + + if ((!max_size) || (!out)) + return -EINVAL; + + reset_count.hard_reset_cnt = hdev->hard_reset_cnt; + reset_count.soft_reset_cnt = hdev->soft_reset_cnt; + + return copy_to_user(out, &reset_count, + min((size_t) max_size, sizeof(reset_count))) ? -EFAULT : 0; +} + static int _hl_info_ioctl(struct hl_fpriv *hpriv, void *data, struct device *dev) { @@ -260,6 +276,9 @@ static int _hl_info_ioctl(struct hl_fpriv *hpriv, void *data, case HL_INFO_DEVICE_STATUS: return device_status_info(hdev, args); + case HL_INFO_RESET_COUNT: + return get_reset_count(hdev, args); + default: break; } diff --git a/include/uapi/misc/habanalabs.h b/include/uapi/misc/habanalabs.h index 716e70750f23..4faa2c9767e5 100644 --- a/include/uapi/misc/habanalabs.h +++ b/include/uapi/misc/habanalabs.h @@ -98,6 +98,9 @@ enum hl_device_status { * HL_INFO_CLK_RATE - Retrieve the current and maximum clock rate * of the device in MHz. The maximum clock rate is * configurable via sysfs parameter + * HL_INFO_RESET_COUNT - Retrieve the counts of the soft and hard reset + * operations performed on the device since the last + * time the driver was loaded. */ #define HL_INFO_HW_IP_INFO 0 #define HL_INFO_HW_EVENTS 1 @@ -107,6 +110,7 @@ enum hl_device_status { #define HL_INFO_DEVICE_UTILIZATION 6 #define HL_INFO_HW_EVENTS_AGGREGATE 7 #define HL_INFO_CLK_RATE 8 +#define HL_INFO_RESET_COUNT 9 #define HL_INFO_VERSION_MAX_LEN 128 #define HL_INFO_CARD_NAME_MAX_LEN 16 @@ -160,6 +164,11 @@ struct hl_info_clk_rate { __u32 max_clk_rate_mhz; }; +struct hl_info_reset_count { + __u32 hard_reset_cnt; + __u32 soft_reset_cnt; +}; + struct hl_info_args { /* Location of relevant struct in userspace */ __u64 return_pointer; From 1af69d30c41d0b0f15d8be80c100cefaa909816c Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Sun, 17 Nov 2019 17:35:49 +0200 Subject: [PATCH 195/198] habanalabs: make the reset code more consistent In the hl_device_reset we ask about the hard_reset argument when we want to differentiate between soft and hard reset, except for three places where we use "from_hard_reset_thread". Replace one of those locations with the hard_reset argument as it is guaranteed that if we reached to that line in the code during hard_reset, it is from a kernel thread. Signed-off-by: Oded Gabbay Reviewed-by: Tomer Tayar --- drivers/misc/habanalabs/device.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/habanalabs/device.c b/drivers/misc/habanalabs/device.c index 2f5a4da707e7..80205d8584ce 100644 --- a/drivers/misc/habanalabs/device.c +++ b/drivers/misc/habanalabs/device.c @@ -891,7 +891,7 @@ again: * can't really exit until all its CSs are done, which is what we * do in cs rollback */ - if (from_hard_reset_thread) + if (hard_reset) device_kill_open_processes(hdev); /* Release kernel context */ From 55f6d680970ea922d4ee23d5ac88d3a8046221fb Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Sun, 17 Nov 2019 17:41:57 +0200 Subject: [PATCH 196/198] habanalabs: flush EQ workers in hard reset During hard-reset, there can be multiple events received from the H/W. For each event, the driver opens a worker thread to handle it. For some of the events, the driver will read/write registers in the code that handles the event. In case of hard-reset, we must prevent reads/writes to the registers during the reset operation because the device might get stuck if that happens. Therefore, flush the EQ workers before resetting the device (in hard-reset only). Additional events won't arrive as we synced and disabled the interrupts. Signed-off-by: Oded Gabbay Reviewed-by: Tomer Tayar --- drivers/misc/habanalabs/device.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/misc/habanalabs/device.c b/drivers/misc/habanalabs/device.c index 80205d8584ce..b155e9549076 100644 --- a/drivers/misc/habanalabs/device.c +++ b/drivers/misc/habanalabs/device.c @@ -887,13 +887,19 @@ again: /* Go over all the queues, release all CS and their jobs */ hl_cs_rollback_all(hdev); - /* Kill processes here after CS rollback. This is because the process - * can't really exit until all its CSs are done, which is what we - * do in cs rollback - */ - if (hard_reset) + if (hard_reset) { + /* Kill processes here after CS rollback. This is because the + * process can't really exit until all its CSs are done, which + * is what we do in cs rollback + */ device_kill_open_processes(hdev); + /* Flush the Event queue workers to make sure no other thread is + * reading or writing to registers during the reset + */ + flush_workqueue(hdev->eq_wq); + } + /* Release kernel context */ if ((hard_reset) && (hl_ctx_put(hdev->kernel_ctx) == 1)) hdev->kernel_ctx = NULL; From 5feccddcf9922ee3c25587d5e609bf58503ad93e Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Mon, 18 Nov 2019 09:41:08 +0200 Subject: [PATCH 197/198] habanalabs: add more protection of device during reset Prevent accesses to the device (register read/write) from debugfs entries during reset as that can cause the device to get stuck. Signed-off-by: Oded Gabbay Reviewed-by: Tomer Tayar --- drivers/misc/habanalabs/debugfs.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/misc/habanalabs/debugfs.c b/drivers/misc/habanalabs/debugfs.c index 1cf75010a379..20413e350343 100644 --- a/drivers/misc/habanalabs/debugfs.c +++ b/drivers/misc/habanalabs/debugfs.c @@ -528,6 +528,12 @@ static int engines_show(struct seq_file *s, void *data) struct hl_dbg_device_entry *dev_entry = entry->dev_entry; struct hl_device *hdev = dev_entry->hdev; + if (atomic_read(&hdev->in_reset)) { + dev_warn_ratelimited(hdev->dev, + "Can't check device idle during reset\n"); + return 0; + } + hdev->asic_funcs->is_device_idle(hdev, NULL, s); return 0; @@ -640,6 +646,11 @@ static ssize_t hl_data_read32(struct file *f, char __user *buf, u32 val; ssize_t rc; + if (atomic_read(&hdev->in_reset)) { + dev_warn_ratelimited(hdev->dev, "Can't read during reset\n"); + return 0; + } + if (*ppos) return 0; @@ -669,6 +680,11 @@ static ssize_t hl_data_write32(struct file *f, const char __user *buf, u32 value; ssize_t rc; + if (atomic_read(&hdev->in_reset)) { + dev_warn_ratelimited(hdev->dev, "Can't write during reset\n"); + return 0; + } + rc = kstrtouint_from_user(buf, count, 16, &value); if (rc) return rc; From 599ea01ce0b322cc3eba5b2ce67bb9972edfeb52 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 21 Nov 2019 21:28:41 +0800 Subject: [PATCH 198/198] char: Fix Kconfig indentation, continued Adjust indentation from seven spaces to tab (+optional two spaces) as in coding style with command like: $ sed -e 's/^ /\t/' -i */Kconfig Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20191121132842.28942-1-krzk@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/char/hw_random/Kconfig | 18 +++---- drivers/char/ipmi/Kconfig | 98 +++++++++++++++++----------------- 2 files changed, 58 insertions(+), 58 deletions(-) diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig index 5011eb673ab4..0457b22e252e 100644 --- a/drivers/char/hw_random/Kconfig +++ b/drivers/char/hw_random/Kconfig @@ -373,17 +373,17 @@ config HW_RANDOM_MESON If unsure, say Y. config HW_RANDOM_CAVIUM - tristate "Cavium ThunderX Random Number Generator support" - depends on HW_RANDOM && PCI && (ARM64 || (COMPILE_TEST && 64BIT)) - default HW_RANDOM - ---help--- - This driver provides kernel-side support for the Random Number - Generator hardware found on Cavium SoCs. + tristate "Cavium ThunderX Random Number Generator support" + depends on HW_RANDOM && PCI && (ARM64 || (COMPILE_TEST && 64BIT)) + default HW_RANDOM + ---help--- + This driver provides kernel-side support for the Random Number + Generator hardware found on Cavium SoCs. - To compile this driver as a module, choose M here: the - module will be called cavium_rng. + To compile this driver as a module, choose M here: the + module will be called cavium_rng. - If unsure, say Y. + If unsure, say Y. config HW_RANDOM_MTK tristate "Mediatek Random Number Generator support" diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig index cc4bea773ded..7dc2c3ec4051 100644 --- a/drivers/char/ipmi/Kconfig +++ b/drivers/char/ipmi/Kconfig @@ -4,38 +4,38 @@ # menuconfig IPMI_HANDLER - tristate 'IPMI top-level message handler' - depends on HAS_IOMEM - select IPMI_DMI_DECODE if DMI - help - This enables the central IPMI message handler, required for IPMI - to work. + tristate 'IPMI top-level message handler' + depends on HAS_IOMEM + select IPMI_DMI_DECODE if DMI + help + This enables the central IPMI message handler, required for IPMI + to work. - IPMI is a standard for managing sensors (temperature, - voltage, etc.) in a system. + IPMI is a standard for managing sensors (temperature, + voltage, etc.) in a system. - See for more details on the driver. + See for more details on the driver. - If unsure, say N. + If unsure, say N. config IPMI_DMI_DECODE - select IPMI_PLAT_DATA - bool + select IPMI_PLAT_DATA + bool config IPMI_PLAT_DATA - bool + bool if IPMI_HANDLER config IPMI_PANIC_EVENT - bool 'Generate a panic event to all BMCs on a panic' - help - When a panic occurs, this will cause the IPMI message handler to, - by default, generate an IPMI event describing the panic to each - interface registered with the message handler. This is always - available, the module parameter for ipmi_msghandler named - panic_op can be set to "event" to chose this value, this config - simply causes the default value to be set to "event". + bool 'Generate a panic event to all BMCs on a panic' + help + When a panic occurs, this will cause the IPMI message handler to, + by default, generate an IPMI event describing the panic to each + interface registered with the message handler. This is always + available, the module parameter for ipmi_msghandler named + panic_op can be set to "event" to chose this value, this config + simply causes the default value to be set to "event". config IPMI_PANIC_STRING bool 'Generate OEM events containing the panic string' @@ -54,43 +54,43 @@ config IPMI_PANIC_STRING causes the default value to be set to "string". config IPMI_DEVICE_INTERFACE - tristate 'Device interface for IPMI' - help - This provides an IOCTL interface to the IPMI message handler so - userland processes may use IPMI. It supports poll() and select(). + tristate 'Device interface for IPMI' + help + This provides an IOCTL interface to the IPMI message handler so + userland processes may use IPMI. It supports poll() and select(). config IPMI_SI - tristate 'IPMI System Interface handler' - select IPMI_PLAT_DATA - help - Provides a driver for System Interfaces (KCS, SMIC, BT). - Currently, only KCS and SMIC are supported. If - you are using IPMI, you should probably say "y" here. + tristate 'IPMI System Interface handler' + select IPMI_PLAT_DATA + help + Provides a driver for System Interfaces (KCS, SMIC, BT). + Currently, only KCS and SMIC are supported. If + you are using IPMI, you should probably say "y" here. config IPMI_SSIF - tristate 'IPMI SMBus handler (SSIF)' - select I2C - help - Provides a driver for a SMBus interface to a BMC, meaning that you - have a driver that must be accessed over an I2C bus instead of a - standard interface. This module requires I2C support. + tristate 'IPMI SMBus handler (SSIF)' + select I2C + help + Provides a driver for a SMBus interface to a BMC, meaning that you + have a driver that must be accessed over an I2C bus instead of a + standard interface. This module requires I2C support. config IPMI_POWERNV - depends on PPC_POWERNV - tristate 'POWERNV (OPAL firmware) IPMI interface' - help - Provides a driver for OPAL firmware-based IPMI interfaces. + depends on PPC_POWERNV + tristate 'POWERNV (OPAL firmware) IPMI interface' + help + Provides a driver for OPAL firmware-based IPMI interfaces. config IPMI_WATCHDOG - tristate 'IPMI Watchdog Timer' - help - This enables the IPMI watchdog timer. + tristate 'IPMI Watchdog Timer' + help + This enables the IPMI watchdog timer. config IPMI_POWEROFF - tristate 'IPMI Poweroff' - help - This enables a function to power off the system with IPMI if - the IPMI management controller is capable of this. + tristate 'IPMI Poweroff' + help + This enables a function to power off the system with IPMI if + the IPMI management controller is capable of this. endif # IPMI_HANDLER @@ -126,7 +126,7 @@ config NPCM7XX_KCS_IPMI_BMC config ASPEED_BT_IPMI_BMC depends on ARCH_ASPEED || COMPILE_TEST - depends on REGMAP && REGMAP_MMIO && MFD_SYSCON + depends on REGMAP && REGMAP_MMIO && MFD_SYSCON tristate "BT IPMI bmc driver" help Provides a driver for the BT (Block Transfer) IPMI interface