|
@@ -54,7 +54,8 @@
|
|
|
struct fsi_master_gpio {
|
|
|
struct fsi_master master;
|
|
|
struct device *dev;
|
|
|
- spinlock_t cmd_lock; /* Lock for commands */
|
|
|
+ struct mutex cmd_lock; /* mutex for command ordering */
|
|
|
+ spinlock_t bit_lock; /* lock for clocking bits out */
|
|
|
struct gpio_desc *gpio_clk;
|
|
|
struct gpio_desc *gpio_data;
|
|
|
struct gpio_desc *gpio_trans; /* Voltage translator */
|
|
@@ -270,10 +271,13 @@ static int read_one_response(struct fsi_master_gpio *master,
|
|
|
uint8_t data_size, struct fsi_gpio_msg *msgp, uint8_t *tagp)
|
|
|
{
|
|
|
struct fsi_gpio_msg msg;
|
|
|
- uint8_t tag;
|
|
|
+ unsigned long flags;
|
|
|
uint32_t crc;
|
|
|
+ uint8_t tag;
|
|
|
int i;
|
|
|
|
|
|
+ spin_lock_irqsave(&master->bit_lock, flags);
|
|
|
+
|
|
|
/* wait for the start bit */
|
|
|
for (i = 0; i < FSI_GPIO_MTOE_COUNT; i++) {
|
|
|
msg.bits = 0;
|
|
@@ -286,6 +290,7 @@ static int read_one_response(struct fsi_master_gpio *master,
|
|
|
dev_dbg(master->dev,
|
|
|
"Master time out waiting for response\n");
|
|
|
fsi_master_gpio_error(master, FSI_GPIO_MTOE);
|
|
|
+ spin_unlock_irqrestore(&master->bit_lock, flags);
|
|
|
return -EIO;
|
|
|
}
|
|
|
|
|
@@ -304,6 +309,8 @@ static int read_one_response(struct fsi_master_gpio *master,
|
|
|
/* read CRC */
|
|
|
serial_in(master, &msg, FSI_GPIO_CRC_SIZE);
|
|
|
|
|
|
+ spin_unlock_irqrestore(&master->bit_lock, flags);
|
|
|
+
|
|
|
/* we have a whole message now; check CRC */
|
|
|
crc = crc4(0, 1, 1);
|
|
|
crc = crc4(crc, msg.msg, msg.bits);
|
|
@@ -324,12 +331,16 @@ static int read_one_response(struct fsi_master_gpio *master,
|
|
|
static int issue_term(struct fsi_master_gpio *master, uint8_t slave)
|
|
|
{
|
|
|
struct fsi_gpio_msg cmd;
|
|
|
+ unsigned long flags;
|
|
|
uint8_t tag;
|
|
|
int rc;
|
|
|
|
|
|
build_term_command(&cmd, slave);
|
|
|
+
|
|
|
+ spin_lock_irqsave(&master->bit_lock, flags);
|
|
|
serial_out(master, &cmd);
|
|
|
echo_delay(master);
|
|
|
+ spin_unlock_irqrestore(&master->bit_lock, flags);
|
|
|
|
|
|
rc = read_one_response(master, 0, NULL, &tag);
|
|
|
if (rc < 0) {
|
|
@@ -349,6 +360,7 @@ static int poll_for_response(struct fsi_master_gpio *master,
|
|
|
{
|
|
|
struct fsi_gpio_msg response, cmd;
|
|
|
int busy_count = 0, rc, i;
|
|
|
+ unsigned long flags;
|
|
|
uint8_t tag;
|
|
|
uint8_t *data_byte = data;
|
|
|
|
|
@@ -377,15 +389,20 @@ retry:
|
|
|
* d-poll, not indicated in the hardware protocol
|
|
|
* spec. < 20 clocks causes slave to hang, 21 ok.
|
|
|
*/
|
|
|
- clock_zeros(master, FSI_GPIO_DPOLL_CLOCKS);
|
|
|
if (busy_count++ < FSI_GPIO_MAX_BUSY) {
|
|
|
build_dpoll_command(&cmd, slave);
|
|
|
+ spin_lock_irqsave(&master->bit_lock, flags);
|
|
|
+ clock_zeros(master, FSI_GPIO_DPOLL_CLOCKS);
|
|
|
serial_out(master, &cmd);
|
|
|
echo_delay(master);
|
|
|
+ spin_unlock_irqrestore(&master->bit_lock, flags);
|
|
|
goto retry;
|
|
|
}
|
|
|
dev_warn(master->dev,
|
|
|
"ERR slave is stuck in busy state, issuing TERM\n");
|
|
|
+ spin_lock_irqsave(&master->bit_lock, flags);
|
|
|
+ clock_zeros(master, FSI_GPIO_DPOLL_CLOCKS);
|
|
|
+ spin_unlock_irqrestore(&master->bit_lock, flags);
|
|
|
issue_term(master, slave);
|
|
|
rc = -EIO;
|
|
|
break;
|
|
@@ -404,27 +421,42 @@ retry:
|
|
|
trace_fsi_master_gpio_poll_response_busy(master, busy_count);
|
|
|
|
|
|
/* Clock the slave enough to be ready for next operation */
|
|
|
+ spin_lock_irqsave(&master->bit_lock, flags);
|
|
|
clock_zeros(master, FSI_GPIO_PRIME_SLAVE_CLOCKS);
|
|
|
+ spin_unlock_irqrestore(&master->bit_lock, flags);
|
|
|
return rc;
|
|
|
}
|
|
|
|
|
|
-static int fsi_master_gpio_xfer(struct fsi_master_gpio *master, uint8_t slave,
|
|
|
- struct fsi_gpio_msg *cmd, size_t resp_len, void *resp)
|
|
|
+static int send_request(struct fsi_master_gpio *master,
|
|
|
+ struct fsi_gpio_msg *cmd)
|
|
|
{
|
|
|
unsigned long flags;
|
|
|
- int rc;
|
|
|
-
|
|
|
- spin_lock_irqsave(&master->cmd_lock, flags);
|
|
|
|
|
|
+ spin_lock_irqsave(&master->bit_lock, flags);
|
|
|
if (master->external_mode) {
|
|
|
- spin_unlock_irqrestore(&master->cmd_lock, flags);
|
|
|
+ spin_unlock_irqrestore(&master->bit_lock, flags);
|
|
|
return -EBUSY;
|
|
|
}
|
|
|
|
|
|
serial_out(master, cmd);
|
|
|
echo_delay(master);
|
|
|
- rc = poll_for_response(master, slave, resp_len, resp);
|
|
|
- spin_unlock_irqrestore(&master->cmd_lock, flags);
|
|
|
+ spin_unlock_irqrestore(&master->bit_lock, flags);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int fsi_master_gpio_xfer(struct fsi_master_gpio *master, uint8_t slave,
|
|
|
+ struct fsi_gpio_msg *cmd, size_t resp_len, void *resp)
|
|
|
+{
|
|
|
+ int rc;
|
|
|
+
|
|
|
+ mutex_lock(&master->cmd_lock);
|
|
|
+
|
|
|
+ rc = send_request(master, cmd);
|
|
|
+ if (!rc)
|
|
|
+ rc = poll_for_response(master, slave, resp_len, resp);
|
|
|
+
|
|
|
+ mutex_unlock(&master->cmd_lock);
|
|
|
|
|
|
return rc;
|
|
|
}
|
|
@@ -478,11 +510,14 @@ static int fsi_master_gpio_break(struct fsi_master *_master, int link)
|
|
|
|
|
|
trace_fsi_master_gpio_break(master);
|
|
|
|
|
|
- spin_lock_irqsave(&master->cmd_lock, flags);
|
|
|
+ mutex_lock(&master->cmd_lock);
|
|
|
if (master->external_mode) {
|
|
|
- spin_unlock_irqrestore(&master->cmd_lock, flags);
|
|
|
+ mutex_unlock(&master->cmd_lock);
|
|
|
return -EBUSY;
|
|
|
}
|
|
|
+
|
|
|
+ spin_lock_irqsave(&master->bit_lock, flags);
|
|
|
+
|
|
|
set_sda_output(master, 1);
|
|
|
sda_out(master, 1);
|
|
|
clock_toggle(master, FSI_PRE_BREAK_CLOCKS);
|
|
@@ -491,7 +526,9 @@ static int fsi_master_gpio_break(struct fsi_master *_master, int link)
|
|
|
echo_delay(master);
|
|
|
sda_out(master, 1);
|
|
|
clock_toggle(master, FSI_POST_BREAK_CLOCKS);
|
|
|
- spin_unlock_irqrestore(&master->cmd_lock, flags);
|
|
|
+
|
|
|
+ spin_unlock_irqrestore(&master->bit_lock, flags);
|
|
|
+ mutex_unlock(&master->cmd_lock);
|
|
|
|
|
|
/* Wait for logic reset to take effect */
|
|
|
udelay(200);
|
|
@@ -501,6 +538,8 @@ static int fsi_master_gpio_break(struct fsi_master *_master, int link)
|
|
|
|
|
|
static void fsi_master_gpio_init(struct fsi_master_gpio *master)
|
|
|
{
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
gpiod_direction_output(master->gpio_mux, 1);
|
|
|
gpiod_direction_output(master->gpio_trans, 1);
|
|
|
gpiod_direction_output(master->gpio_enable, 1);
|
|
@@ -508,7 +547,9 @@ static void fsi_master_gpio_init(struct fsi_master_gpio *master)
|
|
|
gpiod_direction_output(master->gpio_data, 1);
|
|
|
|
|
|
/* todo: evaluate if clocks can be reduced */
|
|
|
+ spin_lock_irqsave(&master->bit_lock, flags);
|
|
|
clock_zeros(master, FSI_INIT_CLOCKS);
|
|
|
+ spin_unlock_irqrestore(&master->bit_lock, flags);
|
|
|
}
|
|
|
|
|
|
static void fsi_master_gpio_init_external(struct fsi_master_gpio *master)
|
|
@@ -523,18 +564,17 @@ static void fsi_master_gpio_init_external(struct fsi_master_gpio *master)
|
|
|
static int fsi_master_gpio_link_enable(struct fsi_master *_master, int link)
|
|
|
{
|
|
|
struct fsi_master_gpio *master = to_fsi_master_gpio(_master);
|
|
|
- unsigned long flags;
|
|
|
int rc = -EBUSY;
|
|
|
|
|
|
if (link != 0)
|
|
|
return -ENODEV;
|
|
|
|
|
|
- spin_lock_irqsave(&master->cmd_lock, flags);
|
|
|
+ mutex_lock(&master->cmd_lock);
|
|
|
if (!master->external_mode) {
|
|
|
gpiod_set_value(master->gpio_enable, 1);
|
|
|
rc = 0;
|
|
|
}
|
|
|
- spin_unlock_irqrestore(&master->cmd_lock, flags);
|
|
|
+ mutex_unlock(&master->cmd_lock);
|
|
|
|
|
|
return rc;
|
|
|
}
|
|
@@ -552,7 +592,7 @@ static ssize_t external_mode_store(struct device *dev,
|
|
|
struct device_attribute *attr, const char *buf, size_t count)
|
|
|
{
|
|
|
struct fsi_master_gpio *master = dev_get_drvdata(dev);
|
|
|
- unsigned long flags, val;
|
|
|
+ unsigned long val;
|
|
|
bool external_mode;
|
|
|
int err;
|
|
|
|
|
@@ -562,10 +602,10 @@ static ssize_t external_mode_store(struct device *dev,
|
|
|
|
|
|
external_mode = !!val;
|
|
|
|
|
|
- spin_lock_irqsave(&master->cmd_lock, flags);
|
|
|
+ mutex_lock(&master->cmd_lock);
|
|
|
|
|
|
if (external_mode == master->external_mode) {
|
|
|
- spin_unlock_irqrestore(&master->cmd_lock, flags);
|
|
|
+ mutex_unlock(&master->cmd_lock);
|
|
|
return count;
|
|
|
}
|
|
|
|
|
@@ -574,7 +614,8 @@ static ssize_t external_mode_store(struct device *dev,
|
|
|
fsi_master_gpio_init_external(master);
|
|
|
else
|
|
|
fsi_master_gpio_init(master);
|
|
|
- spin_unlock_irqrestore(&master->cmd_lock, flags);
|
|
|
+
|
|
|
+ mutex_unlock(&master->cmd_lock);
|
|
|
|
|
|
fsi_master_rescan(&master->master);
|
|
|
|
|
@@ -642,7 +683,8 @@ static int fsi_master_gpio_probe(struct platform_device *pdev)
|
|
|
master->master.send_break = fsi_master_gpio_break;
|
|
|
master->master.link_enable = fsi_master_gpio_link_enable;
|
|
|
platform_set_drvdata(pdev, master);
|
|
|
- spin_lock_init(&master->cmd_lock);
|
|
|
+ spin_lock_init(&master->bit_lock);
|
|
|
+ mutex_init(&master->cmd_lock);
|
|
|
|
|
|
fsi_master_gpio_init(master);
|
|
|
|