Browse Source
Change-Id: I3002486c9ab9347a0537a14168487a1f5a74b0e3 Signed-off-by: Chong Qiao <qiaochong@loongson.cn>master
Chong Qiao
3 years ago
3 changed files with 523 additions and 0 deletions
@ -0,0 +1,86 @@ |
|||
#include <sys/linux/types.h> |
|||
#include <asm/errno.h> |
|||
#define GPIO_IO_CONF(x) ((int)0xbfe10500 + 0) |
|||
#define GPIO_OUT(x) ((int)0xbfe10500 + 0x10) |
|||
#define GPIO_IN(x) ((int)0xbfe10500 + 0x20) |
|||
#define GPIO_INTEN(x) ((int)0xbfe10500 + 0x30) |
|||
#define readq(x) (*(volatile unsigned long long *)(x)) |
|||
#define writeq(v, x) (*(volatile unsigned long long *)(x) = (v)) |
|||
|
|||
static inline void |
|||
__set_direction(unsigned pin, int input) |
|||
{ |
|||
u64 u; |
|||
|
|||
u = readq(GPIO_IO_CONF(lgpio)); |
|||
if (input) |
|||
u |= 1ULL << pin; |
|||
else |
|||
u &= ~(1ULL << pin); |
|||
writeq(u, GPIO_IO_CONF(lgpio)); |
|||
} |
|||
|
|||
static void __set_level(unsigned pin, int high) |
|||
{ |
|||
u64 u; |
|||
|
|||
u = readq(GPIO_OUT(lgpio)); |
|||
if (high) |
|||
u |= 1ULL << pin; |
|||
else |
|||
u &= ~(1ULL << pin); |
|||
writeq(u, GPIO_OUT(lgpio)); |
|||
} |
|||
|
|||
/*
|
|||
* GPIO primitives. |
|||
*/ |
|||
int ls2_gpio_request(unsigned pin) |
|||
{ |
|||
if (pin >= 64) |
|||
return -EINVAL; |
|||
else |
|||
return 0; |
|||
} |
|||
|
|||
int ls2_gpio_direction_input(unsigned pin) |
|||
{ |
|||
__set_direction(pin, 1); |
|||
return 0; |
|||
} |
|||
|
|||
int ls2_gpio_get(unsigned pin) |
|||
{ |
|||
u64 val; |
|||
|
|||
if (readq(GPIO_IO_CONF(lgpio)) & (1ULL << pin)) |
|||
val = readq(GPIO_IN(lgpio)); |
|||
else |
|||
val = readq(GPIO_OUT(lgpio)); |
|||
|
|||
|
|||
return (val >> pin) & 1; |
|||
} |
|||
|
|||
int ls2_gpio_direction_output(unsigned pin, int value) |
|||
{ |
|||
__set_level(pin, value); |
|||
__set_direction(pin, 0); |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
void ls2_gpio_set(unsigned pin, int value) |
|||
{ |
|||
__set_level(pin, value); |
|||
} |
|||
|
|||
int ls2_gpio_inten(unsigned int pin, int value) |
|||
{ |
|||
u64 inten = readq(GPIO_INTEN(pin)); |
|||
if(value) inten |= (1ULL<<pin); |
|||
else inten &= ~(1ULL<<pin); |
|||
writeq(inten, GPIO_INTEN(pin)); |
|||
|
|||
return 0; |
|||
} |
@ -0,0 +1,435 @@ |
|||
/*
|
|||
* (C) Copyright 2015, Samsung Electronics |
|||
* Przemyslaw Marczak <p.marczak@samsung.com> |
|||
* |
|||
* This file is based on: drivers/i2c/soft-i2c.c, |
|||
* with added driver-model support and code cleanup. |
|||
*/ |
|||
#include <pmon.h> |
|||
#include <linux/types.h> |
|||
#include <asm/errno.h> |
|||
#include "target/ls2k.h" |
|||
typedef unsigned char uchar ; |
|||
typedef int bool; |
|||
#define debug(fmt, args...) //printf(fmt, ##args)
|
|||
|
|||
static inline void udelay(unsigned long us) |
|||
{ |
|||
delay(us); |
|||
} |
|||
|
|||
/* All transfers are described by this data structure */ |
|||
struct i2c_msg { |
|||
u16 addr; /* slave address */ |
|||
u16 flags; |
|||
#define I2C_M_TEN 0x0010 /* this is a ten bit chip address */ |
|||
#define I2C_M_RD 0x0001 /* read data, from slave to master */ |
|||
#define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_PROTOCOL_MANGLING */ |
|||
#define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */ |
|||
#define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */ |
|||
#define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */ |
|||
#define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */ |
|||
u16 len; /* msg length */ |
|||
u8 *buf; /* pointer to msg data */ |
|||
}; |
|||
|
|||
#define DEFAULT_UDELAY 5 |
|||
#define RETRIES 0 |
|||
#define I2C_ACK 0 |
|||
#define I2C_NOACK 1 |
|||
|
|||
enum { |
|||
PIN_SDA = 0, |
|||
PIN_SCL, |
|||
PIN_COUNT, |
|||
}; |
|||
|
|||
struct i2c_gpio_bus { |
|||
/**
|
|||
* udelay - delay [us] between GPIO toggle operations, |
|||
* which is 1/4 of I2C speed clock period. |
|||
*/ |
|||
int udelay; |
|||
/* sda, scl */ |
|||
int pin_sda, pin_scl; |
|||
int (*get_sda)(struct i2c_gpio_bus *bus); |
|||
void (*set_sda)(struct i2c_gpio_bus *bus, int bit); |
|||
void (*set_scl)(struct i2c_gpio_bus *bus, int bit); |
|||
}; |
|||
|
|||
static int i2c_gpio_sda_get(struct i2c_gpio_bus *bus) |
|||
{ |
|||
|
|||
ls2_gpio_direction_input(bus->pin_sda); |
|||
return ls2_gpio_get(bus->pin_sda); |
|||
} |
|||
|
|||
static void i2c_gpio_sda_set(struct i2c_gpio_bus *bus, int bit) |
|||
{ |
|||
ls2_gpio_direction_output(bus->pin_sda, bit); |
|||
} |
|||
|
|||
static void i2c_gpio_scl_set(struct i2c_gpio_bus *bus, int bit) |
|||
{ |
|||
ls2_gpio_direction_output(bus->pin_scl, bit); |
|||
} |
|||
|
|||
|
|||
static void i2c_gpio_write_bit(struct i2c_gpio_bus *bus, int delay, uchar bit) |
|||
{ |
|||
bus->set_scl(bus, 0); |
|||
udelay(delay); |
|||
bus->set_sda(bus, bit); |
|||
udelay(delay); |
|||
bus->set_scl(bus, 1); |
|||
udelay(2 * delay); |
|||
} |
|||
|
|||
static int i2c_gpio_read_bit(struct i2c_gpio_bus *bus, int delay) |
|||
{ |
|||
int value; |
|||
|
|||
bus->set_scl(bus, 1); |
|||
udelay(delay); |
|||
value = bus->get_sda(bus); |
|||
udelay(delay); |
|||
bus->set_scl(bus, 0); |
|||
udelay(2 * delay); |
|||
|
|||
return value; |
|||
} |
|||
|
|||
/* START: High -> Low on SDA while SCL is High */ |
|||
static void i2c_gpio_send_start(struct i2c_gpio_bus *bus, int delay) |
|||
{ |
|||
udelay(delay); |
|||
bus->set_sda(bus, 1); |
|||
udelay(delay); |
|||
bus->set_scl(bus, 1); |
|||
udelay(delay); |
|||
bus->set_sda(bus, 0); |
|||
udelay(delay); |
|||
} |
|||
|
|||
/* STOP: Low -> High on SDA while SCL is High */ |
|||
static void i2c_gpio_send_stop(struct i2c_gpio_bus *bus, int delay) |
|||
{ |
|||
bus->set_scl(bus, 0); |
|||
udelay(delay); |
|||
bus->set_sda(bus, 0); |
|||
udelay(delay); |
|||
bus->set_scl(bus, 1); |
|||
udelay(delay); |
|||
bus->set_sda(bus, 1); |
|||
udelay(delay); |
|||
} |
|||
|
|||
/* ack should be I2C_ACK or I2C_NOACK */ |
|||
static void i2c_gpio_send_ack(struct i2c_gpio_bus *bus, int delay, int ack) |
|||
{ |
|||
i2c_gpio_write_bit(bus, delay, ack); |
|||
bus->set_scl(bus, 0); |
|||
udelay(delay); |
|||
} |
|||
|
|||
/**
|
|||
* 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 void i2c_gpio_send_reset(struct i2c_gpio_bus *bus, int delay) |
|||
{ |
|||
int j; |
|||
|
|||
for (j = 0; j < 9; j++) |
|||
i2c_gpio_write_bit(bus, delay, 1); |
|||
|
|||
i2c_gpio_send_stop(bus, delay); |
|||
} |
|||
|
|||
/* Set sda high with low clock, before reading slave data */ |
|||
static void i2c_gpio_sda_high(struct i2c_gpio_bus *bus, int delay) |
|||
{ |
|||
bus->set_scl(bus, 0); |
|||
udelay(delay); |
|||
bus->set_sda(bus, 1); |
|||
udelay(delay); |
|||
} |
|||
|
|||
/* Send 8 bits and look for an acknowledgement */ |
|||
static int i2c_gpio_write_byte(struct i2c_gpio_bus *bus, int delay, uchar data) |
|||
{ |
|||
int j; |
|||
int nack; |
|||
|
|||
for (j = 0; j < 8; j++) { |
|||
i2c_gpio_write_bit(bus, delay, data & 0x80); |
|||
data <<= 1; |
|||
} |
|||
|
|||
udelay(delay); |
|||
|
|||
/* Look for an <ACK>(negative logic) and return it */ |
|||
i2c_gpio_sda_high(bus, delay); |
|||
nack = i2c_gpio_read_bit(bus, delay); |
|||
|
|||
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 uchar i2c_gpio_read_byte(struct i2c_gpio_bus *bus, int delay, int ack) |
|||
{ |
|||
int data; |
|||
int j; |
|||
|
|||
i2c_gpio_sda_high(bus, delay); |
|||
data = 0; |
|||
for (j = 0; j < 8; j++) { |
|||
data <<= 1; |
|||
data |= i2c_gpio_read_bit(bus, delay); |
|||
} |
|||
i2c_gpio_send_ack(bus, delay, ack); |
|||
|
|||
return data; |
|||
} |
|||
|
|||
/* send start and the slave chip address */ |
|||
int i2c_send_slave_addr(struct i2c_gpio_bus *bus, int delay, uchar chip) |
|||
{ |
|||
i2c_gpio_send_start(bus, delay); |
|||
|
|||
if (i2c_gpio_write_byte(bus, delay, chip)) { |
|||
i2c_gpio_send_stop(bus, delay); |
|||
return -EIO; |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
static int i2c_gpio_write_data(struct i2c_gpio_bus *bus, uchar chip, |
|||
uchar *buffer, int len, |
|||
bool end_with_repeated_start) |
|||
{ |
|||
unsigned int delay = bus->udelay; |
|||
int failures = 0; |
|||
|
|||
debug("%s: chip %x buffer %p len %d\n", __func__, chip, buffer, len); |
|||
|
|||
if (i2c_send_slave_addr(bus, delay, chip << 1)) { |
|||
debug("gpio_i2c_write, no chip responded %02X\n", chip); |
|||
return -EIO; |
|||
} |
|||
|
|||
while (len-- > 0) { |
|||
if (i2c_gpio_write_byte(bus, delay, *buffer++)) |
|||
failures++; |
|||
} |
|||
|
|||
if (!end_with_repeated_start) { |
|||
i2c_gpio_send_stop(bus, delay); |
|||
return failures; |
|||
} |
|||
|
|||
if (i2c_send_slave_addr(bus, delay, (chip << 1) | 0x1)) { |
|||
debug("gpio_i2c_write, no chip responded %02X\n", chip); |
|||
return -EIO; |
|||
} |
|||
|
|||
return failures; |
|||
} |
|||
|
|||
static int i2c_gpio_read_data(struct i2c_gpio_bus *bus, uchar chip, |
|||
uchar *buffer, int len) |
|||
{ |
|||
unsigned int delay = bus->udelay; |
|||
|
|||
debug("%s: chip %x buffer: %p len %d\n", __func__, chip, buffer, len); |
|||
|
|||
while (len-- > 0) |
|||
*buffer++ = i2c_gpio_read_byte(bus, delay, len == 0); |
|||
|
|||
i2c_gpio_send_stop(bus, delay); |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
static int i2c_gpio_xfer(struct i2c_gpio_bus *bus, struct i2c_msg *msg, int nmsgs) |
|||
{ |
|||
int ret; |
|||
|
|||
for (; nmsgs > 0; nmsgs--, msg++) { |
|||
bool next_is_read = nmsgs > 1 && (msg[1].flags & I2C_M_RD); |
|||
|
|||
if (msg->flags & I2C_M_RD) { |
|||
ret = i2c_gpio_read_data(bus, msg->addr, msg->buf, |
|||
msg->len); |
|||
} else { |
|||
ret = i2c_gpio_write_data(bus, msg->addr, msg->buf, |
|||
msg->len, next_is_read); |
|||
} |
|||
|
|||
if (ret) |
|||
return -EREMOTEIO; |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
static int i2c_gpio_set_bus_speed(struct i2c_gpio_bus *bus, unsigned int speed_hz) |
|||
{ |
|||
bus->udelay = 1000000 / (speed_hz << 2); |
|||
|
|||
i2c_gpio_send_reset(bus, bus->udelay); |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
|
|||
static struct i2c_gpio_bus i2c_gpio_bus = { |
|||
.udelay = 1, |
|||
.pin_scl = 18, |
|||
.pin_sda = 19, |
|||
.get_sda = i2c_gpio_sda_get, |
|||
.set_sda = i2c_gpio_sda_set, |
|||
.set_scl = i2c_gpio_scl_set, |
|||
}; |
|||
|
|||
|
|||
/**
|
|||
* gpio_i2c_read: - Read multiple bytes from an i2c device |
|||
* |
|||
* The higher level routines take into account that this function is only |
|||
* called with len < page length of the device (see configuration file) |
|||
* |
|||
* @chip: address of the chip which is to be read |
|||
* @addr: i2c data address within the chip |
|||
* @alen: length of the i2c data address (1..2 bytes) |
|||
* @buffer: where to write the data |
|||
* @len: how much byte do we want to read |
|||
* @return: 0 in case of success |
|||
*/ |
|||
|
|||
int gpio_i2c_read(u8 chip, uint addr, int alen, u8 *buffer, int len) |
|||
{ |
|||
struct i2c_msg msg[2] = { { chip, 0, alen, (u8 *)&addr }, |
|||
{ chip, I2C_M_RD, len, buffer } |
|||
}; |
|||
|
|||
debug("gpio_i2c_read(chip=0x%02x, addr=0x%02x, alen=0x%02x, len=0x%02x)\n",chip,addr,alen,len); |
|||
|
|||
i2c_gpio_xfer(&i2c_gpio_bus, msg, 2); |
|||
return 0; |
|||
} |
|||
|
|||
|
|||
/**
|
|||
* gpio_i2c_write: - Write multiple bytes to an i2c device |
|||
* |
|||
* The higher level routines take into account that this function is only |
|||
* called with len < page length of the device (see configuration file) |
|||
* |
|||
* @chip: address of the chip which is to be written |
|||
* @addr: i2c data address within the chip |
|||
* @alen: length of the i2c data address (1..2 bytes) |
|||
* @buffer: where to find the data to be written |
|||
* @len: how much byte do we want to read |
|||
* @return: 0 in case of success |
|||
*/ |
|||
|
|||
int gpio_i2c_write(u8 chip, uint addr, int alen, u8 *buffer, int len) |
|||
{ |
|||
struct i2c_msg msg[2] = { { chip, 0, alen, (u8 *)&addr }, |
|||
{ chip, I2C_M_NOSTART, len, buffer } |
|||
}; |
|||
debug("gpio_i2c_write(chip=0x%02x, addr=0x%02x, alen=0x%02x, len=0x%02x)\n",chip,addr,alen,len); |
|||
|
|||
i2c_gpio_xfer(&i2c_gpio_bus, msg, 2); |
|||
return 0; |
|||
} |
|||
|
|||
|
|||
union commondata{ |
|||
unsigned char data1; |
|||
unsigned short data2; |
|||
unsigned int data4; |
|||
unsigned int data8[2]; |
|||
unsigned char c[8]; |
|||
}; |
|||
extern unsigned int syscall_addrtype; |
|||
extern int (*syscall1)(int type,long long addr,union commondata *mydata); |
|||
extern int (*syscall2)(int type,long long addr,union commondata *mydata); |
|||
|
|||
|
|||
static int syscall_i2c_addrlen; |
|||
static char syscall_i2c_chip; |
|||
static int syscall_i2c_addrlen; |
|||
|
|||
static int gpio_i2c_read_syscall(int type,long long addr,union commondata *mydata) |
|||
{ |
|||
char c; |
|||
switch(type) |
|||
{ |
|||
case 1: |
|||
gpio_i2c_read(syscall_i2c_chip, addr, syscall_i2c_addrlen, &mydata->data1, 1); |
|||
|
|||
break; |
|||
|
|||
default: return -1;break; |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
static int gpio_i2c_write_syscall(int type,long long addr,union commondata *mydata) |
|||
{ |
|||
char c; |
|||
switch(type) |
|||
{ |
|||
case 1: |
|||
gpio_i2c_write(syscall_i2c_chip, addr, syscall_i2c_addrlen, &mydata->data1, 1); |
|||
|
|||
break; |
|||
|
|||
default: return -1;break; |
|||
} |
|||
return 0; |
|||
} |
|||
//----------------------------------------
|
|||
|
|||
static int gpio_i2cs(int argc,char **argv) |
|||
{ |
|||
volatile int bus; |
|||
if(argc<2) |
|||
return -1; |
|||
|
|||
bus=strtoul(argv[1],0,0); |
|||
syscall_i2c_chip = strtoul(argv[2],0,0); |
|||
syscall_i2c_addrlen = argc>3?strtoul(argv[3],0,0):1; |
|||
|
|||
#if !I2C1_SELGPIO |
|||
/*i2c1 as gpio*/ |
|||
*(volatile int *)0xbfe10420 &= ~(1<<11); |
|||
#endif |
|||
syscall1=(void*)gpio_i2c_read_syscall; |
|||
syscall2=(void*)gpio_i2c_write_syscall; |
|||
|
|||
syscall_addrtype=0; |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
static const Cmd Cmds[] = |
|||
{ |
|||
{"MyCmds"}, |
|||
{"gpio_i2cs","bus chip [alen]", 0, "test i2c", gpio_i2cs, 0, 99, CMD_REPEAT}, |
|||
{0, 0} |
|||
}; |
|||
|
|||
|
|||
static void init_cmd __P((void)) __attribute__ ((constructor)); |
|||
static void init_cmd() |
|||
{ |
|||
cmdlist_expand(Cmds, 1); |
|||
} |
Loading…
Reference in new issue