summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2017-07-12 12:16:41 (GMT)
committerTom Rini <trini@konsulko.com>2017-07-12 12:16:41 (GMT)
commite14b1169c0c5a1b362b41451006b492bd16f2fff (patch)
treeb98045698d6fd3173d44a5b6efe1df0ff604c982 /drivers
parent409a81ddd46f444c2262bb10c921efc6967feff2 (diff)
parent172b2e0b561a50a3d8c4275be4ed24b0c8e18896 (diff)
downloadu-boot-e14b1169c0c5a1b362b41451006b492bd16f2fff.tar.xz
Merge git://www.denx.de/git/u-boot-marvell
Diffstat (limited to 'drivers')
-rw-r--r--drivers/ddr/marvell/a38x/ddr3_training.c6
-rw-r--r--drivers/ddr/marvell/a38x/ddr_topology_def.h9
-rw-r--r--drivers/i2c/muxes/pca954x.c42
-rw-r--r--drivers/misc/Kconfig8
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/atsha204a-i2c.c408
-rw-r--r--drivers/watchdog/Kconfig7
-rw-r--r--drivers/watchdog/Makefile1
-rw-r--r--drivers/watchdog/orion_wdt.c177
9 files changed, 656 insertions, 3 deletions
diff --git a/drivers/ddr/marvell/a38x/ddr3_training.c b/drivers/ddr/marvell/a38x/ddr3_training.c
index 7e0749f..e70ca4b 100644
--- a/drivers/ddr/marvell/a38x/ddr3_training.c
+++ b/drivers/ddr/marvell/a38x/ddr3_training.c
@@ -308,6 +308,7 @@ int hws_ddr3_tip_init_controller(u32 dev_num, struct init_cntr_param *init_cntr_
enum hws_speed_bin speed_bin_index = SPEED_BIN_DDR_2133N;
enum hws_mem_size memory_size = MEM_2G;
enum hws_ddr_freq freq = init_freq;
+ enum hws_timing timing;
u32 cs_mask = 0;
u32 cl_value = 0, cwl_val = 0;
u32 refresh_interval_cnt = 0, bus_cnt = 0, adll_tap = 0;
@@ -569,8 +570,13 @@ int hws_ddr3_tip_init_controller(u32 dev_num, struct init_cntr_param *init_cntr_
DUNIT_CONTROL_HIGH_REG,
(init_cntr_prm->msys_init << 7), (1 << 7)));
+ timing = tm->interface_params[if_id].timing;
+
if (mode2_t != 0xff) {
t2t = mode2_t;
+ } else if (timing != HWS_TIM_DEFAULT) {
+ /* Board topology map is forcing timing */
+ t2t = (timing == HWS_TIM_2T) ? 1 : 0;
} else {
/* calculate number of CS (per interface) */
CHECK_STATUS(calc_cs_num
diff --git a/drivers/ddr/marvell/a38x/ddr_topology_def.h b/drivers/ddr/marvell/a38x/ddr_topology_def.h
index f8894e8..229c3a1 100644
--- a/drivers/ddr/marvell/a38x/ddr_topology_def.h
+++ b/drivers/ddr/marvell/a38x/ddr_topology_def.h
@@ -37,6 +37,12 @@ enum hws_mem_size {
MEM_SIZE_LAST
};
+enum hws_timing {
+ HWS_TIM_DEFAULT,
+ HWS_TIM_1T,
+ HWS_TIM_2T
+};
+
struct bus_params {
/* Chip Select (CS) bitmask (bits 0-CS0, bit 1- CS1 ...) */
u8 cs_bitmask;
@@ -84,6 +90,9 @@ struct if_params {
/* operation temperature */
enum hws_temperature interface_temp;
+
+ /* 2T vs 1T mode (by default computed from number of CSs) */
+ enum hws_timing timing;
};
struct hws_topology_map {
diff --git a/drivers/i2c/muxes/pca954x.c b/drivers/i2c/muxes/pca954x.c
index 1a67618..383f72f 100644
--- a/drivers/i2c/muxes/pca954x.c
+++ b/drivers/i2c/muxes/pca954x.c
@@ -13,11 +13,40 @@
DECLARE_GLOBAL_DATA_PTR;
+enum pca_type {
+ PCA9544,
+ PCA9547,
+ PCA9548
+};
+
+struct chip_desc {
+ u8 enable;
+ enum muxtype {
+ pca954x_ismux = 0,
+ pca954x_isswi,
+ } muxtype;
+};
+
struct pca954x_priv {
u32 addr; /* I2C mux address */
u32 width; /* I2C mux width - number of busses */
};
+static const struct chip_desc chips[] = {
+ [PCA9544] = {
+ .enable = 0x4,
+ .muxtype = pca954x_ismux,
+ },
+ [PCA9547] = {
+ .enable = 0x8,
+ .muxtype = pca954x_ismux,
+ },
+ [PCA9548] = {
+ .enable = 0x8,
+ .muxtype = pca954x_isswi,
+ },
+};
+
static int pca954x_deselect(struct udevice *mux, struct udevice *bus,
uint channel)
{
@@ -31,7 +60,13 @@ static int pca954x_select(struct udevice *mux, struct udevice *bus,
uint channel)
{
struct pca954x_priv *priv = dev_get_priv(mux);
- uchar byte = 1 << channel;
+ const struct chip_desc *chip = &chips[dev_get_driver_data(mux)];
+ uchar byte;
+
+ if (chip->muxtype == pca954x_ismux)
+ byte = channel | chip->enable;
+ else
+ byte = 1 << channel;
return dm_i2c_write(mux, priv->addr, &byte, 1);
}
@@ -42,8 +77,9 @@ static const struct i2c_mux_ops pca954x_ops = {
};
static const struct udevice_id pca954x_ids[] = {
- { .compatible = "nxp,pca9548", .data = (ulong)8 },
- { .compatible = "nxp,pca9544", .data = (ulong)4 },
+ { .compatible = "nxp,pca9544", .data = PCA9544 },
+ { .compatible = "nxp,pca9547", .data = PCA9547 },
+ { .compatible = "nxp,pca9548", .data = PCA9548 },
{ }
};
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 92f348f..d1ddbbe 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -20,6 +20,14 @@ config ALTERA_SYSID
Select this to enable a sysid for Altera devices. Please find
details on the "Embedded Peripherals IP User Guide" of Altera.
+config ATSHA204A
+ bool "Support for Atmel ATSHA204A module"
+ depends on MISC
+ help
+ Enable support for I2C connected Atmel's ATSHA204A
+ CryptoAuthentication module found for example on the Turris Omnia
+ board.
+
config ROCKCHIP_EFUSE
bool "Rockchip e-fuse support"
depends on MISC
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index ea64677..10265c8 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -8,6 +8,7 @@
obj-$(CONFIG_MISC) += misc-uclass.o
obj-$(CONFIG_ALI152X) += ali512x.o
obj-$(CONFIG_ALTERA_SYSID) += altera_sysid.o
+obj-$(CONFIG_ATSHA204A) += atsha204a-i2c.o
obj-$(CONFIG_DS4510) += ds4510.o
obj-$(CONFIG_CBMEM_CONSOLE) += cbmem_console.o
ifndef CONFIG_SPL_BUILD
diff --git a/drivers/misc/atsha204a-i2c.c b/drivers/misc/atsha204a-i2c.c
new file mode 100644
index 0000000..934ba5e
--- /dev/null
+++ b/drivers/misc/atsha204a-i2c.c
@@ -0,0 +1,408 @@
+/*
+ * I2C Driver for Atmel ATSHA204 over I2C
+ *
+ * Copyright (C) 2014 Josh Datko, Cryptotronix, jbd@cryptotronix.com
+ * 2016 Tomas Hlavacek, CZ.NIC, tmshlvck@gmail.com
+ * 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <i2c.h>
+#include <errno.h>
+#include <atsha204a-i2c.h>
+
+#define ATSHA204A_TWLO 60
+#define ATSHA204A_TRANSACTION_TIMEOUT 100000
+#define ATSHA204A_TRANSACTION_RETRY 5
+#define ATSHA204A_EXECTIME 5000
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/*
+ * The ATSHA204A uses an (to me) unknown CRC-16 algorithm.
+ * The Reveng CRC-16 catalogue does not contain it.
+ *
+ * Because in Atmel's documentation only a primitive implementation
+ * can be found, I have implemented this one with lookup table.
+ */
+
+/*
+ * This is the code that computes the table below:
+ *
+ * int i, j;
+ * for (i = 0; i < 256; ++i) {
+ * u8 c = 0;
+ * for (j = 0; j < 8; ++j) {
+ * c = (c << 1) | ((i >> j) & 1);
+ * }
+ * bitreverse_table[i] = c;
+ * }
+ */
+
+static u8 const bitreverse_table[256] = {
+ 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
+ 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
+ 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
+ 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
+ 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
+ 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
+ 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
+ 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
+ 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
+ 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
+ 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
+ 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
+ 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
+ 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
+ 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
+ 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
+ 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
+ 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
+ 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
+ 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
+ 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
+ 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
+ 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
+ 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
+ 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
+ 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
+ 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
+ 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
+ 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
+ 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
+ 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
+ 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
+};
+
+/*
+ * This is the code that computes the table below:
+ *
+ * int i, j;
+ * for (i = 0; i < 256; ++i) {
+ * u16 c = i << 8;
+ * for (j = 0; j < 8; ++j) {
+ * int b = c >> 15;
+ * c <<= 1;
+ * if (b)
+ * c ^= 0x8005;
+ * }
+ * crc16_table[i] = c;
+ * }
+ */
+static u16 const crc16_table[256] = {
+ 0x0000, 0x8005, 0x800f, 0x000a, 0x801b, 0x001e, 0x0014, 0x8011,
+ 0x8033, 0x0036, 0x003c, 0x8039, 0x0028, 0x802d, 0x8027, 0x0022,
+ 0x8063, 0x0066, 0x006c, 0x8069, 0x0078, 0x807d, 0x8077, 0x0072,
+ 0x0050, 0x8055, 0x805f, 0x005a, 0x804b, 0x004e, 0x0044, 0x8041,
+ 0x80c3, 0x00c6, 0x00cc, 0x80c9, 0x00d8, 0x80dd, 0x80d7, 0x00d2,
+ 0x00f0, 0x80f5, 0x80ff, 0x00fa, 0x80eb, 0x00ee, 0x00e4, 0x80e1,
+ 0x00a0, 0x80a5, 0x80af, 0x00aa, 0x80bb, 0x00be, 0x00b4, 0x80b1,
+ 0x8093, 0x0096, 0x009c, 0x8099, 0x0088, 0x808d, 0x8087, 0x0082,
+ 0x8183, 0x0186, 0x018c, 0x8189, 0x0198, 0x819d, 0x8197, 0x0192,
+ 0x01b0, 0x81b5, 0x81bf, 0x01ba, 0x81ab, 0x01ae, 0x01a4, 0x81a1,
+ 0x01e0, 0x81e5, 0x81ef, 0x01ea, 0x81fb, 0x01fe, 0x01f4, 0x81f1,
+ 0x81d3, 0x01d6, 0x01dc, 0x81d9, 0x01c8, 0x81cd, 0x81c7, 0x01c2,
+ 0x0140, 0x8145, 0x814f, 0x014a, 0x815b, 0x015e, 0x0154, 0x8151,
+ 0x8173, 0x0176, 0x017c, 0x8179, 0x0168, 0x816d, 0x8167, 0x0162,
+ 0x8123, 0x0126, 0x012c, 0x8129, 0x0138, 0x813d, 0x8137, 0x0132,
+ 0x0110, 0x8115, 0x811f, 0x011a, 0x810b, 0x010e, 0x0104, 0x8101,
+ 0x8303, 0x0306, 0x030c, 0x8309, 0x0318, 0x831d, 0x8317, 0x0312,
+ 0x0330, 0x8335, 0x833f, 0x033a, 0x832b, 0x032e, 0x0324, 0x8321,
+ 0x0360, 0x8365, 0x836f, 0x036a, 0x837b, 0x037e, 0x0374, 0x8371,
+ 0x8353, 0x0356, 0x035c, 0x8359, 0x0348, 0x834d, 0x8347, 0x0342,
+ 0x03c0, 0x83c5, 0x83cf, 0x03ca, 0x83db, 0x03de, 0x03d4, 0x83d1,
+ 0x83f3, 0x03f6, 0x03fc, 0x83f9, 0x03e8, 0x83ed, 0x83e7, 0x03e2,
+ 0x83a3, 0x03a6, 0x03ac, 0x83a9, 0x03b8, 0x83bd, 0x83b7, 0x03b2,
+ 0x0390, 0x8395, 0x839f, 0x039a, 0x838b, 0x038e, 0x0384, 0x8381,
+ 0x0280, 0x8285, 0x828f, 0x028a, 0x829b, 0x029e, 0x0294, 0x8291,
+ 0x82b3, 0x02b6, 0x02bc, 0x82b9, 0x02a8, 0x82ad, 0x82a7, 0x02a2,
+ 0x82e3, 0x02e6, 0x02ec, 0x82e9, 0x02f8, 0x82fd, 0x82f7, 0x02f2,
+ 0x02d0, 0x82d5, 0x82df, 0x02da, 0x82cb, 0x02ce, 0x02c4, 0x82c1,
+ 0x8243, 0x0246, 0x024c, 0x8249, 0x0258, 0x825d, 0x8257, 0x0252,
+ 0x0270, 0x8275, 0x827f, 0x027a, 0x826b, 0x026e, 0x0264, 0x8261,
+ 0x0220, 0x8225, 0x822f, 0x022a, 0x823b, 0x023e, 0x0234, 0x8231,
+ 0x8213, 0x0216, 0x021c, 0x8219, 0x0208, 0x820d, 0x8207, 0x0202,
+};
+
+static inline u16 crc16_byte(u16 crc, const u8 data)
+{
+ u16 t = crc16_table[((crc >> 8) ^ bitreverse_table[data]) & 0xff];
+ return ((crc << 8) ^ t);
+}
+
+static u16 atsha204a_crc16(const u8 *buffer, size_t len)
+{
+ u16 crc = 0;
+
+ while (len--)
+ crc = crc16_byte(crc, *buffer++);
+
+ return cpu_to_le16(crc);
+}
+
+static int atsha204a_send(struct udevice *dev, const u8 *buf, u8 len)
+{
+ fdt_addr_t *priv = dev_get_priv(dev);
+ struct i2c_msg msg;
+
+ msg.addr = *priv;
+ msg.flags = I2C_M_STOP;
+ msg.len = len;
+ msg.buf = (u8 *) buf;
+
+ return dm_i2c_xfer(dev, &msg, 1);
+}
+
+static int atsha204a_recv(struct udevice *dev, u8 *buf, u8 len)
+{
+ fdt_addr_t *priv = dev_get_priv(dev);
+ struct i2c_msg msg;
+
+ msg.addr = *priv;
+ msg.flags = I2C_M_RD | I2C_M_STOP;
+ msg.len = len;
+ msg.buf = (u8 *) buf;
+
+ return dm_i2c_xfer(dev, &msg, 1);
+}
+
+static int atsha204a_recv_resp(struct udevice *dev,
+ struct atsha204a_resp *resp)
+{
+ int res;
+ u16 resp_crc, computed_crc;
+ u8 *p = (u8 *) resp;
+
+ res = atsha204a_recv(dev, p, 4);
+ if (res)
+ return res;
+
+ if (resp->length > 4) {
+ if (resp->length > sizeof(*resp))
+ return -EMSGSIZE;
+
+ res = atsha204a_recv(dev, p + 4, resp->length - 4);
+ if (res)
+ return res;
+ }
+
+ resp_crc = (u16) p[resp->length - 2]
+ | (((u16) p[resp->length - 1]) << 8);
+ computed_crc = atsha204a_crc16(p, resp->length - 2);
+
+ if (resp_crc != computed_crc) {
+ debug("Invalid checksum in ATSHA204A response\n");
+ return -EBADMSG;
+ }
+
+ return 0;
+}
+
+int atsha204a_wakeup(struct udevice *dev)
+{
+ u8 req[4];
+ struct atsha204a_resp resp;
+ int try, res;
+
+ debug("Waking up ATSHA204A\n");
+
+ for (try = 1; try <= 10; ++try) {
+ debug("Try %i... ", try);
+
+ memset(req, 0, 4);
+ res = atsha204a_send(dev, req, 4);
+ if (res) {
+ debug("failed on I2C send, trying again\n");
+ continue;
+ }
+
+ udelay(ATSHA204A_TWLO);
+
+ res = atsha204a_recv_resp(dev, &resp);
+ if (res) {
+ debug("failed on receiving response, ending\n");
+ return res;
+ }
+
+ if (resp.code != ATSHA204A_STATUS_AFTER_WAKE) {
+ debug ("failed (responce code = %02x), ending\n",
+ resp.code);
+ return -EBADMSG;
+ }
+
+ debug("success\n");
+ break;
+ }
+
+ return 0;
+}
+
+int atsha204a_idle(struct udevice *dev)
+{
+ int res;
+ u8 req = ATSHA204A_FUNC_IDLE;
+
+ res = atsha204a_send(dev, &req, 1);
+ if (res)
+ debug("Failed putting ATSHA204A idle\n");
+ return res;
+}
+
+int atsha204a_sleep(struct udevice *dev)
+{
+ int res;
+ u8 req = ATSHA204A_FUNC_IDLE;
+
+ res = atsha204a_send(dev, &req, 1);
+ if (res)
+ debug("Failed putting ATSHA204A to sleep\n");
+ return res;
+}
+
+static int atsha204a_transaction(struct udevice *dev, struct atsha204a_req *req,
+ struct atsha204a_resp *resp)
+{
+ int res, timeout = ATSHA204A_TRANSACTION_TIMEOUT;
+
+ res = atsha204a_send(dev, (u8 *) req, req->length + 1);
+ if (res) {
+ debug("ATSHA204A transaction send failed\n");
+ return -EBUSY;
+ }
+
+ do {
+ res = atsha204a_recv_resp(dev, resp);
+ if (!res || res == -EMSGSIZE || res == -EBADMSG)
+ break;
+
+ debug("ATSHA204A transaction polling for response "
+ "(timeout = %d)\n", timeout);
+
+ udelay(ATSHA204A_EXECTIME);
+ timeout -= ATSHA204A_EXECTIME;
+ } while (timeout > 0);
+
+ if (timeout <= 0) {
+ debug("ATSHA204A transaction timed out\n");
+ return -ETIMEDOUT;
+ }
+
+ return res;
+}
+
+static void atsha204a_req_crc32(struct atsha204a_req *req)
+{
+ u8 *p = (u8 *) req;
+ u16 computed_crc;
+ u16 *crc_ptr = (u16 *) &p[req->length - 1];
+
+ /* The buffer to crc16 starts at byte 1, not 0 */
+ computed_crc = atsha204a_crc16(p + 1, req->length - 2);
+
+ *crc_ptr = cpu_to_le16(computed_crc);
+}
+
+int atsha204a_read(struct udevice *dev, enum atsha204a_zone zone, bool read32,
+ u16 addr, u8 *buffer)
+{
+ int res, retry = ATSHA204A_TRANSACTION_RETRY;
+ struct atsha204a_req req;
+ struct atsha204a_resp resp;
+
+ req.function = ATSHA204A_FUNC_COMMAND;
+ req.length = 7;
+ req.command = ATSHA204A_CMD_READ;
+
+ req.param1 = (u8) zone;
+ if (read32)
+ req.param1 |= 0x80;
+
+ req.param2 = cpu_to_le16(addr);
+
+ atsha204a_req_crc32(&req);
+
+ do {
+ res = atsha204a_transaction(dev, &req, &resp);
+ if (!res)
+ break;
+
+ debug("ATSHA204A read retry (%d)\n", retry);
+ retry--;
+ atsha204a_wakeup(dev);
+ } while (retry >= 0);
+
+ if (res) {
+ debug("ATSHA204A read failed\n");
+ return res;
+ }
+
+ if (resp.length != (read32 ? 32 : 4) + 3) {
+ debug("ATSHA204A read bad response length (%d)\n",
+ resp.length);
+ return -EBADMSG;
+ }
+
+ memcpy(buffer, ((u8 *) &resp) + 1, read32 ? 32 : 4);
+
+ return 0;
+}
+
+int atsha204a_get_random(struct udevice *dev, u8 *buffer, size_t max)
+{
+ int res;
+ struct atsha204a_req req;
+ struct atsha204a_resp resp;
+
+ req.function = ATSHA204A_FUNC_COMMAND;
+ req.length = 7;
+ req.command = ATSHA204A_CMD_RANDOM;
+
+ req.param1 = 1;
+ req.param2 = 0;
+
+ /* We do not have to compute the checksum dynamically */
+ req.data[0] = 0x27;
+ req.data[1] = 0x47;
+
+ res = atsha204a_transaction(dev, &req, &resp);
+ if (res) {
+ debug("ATSHA204A random transaction failed\n");
+ return res;
+ }
+
+ memcpy(buffer, ((u8 *) &resp) + 1, max >= 32 ? 32 : max);
+ return 0;
+}
+
+static int atsha204a_ofdata_to_platdata(struct udevice *dev)
+{
+ fdt_addr_t *priv = dev_get_priv(dev);
+ fdt_addr_t addr;
+
+ addr = fdtdec_get_addr(gd->fdt_blob, dev_of_offset(dev), "reg");
+ if (addr == FDT_ADDR_T_NONE) {
+ debug("Can't get ATSHA204A I2C base address\n");
+ return -ENXIO;
+ }
+
+ *priv = addr;
+ return 0;
+}
+
+static const struct udevice_id atsha204a_ids[] = {
+ { .compatible = "atmel,atsha204a" },
+ { }
+};
+
+U_BOOT_DRIVER(atsha204) = {
+ .name = "atsha204",
+ .id = UCLASS_MISC,
+ .of_match = atsha204a_ids,
+ .ofdata_to_platdata = atsha204a_ofdata_to_platdata,
+ .priv_auto_alloc_size = sizeof(fdt_addr_t),
+};
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index b911233..d360a17 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -62,4 +62,11 @@ config WDT_BCM6345
The watchdog timer is stopped when initialized.
It performs full SoC reset.
+config WDT_ORION
+ bool "Orion watchdog timer support"
+ depends on WDT
+ help
+ Select this to enable Orion watchdog timer, which can be found on some
+ Marvell Armada chips.
+
endmenu
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 4b19e4c..3230cbb 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -20,3 +20,4 @@ obj-$(CONFIG_WDT_SANDBOX) += sandbox_wdt.o
obj-$(CONFIG_WDT_ASPEED) += ast_wdt.o
obj-$(CONFIG_WDT_BCM6345) += bcm6345_wdt.o
obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o
+obj-$(CONFIG_WDT_ORION) += orion_wdt.o
diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c
new file mode 100644
index 0000000..a0df02d
--- /dev/null
+++ b/drivers/watchdog/orion_wdt.c
@@ -0,0 +1,177 @@
+/*
+ * drivers/watchdog/orion_wdt.c
+ *
+ * Watchdog driver for Orion/Kirkwood processors
+ *
+ * Authors: Tomas Hlavacek <tmshlvck@gmail.com>
+ * Sylver Bruneau <sylver.bruneau@googlemail.com>
+ * Marek Behun <marek.behun@nic.cz>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <wdt.h>
+#include <asm/io.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/soc.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct orion_wdt_priv {
+ void __iomem *reg;
+ int wdt_counter_offset;
+ void __iomem *rstout;
+ void __iomem *rstout_mask;
+ u32 timeout;
+};
+
+#define RSTOUT_ENABLE_BIT BIT(8)
+#define RSTOUT_MASK_BIT BIT(10)
+#define WDT_ENABLE_BIT BIT(8)
+
+#define TIMER_CTRL 0x0000
+#define TIMER_A370_STATUS 0x04
+
+#define WDT_AXP_FIXED_ENABLE_BIT BIT(10)
+#define WDT_A370_EXPIRED BIT(31)
+
+static int orion_wdt_reset(struct udevice *dev)
+{
+ struct orion_wdt_priv *priv = dev_get_priv(dev);
+
+ /* Reload watchdog duration */
+ writel(priv->timeout, priv->reg + priv->wdt_counter_offset);
+
+ return 0;
+}
+
+static int orion_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
+{
+ struct orion_wdt_priv *priv = dev_get_priv(dev);
+ u32 reg;
+
+ priv->timeout = (u32) timeout;
+
+ /* Enable the fixed watchdog clock input */
+ reg = readl(priv->reg + TIMER_CTRL);
+ reg |= WDT_AXP_FIXED_ENABLE_BIT;
+ writel(reg, priv->reg + TIMER_CTRL);
+
+ /* Set watchdog duration */
+ writel(priv->timeout, priv->reg + priv->wdt_counter_offset);
+
+ /* Clear the watchdog expiration bit */
+ reg = readl(priv->reg + TIMER_A370_STATUS);
+ reg &= ~WDT_A370_EXPIRED;
+ writel(reg, priv->reg + TIMER_A370_STATUS);
+
+ /* Enable watchdog timer */
+ reg = readl(priv->reg + TIMER_CTRL);
+ reg |= WDT_ENABLE_BIT;
+ writel(reg, priv->reg + TIMER_CTRL);
+
+ /* Enable reset on watchdog */
+ reg = readl(priv->rstout);
+ reg |= RSTOUT_ENABLE_BIT;
+ writel(reg, priv->rstout);
+
+ reg = readl(priv->rstout_mask);
+ reg &= ~RSTOUT_MASK_BIT;
+ writel(reg, priv->rstout_mask);
+
+ return 0;
+}
+
+static int orion_wdt_stop(struct udevice *dev)
+{
+ struct orion_wdt_priv *priv = dev_get_priv(dev);
+ u32 reg;
+
+ /* Disable reset on watchdog */
+ reg = readl(priv->rstout_mask);
+ reg |= RSTOUT_MASK_BIT;
+ writel(reg, priv->rstout_mask);
+
+ reg = readl(priv->rstout);
+ reg &= ~RSTOUT_ENABLE_BIT;
+ writel(reg, priv->rstout);
+
+ /* Disable watchdog timer */
+ reg = readl(priv->reg + TIMER_CTRL);
+ reg &= ~WDT_ENABLE_BIT;
+ writel(reg, priv->reg + TIMER_CTRL);
+
+ return 0;
+}
+
+static inline bool save_reg_from_ofdata(struct udevice *dev, int index,
+ void __iomem **reg, int *offset)
+{
+ fdt_addr_t addr;
+ fdt_size_t off;
+
+ addr = fdtdec_get_addr_size_auto_noparent(
+ gd->fdt_blob, dev_of_offset(dev), "reg", index, &off, true);
+
+ if (addr == FDT_ADDR_T_NONE)
+ return false;
+
+ *reg = (void __iomem *) addr;
+ if (offset)
+ *offset = off;
+
+ return true;
+}
+
+static int orion_wdt_ofdata_to_platdata(struct udevice *dev)
+{
+ struct orion_wdt_priv *priv = dev_get_priv(dev);
+
+ if (!save_reg_from_ofdata(dev, 0, &priv->reg,
+ &priv->wdt_counter_offset))
+ goto err;
+
+ if (!save_reg_from_ofdata(dev, 1, &priv->rstout, NULL))
+ goto err;
+
+ if (!save_reg_from_ofdata(dev, 2, &priv->rstout_mask, NULL))
+ goto err;
+
+ return 0;
+err:
+ debug("%s: Could not determine Orion wdt IO addresses\n", __func__);
+ return -ENXIO;
+}
+
+static int orion_wdt_probe(struct udevice *dev)
+{
+ debug("%s: Probing wdt%u\n", __func__, dev->seq);
+ orion_wdt_stop(dev);
+
+ return 0;
+}
+
+static const struct wdt_ops orion_wdt_ops = {
+ .start = orion_wdt_start,
+ .reset = orion_wdt_reset,
+ .stop = orion_wdt_stop,
+};
+
+static const struct udevice_id orion_wdt_ids[] = {
+ { .compatible = "marvell,armada-380-wdt" },
+ {}
+};
+
+U_BOOT_DRIVER(orion_wdt) = {
+ .name = "orion_wdt",
+ .id = UCLASS_WDT,
+ .of_match = orion_wdt_ids,
+ .probe = orion_wdt_probe,
+ .priv_auto_alloc_size = sizeof(struct orion_wdt_priv),
+ .ofdata_to_platdata = orion_wdt_ofdata_to_platdata,
+ .ops = &orion_wdt_ops,
+};