Skip to content

Commit d34b3eb

Browse files
yliu80Eddie Dong
authored andcommitted
DM: virtio-gpio: emulate GPIO IRQ controller
GPIO IRQ controller emulation is used to handle level trigger and edge trigger interrupts. Use GPIO IRQ virtqueue to handle IRQ chip operations and GPIO event virtqueue to indicate IRQ source to UOS. Tracked-On: #2512 Signed-off-by: Yuan Liu <yuan1.liu@intel.com> Acked-by: Yu Wang <yu1.wang@intel.com>
1 parent 92a0a39 commit d34b3eb

File tree

1 file changed

+304
-1
lines changed

1 file changed

+304
-1
lines changed

devicemodel/hw/pci/virtio/virtio_gpio.c

Lines changed: 304 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ static FILE *dbg_file;
117117
} \
118118
} while (0)
119119

120+
#define BIT(x) (1 << (x))
121+
120122
/* Virtio GPIO supports maximum number of virtual gpio */
121123
#define VIRTIO_GPIO_MAX_VLINES 64
122124

@@ -130,6 +132,14 @@ static FILE *dbg_file;
130132
#define VIRTIO_GPIO_F_CHIP 1
131133
#define VIRTIO_GPIO_S_HOSTCAPS VIRTIO_GPIO_F_CHIP
132134

135+
#define IRQ_TYPE_NONE 0
136+
#define IRQ_TYPE_EDGE_RISING (1 << 0)
137+
#define IRQ_TYPE_EDGE_FALLING (1 << 1)
138+
#define IRQ_TYPE_LEVEL_HIGH (1 << 2)
139+
#define IRQ_TYPE_LEVEL_LOW (1 << 3)
140+
#define IRQ_TYPE_EDGE_BOTH (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)
141+
#define IRQ_TYPE_LEVEL_MASK (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)
142+
133143
/* make virtio gpio mediator a singleton mode */
134144
static bool virtio_gpio_is_active;
135145

@@ -171,6 +181,16 @@ enum virtio_gpio_request_command {
171181
GPIO_REQ_MAX
172182
};
173183

