mirror of https://github.com/OpenIPC/firmware.git
277 lines
6.6 KiB
Diff
277 lines
6.6 KiB
Diff
diff -drupN a/tools/pm-sleep/i2c-gpio.c b/tools/pm-sleep/i2c-gpio.c
|
|
--- a/tools/pm-sleep/i2c-gpio.c 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/tools/pm-sleep/i2c-gpio.c 2022-06-09 05:02:37.000000000 +0300
|
|
@@ -0,0 +1,272 @@
|
|
+/*
|
|
+ * from ingenic_soft_i2c.c in uboot.
|
|
+ */
|
|
+#include "common.h"
|
|
+#include "i2c-gpio.h"
|
|
+#include "gpio.h"
|
|
+
|
|
+#define I2C_ACK 0 /* PD_SDA level to ack a byte */
|
|
+#define I2C_NOACK 1 /* PD_SDA level to noack a byte */
|
|
+
|
|
+
|
|
+#define printf(x,...)
|
|
+
|
|
+/*-----------------------------------------------------------------------
|
|
+ * Local functions
|
|
+ */
|
|
+static void i2c_scl(struct i2c_gpio *i2c,int bit)
|
|
+{
|
|
+ gpio_direction_output(i2c->scl,bit);
|
|
+}
|
|
+
|
|
+static void i2c_sda(struct i2c_gpio *i2c,int bit)
|
|
+{
|
|
+ if(bit) {
|
|
+ gpio_direction_input(i2c->sda);
|
|
+ } else {
|
|
+ gpio_direction_output(i2c->sda,bit);
|
|
+ }
|
|
+}
|
|
+
|
|
+static int i2c_read_sda(struct i2c_gpio *i2c)
|
|
+{
|
|
+ return gpio_get_value(i2c->sda);
|
|
+}
|
|
+/*-----------------------------------------------------------------------
|
|
+ * START: High -> Low on SDA while SCL is High
|
|
+ */
|
|
+static void send_start(struct i2c_gpio *i2c)
|
|
+{
|
|
+
|
|
+ udelay(5);
|
|
+ i2c_sda(i2c,1);
|
|
+ udelay(5);
|
|
+ i2c_scl(i2c,1);
|
|
+ udelay(5);
|
|
+ i2c_sda(i2c,0);
|
|
+ udelay(5);
|
|
+}
|
|
+
|
|
+/*-----------------------------------------------------------------------
|
|
+ * STOP: Low -> High on SDA while SCL is High
|
|
+ */
|
|
+static void send_stop(struct i2c_gpio *i2c)
|
|
+{
|
|
+ i2c_scl(i2c,0);
|
|
+ udelay(5);
|
|
+ i2c_sda(i2c,0);
|
|
+ udelay(5);
|
|
+ i2c_scl(i2c,1);
|
|
+ udelay(5);
|
|
+ i2c_sda(i2c,1);
|
|
+ udelay(5);
|
|
+}
|
|
+
|
|
+/*-----------------------------------------------------------------------
|
|
+ * ack should be I2C_ACK or I2C_NOACK
|
|
+ */
|
|
+static void send_ack(struct i2c_gpio *i2c,int ack)
|
|
+{
|
|
+ i2c_scl(i2c,0);
|
|
+ udelay(5);
|
|
+ i2c_sda(i2c,ack);
|
|
+ udelay(5);
|
|
+ i2c_scl(i2c,1);
|
|
+ udelay(5*2);
|
|
+ i2c_scl(i2c,0);
|
|
+ udelay(5);
|
|
+}
|
|
+
|
|
+/*-----------------------------------------------------------------------
|
|
+ * Send a reset sequence consisting of 9 clocks with the data signal high
|
|
+ * to clock any confused device back into an idle state. Also send a
|
|
+ * <stop> at the end of the sequence for belts & suspenders.
|
|
+ */
|
|
+/* static inline void send_reset(struct i2c_gpio *i2c) */
|
|
+/* { */
|
|
+/* int j; */
|
|
+
|
|
+/* i2c_scl(i2c,1); */
|
|
+/* i2c_sda(i2c,1); */
|
|
+/* for(j = 0; j < 9; j++) { */
|
|
+/* i2c_scl(i2c,0); */
|
|
+/* udelay(5*2); */
|
|
+/* i2c_scl(i2c,1); */
|
|
+/* udelay(5*2); */
|
|
+/* } */
|
|
+/* send_stop(i2c); */
|
|
+/* } */
|
|
+/*-----------------------------------------------------------------------
|
|
+ * Send 8 bits and look for an acknowledgement.
|
|
+ */
|
|
+static int write_byte(struct i2c_gpio *i2c,unsigned char data)
|
|
+{
|
|
+ int j;
|
|
+ int nack;
|
|
+
|
|
+ for(j = 0; j < 8; j++) {
|
|
+ i2c_scl(i2c,0);
|
|
+ udelay(5);
|
|
+ i2c_sda(i2c,data & 0x80);
|
|
+ udelay(5);
|
|
+ i2c_scl(i2c,1);
|
|
+ udelay(5*2);
|
|
+ data <<= 1;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Look for an <ACK>(negative logic) and return it.
|
|
+ */
|
|
+ i2c_scl(i2c,0);
|
|
+ udelay(5);
|
|
+ i2c_sda(i2c,1);
|
|
+ udelay(5);
|
|
+ i2c_scl(i2c,1);
|
|
+ udelay(5*2);
|
|
+ nack = i2c_read_sda(i2c);
|
|
+ i2c_scl(i2c,0);
|
|
+ udelay(5);
|
|
+
|
|
+ return(nack); /* not a nack is an ack */
|
|
+}
|
|
+
|
|
+/*-----------------------------------------------------------------------
|
|
+ * if ack == I2C_ACK, ACK the byte so can continue reading, else
|
|
+ * send I2C_NOACK to end the read.
|
|
+ */
|
|
+static unsigned char read_byte(struct i2c_gpio *i2c,int ack)
|
|
+{
|
|
+ int data;
|
|
+ int j;
|
|
+
|
|
+ /*
|
|
+ * Read 8 bits, MSB first.
|
|
+ */
|
|
+ i2c_sda(i2c,1);
|
|
+ data = 0;
|
|
+ for(j = 0; j < 8; j++) {
|
|
+ i2c_scl(i2c,0);
|
|
+ udelay(5);
|
|
+ i2c_scl(i2c,1);
|
|
+ udelay(5);
|
|
+ data <<= 1;
|
|
+ data |= i2c_read_sda(i2c);
|
|
+ udelay(5);
|
|
+ }
|
|
+ send_ack(i2c,ack);
|
|
+
|
|
+ return(data);
|
|
+}
|
|
+
|
|
+/*=====================================================================*/
|
|
+/* Public Functions */
|
|
+/*=====================================================================*/
|
|
+
|
|
+/*-----------------------------------------------------------------------
|
|
+ * Initialization
|
|
+ */
|
|
+/* void i2c_init(struct i2c_gpio *i2c) */
|
|
+/* { */
|
|
+/* gpio_request(i2c->scl, "soft_i2c"); */
|
|
+/* gpio_request(i2c->sda, "soft_i2c"); */
|
|
+/* gpio_direction_output(i2c->sda,1); */
|
|
+/* gpio_direction_output(i2c->scl,1); */
|
|
+/* send_reset(i2c); */
|
|
+/* } */
|
|
+void i2c_reinit(struct i2c_gpio *i2c)
|
|
+{
|
|
+ gpio_direction_output(i2c->sda,1);
|
|
+ gpio_direction_output(i2c->scl,1);
|
|
+}
|
|
+/*-----------------------------------------------------------------------
|
|
+ * Read bytes
|
|
+ */
|
|
+int i2c_read(struct i2c_gpio *i2c,unsigned char chip,
|
|
+ unsigned int addr, int alen, unsigned char *buffer, int len)
|
|
+{
|
|
+ int shift;
|
|
+#ifdef DEBUG
|
|
+ printf("i2c_read: chip %x addr %x alen %d buffer %p len %d\n",
|
|
+ chip, addr, alen, buffer, len);
|
|
+#endif
|
|
+ /*
|
|
+ * Do the addressing portion of a write cycle to set the
|
|
+ * chip's address pointer. If the address length is zero,
|
|
+ * don't do the normal write cycle to set the address pointer,
|
|
+ * there is no address pointer in this chip.
|
|
+ */
|
|
+ send_start(i2c);
|
|
+ if(alen > 0) {
|
|
+ if(write_byte(i2c,chip << 1)) { /* write cycle */
|
|
+ send_stop(i2c);
|
|
+ printf("i2c_read, no chip responded %02X\n", chip);
|
|
+ return 1;
|
|
+ }
|
|
+ shift = (alen-1) * 8;
|
|
+ while(alen-- > 0) {
|
|
+ if(write_byte(i2c,addr >> shift)) {
|
|
+ printf("i2c_read, address not <ACK>ed\n");
|
|
+ return 1;
|
|
+ }
|
|
+ shift -= 8;
|
|
+ }
|
|
+
|
|
+ /* Some I2C_GPIO chips need a stop/start sequence here,
|
|
+ * other chips don't work with a full stop and need
|
|
+ * only a start. Default behaviour is to send the
|
|
+ * stop/start sequence.
|
|
+ */
|
|
+#ifdef CONFIG_SOFT_I2C_READ_REPEATED_START
|
|
+ send_start(i2c);
|
|
+#else
|
|
+ send_stop(i2c);
|
|
+ send_start(i2c);
|
|
+#endif
|
|
+ }
|
|
+ /*
|
|
+ * Send the chip address again, this time for a read cycle.
|
|
+ * Then read the data. On the last byte, we do a NACK instead
|
|
+ * of an ACK(len == 0) to terminate the read.
|
|
+ */
|
|
+ write_byte(i2c,(chip << 1) | 1); /* read cycle */
|
|
+ while(len-- > 0) {
|
|
+ *buffer++ = read_byte(i2c,len == 0);
|
|
+ }
|
|
+ send_stop(i2c);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*-----------------------------------------------------------------------
|
|
+ * Write bytes
|
|
+ */
|
|
+int i2c_write(struct i2c_gpio *i2c,unsigned char chip, unsigned int addr, int alen, unsigned char *buffer, int len)
|
|
+{
|
|
+ int shift, failures = 0;
|
|
+
|
|
+#ifdef DEBUG
|
|
+ printf("i2c_write: chip %x addr %x alen %d buffer %p len %d\n",
|
|
+ chip, addr, alen, buffer, len);
|
|
+#endif
|
|
+ send_start(i2c);
|
|
+ if(write_byte(i2c,chip << 1)) { /* write cycle */
|
|
+ send_stop(i2c);
|
|
+ printf("i2c_write, no chip responded %02X\n", chip);
|
|
+ return 1;
|
|
+ }
|
|
+ shift = (alen-1) * 8;
|
|
+ while(alen-- > 0) {
|
|
+ if(write_byte(i2c,addr >> shift)) {
|
|
+ printf("i2c_write, address not <ACK>ed\n");
|
|
+ return 1;
|
|
+ }
|
|
+ shift -= 8;
|
|
+ }
|
|
+
|
|
+ while(len-- > 0) {
|
|
+ if(write_byte(i2c,*buffer++)) {
|
|
+ failures++;
|
|
+ }
|
|
+ }
|
|
+ send_stop(i2c);
|
|
+ return failures;
|
|
+}
|