summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Kconfig1
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/base/driver.c6
-rw-r--r--drivers/block/efi-block-io.c2
-rw-r--r--drivers/clk/Makefile3
-rw-r--r--drivers/clk/at91/Makefile6
-rw-r--r--drivers/clk/at91/at91sam9260.c497
-rw-r--r--drivers/clk/at91/at91sam9rl.c177
-rw-r--r--drivers/clk/at91/at91sam9x5.c315
-rw-r--r--drivers/clk/at91/clk-generated.c185
-rw-r--r--drivers/clk/at91/clk-h32mx.c55
-rw-r--r--drivers/clk/at91/clk-main.c112
-rw-r--r--drivers/clk/at91/clk-master.c94
-rw-r--r--drivers/clk/at91/clk-peripheral.c82
-rw-r--r--drivers/clk/at91/clk-pll.c174
-rw-r--r--drivers/clk/at91/clk-plldiv.c27
-rw-r--r--drivers/clk/at91/clk-programmable.c83
-rw-r--r--drivers/clk/at91/clk-slow.c33
-rw-r--r--drivers/clk/at91/clk-smd.c33
-rw-r--r--drivers/clk/at91/clk-system.c42
-rw-r--r--drivers/clk/at91/clk-usb.c94
-rw-r--r--drivers/clk/at91/clk-utmi.c100
-rw-r--r--drivers/clk/at91/dt-compat.c961
-rw-r--r--drivers/clk/at91/pmc.c248
-rw-r--r--drivers/clk/at91/pmc.h169
-rw-r--r--drivers/clk/at91/sama5d2.c342
-rw-r--r--drivers/clk/at91/sama5d4.c270
-rw-r--r--drivers/clk/clk-bulk.c102
-rw-r--r--drivers/clk/clkdev.c5
-rw-r--r--drivers/clocksource/Kconfig1
-rw-r--r--drivers/clocksource/efi_x86.c4
-rw-r--r--drivers/efi/efi-device.c2
-rw-r--r--drivers/hab/habv3.c4
-rw-r--r--drivers/i2c/Kconfig3
-rw-r--r--drivers/i2c/busses/Makefile1
-rw-r--r--drivers/i2c/busses/i2c-imx-early.c310
-rw-r--r--drivers/i2c/busses/i2c-imx.c272
-rw-r--r--drivers/i2c/busses/i2c-imx.h52
-rw-r--r--drivers/mci/Kconfig3
-rw-r--r--drivers/mci/Makefile1
-rw-r--r--drivers/mci/imx-esdhc-pbl.c406
-rw-r--r--drivers/mci/imx-esdhc.c9
-rw-r--r--drivers/mci/imx-esdhc.h8
-rw-r--r--drivers/mfd/syscon.c14
-rw-r--r--drivers/net/e1000/main.c7
-rw-r--r--drivers/net/efi-snp.c2
-rw-r--r--drivers/net/rtl8139.c7
-rw-r--r--drivers/net/rtl8169.c7
-rw-r--r--drivers/nvme/Kconfig5
-rw-r--r--drivers/nvme/Makefile1
-rw-r--r--drivers/nvme/host/Kconfig11
-rw-r--r--drivers/nvme/host/Makefile9
-rw-r--r--drivers/nvme/host/core.c614
-rw-r--r--drivers/nvme/host/nvme.h148
-rw-r--r--drivers/nvme/host/pci.c697
-rw-r--r--drivers/pci/pci-imx6.c16
-rw-r--r--drivers/pci/pcie-designware.c2
-rw-r--r--drivers/phy/Kconfig2
-rw-r--r--drivers/phy/Makefile1
-rw-r--r--drivers/phy/freescale/Kconfig4
-rw-r--r--drivers/phy/freescale/Makefile1
-rw-r--r--drivers/phy/freescale/phy-fsl-imx8mq-usb.c130
-rw-r--r--drivers/phy/phy-core.c4
-rw-r--r--drivers/usb/Kconfig4
-rw-r--r--drivers/usb/Makefile2
-rw-r--r--drivers/usb/dwc3/Kconfig22
-rw-r--r--drivers/usb/dwc3/Makefile10
-rw-r--r--drivers/usb/dwc3/core.c740
-rw-r--r--drivers/usb/dwc3/core.h1267
-rw-r--r--drivers/usb/dwc3/debug.h664
-rw-r--r--drivers/usb/dwc3/host.c36
-rw-r--r--drivers/usb/dwc3/io.h41
-rw-r--r--drivers/usb/host/xhci-hcd.c549
-rw-r--r--drivers/usb/host/xhci-pci.c7
-rw-r--r--drivers/usb/host/xhci.h51
-rw-r--r--drivers/usb/imx/chipidea-imx.c6
-rw-r--r--drivers/usb/misc/Kconfig14
-rw-r--r--drivers/usb/misc/Makefile6
-rw-r--r--drivers/usb/misc/usb251xb.c683
-rw-r--r--drivers/watchdog/Kconfig8
-rw-r--r--drivers/watchdog/Makefile1
-rw-r--r--drivers/watchdog/efi_wdt.c64
82 files changed, 9709 insertions, 1443 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig
index c6c2eb14db..d6fbcbfe16 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -39,5 +39,6 @@ source "drivers/phy/Kconfig"
source "drivers/crypto/Kconfig"
source "drivers/memory/Kconfig"
source "drivers/soc/imx/Kconfig"
+source "drivers/nvme/Kconfig"
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 752fd66242..65fd488ce9 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -39,3 +39,4 @@ obj-$(CONFIG_CRYPTO_HW) += crypto/
obj-$(CONFIG_AIODEV) += aiodev/
obj-y += memory/
obj-y += soc/imx/
+obj-y += nvme/
diff --git a/drivers/base/driver.c b/drivers/base/driver.c
index 1fd6bbc014..eec2a2d8a2 100644
--- a/drivers/base/driver.c
+++ b/drivers/base/driver.c
@@ -439,12 +439,6 @@ int generic_memmap_ro(struct cdev *cdev, void **map, int flags)
return generic_memmap_rw(cdev, map, flags);
}
-int dummy_probe(struct device_d *dev)
-{
- return 0;
-}
-EXPORT_SYMBOL(dummy_probe);
-
/**
* dev_set_name - set a device name
* @dev: device
diff --git a/drivers/block/efi-block-io.c b/drivers/block/efi-block-io.c
index 2bbeb99e69..d167d814c2 100644
--- a/drivers/block/efi-block-io.c
+++ b/drivers/block/efi-block-io.c
@@ -142,7 +142,7 @@ static int is_bio_usbdev(struct efi_device *efidev)
return 0;
}
-int efi_bio_probe(struct efi_device *efidev)
+static int efi_bio_probe(struct efi_device *efidev)
{
int ret;
struct efi_bio_priv *priv;
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index cd4026c944..34c44fff9b 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -1,7 +1,8 @@
obj-$(CONFIG_COMMON_CLK) += clk.o clk-fixed.o clk-divider.o clk-fixed-factor.o \
clk-mux.o clk-gate.o clk-composite.o \
clk-fractional-divider.o clk-conf.o \
- clk-gate-shared.o clk-gpio.o
+ clk-gate-shared.o clk-gpio.o \
+ clk-bulk.o
obj-$(CONFIG_CLKDEV_LOOKUP) += clkdev.o
obj-$(CONFIG_ARCH_MVEBU) += mvebu/
diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile
index 13e67bd35c..bf9b27f0f4 100644
--- a/drivers/clk/at91/Makefile
+++ b/drivers/clk/at91/Makefile
@@ -11,3 +11,9 @@ obj-$(CONFIG_HAVE_AT91_USB_CLK) += clk-usb.o
obj-$(CONFIG_HAVE_AT91_SMD) += clk-smd.o
obj-$(CONFIG_HAVE_AT91_H32MX) += clk-h32mx.o
obj-$(CONFIG_HAVE_AT91_GENERATED_CLK) += clk-generated.o
+obj-$(CONFIG_SOC_AT91SAM9) += at91sam9260.o
+obj-$(CONFIG_SOC_AT91SAM9) += at91sam9rl.o
+obj-$(CONFIG_SOC_AT91SAM9) += at91sam9x5.o
+obj-$(CONFIG_SOC_SAMA5D2) += sama5d2.o
+obj-$(CONFIG_SOC_SAMA5D3) += dt-compat.o
+obj-$(CONFIG_SOC_SAMA5D4) += sama5d4.o
diff --git a/drivers/clk/at91/at91sam9260.c b/drivers/clk/at91/at91sam9260.c
new file mode 100644
index 0000000000..ac67dcc8f7
--- /dev/null
+++ b/drivers/clk/at91/at91sam9260.c
@@ -0,0 +1,497 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <driver.h>
+#include <regmap.h>
+#include <stdio.h>
+#include <mfd/syscon.h>
+
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include <dt-bindings/clock/at91.h>
+
+#include "pmc.h"
+
+struct sck {
+ char *n;
+ char *p;
+ u8 id;
+};
+
+struct pck {
+ char *n;
+ u8 id;
+};
+
+struct at91sam926x_data {
+ const struct clk_pll_layout *plla_layout;
+ const struct clk_pll_characteristics *plla_characteristics;
+ const struct clk_pll_layout *pllb_layout;
+ const struct clk_pll_characteristics *pllb_characteristics;
+ const struct clk_master_characteristics *mck_characteristics;
+ const struct sck *sck;
+ const struct pck *pck;
+ u8 num_sck;
+ u8 num_pck;
+ u8 num_progck;
+ bool has_slck;
+};
+
+static const struct clk_master_characteristics sam9260_mck_characteristics = {
+ .output = { .min = 0, .max = 105000000 },
+ .divisors = { 1, 2, 4, 0 },
+};
+
+static u8 sam9260_plla_out[] = { 0, 2 };
+
+static u16 sam9260_plla_icpll[] = { 1, 1 };
+
+static struct clk_range sam9260_plla_outputs[] = {
+ { .min = 80000000, .max = 160000000 },
+ { .min = 150000000, .max = 240000000 },
+};
+
+static const struct clk_pll_characteristics sam9260_plla_characteristics = {
+ .input = { .min = 1000000, .max = 32000000 },
+ .num_output = ARRAY_SIZE(sam9260_plla_outputs),
+ .output = sam9260_plla_outputs,
+ .icpll = sam9260_plla_icpll,
+ .out = sam9260_plla_out,
+};
+
+static u8 sam9260_pllb_out[] = { 1 };
+
+static u16 sam9260_pllb_icpll[] = { 1 };
+
+static struct clk_range sam9260_pllb_outputs[] = {
+ { .min = 70000000, .max = 130000000 },
+};
+
+static const struct clk_pll_characteristics sam9260_pllb_characteristics = {
+ .input = { .min = 1000000, .max = 5000000 },
+ .num_output = ARRAY_SIZE(sam9260_pllb_outputs),
+ .output = sam9260_pllb_outputs,
+ .icpll = sam9260_pllb_icpll,
+ .out = sam9260_pllb_out,
+};
+
+static const struct sck at91sam9260_systemck[] = {
+ { .n = "uhpck", .p = "usbck", .id = 6 },
+ { .n = "udpck", .p = "usbck", .id = 7 },
+ { .n = "pck0", .p = "prog0", .id = 8 },
+ { .n = "pck1", .p = "prog1", .id = 9 },
+};
+
+static const struct pck at91sam9260_periphck[] = {
+ { .n = "pioA_clk", .id = 2 },
+ { .n = "pioB_clk", .id = 3 },
+ { .n = "pioC_clk", .id = 4 },
+ { .n = "adc_clk", .id = 5 },
+ { .n = "usart0_clk", .id = 6 },
+ { .n = "usart1_clk", .id = 7 },
+ { .n = "usart2_clk", .id = 8 },
+ { .n = "mci0_clk", .id = 9 },
+ { .n = "udc_clk", .id = 10 },
+ { .n = "twi0_clk", .id = 11 },
+ { .n = "spi0_clk", .id = 12 },
+ { .n = "spi1_clk", .id = 13 },
+ { .n = "ssc0_clk", .id = 14 },
+ { .n = "tc0_clk", .id = 17 },
+ { .n = "tc1_clk", .id = 18 },
+ { .n = "tc2_clk", .id = 19 },
+ { .n = "ohci_clk", .id = 20 },
+ { .n = "macb0_clk", .id = 21 },
+ { .n = "isi_clk", .id = 22 },
+ { .n = "usart3_clk", .id = 23 },
+ { .n = "uart0_clk", .id = 24 },
+ { .n = "uart1_clk", .id = 25 },
+ { .n = "tc3_clk", .id = 26 },
+ { .n = "tc4_clk", .id = 27 },
+ { .n = "tc5_clk", .id = 28 },
+};
+
+static struct at91sam926x_data at91sam9260_data = {
+ .plla_layout = &at91rm9200_pll_layout,
+ .plla_characteristics = &sam9260_plla_characteristics,
+ .pllb_layout = &at91rm9200_pll_layout,
+ .pllb_characteristics = &sam9260_pllb_characteristics,
+ .mck_characteristics = &sam9260_mck_characteristics,
+ .sck = at91sam9260_systemck,
+ .num_sck = ARRAY_SIZE(at91sam9260_systemck),
+ .pck = at91sam9260_periphck,
+ .num_pck = ARRAY_SIZE(at91sam9260_periphck),
+ .num_progck = 2,
+ .has_slck = true,
+};
+
+static const struct clk_master_characteristics sam9g20_mck_characteristics = {
+ .output = { .min = 0, .max = 133000000 },
+ .divisors = { 1, 2, 4, 6 },
+};
+
+static u8 sam9g20_plla_out[] = { 0, 1, 2, 3, 0, 1, 2, 3 };
+
+static u16 sam9g20_plla_icpll[] = { 0, 0, 0, 0, 1, 1, 1, 1 };
+
+static struct clk_range sam9g20_plla_outputs[] = {
+ { .min = 745000000, .max = 800000000 },
+ { .min = 695000000, .max = 750000000 },
+ { .min = 645000000, .max = 700000000 },
+ { .min = 595000000, .max = 650000000 },
+ { .min = 545000000, .max = 600000000 },
+ { .min = 495000000, .max = 550000000 },
+ { .min = 445000000, .max = 500000000 },
+ { .min = 400000000, .max = 450000000 },
+};
+
+static const struct clk_pll_characteristics sam9g20_plla_characteristics = {
+ .input = { .min = 2000000, .max = 32000000 },
+ .num_output = ARRAY_SIZE(sam9g20_plla_outputs),
+ .output = sam9g20_plla_outputs,
+ .icpll = sam9g20_plla_icpll,
+ .out = sam9g20_plla_out,
+};
+
+static u8 sam9g20_pllb_out[] = { 0 };
+
+static u16 sam9g20_pllb_icpll[] = { 0 };
+
+static struct clk_range sam9g20_pllb_outputs[] = {
+ { .min = 30000000, .max = 100000000 },
+};
+
+static const struct clk_pll_characteristics sam9g20_pllb_characteristics = {
+ .input = { .min = 2000000, .max = 32000000 },
+ .num_output = ARRAY_SIZE(sam9g20_pllb_outputs),
+ .output = sam9g20_pllb_outputs,
+ .icpll = sam9g20_pllb_icpll,
+ .out = sam9g20_pllb_out,
+};
+
+static struct at91sam926x_data at91sam9g20_data = {
+ .plla_layout = &at91sam9g45_pll_layout,
+ .plla_characteristics = &sam9g20_plla_characteristics,
+ .pllb_layout = &at91sam9g20_pllb_layout,
+ .pllb_characteristics = &sam9g20_pllb_characteristics,
+ .mck_characteristics = &sam9g20_mck_characteristics,
+ .sck = at91sam9260_systemck,
+ .num_sck = ARRAY_SIZE(at91sam9260_systemck),
+ .pck = at91sam9260_periphck,
+ .num_pck = ARRAY_SIZE(at91sam9260_periphck),
+ .num_progck = 2,
+ .has_slck = true,
+};
+
+static const struct clk_master_characteristics sam9261_mck_characteristics = {
+ .output = { .min = 0, .max = 94000000 },
+ .divisors = { 1, 2, 4, 0 },
+};
+
+static struct clk_range sam9261_plla_outputs[] = {
+ { .min = 80000000, .max = 200000000 },
+ { .min = 190000000, .max = 240000000 },
+};
+
+static const struct clk_pll_characteristics sam9261_plla_characteristics = {
+ .input = { .min = 1000000, .max = 32000000 },
+ .num_output = ARRAY_SIZE(sam9261_plla_outputs),
+ .output = sam9261_plla_outputs,
+ .icpll = sam9260_plla_icpll,
+ .out = sam9260_plla_out,
+};
+
+static u8 sam9261_pllb_out[] = { 1 };
+
+static u16 sam9261_pllb_icpll[] = { 1 };
+
+static struct clk_range sam9261_pllb_outputs[] = {
+ { .min = 70000000, .max = 130000000 },
+};
+
+static const struct clk_pll_characteristics sam9261_pllb_characteristics = {
+ .input = { .min = 1000000, .max = 5000000 },
+ .num_output = ARRAY_SIZE(sam9261_pllb_outputs),
+ .output = sam9261_pllb_outputs,
+ .icpll = sam9261_pllb_icpll,
+ .out = sam9261_pllb_out,
+};
+
+static const struct sck at91sam9261_systemck[] = {
+ { .n = "uhpck", .p = "usbck", .id = 6 },
+ { .n = "udpck", .p = "usbck", .id = 7 },
+ { .n = "pck0", .p = "prog0", .id = 8 },
+ { .n = "pck1", .p = "prog1", .id = 9 },
+ { .n = "pck2", .p = "prog2", .id = 10 },
+ { .n = "pck3", .p = "prog3", .id = 11 },
+ { .n = "hclk0", .p = "masterck", .id = 16 },
+ { .n = "hclk1", .p = "masterck", .id = 17 },
+};
+
+static const struct pck at91sam9261_periphck[] = {
+ { .n = "pioA_clk", .id = 2, },
+ { .n = "pioB_clk", .id = 3, },
+ { .n = "pioC_clk", .id = 4, },
+ { .n = "usart0_clk", .id = 6, },
+ { .n = "usart1_clk", .id = 7, },
+ { .n = "usart2_clk", .id = 8, },
+ { .n = "mci0_clk", .id = 9, },
+ { .n = "udc_clk", .id = 10, },
+ { .n = "twi0_clk", .id = 11, },
+ { .n = "spi0_clk", .id = 12, },
+ { .n = "spi1_clk", .id = 13, },
+ { .n = "ssc0_clk", .id = 14, },
+ { .n = "ssc1_clk", .id = 15, },
+ { .n = "ssc2_clk", .id = 16, },
+ { .n = "tc0_clk", .id = 17, },
+ { .n = "tc1_clk", .id = 18, },
+ { .n = "tc2_clk", .id = 19, },
+ { .n = "ohci_clk", .id = 20, },
+ { .n = "lcd_clk", .id = 21, },
+};
+
+static struct at91sam926x_data at91sam9261_data = {
+ .plla_layout = &at91rm9200_pll_layout,
+ .plla_characteristics = &sam9261_plla_characteristics,
+ .pllb_layout = &at91rm9200_pll_layout,
+ .pllb_characteristics = &sam9261_pllb_characteristics,
+ .mck_characteristics = &sam9261_mck_characteristics,
+ .sck = at91sam9261_systemck,
+ .num_sck = ARRAY_SIZE(at91sam9261_systemck),
+ .pck = at91sam9261_periphck,
+ .num_pck = ARRAY_SIZE(at91sam9261_periphck),
+ .num_progck = 4,
+};
+
+static const struct clk_master_characteristics sam9263_mck_characteristics = {
+ .output = { .min = 0, .max = 120000000 },
+ .divisors = { 1, 2, 4, 0 },
+};
+
+static struct clk_range sam9263_pll_outputs[] = {
+ { .min = 80000000, .max = 200000000 },
+ { .min = 190000000, .max = 240000000 },
+};
+
+static const struct clk_pll_characteristics sam9263_pll_characteristics = {
+ .input = { .min = 1000000, .max = 32000000 },
+ .num_output = ARRAY_SIZE(sam9263_pll_outputs),
+ .output = sam9263_pll_outputs,
+ .icpll = sam9260_plla_icpll,
+ .out = sam9260_plla_out,
+};
+
+static const struct sck at91sam9263_systemck[] = {
+ { .n = "uhpck", .p = "usbck", .id = 6 },
+ { .n = "udpck", .p = "usbck", .id = 7 },
+ { .n = "pck0", .p = "prog0", .id = 8 },
+ { .n = "pck1", .p = "prog1", .id = 9 },
+ { .n = "pck2", .p = "prog2", .id = 10 },
+ { .n = "pck3", .p = "prog3", .id = 11 },
+};
+
+static const struct pck at91sam9263_periphck[] = {
+ { .n = "pioA_clk", .id = 2, },
+ { .n = "pioB_clk", .id = 3, },
+ { .n = "pioCDE_clk", .id = 4, },
+ { .n = "usart0_clk", .id = 7, },
+ { .n = "usart1_clk", .id = 8, },
+ { .n = "usart2_clk", .id = 9, },
+ { .n = "mci0_clk", .id = 10, },
+ { .n = "mci1_clk", .id = 11, },
+ { .n = "can_clk", .id = 12, },
+ { .n = "twi0_clk", .id = 13, },
+ { .n = "spi0_clk", .id = 14, },
+ { .n = "spi1_clk", .id = 15, },
+ { .n = "ssc0_clk", .id = 16, },
+ { .n = "ssc1_clk", .id = 17, },
+ { .n = "ac97_clk", .id = 18, },
+ { .n = "tcb_clk", .id = 19, },
+ { .n = "pwm_clk", .id = 20, },
+ { .n = "macb0_clk", .id = 21, },
+ { .n = "g2de_clk", .id = 23, },
+ { .n = "udc_clk", .id = 24, },
+ { .n = "isi_clk", .id = 25, },
+ { .n = "lcd_clk", .id = 26, },
+ { .n = "dma_clk", .id = 27, },
+ { .n = "ohci_clk", .id = 29, },
+};
+
+static struct at91sam926x_data at91sam9263_data = {
+ .plla_layout = &at91rm9200_pll_layout,
+ .plla_characteristics = &sam9263_pll_characteristics,
+ .pllb_layout = &at91rm9200_pll_layout,
+ .pllb_characteristics = &sam9263_pll_characteristics,
+ .mck_characteristics = &sam9263_mck_characteristics,
+ .sck = at91sam9263_systemck,
+ .num_sck = ARRAY_SIZE(at91sam9263_systemck),
+ .pck = at91sam9263_periphck,
+ .num_pck = ARRAY_SIZE(at91sam9263_periphck),
+ .num_progck = 4,
+};
+
+static void __init at91sam926x_pmc_setup(struct device_node *np,
+ struct at91sam926x_data *data)
+{
+ const char *slowxtal_name, *mainxtal_name;
+ struct pmc_data *at91sam9260_pmc;
+ u32 usb_div[] = { 1, 2, 4, 0 };
+ const char *parent_names[6];
+ const char *slck_name;
+ struct regmap *regmap;
+ struct clk *hw;
+ int i;
+ bool bypass;
+
+ i = of_property_match_string(np, "clock-names", "slow_xtal");
+ if (i < 0)
+ return;
+
+ slowxtal_name = of_clk_get_parent_name(np, i);
+
+ i = of_property_match_string(np, "clock-names", "main_xtal");
+ if (i < 0)
+ return;
+ mainxtal_name = of_clk_get_parent_name(np, i);
+
+ regmap = syscon_node_to_regmap(np);
+ if (IS_ERR(regmap))
+ return;
+
+ at91sam9260_pmc = pmc_data_allocate(PMC_MAIN + 1,
+ ndck(data->sck, data->num_sck),
+ ndck(data->pck, data->num_pck), 0);
+ if (!at91sam9260_pmc)
+ return;
+
+ bypass = of_property_read_bool(np, "atmel,osc-bypass");
+
+ hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name,
+ bypass);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ hw = at91_clk_register_rm9200_main(regmap, "mainck", "main_osc");
+ if (IS_ERR(hw))
+ goto err_free;
+
+ at91sam9260_pmc->chws[PMC_MAIN] = hw;
+
+ if (data->has_slck) {
+ hw = clk_fixed("slow_rc_osc", 32768);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ parent_names[0] = "slow_rc_osc";
+ parent_names[1] = "slow_xtal";
+ hw = at91_clk_register_sam9260_slow(regmap, "slck",
+ parent_names, 2);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ at91sam9260_pmc->chws[PMC_SLOW] = hw;
+ slck_name = "slck";
+ } else {
+ slck_name = slowxtal_name;
+ }
+
+ hw = at91_clk_register_pll(regmap, "pllack", "mainck", 0,
+ data->plla_layout,
+ data->plla_characteristics);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ hw = at91_clk_register_pll(regmap, "pllbck", "mainck", 1,
+ data->pllb_layout,
+ data->pllb_characteristics);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ parent_names[0] = slck_name;
+ parent_names[1] = "mainck";
+ parent_names[2] = "pllack";
+ parent_names[3] = "pllbck";
+ hw = at91_clk_register_master(regmap, "masterck", 4, parent_names,
+ &at91rm9200_master_layout,
+ data->mck_characteristics);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ at91sam9260_pmc->chws[PMC_MCK] = hw;
+
+ hw = at91rm9200_clk_register_usb(regmap, "usbck", "pllbck", usb_div);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ parent_names[0] = slck_name;
+ parent_names[1] = "mainck";
+ parent_names[2] = "pllack";
+ parent_names[3] = "pllbck";
+ for (i = 0; i < data->num_progck; i++) {
+ char *name;
+
+ name = xasprintf("prog%d", i);
+
+ hw = at91_clk_register_programmable(regmap, name,
+ parent_names, 4, i,
+ &at91rm9200_programmable_layout);
+ if (IS_ERR(hw))
+ goto err_free;
+ }
+
+ for (i = 0; i < data->num_sck; i++) {
+ hw = at91_clk_register_system(regmap, data->sck[i].n,
+ data->sck[i].p,
+ data->sck[i].id);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ at91sam9260_pmc->shws[data->sck[i].id] = hw;
+ }
+
+ for (i = 0; i < data->num_pck; i++) {
+ hw = at91_clk_register_peripheral(regmap,
+ data->pck[i].n,
+ "masterck",
+ data->pck[i].id);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ at91sam9260_pmc->phws[data->pck[i].id] = hw;
+ }
+
+ of_clk_add_provider(np, of_clk_hw_pmc_get, at91sam9260_pmc);
+
+ return;
+
+err_free:
+ pmc_data_free(at91sam9260_pmc);
+}
+
+static void __init at91sam9260_pmc_setup(struct device_node *np)
+{
+ at91sam926x_pmc_setup(np, &at91sam9260_data);
+}
+CLK_OF_DECLARE_DRIVER(at91sam9260_pmc, "atmel,at91sam9260-pmc",
+ at91sam9260_pmc_setup);
+
+static void __init at91sam9261_pmc_setup(struct device_node *np)
+{
+ at91sam926x_pmc_setup(np, &at91sam9261_data);
+}
+CLK_OF_DECLARE_DRIVER(at91sam9261_pmc, "atmel,at91sam9261-pmc",
+ at91sam9261_pmc_setup);
+
+static void __init at91sam9263_pmc_setup(struct device_node *np)
+{
+ at91sam926x_pmc_setup(np, &at91sam9263_data);
+}
+CLK_OF_DECLARE_DRIVER(at91sam9263_pmc, "atmel,at91sam9263-pmc",
+ at91sam9263_pmc_setup);
+
+static void __init at91sam9g20_pmc_setup(struct device_node *np)
+{
+ at91sam926x_pmc_setup(np, &at91sam9g20_data);
+}
+CLK_OF_DECLARE_DRIVER(at91sam9g20_pmc, "atmel,at91sam9g20-pmc",
+ at91sam9g20_pmc_setup);
diff --git a/drivers/clk/at91/at91sam9rl.c b/drivers/clk/at91/at91sam9rl.c
new file mode 100644
index 0000000000..82acb38257
--- /dev/null
+++ b/drivers/clk/at91/at91sam9rl.c
@@ -0,0 +1,177 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <driver.h>
+#include <regmap.h>
+#include <stdio.h>
+#include <mfd/syscon.h>
+
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include <dt-bindings/clock/at91.h>
+
+#include "pmc.h"
+
+static const struct clk_master_characteristics sam9rl_mck_characteristics = {
+ .output = { .min = 0, .max = 94000000 },
+ .divisors = { 1, 2, 4, 0 },
+};
+
+static u8 sam9rl_plla_out[] = { 0, 2 };
+
+static struct clk_range sam9rl_plla_outputs[] = {
+ { .min = 80000000, .max = 200000000 },
+ { .min = 190000000, .max = 240000000 },
+};
+
+static const struct clk_pll_characteristics sam9rl_plla_characteristics = {
+ .input = { .min = 1000000, .max = 32000000 },
+ .num_output = ARRAY_SIZE(sam9rl_plla_outputs),
+ .output = sam9rl_plla_outputs,
+ .out = sam9rl_plla_out,
+};
+
+static const struct {
+ char *n;
+ char *p;
+ u8 id;
+} at91sam9rl_systemck[] = {
+ { .n = "pck0", .p = "prog0", .id = 8 },
+ { .n = "pck1", .p = "prog1", .id = 9 },
+};
+
+static const struct {
+ char *n;
+ u8 id;
+} at91sam9rl_periphck[] = {
+ { .n = "pioA_clk", .id = 2, },
+ { .n = "pioB_clk", .id = 3, },
+ { .n = "pioC_clk", .id = 4, },
+ { .n = "pioD_clk", .id = 5, },
+ { .n = "usart0_clk", .id = 6, },
+ { .n = "usart1_clk", .id = 7, },
+ { .n = "usart2_clk", .id = 8, },
+ { .n = "usart3_clk", .id = 9, },
+ { .n = "mci0_clk", .id = 10, },
+ { .n = "twi0_clk", .id = 11, },
+ { .n = "twi1_clk", .id = 12, },
+ { .n = "spi0_clk", .id = 13, },
+ { .n = "ssc0_clk", .id = 14, },
+ { .n = "ssc1_clk", .id = 15, },
+ { .n = "tc0_clk", .id = 16, },
+ { .n = "tc1_clk", .id = 17, },
+ { .n = "tc2_clk", .id = 18, },
+ { .n = "pwm_clk", .id = 19, },
+ { .n = "adc_clk", .id = 20, },
+ { .n = "dma0_clk", .id = 21, },
+ { .n = "udphs_clk", .id = 22, },
+ { .n = "lcd_clk", .id = 23, },
+};
+
+static void __init at91sam9rl_pmc_setup(struct device_node *np)
+{
+ const char *slck_name, *mainxtal_name;
+ struct pmc_data *at91sam9rl_pmc;
+ const char *parent_names[6];
+ struct regmap *regmap;
+ struct clk *hw;
+ int i;
+
+ i = of_property_match_string(np, "clock-names", "slow_clk");
+ if (i < 0)
+ return;
+
+ slck_name = of_clk_get_parent_name(np, i);
+
+ i = of_property_match_string(np, "clock-names", "main_xtal");
+ if (i < 0)
+ return;
+ mainxtal_name = of_clk_get_parent_name(np, i);
+
+ regmap = syscon_node_to_regmap(np);
+ if (IS_ERR(regmap))
+ return;
+
+ at91sam9rl_pmc = pmc_data_allocate(PMC_MAIN + 1,
+ nck(at91sam9rl_systemck),
+ nck(at91sam9rl_periphck), 0);
+ if (!at91sam9rl_pmc)
+ return;
+
+ hw = at91_clk_register_rm9200_main(regmap, "mainck", mainxtal_name);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ at91sam9rl_pmc->chws[PMC_MAIN] = hw;
+
+ hw = at91_clk_register_pll(regmap, "pllack", "mainck", 0,
+ &at91rm9200_pll_layout,
+ &sam9rl_plla_characteristics);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ hw = at91_clk_register_utmi(regmap, NULL, "utmick", "mainck");
+ if (IS_ERR(hw))
+ goto err_free;
+
+ at91sam9rl_pmc->chws[PMC_UTMI] = hw;
+
+ parent_names[0] = slck_name;
+ parent_names[1] = "mainck";
+ parent_names[2] = "pllack";
+ parent_names[3] = "utmick";
+ hw = at91_clk_register_master(regmap, "masterck", 4, parent_names,
+ &at91rm9200_master_layout,
+ &sam9rl_mck_characteristics);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ at91sam9rl_pmc->chws[PMC_MCK] = hw;
+
+ parent_names[0] = slck_name;
+ parent_names[1] = "mainck";
+ parent_names[2] = "pllack";
+ parent_names[3] = "utmick";
+ parent_names[4] = "masterck";
+ for (i = 0; i < 2; i++) {
+ char *name;
+
+ name = xasprintf("prog%d", i);
+
+ hw = at91_clk_register_programmable(regmap, name,
+ parent_names, 5, i,
+ &at91rm9200_programmable_layout);
+ if (IS_ERR(hw))
+ goto err_free;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(at91sam9rl_systemck); i++) {
+ hw = at91_clk_register_system(regmap, at91sam9rl_systemck[i].n,
+ at91sam9rl_systemck[i].p,
+ at91sam9rl_systemck[i].id);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ at91sam9rl_pmc->shws[at91sam9rl_systemck[i].id] = hw;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(at91sam9rl_periphck); i++) {
+ hw = at91_clk_register_peripheral(regmap,
+ at91sam9rl_periphck[i].n,
+ "masterck",
+ at91sam9rl_periphck[i].id);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ at91sam9rl_pmc->phws[at91sam9rl_periphck[i].id] = hw;
+ }
+
+ of_clk_add_provider(np, of_clk_hw_pmc_get, at91sam9rl_pmc);
+
+ return;
+
+err_free:
+ pmc_data_free(at91sam9rl_pmc);
+}
+CLK_OF_DECLARE_DRIVER(at91sam9rl_pmc, "atmel,at91sam9rl-pmc", at91sam9rl_pmc_setup);
diff --git a/drivers/clk/at91/at91sam9x5.c b/drivers/clk/at91/at91sam9x5.c
new file mode 100644
index 0000000000..5e0aacfbf6
--- /dev/null
+++ b/drivers/clk/at91/at91sam9x5.c
@@ -0,0 +1,315 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <driver.h>
+#include <regmap.h>
+#include <stdio.h>
+#include <mfd/syscon.h>
+
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include <dt-bindings/clock/at91.h>
+
+#include "pmc.h"
+
+static const struct clk_master_characteristics mck_characteristics = {
+ .output = { .min = 0, .max = 133333333 },
+ .divisors = { 1, 2, 4, 3 },
+ .have_div3_pres = 1,
+};
+
+static u8 plla_out[] = { 0, 1, 2, 3, 0, 1, 2, 3 };
+
+static u16 plla_icpll[] = { 0, 0, 0, 0, 1, 1, 1, 1 };
+
+static struct clk_range plla_outputs[] = {
+ { .min = 745000000, .max = 800000000 },
+ { .min = 695000000, .max = 750000000 },
+ { .min = 645000000, .max = 700000000 },
+ { .min = 595000000, .max = 650000000 },
+ { .min = 545000000, .max = 600000000 },
+ { .min = 495000000, .max = 555000000 },
+ { .min = 445000000, .max = 500000000 },
+ { .min = 400000000, .max = 450000000 },
+};
+
+static const struct clk_pll_characteristics plla_characteristics = {
+ .input = { .min = 2000000, .max = 32000000 },
+ .num_output = ARRAY_SIZE(plla_outputs),
+ .output = plla_outputs,
+ .icpll = plla_icpll,
+ .out = plla_out,
+};
+
+static const struct {
+ char *n;
+ char *p;
+ u8 id;
+} at91sam9x5_systemck[] = {
+ { .n = "ddrck", .p = "masterck", .id = 2 },
+ { .n = "smdck", .p = "smdclk", .id = 4 },
+ { .n = "uhpck", .p = "usbck", .id = 6 },
+ { .n = "udpck", .p = "usbck", .id = 7 },
+ { .n = "pck0", .p = "prog0", .id = 8 },
+ { .n = "pck1", .p = "prog1", .id = 9 },
+};
+
+struct pck {
+ char *n;
+ u8 id;
+};
+
+static const struct pck at91sam9x5_periphck[] = {
+ { .n = "pioAB_clk", .id = 2, },
+ { .n = "pioCD_clk", .id = 3, },
+ { .n = "smd_clk", .id = 4, },
+ { .n = "usart0_clk", .id = 5, },
+ { .n = "usart1_clk", .id = 6, },
+ { .n = "usart2_clk", .id = 7, },
+ { .n = "twi0_clk", .id = 9, },
+ { .n = "twi1_clk", .id = 10, },
+ { .n = "twi2_clk", .id = 11, },
+ { .n = "mci0_clk", .id = 12, },
+ { .n = "spi0_clk", .id = 13, },
+ { .n = "spi1_clk", .id = 14, },
+ { .n = "uart0_clk", .id = 15, },
+ { .n = "uart1_clk", .id = 16, },
+ { .n = "tcb0_clk", .id = 17, },
+ { .n = "pwm_clk", .id = 18, },
+ { .n = "adc_clk", .id = 19, },
+ { .n = "dma0_clk", .id = 20, },
+ { .n = "dma1_clk", .id = 21, },
+ { .n = "uhphs_clk", .id = 22, },
+ { .n = "udphs_clk", .id = 23, },
+ { .n = "mci1_clk", .id = 26, },
+ { .n = "ssc0_clk", .id = 28, },
+};
+
+static const struct pck at91sam9g15_periphck[] = {
+ { .n = "lcdc_clk", .id = 25, },
+ { /* sentinel */}
+};
+
+static const struct pck at91sam9g25_periphck[] = {
+ { .n = "usart3_clk", .id = 8, },
+ { .n = "macb0_clk", .id = 24, },
+ { .n = "isi_clk", .id = 25, },
+ { /* sentinel */}
+};
+
+static const struct pck at91sam9g35_periphck[] = {
+ { .n = "macb0_clk", .id = 24, },
+ { .n = "lcdc_clk", .id = 25, },
+ { /* sentinel */}
+};
+
+static const struct pck at91sam9x25_periphck[] = {
+ { .n = "usart3_clk", .id = 8, },
+ { .n = "macb0_clk", .id = 24, },
+ { .n = "macb1_clk", .id = 27, },
+ { .n = "can0_clk", .id = 29, },
+ { .n = "can1_clk", .id = 30, },
+ { /* sentinel */}
+};
+
+static const struct pck at91sam9x35_periphck[] = {
+ { .n = "macb0_clk", .id = 24, },
+ { .n = "lcdc_clk", .id = 25, },
+ { .n = "can0_clk", .id = 29, },
+ { .n = "can1_clk", .id = 30, },
+ { /* sentinel */}
+};
+
+static void __init at91sam9x5_pmc_setup(struct device_node *np,
+ const struct pck *extra_pcks,
+ bool has_lcdck)
+{
+ struct clk_range range = CLK_RANGE(0, 0);
+ const char *slck_name, *mainxtal_name;
+ struct pmc_data *at91sam9x5_pmc;
+ const char *parent_names[6];
+ struct regmap *regmap;
+ struct clk *hw;
+ int i;
+ bool bypass;
+
+ i = of_property_match_string(np, "clock-names", "slow_clk");
+ if (i < 0)
+ return;
+
+ slck_name = of_clk_get_parent_name(np, i);
+
+ i = of_property_match_string(np, "clock-names", "main_xtal");
+ if (i < 0)
+ return;
+ mainxtal_name = of_clk_get_parent_name(np, i);
+
+ regmap = syscon_node_to_regmap(np);
+ if (IS_ERR(regmap))
+ return;
+
+ at91sam9x5_pmc = pmc_data_allocate(PMC_MAIN + 1,
+ nck(at91sam9x5_systemck),
+ nck(at91sam9x35_periphck), 0);
+ if (!at91sam9x5_pmc)
+ return;
+
+ hw = at91_clk_register_main_rc_osc(regmap, "main_rc_osc", 12000000,
+ 50000000);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ bypass = of_property_read_bool(np, "atmel,osc-bypass");
+
+ hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name,
+ bypass);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ parent_names[0] = "main_rc_osc";
+ parent_names[1] = "main_osc";
+ hw = at91_clk_register_sam9x5_main(regmap, "mainck", parent_names, 2);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ at91sam9x5_pmc->chws[PMC_MAIN] = hw;
+
+ hw = at91_clk_register_pll(regmap, "pllack", "mainck", 0,
+ &at91rm9200_pll_layout, &plla_characteristics);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ hw = at91_clk_register_plldiv(regmap, "plladivck", "pllack");
+ if (IS_ERR(hw))
+ goto err_free;
+
+ hw = at91_clk_register_utmi(regmap, NULL, "utmick", "mainck");
+ if (IS_ERR(hw))
+ goto err_free;
+
+ at91sam9x5_pmc->chws[PMC_UTMI] = hw;
+
+ parent_names[0] = slck_name;
+ parent_names[1] = "mainck";
+ parent_names[2] = "plladivck";
+ parent_names[3] = "utmick";
+ hw = at91_clk_register_master(regmap, "masterck", 4, parent_names,
+ &at91sam9x5_master_layout,
+ &mck_characteristics);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ at91sam9x5_pmc->chws[PMC_MCK] = hw;
+
+ parent_names[0] = "plladivck";
+ parent_names[1] = "utmick";
+ hw = at91sam9x5_clk_register_usb(regmap, "usbck", parent_names, 2);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ hw = at91sam9x5_clk_register_smd(regmap, "smdclk", parent_names, 2);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ parent_names[0] = slck_name;
+ parent_names[1] = "mainck";
+ parent_names[2] = "plladivck";
+ parent_names[3] = "utmick";
+ parent_names[4] = "mck";
+ for (i = 0; i < 2; i++) {
+ char *name;
+
+ name = xasprintf("prog%d", i);
+
+ hw = at91_clk_register_programmable(regmap, name,
+ parent_names, 5, i,
+ &at91sam9x5_programmable_layout);
+ if (IS_ERR(hw))
+ goto err_free;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(at91sam9x5_systemck); i++) {
+ hw = at91_clk_register_system(regmap, at91sam9x5_systemck[i].n,
+ at91sam9x5_systemck[i].p,
+ at91sam9x5_systemck[i].id);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ at91sam9x5_pmc->shws[at91sam9x5_systemck[i].id] = hw;
+ }
+
+ if (has_lcdck) {
+ hw = at91_clk_register_system(regmap, "lcdck", "masterck", 3);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ at91sam9x5_pmc->shws[3] = hw;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(at91sam9x5_periphck); i++) {
+ hw = at91_clk_register_sam9x5_peripheral(regmap,
+ at91sam9x5_periphck[i].n,
+ "masterck",
+ at91sam9x5_periphck[i].id,
+ &range);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ at91sam9x5_pmc->phws[at91sam9x5_periphck[i].id] = hw;
+ }
+
+ for (i = 0; extra_pcks[i].id; i++) {
+ hw = at91_clk_register_sam9x5_peripheral(regmap,
+ extra_pcks[i].n,
+ "masterck",
+ extra_pcks[i].id,
+ &range);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ at91sam9x5_pmc->phws[extra_pcks[i].id] = hw;
+ }
+
+ of_clk_add_provider(np, of_clk_hw_pmc_get, at91sam9x5_pmc);
+
+ return;
+
+err_free:
+ pmc_data_free(at91sam9x5_pmc);
+}
+
+static void __init at91sam9g15_pmc_setup(struct device_node *np)
+{
+ at91sam9x5_pmc_setup(np, at91sam9g15_periphck, true);
+}
+CLK_OF_DECLARE_DRIVER(at91sam9g15_pmc, "atmel,at91sam9g15-pmc",
+ at91sam9g15_pmc_setup);
+
+static void __init at91sam9g25_pmc_setup(struct device_node *np)
+{
+ at91sam9x5_pmc_setup(np, at91sam9g25_periphck, false);
+}
+CLK_OF_DECLARE_DRIVER(at91sam9g25_pmc, "atmel,at91sam9g25-pmc",
+ at91sam9g25_pmc_setup);
+
+static void __init at91sam9g35_pmc_setup(struct device_node *np)
+{
+ at91sam9x5_pmc_setup(np, at91sam9g35_periphck, true);
+}
+CLK_OF_DECLARE_DRIVER(at91sam9g35_pmc, "atmel,at91sam9g35-pmc",
+ at91sam9g35_pmc_setup);
+
+static void __init at91sam9x25_pmc_setup(struct device_node *np)
+{
+ at91sam9x5_pmc_setup(np, at91sam9x25_periphck, false);
+}
+CLK_OF_DECLARE_DRIVER(at91sam9x25_pmc, "atmel,at91sam9x25-pmc",
+ at91sam9x25_pmc_setup);
+
+static void __init at91sam9x35_pmc_setup(struct device_node *np)
+{
+ at91sam9x5_pmc_setup(np, at91sam9x35_periphck, true);
+}
+CLK_OF_DECLARE_DRIVER(at91sam9x35_pmc, "atmel,at91sam9x35-pmc",
+ at91sam9x35_pmc_setup);
diff --git a/drivers/clk/at91/clk-generated.c b/drivers/clk/at91/clk-generated.c
index 4e1cd5aa69..60516ca10f 100644
--- a/drivers/clk/at91/clk-generated.c
+++ b/drivers/clk/at91/clk-generated.c
@@ -11,26 +11,23 @@
*
*/
-#include <linux/clk-provider.h>
-#include <linux/clkdev.h>
+#include <common.h>
+#include <clock.h>
+#include <io.h>
+#include <linux/list.h>
+#include <linux/clk.h>
#include <linux/clk/at91_pmc.h>
-#include <linux/of.h>
-#include <linux/mfd/syscon.h>
-#include <linux/regmap.h>
+#include <mfd/syscon.h>
+#include <regmap.h>
#include "pmc.h"
-#define PERIPHERAL_MAX 64
-#define PERIPHERAL_ID_MIN 2
-
-#define GENERATED_SOURCE_MAX 6
#define GENERATED_MAX_DIV 255
struct clk_generated {
- struct clk_hw hw;
+ struct clk hw;
struct regmap *regmap;
struct clk_range range;
- spinlock_t *lock;
u32 id;
u32 gckdiv;
u8 parent_id;
@@ -39,15 +36,13 @@ struct clk_generated {
#define to_clk_generated(hw) \
container_of(hw, struct clk_generated, hw)
-static int clk_generated_enable(struct clk_hw *hw)
+static int clk_generated_enable(struct clk *hw)
{
struct clk_generated *gck = to_clk_generated(hw);
- unsigned long flags;
pr_debug("GCLK: %s, gckdiv = %d, parent id = %d\n",
__func__, gck->gckdiv, gck->parent_id);
- spin_lock_irqsave(gck->lock, flags);
regmap_write(gck->regmap, AT91_PMC_PCR,
(gck->id & AT91_PMC_PCR_PID_MASK));
regmap_update_bits(gck->regmap, AT91_PMC_PCR,
@@ -57,41 +52,34 @@ static int clk_generated_enable(struct clk_hw *hw)
AT91_PMC_PCR_CMD |
AT91_PMC_PCR_GCKDIV(gck->gckdiv) |
AT91_PMC_PCR_GCKEN);
- spin_unlock_irqrestore(gck->lock, flags);
return 0;
}
-static void clk_generated_disable(struct clk_hw *hw)
+static void clk_generated_disable(struct clk *hw)
{
struct clk_generated *gck = to_clk_generated(hw);
- unsigned long flags;
- spin_lock_irqsave(gck->lock, flags);
regmap_write(gck->regmap, AT91_PMC_PCR,
(gck->id & AT91_PMC_PCR_PID_MASK));
regmap_update_bits(gck->regmap, AT91_PMC_PCR,
AT91_PMC_PCR_CMD | AT91_PMC_PCR_GCKEN,
AT91_PMC_PCR_CMD);
- spin_unlock_irqrestore(gck->lock, flags);
}
-static int clk_generated_is_enabled(struct clk_hw *hw)
+static int clk_generated_is_enabled(struct clk *hw)
{
struct clk_generated *gck = to_clk_generated(hw);
- unsigned long flags;
unsigned int status;
- spin_lock_irqsave(gck->lock, flags);
regmap_write(gck->regmap, AT91_PMC_PCR,
(gck->id & AT91_PMC_PCR_PID_MASK));
regmap_read(gck->regmap, AT91_PMC_PCR, &status);
- spin_unlock_irqrestore(gck->lock, flags);
return status & AT91_PMC_PCR_GCKEN ? 1 : 0;
}
static unsigned long
-clk_generated_recalc_rate(struct clk_hw *hw,
+clk_generated_recalc_rate(struct clk *hw,
unsigned long parent_rate)
{
struct clk_generated *gck = to_clk_generated(hw);
@@ -99,75 +87,19 @@ clk_generated_recalc_rate(struct clk_hw *hw,
return DIV_ROUND_CLOSEST(parent_rate, gck->gckdiv + 1);
}
-static int clk_generated_determine_rate(struct clk_hw *hw,
- struct clk_rate_request *req)
-{
- struct clk_generated *gck = to_clk_generated(hw);
- struct clk_hw *parent = NULL;
- long best_rate = -EINVAL;
- unsigned long tmp_rate, min_rate;
- int best_diff = -1;
- int tmp_diff;
- int i;
-
- for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
- u32 div;
- unsigned long parent_rate;
-
- parent = clk_hw_get_parent_by_index(hw, i);
- if (!parent)
- continue;
-
- parent_rate = clk_hw_get_rate(parent);
- min_rate = DIV_ROUND_CLOSEST(parent_rate, GENERATED_MAX_DIV + 1);
- if (!parent_rate ||
- (gck->range.max && min_rate > gck->range.max))
- continue;
-
- for (div = 1; div < GENERATED_MAX_DIV + 2; div++) {
- tmp_rate = DIV_ROUND_CLOSEST(parent_rate, div);
- tmp_diff = abs(req->rate - tmp_rate);
-
- if (best_diff < 0 || best_diff > tmp_diff) {
- best_rate = tmp_rate;
- best_diff = tmp_diff;
- req->best_parent_rate = parent_rate;
- req->best_parent_hw = parent;
- }
-
- if (!best_diff || tmp_rate < req->rate)
- break;
- }
-
- if (!best_diff)
- break;
- }
-
- pr_debug("GCLK: %s, best_rate = %ld, parent clk: %s @ %ld\n",
- __func__, best_rate,
- __clk_get_name((req->best_parent_hw)->clk),
- req->best_parent_rate);
-
- if (best_rate < 0)
- return best_rate;
-
- req->rate = best_rate;
- return 0;
-}
-
/* No modification of hardware as we have the flag CLK_SET_PARENT_GATE set */
-static int clk_generated_set_parent(struct clk_hw *hw, u8 index)
+static int clk_generated_set_parent(struct clk *hw, u8 index)
{
struct clk_generated *gck = to_clk_generated(hw);
- if (index >= clk_hw_get_num_parents(hw))
+ if (index >= clk_get_num_parents(hw))
return -EINVAL;
gck->parent_id = index;
return 0;
}
-static u8 clk_generated_get_parent(struct clk_hw *hw)
+static int clk_generated_get_parent(struct clk *hw)
{
struct clk_generated *gck = to_clk_generated(hw);
@@ -175,7 +107,7 @@ static u8 clk_generated_get_parent(struct clk_hw *hw)
}
/* No modification of hardware as we have the flag CLK_SET_RATE_GATE set */
-static int clk_generated_set_rate(struct clk_hw *hw,
+static int clk_generated_set_rate(struct clk *hw,
unsigned long rate,
unsigned long parent_rate)
{
@@ -201,7 +133,6 @@ static const struct clk_ops generated_ops = {
.disable = clk_generated_disable,
.is_enabled = clk_generated_is_enabled,
.recalc_rate = clk_generated_recalc_rate,
- .determine_rate = clk_generated_determine_rate,
.get_parent = clk_generated_get_parent,
.set_parent = clk_generated_set_parent,
.set_rate = clk_generated_set_rate,
@@ -219,13 +150,10 @@ static const struct clk_ops generated_ops = {
static void clk_generated_startup(struct clk_generated *gck)
{
u32 tmp;
- unsigned long flags;
- spin_lock_irqsave(gck->lock, flags);
regmap_write(gck->regmap, AT91_PMC_PCR,
(gck->id & AT91_PMC_PCR_PID_MASK));
regmap_read(gck->regmap, AT91_PMC_PCR, &tmp);
- spin_unlock_irqrestore(gck->lock, flags);
gck->parent_id = (tmp & AT91_PMC_PCR_GCKCSS_MASK)
>> AT91_PMC_PCR_GCKCSS_OFFSET;
@@ -233,35 +161,37 @@ static void clk_generated_startup(struct clk_generated *gck)
>> AT91_PMC_PCR_GCKDIV_OFFSET;
}
-static struct clk_hw * __init
-at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock,
+struct clk * __init
+at91_clk_register_generated(struct regmap *regmap,
const char *name, const char **parent_names,
- u8 num_parents, u8 id,
+ u8 num_parents, u8 id, bool pll_audio,
const struct clk_range *range)
{
+ size_t parents_array_size;
struct clk_generated *gck;
- struct clk_init_data init;
- struct clk_hw *hw;
+ struct clk *hw;
int ret;
gck = kzalloc(sizeof(*gck), GFP_KERNEL);
if (!gck)
return ERR_PTR(-ENOMEM);
- init.name = name;
- init.ops = &generated_ops;
- init.parent_names = parent_names;
- init.num_parents = num_parents;
- init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
-
gck->id = id;
- gck->hw.init = &init;
+ gck->hw.name = name;
+ gck->hw.ops = &generated_ops;
+
+ parents_array_size = num_parents * sizeof(gck->hw.parent_names[0]);
+ gck->hw.parent_names = xzalloc(parents_array_size);
+ memcpy(gck->hw.parent_names, parent_names, parents_array_size);
+ gck->hw.num_parents = num_parents;
+
+ /* gck->hw.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE; */
gck->regmap = regmap;
- gck->lock = lock;
gck->range = *range;
+ /* gck->audio_pll_allowed = pll_audio; */
hw = &gck->hw;
- ret = clk_hw_register(NULL, &gck->hw);
+ ret = clk_register(&gck->hw);
if (ret) {
kfree(gck);
hw = ERR_PTR(ret);
@@ -270,54 +200,3 @@ at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock,
return hw;
}
-
-static void __init of_sama5d2_clk_generated_setup(struct device_node *np)
-{
- int num;
- u32 id;
- const char *name;
- struct clk_hw *hw;
- unsigned int num_parents;
- const char *parent_names[GENERATED_SOURCE_MAX];
- struct device_node *gcknp;
- struct clk_range range = CLK_RANGE(0, 0);
- struct regmap *regmap;
-
- num_parents = of_clk_get_parent_count(np);
- if (num_parents == 0 || num_parents > GENERATED_SOURCE_MAX)
- return;
-
- of_clk_parent_fill(np, parent_names, num_parents);
-
- num = of_get_child_count(np);
- if (!num || num > PERIPHERAL_MAX)
- return;
-
- regmap = syscon_node_to_regmap(of_get_parent(np));
- if (IS_ERR(regmap))
- return;
-
- for_each_child_of_node(np, gcknp) {
- if (of_property_read_u32(gcknp, "reg", &id))
- continue;
-
- if (id < PERIPHERAL_ID_MIN || id >= PERIPHERAL_MAX)
- continue;
-
- if (of_property_read_string(np, "clock-output-names", &name))
- name = gcknp->name;
-
- of_at91_get_clk_range(gcknp, "atmel,clk-output-range",
- &range);
-
- hw = at91_clk_register_generated(regmap, &pmc_pcr_lock, name,
- parent_names, num_parents,
- id, &range);
- if (IS_ERR(hw))
- continue;
-
- of_clk_add_hw_provider(gcknp, of_clk_hw_simple_get, hw);
- }
-}
-CLK_OF_DECLARE(of_sama5d2_clk_generated_setup, "atmel,sama5d2-clk-generated",
- of_sama5d2_clk_generated_setup);
diff --git a/drivers/clk/at91/clk-h32mx.c b/drivers/clk/at91/clk-h32mx.c
index e0daa4a31f..31906a9e29 100644
--- a/drivers/clk/at91/clk-h32mx.c
+++ b/drivers/clk/at91/clk-h32mx.c
@@ -12,25 +12,27 @@
*
*/
-#include <linux/clk-provider.h>
-#include <linux/clkdev.h>
+#include <common.h>
+#include <clock.h>
+#include <linux/list.h>
+#include <linux/clk.h>
#include <linux/clk/at91_pmc.h>
-#include <linux/of.h>
-#include <linux/regmap.h>
-#include <linux/mfd/syscon.h>
+#include <regmap.h>
+
#include "pmc.h"
#define H32MX_MAX_FREQ 90000000
struct clk_sama5d4_h32mx {
- struct clk_hw hw;
+ struct clk hw;
struct regmap *regmap;
+ const char *parent;
};
#define to_clk_sama5d4_h32mx(hw) container_of(hw, struct clk_sama5d4_h32mx, hw)
-static unsigned long clk_sama5d4_h32mx_recalc_rate(struct clk_hw *hw,
+static unsigned long clk_sama5d4_h32mx_recalc_rate(struct clk *hw,
unsigned long parent_rate)
{
struct clk_sama5d4_h32mx *h32mxclk = to_clk_sama5d4_h32mx(hw);
@@ -45,7 +47,7 @@ static unsigned long clk_sama5d4_h32mx_recalc_rate(struct clk_hw *hw,
return parent_rate;
}
-static long clk_sama5d4_h32mx_round_rate(struct clk_hw *hw, unsigned long rate,
+static long clk_sama5d4_h32mx_round_rate(struct clk *hw, unsigned long rate,
unsigned long *parent_rate)
{
unsigned long div;
@@ -62,7 +64,7 @@ static long clk_sama5d4_h32mx_round_rate(struct clk_hw *hw, unsigned long rate,
return *parent_rate;
}
-static int clk_sama5d4_h32mx_set_rate(struct clk_hw *hw, unsigned long rate,
+static int clk_sama5d4_h32mx_set_rate(struct clk *hw, unsigned long rate,
unsigned long parent_rate)
{
struct clk_sama5d4_h32mx *h32mxclk = to_clk_sama5d4_h32mx(hw);
@@ -86,40 +88,31 @@ static const struct clk_ops h32mx_ops = {
.set_rate = clk_sama5d4_h32mx_set_rate,
};
-static void __init of_sama5d4_clk_h32mx_setup(struct device_node *np)
+struct clk *
+at91_clk_register_h32mx(struct regmap *regmap, const char *name,
+ const char *parent_name)
{
struct clk_sama5d4_h32mx *h32mxclk;
- struct clk_init_data init;
- const char *parent_name;
- struct regmap *regmap;
int ret;
- regmap = syscon_node_to_regmap(of_get_parent(np));
- if (IS_ERR(regmap))
- return;
-
h32mxclk = kzalloc(sizeof(*h32mxclk), GFP_KERNEL);
if (!h32mxclk)
- return;
-
- parent_name = of_clk_get_parent_name(np, 0);
+ return ERR_PTR(-ENOMEM);
- init.name = np->name;
- init.ops = &h32mx_ops;
- init.parent_names = parent_name ? &parent_name : NULL;
- init.num_parents = parent_name ? 1 : 0;
- init.flags = CLK_SET_RATE_GATE;
+ h32mxclk->parent = parent_name;
+ h32mxclk->hw.name = name;
+ h32mxclk->hw.ops = &h32mx_ops;
+ h32mxclk->hw.parent_names = &h32mxclk->parent;
+ h32mxclk->hw.num_parents = 1;
+ /* h32mxclk.hw.flags = CLK_SET_RATE_GATE; */
- h32mxclk->hw.init = &init;
h32mxclk->regmap = regmap;
- ret = clk_hw_register(NULL, &h32mxclk->hw);
+ ret = clk_register(&h32mxclk->hw);
if (ret) {
kfree(h32mxclk);
- return;
+ return ERR_PTR(ret);
}
- of_clk_add_hw_provider(np, of_clk_hw_simple_get, &h32mxclk->hw);
+ return &h32mxclk->hw;
}
-CLK_OF_DECLARE(of_sama5d4_clk_h32mx_setup, "atmel,sama5d4-clk-h32mx",
- of_sama5d4_clk_h32mx_setup);
diff --git a/drivers/clk/at91/clk-main.c b/drivers/clk/at91/clk-main.c
index 77dfdef518..4d4127dd00 100644
--- a/drivers/clk/at91/clk-main.c
+++ b/drivers/clk/at91/clk-main.c
@@ -9,7 +9,6 @@
*/
#include <common.h>
#include <clock.h>
-#include <of.h>
#include <linux/list.h>
#include <linux/clk.h>
#include <linux/clk/at91_pmc.h>
@@ -129,7 +128,7 @@ static const struct clk_ops main_osc_ops = {
.is_enabled = clk_main_osc_is_enabled,
};
-static struct clk *
+struct clk *
at91_clk_register_main_osc(struct regmap *regmap,
const char *name,
const char *parent_name,
@@ -165,31 +164,6 @@ at91_clk_register_main_osc(struct regmap *regmap,
return &osc->clk;
}
-static int of_at91rm9200_clk_main_osc_setup(struct device_node *np)
-{
- struct clk *clk;
- const char *name = np->name;
- const char *parent_name;
- struct regmap *regmap;
- bool bypass;
-
- of_property_read_string(np, "clock-output-names", &name);
- bypass = of_property_read_bool(np, "atmel,osc-bypass");
- parent_name = of_clk_get_parent_name(np, 0);
-
- regmap = syscon_node_to_regmap(of_get_parent(np));
- if (IS_ERR(regmap))
- return PTR_ERR(regmap);
-
- clk = at91_clk_register_main_osc(regmap, name, parent_name, bypass);
- if (IS_ERR(clk))
- return PTR_ERR(clk);
-
- return of_clk_add_provider(np, of_clk_src_simple_get, clk);
-}
-CLK_OF_DECLARE(at91rm9200_clk_main_osc, "atmel,at91rm9200-clk-main-osc",
- of_at91rm9200_clk_main_osc_setup);
-
static bool clk_main_rc_osc_ready(struct regmap *regmap)
{
unsigned int status;
@@ -260,10 +234,10 @@ static const struct clk_ops main_rc_osc_ops = {
.recalc_rate = clk_main_rc_osc_recalc_rate,
};
-static struct clk *
+struct clk *
at91_clk_register_main_rc_osc(struct regmap *regmap,
const char *name,
- u32 frequency)
+ u32 frequency, u32 accuracy)
{
int ret;
struct clk_main_rc_osc *osc;
@@ -290,30 +264,6 @@ at91_clk_register_main_rc_osc(struct regmap *regmap,
return &osc->clk;
}
-static int of_at91sam9x5_clk_main_rc_osc_setup(struct device_node *np)
-{
- struct clk *clk;
- u32 frequency = 0;
- const char *name = np->name;
- struct regmap *regmap;
-
- of_property_read_string(np, "clock-output-names", &name);
- of_property_read_u32(np, "clock-frequency", &frequency);
-
- regmap = syscon_node_to_regmap(of_get_parent(np));
- if (IS_ERR(regmap))
- return PTR_ERR(regmap);
-
- clk = at91_clk_register_main_rc_osc(regmap, name, frequency);
- if (IS_ERR(clk))
- return PTR_ERR(clk);
-
- return of_clk_add_provider(np, of_clk_src_simple_get, clk);
-}
-CLK_OF_DECLARE(at91sam9x5_clk_main_rc_osc, "atmel,at91sam9x5-clk-main-rc-osc",
- of_at91sam9x5_clk_main_rc_osc_setup);
-
-
static int clk_main_probe_frequency(struct regmap *regmap)
{
unsigned int mcfr;
@@ -375,7 +325,7 @@ static const struct clk_ops rm9200_main_ops = {
.recalc_rate = clk_rm9200_main_recalc_rate,
};
-static struct clk *
+struct clk *
at91_clk_register_rm9200_main(struct regmap *regmap,
const char *name,
const char *parent_name)
@@ -407,29 +357,6 @@ at91_clk_register_rm9200_main(struct regmap *regmap,
return &clkmain->clk;
}
-static int of_at91rm9200_clk_main_setup(struct device_node *np)
-{
- struct clk *clk;
- const char *parent_name;
- const char *name = np->name;
- struct regmap *regmap;
-
- parent_name = of_clk_get_parent_name(np, 0);
- of_property_read_string(np, "clock-output-names", &name);
-
- regmap = syscon_node_to_regmap(of_get_parent(np));
- if (IS_ERR(regmap))
- return PTR_ERR(regmap);
-
- clk = at91_clk_register_rm9200_main(regmap, name, parent_name);
- if (IS_ERR(clk))
- return PTR_ERR(clk);
-
- return of_clk_add_provider(np, of_clk_src_simple_get, clk);
-}
-CLK_OF_DECLARE(at91rm9200_clk_main, "atmel,at91rm9200-clk-main",
- of_at91rm9200_clk_main_setup);
-
static inline bool clk_sam9x5_main_ready(struct regmap *regmap)
{
unsigned int status;
@@ -506,7 +433,7 @@ static const struct clk_ops sam9x5_main_ops = {
.get_parent = clk_sam9x5_main_get_parent,
};
-static struct clk *
+struct clk *
at91_clk_register_sam9x5_main(struct regmap *regmap,
const char *name,
const char **parent_names,
@@ -546,32 +473,3 @@ at91_clk_register_sam9x5_main(struct regmap *regmap,
return &clkmain->clk;
}
-
-static int of_at91sam9x5_clk_main_setup(struct device_node *np)
-{
- struct clk *clk;
- const char *parent_names[2];
- unsigned int num_parents;
- const char *name = np->name;
- struct regmap *regmap;
-
- num_parents = of_clk_get_parent_count(np);
- if (num_parents == 0 || num_parents > 2)
- return -EINVAL;
-
- of_clk_parent_fill(np, parent_names, num_parents);
- regmap = syscon_node_to_regmap(of_get_parent(np));
- if (IS_ERR(regmap))
- return PTR_ERR(regmap);
-
- of_property_read_string(np, "clock-output-names", &name);
-
- clk = at91_clk_register_sam9x5_main(regmap, name, parent_names,
- num_parents);
- if (IS_ERR(clk))
- return PTR_ERR(clk);
-
- return of_clk_add_provider(np, of_clk_src_simple_get, clk);
-}
-CLK_OF_DECLARE(at91sam9x5_clk_main, "atmel,at91sam9x5-clk-main",
- of_at91sam9x5_clk_main_setup);
diff --git a/drivers/clk/at91/clk-master.c b/drivers/clk/at91/clk-master.c
index b3a50ce542..f7a0fb1d18 100644
--- a/drivers/clk/at91/clk-master.c
+++ b/drivers/clk/at91/clk-master.c
@@ -9,7 +9,6 @@
*/
#include <common.h>
#include <clock.h>
-#include <of.h>
#include <linux/list.h>
#include <linux/clk.h>
#include <linux/clk/at91_pmc.h>
@@ -25,17 +24,6 @@
#define MASTER_DIV_SHIFT 8
#define MASTER_DIV_MASK 0x3
-struct clk_master_characteristics {
- struct clk_range output;
- u32 divisors[4];
- u8 have_div3_pres;
-};
-
-struct clk_master_layout {
- u32 mask;
- u8 pres_shift;
-};
-
#define to_clk_master(clk) container_of(clk, struct clk_master, clk)
struct clk_master {
@@ -122,7 +110,7 @@ static const struct clk_ops master_ops = {
.get_parent = clk_master_get_parent,
};
-static struct clk *
+struct clk *
at91_clk_register_master(struct regmap *regmap,
const char *name, int num_parents,
const char **parent_names,
@@ -158,88 +146,12 @@ at91_clk_register_master(struct regmap *regmap,
}
-static const struct clk_master_layout at91rm9200_master_layout = {
+const struct clk_master_layout at91rm9200_master_layout = {
.mask = 0x31F,
.pres_shift = 2,
};
-static const struct clk_master_layout at91sam9x5_master_layout = {
+const struct clk_master_layout at91sam9x5_master_layout = {
.mask = 0x373,
.pres_shift = 4,
};
-
-
-static struct clk_master_characteristics *
-of_at91_clk_master_get_characteristics(struct device_node *np)
-{
- struct clk_master_characteristics *characteristics;
-
- characteristics = xzalloc(sizeof(*characteristics));
-
- if (of_at91_get_clk_range(np, "atmel,clk-output-range", &characteristics->output))
- goto out_free_characteristics;
-
- of_property_read_u32_array(np, "atmel,clk-divisors",
- characteristics->divisors, 4);
-
- characteristics->have_div3_pres =
- of_property_read_bool(np, "atmel,master-clk-have-div3-pres");
-
- return characteristics;
-
-out_free_characteristics:
- kfree(characteristics);
- return NULL;
-}
-
-static int
-of_at91_clk_master_setup(struct device_node *np,
- const struct clk_master_layout *layout)
-{
- struct clk *clk;
- unsigned int num_parents;
- const char *parent_names[MASTER_SOURCE_MAX];
- const char *name = np->name;
- struct clk_master_characteristics *characteristics;
- struct regmap *regmap;
-
- num_parents = of_clk_get_parent_count(np);
- if (num_parents == 0 || num_parents > MASTER_SOURCE_MAX)
- return -EINVAL;
-
- of_clk_parent_fill(np, parent_names, num_parents);
-
- of_property_read_string(np, "clock-output-names", &name);
-
- characteristics = of_at91_clk_master_get_characteristics(np);
- if (!characteristics)
- return -EINVAL;
-
- regmap = syscon_node_to_regmap(of_get_parent(np));
- if (IS_ERR(regmap))
- return PTR_ERR(regmap);
-
- clk = at91_clk_register_master(regmap, name, num_parents,
- parent_names, layout,
- characteristics);
- if (IS_ERR(clk)) {
- kfree(characteristics);
- return PTR_ERR(clk);
- }
-
- return of_clk_add_provider(np, of_clk_src_simple_get, clk);
-}
-
-static void __init of_at91rm9200_clk_master_setup(struct device_node *np)
-{
- of_at91_clk_master_setup(np, &at91rm9200_master_layout);
-}
-CLK_OF_DECLARE(at91rm9200_clk_master, "atmel,at91rm9200-clk-master",
- of_at91rm9200_clk_master_setup);
-
-static void __init of_at91sam9x5_clk_master_setup(struct device_node *np)
-{
- of_at91_clk_master_setup(np, &at91sam9x5_master_layout);
-}
-CLK_OF_DECLARE(at91sam9x5_clk_master, "atmel,at91sam9x5-clk-master",
- of_at91sam9x5_clk_master_setup);
diff --git a/drivers/clk/at91/clk-peripheral.c b/drivers/clk/at91/clk-peripheral.c
index bbe6ffac69..00852672da 100644
--- a/drivers/clk/at91/clk-peripheral.c
+++ b/drivers/clk/at91/clk-peripheral.c
@@ -10,7 +10,6 @@
#include <common.h>
#include <clock.h>
-#include <of.h>
#include <linux/list.h>
#include <linux/clk.h>
#include <linux/clk/at91_pmc.h>
@@ -19,11 +18,6 @@
#include "pmc.h"
-#define PERIPHERAL_MAX 64
-
-#define PERIPHERAL_AT91RM9200 0
-#define PERIPHERAL_AT91SAM9X5 1
-
#define PERIPHERAL_ID_MIN 2
#define PERIPHERAL_ID_MAX 31
#define PERIPHERAL_MASK(id) (1 << ((id) & PERIPHERAL_ID_MAX))
@@ -105,7 +99,7 @@ static const struct clk_ops peripheral_ops = {
.is_enabled = clk_peripheral_is_enabled,
};
-static struct clk *
+struct clk *
at91_clk_register_peripheral(struct regmap *regmap, const char *name,
const char *parent_name, u32 id)
{
@@ -317,7 +311,7 @@ static const struct clk_ops sam9x5_peripheral_ops = {
.set_rate = clk_sam9x5_peripheral_set_rate,
};
-static struct clk *
+struct clk *
at91_clk_register_sam9x5_peripheral(struct regmap *regmap,
const char *name, const char *parent_name,
u32 id, const struct clk_range *range)
@@ -355,75 +349,3 @@ at91_clk_register_sam9x5_peripheral(struct regmap *regmap,
return &periph->clk;
}
-
-static int
-of_at91_clk_periph_setup(struct device_node *np, u8 type)
-{
- int num;
- u32 id;
- struct clk *clk;
- const char *parent_name;
- const char *name;
- struct device_node *periphclknp;
- struct regmap *regmap;
-
- parent_name = of_clk_get_parent_name(np, 0);
- if (!parent_name)
- return -ENOENT;
-
- num = of_get_child_count(np);
- if (!num || num > PERIPHERAL_MAX)
- return -EINVAL;
-
- regmap = syscon_node_to_regmap(of_get_parent(np));
- if (IS_ERR(regmap))
- return PTR_ERR(regmap);
-
- for_each_child_of_node(np, periphclknp) {
- if (of_property_read_u32(periphclknp, "reg", &id))
- continue;
-
- if (id >= PERIPHERAL_MAX)
- continue;
-
- if (of_property_read_string(np, "clock-output-names", &name))
- name = periphclknp->name;
-
- if (type == PERIPHERAL_AT91RM9200) {
- clk = at91_clk_register_peripheral(regmap, name,
- parent_name, id);
- } else {
- struct clk_range range = CLK_RANGE(0, 0);
-
- of_at91_get_clk_range(periphclknp,
- "atmel,clk-output-range",
- &range);
-
- clk = at91_clk_register_sam9x5_peripheral(regmap,
- name,
- parent_name,
- id, &range);
- }
-
- if (IS_ERR(clk))
- continue;
-
- of_clk_add_provider(periphclknp, of_clk_src_simple_get, clk);
- }
-
- return 0;
-}
-
-static int of_at91rm9200_clk_periph_setup(struct device_node *np)
-{
- return of_at91_clk_periph_setup(np, PERIPHERAL_AT91RM9200);
-}
-CLK_OF_DECLARE(at91rm9200_clk_periph, "atmel,at91rm9200-clk-peripheral",
- of_at91rm9200_clk_periph_setup);
-
-static int of_at91sam9x5_clk_periph_setup(struct device_node *np)
-{
- return of_at91_clk_periph_setup(np, PERIPHERAL_AT91SAM9X5);
-}
-CLK_OF_DECLARE(at91sam9x5_clk_periph, "atmel,at91sam9x5-clk-peripheral",
- of_at91sam9x5_clk_periph_setup);
diff --git a/drivers/clk/at91/clk-pll.c b/drivers/clk/at91/clk-pll.c
index e0af4fe5a8..bc504e8a95 100644
--- a/drivers/clk/at91/clk-pll.c
+++ b/drivers/clk/at91/clk-pll.c
@@ -36,20 +36,6 @@
#define PLL_OUT_SHIFT 14
#define PLL_MAX_ID 1
-struct clk_pll_characteristics {
- struct clk_range input;
- int num_output;
- struct clk_range *output;
- u16 *icpll;
- u8 *out;
-};
-
-struct clk_pll_layout {
- u32 pllr_mask;
- u16 mul_mask;
- u8 mul_shift;
-};
-
#define to_clk_pll(clk) container_of(clk, struct clk_pll, clk)
struct clk_pll {
@@ -299,7 +285,7 @@ static const struct clk_ops pll_ops = {
.set_rate = clk_pll_set_rate,
};
-static struct clk *
+struct clk *
at91_clk_register_pll(struct regmap *regmap, const char *name,
const char *parent_name, u8 id,
const struct clk_pll_layout *layout,
@@ -341,176 +327,26 @@ at91_clk_register_pll(struct regmap *regmap, const char *name,
}
-static const struct clk_pll_layout at91rm9200_pll_layout = {
+const struct clk_pll_layout at91rm9200_pll_layout = {
.pllr_mask = 0x7FFFFFF,
.mul_shift = 16,
.mul_mask = 0x7FF,
};
-static const struct clk_pll_layout at91sam9g45_pll_layout = {
+const struct clk_pll_layout at91sam9g45_pll_layout = {
.pllr_mask = 0xFFFFFF,
.mul_shift = 16,
.mul_mask = 0xFF,
};
-static const struct clk_pll_layout at91sam9g20_pllb_layout = {
+const struct clk_pll_layout at91sam9g20_pllb_layout = {
.pllr_mask = 0x3FFFFF,
.mul_shift = 16,
.mul_mask = 0x3F,
};
-static const struct clk_pll_layout sama5d3_pll_layout = {
+const struct clk_pll_layout sama5d3_pll_layout = {
.pllr_mask = 0x1FFFFFF,
.mul_shift = 18,
.mul_mask = 0x7F,
};
-
-
-static struct clk_pll_characteristics *
-of_at91_clk_pll_get_characteristics(struct device_node *np)
-{
- int i;
- int offset;
- u32 tmp;
- int num_output;
- u32 num_cells;
- struct clk_range input;
- struct clk_range *output;
- u8 *out = NULL;
- u16 *icpll = NULL;
- struct clk_pll_characteristics *characteristics;
-
- if (of_at91_get_clk_range(np, "atmel,clk-input-range", &input))
- return NULL;
-
- if (of_property_read_u32(np, "#atmel,pll-clk-output-range-cells",
- &num_cells))
- return NULL;
-
- if (num_cells < 2 || num_cells > 4)
- return NULL;
-
- if (!of_get_property(np, "atmel,pll-clk-output-ranges", &tmp))
- return NULL;
- num_output = tmp / (sizeof(u32) * num_cells);
-
- characteristics = xzalloc(sizeof(*characteristics));
- output = xzalloc(sizeof(*output) * num_output);
-
- if (num_cells > 2)
- out = xzalloc(sizeof(*out) * num_output);
-
- if (num_cells > 3)
- icpll = xzalloc(sizeof(*icpll) * num_output);
-
-
- for (i = 0; i < num_output; i++) {
- offset = i * num_cells;
- if (of_property_read_u32_index(np,
- "atmel,pll-clk-output-ranges",
- offset, &tmp))
- goto out_free_output;
- output[i].min = tmp;
- if (of_property_read_u32_index(np,
- "atmel,pll-clk-output-ranges",
- offset + 1, &tmp))
- goto out_free_output;
- output[i].max = tmp;
-
- if (num_cells == 2)
- continue;
-
- if (of_property_read_u32_index(np,
- "atmel,pll-clk-output-ranges",
- offset + 2, &tmp))
- goto out_free_output;
- out[i] = tmp;
-
- if (num_cells == 3)
- continue;
-
- if (of_property_read_u32_index(np,
- "atmel,pll-clk-output-ranges",
- offset + 3, &tmp))
- goto out_free_output;
- icpll[i] = tmp;
- }
-
- characteristics->input = input;
- characteristics->num_output = num_output;
- characteristics->output = output;
- characteristics->out = out;
- characteristics->icpll = icpll;
- return characteristics;
-
-out_free_output:
- kfree(icpll);
- kfree(out);
- kfree(output);
- kfree(characteristics);
- return NULL;
-}
-
-static int
-of_at91_clk_pll_setup(struct device_node *np,
- const struct clk_pll_layout *layout)
-{
- u32 id;
- struct clk *clk;
- struct regmap *regmap;
- const char *parent_name;
- const char *name = np->name;
- struct clk_pll_characteristics *characteristics;
-
- if (of_property_read_u32(np, "reg", &id))
- return -EINVAL;
-
- parent_name = of_clk_get_parent_name(np, 0);
-
- of_property_read_string(np, "clock-output-names", &name);
-
- regmap = syscon_node_to_regmap(of_get_parent(np));
- if (IS_ERR(regmap))
- return PTR_ERR(regmap);
-
- characteristics = of_at91_clk_pll_get_characteristics(np);
- if (!characteristics)
- return -EINVAL;
-
- clk = at91_clk_register_pll(regmap, name, parent_name, id, layout,
- characteristics);
- if (IS_ERR(clk)) {
- kfree(characteristics);
- return PTR_ERR(clk);
- }
-
- return of_clk_add_provider(np, of_clk_src_simple_get, clk);
-}
-
-static int of_at91rm9200_clk_pll_setup(struct device_node *np)
-{
- return of_at91_clk_pll_setup(np, &at91rm9200_pll_layout);
-}
-CLK_OF_DECLARE(at91rm9200_clk_pll, "atmel,at91rm9200-clk-pll",
- of_at91rm9200_clk_pll_setup);
-
-static int of_at91sam9g45_clk_pll_setup(struct device_node *np)
-{
- return of_at91_clk_pll_setup(np, &at91sam9g45_pll_layout);
-}
-CLK_OF_DECLARE(at91sam9g45_clk_pll, "atmel,at91sam9g45-clk-pll",
- of_at91sam9g45_clk_pll_setup);
-
-static int of_at91sam9g20_clk_pllb_setup(struct device_node *np)
-{
- return of_at91_clk_pll_setup(np, &at91sam9g20_pllb_layout);
-}
-CLK_OF_DECLARE(at91sam9g20_clk_pllb, "atmel,at91sam9g20-clk-pllb",
- of_at91sam9g20_clk_pllb_setup);
-
-static int of_sama5d3_clk_pll_setup(struct device_node *np)
-{
- return of_at91_clk_pll_setup(np, &sama5d3_pll_layout);
-}
-CLK_OF_DECLARE(sama5d3_clk_pll, "atmel,sama5d3-clk-pll",
- of_sama5d3_clk_pll_setup);
diff --git a/drivers/clk/at91/clk-plldiv.c b/drivers/clk/at91/clk-plldiv.c
index 917108e84c..98d79ef599 100644
--- a/drivers/clk/at91/clk-plldiv.c
+++ b/drivers/clk/at91/clk-plldiv.c
@@ -78,7 +78,7 @@ static const struct clk_ops plldiv_ops = {
.set_rate = clk_plldiv_set_rate,
};
-static struct clk *
+struct clk *
at91_clk_register_plldiv(struct regmap *regmap, const char *name,
const char *parent_name)
{
@@ -108,28 +108,3 @@ at91_clk_register_plldiv(struct regmap *regmap, const char *name,
return &plldiv->clk;
}
-
-static int
-of_at91sam9x5_clk_plldiv_setup(struct device_node *np)
-{
- struct clk *clk;
- const char *parent_name;
- const char *name = np->name;
- struct regmap *regmap;
-
- parent_name = of_clk_get_parent_name(np, 0);
-
- of_property_read_string(np, "clock-output-names", &name);
-
- regmap = syscon_node_to_regmap(of_get_parent(np));
- if (IS_ERR(regmap))
- return PTR_ERR(regmap);
-
- clk = at91_clk_register_plldiv(regmap, name, parent_name);
- if (IS_ERR(clk))
- return PTR_ERR(clk);
-
- return of_clk_add_provider(np, of_clk_src_simple_get, clk);
-}
-CLK_OF_DECLARE(at91sam9x5_clk_plldiv, "atmel,at91sam9x5-clk-plldiv",
- of_at91sam9x5_clk_plldiv_setup);
diff --git a/drivers/clk/at91/clk-programmable.c b/drivers/clk/at91/clk-programmable.c
index ddb18c0f7c..857ede1ca9 100644
--- a/drivers/clk/at91/clk-programmable.c
+++ b/drivers/clk/at91/clk-programmable.c
@@ -10,7 +10,6 @@
#include <common.h>
#include <clock.h>
-#include <of.h>
#include <io.h>
#include <linux/list.h>
#include <linux/clk.h>
@@ -28,12 +27,6 @@
#define PROG_PRES(layout, pckr) ((pckr >> layout->pres_shift) & PROG_PRES_MASK)
#define PROG_MAX_RM9200_CSS 3
-struct clk_programmable_layout {
- u8 pres_shift;
- u8 css_mask;
- u8 have_slck_mck;
-};
-
struct clk_programmable {
struct clk clk;
struct regmap *regmap;
@@ -130,7 +123,7 @@ static const struct clk_ops programmable_ops = {
.set_rate = clk_programmable_set_rate,
};
-static struct clk *
+struct clk *
at91_clk_register_programmable(struct regmap *regmap,
const char *name, const char **parent_names,
u8 num_parents, u8 id,
@@ -167,88 +160,20 @@ at91_clk_register_programmable(struct regmap *regmap,
return &prog->clk;
}
-static const struct clk_programmable_layout at91rm9200_programmable_layout = {
+const struct clk_programmable_layout at91rm9200_programmable_layout = {
.pres_shift = 2,
.css_mask = 0x3,
.have_slck_mck = 0,
};
-static const struct clk_programmable_layout at91sam9g45_programmable_layout = {
+const struct clk_programmable_layout at91sam9g45_programmable_layout = {
.pres_shift = 2,
.css_mask = 0x3,
.have_slck_mck = 1,
};
-static const struct clk_programmable_layout at91sam9x5_programmable_layout = {
+const struct clk_programmable_layout at91sam9x5_programmable_layout = {
.pres_shift = 4,
.css_mask = 0x7,
.have_slck_mck = 0,
};
-
-static int
-of_at91_clk_prog_setup(struct device_node *np,
- const struct clk_programmable_layout *layout)
-{
- int num;
- u32 id;
- struct clk *clk;
- unsigned int num_parents;
- const char *parent_names[PROG_SOURCE_MAX];
- const char *name;
- struct device_node *progclknp;
- struct regmap *regmap;
-
- num_parents = of_clk_get_parent_count(np);
- if (num_parents == 0 || num_parents > PROG_SOURCE_MAX)
- return -EINVAL;
-
- of_clk_parent_fill(np, parent_names, num_parents);
-
- num = of_get_child_count(np);
- if (!num || num > (PROG_ID_MAX + 1))
- return -EINVAL;
-
- regmap = syscon_node_to_regmap(of_get_parent(np));
- if (IS_ERR(regmap))
- return PTR_ERR(regmap);
-
- for_each_child_of_node(np, progclknp) {
- if (of_property_read_u32(progclknp, "reg", &id))
- continue;
-
- if (of_property_read_string(np, "clock-output-names", &name))
- name = progclknp->name;
-
- clk = at91_clk_register_programmable(regmap, name,
- parent_names, num_parents,
- id, layout);
- if (IS_ERR(clk))
- continue;
-
- of_clk_add_provider(progclknp, of_clk_src_simple_get, clk);
- }
-
- return 0;
-}
-
-
-static void __init of_at91rm9200_clk_prog_setup(struct device_node *np)
-{
- of_at91_clk_prog_setup(np, &at91rm9200_programmable_layout);
-}
-CLK_OF_DECLARE(at91rm9200_clk_prog, "atmel,at91rm9200-clk-programmable",
- of_at91rm9200_clk_prog_setup);
-
-static int of_at91sam9g45_clk_prog_setup(struct device_node *np)
-{
- return of_at91_clk_prog_setup(np, &at91sam9g45_programmable_layout);
-}
-CLK_OF_DECLARE(at91sam9g45_clk_prog, "atmel,at91sam9g45-clk-programmable",
- of_at91sam9g45_clk_prog_setup);
-
-static int of_at91sam9x5_clk_prog_setup(struct device_node *np)
-{
- return of_at91_clk_prog_setup(np, &at91sam9x5_programmable_layout);
-}
-CLK_OF_DECLARE(at91sam9x5_clk_prog, "atmel,at91sam9x5-clk-programmable",
- of_at91sam9x5_clk_prog_setup);
diff --git a/drivers/clk/at91/clk-slow.c b/drivers/clk/at91/clk-slow.c
index d4981e7b4d..d19f7e15ac 100644
--- a/drivers/clk/at91/clk-slow.c
+++ b/drivers/clk/at91/clk-slow.c
@@ -12,7 +12,6 @@
#include <common.h>
#include <clock.h>
-#include <of.h>
#include <io.h>
#include <linux/list.h>
#include <linux/clk.h>
@@ -44,7 +43,7 @@ static const struct clk_ops sam9260_slow_ops = {
.get_parent = clk_sam9260_slow_get_parent,
};
-static struct clk * __init
+struct clk * __init
at91_clk_register_sam9260_slow(struct regmap *regmap,
const char *name,
const char **parent_names,
@@ -76,33 +75,3 @@ at91_clk_register_sam9260_slow(struct regmap *regmap,
return &slowck->clk;
}
-
-static int of_at91sam9260_clk_slow_setup(struct device_node *np)
-{
- struct clk *clk;
- const char *parent_names[2];
- unsigned int num_parents;
- const char *name = np->name;
- struct regmap *regmap;
-
- num_parents = of_clk_get_parent_count(np);
- if (num_parents != 2)
- return -EINVAL;
-
- of_clk_parent_fill(np, parent_names, num_parents);
- regmap = syscon_node_to_regmap(of_get_parent(np));
- if (IS_ERR(regmap))
- return PTR_ERR(regmap);
-
- of_property_read_string(np, "clock-output-names", &name);
-
- clk = at91_clk_register_sam9260_slow(regmap, name, parent_names,
- num_parents);
- if (IS_ERR(clk))
- return PTR_ERR(clk);
-
- return of_clk_add_provider(np, of_clk_src_simple_get, clk);
-}
-
-CLK_OF_DECLARE(at91sam9260_clk_slow, "atmel,at91sam9260-clk-slow",
- of_at91sam9260_clk_slow_setup);
diff --git a/drivers/clk/at91/clk-smd.c b/drivers/clk/at91/clk-smd.c
index 65c53efbba..e81f0d4d4e 100644
--- a/drivers/clk/at91/clk-smd.c
+++ b/drivers/clk/at91/clk-smd.c
@@ -10,7 +10,6 @@
#include <common.h>
#include <clock.h>
-#include <of.h>
#include <io.h>
#include <linux/list.h>
#include <linux/clk.h>
@@ -115,7 +114,7 @@ static const struct clk_ops at91sam9x5_smd_ops = {
.set_rate = at91sam9x5_clk_smd_set_rate,
};
-static struct clk *
+struct clk *
at91sam9x5_clk_register_smd(struct regmap *regmap, const char *name,
const char **parent_names, u8 num_parents)
{
@@ -140,33 +139,3 @@ at91sam9x5_clk_register_smd(struct regmap *regmap, const char *name,
return &smd->clk;
}
-
-static int of_at91sam9x5_clk_smd_setup(struct device_node *np)
-{
- struct clk *clk;
- unsigned int num_parents;
- const char *parent_names[SMD_SOURCE_MAX];
- const char *name = np->name;
- struct regmap *regmap;
-
- num_parents = of_clk_get_parent_count(np);
- if (num_parents == 0 || num_parents > SMD_SOURCE_MAX)
- return -EINVAL;
-
- of_clk_parent_fill(np, parent_names, num_parents);
-
- of_property_read_string(np, "clock-output-names", &name);
-
- regmap = syscon_node_to_regmap(of_get_parent(np));
- if (IS_ERR(regmap))
- return PTR_ERR(regmap);
-
- clk = at91sam9x5_clk_register_smd(regmap, name, parent_names,
- num_parents);
- if (IS_ERR(clk))
- return PTR_ERR(clk);
-
- return of_clk_add_provider(np, of_clk_src_simple_get, clk);
-}
-CLK_OF_DECLARE(at91sam9x5_clk_smd, "atmel,at91sam9x5-clk-smd",
- of_at91sam9x5_clk_smd_setup);
diff --git a/drivers/clk/at91/clk-system.c b/drivers/clk/at91/clk-system.c
index 021930e546..8be5c7f2b3 100644
--- a/drivers/clk/at91/clk-system.c
+++ b/drivers/clk/at91/clk-system.c
@@ -9,7 +9,6 @@
*/
#include <common.h>
#include <clock.h>
-#include <of.h>
#include <io.h>
#include <linux/list.h>
#include <linux/clk.h>
@@ -91,7 +90,7 @@ static const struct clk_ops system_ops = {
.is_enabled = clk_system_is_enabled,
};
-static struct clk *
+struct clk *
at91_clk_register_system(struct regmap *regmap, const char *name,
const char *parent_name, u8 id)
{
@@ -119,42 +118,3 @@ at91_clk_register_system(struct regmap *regmap, const char *name,
return &sys->clk;
}
-
-static int of_at91rm9200_clk_sys_setup(struct device_node *np)
-{
- int num;
- u32 id;
- struct clk *clk;
- const char *name;
- struct device_node *sysclknp;
- const char *parent_name;
- struct regmap *regmap;
-
- num = of_get_child_count(np);
- if (num > (SYSTEM_MAX_ID + 1))
- return -EINVAL;
-
- regmap = syscon_node_to_regmap(of_get_parent(np));
- if (IS_ERR(regmap))
- return PTR_ERR(regmap);
-
- for_each_child_of_node(np, sysclknp) {
- if (of_property_read_u32(sysclknp, "reg", &id))
- continue;
-
- if (of_property_read_string(np, "clock-output-names", &name))
- name = sysclknp->name;
-
- parent_name = of_clk_get_parent_name(sysclknp, 0);
-
- clk = at91_clk_register_system(regmap, name, parent_name, id);
- if (IS_ERR(clk))
- continue;
-
- of_clk_add_provider(sysclknp, of_clk_src_simple_get, clk);
- }
-
- return 0;
-}
-CLK_OF_DECLARE(at91rm9200_clk_sys, "atmel,at91rm9200-clk-system",
- of_at91rm9200_clk_sys_setup);
diff --git a/drivers/clk/at91/clk-usb.c b/drivers/clk/at91/clk-usb.c
index 99ba671c98..0eb0b1f5bc 100644
--- a/drivers/clk/at91/clk-usb.c
+++ b/drivers/clk/at91/clk-usb.c
@@ -10,7 +10,6 @@
#include <common.h>
#include <clock.h>
-#include <of.h>
#include <io.h>
#include <linux/list.h>
#include <linux/clk.h>
@@ -144,7 +143,7 @@ static const struct clk_ops at91sam9n12_usb_ops = {
.set_rate = at91sam9x5_clk_usb_set_rate,
};
-static struct clk *
+struct clk *
at91sam9x5_clk_register_usb(struct regmap *regmap, const char *name,
const char **parent_names, u8 num_parents)
{
@@ -172,7 +171,7 @@ at91sam9x5_clk_register_usb(struct regmap *regmap, const char *name,
return &usb->clk;
}
-static struct clk *
+struct clk *
at91sam9n12_clk_register_usb(struct regmap *regmap, const char *name,
const char *parent_name)
{
@@ -282,7 +281,7 @@ static const struct clk_ops at91rm9200_usb_ops = {
.set_rate = at91rm9200_clk_usb_set_rate,
};
-static struct clk *
+struct clk *
at91rm9200_clk_register_usb(struct regmap *regmap, const char *name,
const char *parent_name, const u32 *divisors)
{
@@ -308,90 +307,3 @@ at91rm9200_clk_register_usb(struct regmap *regmap, const char *name,
return &usb->clk;
}
-
-static int of_at91sam9x5_clk_usb_setup(struct device_node *np)
-{
- struct clk *clk;
- unsigned int num_parents;
- const char *parent_names[USB_SOURCE_MAX];
- const char *name = np->name;
- struct regmap *regmap;
-
- num_parents = of_clk_get_parent_count(np);
- if (num_parents == 0 || num_parents > USB_SOURCE_MAX)
- return -EINVAL;
-
- of_clk_parent_fill(np, parent_names, num_parents);
-
- of_property_read_string(np, "clock-output-names", &name);
-
- regmap = syscon_node_to_regmap(of_get_parent(np));
- if (IS_ERR(regmap))
- return PTR_ERR(regmap);
-
- clk = at91sam9x5_clk_register_usb(regmap, name, parent_names,
- num_parents);
- if (IS_ERR(clk))
- return PTR_ERR(clk);
-
- return of_clk_add_provider(np, of_clk_src_simple_get, clk);
-}
-CLK_OF_DECLARE(at91sam9x5_clk_usb, "atmel,at91sam9x5-clk-usb",
- of_at91sam9x5_clk_usb_setup);
-
-static int of_at91sam9n12_clk_usb_setup(struct device_node *np)
-{
- struct clk *clk;
- const char *parent_name;
- const char *name = np->name;
- struct regmap *regmap;
-
- parent_name = of_clk_get_parent_name(np, 0);
- if (!parent_name)
- return -EINVAL;
-
- of_property_read_string(np, "clock-output-names", &name);
-
- regmap = syscon_node_to_regmap(of_get_parent(np));
- if (IS_ERR(regmap))
- return PTR_ERR(regmap);
-
- clk = at91sam9n12_clk_register_usb(regmap, name, parent_name);
- if (IS_ERR(clk))
- return PTR_ERR(clk);
-
- return of_clk_add_provider(np, of_clk_src_simple_get, clk);
-}
-CLK_OF_DECLARE(at91sam9n12_clk_usb, "atmel,at91sam9n12-clk-usb",
- of_at91sam9n12_clk_usb_setup);
-
-static int of_at91rm9200_clk_usb_setup(struct device_node *np)
-{
- struct clk *clk;
- const char *parent_name;
- const char *name = np->name;
- u32 divisors[4] = {0, 0, 0, 0};
- struct regmap *regmap;
-
- parent_name = of_clk_get_parent_name(np, 0);
- if (!parent_name)
- return -EINVAL;
-
- of_property_read_u32_array(np, "atmel,clk-divisors", divisors, 4);
- if (!divisors[0])
- return -EINVAL;
-
- of_property_read_string(np, "clock-output-names", &name);
-
- regmap = syscon_node_to_regmap(of_get_parent(np));
- if (IS_ERR(regmap))
- return PTR_ERR(regmap);
-
- clk = at91rm9200_clk_register_usb(regmap, name, parent_name, divisors);
- if (IS_ERR(clk))
- return PTR_ERR(clk);
-
- return of_clk_add_provider(np, of_clk_src_simple_get, clk);
-}
-CLK_OF_DECLARE(at91rm9200_clk_usb, "atmel,at91rm9200-clk-usb",
- of_at91rm9200_clk_usb_setup);
diff --git a/drivers/clk/at91/clk-utmi.c b/drivers/clk/at91/clk-utmi.c
index 6a1c5e6df3..c40af34d0d 100644
--- a/drivers/clk/at91/clk-utmi.c
+++ b/drivers/clk/at91/clk-utmi.c
@@ -10,21 +10,27 @@
#include <common.h>
#include <clock.h>
-#include <of.h>
#include <linux/list.h>
#include <linux/clk.h>
#include <linux/clk/at91_pmc.h>
#include <mfd/syscon.h>
#include <regmap.h>
+#include <soc/at91/atmel-sfr.h>
+
#include "pmc.h"
-#define UTMI_FIXED_MUL 40
+/*
+ * The purpose of this clock is to generate a 480 MHz signal. A different
+ * rate can't be configured.
+ */
+#define UTMI_RATE 480000000
struct clk_utmi {
struct clk clk;
- struct regmap *regmap;
const char *parent;
+ struct regmap *regmap_pmc;
+ struct regmap *regmap_sfr;
};
#define to_clk_utmi(clk) container_of(clk, struct clk_utmi, clk)
@@ -40,13 +46,55 @@ static inline bool clk_utmi_ready(struct regmap *regmap)
static int clk_utmi_enable(struct clk *clk)
{
+ struct clk *hw_parent;
struct clk_utmi *utmi = to_clk_utmi(clk);
unsigned int uckr = AT91_PMC_UPLLEN | AT91_PMC_UPLLCOUNT |
AT91_PMC_BIASEN;
+ unsigned int utmi_ref_clk_freq;
+ unsigned long parent_rate;
+
+ /*
+ * If mainck rate is different from 12 MHz, we have to configure the
+ * FREQ field of the SFR_UTMICKTRIM register to generate properly
+ * the utmi clock.
+ */
+ hw_parent = clk_get_parent(clk);
+ parent_rate = clk_get_rate(hw_parent);
+
+ switch (parent_rate) {
+ case 12000000:
+ utmi_ref_clk_freq = 0;
+ break;
+ case 16000000:
+ utmi_ref_clk_freq = 1;
+ break;
+ case 24000000:
+ utmi_ref_clk_freq = 2;
+ break;
+ /*
+ * Not supported on SAMA5D2 but it's not an issue since MAINCK
+ * maximum value is 24 MHz.
+ */
+ case 48000000:
+ utmi_ref_clk_freq = 3;
+ break;
+ default:
+ pr_err("UTMICK: unsupported mainck rate\n");
+ return -EINVAL;
+ }
+
+
+ if (utmi->regmap_sfr) {
+ regmap_write_bits(utmi->regmap_sfr, AT91_SFR_UTMICKTRIM,
+ AT91_UTMICKTRIM_FREQ, utmi_ref_clk_freq);
+ } else if (utmi_ref_clk_freq) {
+ pr_err("UTMICK: sfr node required\n");
+ return -EINVAL;
+ }
+ regmap_write_bits(utmi->regmap_pmc, AT91_CKGR_UCKR, uckr, uckr);
- regmap_write_bits(utmi->regmap, AT91_CKGR_UCKR, uckr, uckr);
- while (!clk_utmi_ready(utmi->regmap))
+ while (!clk_utmi_ready(utmi->regmap_pmc))
barrier();
return 0;
@@ -56,21 +104,22 @@ static int clk_utmi_is_enabled(struct clk *clk)
{
struct clk_utmi *utmi = to_clk_utmi(clk);
- return clk_utmi_ready(utmi->regmap);
+ return clk_utmi_ready(utmi->regmap_pmc);
}
static void clk_utmi_disable(struct clk *clk)
{
struct clk_utmi *utmi = to_clk_utmi(clk);
- regmap_write_bits(utmi->regmap, AT91_CKGR_UCKR, AT91_PMC_UPLLEN, 0);
+ regmap_write_bits(utmi->regmap_pmc, AT91_CKGR_UCKR,
+ AT91_PMC_UPLLEN, 0);
}
static unsigned long clk_utmi_recalc_rate(struct clk *clk,
unsigned long parent_rate)
{
- /* UTMI clk is a fixed clk multiplier */
- return parent_rate * UTMI_FIXED_MUL;
+ /* UTMI clk rate is fixed */
+ return UTMI_RATE;
}
static const struct clk_ops utmi_ops = {
@@ -80,8 +129,8 @@ static const struct clk_ops utmi_ops = {
.recalc_rate = clk_utmi_recalc_rate,
};
-static struct clk * __init
-at91_clk_register_utmi(struct regmap *regmap,
+struct clk * __init
+at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr,
const char *name, const char *parent_name)
{
int ret;
@@ -100,7 +149,8 @@ at91_clk_register_utmi(struct regmap *regmap,
/* utmi->clk.flags = CLK_SET_RATE_GATE; */
- utmi->regmap = regmap;
+ utmi->regmap_pmc = regmap_pmc;
+ utmi->regmap_sfr = regmap_sfr;
ret = clk_register(&utmi->clk);
if (ret) {
@@ -110,29 +160,3 @@ at91_clk_register_utmi(struct regmap *regmap,
return &utmi->clk;
}
-#if defined(CONFIG_OFTREE) && defined(CONFIG_COMMON_CLK_OF_PROVIDER)
-static void __init of_at91sam9x5_clk_utmi_setup(struct device_node *np)
-{
- struct clk *clk;
- const char *parent_name;
- const char *name = np->name;
- struct regmap *regmap;
-
- parent_name = of_clk_get_parent_name(np, 0);
-
- of_property_read_string(np, "clock-output-names", &name);
-
- regmap = syscon_node_to_regmap(of_get_parent(np));
- if (IS_ERR(regmap))
- return;
-
- clk = at91_clk_register_utmi(regmap, name, parent_name);
- if (IS_ERR(clk))
- return;
-
- of_clk_add_provider(np, of_clk_src_simple_get, clk);
- return;
-}
-CLK_OF_DECLARE(at91sam9x5_clk_utmi, "atmel,at91sam9x5-clk-utmi",
- of_at91sam9x5_clk_utmi_setup);
-#endif
diff --git a/drivers/clk/at91/dt-compat.c b/drivers/clk/at91/dt-compat.c
new file mode 100644
index 0000000000..bbd670641b
--- /dev/null
+++ b/drivers/clk/at91/dt-compat.c
@@ -0,0 +1,961 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <of.h>
+#include <driver.h>
+#include <regmap.h>
+#include <mfd/syscon.h>
+
+
+#include "pmc.h"
+
+#define MASTER_SOURCE_MAX 4
+
+#define PERIPHERAL_AT91RM9200 0
+#define PERIPHERAL_AT91SAM9X5 1
+
+#define PERIPHERAL_MAX 64
+
+#define PERIPHERAL_ID_MIN 2
+
+#define PROG_SOURCE_MAX 5
+#define PROG_ID_MAX 7
+
+#define SYSTEM_MAX_ID 31
+
+#ifdef CONFIG_HAVE_AT91_AUDIO_PLL
+static void __init of_sama5d2_clk_audio_pll_frac_setup(struct device_node *np)
+{
+ struct clk *hw;
+ const char *name = np->name;
+ const char *parent_name;
+ struct regmap *regmap;
+
+ regmap = syscon_node_to_regmap(of_get_parent(np));
+ if (IS_ERR(regmap))
+ return;
+
+ parent_name = of_clk_get_parent_name(np, 0);
+
+ hw = at91_clk_register_audio_pll_frac(regmap, name, parent_name);
+ if (IS_ERR(hw))
+ return;
+
+ of_clk_add_provider(np, of_clk_src_simple_get, hw);
+}
+CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_frac_setup,
+ "atmel,sama5d2-clk-audio-pll-frac",
+ of_sama5d2_clk_audio_pll_frac_setup);
+
+static void __init of_sama5d2_clk_audio_pll_pad_setup(struct device_node *np)
+{
+ struct clk *hw;
+ const char *name = np->name;
+ const char *parent_name;
+ struct regmap *regmap;
+
+ regmap = syscon_node_to_regmap(of_get_parent(np));
+ if (IS_ERR(regmap))
+ return;
+
+ parent_name = of_clk_get_parent_name(np, 0);
+
+ hw = at91_clk_register_audio_pll_pad(regmap, name, parent_name);
+ if (IS_ERR(hw))
+ return;
+
+ of_clk_add_provider(np, of_clk_src_simple_get, hw);
+}
+CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_pad_setup,
+ "atmel,sama5d2-clk-audio-pll-pad",
+ of_sama5d2_clk_audio_pll_pad_setup);
+
+static void __init of_sama5d2_clk_audio_pll_pmc_setup(struct device_node *np)
+{
+ struct clk *hw;
+ const char *name = np->name;
+ const char *parent_name;
+ struct regmap *regmap;
+
+ regmap = syscon_node_to_regmap(of_get_parent(np));
+ if (IS_ERR(regmap))
+ return;
+
+ parent_name = of_clk_get_parent_name(np, 0);
+
+ hw = at91_clk_register_audio_pll_pmc(regmap, name, parent_name);
+ if (IS_ERR(hw))
+ return;
+
+ of_clk_add_provider(np, of_clk_src_simple_get, hw);
+}
+CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_pmc_setup,
+ "atmel,sama5d2-clk-audio-pll-pmc",
+ of_sama5d2_clk_audio_pll_pmc_setup);
+#endif /* CONFIG_HAVE_AT91_AUDIO_PLL */
+
+#ifdef CONFIG_HAVE_AT91_GENERATED_CLK
+#define GENERATED_SOURCE_MAX 6
+
+#define GCK_ID_I2S0 54
+#define GCK_ID_I2S1 55
+#define GCK_ID_CLASSD 59
+
+static void __init of_sama5d2_clk_generated_setup(struct device_node *np)
+{
+ int num;
+ u32 id;
+ const char *name;
+ struct clk *hw;
+ unsigned int num_parents;
+ const char *parent_names[GENERATED_SOURCE_MAX];
+ struct device_node *gcknp;
+ struct clk_range range = CLK_RANGE(0, 0);
+ struct regmap *regmap;
+
+ num_parents = of_clk_get_parent_count(np);
+ if (num_parents == 0 || num_parents > GENERATED_SOURCE_MAX)
+ return;
+
+ of_clk_parent_fill(np, parent_names, num_parents);
+
+ num = of_get_child_count(np);
+ if (!num || num > PERIPHERAL_MAX)
+ return;
+
+ regmap = syscon_node_to_regmap(of_get_parent(np));
+ if (IS_ERR(regmap))
+ return;
+
+ for_each_child_of_node(np, gcknp) {
+ bool pll_audio = false;
+
+ if (of_property_read_u32(gcknp, "reg", &id))
+ continue;
+
+ if (id < PERIPHERAL_ID_MIN || id >= PERIPHERAL_MAX)
+ continue;
+
+ if (of_property_read_string(np, "clock-output-names", &name))
+ name = gcknp->name;
+
+ of_at91_get_clk_range(gcknp, "atmel,clk-output-range",
+ &range);
+
+ if (of_device_is_compatible(np, "atmel,sama5d2-clk-generated") &&
+ (id == GCK_ID_I2S0 || id == GCK_ID_I2S1 ||
+ id == GCK_ID_CLASSD))
+ pll_audio = true;
+
+ hw = at91_clk_register_generated(regmap, name,
+ parent_names, num_parents,
+ id, pll_audio, &range);
+ if (IS_ERR(hw))
+ continue;
+
+ of_clk_add_provider(gcknp, of_clk_src_simple_get, hw);
+ }
+}
+CLK_OF_DECLARE(of_sama5d2_clk_generated_setup, "atmel,sama5d2-clk-generated",
+ of_sama5d2_clk_generated_setup);
+#endif /* CONFIG_HAVE_AT91_GENERATED_CLK */
+
+#ifdef CONFIG_HAVE_AT91_H32MX
+static void __init of_sama5d4_clk_h32mx_setup(struct device_node *np)
+{
+ struct clk *hw;
+ const char *name = np->name;
+ const char *parent_name;
+ struct regmap *regmap;
+
+ regmap = syscon_node_to_regmap(of_get_parent(np));
+ if (IS_ERR(regmap))
+ return;
+
+ parent_name = of_clk_get_parent_name(np, 0);
+
+ hw = at91_clk_register_h32mx(regmap, name, parent_name);
+ if (IS_ERR(hw))
+ return;
+
+ of_clk_add_provider(np, of_clk_src_simple_get, hw);
+}
+CLK_OF_DECLARE(of_sama5d4_clk_h32mx_setup, "atmel,sama5d4-clk-h32mx",
+ of_sama5d4_clk_h32mx_setup);
+#endif /* CONFIG_HAVE_AT91_H32MX */
+
+#ifdef CONFIG_HAVE_AT91_I2S_MUX_CLK
+#define I2S_BUS_NR 2
+
+static void __init of_sama5d2_clk_i2s_mux_setup(struct device_node *np)
+{
+ struct regmap *regmap_sfr;
+ u8 bus_id;
+ const char *parent_names[2];
+ struct device_node *i2s_mux_np;
+ struct clk *hw;
+ int ret;
+
+ regmap_sfr = syscon_regmap_lookup_by_compatible("atmel,sama5d2-sfr");
+ if (IS_ERR(regmap_sfr))
+ return;
+
+ for_each_child_of_node(np, i2s_mux_np) {
+ if (of_property_read_u8(i2s_mux_np, "reg", &bus_id))
+ continue;
+
+ if (bus_id > I2S_BUS_NR)
+ continue;
+
+ ret = of_clk_parent_fill(i2s_mux_np, parent_names, 2);
+ if (ret != 2)
+ continue;
+
+ hw = at91_clk_i2s_mux_register(regmap_sfr, i2s_mux_np->name,
+ parent_names, 2, bus_id);
+ if (IS_ERR(hw))
+ continue;
+
+ of_clk_add_provider(i2s_mux_np, of_clk_src_simple_get, hw);
+ }
+}
+CLK_OF_DECLARE(sama5d2_clk_i2s_mux, "atmel,sama5d2-clk-i2s-mux",
+ of_sama5d2_clk_i2s_mux_setup);
+#endif /* CONFIG_HAVE_AT91_I2S_MUX_CLK */
+
+static void __init of_at91rm9200_clk_main_osc_setup(struct device_node *np)
+{
+ struct clk *hw;
+ const char *name = np->name;
+ const char *parent_name;
+ struct regmap *regmap;
+ bool bypass;
+
+ of_property_read_string(np, "clock-output-names", &name);
+ bypass = of_property_read_bool(np, "atmel,osc-bypass");
+ parent_name = of_clk_get_parent_name(np, 0);
+
+ regmap = syscon_node_to_regmap(of_get_parent(np));
+ if (IS_ERR(regmap))
+ return;
+
+ hw = at91_clk_register_main_osc(regmap, name, parent_name, bypass);
+ if (IS_ERR(hw))
+ return;
+
+ of_clk_add_provider(np, of_clk_src_simple_get, hw);
+}
+CLK_OF_DECLARE(at91rm9200_clk_main_osc, "atmel,at91rm9200-clk-main-osc",
+ of_at91rm9200_clk_main_osc_setup);
+
+static void __init of_at91sam9x5_clk_main_rc_osc_setup(struct device_node *np)
+{
+ struct clk *hw;
+ u32 frequency = 0;
+ u32 accuracy = 0;
+ const char *name = np->name;
+ struct regmap *regmap;
+
+ of_property_read_string(np, "clock-output-names", &name);
+ of_property_read_u32(np, "clock-frequency", &frequency);
+ of_property_read_u32(np, "clock-accuracy", &accuracy);
+
+ regmap = syscon_node_to_regmap(of_get_parent(np));
+ if (IS_ERR(regmap))
+ return;
+
+ hw = at91_clk_register_main_rc_osc(regmap, name, frequency, accuracy);
+ if (IS_ERR(hw))
+ return;
+
+ of_clk_add_provider(np, of_clk_src_simple_get, hw);
+}
+CLK_OF_DECLARE(at91sam9x5_clk_main_rc_osc, "atmel,at91sam9x5-clk-main-rc-osc",
+ of_at91sam9x5_clk_main_rc_osc_setup);
+
+static void __init of_at91rm9200_clk_main_setup(struct device_node *np)
+{
+ struct clk *hw;
+ const char *parent_name;
+ const char *name = np->name;
+ struct regmap *regmap;
+
+ parent_name = of_clk_get_parent_name(np, 0);
+ of_property_read_string(np, "clock-output-names", &name);
+
+ regmap = syscon_node_to_regmap(of_get_parent(np));
+ if (IS_ERR(regmap))
+ return;
+
+ hw = at91_clk_register_rm9200_main(regmap, name, parent_name);
+ if (IS_ERR(hw))
+ return;
+
+ of_clk_add_provider(np, of_clk_src_simple_get, hw);
+}
+CLK_OF_DECLARE(at91rm9200_clk_main, "atmel,at91rm9200-clk-main",
+ of_at91rm9200_clk_main_setup);
+
+static void __init of_at91sam9x5_clk_main_setup(struct device_node *np)
+{
+ struct clk *hw;
+ const char *parent_names[2];
+ unsigned int num_parents;
+ const char *name = np->name;
+ struct regmap *regmap;
+
+ num_parents = of_clk_get_parent_count(np);
+ if (num_parents == 0 || num_parents > 2)
+ return;
+
+ of_clk_parent_fill(np, parent_names, num_parents);
+ regmap = syscon_node_to_regmap(of_get_parent(np));
+ if (IS_ERR(regmap))
+ return;
+
+ of_property_read_string(np, "clock-output-names", &name);
+
+ hw = at91_clk_register_sam9x5_main(regmap, name, parent_names,
+ num_parents);
+ if (IS_ERR(hw))
+ return;
+
+ of_clk_add_provider(np, of_clk_src_simple_get, hw);
+}
+CLK_OF_DECLARE(at91sam9x5_clk_main, "atmel,at91sam9x5-clk-main",
+ of_at91sam9x5_clk_main_setup);
+
+static struct clk_master_characteristics * __init
+of_at91_clk_master_get_characteristics(struct device_node *np)
+{
+ struct clk_master_characteristics *characteristics;
+
+ characteristics = kzalloc(sizeof(*characteristics), GFP_KERNEL);
+ if (!characteristics)
+ return NULL;
+
+ if (of_at91_get_clk_range(np, "atmel,clk-output-range", &characteristics->output))
+ goto out_free_characteristics;
+
+ of_property_read_u32_array(np, "atmel,clk-divisors",
+ characteristics->divisors, 4);
+
+ characteristics->have_div3_pres =
+ of_property_read_bool(np, "atmel,master-clk-have-div3-pres");
+
+ return characteristics;
+
+out_free_characteristics:
+ kfree(characteristics);
+ return NULL;
+}
+
+static void __init
+of_at91_clk_master_setup(struct device_node *np,
+ const struct clk_master_layout *layout)
+{
+ struct clk *hw;
+ unsigned int num_parents;
+ const char *parent_names[MASTER_SOURCE_MAX];
+ const char *name = np->name;
+ struct clk_master_characteristics *characteristics;
+ struct regmap *regmap;
+
+ num_parents = of_clk_get_parent_count(np);
+ if (num_parents == 0 || num_parents > MASTER_SOURCE_MAX)
+ return;
+
+ of_clk_parent_fill(np, parent_names, num_parents);
+
+ of_property_read_string(np, "clock-output-names", &name);
+
+ characteristics = of_at91_clk_master_get_characteristics(np);
+ if (!characteristics)
+ return;
+
+ regmap = syscon_node_to_regmap(of_get_parent(np));
+ if (IS_ERR(regmap))
+ return;
+
+ hw = at91_clk_register_master(regmap, name, num_parents,
+ parent_names, layout,
+ characteristics);
+ if (IS_ERR(hw))
+ goto out_free_characteristics;
+
+ of_clk_add_provider(np, of_clk_src_simple_get, hw);
+ return;
+
+out_free_characteristics:
+ kfree(characteristics);
+}
+
+static void __init of_at91rm9200_clk_master_setup(struct device_node *np)
+{
+ of_at91_clk_master_setup(np, &at91rm9200_master_layout);
+}
+CLK_OF_DECLARE(at91rm9200_clk_master, "atmel,at91rm9200-clk-master",
+ of_at91rm9200_clk_master_setup);
+
+static void __init of_at91sam9x5_clk_master_setup(struct device_node *np)
+{
+ of_at91_clk_master_setup(np, &at91sam9x5_master_layout);
+}
+CLK_OF_DECLARE(at91sam9x5_clk_master, "atmel,at91sam9x5-clk-master",
+ of_at91sam9x5_clk_master_setup);
+
+static void __init
+of_at91_clk_periph_setup(struct device_node *np, u8 type)
+{
+ int num;
+ u32 id;
+ struct clk *hw;
+ const char *parent_name;
+ const char *name;
+ struct device_node *periphclknp;
+ struct regmap *regmap;
+
+ parent_name = of_clk_get_parent_name(np, 0);
+ if (!parent_name)
+ return;
+
+ num = of_get_child_count(np);
+ if (!num || num > PERIPHERAL_MAX)
+ return;
+
+ regmap = syscon_node_to_regmap(of_get_parent(np));
+ if (IS_ERR(regmap))
+ return;
+
+ for_each_child_of_node(np, periphclknp) {
+ if (of_property_read_u32(periphclknp, "reg", &id))
+ continue;
+
+ if (id >= PERIPHERAL_MAX)
+ continue;
+
+ if (of_property_read_string(np, "clock-output-names", &name))
+ name = periphclknp->name;
+
+ if (type == PERIPHERAL_AT91RM9200) {
+ hw = at91_clk_register_peripheral(regmap, name,
+ parent_name, id);
+ } else {
+ struct clk_range range = CLK_RANGE(0, 0);
+
+ of_at91_get_clk_range(periphclknp,
+ "atmel,clk-output-range",
+ &range);
+
+ hw = at91_clk_register_sam9x5_peripheral(regmap,
+ name,
+ parent_name,
+ id, &range);
+ }
+
+ if (IS_ERR(hw))
+ continue;
+
+ of_clk_add_provider(periphclknp, of_clk_src_simple_get, hw);
+ }
+}
+
+static void __init of_at91rm9200_clk_periph_setup(struct device_node *np)
+{
+ of_at91_clk_periph_setup(np, PERIPHERAL_AT91RM9200);
+}
+CLK_OF_DECLARE(at91rm9200_clk_periph, "atmel,at91rm9200-clk-peripheral",
+ of_at91rm9200_clk_periph_setup);
+
+static void __init of_at91sam9x5_clk_periph_setup(struct device_node *np)
+{
+ of_at91_clk_periph_setup(np, PERIPHERAL_AT91SAM9X5);
+}
+CLK_OF_DECLARE(at91sam9x5_clk_periph, "atmel,at91sam9x5-clk-peripheral",
+ of_at91sam9x5_clk_periph_setup);
+
+static struct clk_pll_characteristics * __init
+of_at91_clk_pll_get_characteristics(struct device_node *np)
+{
+ int i;
+ int offset;
+ u32 tmp;
+ int num_output;
+ u32 num_cells;
+ struct clk_range input;
+ struct clk_range *output;
+ u8 *out = NULL;
+ u16 *icpll = NULL;
+ struct clk_pll_characteristics *characteristics;
+
+ if (of_at91_get_clk_range(np, "atmel,clk-input-range", &input))
+ return NULL;
+
+ if (of_property_read_u32(np, "#atmel,pll-clk-output-range-cells",
+ &num_cells))
+ return NULL;
+
+ if (num_cells < 2 || num_cells > 4)
+ return NULL;
+
+ if (!of_get_property(np, "atmel,pll-clk-output-ranges", &tmp))
+ return NULL;
+ num_output = tmp / (sizeof(u32) * num_cells);
+
+ characteristics = kzalloc(sizeof(*characteristics), GFP_KERNEL);
+ if (!characteristics)
+ return NULL;
+
+ output = kcalloc(num_output, sizeof(*output), GFP_KERNEL);
+ if (!output)
+ goto out_free_characteristics;
+
+ if (num_cells > 2) {
+ out = kcalloc(num_output, sizeof(*out), GFP_KERNEL);
+ if (!out)
+ goto out_free_output;
+ }
+
+ if (num_cells > 3) {
+ icpll = kcalloc(num_output, sizeof(*icpll), GFP_KERNEL);
+ if (!icpll)
+ goto out_free_output;
+ }
+
+ for (i = 0; i < num_output; i++) {
+ offset = i * num_cells;
+ if (of_property_read_u32_index(np,
+ "atmel,pll-clk-output-ranges",
+ offset, &tmp))
+ goto out_free_output;
+ output[i].min = tmp;
+ if (of_property_read_u32_index(np,
+ "atmel,pll-clk-output-ranges",
+ offset + 1, &tmp))
+ goto out_free_output;
+ output[i].max = tmp;
+
+ if (num_cells == 2)
+ continue;
+
+ if (of_property_read_u32_index(np,
+ "atmel,pll-clk-output-ranges",
+ offset + 2, &tmp))
+ goto out_free_output;
+ out[i] = tmp;
+
+ if (num_cells == 3)
+ continue;
+
+ if (of_property_read_u32_index(np,
+ "atmel,pll-clk-output-ranges",
+ offset + 3, &tmp))
+ goto out_free_output;
+ icpll[i] = tmp;
+ }
+
+ characteristics->input = input;
+ characteristics->num_output = num_output;
+ characteristics->output = output;
+ characteristics->out = out;
+ characteristics->icpll = icpll;
+ return characteristics;
+
+out_free_output:
+ kfree(icpll);
+ kfree(out);
+ kfree(output);
+out_free_characteristics:
+ kfree(characteristics);
+ return NULL;
+}
+
+static void __init
+of_at91_clk_pll_setup(struct device_node *np,
+ const struct clk_pll_layout *layout)
+{
+ u32 id;
+ struct clk *hw;
+ struct regmap *regmap;
+ const char *parent_name;
+ const char *name = np->name;
+ struct clk_pll_characteristics *characteristics;
+
+ if (of_property_read_u32(np, "reg", &id))
+ return;
+
+ parent_name = of_clk_get_parent_name(np, 0);
+
+ of_property_read_string(np, "clock-output-names", &name);
+
+ regmap = syscon_node_to_regmap(of_get_parent(np));
+ if (IS_ERR(regmap))
+ return;
+
+ characteristics = of_at91_clk_pll_get_characteristics(np);
+ if (!characteristics)
+ return;
+
+ hw = at91_clk_register_pll(regmap, name, parent_name, id, layout,
+ characteristics);
+ if (IS_ERR(hw))
+ goto out_free_characteristics;
+
+ of_clk_add_provider(np, of_clk_src_simple_get, hw);
+ return;
+
+out_free_characteristics:
+ kfree(characteristics);
+}
+
+static void __init of_at91rm9200_clk_pll_setup(struct device_node *np)
+{
+ of_at91_clk_pll_setup(np, &at91rm9200_pll_layout);
+}
+CLK_OF_DECLARE(at91rm9200_clk_pll, "atmel,at91rm9200-clk-pll",
+ of_at91rm9200_clk_pll_setup);
+
+static void __init of_at91sam9g45_clk_pll_setup(struct device_node *np)
+{
+ of_at91_clk_pll_setup(np, &at91sam9g45_pll_layout);
+}
+CLK_OF_DECLARE(at91sam9g45_clk_pll, "atmel,at91sam9g45-clk-pll",
+ of_at91sam9g45_clk_pll_setup);
+
+static void __init of_at91sam9g20_clk_pllb_setup(struct device_node *np)
+{
+ of_at91_clk_pll_setup(np, &at91sam9g20_pllb_layout);
+}
+CLK_OF_DECLARE(at91sam9g20_clk_pllb, "atmel,at91sam9g20-clk-pllb",
+ of_at91sam9g20_clk_pllb_setup);
+
+static void __init of_sama5d3_clk_pll_setup(struct device_node *np)
+{
+ of_at91_clk_pll_setup(np, &sama5d3_pll_layout);
+}
+CLK_OF_DECLARE(sama5d3_clk_pll, "atmel,sama5d3-clk-pll",
+ of_sama5d3_clk_pll_setup);
+
+static void __init
+of_at91sam9x5_clk_plldiv_setup(struct device_node *np)
+{
+ struct clk *hw;
+ const char *parent_name;
+ const char *name = np->name;
+ struct regmap *regmap;
+
+ parent_name = of_clk_get_parent_name(np, 0);
+
+ of_property_read_string(np, "clock-output-names", &name);
+
+ regmap = syscon_node_to_regmap(of_get_parent(np));
+ if (IS_ERR(regmap))
+ return;
+
+ hw = at91_clk_register_plldiv(regmap, name, parent_name);
+ if (IS_ERR(hw))
+ return;
+
+ of_clk_add_provider(np, of_clk_src_simple_get, hw);
+}
+CLK_OF_DECLARE(at91sam9x5_clk_plldiv, "atmel,at91sam9x5-clk-plldiv",
+ of_at91sam9x5_clk_plldiv_setup);
+
+static void __init
+of_at91_clk_prog_setup(struct device_node *np,
+ const struct clk_programmable_layout *layout)
+{
+ int num;
+ u32 id;
+ struct clk *hw;
+ unsigned int num_parents;
+ const char *parent_names[PROG_SOURCE_MAX];
+ const char *name;
+ struct device_node *progclknp;
+ struct regmap *regmap;
+
+ num_parents = of_clk_get_parent_count(np);
+ if (num_parents == 0 || num_parents > PROG_SOURCE_MAX)
+ return;
+
+ of_clk_parent_fill(np, parent_names, num_parents);
+
+ num = of_get_child_count(np);
+ if (!num || num > (PROG_ID_MAX + 1))
+ return;
+
+ regmap = syscon_node_to_regmap(of_get_parent(np));
+ if (IS_ERR(regmap))
+ return;
+
+ for_each_child_of_node(np, progclknp) {
+ if (of_property_read_u32(progclknp, "reg", &id))
+ continue;
+
+ if (of_property_read_string(np, "clock-output-names", &name))
+ name = progclknp->name;
+
+ hw = at91_clk_register_programmable(regmap, name,
+ parent_names, num_parents,
+ id, layout);
+ if (IS_ERR(hw))
+ continue;
+
+ of_clk_add_provider(progclknp, of_clk_src_simple_get, hw);
+ }
+}
+
+static void __init of_at91rm9200_clk_prog_setup(struct device_node *np)
+{
+ of_at91_clk_prog_setup(np, &at91rm9200_programmable_layout);
+}
+CLK_OF_DECLARE(at91rm9200_clk_prog, "atmel,at91rm9200-clk-programmable",
+ of_at91rm9200_clk_prog_setup);
+
+static void __init of_at91sam9g45_clk_prog_setup(struct device_node *np)
+{
+ of_at91_clk_prog_setup(np, &at91sam9g45_programmable_layout);
+}
+CLK_OF_DECLARE(at91sam9g45_clk_prog, "atmel,at91sam9g45-clk-programmable",
+ of_at91sam9g45_clk_prog_setup);
+
+static void __init of_at91sam9x5_clk_prog_setup(struct device_node *np)
+{
+ of_at91_clk_prog_setup(np, &at91sam9x5_programmable_layout);
+}
+CLK_OF_DECLARE(at91sam9x5_clk_prog, "atmel,at91sam9x5-clk-programmable",
+ of_at91sam9x5_clk_prog_setup);
+
+static void __init of_at91sam9260_clk_slow_setup(struct device_node *np)
+{
+ struct clk *hw;
+ const char *parent_names[2];
+ unsigned int num_parents;
+ const char *name = np->name;
+ struct regmap *regmap;
+
+ num_parents = of_clk_get_parent_count(np);
+ if (num_parents != 2)
+ return;
+
+ of_clk_parent_fill(np, parent_names, num_parents);
+ regmap = syscon_node_to_regmap(of_get_parent(np));
+ if (IS_ERR(regmap))
+ return;
+
+ of_property_read_string(np, "clock-output-names", &name);
+
+ hw = at91_clk_register_sam9260_slow(regmap, name, parent_names,
+ num_parents);
+ if (IS_ERR(hw))
+ return;
+
+ of_clk_add_provider(np, of_clk_src_simple_get, hw);
+}
+CLK_OF_DECLARE(at91sam9260_clk_slow, "atmel,at91sam9260-clk-slow",
+ of_at91sam9260_clk_slow_setup);
+
+#ifdef CONFIG_HAVE_AT91_SMD
+#define SMD_SOURCE_MAX 2
+
+static void __init of_at91sam9x5_clk_smd_setup(struct device_node *np)
+{
+ struct clk *hw;
+ unsigned int num_parents;
+ const char *parent_names[SMD_SOURCE_MAX];
+ const char *name = np->name;
+ struct regmap *regmap;
+
+ num_parents = of_clk_get_parent_count(np);
+ if (num_parents == 0 || num_parents > SMD_SOURCE_MAX)
+ return;
+
+ of_clk_parent_fill(np, parent_names, num_parents);
+
+ of_property_read_string(np, "clock-output-names", &name);
+
+ regmap = syscon_node_to_regmap(of_get_parent(np));
+ if (IS_ERR(regmap))
+ return;
+
+ hw = at91sam9x5_clk_register_smd(regmap, name, parent_names,
+ num_parents);
+ if (IS_ERR(hw))
+ return;
+
+ of_clk_add_provider(np, of_clk_src_simple_get, hw);
+}
+CLK_OF_DECLARE(at91sam9x5_clk_smd, "atmel,at91sam9x5-clk-smd",
+ of_at91sam9x5_clk_smd_setup);
+#endif /* CONFIG_HAVE_AT91_SMD */
+
+static void __init of_at91rm9200_clk_sys_setup(struct device_node *np)
+{
+ int num;
+ u32 id;
+ struct clk *hw;
+ const char *name;
+ struct device_node *sysclknp;
+ const char *parent_name;
+ struct regmap *regmap;
+
+ num = of_get_child_count(np);
+ if (num > (SYSTEM_MAX_ID + 1))
+ return;
+
+ regmap = syscon_node_to_regmap(of_get_parent(np));
+ if (IS_ERR(regmap))
+ return;
+
+ for_each_child_of_node(np, sysclknp) {
+ if (of_property_read_u32(sysclknp, "reg", &id))
+ continue;
+
+ if (of_property_read_string(np, "clock-output-names", &name))
+ name = sysclknp->name;
+
+ parent_name = of_clk_get_parent_name(sysclknp, 0);
+
+ hw = at91_clk_register_system(regmap, name, parent_name, id);
+ if (IS_ERR(hw))
+ continue;
+
+ of_clk_add_provider(sysclknp, of_clk_src_simple_get, hw);
+ }
+}
+CLK_OF_DECLARE(at91rm9200_clk_sys, "atmel,at91rm9200-clk-system",
+ of_at91rm9200_clk_sys_setup);
+
+#ifdef CONFIG_HAVE_AT91_USB_CLK
+#define USB_SOURCE_MAX 2
+
+static void __init of_at91sam9x5_clk_usb_setup(struct device_node *np)
+{
+ struct clk *hw;
+ unsigned int num_parents;
+ const char *parent_names[USB_SOURCE_MAX];
+ const char *name = np->name;
+ struct regmap *regmap;
+
+ num_parents = of_clk_get_parent_count(np);
+ if (num_parents == 0 || num_parents > USB_SOURCE_MAX)
+ return;
+
+ of_clk_parent_fill(np, parent_names, num_parents);
+
+ of_property_read_string(np, "clock-output-names", &name);
+
+ regmap = syscon_node_to_regmap(of_get_parent(np));
+ if (IS_ERR(regmap))
+ return;
+
+ hw = at91sam9x5_clk_register_usb(regmap, name, parent_names,
+ num_parents);
+ if (IS_ERR(hw))
+ return;
+
+ of_clk_add_provider(np, of_clk_src_simple_get, hw);
+}
+CLK_OF_DECLARE(at91sam9x5_clk_usb, "atmel,at91sam9x5-clk-usb",
+ of_at91sam9x5_clk_usb_setup);
+
+static void __init of_at91sam9n12_clk_usb_setup(struct device_node *np)
+{
+ struct clk *hw;
+ const char *parent_name;
+ const char *name = np->name;
+ struct regmap *regmap;
+
+ parent_name = of_clk_get_parent_name(np, 0);
+ if (!parent_name)
+ return;
+
+ of_property_read_string(np, "clock-output-names", &name);
+
+ regmap = syscon_node_to_regmap(of_get_parent(np));
+ if (IS_ERR(regmap))
+ return;
+
+ hw = at91sam9n12_clk_register_usb(regmap, name, parent_name);
+ if (IS_ERR(hw))
+ return;
+
+ of_clk_add_provider(np, of_clk_src_simple_get, hw);
+}
+CLK_OF_DECLARE(at91sam9n12_clk_usb, "atmel,at91sam9n12-clk-usb",
+ of_at91sam9n12_clk_usb_setup);
+
+static void __init of_at91rm9200_clk_usb_setup(struct device_node *np)
+{
+ struct clk *hw;
+ const char *parent_name;
+ const char *name = np->name;
+ u32 divisors[4] = {0, 0, 0, 0};
+ struct regmap *regmap;
+
+ parent_name = of_clk_get_parent_name(np, 0);
+ if (!parent_name)
+ return;
+
+ of_property_read_u32_array(np, "atmel,clk-divisors", divisors, 4);
+ if (!divisors[0])
+ return;
+
+ of_property_read_string(np, "clock-output-names", &name);
+
+ regmap = syscon_node_to_regmap(of_get_parent(np));
+ if (IS_ERR(regmap))
+ return;
+ hw = at91rm9200_clk_register_usb(regmap, name, parent_name, divisors);
+ if (IS_ERR(hw))
+ return;
+
+ of_clk_add_provider(np, of_clk_src_simple_get, hw);
+}
+CLK_OF_DECLARE(at91rm9200_clk_usb, "atmel,at91rm9200-clk-usb",
+ of_at91rm9200_clk_usb_setup);
+#endif /* CONFIG_HAVE_AT91_USB_CLK */
+
+#ifdef CONFIG_HAVE_AT91_UTMI
+static void __init of_at91sam9x5_clk_utmi_setup(struct device_node *np)
+{
+ struct clk *hw;
+ const char *parent_name;
+ const char *name = np->name;
+ struct regmap *regmap_pmc, *regmap_sfr;
+
+ parent_name = of_clk_get_parent_name(np, 0);
+
+ of_property_read_string(np, "clock-output-names", &name);
+
+ regmap_pmc = syscon_node_to_regmap(of_get_parent(np));
+ if (IS_ERR(regmap_pmc))
+ return;
+
+ /*
+ * If the device supports different mainck rates, this value has to be
+ * set in the UTMI Clock Trimming register.
+ * - 9x5: mainck supports several rates but it is indicated that a
+ * 12 MHz is needed in case of USB.
+ * - sama5d3 and sama5d2: mainck supports several rates. Configuring
+ * the FREQ field of the UTMI Clock Trimming register is mandatory.
+ * - sama5d4: mainck is at 12 MHz.
+ *
+ * We only need to retrieve sama5d3 or sama5d2 sfr regmap.
+ */
+ regmap_sfr = syscon_regmap_lookup_by_compatible("atmel,sama5d3-sfr");
+ if (IS_ERR(regmap_sfr)) {
+ regmap_sfr = syscon_regmap_lookup_by_compatible("atmel,sama5d2-sfr");
+ if (IS_ERR(regmap_sfr))
+ regmap_sfr = NULL;
+ }
+
+ hw = at91_clk_register_utmi(regmap_pmc, regmap_sfr, name, parent_name);
+ if (IS_ERR(hw))
+ return;
+
+ of_clk_add_provider(np, of_clk_src_simple_get, hw);
+}
+CLK_OF_DECLARE(at91sam9x5_clk_utmi, "atmel,at91sam9x5-clk-utmi",
+ of_at91sam9x5_clk_utmi_setup);
+#endif /* CONFIG_HAVE_AT91_UTMI */
diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
index d156d50ca8..aa73d61c5e 100644
--- a/drivers/clk/at91/pmc.c
+++ b/drivers/clk/at91/pmc.c
@@ -15,8 +15,13 @@
#include <mfd/syscon.h>
#include <regmap.h>
+#include <dt-bindings/clock/at91.h>
+
#include "pmc.h"
+#define PMC_MAX_IDS 128
+#define PMC_MAX_PCKS 8
+
int of_at91_get_clk_range(struct device_node *np, const char *propname,
struct clk_range *range)
{
@@ -39,3 +44,246 @@ int of_at91_get_clk_range(struct device_node *np, const char *propname,
return 0;
}
EXPORT_SYMBOL_GPL(of_at91_get_clk_range);
+
+struct clk *of_clk_hw_pmc_get(struct of_phandle_args *clkspec, void *data)
+{
+ unsigned int type = clkspec->args[0];
+ unsigned int idx = clkspec->args[1];
+ struct pmc_data *pmc_data = data;
+
+ switch (type) {
+ case PMC_TYPE_CORE:
+ if (idx < pmc_data->ncore)
+ return pmc_data->chws[idx];
+ break;
+ case PMC_TYPE_SYSTEM:
+ if (idx < pmc_data->nsystem)
+ return pmc_data->shws[idx];
+ break;
+ case PMC_TYPE_PERIPHERAL:
+ if (idx < pmc_data->nperiph)
+ return pmc_data->phws[idx];
+ break;
+ case PMC_TYPE_GCK:
+ if (idx < pmc_data->ngck)
+ return pmc_data->ghws[idx];
+ break;
+ default:
+ break;
+ }
+
+ pr_err("%s: invalid type (%u) or index (%u)\n", __func__, type, idx);
+
+ return ERR_PTR(-EINVAL);
+}
+
+void pmc_data_free(struct pmc_data *pmc_data)
+{
+ kfree(pmc_data->chws);
+ kfree(pmc_data->shws);
+ kfree(pmc_data->phws);
+ kfree(pmc_data->ghws);
+}
+
+struct pmc_data *pmc_data_allocate(unsigned int ncore, unsigned int nsystem,
+ unsigned int nperiph, unsigned int ngck)
+{
+ struct pmc_data *pmc_data = kzalloc(sizeof(*pmc_data), GFP_KERNEL);
+
+ if (!pmc_data)
+ return NULL;
+
+ pmc_data->ncore = ncore;
+ pmc_data->chws = kcalloc(ncore, sizeof(struct clk_hw *), GFP_KERNEL);
+ if (!pmc_data->chws)
+ goto err;
+
+ pmc_data->nsystem = nsystem;
+ pmc_data->shws = kcalloc(nsystem, sizeof(struct clk_hw *), GFP_KERNEL);
+ if (!pmc_data->shws)
+ goto err;
+
+ pmc_data->nperiph = nperiph;
+ pmc_data->phws = kcalloc(nperiph, sizeof(struct clk_hw *), GFP_KERNEL);
+ if (!pmc_data->phws)
+ goto err;
+
+ pmc_data->ngck = ngck;
+ pmc_data->ghws = kcalloc(ngck, sizeof(struct clk_hw *), GFP_KERNEL);
+ if (!pmc_data->ghws)
+ goto err;
+
+ return pmc_data;
+
+err:
+ pmc_data_free(pmc_data);
+
+ return NULL;
+}
+
+#ifdef CONFIG_PM
+static struct regmap *pmcreg;
+
+static u8 registered_ids[PMC_MAX_IDS];
+static u8 registered_pcks[PMC_MAX_PCKS];
+
+static struct
+{
+ u32 scsr;
+ u32 pcsr0;
+ u32 uckr;
+ u32 mor;
+ u32 mcfr;
+ u32 pllar;
+ u32 mckr;
+ u32 usb;
+ u32 imr;
+ u32 pcsr1;
+ u32 pcr[PMC_MAX_IDS];
+ u32 audio_pll0;
+ u32 audio_pll1;
+ u32 pckr[PMC_MAX_PCKS];
+} pmc_cache;
+
+/*
+ * As Peripheral ID 0 is invalid on AT91 chips, the identifier is stored
+ * without alteration in the table, and 0 is for unused clocks.
+ */
+void pmc_register_id(u8 id)
+{
+ int i;
+
+ for (i = 0; i < PMC_MAX_IDS; i++) {
+ if (registered_ids[i] == 0) {
+ registered_ids[i] = id;
+ break;
+ }
+ if (registered_ids[i] == id)
+ break;
+ }
+}
+
+/*
+ * As Programmable Clock 0 is valid on AT91 chips, there is an offset
+ * of 1 between the stored value and the real clock ID.
+ */
+void pmc_register_pck(u8 pck)
+{
+ int i;
+
+ for (i = 0; i < PMC_MAX_PCKS; i++) {
+ if (registered_pcks[i] == 0) {
+ registered_pcks[i] = pck + 1;
+ break;
+ }
+ if (registered_pcks[i] == (pck + 1))
+ break;
+ }
+}
+
+static int pmc_suspend(void)
+{
+ int i;
+ u8 num;
+
+ regmap_read(pmcreg, AT91_PMC_SCSR, &pmc_cache.scsr);
+ regmap_read(pmcreg, AT91_PMC_PCSR, &pmc_cache.pcsr0);
+ regmap_read(pmcreg, AT91_CKGR_UCKR, &pmc_cache.uckr);
+ regmap_read(pmcreg, AT91_CKGR_MOR, &pmc_cache.mor);
+ regmap_read(pmcreg, AT91_CKGR_MCFR, &pmc_cache.mcfr);
+ regmap_read(pmcreg, AT91_CKGR_PLLAR, &pmc_cache.pllar);
+ regmap_read(pmcreg, AT91_PMC_MCKR, &pmc_cache.mckr);
+ regmap_read(pmcreg, AT91_PMC_USB, &pmc_cache.usb);
+ regmap_read(pmcreg, AT91_PMC_IMR, &pmc_cache.imr);
+ regmap_read(pmcreg, AT91_PMC_PCSR1, &pmc_cache.pcsr1);
+
+ for (i = 0; registered_ids[i]; i++) {
+ regmap_write(pmcreg, AT91_PMC_PCR,
+ (registered_ids[i] & AT91_PMC_PCR_PID_MASK));
+ regmap_read(pmcreg, AT91_PMC_PCR,
+ &pmc_cache.pcr[registered_ids[i]]);
+ }
+ for (i = 0; registered_pcks[i]; i++) {
+ num = registered_pcks[i] - 1;
+ regmap_read(pmcreg, AT91_PMC_PCKR(num), &pmc_cache.pckr[num]);
+ }
+
+ return 0;
+}
+
+static bool pmc_ready(unsigned int mask)
+{
+ unsigned int status;
+
+ regmap_read(pmcreg, AT91_PMC_SR, &status);
+
+ return ((status & mask) == mask) ? 1 : 0;
+}
+
+static void pmc_resume(void)
+{
+ int i;
+ u8 num;
+ u32 tmp;
+ u32 mask = AT91_PMC_MCKRDY | AT91_PMC_LOCKA;
+
+ regmap_read(pmcreg, AT91_PMC_MCKR, &tmp);
+ if (pmc_cache.mckr != tmp)
+ pr_warn("MCKR was not configured properly by the firmware\n");
+ regmap_read(pmcreg, AT91_CKGR_PLLAR, &tmp);
+ if (pmc_cache.pllar != tmp)
+ pr_warn("PLLAR was not configured properly by the firmware\n");
+
+ regmap_write(pmcreg, AT91_PMC_SCER, pmc_cache.scsr);
+ regmap_write(pmcreg, AT91_PMC_PCER, pmc_cache.pcsr0);
+ regmap_write(pmcreg, AT91_CKGR_UCKR, pmc_cache.uckr);
+ regmap_write(pmcreg, AT91_CKGR_MOR, pmc_cache.mor);
+ regmap_write(pmcreg, AT91_CKGR_MCFR, pmc_cache.mcfr);
+ regmap_write(pmcreg, AT91_PMC_USB, pmc_cache.usb);
+ regmap_write(pmcreg, AT91_PMC_IMR, pmc_cache.imr);
+ regmap_write(pmcreg, AT91_PMC_PCER1, pmc_cache.pcsr1);
+
+ for (i = 0; registered_ids[i]; i++) {
+ regmap_write(pmcreg, AT91_PMC_PCR,
+ pmc_cache.pcr[registered_ids[i]] |
+ AT91_PMC_PCR_CMD);
+ }
+ for (i = 0; registered_pcks[i]; i++) {
+ num = registered_pcks[i] - 1;
+ regmap_write(pmcreg, AT91_PMC_PCKR(num), pmc_cache.pckr[num]);
+ }
+
+ if (pmc_cache.uckr & AT91_PMC_UPLLEN)
+ mask |= AT91_PMC_LOCKU;
+
+ while (!pmc_ready(mask))
+ cpu_relax();
+}
+
+static struct syscore_ops pmc_syscore_ops = {
+ .suspend = pmc_suspend,
+ .resume = pmc_resume,
+};
+
+static const struct of_device_id sama5d2_pmc_dt_ids[] = {
+ { .compatible = "atmel,sama5d2-pmc" },
+ { /* sentinel */ }
+};
+
+static int __init pmc_register_ops(void)
+{
+ struct device_node *np;
+
+ np = of_find_matching_node(NULL, sama5d2_pmc_dt_ids);
+
+ pmcreg = syscon_node_to_regmap(np);
+ if (IS_ERR(pmcreg))
+ return PTR_ERR(pmcreg);
+
+ register_syscore_ops(&pmc_syscore_ops);
+
+ return 0;
+}
+/* This has to happen before arch_initcall because of the tcb_clksrc driver */
+postcore_initcall(pmc_register_ops);
+#endif
diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
index c6c14a79a4..529498308f 100644
--- a/drivers/clk/at91/pmc.h
+++ b/drivers/clk/at91/pmc.h
@@ -13,6 +13,19 @@
#define __PMC_H_
#include <io.h>
+#include <linux/spinlock.h>
+#include <printk.h>
+
+struct pmc_data {
+ unsigned int ncore;
+ struct clk **chws;
+ unsigned int nsystem;
+ struct clk **shws;
+ unsigned int nperiph;
+ struct clk **phws;
+ unsigned int ngck;
+ struct clk **ghws;
+};
struct clk_range {
unsigned long min;
@@ -21,7 +34,163 @@ struct clk_range {
#define CLK_RANGE(MIN, MAX) {.min = MIN, .max = MAX,}
+struct clk_master_layout {
+ u32 mask;
+ u8 pres_shift;
+};
+
+extern const struct clk_master_layout at91rm9200_master_layout;
+extern const struct clk_master_layout at91sam9x5_master_layout;
+
+struct clk_master_characteristics {
+ struct clk_range output;
+ u32 divisors[4];
+ u8 have_div3_pres;
+};
+
+struct clk_pll_layout {
+ u32 pllr_mask;
+ u16 mul_mask;
+ u8 mul_shift;
+};
+
+extern const struct clk_pll_layout at91rm9200_pll_layout;
+extern const struct clk_pll_layout at91sam9g45_pll_layout;
+extern const struct clk_pll_layout at91sam9g20_pllb_layout;
+extern const struct clk_pll_layout sama5d3_pll_layout;
+
+struct clk_pll_characteristics {
+ struct clk_range input;
+ int num_output;
+ struct clk_range *output;
+ u16 *icpll;
+ u8 *out;
+};
+
+struct clk_programmable_layout {
+ u8 pres_shift;
+ u8 css_mask;
+ u8 have_slck_mck;
+};
+
+extern const struct clk_programmable_layout at91rm9200_programmable_layout;
+extern const struct clk_programmable_layout at91sam9g45_programmable_layout;
+extern const struct clk_programmable_layout at91sam9x5_programmable_layout;
+
+#define ndck(a, s) (a[s - 1].id + 1)
+#define nck(a) (a[ARRAY_SIZE(a) - 1].id + 1)
+struct pmc_data *pmc_data_allocate(unsigned int ncore, unsigned int nsystem,
+ unsigned int nperiph, unsigned int ngck);
+void pmc_data_free(struct pmc_data *pmc_data);
+
int of_at91_get_clk_range(struct device_node *np, const char *propname,
struct clk_range *range);
+struct clk *of_clk_hw_pmc_get(struct of_phandle_args *clkspec, void *data);
+
+struct clk *
+at91_clk_register_audio_pll_frac(struct regmap *regmap, const char *name,
+ const char *parent_name);
+
+struct clk *
+at91_clk_register_audio_pll_pad(struct regmap *regmap, const char *name,
+ const char *parent_name);
+
+struct clk *
+at91_clk_register_audio_pll_pmc(struct regmap *regmap, const char *name,
+ const char *parent_name);
+
+struct clk *
+at91_clk_register_generated(struct regmap *regmap,
+ const char *name, const char **parent_names,
+ u8 num_parents, u8 id, bool pll_audio,
+ const struct clk_range *range);
+
+struct clk *
+at91_clk_register_h32mx(struct regmap *regmap, const char *name,
+ const char *parent_name);
+
+struct clk *
+at91_clk_i2s_mux_register(struct regmap *regmap, const char *name,
+ const char * const *parent_names,
+ unsigned int num_parents, u8 bus_id);
+
+struct clk *
+at91_clk_register_main_rc_osc(struct regmap *regmap, const char *name,
+ u32 frequency, u32 accuracy);
+struct clk *
+at91_clk_register_main_osc(struct regmap *regmap, const char *name,
+ const char *parent_name, bool bypass);
+struct clk *
+at91_clk_register_rm9200_main(struct regmap *regmap,
+ const char *name,
+ const char *parent_name);
+struct clk *
+at91_clk_register_sam9x5_main(struct regmap *regmap, const char *name,
+ const char **parent_names, int num_parents);
+
+struct clk *
+at91_clk_register_master(struct regmap *regmap, const char *name,
+ int num_parents, const char **parent_names,
+ const struct clk_master_layout *layout,
+ const struct clk_master_characteristics *characteristics);
+
+struct clk *
+at91_clk_register_peripheral(struct regmap *regmap, const char *name,
+ const char *parent_name, u32 id);
+struct clk *
+at91_clk_register_sam9x5_peripheral(struct regmap *regmap,
+ const char *name, const char *parent_name,
+ u32 id, const struct clk_range *range);
+
+struct clk *
+at91_clk_register_pll(struct regmap *regmap, const char *name,
+ const char *parent_name, u8 id,
+ const struct clk_pll_layout *layout,
+ const struct clk_pll_characteristics *characteristics);
+struct clk *
+at91_clk_register_plldiv(struct regmap *regmap, const char *name,
+ const char *parent_name);
+
+struct clk *
+at91_clk_register_programmable(struct regmap *regmap, const char *name,
+ const char **parent_names, u8 num_parents, u8 id,
+ const struct clk_programmable_layout *layout);
+
+struct clk *
+at91_clk_register_sam9260_slow(struct regmap *regmap,
+ const char *name,
+ const char **parent_names,
+ int num_parents);
+
+struct clk *
+at91sam9x5_clk_register_smd(struct regmap *regmap, const char *name,
+ const char **parent_names, u8 num_parents);
+
+struct clk *
+at91_clk_register_system(struct regmap *regmap, const char *name,
+ const char *parent_name, u8 id);
+
+struct clk *
+at91sam9x5_clk_register_usb(struct regmap *regmap, const char *name,
+ const char **parent_names, u8 num_parents);
+struct clk *
+at91sam9n12_clk_register_usb(struct regmap *regmap, const char *name,
+ const char *parent_name);
+struct clk *
+at91rm9200_clk_register_usb(struct regmap *regmap, const char *name,
+ const char *parent_name, const u32 *divisors);
+
+struct clk *
+at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr,
+ const char *name, const char *parent_name);
+
+#ifdef CONFIG_PM
+void pmc_register_id(u8 id);
+void pmc_register_pck(u8 pck);
+#else
+static inline void pmc_register_id(u8 id) {}
+static inline void pmc_register_pck(u8 pck) {}
+#endif
+
#endif /* __PMC_H_ */
diff --git a/drivers/clk/at91/sama5d2.c b/drivers/clk/at91/sama5d2.c
new file mode 100644
index 0000000000..dc15f7d9cb
--- /dev/null
+++ b/drivers/clk/at91/sama5d2.c
@@ -0,0 +1,342 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <driver.h>
+#include <regmap.h>
+#include <stdio.h>
+#include <mfd/syscon.h>
+
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include <dt-bindings/clock/at91.h>
+
+#include "pmc.h"
+
+static const struct clk_master_characteristics mck_characteristics = {
+ .output = { .min = 124000000, .max = 166000000 },
+ .divisors = { 1, 2, 4, 3 },
+};
+
+static u8 plla_out[] = { 0 };
+
+static u16 plla_icpll[] = { 0 };
+
+static struct clk_range plla_outputs[] = {
+ { .min = 600000000, .max = 1200000000 },
+};
+
+static const struct clk_pll_characteristics plla_characteristics = {
+ .input = { .min = 12000000, .max = 12000000 },
+ .num_output = ARRAY_SIZE(plla_outputs),
+ .output = plla_outputs,
+ .icpll = plla_icpll,
+ .out = plla_out,
+};
+
+static const struct {
+ char *n;
+ char *p;
+ u8 id;
+} sama5d2_systemck[] = {
+ { .n = "ddrck", .p = "masterck", .id = 2 },
+ { .n = "lcdck", .p = "masterck", .id = 3 },
+ { .n = "uhpck", .p = "usbck", .id = 6 },
+ { .n = "udpck", .p = "usbck", .id = 7 },
+ { .n = "pck0", .p = "prog0", .id = 8 },
+ { .n = "pck1", .p = "prog1", .id = 9 },
+ { .n = "pck2", .p = "prog2", .id = 10 },
+ { .n = "iscck", .p = "masterck", .id = 18 },
+};
+
+static const struct {
+ char *n;
+ u8 id;
+ struct clk_range r;
+} sama5d2_periph32ck[] = {
+ { .n = "macb0_clk", .id = 5, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "tdes_clk", .id = 11, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "matrix1_clk", .id = 14, },
+ { .n = "hsmc_clk", .id = 17, },
+ { .n = "pioA_clk", .id = 18, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "flx0_clk", .id = 19, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "flx1_clk", .id = 20, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "flx2_clk", .id = 21, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "flx3_clk", .id = 22, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "flx4_clk", .id = 23, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "uart0_clk", .id = 24, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "uart1_clk", .id = 25, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "uart2_clk", .id = 26, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "uart3_clk", .id = 27, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "uart4_clk", .id = 28, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "twi0_clk", .id = 29, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "twi1_clk", .id = 30, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "spi0_clk", .id = 33, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "spi1_clk", .id = 34, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "tcb0_clk", .id = 35, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "tcb1_clk", .id = 36, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "pwm_clk", .id = 38, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "adc_clk", .id = 40, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "uhphs_clk", .id = 41, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "udphs_clk", .id = 42, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "ssc0_clk", .id = 43, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "ssc1_clk", .id = 44, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "trng_clk", .id = 47, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "pdmic_clk", .id = 48, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "securam_clk", .id = 51, },
+ { .n = "i2s0_clk", .id = 54, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "i2s1_clk", .id = 55, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "can0_clk", .id = 56, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "can1_clk", .id = 57, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "classd_clk", .id = 59, .r = { .min = 0, .max = 83000000 }, },
+};
+
+static const struct {
+ char *n;
+ u8 id;
+} sama5d2_periphck[] = {
+ { .n = "dma0_clk", .id = 6, },
+ { .n = "dma1_clk", .id = 7, },
+ { .n = "aes_clk", .id = 9, },
+ { .n = "aesb_clk", .id = 10, },
+ { .n = "sha_clk", .id = 12, },
+ { .n = "mpddr_clk", .id = 13, },
+ { .n = "matrix0_clk", .id = 15, },
+ { .n = "sdmmc0_hclk", .id = 31, },
+ { .n = "sdmmc1_hclk", .id = 32, },
+ { .n = "lcdc_clk", .id = 45, },
+ { .n = "isc_clk", .id = 46, },
+ { .n = "qspi0_clk", .id = 52, },
+ { .n = "qspi1_clk", .id = 53, },
+};
+
+static const struct {
+ char *n;
+ u8 id;
+ struct clk_range r;
+ bool pll;
+} sama5d2_gck[] = {
+ { .n = "sdmmc0_gclk", .id = 31, },
+ { .n = "sdmmc1_gclk", .id = 32, },
+ { .n = "tcb0_gclk", .id = 35, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "tcb1_gclk", .id = 36, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "pwm_gclk", .id = 38, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "isc_gclk", .id = 46, },
+ { .n = "pdmic_gclk", .id = 48, },
+ { .n = "i2s0_gclk", .id = 54, .pll = true },
+ { .n = "i2s1_gclk", .id = 55, .pll = true },
+ { .n = "can0_gclk", .id = 56, .r = { .min = 0, .max = 80000000 }, },
+ { .n = "can1_gclk", .id = 57, .r = { .min = 0, .max = 80000000 }, },
+ { .n = "classd_gclk", .id = 59, .r = { .min = 0, .max = 100000000 },
+ .pll = true },
+};
+
+static void __init sama5d2_pmc_setup(struct device_node *np)
+{
+ struct clk_range range = CLK_RANGE(0, 0);
+ const char *slck_name, *mainxtal_name;
+ struct pmc_data *sama5d2_pmc;
+ const char *parent_names[6];
+ struct regmap *regmap, *regmap_sfr;
+ struct clk *hw;
+ int i;
+ bool bypass;
+
+ i = of_property_match_string(np, "clock-names", "slow_clk");
+ if (i < 0)
+ return;
+
+ slck_name = of_clk_get_parent_name(np, i);
+
+ i = of_property_match_string(np, "clock-names", "main_xtal");
+ if (i < 0)
+ return;
+ mainxtal_name = of_clk_get_parent_name(np, i);
+
+ regmap = syscon_node_to_regmap(np);
+ if (IS_ERR(regmap))
+ return;
+
+ sama5d2_pmc = pmc_data_allocate(PMC_I2S1_MUX + 1,
+ nck(sama5d2_systemck),
+ nck(sama5d2_periph32ck),
+ nck(sama5d2_gck));
+ if (!sama5d2_pmc)
+ return;
+
+ hw = at91_clk_register_main_rc_osc(regmap, "main_rc_osc", 12000000,
+ 100000000);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ bypass = of_property_read_bool(np, "atmel,osc-bypass");
+
+ hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name,
+ bypass);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ parent_names[0] = "main_rc_osc";
+ parent_names[1] = "main_osc";
+ hw = at91_clk_register_sam9x5_main(regmap, "mainck", parent_names, 2);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama5d2_pmc->chws[PMC_MAIN] = hw;
+
+ hw = at91_clk_register_pll(regmap, "pllack", "mainck", 0,
+ &sama5d3_pll_layout, &plla_characteristics);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ hw = at91_clk_register_plldiv(regmap, "plladivck", "pllack");
+ if (IS_ERR(hw))
+ goto err_free;
+
+ hw = at91_clk_register_audio_pll_frac(regmap, "audiopll_fracck",
+ "mainck");
+ if (IS_ERR(hw))
+ goto err_free;
+
+ hw = at91_clk_register_audio_pll_pad(regmap, "audiopll_padck",
+ "audiopll_fracck");
+ if (IS_ERR(hw))
+ goto err_free;
+
+ hw = at91_clk_register_audio_pll_pmc(regmap, "audiopll_pmcck",
+ "audiopll_fracck");
+ if (IS_ERR(hw))
+ goto err_free;
+
+ regmap_sfr = syscon_regmap_lookup_by_compatible("atmel,sama5d2-sfr");
+ if (IS_ERR(regmap_sfr))
+ regmap_sfr = NULL;
+
+ hw = at91_clk_register_utmi(regmap, regmap_sfr, "utmick", "mainck");
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama5d2_pmc->chws[PMC_UTMI] = hw;
+
+ parent_names[0] = slck_name;
+ parent_names[1] = "mainck";
+ parent_names[2] = "plladivck";
+ parent_names[3] = "utmick";
+ hw = at91_clk_register_master(regmap, "masterck", 4, parent_names,
+ &at91sam9x5_master_layout,
+ &mck_characteristics);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama5d2_pmc->chws[PMC_MCK] = hw;
+
+ hw = at91_clk_register_h32mx(regmap, "h32mxck", "masterck");
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama5d2_pmc->chws[PMC_MCK2] = hw;
+
+ parent_names[0] = "plladivck";
+ parent_names[1] = "utmick";
+ hw = at91sam9x5_clk_register_usb(regmap, "usbck", parent_names, 2);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ parent_names[0] = slck_name;
+ parent_names[1] = "mainck";
+ parent_names[2] = "plladivck";
+ parent_names[3] = "utmick";
+ parent_names[4] = "mck";
+ for (i = 0; i < 3; i++) {
+ char *name;
+
+ name = xasprintf("prog%d", i);
+
+ hw = at91_clk_register_programmable(regmap, name,
+ parent_names, 5, i,
+ &at91sam9x5_programmable_layout);
+ if (IS_ERR(hw))
+ goto err_free;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(sama5d2_systemck); i++) {
+ hw = at91_clk_register_system(regmap, sama5d2_systemck[i].n,
+ sama5d2_systemck[i].p,
+ sama5d2_systemck[i].id);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama5d2_pmc->shws[sama5d2_systemck[i].id] = hw;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(sama5d2_periphck); i++) {
+ hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
+ sama5d2_periphck[i].n,
+ "masterck",
+ sama5d2_periphck[i].id,
+ &range);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama5d2_pmc->phws[sama5d2_periphck[i].id] = hw;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(sama5d2_periph32ck); i++) {
+ hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
+ sama5d2_periph32ck[i].n,
+ "h32mxck",
+ sama5d2_periph32ck[i].id,
+ &sama5d2_periph32ck[i].r);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama5d2_pmc->phws[sama5d2_periph32ck[i].id] = hw;
+ }
+
+ parent_names[0] = slck_name;
+ parent_names[1] = "mainck";
+ parent_names[2] = "plladivck";
+ parent_names[3] = "utmick";
+ parent_names[4] = "mck";
+ parent_names[5] = "audiopll_pmcck";
+ for (i = 0; i < ARRAY_SIZE(sama5d2_gck); i++) {
+ hw = at91_clk_register_generated(regmap, &pmc_pcr_lock,
+ sama5d2_gck[i].n,
+ parent_names, 6,
+ sama5d2_gck[i].id,
+ sama5d2_gck[i].pll,
+ &sama5d2_gck[i].r);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama5d2_pmc->ghws[sama5d2_gck[i].id] = hw;
+ }
+
+ if (regmap_sfr) {
+ parent_names[0] = "i2s0_clk";
+ parent_names[1] = "i2s0_gclk";
+ hw = at91_clk_i2s_mux_register(regmap_sfr, "i2s0_muxclk",
+ parent_names, 2, 0);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama5d2_pmc->chws[PMC_I2S0_MUX] = hw;
+
+ parent_names[0] = "i2s1_clk";
+ parent_names[1] = "i2s1_gclk";
+ hw = at91_clk_i2s_mux_register(regmap_sfr, "i2s1_muxclk",
+ parent_names, 2, 1);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama5d2_pmc->chws[PMC_I2S1_MUX] = hw;
+ }
+
+ of_clk_add_provider(np, of_clk_hw_pmc_get, sama5d2_pmc);
+
+ return;
+
+err_free:
+ pmc_data_free(sama5d2_pmc);
+}
+CLK_OF_DECLARE_DRIVER(sama5d2_pmc, "atmel,sama5d2-pmc", sama5d2_pmc_setup);
diff --git a/drivers/clk/at91/sama5d4.c b/drivers/clk/at91/sama5d4.c
new file mode 100644
index 0000000000..2fbfca6f85
--- /dev/null
+++ b/drivers/clk/at91/sama5d4.c
@@ -0,0 +1,270 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <driver.h>
+#include <regmap.h>
+#include <stdio.h>
+#include <mfd/syscon.h>
+
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include <dt-bindings/clock/at91.h>
+
+#include "pmc.h"
+
+static const struct clk_master_characteristics mck_characteristics = {
+ .output = { .min = 125000000, .max = 200000000 },
+ .divisors = { 1, 2, 4, 3 },
+};
+
+static u8 plla_out[] = { 0 };
+
+static u16 plla_icpll[] = { 0 };
+
+static struct clk_range plla_outputs[] = {
+ { .min = 600000000, .max = 1200000000 },
+};
+
+static const struct clk_pll_characteristics plla_characteristics = {
+ .input = { .min = 12000000, .max = 12000000 },
+ .num_output = ARRAY_SIZE(plla_outputs),
+ .output = plla_outputs,
+ .icpll = plla_icpll,
+ .out = plla_out,
+};
+
+static const struct {
+ char *n;
+ char *p;
+ u8 id;
+} sama5d4_systemck[] = {
+ { .n = "ddrck", .p = "masterck", .id = 2 },
+ { .n = "lcdck", .p = "masterck", .id = 3 },
+ { .n = "smdck", .p = "smdclk", .id = 4 },
+ { .n = "uhpck", .p = "usbck", .id = 6 },
+ { .n = "udpck", .p = "usbck", .id = 7 },
+ { .n = "pck0", .p = "prog0", .id = 8 },
+ { .n = "pck1", .p = "prog1", .id = 9 },
+ { .n = "pck2", .p = "prog2", .id = 10 },
+};
+
+static const struct {
+ char *n;
+ u8 id;
+} sama5d4_periph32ck[] = {
+ { .n = "pioD_clk", .id = 5 },
+ { .n = "usart0_clk", .id = 6 },
+ { .n = "usart1_clk", .id = 7 },
+ { .n = "icm_clk", .id = 9 },
+ { .n = "aes_clk", .id = 12 },
+ { .n = "tdes_clk", .id = 14 },
+ { .n = "sha_clk", .id = 15 },
+ { .n = "matrix1_clk", .id = 17 },
+ { .n = "hsmc_clk", .id = 22 },
+ { .n = "pioA_clk", .id = 23 },
+ { .n = "pioB_clk", .id = 24 },
+ { .n = "pioC_clk", .id = 25 },
+ { .n = "pioE_clk", .id = 26 },
+ { .n = "uart0_clk", .id = 27 },
+ { .n = "uart1_clk", .id = 28 },
+ { .n = "usart2_clk", .id = 29 },
+ { .n = "usart3_clk", .id = 30 },
+ { .n = "usart4_clk", .id = 31 },
+ { .n = "twi0_clk", .id = 32 },
+ { .n = "twi1_clk", .id = 33 },
+ { .n = "twi2_clk", .id = 34 },
+ { .n = "mci0_clk", .id = 35 },
+ { .n = "mci1_clk", .id = 36 },
+ { .n = "spi0_clk", .id = 37 },
+ { .n = "spi1_clk", .id = 38 },
+ { .n = "spi2_clk", .id = 39 },
+ { .n = "tcb0_clk", .id = 40 },
+ { .n = "tcb1_clk", .id = 41 },
+ { .n = "tcb2_clk", .id = 42 },
+ { .n = "pwm_clk", .id = 43 },
+ { .n = "adc_clk", .id = 44 },
+ { .n = "dbgu_clk", .id = 45 },
+ { .n = "uhphs_clk", .id = 46 },
+ { .n = "udphs_clk", .id = 47 },
+ { .n = "ssc0_clk", .id = 48 },
+ { .n = "ssc1_clk", .id = 49 },
+ { .n = "trng_clk", .id = 53 },
+ { .n = "macb0_clk", .id = 54 },
+ { .n = "macb1_clk", .id = 55 },
+ { .n = "fuse_clk", .id = 57 },
+ { .n = "securam_clk", .id = 59 },
+ { .n = "smd_clk", .id = 61 },
+ { .n = "twi3_clk", .id = 62 },
+ { .n = "catb_clk", .id = 63 },
+};
+
+static const struct {
+ char *n;
+ u8 id;
+} sama5d4_periphck[] = {
+ { .n = "dma0_clk", .id = 8 },
+ { .n = "cpkcc_clk", .id = 10 },
+ { .n = "aesb_clk", .id = 13 },
+ { .n = "mpddr_clk", .id = 16 },
+ { .n = "matrix0_clk", .id = 18 },
+ { .n = "vdec_clk", .id = 19 },
+ { .n = "dma1_clk", .id = 50 },
+ { .n = "lcdc_clk", .id = 51 },
+ { .n = "isi_clk", .id = 52 },
+};
+
+static void __init sama5d4_pmc_setup(struct device_node *np)
+{
+ struct clk_range range = CLK_RANGE(0, 0);
+ const char *slck_name, *mainxtal_name;
+ struct pmc_data *sama5d4_pmc;
+ const char *parent_names[5];
+ struct regmap *regmap;
+ struct clk *hw;
+ int i;
+ bool bypass;
+
+ i = of_property_match_string(np, "clock-names", "slow_clk");
+ if (i < 0)
+ return;
+
+ slck_name = of_clk_get_parent_name(np, i);
+
+ i = of_property_match_string(np, "clock-names", "main_xtal");
+ if (i < 0)
+ return;
+ mainxtal_name = of_clk_get_parent_name(np, i);
+
+ regmap = syscon_node_to_regmap(np);
+ if (IS_ERR(regmap))
+ return;
+
+ sama5d4_pmc = pmc_data_allocate(PMC_MCK2 + 1,
+ nck(sama5d4_systemck),
+ nck(sama5d4_periph32ck), 0);
+ if (!sama5d4_pmc)
+ return;
+
+ hw = at91_clk_register_main_rc_osc(regmap, "main_rc_osc", 12000000,
+ 100000000);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ bypass = of_property_read_bool(np, "atmel,osc-bypass");
+
+ hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name,
+ bypass);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ parent_names[0] = "main_rc_osc";
+ parent_names[1] = "main_osc";
+ hw = at91_clk_register_sam9x5_main(regmap, "mainck", parent_names, 2);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ hw = at91_clk_register_pll(regmap, "pllack", "mainck", 0,
+ &sama5d3_pll_layout, &plla_characteristics);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ hw = at91_clk_register_plldiv(regmap, "plladivck", "pllack");
+ if (IS_ERR(hw))
+ goto err_free;
+
+ hw = at91_clk_register_utmi(regmap, NULL, "utmick", "mainck");
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama5d4_pmc->chws[PMC_UTMI] = hw;
+
+ parent_names[0] = slck_name;
+ parent_names[1] = "mainck";
+ parent_names[2] = "plladivck";
+ parent_names[3] = "utmick";
+ hw = at91_clk_register_master(regmap, "masterck", 4, parent_names,
+ &at91sam9x5_master_layout,
+ &mck_characteristics);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama5d4_pmc->chws[PMC_MCK] = hw;
+
+ hw = at91_clk_register_h32mx(regmap, "h32mxck", "masterck");
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama5d4_pmc->chws[PMC_MCK2] = hw;
+
+ parent_names[0] = "plladivck";
+ parent_names[1] = "utmick";
+ hw = at91sam9x5_clk_register_usb(regmap, "usbck", parent_names, 2);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ parent_names[0] = "plladivck";
+ parent_names[1] = "utmick";
+ hw = at91sam9x5_clk_register_smd(regmap, "smdclk", parent_names, 2);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ parent_names[0] = slck_name;
+ parent_names[1] = "mainck";
+ parent_names[2] = "plladivck";
+ parent_names[3] = "utmick";
+ parent_names[4] = "mck";
+ for (i = 0; i < 3; i++) {
+ char *name;
+
+ name = xasprintf("prog%d", i);
+
+ hw = at91_clk_register_programmable(regmap, name,
+ parent_names, 5, i,
+ &at91sam9x5_programmable_layout);
+ if (IS_ERR(hw))
+ goto err_free;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(sama5d4_systemck); i++) {
+ hw = at91_clk_register_system(regmap, sama5d4_systemck[i].n,
+ sama5d4_systemck[i].p,
+ sama5d4_systemck[i].id);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama5d4_pmc->shws[sama5d4_systemck[i].id] = hw;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(sama5d4_periphck); i++) {
+ hw = at91_clk_register_sam9x5_peripheral(regmap,
+ sama5d4_periphck[i].n,
+ "masterck",
+ sama5d4_periphck[i].id,
+ &range);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama5d4_pmc->phws[sama5d4_periphck[i].id] = hw;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(sama5d4_periph32ck); i++) {
+ hw = at91_clk_register_sam9x5_peripheral(regmap,
+ sama5d4_periph32ck[i].n,
+ "h32mxck",
+ sama5d4_periph32ck[i].id,
+ &range);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama5d4_pmc->phws[sama5d4_periph32ck[i].id] = hw;
+ }
+
+ of_clk_add_provider(np, of_clk_hw_pmc_get, sama5d4_pmc);
+
+ return;
+
+err_free:
+ pmc_data_free(sama5d4_pmc);
+}
+CLK_OF_DECLARE_DRIVER(sama5d4_pmc, "atmel,sama5d4-pmc", sama5d4_pmc_setup);
diff --git a/drivers/clk/clk-bulk.c b/drivers/clk/clk-bulk.c
new file mode 100644
index 0000000000..ddbe32f9c2
--- /dev/null
+++ b/drivers/clk/clk-bulk.c
@@ -0,0 +1,102 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2017 NXP
+ *
+ * Dong Aisheng <aisheng.dong@nxp.com>
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <malloc.h>
+#include <stringlist.h>
+#include <complete.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/clk/clk-conf.h>
+
+void clk_bulk_put(int num_clks, struct clk_bulk_data *clks)
+{
+ while (--num_clks >= 0) {
+ clk_put(clks[num_clks].clk);
+ clks[num_clks].clk = NULL;
+ }
+}
+EXPORT_SYMBOL_GPL(clk_bulk_put);
+
+int __must_check clk_bulk_get(struct device_d *dev, int num_clks,
+ struct clk_bulk_data *clks)
+{
+ int ret;
+ int i;
+
+ for (i = 0; i < num_clks; i++)
+ clks[i].clk = NULL;
+
+ for (i = 0; i < num_clks; i++) {
+ clks[i].clk = clk_get(dev, clks[i].id);
+ if (IS_ERR(clks[i].clk)) {
+ ret = PTR_ERR(clks[i].clk);
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "Failed to get clk '%s': %d\n",
+ clks[i].id, ret);
+ clks[i].clk = NULL;
+ goto err;
+ }
+ }
+
+ return 0;
+
+err:
+ clk_bulk_put(i, clks);
+
+ return ret;
+}
+EXPORT_SYMBOL(clk_bulk_get);
+
+/**
+ * clk_bulk_disable - gate a set of clocks
+ * @num_clks: the number of clk_bulk_data
+ * @clks: the clk_bulk_data table being gated
+ *
+ * clk_bulk_disable must not sleep, which differentiates it from
+ * clk_bulk_unprepare. clk_bulk_disable must be called before
+ * clk_bulk_unprepare.
+ */
+void clk_bulk_disable(int num_clks, const struct clk_bulk_data *clks)
+{
+
+ while (--num_clks >= 0)
+ clk_disable(clks[num_clks].clk);
+}
+EXPORT_SYMBOL_GPL(clk_bulk_disable);
+
+/**
+ * clk_bulk_enable - ungate a set of clocks
+ * @num_clks: the number of clk_bulk_data
+ * @clks: the clk_bulk_data table being ungated
+ *
+ * clk_bulk_enable must not sleep
+ * Returns 0 on success, -EERROR otherwise.
+ */
+int __must_check clk_bulk_enable(int num_clks, const struct clk_bulk_data *clks)
+{
+ int ret;
+ int i;
+
+ for (i = 0; i < num_clks; i++) {
+ ret = clk_enable(clks[i].clk);
+ if (ret) {
+ pr_err("Failed to enable clk '%s': %d\n",
+ clks[i].id, ret);
+ goto err;
+ }
+ }
+
+ return 0;
+
+err:
+ clk_bulk_disable(i, clks);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(clk_bulk_enable);
diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c
index abdc415272..f67a5c4d98 100644
--- a/drivers/clk/clkdev.c
+++ b/drivers/clk/clkdev.c
@@ -189,11 +189,6 @@ struct clk *clk_get(struct device_d *dev, const char *con_id)
}
EXPORT_SYMBOL(clk_get);
-void clk_put(struct clk *clk)
-{
-}
-EXPORT_SYMBOL(clk_put);
-
void clkdev_add(struct clk_lookup *cl)
{
if (cl->dev_id)
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 7b04663d2e..b0502c3036 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -69,7 +69,6 @@ config CLOCKSOURCE_ROCKCHIP
config CLOCKSOURCE_ATMEL_PIT
bool
- depends on SOC_AT91SAM9 || SOC_SAMA5
config CLOCKSOURCE_ARMV8_TIMER
bool
diff --git a/drivers/clocksource/efi_x86.c b/drivers/clocksource/efi_x86.c
index 4d2657ea1d..f8d3ff8a43 100644
--- a/drivers/clocksource/efi_x86.c
+++ b/drivers/clocksource/efi_x86.c
@@ -6,7 +6,7 @@
#include <clock.h>
#ifdef __x86_64__
-uint64_t ticks_read(void)
+static uint64_t ticks_read(void)
{
uint64_t a, d;
@@ -15,7 +15,7 @@ uint64_t ticks_read(void)
return (d << 32) | a;
}
#else
-uint64_t ticks_read(void)
+static uint64_t ticks_read(void)
{
uint64_t val;
diff --git a/drivers/efi/efi-device.c b/drivers/efi/efi-device.c
index 5cc68fb781..305d337aab 100644
--- a/drivers/efi/efi-device.c
+++ b/drivers/efi/efi-device.c
@@ -32,7 +32,7 @@
#include <efi/efi-device.h>
#include <linux/err.h>
-int efi_locate_handle(enum efi_locate_search_type search_type,
+static int efi_locate_handle(enum efi_locate_search_type search_type,
efi_guid_t *protocol,
void *search_key,
unsigned long *no_handles,
diff --git a/drivers/hab/habv3.c b/drivers/hab/habv3.c
index 47d3caf864..f3f94bc44c 100644
--- a/drivers/hab/habv3.c
+++ b/drivers/hab/habv3.c
@@ -10,9 +10,11 @@
*/
#define pr_fmt(fmt) "HABv3: " fmt
+#include <init.h>
#include <common.h>
#include <hab.h>
#include <io.h>
+#include <mach/generic.h>
struct hab_status {
u8 value;
@@ -55,7 +57,7 @@ static struct hab_status hab_status[] = {
{ 0x8e, "algorithm type is either invalid or ortherwise unsupported" },
};
-int imx_habv3_get_status(uint32_t status)
+static int imx_habv3_get_status(uint32_t status)
{
int i;
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
index 56259d82d4..fc314ec9c6 100644
--- a/drivers/i2c/Kconfig
+++ b/drivers/i2c/Kconfig
@@ -16,3 +16,6 @@ config I2C_MUX
source drivers/i2c/muxes/Kconfig
endif
+
+config I2C_IMX_EARLY
+ bool
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 8dccc38379..61d7c86e76 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -1,6 +1,7 @@
obj-$(CONFIG_I2C_AT91) += i2c-at91.o
obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o
obj-$(CONFIG_I2C_IMX) += i2c-imx.o
+lwl-$(CONFIG_I2C_IMX_EARLY) += i2c-imx-early.o
obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o
obj-$(CONFIG_I2C_OMAP) += i2c-omap.o
obj-$(CONFIG_I2C_TEGRA) += i2c-tegra.o
diff --git a/drivers/i2c/busses/i2c-imx-early.c b/drivers/i2c/busses/i2c-imx-early.c
new file mode 100644
index 0000000000..d67226441e
--- /dev/null
+++ b/drivers/i2c/busses/i2c-imx-early.c
@@ -0,0 +1,310 @@
+/*
+ * Copyright 2013 GE Intelligent Platforms, Inc
+ * Copyright 2006,2009 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Early I2C support functions to read SPD data or board
+ * information.
+ * Based on U-Boot drivers/i2c/fsl_i2c.c
+ */
+#include <common.h>
+#include <i2c/i2c.h>
+#include <i2c/i2c-early.h>
+
+#include "i2c-imx.h"
+
+struct fsl_i2c {
+ void __iomem *regs;
+ unsigned int i2cr_ien_opcode;
+ unsigned int i2sr_clr_opcode;
+ unsigned int ifdr;
+ unsigned int regshift;
+};
+
+static inline void fsl_i2c_write_reg(unsigned int val,
+ struct fsl_i2c *fsl_i2c,
+ unsigned int reg)
+{
+ reg <<= fsl_i2c->regshift;
+
+ writeb(val, fsl_i2c->regs + reg);
+}
+
+static inline unsigned char fsl_i2c_read_reg(struct fsl_i2c *fsl_i2c,
+ unsigned int reg)
+{
+ reg <<= fsl_i2c->regshift;
+
+ return readb(fsl_i2c->regs + reg);
+}
+
+static int i2c_fsl_poll_status(struct fsl_i2c *fsl_i2c, uint8_t set, uint8_t clear)
+{
+ int timeout = 1000000;
+ uint8_t temp;
+
+ while (1) {
+ temp = fsl_i2c_read_reg(fsl_i2c, FSL_I2C_I2SR);
+ if (temp & set)
+ return 0;
+ if (~temp & clear)
+ return 0;
+
+ if (!--timeout) {
+ pr_debug("timeout waiting for status %s 0x%02x, cur status: 0x%02x\n",
+ set ? "set" : "clear",
+ set ? set : clear,
+ temp);
+ return -EIO;
+ }
+ }
+}
+
+static int i2c_fsl_bus_busy(struct fsl_i2c *fsl_i2c)
+{
+ return i2c_fsl_poll_status(fsl_i2c, I2SR_IBB, 0);
+}
+
+static int i2c_fsl_bus_idle(struct fsl_i2c *fsl_i2c)
+{
+ return i2c_fsl_poll_status(fsl_i2c, 0, I2SR_IBB);
+}
+
+static int i2c_fsl_trx_complete(struct fsl_i2c *fsl_i2c)
+{
+ int ret;
+
+ ret = i2c_fsl_poll_status(fsl_i2c, I2SR_IIF, 0);
+ if (ret)
+ return ret;
+
+ fsl_i2c_write_reg(fsl_i2c->i2sr_clr_opcode,
+ fsl_i2c, FSL_I2C_I2SR);
+
+ return 0;
+}
+
+static int i2c_fsl_acked(struct fsl_i2c *fsl_i2c)
+{
+ return i2c_fsl_poll_status(fsl_i2c, 0, I2SR_RXAK);
+}
+
+static int i2c_fsl_start(struct fsl_i2c *fsl_i2c)
+{
+ unsigned int temp = 0;
+ int ret;
+
+ fsl_i2c_write_reg(fsl_i2c->ifdr, fsl_i2c, FSL_I2C_IFDR);
+
+ /* Enable I2C controller */
+ fsl_i2c_write_reg(fsl_i2c->i2sr_clr_opcode,
+ fsl_i2c, FSL_I2C_I2SR);
+ fsl_i2c_write_reg(fsl_i2c->i2cr_ien_opcode,
+ fsl_i2c, FSL_I2C_I2CR);
+
+ /* Wait controller to be stable */
+ udelay(100);
+
+ /* Start I2C transaction */
+ temp = fsl_i2c_read_reg(fsl_i2c, FSL_I2C_I2CR);
+ temp |= I2CR_MSTA;
+ fsl_i2c_write_reg(temp, fsl_i2c, FSL_I2C_I2CR);
+
+ ret = i2c_fsl_bus_busy(fsl_i2c);
+ if (ret)
+ return -EAGAIN;
+
+ temp |= I2CR_MTX | I2CR_TXAK;
+ fsl_i2c_write_reg(temp, fsl_i2c, FSL_I2C_I2CR);
+
+ return ret;
+}
+
+static void i2c_fsl_stop(struct fsl_i2c *fsl_i2c)
+{
+ unsigned int temp = 0;
+
+ /* Stop I2C transaction */
+ temp = fsl_i2c_read_reg(fsl_i2c, FSL_I2C_I2CR);
+ temp &= ~(I2CR_MSTA | I2CR_MTX);
+ fsl_i2c_write_reg(temp, fsl_i2c, FSL_I2C_I2CR);
+ /* wait for the stop condition to be send, otherwise the i2c
+ * controller is disabled before the STOP is sent completely */
+
+ i2c_fsl_bus_idle(fsl_i2c);
+}
+
+static int i2c_fsl_send(struct fsl_i2c *fsl_i2c, uint8_t data)
+{
+ int ret;
+
+ pr_debug("%s send 0x%02x\n", __func__, data);
+
+ fsl_i2c_write_reg(data, fsl_i2c, FSL_I2C_I2DR);
+
+ ret = i2c_fsl_trx_complete(fsl_i2c);
+ if (ret) {
+ pr_debug("%s timeout 1\n", __func__);
+ return ret;
+ }
+
+ ret = i2c_fsl_acked(fsl_i2c);
+ if (ret) {
+ pr_debug("%s timeout 2\n", __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int i2c_fsl_write(struct fsl_i2c *fsl_i2c, struct i2c_msg *msg)
+{
+ int i, ret;
+
+ if (!(msg->flags & I2C_M_DATA_ONLY)) {
+ ret = i2c_fsl_send(fsl_i2c, msg->addr << 1);
+ if (ret)
+ return ret;
+ }
+
+ /* write data */
+ for (i = 0; i < msg->len; i++) {
+ ret = i2c_fsl_send(fsl_i2c, msg->buf[i]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int i2c_fsl_read(struct fsl_i2c *fsl_i2c, struct i2c_msg *msg)
+{
+ int i, ret;
+ unsigned int temp;
+
+ /* clear IIF */
+ fsl_i2c_write_reg(fsl_i2c->i2sr_clr_opcode,
+ fsl_i2c, FSL_I2C_I2SR);
+
+ if (!(msg->flags & I2C_M_DATA_ONLY)) {
+ ret = i2c_fsl_send(fsl_i2c, (msg->addr << 1) | 1);
+ if (ret)
+ return ret;
+ }
+
+ /* setup bus to read data */
+ temp = fsl_i2c_read_reg(fsl_i2c, FSL_I2C_I2CR);
+ temp &= ~I2CR_MTX;
+ if (msg->len - 1)
+ temp &= ~I2CR_TXAK;
+ fsl_i2c_write_reg(temp, fsl_i2c, FSL_I2C_I2CR);
+
+ fsl_i2c_read_reg(fsl_i2c, FSL_I2C_I2DR); /* dummy read */
+
+ /* read data */
+ for (i = 0; i < msg->len; i++) {
+ ret = i2c_fsl_trx_complete(fsl_i2c);
+ if (ret)
+ return ret;
+
+ if (i == (msg->len - 1)) {
+ i2c_fsl_stop(fsl_i2c);
+ } else if (i == (msg->len - 2)) {
+ temp = fsl_i2c_read_reg(fsl_i2c, FSL_I2C_I2CR);
+ temp |= I2CR_TXAK;
+ fsl_i2c_write_reg(temp, fsl_i2c, FSL_I2C_I2CR);
+ }
+ msg->buf[i] = fsl_i2c_read_reg(fsl_i2c, FSL_I2C_I2DR);
+ }
+ return 0;
+}
+
+/**
+ * i2c_fsl_xfer - transfer I2C messages on i.MX compatible I2C controllers
+ * @ctx: driver context pointer
+ * @msgs: pointer to I2C messages
+ * @num: number of messages to transfer
+ *
+ * This function transfers I2C messages on i.MX and compatible I2C controllers.
+ * If successful returns the number of messages transferred, otherwise a negative
+ * error code is returned.
+ */
+int i2c_fsl_xfer(void *ctx, struct i2c_msg *msgs, int num)
+{
+ struct fsl_i2c *fsl_i2c = ctx;
+ unsigned int i, temp;
+ int ret;
+
+ pr_debug("%s enter\n", __func__);
+
+ /* Start I2C transfer */
+ for (i = 0; i < 3; i++) {
+ ret = i2c_fsl_start(fsl_i2c);
+ if (!ret)
+ break;
+ if (ret == -EAGAIN)
+ continue;
+ return ret;
+ }
+
+ /* read/write data */
+ for (i = 0; i < num; i++) {
+ if (i && !(msgs[i].flags & I2C_M_DATA_ONLY)) {
+ temp = fsl_i2c_read_reg(fsl_i2c, FSL_I2C_I2CR);
+ temp |= I2CR_RSTA;
+ fsl_i2c_write_reg(temp, fsl_i2c, FSL_I2C_I2CR);
+
+ ret = i2c_fsl_bus_busy(fsl_i2c);
+ if (ret)
+ goto fail0;
+ }
+
+ /* write/read data */
+ if (msgs[i].flags & I2C_M_RD)
+ ret = i2c_fsl_read(fsl_i2c, &msgs[i]);
+ else
+ ret = i2c_fsl_write(fsl_i2c, &msgs[i]);
+ if (ret)
+ goto fail0;
+ }
+
+fail0:
+ /* Stop I2C transfer */
+ i2c_fsl_stop(fsl_i2c);
+
+ /* Disable I2C controller, and force our state to stopped */
+ temp = fsl_i2c->i2cr_ien_opcode ^ I2CR_IEN,
+ fsl_i2c_write_reg(temp, fsl_i2c, FSL_I2C_I2CR);
+
+ return (ret < 0) ? ret : num;
+}
+
+static struct fsl_i2c fsl_i2c;
+
+/**
+ * ls1046_i2c_init - Return a context pointer for accessing I2C on LS1046a
+ * @regs: The base address of the I2C controller to access
+ *
+ * This function returns a context pointer suitable to transfer I2C messages
+ * using i2c_fsl_xfer.
+ */
+void *ls1046_i2c_init(void __iomem *regs)
+{
+ fsl_i2c.regs = regs;
+ fsl_i2c.regshift = 0;
+ fsl_i2c.i2cr_ien_opcode = I2CR_IEN_OPCODE_0;
+ fsl_i2c.i2sr_clr_opcode = I2SR_CLR_OPCODE_W1C;
+ /* Divider for ~100kHz when coming from the ROM */
+ fsl_i2c.ifdr = 0x3e;
+
+ return &fsl_i2c;
+}
diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c
index 67937da73a..4c7346063c 100644
--- a/drivers/i2c/busses/i2c-imx.c
+++ b/drivers/i2c/busses/i2c-imx.c
@@ -49,61 +49,7 @@
#include <i2c/i2c.h>
#include <mach/clock.h>
-/* This will be the driver name */
-#define DRIVER_NAME "i2c-fsl"
-
-/* Default value */
-#define FSL_I2C_BIT_RATE 100000 /* 100kHz */
-
-/* IMX I2C registers:
- * the I2C register offset is different between SoCs,
- * to provid support for all these chips, split the
- * register offset into a fixed base address and a
- * variable shift value, then the full register offset
- * will be calculated by
- * reg_off = ( reg_base_addr << reg_shift)
- */
-#define FSL_I2C_IADR 0x00 /* i2c slave address */
-#define FSL_I2C_IFDR 0x01 /* i2c frequency divider */
-#define FSL_I2C_I2CR 0x02 /* i2c control */
-#define FSL_I2C_I2SR 0x03 /* i2c status */
-#define FSL_I2C_I2DR 0x04 /* i2c transfer data */
-#define FSL_I2C_DFSRR 0x14 /* i2c digital filter sampling rate */
-
-#define IMX_I2C_REGSHIFT 2
-#define VF610_I2C_REGSHIFT 0
-
-
-/* Bits of FSL I2C registers */
-#define I2SR_RXAK 0x01
-#define I2SR_IIF 0x02
-#define I2SR_SRW 0x04
-#define I2SR_IAL 0x10
-#define I2SR_IBB 0x20
-#define I2SR_IAAS 0x40
-#define I2SR_ICF 0x80
-#define I2CR_RSTA 0x04
-#define I2CR_TXAK 0x08
-#define I2CR_MTX 0x10
-#define I2CR_MSTA 0x20
-#define I2CR_IIEN 0x40
-#define I2CR_IEN 0x80
-
-/* register bits different operating codes definition:
- * 1) I2SR: Interrupt flags clear operation differ between SoCs:
- * - write zero to clear(w0c) INT flag on i.MX,
- * - but write one to clear(w1c) INT flag on Vybrid.
- * 2) I2CR: I2C module enable operation also differ between SoCs:
- * - set I2CR_IEN bit enable the module on i.MX,
- * - but clear I2CR_IEN bit enable the module on Vybrid.
- */
-#define I2SR_CLR_OPCODE_W0C 0x0
-#define I2SR_CLR_OPCODE_W1C (I2SR_IAL | I2SR_IIF)
-#define I2CR_IEN_OPCODE_0 0x0
-#define I2CR_IEN_OPCODE_1 I2CR_IEN
-
-#define I2C_PM_TIMEOUT 10 /* ms */
-
+#include "i2c-imx.h"
/*
* sorted list of clock divider, register value pairs
@@ -168,7 +114,6 @@ struct fsl_i2c_struct {
struct clk *clk;
struct i2c_adapter adapter;
unsigned int disable_delay;
- int stopped;
unsigned int ifdr; /* FSL_I2C_IFDR */
unsigned int dfsrr; /* FSL_I2C_DFSRR */
struct i2c_bus_recovery_info rinfo;
@@ -191,7 +136,6 @@ static inline unsigned char fsl_i2c_read_reg(struct fsl_i2c_struct *i2c_fsl,
return readb(i2c_fsl->base + reg);
}
-#ifdef CONFIG_I2C_DEBUG
static void i2c_fsl_dump_reg(struct i2c_adapter *adapter)
{
struct fsl_i2c_struct *i2c_fsl = to_fsl_i2c_struct(adapter);
@@ -213,54 +157,51 @@ static void i2c_fsl_dump_reg(struct i2c_adapter *adapter)
(reg_sr & I2SR_SRW ? 1 : 0), (reg_sr & I2SR_IIF ? 1 : 0),
(reg_sr & I2SR_RXAK ? 1 : 0));
}
-#else
-static inline void i2c_fsl_dump_reg(struct i2c_adapter *adapter)
-{
- return;
-}
-#endif
-
-static int i2c_fsl_bus_busy(struct i2c_adapter *adapter, int for_busy)
+static int i2c_fsl_poll_status(struct i2c_adapter *adapter, int timeout_ms,
+ uint8_t set, uint8_t clear)
{
struct fsl_i2c_struct *i2c_fsl = to_fsl_i2c_struct(adapter);
uint64_t start;
- unsigned int temp;
+ uint8_t temp;
start = get_time_ns();
while (1) {
temp = fsl_i2c_read_reg(i2c_fsl, FSL_I2C_I2SR);
- if (for_busy && (temp & I2SR_IBB))
- break;
- if (!for_busy && !(temp & I2SR_IBB))
- break;
- if (is_timeout(start, 500 * MSECOND)) {
- dev_err(&adapter->dev,
- "<%s> timeout waiting for I2C bus %s\n",
- __func__,for_busy ? "busy" : "not busy");
+ if (temp & set)
+ return 0;
+ if (~temp & clear)
+ return 0;
+
+ if (is_timeout(start, timeout_ms * MSECOND)) {
+ dev_dbg(&adapter->dev,
+ "timeout waiting for status %s 0x%02x, cur status: 0x%02x\n",
+ set ? "set" : "clear",
+ set ? set : clear,
+ temp);
return -EIO;
}
}
+}
- return 0;
+static int i2c_fsl_bus_busy(struct i2c_adapter *adapter)
+{
+ return i2c_fsl_poll_status(adapter, 500, I2SR_IBB, 0);
+}
+
+static int i2c_fsl_bus_idle(struct i2c_adapter *adapter)
+{
+ return i2c_fsl_poll_status(adapter, 500, 0, I2SR_IBB);
}
static int i2c_fsl_trx_complete(struct i2c_adapter *adapter)
{
struct fsl_i2c_struct *i2c_fsl = to_fsl_i2c_struct(adapter);
- uint64_t start;
-
- start = get_time_ns();
- while (1) {
- unsigned int reg = fsl_i2c_read_reg(i2c_fsl, FSL_I2C_I2SR);
- if (reg & I2SR_IIF)
- break;
+ int ret;
- if (is_timeout(start, 100 * MSECOND)) {
- dev_err(&adapter->dev, "<%s> TXR timeout\n", __func__);
- return -EIO;
- }
- }
+ ret = i2c_fsl_poll_status(adapter, 100, I2SR_IIF, 0);
+ if (ret)
+ return ret;
fsl_i2c_write_reg(i2c_fsl->hwdata->i2sr_clr_opcode,
i2c_fsl, FSL_I2C_I2SR);
@@ -270,22 +211,7 @@ static int i2c_fsl_trx_complete(struct i2c_adapter *adapter)
static int i2c_fsl_acked(struct i2c_adapter *adapter)
{
- struct fsl_i2c_struct *i2c_fsl = to_fsl_i2c_struct(adapter);
- uint64_t start;
-
- start = get_time_ns();
- while (1) {
- unsigned int reg = fsl_i2c_read_reg(i2c_fsl, FSL_I2C_I2SR);
- if (!(reg & I2SR_RXAK))
- break;
-
- if (is_timeout(start, MSECOND)) {
- dev_dbg(&adapter->dev, "<%s> No ACK\n", __func__);
- return -EIO;
- }
- }
-
- return 0;
+ return i2c_fsl_poll_status(adapter, 1, 0, I2SR_RXAK);
}
static int i2c_fsl_start(struct i2c_adapter *adapter)
@@ -314,7 +240,7 @@ static int i2c_fsl_start(struct i2c_adapter *adapter)
temp |= I2CR_MSTA;
fsl_i2c_write_reg(temp, i2c_fsl, FSL_I2C_I2CR);
- result = i2c_fsl_bus_busy(adapter, 1);
+ result = i2c_fsl_bus_busy(adapter);
if (result) {
result = i2c_recover_bus(&i2c_fsl->adapter);
if (result)
@@ -322,8 +248,6 @@ static int i2c_fsl_start(struct i2c_adapter *adapter)
return -EAGAIN;
}
- i2c_fsl->stopped = 0;
-
temp |= I2CR_MTX | I2CR_TXAK;
fsl_i2c_write_reg(temp, i2c_fsl, FSL_I2C_I2CR);
@@ -335,24 +259,20 @@ static void i2c_fsl_stop(struct i2c_adapter *adapter)
struct fsl_i2c_struct *i2c_fsl = to_fsl_i2c_struct(adapter);
unsigned int temp = 0;
- if (!i2c_fsl->stopped) {
- /* Stop I2C transaction */
- temp = fsl_i2c_read_reg(i2c_fsl, FSL_I2C_I2CR);
- temp &= ~(I2CR_MSTA | I2CR_MTX);
- fsl_i2c_write_reg(temp, i2c_fsl, FSL_I2C_I2CR);
- /* wait for the stop condition to be send, otherwise the i2c
- * controller is disabled before the STOP is sent completely */
- i2c_fsl->stopped = i2c_fsl_bus_busy(adapter, 0) ? 0 : 1;
- }
-
- if (!i2c_fsl->stopped) {
- i2c_fsl_bus_busy(adapter, 0);
- i2c_fsl->stopped = 1;
- }
+ /* Stop I2C transaction */
+ temp = fsl_i2c_read_reg(i2c_fsl, FSL_I2C_I2CR);
+ if (!(temp & I2CR_MSTA))
+ return;
- /* Disable I2C controller, and force our state to stopped */
- temp = i2c_fsl->hwdata->i2cr_ien_opcode ^ I2CR_IEN,
+ temp &= ~(I2CR_MSTA | I2CR_MTX);
fsl_i2c_write_reg(temp, i2c_fsl, FSL_I2C_I2CR);
+ /* wait for the stop condition to be send, otherwise the i2c
+ * controller is disabled before the STOP is sent completely */
+
+ /* adding this delay helps on low bitrates */
+ udelay(i2c_fsl->disable_delay);
+
+ i2c_fsl_bus_idle(adapter);
}
#ifdef CONFIG_PPC
@@ -462,45 +382,44 @@ static void i2c_fsl_set_clk(struct fsl_i2c_struct *i2c_fsl,
}
#endif
-static int i2c_fsl_write(struct i2c_adapter *adapter, struct i2c_msg *msgs)
+static int i2c_fsl_send(struct i2c_adapter *adapter, uint8_t data)
{
struct fsl_i2c_struct *i2c_fsl = to_fsl_i2c_struct(adapter);
- int i, result;
+ int result;
- if ( !(msgs->flags & I2C_M_DATA_ONLY) ) {
- dev_dbg(&adapter->dev,
- "<%s> write slave address: addr=0x%02x\n",
- __func__, msgs->addr << 1);
+ dev_dbg(&adapter->dev, "<%s> send 0x%02x\n", __func__, data);
- /* write slave address */
- fsl_i2c_write_reg(msgs->addr << 1, i2c_fsl, FSL_I2C_I2DR);
+ fsl_i2c_write_reg(data, i2c_fsl, FSL_I2C_I2DR);
- result = i2c_fsl_trx_complete(adapter);
- if (result)
- return result;
- result = i2c_fsl_acked(adapter);
+ result = i2c_fsl_trx_complete(adapter);
+ if (result)
+ return result;
+
+ return i2c_fsl_acked(adapter);
+}
+
+static int i2c_fsl_write(struct i2c_adapter *adapter, struct i2c_msg *msg)
+{
+ int i, result;
+
+ if (!(msg->flags & I2C_M_DATA_ONLY)) {
+ result = i2c_fsl_send(adapter, msg->addr << 1);
if (result)
return result;
}
/* write data */
- for (i = 0; i < msgs->len; i++) {
- dev_dbg(&adapter->dev,
- "<%s> write byte: B%d=0x%02X\n",
- __func__, i, msgs->buf[i]);
- fsl_i2c_write_reg(msgs->buf[i], i2c_fsl, FSL_I2C_I2DR);
-
- result = i2c_fsl_trx_complete(adapter);
- if (result)
- return result;
- result = i2c_fsl_acked(adapter);
+ for (i = 0; i < msg->len; i++) {
+ result = i2c_fsl_send(adapter, msg->buf[i]);
if (result)
return result;
}
+
return 0;
}
-static int i2c_fsl_read(struct i2c_adapter *adapter, struct i2c_msg *msgs)
+static int i2c_fsl_read(struct i2c_adapter *adapter, struct i2c_msg *msg,
+ bool is_last)
{
struct fsl_i2c_struct *i2c_fsl = to_fsl_i2c_struct(adapter);
int i, result;
@@ -510,18 +429,8 @@ static int i2c_fsl_read(struct i2c_adapter *adapter, struct i2c_msg *msgs)
fsl_i2c_write_reg(i2c_fsl->hwdata->i2sr_clr_opcode,
i2c_fsl, FSL_I2C_I2SR);
- if ( !(msgs->flags & I2C_M_DATA_ONLY) ) {
- dev_dbg(&adapter->dev,
- "<%s> write slave address: addr=0x%02x\n",
- __func__, (msgs->addr << 1) | 0x01);
-
- /* write slave address */
- fsl_i2c_write_reg((msgs->addr << 1) | 0x01, i2c_fsl, FSL_I2C_I2DR);
-
- result = i2c_fsl_trx_complete(adapter);
- if (result)
- return result;
- result = i2c_fsl_acked(adapter);
+ if (!(msg->flags & I2C_M_DATA_ONLY)) {
+ result = i2c_fsl_send(adapter, (msg->addr << 1) | 1);
if (result)
return result;
}
@@ -529,43 +438,29 @@ static int i2c_fsl_read(struct i2c_adapter *adapter, struct i2c_msg *msgs)
/* setup bus to read data */
temp = fsl_i2c_read_reg(i2c_fsl, FSL_I2C_I2CR);
temp &= ~I2CR_MTX;
- if (msgs->len - 1)
+ if (msg->len - 1)
temp &= ~I2CR_TXAK;
fsl_i2c_write_reg(temp, i2c_fsl, FSL_I2C_I2CR);
fsl_i2c_read_reg(i2c_fsl, FSL_I2C_I2DR); /* dummy read */
/* read data */
- for (i = 0; i < msgs->len; i++) {
+ for (i = 0; i < msg->len; i++) {
result = i2c_fsl_trx_complete(adapter);
if (result)
return result;
- if (i == (msgs->len - 1)) {
- /*
- * It must generate STOP before read I2DR to prevent
- * controller from generating another clock cycle
- */
- temp = fsl_i2c_read_reg(i2c_fsl, FSL_I2C_I2CR);
- temp &= ~(I2CR_MSTA | I2CR_MTX);
- fsl_i2c_write_reg(temp, i2c_fsl, FSL_I2C_I2CR);
-
- /*
- * adding this delay helps on low bitrates
- */
- udelay(i2c_fsl->disable_delay);
-
- i2c_fsl_bus_busy(adapter, 0);
- i2c_fsl->stopped = 1;
- } else if (i == (msgs->len - 2)) {
+ if (is_last && i == msg->len - 1) {
+ i2c_fsl_stop(adapter);
+ } else if (i == (msg->len - 2)) {
temp = fsl_i2c_read_reg(i2c_fsl, FSL_I2C_I2CR);
temp |= I2CR_TXAK;
fsl_i2c_write_reg(temp, i2c_fsl, FSL_I2C_I2CR);
}
- msgs->buf[i] = fsl_i2c_read_reg(i2c_fsl, FSL_I2C_I2DR);
+ msg->buf[i] = fsl_i2c_read_reg(i2c_fsl, FSL_I2C_I2DR);
dev_dbg(&adapter->dev, "<%s> read byte: B%d=0x%02X\n",
- __func__, i, msgs->buf[i]);
+ __func__, i, msg->buf[i]);
}
return 0;
}
@@ -594,7 +489,7 @@ static int i2c_fsl_xfer(struct i2c_adapter *adapter,
temp |= I2CR_RSTA;
fsl_i2c_write_reg(temp, i2c_fsl, FSL_I2C_I2CR);
- result = i2c_fsl_bus_busy(adapter, 1);
+ result = i2c_fsl_bus_busy(adapter);
if (result)
goto fail0;
}
@@ -602,7 +497,7 @@ static int i2c_fsl_xfer(struct i2c_adapter *adapter,
/* write/read data */
if (msgs[i].flags & I2C_M_RD)
- result = i2c_fsl_read(adapter, &msgs[i]);
+ result = i2c_fsl_read(adapter, &msgs[i], i == num - 1);
else
result = i2c_fsl_write(adapter, &msgs[i]);
if (result)
@@ -613,6 +508,10 @@ fail0:
/* Stop I2C transfer */
i2c_fsl_stop(adapter);
+ /* Disable I2C controller, and force our state to stopped */
+ temp = i2c_fsl->hwdata->i2cr_ien_opcode ^ I2CR_IEN,
+ fsl_i2c_write_reg(temp, i2c_fsl, FSL_I2C_I2CR);
+
return (result < 0) ? result : num;
}
@@ -664,6 +563,7 @@ static int __init i2c_fsl_probe(struct device_d *pdev)
struct fsl_i2c_struct *i2c_fsl;
struct i2c_platform_data *pdata;
int ret;
+ int bitrate;
pdata = pdev->platform_data;
@@ -705,10 +605,12 @@ static int __init i2c_fsl_probe(struct device_d *pdev)
i2c_fsl->dfsrr = -1;
/* Set up clock divider */
+ bitrate = 100000;
+ of_property_read_u32(pdev->device_node, "clock-frequency", &bitrate);
if (pdata && pdata->bitrate)
- i2c_fsl_set_clk(i2c_fsl, pdata->bitrate);
- else
- i2c_fsl_set_clk(i2c_fsl, FSL_I2C_BIT_RATE);
+ bitrate = pdata->bitrate;
+
+ i2c_fsl_set_clk(i2c_fsl, bitrate);
/* Set up chip registers to defaults */
fsl_i2c_write_reg(i2c_fsl->hwdata->i2cr_ien_opcode ^ I2CR_IEN,
@@ -753,9 +655,7 @@ static __maybe_unused struct of_device_id imx_i2c_dt_ids[] = {
static struct driver_d i2c_fsl_driver = {
.probe = i2c_fsl_probe,
- .name = DRIVER_NAME,
-#ifndef CONFIG_PPC
+ .name = "i2c-fsl",
.of_compatible = DRV_OF_COMPAT(imx_i2c_dt_ids),
-#endif
};
coredevice_platform_driver(i2c_fsl_driver);
diff --git a/drivers/i2c/busses/i2c-imx.h b/drivers/i2c/busses/i2c-imx.h
new file mode 100644
index 0000000000..3e3e1317f2
--- /dev/null
+++ b/drivers/i2c/busses/i2c-imx.h
@@ -0,0 +1,52 @@
+#ifndef I2C_IMX_H
+#define I2C_IMX_H
+
+/*
+ * IMX I2C registers:
+ * the I2C register offset is different between SoCs, to provide support for
+ * all these chips, split the register offset into a fixed base address and a
+ * variable shift value, then the full register offset will be calculated by:
+ * reg_off = reg_base_addr << reg_shift
+ */
+#define FSL_I2C_IADR 0x00 /* i2c slave address */
+#define FSL_I2C_IFDR 0x01 /* i2c frequency divider */
+#define FSL_I2C_I2CR 0x02 /* i2c control */
+#define FSL_I2C_I2SR 0x03 /* i2c status */
+#define FSL_I2C_I2DR 0x04 /* i2c transfer data */
+#define FSL_I2C_DFSRR 0x05 /* i2c digital filter sampling rate */
+
+#define IMX_I2C_REGSHIFT 2
+#define VF610_I2C_REGSHIFT 0
+
+/* Bits of FSL I2C registers */
+#define I2SR_RXAK 0x01
+#define I2SR_IIF 0x02
+#define I2SR_SRW 0x04
+#define I2SR_IAL 0x10
+#define I2SR_IBB 0x20
+#define I2SR_IAAS 0x40
+#define I2SR_ICF 0x80
+#define I2CR_RSTA 0x04
+#define I2CR_TXAK 0x08
+#define I2CR_MTX 0x10
+#define I2CR_MSTA 0x20
+#define I2CR_IIEN 0x40
+#define I2CR_IEN 0x80
+
+/*
+ * register bits different operating codes definition:
+ *
+ * 1) I2SR: Interrupt flags clear operation differ between SoCs:
+ * - write zero to clear(w0c) INT flag on i.MX,
+ * - but write one to clear(w1c) INT flag on Vybrid.
+ *
+ * 2) I2CR: I2C module enable operation also differ between SoCs:
+ * - set I2CR_IEN bit enable the module on i.MX,
+ * - but clear I2CR_IEN bit enable the module on Vybrid.
+ */
+#define I2SR_CLR_OPCODE_W0C 0x0
+#define I2SR_CLR_OPCODE_W1C (I2SR_IAL | I2SR_IIF)
+#define I2CR_IEN_OPCODE_0 0x0
+#define I2CR_IEN_OPCODE_1 I2CR_IEN
+
+#endif /* I2C_IMX_H */
diff --git a/drivers/mci/Kconfig b/drivers/mci/Kconfig
index 2075151d67..cd28fefa43 100644
--- a/drivers/mci/Kconfig
+++ b/drivers/mci/Kconfig
@@ -93,6 +93,9 @@ config MCI_IMX_ESDHC_PIO
help
mostly useful for debugging. Normally you should use DMA.
+config MCI_IMX_ESDHC_PBL
+ bool
+
config MCI_OMAP_HSMMC
bool "OMAP HSMMC"
depends on ARCH_OMAP4 || ARCH_OMAP3 || ARCH_AM33XX
diff --git a/drivers/mci/Makefile b/drivers/mci/Makefile
index fe2c8adbac..f6214c0cbb 100644
--- a/drivers/mci/Makefile
+++ b/drivers/mci/Makefile
@@ -4,6 +4,7 @@ obj-$(CONFIG_MCI_BCM283X) += mci-bcm2835.o
obj-$(CONFIG_MCI_DOVE) += dove-sdhci.o
obj-$(CONFIG_MCI_IMX) += imx.o
obj-$(CONFIG_MCI_IMX_ESDHC) += imx-esdhc.o
+pbl-$(CONFIG_MCI_IMX_ESDHC_PBL) += imx-esdhc-pbl.o
obj-$(CONFIG_MCI_MXS) += mxs.o
obj-$(CONFIG_MCI_OMAP_HSMMC) += omap_hsmmc.o
obj-$(CONFIG_MCI_PXA) += pxamci.o
diff --git a/drivers/mci/imx-esdhc-pbl.c b/drivers/mci/imx-esdhc-pbl.c
new file mode 100644
index 0000000000..f77530d310
--- /dev/null
+++ b/drivers/mci/imx-esdhc-pbl.c
@@ -0,0 +1,406 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "xload-esdhc: " fmt
+
+#include <common.h>
+#include <io.h>
+#include <mci.h>
+#include <linux/sizes.h>
+#ifdef CONFIG_ARCH_IMX
+#include <mach/atf.h>
+#include <mach/imx6-regs.h>
+#include <mach/imx8mq-regs.h>
+#include <mach/xload.h>
+#include <mach/imx-header.h>
+#endif
+#include "sdhci.h"
+#include "imx-esdhc.h"
+
+#define SECTOR_SIZE 512
+
+struct esdhc {
+ void __iomem *regs;
+ bool is_mx6;
+ bool is_be;
+};
+
+static uint32_t esdhc_read32(struct esdhc *esdhc, int reg)
+{
+ if (esdhc->is_be)
+ return in_be32(esdhc->regs + reg);
+ else
+ return readl(esdhc->regs + reg);
+}
+
+static void esdhc_write32(struct esdhc *esdhc, int reg, uint32_t val)
+{
+ if (esdhc->is_be)
+ out_be32(esdhc->regs + reg, val);
+ else
+ writel(val, esdhc->regs + reg);
+}
+
+static void __udelay(int us)
+{
+ volatile int i;
+
+ for (i = 0; i < us * 4; i++);
+}
+
+static u32 esdhc_xfertyp(struct mci_cmd *cmd, struct mci_data *data)
+{
+ u32 xfertyp = 0;
+
+ if (data)
+ xfertyp |= COMMAND_DPSEL | TRANSFER_MODE_MSBSEL |
+ TRANSFER_MODE_BCEN |TRANSFER_MODE_DTDSEL;
+
+ if (cmd->resp_type & MMC_RSP_CRC)
+ xfertyp |= COMMAND_CCCEN;
+ if (cmd->resp_type & MMC_RSP_OPCODE)
+ xfertyp |= COMMAND_CICEN;
+ if (cmd->resp_type & MMC_RSP_136)
+ xfertyp |= COMMAND_RSPTYP_136;
+ else if (cmd->resp_type & MMC_RSP_BUSY)
+ xfertyp |= COMMAND_RSPTYP_48_BUSY;
+ else if (cmd->resp_type & MMC_RSP_PRESENT)
+ xfertyp |= COMMAND_RSPTYP_48;
+
+ return COMMAND_CMD(cmd->cmdidx) | xfertyp;
+}
+
+static int esdhc_do_data(struct esdhc *esdhc, struct mci_data *data)
+{
+ char *buffer;
+ u32 databuf;
+ u32 size;
+ u32 irqstat;
+ u32 present;
+
+ buffer = data->dest;
+
+ size = data->blocksize * data->blocks;
+ irqstat = esdhc_read32(esdhc, SDHCI_INT_STATUS);
+
+ while (size) {
+ int i;
+ int timeout = 1000000;
+
+ while (1) {
+ present = esdhc_read32(esdhc, SDHCI_PRESENT_STATE) & PRSSTAT_BREN;
+ if (present)
+ break;
+ if (!--timeout) {
+ pr_err("read time out\n");
+ return -ETIMEDOUT;
+ }
+ }
+
+ for (i = 0; i < SECTOR_SIZE / sizeof(uint32_t); i++) {
+ databuf = esdhc_read32(esdhc, SDHCI_BUFFER);
+ *((u32 *)buffer) = databuf;
+ buffer += 4;
+ size -= 4;
+ }
+ }
+
+ return 0;
+}
+
+static int
+esdhc_send_cmd(struct esdhc *esdhc, struct mci_cmd *cmd, struct mci_data *data)
+{
+ u32 xfertyp, mixctrl;
+ u32 irqstat;
+ int ret;
+ int timeout;
+
+ esdhc_write32(esdhc, SDHCI_INT_STATUS, -1);
+
+ /* Wait at least 8 SD clock cycles before the next command */
+ __udelay(1);
+
+ if (data) {
+ unsigned long dest = (unsigned long)data->dest;
+
+ if (dest > 0xffffffff)
+ return -EINVAL;
+
+ /* Set up for a data transfer if we have one */
+ esdhc_write32(esdhc, SDHCI_DMA_ADDRESS, (u32)dest);
+ esdhc_write32(esdhc, SDHCI_BLOCK_SIZE__BLOCK_COUNT, data->blocks << 16 | SECTOR_SIZE);
+ }
+
+ /* Figure out the transfer arguments */
+ xfertyp = esdhc_xfertyp(cmd, data);
+
+ /* Send the command */
+ esdhc_write32(esdhc, SDHCI_ARGUMENT, cmd->cmdarg);
+
+ if (esdhc->is_mx6) {
+ /* write lower-half of xfertyp to mixctrl */
+ mixctrl = xfertyp & 0xFFFF;
+ /* Keep the bits 22-25 of the register as is */
+ mixctrl |= (esdhc_read32(esdhc, IMX_SDHCI_MIXCTRL) & (0xF << 22));
+ esdhc_write32(esdhc, IMX_SDHCI_MIXCTRL, mixctrl);
+ }
+
+ esdhc_write32(esdhc, SDHCI_TRANSFER_MODE__COMMAND, xfertyp);
+
+ /* Wait for the command to complete */
+ timeout = 10000;
+ while (!(esdhc_read32(esdhc, SDHCI_INT_STATUS) & IRQSTAT_CC)) {
+ __udelay(1);
+ if (!timeout--)
+ return -ETIMEDOUT;
+ }
+
+ irqstat = esdhc_read32(esdhc, SDHCI_INT_STATUS);
+ esdhc_write32(esdhc, SDHCI_INT_STATUS, irqstat);
+
+ if (irqstat & CMD_ERR)
+ return -EIO;
+
+ if (irqstat & IRQSTAT_CTOE)
+ return -ETIMEDOUT;
+
+ /* Copy the response to the response buffer */
+ cmd->response[0] = esdhc_read32(esdhc, SDHCI_RESPONSE_0);
+
+ /* Wait until all of the blocks are transferred */
+ if (data) {
+ ret = esdhc_do_data(esdhc, data);
+ if (ret)
+ return ret;
+ }
+
+ esdhc_write32(esdhc, SDHCI_INT_STATUS, -1);
+
+ /* Wait for the bus to be idle */
+ timeout = 10000;
+ while (esdhc_read32(esdhc, SDHCI_PRESENT_STATE) &
+ (PRSSTAT_CICHB | PRSSTAT_CIDHB | PRSSTAT_DLA)) {
+ __udelay(1);
+ if (!timeout--)
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int esdhc_read_blocks(struct esdhc *esdhc, void *dst, size_t len)
+{
+ struct mci_cmd cmd;
+ struct mci_data data;
+ u32 val;
+ int ret;
+
+ esdhc_write32(esdhc, SDHCI_INT_ENABLE,
+ IRQSTATEN_CC | IRQSTATEN_TC | IRQSTATEN_CINT | IRQSTATEN_CTOE |
+ IRQSTATEN_CCE | IRQSTATEN_CEBE | IRQSTATEN_CIE |
+ IRQSTATEN_DTOE | IRQSTATEN_DCE | IRQSTATEN_DEBE |
+ IRQSTATEN_DINT);
+
+ esdhc_write32(esdhc, IMX_SDHCI_WML, 0x0);
+
+ val = esdhc_read32(esdhc, SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET);
+ val |= SYSCTL_HCKEN | SYSCTL_IPGEN;
+ esdhc_write32(esdhc, SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET, val);
+
+ cmd.cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK;
+ cmd.cmdarg = 0;
+ cmd.resp_type = MMC_RSP_R1;
+
+ data.dest = dst;
+ data.blocks = len / SECTOR_SIZE;
+ data.blocksize = SECTOR_SIZE;
+ data.flags = MMC_DATA_READ;
+
+ ret = esdhc_send_cmd(esdhc, &cmd, &data);
+ if (ret) {
+ pr_debug("send command failed with %d\n", ret);
+ return ret;
+ }
+
+ cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
+ cmd.cmdarg = 0;
+ cmd.resp_type = MMC_RSP_R1b;
+
+ esdhc_send_cmd(esdhc, &cmd, NULL);
+
+ return 0;
+}
+
+#ifdef CONFIG_ARCH_IMX
+static int
+esdhc_start_image(struct esdhc *esdhc, ptrdiff_t address, ptrdiff_t entry, u32 offset)
+{
+
+ void *buf = (void *)address;
+ struct imx_flash_header_v2 *hdr;
+ int ret, len;
+ void __noreturn (*bb)(void);
+ unsigned int ofs;
+ int i, header_count = 1;
+
+ len = imx_image_size();
+ len = ALIGN(len, SECTOR_SIZE);
+
+ for (i = 0; i < header_count; i++) {
+ ret = esdhc_read_blocks(esdhc, buf,
+ offset + SZ_1K + SECTOR_SIZE);
+ if (ret)
+ return ret;
+
+ hdr = buf + offset + SZ_1K;
+
+ if (!is_imx_flash_header_v2(hdr)) {
+ pr_debug("IVT header not found on SD card. "
+ "Found tag: 0x%02x length: 0x%04x "
+ "version: %02x\n",
+ hdr->header.tag, hdr->header.length,
+ hdr->header.version);
+ return -EINVAL;
+ }
+
+ if (IS_ENABLED(CONFIG_ARCH_IMX8MQ) &&
+ hdr->boot_data.plugin & PLUGIN_HDMI_IMAGE) {
+ /*
+ * In images that include signed HDMI
+ * firmware, first v2 header would be
+ * dedicated to that and would not contain any
+ * useful for us information. In order for us
+ * to pull the rest of the bootloader image
+ * in, we need to re-read header from SD/MMC,
+ * this time skipping anything HDMI firmware
+ * related.
+ */
+ offset += hdr->boot_data.size + hdr->header.length;
+ header_count++;
+ }
+ }
+
+ pr_debug("Check ok, loading image\n");
+
+ ofs = offset + hdr->entry - hdr->boot_data.start;
+
+ if (entry != address) {
+ /*
+ * Passing entry different from address is interpreted
+ * as a request to place the image such that its entry
+ * point would be exactly at 'entry', that is:
+ *
+ * buf + ofs = entry
+ *
+ * solving the above for 'buf' gvies us the
+ * adjustement that needs to be made:
+ *
+ * buf = entry - ofs
+ *
+ */
+ if (WARN_ON(entry - ofs < address)) {
+ /*
+ * We want to make sure we won't try to place
+ * the start of the image before the beginning
+ * of the memory buffer we were given in
+ * address.
+ */
+ return -EINVAL;
+ }
+
+ buf = (void *)(entry - ofs);
+ }
+
+ ret = esdhc_read_blocks(esdhc, buf, offset + len);
+ if (ret) {
+ pr_err("Loading image failed with %d\n", ret);
+ return ret;
+ }
+
+ pr_debug("Image loaded successfully\n");
+
+ bb = buf + ofs;
+
+ bb();
+}
+
+/**
+ * imx6_esdhc_start_image - Load and start an image from USDHC controller
+ * @instance: The USDHC controller instance (0..4)
+ *
+ * This uses esdhc_start_image() to load an image from SD/MMC. It is
+ * assumed that the image is the currently running barebox image (This
+ * information is used to calculate the length of the image). The
+ * image is started afterwards.
+ *
+ * Return: If successful, this function does not return. A negative error
+ * code is returned when this function fails.
+ */
+int imx6_esdhc_start_image(int instance)
+{
+ struct esdhc esdhc;
+
+ switch (instance) {
+ case 0:
+ esdhc.regs = IOMEM(MX6_USDHC1_BASE_ADDR);
+ break;
+ case 1:
+ esdhc.regs = IOMEM(MX6_USDHC2_BASE_ADDR);
+ break;
+ case 2:
+ esdhc.regs = IOMEM(MX6_USDHC3_BASE_ADDR);
+ break;
+ case 3:
+ esdhc.regs = IOMEM(MX6_USDHC4_BASE_ADDR);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ esdhc.is_mx6 = 1;
+
+ return esdhc_start_image(&esdhc, 0x10000000, 0x10000000, 0);
+}
+
+/**
+ * imx8_esdhc_start_image - Load and start an image from USDHC controller
+ * @instance: The USDHC controller instance (0..2)
+ *
+ * This uses esdhc_start_image() to load an image from SD/MMC. It is
+ * assumed that the image is the currently running barebox image (This
+ * information is used to calculate the length of the image). The
+ * image is started afterwards.
+ *
+ * Return: If successful, this function does not return. A negative error
+ * code is returned when this function fails.
+ */
+int imx8_esdhc_start_image(int instance)
+{
+ struct esdhc esdhc;
+
+ switch (instance) {
+ case 0:
+ esdhc.regs = IOMEM(MX8MQ_USDHC1_BASE_ADDR);
+ break;
+ case 1:
+ esdhc.regs = IOMEM(MX8MQ_USDHC2_BASE_ADDR);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ esdhc.is_mx6 = 1;
+
+ return esdhc_start_image(&esdhc, MX8MQ_DDR_CSD1_BASE_ADDR,
+ MX8MQ_ATF_BL33_BASE_ADDR, SZ_32K);
+}
+#endif
diff --git a/drivers/mci/imx-esdhc.c b/drivers/mci/imx-esdhc.c
index a9c5440758..cedfb3db42 100644
--- a/drivers/mci/imx-esdhc.c
+++ b/drivers/mci/imx-esdhc.c
@@ -83,15 +83,6 @@
/* The IP supports HS400 mode */
#define ESDHC_FLAG_HS400 BIT(9)
-
-#define IMX_SDHCI_WML 0x44
-#define IMX_SDHCI_MIXCTRL 0x48
-#define IMX_SDHCI_DLL_CTRL 0x60
-#define IMX_SDHCI_MIX_CTRL_FBCLK_SEL (BIT(25))
-
-#define ESDHC_DMA_SYSCTL 0x40c /* Layerscape specific */
-#define ESDHC_SYSCTL_DMA_SNOOP BIT(6)
-
struct esdhc_soc_data {
u32 flags;
const char *clkidx;
diff --git a/drivers/mci/imx-esdhc.h b/drivers/mci/imx-esdhc.h
index 9003843abb..9b79346f90 100644
--- a/drivers/mci/imx-esdhc.h
+++ b/drivers/mci/imx-esdhc.h
@@ -58,6 +58,14 @@
#define PIO_TIMEOUT 100000
+#define IMX_SDHCI_WML 0x44
+#define IMX_SDHCI_MIXCTRL 0x48
+#define IMX_SDHCI_DLL_CTRL 0x60
+#define IMX_SDHCI_MIX_CTRL_FBCLK_SEL BIT(25)
+
+#define ESDHC_DMA_SYSCTL 0x40c /* Layerscape specific */
+#define ESDHC_SYSCTL_DMA_SNOOP BIT(6)
+
struct fsl_esdhc_cfg {
u32 esdhc_base;
u32 no_snoop;
diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c
index 30ed65f737..b1ff1b1eac 100644
--- a/drivers/mfd/syscon.c
+++ b/drivers/mfd/syscon.c
@@ -160,6 +160,20 @@ struct regmap *syscon_node_to_regmap(struct device_node *np)
return syscon->regmap;
}
+struct regmap *syscon_regmap_lookup_by_compatible(const char *s)
+{
+ struct device_node *syscon_np;
+ struct regmap *regmap;
+
+ syscon_np = of_find_compatible_node(NULL, NULL, s);
+ if (!syscon_np)
+ return ERR_PTR(-ENODEV);
+
+ regmap = syscon_node_to_regmap(syscon_np);
+
+ return regmap;
+}
+
static int syscon_probe(struct device_d *dev)
{
struct syscon *syscon;
diff --git a/drivers/net/e1000/main.c b/drivers/net/e1000/main.c
index 774e3d030f..f67c5d867b 100644
--- a/drivers/net/e1000/main.c
+++ b/drivers/net/e1000/main.c
@@ -3713,9 +3713,4 @@ static struct pci_driver e1000_eth_driver = {
.probe = e1000_probe,
.remove = e1000_remove,
};
-
-static int e1000_driver_init(void)
-{
- return pci_register_driver(&e1000_eth_driver);
-}
-device_initcall(e1000_driver_init);
+device_pci_driver(e1000_eth_driver);
diff --git a/drivers/net/efi-snp.c b/drivers/net/efi-snp.c
index 4e32513739..def2714bee 100644
--- a/drivers/net/efi-snp.c
+++ b/drivers/net/efi-snp.c
@@ -231,7 +231,7 @@ static int efi_snp_set_ethaddr(struct eth_device *edev, const unsigned char *adr
return 0;
}
-int efi_snp_probe(struct efi_device *efidev)
+static int efi_snp_probe(struct efi_device *efidev)
{
struct eth_device *edev;
struct efi_snp_priv *priv;
diff --git a/drivers/net/rtl8139.c b/drivers/net/rtl8139.c
index cfa34a2f2a..e1c57e6b7c 100644
--- a/drivers/net/rtl8139.c
+++ b/drivers/net/rtl8139.c
@@ -594,9 +594,4 @@ static struct pci_driver rtl8139_eth_driver = {
.id_table = rtl8139_pci_tbl,
.probe = rtl8139_probe,
};
-
-static int rtl8139_init(void)
-{
- return pci_register_driver(&rtl8139_eth_driver);
-}
-device_initcall(rtl8139_init);
+device_pci_driver(rtl8139_eth_driver);
diff --git a/drivers/net/rtl8169.c b/drivers/net/rtl8169.c
index ba257509ee..4e9823d424 100644
--- a/drivers/net/rtl8169.c
+++ b/drivers/net/rtl8169.c
@@ -544,9 +544,4 @@ static struct pci_driver rtl8169_eth_driver = {
.id_table = rtl8169_pci_tbl,
.probe = rtl8169_probe,
};
-
-static int rtl8169_init(void)
-{
- return pci_register_driver(&rtl8169_eth_driver);
-}
-device_initcall(rtl8169_init);
+device_pci_driver(rtl8169_eth_driver);
diff --git a/drivers/nvme/Kconfig b/drivers/nvme/Kconfig
new file mode 100644
index 0000000000..27ac9654ac
--- /dev/null
+++ b/drivers/nvme/Kconfig
@@ -0,0 +1,5 @@
+menu "NVME Support"
+
+source "drivers/nvme/host/Kconfig"
+
+endmenu
diff --git a/drivers/nvme/Makefile b/drivers/nvme/Makefile
new file mode 100644
index 0000000000..6d7d51c801
--- /dev/null
+++ b/drivers/nvme/Makefile
@@ -0,0 +1 @@
+obj-y += host/
diff --git a/drivers/nvme/host/Kconfig b/drivers/nvme/host/Kconfig
new file mode 100644
index 0000000000..8888c8900b
--- /dev/null
+++ b/drivers/nvme/host/Kconfig
@@ -0,0 +1,11 @@
+config NVME_CORE
+ bool
+
+config BLK_DEV_NVME
+ bool "NVM Express block device"
+ depends on PCI && BLOCK
+ select NVME_CORE
+ ---help---
+ The NVM Express driver is for solid state drives directly
+ connected to the PCI or PCI Express bus. If you know you
+ don't have one of these, it is safe to answer N.
diff --git a/drivers/nvme/host/Makefile b/drivers/nvme/host/Makefile
new file mode 100644
index 0000000000..9afbc0d2e1
--- /dev/null
+++ b/drivers/nvme/host/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0
+
+ccflags-y += -I$(src)
+
+obj-$(CONFIG_NVME_CORE) += nvme-core.o
+obj-$(CONFIG_BLK_DEV_NVME) += nvme.o
+
+nvme-core-y := core.o
+nvme-y += pci.o
diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
new file mode 100644
index 0000000000..e0984708b4
--- /dev/null
+++ b/drivers/nvme/host/core.c
@@ -0,0 +1,614 @@
+#include <common.h>
+
+#include "nvme.h"
+
+int __nvme_submit_sync_cmd(struct nvme_ctrl *ctrl,
+ struct nvme_command *cmd,
+ union nvme_result *result,
+ void *buffer, unsigned bufflen,
+ unsigned timeout, int qid)
+{
+ return ctrl->ops->submit_sync_cmd(ctrl, cmd, result, buffer, bufflen,
+ timeout, qid);
+}
+EXPORT_SYMBOL_GPL(__nvme_submit_sync_cmd);
+
+int nvme_submit_sync_cmd(struct nvme_ctrl *ctrl,
+ struct nvme_command *cmd,
+ void *buffer, unsigned bufflen)
+{
+ return __nvme_submit_sync_cmd(ctrl, cmd, NULL, buffer, bufflen, 0,
+ NVME_QID_ADMIN);
+}
+EXPORT_SYMBOL_GPL(nvme_sec_submit);
+
+static int nvme_identify_ctrl(struct nvme_ctrl *dev, struct nvme_id_ctrl **id)
+{
+ struct nvme_command c = { };
+ int error;
+
+ /* gcc-4.4.4 (at least) has issues with initializers and anon unions */
+ c.identify.opcode = nvme_admin_identify;
+ c.identify.cns = NVME_ID_CNS_CTRL;
+
+ *id = kmalloc(sizeof(struct nvme_id_ctrl), GFP_KERNEL);
+ if (!*id)
+ return -ENOMEM;
+
+ error = nvme_submit_sync_cmd(dev, &c, *id,
+ sizeof(struct nvme_id_ctrl));
+ if (error)
+ kfree(*id);
+
+ return error;
+}
+
+static int
+nvme_set_features(struct nvme_ctrl *dev, unsigned fid, unsigned dword11,
+ void *buffer, size_t buflen, u32 *result)
+{
+ struct nvme_command c;
+ union nvme_result res;
+ int ret;
+
+ memset(&c, 0, sizeof(c));
+ c.features.opcode = nvme_admin_set_features;
+ c.features.fid = cpu_to_le32(fid);
+ c.features.dword11 = cpu_to_le32(dword11);
+
+ ret = __nvme_submit_sync_cmd(dev, &c, &res, buffer, buflen, 0,
+ NVME_QID_ADMIN);
+ if (ret >= 0 && result)
+ *result = le32_to_cpu(res.u32);
+ return ret;
+}
+
+int nvme_set_queue_count(struct nvme_ctrl *ctrl, int *count)
+{
+ u32 q_count = (*count - 1) | ((*count - 1) << 16);
+ u32 result;
+ int status, nr_io_queues;
+
+ status = nvme_set_features(ctrl, NVME_FEAT_NUM_QUEUES, q_count, NULL, 0,
+ &result);
+ if (status < 0)
+ return status;
+
+ /*
+ * Degraded controllers might return an error when setting the queue
+ * count. We still want to be able to bring them online and offer
+ * access to the admin queue, as that might be only way to fix them up.
+ */
+ if (status > 0) {
+ dev_err(ctrl->dev, "Could not set queue count (%d)\n", status);
+ *count = 0;
+ } else {
+ nr_io_queues = min(result & 0xffff, result >> 16) + 1;
+ *count = min(*count, nr_io_queues);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nvme_set_queue_count);
+
+static int nvme_wait_ready(struct nvme_ctrl *ctrl, u64 cap, bool enabled)
+{
+ uint64_t start = get_time_ns();
+ unsigned long timeout =
+ ((NVME_CAP_TIMEOUT(cap) + 1) * HZ / 2);
+ u32 csts, bit = enabled ? NVME_CSTS_RDY : 0;
+ int ret;
+
+ while ((ret = ctrl->ops->reg_read32(ctrl, NVME_REG_CSTS, &csts)) == 0) {
+ if (csts == ~0)
+ return -ENODEV;
+ if ((csts & NVME_CSTS_RDY) == bit)
+ break;
+
+ mdelay(100);
+
+ if (is_timeout(start, timeout)) {
+ dev_err(ctrl->dev,
+ "Device not ready; aborting %s\n", enabled ?
+ "initialisation" : "reset");
+ return -ENODEV;
+ }
+ }
+
+ return ret;
+}
+
+static int nvme_identify_ns_list(struct nvme_ctrl *dev, unsigned nsid, __le32 *ns_list)
+{
+ struct nvme_command c = { };
+
+ c.identify.opcode = nvme_admin_identify;
+ c.identify.cns = NVME_ID_CNS_NS_ACTIVE_LIST;
+ c.identify.nsid = cpu_to_le32(nsid);
+ return nvme_submit_sync_cmd(dev, &c, ns_list, NVME_IDENTIFY_DATA_SIZE);
+}
+
+static struct nvme_id_ns *nvme_identify_ns(struct nvme_ctrl *ctrl,
+ unsigned nsid)
+{
+ struct nvme_id_ns *id;
+ struct nvme_command c = { };
+ int error;
+
+ /* gcc-4.4.4 (at least) has issues with initializers and anon unions */
+ c.identify.opcode = nvme_admin_identify;
+ c.identify.nsid = cpu_to_le32(nsid);
+ c.identify.cns = NVME_ID_CNS_NS;
+
+ id = kmalloc(sizeof(*id), GFP_KERNEL);
+ if (!id)
+ return NULL;
+
+ error = nvme_submit_sync_cmd(ctrl, &c, id, sizeof(*id));
+ if (error) {
+ dev_warn(ctrl->dev, "Identify namespace failed\n");
+ kfree(id);
+ return NULL;
+ }
+
+ return id;
+}
+
+static struct nvme_ns_head *nvme_alloc_ns_head(struct nvme_ctrl *ctrl,
+ unsigned nsid, struct nvme_id_ns *id)
+{
+ static int instance = 1;
+ struct nvme_ns_head *head;
+ int ret = -ENOMEM;
+
+ head = kzalloc(sizeof(*head), GFP_KERNEL);
+ if (!head)
+ goto out;
+
+ head->instance = instance++;
+ head->ns_id = nsid;
+
+ return head;
+out:
+ return ERR_PTR(ret);
+}
+
+static int nvme_init_ns_head(struct nvme_ns *ns, unsigned nsid,
+ struct nvme_id_ns *id)
+{
+ struct nvme_ctrl *ctrl = ns->ctrl;
+ const bool is_shared = id->nmic & (1 << 0);
+ struct nvme_ns_head *head = NULL;
+
+ if (is_shared) {
+ dev_info(ctrl->dev, "Skipping shared namespace %u\n", nsid);
+ return -ENOTSUPP;
+ }
+
+ head = nvme_alloc_ns_head(ctrl, nsid, id);
+ if (IS_ERR(head))
+ return PTR_ERR(head);
+
+ ns->head = head;
+
+ return 0;
+}
+
+#define DISK_NAME_LEN 32
+
+static void nvme_update_disk_info(struct block_device *blk, struct nvme_ns *ns,
+ struct nvme_id_ns *id)
+{
+ blk->blockbits = ns->lba_shift;
+ blk->num_blocks = le64_to_cpup(&id->nsze);
+
+ ns->readonly = id->nsattr & (1 << 0);
+}
+
+static void __nvme_revalidate_disk(struct block_device *blk,
+ struct nvme_id_ns *id)
+{
+ struct nvme_ns *ns = to_nvme_ns(blk);
+
+ /*
+ * If identify namespace failed, use default 512 byte block size so
+ * block layer can use before failing read/write for 0 capacity.
+ */
+ ns->lba_shift = id->lbaf[id->flbas & NVME_NS_FLBAS_LBA_MASK].ds;
+ if (ns->lba_shift == 0)
+ ns->lba_shift = 9;
+
+ nvme_update_disk_info(blk, ns, id);
+}
+
+static void nvme_setup_rw(struct nvme_ns *ns, struct nvme_command *cmnd,
+ int block, int num_block)
+{
+ cmnd->rw.nsid = cpu_to_le32(ns->head->ns_id);
+ cmnd->rw.slba = cpu_to_le64(nvme_block_nr(ns, block));
+ cmnd->rw.length = cpu_to_le16(num_block - 1);
+ cmnd->rw.control = 0;
+ cmnd->rw.dsmgmt = 0;
+}
+
+static void nvme_setup_flush(struct nvme_ns *ns, struct nvme_command *cmnd)
+{
+ memset(cmnd, 0, sizeof(*cmnd));
+ cmnd->common.opcode = nvme_cmd_flush;
+ cmnd->common.nsid = cpu_to_le32(ns->head->ns_id);
+}
+
+static int nvme_submit_sync_rw(struct nvme_ns *ns, struct nvme_command *cmnd,
+ void *buffer, int block, int num_blocks)
+{
+ /*
+ * ns->ctrl->max_hw_sectors is in units of 512 bytes, so we
+ * need to make sure we adjust it to discovered lba_shift
+ */
+ const u32 max_hw_sectors =
+ ns->ctrl->max_hw_sectors >> (ns->lba_shift - 9);
+ int ret;
+
+ if (num_blocks > max_hw_sectors) {
+ while (num_blocks) {
+ const int chunk = min_t(int, num_blocks,
+ max_hw_sectors);
+
+ ret = nvme_submit_sync_rw(ns, cmnd, buffer, block,
+ chunk);
+ if (ret)
+ break;
+
+ num_blocks -= chunk;
+ buffer += chunk;
+ block += chunk;
+ }
+
+ return ret;
+ }
+
+ nvme_setup_rw(ns, cmnd, block, num_blocks);
+
+ ret = __nvme_submit_sync_cmd(ns->ctrl, cmnd, NULL, buffer,
+ num_blocks << ns->lba_shift,
+ 0, NVME_QID_IO);
+
+ if (ret) {
+ dev_err(ns->ctrl->dev,
+ "I/O failed: block: %d, num blocks: %d, status code type: %xh, status code %02xh\n",
+ block, num_blocks, (ret >> 8) & 0xf,
+ ret & 0xff);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+
+static int nvme_block_device_read(struct block_device *blk, void *buffer,
+ int block, int num_blocks)
+{
+ struct nvme_ns *ns = to_nvme_ns(blk);
+ struct nvme_command cmnd = { };
+
+ cmnd.rw.opcode = nvme_cmd_read;
+
+ return nvme_submit_sync_rw(ns, &cmnd, buffer, block, num_blocks);
+}
+
+static int __maybe_unused
+nvme_block_device_write(struct block_device *blk, const void *buffer,
+ int block, int num_blocks)
+{
+ struct nvme_ns *ns = to_nvme_ns(blk);
+ struct nvme_command cmnd = { };
+
+ if (ns->readonly)
+ return -EINVAL;
+
+ cmnd.rw.opcode = nvme_cmd_write;
+
+ return nvme_submit_sync_rw(ns, &cmnd, (void *)buffer, block,
+ num_blocks);
+}
+
+static int __maybe_unused nvme_block_device_flush(struct block_device *blk)
+{
+ struct nvme_ns *ns = to_nvme_ns(blk);
+ struct nvme_command cmnd = { };
+
+ nvme_setup_flush(ns, &cmnd);
+
+ return __nvme_submit_sync_cmd(ns->ctrl, &cmnd, NULL, NULL,
+ 0, 0, NVME_QID_IO);
+}
+
+static struct block_device_ops nvme_block_device_ops = {
+ .read = nvme_block_device_read,
+#ifdef CONFIG_BLOCK_WRITE
+ .write = nvme_block_device_write,
+ .flush = nvme_block_device_flush,
+#endif
+};
+
+static void nvme_alloc_ns(struct nvme_ctrl *ctrl, unsigned nsid)
+{
+ struct nvme_ns *ns;
+ struct nvme_id_ns *id;
+ char disk_name[DISK_NAME_LEN];
+ int ret, flags;
+
+ ns = kzalloc(sizeof(*ns), GFP_KERNEL);
+ if (!ns)
+ return;
+
+ ns->ctrl = ctrl;
+ ns->lba_shift = 9; /* set to a default value for 512 until
+ * disk is validated */
+
+ id = nvme_identify_ns(ctrl, nsid);
+ if (!id)
+ goto out_free_ns;
+
+ if (id->ncap == 0)
+ goto out_free_id;
+
+ if (nvme_init_ns_head(ns, nsid, id))
+ goto out_free_id;
+
+ nvme_set_disk_name(disk_name, ns, ctrl, &flags);
+
+ ns->blk.dev = ctrl->dev;
+ ns->blk.ops = &nvme_block_device_ops;
+ ns->blk.cdev.name = strdup(disk_name);
+
+ __nvme_revalidate_disk(&ns->blk, id);
+ kfree(id);
+
+ ret = blockdevice_register(&ns->blk);
+ if (ret) {
+ dev_err(ctrl->dev, "Cannot register block device (%d)\n", ret);
+ goto out_free_id;
+ }
+
+ return;
+out_free_id:
+ kfree(id);
+out_free_ns:
+ kfree(ns);
+}
+
+static int nvme_scan_ns_list(struct nvme_ctrl *ctrl, unsigned nn)
+{
+ __le32 *ns_list;
+ unsigned i, j, nsid, prev = 0, num_lists = DIV_ROUND_UP(nn, 1024);
+ int ret = 0;
+
+ ns_list = kzalloc(NVME_IDENTIFY_DATA_SIZE, GFP_KERNEL);
+ if (!ns_list)
+ return -ENOMEM;
+
+ for (i = 0; i < num_lists; i++) {
+ ret = nvme_identify_ns_list(ctrl, prev, ns_list);
+ if (ret)
+ goto out;
+
+ for (j = 0; j < min(nn, 1024U); j++) {
+ nsid = le32_to_cpu(ns_list[j]);
+ if (!nsid)
+ goto out;
+
+ nvme_alloc_ns(ctrl, nsid);
+ }
+ nn -= j;
+ }
+ out:
+ kfree(ns_list);
+ return ret;
+}
+
+static void nvme_scan_ns_sequential(struct nvme_ctrl *ctrl, unsigned nn)
+{
+ unsigned i;
+
+ for (i = 1; i <= nn; i++)
+ nvme_alloc_ns(ctrl, i);
+}
+
+static void nvme_scan_work(struct nvme_ctrl *ctrl)
+{
+ struct nvme_id_ctrl *id;
+ unsigned nn;
+
+ if (nvme_identify_ctrl(ctrl, &id))
+ return;
+
+ nn = le32_to_cpu(id->nn);
+ if (ctrl->vs >= NVME_VS(1, 1, 0)) {
+ if (!nvme_scan_ns_list(ctrl, nn))
+ goto out_free_id;
+ }
+ nvme_scan_ns_sequential(ctrl, nn);
+out_free_id:
+ kfree(id);
+}
+
+void nvme_start_ctrl(struct nvme_ctrl *ctrl)
+{
+ if (ctrl->queue_count > 1)
+ nvme_scan_work(ctrl);
+}
+EXPORT_SYMBOL_GPL(nvme_start_ctrl);
+
+/*
+ * If the device has been passed off to us in an enabled state, just clear
+ * the enabled bit. The spec says we should set the 'shutdown notification
+ * bits', but doing so may cause the device to complete commands to the
+ * admin queue ... and we don't know what memory that might be pointing at!
+ */
+int nvme_disable_ctrl(struct nvme_ctrl *ctrl, u64 cap)
+{
+ int ret;
+
+ ctrl->ctrl_config &= ~NVME_CC_SHN_MASK;
+ ctrl->ctrl_config &= ~NVME_CC_ENABLE;
+
+ ret = ctrl->ops->reg_write32(ctrl, NVME_REG_CC, ctrl->ctrl_config);
+ if (ret)
+ return ret;
+
+ return nvme_wait_ready(ctrl, cap, false);
+}
+EXPORT_SYMBOL_GPL(nvme_disable_ctrl);
+
+int nvme_enable_ctrl(struct nvme_ctrl *ctrl, u64 cap)
+{
+ /*
+ * Default to a 4K page size, with the intention to update this
+ * path in the future to accomodate architectures with differing
+ * kernel and IO page sizes.
+ */
+ unsigned dev_page_min = NVME_CAP_MPSMIN(cap) + 12, page_shift = 12;
+ int ret;
+
+ if (page_shift < dev_page_min) {
+ dev_err(ctrl->dev,
+ "Minimum device page size %u too large for host (%u)\n",
+ 1 << dev_page_min, 1 << page_shift);
+ return -ENODEV;
+ }
+
+ ctrl->page_size = 1 << page_shift;
+
+ ctrl->ctrl_config = NVME_CC_CSS_NVM;
+ ctrl->ctrl_config |= (page_shift - 12) << NVME_CC_MPS_SHIFT;
+ ctrl->ctrl_config |= NVME_CC_AMS_RR | NVME_CC_SHN_NONE;
+ ctrl->ctrl_config |= NVME_CC_IOSQES | NVME_CC_IOCQES;
+ ctrl->ctrl_config |= NVME_CC_ENABLE;
+
+ ret = ctrl->ops->reg_write32(ctrl, NVME_REG_CC, ctrl->ctrl_config);
+ if (ret)
+ return ret;
+ return nvme_wait_ready(ctrl, cap, true);
+}
+EXPORT_SYMBOL_GPL(nvme_enable_ctrl);
+
+int nvme_shutdown_ctrl(struct nvme_ctrl *ctrl)
+{
+ uint64_t start = get_time_ns();
+ unsigned long timeout = SHUTDOWN_TIMEOUT;
+ u32 csts;
+ int ret;
+
+ ctrl->ctrl_config &= ~NVME_CC_SHN_MASK;
+ ctrl->ctrl_config |= NVME_CC_SHN_NORMAL;
+
+ ret = ctrl->ops->reg_write32(ctrl, NVME_REG_CC, ctrl->ctrl_config);
+ if (ret)
+ return ret;
+
+ while ((ret = ctrl->ops->reg_read32(ctrl, NVME_REG_CSTS, &csts)) == 0) {
+ if ((csts & NVME_CSTS_SHST_MASK) == NVME_CSTS_SHST_CMPLT)
+ break;
+
+ mdelay(100);
+
+ if (is_timeout(start, timeout)) {
+ dev_err(ctrl->dev,
+ "Device shutdown incomplete; abort shutdown\n");
+ return -ENODEV;
+ }
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(nvme_shutdown_ctrl);
+
+#define NVME_ID_MAX_LEN 41
+
+static void nvme_print(struct nvme_ctrl *ctrl, const char *prefix,
+ const char *_string, size_t _length)
+{
+ char string[NVME_ID_MAX_LEN];
+ const size_t length = min(_length, sizeof(string) - 1);
+
+ memcpy(string, _string, length);
+ string[length - 1] = '\0';
+
+ dev_info(ctrl->dev, "%s: %s\n", prefix, string);
+}
+
+static int nvme_init_subsystem(struct nvme_ctrl *ctrl, struct nvme_id_ctrl *id)
+{
+ nvme_print(ctrl, "serial", id->sn, sizeof(id->sn));
+ nvme_print(ctrl, "model", id->mn, sizeof(id->mn));
+ nvme_print(ctrl, "firmware", id->fr, sizeof(id->fr));
+
+ return 0;
+}
+
+/*
+ * Initialize the cached copies of the Identify data and various controller
+ * register in our nvme_ctrl structure. This should be called as soon as
+ * the admin queue is fully up and running.
+ */
+int nvme_init_identify(struct nvme_ctrl *ctrl)
+{
+ struct nvme_id_ctrl *id;
+ u64 cap;
+ int ret, page_shift;
+ u32 max_hw_sectors;
+
+ ret = ctrl->ops->reg_read32(ctrl, NVME_REG_VS, &ctrl->vs);
+ if (ret) {
+ dev_err(ctrl->dev, "Reading VS failed (%d)\n", ret);
+ return ret;
+ }
+
+ ret = ctrl->ops->reg_read64(ctrl, NVME_REG_CAP, &cap);
+ if (ret) {
+ dev_err(ctrl->dev, "Reading CAP failed (%d)\n", ret);
+ return ret;
+ }
+ page_shift = NVME_CAP_MPSMIN(cap) + 12;
+
+ ret = nvme_identify_ctrl(ctrl, &id);
+ if (ret) {
+ dev_err(ctrl->dev, "Identify Controller failed (%d)\n", ret);
+ return -EIO;
+ }
+
+ ret = nvme_init_subsystem(ctrl, id);
+ if (ret)
+ return ret;
+
+ if (id->mdts)
+ max_hw_sectors = 1 << (id->mdts + page_shift - 9);
+ else
+ max_hw_sectors = UINT_MAX;
+ ctrl->max_hw_sectors =
+ min_not_zero(ctrl->max_hw_sectors, max_hw_sectors);
+
+ kfree(id);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nvme_init_identify);
+
+
+/*
+ * Initialize a NVMe controller structures. This needs to be called during
+ * earliest initialization so that we have the initialized structured around
+ * during probing.
+ */
+int nvme_init_ctrl(struct nvme_ctrl *ctrl, struct device_d *dev,
+ const struct nvme_ctrl_ops *ops)
+{
+ static int instance = 0;
+
+ ctrl->dev = dev;
+ ctrl->ops = ops;
+ ctrl->instance = instance++;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nvme_init_ctrl);
diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h
new file mode 100644
index 0000000000..4ec4aef972
--- /dev/null
+++ b/drivers/nvme/host/nvme.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2011-2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#ifndef _NVME_H
+#define _NVME_H
+
+#include <linux/nvme.h>
+#include <dma.h>
+#include <block.h>
+
+#define ADMIN_TIMEOUT (60 * HZ)
+#define SHUTDOWN_TIMEOUT ( 5 * HZ)
+
+/*
+ * Common request structure for NVMe passthrough. All drivers must have
+ * this structure as the first member of their request-private data.
+ */
+struct nvme_request {
+ struct nvme_command *cmd;
+ union nvme_result result;
+ u16 status;
+
+ void *buffer;
+ unsigned int buffer_len;
+ dma_addr_t buffer_dma_addr;
+ enum dma_data_direction dma_dir;
+};
+
+struct nvme_ctrl {
+ const struct nvme_ctrl_ops *ops;
+ struct device_d *dev;
+ int instance;
+
+ u32 ctrl_config;
+ u32 queue_count;
+ u64 cap;
+ u32 page_size;
+ u32 max_hw_sectors;
+ u32 vs;
+};
+
+/*
+ * Anchor structure for namespaces. There is one for each namespace in a
+ * NVMe subsystem that any of our controllers can see, and the namespace
+ * structure for each controller is chained of it. For private namespaces
+ * there is a 1:1 relation to our namespace structures, that is ->list
+ * only ever has a single entry for private namespaces.
+ */
+struct nvme_ns_head {
+ unsigned ns_id;
+ int instance;
+};
+
+struct nvme_ns {
+ struct nvme_ctrl *ctrl;
+ struct nvme_ns_head *head;
+ struct block_device blk;
+
+ int lba_shift;
+ bool readonly;
+};
+
+static inline struct nvme_ns *to_nvme_ns(struct block_device *blk)
+{
+ return container_of(blk, struct nvme_ns, blk);
+}
+
+struct nvme_ctrl_ops {
+ int (*reg_read32)(struct nvme_ctrl *ctrl, u32 off, u32 *val);
+ int (*reg_write32)(struct nvme_ctrl *ctrl, u32 off, u32 val);
+ int (*reg_read64)(struct nvme_ctrl *ctrl, u32 off, u64 *val);
+
+ int (*submit_sync_cmd)(struct nvme_ctrl *ctrl,
+ struct nvme_command *cmd,
+ union nvme_result *result,
+ void *buffer,
+ unsigned bufflen,
+ unsigned timeout, int qid);
+};
+
+static inline bool nvme_ctrl_ready(struct nvme_ctrl *ctrl)
+{
+ u32 val = 0;
+
+ if (ctrl->ops->reg_read32(ctrl, NVME_REG_CSTS, &val))
+ return false;
+ return val & NVME_CSTS_RDY;
+}
+
+static inline u64 nvme_block_nr(struct nvme_ns *ns, sector_t sector)
+{
+ return (sector >> (ns->lba_shift - 9));
+}
+
+static inline void nvme_end_request(struct nvme_request *rq, __le16 status,
+ union nvme_result result)
+{
+ rq->status = le16_to_cpu(status) >> 1;
+ rq->result = result;
+}
+
+int nvme_disable_ctrl(struct nvme_ctrl *ctrl, u64 cap);
+int nvme_enable_ctrl(struct nvme_ctrl *ctrl, u64 cap);
+int nvme_shutdown_ctrl(struct nvme_ctrl *ctrl);
+int nvme_init_ctrl(struct nvme_ctrl *ctrl, struct device_d *dev,
+ const struct nvme_ctrl_ops *ops);
+void nvme_start_ctrl(struct nvme_ctrl *ctrl);
+int nvme_init_identify(struct nvme_ctrl *ctrl);
+
+enum nvme_queue_id {
+ NVME_QID_ADMIN,
+ NVME_QID_IO,
+ NVME_QID_NUM,
+ NVME_QID_ANY = -1,
+};
+
+int __nvme_submit_sync_cmd(struct nvme_ctrl *ctrl,
+ struct nvme_command *cmd,
+ union nvme_result *result,
+ void *buffer, unsigned bufflen,
+ unsigned timeout, int qid);
+int nvme_submit_sync_cmd(struct nvme_ctrl *ctrl,
+ struct nvme_command *cmd,
+ void *buffer, unsigned bufflen);
+
+
+int nvme_set_queue_count(struct nvme_ctrl *ctrl, int *count);
+/*
+ * Without the multipath code enabled, multiple controller per subsystems are
+ * visible as devices and thus we cannot use the subsystem instance.
+ */
+static inline void nvme_set_disk_name(char *disk_name, struct nvme_ns *ns,
+ struct nvme_ctrl *ctrl, int *flags)
+{
+ sprintf(disk_name, "nvme%dn%d", ctrl->instance, ns->head->instance);
+}
+
+#endif /* _NVME_H */
diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c
new file mode 100644
index 0000000000..387bc45a7b
--- /dev/null
+++ b/drivers/nvme/host/pci.c
@@ -0,0 +1,697 @@
+
+#include <common.h>
+#include <init.h>
+#include <io.h>
+#include <io-64-nonatomic-lo-hi.h>
+#include <linux/pci.h>
+
+#include <dma.h>
+
+#include "nvme.h"
+
+#define SQ_SIZE(depth) (depth * sizeof(struct nvme_command))
+#define CQ_SIZE(depth) (depth * sizeof(struct nvme_completion))
+
+#define NVME_MAX_KB_SZ 4096
+
+static int io_queue_depth = 2;
+
+struct nvme_dev;
+
+/*
+ * An NVM Express queue. Each device has at least two (one for admin
+ * commands and one for I/O commands).
+ */
+struct nvme_queue {
+ struct nvme_dev *dev;
+ struct nvme_request *req;
+ struct nvme_command *sq_cmds;
+ volatile struct nvme_completion *cqes;
+ dma_addr_t sq_dma_addr;
+ dma_addr_t cq_dma_addr;
+ u32 __iomem *q_db;
+ u16 q_depth;
+ u16 sq_tail;
+ u16 cq_head;
+ u16 qid;
+ u8 cq_phase;
+
+ u16 counter;
+};
+
+/*
+ * Represents an NVM Express device. Each nvme_dev is a PCI function.
+ */
+struct nvme_dev {
+ struct nvme_queue queues[NVME_QID_NUM];
+ u32 __iomem *dbs;
+ struct device_d *dev;
+ unsigned online_queues;
+ unsigned max_qid;
+ int q_depth;
+ u32 db_stride;
+ void __iomem *bar;
+ bool subsystem;
+ struct nvme_ctrl ctrl;
+ __le64 *prp_pool;
+ unsigned int prp_pool_size;
+ dma_addr_t prp_dma;
+};
+
+static inline struct nvme_dev *to_nvme_dev(struct nvme_ctrl *ctrl)
+{
+ return container_of(ctrl, struct nvme_dev, ctrl);
+}
+
+static int nvme_pci_setup_prps(struct nvme_dev *dev,
+ const struct nvme_request *req,
+ struct nvme_rw_command *cmnd)
+{
+ int length = req->buffer_len;
+ const int page_size = dev->ctrl.page_size;
+ dma_addr_t dma_addr = req->buffer_dma_addr;
+ u32 offset = dma_addr & (page_size - 1);
+ u64 prp1 = dma_addr;
+ __le64 *prp_list;
+ int i, nprps;
+ dma_addr_t prp_dma;
+
+
+ length -= (page_size - offset);
+ if (length <= 0) {
+ prp_dma = 0;
+ goto done;
+ }
+
+ dma_addr += (page_size - offset);
+
+ if (length <= page_size) {
+ prp_dma = dma_addr;
+ goto done;
+ }
+
+ nprps = DIV_ROUND_UP(length, page_size);
+ if (nprps > dev->prp_pool_size) {
+ dma_free_coherent(dev->prp_pool, dev->prp_dma,
+ dev->prp_pool_size * sizeof(u64));
+ dev->prp_pool_size = nprps;
+ dev->prp_pool = dma_alloc_coherent(nprps * sizeof(u64),
+ &dev->prp_dma);
+ }
+
+ prp_list = dev->prp_pool;
+ prp_dma = dev->prp_dma;
+
+ i = 0;
+ for (;;) {
+ if (i == page_size >> 3) {
+ __le64 *old_prp_list = prp_list;
+ prp_list = &prp_list[i];
+ prp_dma += page_size;
+ prp_list[0] = old_prp_list[i - 1];
+ old_prp_list[i - 1] = cpu_to_le64(prp_dma);
+ i = 1;
+ }
+
+ prp_list[i++] = cpu_to_le64(dma_addr);
+ dma_addr += page_size;
+ length -= page_size;
+ if (length <= 0)
+ break;
+ }
+
+done:
+ cmnd->dptr.prp1 = cpu_to_le64(prp1);
+ cmnd->dptr.prp2 = cpu_to_le64(prp_dma);
+
+ return 0;
+}
+
+static int nvme_map_data(struct nvme_dev *dev, struct nvme_request *req)
+{
+ if (!req->buffer || !req->buffer_len)
+ return 0;
+
+ req->buffer_dma_addr = dma_map_single(dev->dev, req->buffer,
+ req->buffer_len, req->dma_dir);
+ if (dma_mapping_error(dev->dev, req->buffer_dma_addr))
+ return -EFAULT;
+
+ return nvme_pci_setup_prps(dev, req, &req->cmd->rw);
+}
+
+static void nvme_unmap_data(struct nvme_dev *dev, struct nvme_request *req)
+{
+ if (!req->buffer || !req->buffer_len)
+ return;
+
+ dma_unmap_single(dev->dev, req->buffer_dma_addr, req->buffer_len,
+ req->dma_dir);
+}
+
+static int nvme_alloc_queue(struct nvme_dev *dev, int qid, int depth)
+{
+ struct nvme_queue *nvmeq = &dev->queues[qid];
+
+ if (dev->ctrl.queue_count > qid)
+ return 0;
+
+ nvmeq->cqes = dma_alloc_coherent(CQ_SIZE(depth),
+ &nvmeq->cq_dma_addr);
+ if (!nvmeq->cqes)
+ goto free_nvmeq;
+
+ nvmeq->sq_cmds = dma_alloc_coherent(SQ_SIZE(depth),
+ &nvmeq->sq_dma_addr);
+ if (!nvmeq->sq_cmds)
+ goto free_cqdma;
+
+ nvmeq->dev = dev;
+ nvmeq->cq_head = 0;
+ nvmeq->cq_phase = 1;
+ nvmeq->q_db = &dev->dbs[qid * 2 * dev->db_stride];
+ nvmeq->q_depth = depth;
+ nvmeq->qid = qid;
+ dev->ctrl.queue_count++;
+
+ return 0;
+
+ free_cqdma:
+ dma_free_coherent((void *)nvmeq->cqes, nvmeq->cq_dma_addr,
+ CQ_SIZE(depth));
+ free_nvmeq:
+ return -ENOMEM;
+}
+
+static int adapter_delete_queue(struct nvme_dev *dev, u8 opcode, u16 id)
+{
+ struct nvme_command c;
+
+ memset(&c, 0, sizeof(c));
+ c.delete_queue.opcode = opcode;
+ c.delete_queue.qid = cpu_to_le16(id);
+
+ return nvme_submit_sync_cmd(&dev->ctrl, &c, NULL, 0);
+}
+
+static int adapter_alloc_cq(struct nvme_dev *dev, u16 qid,
+ struct nvme_queue *nvmeq, s16 vector)
+{
+ struct nvme_command c;
+ int flags = NVME_QUEUE_PHYS_CONTIG | NVME_CQ_IRQ_ENABLED;
+
+ /*
+ * Note: we (ab)use the fact that the prp fields survive if no data
+ * is attached to the request.
+ */
+ memset(&c, 0, sizeof(c));
+ c.create_cq.opcode = nvme_admin_create_cq;
+ c.create_cq.prp1 = cpu_to_le64(nvmeq->cq_dma_addr);
+ c.create_cq.cqid = cpu_to_le16(qid);
+ c.create_cq.qsize = cpu_to_le16(nvmeq->q_depth - 1);
+ c.create_cq.cq_flags = cpu_to_le16(flags);
+ c.create_cq.irq_vector = cpu_to_le16(vector);
+
+ return nvme_submit_sync_cmd(&dev->ctrl, &c, NULL, 0);
+}
+
+static int adapter_alloc_sq(struct nvme_dev *dev, u16 qid,
+ struct nvme_queue *nvmeq)
+{
+ struct nvme_command c;
+ int flags = NVME_QUEUE_PHYS_CONTIG;
+
+ /*
+ * Note: we (ab)use the fact that the prp fields survive if no data
+ * is attached to the request.
+ */
+ memset(&c, 0, sizeof(c));
+ c.create_sq.opcode = nvme_admin_create_sq;
+ c.create_sq.prp1 = cpu_to_le64(nvmeq->sq_dma_addr);
+ c.create_sq.sqid = cpu_to_le16(qid);
+ c.create_sq.qsize = cpu_to_le16(nvmeq->q_depth - 1);
+ c.create_sq.sq_flags = cpu_to_le16(flags);
+ c.create_sq.cqid = cpu_to_le16(qid);
+
+ return nvme_submit_sync_cmd(&dev->ctrl, &c, NULL, 0);
+}
+
+static int adapter_delete_cq(struct nvme_dev *dev, u16 cqid)
+{
+ return adapter_delete_queue(dev, nvme_admin_delete_cq, cqid);
+}
+
+static void nvme_init_queue(struct nvme_queue *nvmeq, u16 qid)
+{
+ struct nvme_dev *dev = nvmeq->dev;
+
+ nvmeq->sq_tail = 0;
+ nvmeq->cq_head = 0;
+ nvmeq->cq_phase = 1;
+ nvmeq->q_db = &dev->dbs[qid * 2 * dev->db_stride];
+ dev->online_queues++;
+}
+
+static int nvme_create_queue(struct nvme_queue *nvmeq, int qid)
+{
+ struct nvme_dev *dev = nvmeq->dev;
+ int result;
+ s16 vector;
+
+ vector = 0;
+ result = adapter_alloc_cq(dev, qid, nvmeq, vector);
+ if (result)
+ return result;
+
+ result = adapter_alloc_sq(dev, qid, nvmeq);
+ if (result < 0)
+ return result;
+ else if (result)
+ goto release_cq;
+
+ nvme_init_queue(nvmeq, qid);
+
+ return result;
+
+release_cq:
+ adapter_delete_cq(dev, qid);
+ return result;
+}
+
+/**
+ * nvme_submit_cmd() - Copy a command into a queue and ring the doorbell
+ * @nvmeq: The queue to use
+ * @cmd: The command to send
+ */
+static void nvme_submit_cmd(struct nvme_queue *nvmeq, struct nvme_command *cmd)
+{
+ memcpy(&nvmeq->sq_cmds[nvmeq->sq_tail], cmd, sizeof(*cmd));
+
+ if (++nvmeq->sq_tail == nvmeq->q_depth)
+ nvmeq->sq_tail = 0;
+ writel(nvmeq->sq_tail, nvmeq->q_db);
+}
+
+/* We read the CQE phase first to check if the rest of the entry is valid */
+static inline bool nvme_cqe_pending(struct nvme_queue *nvmeq)
+{
+ return (le16_to_cpu(nvmeq->cqes[nvmeq->cq_head].status) & 1) ==
+ nvmeq->cq_phase;
+}
+
+static inline void nvme_ring_cq_doorbell(struct nvme_queue *nvmeq)
+{
+ u16 head = nvmeq->cq_head;
+
+ writel(head, nvmeq->q_db + nvmeq->dev->db_stride);
+}
+
+static inline void nvme_handle_cqe(struct nvme_queue *nvmeq, u16 idx)
+{
+ volatile struct nvme_completion *cqe = &nvmeq->cqes[idx];
+ struct nvme_request *req = nvmeq->req;
+
+ if (unlikely(cqe->command_id >= nvmeq->q_depth)) {
+ dev_warn(nvmeq->dev->ctrl.dev,
+ "invalid id %d completed on queue %d\n",
+ cqe->command_id, le16_to_cpu(cqe->sq_id));
+ return;
+ }
+
+ if (WARN_ON(cqe->command_id != req->cmd->common.command_id))
+ return;
+
+ nvme_end_request(req, cqe->status, cqe->result);
+}
+
+static void nvme_complete_cqes(struct nvme_queue *nvmeq, u16 start, u16 end)
+{
+ while (start != end) {
+ nvme_handle_cqe(nvmeq, start);
+ if (++start == nvmeq->q_depth)
+ start = 0;
+ }
+}
+
+static inline void nvme_update_cq_head(struct nvme_queue *nvmeq)
+{
+ if (++nvmeq->cq_head == nvmeq->q_depth) {
+ nvmeq->cq_head = 0;
+ nvmeq->cq_phase = !nvmeq->cq_phase;
+ }
+}
+
+static inline bool nvme_process_cq(struct nvme_queue *nvmeq, u16 *start,
+ u16 *end, int tag)
+{
+ bool found = false;
+
+ *start = nvmeq->cq_head;
+ while (!found && nvme_cqe_pending(nvmeq)) {
+ if (nvmeq->cqes[nvmeq->cq_head].command_id == tag)
+ found = true;
+ nvme_update_cq_head(nvmeq);
+ }
+ *end = nvmeq->cq_head;
+
+ if (*start != *end)
+ nvme_ring_cq_doorbell(nvmeq);
+ return found;
+}
+
+static bool nvme_poll(struct nvme_queue *nvmeq, unsigned int tag)
+{
+ u16 start, end;
+ bool found;
+
+ if (!nvme_cqe_pending(nvmeq))
+ return false;
+
+ found = nvme_process_cq(nvmeq, &start, &end, tag);
+
+ nvme_complete_cqes(nvmeq, start, end);
+ return found;
+}
+
+static int nvme_pci_submit_sync_cmd(struct nvme_ctrl *ctrl,
+ struct nvme_command *cmd,
+ union nvme_result *result,
+ void *buffer,
+ unsigned int buffer_len,
+ unsigned timeout, int qid)
+{
+ struct nvme_dev *dev = to_nvme_dev(ctrl);
+ struct nvme_queue *nvmeq = &dev->queues[qid];
+ struct nvme_request req = { };
+ const u16 tag = nvmeq->counter++ & (nvmeq->q_depth - 1);
+ enum dma_data_direction dma_dir;
+ int ret;
+
+ switch (qid) {
+ case NVME_QID_ADMIN:
+ switch (cmd->common.opcode) {
+ case nvme_admin_create_sq:
+ case nvme_admin_create_cq:
+ case nvme_admin_delete_sq:
+ case nvme_admin_delete_cq:
+ case nvme_admin_set_features:
+ dma_dir = DMA_TO_DEVICE;
+ break;
+ case nvme_admin_identify:
+ dma_dir = DMA_FROM_DEVICE;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case NVME_QID_IO:
+ switch (cmd->rw.opcode) {
+ case nvme_cmd_write:
+ dma_dir = DMA_TO_DEVICE;
+ break;
+ case nvme_cmd_read:
+ dma_dir = DMA_FROM_DEVICE;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ cmd->common.command_id = tag;
+
+ timeout = timeout ?: ADMIN_TIMEOUT;
+
+ req.cmd = cmd;
+ req.buffer = buffer;
+ req.buffer_len = buffer_len;
+ req.dma_dir = dma_dir;
+
+ ret = nvme_map_data(dev, &req);
+ if (ret) {
+ dev_err(dev->dev, "Failed to map request data\n");
+ return ret;
+ }
+
+ nvme_submit_cmd(nvmeq, cmd);
+
+ nvmeq->req = &req;
+ ret = wait_on_timeout(timeout, nvme_poll(nvmeq, tag));
+ nvmeq->req = NULL;
+
+ nvme_unmap_data(dev, &req);
+
+ if (result)
+ *result = req.result;
+
+ return ret ?: req.status;
+}
+
+static int nvme_pci_configure_admin_queue(struct nvme_dev *dev)
+{
+ int result;
+ u32 aqa;
+ struct nvme_queue *nvmeq;
+
+ dev->subsystem = readl(dev->bar + NVME_REG_VS) >= NVME_VS(1, 1, 0) ?
+ NVME_CAP_NSSRC(dev->ctrl.cap) : 0;
+
+ if (dev->subsystem &&
+ (readl(dev->bar + NVME_REG_CSTS) & NVME_CSTS_NSSRO))
+ writel(NVME_CSTS_NSSRO, dev->bar + NVME_REG_CSTS);
+
+ result = nvme_disable_ctrl(&dev->ctrl, dev->ctrl.cap);
+ if (result < 0)
+ return result;
+
+ result = nvme_alloc_queue(dev, NVME_QID_ADMIN, NVME_AQ_DEPTH);
+ if (result)
+ return result;
+
+ nvmeq = &dev->queues[NVME_QID_ADMIN];
+ aqa = nvmeq->q_depth - 1;
+ aqa |= aqa << 16;
+
+ writel(aqa, dev->bar + NVME_REG_AQA);
+ writeq(nvmeq->sq_dma_addr, dev->bar + NVME_REG_ASQ);
+ writeq(nvmeq->cq_dma_addr, dev->bar + NVME_REG_ACQ);
+
+ result = nvme_enable_ctrl(&dev->ctrl, dev->ctrl.cap);
+ if (result)
+ return result;
+
+ nvme_init_queue(nvmeq, NVME_QID_ADMIN);
+
+ return result;
+}
+
+static int nvme_create_io_queues(struct nvme_dev *dev)
+{
+ unsigned i, max;
+ int ret = 0;
+
+ for (i = dev->ctrl.queue_count; i <= dev->max_qid; i++) {
+ if (nvme_alloc_queue(dev, i, dev->q_depth)) {
+ ret = -ENOMEM;
+ break;
+ }
+ }
+
+ max = min(dev->max_qid, dev->ctrl.queue_count - 1);
+ for (i = dev->online_queues; i <= max; i++) {
+ ret = nvme_create_queue(&dev->queues[i], i);
+ if (ret)
+ break;
+ }
+
+ /*
+ * Ignore failing Create SQ/CQ commands, we can continue with less
+ * than the desired amount of queues, and even a controller without
+ * I/O queues can still be used to issue admin commands. This might
+ * be useful to upgrade a buggy firmware for example.
+ */
+ return ret >= 0 ? 0 : ret;
+}
+
+static int nvme_setup_io_queues(struct nvme_dev *dev)
+{
+ int result, nr_io_queues;
+
+ nr_io_queues = NVME_QID_NUM - 1;
+ result = nvme_set_queue_count(&dev->ctrl, &nr_io_queues);
+ if (result < 0)
+ return result;
+
+ dev->max_qid = nr_io_queues;
+
+ return nvme_create_io_queues(dev);
+}
+
+static int nvme_pci_enable(struct nvme_dev *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev->dev);
+
+ if (pci_enable_device(pdev))
+ return -ENOMEM;
+
+ pci_set_master(pdev);
+
+ if (readl(dev->bar + NVME_REG_CSTS) == -1)
+ return -ENODEV;
+
+ dev->ctrl.cap = readq(dev->bar + NVME_REG_CAP);
+
+ dev->q_depth = min_t(int, NVME_CAP_MQES(dev->ctrl.cap) + 1,
+ io_queue_depth);
+ dev->db_stride = 1 << NVME_CAP_STRIDE(dev->ctrl.cap);
+ dev->dbs = dev->bar + 4096;
+
+ return 0;
+}
+
+static void nvme_reset_work(struct nvme_dev *dev)
+{
+ int result = -ENODEV;
+
+ result = nvme_pci_enable(dev);
+ if (result)
+ goto out;
+
+ result = nvme_pci_configure_admin_queue(dev);
+ if (result)
+ goto out;
+
+ /*
+ * Limit the max command size to prevent iod->sg allocations going
+ * over a single page.
+ */
+ dev->ctrl.max_hw_sectors = NVME_MAX_KB_SZ << 1;
+
+ result = nvme_init_identify(&dev->ctrl);
+ if (result)
+ goto out;
+
+ result = nvme_setup_io_queues(dev);
+ if (result) {
+ dev_err(dev->ctrl.dev, "IO queues not created\n");
+ goto out;
+ }
+
+ nvme_start_ctrl(&dev->ctrl);
+out:
+ return;
+}
+
+static int nvme_pci_reg_read32(struct nvme_ctrl *ctrl, u32 off, u32 *val)
+{
+ *val = readl(to_nvme_dev(ctrl)->bar + off);
+ return 0;
+}
+
+static int nvme_pci_reg_write32(struct nvme_ctrl *ctrl, u32 off, u32 val)
+{
+ writel(val, to_nvme_dev(ctrl)->bar + off);
+ return 0;
+}
+
+static int nvme_pci_reg_read64(struct nvme_ctrl *ctrl, u32 off, u64 *val)
+{
+ *val = readq(to_nvme_dev(ctrl)->bar + off);
+ return 0;
+}
+
+static const struct nvme_ctrl_ops nvme_pci_ctrl_ops = {
+ .reg_read32 = nvme_pci_reg_read32,
+ .reg_write32 = nvme_pci_reg_write32,
+ .reg_read64 = nvme_pci_reg_read64,
+ .submit_sync_cmd = nvme_pci_submit_sync_cmd,
+};
+
+static void nvme_dev_map(struct nvme_dev *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev->dev);
+
+ dev->bar = pci_iomap(pdev, 0);
+}
+
+static void nvme_delete_queue(struct nvme_queue *nvmeq, u8 opcode)
+{
+ int ret;
+ ret = adapter_delete_queue(nvmeq->dev, opcode, nvmeq->qid);
+ if (ret < 0)
+ dev_err(nvmeq->dev->dev, "%s: %s\n", __func__,
+ strerror(-ret));
+ else if (ret)
+ dev_err(nvmeq->dev->dev,
+ "%s: status code type: %xh, status code %02xh\n",
+ __func__, (ret >> 8) & 0xf, ret & 0xff);
+}
+
+static void nvme_disable_io_queues(struct nvme_dev *dev)
+{
+ int i, queues = dev->online_queues - 1;
+
+ for (i = queues; i > 0; i--) {
+ nvme_delete_queue(&dev->queues[i], nvme_admin_delete_sq);
+ nvme_delete_queue(&dev->queues[i], nvme_admin_delete_cq);
+ }
+}
+
+static void nvme_disable_admin_queue(struct nvme_dev *dev)
+{
+ struct nvme_queue *nvmeq = &dev->queues[0];
+ u16 start, end;
+
+ nvme_shutdown_ctrl(&dev->ctrl);
+ nvme_process_cq(nvmeq, &start, &end, -1);
+ nvme_complete_cqes(nvmeq, start, end);
+}
+
+static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ struct nvme_dev *dev;
+ int result;
+
+ dev = xzalloc(sizeof(*dev));
+ dev->dev = &pdev->dev;
+ pdev->dev.priv = dev;
+
+ nvme_dev_map(dev);
+ result = nvme_init_ctrl(&dev->ctrl, &pdev->dev, &nvme_pci_ctrl_ops);
+ if (result)
+ return result;
+
+ nvme_reset_work(dev);
+
+ return 0;
+}
+
+static void nvme_remove(struct pci_dev *pdev)
+{
+ struct nvme_dev *dev = pdev->dev.priv;
+ bool dead = true;
+
+ u32 csts = readl(dev->bar + NVME_REG_CSTS);
+
+ dead = !!((csts & NVME_CSTS_CFS) || !(csts & NVME_CSTS_RDY));
+
+ if (!dead && dev->ctrl.queue_count > 0) {
+ nvme_disable_io_queues(dev);
+ nvme_disable_admin_queue(dev);
+ }
+}
+
+static const struct pci_device_id nvme_id_table[] = {
+ { PCI_DEVICE_CLASS(PCI_CLASS_STORAGE_EXPRESS, PCI_ANY_ID) },
+ { 0, },
+};
+
+static struct pci_driver nvme_driver = {
+ .name = "nvme",
+ .id_table = nvme_id_table,
+ .probe = nvme_probe,
+ .remove = nvme_remove,
+};
+device_pci_driver(nvme_driver);
diff --git a/drivers/pci/pci-imx6.c b/drivers/pci/pci-imx6.c
index 138b4ca8b3..85307bad3e 100644
--- a/drivers/pci/pci-imx6.c
+++ b/drivers/pci/pci-imx6.c
@@ -65,6 +65,7 @@ struct imx6_pcie {
struct clk *pcie_bus;
struct clk *pcie_phy;
struct clk *pcie;
+ struct clk *pcie_aux;
void __iomem *iomuxc_gpr;
u32 controller_id;
struct reset_control *pciephy_reset;
@@ -299,8 +300,10 @@ static unsigned int imx6_pcie_grp_offset(const struct imx6_pcie *imx6_pcie)
static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie)
{
+ struct device_d *dev = imx6_pcie->pci->dev;
u32 gpr1, gpr1x;
unsigned int offset;
+ int ret;
switch (imx6_pcie->drvdata->variant) {
case IMX6QP:
@@ -323,6 +326,12 @@ static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie)
case IMX7D:
break;
case IMX8MQ:
+ ret = clk_enable(imx6_pcie->pcie_aux);
+ if (ret) {
+ dev_err(dev, "unable to enable pcie_aux clock\n");
+ return ret;
+ }
+
offset = imx6_pcie_grp_offset(imx6_pcie);
/*
* Set the over ride low and enabled
@@ -742,6 +751,13 @@ static int imx6_pcie_probe(struct device_d *dev)
if (iores->start == IMX8MQ_PCIE2_BASE_ADDR)
imx6_pcie->controller_id = 1;
+ imx6_pcie->pcie_aux = clk_get(dev, "pcie_aux");
+ if (IS_ERR(imx6_pcie->pcie_aux)) {
+ dev_err(dev,
+ "pcie_aux clock source missing or invalid\n");
+ return PTR_ERR(imx6_pcie->pcie_aux);
+ }
+
goto imx7d_init;
case IMX7D:
imx6_pcie->iomuxc_gpr = IOMEM(MX7_IOMUXC_GPR_BASE_ADDR);
diff --git a/drivers/pci/pcie-designware.c b/drivers/pci/pcie-designware.c
index aaea316e90..c6d19559f4 100644
--- a/drivers/pci/pcie-designware.c
+++ b/drivers/pci/pcie-designware.c
@@ -195,7 +195,7 @@ int dw_pcie_wait_for_link(struct dw_pcie *pci)
/* Check if the link is up or not */
for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) {
if (dw_pcie_link_up(pci)) {
- dev_info(pci->dev, "Link up\n");
+ dev_dbg(pci->dev, "Link up\n");
return 0;
}
udelay(LINK_WAIT_USLEEP_MAX);
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index b0c8b9bf0e..b5cefb2ff3 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -22,4 +22,6 @@ config USB_NOP_XCEIV
built-in with usb ip or which are autonomous and doesn't require any
phy programming such as ISP1x04 etc.
+source "drivers/phy/freescale/Kconfig"
+
endif
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index 8fc85953b3..179c55e60a 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -4,3 +4,4 @@
obj-$(CONFIG_GENERIC_PHY) += phy-core.o
obj-$(CONFIG_USB_NOP_XCEIV) += usb-nop-xceiv.o
+obj-y += freescale/
diff --git a/drivers/phy/freescale/Kconfig b/drivers/phy/freescale/Kconfig
new file mode 100644
index 0000000000..8e56dd7e79
--- /dev/null
+++ b/drivers/phy/freescale/Kconfig
@@ -0,0 +1,4 @@
+config PHY_FSL_IMX8MQ_USB
+ bool "Freescale i.MX8M USB3 PHY"
+ default SOC_IMX8MQ
+
diff --git a/drivers/phy/freescale/Makefile b/drivers/phy/freescale/Makefile
new file mode 100644
index 0000000000..dc2b3f1f2f
--- /dev/null
+++ b/drivers/phy/freescale/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_PHY_FSL_IMX8MQ_USB) += phy-fsl-imx8mq-usb.o
diff --git a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
new file mode 100644
index 0000000000..1aef2b3004
--- /dev/null
+++ b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
@@ -0,0 +1,130 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (c) 2017 NXP. */
+
+#include <common.h>
+#include <init.h>
+#include <io.h>
+#include <of.h>
+#include <errno.h>
+#include <driver.h>
+#include <malloc.h>
+#include <usb/phy.h>
+#include <linux/phy/phy.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+
+
+#define PHY_CTRL0 0x0
+#define PHY_CTRL0_REF_SSP_EN BIT(2)
+
+#define PHY_CTRL1 0x4
+#define PHY_CTRL1_RESET BIT(0)
+#define PHY_CTRL1_COMMONONN BIT(1)
+#define PHY_CTRL1_ATERESET BIT(3)
+#define PHY_CTRL1_VDATSRCENB0 BIT(19)
+#define PHY_CTRL1_VDATDETENB0 BIT(20)
+
+#define PHY_CTRL2 0x8
+#define PHY_CTRL2_TXENABLEN0 BIT(8)
+
+struct imx8mq_usb_phy {
+ struct phy *phy;
+ struct clk *clk;
+ void __iomem *base;
+};
+
+static int imx8mq_usb_phy_init(struct phy *phy)
+{
+ struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy);
+ u32 value;
+
+ value = readl(imx_phy->base + PHY_CTRL1);
+ value &= ~(PHY_CTRL1_VDATSRCENB0 | PHY_CTRL1_VDATDETENB0 |
+ PHY_CTRL1_COMMONONN);
+ value |= PHY_CTRL1_RESET | PHY_CTRL1_ATERESET;
+ writel(value, imx_phy->base + PHY_CTRL1);
+
+ value = readl(imx_phy->base + PHY_CTRL0);
+ value |= PHY_CTRL0_REF_SSP_EN;
+ writel(value, imx_phy->base + PHY_CTRL0);
+
+ value = readl(imx_phy->base + PHY_CTRL2);
+ value |= PHY_CTRL2_TXENABLEN0;
+ writel(value, imx_phy->base + PHY_CTRL2);
+
+ value = readl(imx_phy->base + PHY_CTRL1);
+ value &= ~(PHY_CTRL1_RESET | PHY_CTRL1_ATERESET);
+ writel(value, imx_phy->base + PHY_CTRL1);
+
+ return 0;
+}
+
+static int imx8mq_phy_power_on(struct phy *phy)
+{
+ struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy);
+
+ return clk_enable(imx_phy->clk);
+}
+
+static int imx8mq_phy_power_off(struct phy *phy)
+{
+ struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy);
+
+ clk_disable(imx_phy->clk);
+
+ return 0;
+}
+
+static struct phy_ops imx8mq_usb_phy_ops = {
+ .init = imx8mq_usb_phy_init,
+ .power_on = imx8mq_phy_power_on,
+ .power_off = imx8mq_phy_power_off,
+};
+
+static struct phy *imx8mq_usb_phy_xlate(struct device_d *dev,
+ struct of_phandle_args *args)
+{
+ struct imx8mq_usb_phy *imx_phy = dev->priv;
+
+ return imx_phy->phy;
+}
+
+static int imx8mq_usb_phy_probe(struct device_d *dev)
+{
+ struct phy_provider *phy_provider;
+ struct imx8mq_usb_phy *imx_phy;
+
+ imx_phy = xzalloc(sizeof(*imx_phy));
+
+ dev->priv = imx_phy;
+
+ imx_phy->clk = clk_get(dev, "phy");
+ if (IS_ERR(imx_phy->clk))
+ return PTR_ERR(imx_phy->clk);
+
+ imx_phy->base = dev_get_mem_region(dev, 0);
+ if (IS_ERR(imx_phy->base))
+ return PTR_ERR(imx_phy->base);
+
+ imx_phy->phy = phy_create(dev, NULL, &imx8mq_usb_phy_ops, NULL);
+ if (IS_ERR(imx_phy->phy))
+ return PTR_ERR(imx_phy->phy);
+
+ phy_set_drvdata(imx_phy->phy, imx_phy);
+
+ phy_provider = of_phy_provider_register(dev, imx8mq_usb_phy_xlate);
+
+ return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static const struct of_device_id imx8mq_usb_phy_of_match[] = {
+ {.compatible = "fsl,imx8mq-usb-phy",},
+ { },
+};
+
+static struct driver_d imx8mq_usb_phy_driver = {
+ .name = "imx8mq-usb-phy",
+ .probe = imx8mq_usb_phy_probe,
+ .of_compatible = DRV_OF_COMPAT(imx8mq_usb_phy_of_match),
+};
+device_platform_driver(imx8mq_usb_phy_driver);
diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
index 9d6288fc07..066a887a22 100644
--- a/drivers/phy/phy-core.c
+++ b/drivers/phy/phy-core.c
@@ -227,7 +227,7 @@ static struct phy_provider *of_phy_provider_lookup(struct device_node *node)
return phy_provider;
}
- return ERR_PTR(-ENODEV);
+ return ERR_PTR(-EPROBE_DEFER);
}
/**
@@ -254,7 +254,7 @@ static struct phy *_of_phy_get(struct device_node *np, int index)
phy_provider = of_phy_provider_lookup(args.np);
if (IS_ERR(phy_provider)) {
- return ERR_PTR(-ENODEV);
+ return ERR_CAST(phy_provider);
}
return phy_provider->of_xlate(phy_provider->dev, &args);
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 8520a2fd9b..8ff3d18d4a 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -9,12 +9,16 @@ if USB_HOST
source drivers/usb/imx/Kconfig
+source "drivers/usb/dwc3/Kconfig"
+
source drivers/usb/host/Kconfig
source drivers/usb/otg/Kconfig
source drivers/usb/storage/Kconfig
+source "drivers/usb/misc/Kconfig"
+
endif
source drivers/usb/gadget/Kconfig
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 047f184800..9e98099502 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -1,8 +1,10 @@
obj-$(CONFIG_USB) += core/
obj-$(CONFIG_USB_IMX_CHIPIDEA) += imx/
+obj-$(CONFIG_USB_DWC3) += dwc3/
obj-$(CONFIG_USB_MUSB) += musb/
obj-$(CONFIG_USB_GADGET) += gadget/
obj-$(CONFIG_USB_STORAGE) += storage/
obj-y += host/
obj-y += otg/
+obj-$(CONFIG_USB) += misc/
diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
new file mode 100644
index 0000000000..c91fd36a95
--- /dev/null
+++ b/drivers/usb/dwc3/Kconfig
@@ -0,0 +1,22 @@
+config USB_DWC3
+ tristate "DesignWare USB3 DRD Core Support"
+ depends on USB && HAS_DMA
+ select USB_XHCI
+ select USB_DWC3_HOST # Remove this once we support more
+ # than USB host
+ help
+ Say Y or M here if your system has a Dual Role SuperSpeed
+ USB controller based on the DesignWare USB3 IP Core.
+
+ If you choose to build this driver is a dynamically linked
+ module, the module will be called dwc3.ko.
+
+if USB_DWC3
+
+config USB_DWC3_HOST
+ bool "Host only mode"
+ help
+ Select this when you want to use DWC3 in host mode only,
+ thereby the gadget feature will be regressed.
+
+endif
diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
new file mode 100644
index 0000000000..d43b23eb2d
--- /dev/null
+++ b/drivers/usb/dwc3/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_USB_DWC3) += dwc3.o
+
+dwc3-y := core.o
+
+ifneq ($(filter y,$(CONFIG_USB_DWC3_HOST)),)
+ dwc3-y += host.o
+endif
+
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
new file mode 100644
index 0000000000..2e7031a348
--- /dev/null
+++ b/drivers/usb/dwc3/core.c
@@ -0,0 +1,740 @@
+// SPDX-License-Identifier: GPL-2.0
+/**
+ * core.c - DesignWare USB3 DRD Controller Core file
+ *
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Authors: Felipe Balbi <balbi@ti.com>,
+ * Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+ */
+
+#include <common.h>
+#include <linux/clk.h>
+#include <linux/phy/phy.h>
+#include <driver.h>
+#include <init.h>
+
+#include "core.h"
+#include "io.h"
+
+
+#define DWC3_DEFAULT_AUTOSUSPEND_DELAY 5000 /* ms */
+
+void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode)
+{
+ u32 reg;
+
+ reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+ reg &= ~(DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG));
+ reg |= DWC3_GCTL_PRTCAPDIR(mode);
+ dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+
+ dwc->current_dr_role = mode;
+}
+
+/**
+ * dwc3_core_soft_reset - Issues core soft reset and PHY reset
+ * @dwc: pointer to our context structure
+ */
+static int dwc3_core_soft_reset(struct dwc3 *dwc)
+{
+ u32 reg;
+ int retries = 1000;
+ int ret;
+
+ ret = phy_init(dwc->usb2_generic_phy);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_init(dwc->usb3_generic_phy);
+ if (ret < 0) {
+ phy_exit(dwc->usb2_generic_phy);
+ return ret;
+ }
+
+ /*
+ * We're resetting only the device side because, if we're in host mode,
+ * XHCI driver will reset the host block. If dwc3 was configured for
+ * host-only mode, then we can return early.
+ */
+ if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST)
+ return 0;
+
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg |= DWC3_DCTL_CSFTRST;
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+
+ do {
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ if (!(reg & DWC3_DCTL_CSFTRST))
+ goto done;
+
+ udelay(1);
+ } while (--retries);
+
+ phy_exit(dwc->usb3_generic_phy);
+ phy_exit(dwc->usb2_generic_phy);
+
+ return -ETIMEDOUT;
+
+done:
+ /*
+ * For DWC_usb31 controller, once DWC3_DCTL_CSFTRST bit is cleared,
+ * we must wait at least 50ms before accessing the PHY domain
+ * (synchronization delay). DWC_usb31 programming guide section 1.3.2.
+ */
+ if (dwc3_is_usb31(dwc))
+ mdelay(50);
+
+ return 0;
+}
+
+static const struct clk_bulk_data dwc3_core_clks[] = {
+ { .id = "ref" },
+ { .id = "bus_early" },
+ { .id = "suspend" },
+};
+
+/*
+ * dwc3_frame_length_adjustment - Adjusts frame length if required
+ * @dwc3: Pointer to our controller context structure
+ */
+static void dwc3_frame_length_adjustment(struct dwc3 *dwc)
+{
+ u32 reg;
+ u32 dft;
+
+ if (dwc->revision < DWC3_REVISION_250A)
+ return;
+
+ if (dwc->fladj == 0)
+ return;
+
+ reg = dwc3_readl(dwc->regs, DWC3_GFLADJ);
+ dft = reg & DWC3_GFLADJ_30MHZ_MASK;
+ if (!WARN(dft == dwc->fladj,
+ "request value same as default, ignoring\n")) {
+ reg &= ~DWC3_GFLADJ_30MHZ_MASK;
+ reg |= DWC3_GFLADJ_30MHZ_SDBND_SEL | dwc->fladj;
+ dwc3_writel(dwc->regs, DWC3_GFLADJ, reg);
+ }
+}
+
+static void dwc3_core_num_eps(struct dwc3 *dwc)
+{
+ struct dwc3_hwparams *parms = &dwc->hwparams;
+
+ dwc->num_eps = DWC3_NUM_EPS(parms);
+}
+
+static void dwc3_cache_hwparams(struct dwc3 *dwc)
+{
+ struct dwc3_hwparams *parms = &dwc->hwparams;
+
+ parms->hwparams0 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS0);
+ parms->hwparams1 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS1);
+ parms->hwparams2 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS2);
+ parms->hwparams3 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS3);
+ parms->hwparams4 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS4);
+ parms->hwparams5 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS5);
+ parms->hwparams6 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS6);
+ parms->hwparams7 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS7);
+ parms->hwparams8 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS8);
+}
+
+/**
+ * dwc3_phy_setup - Configure USB PHY Interface of DWC3 Core
+ * @dwc: Pointer to our controller context structure
+ *
+ * Returns 0 on success. The USB PHY interfaces are configured but not
+ * initialized. The PHY interfaces and the PHYs get initialized together with
+ * the core in dwc3_core_init.
+ */
+static int dwc3_phy_setup(struct dwc3 *dwc)
+{
+ u32 reg;
+
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
+
+ /*
+ * Make sure UX_EXIT_PX is cleared as that causes issues with some
+ * PHYs. Also, this bit is not supposed to be used in normal operation.
+ */
+ reg &= ~DWC3_GUSB3PIPECTL_UX_EXIT_PX;
+
+ /*
+ * Above 1.94a, it is recommended to set DWC3_GUSB3PIPECTL_SUSPHY
+ * to '0' during coreConsultant configuration. So default value
+ * will be '0' when the core is reset. Application needs to set it
+ * to '1' after the core initialization is completed.
+ */
+ if (dwc->revision > DWC3_REVISION_194A)
+ reg |= DWC3_GUSB3PIPECTL_SUSPHY;
+
+ if (dwc->u2ss_inp3_quirk)
+ reg |= DWC3_GUSB3PIPECTL_U2SSINP3OK;
+
+ if (dwc->dis_rxdet_inp3_quirk)
+ reg |= DWC3_GUSB3PIPECTL_DISRXDETINP3;
+
+ if (dwc->req_p1p2p3_quirk)
+ reg |= DWC3_GUSB3PIPECTL_REQP1P2P3;
+
+ if (dwc->del_p1p2p3_quirk)
+ reg |= DWC3_GUSB3PIPECTL_DEP1P2P3_EN;
+
+ if (dwc->del_phy_power_chg_quirk)
+ reg |= DWC3_GUSB3PIPECTL_DEPOCHANGE;
+
+ if (dwc->lfps_filter_quirk)
+ reg |= DWC3_GUSB3PIPECTL_LFPSFILT;
+
+ if (dwc->rx_detect_poll_quirk)
+ reg |= DWC3_GUSB3PIPECTL_RX_DETOPOLL;
+
+ if (dwc->tx_de_emphasis_quirk)
+ reg |= DWC3_GUSB3PIPECTL_TX_DEEPH(dwc->tx_de_emphasis);
+
+ if (dwc->dis_u3_susphy_quirk)
+ reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
+
+ if (dwc->dis_del_phy_power_chg_quirk)
+ reg &= ~DWC3_GUSB3PIPECTL_DEPOCHANGE;
+
+ dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
+
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+
+ /* Select the HS PHY interface */
+ switch (DWC3_GHWPARAMS3_HSPHY_IFC(dwc->hwparams.hwparams3)) {
+ case DWC3_GHWPARAMS3_HSPHY_IFC_UTMI_ULPI:
+ if (dwc->hsphy_interface &&
+ !strncmp(dwc->hsphy_interface, "utmi", 4)) {
+ reg &= ~DWC3_GUSB2PHYCFG_ULPI_UTMI;
+ break;
+ } else if (dwc->hsphy_interface &&
+ !strncmp(dwc->hsphy_interface, "ulpi", 4)) {
+ reg |= DWC3_GUSB2PHYCFG_ULPI_UTMI;
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ } else {
+ /* Relying on default value. */
+ if (!(reg & DWC3_GUSB2PHYCFG_ULPI_UTMI))
+ break;
+ }
+ /* FALLTHROUGH */
+ case DWC3_GHWPARAMS3_HSPHY_IFC_ULPI:
+ /* FALLTHROUGH */
+ default:
+ break;
+ }
+
+ /*
+ * Above 1.94a, it is recommended to set DWC3_GUSB2PHYCFG_SUSPHY to
+ * '0' during coreConsultant configuration. So default value will
+ * be '0' when the core is reset. Application needs to set it to
+ * '1' after the core initialization is completed.
+ */
+ if (dwc->revision > DWC3_REVISION_194A)
+ reg |= DWC3_GUSB2PHYCFG_SUSPHY;
+
+ if (dwc->dis_u2_susphy_quirk)
+ reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
+
+ if (dwc->dis_enblslpm_quirk)
+ reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM;
+ else
+ reg |= DWC3_GUSB2PHYCFG_ENBLSLPM;
+
+ if (dwc->dis_u2_freeclk_exists_quirk)
+ reg &= ~DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS;
+
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+
+ return 0;
+}
+
+static void dwc3_core_exit(struct dwc3 *dwc)
+{
+ phy_exit(dwc->usb2_generic_phy);
+ phy_exit(dwc->usb3_generic_phy);
+
+ phy_power_off(dwc->usb2_generic_phy);
+ phy_power_off(dwc->usb3_generic_phy);
+ clk_bulk_disable(dwc->num_clks, dwc->clks);
+}
+
+static bool dwc3_core_is_valid(struct dwc3 *dwc)
+{
+ u32 reg;
+
+ reg = dwc3_readl(dwc->regs, DWC3_GSNPSID);
+
+ /* This should read as U3 followed by revision number */
+ if ((reg & DWC3_GSNPSID_MASK) == 0x55330000) {
+ /* Detected DWC_usb3 IP */
+ dwc->revision = reg;
+ } else if ((reg & DWC3_GSNPSID_MASK) == 0x33310000) {
+ /* Detected DWC_usb31 IP */
+ dwc->revision = dwc3_readl(dwc->regs, DWC3_VER_NUMBER);
+ dwc->revision |= DWC3_REVISION_IS_DWC31;
+ dwc->version_type = dwc3_readl(dwc->regs, DWC3_VER_TYPE);
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+static void dwc3_core_setup_global_control(struct dwc3 *dwc)
+{
+ u32 hwparams4 = dwc->hwparams.hwparams4;
+ u32 reg;
+
+ reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+ reg &= ~DWC3_GCTL_SCALEDOWN_MASK;
+
+ switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1)) {
+ case DWC3_GHWPARAMS1_EN_PWROPT_CLK:
+ /**
+ * WORKAROUND: DWC3 revisions between 2.10a and 2.50a have an
+ * issue which would cause xHCI compliance tests to fail.
+ *
+ * Because of that we cannot enable clock gating on such
+ * configurations.
+ *
+ * Refers to:
+ *
+ * STAR#9000588375: Clock Gating, SOF Issues when ref_clk-Based
+ * SOF/ITP Mode Used
+ */
+ if ((dwc->dr_mode == USB_DR_MODE_HOST ||
+ dwc->dr_mode == USB_DR_MODE_OTG) &&
+ (dwc->revision >= DWC3_REVISION_210A &&
+ dwc->revision <= DWC3_REVISION_250A))
+ reg |= DWC3_GCTL_DSBLCLKGTNG | DWC3_GCTL_SOFITPSYNC;
+ else
+ reg &= ~DWC3_GCTL_DSBLCLKGTNG;
+ break;
+ case DWC3_GHWPARAMS1_EN_PWROPT_HIB:
+ /* enable hibernation here */
+ dwc->nr_scratch = DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(hwparams4);
+
+ /*
+ * REVISIT Enabling this bit so that host-mode hibernation
+ * will work. Device-mode hibernation is not yet implemented.
+ */
+ reg |= DWC3_GCTL_GBLHIBERNATIONEN;
+ break;
+ default:
+ /* nothing */
+ break;
+ }
+
+ /* check if current dwc3 is on simulation board */
+ if (dwc->hwparams.hwparams6 & DWC3_GHWPARAMS6_EN_FPGA) {
+ dev_info(dwc->dev, "Running with FPGA optimizations\n");
+ dwc->is_fpga = true;
+ }
+
+ WARN(dwc->disable_scramble_quirk && !dwc->is_fpga,
+ "disable_scramble cannot be used on non-FPGA builds\n");
+
+ if (dwc->disable_scramble_quirk && dwc->is_fpga)
+ reg |= DWC3_GCTL_DISSCRAMBLE;
+ else
+ reg &= ~DWC3_GCTL_DISSCRAMBLE;
+
+ if (dwc->u2exit_lfps_quirk)
+ reg |= DWC3_GCTL_U2EXIT_LFPS;
+
+ /*
+ * WORKAROUND: DWC3 revisions <1.90a have a bug
+ * where the device can fail to connect at SuperSpeed
+ * and falls back to high-speed mode which causes
+ * the device to enter a Connect/Disconnect loop
+ */
+ if (dwc->revision < DWC3_REVISION_190A)
+ reg |= DWC3_GCTL_U2RSTECN;
+
+ dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+}
+
+static int dwc3_core_get_phy(struct dwc3 *dwc);
+
+/**
+ * dwc3_core_init - Low-level initialization of DWC3 Core
+ * @dwc: Pointer to our controller context structure
+ *
+ * Returns 0 on success otherwise negative errno.
+ */
+static int dwc3_core_init(struct dwc3 *dwc)
+{
+ u32 reg;
+ int ret;
+
+ if (!dwc3_core_is_valid(dwc)) {
+ dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n");
+ ret = -ENODEV;
+ goto err0;
+ }
+
+ /*
+ * Write Linux Version Code to our GUID register so it's easy to figure
+ * out which kernel version a bug was found.
+ */
+ dwc3_writel(dwc->regs, DWC3_GUID, 0xdeadbeef);
+
+ /* Handle USB2.0-only core configuration */
+ if (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) ==
+ DWC3_GHWPARAMS3_SSPHY_IFC_DIS) {
+ if (dwc->maximum_speed == USB_SPEED_SUPER)
+ dwc->maximum_speed = USB_SPEED_HIGH;
+ }
+
+ ret = dwc3_phy_setup(dwc);
+ if (ret)
+ goto err0;
+
+ if (!dwc->phys_ready) {
+ ret = dwc3_core_get_phy(dwc);
+ if (ret)
+ goto err0a;
+ dwc->phys_ready = true;
+ }
+
+ ret = dwc3_core_soft_reset(dwc);
+ if (ret)
+ goto err0a;
+
+ dwc3_core_setup_global_control(dwc);
+ dwc3_core_num_eps(dwc);
+
+ /* Adjust Frame Length */
+ dwc3_frame_length_adjustment(dwc);
+
+ ret = phy_power_on(dwc->usb2_generic_phy);
+ if (ret < 0)
+ goto err2;
+
+ ret = phy_power_on(dwc->usb3_generic_phy);
+ if (ret < 0)
+ goto err3;
+ /*
+ * ENDXFER polling is available on version 3.10a and later of
+ * the DWC_usb3 controller. It is NOT available in the
+ * DWC_usb31 controller.
+ */
+ if (!dwc3_is_usb31(dwc) && dwc->revision >= DWC3_REVISION_310A) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUCTL2);
+ reg |= DWC3_GUCTL2_RST_ACTBITLATER;
+ dwc3_writel(dwc->regs, DWC3_GUCTL2, reg);
+ }
+
+ if (dwc->revision >= DWC3_REVISION_250A) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUCTL1);
+
+ /*
+ * Enable hardware control of sending remote wakeup
+ * in HS when the device is in the L1 state.
+ */
+ if (dwc->revision >= DWC3_REVISION_290A)
+ reg |= DWC3_GUCTL1_DEV_L1_EXIT_BY_HW;
+
+ if (dwc->dis_tx_ipgap_linecheck_quirk)
+ reg |= DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS;
+
+ dwc3_writel(dwc->regs, DWC3_GUCTL1, reg);
+ }
+
+ if (dwc->dr_mode == USB_DR_MODE_HOST ||
+ dwc->dr_mode == USB_DR_MODE_OTG) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUCTL);
+
+ /*
+ * Enable Auto retry Feature to make the controller operating in
+ * Host mode on seeing transaction errors(CRC errors or internal
+ * overrun scenerios) on IN transfers to reply to the device
+ * with a non-terminating retry ACK (i.e, an ACK transcation
+ * packet with Retry=1 & Nump != 0)
+ */
+ reg |= DWC3_GUCTL_HSTINAUTORETRY;
+
+ dwc3_writel(dwc->regs, DWC3_GUCTL, reg);
+ }
+
+ /*
+ * Must config both number of packets and max burst settings to enable
+ * RX and/or TX threshold.
+ */
+ if (dwc3_is_usb31(dwc) && dwc->dr_mode == USB_DR_MODE_HOST) {
+ u8 rx_thr_num = dwc->rx_thr_num_pkt_prd;
+ u8 rx_maxburst = dwc->rx_max_burst_prd;
+ u8 tx_thr_num = dwc->tx_thr_num_pkt_prd;
+ u8 tx_maxburst = dwc->tx_max_burst_prd;
+
+ if (rx_thr_num && rx_maxburst) {
+ reg = dwc3_readl(dwc->regs, DWC3_GRXTHRCFG);
+ reg |= DWC31_RXTHRNUMPKTSEL_PRD;
+
+ reg &= ~DWC31_RXTHRNUMPKT_PRD(~0);
+ reg |= DWC31_RXTHRNUMPKT_PRD(rx_thr_num);
+
+ reg &= ~DWC31_MAXRXBURSTSIZE_PRD(~0);
+ reg |= DWC31_MAXRXBURSTSIZE_PRD(rx_maxburst);
+
+ dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg);
+ }
+
+ if (tx_thr_num && tx_maxburst) {
+ reg = dwc3_readl(dwc->regs, DWC3_GTXTHRCFG);
+ reg |= DWC31_TXTHRNUMPKTSEL_PRD;
+
+ reg &= ~DWC31_TXTHRNUMPKT_PRD(~0);
+ reg |= DWC31_TXTHRNUMPKT_PRD(tx_thr_num);
+
+ reg &= ~DWC31_MAXTXBURSTSIZE_PRD(~0);
+ reg |= DWC31_MAXTXBURSTSIZE_PRD(tx_maxburst);
+
+ dwc3_writel(dwc->regs, DWC3_GTXTHRCFG, reg);
+ }
+ }
+
+ return 0;
+
+/* err4: */
+ /* phy_power_off(dwc->usb3_generic_phy); */
+err3:
+ phy_power_off(dwc->usb2_generic_phy);
+err2:
+/* err1: */
+ phy_exit(dwc->usb2_generic_phy);
+ phy_exit(dwc->usb3_generic_phy);
+err0a:
+err0:
+ return ret;
+}
+
+static int dwc3_core_get_phy(struct dwc3 *dwc)
+{
+ struct device_d *dev = dwc->dev;
+ int ret;
+
+ dwc->usb2_generic_phy = phy_get(dev, "usb2-phy");
+ if (IS_ERR(dwc->usb2_generic_phy)) {
+ ret = PTR_ERR(dwc->usb2_generic_phy);
+ if (ret == -ENOSYS || ret == -ENODEV) {
+ dev_err(dev, "no usb2 phy configured\n");
+ dwc->usb2_generic_phy = NULL;
+ } else if (ret == -EPROBE_DEFER) {
+ return ret;
+ } else {
+ dev_err(dev, "no usb2 phy configured\n");
+ return ret;
+ }
+ }
+
+ dwc->usb3_generic_phy = phy_get(dev, "usb3-phy");
+ if (IS_ERR(dwc->usb3_generic_phy)) {
+ ret = PTR_ERR(dwc->usb3_generic_phy);
+ if (ret == -ENOSYS || ret == -ENODEV) {
+ dev_err(dev, "no usb2 phy configured\n");
+ dwc->usb3_generic_phy = NULL;
+ } else if (ret == -EPROBE_DEFER) {
+ return ret;
+ } else {
+ dev_err(dev, "no usb3 phy configured\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int dwc3_core_init_mode(struct dwc3 *dwc)
+{
+ struct device_d *dev = dwc->dev;
+ int ret;
+
+ switch (dwc->dr_mode) {
+ case USB_DR_MODE_HOST:
+ dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST);
+
+ ret = dwc3_host_init(dwc);
+ if (ret) {
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "failed to initialize host\n");
+ return ret;
+ }
+ break;
+ default:
+ dev_err(dev, "Unsupported mode of operation %d\n", dwc->dr_mode);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void dwc3_get_properties(struct dwc3 *dwc)
+{
+ struct device_d *dev = dwc->dev;
+ u8 lpm_nyet_threshold;
+ u8 tx_de_emphasis;
+ u8 hird_threshold;
+
+ /* default to highest possible threshold */
+ lpm_nyet_threshold = 0xff;
+
+ /* default to -3.5dB de-emphasis */
+ tx_de_emphasis = 1;
+
+ /*
+ * default to assert utmi_sleep_n and use maximum allowed HIRD
+ * threshold value of 0b1100
+ */
+ hird_threshold = 12;
+
+ dwc->maximum_speed = of_usb_get_maximum_speed(dev->device_node, NULL);
+ dwc->dr_mode = of_usb_get_dr_mode(dev->device_node, NULL);
+
+ dwc->lpm_nyet_threshold = lpm_nyet_threshold;
+ dwc->tx_de_emphasis = tx_de_emphasis;
+
+ dwc->hird_threshold = hird_threshold
+ | (dwc->is_utmi_l1_suspend << 4);
+
+ dwc->imod_interval = 0;
+}
+
+/* check whether the core supports IMOD */
+bool dwc3_has_imod(struct dwc3 *dwc)
+{
+ return ((dwc3_is_usb3(dwc) &&
+ dwc->revision >= DWC3_REVISION_300A) ||
+ (dwc3_is_usb31(dwc) &&
+ dwc->revision >= DWC3_USB31_REVISION_120A));
+}
+
+static void dwc3_check_params(struct dwc3 *dwc)
+{
+ struct device_d *dev = dwc->dev;
+
+ /* Check for proper value of imod_interval */
+ if (dwc->imod_interval && !dwc3_has_imod(dwc)) {
+ dev_warn(dwc->dev, "Interrupt moderation not supported\n");
+ dwc->imod_interval = 0;
+ }
+
+ /*
+ * Workaround for STAR 9000961433 which affects only version
+ * 3.00a of the DWC_usb3 core. This prevents the controller
+ * interrupt from being masked while handling events. IMOD
+ * allows us to work around this issue. Enable it for the
+ * affected version.
+ */
+ if (!dwc->imod_interval &&
+ (dwc->revision == DWC3_REVISION_300A))
+ dwc->imod_interval = 1;
+
+ /* Check the maximum_speed parameter */
+ switch (dwc->maximum_speed) {
+ case USB_SPEED_LOW:
+ case USB_SPEED_FULL:
+ case USB_SPEED_HIGH:
+ case USB_SPEED_SUPER:
+ case USB_SPEED_SUPER_PLUS:
+ break;
+ default:
+ dev_err(dev, "invalid maximum_speed parameter %d\n",
+ dwc->maximum_speed);
+ /* fall through */
+ case USB_SPEED_UNKNOWN:
+ /* default to superspeed */
+ dwc->maximum_speed = USB_SPEED_SUPER;
+
+ /*
+ * default to superspeed plus if we are capable.
+ */
+ if (dwc3_is_usb31(dwc) &&
+ (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) ==
+ DWC3_GHWPARAMS3_SSPHY_IFC_GEN2))
+ dwc->maximum_speed = USB_SPEED_SUPER_PLUS;
+
+ break;
+ }
+}
+
+static int dwc3_probe(struct device_d *dev)
+{
+ struct dwc3 *dwc;
+ int ret;
+
+ dwc = xzalloc(sizeof(*dwc));
+ dev->priv = dwc;
+
+ dwc->clks = xmemdup(dwc3_core_clks, sizeof(dwc3_core_clks));
+ dwc->dev = dev;
+ dwc->regs = dev_get_mem_region(dwc->dev, 0) + DWC3_GLOBALS_REGS_START;
+
+ dwc3_get_properties(dwc);
+
+ if (dev->device_node) {
+ dwc->num_clks = ARRAY_SIZE(dwc3_core_clks);
+
+ ret = clk_bulk_get(dev, dwc->num_clks, dwc->clks);
+ if (ret == -EPROBE_DEFER)
+ return ret;
+ /*
+ * Clocks are optional, but new DT platforms should support all
+ * clocks as required by the DT-binding.
+ */
+ if (ret)
+ dwc->num_clks = 0;
+ }
+
+ ret = clk_bulk_enable(dwc->num_clks, dwc->clks);
+ if (ret)
+ return ret;
+
+ dwc3_cache_hwparams(dwc);
+
+ ret = dwc3_core_init(dwc);
+ if (ret) {
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "failed to initialize core: %d\n", ret);
+ return ret;
+ }
+
+ dwc3_check_params(dwc);
+
+ ret = dwc3_core_init_mode(dwc);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void dwc3_remove(struct device_d *dev)
+{
+ struct dwc3 *dwc = dev->priv;
+
+ dwc3_core_exit(dwc);
+ clk_bulk_put(dwc->num_clks, dwc->clks);
+}
+
+static const struct of_device_id of_dwc3_match[] = {
+ {
+ .compatible = "snps,dwc3"
+ },
+ {
+ .compatible = "synopsys,dwc3"
+ },
+ { },
+};
+
+static struct driver_d dwc3_driver = {
+ .probe = dwc3_probe,
+ .remove = dwc3_remove,
+ .name = "dwc3",
+ .of_compatible = DRV_OF_COMPAT(of_dwc3_match),
+};
+device_platform_driver(dwc3_driver);
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
new file mode 100644
index 0000000000..a404e4cd6a
--- /dev/null
+++ b/drivers/usb/dwc3/core.h
@@ -0,0 +1,1267 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * core.h - DesignWare USB3 DRD Core Header
+ *
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Authors: Felipe Balbi <balbi@ti.com>,
+ * Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+ */
+
+#ifndef __DRIVERS_USB_DWC3_CORE_H
+#define __DRIVERS_USB_DWC3_CORE_H
+
+#include <usb/usb.h>
+
+#define DWC3_MSG_MAX 500
+
+/* Global constants */
+#define DWC3_PULL_UP_TIMEOUT 500 /* ms */
+#define DWC3_BOUNCE_SIZE 1024 /* size of a superspeed bulk */
+#define DWC3_EP0_SETUP_SIZE 512
+#define DWC3_ENDPOINTS_NUM 32
+#define DWC3_XHCI_RESOURCES_NUM 2
+#define DWC3_ISOC_MAX_RETRIES 5
+
+#define DWC3_SCRATCHBUF_SIZE 4096 /* each buffer is assumed to be 4KiB */
+#define DWC3_EVENT_BUFFERS_SIZE 4096
+#define DWC3_EVENT_TYPE_MASK 0xfe
+
+#define DWC3_EVENT_TYPE_DEV 0
+#define DWC3_EVENT_TYPE_CARKIT 3
+#define DWC3_EVENT_TYPE_I2C 4
+
+#define DWC3_DEVICE_EVENT_DISCONNECT 0
+#define DWC3_DEVICE_EVENT_RESET 1
+#define DWC3_DEVICE_EVENT_CONNECT_DONE 2
+#define DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE 3
+#define DWC3_DEVICE_EVENT_WAKEUP 4
+#define DWC3_DEVICE_EVENT_HIBER_REQ 5
+#define DWC3_DEVICE_EVENT_EOPF 6
+#define DWC3_DEVICE_EVENT_SOF 7
+#define DWC3_DEVICE_EVENT_ERRATIC_ERROR 9
+#define DWC3_DEVICE_EVENT_CMD_CMPL 10
+#define DWC3_DEVICE_EVENT_OVERFLOW 11
+
+/* Controller's role while using the OTG block */
+#define DWC3_OTG_ROLE_IDLE 0
+#define DWC3_OTG_ROLE_HOST 1
+#define DWC3_OTG_ROLE_DEVICE 2
+
+#define DWC3_GEVNTCOUNT_MASK 0xfffc
+#define DWC3_GEVNTCOUNT_EHB BIT(31)
+#define DWC3_GSNPSID_MASK 0xffff0000
+#define DWC3_GSNPSREV_MASK 0xffff
+
+/* DWC3 registers memory space boundries */
+#define DWC3_XHCI_REGS_START 0x0
+#define DWC3_XHCI_REGS_END 0x7fff
+#define DWC3_GLOBALS_REGS_START 0xc100
+#define DWC3_GLOBALS_REGS_END 0xc6ff
+#define DWC3_DEVICE_REGS_START 0xc700
+#define DWC3_DEVICE_REGS_END 0xcbff
+#define DWC3_OTG_REGS_START 0xcc00
+#define DWC3_OTG_REGS_END 0xccff
+
+/* Global Registers */
+#define DWC3_GSBUSCFG0 0xc100
+#define DWC3_GSBUSCFG1 0xc104
+#define DWC3_GTXTHRCFG 0xc108
+#define DWC3_GRXTHRCFG 0xc10c
+#define DWC3_GCTL 0xc110
+#define DWC3_GEVTEN 0xc114
+#define DWC3_GSTS 0xc118
+#define DWC3_GUCTL1 0xc11c
+#define DWC3_GSNPSID 0xc120
+#define DWC3_GGPIO 0xc124
+#define DWC3_GUID 0xc128
+#define DWC3_GUCTL 0xc12c
+#define DWC3_GBUSERRADDR0 0xc130
+#define DWC3_GBUSERRADDR1 0xc134
+#define DWC3_GPRTBIMAP0 0xc138
+#define DWC3_GPRTBIMAP1 0xc13c
+#define DWC3_GHWPARAMS0 0xc140
+#define DWC3_GHWPARAMS1 0xc144
+#define DWC3_GHWPARAMS2 0xc148
+#define DWC3_GHWPARAMS3 0xc14c
+#define DWC3_GHWPARAMS4 0xc150
+#define DWC3_GHWPARAMS5 0xc154
+#define DWC3_GHWPARAMS6 0xc158
+#define DWC3_GHWPARAMS7 0xc15c
+#define DWC3_GDBGFIFOSPACE 0xc160
+#define DWC3_GDBGLTSSM 0xc164
+#define DWC3_GDBGBMU 0xc16c
+#define DWC3_GDBGLSPMUX 0xc170
+#define DWC3_GDBGLSP 0xc174
+#define DWC3_GDBGEPINFO0 0xc178
+#define DWC3_GDBGEPINFO1 0xc17c
+#define DWC3_GPRTBIMAP_HS0 0xc180
+#define DWC3_GPRTBIMAP_HS1 0xc184
+#define DWC3_GPRTBIMAP_FS0 0xc188
+#define DWC3_GPRTBIMAP_FS1 0xc18c
+#define DWC3_GUCTL2 0xc19c
+
+#define DWC3_VER_NUMBER 0xc1a0
+#define DWC3_VER_TYPE 0xc1a4
+
+#define DWC3_GUSB2PHYCFG(n) (0xc200 + ((n) * 0x04))
+#define DWC3_GUSB2I2CCTL(n) (0xc240 + ((n) * 0x04))
+
+#define DWC3_GUSB2PHYACC(n) (0xc280 + ((n) * 0x04))
+
+#define DWC3_GUSB3PIPECTL(n) (0xc2c0 + ((n) * 0x04))
+
+#define DWC3_GTXFIFOSIZ(n) (0xc300 + ((n) * 0x04))
+#define DWC3_GRXFIFOSIZ(n) (0xc380 + ((n) * 0x04))
+
+#define DWC3_GEVNTADRLO(n) (0xc400 + ((n) * 0x10))
+#define DWC3_GEVNTADRHI(n) (0xc404 + ((n) * 0x10))
+#define DWC3_GEVNTSIZ(n) (0xc408 + ((n) * 0x10))
+#define DWC3_GEVNTCOUNT(n) (0xc40c + ((n) * 0x10))
+
+#define DWC3_GHWPARAMS8 0xc600
+#define DWC3_GFLADJ 0xc630
+
+/* Device Registers */
+#define DWC3_DCFG 0xc700
+#define DWC3_DCTL 0xc704
+#define DWC3_DEVTEN 0xc708
+#define DWC3_DSTS 0xc70c
+#define DWC3_DGCMDPAR 0xc710
+#define DWC3_DGCMD 0xc714
+#define DWC3_DALEPENA 0xc720
+
+#define DWC3_DEP_BASE(n) (0xc800 + ((n) * 0x10))
+#define DWC3_DEPCMDPAR2 0x00
+#define DWC3_DEPCMDPAR1 0x04
+#define DWC3_DEPCMDPAR0 0x08
+#define DWC3_DEPCMD 0x0c
+
+#define DWC3_DEV_IMOD(n) (0xca00 + ((n) * 0x4))
+
+/* OTG Registers */
+#define DWC3_OCFG 0xcc00
+#define DWC3_OCTL 0xcc04
+#define DWC3_OEVT 0xcc08
+#define DWC3_OEVTEN 0xcc0C
+#define DWC3_OSTS 0xcc10
+
+/* Bit fields */
+
+/* Global SoC Bus Configuration INCRx Register 0 */
+#define DWC3_GSBUSCFG0_INCR256BRSTENA (1 << 7) /* INCR256 burst */
+#define DWC3_GSBUSCFG0_INCR128BRSTENA (1 << 6) /* INCR128 burst */
+#define DWC3_GSBUSCFG0_INCR64BRSTENA (1 << 5) /* INCR64 burst */
+#define DWC3_GSBUSCFG0_INCR32BRSTENA (1 << 4) /* INCR32 burst */
+#define DWC3_GSBUSCFG0_INCR16BRSTENA (1 << 3) /* INCR16 burst */
+#define DWC3_GSBUSCFG0_INCR8BRSTENA (1 << 2) /* INCR8 burst */
+#define DWC3_GSBUSCFG0_INCR4BRSTENA (1 << 1) /* INCR4 burst */
+#define DWC3_GSBUSCFG0_INCRBRSTENA (1 << 0) /* undefined length enable */
+#define DWC3_GSBUSCFG0_INCRBRST_MASK 0xff
+
+/* Global Debug LSP MUX Select */
+#define DWC3_GDBGLSPMUX_ENDBC BIT(15) /* Host only */
+#define DWC3_GDBGLSPMUX_HOSTSELECT(n) ((n) & 0x3fff)
+#define DWC3_GDBGLSPMUX_DEVSELECT(n) (((n) & 0xf) << 4)
+#define DWC3_GDBGLSPMUX_EPSELECT(n) ((n) & 0xf)
+
+/* Global Debug Queue/FIFO Space Available Register */
+#define DWC3_GDBGFIFOSPACE_NUM(n) ((n) & 0x1f)
+#define DWC3_GDBGFIFOSPACE_TYPE(n) (((n) << 5) & 0x1e0)
+#define DWC3_GDBGFIFOSPACE_SPACE_AVAILABLE(n) (((n) >> 16) & 0xffff)
+
+#define DWC3_TXFIFO 0
+#define DWC3_RXFIFO 1
+#define DWC3_TXREQQ 2
+#define DWC3_RXREQQ 3
+#define DWC3_RXINFOQ 4
+#define DWC3_PSTATQ 5
+#define DWC3_DESCFETCHQ 6
+#define DWC3_EVENTQ 7
+#define DWC3_AUXEVENTQ 8
+
+/* Global RX Threshold Configuration Register */
+#define DWC3_GRXTHRCFG_MAXRXBURSTSIZE(n) (((n) & 0x1f) << 19)
+#define DWC3_GRXTHRCFG_RXPKTCNT(n) (((n) & 0xf) << 24)
+#define DWC3_GRXTHRCFG_PKTCNTSEL BIT(29)
+
+/* Global RX Threshold Configuration Register for DWC_usb31 only */
+#define DWC31_GRXTHRCFG_MAXRXBURSTSIZE(n) (((n) & 0x1f) << 16)
+#define DWC31_GRXTHRCFG_RXPKTCNT(n) (((n) & 0x1f) << 21)
+#define DWC31_GRXTHRCFG_PKTCNTSEL BIT(26)
+#define DWC31_RXTHRNUMPKTSEL_HS_PRD BIT(15)
+#define DWC31_RXTHRNUMPKT_HS_PRD(n) (((n) & 0x3) << 13)
+#define DWC31_RXTHRNUMPKTSEL_PRD BIT(10)
+#define DWC31_RXTHRNUMPKT_PRD(n) (((n) & 0x1f) << 5)
+#define DWC31_MAXRXBURSTSIZE_PRD(n) ((n) & 0x1f)
+
+/* Global TX Threshold Configuration Register for DWC_usb31 only */
+#define DWC31_GTXTHRCFG_MAXTXBURSTSIZE(n) (((n) & 0x1f) << 16)
+#define DWC31_GTXTHRCFG_TXPKTCNT(n) (((n) & 0x1f) << 21)
+#define DWC31_GTXTHRCFG_PKTCNTSEL BIT(26)
+#define DWC31_TXTHRNUMPKTSEL_HS_PRD BIT(15)
+#define DWC31_TXTHRNUMPKT_HS_PRD(n) (((n) & 0x3) << 13)
+#define DWC31_TXTHRNUMPKTSEL_PRD BIT(10)
+#define DWC31_TXTHRNUMPKT_PRD(n) (((n) & 0x1f) << 5)
+#define DWC31_MAXTXBURSTSIZE_PRD(n) ((n) & 0x1f)
+
+/* Global Configuration Register */
+#define DWC3_GCTL_PWRDNSCALE(n) ((n) << 19)
+#define DWC3_GCTL_U2RSTECN BIT(16)
+#define DWC3_GCTL_RAMCLKSEL(x) (((x) & DWC3_GCTL_CLK_MASK) << 6)
+#define DWC3_GCTL_CLK_BUS (0)
+#define DWC3_GCTL_CLK_PIPE (1)
+#define DWC3_GCTL_CLK_PIPEHALF (2)
+#define DWC3_GCTL_CLK_MASK (3)
+
+#define DWC3_GCTL_PRTCAP(n) (((n) & (3 << 12)) >> 12)
+#define DWC3_GCTL_PRTCAPDIR(n) ((n) << 12)
+#define DWC3_GCTL_PRTCAP_HOST 1
+#define DWC3_GCTL_PRTCAP_DEVICE 2
+#define DWC3_GCTL_PRTCAP_OTG 3
+
+#define DWC3_GCTL_CORESOFTRESET BIT(11)
+#define DWC3_GCTL_SOFITPSYNC BIT(10)
+#define DWC3_GCTL_SCALEDOWN(n) ((n) << 4)
+#define DWC3_GCTL_SCALEDOWN_MASK DWC3_GCTL_SCALEDOWN(3)
+#define DWC3_GCTL_DISSCRAMBLE BIT(3)
+#define DWC3_GCTL_U2EXIT_LFPS BIT(2)
+#define DWC3_GCTL_GBLHIBERNATIONEN BIT(1)
+#define DWC3_GCTL_DSBLCLKGTNG BIT(0)
+
+/* Global User Control Register */
+#define DWC3_GUCTL_HSTINAUTORETRY BIT(14)
+
+/* Global User Control 1 Register */
+#define DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS BIT(28)
+#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24)
+
+/* Global Status Register */
+#define DWC3_GSTS_OTG_IP BIT(10)
+#define DWC3_GSTS_BC_IP BIT(9)
+#define DWC3_GSTS_ADP_IP BIT(8)
+#define DWC3_GSTS_HOST_IP BIT(7)
+#define DWC3_GSTS_DEVICE_IP BIT(6)
+#define DWC3_GSTS_CSR_TIMEOUT BIT(5)
+#define DWC3_GSTS_BUS_ERR_ADDR_VLD BIT(4)
+#define DWC3_GSTS_CURMOD(n) ((n) & 0x3)
+#define DWC3_GSTS_CURMOD_DEVICE 0
+#define DWC3_GSTS_CURMOD_HOST 1
+
+/* Global USB2 PHY Configuration Register */
+#define DWC3_GUSB2PHYCFG_PHYSOFTRST BIT(31)
+#define DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS BIT(30)
+#define DWC3_GUSB2PHYCFG_SUSPHY BIT(6)
+#define DWC3_GUSB2PHYCFG_ULPI_UTMI BIT(4)
+#define DWC3_GUSB2PHYCFG_ENBLSLPM BIT(8)
+#define DWC3_GUSB2PHYCFG_PHYIF(n) (n << 3)
+#define DWC3_GUSB2PHYCFG_PHYIF_MASK DWC3_GUSB2PHYCFG_PHYIF(1)
+#define DWC3_GUSB2PHYCFG_USBTRDTIM(n) (n << 10)
+#define DWC3_GUSB2PHYCFG_USBTRDTIM_MASK DWC3_GUSB2PHYCFG_USBTRDTIM(0xf)
+#define USBTRDTIM_UTMI_8_BIT 9
+#define USBTRDTIM_UTMI_16_BIT 5
+#define UTMI_PHYIF_16_BIT 1
+#define UTMI_PHYIF_8_BIT 0
+
+/* Global USB2 PHY Vendor Control Register */
+#define DWC3_GUSB2PHYACC_NEWREGREQ BIT(25)
+#define DWC3_GUSB2PHYACC_BUSY BIT(23)
+#define DWC3_GUSB2PHYACC_WRITE BIT(22)
+#define DWC3_GUSB2PHYACC_ADDR(n) (n << 16)
+#define DWC3_GUSB2PHYACC_EXTEND_ADDR(n) (n << 8)
+#define DWC3_GUSB2PHYACC_DATA(n) (n & 0xff)
+
+/* Global USB3 PIPE Control Register */
+#define DWC3_GUSB3PIPECTL_PHYSOFTRST BIT(31)
+#define DWC3_GUSB3PIPECTL_U2SSINP3OK BIT(29)
+#define DWC3_GUSB3PIPECTL_DISRXDETINP3 BIT(28)
+#define DWC3_GUSB3PIPECTL_UX_EXIT_PX BIT(27)
+#define DWC3_GUSB3PIPECTL_REQP1P2P3 BIT(24)
+#define DWC3_GUSB3PIPECTL_DEP1P2P3(n) ((n) << 19)
+#define DWC3_GUSB3PIPECTL_DEP1P2P3_MASK DWC3_GUSB3PIPECTL_DEP1P2P3(7)
+#define DWC3_GUSB3PIPECTL_DEP1P2P3_EN DWC3_GUSB3PIPECTL_DEP1P2P3(1)
+#define DWC3_GUSB3PIPECTL_DEPOCHANGE BIT(18)
+#define DWC3_GUSB3PIPECTL_SUSPHY BIT(17)
+#define DWC3_GUSB3PIPECTL_LFPSFILT BIT(9)
+#define DWC3_GUSB3PIPECTL_RX_DETOPOLL BIT(8)
+#define DWC3_GUSB3PIPECTL_TX_DEEPH_MASK DWC3_GUSB3PIPECTL_TX_DEEPH(3)
+#define DWC3_GUSB3PIPECTL_TX_DEEPH(n) ((n) << 1)
+
+/* Global TX Fifo Size Register */
+#define DWC31_GTXFIFOSIZ_TXFRAMNUM BIT(15) /* DWC_usb31 only */
+#define DWC31_GTXFIFOSIZ_TXFDEF(n) ((n) & 0x7fff) /* DWC_usb31 only */
+#define DWC3_GTXFIFOSIZ_TXFDEF(n) ((n) & 0xffff)
+#define DWC3_GTXFIFOSIZ_TXFSTADDR(n) ((n) & 0xffff0000)
+
+/* Global Event Size Registers */
+#define DWC3_GEVNTSIZ_INTMASK BIT(31)
+#define DWC3_GEVNTSIZ_SIZE(n) ((n) & 0xffff)
+
+/* Global HWPARAMS0 Register */
+#define DWC3_GHWPARAMS0_MODE(n) ((n) & 0x3)
+#define DWC3_GHWPARAMS0_MODE_GADGET 0
+#define DWC3_GHWPARAMS0_MODE_HOST 1
+#define DWC3_GHWPARAMS0_MODE_DRD 2
+#define DWC3_GHWPARAMS0_MBUS_TYPE(n) (((n) >> 3) & 0x7)
+#define DWC3_GHWPARAMS0_SBUS_TYPE(n) (((n) >> 6) & 0x3)
+#define DWC3_GHWPARAMS0_MDWIDTH(n) (((n) >> 8) & 0xff)
+#define DWC3_GHWPARAMS0_SDWIDTH(n) (((n) >> 16) & 0xff)
+#define DWC3_GHWPARAMS0_AWIDTH(n) (((n) >> 24) & 0xff)
+
+/* Global HWPARAMS1 Register */
+#define DWC3_GHWPARAMS1_EN_PWROPT(n) (((n) & (3 << 24)) >> 24)
+#define DWC3_GHWPARAMS1_EN_PWROPT_NO 0
+#define DWC3_GHWPARAMS1_EN_PWROPT_CLK 1
+#define DWC3_GHWPARAMS1_EN_PWROPT_HIB 2
+#define DWC3_GHWPARAMS1_PWROPT(n) ((n) << 24)
+#define DWC3_GHWPARAMS1_PWROPT_MASK DWC3_GHWPARAMS1_PWROPT(3)
+#define DWC3_GHWPARAMS1_ENDBC BIT(31)
+
+/* Global HWPARAMS3 Register */
+#define DWC3_GHWPARAMS3_SSPHY_IFC(n) ((n) & 3)
+#define DWC3_GHWPARAMS3_SSPHY_IFC_DIS 0
+#define DWC3_GHWPARAMS3_SSPHY_IFC_GEN1 1
+#define DWC3_GHWPARAMS3_SSPHY_IFC_GEN2 2 /* DWC_usb31 only */
+#define DWC3_GHWPARAMS3_HSPHY_IFC(n) (((n) & (3 << 2)) >> 2)
+#define DWC3_GHWPARAMS3_HSPHY_IFC_DIS 0
+#define DWC3_GHWPARAMS3_HSPHY_IFC_UTMI 1
+#define DWC3_GHWPARAMS3_HSPHY_IFC_ULPI 2
+#define DWC3_GHWPARAMS3_HSPHY_IFC_UTMI_ULPI 3
+#define DWC3_GHWPARAMS3_FSPHY_IFC(n) (((n) & (3 << 4)) >> 4)
+#define DWC3_GHWPARAMS3_FSPHY_IFC_DIS 0
+#define DWC3_GHWPARAMS3_FSPHY_IFC_ENA 1
+
+/* Global HWPARAMS4 Register */
+#define DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(n) (((n) & (0x0f << 13)) >> 13)
+#define DWC3_MAX_HIBER_SCRATCHBUFS 15
+
+/* Global HWPARAMS6 Register */
+#define DWC3_GHWPARAMS6_BCSUPPORT BIT(14)
+#define DWC3_GHWPARAMS6_OTG3SUPPORT BIT(13)
+#define DWC3_GHWPARAMS6_ADPSUPPORT BIT(12)
+#define DWC3_GHWPARAMS6_HNPSUPPORT BIT(11)
+#define DWC3_GHWPARAMS6_SRPSUPPORT BIT(10)
+#define DWC3_GHWPARAMS6_EN_FPGA BIT(7)
+
+/* Global HWPARAMS7 Register */
+#define DWC3_GHWPARAMS7_RAM1_DEPTH(n) ((n) & 0xffff)
+#define DWC3_GHWPARAMS7_RAM2_DEPTH(n) (((n) >> 16) & 0xffff)
+
+/* Global Frame Length Adjustment Register */
+#define DWC3_GFLADJ_30MHZ_SDBND_SEL BIT(7)
+#define DWC3_GFLADJ_30MHZ_MASK 0x3f
+
+/* Global User Control Register 2 */
+#define DWC3_GUCTL2_RST_ACTBITLATER BIT(14)
+
+/* Device Configuration Register */
+#define DWC3_DCFG_DEVADDR(addr) ((addr) << 3)
+#define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f)
+
+#define DWC3_DCFG_SPEED_MASK (7 << 0)
+#define DWC3_DCFG_SUPERSPEED_PLUS (5 << 0) /* DWC_usb31 only */
+#define DWC3_DCFG_SUPERSPEED (4 << 0)
+#define DWC3_DCFG_HIGHSPEED (0 << 0)
+#define DWC3_DCFG_FULLSPEED BIT(0)
+#define DWC3_DCFG_LOWSPEED (2 << 0)
+
+#define DWC3_DCFG_NUMP_SHIFT 17
+#define DWC3_DCFG_NUMP(n) (((n) >> DWC3_DCFG_NUMP_SHIFT) & 0x1f)
+#define DWC3_DCFG_NUMP_MASK (0x1f << DWC3_DCFG_NUMP_SHIFT)
+#define DWC3_DCFG_LPM_CAP BIT(22)
+
+/* Device Control Register */
+#define DWC3_DCTL_RUN_STOP BIT(31)
+#define DWC3_DCTL_CSFTRST BIT(30)
+#define DWC3_DCTL_LSFTRST BIT(29)
+
+#define DWC3_DCTL_HIRD_THRES_MASK (0x1f << 24)
+#define DWC3_DCTL_HIRD_THRES(n) ((n) << 24)
+
+#define DWC3_DCTL_APPL1RES BIT(23)
+
+/* These apply for core versions 1.87a and earlier */
+#define DWC3_DCTL_TRGTULST_MASK (0x0f << 17)
+#define DWC3_DCTL_TRGTULST(n) ((n) << 17)
+#define DWC3_DCTL_TRGTULST_U2 (DWC3_DCTL_TRGTULST(2))
+#define DWC3_DCTL_TRGTULST_U3 (DWC3_DCTL_TRGTULST(3))
+#define DWC3_DCTL_TRGTULST_SS_DIS (DWC3_DCTL_TRGTULST(4))
+#define DWC3_DCTL_TRGTULST_RX_DET (DWC3_DCTL_TRGTULST(5))
+#define DWC3_DCTL_TRGTULST_SS_INACT (DWC3_DCTL_TRGTULST(6))
+
+/* These apply for core versions 1.94a and later */
+#define DWC3_DCTL_LPM_ERRATA_MASK DWC3_DCTL_LPM_ERRATA(0xf)
+#define DWC3_DCTL_LPM_ERRATA(n) ((n) << 20)
+
+#define DWC3_DCTL_KEEP_CONNECT BIT(19)
+#define DWC3_DCTL_L1_HIBER_EN BIT(18)
+#define DWC3_DCTL_CRS BIT(17)
+#define DWC3_DCTL_CSS BIT(16)
+
+#define DWC3_DCTL_INITU2ENA BIT(12)
+#define DWC3_DCTL_ACCEPTU2ENA BIT(11)
+#define DWC3_DCTL_INITU1ENA BIT(10)
+#define DWC3_DCTL_ACCEPTU1ENA BIT(9)
+#define DWC3_DCTL_TSTCTRL_MASK (0xf << 1)
+
+#define DWC3_DCTL_ULSTCHNGREQ_MASK (0x0f << 5)
+#define DWC3_DCTL_ULSTCHNGREQ(n) (((n) << 5) & DWC3_DCTL_ULSTCHNGREQ_MASK)
+
+#define DWC3_DCTL_ULSTCHNG_NO_ACTION (DWC3_DCTL_ULSTCHNGREQ(0))
+#define DWC3_DCTL_ULSTCHNG_SS_DISABLED (DWC3_DCTL_ULSTCHNGREQ(4))
+#define DWC3_DCTL_ULSTCHNG_RX_DETECT (DWC3_DCTL_ULSTCHNGREQ(5))
+#define DWC3_DCTL_ULSTCHNG_SS_INACTIVE (DWC3_DCTL_ULSTCHNGREQ(6))
+#define DWC3_DCTL_ULSTCHNG_RECOVERY (DWC3_DCTL_ULSTCHNGREQ(8))
+#define DWC3_DCTL_ULSTCHNG_COMPLIANCE (DWC3_DCTL_ULSTCHNGREQ(10))
+#define DWC3_DCTL_ULSTCHNG_LOOPBACK (DWC3_DCTL_ULSTCHNGREQ(11))
+
+/* Device Event Enable Register */
+#define DWC3_DEVTEN_VNDRDEVTSTRCVEDEN BIT(12)
+#define DWC3_DEVTEN_EVNTOVERFLOWEN BIT(11)
+#define DWC3_DEVTEN_CMDCMPLTEN BIT(10)
+#define DWC3_DEVTEN_ERRTICERREN BIT(9)
+#define DWC3_DEVTEN_SOFEN BIT(7)
+#define DWC3_DEVTEN_EOPFEN BIT(6)
+#define DWC3_DEVTEN_HIBERNATIONREQEVTEN BIT(5)
+#define DWC3_DEVTEN_WKUPEVTEN BIT(4)
+#define DWC3_DEVTEN_ULSTCNGEN BIT(3)
+#define DWC3_DEVTEN_CONNECTDONEEN BIT(2)
+#define DWC3_DEVTEN_USBRSTEN BIT(1)
+#define DWC3_DEVTEN_DISCONNEVTEN BIT(0)
+
+/* Device Status Register */
+#define DWC3_DSTS_DCNRD BIT(29)
+
+/* This applies for core versions 1.87a and earlier */
+#define DWC3_DSTS_PWRUPREQ BIT(24)
+
+/* These apply for core versions 1.94a and later */
+#define DWC3_DSTS_RSS BIT(25)
+#define DWC3_DSTS_SSS BIT(24)
+
+#define DWC3_DSTS_COREIDLE BIT(23)
+#define DWC3_DSTS_DEVCTRLHLT BIT(22)
+
+#define DWC3_DSTS_USBLNKST_MASK (0x0f << 18)
+#define DWC3_DSTS_USBLNKST(n) (((n) & DWC3_DSTS_USBLNKST_MASK) >> 18)
+
+#define DWC3_DSTS_RXFIFOEMPTY BIT(17)
+
+#define DWC3_DSTS_SOFFN_MASK (0x3fff << 3)
+#define DWC3_DSTS_SOFFN(n) (((n) & DWC3_DSTS_SOFFN_MASK) >> 3)
+
+#define DWC3_DSTS_CONNECTSPD (7 << 0)
+
+#define DWC3_DSTS_SUPERSPEED_PLUS (5 << 0) /* DWC_usb31 only */
+#define DWC3_DSTS_SUPERSPEED (4 << 0)
+#define DWC3_DSTS_HIGHSPEED (0 << 0)
+#define DWC3_DSTS_FULLSPEED BIT(0)
+#define DWC3_DSTS_LOWSPEED (2 << 0)
+
+/* Device Generic Command Register */
+#define DWC3_DGCMD_SET_LMP 0x01
+#define DWC3_DGCMD_SET_PERIODIC_PAR 0x02
+#define DWC3_DGCMD_XMIT_FUNCTION 0x03
+
+/* These apply for core versions 1.94a and later */
+#define DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO 0x04
+#define DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI 0x05
+
+#define DWC3_DGCMD_SELECTED_FIFO_FLUSH 0x09
+#define DWC3_DGCMD_ALL_FIFO_FLUSH 0x0a
+#define DWC3_DGCMD_SET_ENDPOINT_NRDY 0x0c
+#define DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK 0x10
+
+#define DWC3_DGCMD_STATUS(n) (((n) >> 12) & 0x0F)
+#define DWC3_DGCMD_CMDACT BIT(10)
+#define DWC3_DGCMD_CMDIOC BIT(8)
+
+/* Device Generic Command Parameter Register */
+#define DWC3_DGCMDPAR_FORCE_LINKPM_ACCEPT BIT(0)
+#define DWC3_DGCMDPAR_FIFO_NUM(n) ((n) << 0)
+#define DWC3_DGCMDPAR_RX_FIFO (0 << 5)
+#define DWC3_DGCMDPAR_TX_FIFO BIT(5)
+#define DWC3_DGCMDPAR_LOOPBACK_DIS (0 << 0)
+#define DWC3_DGCMDPAR_LOOPBACK_ENA BIT(0)
+
+/* Device Endpoint Command Register */
+#define DWC3_DEPCMD_PARAM_SHIFT 16
+#define DWC3_DEPCMD_PARAM(x) ((x) << DWC3_DEPCMD_PARAM_SHIFT)
+#define DWC3_DEPCMD_GET_RSC_IDX(x) (((x) >> DWC3_DEPCMD_PARAM_SHIFT) & 0x7f)
+#define DWC3_DEPCMD_STATUS(x) (((x) >> 12) & 0x0F)
+#define DWC3_DEPCMD_HIPRI_FORCERM BIT(11)
+#define DWC3_DEPCMD_CLEARPENDIN BIT(11)
+#define DWC3_DEPCMD_CMDACT BIT(10)
+#define DWC3_DEPCMD_CMDIOC BIT(8)
+
+#define DWC3_DEPCMD_DEPSTARTCFG (0x09 << 0)
+#define DWC3_DEPCMD_ENDTRANSFER (0x08 << 0)
+#define DWC3_DEPCMD_UPDATETRANSFER (0x07 << 0)
+#define DWC3_DEPCMD_STARTTRANSFER (0x06 << 0)
+#define DWC3_DEPCMD_CLEARSTALL (0x05 << 0)
+#define DWC3_DEPCMD_SETSTALL (0x04 << 0)
+/* This applies for core versions 1.90a and earlier */
+#define DWC3_DEPCMD_GETSEQNUMBER (0x03 << 0)
+/* This applies for core versions 1.94a and later */
+#define DWC3_DEPCMD_GETEPSTATE (0x03 << 0)
+#define DWC3_DEPCMD_SETTRANSFRESOURCE (0x02 << 0)
+#define DWC3_DEPCMD_SETEPCONFIG (0x01 << 0)
+
+#define DWC3_DEPCMD_CMD(x) ((x) & 0xf)
+
+/* The EP number goes 0..31 so ep0 is always out and ep1 is always in */
+#define DWC3_DALEPENA_EP(n) BIT(n)
+
+#define DWC3_DEPCMD_TYPE_CONTROL 0
+#define DWC3_DEPCMD_TYPE_ISOC 1
+#define DWC3_DEPCMD_TYPE_BULK 2
+#define DWC3_DEPCMD_TYPE_INTR 3
+
+#define DWC3_DEV_IMOD_COUNT_SHIFT 16
+#define DWC3_DEV_IMOD_COUNT_MASK (0xffff << 16)
+#define DWC3_DEV_IMOD_INTERVAL_SHIFT 0
+#define DWC3_DEV_IMOD_INTERVAL_MASK (0xffff << 0)
+
+/* OTG Configuration Register */
+#define DWC3_OCFG_DISPWRCUTTOFF BIT(5)
+#define DWC3_OCFG_HIBDISMASK BIT(4)
+#define DWC3_OCFG_SFTRSTMASK BIT(3)
+#define DWC3_OCFG_OTGVERSION BIT(2)
+#define DWC3_OCFG_HNPCAP BIT(1)
+#define DWC3_OCFG_SRPCAP BIT(0)
+
+/* OTG CTL Register */
+#define DWC3_OCTL_OTG3GOERR BIT(7)
+#define DWC3_OCTL_PERIMODE BIT(6)
+#define DWC3_OCTL_PRTPWRCTL BIT(5)
+#define DWC3_OCTL_HNPREQ BIT(4)
+#define DWC3_OCTL_SESREQ BIT(3)
+#define DWC3_OCTL_TERMSELIDPULSE BIT(2)
+#define DWC3_OCTL_DEVSETHNPEN BIT(1)
+#define DWC3_OCTL_HSTSETHNPEN BIT(0)
+
+/* OTG Event Register */
+#define DWC3_OEVT_DEVICEMODE BIT(31)
+#define DWC3_OEVT_XHCIRUNSTPSET BIT(27)
+#define DWC3_OEVT_DEVRUNSTPSET BIT(26)
+#define DWC3_OEVT_HIBENTRY BIT(25)
+#define DWC3_OEVT_CONIDSTSCHNG BIT(24)
+#define DWC3_OEVT_HRRCONFNOTIF BIT(23)
+#define DWC3_OEVT_HRRINITNOTIF BIT(22)
+#define DWC3_OEVT_ADEVIDLE BIT(21)
+#define DWC3_OEVT_ADEVBHOSTEND BIT(20)
+#define DWC3_OEVT_ADEVHOST BIT(19)
+#define DWC3_OEVT_ADEVHNPCHNG BIT(18)
+#define DWC3_OEVT_ADEVSRPDET BIT(17)
+#define DWC3_OEVT_ADEVSESSENDDET BIT(16)
+#define DWC3_OEVT_BDEVBHOSTEND BIT(11)
+#define DWC3_OEVT_BDEVHNPCHNG BIT(10)
+#define DWC3_OEVT_BDEVSESSVLDDET BIT(9)
+#define DWC3_OEVT_BDEVVBUSCHNG BIT(8)
+#define DWC3_OEVT_BSESSVLD BIT(3)
+#define DWC3_OEVT_HSTNEGSTS BIT(2)
+#define DWC3_OEVT_SESREQSTS BIT(1)
+#define DWC3_OEVT_ERROR BIT(0)
+
+/* OTG Event Enable Register */
+#define DWC3_OEVTEN_XHCIRUNSTPSETEN BIT(27)
+#define DWC3_OEVTEN_DEVRUNSTPSETEN BIT(26)
+#define DWC3_OEVTEN_HIBENTRYEN BIT(25)
+#define DWC3_OEVTEN_CONIDSTSCHNGEN BIT(24)
+#define DWC3_OEVTEN_HRRCONFNOTIFEN BIT(23)
+#define DWC3_OEVTEN_HRRINITNOTIFEN BIT(22)
+#define DWC3_OEVTEN_ADEVIDLEEN BIT(21)
+#define DWC3_OEVTEN_ADEVBHOSTENDEN BIT(20)
+#define DWC3_OEVTEN_ADEVHOSTEN BIT(19)
+#define DWC3_OEVTEN_ADEVHNPCHNGEN BIT(18)
+#define DWC3_OEVTEN_ADEVSRPDETEN BIT(17)
+#define DWC3_OEVTEN_ADEVSESSENDDETEN BIT(16)
+#define DWC3_OEVTEN_BDEVBHOSTENDEN BIT(11)
+#define DWC3_OEVTEN_BDEVHNPCHNGEN BIT(10)
+#define DWC3_OEVTEN_BDEVSESSVLDDETEN BIT(9)
+#define DWC3_OEVTEN_BDEVVBUSCHNGEN BIT(8)
+
+/* OTG Status Register */
+#define DWC3_OSTS_DEVRUNSTP BIT(13)
+#define DWC3_OSTS_XHCIRUNSTP BIT(12)
+#define DWC3_OSTS_PERIPHERALSTATE BIT(4)
+#define DWC3_OSTS_XHCIPRTPOWER BIT(3)
+#define DWC3_OSTS_BSESVLD BIT(2)
+#define DWC3_OSTS_VBUSVLD BIT(1)
+#define DWC3_OSTS_CONIDSTS BIT(0)
+
+/* Structures */
+
+struct dwc3_trb;
+
+#define DWC3_EP_FLAG_STALLED BIT(0)
+#define DWC3_EP_FLAG_WEDGED BIT(1)
+
+#define DWC3_EP_DIRECTION_TX true
+#define DWC3_EP_DIRECTION_RX false
+
+#define DWC3_TRB_NUM 256
+
+enum dwc3_phy {
+ DWC3_PHY_UNKNOWN = 0,
+ DWC3_PHY_USB3,
+ DWC3_PHY_USB2,
+};
+
+enum dwc3_ep0_next {
+ DWC3_EP0_UNKNOWN = 0,
+ DWC3_EP0_COMPLETE,
+ DWC3_EP0_NRDY_DATA,
+ DWC3_EP0_NRDY_STATUS,
+};
+
+enum dwc3_ep0_state {
+ EP0_UNCONNECTED = 0,
+ EP0_SETUP_PHASE,
+ EP0_DATA_PHASE,
+ EP0_STATUS_PHASE,
+};
+
+enum dwc3_link_state {
+ /* In SuperSpeed */
+ DWC3_LINK_STATE_U0 = 0x00, /* in HS, means ON */
+ DWC3_LINK_STATE_U1 = 0x01,
+ DWC3_LINK_STATE_U2 = 0x02, /* in HS, means SLEEP */
+ DWC3_LINK_STATE_U3 = 0x03, /* in HS, means SUSPEND */
+ DWC3_LINK_STATE_SS_DIS = 0x04,
+ DWC3_LINK_STATE_RX_DET = 0x05, /* in HS, means Early Suspend */
+ DWC3_LINK_STATE_SS_INACT = 0x06,
+ DWC3_LINK_STATE_POLL = 0x07,
+ DWC3_LINK_STATE_RECOV = 0x08,
+ DWC3_LINK_STATE_HRESET = 0x09,
+ DWC3_LINK_STATE_CMPLY = 0x0a,
+ DWC3_LINK_STATE_LPBK = 0x0b,
+ DWC3_LINK_STATE_RESET = 0x0e,
+ DWC3_LINK_STATE_RESUME = 0x0f,
+ DWC3_LINK_STATE_MASK = 0x0f,
+};
+
+/* TRB Length, PCM and Status */
+#define DWC3_TRB_SIZE_MASK (0x00ffffff)
+#define DWC3_TRB_SIZE_LENGTH(n) ((n) & DWC3_TRB_SIZE_MASK)
+#define DWC3_TRB_SIZE_PCM1(n) (((n) & 0x03) << 24)
+#define DWC3_TRB_SIZE_TRBSTS(n) (((n) & (0x0f << 28)) >> 28)
+
+#define DWC3_TRBSTS_OK 0
+#define DWC3_TRBSTS_MISSED_ISOC 1
+#define DWC3_TRBSTS_SETUP_PENDING 2
+#define DWC3_TRB_STS_XFER_IN_PROG 4
+
+/* TRB Control */
+#define DWC3_TRB_CTRL_HWO BIT(0)
+#define DWC3_TRB_CTRL_LST BIT(1)
+#define DWC3_TRB_CTRL_CHN BIT(2)
+#define DWC3_TRB_CTRL_CSP BIT(3)
+#define DWC3_TRB_CTRL_TRBCTL(n) (((n) & 0x3f) << 4)
+#define DWC3_TRB_CTRL_ISP_IMI BIT(10)
+#define DWC3_TRB_CTRL_IOC BIT(11)
+#define DWC3_TRB_CTRL_SID_SOFN(n) (((n) & 0xffff) << 14)
+#define DWC3_TRB_CTRL_GET_SID_SOFN(n) (((n) & (0xffff << 14)) >> 14)
+
+#define DWC3_TRBCTL_TYPE(n) ((n) & (0x3f << 4))
+#define DWC3_TRBCTL_NORMAL DWC3_TRB_CTRL_TRBCTL(1)
+#define DWC3_TRBCTL_CONTROL_SETUP DWC3_TRB_CTRL_TRBCTL(2)
+#define DWC3_TRBCTL_CONTROL_STATUS2 DWC3_TRB_CTRL_TRBCTL(3)
+#define DWC3_TRBCTL_CONTROL_STATUS3 DWC3_TRB_CTRL_TRBCTL(4)
+#define DWC3_TRBCTL_CONTROL_DATA DWC3_TRB_CTRL_TRBCTL(5)
+#define DWC3_TRBCTL_ISOCHRONOUS_FIRST DWC3_TRB_CTRL_TRBCTL(6)
+#define DWC3_TRBCTL_ISOCHRONOUS DWC3_TRB_CTRL_TRBCTL(7)
+#define DWC3_TRBCTL_LINK_TRB DWC3_TRB_CTRL_TRBCTL(8)
+
+/**
+ * struct dwc3_trb - transfer request block (hw format)
+ * @bpl: DW0-3
+ * @bph: DW4-7
+ * @size: DW8-B
+ * @ctrl: DWC-F
+ */
+struct dwc3_trb {
+ u32 bpl;
+ u32 bph;
+ u32 size;
+ u32 ctrl;
+} __packed;
+
+/**
+ * struct dwc3_hwparams - copy of HWPARAMS registers
+ * @hwparams0: GHWPARAMS0
+ * @hwparams1: GHWPARAMS1
+ * @hwparams2: GHWPARAMS2
+ * @hwparams3: GHWPARAMS3
+ * @hwparams4: GHWPARAMS4
+ * @hwparams5: GHWPARAMS5
+ * @hwparams6: GHWPARAMS6
+ * @hwparams7: GHWPARAMS7
+ * @hwparams8: GHWPARAMS8
+ */
+struct dwc3_hwparams {
+ u32 hwparams0;
+ u32 hwparams1;
+ u32 hwparams2;
+ u32 hwparams3;
+ u32 hwparams4;
+ u32 hwparams5;
+ u32 hwparams6;
+ u32 hwparams7;
+ u32 hwparams8;
+};
+
+/* HWPARAMS0 */
+#define DWC3_MODE(n) ((n) & 0x7)
+
+#define DWC3_MDWIDTH(n) (((n) & 0xff00) >> 8)
+
+/* HWPARAMS1 */
+#define DWC3_NUM_INT(n) (((n) & (0x3f << 15)) >> 15)
+
+/* HWPARAMS3 */
+#define DWC3_NUM_IN_EPS_MASK (0x1f << 18)
+#define DWC3_NUM_EPS_MASK (0x3f << 12)
+#define DWC3_NUM_EPS(p) (((p)->hwparams3 & \
+ (DWC3_NUM_EPS_MASK)) >> 12)
+#define DWC3_NUM_IN_EPS(p) (((p)->hwparams3 & \
+ (DWC3_NUM_IN_EPS_MASK)) >> 18)
+
+/* HWPARAMS7 */
+#define DWC3_RAM1_DEPTH(n) ((n) & 0xffff)
+
+/**
+ * struct dwc3 - representation of our controller
+ * @drd_work: workqueue used for role swapping
+ * @ep0_trb: trb which is used for the ctrl_req
+ * @bounce: address of bounce buffer
+ * @scratchbuf: address of scratch buffer
+ * @setup_buf: used while precessing STD USB requests
+ * @ep0_trb_addr: dma address of @ep0_trb
+ * @bounce_addr: dma address of @bounce
+ * @ep0_usb_req: dummy req used while handling STD USB requests
+ * @scratch_addr: dma address of scratchbuf
+ * @ep0_in_setup: one control transfer is completed and enter setup phase
+ * @lock: for synchronizing
+ * @dev: pointer to our struct device
+ * @sysdev: pointer to the DMA-capable device
+ * @xhci: pointer to our xHCI child
+ * @xhci_resources: struct resources for our @xhci child
+ * @ev_buf: struct dwc3_event_buffer pointer
+ * @eps: endpoint array
+ * @gadget: device side representation of the peripheral controller
+ * @gadget_driver: pointer to the gadget driver
+ * @clks: array of clocks
+ * @num_clks: number of clocks
+ * @reset: reset control
+ * @regs: base address for our registers
+ * @regs_size: address space size
+ * @fladj: frame length adjustment
+ * @irq_gadget: peripheral controller's IRQ number
+ * @otg_irq: IRQ number for OTG IRQs
+ * @current_otg_role: current role of operation while using the OTG block
+ * @desired_otg_role: desired role of operation while using the OTG block
+ * @otg_restart_host: flag that OTG controller needs to restart host
+ * @nr_scratch: number of scratch buffers
+ * @u1u2: only used on revisions <1.83a for workaround
+ * @maximum_speed: maximum speed requested (mainly for testing purposes)
+ * @revision: revision register contents
+ * @version_type: VERSIONTYPE register contents, a sub release of a revision
+ * @dr_mode: requested mode of operation
+ * @current_dr_role: current role of operation when in dual-role mode
+ * @desired_dr_role: desired role of operation when in dual-role mode
+ * @edev: extcon handle
+ * @edev_nb: extcon notifier
+ * @hsphy_mode: UTMI phy mode, one of following:
+ * - USBPHY_INTERFACE_MODE_UTMI
+ * - USBPHY_INTERFACE_MODE_UTMIW
+ * @usb2_phy: pointer to USB2 PHY
+ * @usb3_phy: pointer to USB3 PHY
+ * @usb2_generic_phy: pointer to USB2 PHY
+ * @usb3_generic_phy: pointer to USB3 PHY
+ * @phys_ready: flag to indicate that PHYs are ready
+ * @ulpi: pointer to ulpi interface
+ * @ulpi_ready: flag to indicate that ULPI is initialized
+ * @u2sel: parameter from Set SEL request.
+ * @u2pel: parameter from Set SEL request.
+ * @u1sel: parameter from Set SEL request.
+ * @u1pel: parameter from Set SEL request.
+ * @num_eps: number of endpoints
+ * @ep0_next_event: hold the next expected event
+ * @ep0state: state of endpoint zero
+ * @link_state: link state
+ * @speed: device speed (super, high, full, low)
+ * @hwparams: copy of hwparams registers
+ * @root: debugfs root folder pointer
+ * @regset: debugfs pointer to regdump file
+ * @dbg_lsp_select: current debug lsp mux register selection
+ * @test_mode: true when we're entering a USB test mode
+ * @test_mode_nr: test feature selector
+ * @lpm_nyet_threshold: LPM NYET response threshold
+ * @hird_threshold: HIRD threshold
+ * @rx_thr_num_pkt_prd: periodic ESS receive packet count
+ * @rx_max_burst_prd: max periodic ESS receive burst size
+ * @tx_thr_num_pkt_prd: periodic ESS transmit packet count
+ * @tx_max_burst_prd: max periodic ESS transmit burst size
+ * @hsphy_interface: "utmi" or "ulpi"
+ * @connected: true when we're connected to a host, false otherwise
+ * @delayed_status: true when gadget driver asks for delayed status
+ * @ep0_bounced: true when we used bounce buffer
+ * @ep0_expect_in: true when we expect a DATA IN transfer
+ * @has_hibernation: true when dwc3 was configured with Hibernation
+ * @sysdev_is_parent: true when dwc3 device has a parent driver
+ * @has_lpm_erratum: true when core was configured with LPM Erratum. Note that
+ * there's now way for software to detect this in runtime.
+ * @is_utmi_l1_suspend: the core asserts output signal
+ * 0 - utmi_sleep_n
+ * 1 - utmi_l1_suspend_n
+ * @is_fpga: true when we are using the FPGA board
+ * @pending_events: true when we have pending IRQs to be handled
+ * @pullups_connected: true when Run/Stop bit is set
+ * @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround
+ * @three_stage_setup: set if we perform a three phase setup
+ * @dis_start_transfer_quirk: set if start_transfer failure SW workaround is
+ * not needed for DWC_usb31 version 1.70a-ea06 and below
+ * @usb3_lpm_capable: set if hadrware supports Link Power Management
+ * @usb2_lpm_disable: set to disable usb2 lpm
+ * @disable_scramble_quirk: set if we enable the disable scramble quirk
+ * @u2exit_lfps_quirk: set if we enable u2exit lfps quirk
+ * @u2ss_inp3_quirk: set if we enable P3 OK for U2/SS Inactive quirk
+ * @req_p1p2p3_quirk: set if we enable request p1p2p3 quirk
+ * @del_p1p2p3_quirk: set if we enable delay p1p2p3 quirk
+ * @del_phy_power_chg_quirk: set if we enable delay phy power change quirk
+ * @lfps_filter_quirk: set if we enable LFPS filter quirk
+ * @rx_detect_poll_quirk: set if we enable rx_detect to polling lfps quirk
+ * @dis_u3_susphy_quirk: set if we disable usb3 suspend phy
+ * @dis_u2_susphy_quirk: set if we disable usb2 suspend phy
+ * @dis_enblslpm_quirk: set if we clear enblslpm in GUSB2PHYCFG,
+ * disabling the suspend signal to the PHY.
+ * @dis_rxdet_inp3_quirk: set if we disable Rx.Detect in P3
+ * @dis_u2_freeclk_exists_quirk : set if we clear u2_freeclk_exists
+ * in GUSB2PHYCFG, specify that USB2 PHY doesn't
+ * provide a free-running PHY clock.
+ * @dis_del_phy_power_chg_quirk: set if we disable delay phy power
+ * change quirk.
+ * @dis_tx_ipgap_linecheck_quirk: set if we disable u2mac linestate
+ * check during HS transmit.
+ * @tx_de_emphasis_quirk: set if we enable Tx de-emphasis quirk
+ * @tx_de_emphasis: Tx de-emphasis value
+ * 0 - -6dB de-emphasis
+ * 1 - -3.5dB de-emphasis
+ * 2 - No de-emphasis
+ * 3 - Reserved
+ * @dis_metastability_quirk: set to disable metastability quirk.
+ * @imod_interval: set the interrupt moderation interval in 250ns
+ * increments or 0 to disable.
+ */
+struct dwc3 {
+ struct device_d *dev;
+ struct device_d *xhci;
+
+ struct clk_bulk_data *clks;
+ int num_clks;
+
+ struct phy *usb2_generic_phy;
+ struct phy *usb3_generic_phy;
+
+ bool phys_ready;
+
+ void __iomem *regs;
+
+ enum usb_dr_mode dr_mode;
+ u32 current_dr_role;
+ u32 desired_dr_role;
+
+ u32 fladj;
+ u32 irq_gadget;
+ u32 otg_irq;
+ u32 current_otg_role;
+ u32 desired_otg_role;
+ bool otg_restart_host;
+ u32 nr_scratch;
+ u32 u1u2;
+ u32 maximum_speed;
+
+ /*
+ * All 3.1 IP version constants are greater than the 3.0 IP
+ * version constants. This works for most version checks in
+ * dwc3. However, in the future, this may not apply as
+ * features may be developed on newer versions of the 3.0 IP
+ * that are not in the 3.1 IP.
+ */
+ u32 revision;
+
+#define DWC3_REVISION_173A 0x5533173a
+#define DWC3_REVISION_175A 0x5533175a
+#define DWC3_REVISION_180A 0x5533180a
+#define DWC3_REVISION_183A 0x5533183a
+#define DWC3_REVISION_185A 0x5533185a
+#define DWC3_REVISION_187A 0x5533187a
+#define DWC3_REVISION_188A 0x5533188a
+#define DWC3_REVISION_190A 0x5533190a
+#define DWC3_REVISION_194A 0x5533194a
+#define DWC3_REVISION_200A 0x5533200a
+#define DWC3_REVISION_202A 0x5533202a
+#define DWC3_REVISION_210A 0x5533210a
+#define DWC3_REVISION_220A 0x5533220a
+#define DWC3_REVISION_230A 0x5533230a
+#define DWC3_REVISION_240A 0x5533240a
+#define DWC3_REVISION_250A 0x5533250a
+#define DWC3_REVISION_260A 0x5533260a
+#define DWC3_REVISION_270A 0x5533270a
+#define DWC3_REVISION_280A 0x5533280a
+#define DWC3_REVISION_290A 0x5533290a
+#define DWC3_REVISION_300A 0x5533300a
+#define DWC3_REVISION_310A 0x5533310a
+#define DWC3_REVISION_330A 0x5533330a
+
+/*
+ * NOTICE: we're using bit 31 as a "is usb 3.1" flag. This is really
+ * just so dwc31 revisions are always larger than dwc3.
+ */
+#define DWC3_REVISION_IS_DWC31 0x80000000
+#define DWC3_USB31_REVISION_110A (0x3131302a | DWC3_REVISION_IS_DWC31)
+#define DWC3_USB31_REVISION_120A (0x3132302a | DWC3_REVISION_IS_DWC31)
+#define DWC3_USB31_REVISION_160A (0x3136302a | DWC3_REVISION_IS_DWC31)
+#define DWC3_USB31_REVISION_170A (0x3137302a | DWC3_REVISION_IS_DWC31)
+
+ u32 version_type;
+
+#define DWC31_VERSIONTYPE_EA01 0x65613031
+#define DWC31_VERSIONTYPE_EA02 0x65613032
+#define DWC31_VERSIONTYPE_EA03 0x65613033
+#define DWC31_VERSIONTYPE_EA04 0x65613034
+#define DWC31_VERSIONTYPE_EA05 0x65613035
+#define DWC31_VERSIONTYPE_EA06 0x65613036
+
+ u16 u2sel;
+ u16 u2pel;
+ u8 u1sel;
+ u8 u1pel;
+
+ u8 speed;
+
+ u8 num_eps;
+
+ struct dwc3_hwparams hwparams;
+
+ u32 dbg_lsp_select;
+
+ u8 test_mode;
+ u8 test_mode_nr;
+ u8 lpm_nyet_threshold;
+ u8 hird_threshold;
+ u8 rx_thr_num_pkt_prd;
+ u8 rx_max_burst_prd;
+ u8 tx_thr_num_pkt_prd;
+ u8 tx_max_burst_prd;
+
+ const char *hsphy_interface;
+
+ unsigned connected:1;
+ unsigned delayed_status:1;
+ unsigned ep0_bounced:1;
+ unsigned ep0_expect_in:1;
+ unsigned has_hibernation:1;
+ unsigned sysdev_is_parent:1;
+ unsigned has_lpm_erratum:1;
+ unsigned is_utmi_l1_suspend:1;
+ unsigned is_fpga:1;
+ unsigned pending_events:1;
+ unsigned pullups_connected:1;
+ unsigned setup_packet_pending:1;
+ unsigned three_stage_setup:1;
+ unsigned dis_start_transfer_quirk:1;
+ unsigned usb3_lpm_capable:1;
+ unsigned usb2_lpm_disable:1;
+
+ unsigned disable_scramble_quirk:1;
+ unsigned u2exit_lfps_quirk:1;
+ unsigned u2ss_inp3_quirk:1;
+ unsigned req_p1p2p3_quirk:1;
+ unsigned del_p1p2p3_quirk:1;
+ unsigned del_phy_power_chg_quirk:1;
+ unsigned lfps_filter_quirk:1;
+ unsigned rx_detect_poll_quirk:1;
+ unsigned dis_u3_susphy_quirk:1;
+ unsigned dis_u2_susphy_quirk:1;
+ unsigned dis_enblslpm_quirk:1;
+ unsigned dis_rxdet_inp3_quirk:1;
+ unsigned dis_u2_freeclk_exists_quirk:1;
+ unsigned dis_del_phy_power_chg_quirk:1;
+ unsigned dis_tx_ipgap_linecheck_quirk:1;
+
+ unsigned tx_de_emphasis_quirk:1;
+ unsigned tx_de_emphasis:2;
+
+ unsigned dis_metastability_quirk:1;
+
+ u16 imod_interval;
+};
+
+#define INCRX_BURST_MODE 0
+#define INCRX_UNDEF_LENGTH_BURST_MODE 1
+
+#define work_to_dwc(w) (container_of((w), struct dwc3, drd_work))
+
+/* -------------------------------------------------------------------------- */
+
+struct dwc3_event_type {
+ u32 is_devspec:1;
+ u32 type:7;
+ u32 reserved8_31:24;
+} __packed;
+
+#define DWC3_DEPEVT_XFERCOMPLETE 0x01
+#define DWC3_DEPEVT_XFERINPROGRESS 0x02
+#define DWC3_DEPEVT_XFERNOTREADY 0x03
+#define DWC3_DEPEVT_RXTXFIFOEVT 0x04
+#define DWC3_DEPEVT_STREAMEVT 0x06
+#define DWC3_DEPEVT_EPCMDCMPLT 0x07
+
+/**
+ * struct dwc3_event_depvt - Device Endpoint Events
+ * @one_bit: indicates this is an endpoint event (not used)
+ * @endpoint_number: number of the endpoint
+ * @endpoint_event: The event we have:
+ * 0x00 - Reserved
+ * 0x01 - XferComplete
+ * 0x02 - XferInProgress
+ * 0x03 - XferNotReady
+ * 0x04 - RxTxFifoEvt (IN->Underrun, OUT->Overrun)
+ * 0x05 - Reserved
+ * 0x06 - StreamEvt
+ * 0x07 - EPCmdCmplt
+ * @reserved11_10: Reserved, don't use.
+ * @status: Indicates the status of the event. Refer to databook for
+ * more information.
+ * @parameters: Parameters of the current event. Refer to databook for
+ * more information.
+ */
+struct dwc3_event_depevt {
+ u32 one_bit:1;
+ u32 endpoint_number:5;
+ u32 endpoint_event:4;
+ u32 reserved11_10:2;
+ u32 status:4;
+
+/* Within XferNotReady */
+#define DEPEVT_STATUS_TRANSFER_ACTIVE BIT(3)
+
+/* Within XferComplete or XferInProgress */
+#define DEPEVT_STATUS_BUSERR BIT(0)
+#define DEPEVT_STATUS_SHORT BIT(1)
+#define DEPEVT_STATUS_IOC BIT(2)
+#define DEPEVT_STATUS_LST BIT(3) /* XferComplete */
+#define DEPEVT_STATUS_MISSED_ISOC BIT(3) /* XferInProgress */
+
+/* Stream event only */
+#define DEPEVT_STREAMEVT_FOUND 1
+#define DEPEVT_STREAMEVT_NOTFOUND 2
+
+/* Control-only Status */
+#define DEPEVT_STATUS_CONTROL_DATA 1
+#define DEPEVT_STATUS_CONTROL_STATUS 2
+#define DEPEVT_STATUS_CONTROL_PHASE(n) ((n) & 3)
+
+/* In response to Start Transfer */
+#define DEPEVT_TRANSFER_NO_RESOURCE 1
+#define DEPEVT_TRANSFER_BUS_EXPIRY 2
+
+ u32 parameters:16;
+
+/* For Command Complete Events */
+#define DEPEVT_PARAMETER_CMD(n) (((n) & (0xf << 8)) >> 8)
+} __packed;
+
+/**
+ * struct dwc3_event_devt - Device Events
+ * @one_bit: indicates this is a non-endpoint event (not used)
+ * @device_event: indicates it's a device event. Should read as 0x00
+ * @type: indicates the type of device event.
+ * 0 - DisconnEvt
+ * 1 - USBRst
+ * 2 - ConnectDone
+ * 3 - ULStChng
+ * 4 - WkUpEvt
+ * 5 - Reserved
+ * 6 - EOPF
+ * 7 - SOF
+ * 8 - Reserved
+ * 9 - ErrticErr
+ * 10 - CmdCmplt
+ * 11 - EvntOverflow
+ * 12 - VndrDevTstRcved
+ * @reserved15_12: Reserved, not used
+ * @event_info: Information about this event
+ * @reserved31_25: Reserved, not used
+ */
+struct dwc3_event_devt {
+ u32 one_bit:1;
+ u32 device_event:7;
+ u32 type:4;
+ u32 reserved15_12:4;
+ u32 event_info:9;
+ u32 reserved31_25:7;
+} __packed;
+
+/**
+ * struct dwc3_event_gevt - Other Core Events
+ * @one_bit: indicates this is a non-endpoint event (not used)
+ * @device_event: indicates it's (0x03) Carkit or (0x04) I2C event.
+ * @phy_port_number: self-explanatory
+ * @reserved31_12: Reserved, not used.
+ */
+struct dwc3_event_gevt {
+ u32 one_bit:1;
+ u32 device_event:7;
+ u32 phy_port_number:4;
+ u32 reserved31_12:20;
+} __packed;
+
+/**
+ * union dwc3_event - representation of Event Buffer contents
+ * @raw: raw 32-bit event
+ * @type: the type of the event
+ * @depevt: Device Endpoint Event
+ * @devt: Device Event
+ * @gevt: Global Event
+ */
+union dwc3_event {
+ u32 raw;
+ struct dwc3_event_type type;
+ struct dwc3_event_depevt depevt;
+ struct dwc3_event_devt devt;
+ struct dwc3_event_gevt gevt;
+};
+
+/**
+ * struct dwc3_gadget_ep_cmd_params - representation of endpoint command
+ * parameters
+ * @param2: third parameter
+ * @param1: second parameter
+ * @param0: first parameter
+ */
+struct dwc3_gadget_ep_cmd_params {
+ u32 param2;
+ u32 param1;
+ u32 param0;
+};
+
+/*
+ * DWC3 Features to be used as Driver Data
+ */
+
+#define DWC3_HAS_PERIPHERAL BIT(0)
+#define DWC3_HAS_XHCI BIT(1)
+#define DWC3_HAS_OTG BIT(3)
+
+/* prototypes */
+void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode);
+void dwc3_set_mode(struct dwc3 *dwc, u32 mode);
+
+/* check whether we are on the DWC_usb3 core */
+static inline bool dwc3_is_usb3(struct dwc3 *dwc)
+{
+ return !(dwc->revision & DWC3_REVISION_IS_DWC31);
+}
+
+/* check whether we are on the DWC_usb31 core */
+static inline bool dwc3_is_usb31(struct dwc3 *dwc)
+{
+ return !!(dwc->revision & DWC3_REVISION_IS_DWC31);
+}
+
+bool dwc3_has_imod(struct dwc3 *dwc);
+
+int dwc3_event_buffers_setup(struct dwc3 *dwc);
+void dwc3_event_buffers_cleanup(struct dwc3 *dwc);
+
+#if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
+int dwc3_host_init(struct dwc3 *dwc);
+#else
+static inline int dwc3_host_init(struct dwc3 *dwc)
+{ return 0; }
+#endif
+
+#if IS_ENABLED(CONFIG_USB_DWC3_GADGET) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
+int dwc3_gadget_init(struct dwc3 *dwc);
+void dwc3_gadget_exit(struct dwc3 *dwc);
+int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode);
+int dwc3_gadget_get_link_state(struct dwc3 *dwc);
+int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state);
+int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
+ struct dwc3_gadget_ep_cmd_params *params);
+int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param);
+#else
+static inline int dwc3_gadget_init(struct dwc3 *dwc)
+{ return 0; }
+static inline void dwc3_gadget_exit(struct dwc3 *dwc)
+{ }
+static inline int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode)
+{ return 0; }
+static inline int dwc3_gadget_get_link_state(struct dwc3 *dwc)
+{ return 0; }
+static inline int dwc3_gadget_set_link_state(struct dwc3 *dwc,
+ enum dwc3_link_state state)
+{ return 0; }
+
+static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc,
+ int cmd, u32 param)
+{ return 0; }
+#endif
+
+#if IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
+int dwc3_drd_init(struct dwc3 *dwc);
+void dwc3_drd_exit(struct dwc3 *dwc);
+void dwc3_otg_init(struct dwc3 *dwc);
+void dwc3_otg_exit(struct dwc3 *dwc);
+void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus);
+void dwc3_otg_host_init(struct dwc3 *dwc);
+#else
+static inline int dwc3_drd_init(struct dwc3 *dwc)
+{ return 0; }
+static inline void dwc3_drd_exit(struct dwc3 *dwc)
+{ }
+static inline void dwc3_otg_init(struct dwc3 *dwc)
+{ }
+static inline void dwc3_otg_exit(struct dwc3 *dwc)
+{ }
+static inline void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus)
+{ }
+static inline void dwc3_otg_host_init(struct dwc3 *dwc)
+{ }
+#endif
+
+/* power management interface */
+#if !IS_ENABLED(CONFIG_USB_DWC3_HOST)
+int dwc3_gadget_suspend(struct dwc3 *dwc);
+int dwc3_gadget_resume(struct dwc3 *dwc);
+void dwc3_gadget_process_pending_events(struct dwc3 *dwc);
+#else
+static inline int dwc3_gadget_suspend(struct dwc3 *dwc)
+{
+ return 0;
+}
+
+static inline int dwc3_gadget_resume(struct dwc3 *dwc)
+{
+ return 0;
+}
+
+static inline void dwc3_gadget_process_pending_events(struct dwc3 *dwc)
+{
+}
+#endif /* !IS_ENABLED(CONFIG_USB_DWC3_HOST) */
+
+#if IS_ENABLED(CONFIG_USB_DWC3_ULPI)
+int dwc3_ulpi_init(struct dwc3 *dwc);
+void dwc3_ulpi_exit(struct dwc3 *dwc);
+#else
+static inline int dwc3_ulpi_init(struct dwc3 *dwc)
+{ return 0; }
+static inline void dwc3_ulpi_exit(struct dwc3 *dwc)
+{ }
+#endif
+
+#endif /* __DRIVERS_USB_DWC3_CORE_H */
diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h
new file mode 100644
index 0000000000..4f75ab3505
--- /dev/null
+++ b/drivers/usb/dwc3/debug.h
@@ -0,0 +1,664 @@
+// SPDX-License-Identifier: GPL-2.0
+/**
+ * debug.h - DesignWare USB3 DRD Controller Debug Header
+ *
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Authors: Felipe Balbi <balbi@ti.com>,
+ * Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+ */
+
+#ifndef __DWC3_DEBUG_H
+#define __DWC3_DEBUG_H
+
+#include "core.h"
+
+/**
+ * dwc3_gadget_ep_cmd_string - returns endpoint command string
+ * @cmd: command code
+ */
+static inline const char *
+dwc3_gadget_ep_cmd_string(u8 cmd)
+{
+ switch (cmd) {
+ case DWC3_DEPCMD_DEPSTARTCFG:
+ return "Start New Configuration";
+ case DWC3_DEPCMD_ENDTRANSFER:
+ return "End Transfer";
+ case DWC3_DEPCMD_UPDATETRANSFER:
+ return "Update Transfer";
+ case DWC3_DEPCMD_STARTTRANSFER:
+ return "Start Transfer";
+ case DWC3_DEPCMD_CLEARSTALL:
+ return "Clear Stall";
+ case DWC3_DEPCMD_SETSTALL:
+ return "Set Stall";
+ case DWC3_DEPCMD_GETEPSTATE:
+ return "Get Endpoint State";
+ case DWC3_DEPCMD_SETTRANSFRESOURCE:
+ return "Set Endpoint Transfer Resource";
+ case DWC3_DEPCMD_SETEPCONFIG:
+ return "Set Endpoint Configuration";
+ default:
+ return "UNKNOWN command";
+ }
+}
+
+/**
+ * dwc3_gadget_generic_cmd_string - returns generic command string
+ * @cmd: command code
+ */
+static inline const char *
+dwc3_gadget_generic_cmd_string(u8 cmd)
+{
+ switch (cmd) {
+ case DWC3_DGCMD_SET_LMP:
+ return "Set LMP";
+ case DWC3_DGCMD_SET_PERIODIC_PAR:
+ return "Set Periodic Parameters";
+ case DWC3_DGCMD_XMIT_FUNCTION:
+ return "Transmit Function Wake Device Notification";
+ case DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO:
+ return "Set Scratchpad Buffer Array Address Lo";
+ case DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI:
+ return "Set Scratchpad Buffer Array Address Hi";
+ case DWC3_DGCMD_SELECTED_FIFO_FLUSH:
+ return "Selected FIFO Flush";
+ case DWC3_DGCMD_ALL_FIFO_FLUSH:
+ return "All FIFO Flush";
+ case DWC3_DGCMD_SET_ENDPOINT_NRDY:
+ return "Set Endpoint NRDY";
+ case DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK:
+ return "Run SoC Bus Loopback Test";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+/**
+ * dwc3_gadget_link_string - returns link name
+ * @link_state: link state code
+ */
+static inline const char *
+dwc3_gadget_link_string(enum dwc3_link_state link_state)
+{
+ switch (link_state) {
+ case DWC3_LINK_STATE_U0:
+ return "U0";
+ case DWC3_LINK_STATE_U1:
+ return "U1";
+ case DWC3_LINK_STATE_U2:
+ return "U2";
+ case DWC3_LINK_STATE_U3:
+ return "U3";
+ case DWC3_LINK_STATE_SS_DIS:
+ return "SS.Disabled";
+ case DWC3_LINK_STATE_RX_DET:
+ return "RX.Detect";
+ case DWC3_LINK_STATE_SS_INACT:
+ return "SS.Inactive";
+ case DWC3_LINK_STATE_POLL:
+ return "Polling";
+ case DWC3_LINK_STATE_RECOV:
+ return "Recovery";
+ case DWC3_LINK_STATE_HRESET:
+ return "Hot Reset";
+ case DWC3_LINK_STATE_CMPLY:
+ return "Compliance";
+ case DWC3_LINK_STATE_LPBK:
+ return "Loopback";
+ case DWC3_LINK_STATE_RESET:
+ return "Reset";
+ case DWC3_LINK_STATE_RESUME:
+ return "Resume";
+ default:
+ return "UNKNOWN link state\n";
+ }
+}
+
+/**
+ * dwc3_gadget_hs_link_string - returns highspeed and below link name
+ * @link_state: link state code
+ */
+static inline const char *
+dwc3_gadget_hs_link_string(enum dwc3_link_state link_state)
+{
+ switch (link_state) {
+ case DWC3_LINK_STATE_U0:
+ return "On";
+ case DWC3_LINK_STATE_U2:
+ return "Sleep";
+ case DWC3_LINK_STATE_U3:
+ return "Suspend";
+ case DWC3_LINK_STATE_SS_DIS:
+ return "Disconnected";
+ case DWC3_LINK_STATE_RX_DET:
+ return "Early Suspend";
+ case DWC3_LINK_STATE_RECOV:
+ return "Recovery";
+ case DWC3_LINK_STATE_RESET:
+ return "Reset";
+ case DWC3_LINK_STATE_RESUME:
+ return "Resume";
+ default:
+ return "UNKNOWN link state\n";
+ }
+}
+
+/**
+ * dwc3_trb_type_string - returns TRB type as a string
+ * @type: the type of the TRB
+ */
+static inline const char *dwc3_trb_type_string(unsigned int type)
+{
+ switch (type) {
+ case DWC3_TRBCTL_NORMAL:
+ return "normal";
+ case DWC3_TRBCTL_CONTROL_SETUP:
+ return "setup";
+ case DWC3_TRBCTL_CONTROL_STATUS2:
+ return "status2";
+ case DWC3_TRBCTL_CONTROL_STATUS3:
+ return "status3";
+ case DWC3_TRBCTL_CONTROL_DATA:
+ return "data";
+ case DWC3_TRBCTL_ISOCHRONOUS_FIRST:
+ return "isoc-first";
+ case DWC3_TRBCTL_ISOCHRONOUS:
+ return "isoc";
+ case DWC3_TRBCTL_LINK_TRB:
+ return "link";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+static inline const char *dwc3_ep0_state_string(enum dwc3_ep0_state state)
+{
+ switch (state) {
+ case EP0_UNCONNECTED:
+ return "Unconnected";
+ case EP0_SETUP_PHASE:
+ return "Setup Phase";
+ case EP0_DATA_PHASE:
+ return "Data Phase";
+ case EP0_STATUS_PHASE:
+ return "Status Phase";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+/**
+ * dwc3_gadget_event_string - returns event name
+ * @event: the event code
+ */
+static inline const char *
+dwc3_gadget_event_string(char *str, const struct dwc3_event_devt *event)
+{
+ enum dwc3_link_state state = event->event_info & DWC3_LINK_STATE_MASK;
+
+ switch (event->type) {
+ case DWC3_DEVICE_EVENT_DISCONNECT:
+ sprintf(str, "Disconnect: [%s]",
+ dwc3_gadget_link_string(state));
+ break;
+ case DWC3_DEVICE_EVENT_RESET:
+ sprintf(str, "Reset [%s]", dwc3_gadget_link_string(state));
+ break;
+ case DWC3_DEVICE_EVENT_CONNECT_DONE:
+ sprintf(str, "Connection Done [%s]",
+ dwc3_gadget_link_string(state));
+ break;
+ case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE:
+ sprintf(str, "Link Change [%s]",
+ dwc3_gadget_link_string(state));
+ break;
+ case DWC3_DEVICE_EVENT_WAKEUP:
+ sprintf(str, "WakeUp [%s]", dwc3_gadget_link_string(state));
+ break;
+ case DWC3_DEVICE_EVENT_EOPF:
+ sprintf(str, "End-Of-Frame [%s]",
+ dwc3_gadget_link_string(state));
+ break;
+ case DWC3_DEVICE_EVENT_SOF:
+ sprintf(str, "Start-Of-Frame [%s]",
+ dwc3_gadget_link_string(state));
+ break;
+ case DWC3_DEVICE_EVENT_ERRATIC_ERROR:
+ sprintf(str, "Erratic Error [%s]",
+ dwc3_gadget_link_string(state));
+ break;
+ case DWC3_DEVICE_EVENT_CMD_CMPL:
+ sprintf(str, "Command Complete [%s]",
+ dwc3_gadget_link_string(state));
+ break;
+ case DWC3_DEVICE_EVENT_OVERFLOW:
+ sprintf(str, "Overflow [%s]", dwc3_gadget_link_string(state));
+ break;
+ default:
+ sprintf(str, "UNKNOWN");
+ }
+
+ return str;
+}
+
+static inline void dwc3_decode_get_status(__u8 t, __u16 i, __u16 l, char *str)
+{
+ switch (t & USB_RECIP_MASK) {
+ case USB_RECIP_INTERFACE:
+ sprintf(str, "Get Interface Status(Intf = %d, Length = %d)",
+ i, l);
+ break;
+ case USB_RECIP_ENDPOINT:
+ sprintf(str, "Get Endpoint Status(ep%d%s)",
+ i & ~USB_DIR_IN,
+ i & USB_DIR_IN ? "in" : "out");
+ break;
+ }
+}
+
+static inline void dwc3_decode_set_clear_feature(__u8 t, __u8 b, __u16 v,
+ __u16 i, char *str)
+{
+ switch (t & USB_RECIP_MASK) {
+ case USB_RECIP_DEVICE:
+ sprintf(str, "%s Device Feature(%s%s)",
+ b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
+ ({char *s;
+ switch (v) {
+ case USB_DEVICE_SELF_POWERED:
+ s = "Self Powered";
+ break;
+ case USB_DEVICE_REMOTE_WAKEUP:
+ s = "Remote Wakeup";
+ break;
+ case USB_DEVICE_TEST_MODE:
+ s = "Test Mode";
+ break;
+ case USB_DEVICE_U1_ENABLE:
+ s = "U1 Enable";
+ break;
+ case USB_DEVICE_U2_ENABLE:
+ s = "U2 Enable";
+ break;
+ case USB_DEVICE_LTM_ENABLE:
+ s = "LTM Enable";
+ break;
+ default:
+ s = "UNKNOWN";
+ } s; }),
+ v == USB_DEVICE_TEST_MODE ?
+ ({ char *s;
+ switch (i) {
+ case TEST_J:
+ s = ": TEST_J";
+ break;
+ case TEST_K:
+ s = ": TEST_K";
+ break;
+ case TEST_SE0_NAK:
+ s = ": TEST_SE0_NAK";
+ break;
+ case TEST_PACKET:
+ s = ": TEST_PACKET";
+ break;
+ case TEST_FORCE_EN:
+ s = ": TEST_FORCE_EN";
+ break;
+ default:
+ s = ": UNKNOWN";
+ } s; }) : "");
+ break;
+ case USB_RECIP_INTERFACE:
+ sprintf(str, "%s Interface Feature(%s)",
+ b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
+ v == USB_INTRF_FUNC_SUSPEND ?
+ "Function Suspend" : "UNKNOWN");
+ break;
+ case USB_RECIP_ENDPOINT:
+ sprintf(str, "%s Endpoint Feature(%s ep%d%s)",
+ b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
+ v == USB_ENDPOINT_HALT ? "Halt" : "UNKNOWN",
+ i & ~USB_DIR_IN,
+ i & USB_DIR_IN ? "in" : "out");
+ break;
+ }
+}
+
+static inline void dwc3_decode_set_address(__u16 v, char *str)
+{
+ sprintf(str, "Set Address(Addr = %02x)", v);
+}
+
+static inline void dwc3_decode_get_set_descriptor(__u8 t, __u8 b, __u16 v,
+ __u16 i, __u16 l, char *str)
+{
+ sprintf(str, "%s %s Descriptor(Index = %d, Length = %d)",
+ b == USB_REQ_GET_DESCRIPTOR ? "Get" : "Set",
+ ({ char *s;
+ switch (v >> 8) {
+ case USB_DT_DEVICE:
+ s = "Device";
+ break;
+ case USB_DT_CONFIG:
+ s = "Configuration";
+ break;
+ case USB_DT_STRING:
+ s = "String";
+ break;
+ case USB_DT_INTERFACE:
+ s = "Interface";
+ break;
+ case USB_DT_ENDPOINT:
+ s = "Endpoint";
+ break;
+ case USB_DT_DEVICE_QUALIFIER:
+ s = "Device Qualifier";
+ break;
+ case USB_DT_OTHER_SPEED_CONFIG:
+ s = "Other Speed Config";
+ break;
+ case USB_DT_INTERFACE_POWER:
+ s = "Interface Power";
+ break;
+ case USB_DT_OTG:
+ s = "OTG";
+ break;
+ case USB_DT_DEBUG:
+ s = "Debug";
+ break;
+ case USB_DT_INTERFACE_ASSOCIATION:
+ s = "Interface Association";
+ break;
+ case USB_DT_BOS:
+ s = "BOS";
+ break;
+ case USB_DT_DEVICE_CAPABILITY:
+ s = "Device Capability";
+ break;
+ case USB_DT_PIPE_USAGE:
+ s = "Pipe Usage";
+ break;
+ case USB_DT_SS_ENDPOINT_COMP:
+ s = "SS Endpoint Companion";
+ break;
+ case USB_DT_SSP_ISOC_ENDPOINT_COMP:
+ s = "SSP Isochronous Endpoint Companion";
+ break;
+ default:
+ s = "UNKNOWN";
+ break;
+ } s; }), v & 0xff, l);
+}
+
+
+static inline void dwc3_decode_get_configuration(__u16 l, char *str)
+{
+ sprintf(str, "Get Configuration(Length = %d)", l);
+}
+
+static inline void dwc3_decode_set_configuration(__u8 v, char *str)
+{
+ sprintf(str, "Set Configuration(Config = %d)", v);
+}
+
+static inline void dwc3_decode_get_intf(__u16 i, __u16 l, char *str)
+{
+ sprintf(str, "Get Interface(Intf = %d, Length = %d)", i, l);
+}
+
+static inline void dwc3_decode_set_intf(__u8 v, __u16 i, char *str)
+{
+ sprintf(str, "Set Interface(Intf = %d, Alt.Setting = %d)", i, v);
+}
+
+static inline void dwc3_decode_synch_frame(__u16 i, __u16 l, char *str)
+{
+ sprintf(str, "Synch Frame(Endpoint = %d, Length = %d)", i, l);
+}
+
+static inline void dwc3_decode_set_sel(__u16 l, char *str)
+{
+ sprintf(str, "Set SEL(Length = %d)", l);
+}
+
+static inline void dwc3_decode_set_isoch_delay(__u8 v, char *str)
+{
+ sprintf(str, "Set Isochronous Delay(Delay = %d ns)", v);
+}
+
+/**
+ * dwc3_decode_ctrl - returns a string represetion of ctrl request
+ */
+static inline const char *dwc3_decode_ctrl(char *str, __u8 bRequestType,
+ __u8 bRequest, __u16 wValue, __u16 wIndex, __u16 wLength)
+{
+ switch (bRequest) {
+ case USB_REQ_GET_STATUS:
+ dwc3_decode_get_status(bRequestType, wIndex, wLength, str);
+ break;
+ case USB_REQ_CLEAR_FEATURE:
+ case USB_REQ_SET_FEATURE:
+ dwc3_decode_set_clear_feature(bRequestType, bRequest, wValue,
+ wIndex, str);
+ break;
+ case USB_REQ_SET_ADDRESS:
+ dwc3_decode_set_address(wValue, str);
+ break;
+ case USB_REQ_GET_DESCRIPTOR:
+ case USB_REQ_SET_DESCRIPTOR:
+ dwc3_decode_get_set_descriptor(bRequestType, bRequest, wValue,
+ wIndex, wLength, str);
+ break;
+ case USB_REQ_GET_CONFIGURATION:
+ dwc3_decode_get_configuration(wLength, str);
+ break;
+ case USB_REQ_SET_CONFIGURATION:
+ dwc3_decode_set_configuration(wValue, str);
+ break;
+ case USB_REQ_GET_INTERFACE:
+ dwc3_decode_get_intf(wIndex, wLength, str);
+ break;
+ case USB_REQ_SET_INTERFACE:
+ dwc3_decode_set_intf(wValue, wIndex, str);
+ break;
+ case USB_REQ_SYNCH_FRAME:
+ dwc3_decode_synch_frame(wIndex, wLength, str);
+ break;
+ case USB_REQ_SET_SEL:
+ dwc3_decode_set_sel(wLength, str);
+ break;
+ case USB_REQ_SET_ISOCH_DELAY:
+ dwc3_decode_set_isoch_delay(wValue, str);
+ break;
+ default:
+ sprintf(str, "%02x %02x %02x %02x %02x %02x %02x %02x",
+ bRequestType, bRequest,
+ cpu_to_le16(wValue) & 0xff,
+ cpu_to_le16(wValue) >> 8,
+ cpu_to_le16(wIndex) & 0xff,
+ cpu_to_le16(wIndex) >> 8,
+ cpu_to_le16(wLength) & 0xff,
+ cpu_to_le16(wLength) >> 8);
+ }
+
+ return str;
+}
+
+/**
+ * dwc3_ep_event_string - returns event name
+ * @event: then event code
+ */
+static inline const char *
+dwc3_ep_event_string(char *str, const struct dwc3_event_depevt *event,
+ u32 ep0state)
+{
+ u8 epnum = event->endpoint_number;
+ size_t len;
+ int status;
+ int ret;
+
+ ret = sprintf(str, "ep%d%s: ", epnum >> 1,
+ (epnum & 1) ? "in" : "out");
+ if (ret < 0)
+ return "UNKNOWN";
+
+ status = event->status;
+
+ switch (event->endpoint_event) {
+ case DWC3_DEPEVT_XFERCOMPLETE:
+ len = strlen(str);
+ sprintf(str + len, "Transfer Complete (%c%c%c)",
+ status & DEPEVT_STATUS_SHORT ? 'S' : 's',
+ status & DEPEVT_STATUS_IOC ? 'I' : 'i',
+ status & DEPEVT_STATUS_LST ? 'L' : 'l');
+
+ len = strlen(str);
+
+ if (epnum <= 1)
+ sprintf(str + len, " [%s]", dwc3_ep0_state_string(ep0state));
+ break;
+ case DWC3_DEPEVT_XFERINPROGRESS:
+ len = strlen(str);
+
+ sprintf(str + len, "Transfer In Progress [%d] (%c%c%c)",
+ event->parameters,
+ status & DEPEVT_STATUS_SHORT ? 'S' : 's',
+ status & DEPEVT_STATUS_IOC ? 'I' : 'i',
+ status & DEPEVT_STATUS_LST ? 'M' : 'm');
+ break;
+ case DWC3_DEPEVT_XFERNOTREADY:
+ len = strlen(str);
+
+ sprintf(str + len, "Transfer Not Ready [%d]%s",
+ event->parameters,
+ status & DEPEVT_STATUS_TRANSFER_ACTIVE ?
+ " (Active)" : " (Not Active)");
+
+ /* Control Endpoints */
+ if (epnum <= 1) {
+ int phase = DEPEVT_STATUS_CONTROL_PHASE(event->status);
+
+ switch (phase) {
+ case DEPEVT_STATUS_CONTROL_DATA:
+ strcat(str, " [Data Phase]");
+ break;
+ case DEPEVT_STATUS_CONTROL_STATUS:
+ strcat(str, " [Status Phase]");
+ }
+ }
+ break;
+ case DWC3_DEPEVT_RXTXFIFOEVT:
+ strcat(str, "FIFO");
+ break;
+ case DWC3_DEPEVT_STREAMEVT:
+ status = event->status;
+
+ switch (status) {
+ case DEPEVT_STREAMEVT_FOUND:
+ sprintf(str + ret, " Stream %d Found",
+ event->parameters);
+ break;
+ case DEPEVT_STREAMEVT_NOTFOUND:
+ default:
+ strcat(str, " Stream Not Found");
+ break;
+ }
+
+ break;
+ case DWC3_DEPEVT_EPCMDCMPLT:
+ strcat(str, "Endpoint Command Complete");
+ break;
+ default:
+ sprintf(str, "UNKNOWN");
+ }
+
+ return str;
+}
+
+/**
+ * dwc3_gadget_event_type_string - return event name
+ * @event: the event code
+ */
+static inline const char *dwc3_gadget_event_type_string(u8 event)
+{
+ switch (event) {
+ case DWC3_DEVICE_EVENT_DISCONNECT:
+ return "Disconnect";
+ case DWC3_DEVICE_EVENT_RESET:
+ return "Reset";
+ case DWC3_DEVICE_EVENT_CONNECT_DONE:
+ return "Connect Done";
+ case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE:
+ return "Link Status Change";
+ case DWC3_DEVICE_EVENT_WAKEUP:
+ return "Wake-Up";
+ case DWC3_DEVICE_EVENT_HIBER_REQ:
+ return "Hibernation";
+ case DWC3_DEVICE_EVENT_EOPF:
+ return "End of Periodic Frame";
+ case DWC3_DEVICE_EVENT_SOF:
+ return "Start of Frame";
+ case DWC3_DEVICE_EVENT_ERRATIC_ERROR:
+ return "Erratic Error";
+ case DWC3_DEVICE_EVENT_CMD_CMPL:
+ return "Command Complete";
+ case DWC3_DEVICE_EVENT_OVERFLOW:
+ return "Overflow";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+static inline const char *dwc3_decode_event(char *str, u32 event, u32 ep0state)
+{
+ const union dwc3_event evt = (union dwc3_event) event;
+
+ if (evt.type.is_devspec)
+ return dwc3_gadget_event_string(str, &evt.devt);
+ else
+ return dwc3_ep_event_string(str, &evt.depevt, ep0state);
+}
+
+static inline const char *dwc3_ep_cmd_status_string(int status)
+{
+ switch (status) {
+ case -ETIMEDOUT:
+ return "Timed Out";
+ case 0:
+ return "Successful";
+ case DEPEVT_TRANSFER_NO_RESOURCE:
+ return "No Resource";
+ case DEPEVT_TRANSFER_BUS_EXPIRY:
+ return "Bus Expiry";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+static inline const char *dwc3_gadget_generic_cmd_status_string(int status)
+{
+ switch (status) {
+ case -ETIMEDOUT:
+ return "Timed Out";
+ case 0:
+ return "Successful";
+ case 1:
+ return "Error";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+
+#ifdef CONFIG_DEBUG_FS
+extern void dwc3_debugfs_init(struct dwc3 *);
+extern void dwc3_debugfs_exit(struct dwc3 *);
+#else
+static inline void dwc3_debugfs_init(struct dwc3 *d)
+{ }
+static inline void dwc3_debugfs_exit(struct dwc3 *d)
+{ }
+#endif
+#endif /* __DWC3_DEBUG_H */
diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c
new file mode 100644
index 0000000000..d5daa7f19e
--- /dev/null
+++ b/drivers/usb/dwc3/host.c
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0
+/**
+ * host.c - DesignWare USB3 DRD Controller Host Glue
+ *
+ * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Authors: Felipe Balbi <balbi@ti.com>,
+ */
+
+#include <common.h>
+#include <driver.h>
+#include <init.h>
+
+#include "core.h"
+
+int dwc3_host_init(struct dwc3 *dwc)
+{
+ struct resource *io;
+ struct device_d *dev = dwc->dev;
+
+ io = dev_get_resource(dev, IORESOURCE_MEM, 0);
+ if (IS_ERR(io)) {
+ dev_err(dev, "Failed to get IORESOURCE_MEM\n");
+ return PTR_ERR(io);
+ }
+
+ dwc->xhci = add_generic_device("xHCI", DEVICE_ID_DYNAMIC, NULL,
+ io->start, resource_size(io),
+ IORESOURCE_MEM, NULL);
+ if (!dwc->xhci) {
+ dev_err(dev, "Failed to register xHCI device\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
diff --git a/drivers/usb/dwc3/io.h b/drivers/usb/dwc3/io.h
new file mode 100644
index 0000000000..f87b173e90
--- /dev/null
+++ b/drivers/usb/dwc3/io.h
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-2.0
+/**
+ * io.h - DesignWare USB3 DRD IO Header
+ *
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Authors: Felipe Balbi <balbi@ti.com>,
+ * Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+ */
+
+#ifndef __DRIVERS_USB_DWC3_IO_H
+#define __DRIVERS_USB_DWC3_IO_H
+
+#include <io.h>
+#include "core.h"
+
+static inline u32 dwc3_readl(void __iomem *base, u32 offset)
+{
+ u32 value;
+
+ /*
+ * We requested the mem region starting from the Globals address
+ * space, see dwc3_probe in core.c.
+ * However, the offsets are given starting from xHCI address space.
+ */
+ value = readl(base + offset - DWC3_GLOBALS_REGS_START);
+
+ return value;
+}
+
+static inline void dwc3_writel(void __iomem *base, u32 offset, u32 value)
+{
+ /*
+ * We requested the mem region starting from the Globals address
+ * space, see dwc3_probe in core.c.
+ * However, the offsets are given starting from xHCI address space.
+ */
+ writel(value, base + offset - DWC3_GLOBALS_REGS_START);
+}
+
+#endif /* __DRIVERS_USB_DWC3_IO_H */
diff --git a/drivers/usb/host/xhci-hcd.c b/drivers/usb/host/xhci-hcd.c
index 2b808cc875..32a6ccd5cd 100644
--- a/drivers/usb/host/xhci-hcd.c
+++ b/drivers/usb/host/xhci-hcd.c
@@ -23,16 +23,49 @@
#include "xhci.h"
+
+static struct xhci_input_control_ctx *
+xhci_get_input_control_ctx(struct xhci_container_ctx *ctx)
+{
+ if (ctx->type != XHCI_CTX_TYPE_INPUT)
+ return NULL;
+
+ return (struct xhci_input_control_ctx *)ctx->bytes;
+}
+
+static struct xhci_slot_ctx *xhci_get_slot_ctx(struct xhci_hcd *xhci,
+ struct xhci_container_ctx *ctx)
+{
+ if (ctx->type == XHCI_CTX_TYPE_DEVICE)
+ return (struct xhci_slot_ctx *)ctx->bytes;
+
+ return (struct xhci_slot_ctx *)
+ (ctx->bytes + HCC_CTX_SIZE(xhci->hcc_params));
+}
+
+static struct xhci_ep_ctx *xhci_get_ep_ctx(struct xhci_hcd *xhci,
+ struct xhci_container_ctx *ctx,
+ unsigned int ep_index)
+{
+ /* increment ep index by offset of start of ep ctx array */
+ ep_index++;
+ if (ctx->type == XHCI_CTX_TYPE_INPUT)
+ ep_index++;
+
+ return (struct xhci_ep_ctx *)
+ (ctx->bytes + (ep_index * HCC_CTX_SIZE(xhci->hcc_params)));
+}
+
/*
* xHCI ring handling
*/
static int xhci_ring_is_last_trb(struct xhci_ring *ring, union xhci_trb *trb)
{
- if (ring->type == TYPE_EVENT)
- return trb == &ring->trbs[NUM_EVENT_TRBS];
- else
- return TRB_TYPE_LINK(le32_to_cpu(trb->link.control));
+ if (ring->type == TYPE_EVENT)
+ return trb == &ring->trbs[NUM_EVENT_TRBS];
+ else
+ return TRB_TYPE_LINK(le32_to_cpu(trb->link.control));
}
static int xhci_ring_increment(struct xhci_ring *ring, bool enqueue)
@@ -41,7 +74,7 @@ static int xhci_ring_increment(struct xhci_ring *ring, bool enqueue)
(*queue)++;
- if (!xhci_ring_is_last_trb(ring, *queue))
+ if (!xhci_ring_is_last_trb(ring, *queue))
return 0;
if (ring->type == TYPE_EVENT) {
@@ -139,15 +172,15 @@ static unsigned int xhci_get_endpoint_index(u8 epaddress, u8 epattributes)
{
u8 epnum = epaddress & USB_ENDPOINT_NUMBER_MASK;
u8 xfer = epattributes & USB_ENDPOINT_XFERTYPE_MASK;
- unsigned int index;
+ unsigned int index;
- if (xfer == USB_ENDPOINT_XFER_CONTROL)
- index = (unsigned int)(epnum * 2);
- else
- index = (unsigned int)(epnum * 2) +
- ((epaddress & USB_DIR_IN) ? 1 : 0) - 1;
+ if (xfer == USB_ENDPOINT_XFER_CONTROL)
+ index = (unsigned int)(epnum * 2);
+ else
+ index = (unsigned int)(epnum * 2) +
+ ((epaddress & USB_DIR_IN) ? 1 : 0) - 1;
- return index;
+ return index;
}
static u8 xhci_get_endpoint_type(u8 epaddress, u8 epattributes)
@@ -341,15 +374,15 @@ int xhci_issue_command(struct xhci_hcd *xhci, union xhci_trb *trb)
static void xhci_set_event_dequeue(struct xhci_hcd *xhci)
{
- u64 reg64;
+ u64 reg64;
- reg64 = xhci_read_64(&xhci->ir_set->erst_dequeue);
- reg64 &= ERST_PTR_MASK;
- /*
+ reg64 = xhci_read_64(&xhci->ir_set->erst_dequeue);
+ reg64 &= ERST_PTR_MASK;
+ /*
* Don't clear the EHB bit (which is RW1C) because
- * there might be more events to service.
- */
- reg64 &= ~ERST_EHB;
+ * there might be more events to service.
+ */
+ reg64 &= ~ERST_EHB;
reg64 |= (dma_addr_t)xhci->event_ring.dequeue &
~(dma_addr_t)ERST_PTR_MASK;
@@ -426,29 +459,48 @@ static struct xhci_virtual_device *xhci_find_virtdev(struct xhci_hcd *xhci,
return NULL;
}
+static struct xhci_container_ctx *
+xhci_alloc_container_ctx(struct xhci_hcd *xhci, int type)
+{
+ struct xhci_container_ctx *ctx;
+
+ if ((type != XHCI_CTX_TYPE_DEVICE) && (type != XHCI_CTX_TYPE_INPUT))
+ return NULL;
+
+ ctx = xzalloc(sizeof(*ctx));
+ ctx->type = type;
+ ctx->size = HCC_64BYTE_CONTEXT(xhci->hcc_params) ? 2048 : 1024;
+ if (type == XHCI_CTX_TYPE_INPUT)
+ ctx->size += HCC_CTX_SIZE(xhci->hcc_params);
+
+ ctx->bytes = dma_alloc_coherent(ctx->size, &ctx->dma);
+ if (WARN_ON(!ctx->bytes)) {
+ kfree(ctx);
+ return NULL;
+ }
+ return ctx;
+}
+
+static void xhci_free_container_ctx(struct xhci_hcd *xhci,
+ struct xhci_container_ctx *ctx)
+{
+ if (!ctx)
+ return;
+ dma_free_coherent(ctx->bytes, ctx->dma, ctx->size);
+ kfree(ctx);
+}
+
static struct xhci_virtual_device *xhci_alloc_virtdev(struct xhci_hcd *xhci,
struct usb_device *udev)
{
struct xhci_virtual_device *vdev;
- size_t sz_ctx, sz_ictx, sz_dctx;
- void *p;
vdev = xzalloc(sizeof(*vdev));
vdev->udev = udev;
list_add_tail(&vdev->list, &xhci->vdev_list);
- sz_ctx = HCC_64BYTE_CONTEXT(xhci->hcc_params) ? 2048 : 1024;
- /* Device Context: 64B aligned */
- sz_dctx = ALIGN(sz_ctx, 64);
- /* Input Control Context: 64B aligned */
- sz_ictx = ALIGN(sz_ctx + HCC_CTX_SIZE(xhci->hcc_params), 64);
-
- vdev->dma_size = sz_ictx + sz_dctx;
- p = vdev->dma = dma_alloc_coherent(vdev->dma_size, DMA_ADDRESS_BROKEN);
- memset(vdev->dma, 0, vdev->dma_size);
-
- vdev->out_ctx = p; p += sz_dctx;
- vdev->in_ctx = p; p += sz_ictx;
+ vdev->out_ctx = xhci_alloc_container_ctx(xhci, XHCI_CTX_TYPE_DEVICE);
+ vdev->in_ctx = xhci_alloc_container_ctx(xhci, XHCI_CTX_TYPE_INPUT);
return vdev;
}
@@ -463,7 +515,8 @@ static void xhci_free_virtdev(struct xhci_virtual_device *vdev)
xhci_put_endpoint_ring(xhci, vdev->ep[i]);
list_del(&vdev->list);
- dma_free_coherent(vdev->dma, 0, vdev->dma_size);
+ xhci_free_container_ctx(xhci, vdev->out_ctx);
+ xhci_free_container_ctx(xhci, vdev->in_ctx);
free(vdev);
}
@@ -487,26 +540,43 @@ static int xhci_virtdev_issue_transfer(struct xhci_virtual_device *vdev,
static void xhci_virtdev_zero_in_ctx(struct xhci_virtual_device *vdev)
{
+ struct xhci_hcd *xhci = to_xhci_hcd(vdev->udev->host);
+ struct xhci_input_control_ctx *in_icc;
+ struct xhci_slot_ctx *in_slot;
+ struct xhci_ep_ctx *in_ep;
int i;
- /* When a device's add flag and drop flag are zero, any subsequent
- * configure endpoint command will leave that endpoint's state
- * untouched. Make sure we don't leave any old state in the input
- * endpoint contexts.
- */
- vdev->in_ctx->icc.drop_flags = 0;
- vdev->in_ctx->icc.add_flags = 0;
- vdev->in_ctx->slot.dev_info &= cpu_to_le32(~LAST_CTX_MASK);
- /* Endpoint 0 is always valid */
- vdev->in_ctx->slot.dev_info |= cpu_to_le32(LAST_CTX(1));
+ in_icc = xhci_get_input_control_ctx(vdev->in_ctx);
+ in_slot = xhci_get_slot_ctx(xhci, vdev->in_ctx);
+
+ /* When a device's add flag and drop flag are zero, any subsequent
+ * configure endpoint command will leave that endpoint's state
+ * untouched. Make sure we don't leave any old state in the input
+ * endpoint contexts.
+ */
+ in_icc->drop_flags = 0;
+ in_icc->add_flags = 0;
+ in_slot->dev_info &= cpu_to_le32(~LAST_CTX_MASK);
+ /* Endpoint 0 is always valid */
+ in_slot->dev_info |= cpu_to_le32(LAST_CTX(1));
for (i = 1; i < 31; i++) {
- vdev->in_ctx->ep[i].ep_info = 0;
- vdev->in_ctx->ep[i].ep_info2 = 0;
- vdev->in_ctx->ep[i].deq = 0;
- vdev->in_ctx->ep[i].tx_info = 0;
+ in_ep = xhci_get_ep_ctx(xhci, vdev->in_ctx, i);
+
+ in_ep->ep_info = 0;
+ in_ep->ep_info2 = 0;
+ in_ep->deq = 0;
+ in_ep->tx_info = 0;
}
}
+static void xhci_init_event_cmd_trb(union xhci_trb *trb,
+ u64 cmd_trb, u32 status, u32 flags)
+{
+ xhci_write_64(cmd_trb, &trb->event_cmd.cmd_trb);
+ writel(status, &trb->event_cmd.status);
+ writel(flags, &trb->event_cmd.flags);
+}
+
static int xhci_virtdev_disable_slot(struct xhci_virtual_device *vdev)
{
struct xhci_hcd *xhci = to_xhci_hcd(vdev->udev->host);
@@ -514,15 +584,17 @@ static int xhci_virtdev_disable_slot(struct xhci_virtual_device *vdev)
int ret;
/* Issue Disable Slot Command */
- memset(&trb, 0, sizeof(union xhci_trb));
- trb.event_cmd.flags = TRB_TYPE(TRB_DISABLE_SLOT) |
- SLOT_ID_FOR_TRB(vdev->slot_id);
+ xhci_init_event_cmd_trb(&trb,
+ 0,
+ 0,
+ TRB_TYPE(TRB_DISABLE_SLOT) |
+ SLOT_ID_FOR_TRB(vdev->slot_id));
xhci_print_trb(xhci, &trb, "Request DisableSlot");
xhci_issue_command(xhci, &trb);
ret = xhci_wait_for_event(xhci, TRB_COMPLETION, &trb);
xhci_print_trb(xhci, &trb, "Response DisableSlot");
- /* Clear Device Context Base Address Array */
+ /* Clear Device Context Base Address Array */
xhci->dcbaa[vdev->slot_id] = 0;
return ret;
@@ -536,8 +608,10 @@ static int xhci_virtdev_enable_slot(struct xhci_virtual_device *vdev)
int ret;
/* Issue Enable Slot Command */
- memset(&trb, 0, sizeof(union xhci_trb));
- trb.event_cmd.flags = TRB_TYPE(TRB_ENABLE_SLOT);
+ xhci_init_event_cmd_trb(&trb,
+ 0,
+ 0,
+ TRB_TYPE(TRB_ENABLE_SLOT));
xhci_print_trb(xhci, &trb, "Request EnableSlot");
xhci_issue_command(xhci, &trb);
ret = xhci_wait_for_event(xhci, TRB_COMPLETION, &trb);
@@ -559,42 +633,47 @@ static int xhci_virtdev_enable_slot(struct xhci_virtual_device *vdev)
int xhci_virtdev_reset(struct xhci_virtual_device *vdev)
{
struct xhci_hcd *xhci = to_xhci_hcd(vdev->udev->host);
+ struct xhci_slot_ctx *out_slot;
union xhci_trb trb;
int ret;
+ out_slot = xhci_get_slot_ctx(xhci, vdev->out_ctx);
+
/* If device is not setup, there is no point in resetting it */
- if (GET_SLOT_STATE(le32_to_cpu(vdev->out_ctx->slot.dev_state)) ==
+ if (GET_SLOT_STATE(le32_to_cpu(out_slot->dev_state)) ==
SLOT_STATE_DISABLED)
- return 0;
+ return 0;
- memset(&trb, 0, sizeof(union xhci_trb));
- trb.event_cmd.flags = TRB_TYPE(TRB_RESET_DEV) |
- SLOT_ID_FOR_TRB(vdev->slot_id);
+ xhci_init_event_cmd_trb(&trb,
+ 0,
+ 0,
+ TRB_TYPE(TRB_RESET_DEV) |
+ SLOT_ID_FOR_TRB(vdev->slot_id));
xhci_print_trb(xhci, &trb, "Request Reset");
xhci_issue_command(xhci, &trb);
ret = xhci_wait_for_event(xhci, TRB_COMPLETION, &trb);
xhci_print_trb(xhci, &trb, "Response Reset");
- /*
+ /*
* The Reset Device command can't fail, according to the 0.95/0.96 spec,
- * unless we tried to reset a slot ID that wasn't enabled,
- * or the device wasn't in the addressed or configured state.
- */
- switch (GET_COMP_CODE(trb.event_cmd.status)) {
- case COMP_CMD_ABORT:
- case COMP_CMD_STOP:
- dev_warn(xhci->dev, "Timeout waiting for reset device command\n");
- ret = -ETIMEDOUT;
+ * unless we tried to reset a slot ID that wasn't enabled,
+ * or the device wasn't in the addressed or configured state.
+ */
+ switch (GET_COMP_CODE(trb.event_cmd.status)) {
+ case COMP_CMD_ABORT:
+ case COMP_CMD_STOP:
+ dev_warn(xhci->dev, "Timeout waiting for reset device command\n");
+ ret = -ETIMEDOUT;
+ break;
+ case COMP_EBADSLT: /* 0.95 completion code for bad slot ID */
+ case COMP_CTX_STATE: /* 0.96 completion code for same thing */
+ /* Don't treat this as an error. May change my mind later. */
+ ret = 0;
+ case COMP_SUCCESS:
break;
- case COMP_EBADSLT: /* 0.95 completion code for bad slot ID */
- case COMP_CTX_STATE: /* 0.96 completion code for same thing */
- /* Don't treat this as an error. May change my mind later. */
- ret = 0;
- case COMP_SUCCESS:
- break;
- default:
- ret = -EINVAL;
- }
+ default:
+ ret = -EINVAL;
+ }
return ret;
}
@@ -608,31 +687,38 @@ static int xhci_virtdev_update_hub_device(struct xhci_virtual_device *vdev,
{
struct xhci_hcd *xhci = to_xhci_hcd(vdev->udev->host);
struct usb_hub_descriptor *desc = buffer;
+ struct xhci_input_control_ctx *in_icc;
+ struct xhci_slot_ctx *in_slot, *out_slot;
union xhci_trb trb;
u32 dev_info, dev_info2, tt_info;
u8 maxchild;
u16 hubchar;
+ u32 flags;
int ret;
+ out_slot = xhci_get_slot_ctx(xhci, vdev->out_ctx);
+
/* Need at least first byte of wHubCharacteristics */
if (length < 4)
return 0;
/* Skip already configured hub device */
- if (vdev->out_ctx->slot.dev_info & DEV_HUB)
+ if (out_slot->dev_info & DEV_HUB)
return 0;
maxchild = desc->bNbrPorts;
hubchar = le16_to_cpu(desc->wHubCharacteristics);
+ in_slot = xhci_get_slot_ctx(xhci, vdev->in_ctx);
+ in_icc = xhci_get_input_control_ctx(vdev->in_ctx);
+
/* update slot context */
- memcpy(&vdev->in_ctx->slot, &vdev->out_ctx->slot,
- sizeof(struct xhci_slot_ctx));
- vdev->in_ctx->icc.add_flags |= cpu_to_le32(SLOT_FLAG);
- vdev->in_ctx->icc.drop_flags = 0;
- vdev->in_ctx->slot.dev_state = 0;
- dev_info = le32_to_cpu(vdev->in_ctx->slot.dev_info);
- dev_info2 = le32_to_cpu(vdev->in_ctx->slot.dev_info2);
- tt_info = le32_to_cpu(vdev->in_ctx->slot.tt_info);
+ memcpy(in_slot, out_slot, sizeof(struct xhci_slot_ctx));
+ in_icc->add_flags |= cpu_to_le32(SLOT_FLAG);
+ in_icc->drop_flags = 0;
+ in_slot->dev_state = 0;
+ dev_info = le32_to_cpu(in_slot->dev_info);
+ dev_info2 = le32_to_cpu(in_slot->dev_info2);
+ tt_info = le32_to_cpu(in_slot->tt_info);
dev_info |= DEV_HUB;
/* HS Multi-TT in bDeviceProtocol */
@@ -651,21 +737,23 @@ static int xhci_virtdev_update_hub_device(struct xhci_virtual_device *vdev,
if (xhci->hci_version < 0x100 ||
vdev->udev->speed == USB_SPEED_HIGH) {
u32 think_time = (hubchar & HUB_CHAR_TTTT) >> 5;
- tt_info |= TT_THINK_TIME(think_time);
+ tt_info |= TT_THINK_TIME(think_time);
}
- }
- vdev->in_ctx->slot.dev_info = cpu_to_le32(dev_info);
- vdev->in_ctx->slot.dev_info2 = cpu_to_le32(dev_info2);
- vdev->in_ctx->slot.tt_info = cpu_to_le32(tt_info);
-
- /* Issue Configure Endpoint or Evaluate Context Command */
- memset(&trb, 0, sizeof(union xhci_trb));
- xhci_write_64((dma_addr_t)vdev->in_ctx, &trb.event_cmd.cmd_trb);
- trb.event_cmd.flags = SLOT_ID_FOR_TRB(vdev->slot_id);
+ }
+ in_slot->dev_info = cpu_to_le32(dev_info);
+ in_slot->dev_info2 = cpu_to_le32(dev_info2);
+ in_slot->tt_info = cpu_to_le32(tt_info);
+
+ /* Issue Configure Endpoint or Evaluate Context Command */
+ flags = SLOT_ID_FOR_TRB(vdev->slot_id);
if (xhci->hci_version > 0x95)
- trb.event_cmd.flags |= TRB_TYPE(TRB_CONFIG_EP);
+ flags |= TRB_TYPE(TRB_CONFIG_EP);
else
- trb.event_cmd.flags |= TRB_TYPE(TRB_EVAL_CONTEXT);
+ flags |= TRB_TYPE(TRB_EVAL_CONTEXT);
+ xhci_init_event_cmd_trb(&trb,
+ vdev->in_ctx->dma,
+ 0,
+ flags);
xhci_print_trb(xhci, &trb, "Request ConfigureEndpoint");
xhci_issue_command(xhci, &trb);
ret = xhci_wait_for_event(xhci, TRB_COMPLETION, &trb);
@@ -696,6 +784,8 @@ static int xhci_virtdev_update_hub_status(struct xhci_virtual_device *vhub,
static int xhci_virtdev_configure(struct xhci_virtual_device *vdev, int config)
{
struct xhci_hcd *xhci = to_xhci_hcd(vdev->udev->host);
+ struct xhci_input_control_ctx *in_icc;
+ struct xhci_slot_ctx *in_slot;
struct usb_device *udev = vdev->udev;
union xhci_trb trb;
u32 add_flags = 0, last_ctx;
@@ -712,10 +802,12 @@ static int xhci_virtdev_configure(struct xhci_virtual_device *vdev, int config)
ep->bmAttributes);
u8 epi = xhci_get_endpoint_index(ep->bEndpointAddress,
ep->bmAttributes);
- struct xhci_ep_ctx *ctx = &vdev->in_ctx->ep[epi];
+ struct xhci_ep_ctx *ctx;
u32 mps, interval, mult, esit, max_packet, max_burst;
u32 ep_info, ep_info2, tx_info;
+ ctx = xhci_get_ep_ctx(xhci, vdev->in_ctx, epi);
+
vdev->ep[epi] = xhci_get_endpoint_ring(xhci);
if (!vdev->ep[epi])
return -ENOMEM;
@@ -782,25 +874,29 @@ static int xhci_virtdev_configure(struct xhci_virtual_device *vdev, int config)
last_ctx = fls(add_flags) - 1;
- /* See section 4.6.6 - A0 = 1; A1 = D0 = D1 = 0 */
- vdev->in_ctx->icc.add_flags = cpu_to_le32(add_flags);
- vdev->in_ctx->icc.add_flags |= cpu_to_le32(SLOT_FLAG);
- vdev->in_ctx->icc.add_flags &= cpu_to_le32(~EP0_FLAG);
- vdev->in_ctx->icc.drop_flags &= cpu_to_le32(~(SLOT_FLAG | EP0_FLAG));
+ in_slot = xhci_get_slot_ctx(xhci, vdev->in_ctx);
+ in_icc = xhci_get_input_control_ctx(vdev->in_ctx);
+
+ /* See section 4.6.6 - A0 = 1; A1 = D0 = D1 = 0 */
+ in_icc->add_flags = cpu_to_le32(add_flags);
+ in_icc->add_flags |= cpu_to_le32(SLOT_FLAG);
+ in_icc->add_flags &= cpu_to_le32(~EP0_FLAG);
+ in_icc->drop_flags &= cpu_to_le32(~(SLOT_FLAG | EP0_FLAG));
/* Don't issue the command if there's no endpoints to update. */
- if (vdev->in_ctx->icc.add_flags == cpu_to_le32(SLOT_FLAG) &&
- vdev->in_ctx->icc.drop_flags == 0)
+ if (in_icc->add_flags == cpu_to_le32(SLOT_FLAG) &&
+ in_icc->drop_flags == 0)
return 0;
- vdev->in_ctx->slot.dev_info &= cpu_to_le32(~LAST_CTX_MASK);
- vdev->in_ctx->slot.dev_info |= cpu_to_le32(LAST_CTX(last_ctx));
+ in_slot->dev_info &= cpu_to_le32(~LAST_CTX_MASK);
+ in_slot->dev_info |= cpu_to_le32(LAST_CTX(last_ctx));
- /* Issue Configure Endpoint Command */
- memset(&trb, 0, sizeof(union xhci_trb));
- xhci_write_64((dma_addr_t)vdev->in_ctx, &trb.event_cmd.cmd_trb);
- trb.event_cmd.flags = TRB_TYPE(TRB_CONFIG_EP) |
- SLOT_ID_FOR_TRB(vdev->slot_id);
+ /* Issue Configure Endpoint Command */
+ xhci_init_event_cmd_trb(&trb,
+ vdev->in_ctx->dma,
+ 0,
+ TRB_TYPE(TRB_CONFIG_EP) |
+ SLOT_ID_FOR_TRB(vdev->slot_id));
xhci_print_trb(xhci, &trb, "Request ConfigureEndpoint");
xhci_issue_command(xhci, &trb);
ret = xhci_wait_for_event(xhci, TRB_COMPLETION, &trb);
@@ -816,11 +912,12 @@ static int xhci_virtdev_deconfigure(struct xhci_virtual_device *vdev)
union xhci_trb trb;
int ret;
- /* Issue Deconfigure Endpoint Command */
- memset(&trb, 0, sizeof(union xhci_trb));
- xhci_write_64((dma_addr_t)vdev->in_ctx, &trb.event_cmd.cmd_trb);
- trb.event_cmd.flags = TRB_TYPE(TRB_CONFIG_EP) | TRB_DC |
- SLOT_ID_FOR_TRB(vdev->slot_id);
+ /* Issue Deconfigure Endpoint Command */
+ xhci_init_event_cmd_trb(&trb,
+ vdev->in_ctx->dma,
+ 0,
+ TRB_TYPE(TRB_CONFIG_EP) | TRB_DC |
+ SLOT_ID_FOR_TRB(vdev->slot_id));
xhci_print_trb(xhci, &trb, "Request DeconfigureEndpoint");
xhci_issue_command(xhci, &trb);
ret = xhci_wait_for_event(xhci, TRB_COMPLETION, &trb);
@@ -834,23 +931,30 @@ static int xhci_virtdev_init(struct xhci_virtual_device *vdev)
{
struct xhci_hcd *xhci = to_xhci_hcd(vdev->udev->host);
struct usb_device *top_dev;
+ struct xhci_slot_ctx *in_slot;
+ struct xhci_ep_ctx *in_ep;
int max_packets;
u32 route = 0, dev_info, dev_info2, tt_info, ep_info2, tx_info;
bool on_hs_hub = false;
int hs_slot_id = 0;
+ in_ep = xhci_get_ep_ctx(xhci, vdev->in_ctx, 0);
+ in_slot = xhci_get_slot_ctx(xhci, vdev->in_ctx);
+
/*
* Find the root hub port this device is under, also determine SlotID
* of possible external HS hub a LS/FS device could be connected to.
*/
for (top_dev = vdev->udev; top_dev->parent && top_dev->parent->parent;
top_dev = top_dev->parent) {
- if (top_dev->parent->descriptor->bDeviceClass == USB_CLASS_HUB)
- route = (route << 4) | (top_dev->portnr & 0xf);
- if (top_dev->parent->descriptor->bDeviceClass == USB_CLASS_HUB &&
- top_dev->parent->speed != USB_SPEED_LOW &&
+ if (top_dev->parent->descriptor->bDeviceClass != USB_CLASS_HUB)
+ continue;
+
+ route = (route << 4) | (top_dev->portnr & 0xf);
+
+ if (top_dev->parent->speed != USB_SPEED_LOW &&
top_dev->parent->speed != USB_SPEED_FULL) {
- on_hs_hub |= true;
+ on_hs_hub = true;
if (!hs_slot_id) {
struct xhci_virtual_device *vhub =
xhci_find_virtdev(xhci, top_dev->parent);
@@ -893,9 +997,9 @@ static int xhci_virtdev_init(struct xhci_virtual_device *vdev)
tt_info |= (top_dev->portnr << 8) | hs_slot_id;
}
- vdev->in_ctx->slot.dev_info = cpu_to_le32(dev_info);
- vdev->in_ctx->slot.dev_info2 = cpu_to_le32(dev_info2);
- vdev->in_ctx->slot.tt_info = cpu_to_le32(tt_info);
+ in_slot->dev_info = cpu_to_le32(dev_info);
+ in_slot->dev_info2 = cpu_to_le32(dev_info2);
+ in_slot->tt_info = cpu_to_le32(tt_info);
/* 4.3.3 4) Initalize Transfer Ring */
vdev->ep[0] = xhci_get_endpoint_ring(xhci);
@@ -907,13 +1011,13 @@ static int xhci_virtdev_init(struct xhci_virtual_device *vdev)
ep_info2 = EP_TYPE(CTRL_EP) | MAX_BURST(0) | ERROR_COUNT(3);
ep_info2 |= MAX_PACKET(max_packets);
tx_info = AVG_TRB_LENGTH_FOR_EP(8);
- vdev->in_ctx->ep[0].ep_info2 = cpu_to_le32(ep_info2);
- vdev->in_ctx->ep[0].tx_info = cpu_to_le32(tx_info);
- vdev->in_ctx->ep[0].deq = cpu_to_le64((dma_addr_t)vdev->ep[0]->enqueue |
- vdev->ep[0]->cycle_state);
+ in_ep->ep_info2 = cpu_to_le32(ep_info2);
+ in_ep->tx_info = cpu_to_le32(tx_info);
+ in_ep->deq = cpu_to_le64((dma_addr_t)vdev->ep[0]->enqueue |
+ vdev->ep[0]->cycle_state);
- /* 4.3.3 6+7) Initalize and Set Device Context Base Address Array */
- xhci->dcbaa[vdev->slot_id] = cpu_to_le64((dma_addr_t)vdev->out_ctx);
+ /* 4.3.3 6+7) Initalize and Set Device Context Base Address Array */
+ xhci->dcbaa[vdev->slot_id] = cpu_to_le64(vdev->out_ctx->dma);
return 0;
}
@@ -922,43 +1026,53 @@ static int xhci_virtdev_setup(struct xhci_virtual_device *vdev,
enum xhci_setup_dev setup)
{
struct xhci_hcd *xhci = to_xhci_hcd(vdev->udev->host);
+ static struct xhci_input_control_ctx *in_icc;
+ struct xhci_slot_ctx *in_slot;
+ struct xhci_ep_ctx *in_ep;
union xhci_trb trb;
+ u32 flags;
int ret;
+ in_slot = xhci_get_slot_ctx(xhci, vdev->in_ctx);
+ in_icc = xhci_get_input_control_ctx(vdev->in_ctx);
+
/*
* If this is the first Set Address since device
* plug-in then initialize Slot Context
*/
- if (!vdev->in_ctx->slot.dev_info)
+ if (!in_slot->dev_info)
xhci_virtdev_init(vdev);
else {
+ in_ep = xhci_get_ep_ctx(xhci, vdev->in_ctx, 0);
+
/* Otherwise, update Control Ring Dequeue pointer */
- vdev->in_ctx->ep[0].deq =
- cpu_to_le64((dma_addr_t)vdev->ep[0]->enqueue |
- vdev->ep[0]->cycle_state);
+ in_ep->deq = cpu_to_le64((dma_addr_t)vdev->ep[0]->enqueue |
+ vdev->ep[0]->cycle_state);
/*
* FS devices have MaxPacketSize0 of 8 or 64, we start
* with 64. If assumtion was wrong, fix it up here.
*/
if (vdev->udev->speed == USB_SPEED_FULL &&
vdev->udev->maxpacketsize == PACKET_SIZE_8) {
- u32 info = le32_to_cpu(vdev->in_ctx->ep[0].ep_info2);
+ u32 info = le32_to_cpu(in_ep->ep_info2);
info &= ~MAX_PACKET_MASK;
info |= MAX_PACKET(8);
- vdev->in_ctx->ep[0].ep_info2 = cpu_to_le32(info);
+ in_ep->ep_info2 = cpu_to_le32(info);
}
}
- vdev->in_ctx->icc.add_flags = cpu_to_le32(SLOT_FLAG | EP0_FLAG);
- vdev->in_ctx->icc.drop_flags = 0;
+ in_icc->add_flags = cpu_to_le32(SLOT_FLAG | EP0_FLAG);
+ in_icc->drop_flags = 0;
- /* Issue Address Device Command */
- memset(&trb, 0, sizeof(union xhci_trb));
- xhci_write_64((dma_addr_t)vdev->in_ctx, &trb.event_cmd.cmd_trb);
- trb.event_cmd.flags = TRB_TYPE(TRB_ADDR_DEV) |
+ /* Issue Address Device Command */
+ flags = TRB_TYPE(TRB_ADDR_DEV) |
SLOT_ID_FOR_TRB(vdev->slot_id);
if (setup == SETUP_CONTEXT_ONLY)
- trb.event_cmd.flags |= TRB_BSR;
+ flags |= TRB_BSR;
+ xhci_init_event_cmd_trb(&trb,
+ vdev->in_ctx->dma,
+ 0,
+ flags);
xhci_print_trb(xhci, &trb, "Request AddressDevice");
xhci_issue_command(xhci, &trb);
ret = xhci_wait_for_event(xhci, TRB_COMPLETION, &trb);
@@ -970,12 +1084,12 @@ static int xhci_virtdev_setup(struct xhci_virtual_device *vdev,
static int xhci_virtdev_set_address(struct xhci_virtual_device *vdev)
{
- return xhci_virtdev_setup(vdev, SETUP_CONTEXT_ADDRESS);
+ return xhci_virtdev_setup(vdev, SETUP_CONTEXT_ADDRESS);
}
static int xhci_virtdev_enable(struct xhci_virtual_device *vdev)
{
- return xhci_virtdev_setup(vdev, SETUP_CONTEXT_ONLY);
+ return xhci_virtdev_setup(vdev, SETUP_CONTEXT_ONLY);
}
static int xhci_virtdev_attach(struct xhci_hcd *xhci, struct usb_device *udev)
@@ -1008,45 +1122,57 @@ static int xhci_submit_normal(struct usb_device *udev, unsigned long pipe,
{
struct usb_host *host = udev->host;
struct xhci_hcd *xhci = to_xhci_hcd(host);
+ enum dma_data_direction dma_direction;
struct xhci_virtual_device *vdev;
+ struct xhci_slot_ctx *out_slot;
+ dma_addr_t buffer_dma;
union xhci_trb trb;
- u8 epaddr = (usb_pipein(pipe) ? USB_DIR_IN : USB_DIR_OUT) |
- usb_pipeendpoint(pipe);
- u8 epi = xhci_get_endpoint_index(epaddr, usb_pipetype(pipe));
+ u8 epaddr = usb_pipeendpoint(pipe);
+ u8 epi;
+ u32 flags = TRB_TYPE(TRB_NORMAL) | TRB_IOC;
int ret;
+ if (usb_pipein(pipe)) {
+ epaddr |= USB_DIR_IN;
+ flags |= TRB_ISP;
+ dma_direction = DMA_FROM_DEVICE;
+ } else {
+ epaddr |= USB_DIR_OUT;
+ dma_direction = DMA_TO_DEVICE;
+ }
+
+ epi = xhci_get_endpoint_index(epaddr, usb_pipetype(pipe));
vdev = xhci_find_virtdev(xhci, udev);
if (!vdev)
return -ENODEV;
+ out_slot = xhci_get_slot_ctx(xhci, vdev->out_ctx);
+
dev_dbg(xhci->dev, "%s udev %p vdev %p slot %u state %u epi %u in_ctx %p out_ctx %p\n",
__func__, udev, vdev, vdev->slot_id,
- GET_SLOT_STATE(le32_to_cpu(vdev->out_ctx->slot.dev_state)), epi,
- vdev->in_ctx, vdev->out_ctx);
+ GET_SLOT_STATE(le32_to_cpu(out_slot->dev_state)), epi,
+ vdev->in_ctx->bytes, vdev->out_ctx->bytes);
/* pass ownership of data buffer to device */
- dma_sync_single_for_device((unsigned long)buffer, length,
- usb_pipein(pipe) ?
- DMA_FROM_DEVICE : DMA_TO_DEVICE);
+ buffer_dma = dma_map_single(xhci->dev, buffer, length,
+ dma_direction);
+ if (dma_mapping_error(xhci->dev, buffer_dma))
+ return -EFAULT;
/* Normal TRB */
- memset(&trb, 0, sizeof(union xhci_trb));
- trb.event_cmd.cmd_trb = cpu_to_le64((dma_addr_t)buffer);
/* FIXME: TD remainder */
- trb.event_cmd.status = TRB_LEN(length) | TRB_INTR_TARGET(0);
- trb.event_cmd.flags = TRB_TYPE(TRB_NORMAL) | TRB_IOC;
- if (usb_pipein(pipe))
- trb.event_cmd.flags |= TRB_ISP;
+ xhci_init_event_cmd_trb(&trb,
+ buffer_dma,
+ TRB_LEN(length) | TRB_INTR_TARGET(0),
+ flags);
xhci_print_trb(xhci, &trb, "Request Normal");
xhci_virtdev_issue_transfer(vdev, epi, &trb, true);
ret = xhci_wait_for_event(xhci, TRB_TRANSFER, &trb);
xhci_print_trb(xhci, &trb, "Response Normal");
/* Regain ownership of data buffer from device */
- dma_sync_single_for_cpu((unsigned long)buffer, length,
- usb_pipein(pipe) ?
- DMA_FROM_DEVICE : DMA_TO_DEVICE);
-
+ dma_unmap_single(xhci->dev, buffer_dma, length,
+ dma_direction);
switch (ret) {
case -COMP_SHORT_TX:
udev->status = 0;
@@ -1070,8 +1196,12 @@ static int xhci_submit_control(struct usb_device *udev, unsigned long pipe,
struct usb_host *host = udev->host;
struct xhci_hcd *xhci = to_xhci_hcd(host);
struct xhci_virtual_device *vdev;
+ struct xhci_slot_ctx *out_slot;
+ dma_addr_t buffer_dma = 0;
union xhci_trb trb;
u16 typeReq = (req->requesttype << 8) | req->request;
+ u64 field[2];
+ u32 flags;
int ret;
dev_dbg(xhci->dev, "%s req %u (%#x), type %u (%#x), value %u (%#x), index %u (%#x), length %u (%#x)\n",
@@ -1091,10 +1221,12 @@ static int xhci_submit_control(struct usb_device *udev, unsigned long pipe,
if (!vdev)
return -ENODEV;
+ out_slot = xhci_get_slot_ctx(xhci, vdev->out_ctx);
+
dev_dbg(xhci->dev, "%s udev %p vdev %p slot %u state %u epi %u in_ctx %p out_ctx %p\n",
__func__, udev, vdev, vdev->slot_id,
- GET_SLOT_STATE(le32_to_cpu(vdev->out_ctx->slot.dev_state)), 0,
- vdev->in_ctx, vdev->out_ctx);
+ GET_SLOT_STATE(le32_to_cpu(out_slot->dev_state)), 0,
+ vdev->in_ctx->bytes, vdev->out_ctx->bytes);
if (req->request == USB_REQ_SET_ADDRESS)
return xhci_virtdev_set_address(vdev);
@@ -1104,53 +1236,59 @@ static int xhci_submit_control(struct usb_device *udev, unsigned long pipe,
return ret;
}
- /* Pass ownership of data buffer to device */
- dma_sync_single_for_device((unsigned long)buffer, length,
- (req->requesttype & USB_DIR_IN) ?
- DMA_FROM_DEVICE : DMA_TO_DEVICE);
-
+ if (length > 0) {
+ /* Pass ownership of data buffer to device */
+ buffer_dma = dma_map_single(xhci->dev, buffer, length,
+ (req->requesttype & USB_DIR_IN) ?
+ DMA_FROM_DEVICE : DMA_TO_DEVICE);
+ if (dma_mapping_error(xhci->dev, buffer_dma))
+ return -EFAULT;
+ }
/* Setup TRB */
- memset(&trb, 0, sizeof(union xhci_trb));
- trb.generic.field[0] = le16_to_cpu(req->value) << 16 |
+ field[0] = le16_to_cpu(req->value) << 16 |
req->request << 8 | req->requesttype;
- trb.generic.field[1] = le16_to_cpu(req->length) << 16 |
+ field[1] = le16_to_cpu(req->length) << 16 |
le16_to_cpu(req->index);
- trb.event_cmd.status = TRB_LEN(8) | TRB_INTR_TARGET(0);
- trb.event_cmd.flags = TRB_TYPE(TRB_SETUP) | TRB_IDT;
- if (xhci->hci_version == 0x100 && length > 0) {
+ flags = TRB_TYPE(TRB_SETUP) | TRB_IDT;
+ if (xhci->hci_version >= 0x100 && length > 0) {
if (req->requesttype & USB_DIR_IN)
- trb.event_cmd.flags |= TRB_TX_TYPE(TRB_DATA_IN);
+ flags |= TRB_TX_TYPE(TRB_DATA_IN);
else
- trb.event_cmd.flags |= TRB_TX_TYPE(TRB_DATA_OUT);
+ flags |= TRB_TX_TYPE(TRB_DATA_OUT);
}
+ xhci_init_event_cmd_trb(&trb,
+ field[1] << 32 | field[0],
+ TRB_LEN(8) | TRB_INTR_TARGET(0),
+ flags);
xhci_print_trb(xhci, &trb, "Request Setup ");
xhci_virtdev_issue_transfer(vdev, 0, &trb, false);
/* Data TRB */
if (length > 0) {
- memset(&trb, 0, sizeof(union xhci_trb));
- trb.event_cmd.cmd_trb = cpu_to_le64((dma_addr_t)buffer);
/* FIXME: TD remainder */
- trb.event_cmd.status = TRB_LEN(length) | TRB_INTR_TARGET(0);
- trb.event_cmd.flags = TRB_TYPE(TRB_DATA) | TRB_IOC;
+ flags = TRB_TYPE(TRB_DATA) | TRB_IOC;
if (req->requesttype & USB_DIR_IN)
- trb.event_cmd.flags |= TRB_ISP | TRB_DIR_IN;
+ flags |= TRB_ISP | TRB_DIR_IN;
+ xhci_init_event_cmd_trb(&trb,
+ buffer_dma,
+ TRB_LEN(length) | TRB_INTR_TARGET(0),
+ flags);
xhci_print_trb(xhci, &trb, "Request Data ");
xhci_virtdev_issue_transfer(vdev, 0, &trb, false);
}
/* Status TRB */
- memset(&trb, 0, sizeof(union xhci_trb));
- trb.event_cmd.status = TRB_INTR_TARGET(0);
- if (length > 0 && req->requesttype & USB_DIR_IN)
- trb.event_cmd.flags = 0;
- else
- trb.event_cmd.flags = TRB_DIR_IN;
- trb.event_cmd.flags |= TRB_TYPE(TRB_STATUS) | TRB_IOC;
+ flags = TRB_TYPE(TRB_STATUS) | TRB_IOC;
+ if (!(length > 0 && req->requesttype & USB_DIR_IN))
+ flags |= TRB_DIR_IN;
+ xhci_init_event_cmd_trb(&trb,
+ 0,
+ TRB_INTR_TARGET(0),
+ flags);
xhci_print_trb(xhci, &trb, "Request Status");
xhci_virtdev_issue_transfer(vdev, 0, &trb, true);
- if (length > 0 && req->requesttype & USB_DIR_IN) {
+ if (length > 0) {
ret = xhci_wait_for_event(xhci, TRB_TRANSFER, &trb);
xhci_print_trb(xhci, &trb, "Response Data ");
if (ret == -COMP_SHORT_TX)
@@ -1163,10 +1301,13 @@ static int xhci_submit_control(struct usb_device *udev, unsigned long pipe,
xhci_print_trb(xhci, &trb, "Response Status");
dma_regain:
- /* Regain ownership of data buffer from device */
- dma_sync_single_for_cpu((unsigned long)buffer, length,
- (req->requesttype & USB_DIR_IN) ?
- DMA_FROM_DEVICE : DMA_TO_DEVICE);
+ if (length > 0) {
+ /* Regain ownership of data buffer from device */
+ dma_unmap_single(xhci->dev, buffer_dma, length,
+ (req->requesttype & USB_DIR_IN) ?
+ DMA_FROM_DEVICE : DMA_TO_DEVICE);
+ }
+
if (ret < 0)
return ret;
@@ -1224,8 +1365,7 @@ static void xhci_dma_alloc(struct xhci_hcd *xhci)
num_ep = max(MAX_EP_RINGS, MIN_EP_RINGS + num_ep);
xhci->dma_size += num_ep * sz_ep;
- p = xhci->dma = dma_alloc_coherent(xhci->dma_size, DMA_ADDRESS_BROKEN);
- memset(xhci->dma, 0, xhci->dma_size);
+ p = dma_alloc_coherent(xhci->dma_size, DMA_ADDRESS_BROKEN);
xhci->sp = p; p += sz_sp;
xhci->dcbaa = p; p += sz_dca;
@@ -1261,7 +1401,7 @@ static void xhci_dma_alloc(struct xhci_hcd *xhci)
static int xhci_halt(struct xhci_hcd *xhci)
{
u32 reg = readl(&xhci->op_regs->status);
- u32 mask = ~XHCI_IRQS;
+ u32 mask = (u32)~XHCI_IRQS;
if (!(reg & STS_HALT))
mask &= ~CMD_RUN;
@@ -1291,7 +1431,7 @@ static int xhci_reset(struct xhci_hcd *xhci)
return ret;
}
- return 0;
+ return 0;
}
static int xhci_start(struct xhci_hcd *xhci)
@@ -1305,8 +1445,8 @@ static int xhci_start(struct xhci_hcd *xhci)
ret = xhci_handshake(&xhci->op_regs->status,
STS_HALT, 0, XHCI_MAX_HALT_USEC);
- if (ret) {
- dev_err(xhci->dev, "failed to start\n");
+ if (ret) {
+ dev_err(xhci->dev, "failed to start\n");
return ret;
}
@@ -1314,7 +1454,7 @@ static int xhci_start(struct xhci_hcd *xhci)
for (i = 0; i < xhci->num_usb_ports; i++)
xhci_hub_port_power(xhci, i, false);
- return 0;
+ return 0;
}
static int xhci_init(struct usb_host *host)
@@ -1472,6 +1612,7 @@ int xhci_register(struct device_d *dev, struct xhci_data *data)
xhci = xzalloc(sizeof(*xhci));
host = &xhci->host;
INIT_LIST_HEAD(&xhci->vdev_list);
+ INIT_LIST_HEAD(&xhci->rings_list);
xhci->dev = dev;
xhci->cap_regs = data->regs;
xhci->op_regs = (void __iomem *)xhci->cap_regs +
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index a140b1dd07..7a9315a0b6 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -37,9 +37,4 @@ static struct pci_driver xhci_pci_driver = {
.id_table = xhci_pci_tbl,
.probe = xhci_pci_probe,
};
-
-static int xhci_pci_init(void)
-{
- return pci_register_driver(&xhci_pci_driver);
-}
-device_initcall(xhci_pci_init);
+device_pci_driver(xhci_pci_driver);
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 078f881183..84a14dd1fc 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -24,6 +24,8 @@
#ifndef __XHCI_H
#define __XHCI_H
+#include <io-64-nonatomic-lo-hi.h>
+
#define NUM_COMMAND_TRBS 8
#define NUM_TRANSFER_TRBS 8
#define NUM_EVENT_SEGM 1 /* only one supported */
@@ -504,6 +506,27 @@ struct xhci_doorbell_array {
#define DB_VALUE_HOST 0x00000000
/**
+ * struct xhci_container_ctx
+ * @type: Type of context. Used to calculated offsets to contained contexts.
+ * @size: Size of the context data
+ * @bytes: The raw context data given to HW
+ * @dma: dma address of the bytes
+ *
+ * Represents either a Device or Input context. Holds a pointer to the raw
+ * memory used for the context (bytes) and dma address of it (dma).
+ */
+struct xhci_container_ctx {
+ unsigned type;
+#define XHCI_CTX_TYPE_DEVICE 0x1
+#define XHCI_CTX_TYPE_INPUT 0x2
+
+ int size;
+
+ u8 *bytes;
+ dma_addr_t dma;
+};
+
+/**
* struct xhci_slot_ctx
* @dev_info: Route string, device speed, hub info, and last valid endpoint
* @dev_info2: Max exit latency for device number, root hub port number
@@ -1144,19 +1167,11 @@ struct xhci_erst_entry {
*/
static inline u64 xhci_read_64(__le64 __iomem *regs)
{
- __u32 __iomem *ptr = (__u32 __iomem *)regs;
- u64 val_lo = readl(ptr);
- u64 val_hi = readl(ptr + 1);
- return val_lo + (val_hi << 32);
+ return lo_hi_readq(regs);
}
static inline void xhci_write_64(const u64 val, __le64 __iomem *regs)
{
- __u32 __iomem *ptr = (__u32 __iomem *)regs;
- u32 val_lo = lower_32_bits(val);
- u32 val_hi = upper_32_bits(val);
-
- writel(val_lo, ptr);
- writel(val_hi, ptr + 1);
+ lo_hi_writeq(val, regs);
}
/*
@@ -1183,17 +1198,6 @@ struct xhci_ring {
int cycle_state;
};
-struct xhci_device_context {
- struct xhci_slot_ctx slot;
- struct xhci_ep_ctx ep[31];
-};
-
-struct xhci_input_context {
- struct xhci_input_control_ctx icc;
- struct xhci_slot_ctx slot;
- struct xhci_ep_ctx ep[31];
-};
-
struct xhci_virtual_device {
struct list_head list;
struct usb_device *udev;
@@ -1201,8 +1205,8 @@ struct xhci_virtual_device {
size_t dma_size;
int slot_id;
struct xhci_ring *ep[USB_MAXENDPOINTS];
- struct xhci_input_context *in_ctx;
- struct xhci_device_context *out_ctx;
+ struct xhci_container_ctx *in_ctx;
+ struct xhci_container_ctx *out_ctx;
};
struct usb_root_hub_info {
@@ -1232,7 +1236,6 @@ struct xhci_hcd {
int num_sp;
int page_size;
int page_shift;
- void *dma;
size_t dma_size;
__le64 *dcbaa;
void *sp;
diff --git a/drivers/usb/imx/chipidea-imx.c b/drivers/usb/imx/chipidea-imx.c
index 8792217706..6c60c383f0 100644
--- a/drivers/usb/imx/chipidea-imx.c
+++ b/drivers/usb/imx/chipidea-imx.c
@@ -201,14 +201,14 @@ static int ci_register_role(struct imx_chipidea *ci)
return ret;
ehci = ehci_register(ci->dev, &ci->data);
- if (IS_ERR(ehci))
+ if (IS_ERR(ehci)) {
+ regulator_disable(ci->vbus);
return PTR_ERR(ehci);
+ }
ci->ehci = ehci;
ci->dev->detect = ci_ehci_detect;
-
- regulator_disable(ci->vbus);
} else {
dev_err(ci->dev, "Host support not available\n");
return -ENODEV;
diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig
new file mode 100644
index 0000000000..7d6c9da594
--- /dev/null
+++ b/drivers/usb/misc/Kconfig
@@ -0,0 +1,14 @@
+#
+# USB Miscellaneous driver configuration
+#
+comment "USB Miscellaneous drivers"
+
+config USB_HUB_USB251XB
+ bool "USB251XB Hub Controller Configuration Driver"
+ depends on I2C
+ select NLS
+ help
+ This option enables support for configuration via SMBus of the
+ Microchip USB251x/xBi USB 2.0 Hub Controller series. Configuration
+ parameters may be set in devicetree or platform data.
+ Say Y or M here if you need to configure such a device via SMBus.
diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile
new file mode 100644
index 0000000000..fb69c454bd
--- /dev/null
+++ b/drivers/usb/misc/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the rest of the USB drivers
+# (the ones that don't fit into any other categories)
+#
+obj-$(CONFIG_USB_HUB_USB251XB) += usb251xb.o
diff --git a/drivers/usb/misc/usb251xb.c b/drivers/usb/misc/usb251xb.c
new file mode 100644
index 0000000000..97f55efa82
--- /dev/null
+++ b/drivers/usb/misc/usb251xb.c
@@ -0,0 +1,683 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Driver for Microchip USB251xB USB 2.0 Hi-Speed Hub Controller
+ * Configuration via SMBus.
+ *
+ * Copyright (c) 2017 SKIDATA AG
+ *
+ * This work is based on the USB3503 driver by Dongjin Kim and
+ * a not-accepted patch by Fabien Lahoudere, see:
+ * https://patchwork.kernel.org/patch/9257715/
+ */
+
+#include <common.h>
+#include <init.h>
+#include <driver.h>
+#include <i2c/i2c.h>
+#include <of.h>
+#include <gpio.h>
+#include <of_gpio.h>
+#include <of_device.h>
+
+#include <linux/nls.h>
+
+/* Internal Register Set Addresses & Default Values acc. to DS00001692C */
+#define USB251XB_ADDR_VENDOR_ID_LSB 0x00
+#define USB251XB_ADDR_VENDOR_ID_MSB 0x01
+#define USB251XB_DEF_VENDOR_ID 0x0424
+
+#define USB251XB_ADDR_PRODUCT_ID_LSB 0x02
+#define USB251XB_ADDR_PRODUCT_ID_MSB 0x03
+#define USB251XB_DEF_PRODUCT_ID_12 0x2512 /* USB2512B/12Bi */
+#define USB251XB_DEF_PRODUCT_ID_13 0x2513 /* USB2513B/13Bi */
+#define USB251XB_DEF_PRODUCT_ID_14 0x2514 /* USB2514B/14Bi */
+#define USB251XB_DEF_PRODUCT_ID_17 0x2517 /* USB2517/17i */
+
+#define USB251XB_ADDR_DEVICE_ID_LSB 0x04
+#define USB251XB_ADDR_DEVICE_ID_MSB 0x05
+#define USB251XB_DEF_DEVICE_ID 0x0BB3
+
+#define USB251XB_ADDR_CONFIG_DATA_1 0x06
+#define USB251XB_DEF_CONFIG_DATA_1 0x9B
+#define USB251XB_ADDR_CONFIG_DATA_2 0x07
+#define USB251XB_DEF_CONFIG_DATA_2 0x20
+#define USB251XB_ADDR_CONFIG_DATA_3 0x08
+#define USB251XB_DEF_CONFIG_DATA_3 0x02
+
+#define USB251XB_ADDR_NON_REMOVABLE_DEVICES 0x09
+#define USB251XB_DEF_NON_REMOVABLE_DEVICES 0x00
+
+#define USB251XB_ADDR_PORT_DISABLE_SELF 0x0A
+#define USB251XB_DEF_PORT_DISABLE_SELF 0x00
+#define USB251XB_ADDR_PORT_DISABLE_BUS 0x0B
+#define USB251XB_DEF_PORT_DISABLE_BUS 0x00
+
+#define USB251XB_ADDR_MAX_POWER_SELF 0x0C
+#define USB251XB_DEF_MAX_POWER_SELF 0x01
+#define USB251XB_ADDR_MAX_POWER_BUS 0x0D
+#define USB251XB_DEF_MAX_POWER_BUS 0x32
+
+#define USB251XB_ADDR_MAX_CURRENT_SELF 0x0E
+#define USB251XB_DEF_MAX_CURRENT_SELF 0x01
+#define USB251XB_ADDR_MAX_CURRENT_BUS 0x0F
+#define USB251XB_DEF_MAX_CURRENT_BUS 0x32
+
+#define USB251XB_ADDR_POWER_ON_TIME 0x10
+#define USB251XB_DEF_POWER_ON_TIME 0x32
+
+#define USB251XB_ADDR_LANGUAGE_ID_HIGH 0x11
+#define USB251XB_ADDR_LANGUAGE_ID_LOW 0x12
+#define USB251XB_DEF_LANGUAGE_ID 0x0000
+
+#define USB251XB_STRING_BUFSIZE 62
+#define USB251XB_ADDR_MANUFACTURER_STRING_LEN 0x13
+#define USB251XB_ADDR_MANUFACTURER_STRING 0x16
+#define USB251XB_DEF_MANUFACTURER_STRING "Microchip"
+
+#define USB251XB_ADDR_PRODUCT_STRING_LEN 0x14
+#define USB251XB_ADDR_PRODUCT_STRING 0x54
+#define USB251XB_DEF_PRODUCT_STRING "USB251xB/xBi/7i"
+
+#define USB251XB_ADDR_SERIAL_STRING_LEN 0x15
+#define USB251XB_ADDR_SERIAL_STRING 0x92
+#define USB251XB_DEF_SERIAL_STRING ""
+
+#define USB251XB_ADDR_BATTERY_CHARGING_ENABLE 0xD0
+#define USB251XB_DEF_BATTERY_CHARGING_ENABLE 0x00
+
+#define USB251XB_ADDR_BOOST_UP 0xF6
+#define USB251XB_DEF_BOOST_UP 0x00
+#define USB251XB_ADDR_BOOST_57 0xF7
+#define USB251XB_DEF_BOOST_57 0x00
+#define USB251XB_ADDR_BOOST_14 0xF8
+#define USB251XB_DEF_BOOST_14 0x00
+
+#define USB251XB_ADDR_PORT_SWAP 0xFA
+#define USB251XB_DEF_PORT_SWAP 0x00
+
+#define USB251XB_ADDR_PORT_MAP_12 0xFB
+#define USB251XB_DEF_PORT_MAP_12 0x00
+#define USB251XB_ADDR_PORT_MAP_34 0xFC
+#define USB251XB_DEF_PORT_MAP_34 0x00 /* USB251{3B/i,4B/i,7/i} only */
+#define USB251XB_ADDR_PORT_MAP_56 0xFD
+#define USB251XB_DEF_PORT_MAP_56 0x00 /* USB2517/i only */
+#define USB251XB_ADDR_PORT_MAP_7 0xFE
+#define USB251XB_DEF_PORT_MAP_7 0x00 /* USB2517/i only */
+
+#define USB251XB_ADDR_STATUS_COMMAND 0xFF
+#define USB251XB_STATUS_COMMAND_SMBUS_DOWN 0x04
+#define USB251XB_STATUS_COMMAND_RESET 0x02
+#define USB251XB_STATUS_COMMAND_ATTACH 0x01
+
+#define USB251XB_I2C_REG_SZ 0x100
+#define USB251XB_I2C_WRITE_SZ 0x10
+
+#define DRIVER_NAME "usb251xb"
+#define DRIVER_DESC "Microchip USB 2.0 Hi-Speed Hub Controller"
+
+struct usb251xb {
+ struct device_d *dev;
+ struct i2c_client *i2c;
+ u8 skip_config;
+ int gpio_reset;
+ u16 vendor_id;
+ u16 product_id;
+ u16 device_id;
+ u8 conf_data1;
+ u8 conf_data2;
+ u8 conf_data3;
+ u8 non_rem_dev;
+ u8 port_disable_sp;
+ u8 port_disable_bp;
+ u8 max_power_sp;
+ u8 max_power_bp;
+ u8 max_current_sp;
+ u8 max_current_bp;
+ u8 power_on_time;
+ u16 lang_id;
+ u8 manufacturer_len;
+ u8 product_len;
+ u8 serial_len;
+ char manufacturer[USB251XB_STRING_BUFSIZE];
+ char product[USB251XB_STRING_BUFSIZE];
+ char serial[USB251XB_STRING_BUFSIZE];
+ u8 bat_charge_en;
+ u8 boost_up;
+ u8 boost_57;
+ u8 boost_14;
+ u8 port_swap;
+ u8 port_map12;
+ u8 port_map34;
+ u8 port_map56;
+ u8 port_map7;
+ u8 status;
+};
+
+struct usb251xb_data {
+ u16 product_id;
+ u8 port_cnt;
+ bool led_support;
+ bool bat_support;
+ char product_str[USB251XB_STRING_BUFSIZE / 2]; /* ASCII string */
+};
+
+static const struct usb251xb_data usb2512b_data = {
+ .product_id = 0x2512,
+ .port_cnt = 2,
+ .led_support = false,
+ .bat_support = true,
+ .product_str = "USB2512B",
+};
+
+static const struct usb251xb_data usb2512bi_data = {
+ .product_id = 0x2512,
+ .port_cnt = 2,
+ .led_support = false,
+ .bat_support = true,
+ .product_str = "USB2512Bi",
+};
+
+static const struct usb251xb_data usb2513b_data = {
+ .product_id = 0x2513,
+ .port_cnt = 3,
+ .led_support = false,
+ .bat_support = true,
+ .product_str = "USB2513B",
+};
+
+static const struct usb251xb_data usb2513bi_data = {
+ .product_id = 0x2513,
+ .port_cnt = 3,
+ .led_support = false,
+ .bat_support = true,
+ .product_str = "USB2513Bi",
+};
+
+static const struct usb251xb_data usb2514b_data = {
+ .product_id = 0x2514,
+ .port_cnt = 4,
+ .led_support = false,
+ .bat_support = true,
+ .product_str = "USB2514B",
+};
+
+static const struct usb251xb_data usb2514bi_data = {
+ .product_id = 0x2514,
+ .port_cnt = 4,
+ .led_support = false,
+ .bat_support = true,
+ .product_str = "USB2514Bi",
+};
+
+static const struct usb251xb_data usb2517_data = {
+ .product_id = 0x2517,
+ .port_cnt = 7,
+ .led_support = true,
+ .bat_support = false,
+ .product_str = "USB2517",
+};
+
+static const struct usb251xb_data usb2517i_data = {
+ .product_id = 0x2517,
+ .port_cnt = 7,
+ .led_support = true,
+ .bat_support = false,
+ .product_str = "USB2517i",
+};
+
+static void usb251xb_reset(struct usb251xb *hub, int state)
+{
+ if (!gpio_is_valid(hub->gpio_reset))
+ return;
+
+ gpio_set_active(hub->gpio_reset, state);
+
+ /* wait for hub recovery/stabilization */
+ if (!state)
+ udelay(750); /* >=500us at power on */
+ else
+ udelay(10); /* >=1us at power down */
+}
+
+static int usb251xb_connect(struct usb251xb *hub)
+{
+ struct device_d *dev = hub->dev;
+ int err, i;
+ char i2c_wb[USB251XB_I2C_REG_SZ];
+
+ memset(i2c_wb, 0, USB251XB_I2C_REG_SZ);
+
+ if (hub->skip_config) {
+ dev_info(dev, "Skip hub configuration, only attach.\n");
+ i2c_wb[0] = 0x01;
+ i2c_wb[1] = USB251XB_STATUS_COMMAND_ATTACH;
+
+ usb251xb_reset(hub, 0);
+
+ err = i2c_smbus_write_i2c_block_data(hub->i2c,
+ USB251XB_ADDR_STATUS_COMMAND, 2, i2c_wb);
+ if (err) {
+ dev_err(dev, "attaching hub failed: %d\n", err);
+ return err;
+ }
+ return 0;
+ }
+
+ i2c_wb[USB251XB_ADDR_VENDOR_ID_MSB] = (hub->vendor_id >> 8) & 0xFF;
+ i2c_wb[USB251XB_ADDR_VENDOR_ID_LSB] = hub->vendor_id & 0xFF;
+ i2c_wb[USB251XB_ADDR_PRODUCT_ID_MSB] = (hub->product_id >> 8) & 0xFF;
+ i2c_wb[USB251XB_ADDR_PRODUCT_ID_LSB] = hub->product_id & 0xFF;
+ i2c_wb[USB251XB_ADDR_DEVICE_ID_MSB] = (hub->device_id >> 8) & 0xFF;
+ i2c_wb[USB251XB_ADDR_DEVICE_ID_LSB] = hub->device_id & 0xFF;
+ i2c_wb[USB251XB_ADDR_CONFIG_DATA_1] = hub->conf_data1;
+ i2c_wb[USB251XB_ADDR_CONFIG_DATA_2] = hub->conf_data2;
+ i2c_wb[USB251XB_ADDR_CONFIG_DATA_3] = hub->conf_data3;
+ i2c_wb[USB251XB_ADDR_NON_REMOVABLE_DEVICES] = hub->non_rem_dev;
+ i2c_wb[USB251XB_ADDR_PORT_DISABLE_SELF] = hub->port_disable_sp;
+ i2c_wb[USB251XB_ADDR_PORT_DISABLE_BUS] = hub->port_disable_bp;
+ i2c_wb[USB251XB_ADDR_MAX_POWER_SELF] = hub->max_power_sp;
+ i2c_wb[USB251XB_ADDR_MAX_POWER_BUS] = hub->max_power_bp;
+ i2c_wb[USB251XB_ADDR_MAX_CURRENT_SELF] = hub->max_current_sp;
+ i2c_wb[USB251XB_ADDR_MAX_CURRENT_BUS] = hub->max_current_bp;
+ i2c_wb[USB251XB_ADDR_POWER_ON_TIME] = hub->power_on_time;
+ i2c_wb[USB251XB_ADDR_LANGUAGE_ID_HIGH] = (hub->lang_id >> 8) & 0xFF;
+ i2c_wb[USB251XB_ADDR_LANGUAGE_ID_LOW] = hub->lang_id & 0xFF;
+ i2c_wb[USB251XB_ADDR_MANUFACTURER_STRING_LEN] = hub->manufacturer_len;
+ i2c_wb[USB251XB_ADDR_PRODUCT_STRING_LEN] = hub->product_len;
+ i2c_wb[USB251XB_ADDR_SERIAL_STRING_LEN] = hub->serial_len;
+ memcpy(&i2c_wb[USB251XB_ADDR_MANUFACTURER_STRING], hub->manufacturer,
+ USB251XB_STRING_BUFSIZE);
+ memcpy(&i2c_wb[USB251XB_ADDR_SERIAL_STRING], hub->serial,
+ USB251XB_STRING_BUFSIZE);
+ memcpy(&i2c_wb[USB251XB_ADDR_PRODUCT_STRING], hub->product,
+ USB251XB_STRING_BUFSIZE);
+ i2c_wb[USB251XB_ADDR_BATTERY_CHARGING_ENABLE] = hub->bat_charge_en;
+ i2c_wb[USB251XB_ADDR_BOOST_UP] = hub->boost_up;
+ i2c_wb[USB251XB_ADDR_BOOST_57] = hub->boost_57;
+ i2c_wb[USB251XB_ADDR_BOOST_14] = hub->boost_14;
+ i2c_wb[USB251XB_ADDR_PORT_SWAP] = hub->port_swap;
+ i2c_wb[USB251XB_ADDR_PORT_MAP_12] = hub->port_map12;
+ i2c_wb[USB251XB_ADDR_PORT_MAP_34] = hub->port_map34;
+ i2c_wb[USB251XB_ADDR_PORT_MAP_56] = hub->port_map56;
+ i2c_wb[USB251XB_ADDR_PORT_MAP_7] = hub->port_map7;
+ i2c_wb[USB251XB_ADDR_STATUS_COMMAND] = USB251XB_STATUS_COMMAND_ATTACH;
+
+ usb251xb_reset(hub, 0);
+
+ /* write registers */
+ for (i = 0; i < (USB251XB_I2C_REG_SZ / USB251XB_I2C_WRITE_SZ); i++) {
+ int offset = i * USB251XB_I2C_WRITE_SZ;
+ char wbuf[USB251XB_I2C_WRITE_SZ + 1];
+
+ /* The first data byte transferred tells the hub how many data
+ * bytes will follow (byte count).
+ */
+ wbuf[0] = USB251XB_I2C_WRITE_SZ;
+ memcpy(&wbuf[1], &i2c_wb[offset], USB251XB_I2C_WRITE_SZ);
+
+ dev_dbg(dev, "writing %d byte block %d to 0x%02X\n",
+ USB251XB_I2C_WRITE_SZ, i, offset);
+
+ err = i2c_smbus_write_i2c_block_data(hub->i2c, offset,
+ USB251XB_I2C_WRITE_SZ + 1,
+ wbuf);
+ if (err)
+ goto out_err;
+ }
+
+ dev_info(dev, "Hub configuration was successful.\n");
+ return 0;
+
+out_err:
+ dev_err(dev, "configuring block %d failed: %d\n", i, err);
+ return err;
+}
+
+#ifdef CONFIG_OFDEVICE
+static int usb251xb_get_ofdata(struct usb251xb *hub,
+ struct usb251xb_data *data)
+{
+ struct device_d *dev = hub->dev;
+ struct device_node *np = dev->device_node;
+ int len, err, i;
+ u32 port, property_u32 = 0;
+ const u32 *cproperty_u32;
+ const char *cproperty_char;
+ enum of_gpio_flags of_flags;
+ unsigned long flags = GPIOF_OUT_INIT_ACTIVE;
+ char str[USB251XB_STRING_BUFSIZE / 2];
+ struct property *prop;
+ const __be32 *p;
+
+ if (!np) {
+ dev_err(dev, "failed to get ofdata\n");
+ return -ENODEV;
+ }
+
+ if (of_get_property(np, "skip-config", NULL))
+ hub->skip_config = 1;
+ else
+ hub->skip_config = 0;
+
+ hub->gpio_reset = of_get_named_gpio_flags(np, "reset-gpios", 0,
+ &of_flags);
+ if (gpio_is_valid(hub->gpio_reset)) {
+ char *name;
+ int ret;
+
+ if (of_flags & OF_GPIO_ACTIVE_LOW)
+ flags |= GPIOF_ACTIVE_LOW;
+
+ name = basprintf("%s reset", dev_name(dev));
+ ret = gpio_request_one(hub->gpio_reset, flags, name);
+ if (ret < 0)
+ return ret;
+ } else if (hub->gpio_reset == -EPROBE_DEFER) {
+ return -EPROBE_DEFER;
+ } else {
+ err = hub->gpio_reset;
+ dev_err(dev, "unable to request GPIO reset pin (%d)\n", err);
+ return err;
+ }
+
+ if (of_property_read_u16_array(np, "vendor-id", &hub->vendor_id, 1))
+ hub->vendor_id = USB251XB_DEF_VENDOR_ID;
+
+ if (of_property_read_u16_array(np, "product-id",
+ &hub->product_id, 1))
+ hub->product_id = data->product_id;
+
+ if (of_property_read_u16_array(np, "device-id", &hub->device_id, 1))
+ hub->device_id = USB251XB_DEF_DEVICE_ID;
+
+ hub->conf_data1 = USB251XB_DEF_CONFIG_DATA_1;
+ if (of_get_property(np, "self-powered", NULL)) {
+ hub->conf_data1 |= BIT(7);
+
+ /* Configure Over-Current sens when self-powered */
+ hub->conf_data1 &= ~BIT(2);
+ if (of_get_property(np, "ganged-sensing", NULL))
+ hub->conf_data1 &= ~BIT(1);
+ else if (of_get_property(np, "individual-sensing", NULL))
+ hub->conf_data1 |= BIT(1);
+ } else if (of_get_property(np, "bus-powered", NULL)) {
+ hub->conf_data1 &= ~BIT(7);
+
+ /* Disable Over-Current sense when bus-powered */
+ hub->conf_data1 |= BIT(2);
+ }
+
+ if (of_get_property(np, "disable-hi-speed", NULL))
+ hub->conf_data1 |= BIT(5);
+
+ if (of_get_property(np, "multi-tt", NULL))
+ hub->conf_data1 |= BIT(4);
+ else if (of_get_property(np, "single-tt", NULL))
+ hub->conf_data1 &= ~BIT(4);
+
+ if (of_get_property(np, "disable-eop", NULL))
+ hub->conf_data1 |= BIT(3);
+
+ if (of_get_property(np, "individual-port-switching", NULL))
+ hub->conf_data1 |= BIT(0);
+ else if (of_get_property(np, "ganged-port-switching", NULL))
+ hub->conf_data1 &= ~BIT(0);
+
+ hub->conf_data2 = USB251XB_DEF_CONFIG_DATA_2;
+ if (of_get_property(np, "dynamic-power-switching", NULL))
+ hub->conf_data2 |= BIT(7);
+
+ if (!of_property_read_u32(np, "oc-delay-us", &property_u32)) {
+ if (property_u32 == 100) {
+ /* 100 us*/
+ hub->conf_data2 &= ~BIT(5);
+ hub->conf_data2 &= ~BIT(4);
+ } else if (property_u32 == 4000) {
+ /* 4 ms */
+ hub->conf_data2 &= ~BIT(5);
+ hub->conf_data2 |= BIT(4);
+ } else if (property_u32 == 16000) {
+ /* 16 ms */
+ hub->conf_data2 |= BIT(5);
+ hub->conf_data2 |= BIT(4);
+ } else {
+ /* 8 ms (DEFAULT) */
+ hub->conf_data2 |= BIT(5);
+ hub->conf_data2 &= ~BIT(4);
+ }
+ }
+
+ if (of_get_property(np, "compound-device", NULL))
+ hub->conf_data2 |= BIT(3);
+
+ hub->conf_data3 = USB251XB_DEF_CONFIG_DATA_3;
+ if (of_get_property(np, "port-mapping-mode", NULL))
+ hub->conf_data3 |= BIT(3);
+
+ if (data->led_support && of_get_property(np, "led-usb-mode", NULL))
+ hub->conf_data3 &= ~BIT(1);
+
+ if (of_get_property(np, "string-support", NULL))
+ hub->conf_data3 |= BIT(0);
+
+ hub->non_rem_dev = USB251XB_DEF_NON_REMOVABLE_DEVICES;
+ cproperty_u32 = of_get_property(np, "non-removable-ports", &len);
+ if (cproperty_u32 && (len / sizeof(u32)) > 0) {
+ for (i = 0; i < len / sizeof(u32); i++) {
+ u32 port = be32_to_cpu(cproperty_u32[i]);
+
+ if ((port >= 1) && (port <= data->port_cnt))
+ hub->non_rem_dev |= BIT(port);
+ else
+ dev_warn(dev, "NRD port %u doesn't exist\n",
+ port);
+ }
+ }
+
+ hub->port_disable_sp = USB251XB_DEF_PORT_DISABLE_SELF;
+ cproperty_u32 = of_get_property(np, "sp-disabled-ports", &len);
+ if (cproperty_u32 && (len / sizeof(u32)) > 0) {
+ for (i = 0; i < len / sizeof(u32); i++) {
+ u32 port = be32_to_cpu(cproperty_u32[i]);
+
+ if ((port >= 1) && (port <= data->port_cnt))
+ hub->port_disable_sp |= BIT(port);
+ else
+ dev_warn(dev, "PDS port %u doesn't exist\n",
+ port);
+ }
+ }
+
+ hub->port_disable_bp = USB251XB_DEF_PORT_DISABLE_BUS;
+ cproperty_u32 = of_get_property(np, "bp-disabled-ports", &len);
+ if (cproperty_u32 && (len / sizeof(u32)) > 0) {
+ for (i = 0; i < len / sizeof(u32); i++) {
+ u32 port = be32_to_cpu(cproperty_u32[i]);
+
+ if ((port >= 1) && (port <= data->port_cnt))
+ hub->port_disable_bp |= BIT(port);
+ else
+ dev_warn(dev, "PDB port %u doesn't exist\n",
+ port);
+ }
+ }
+
+ hub->max_power_sp = USB251XB_DEF_MAX_POWER_SELF;
+ if (!of_property_read_u32(np, "sp-max-total-current-microamp",
+ &property_u32))
+ hub->max_power_sp = min_t(u8, property_u32 / 2000, 50);
+
+ hub->max_power_bp = USB251XB_DEF_MAX_POWER_BUS;
+ if (!of_property_read_u32(np, "bp-max-total-current-microamp",
+ &property_u32))
+ hub->max_power_bp = min_t(u8, property_u32 / 2000, 255);
+
+ hub->max_current_sp = USB251XB_DEF_MAX_CURRENT_SELF;
+ if (!of_property_read_u32(np, "sp-max-removable-current-microamp",
+ &property_u32))
+ hub->max_current_sp = min_t(u8, property_u32 / 2000, 50);
+
+ hub->max_current_bp = USB251XB_DEF_MAX_CURRENT_BUS;
+ if (!of_property_read_u32(np, "bp-max-removable-current-microamp",
+ &property_u32))
+ hub->max_current_bp = min_t(u8, property_u32 / 2000, 255);
+
+ hub->power_on_time = USB251XB_DEF_POWER_ON_TIME;
+ if (!of_property_read_u32(np, "power-on-time-ms", &property_u32))
+ hub->power_on_time = min_t(u8, property_u32 / 2, 255);
+
+ if (of_property_read_u16_array(np, "language-id", &hub->lang_id, 1))
+ hub->lang_id = USB251XB_DEF_LANGUAGE_ID;
+
+ cproperty_char = of_get_property(np, "manufacturer", NULL);
+ strlcpy(str, cproperty_char ? : USB251XB_DEF_MANUFACTURER_STRING,
+ sizeof(str));
+ hub->manufacturer_len = strlen(str) & 0xFF;
+ memset(hub->manufacturer, 0, USB251XB_STRING_BUFSIZE);
+ len = min_t(size_t, USB251XB_STRING_BUFSIZE / 2, strlen(str));
+ len = utf8s_to_utf16s(str, len, UTF16_LITTLE_ENDIAN,
+ (wchar_t *)hub->manufacturer,
+ USB251XB_STRING_BUFSIZE);
+
+ cproperty_char = of_get_property(np, "product", NULL);
+ strlcpy(str, cproperty_char ? : data->product_str, sizeof(str));
+ hub->product_len = strlen(str) & 0xFF;
+ memset(hub->product, 0, USB251XB_STRING_BUFSIZE);
+ len = min_t(size_t, USB251XB_STRING_BUFSIZE / 2, strlen(str));
+ len = utf8s_to_utf16s(str, len, UTF16_LITTLE_ENDIAN,
+ (wchar_t *)hub->product,
+ USB251XB_STRING_BUFSIZE);
+
+ cproperty_char = of_get_property(np, "serial", NULL);
+ strlcpy(str, cproperty_char ? : USB251XB_DEF_SERIAL_STRING,
+ sizeof(str));
+ hub->serial_len = strlen(str) & 0xFF;
+ memset(hub->serial, 0, USB251XB_STRING_BUFSIZE);
+ len = min_t(size_t, USB251XB_STRING_BUFSIZE / 2, strlen(str));
+ len = utf8s_to_utf16s(str, len, UTF16_LITTLE_ENDIAN,
+ (wchar_t *)hub->serial,
+ USB251XB_STRING_BUFSIZE);
+
+ /*
+ * The datasheet documents the register as 'Port Swap' but the register
+ * controls the USB DP/DM signal swapping
+ */
+ hub->port_swap = USB251XB_DEF_PORT_SWAP;
+ of_property_for_each_u32(np, "swap-dx-lanes", prop, p, port) {
+ if ((port >= 0) && (port <= data->port_cnt))
+ hub->port_swap |= BIT(port);
+ }
+
+ /* The following parameters are currently not exposed to devicetree, but
+ * may be as soon as needed.
+ */
+ hub->bat_charge_en = USB251XB_DEF_BATTERY_CHARGING_ENABLE;
+ hub->boost_up = USB251XB_DEF_BOOST_UP;
+ hub->boost_57 = USB251XB_DEF_BOOST_57;
+ hub->boost_14 = USB251XB_DEF_BOOST_14;
+ hub->port_map12 = USB251XB_DEF_PORT_MAP_12;
+ hub->port_map34 = USB251XB_DEF_PORT_MAP_34;
+ hub->port_map56 = USB251XB_DEF_PORT_MAP_56;
+ hub->port_map7 = USB251XB_DEF_PORT_MAP_7;
+
+ return 0;
+}
+
+static const struct of_device_id usb251xb_of_match[] = {
+ {
+ .compatible = "microchip,usb2512b",
+ .data = &usb2512b_data,
+ }, {
+ .compatible = "microchip,usb2512bi",
+ .data = &usb2512bi_data,
+ }, {
+ .compatible = "microchip,usb2513b",
+ .data = &usb2513b_data,
+ }, {
+ .compatible = "microchip,usb2513bi",
+ .data = &usb2513bi_data,
+ }, {
+ .compatible = "microchip,usb2514b",
+ .data = &usb2514b_data,
+ }, {
+ .compatible = "microchip,usb2514bi",
+ .data = &usb2514bi_data,
+ }, {
+ .compatible = "microchip,usb2517",
+ .data = &usb2517_data,
+ }, {
+ .compatible = "microchip,usb2517i",
+ .data = &usb2517i_data,
+ }, {
+ /* sentinel */
+ }
+};
+#else /* CONFIG_OF */
+static int usb251xb_get_ofdata(struct usb251xb *hub,
+ struct usb251xb_data *data)
+{
+ return 0;
+}
+#endif /* CONFIG_OF */
+
+static int usb251xb_probe(struct usb251xb *hub)
+{
+ struct device_d *dev = hub->dev;
+ struct device_node *np = dev->device_node;
+ const struct of_device_id *of_id = of_match_device(usb251xb_of_match,
+ dev);
+ int err;
+
+ if (np) {
+ err = usb251xb_get_ofdata(hub,
+ (struct usb251xb_data *)of_id->data);
+ if (err) {
+ dev_err(dev, "failed to get ofdata: %d\n", err);
+ return err;
+ }
+ }
+
+ err = usb251xb_connect(hub);
+ if (err) {
+ dev_err(dev, "Failed to connect hub (%d)\n", err);
+ return err;
+ }
+
+ dev_info(dev, "Hub probed successfully\n");
+
+ return 0;
+}
+
+static int usb251xb_i2c_probe(struct device_d *dev)
+{
+ struct i2c_client *i2c = to_i2c_client(dev);
+ struct usb251xb *hub;
+
+ hub = xzalloc(sizeof(struct usb251xb));
+
+ i2c_set_clientdata(i2c, hub);
+ hub->dev = &i2c->dev;
+ hub->i2c = i2c;
+
+ return usb251xb_probe(hub);
+}
+
+static const struct platform_device_id usb251xb_id[] = {
+ { "usb2512b", 0 },
+ { "usb2512bi", 0 },
+ { "usb2513b", 0 },
+ { "usb2513bi", 0 },
+ { "usb2514b", 0 },
+ { "usb2514bi", 0 },
+ { "usb2517", 0 },
+ { "usb2517i", 0 },
+ { /* sentinel */ }
+};
+
+static struct driver_d usb251xb_i2c_driver = {
+ .name = DRIVER_NAME,
+ .probe = usb251xb_i2c_probe,
+ .id_table = usb251xb_id,
+ .of_compatible = DRV_OF_COMPAT(usb251xb_of_match),
+};
+device_i2c_driver(usb251xb_i2c_driver);
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 27e9f6d8b4..2793ee93d9 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -10,7 +10,7 @@ menuconfig WATCHDOG
if WATCHDOG
-menuconfig WATCHDOG_POLLER
+config WATCHDOG_POLLER
bool "Watchdog periodic feeder support"
select POLLER
help
@@ -22,6 +22,12 @@ config WATCHDOG_AR9344
help
Add support for watchdog on the QCA AR9344 SoC.
+config WATCHDOG_EFI
+ bool "Generic EFI Watchdog Driver"
+ depends on EFI_BOOTUP
+ help
+ Add support for the EFI watchdog.
+
config WATCHDOG_DAVINCI
bool "TI Davinci"
depends on ARCH_DAVINCI
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index faf06110a3..69189ba1f3 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -1,5 +1,6 @@
obj-$(CONFIG_WATCHDOG) += wd_core.o
obj-$(CONFIG_WATCHDOG_AR9344) += ar9344_wdt.o
+obj-$(CONFIG_WATCHDOG_EFI) += efi_wdt.o
obj-$(CONFIG_WATCHDOG_DAVINCI) += davinci_wdt.o
obj-$(CONFIG_WATCHDOG_OMAP) += omap_wdt.o
obj-$(CONFIG_WATCHDOG_MXS28) += im28wd.o
diff --git a/drivers/watchdog/efi_wdt.c b/drivers/watchdog/efi_wdt.c
new file mode 100644
index 0000000000..8e3e51b7a9
--- /dev/null
+++ b/drivers/watchdog/efi_wdt.c
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2019 Oleksij Rempel <o.rempel@pengutronix.de>, Pengutronix
+ */
+
+#include <common.h>
+#include <init.h>
+#include <driver.h>
+#include <efi.h>
+#include <efi/efi.h>
+#include <watchdog.h>
+
+struct efi_wdt_priv {
+ struct watchdog wd;
+ struct device_d *dev;
+};
+
+#define to_efi_wdt(h) container_of(h, struct efi_wdt_priv, wd)
+
+static int efi_wdt_set_timeout(struct watchdog *wd, unsigned timeout)
+{
+ struct efi_wdt_priv *priv = to_efi_wdt(wd);
+ efi_status_t efiret;
+
+ efiret = BS->set_watchdog_timer(timeout, 0, 0, NULL);
+ if (EFI_ERROR(efiret)) {
+ dev_err(priv->dev, "filed to set EFI watchdog: %lx\n", efiret);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int efi_wdt_probe(struct device_d *dev)
+{
+ struct efi_wdt_priv *priv;
+ int ret;
+
+ priv = xzalloc(sizeof(*priv));
+
+ priv->wd.set_timeout = efi_wdt_set_timeout;
+ priv->wd.hwdev = dev;
+ priv->dev = dev;
+
+ dev->priv = priv;
+
+ priv->wd.timeout_max = U32_MAX;
+
+ ret = watchdog_register(&priv->wd);
+ if (ret)
+ goto on_error;
+
+ return 0;
+
+on_error:
+ free(priv);
+ return ret;
+}
+
+static struct driver_d efi_wdt_driver = {
+ .name = "efi-wdt",
+ .probe = efi_wdt_probe,
+};
+device_platform_driver(efi_wdt_driver);