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 */
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;
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;
}
/* 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);
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) {
{
struct fsi_gpio_msg response, cmd;
int busy_count = 0, rc, i;
+ unsigned long flags;
uint8_t tag;
uint8_t *data_byte = data;
* 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;
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;
}
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);
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);
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);
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)
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;
}
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;
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;
}
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);
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);