dm: pci: Add functions to emulate 8- and 16-bit access
authorSimon Glass <sjg@chromium.org>
Fri, 20 Nov 2015 03:26:59 +0000 (20:26 -0700)
committerSimon Glass <sjg@chromium.org>
Tue, 1 Dec 2015 13:26:36 +0000 (06:26 -0700)
Provide a few functions to support using 32-bit access to emulate 8- and
16-bit access.

Signed-off-by: Simon Glass <sjg@chromium.org>
Reviewed-by: Stephen Warren <swarren@nvidia.com>
Tested-by: Stephen Warren <swarren@nvidia.com>
drivers/pci/pci-uclass.c
include/pci.h

index 7b488795be0fd7a11df6c485592bdc9338692530..e38e0b25947a63e27512b6fde28fca1f4bbad48e 100644 (file)
@@ -927,6 +927,45 @@ int pci_find_first_device(struct udevice **devp)
        return skip_to_next_device(bus, devp);
 }
 
+ulong pci_conv_32_to_size(ulong value, uint offset, enum pci_size_t size)
+{
+       switch (size) {
+       case PCI_SIZE_8:
+               return (value >> ((offset & 3) * 8)) & 0xff;
+       case PCI_SIZE_16:
+               return (value >> ((offset & 2) * 8)) & 0xffff;
+       default:
+               return value;
+       }
+}
+
+ulong pci_conv_size_to_32(ulong old, ulong value, uint offset,
+                         enum pci_size_t size)
+{
+       uint off_mask;
+       uint val_mask, shift;
+       ulong ldata, mask;
+
+       switch (size) {
+       case PCI_SIZE_8:
+               off_mask = 3;
+               val_mask = 0xff;
+               break;
+       case PCI_SIZE_16:
+               off_mask = 2;
+               val_mask = 0xffff;
+               break;
+       default:
+               return value;
+       }
+       shift = (offset & off_mask) * 8;
+       ldata = (value & val_mask) << shift;
+       mask = val_mask << shift;
+       value = (old & ~mask) | ldata;
+
+       return value;
+}
+
 UCLASS_DRIVER(pci) = {
        .id             = UCLASS_PCI,
        .name           = "pci",
index ed135a51227cc6adab0e173e0d26a5630438ecd7..ec2d104dffef7442eb5841a7781763a85500883d 100644 (file)
@@ -1091,6 +1091,37 @@ static inline int pci_read_config_byte(pci_dev_t pcidev, int offset,
        return pci_read_config8(pcidev, offset, valuep);
 }
 
+/**
+ * pci_conv_32_to_size() - convert a 32-bit read value to the given size
+ *
+ * Some PCI buses must always perform 32-bit reads. The data must then be
+ * shifted and masked to reflect the required access size and offset. This
+ * function performs this transformation.
+ *
+ * @value:     Value to transform (32-bit value read from @offset & ~3)
+ * @offset:    Register offset that was read
+ * @size:      Required size of the result
+ * @return the value that would have been obtained if the read had been
+ * performed at the given offset with the correct size
+ */
+ulong pci_conv_32_to_size(ulong value, uint offset, enum pci_size_t size);
+
+/**
+ * pci_conv_size_to_32() - update a 32-bit value to prepare for a write
+ *
+ * Some PCI buses must always perform 32-bit writes. To emulate a smaller
+ * write the old 32-bit data must be read, updated with the required new data
+ * and written back as a 32-bit value. This function performs the
+ * transformation from the old value to the new value.
+ *
+ * @value:     Value to transform (32-bit value read from @offset & ~3)
+ * @offset:    Register offset that should be written
+ * @size:      Required size of the write
+ * @return the value that should be written as a 32-bit access to @offset & ~3.
+ */
+ulong pci_conv_size_to_32(ulong old, ulong value, uint offset,
+                         enum pci_size_t size);
+
 /**
  * struct dm_pci_emul_ops - PCI device emulator operations
  */