184+
enum gpio_irq_action {
185+
IRQ_ACTION_ENABLE = 0,
186+
IRQ_ACTION_DISABLE,
187+
IRQ_ACTION_ACK,
188+
IRQ_ACTION_MASK,
189+
IRQ_ACTION_UNMASK,
190+
191+
IRQ_ACTION_MAX
192+
};
193+
174194
struct virtio_gpio_request {
175195
uint8_t cmd;
176196
uint8_t offset;
@@ -807,10 +827,255 @@ native_gpio_init(struct virtio_gpio *gpio, char *opts)
807827
free(b);
808828
return ln == 0 ? -1 : 0;
809829
}
830+
831+
static void
832+
gpio_irq_deliver_intr(struct virtio_gpio *gpio, uint64_t mask)
833+
{
834+
struct virtio_vq_info *vq;
835+
struct iovec iov[1];
836+
uint16_t idx;
837+
uint64_t *data;
838+
839+
vq = &gpio->queues[2];
840+
if (vq_has_descs(vq) && mask) {
841+
vq_getchain(vq, &idx, iov, 1, NULL);
842+
data = iov[0].iov_base;
843+
assert(sizeof(*data) == iov[0].iov_len);
844+
845+
*data = mask;
846+
847+
/*
848+
* Release this chain and handle more
849+
*/
850+
vq_relchain(vq, idx, sizeof(*data));
851+
852+
/* Generate interrupt if appropriate. */
853+
vq_endchains(vq, 1);
854+
855+
} else
856+
DPRINTF("virtio gpio failed to send an IRQ, mask %lu", mask);
857+
}
858+
859+
static void
860+
gpio_irq_generate_intr(struct virtio_gpio *gpio, int pin)
861+
{
862+
struct gpio_irq_chip *chip;
863+
struct gpio_irq_desc *desc;
864+
865+
chip = &gpio->irq_chip;
866+
desc = &chip->descs[pin];
867+
868+
/* Ignore interrupt until it is unmasked */
869+
if (desc->mask)
870+
return;
871+
872+
pthread_mutex_lock(&chip->intr_mtx);
873+
874+
/* set it to pending mask */
875+
chip->intr_pending |= BIT(pin);
876+
877+
/*
878+
* if all interrupts in service are acknowledged, then send pending
879+
* interrupts.
880+
*/
881+
if (!chip->intr_service) {
882+
chip->intr_service = chip->intr_pending;
883+
chip->intr_pending = 0;
884+
885+
/* deliver interrupt */
886+
gpio_irq_deliver_intr(gpio, chip->intr_service);
887+
}
888+
pthread_mutex_unlock(&chip->intr_mtx);
889+
}
890+
891+
static void
892+
gpio_irq_set_pin_state(int fd __attribute__((unused)),
893+
enum ev_type t __attribute__((unused)),
894+
void *arg)
895+
{
896+
struct gpioevent_data data;
897+
struct virtio_gpio *gpio;
898+
struct gpio_irq_desc *desc;
899+
int last_level, err;
900+
901+
assert(arg != NULL);
902+
desc = (struct gpio_irq_desc *) arg;
903+
gpio = (struct virtio_gpio *) desc->data;
904+
last_level = desc->level;
905+
906+
/* get pin state */
907+
memset(&data, 0, sizeof(data));
908+
err = read(desc->fd, &data, sizeof(data));
909+
if (err != sizeof(data)) {
910+
DPRINTF("virtio gpio, gpio mevent read error %s, len %d\n",
911+
strerror(errno), err);
912+
return;
913+
}
914+
915+
if (data.id == GPIOEVENT_EVENT_RISING_EDGE) {
916+
917+
/* pin level is high */
918+
desc->level = 1;
919+
920+
/* jitter protection */
921+
if (((desc->mode & IRQ_TYPE_EDGE_RISING) && (last_level == 0))
922+
|| (desc->mode & IRQ_TYPE_LEVEL_HIGH)) {
923+
gpio_irq_generate_intr(gpio, desc->pin);
924+
}
925+
} else if (data.id == GPIOEVENT_EVENT_FALLING_EDGE) {
926+
927+
/* pin level is low */
928+
desc->level = 0;
929+
930+
/* jitter protection */
931+
if (((desc->mode & IRQ_TYPE_EDGE_FALLING) && (last_level == 1))
932+
|| (desc->mode & IRQ_TYPE_LEVEL_LOW)) {
933+
gpio_irq_generate_intr(gpio, desc->pin);
934+
}
935+
} else
936+
DPRINTF("virtio gpio, undefined GPIO event id %d\n", data.id);
937+
}
938+
939+
static void
940+
gpio_irq_disable(struct gpio_irq_chip *chip, unsigned int pin)
941+
{
942+
struct gpio_irq_desc *desc;
943+
944+
if (pin >= VIRTIO_GPIO_MAX_VLINES) {
945+
DPRINTF(" gpio irq disable pin %d is invalid\n", pin);
946+
return;
947+
}
948+
desc = &chip->descs[pin];
949+
DPRINTF("disable IRQ pin %d <-> native chip %s, GPIO %d\n",
950+
pin, desc->gpio->chip->dev_name, desc->gpio->offset);
951+
952+
/* Release the mevent, mevent teardown handles IRQ desc reset */
953+
if (desc->mevt) {
954+
mevent_delete(desc->mevt);
955+
desc->mevt = NULL;
956+
}
957+
}
958+
959+
static void
960+
gpio_irq_teardown(void *param)
961+
{
962+
struct gpio_irq_desc *desc;
963+
964+
DPRINTF("%s", "virtio gpio tear down\n");
965+
assert(param != NULL);
966+
desc = (struct gpio_irq_desc *) param;
967+
desc->mask = false;
968+
desc->mode = IRQ_TYPE_NONE;
969+
if (desc->fd > -1) {
970+
close(desc->fd);
971+
desc->fd = -1;
972+
}
973+
}
974+
975+
static void
976+
gpio_irq_enable(struct virtio_gpio *gpio, unsigned int pin,
977+
uint64_t mode)
978+
{
979+
struct gpioevent_request req;
980+
struct gpio_line *line;
981+
struct gpio_irq_chip *chip;
982+
struct gpio_irq_desc *desc;
983+
int err;
984+
985+
chip = &gpio->irq_chip;
986+
desc = &chip->descs[pin];
987+
line = desc->gpio;
988+
DPRINTF("enable IRQ pin %d, mode %lu <-> chip %s, GPIO %d\n",
989+
pin, mode, desc->gpio->chip->dev_name, desc->gpio->offset);
990+
991+
/*
992+
* Front-end should set the gpio direction to input before
993+
* enable one gpio to irq, so get the gpio value directly
994+
* no need to set it to input direction.
995+
*/
996+
desc->level = gpio_get_value(gpio, pin);
997+
998+
/* Release the GPIO line before enable it for IRQ */
999+
native_gpio_close_line(line);
1000+
1001+
memset(&req, 0, sizeof(req));
1002+
if (mode & IRQ_TYPE_EDGE_RISING)
1003+
req.eventflags |= GPIOEVENT_REQUEST_RISING_EDGE;
1004+
if (mode & IRQ_TYPE_EDGE_FALLING)
1005+
req.eventflags |= GPIOEVENT_REQUEST_FALLING_EDGE;
1006+
1007+
/*
1008+
* For level tigger, detect rising and fallling edges to
1009+
* update the IRQ level value, the value is used to check
1010+
* the level IRQ is active.
1011+
*/
1012+
if (mode & IRQ_TYPE_LEVEL_MASK)
1013+
req.eventflags |= GPIOEVENT_REQUEST_BOTH_EDGES;
1014+
if (!req.eventflags) {
1015+
DPRINTF("failed to enable pin %d to IRQ with invalid flags\n",
1016+
pin);
1017+
return;
1018+
}
1019+
1020+
desc->mode = mode;
1021+
req.lineoffset = line->offset;
1022+
strncpy(req.consumer_label, "acrn_dm_irq",
1023+
sizeof(req.consumer_label) - 1);
1024+
err = ioctl(line->chip->fd, GPIO_GET_LINEEVENT_IOCTL, &req);
1025+
if (err < 0) {
1026+
DPRINTF("ioctl GPIO_GET_LINEEVENT_IOCTL error %s\n",
1027+
strerror(errno));
1028+
goto error;
1029+
}
1030+
1031+
desc->fd = req.fd;
1032+
desc->mevt = mevent_add(desc->fd, EVF_READ,
1033+
gpio_irq_set_pin_state, desc,
1034+
gpio_irq_teardown, desc);
1035+
if (!desc->mevt) {
1036+
DPRINTF("failed to enable IRQ pin %d, mevent add error\n",
1037+
pin);
1038+
goto error;
1039+
}
1040+
1041+
return;
1042+
error:
1043+
gpio_irq_disable(chip, pin);
1044+
}
1045+
1046+
static bool
1047+
gpio_irq_has_pending_intr(struct gpio_irq_desc *desc)
1048+
{
1049+
bool level_high, level_low;
1050+
1051+
/*
1052+
* For level trigger mode, check the pin mode and level value
1053+
* to resend an interrupt.
1054+
*/
1055+
if (desc->mode & IRQ_TYPE_LEVEL_MASK) {
1056+
level_high = desc->mode & IRQ_TYPE_LEVEL_HIGH;
1057+
level_low = desc->mode & IRQ_TYPE_LEVEL_LOW;
1058+
if ((level_high && desc->level == 1) ||
1059+
(level_low && desc->level == 0))
1060+
return true;
1061+
}
1062+
return false;
1063+
}
1064+
1065+
static void
1066+
gpio_irq_clear_intr(struct gpio_irq_chip *chip, int pin)
1067+
{
1068+
pthread_mutex_lock(&chip->intr_mtx);
1069+
chip->intr_service &= ~BIT(pin);
1070+
pthread_mutex_unlock(&chip->intr_mtx);
1071+
}
1072+
8101073
static void
8111074
virtio_gpio_irq_proc(struct virtio_gpio *gpio, struct iovec *iov, uint16_t flag)
8121075
{
8131076
struct virtio_gpio_irq_request *req;
1077+
struct gpio_irq_chip *chip;
1078+
struct gpio_irq_desc *desc;
8141079
int len;
8151080

8161081
req = iov[0].iov_base;
@@ -822,7 +1087,42 @@ virtio_gpio_irq_proc(struct virtio_gpio *gpio, struct iovec *iov, uint16_t flag)
8221087
return;
8231088
}
8241089

