summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/clk/Kconfig10
-rw-r--r--drivers/clk/Makefile1
-rw-r--r--drivers/clk/intel/Makefile6
-rw-r--r--drivers/clk/intel/clk_intel.c41
-rw-r--r--drivers/core/syscon-uclass.c15
-rw-r--r--drivers/core/uclass.c17
-rw-r--r--drivers/ddr/altera/sdram_gen5.c6
-rw-r--r--drivers/ddr/altera/sequencer.c194
-rw-r--r--drivers/ddr/altera/sequencer.h1
-rw-r--r--drivers/gpio/Kconfig8
-rw-r--r--drivers/gpio/Makefile5
-rw-r--r--drivers/gpio/cortina_gpio.c111
-rw-r--r--drivers/i2c/tegra_i2c.c13
-rw-r--r--drivers/misc/i2c_eeprom.c35
-rw-r--r--drivers/misc/irq-uclass.c131
-rw-r--r--drivers/misc/irq_sandbox.c43
-rw-r--r--drivers/net/mtk_eth.c57
-rw-r--r--drivers/net/mtk_eth.h15
-rw-r--r--drivers/pinctrl/intel/pinctrl.c2
-rw-r--r--drivers/power/regulator/regulator-uclass.c3
-rw-r--r--drivers/power/regulator/regulator_common.c5
-rw-r--r--drivers/reset/reset-socfpga.c6
-rw-r--r--drivers/serial/Kconfig7
-rw-r--r--drivers/serial/Makefile1
-rw-r--r--drivers/serial/serial_cortina.c164
-rw-r--r--drivers/tpm/Kconfig10
-rw-r--r--drivers/tpm/Makefile1
-rw-r--r--drivers/tpm/cr50_i2c.c659
-rw-r--r--drivers/video/Kconfig7
-rw-r--r--drivers/video/mxsfb.c2
-rw-r--r--drivers/video/vidconsole-uclass.c20
-rw-r--r--drivers/watchdog/Kconfig15
-rw-r--r--drivers/watchdog/Makefile1
-rw-r--r--drivers/watchdog/cortina_wdt.c139
-rw-r--r--drivers/watchdog/designware_wdt.c150
35 files changed, 1787 insertions, 114 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 16d4237f891..1992d4a4b47 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -73,6 +73,16 @@ config CLK_COMPOSITE_CCF
Enable this option if you want to (re-)use the Linux kernel's Common
Clock Framework [CCF] composite code in U-Boot's clock driver.
+config CLK_INTEL
+ bool "Enable clock driver for Intel x86"
+ depends on CLK && X86
+ help
+ This provides very basic support for clocks on Intel SoCs. The driver
+ is barely used at present but could be expanded as needs arise.
+ Much clock configuration in U-Boot is either set up by the FSP, or
+ set up by U-Boot itself but only statically. Thus the driver does not
+ support changing clock rates, only querying them.
+
config CLK_STM32F
bool "Enable clock driver support for STM32F family"
depends on CLK && (STM32F7 || STM32F4)
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 06131edb9fe..e01783391dc 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_CLK_MVEBU) += mvebu/
obj-$(CONFIG_CLK_BCM6345) += clk_bcm6345.o
obj-$(CONFIG_CLK_BOSTON) += clk_boston.o
obj-$(CONFIG_CLK_EXYNOS) += exynos/
+obj-$(CONFIG_$(SPL_TPL_)CLK_INTEL) += intel/
obj-$(CONFIG_CLK_HSDK) += clk-hsdk-cgu.o
obj-$(CONFIG_CLK_MPC83XX) += mpc83xx_clk.o
obj-$(CONFIG_CLK_OWL) += owl/
diff --git a/drivers/clk/intel/Makefile b/drivers/clk/intel/Makefile
new file mode 100644
index 00000000000..45e93d70248
--- /dev/null
+++ b/drivers/clk/intel/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright 2010 Google LLC
+#
+
+obj-y += clk_intel.o
diff --git a/drivers/clk/intel/clk_intel.c b/drivers/clk/intel/clk_intel.c
new file mode 100644
index 00000000000..d2e15491a3d
--- /dev/null
+++ b/drivers/clk/intel/clk_intel.c
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2019 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <clk-uclass.h>
+#include <dt-bindings/clock/intel-clock.h>
+
+static ulong intel_clk_get_rate(struct clk *clk)
+{
+ ulong rate;
+
+ switch (clk->id) {
+ case CLK_I2C:
+ /* Hard-coded to 133MHz on current platforms */
+ return 133333333;
+ default:
+ return -ENODEV;
+ }
+
+ return rate;
+}
+
+static struct clk_ops intel_clk_ops = {
+ .get_rate = intel_clk_get_rate,
+};
+
+static const struct udevice_id intel_clk_ids[] = {
+ { .compatible = "intel,apl-clk" },
+ { }
+};
+
+U_BOOT_DRIVER(clk_intel) = {
+ .name = "clk_intel",
+ .id = UCLASS_CLK,
+ .of_match = intel_clk_ids,
+ .ops = &intel_clk_ops,
+};
diff --git a/drivers/core/syscon-uclass.c b/drivers/core/syscon-uclass.c
index ac2de475156..15f0e42a85b 100644
--- a/drivers/core/syscon-uclass.c
+++ b/drivers/core/syscon-uclass.c
@@ -129,22 +129,15 @@ struct regmap *syscon_regmap_lookup_by_phandle(struct udevice *dev,
int syscon_get_by_driver_data(ulong driver_data, struct udevice **devp)
{
- struct udevice *dev;
- struct uclass *uc;
int ret;
*devp = NULL;
- ret = uclass_get(UCLASS_SYSCON, &uc);
+
+ ret = uclass_first_device_drvdata(UCLASS_SYSCON, driver_data, devp);
if (ret)
- return ret;
- uclass_foreach_dev(dev, uc) {
- if (dev->driver_data == driver_data) {
- *devp = dev;
- return device_probe(dev);
- }
- }
+ return log_msg_ret("find", ret);
- return -ENODEV;
+ return 0;
}
struct regmap *syscon_get_regmap_by_driver_data(ulong driver_data)
diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c
index cd6ee471466..58b19a42109 100644
--- a/drivers/core/uclass.c
+++ b/drivers/core/uclass.c
@@ -625,6 +625,23 @@ int uclass_next_device_check(struct udevice **devp)
return device_probe(*devp);
}
+int uclass_first_device_drvdata(enum uclass_id id, ulong driver_data,
+ struct udevice **devp)
+{
+ struct udevice *dev;
+ struct uclass *uc;
+
+ uclass_id_foreach_dev(id, dev, uc) {
+ if (dev_get_driver_data(dev) == driver_data) {
+ *devp = dev;
+
+ return device_probe(dev);
+ }
+ }
+
+ return -ENODEV;
+}
+
int uclass_bind_device(struct udevice *dev)
{
struct uclass *uc;
diff --git a/drivers/ddr/altera/sdram_gen5.c b/drivers/ddr/altera/sdram_gen5.c
index 381b11b503e..314c7aea00e 100644
--- a/drivers/ddr/altera/sdram_gen5.c
+++ b/drivers/ddr/altera/sdram_gen5.c
@@ -435,8 +435,10 @@ static void sdr_load_regs(struct socfpga_sdr_ctrl *sdr_ctrl,
debug("Configuring DRAMODT\n");
writel(cfg->dram_odt, &sdr_ctrl->dram_odt);
- debug("Configuring EXTRATIME1\n");
- writel(cfg->extratime1, &sdr_ctrl->extratime1);
+ if (dram_is_ddr(3)) {
+ debug("Configuring EXTRATIME1\n");
+ writel(cfg->extratime1, &sdr_ctrl->extratime1);
+ }
}
/**
diff --git a/drivers/ddr/altera/sequencer.c b/drivers/ddr/altera/sequencer.c
index b85b56efe5d..35bda9b34c8 100644
--- a/drivers/ddr/altera/sequencer.c
+++ b/drivers/ddr/altera/sequencer.c
@@ -7,6 +7,7 @@
#include <asm/io.h>
#include <asm/arch/sdram.h>
#include <errno.h>
+#include <hang.h>
#include "sequencer.h"
static const struct socfpga_sdr_rw_load_manager *sdr_rw_load_mgr_regs =
@@ -54,6 +55,21 @@ static const struct socfpga_sdr_ctrl *sdr_ctrl =
#define SKIP_DELAY_LOOP_VALUE_OR_ZERO(non_skip_value) \
((non_skip_value) & seq->skip_delay_mask)
+bool dram_is_ddr(const u8 ddr)
+{
+ const struct socfpga_sdram_config *cfg = socfpga_get_sdram_config();
+ const u8 type = (cfg->ctrl_cfg >> SDR_CTRLGRP_CTRLCFG_MEMTYPE_LSB) &
+ SDR_CTRLGRP_CTRLCFG_MEMTYPE_MASK;
+
+ if (ddr == 2 && type == 1) /* DDR2 */
+ return true;
+
+ if (ddr == 3 && type == 2) /* DDR3 */
+ return true;
+
+ return false;
+}
+
static void set_failing_group_stage(struct socfpga_sdrseq *seq,
u32 group, u32 stage, u32 substage)
{
@@ -164,6 +180,8 @@ static void set_rank_and_odt_mask(struct socfpga_sdrseq *seq,
*/
odt_mask_0 = 0x3 & ~(1 << rank);
odt_mask_1 = 0x3;
+ if (dram_is_ddr(2))
+ odt_mask_1 &= ~(1 << rank);
} else {
/*
* - Single-Slot , Dual-Rank (2 CS per DIMM)
@@ -176,10 +194,11 @@ static void set_rank_and_odt_mask(struct socfpga_sdrseq *seq,
}
break;
case 4: /* 4 Ranks */
- /* Read:
+ /*
+ * DDR3 Read, DDR2 Read/Write:
* ----------+-----------------------+
* | ODT |
- * Read From +-----------------------+
+ * +-----------------------+
* Rank | 3 | 2 | 1 | 0 |
* ----------+-----+-----+-----+-----+
* 0 | 0 | 1 | 0 | 0 |
@@ -188,7 +207,7 @@ static void set_rank_and_odt_mask(struct socfpga_sdrseq *seq,
* 3 | 0 | 0 | 1 | 0 |
* ----------+-----+-----+-----+-----+
*
- * Write:
+ * DDR3 Write:
* ----------+-----------------------+
* | ODT |
* Write To +-----------------------+
@@ -203,19 +222,31 @@ static void set_rank_and_odt_mask(struct socfpga_sdrseq *seq,
switch (rank) {
case 0:
odt_mask_0 = 0x4;
- odt_mask_1 = 0x5;
+ if (dram_is_ddr(2))
+ odt_mask_1 = 0x4;
+ else if (dram_is_ddr(3))
+ odt_mask_1 = 0x5;
break;
case 1:
odt_mask_0 = 0x8;
- odt_mask_1 = 0xA;
+ if (dram_is_ddr(2))
+ odt_mask_1 = 0x8;
+ else if (dram_is_ddr(3))
+ odt_mask_1 = 0xA;
break;
case 2:
odt_mask_0 = 0x1;
- odt_mask_1 = 0x5;
+ if (dram_is_ddr(2))
+ odt_mask_1 = 0x1;
+ else if (dram_is_ddr(3))
+ odt_mask_1 = 0x5;
break;
case 3:
odt_mask_0 = 0x2;
- odt_mask_1 = 0xA;
+ if (dram_is_ddr(2))
+ odt_mask_1 = 0x2;
+ else if (dram_is_ddr(3))
+ odt_mask_1 = 0xA;
break;
}
break;
@@ -839,6 +870,12 @@ static void delay_for_n_mem_clocks(struct socfpga_sdrseq *seq,
debug("%s:%d clocks=%u ... end\n", __func__, __LINE__, clocks);
}
+static void delay_for_n_ns(struct socfpga_sdrseq *seq, const u32 ns)
+{
+ delay_for_n_mem_clocks(seq, (ns * seq->misccfg->afi_clk_freq *
+ seq->misccfg->afi_rate_ratio) / 1000);
+}
+
/**
* rw_mgr_mem_init_load_regs() - Load instruction registers
* @cntr0: Counter 0 value
@@ -872,14 +909,59 @@ static void rw_mgr_mem_init_load_regs(struct socfpga_sdrseq *seq,
}
/**
- * rw_mgr_mem_load_user() - Load user calibration values
+ * rw_mgr_mem_load_user_ddr2() - Load user calibration values for DDR2
+ * @handoff: Indicate whether this is initialization or handoff phase
+ *
+ * Load user calibration values and optionally precharge the banks.
+ */
+static void rw_mgr_mem_load_user_ddr2(struct socfpga_sdrseq *seq,
+ const int handoff)
+{
+ u32 grpaddr = SDR_PHYGRP_RWMGRGRP_ADDRESS |
+ RW_MGR_RUN_SINGLE_GROUP_OFFSET;
+ u32 r;
+
+ for (r = 0; r < seq->rwcfg->mem_number_of_ranks; r++) {
+ /* set rank */
+ set_rank_and_odt_mask(seq, r, RW_MGR_ODT_MODE_OFF);
+
+ /* precharge all banks ... */
+ writel(seq->rwcfg->precharge_all, grpaddr);
+
+ writel(seq->rwcfg->emr2, grpaddr);
+ writel(seq->rwcfg->emr3, grpaddr);
+ writel(seq->rwcfg->emr, grpaddr);
+
+ if (handoff) {
+ writel(seq->rwcfg->mr_user, grpaddr);
+ continue;
+ }
+
+ writel(seq->rwcfg->mr_dll_reset, grpaddr);
+
+ writel(seq->rwcfg->precharge_all, grpaddr);
+
+ writel(seq->rwcfg->refresh, grpaddr);
+ delay_for_n_ns(seq, 200);
+ writel(seq->rwcfg->refresh, grpaddr);
+ delay_for_n_ns(seq, 200);
+
+ writel(seq->rwcfg->mr_calib, grpaddr);
+ writel(/*seq->rwcfg->*/0x0b, grpaddr); // EMR_OCD_ENABLE
+ writel(seq->rwcfg->emr, grpaddr);
+ delay_for_n_mem_clocks(seq, 200);
+ }
+}
+
+/**
+ * rw_mgr_mem_load_user_ddr3() - Load user calibration values
* @fin1: Final instruction 1
* @fin2: Final instruction 2
* @precharge: If 1, precharge the banks at the end
*
* Load user calibration values and optionally precharge the banks.
*/
-static void rw_mgr_mem_load_user(struct socfpga_sdrseq *seq,
+static void rw_mgr_mem_load_user_ddr3(struct socfpga_sdrseq *seq,
const u32 fin1, const u32 fin2,
const int precharge)
{
@@ -936,6 +1018,25 @@ static void rw_mgr_mem_load_user(struct socfpga_sdrseq *seq,
}
/**
+ * rw_mgr_mem_load_user() - Load user calibration values
+ * @fin1: Final instruction 1
+ * @fin2: Final instruction 2
+ * @precharge: If 1, precharge the banks at the end
+ *
+ * Load user calibration values and optionally precharge the banks.
+ */
+static void rw_mgr_mem_load_user(struct socfpga_sdrseq *seq,
+ const u32 fin1, const u32 fin2,
+ const int precharge)
+{
+ if (dram_is_ddr(2))
+ rw_mgr_mem_load_user_ddr2(seq, precharge);
+ else if (dram_is_ddr(3))
+ rw_mgr_mem_load_user_ddr3(seq, fin1, fin2, precharge);
+ else
+ hang();
+}
+/**
* rw_mgr_mem_initialize() - Initialize RW Manager
*
* Initialize RW Manager.
@@ -945,8 +1046,10 @@ static void rw_mgr_mem_initialize(struct socfpga_sdrseq *seq)
debug("%s:%d\n", __func__, __LINE__);
/* The reset / cke part of initialization is broadcasted to all ranks */
- writel(RW_MGR_RANK_ALL, SDR_PHYGRP_RWMGRGRP_ADDRESS |
- RW_MGR_SET_CS_AND_ODT_MASK_OFFSET);
+ if (dram_is_ddr(3)) {
+ writel(RW_MGR_RANK_ALL, SDR_PHYGRP_RWMGRGRP_ADDRESS |
+ RW_MGR_SET_CS_AND_ODT_MASK_OFFSET);
+ }
/*
* Here's how you load register for a loop
@@ -979,29 +1082,38 @@ static void rw_mgr_mem_initialize(struct socfpga_sdrseq *seq)
/* Indicate that memory is stable. */
writel(1, &phy_mgr_cfg->reset_mem_stbl);
- /*
- * transition the RESET to high
- * Wait for 500us
- */
+ if (dram_is_ddr(2)) {
+ writel(seq->rwcfg->nop, SDR_PHYGRP_RWMGRGRP_ADDRESS |
+ RW_MGR_RUN_SINGLE_GROUP_OFFSET);
- /*
- * 500us @ 266MHz (3.75 ns) ~ 134000 clock cycles
- * If a and b are the number of iteration in 2 nested loops
- * it takes the following number of cycles to complete the operation
- * number_of_cycles = ((2 + n) * a + 2) * b
- * where n is the number of instruction in the inner loop
- * One possible solution is n = 2 , a = 131 , b = 256 => a = 83,
- * b = FF
- */
- rw_mgr_mem_init_load_regs(seq, seq->misccfg->treset_cntr0_val,
- seq->misccfg->treset_cntr1_val,
- seq->misccfg->treset_cntr2_val,
- seq->rwcfg->init_reset_1_cke_0);
+ /* Bring up clock enable. */
- /* Bring up clock enable. */
+ /* tXRP < 400 ck cycles */
+ delay_for_n_ns(seq, 400);
+ } else if (dram_is_ddr(3)) {
+ /*
+ * transition the RESET to high
+ * Wait for 500us
+ */
+
+ /*
+ * 500us @ 266MHz (3.75 ns) ~ 134000 clock cycles
+ * If a and b are the number of iteration in 2 nested loops
+ * it takes the following number of cycles to complete the
+ * operation number_of_cycles = ((2 + n) * a + 2) * b
+ * where n is the number of instruction in the inner loop
+ * One possible solution is
+ * n = 2 , a = 131 , b = 256 => a = 83, b = FF
+ */
+ rw_mgr_mem_init_load_regs(seq, seq->misccfg->treset_cntr0_val,
+ seq->misccfg->treset_cntr1_val,
+ seq->misccfg->treset_cntr2_val,
+ seq->rwcfg->init_reset_1_cke_0);
+ /* Bring up clock enable. */
- /* tXRP < 250 ck cycles */
- delay_for_n_mem_clocks(seq, 250);
+ /* tXRP < 250 ck cycles */
+ delay_for_n_mem_clocks(seq, 250);
+ }
rw_mgr_mem_load_user(seq, seq->rwcfg->mrs0_dll_reset_mirr,
seq->rwcfg->mrs0_dll_reset, 0);
@@ -3769,16 +3881,26 @@ static void initialize_tracking(struct socfpga_sdrseq *seq)
&sdr_reg_file->delays);
/* mux delay */
- writel((seq->rwcfg->idle << 24) | (seq->rwcfg->activate_1 << 16) |
- (seq->rwcfg->sgle_read << 8) | (seq->rwcfg->precharge_all << 0),
- &sdr_reg_file->trk_rw_mgr_addr);
+ if (dram_is_ddr(2)) {
+ writel(0, &sdr_reg_file->trk_rw_mgr_addr);
+ } else if (dram_is_ddr(3)) {
+ writel((seq->rwcfg->idle << 24) |
+ (seq->rwcfg->activate_1 << 16) |
+ (seq->rwcfg->sgle_read << 8) |
+ (seq->rwcfg->precharge_all << 0),
+ &sdr_reg_file->trk_rw_mgr_addr);
+ }
writel(seq->rwcfg->mem_if_read_dqs_width,
&sdr_reg_file->trk_read_dqs_width);
/* trefi [7:0] */
- writel((seq->rwcfg->refresh_all << 24) | (1000 << 0),
- &sdr_reg_file->trk_rfsh);
+ if (dram_is_ddr(2)) {
+ writel(1000 << 0, &sdr_reg_file->trk_rfsh);
+ } else if (dram_is_ddr(3)) {
+ writel((seq->rwcfg->refresh_all << 24) | (1000 << 0),
+ &sdr_reg_file->trk_rfsh);
+ }
}
int sdram_calibration_full(struct socfpga_sdr *sdr)
diff --git a/drivers/ddr/altera/sequencer.h b/drivers/ddr/altera/sequencer.h
index 4a03c3fdf98..c72a683ffef 100644
--- a/drivers/ddr/altera/sequencer.h
+++ b/drivers/ddr/altera/sequencer.h
@@ -279,5 +279,6 @@ struct socfpga_sdrseq {
};
int sdram_calibration_full(struct socfpga_sdr *sdr);
+bool dram_is_ddr(const u8 ddr);
#endif /* _SEQUENCER_H_ */
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 4e5a70780a9..f751a8b9ea8 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -60,6 +60,14 @@ config BCM6345_GPIO
help
This driver supports the GPIO banks on BCM6345 SoCs.
+config CORTINA_GPIO
+ bool "Cortina-Access GPIO driver"
+ depends on DM_GPIO && CORTINA_PLATFORM
+ help
+ Enable support for the GPIO controller in Cortina CAxxxx SoCs.
+ This driver supports all CPU ISA variants supported by Cortina
+ Access CAxxxx SoCs.
+
config DWAPB_GPIO
bool "DWAPB GPIO driver"
depends on DM && DM_GPIO
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 449046b64c2..9dd5a583898 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -6,17 +6,16 @@
ifndef CONFIG_SPL_BUILD
obj-$(CONFIG_DWAPB_GPIO) += dwapb_gpio.o
obj-$(CONFIG_AXP_GPIO) += axp_gpio.o
+obj-$(CONFIG_DM_74X164) += 74x164_gpio.o
endif
obj-$(CONFIG_$(SPL_TPL_)DM_GPIO) += gpio-uclass.o
obj-$(CONFIG_$(SPL_)DM_PCA953X) += pca953x_gpio.o
-ifdef CONFIG_$(SPL_TPL_)GPIO
-obj-$(CONFIG_DM_74X164) += 74x164_gpio.o
-endif
obj-$(CONFIG_AT91_GPIO) += at91_gpio.o
obj-$(CONFIG_ATMEL_PIO4) += atmel_pio4.o
obj-$(CONFIG_BCM6345_GPIO) += bcm6345_gpio.o
+obj-$(CONFIG_CORTINA_GPIO) += cortina_gpio.o
obj-$(CONFIG_INTEL_GPIO) += intel_gpio.o
obj-$(CONFIG_INTEL_ICH6_GPIO) += intel_ich6_gpio.o
obj-$(CONFIG_INTEL_BROADWELL_GPIO) += intel_broadwell_gpio.o
diff --git a/drivers/gpio/cortina_gpio.c b/drivers/gpio/cortina_gpio.c
new file mode 100644
index 00000000000..e2374ce1e76
--- /dev/null
+++ b/drivers/gpio/cortina_gpio.c
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020 Cortina-Access
+ *
+ * GPIO Driver for Cortina Access CAxxxx Line of SoCs
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <asm/io.h>
+#include <asm/gpio.h>
+#include <linux/compat.h>
+#include <linux/compiler.h>
+
+/* GPIO Register Map */
+#define CORTINA_GPIO_CFG 0x00
+#define CORTINA_GPIO_OUT 0x04
+#define CORTINA_GPIO_IN 0x08
+#define CORTINA_GPIO_LVL 0x0C
+#define CORTINA_GPIO_EDGE 0x10
+#define CORTINA_GPIO_BOTHEDGE 0x14
+#define CORTINA_GPIO_IE 0x18
+#define CORTINA_GPIO_INT 0x1C
+#define CORTINA_GPIO_STAT 0x20
+
+struct cortina_gpio_bank {
+ void __iomem *base;
+};
+
+#ifdef CONFIG_DM_GPIO
+static int ca_gpio_direction_input(struct udevice *dev, unsigned int offset)
+{
+ struct cortina_gpio_bank *priv = dev_get_priv(dev);
+
+ setbits_32(priv->base, BIT(offset));
+ return 0;
+}
+
+static int
+ca_gpio_direction_output(struct udevice *dev, unsigned int offset, int value)
+{
+ struct cortina_gpio_bank *priv = dev_get_priv(dev);
+
+ clrbits_32(priv->base, BIT(offset));
+ return 0;
+}
+
+static int ca_gpio_get_value(struct udevice *dev, unsigned int offset)
+{
+ struct cortina_gpio_bank *priv = dev_get_priv(dev);
+
+ return readl(priv->base + CORTINA_GPIO_IN) & BIT(offset);
+}
+
+static int ca_gpio_set_value(struct udevice *dev, unsigned int offset,
+ int value)
+{
+ struct cortina_gpio_bank *priv = dev_get_priv(dev);
+
+ setbits_32(priv->base + CORTINA_GPIO_OUT, BIT(offset));
+ return 0;
+}
+
+static int ca_gpio_get_function(struct udevice *dev, unsigned int offset)
+{
+ struct cortina_gpio_bank *priv = dev_get_priv(dev);
+
+ if (readl(priv->base) & BIT(offset))
+ return GPIOF_INPUT;
+ else
+ return GPIOF_OUTPUT;
+}
+
+static const struct dm_gpio_ops gpio_cortina_ops = {
+ .direction_input = ca_gpio_direction_input,
+ .direction_output = ca_gpio_direction_output,
+ .get_value = ca_gpio_get_value,
+ .set_value = ca_gpio_set_value,
+ .get_function = ca_gpio_get_function,
+};
+
+static int ca_gpio_probe(struct udevice *dev)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct cortina_gpio_bank *priv = dev_get_priv(dev);
+
+ priv->base = dev_remap_addr_index(dev, 0);
+ if (!priv->base)
+ return -EINVAL;
+
+ uc_priv->gpio_count = dev_read_u32_default(dev, "ngpios", 32);
+ uc_priv->bank_name = dev->name;
+
+ debug("Done Cortina GPIO init\n");
+ return 0;
+}
+
+static const struct udevice_id ca_gpio_ids[] = {
+ {.compatible = "cortina,ca-gpio"},
+ {}
+};
+
+U_BOOT_DRIVER(cortina_gpio) = {
+ .name = "cortina-gpio",
+ .id = UCLASS_GPIO,
+ .ops = &gpio_cortina_ops,
+ .probe = ca_gpio_probe,
+ .priv_auto_alloc_size = sizeof(struct cortina_gpio_bank),
+ .of_match = ca_gpio_ids,
+};
+#endif /* CONFIG_DM_GPIO */
diff --git a/drivers/i2c/tegra_i2c.c b/drivers/i2c/tegra_i2c.c
index f37db31c3c8..66a0148254c 100644
--- a/drivers/i2c/tegra_i2c.c
+++ b/drivers/i2c/tegra_i2c.c
@@ -500,18 +500,7 @@ static int tegra_i2c_xfer(struct udevice *bus, struct i2c_msg *msg,
int tegra_i2c_get_dvc_bus(struct udevice **busp)
{
- struct udevice *bus;
-
- for (uclass_first_device(UCLASS_I2C, &bus);
- bus;
- uclass_next_device(&bus)) {
- if (dev_get_driver_data(bus) == TYPE_DVC) {
- *busp = bus;
- return 0;
- }
- }
-
- return -ENODEV;
+ return uclass_first_device_drvdata(UCLASS_I2C, TYPE_DVC, busp);
}
static const struct dm_i2c_ops tegra_i2c_ops = {
diff --git a/drivers/misc/i2c_eeprom.c b/drivers/misc/i2c_eeprom.c
index 934f82074d5..6c0459dc555 100644
--- a/drivers/misc/i2c_eeprom.c
+++ b/drivers/misc/i2c_eeprom.c
@@ -15,6 +15,8 @@
struct i2c_eeprom_drv_data {
u32 size; /* size in bytes */
u32 pagewidth; /* pagesize = 2^pagewidth */
+ u32 addr_offset_mask; /* bits in addr used for offset overflow */
+ u32 offset_len; /* size in bytes of offset */
};
int i2c_eeprom_read(struct udevice *dev, int offset, uint8_t *buf, int size)
@@ -140,6 +142,11 @@ static int i2c_eeprom_std_probe(struct udevice *dev)
{
u8 test_byte;
int ret;
+ struct i2c_eeprom_drv_data *data =
+ (struct i2c_eeprom_drv_data *)dev_get_driver_data(dev);
+
+ i2c_set_chip_offset_len(dev, data->offset_len);
+ i2c_set_chip_addr_offset_mask(dev, data->addr_offset_mask);
/* Verify that the chip is functional */
ret = i2c_eeprom_read(dev, 0, &test_byte, 1);
@@ -152,71 +159,99 @@ static int i2c_eeprom_std_probe(struct udevice *dev)
static const struct i2c_eeprom_drv_data eeprom_data = {
.size = 0,
.pagewidth = 0,
+ .addr_offset_mask = 0,
+ .offset_len = 1,
};
static const struct i2c_eeprom_drv_data mc24aa02e48_data = {
.size = 256,
.pagewidth = 3,
+ .addr_offset_mask = 0,
+ .offset_len = 1,
};
static const struct i2c_eeprom_drv_data atmel24c01a_data = {
.size = 128,
.pagewidth = 3,
+ .addr_offset_mask = 0,
+ .offset_len = 1,
};
static const struct i2c_eeprom_drv_data atmel24c02_data = {
.size = 256,
.pagewidth = 3,
+ .addr_offset_mask = 0,
+ .offset_len = 1,
};
static const struct i2c_eeprom_drv_data atmel24c04_data = {
.size = 512,
.pagewidth = 4,
+ .addr_offset_mask = 0x1,
+ .offset_len = 1,
};
static const struct i2c_eeprom_drv_data atmel24c08_data = {
.size = 1024,
.pagewidth = 4,
+ .addr_offset_mask = 0x3,
+ .offset_len = 1,
};
static const struct i2c_eeprom_drv_data atmel24c08a_data = {
.size = 1024,
.pagewidth = 4,
+ .addr_offset_mask = 0x3,
+ .offset_len = 1,
};
static const struct i2c_eeprom_drv_data atmel24c16a_data = {
.size = 2048,
.pagewidth = 4,
+ .addr_offset_mask = 0x7,
+ .offset_len = 1,
};
static const struct i2c_eeprom_drv_data atmel24mac402_data = {
.size = 256,
.pagewidth = 4,
+ .addr_offset_mask = 0,
+ .offset_len = 1,
};
static const struct i2c_eeprom_drv_data atmel24c32_data = {
.size = 4096,
.pagewidth = 5,
+ .addr_offset_mask = 0,
+ .offset_len = 2,
};
static const struct i2c_eeprom_drv_data atmel24c64_data = {
.size = 8192,
.pagewidth = 5,
+ .addr_offset_mask = 0,
+ .offset_len = 2,
};
static const struct i2c_eeprom_drv_data atmel24c128_data = {
.size = 16384,
.pagewidth = 6,
+ .addr_offset_mask = 0,
+ .offset_len = 2,
};
static const struct i2c_eeprom_drv_data atmel24c256_data = {
.size = 32768,
.pagewidth = 6,
+ .addr_offset_mask = 0,
+ .offset_len = 2,
};
static const struct i2c_eeprom_drv_data atmel24c512_data = {
.size = 65536,
.pagewidth = 6,
+ .addr_offset_mask = 0,
+ .offset_len = 2,
};
static const struct udevice_id i2c_eeprom_std_ids[] = {
diff --git a/drivers/misc/irq-uclass.c b/drivers/misc/irq-uclass.c
index d5182cf1497..61aa10e4658 100644
--- a/drivers/misc/irq-uclass.c
+++ b/drivers/misc/irq-uclass.c
@@ -1,11 +1,16 @@
// SPDX-License-Identifier: GPL-2.0+
/*
- * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
+ * Copyright 2019 Google, LLC
+ * Written by Simon Glass <sjg@chromium.org>
*/
+#define LOG_CATEGORY UCLASS_IRQ
+
#include <common.h>
#include <dm.h>
+#include <dt-structs.h>
#include <irq.h>
+#include <dm/device-internal.h>
int irq_route_pmc_gpio_gpe(struct udevice *dev, uint pmc_gpe_num)
{
@@ -47,6 +52,130 @@ int irq_restore_polarities(struct udevice *dev)
return ops->restore_polarities(dev);
}
+int irq_read_and_clear(struct irq *irq)
+{
+ const struct irq_ops *ops = irq_get_ops(irq->dev);
+
+ if (!ops->read_and_clear)
+ return -ENOSYS;
+
+ return ops->read_and_clear(irq);
+}
+
+#if CONFIG_IS_ENABLED(OF_PLATDATA)
+int irq_get_by_index_platdata(struct udevice *dev, int index,
+ struct phandle_1_arg *cells, struct irq *irq)
+{
+ int ret;
+
+ if (index != 0)
+ return -ENOSYS;
+ ret = uclass_get_device(UCLASS_IRQ, 0, &irq->dev);
+ if (ret)
+ return ret;
+ irq->id = cells[0].arg[0];
+
+ return 0;
+}
+#else
+static int irq_of_xlate_default(struct irq *irq,
+ struct ofnode_phandle_args *args)
+{
+ log_debug("(irq=%p)\n", irq);
+
+ if (args->args_count > 1) {
+ log_debug("Invaild args_count: %d\n", args->args_count);
+ return -EINVAL;
+ }
+
+ if (args->args_count)
+ irq->id = args->args[0];
+ else
+ irq->id = 0;
+
+ return 0;
+}
+
+static int irq_get_by_index_tail(int ret, ofnode node,
+ struct ofnode_phandle_args *args,
+ const char *list_name, int index,
+ struct irq *irq)
+{
+ struct udevice *dev_irq;
+ const struct irq_ops *ops;
+
+ assert(irq);
+ irq->dev = NULL;
+ if (ret)
+ goto err;
+
+ ret = uclass_get_device_by_ofnode(UCLASS_IRQ, args->node, &dev_irq);
+ if (ret) {
+ log_debug("uclass_get_device_by_ofnode failed: err=%d\n", ret);
+ return ret;
+ }
+
+ irq->dev = dev_irq;
+
+ ops = irq_get_ops(dev_irq);
+
+ if (ops->of_xlate)
+ ret = ops->of_xlate(irq, args);
+ else
+ ret = irq_of_xlate_default(irq, args);
+ if (ret) {
+ log_debug("of_xlate() failed: %d\n", ret);
+ return ret;
+ }
+
+ return irq_request(dev_irq, irq);
+err:
+ log_debug("Node '%s', property '%s', failed to request IRQ index %d: %d\n",
+ ofnode_get_name(node), list_name, index, ret);
+ return ret;
+}
+
+int irq_get_by_index(struct udevice *dev, int index, struct irq *irq)
+{
+ struct ofnode_phandle_args args;
+ int ret;
+
+ ret = dev_read_phandle_with_args(dev, "interrupts-extended",
+ "#interrupt-cells", 0, index, &args);
+
+ return irq_get_by_index_tail(ret, dev_ofnode(dev), &args,
+ "interrupts-extended", index > 0, irq);
+}
+#endif /* OF_PLATDATA */
+
+int irq_request(struct udevice *dev, struct irq *irq)
+{
+ const struct irq_ops *ops;
+
+ log_debug("(dev=%p, irq=%p)\n", dev, irq);
+ if (!irq)
+ return 0;
+ ops = irq_get_ops(dev);
+
+ irq->dev = dev;
+
+ if (!ops->request)
+ return 0;
+
+ return ops->request(irq);
+}
+
+int irq_first_device_type(enum irq_dev_t type, struct udevice **devp)
+{
+ int ret;
+
+ ret = uclass_first_device_drvdata(UCLASS_IRQ, type, devp);
+ if (ret)
+ return log_msg_ret("find", ret);
+
+ return 0;
+}
+
UCLASS_DRIVER(irq) = {
.id = UCLASS_IRQ,
.name = "irq",
diff --git a/drivers/misc/irq_sandbox.c b/drivers/misc/irq_sandbox.c
index 6dda1a4c442..54bc47c8d8a 100644
--- a/drivers/misc/irq_sandbox.c
+++ b/drivers/misc/irq_sandbox.c
@@ -8,6 +8,18 @@
#include <common.h>
#include <dm.h>
#include <irq.h>
+#include <asm/test.h>
+
+/**
+ * struct sandbox_irq_priv - private data for this driver
+ *
+ * @count: Counts the number calls to the read_and_clear() method
+ * @pending: true if an interrupt is pending, else false
+ */
+struct sandbox_irq_priv {
+ int count;
+ bool pending;
+};
static int sandbox_set_polarity(struct udevice *dev, uint irq, bool active_low)
{
@@ -35,15 +47,43 @@ static int sandbox_restore_polarities(struct udevice *dev)
return 0;
}
+static int sandbox_irq_read_and_clear(struct irq *irq)
+{
+ struct sandbox_irq_priv *priv = dev_get_priv(irq->dev);
+
+ if (irq->id != SANDBOX_IRQN_PEND)
+ return -EINVAL;
+ priv->count++;
+ if (priv->pending) {
+ priv->pending = false;
+ return 1;
+ }
+
+ if (!(priv->count % 3))
+ priv->pending = true;
+
+ return 0;
+}
+
+static int sandbox_irq_of_xlate(struct irq *irq,
+ struct ofnode_phandle_args *args)
+{
+ irq->id = args->args[0];
+
+ return 0;
+}
+
static const struct irq_ops sandbox_irq_ops = {
.route_pmc_gpio_gpe = sandbox_route_pmc_gpio_gpe,
.set_polarity = sandbox_set_polarity,
.snapshot_polarities = sandbox_snapshot_polarities,
.restore_polarities = sandbox_restore_polarities,
+ .read_and_clear = sandbox_irq_read_and_clear,
+ .of_xlate = sandbox_irq_of_xlate,
};
static const struct udevice_id sandbox_irq_ids[] = {
- { .compatible = "sandbox,irq"},
+ { .compatible = "sandbox,irq", SANDBOX_IRQT_BASE },
{ }
};
@@ -52,4 +92,5 @@ U_BOOT_DRIVER(sandbox_irq_drv) = {
.id = UCLASS_IRQ,
.of_match = sandbox_irq_ids,
.ops = &sandbox_irq_ops,
+ .priv_auto_alloc_size = sizeof(struct sandbox_irq_priv),
};
diff --git a/drivers/net/mtk_eth.c b/drivers/net/mtk_eth.c
index 77589b2a04d..126b824b1a8 100644
--- a/drivers/net/mtk_eth.c
+++ b/drivers/net/mtk_eth.c
@@ -137,7 +137,8 @@ enum mtk_switch {
enum mtk_soc {
SOC_MT7623,
- SOC_MT7629
+ SOC_MT7629,
+ SOC_MT7622
};
struct mtk_eth_priv {
@@ -152,6 +153,7 @@ struct mtk_eth_priv {
void __iomem *fe_base;
void __iomem *gmac_base;
void __iomem *ethsys_base;
+ void __iomem *sgmii_base;
struct mii_dev *mdio_bus;
int (*mii_read)(struct mtk_eth_priv *priv, u8 phy, u8 reg);
@@ -751,6 +753,24 @@ static int mtk_phy_probe(struct udevice *dev)
return 0;
}
+static void mtk_sgmii_init(struct mtk_eth_priv *priv)
+{
+ /* Set SGMII GEN2 speed(2.5G) */
+ clrsetbits_le32(priv->sgmii_base + SGMSYS_GEN2_SPEED,
+ SGMSYS_SPEED_2500, SGMSYS_SPEED_2500);
+
+ /* Disable SGMII AN */
+ clrsetbits_le32(priv->sgmii_base + SGMSYS_PCS_CONTROL_1,
+ SGMII_AN_ENABLE, 0);
+
+ /* SGMII force mode setting */
+ writel(SGMII_FORCE_MODE, priv->sgmii_base + SGMSYS_SGMII_MODE);
+
+ /* Release PHYA power down state */
+ clrsetbits_le32(priv->sgmii_base + SGMSYS_QPHY_PWR_STATE_CTRL,
+ SGMII_PHYA_PWD, 0);
+}
+
static void mtk_mac_init(struct mtk_eth_priv *priv)
{
int i, ge_mode = 0;
@@ -759,8 +779,13 @@ static void mtk_mac_init(struct mtk_eth_priv *priv)
switch (priv->phy_interface) {
case PHY_INTERFACE_MODE_RGMII_RXID:
case PHY_INTERFACE_MODE_RGMII:
+ ge_mode = GE_MODE_RGMII;
+ break;
case PHY_INTERFACE_MODE_SGMII:
ge_mode = GE_MODE_RGMII;
+ mtk_ethsys_rmw(priv, ETHSYS_SYSCFG0_REG, SYSCFG0_SGMII_SEL_M,
+ SYSCFG0_SGMII_SEL(priv->gmac_id));
+ mtk_sgmii_init(priv);
break;
case PHY_INTERFACE_MODE_MII:
case PHY_INTERFACE_MODE_GMII:
@@ -829,7 +854,8 @@ static void mtk_eth_fifo_init(struct mtk_eth_priv *priv)
memset(priv->rx_ring_noc, 0, NUM_RX_DESC * sizeof(struct pdma_rxdesc));
memset(priv->pkt_pool, 0, TOTAL_PKT_BUF_SIZE);
- flush_dcache_range((u32)pkt_base, (u32)(pkt_base + TOTAL_PKT_BUF_SIZE));
+ flush_dcache_range((ulong)pkt_base,
+ (ulong)(pkt_base + TOTAL_PKT_BUF_SIZE));
priv->rx_dma_owner_idx0 = 0;
priv->tx_cpu_owner_idx0 = 0;
@@ -941,7 +967,7 @@ static int mtk_eth_send(struct udevice *dev, void *packet, int length)
pkt_base = (void *)phys_to_virt(priv->tx_ring_noc[idx].txd_info1.SDP0);
memcpy(pkt_base, packet, length);
- flush_dcache_range((u32)pkt_base, (u32)pkt_base +
+ flush_dcache_range((ulong)pkt_base, (ulong)pkt_base +
roundup(length, ARCH_DMA_MINALIGN));
priv->tx_ring_noc[idx].txd_info2.SDL0 = length;
@@ -967,7 +993,7 @@ static int mtk_eth_recv(struct udevice *dev, int flags, uchar **packetp)
length = priv->rx_ring_noc[idx].rxd_info2.PLEN0;
pkt_base = (void *)phys_to_virt(priv->rx_ring_noc[idx].rxd_info1.PDP0);
- invalidate_dcache_range((u32)pkt_base, (u32)pkt_base +
+ invalidate_dcache_range((ulong)pkt_base, (ulong)pkt_base +
roundup(length, ARCH_DMA_MINALIGN));
if (packetp)
@@ -995,7 +1021,7 @@ static int mtk_eth_probe(struct udevice *dev)
{
struct eth_pdata *pdata = dev_get_platdata(dev);
struct mtk_eth_priv *priv = dev_get_priv(dev);
- u32 iobase = pdata->iobase;
+ ulong iobase = pdata->iobase;
int ret;
/* Frame Engine Register Base */
@@ -1105,6 +1131,26 @@ static int mtk_eth_ofdata_to_platdata(struct udevice *dev)
}
}
+ if (priv->phy_interface == PHY_INTERFACE_MODE_SGMII) {
+ /* get corresponding sgmii phandle */
+ ret = dev_read_phandle_with_args(dev, "mediatek,sgmiisys",
+ NULL, 0, 0, &args);
+ if (ret)
+ return ret;
+
+ regmap = syscon_node_to_regmap(args.node);
+
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ priv->sgmii_base = regmap_get_range(regmap, 0);
+
+ if (!priv->sgmii_base) {
+ dev_err(dev, "Unable to find sgmii\n");
+ return -ENODEV;
+ }
+ }
+
/* check for switch first, otherwise phy will be used */
priv->sw = SW_NONE;
priv->switch_init = NULL;
@@ -1152,6 +1198,7 @@ static int mtk_eth_ofdata_to_platdata(struct udevice *dev)
static const struct udevice_id mtk_eth_ids[] = {
{ .compatible = "mediatek,mt7629-eth", .data = SOC_MT7629 },
{ .compatible = "mediatek,mt7623-eth", .data = SOC_MT7623 },
+ { .compatible = "mediatek,mt7622-eth", .data = SOC_MT7622 },
{}
};
diff --git a/drivers/net/mtk_eth.h b/drivers/net/mtk_eth.h
index fe89a03739f..9bb037d4400 100644
--- a/drivers/net/mtk_eth.h
+++ b/drivers/net/mtk_eth.h
@@ -20,6 +20,8 @@
#define ETHSYS_SYSCFG0_REG 0x14
#define SYSCFG0_GE_MODE_S(n) (12 + ((n) * 2))
#define SYSCFG0_GE_MODE_M 0x3
+#define SYSCFG0_SGMII_SEL_M (0x3 << 8)
+#define SYSCFG0_SGMII_SEL(gmac) ((!(gmac)) ? BIT(9) : BIT(8))
#define ETHSYS_CLKCFG0_REG 0x2c
#define ETHSYS_TRGMII_CLK_SEL362_5 BIT(11)
@@ -30,6 +32,19 @@
#define GE_MODE_MII_PHY 2
#define GE_MODE_RMII 3
+/* SGMII subsystem config registers */
+#define SGMSYS_PCS_CONTROL_1 0x0
+#define SGMII_AN_ENABLE BIT(12)
+
+#define SGMSYS_SGMII_MODE 0x20
+#define SGMII_FORCE_MODE 0x31120019
+
+#define SGMSYS_QPHY_PWR_STATE_CTRL 0xe8
+#define SGMII_PHYA_PWD BIT(4)
+
+#define SGMSYS_GEN2_SPEED 0x2028
+#define SGMSYS_SPEED_2500 BIT(2)
+
/* Frame Engine Registers */
/* PDMA */
diff --git a/drivers/pinctrl/intel/pinctrl.c b/drivers/pinctrl/intel/pinctrl.c
index 168595a3e12..e280054f945 100644
--- a/drivers/pinctrl/intel/pinctrl.c
+++ b/drivers/pinctrl/intel/pinctrl.c
@@ -615,7 +615,7 @@ int intel_pinctrl_ofdata_to_platdata(struct udevice *dev,
log_err("Cannot find community for pid %d\n", pplat->pid);
return -EDOM;
}
- ret = uclass_first_device_err(UCLASS_IRQ, &priv->itss);
+ ret = irq_first_device_type(X86_IRQT_ITSS, &priv->itss);
if (ret)
return log_msg_ret("Cannot find ITSS", ret);
priv->comm = comm;
diff --git a/drivers/power/regulator/regulator-uclass.c b/drivers/power/regulator/regulator-uclass.c
index 90961de95c6..c9d26344d78 100644
--- a/drivers/power/regulator/regulator-uclass.c
+++ b/drivers/power/regulator/regulator-uclass.c
@@ -464,6 +464,9 @@ static int regulator_pre_probe(struct udevice *dev)
(uc_pdata->min_uA == uc_pdata->max_uA))
uc_pdata->flags |= REGULATOR_FLAG_AUTOSET_UA;
+ if (uc_pdata->boot_on)
+ regulator_set_enable(dev, uc_pdata->boot_on);
+
return 0;
}
diff --git a/drivers/power/regulator/regulator_common.c b/drivers/power/regulator/regulator_common.c
index 939efb2c0d0..33b73b7c2f5 100644
--- a/drivers/power/regulator/regulator_common.c
+++ b/drivers/power/regulator/regulator_common.c
@@ -12,16 +12,11 @@ int regulator_common_ofdata_to_platdata(struct udevice *dev,
struct regulator_common_platdata *dev_pdata, const char *enable_gpio_name)
{
struct gpio_desc *gpio;
- struct dm_regulator_uclass_platdata *uc_pdata;
int flags = GPIOD_IS_OUT;
int ret;
- uc_pdata = dev_get_uclass_platdata(dev);
-
if (!dev_read_bool(dev, "enable-active-high"))
flags |= GPIOD_ACTIVE_LOW;
- if (uc_pdata->boot_on)
- flags |= GPIOD_IS_OUT_ACTIVE;
/* Get optional enable GPIO desc */
gpio = &dev_pdata->gpio;
diff --git a/drivers/reset/reset-socfpga.c b/drivers/reset/reset-socfpga.c
index 3ad5e35cc25..c0930a624b7 100644
--- a/drivers/reset/reset-socfpga.c
+++ b/drivers/reset/reset-socfpga.c
@@ -19,6 +19,7 @@
#include <dm/of_access.h>
#include <env.h>
#include <reset-uclass.h>
+#include <wait_bit.h>
#include <linux/bitops.h>
#include <linux/io.h>
#include <linux/sizes.h>
@@ -81,7 +82,10 @@ static int socfpga_reset_deassert(struct reset_ctl *reset_ctl)
int offset = id % (reg_width * BITS_PER_BYTE);
clrbits_le32(data->modrst_base + (bank * BANK_INCREMENT), BIT(offset));
- return 0;
+
+ return wait_for_bit_le32(data->modrst_base + (bank * BANK_INCREMENT),
+ BIT(offset),
+ false, 500, false);
}
static int socfpga_reset_request(struct reset_ctl *reset_ctl)
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index cd2e098883f..90e3983170c 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -553,6 +553,13 @@ config COREBOOT_SERIAL
a serial console on any platform without needing to change the
device tree, etc.
+config CORTINA_UART
+ bool "Cortina UART support"
+ depends on DM_SERIAL
+ help
+ Select this to enable UART support for Cortina-Access UART devices
+ found on CAxxxx SoCs.
+
config FSL_LINFLEXUART
bool "Freescale Linflex UART support"
depends on DM_SERIAL
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index 76b1811510d..e26b64494e5 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -36,6 +36,7 @@ obj-$(CONFIG_ARM_DCC) += arm_dcc.o
obj-$(CONFIG_ATMEL_USART) += atmel_usart.o
obj-$(CONFIG_BCM6345_SERIAL) += serial_bcm6345.o
obj-$(CONFIG_COREBOOT_SERIAL) += serial_coreboot.o
+obj-$(CONFIG_CORTINA_UART) += serial_cortina.o
obj-$(CONFIG_EFI_APP) += serial_efi.o
obj-$(CONFIG_LPC32XX_HSUART) += lpc32xx_hsuart.o
obj-$(CONFIG_MCFUART) += mcfuart.o
diff --git a/drivers/serial/serial_cortina.c b/drivers/serial/serial_cortina.c
new file mode 100644
index 00000000000..4f227bfe0ad
--- /dev/null
+++ b/drivers/serial/serial_cortina.c
@@ -0,0 +1,164 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2020 Cortina-Access Ltd.
+ * Common UART Driver for Cortina Access CAxxxx line of SoCs
+ *
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <watchdog.h>
+#include <asm/io.h>
+#include <serial.h>
+#include <linux/compiler.h>
+
+/* Register definitions */
+#define UCFG 0x00 /* UART config register */
+#define UFC 0x04 /* Flow Control */
+#define URX_SAMPLE 0x08 /* UART RX Sample register */
+#define URT_TUNE 0x0C /* Fine tune of UART clk */
+#define UTX_DATA 0x10 /* UART TX Character data */
+#define URX_DATA 0x14 /* UART RX Character data */
+#define UINFO 0x18 /* UART Info */
+#define UINT_EN0 0x1C /* UART Interrupt enable 0 */
+#define UINT_EN1 0x20 /* UART Interrupt enable 1 */
+#define UINT0 0x24 /* UART Interrupt 0 setting/clearing */
+#define UINT1 0x28 /* UART Interrupt 1 setting/clearing */
+#define UINT_STAT 0x2C /* UART Interrupt Status */
+
+/* UART Control Register Bit Fields */
+#define UCFG_BAUD_COUNT_MASK 0xFFFFFF00
+#define UCFG_BAUD_COUNT(x) ((x << 8) & UCFG_BAUD_COUNT_MASK)
+#define UCFG_EN BIT(7)
+#define UCFG_RX_EN BIT(6)
+#define UCFG_TX_EN BIT(5)
+#define UCFG_PARITY_EN BIT(4)
+#define UCFG_PARITY_SEL BIT(3)
+#define UCFG_2STOP_BIT BIT(2)
+#define UCFG_CNT1 BIT(1)
+#define UCFG_CNT0 BIT(0)
+#define UCFG_CHAR_5 0
+#define UCFG_CHAR_6 1
+#define UCFG_CHAR_7 2
+#define UCFG_CHAR_8 3
+
+#define UINFO_TX_FIFO_EMPTY BIT(3)
+#define UINFO_TX_FIFO_FULL BIT(2)
+#define UINFO_RX_FIFO_EMPTY BIT(1)
+#define UINFO_RX_FIFO_FULL BIT(0)
+
+#define UINT_RX_NON_EMPTY BIT(6)
+#define UINT_TX_EMPTY BIT(5)
+#define UINT_RX_UNDERRUN BIT(4)
+#define UINT_RX_OVERRUN BIT(3)
+#define UINT_RX_PARITY_ERR BIT(2)
+#define UINT_RX_STOP_ERR BIT(1)
+#define UINT_TX_OVERRUN BIT(0)
+#define UINT_MASK_ALL 0x7F
+
+struct ca_uart_priv {
+ void __iomem *base;
+};
+
+int ca_serial_setbrg(struct udevice *dev, int baudrate)
+{
+ struct ca_uart_priv *priv = dev_get_priv(dev);
+ unsigned int uart_ctrl, baud, sample;
+
+ baud = CORTINA_UART_CLOCK / baudrate;
+
+ uart_ctrl = readl(priv->base + UCFG);
+ uart_ctrl &= ~UCFG_BAUD_COUNT_MASK;
+ uart_ctrl |= UCFG_BAUD_COUNT(baud);
+ writel(uart_ctrl, priv->base + UCFG);
+
+ sample = baud / 2;
+ sample = (sample < 7) ? 7 : sample;
+ writel(sample, priv->base + URX_SAMPLE);
+
+ return 0;
+}
+
+static int ca_serial_getc(struct udevice *dev)
+{
+ struct ca_uart_priv *priv = dev_get_priv(dev);
+ int ch;
+
+ ch = readl(priv->base + URX_DATA) & 0xFF;
+
+ return (int)ch;
+}
+
+static int ca_serial_putc(struct udevice *dev, const char ch)
+{
+ struct ca_uart_priv *priv = dev_get_priv(dev);
+ unsigned int status;
+
+ /* Retry if TX FIFO full */
+ status = readl(priv->base + UINFO);
+ if (status & UINFO_TX_FIFO_FULL)
+ return -EAGAIN;
+
+ writel(ch, priv->base + UTX_DATA);
+
+ return 0;
+}
+
+static int ca_serial_pending(struct udevice *dev, bool input)
+{
+ struct ca_uart_priv *priv = dev_get_priv(dev);
+ unsigned int status;
+
+ status = readl(priv->base + UINFO);
+
+ if (input)
+ return (status & UINFO_RX_FIFO_EMPTY) ? 0 : 1;
+ else
+ return (status & UINFO_TX_FIFO_FULL) ? 1 : 0;
+}
+
+static int ca_serial_probe(struct udevice *dev)
+{
+ struct ca_uart_priv *priv = dev_get_priv(dev);
+ u32 uart_ctrl;
+
+ /* Set data, parity and stop bits */
+ uart_ctrl = UCFG_EN | UCFG_TX_EN | UCFG_RX_EN | UCFG_CHAR_8;
+ writel(uart_ctrl, priv->base + UCFG);
+
+ return 0;
+}
+
+static int ca_serial_ofdata_to_platdata(struct udevice *dev)
+{
+ struct ca_uart_priv *priv = dev_get_priv(dev);
+
+ priv->base = dev_remap_addr_index(dev, 0);
+ if (!priv->base)
+ return -ENOENT;
+
+ return 0;
+}
+
+static const struct dm_serial_ops ca_serial_ops = {
+ .putc = ca_serial_putc,
+ .pending = ca_serial_pending,
+ .getc = ca_serial_getc,
+ .setbrg = ca_serial_setbrg,
+};
+
+static const struct udevice_id ca_serial_ids[] = {
+ {.compatible = "cortina,ca-uart"},
+ {}
+};
+
+U_BOOT_DRIVER(serial_cortina) = {
+ .name = "serial_cortina",
+ .id = UCLASS_SERIAL,
+ .of_match = ca_serial_ids,
+ .ofdata_to_platdata = ca_serial_ofdata_to_platdata,
+ .priv_auto_alloc_size = sizeof(struct ca_uart_priv),
+ .probe = ca_serial_probe,
+ .ops = &ca_serial_ops
+};
diff --git a/drivers/tpm/Kconfig b/drivers/tpm/Kconfig
index 81bbffc50d1..9eebab5cfd9 100644
--- a/drivers/tpm/Kconfig
+++ b/drivers/tpm/Kconfig
@@ -127,6 +127,16 @@ config TPM_V2
if TPM_V2
+config TPM2_CR50_I2C
+ bool "Enable support for Google cr50 TPM"
+ depends on DM_I2C
+ help
+ Cr50 is an implementation of a TPM on Google's H1 security chip.
+ This uses the same open-source firmware as the Chromium OS EC.
+ While Cr50 has other features, its primary role is as the root of
+ trust for a device, It operates like a TPM and can be used with
+ verified boot. Cr50 is used on recent Chromebooks (since 2017).
+
config TPM2_TIS_SANDBOX
bool "Enable sandbox TPMv2.x driver"
depends on TPM_V2 && SANDBOX
diff --git a/drivers/tpm/Makefile b/drivers/tpm/Makefile
index b1be3feac86..8f075b9f45f 100644
--- a/drivers/tpm/Makefile
+++ b/drivers/tpm/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_TPM_TIS_SANDBOX) += tpm_tis_sandbox.o
obj-$(CONFIG_TPM_ST33ZP24_I2C) += tpm_tis_st33zp24_i2c.o
obj-$(CONFIG_TPM_ST33ZP24_SPI) += tpm_tis_st33zp24_spi.o
+obj-$(CONFIG_TPM2_CR50_I2C) += cr50_i2c.o
obj-$(CONFIG_TPM2_TIS_SANDBOX) += tpm2_tis_sandbox.o
obj-$(CONFIG_TPM2_TIS_SPI) += tpm2_tis_spi.o
obj-$(CONFIG_TPM2_FTPM_TEE) += tpm2_ftpm_tee.o
diff --git a/drivers/tpm/cr50_i2c.c b/drivers/tpm/cr50_i2c.c
new file mode 100644
index 00000000000..b904a7d426e
--- /dev/null
+++ b/drivers/tpm/cr50_i2c.c
@@ -0,0 +1,659 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Cr50 / H1 TPM support
+ *
+ * Copyright 2018 Google LLC
+ */
+
+#define LOG_CATEGORY UCLASS_TPM
+
+#include <common.h>
+#include <dm.h>
+#include <i2c.h>
+#include <irq.h>
+#include <spl.h>
+#include <tpm-v2.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <asm/arch/iomap.h>
+#include <asm/arch/pm.h>
+
+enum {
+ TIMEOUT_INIT_MS = 30000, /* Very long timeout for TPM init */
+ TIMEOUT_LONG_US = 2 * 1000 * 1000,
+ TIMEOUT_SHORT_US = 2 * 1000,
+ TIMEOUT_NO_IRQ_US = 20 * 1000,
+ TIMEOUT_IRQ_US = 100 * 1000,
+};
+
+enum {
+ CR50_DID_VID = 0x00281ae0L
+};
+
+enum {
+ CR50_MAX_BUF_SIZE = 63,
+};
+
+struct cr50_priv {
+ struct gpio_desc ready_gpio;
+ struct irq irq;
+ int locality;
+ uint vendor;
+ bool use_irq;
+};
+
+/* Wait for interrupt to indicate TPM is ready */
+static int cr50_i2c_wait_tpm_ready(struct udevice *dev)
+{
+ struct cr50_priv *priv = dev_get_priv(dev);
+ ulong timeout, base;
+ int i;
+
+ if (!priv->use_irq && !dm_gpio_is_valid(&priv->ready_gpio)) {
+ /* Fixed delay if interrupt not supported */
+ udelay(TIMEOUT_NO_IRQ_US);
+ return 0;
+ }
+
+ base = timer_get_us();
+ timeout = base + TIMEOUT_IRQ_US;
+
+ i = 0;
+ while (priv->use_irq ? !irq_read_and_clear(&priv->irq) :
+ !dm_gpio_get_value(&priv->ready_gpio)) {
+ i++;
+ if ((int)(timer_get_us() - timeout) >= 0) {
+ log_warning("Timeout\n");
+ /* Use this instead of the -ETIMEDOUT used by i2c */
+ return -ETIME;
+ }
+ }
+ log_debug("i=%d\n", i);
+
+ return 0;
+}
+
+/* Clear pending interrupts */
+static void cr50_i2c_clear_tpm_irq(struct udevice *dev)
+{
+ struct cr50_priv *priv = dev_get_priv(dev);
+
+ if (priv->use_irq)
+ irq_read_and_clear(&priv->irq);
+}
+
+/*
+ * cr50_i2c_read() - read from TPM register
+ *
+ * @dev: TPM chip information
+ * @addr: register address to read from
+ * @buffer: provided by caller
+ * @len: number of bytes to read
+ *
+ * 1) send register address byte 'addr' to the TPM
+ * 2) wait for TPM to indicate it is ready
+ * 3) read 'len' bytes of TPM response into the provided 'buffer'
+ *
+ * Return 0 on success. -ve on error
+ */
+static int cr50_i2c_read(struct udevice *dev, u8 addr, u8 *buffer,
+ size_t len)
+{
+ int ret;
+
+ /* Clear interrupt before starting transaction */
+ cr50_i2c_clear_tpm_irq(dev);
+
+ /* Send the register address byte to the TPM */
+ ret = dm_i2c_write(dev, 0, &addr, 1);
+ if (ret) {
+ log_err("Address write failed (err=%d)\n", ret);
+ return ret;
+ }
+
+ /* Wait for TPM to be ready with response data */
+ ret = cr50_i2c_wait_tpm_ready(dev);
+ if (ret)
+ return ret;
+
+ /* Read response data frrom the TPM */
+ ret = dm_i2c_read(dev, 0, buffer, len);
+ if (ret) {
+ log_err("Read response failed (err=%d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * cr50_i2c_write() - write to TPM register
+ *
+ * @dev: TPM chip information
+ * @addr: register address to write to
+ * @buffer: data to write
+ * @len: number of bytes to write
+ *
+ * 1) prepend the provided address to the provided data
+ * 2) send the address+data to the TPM
+ * 3) wait for TPM to indicate it is done writing
+ *
+ * Returns -1 on error, 0 on success.
+ */
+static int cr50_i2c_write(struct udevice *dev, u8 addr, const u8 *buffer,
+ size_t len)
+{
+ u8 buf[len + 1];
+ int ret;
+
+ if (len > CR50_MAX_BUF_SIZE) {
+ log_err("Length %zd is too large\n", len);
+ return -E2BIG;
+ }
+
+ /* Prepend the 'register address' to the buffer */
+ buf[0] = addr;
+ memcpy(buf + 1, buffer, len);
+
+ /* Clear interrupt before starting transaction */
+ cr50_i2c_clear_tpm_irq(dev);
+
+ /* Send write request buffer with address */
+ ret = dm_i2c_write(dev, 0, buf, len + 1);
+ if (ret) {
+ log_err("Error writing to TPM (err=%d)\n", ret);
+ return ret;
+ }
+
+ /* Wait for TPM to be ready */
+ return cr50_i2c_wait_tpm_ready(dev);
+}
+
+static inline u8 tpm_access(u8 locality)
+{
+ return 0x0 | (locality << 4);
+}
+
+static inline u8 tpm_sts(u8 locality)
+{
+ return 0x1 | (locality << 4);
+}
+
+static inline u8 tpm_data_fifo(u8 locality)
+{
+ return 0x5 | (locality << 4);
+}
+
+static inline u8 tpm_did_vid(u8 locality)
+{
+ return 0x6 | (locality << 4);
+}
+
+static int release_locality(struct udevice *dev, int force)
+{
+ struct cr50_priv *priv = dev_get_priv(dev);
+ u8 mask = TPM_ACCESS_VALID | TPM_ACCESS_REQUEST_PENDING;
+ u8 addr = tpm_access(priv->locality);
+ int ret;
+ u8 buf;
+
+ ret = cr50_i2c_read(dev, addr, &buf, 1);
+ if (ret)
+ return ret;
+
+ if (force || (buf & mask) == mask) {
+ buf = TPM_ACCESS_ACTIVE_LOCALITY;
+ cr50_i2c_write(dev, addr, &buf, 1);
+ }
+
+ priv->locality = 0;
+
+ return 0;
+}
+
+/* cr50 requires all 4 bytes of status register to be read */
+static int cr50_i2c_status(struct udevice *dev)
+{
+ struct cr50_priv *priv = dev_get_priv(dev);
+ u8 buf[4];
+ int ret;
+
+ ret = cr50_i2c_read(dev, tpm_sts(priv->locality), buf, sizeof(buf));
+ if (ret) {
+ log_warning("%s: Failed to read status\n", __func__);
+ return ret;
+ }
+
+ return buf[0];
+}
+
+/* cr50 requires all 4 bytes of status register to be written */
+static int cr50_i2c_ready(struct udevice *dev)
+{
+ struct cr50_priv *priv = dev_get_priv(dev);
+ u8 buf[4] = { TPM_STS_COMMAND_READY };
+ int ret;
+
+ ret = cr50_i2c_write(dev, tpm_sts(priv->locality), buf, sizeof(buf));
+ if (ret)
+ return ret;
+
+ udelay(TIMEOUT_SHORT_US);
+
+ return 0;
+}
+
+static int cr50_i2c_wait_burststs(struct udevice *dev, u8 mask,
+ size_t *burst, int *status)
+{
+ struct cr50_priv *priv = dev_get_priv(dev);
+ ulong timeout;
+ u32 buf;
+
+ /*
+ * cr50 uses bytes 3:2 of status register for burst count and all 4
+ * bytes must be read
+ */
+ timeout = timer_get_us() + TIMEOUT_LONG_US;
+ while (timer_get_us() < timeout) {
+ if (cr50_i2c_read(dev, tpm_sts(priv->locality),
+ (u8 *)&buf, sizeof(buf)) < 0) {
+ udelay(TIMEOUT_SHORT_US);
+ continue;
+ }
+
+ *status = buf & 0xff;
+ *burst = le16_to_cpu((buf >> 8) & 0xffff);
+
+ if ((*status & mask) == mask &&
+ *burst > 0 && *burst <= CR50_MAX_BUF_SIZE)
+ return 0;
+
+ udelay(TIMEOUT_SHORT_US);
+ }
+
+ log_warning("Timeout reading burst and status\n");
+
+ return -ETIMEDOUT;
+}
+
+static int cr50_i2c_recv(struct udevice *dev, u8 *buf, size_t buf_len)
+{
+ struct cr50_priv *priv = dev_get_priv(dev);
+ size_t burstcnt, expected, current, len;
+ u8 addr = tpm_data_fifo(priv->locality);
+ u8 mask = TPM_STS_VALID | TPM_STS_DATA_AVAIL;
+ u32 expected_buf;
+ int status;
+ int ret;
+
+ log_debug("%s: len=%x\n", __func__, buf_len);
+ if (buf_len < TPM_HEADER_SIZE)
+ return -E2BIG;
+
+ ret = cr50_i2c_wait_burststs(dev, mask, &burstcnt, &status);
+ if (ret < 0) {
+ log_warning("First chunk not available\n");
+ goto out_err;
+ }
+
+ /* Read first chunk of burstcnt bytes */
+ if (cr50_i2c_read(dev, addr, buf, burstcnt) < 0) {
+ log_warning("Read failed\n");
+ goto out_err;
+ }
+
+ /* Determine expected data in the return buffer */
+ memcpy(&expected_buf, buf + TPM_CMD_COUNT_OFFSET, sizeof(expected_buf));
+ expected = be32_to_cpu(expected_buf);
+ if (expected > buf_len) {
+ log_warning("Too much data: %zu > %zu\n", expected, buf_len);
+ goto out_err;
+ }
+
+ /* Now read the rest of the data */
+ current = burstcnt;
+ while (current < expected) {
+ /* Read updated burst count and check status */
+ if (cr50_i2c_wait_burststs(dev, mask, &burstcnt, &status) < 0) {
+ log_warning("- burst failure1\n");
+ goto out_err;
+ }
+
+ len = min(burstcnt, expected - current);
+ if (cr50_i2c_read(dev, addr, buf + current, len) != 0) {
+ log_warning("Read failed\n");
+ goto out_err;
+ }
+
+ current += len;
+ }
+
+ if (cr50_i2c_wait_burststs(dev, TPM_STS_VALID, &burstcnt,
+ &status) < 0) {
+ log_warning("- burst failure2\n");
+ goto out_err;
+ }
+ if (status & TPM_STS_DATA_AVAIL) {
+ log_warning("Data still available\n");
+ goto out_err;
+ }
+
+ return current;
+
+out_err:
+ /* Abort current transaction if still pending */
+ ret = cr50_i2c_status(dev);
+ if (ret < 0)
+ return ret;
+ if (ret & TPM_STS_COMMAND_READY) {
+ ret = cr50_i2c_ready(dev);
+ if (ret)
+ return ret;
+ }
+
+ return -EIO;
+}
+
+static int cr50_i2c_send(struct udevice *dev, const u8 *buf, size_t len)
+{
+ struct cr50_priv *priv = dev_get_priv(dev);
+
+ int status;
+ size_t burstcnt, limit, sent = 0;
+ u8 tpm_go[4] = { TPM_STS_GO };
+ ulong timeout;
+ int ret;
+
+ log_debug("%s: len=%x\n", __func__, len);
+ timeout = timer_get_us() + TIMEOUT_LONG_US;
+ do {
+ ret = cr50_i2c_status(dev);
+ if (ret < 0)
+ goto out_err;
+ if (ret & TPM_STS_COMMAND_READY)
+ break;
+
+ if (timer_get_us() > timeout)
+ goto out_err;
+
+ ret = cr50_i2c_ready(dev);
+ if (ret)
+ goto out_err;
+ } while (1);
+
+ while (len > 0) {
+ u8 mask = TPM_STS_VALID;
+
+ /* Wait for data if this is not the first chunk */
+ if (sent > 0)
+ mask |= TPM_STS_DATA_EXPECT;
+
+ if (cr50_i2c_wait_burststs(dev, mask, &burstcnt, &status) < 0)
+ goto out_err;
+
+ /*
+ * Use burstcnt - 1 to account for the address byte
+ * that is inserted by cr50_i2c_write()
+ */
+ limit = min(burstcnt - 1, len);
+ if (cr50_i2c_write(dev, tpm_data_fifo(priv->locality),
+ &buf[sent], limit) != 0) {
+ log_warning("Write failed\n");
+ goto out_err;
+ }
+
+ sent += limit;
+ len -= limit;
+ }
+
+ /* Ensure TPM is not expecting more data */
+ if (cr50_i2c_wait_burststs(dev, TPM_STS_VALID, &burstcnt, &status) < 0)
+ goto out_err;
+ if (status & TPM_STS_DATA_EXPECT) {
+ log_warning("Data still expected\n");
+ goto out_err;
+ }
+
+ /* Start the TPM command */
+ ret = cr50_i2c_write(dev, tpm_sts(priv->locality), tpm_go,
+ sizeof(tpm_go));
+ if (ret) {
+ log_warning("Start command failed\n");
+ goto out_err;
+ }
+
+ return sent;
+
+out_err:
+ /* Abort current transaction if still pending */
+ ret = cr50_i2c_status(dev);
+
+ if (ret < 0 || (ret & TPM_STS_COMMAND_READY)) {
+ ret = cr50_i2c_ready(dev);
+ if (ret)
+ return ret;
+ }
+
+ return -EIO;
+}
+
+/**
+ * process_reset() - Wait for the Cr50 to reset
+ *
+ * Cr50 processes reset requests asynchronously and conceivably could be busy
+ * executing a long command and not reacting to the reset pulse for a while.
+ *
+ * This function will make sure that the AP does not proceed with boot until
+ * TPM finished reset processing.
+ *
+ * @dev: Cr50 device
+ * @return 0 if OK, -EPERM if locality could not be taken
+ */
+static int process_reset(struct udevice *dev)
+{
+ const int loc = 0;
+ u8 access;
+ ulong start;
+
+ /*
+ * Locality is released by TPM reset.
+ *
+ * If locality is taken at this point, this could be due to the fact
+ * that the TPM is performing a long operation and has not processed
+ * reset request yet. We'll wait up to CR50_TIMEOUT_INIT_MS and see if
+ * it releases locality when reset is processed.
+ */
+ start = get_timer(0);
+ do {
+ const u8 mask = TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY;
+ int ret;
+
+ ret = cr50_i2c_read(dev, tpm_access(loc),
+ &access, sizeof(access));
+ if (ret || ((access & mask) == mask)) {
+ /*
+ * Don't bombard the chip with traffic; let it keep
+ * processing the command.
+ */
+ mdelay(2);
+ continue;
+ }
+
+ log_warning("TPM ready after %ld ms\n", get_timer(start));
+
+ return 0;
+ } while (get_timer(start) < TIMEOUT_INIT_MS);
+
+ log_warning("TPM failed to reset after %ld ms, status: %#x\n",
+ get_timer(start), access);
+
+ return -EPERM;
+}
+
+/*
+ * Locality could be already claimed (if this is a later U-Boot phase and the
+ * read-only U-Boot did not release it), or not yet claimed, if this is TPL or
+ * the older read-only U-Boot did release it.
+ */
+static int claim_locality(struct udevice *dev, int loc)
+{
+ const u8 mask = TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY;
+ u8 access;
+ int ret;
+
+ ret = cr50_i2c_read(dev, tpm_access(loc), &access, sizeof(access));
+ if (ret)
+ return log_msg_ret("read1", ret);
+
+ if ((access & mask) == mask) {
+ log_warning("Locality already claimed\n");
+ return 0;
+ }
+
+ access = TPM_ACCESS_REQUEST_USE;
+ ret = cr50_i2c_write(dev, tpm_access(loc), &access, sizeof(access));
+ if (ret)
+ return log_msg_ret("write", ret);
+
+ ret = cr50_i2c_read(dev, tpm_access(loc), &access, sizeof(access));
+ if (ret)
+ return log_msg_ret("read2", ret);
+
+ if ((access & mask) != mask) {
+ log_err("Failed to claim locality\n");
+ return -EPERM;
+ }
+ log_info("Claimed locality %d\n", loc);
+
+ return 0;
+}
+
+static int cr50_i2c_get_desc(struct udevice *dev, char *buf, int size)
+{
+ struct dm_i2c_chip *chip = dev_get_parent_platdata(dev);
+ struct cr50_priv *priv = dev_get_priv(dev);
+
+ return snprintf(buf, size, "cr50 TPM 2.0 (i2c %02x id %x) irq=%d",
+ chip->chip_addr, priv->vendor >> 16, priv->use_irq);
+}
+
+static int cr50_i2c_open(struct udevice *dev)
+{
+ char buf[80];
+ int ret;
+
+ ret = process_reset(dev);
+ if (ret)
+ return log_msg_ret("reset", ret);
+
+ ret = claim_locality(dev, 0);
+ if (ret)
+ return log_msg_ret("claim", ret);
+
+ cr50_i2c_get_desc(dev, buf, sizeof(buf));
+ log_debug("%s\n", buf);
+
+ return 0;
+}
+
+static int cr50_i2c_cleanup(struct udevice *dev)
+{
+ release_locality(dev, 1);
+
+ return 0;
+}
+
+enum {
+ TPM_TIMEOUT_MS = 5,
+ SHORT_TIMEOUT_MS = 750,
+ LONG_TIMEOUT_MS = 2000,
+};
+
+static int cr50_i2c_ofdata_to_platdata(struct udevice *dev)
+{
+ struct tpm_chip_priv *upriv = dev_get_uclass_priv(dev);
+ struct cr50_priv *priv = dev_get_priv(dev);
+ struct irq irq;
+ int ret;
+
+ upriv->version = TPM_V2;
+ upriv->duration_ms[TPM_SHORT] = SHORT_TIMEOUT_MS;
+ upriv->duration_ms[TPM_MEDIUM] = LONG_TIMEOUT_MS;
+ upriv->duration_ms[TPM_LONG] = LONG_TIMEOUT_MS;
+ upriv->retry_time_ms = TPM_TIMEOUT_MS;
+
+ upriv->pcr_count = 32;
+ upriv->pcr_select_min = 2;
+
+ /* Optional GPIO to track when cr50 is ready */
+ ret = irq_get_by_index(dev, 0, &irq);
+ if (!ret) {
+ priv->irq = irq;
+ priv->use_irq = true;
+ } else {
+ ret = gpio_request_by_name(dev, "ready-gpio", 0,
+ &priv->ready_gpio, GPIOD_IS_IN);
+ if (ret) {
+ log_warning("Cr50 does not have an ready GPIO/interrupt (err=%d)\n",
+ ret);
+ }
+ }
+
+ return 0;
+}
+
+static int cr50_i2c_probe(struct udevice *dev)
+{
+ struct cr50_priv *priv = dev_get_priv(dev);
+ u32 vendor = 0;
+ ulong start;
+
+ /*
+ * 150ms should be enough to synchronise with the TPM even under the
+ * worst nested-reset-request conditions. In the vast majority of cases
+ * there will be no wait at all.
+ */
+ start = get_timer(0);
+ while (get_timer(start) < 150) {
+ int ret;
+
+ /* Exit once DID and VID verified */
+ ret = cr50_i2c_read(dev, tpm_did_vid(0), (u8 *)&vendor, 4);
+ if (!ret && vendor == CR50_DID_VID)
+ break;
+
+ /* TPM might be resetting; let's retry in a bit */
+ mdelay(10);
+ }
+ if (vendor != CR50_DID_VID) {
+ log_debug("DID_VID %08x not recognised\n", vendor);
+ return log_msg_ret("vendor-id", -EXDEV);
+ }
+ priv->vendor = vendor;
+
+ return 0;
+}
+
+static const struct tpm_ops cr50_i2c_ops = {
+ .open = cr50_i2c_open,
+ .get_desc = cr50_i2c_get_desc,
+ .send = cr50_i2c_send,
+ .recv = cr50_i2c_recv,
+ .cleanup = cr50_i2c_cleanup,
+};
+
+static const struct udevice_id cr50_i2c_ids[] = {
+ { .compatible = "google,cr50" },
+ { }
+};
+
+U_BOOT_DRIVER(cr50_i2c) = {
+ .name = "cr50_i2c",
+ .id = UCLASS_TPM,
+ .of_match = cr50_i2c_ids,
+ .ops = &cr50_i2c_ops,
+ .ofdata_to_platdata = cr50_i2c_ofdata_to_platdata,
+ .probe = cr50_i2c_probe,
+ .priv_auto_alloc_size = sizeof(struct cr50_priv),
+};
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index d7e62bea9cf..4c933697022 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -38,7 +38,7 @@ config BACKLIGHT_GPIO
config VIDEO_BPP8
bool "Support 8-bit-per-pixel displays"
depends on DM_VIDEO
- default y if SANDBOX || X86
+ default y
help
Support drawing text and bitmaps onto a 8-bit-per-pixel display.
Enabling this will include code to support this display. Without
@@ -48,7 +48,7 @@ config VIDEO_BPP8
config VIDEO_BPP16
bool "Support 16-bit-per-pixel displays"
depends on DM_VIDEO
- default y if SANDBOX || X86
+ default y
help
Support drawing text and bitmaps onto a 16-bit-per-pixel display.
Enabling this will include code to support this display. Without
@@ -58,7 +58,7 @@ config VIDEO_BPP16
config VIDEO_BPP32
bool "Support 32-bit-per-pixel displays"
depends on DM_VIDEO
- default y if SANDBOX || X86
+ default y
help
Support drawing text and bitmaps onto a 32-bit-per-pixel display.
Enabling this will include code to support this display. Without
@@ -68,6 +68,7 @@ config VIDEO_BPP32
config VIDEO_ANSI
bool "Support ANSI escape sequences in video console"
depends on DM_VIDEO
+ default y
help
Enable ANSI escape sequence decoding for a more fully functional
console.
diff --git a/drivers/video/mxsfb.c b/drivers/video/mxsfb.c
index 6f80fbaaf3e..585af3d5714 100644
--- a/drivers/video/mxsfb.c
+++ b/drivers/video/mxsfb.c
@@ -430,6 +430,6 @@ U_BOOT_DRIVER(mxs_video) = {
.bind = mxs_video_bind,
.probe = mxs_video_probe,
.remove = mxs_video_remove,
- .flags = DM_FLAG_PRE_RELOC,
+ .flags = DM_FLAG_PRE_RELOC | DM_FLAG_OS_PREPARE,
};
#endif /* ifndef CONFIG_DM_VIDEO */
diff --git a/drivers/video/vidconsole-uclass.c b/drivers/video/vidconsole-uclass.c
index 75c7e25095d..8e0fc7f3ecf 100644
--- a/drivers/video/vidconsole-uclass.c
+++ b/drivers/video/vidconsole-uclass.c
@@ -144,22 +144,26 @@ u32 vid_console_color(struct video_priv *priv, unsigned int idx)
((colors[idx].g >> 2) << 5) |
((colors[idx].b >> 3) << 0);
}
+ break;
case VIDEO_BPP32:
if (CONFIG_IS_ENABLED(VIDEO_BPP32)) {
return (colors[idx].r << 16) |
(colors[idx].g << 8) |
(colors[idx].b << 0);
}
+ break;
default:
- /*
- * For unknown bit arrangements just support
- * black and white.
- */
- if (idx)
- return 0xffffff; /* white */
- else
- return 0x000000; /* black */
+ break;
}
+
+ /*
+ * For unknown bit arrangements just support
+ * black and white.
+ */
+ if (idx)
+ return 0xffffff; /* white */
+
+ return 0x000000; /* black */
}
static char *parsenum(char *s, int *num)
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 2b8064dfae0..36fbdce5520 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -45,6 +45,13 @@ config ULP_WATCHDOG
help
Say Y here to enable i.MX7ULP watchdog driver.
+config DESIGNWARE_WATCHDOG
+ bool "Designware watchdog timer support"
+ select HW_WATCHDOG if !WDT
+ help
+ Enable this to support Designware Watchdog Timer IP, present e.g.
+ on Altera SoCFPGA SoCs.
+
config WDT
bool "Enable driver model for watchdog timer drivers"
depends on DM
@@ -100,6 +107,14 @@ config WDT_CDNS
Select this to enable Cadence watchdog timer, which can be found on some
Xilinx Microzed Platform.
+config WDT_CORTINA
+ bool "Cortina Access CAxxxx watchdog timer support"
+ depends on WDT
+ help
+ Cortina Access CAxxxx watchdog timer support.
+ This driver support all CPU ISAs supported by Cortina
+ Access CAxxxx SoCs.
+
config WDT_MPC8xx
bool "MPC8xx watchdog timer support"
depends on WDT && MPC8xx
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 955caef815e..87f92a43b14 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_WDT_SANDBOX) += sandbox_wdt.o
obj-$(CONFIG_WDT_ARMADA_37XX) += armada-37xx-wdt.o
obj-$(CONFIG_WDT_ASPEED) += ast_wdt.o
obj-$(CONFIG_WDT_BCM6345) += bcm6345_wdt.o
+obj-$(CONFIG_WDT_CORTINA) += cortina_wdt.o
obj-$(CONFIG_WDT_ORION) += orion_wdt.o
obj-$(CONFIG_WDT_CDNS) += cdns_wdt.o
obj-$(CONFIG_WDT_MPC8xx) += mpc8xx_wdt.o
diff --git a/drivers/watchdog/cortina_wdt.c b/drivers/watchdog/cortina_wdt.c
new file mode 100644
index 00000000000..7ab9d7b2db9
--- /dev/null
+++ b/drivers/watchdog/cortina_wdt.c
@@ -0,0 +1,139 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020 Cortina-Access
+ *
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <hang.h>
+#include <asm/io.h>
+#include <wdt.h>
+#include <linux/bitops.h>
+
+#define CA_WDT_CTRL 0x00
+#define CA_WDT_PS 0x04
+#define CA_WDT_DIV 0x08
+#define CA_WDT_LD 0x0C
+#define CA_WDT_LOADE 0x10
+#define CA_WDT_CNT 0x14
+#define CA_WDT_IE 0x18
+#define CA_WDT_INT 0x1C
+#define CA_WDT_STAT 0x20
+
+/* CA_WDT_CTRL */
+#define CTL_WDT_EN BIT(0)
+#define CTL_WDT_RSTEN BIT(1)
+#define CTL_WDT_CLK_SEL BIT(2)
+/* CA_WDT_LOADE */
+#define WDT_UPD BIT(0)
+#define WDT_UPD_PS BIT(1)
+
+/* Global config */
+#define WDT_RESET_SUB BIT(4)
+#define WDT_RESET_ALL_BLOCK BIT(6)
+#define WDT_RESET_REMAP BIT(7)
+#define WDT_EXT_RESET BIT(8)
+#define WDT_RESET_DEFAULT (WDT_EXT_RESET | WDT_RESET_REMAP | \
+ WDT_RESET_ALL_BLOCK | WDT_RESET_SUB)
+
+struct ca_wdt_priv {
+ void __iomem *base;
+ void __iomem *global_config;
+};
+
+static void cortina_wdt_set_timeout(struct udevice *dev, u64 timeout_ms)
+{
+ struct ca_wdt_priv *priv = dev_get_priv(dev);
+
+ /* Prescale using millisecond unit */
+ writel(CORTINA_PER_IO_FREQ / 1000, priv->base + CA_WDT_PS);
+
+ /* Millisecond */
+ writel(1, priv->base + CA_WDT_DIV);
+
+ writel(timeout_ms, priv->base + CA_WDT_LD);
+ writel(WDT_UPD | WDT_UPD_PS, priv->base + CA_WDT_LOADE);
+}
+
+static int cortina_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
+{
+ struct ca_wdt_priv *priv = dev_get_priv(dev);
+
+ cortina_wdt_set_timeout(dev, timeout);
+
+ /* WDT Reset option */
+ setbits_32(priv->global_config, WDT_RESET_DEFAULT);
+
+ /* Enable WDT */
+ setbits_32(priv->base, CTL_WDT_EN | CTL_WDT_RSTEN | CTL_WDT_CLK_SEL);
+
+ return 0;
+}
+
+static int cortina_wdt_stop(struct udevice *dev)
+{
+ struct ca_wdt_priv *priv = dev_get_priv(dev);
+
+ /* Disable WDT */
+ writel(0, priv->base);
+
+ return 0;
+}
+
+static int cortina_wdt_reset(struct udevice *dev)
+{
+ struct ca_wdt_priv *priv = dev_get_priv(dev);
+
+ /* Reload WDT counter */
+ writel(WDT_UPD, priv->base + CA_WDT_LOADE);
+
+ return 0;
+}
+
+static int cortina_wdt_expire_now(struct udevice *dev, ulong flags)
+{
+ /* Set 1ms timeout to reset system */
+ cortina_wdt_set_timeout(dev, 1);
+ hang();
+
+ return 0;
+}
+
+static int cortina_wdt_probe(struct udevice *dev)
+{
+ struct ca_wdt_priv *priv = dev_get_priv(dev);
+
+ priv->base = dev_remap_addr_index(dev, 0);
+ if (!priv->base)
+ return -ENOENT;
+
+ priv->global_config = dev_remap_addr_index(dev, 1);
+ if (!priv->global_config)
+ return -ENOENT;
+
+ /* Stop WDT */
+ cortina_wdt_stop(dev);
+
+ return 0;
+}
+
+static const struct wdt_ops cortina_wdt_ops = {
+ .start = cortina_wdt_start,
+ .reset = cortina_wdt_reset,
+ .stop = cortina_wdt_stop,
+ .expire_now = cortina_wdt_expire_now,
+};
+
+static const struct udevice_id cortina_wdt_ids[] = {
+ {.compatible = "cortina,ca-wdt"},
+ {}
+};
+
+U_BOOT_DRIVER(cortina_wdt) = {
+ .name = "cortina_wdt",
+ .id = UCLASS_WDT,
+ .probe = cortina_wdt_probe,
+ .of_match = cortina_wdt_ids,
+ .ops = &cortina_wdt_ops,
+};
diff --git a/drivers/watchdog/designware_wdt.c b/drivers/watchdog/designware_wdt.c
index c668567c663..1024a04596f 100644
--- a/drivers/watchdog/designware_wdt.c
+++ b/drivers/watchdog/designware_wdt.c
@@ -3,8 +3,11 @@
* Copyright (C) 2013 Altera Corporation <www.altera.com>
*/
+#include <clk.h>
#include <common.h>
-#include <watchdog.h>
+#include <dm.h>
+#include <reset.h>
+#include <wdt.h>
#include <asm/io.h>
#include <asm/utils.h>
@@ -14,49 +17,52 @@
#define DW_WDT_CR_EN_OFFSET 0x00
#define DW_WDT_CR_RMOD_OFFSET 0x01
-#define DW_WDT_CR_RMOD_VAL 0x00
#define DW_WDT_CRR_RESTART_VAL 0x76
+struct designware_wdt_priv {
+ void __iomem *base;
+ unsigned int clk_khz;
+};
+
/*
* Set the watchdog time interval.
* Counter is 32 bit.
*/
-static int designware_wdt_settimeout(unsigned int timeout)
+static int designware_wdt_settimeout(void __iomem *base, unsigned int clk_khz,
+ unsigned int timeout)
{
signed int i;
/* calculate the timeout range value */
- i = (log_2_n_round_up(timeout * CONFIG_DW_WDT_CLOCK_KHZ)) - 16;
- if (i > 15)
- i = 15;
- if (i < 0)
- i = 0;
+ i = log_2_n_round_up(timeout * clk_khz) - 16;
+ i = clamp(i, 0, 15);
+
+ writel(i | (i << 4), base + DW_WDT_TORR);
- writel((i | (i << 4)), (CONFIG_DW_WDT_BASE + DW_WDT_TORR));
return 0;
}
-static void designware_wdt_enable(void)
+static void designware_wdt_enable(void __iomem *base)
{
- writel(((DW_WDT_CR_RMOD_VAL << DW_WDT_CR_RMOD_OFFSET) |
- (0x1 << DW_WDT_CR_EN_OFFSET)),
- (CONFIG_DW_WDT_BASE + DW_WDT_CR));
+ writel(BIT(DW_WDT_CR_EN_OFFSET), base + DW_WDT_CR);
}
-static unsigned int designware_wdt_is_enabled(void)
+static unsigned int designware_wdt_is_enabled(void __iomem *base)
{
- unsigned long val;
- val = readl((CONFIG_DW_WDT_BASE + DW_WDT_CR));
- return val & 0x1;
+ return readl(base + DW_WDT_CR) & BIT(0);
}
-#if defined(CONFIG_HW_WATCHDOG)
-void hw_watchdog_reset(void)
+static void designware_wdt_reset_common(void __iomem *base)
{
- if (designware_wdt_is_enabled())
+ if (designware_wdt_is_enabled(base))
/* restart the watchdog counter */
- writel(DW_WDT_CRR_RESTART_VAL,
- (CONFIG_DW_WDT_BASE + DW_WDT_CRR));
+ writel(DW_WDT_CRR_RESTART_VAL, base + DW_WDT_CRR);
+}
+
+#if !CONFIG_IS_ENABLED(WDT)
+void hw_watchdog_reset(void)
+{
+ designware_wdt_reset_common((void __iomem *)CONFIG_DW_WDT_BASE);
}
void hw_watchdog_init(void)
@@ -64,10 +70,106 @@ void hw_watchdog_init(void)
/* reset to disable the watchdog */
hw_watchdog_reset();
/* set timer in miliseconds */
- designware_wdt_settimeout(CONFIG_WATCHDOG_TIMEOUT_MSECS);
+ designware_wdt_settimeout((void __iomem *)CONFIG_DW_WDT_BASE,
+ CONFIG_DW_WDT_CLOCK_KHZ,
+ CONFIG_WATCHDOG_TIMEOUT_MSECS);
/* enable the watchdog */
- designware_wdt_enable();
+ designware_wdt_enable((void __iomem *)CONFIG_DW_WDT_BASE);
/* reset the watchdog */
hw_watchdog_reset();
}
+#else
+static int designware_wdt_reset(struct udevice *dev)
+{
+ struct designware_wdt_priv *priv = dev_get_priv(dev);
+
+ designware_wdt_reset_common(priv->base);
+
+ return 0;
+}
+
+static int designware_wdt_stop(struct udevice *dev)
+{
+ struct designware_wdt_priv *priv = dev_get_priv(dev);
+
+ designware_wdt_reset(dev);
+ writel(0, priv->base + DW_WDT_CR);
+
+ return 0;
+}
+
+static int designware_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
+{
+ struct designware_wdt_priv *priv = dev_get_priv(dev);
+
+ designware_wdt_stop(dev);
+
+ /* set timer in miliseconds */
+ designware_wdt_settimeout(priv->base, priv->clk_khz, timeout);
+
+ designware_wdt_enable(priv->base);
+
+ /* reset the watchdog */
+ return designware_wdt_reset(dev);
+}
+
+static int designware_wdt_probe(struct udevice *dev)
+{
+ struct designware_wdt_priv *priv = dev_get_priv(dev);
+ __maybe_unused int ret;
+
+ priv->base = dev_remap_addr(dev);
+ if (!priv->base)
+ return -EINVAL;
+
+#if CONFIG_IS_ENABLED(CLK)
+ struct clk clk;
+
+ ret = clk_get_by_index(dev, 0, &clk);
+ if (ret)
+ return ret;
+
+ priv->clk_khz = clk_get_rate(&clk);
+ if (!priv->clk_khz)
+ return -EINVAL;
+#else
+ priv->clk_khz = CONFIG_DW_WDT_CLOCK_KHZ;
+#endif
+
+#if CONFIG_IS_ENABLED(DM_RESET)
+ struct reset_ctl_bulk resets;
+
+ ret = reset_get_bulk(dev, &resets);
+ if (ret)
+ return ret;
+
+ ret = reset_deassert_bulk(&resets);
+ if (ret)
+ return ret;
+#endif
+
+ /* reset to disable the watchdog */
+ return designware_wdt_stop(dev);
+}
+
+static const struct wdt_ops designware_wdt_ops = {
+ .start = designware_wdt_start,
+ .reset = designware_wdt_reset,
+ .stop = designware_wdt_stop,
+};
+
+static const struct udevice_id designware_wdt_ids[] = {
+ { .compatible = "snps,dw-wdt"},
+ {}
+};
+
+U_BOOT_DRIVER(designware_wdt) = {
+ .name = "designware_wdt",
+ .id = UCLASS_WDT,
+ .of_match = designware_wdt_ids,
+ .priv_auto_alloc_size = sizeof(struct designware_wdt_priv),
+ .probe = designware_wdt_probe,
+ .ops = &designware_wdt_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
#endif