1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2013 Imagination Technologies
4  * Author: Paul Burton <paul.burton@mips.com>
5  */
6 
7 #include <dm.h>
8 #include <init.h>
9 #include <msc01.h>
10 #include <pci.h>
11 #include <pci_msc01.h>
12 #include <asm/io.h>
13 
14 #define PCI_ACCESS_READ  0
15 #define PCI_ACCESS_WRITE 1
16 
17 struct msc01_pci_controller {
18 	struct pci_controller hose;
19 	void *base;
20 };
21 
22 static inline struct msc01_pci_controller *
hose_to_msc01(struct pci_controller * hose)23 hose_to_msc01(struct pci_controller *hose)
24 {
25 	return container_of(hose, struct msc01_pci_controller, hose);
26 }
27 
msc01_config_access(struct msc01_pci_controller * msc01,unsigned char access_type,pci_dev_t bdf,int where,u32 * data)28 static int msc01_config_access(struct msc01_pci_controller *msc01,
29 			       unsigned char access_type, pci_dev_t bdf,
30 			       int where, u32 *data)
31 {
32 	const u32 aborts = MSC01_PCI_INTSTAT_MA_MSK | MSC01_PCI_INTSTAT_TA_MSK;
33 	void *intstat = msc01->base + MSC01_PCI_INTSTAT_OFS;
34 	void *cfgdata = msc01->base + MSC01_PCI_CFGDATA_OFS;
35 	unsigned int bus = PCI_BUS(bdf);
36 	unsigned int dev = PCI_DEV(bdf);
37 	unsigned int devfn = PCI_DEV(bdf) << 3 | PCI_FUNC(bdf);
38 
39 	/* clear abort status */
40 	__raw_writel(aborts, intstat);
41 
42 	/* setup address */
43 	__raw_writel((bus << MSC01_PCI_CFGADDR_BNUM_SHF) |
44 		     (dev << MSC01_PCI_CFGADDR_DNUM_SHF) |
45 		     (devfn << MSC01_PCI_CFGADDR_FNUM_SHF) |
46 		     ((where / 4) << MSC01_PCI_CFGADDR_RNUM_SHF),
47 		     msc01->base + MSC01_PCI_CFGADDR_OFS);
48 
49 	/* perform access */
50 	if (access_type == PCI_ACCESS_WRITE)
51 		__raw_writel(*data, cfgdata);
52 	else
53 		*data = __raw_readl(cfgdata);
54 
55 	/* check for aborts */
56 	if (__raw_readl(intstat) & aborts) {
57 		/* clear abort status */
58 		__raw_writel(aborts, intstat);
59 		return -1;
60 	}
61 
62 	return 0;
63 }
64 
msc01_pci_read_config(const struct udevice * dev,pci_dev_t bdf,uint where,ulong * val,enum pci_size_t size)65 static int msc01_pci_read_config(const struct udevice *dev, pci_dev_t bdf,
66 				 uint where, ulong *val, enum pci_size_t size)
67 {
68 	struct msc01_pci_controller *msc01 = dev_get_priv(dev);
69 	u32 data = 0;
70 
71 	if (msc01_config_access(msc01, PCI_ACCESS_READ, bdf, where, &data)) {
72 		*val = pci_get_ff(size);
73 		return 0;
74 	}
75 
76 	*val = pci_conv_32_to_size(data, where, size);
77 
78 	return 0;
79 }
80 
msc01_pci_write_config(struct udevice * dev,pci_dev_t bdf,uint where,ulong val,enum pci_size_t size)81 static int msc01_pci_write_config(struct udevice *dev, pci_dev_t bdf,
82 				  uint where, ulong val, enum pci_size_t size)
83 {
84 	struct msc01_pci_controller *msc01 = dev_get_priv(dev);
85 	u32 data = 0;
86 
87 	if (size == PCI_SIZE_32) {
88 		data = val;
89 	} else {
90 		u32 old;
91 
92 		if (msc01_config_access(msc01, PCI_ACCESS_READ, bdf, where, &old))
93 			return 0;
94 
95 		data = pci_conv_size_to_32(old, val, where, size);
96 	}
97 
98 	msc01_config_access(msc01, PCI_ACCESS_WRITE, bdf, where, &data);
99 
100 	return 0;
101 }
102 
msc01_pci_probe(struct udevice * dev)103 static int msc01_pci_probe(struct udevice *dev)
104 {
105 	struct msc01_pci_controller *msc01 = dev_get_priv(dev);
106 
107 	msc01->base = dev_remap_addr(dev);
108 	if (!msc01->base)
109 		return -EINVAL;
110 
111 	return 0;
112 }
113 
114 static const struct dm_pci_ops msc01_pci_ops = {
115 	.read_config	= msc01_pci_read_config,
116 	.write_config	= msc01_pci_write_config,
117 };
118 
119 static const struct udevice_id msc01_pci_ids[] = {
120 	{ .compatible = "mips,pci-msc01" },
121 	{ }
122 };
123 
124 U_BOOT_DRIVER(msc01_pci) = {
125 	.name		= "msc01_pci",
126 	.id		= UCLASS_PCI,
127 	.of_match	= msc01_pci_ids,
128 	.ops		= &msc01_pci_ops,
129 	.probe		= msc01_pci_probe,
130 	.priv_auto	= sizeof(struct msc01_pci_controller),
131 };
132