825-
/* implement in next patch */
1090+
chip = &gpio->irq_chip;
1091+
desc = &chip->descs[req->pin];
1092+
switch (req->action) {
1093+
case IRQ_ACTION_ENABLE:
1094+
/*
1095+
* TODO: need to notify the FE driver
1096+
* if gpio_irq_enable failure.
1097+
*/
1098+
gpio_irq_enable(gpio, req->pin, req->mode);
1099+
break;
1100+
case IRQ_ACTION_DISABLE:
1101+
gpio_irq_disable(chip, req->pin);
1102+
1103+
/* reopen the GPIO */
1104+
native_gpio_open_line(desc->gpio, 0, 0);
1105+
break;
1106+
case IRQ_ACTION_ACK:
1107+
/*
1108+
* For level trigger, we need to check the level value
1109+
* for next interrupt.
1110+
*/
1111+
gpio_irq_clear_intr(chip, req->pin);
1112+
if (gpio_irq_has_pending_intr(desc))
1113+
gpio_irq_generate_intr(gpio, req->pin);
1114+
break;
1115+
case IRQ_ACTION_MASK:
1116+
desc->mask = true;
1117+
break;
1118+
case IRQ_ACTION_UNMASK:
1119+
desc->mask = false;
1120+
if (gpio_irq_has_pending_intr(desc))
1121+
gpio_irq_generate_intr(gpio, req->pin);
1122+
break;
1123+
default:
1124+
DPRINTF("virtio gpio, unknown IRQ action %d\n", req->action);
1125+
}
8261126
}
8271127

8281128
static void
@@ -850,6 +1150,9 @@ virtio_irq_notify(void *vdev, struct virtio_vq_info *vq)
8501150
* Release this chain and handle more
8511151
*/
8521152
vq_relchain(vq, idx, 1);
1153+
1154+
/* Generate interrupt if appropriate. */
1155+
vq_endchains(vq, 1);
8531156
}
8541157
}
8551158

0 commit comments

Comments
 (0)