162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Old U-boot compatibility for PowerQUICC II
462306a36Sopenharmony_ci * (a.k.a. 82xx with CPM, not the 8240 family of chips)
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Author: Scott Wood <scottwood@freescale.com>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Copyright (c) 2007 Freescale Semiconductor, Inc.
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include "ops.h"
1262306a36Sopenharmony_ci#include "stdio.h"
1362306a36Sopenharmony_ci#include "cuboot.h"
1462306a36Sopenharmony_ci#include "io.h"
1562306a36Sopenharmony_ci#include "fsl-soc.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#define TARGET_CPM2
1862306a36Sopenharmony_ci#define TARGET_HAS_ETH1
1962306a36Sopenharmony_ci#include "ppcboot.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic bd_t bd;
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistruct cs_range {
2462306a36Sopenharmony_ci	u32 csnum;
2562306a36Sopenharmony_ci	u32 base; /* must be zero */
2662306a36Sopenharmony_ci	u32 addr;
2762306a36Sopenharmony_ci	u32 size;
2862306a36Sopenharmony_ci};
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistruct pci_range {
3162306a36Sopenharmony_ci	u32 flags;
3262306a36Sopenharmony_ci	u32 pci_addr[2];
3362306a36Sopenharmony_ci	u32 phys_addr;
3462306a36Sopenharmony_ci	u32 size[2];
3562306a36Sopenharmony_ci};
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistruct cs_range cs_ranges_buf[MAX_PROP_LEN / sizeof(struct cs_range)];
3862306a36Sopenharmony_cistruct pci_range pci_ranges_buf[MAX_PROP_LEN / sizeof(struct pci_range)];
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci/* Different versions of u-boot put the BCSR in different places, and
4162306a36Sopenharmony_ci * some don't set up the PCI PIC at all, so we assume the device tree is
4262306a36Sopenharmony_ci * sane and update the BRx registers appropriately.
4362306a36Sopenharmony_ci *
4462306a36Sopenharmony_ci * For any node defined as compatible with fsl,pq2-localbus,
4562306a36Sopenharmony_ci * #address/#size must be 2/1 for the localbus, and 1/1 for the parent bus.
4662306a36Sopenharmony_ci * Ranges must be for whole chip selects.
4762306a36Sopenharmony_ci */
4862306a36Sopenharmony_cistatic void update_cs_ranges(void)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	void *bus_node, *parent_node;
5162306a36Sopenharmony_ci	u32 *ctrl_addr;
5262306a36Sopenharmony_ci	unsigned long ctrl_size;
5362306a36Sopenharmony_ci	u32 naddr, nsize;
5462306a36Sopenharmony_ci	int len;
5562306a36Sopenharmony_ci	int i;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	bus_node = finddevice("/localbus");
5862306a36Sopenharmony_ci	if (!bus_node || !dt_is_compatible(bus_node, "fsl,pq2-localbus"))
5962306a36Sopenharmony_ci		return;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	dt_get_reg_format(bus_node, &naddr, &nsize);
6262306a36Sopenharmony_ci	if (naddr != 2 || nsize != 1)
6362306a36Sopenharmony_ci		goto err;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	parent_node = get_parent(bus_node);
6662306a36Sopenharmony_ci	if (!parent_node)
6762306a36Sopenharmony_ci		goto err;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	dt_get_reg_format(parent_node, &naddr, &nsize);
7062306a36Sopenharmony_ci	if (naddr != 1 || nsize != 1)
7162306a36Sopenharmony_ci		goto err;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	if (!dt_xlate_reg(bus_node, 0, (unsigned long *)&ctrl_addr,
7462306a36Sopenharmony_ci	                  &ctrl_size))
7562306a36Sopenharmony_ci		goto err;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	len = getprop(bus_node, "ranges", cs_ranges_buf, sizeof(cs_ranges_buf));
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	for (i = 0; i < len / sizeof(struct cs_range); i++) {
8062306a36Sopenharmony_ci		u32 base, option;
8162306a36Sopenharmony_ci		int cs = cs_ranges_buf[i].csnum;
8262306a36Sopenharmony_ci		if (cs >= ctrl_size / 8)
8362306a36Sopenharmony_ci			goto err;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci		if (cs_ranges_buf[i].base != 0)
8662306a36Sopenharmony_ci			goto err;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci		base = in_be32(&ctrl_addr[cs * 2]);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci		/* If CS is already valid, use the existing flags.
9162306a36Sopenharmony_ci		 * Otherwise, guess a sane default.
9262306a36Sopenharmony_ci		 */
9362306a36Sopenharmony_ci		if (base & 1) {
9462306a36Sopenharmony_ci			base &= 0x7fff;
9562306a36Sopenharmony_ci			option = in_be32(&ctrl_addr[cs * 2 + 1]) & 0x7fff;
9662306a36Sopenharmony_ci		} else {
9762306a36Sopenharmony_ci			base = 0x1801;
9862306a36Sopenharmony_ci			option = 0x10;
9962306a36Sopenharmony_ci		}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci		out_be32(&ctrl_addr[cs * 2], 0);
10262306a36Sopenharmony_ci		out_be32(&ctrl_addr[cs * 2 + 1],
10362306a36Sopenharmony_ci		         option | ~(cs_ranges_buf[i].size - 1));
10462306a36Sopenharmony_ci		out_be32(&ctrl_addr[cs * 2], base | cs_ranges_buf[i].addr);
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	return;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cierr:
11062306a36Sopenharmony_ci	printf("Bad /localbus node\r\n");
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci/* Older u-boots don't set PCI up properly.  Update the hardware to match
11462306a36Sopenharmony_ci * the device tree.  The prefetch mem region and non-prefetch mem region
11562306a36Sopenharmony_ci * must be contiguous in the host bus.  As required by the PCI binding,
11662306a36Sopenharmony_ci * PCI #addr/#size must be 3/2.  The parent bus must be 1/1.  Only
11762306a36Sopenharmony_ci * 32-bit PCI is supported.  All three region types (prefetchable mem,
11862306a36Sopenharmony_ci * non-prefetchable mem, and I/O) must be present.
11962306a36Sopenharmony_ci */
12062306a36Sopenharmony_cistatic void fixup_pci(void)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	struct pci_range *mem = NULL, *mmio = NULL,
12362306a36Sopenharmony_ci	                 *io = NULL, *mem_base = NULL;
12462306a36Sopenharmony_ci	u32 *pci_regs[3];
12562306a36Sopenharmony_ci	u8 *soc_regs;
12662306a36Sopenharmony_ci	int i, len;
12762306a36Sopenharmony_ci	void *node, *parent_node;
12862306a36Sopenharmony_ci	u32 naddr, nsize, mem_pow2, mem_mask;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	node = finddevice("/pci");
13162306a36Sopenharmony_ci	if (!node || !dt_is_compatible(node, "fsl,pq2-pci"))
13262306a36Sopenharmony_ci		return;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	for (i = 0; i < 3; i++)
13562306a36Sopenharmony_ci		if (!dt_xlate_reg(node, i,
13662306a36Sopenharmony_ci		                  (unsigned long *)&pci_regs[i], NULL))
13762306a36Sopenharmony_ci			goto err;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	soc_regs = (u8 *)fsl_get_immr();
14062306a36Sopenharmony_ci	if (!soc_regs)
14162306a36Sopenharmony_ci		goto unhandled;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	dt_get_reg_format(node, &naddr, &nsize);
14462306a36Sopenharmony_ci	if (naddr != 3 || nsize != 2)
14562306a36Sopenharmony_ci		goto err;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	parent_node = get_parent(node);
14862306a36Sopenharmony_ci	if (!parent_node)
14962306a36Sopenharmony_ci		goto err;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	dt_get_reg_format(parent_node, &naddr, &nsize);
15262306a36Sopenharmony_ci	if (naddr != 1 || nsize != 1)
15362306a36Sopenharmony_ci		goto unhandled;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	len = getprop(node, "ranges", pci_ranges_buf,
15662306a36Sopenharmony_ci	              sizeof(pci_ranges_buf));
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	for (i = 0; i < len / sizeof(struct pci_range); i++) {
15962306a36Sopenharmony_ci		u32 flags = pci_ranges_buf[i].flags & 0x43000000;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci		if (flags == 0x42000000)
16262306a36Sopenharmony_ci			mem = &pci_ranges_buf[i];
16362306a36Sopenharmony_ci		else if (flags == 0x02000000)
16462306a36Sopenharmony_ci			mmio = &pci_ranges_buf[i];
16562306a36Sopenharmony_ci		else if (flags == 0x01000000)
16662306a36Sopenharmony_ci			io = &pci_ranges_buf[i];
16762306a36Sopenharmony_ci	}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	if (!mem || !mmio || !io)
17062306a36Sopenharmony_ci		goto unhandled;
17162306a36Sopenharmony_ci	if (mem->size[1] != mmio->size[1])
17262306a36Sopenharmony_ci		goto unhandled;
17362306a36Sopenharmony_ci	if (mem->size[1] & (mem->size[1] - 1))
17462306a36Sopenharmony_ci		goto unhandled;
17562306a36Sopenharmony_ci	if (io->size[1] & (io->size[1] - 1))
17662306a36Sopenharmony_ci		goto unhandled;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	if (mem->phys_addr + mem->size[1] == mmio->phys_addr)
17962306a36Sopenharmony_ci		mem_base = mem;
18062306a36Sopenharmony_ci	else if (mmio->phys_addr + mmio->size[1] == mem->phys_addr)
18162306a36Sopenharmony_ci		mem_base = mmio;
18262306a36Sopenharmony_ci	else
18362306a36Sopenharmony_ci		goto unhandled;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	out_be32(&pci_regs[1][0], mem_base->phys_addr | 1);
18662306a36Sopenharmony_ci	out_be32(&pci_regs[2][0], ~(mem->size[1] + mmio->size[1] - 1));
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	out_be32(&pci_regs[1][1], io->phys_addr | 1);
18962306a36Sopenharmony_ci	out_be32(&pci_regs[2][1], ~(io->size[1] - 1));
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	out_le32(&pci_regs[0][0], mem->pci_addr[1] >> 12);
19262306a36Sopenharmony_ci	out_le32(&pci_regs[0][2], mem->phys_addr >> 12);
19362306a36Sopenharmony_ci	out_le32(&pci_regs[0][4], (~(mem->size[1] - 1) >> 12) | 0xa0000000);
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	out_le32(&pci_regs[0][6], mmio->pci_addr[1] >> 12);
19662306a36Sopenharmony_ci	out_le32(&pci_regs[0][8], mmio->phys_addr >> 12);
19762306a36Sopenharmony_ci	out_le32(&pci_regs[0][10], (~(mmio->size[1] - 1) >> 12) | 0x80000000);
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	out_le32(&pci_regs[0][12], io->pci_addr[1] >> 12);
20062306a36Sopenharmony_ci	out_le32(&pci_regs[0][14], io->phys_addr >> 12);
20162306a36Sopenharmony_ci	out_le32(&pci_regs[0][16], (~(io->size[1] - 1) >> 12) | 0xc0000000);
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	/* Inbound translation */
20462306a36Sopenharmony_ci	out_le32(&pci_regs[0][58], 0);
20562306a36Sopenharmony_ci	out_le32(&pci_regs[0][60], 0);
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	mem_pow2 = 1 << (__ilog2_u32(bd.bi_memsize - 1) + 1);
20862306a36Sopenharmony_ci	mem_mask = ~(mem_pow2 - 1) >> 12;
20962306a36Sopenharmony_ci	out_le32(&pci_regs[0][62], 0xa0000000 | mem_mask);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	/* If PCI is disabled, drive RST high to enable. */
21262306a36Sopenharmony_ci	if (!(in_le32(&pci_regs[0][32]) & 1)) {
21362306a36Sopenharmony_ci		 /* Tpvrh (Power valid to RST# high) 100 ms */
21462306a36Sopenharmony_ci		udelay(100000);
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci		out_le32(&pci_regs[0][32], 1);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci		/* Trhfa (RST# high to first cfg access) 2^25 clocks */
21962306a36Sopenharmony_ci		udelay(1020000);
22062306a36Sopenharmony_ci	}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	/* Enable bus master and memory access */
22362306a36Sopenharmony_ci	out_le32(&pci_regs[0][64], 0x80000004);
22462306a36Sopenharmony_ci	out_le32(&pci_regs[0][65], in_le32(&pci_regs[0][65]) | 6);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	/* Park the bus on PCI, and elevate PCI's arbitration priority,
22762306a36Sopenharmony_ci	 * as required by section 9.6 of the user's manual.
22862306a36Sopenharmony_ci	 */
22962306a36Sopenharmony_ci	out_8(&soc_regs[0x10028], 3);
23062306a36Sopenharmony_ci	out_be32((u32 *)&soc_regs[0x1002c], 0x01236745);
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	return;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_cierr:
23562306a36Sopenharmony_ci	printf("Bad PCI node -- using existing firmware setup.\r\n");
23662306a36Sopenharmony_ci	return;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ciunhandled:
23962306a36Sopenharmony_ci	printf("Unsupported PCI node -- using existing firmware setup.\r\n");
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_cistatic void pq2_platform_fixups(void)
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci	void *node;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	dt_fixup_memory(bd.bi_memstart, bd.bi_memsize);
24762306a36Sopenharmony_ci	dt_fixup_mac_addresses(bd.bi_enetaddr, bd.bi_enet1addr);
24862306a36Sopenharmony_ci	dt_fixup_cpu_clocks(bd.bi_intfreq, bd.bi_busfreq / 4, bd.bi_busfreq);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	node = finddevice("/soc/cpm");
25162306a36Sopenharmony_ci	if (node)
25262306a36Sopenharmony_ci		setprop(node, "clock-frequency", &bd.bi_cpmfreq, 4);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	node = finddevice("/soc/cpm/brg");
25562306a36Sopenharmony_ci	if (node)
25662306a36Sopenharmony_ci		setprop(node, "clock-frequency",  &bd.bi_brgfreq, 4);
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	update_cs_ranges();
25962306a36Sopenharmony_ci	fixup_pci();
26062306a36Sopenharmony_ci}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_civoid platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
26362306a36Sopenharmony_ci                   unsigned long r6, unsigned long r7)
26462306a36Sopenharmony_ci{
26562306a36Sopenharmony_ci	CUBOOT_INIT();
26662306a36Sopenharmony_ci	fdt_init(_dtb_start);
26762306a36Sopenharmony_ci	serial_console_init();
26862306a36Sopenharmony_ci	platform_ops.fixups = pq2_platform_fixups;
26962306a36Sopenharmony_ci}
270