summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/clk/clk.c49
-rw-r--r--drivers/mci/Kconfig4
-rw-r--r--drivers/mci/Makefile1
-rw-r--r--drivers/mci/atmel-sdhci-common.c58
-rw-r--r--drivers/mci/atmel-sdhci-pbl.c128
-rw-r--r--drivers/mci/atmel-sdhci.c4
-rw-r--r--drivers/mci/atmel-sdhci.h1
-rw-r--r--drivers/watchdog/Kconfig6
-rw-r--r--drivers/watchdog/Makefile1
-rw-r--r--drivers/watchdog/at91sam9_wdt.c109
10 files changed, 333 insertions, 28 deletions
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 4386c95aa9..f2e459a760 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -469,21 +469,60 @@ unsigned int of_clk_get_parent_count(struct device_node *np)
}
EXPORT_SYMBOL_GPL(of_clk_get_parent_count);
-char *of_clk_get_parent_name(struct device_node *np, unsigned int index)
+char *of_clk_get_parent_name(const struct device_node *np, int index)
{
struct of_phandle_args clkspec;
+ struct property *prop;
const char *clk_name;
+ const __be32 *vp;
+ u32 pv;
int rc;
+ int count;
+ struct clk *clk;
rc = of_parse_phandle_with_args(np, "clocks", "#clock-cells", index,
- &clkspec);
+ &clkspec);
if (rc)
return NULL;
+ index = clkspec.args_count ? clkspec.args[0] : 0;
+ count = 0;
+
+ /* if there is an indices property, use it to transfer the index
+ * specified into an array offset for the clock-output-names property.
+ */
+ of_property_for_each_u32(clkspec.np, "clock-indices", prop, vp, pv) {
+ if (index == pv) {
+ index = count;
+ break;
+ }
+ count++;
+ }
+ /* We went off the end of 'clock-indices' without finding it */
+ if (prop && !vp)
+ return NULL;
+
if (of_property_read_string_index(clkspec.np, "clock-output-names",
- clkspec.args_count ? clkspec.args[0] : 0,
- &clk_name) < 0)
- clk_name = clkspec.np->name;
+ index,
+ &clk_name) < 0) {
+ /*
+ * Best effort to get the name if the clock has been
+ * registered with the framework. If the clock isn't
+ * registered, we return the node name as the name of
+ * the clock as long as #clock-cells = 0.
+ */
+ clk = of_clk_get_from_provider(&clkspec);
+ if (IS_ERR(clk)) {
+ if (clkspec.args_count == 0)
+ clk_name = clkspec.np->name;
+ else
+ clk_name = NULL;
+ } else {
+ clk_name = __clk_get_name(clk);
+ clk_put(clk);
+ }
+ }
+
return xstrdup(clk_name);
}
diff --git a/drivers/mci/Kconfig b/drivers/mci/Kconfig
index 6ae1e81252..3026b25cad 100644
--- a/drivers/mci/Kconfig
+++ b/drivers/mci/Kconfig
@@ -187,3 +187,7 @@ endif
config MCI_IMX_ESDHC_PBL
bool
select MCI_SDHCI
+
+config MCI_ATMEL_SDHCI_PBL
+ bool
+ select MCI_SDHCI
diff --git a/drivers/mci/Makefile b/drivers/mci/Makefile
index 177483dcfb..4a53633674 100644
--- a/drivers/mci/Makefile
+++ b/drivers/mci/Makefile
@@ -5,6 +5,7 @@ obj-$(CONFIG_MCI_ATMEL_SDHCI) += atmel-sdhci.o atmel-sdhci-common.o
obj-$(CONFIG_MCI_BCM283X) += mci-bcm2835.o
obj-$(CONFIG_MCI_BCM283X_SDHOST) += bcm2835-sdhost.o
obj-$(CONFIG_MCI_DOVE) += dove-sdhci.o
+pbl-$(CONFIG_MCI_ATMEL_SDHCI_PBL) += atmel-sdhci-pbl.o atmel-sdhci-common.o
obj-$(CONFIG_MCI_IMX) += imx.o
obj-$(CONFIG_MCI_IMX_ESDHC) += imx-esdhc.o imx-esdhc-common.o
pbl-$(CONFIG_MCI_IMX_ESDHC_PBL) += imx-esdhc-pbl.o imx-esdhc-common.o
diff --git a/drivers/mci/atmel-sdhci-common.c b/drivers/mci/atmel-sdhci-common.c
index b952462262..a83610c3d0 100644
--- a/drivers/mci/atmel-sdhci-common.c
+++ b/drivers/mci/atmel-sdhci-common.c
@@ -12,6 +12,16 @@
#include <common.h>
#include <mci.h>
+#include <mach/early_udelay.h>
+
+#ifdef __PBL__
+#define udelay early_udelay
+#undef dev_err
+#define dev_err(d, ...) pr_err(__VA_ARGS__)
+#undef dev_warn
+#define dev_warn(d, ...) pr_warn(__VA_ARGS__)
+#endif
+
#include "atmel-sdhci.h"
#define AT91_SDHCI_CA1R 0x44 /* Capabilities 1 Register */
@@ -83,25 +93,27 @@ exit:
static int at91_sdhci_wait_for_done(struct at91_sdhci *host, u32 mask)
{
struct sdhci *sdhci = &host->sdhci;
- u16 status;
+ u32 status;
int ret;
- ret = sdhci_read16_poll_timeout(sdhci, SDHCI_INT_NORMAL_STATUS, status,
+ ret = sdhci_read32_poll_timeout(sdhci, SDHCI_INT_STATUS, status,
(status & mask) == mask || (status & SDHCI_INT_ERROR),
USEC_PER_SEC);
if (ret < 0) {
- pr_err("SDHCI timeout while waiting for done\n");
+ dev_err(host->dev, "SDHCI timeout while waiting for done\n");
return ret;
}
+ if (status & SDHCI_INT_TIMEOUT)
+ return -ETIMEDOUT;
+
if (status & SDHCI_INT_ERROR) {
- pr_err("SDHCI_INT_ERROR: 0x%08x\n",
- sdhci_read16(sdhci, SDHCI_INT_ERROR_STATUS));
+ dev_err(host->dev, "SDHCI_INT_STATUS: 0x%08x\n", status);
return -EPERM;
}
- return status;
+ return status & 0xFFFF;
}
int at91_sdhci_send_command(struct at91_sdhci *host, struct mci_cmd *cmd,
@@ -109,7 +121,8 @@ int at91_sdhci_send_command(struct at91_sdhci *host, struct mci_cmd *cmd,
{
unsigned command, xfer;
struct sdhci *sdhci = &host->sdhci;
- u32 mask, status, state;
+ u32 mask, state;
+ int status;
int ret;
/* Wait for idle before next command */
@@ -120,7 +133,7 @@ int at91_sdhci_send_command(struct at91_sdhci *host, struct mci_cmd *cmd,
ret = sdhci_read32_poll_timeout(sdhci, SDHCI_PRESENT_STATE, state,
!(state & mask), 100 * USEC_PER_MSEC);
if (ret) {
- pr_err("timeout while waiting for idle\n");
+ dev_err(host->dev, "timeout while waiting for idle\n");
return ret;
}
@@ -147,28 +160,29 @@ int at91_sdhci_send_command(struct at91_sdhci *host, struct mci_cmd *cmd,
sdhci_write16(sdhci, SDHCI_COMMAND, command);
status = at91_sdhci_wait_for_done(host, mask);
- if (status >= 0 && (status & (SDHCI_INT_ERROR | mask)) == mask) {
- sdhci_read_response(sdhci, cmd);
- sdhci_write32(sdhci, SDHCI_INT_STATUS, mask);
-
- if (data)
- sdhci_transfer_data(sdhci, data);
+ if (status < 0)
+ goto error;
- udelay(1000);
+ sdhci_read_response(sdhci, cmd);
+ sdhci_write32(sdhci, SDHCI_INT_STATUS, mask);
- status = sdhci_read32(sdhci, SDHCI_INT_STATUS);
- sdhci_write32(sdhci, SDHCI_INT_STATUS, ~0U);
+ if (data)
+ sdhci_transfer_data(sdhci, data);
- return 0;
- }
+ udelay(1000);
status = sdhci_read32(sdhci, SDHCI_INT_STATUS);
sdhci_write32(sdhci, SDHCI_INT_STATUS, ~0U);
+ return 0;
+
+error:
+ sdhci_write32(sdhci, SDHCI_INT_STATUS, ~0U);
+
sdhci_reset(sdhci, SDHCI_RESET_CMD);
sdhci_reset(sdhci, SDHCI_RESET_DATA);
- return status & SDHCI_INT_TIMEOUT ? -ETIMEDOUT : -ECOMM;
+ return status;
}
static void at91_sdhci_set_power(struct at91_sdhci *host, unsigned vdd)
@@ -214,7 +228,7 @@ static int at91_sdhci_set_clock(struct at91_sdhci *host, unsigned clock)
!(reg & present_mask),
100 * USEC_PER_MSEC);
if (ret) {
- pr_warn("Timeout waiting for CMD and DAT Inhibit bits\n");
+ dev_warn(host->dev, "Timeout waiting for CMD and DAT Inhibit bits\n");
return ret;
}
@@ -258,7 +272,7 @@ static int at91_sdhci_set_clock(struct at91_sdhci *host, unsigned clock)
clk & SDHCI_INTCLOCK_STABLE,
20 * USEC_PER_MSEC);
if (ret) {
- pr_warn("Timeout waiting for clock stable\n");
+ dev_warn(host->dev, "Timeout waiting for clock stable\n");
return ret;
}
diff --git a/drivers/mci/atmel-sdhci-pbl.c b/drivers/mci/atmel-sdhci-pbl.c
new file mode 100644
index 0000000000..626e4008fe
--- /dev/null
+++ b/drivers/mci/atmel-sdhci-pbl.c
@@ -0,0 +1,128 @@
+// SPDX-License-Identifier: BSD-1-Clause
+/*
+ * Copyright (c) 2015, Atmel Corporation
+ * Copyright (c) 2019, Ahmad Fatoum, Pengutronix
+ *
+ * Atmel's name may not be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ */
+
+#include <common.h>
+#include <pbl.h>
+#include <mci.h>
+#include <debug_ll.h>
+#include <mach/xload.h>
+#include "atmel-sdhci.h"
+
+#include <mach/early_udelay.h>
+
+#ifdef __PBL__
+#define udelay early_udelay
+#endif
+
+#define SECTOR_SIZE 512
+#define SUPPORT_MAX_BLOCKS 16U
+
+struct at91_sdhci_priv {
+ struct at91_sdhci host;
+ bool highcapacity_card;
+};
+
+static int sd_cmd_stop_transmission(struct at91_sdhci_priv *priv)
+{
+ struct mci_cmd cmd = {
+ .cmdidx = MMC_CMD_STOP_TRANSMISSION,
+ .resp_type = MMC_RSP_R1b,
+ };
+
+ return at91_sdhci_send_command(&priv->host, &cmd, NULL);
+}
+
+static int sd_cmd_read_multiple_block(struct at91_sdhci_priv *priv,
+ void *buf,
+ unsigned int start,
+ unsigned int block_count)
+{
+ u16 block_len = SECTOR_SIZE;
+ struct mci_data data;
+ struct mci_cmd cmd = {
+ .cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK,
+ .resp_type = MMC_RSP_R1,
+ .cmdarg = start,
+ };
+
+ if (!priv->highcapacity_card)
+ cmd.cmdarg *= block_len;
+
+ data.dest = buf;
+ data.flags = MMC_DATA_READ;
+ data.blocksize = block_len;
+ data.blocks = block_count;
+
+ return at91_sdhci_send_command(&priv->host, &cmd, &data);
+}
+
+static int at91_sdhci_bio_read(struct pbl_bio *bio, off_t start,
+ void *buf, unsigned int nblocks)
+{
+ struct at91_sdhci_priv *priv = bio->priv;
+ unsigned int blocks_done = 0;
+ unsigned int blocks;
+ unsigned int block_len = SECTOR_SIZE;
+ unsigned int blocks_read;
+ int ret;
+
+ /*
+ * Refer to the at91sam9g20 datasheet:
+ * Figure 35-10. Read Function Flow Diagram
+ */
+
+ while (blocks_done < nblocks) {
+ blocks = min(nblocks - blocks_done, SUPPORT_MAX_BLOCKS);
+
+ blocks_read = sd_cmd_read_multiple_block(priv, buf,
+ start + blocks_done,
+ blocks);
+
+ ret = sd_cmd_stop_transmission(priv);
+ if (ret)
+ return ret;
+
+ blocks_done += blocks_read;
+
+ if (blocks_read != blocks)
+ break;
+
+ buf += blocks * block_len;
+ }
+
+ return blocks_done;
+}
+
+static struct at91_sdhci_priv atmel_sdcard;
+
+int at91_sdhci_bio_init(struct pbl_bio *bio, void __iomem *base)
+{
+ struct at91_sdhci_priv *priv = &atmel_sdcard;
+ struct at91_sdhci *host = &priv->host;
+ struct mci_ios ios = { .bus_width = MMC_BUS_WIDTH_1, .clock = 25000000 };
+ int ret;
+
+ bio->priv = priv;
+ bio->read = at91_sdhci_bio_read;
+
+ at91_sdhci_mmio_init(host, base);
+
+ sdhci_reset(&host->sdhci, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
+
+ ret = at91_sdhci_init(host, 240000000, true, true);
+ if (ret)
+ return ret;
+
+ ret = at91_sdhci_set_ios(host, &ios);
+
+ // FIXME can we determine this without leaving SD transfer mode?
+ priv->highcapacity_card = 1;
+
+ return 0;
+}
diff --git a/drivers/mci/atmel-sdhci.c b/drivers/mci/atmel-sdhci.c
index 6351186476..59cd002aae 100644
--- a/drivers/mci/atmel-sdhci.c
+++ b/drivers/mci/atmel-sdhci.c
@@ -46,7 +46,9 @@ static int at91_sdhci_mci_init(struct mci_host *mci, struct device_d *dev)
struct sdhci *sdhci = &priv->host.sdhci;
int ret;
- ret = sdhci_reset(sdhci, SDHCI_RESET_ALL);
+ priv->host.dev = dev;
+
+ ret = sdhci_reset(sdhci, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
if (ret)
return ret;
diff --git a/drivers/mci/atmel-sdhci.h b/drivers/mci/atmel-sdhci.h
index 897ed4e4de..7032294647 100644
--- a/drivers/mci/atmel-sdhci.h
+++ b/drivers/mci/atmel-sdhci.h
@@ -11,6 +11,7 @@
struct at91_sdhci {
struct sdhci sdhci;
+ struct device_d *dev;
void __iomem *base;
u32 caps_max_clock;
};
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index d9734ef588..cf83b6a15b 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -22,6 +22,12 @@ config WATCHDOG_AR9344
help
Add support for watchdog on the QCA AR9344 SoC.
+config WATCHDOG_AT91SAM9
+ bool "Watchdog for AT91SAM9 and SAMA5 SoCs"
+ depends on ARCH_AT91
+ help
+ Support for the watchdog in AT91SAM9X and SAMA5D{2,3,4} SoCs.
+
config WATCHDOG_EFI
bool "Generic EFI Watchdog Driver"
depends on EFI_BOOTUP
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 3af64db3f2..dc9842770a 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_AT91SAM9) += at91sam9_wdt.o
obj-$(CONFIG_WATCHDOG_EFI) += efi_wdt.o
obj-$(CONFIG_WATCHDOG_DAVINCI) += davinci_wdt.o
obj-$(CONFIG_WATCHDOG_OMAP) += omap_wdt.o
diff --git a/drivers/watchdog/at91sam9_wdt.c b/drivers/watchdog/at91sam9_wdt.c
new file mode 100644
index 0000000000..3f554bf47b
--- /dev/null
+++ b/drivers/watchdog/at91sam9_wdt.c
@@ -0,0 +1,109 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 Pengutronix, Ahmad Fatoum <a.fatoum@pengutronix.de>
+ */
+
+#include <common.h>
+#include <init.h>
+#include <io.h>
+#include <watchdog.h>
+#include <linux/clk.h>
+#include <mach/at91_wdt.h>
+
+#define MIN_WDT_TIMEOUT 1
+#define MAX_WDT_TIMEOUT 16
+#define SECS_TO_WDOG_TICKS(s) ((s) ? (((s) << 8) - 1) : 0)
+
+struct at91sam9x_wdt {
+ struct watchdog wdd;
+ void __iomem *base;
+};
+
+static inline void at91sam9x_wdt_ping(struct at91sam9x_wdt *wdt)
+{
+ writel(AT91_WDT_WDRSTT | AT91_WDT_KEY, wdt->base + AT91_WDT_CR);
+}
+
+static int at91sam9x_wdt_set_timeout(struct watchdog *wdd, unsigned timeout)
+{
+ struct at91sam9x_wdt *wdt = container_of(wdd, struct at91sam9x_wdt, wdd);
+ u32 mr_old, mr_new;
+
+ mr_old = readl(wdt->base + AT91_WDT_MR);
+
+ if (!timeout) {
+ mr_new = mr_old | AT91_WDT_WDDIS;
+ writel(mr_new, wdt->base + AT91_WDT_MR);
+ return 0;
+ }
+
+ mr_new = AT91_WDT_WDRSTEN
+ | AT91_WDT_WDDBGHLT | AT91_WDT_WDIDLEHLT
+ | AT91_WDT_WDD
+ | (SECS_TO_WDOG_TICKS(timeout) & AT91_WDT_WDV);
+
+ if (mr_new != mr_old)
+ writel(mr_new, wdt->base + AT91_WDT_MR);
+
+ at91sam9x_wdt_ping(wdt);
+ return 0;
+}
+
+static inline bool at91sam9x_wdt_is_disabled(struct at91sam9x_wdt *wdt)
+{
+ return readl(wdt->base + AT91_WDT_MR) & AT91_WDT_WDDIS;
+}
+
+static int at91sam9x_wdt_probe(struct device_d *dev)
+{
+ struct at91sam9x_wdt *wdt;
+ struct resource *iores;
+ struct clk *clk;
+ int ret;
+
+ wdt = xzalloc(sizeof(*wdt));
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores)) {
+ dev_err(dev, "could not get watchdog memory region\n");
+ return PTR_ERR(iores);
+ }
+ wdt->base = IOMEM(iores->start);
+ clk = clk_get(dev, NULL);
+ if (WARN_ON(IS_ERR(clk)))
+ return PTR_ERR(clk);
+
+ clk_enable(clk);
+
+ wdt->wdd.set_timeout = at91sam9x_wdt_set_timeout;
+ wdt->wdd.timeout_max = MAX_WDT_TIMEOUT;
+ wdt->wdd.hwdev = dev;
+
+ if (at91sam9x_wdt_is_disabled(wdt))
+ wdt->wdd.running = WDOG_HW_NOT_RUNNING;
+ else
+ wdt->wdd.running = WDOG_HW_RUNNING;
+
+ ret = watchdog_register(&wdt->wdd);
+ if (ret)
+ free(wdt);
+
+ return ret;
+}
+
+static const __maybe_unused struct of_device_id at91sam9x_wdt_dt_ids[] = {
+ { .compatible = "atmel,at91sam9260-wdt", },
+ { .compatible = "atmel,sama5d4-wdt", },
+ { /* sentinel */ },
+};
+
+static struct driver_d at91sam9x_wdt_driver = {
+ .name = "at91sam9x-wdt",
+ .of_compatible = DRV_OF_COMPAT(at91sam9x_wdt_dt_ids),
+ .probe = at91sam9x_wdt_probe,
+};
+
+static int __init at91sam9x_wdt_init(void)
+{
+ return platform_driver_register(&at91sam9x_wdt_driver);
+}
+device_initcall(at91sam9x_wdt_init);