|
52 | 52 | #include <linux/init.h>
|
53 | 53 | #include <linux/interrupt.h>
|
54 | 54 | #include <linux/list.h>
|
| 55 | +#include <linux/log2.h> |
55 | 56 | #include <linux/platform_device.h>
|
56 | 57 | #include <linux/mailbox_controller.h>
|
57 | 58 | #include <linux/mailbox_client.h>
|
|
64 | 65 |
|
65 | 66 | static struct mbox_chan *pcc_mbox_channels;
|
66 | 67 |
|
| 68 | +/** |
| 69 | + * struct pcc_chan_reg - PCC register bundle |
| 70 | + * |
| 71 | + * @vaddr: cached virtual address for this register |
| 72 | + * @gas: pointer to the generic address structure for this register |
| 73 | + * @preserve_mask: bitmask to preserve when writing to this register |
| 74 | + * @set_mask: bitmask to set when writing to this register |
| 75 | + * @status_mask: bitmask to determine and/or update the status for this register |
| 76 | + */ |
| 77 | +struct pcc_chan_reg { |
| 78 | + void __iomem *vaddr; |
| 79 | + struct acpi_generic_address *gas; |
| 80 | + u64 preserve_mask; |
| 81 | + u64 set_mask; |
| 82 | + u64 status_mask; |
| 83 | +}; |
| 84 | + |
67 | 85 | /**
|
68 | 86 | * struct pcc_chan_info - PCC channel specific information
|
69 | 87 | *
|
@@ -144,6 +162,53 @@ static int write_register(void __iomem *vaddr, u64 val, unsigned int bit_width)
|
144 | 162 | return ret_val;
|
145 | 163 | }
|
146 | 164 |
|
| 165 | +static int pcc_chan_reg_read(struct pcc_chan_reg *reg, u64 *val) |
| 166 | +{ |
| 167 | + int ret = 0; |
| 168 | + |
| 169 | + if (!reg->gas) { |
| 170 | + *val = 0; |
| 171 | + return 0; |
| 172 | + } |
| 173 | + |
| 174 | + if (reg->vaddr) |
| 175 | + ret = read_register(reg->vaddr, val, reg->gas->bit_width); |
| 176 | + else |
| 177 | + ret = acpi_read(val, reg->gas); |
| 178 | + |
| 179 | + return ret; |
| 180 | +} |
| 181 | + |
| 182 | +static int pcc_chan_reg_write(struct pcc_chan_reg *reg, u64 val) |
| 183 | +{ |
| 184 | + int ret = 0; |
| 185 | + |
| 186 | + if (!reg->gas) |
| 187 | + return 0; |
| 188 | + |
| 189 | + if (reg->vaddr) |
| 190 | + ret = write_register(reg->vaddr, val, reg->gas->bit_width); |
| 191 | + else |
| 192 | + ret = acpi_write(val, reg->gas); |
| 193 | + |
| 194 | + return ret; |
| 195 | +} |
| 196 | + |
| 197 | +static int pcc_chan_reg_read_modify_write(struct pcc_chan_reg *reg) |
| 198 | +{ |
| 199 | + int ret = 0; |
| 200 | + u64 val; |
| 201 | + |
| 202 | + ret = pcc_chan_reg_read(reg, &val); |
| 203 | + if (ret) |
| 204 | + return ret; |
| 205 | + |
| 206 | + val &= reg->preserve_mask; |
| 207 | + val |= reg->set_mask; |
| 208 | + |
| 209 | + return pcc_chan_reg_write(reg, val); |
| 210 | +} |
| 211 | + |
147 | 212 | /**
|
148 | 213 | * pcc_map_interrupt - Map a PCC subspace GSI to a linux IRQ number
|
149 | 214 | * @interrupt: GSI number.
|
@@ -379,6 +444,31 @@ static int parse_pcc_subspace(union acpi_subtable_headers *header,
|
379 | 444 | return -EINVAL;
|
380 | 445 | }
|
381 | 446 |
|
| 447 | +static int |
| 448 | +pcc_chan_reg_init(struct pcc_chan_reg *reg, struct acpi_generic_address *gas, |
| 449 | + u64 preserve_mask, u64 set_mask, u64 status_mask, char *name) |
| 450 | +{ |
| 451 | + if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) { |
| 452 | + if (!(gas->bit_width >= 8 && gas->bit_width <= 64 && |
| 453 | + is_power_of_2(gas->bit_width))) { |
| 454 | + pr_err("Error: Cannot access register of %u bit width", |
| 455 | + gas->bit_width); |
| 456 | + return -EFAULT; |
| 457 | + } |
| 458 | + |
| 459 | + reg->vaddr = acpi_os_ioremap(gas->address, gas->bit_width / 8); |
| 460 | + if (!reg->vaddr) { |
| 461 | + pr_err("Failed to ioremap PCC %s register\n", name); |
| 462 | + return -ENOMEM; |
| 463 | + } |
| 464 | + } |
| 465 | + reg->gas = gas; |
| 466 | + reg->preserve_mask = preserve_mask; |
| 467 | + reg->set_mask = set_mask; |
| 468 | + reg->status_mask = status_mask; |
| 469 | + return 0; |
| 470 | +} |
| 471 | + |
382 | 472 | /**
|
383 | 473 | * pcc_parse_subspace_irq - Parse the PCC IRQ and PCC ACK register
|
384 | 474 | *
|
|
0 commit comments