// SPDX-License-Identifier: GPL-2.0-only /* * GPIO based serio bus driver for bit banging the PS/2 protocol * * Author: Danilo Krummrich <danilokrummrich@dk-develop.de>
*/
/* * The PS2 protocol specifies a clock frequency between 10kHz and 16.7kHz, * therefore the maximal interrupt interval should be 100us and the minimum * interrupt interval should be ~60us. Let's allow +/- 20us for frequency * deviations and interrupt latency. * * The data line must be samples after ~30us to 50us after the falling edge, * since the device updates the data line at the rising edge. * * ___ ______ ______ ______ ___ * \ / \ / \ / \ / * \ / \ / \ / \ / * \______/ \______/ \______/ \______/ * * |-----------------| |--------| * 60us/100us 30us/50us
*/ #define PS2_CLK_FREQ_MIN_HZ 10000 #define PS2_CLK_FREQ_MAX_HZ 16700 #define PS2_CLK_MIN_INTERVAL_US ((1000 * 1000) / PS2_CLK_FREQ_MAX_HZ) #define PS2_CLK_MAX_INTERVAL_US ((1000 * 1000) / PS2_CLK_FREQ_MIN_HZ) #define PS2_IRQ_MIN_INTERVAL_US (PS2_CLK_MIN_INTERVAL_US - 20) #define PS2_IRQ_MAX_INTERVAL_US (PS2_CLK_MAX_INTERVAL_US + 20)
static irqreturn_t ps2_gpio_irq_rx(struct ps2_gpio_data *drvdata)
{ unsignedchar byte, cnt; int data; int rxflags = 0;
s64 us_delta;
byte = drvdata->rx.byte;
cnt = drvdata->rx.cnt;
drvdata->t_irq_now = ktime_get();
/* * We need to consider spurious interrupts happening right after * a TX xfer finished.
*/
us_delta = ktime_us_delta(drvdata->t_irq_now, drvdata->tx.t_xfer_end); if (unlikely(us_delta < PS2_IRQ_MIN_INTERVAL_US)) goto end;
data = gpiod_get_value(drvdata->gpio_data); if (unlikely(data < 0)) {
dev_err(drvdata->dev, "RX: failed to get data gpio val: %d\n",
data); goto err;
}
switch (cnt) { case PS2_START_BIT: /* start bit should be low */ if (unlikely(data)) {
dev_err(drvdata->dev, "RX: start bit should be low\n"); goto err;
} break; case PS2_DATA_BIT0: case PS2_DATA_BIT1: case PS2_DATA_BIT2: case PS2_DATA_BIT3: case PS2_DATA_BIT4: case PS2_DATA_BIT5: case PS2_DATA_BIT6: case PS2_DATA_BIT7: /* processing data bits */ if (data)
byte |= (data << (cnt - 1)); break; case PS2_PARITY_BIT: /* check odd parity */ if (!((hweight8(byte) & 1) ^ data)) {
rxflags |= SERIO_PARITY;
dev_warn(drvdata->dev, "RX: parity error\n"); if (!drvdata->write_enable) goto err;
} break; case PS2_STOP_BIT: /* stop bit should be high */ if (unlikely(!data)) {
dev_err(drvdata->dev, "RX: stop bit should be high\n"); goto err;
}
/* * Do not send spurious ACK's and NACK's when write fn is * not provided.
*/ if (!drvdata->write_enable) { if (byte == PS2_DEV_RET_NACK) goto err; elseif (byte == PS2_DEV_RET_ACK) break;
}
/* * There might be pending IRQs since we disabled IRQs in * __ps2_gpio_write(). We can expect at least one clock period until * the device generates the first falling edge after releasing the * clock line.
*/
us_delta = ktime_us_delta(drvdata->t_irq_now,
drvdata->tx.t_xfer_start); if (unlikely(us_delta < PS2_CLK_MIN_INTERVAL_US)) goto end;
switch (cnt) { case PS2_START_BIT: /* should never happen */
dev_err(drvdata->dev, "TX: start bit should have been sent already\n"); goto err; case PS2_DATA_BIT0: case PS2_DATA_BIT1: case PS2_DATA_BIT2: case PS2_DATA_BIT3: case PS2_DATA_BIT4: case PS2_DATA_BIT5: case PS2_DATA_BIT6: case PS2_DATA_BIT7:
data = byte & BIT(cnt - 1);
gpiod_set_value(drvdata->gpio_data, data); break; case PS2_PARITY_BIT: /* do odd parity */
data = !(hweight8(byte) & 1);
gpiod_set_value(drvdata->gpio_data, data); break; case PS2_STOP_BIT: /* release data line to generate stop bit */
gpiod_direction_input(drvdata->gpio_data); break; case PS2_ACK_BIT:
data = gpiod_get_value(drvdata->gpio_data); if (data) {
dev_warn(drvdata->dev, "TX: received NACK, retry\n"); goto err;
}
cnt = 1; goto end; /* success */ default: /* * Probably we missed the stop bit. Therefore we release data * line and try again.
*/
gpiod_direction_input(drvdata->gpio_data);
dev_err(drvdata->dev, "TX: got out of sync with the device\n"); goto err;
}
error = ps2_gpio_get_props(dev, drvdata); if (error) goto err_free_serio;
if (gpiod_cansleep(drvdata->gpio_data) ||
gpiod_cansleep(drvdata->gpio_clk)) {
dev_err(dev, "GPIO data or clk are connected via slow bus\n");
error = -EINVAL; goto err_free_serio;
}
serio->id.type = SERIO_8042;
serio->open = ps2_gpio_open;
serio->close = ps2_gpio_close; /* * Write can be enabled in platform/dt data, but possibly it will not * work because of the tough timings.
*/
serio->write = drvdata->write_enable ? ps2_gpio_write : NULL;
serio->port_data = drvdata;
serio->dev.parent = dev;
strscpy(serio->name, dev_name(dev), sizeof(serio->name));
strscpy(serio->phys, dev_name(dev), sizeof(serio->phys));
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.