1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Uclass for Primary-to-sideband bus, used to access various peripherals
4  *
5  * Copyright 2019 Google LLC
6  * Written by Simon Glass <sjg@chromium.org>
7  */
8 
9 #include <common.h>
10 #include <dm.h>
11 #include <log.h>
12 #include <malloc.h>
13 #include <mapmem.h>
14 #include <p2sb.h>
15 #include <spl.h>
16 #include <asm/io.h>
17 #include <dm/uclass-internal.h>
18 
19 #define PCR_COMMON_IOSF_1_0	1
20 
p2sb_set_hide(struct udevice * dev,bool hide)21 int p2sb_set_hide(struct udevice *dev, bool hide)
22 {
23 	struct p2sb_ops *ops = p2sb_get_ops(dev);
24 
25 	if (!ops->set_hide)
26 		return -ENOSYS;
27 
28 	return ops->set_hide(dev, hide);
29 }
30 
pcr_reg_address(struct udevice * dev,uint offset)31 void *pcr_reg_address(struct udevice *dev, uint offset)
32 {
33 	struct p2sb_child_plat *pplat = dev_get_parent_plat(dev);
34 	struct udevice *p2sb = dev_get_parent(dev);
35 	struct p2sb_uc_priv *upriv = dev_get_uclass_priv(p2sb);
36 	uintptr_t reg_addr;
37 
38 	/* Create an address based off of port id and offset */
39 	reg_addr = upriv->mmio_base;
40 	reg_addr += pplat->pid << PCR_PORTID_SHIFT;
41 	reg_addr += offset;
42 
43 	return map_sysmem(reg_addr, 4);
44 }
45 
46 /*
47  * The mapping of addresses via the SBREG_BAR assumes the IOSF-SB
48  * agents are using 32-bit aligned accesses for their configuration
49  * registers. For IOSF versions greater than 1_0, IOSF-SB
50  * agents can use any access (8/16/32 bit aligned) for their
51  * configuration registers
52  */
check_pcr_offset_align(uint offset,uint size)53 static inline void check_pcr_offset_align(uint offset, uint size)
54 {
55 	const size_t align = PCR_COMMON_IOSF_1_0 ? sizeof(uint32_t) : size;
56 
57 	assert(IS_ALIGNED(offset, align));
58 }
59 
pcr_read32(struct udevice * dev,uint offset)60 uint pcr_read32(struct udevice *dev, uint offset)
61 {
62 	void *ptr;
63 	uint val;
64 
65 	/* Ensure the PCR offset is correctly aligned */
66 	assert(IS_ALIGNED(offset, sizeof(uint32_t)));
67 
68 	ptr = pcr_reg_address(dev, offset);
69 	val = readl(ptr);
70 	unmap_sysmem(ptr);
71 
72 	return val;
73 }
74 
pcr_read16(struct udevice * dev,uint offset)75 uint pcr_read16(struct udevice *dev, uint offset)
76 {
77 	/* Ensure the PCR offset is correctly aligned */
78 	check_pcr_offset_align(offset, sizeof(uint16_t));
79 
80 	return readw(pcr_reg_address(dev, offset));
81 }
82 
pcr_read8(struct udevice * dev,uint offset)83 uint pcr_read8(struct udevice *dev, uint offset)
84 {
85 	/* Ensure the PCR offset is correctly aligned */
86 	check_pcr_offset_align(offset, sizeof(uint8_t));
87 
88 	return readb(pcr_reg_address(dev, offset));
89 }
90 
91 /*
92  * After every write one needs to perform a read an innocuous register to
93  * ensure the writes are completed for certain ports. This is done for
94  * all ports so that the callers don't need the per-port knowledge for
95  * each transaction.
96  */
write_completion(struct udevice * dev,uint offset)97 static void write_completion(struct udevice *dev, uint offset)
98 {
99 	readl(pcr_reg_address(dev, ALIGN_DOWN(offset, sizeof(uint32_t))));
100 }
101 
pcr_write32(struct udevice * dev,uint offset,uint indata)102 void pcr_write32(struct udevice *dev, uint offset, uint indata)
103 {
104 	/* Ensure the PCR offset is correctly aligned */
105 	assert(IS_ALIGNED(offset, sizeof(indata)));
106 
107 	writel(indata, pcr_reg_address(dev, offset));
108 	/* Ensure the writes complete */
109 	write_completion(dev, offset);
110 }
111 
pcr_write16(struct udevice * dev,uint offset,uint indata)112 void pcr_write16(struct udevice *dev, uint offset, uint indata)
113 {
114 	/* Ensure the PCR offset is correctly aligned */
115 	check_pcr_offset_align(offset, sizeof(uint16_t));
116 
117 	writew(indata, pcr_reg_address(dev, offset));
118 	/* Ensure the writes complete */
119 	write_completion(dev, offset);
120 }
121 
pcr_write8(struct udevice * dev,uint offset,uint indata)122 void pcr_write8(struct udevice *dev, uint offset, uint indata)
123 {
124 	/* Ensure the PCR offset is correctly aligned */
125 	check_pcr_offset_align(offset, sizeof(uint8_t));
126 
127 	writeb(indata, pcr_reg_address(dev, offset));
128 	/* Ensure the writes complete */
129 	write_completion(dev, offset);
130 }
131 
pcr_clrsetbits32(struct udevice * dev,uint offset,uint clr,uint set)132 void pcr_clrsetbits32(struct udevice *dev, uint offset, uint clr, uint set)
133 {
134 	uint data32;
135 
136 	data32 = pcr_read32(dev, offset);
137 	data32 &= ~clr;
138 	data32 |= set;
139 	pcr_write32(dev, offset, data32);
140 }
141 
pcr_clrsetbits16(struct udevice * dev,uint offset,uint clr,uint set)142 void pcr_clrsetbits16(struct udevice *dev, uint offset, uint clr, uint set)
143 {
144 	uint data16;
145 
146 	data16 = pcr_read16(dev, offset);
147 	data16 &= ~clr;
148 	data16 |= set;
149 	pcr_write16(dev, offset, data16);
150 }
151 
pcr_clrsetbits8(struct udevice * dev,uint offset,uint clr,uint set)152 void pcr_clrsetbits8(struct udevice *dev, uint offset, uint clr, uint set)
153 {
154 	uint data8;
155 
156 	data8 = pcr_read8(dev, offset);
157 	data8 &= ~clr;
158 	data8 |= set;
159 	pcr_write8(dev, offset, data8);
160 }
161 
p2sb_get_port_id(struct udevice * dev)162 int p2sb_get_port_id(struct udevice *dev)
163 {
164 	struct p2sb_child_plat *pplat = dev_get_parent_plat(dev);
165 
166 	return pplat->pid;
167 }
168 
p2sb_set_port_id(struct udevice * dev,int portid)169 int p2sb_set_port_id(struct udevice *dev, int portid)
170 {
171 	struct p2sb_child_plat *pplat;
172 
173 	if (!CONFIG_IS_ENABLED(OF_PLATDATA))
174 		return -ENOSYS;
175 
176 	pplat = dev_get_parent_plat(dev);
177 	pplat->pid = portid;
178 
179 	return 0;
180 }
181 
p2sb_child_post_bind(struct udevice * dev)182 static int p2sb_child_post_bind(struct udevice *dev)
183 {
184 #if !CONFIG_IS_ENABLED(OF_PLATDATA)
185 	struct p2sb_child_plat *pplat = dev_get_parent_plat(dev);
186 	int ret;
187 	u32 pid;
188 
189 	ret = dev_read_u32(dev, "intel,p2sb-port-id", &pid);
190 	if (ret)
191 		return ret;
192 	pplat->pid = pid;
193 #endif
194 
195 	return 0;
196 }
197 
p2sb_post_bind(struct udevice * dev)198 static int p2sb_post_bind(struct udevice *dev)
199 {
200 	if (spl_phase() > PHASE_TPL && !CONFIG_IS_ENABLED(OF_PLATDATA))
201 		return dm_scan_fdt_dev(dev);
202 
203 	return 0;
204 }
205 
206 UCLASS_DRIVER(p2sb) = {
207 	.id		= UCLASS_P2SB,
208 	.name		= "p2sb",
209 	.per_device_auto	= sizeof(struct p2sb_uc_priv),
210 	.post_bind	= p2sb_post_bind,
211 	.child_post_bind = p2sb_child_post_bind,
212 	.per_child_plat_auto = sizeof(struct p2sb_child_plat),
213 };
214