summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/misc/atsha204a-i2c.c90
1 files changed, 61 insertions, 29 deletions
diff --git a/drivers/misc/atsha204a-i2c.c b/drivers/misc/atsha204a-i2c.c
index d3c515828ff..707daa90bdb 100644
--- a/drivers/misc/atsha204a-i2c.c
+++ b/drivers/misc/atsha204a-i2c.c
@@ -21,7 +21,6 @@
#include <linux/bitrev.h>
#include <u-boot/crc.h>
-#define ATSHA204A_TWLO_US 60
#define ATSHA204A_TWHI_US 2500
#define ATSHA204A_TRANSACTION_TIMEOUT 100000
#define ATSHA204A_TRANSACTION_RETRY 5
@@ -34,6 +33,48 @@ static inline u16 atsha204a_crc16(const u8 *buffer, size_t len)
return bitrev16(crc16(0, buffer, len));
}
+static int atsha204a_ping_bus(struct udevice *dev)
+{
+ struct udevice *bus = dev_get_parent(dev);
+ struct i2c_msg msg;
+ int speed;
+ int res;
+ u8 val = 0;
+
+ speed = dm_i2c_get_bus_speed(bus);
+ if (speed != I2C_SPEED_STANDARD_RATE) {
+ int rv;
+
+ rv = dm_i2c_set_bus_speed(bus, I2C_SPEED_STANDARD_RATE);
+ if (rv)
+ debug("Couldn't change the I2C bus speed\n");
+ }
+
+ /*
+ * The I2C drivers don't support sending messages when NAK is received.
+ * This chip requires wake up low signal on SDA for >= 60us.
+ * To achieve this, we slow the bus to 100kHz and send an empty
+ * message to address 0. This will hold the SDA line low for the
+ * required time to wake up the chip.
+ */
+ msg.addr = 0;
+ msg.flags = I2C_M_STOP;
+ msg.len = sizeof(val);
+ msg.buf = &val;
+
+ res = dm_i2c_xfer(dev, &msg, 1);
+
+ if (speed != I2C_SPEED_STANDARD_RATE) {
+ int rv;
+
+ rv = dm_i2c_set_bus_speed(bus, speed);
+ if (rv)
+ debug("Couldn't restore the I2C bus speed\n");
+ }
+
+ return res;
+}
+
static int atsha204a_send(struct udevice *dev, const u8 *buf, u8 len)
{
fdt_addr_t *priv = dev_get_priv(dev);
@@ -94,42 +135,33 @@ static int atsha204a_recv_resp(struct udevice *dev,
int atsha204a_wakeup(struct udevice *dev)
{
- u8 req[4];
struct atsha204a_resp resp;
- int try, res;
+ int res;
debug("Waking up ATSHA204A\n");
- for (try = 1; try <= 10; ++try) {
- debug("Try %i... ", try);
+ /*
+ * The device ignores any levels or transitions on the SCL pin
+ * when the device is idle, asleep or during waking up.
+ * Don't check for error when waking up the device.
+ */
+ atsha204a_ping_bus(dev);
- /*
- * The device ignores any levels or transitions on the SCL pin
- * when the device is idle, asleep or during waking up.
- * Don't check for error when waking up the device.
- */
- memset(req, 0, 4);
- atsha204a_send(dev, req, 4);
-
- udelay(ATSHA204A_TWLO_US + ATSHA204A_TWHI_US);
-
- res = atsha204a_recv_resp(dev, &resp);
- if (res) {
- debug("failed on receiving response, ending\n");
- return res;
- }
+ udelay(ATSHA204A_TWHI_US);
- if (resp.code != ATSHA204A_STATUS_AFTER_WAKE) {
- debug ("failed (responce code = %02x), ending\n",
- resp.code);
- return -EBADMSG;
- }
+ res = atsha204a_recv_resp(dev, &resp);
+ if (res) {
+ debug("failed on receiving response, ending\n");
+ return res;
+ }
- debug("success\n");
- return 0;
+ if (resp.code != ATSHA204A_STATUS_AFTER_WAKE) {
+ debug("failed (response code = %02x), ending\n", resp.code);
+ return -EBADMSG;
}
- return -ETIMEDOUT;
+ debug("success\n");
+ return 0;
}
int atsha204a_idle(struct udevice *dev)
@@ -146,7 +178,7 @@ int atsha204a_idle(struct udevice *dev)
int atsha204a_sleep(struct udevice *dev)
{
int res;
- u8 req = ATSHA204A_FUNC_IDLE;
+ u8 req = ATSHA204A_FUNC_SLEEP;
res = atsha204a_send(dev, &req, 1);
if (res)