162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  cx18 firmware functions
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
662306a36Sopenharmony_ci *  Copyright (C) 2008  Andy Walls <awalls@md.metrocast.net>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include "cx18-driver.h"
1062306a36Sopenharmony_ci#include "cx18-io.h"
1162306a36Sopenharmony_ci#include "cx18-scb.h"
1262306a36Sopenharmony_ci#include "cx18-irq.h"
1362306a36Sopenharmony_ci#include "cx18-firmware.h"
1462306a36Sopenharmony_ci#include "cx18-cards.h"
1562306a36Sopenharmony_ci#include <linux/firmware.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#define CX18_PROC_SOFT_RESET		0xc70010
1862306a36Sopenharmony_ci#define CX18_DDR_SOFT_RESET		0xc70014
1962306a36Sopenharmony_ci#define CX18_CLOCK_SELECT1		0xc71000
2062306a36Sopenharmony_ci#define CX18_CLOCK_SELECT2		0xc71004
2162306a36Sopenharmony_ci#define CX18_HALF_CLOCK_SELECT1		0xc71008
2262306a36Sopenharmony_ci#define CX18_HALF_CLOCK_SELECT2		0xc7100C
2362306a36Sopenharmony_ci#define CX18_CLOCK_POLARITY1		0xc71010
2462306a36Sopenharmony_ci#define CX18_CLOCK_POLARITY2		0xc71014
2562306a36Sopenharmony_ci#define CX18_ADD_DELAY_ENABLE1		0xc71018
2662306a36Sopenharmony_ci#define CX18_ADD_DELAY_ENABLE2		0xc7101C
2762306a36Sopenharmony_ci#define CX18_CLOCK_ENABLE1		0xc71020
2862306a36Sopenharmony_ci#define CX18_CLOCK_ENABLE2		0xc71024
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define CX18_REG_BUS_TIMEOUT_EN		0xc72024
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#define CX18_FAST_CLOCK_PLL_INT		0xc78000
3362306a36Sopenharmony_ci#define CX18_FAST_CLOCK_PLL_FRAC	0xc78004
3462306a36Sopenharmony_ci#define CX18_FAST_CLOCK_PLL_POST	0xc78008
3562306a36Sopenharmony_ci#define CX18_FAST_CLOCK_PLL_PRESCALE	0xc7800C
3662306a36Sopenharmony_ci#define CX18_FAST_CLOCK_PLL_ADJUST_BANDWIDTH 0xc78010
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#define CX18_SLOW_CLOCK_PLL_INT		0xc78014
3962306a36Sopenharmony_ci#define CX18_SLOW_CLOCK_PLL_FRAC	0xc78018
4062306a36Sopenharmony_ci#define CX18_SLOW_CLOCK_PLL_POST	0xc7801C
4162306a36Sopenharmony_ci#define CX18_MPEG_CLOCK_PLL_INT		0xc78040
4262306a36Sopenharmony_ci#define CX18_MPEG_CLOCK_PLL_FRAC	0xc78044
4362306a36Sopenharmony_ci#define CX18_MPEG_CLOCK_PLL_POST	0xc78048
4462306a36Sopenharmony_ci#define CX18_PLL_POWER_DOWN		0xc78088
4562306a36Sopenharmony_ci#define CX18_SW1_INT_STATUS             0xc73104
4662306a36Sopenharmony_ci#define CX18_SW1_INT_ENABLE_PCI         0xc7311C
4762306a36Sopenharmony_ci#define CX18_SW2_INT_SET                0xc73140
4862306a36Sopenharmony_ci#define CX18_SW2_INT_STATUS             0xc73144
4962306a36Sopenharmony_ci#define CX18_ADEC_CONTROL		0xc78120
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci#define CX18_DDR_REQUEST_ENABLE		0xc80000
5262306a36Sopenharmony_ci#define CX18_DDR_CHIP_CONFIG		0xc80004
5362306a36Sopenharmony_ci#define CX18_DDR_REFRESH		0xc80008
5462306a36Sopenharmony_ci#define CX18_DDR_TIMING1		0xc8000C
5562306a36Sopenharmony_ci#define CX18_DDR_TIMING2		0xc80010
5662306a36Sopenharmony_ci#define CX18_DDR_POWER_REG		0xc8001C
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci#define CX18_DDR_TUNE_LANE		0xc80048
5962306a36Sopenharmony_ci#define CX18_DDR_INITIAL_EMRS		0xc80054
6062306a36Sopenharmony_ci#define CX18_DDR_MB_PER_ROW_7		0xc8009C
6162306a36Sopenharmony_ci#define CX18_DDR_BASE_63_ADDR		0xc804FC
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci#define CX18_WMB_CLIENT02		0xc90108
6462306a36Sopenharmony_ci#define CX18_WMB_CLIENT05		0xc90114
6562306a36Sopenharmony_ci#define CX18_WMB_CLIENT06		0xc90118
6662306a36Sopenharmony_ci#define CX18_WMB_CLIENT07		0xc9011C
6762306a36Sopenharmony_ci#define CX18_WMB_CLIENT08		0xc90120
6862306a36Sopenharmony_ci#define CX18_WMB_CLIENT09		0xc90124
6962306a36Sopenharmony_ci#define CX18_WMB_CLIENT10		0xc90128
7062306a36Sopenharmony_ci#define CX18_WMB_CLIENT11		0xc9012C
7162306a36Sopenharmony_ci#define CX18_WMB_CLIENT12		0xc90130
7262306a36Sopenharmony_ci#define CX18_WMB_CLIENT13		0xc90134
7362306a36Sopenharmony_ci#define CX18_WMB_CLIENT14		0xc90138
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci#define CX18_DSP0_INTERRUPT_MASK	0xd0004C
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci#define APU_ROM_SYNC1 0x6D676553 /* "mgeS" */
7862306a36Sopenharmony_ci#define APU_ROM_SYNC2 0x72646548 /* "rdeH" */
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistruct cx18_apu_rom_seghdr {
8162306a36Sopenharmony_ci	u32 sync1;
8262306a36Sopenharmony_ci	u32 sync2;
8362306a36Sopenharmony_ci	u32 addr;
8462306a36Sopenharmony_ci	u32 size;
8562306a36Sopenharmony_ci};
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic int load_cpu_fw_direct(const char *fn, u8 __iomem *mem, struct cx18 *cx)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	const struct firmware *fw = NULL;
9062306a36Sopenharmony_ci	int i, j;
9162306a36Sopenharmony_ci	unsigned size;
9262306a36Sopenharmony_ci	u32 __iomem *dst = (u32 __iomem *)mem;
9362306a36Sopenharmony_ci	const u32 *src;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	if (request_firmware(&fw, fn, &cx->pci_dev->dev)) {
9662306a36Sopenharmony_ci		CX18_ERR("Unable to open firmware %s\n", fn);
9762306a36Sopenharmony_ci		CX18_ERR("Did you put the firmware in the hotplug firmware directory?\n");
9862306a36Sopenharmony_ci		return -ENOMEM;
9962306a36Sopenharmony_ci	}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	src = (const u32 *)fw->data;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	for (i = 0; i < fw->size; i += 4096) {
10462306a36Sopenharmony_ci		cx18_setup_page(cx, i);
10562306a36Sopenharmony_ci		for (j = i; j < fw->size && j < i + 4096; j += 4) {
10662306a36Sopenharmony_ci			/* no need for endianness conversion on the ppc */
10762306a36Sopenharmony_ci			cx18_raw_writel(cx, *src, dst);
10862306a36Sopenharmony_ci			if (cx18_raw_readl(cx, dst) != *src) {
10962306a36Sopenharmony_ci				CX18_ERR("Mismatch at offset %x\n", i);
11062306a36Sopenharmony_ci				release_firmware(fw);
11162306a36Sopenharmony_ci				cx18_setup_page(cx, 0);
11262306a36Sopenharmony_ci				return -EIO;
11362306a36Sopenharmony_ci			}
11462306a36Sopenharmony_ci			dst++;
11562306a36Sopenharmony_ci			src++;
11662306a36Sopenharmony_ci		}
11762306a36Sopenharmony_ci	}
11862306a36Sopenharmony_ci	if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags))
11962306a36Sopenharmony_ci		CX18_INFO("loaded %s firmware (%zu bytes)\n", fn, fw->size);
12062306a36Sopenharmony_ci	size = fw->size;
12162306a36Sopenharmony_ci	release_firmware(fw);
12262306a36Sopenharmony_ci	cx18_setup_page(cx, SCB_OFFSET);
12362306a36Sopenharmony_ci	return size;
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx,
12762306a36Sopenharmony_ci				u32 *entry_addr)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	const struct firmware *fw = NULL;
13062306a36Sopenharmony_ci	int i, j;
13162306a36Sopenharmony_ci	unsigned size;
13262306a36Sopenharmony_ci	const u32 *src;
13362306a36Sopenharmony_ci	struct cx18_apu_rom_seghdr seghdr;
13462306a36Sopenharmony_ci	const u8 *vers;
13562306a36Sopenharmony_ci	u32 offset = 0;
13662306a36Sopenharmony_ci	u32 apu_version = 0;
13762306a36Sopenharmony_ci	int sz;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	if (request_firmware(&fw, fn, &cx->pci_dev->dev)) {
14062306a36Sopenharmony_ci		CX18_ERR("unable to open firmware %s\n", fn);
14162306a36Sopenharmony_ci		CX18_ERR("did you put the firmware in the hotplug firmware directory?\n");
14262306a36Sopenharmony_ci		cx18_setup_page(cx, 0);
14362306a36Sopenharmony_ci		return -ENOMEM;
14462306a36Sopenharmony_ci	}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	*entry_addr = 0;
14762306a36Sopenharmony_ci	src = (const u32 *)fw->data;
14862306a36Sopenharmony_ci	vers = fw->data + sizeof(seghdr);
14962306a36Sopenharmony_ci	sz = fw->size;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	apu_version = (vers[0] << 24) | (vers[4] << 16) | vers[32];
15262306a36Sopenharmony_ci	while (offset + sizeof(seghdr) < fw->size) {
15362306a36Sopenharmony_ci		const __le32 *shptr = (__force __le32 *)src + offset / 4;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci		seghdr.sync1 = le32_to_cpu(shptr[0]);
15662306a36Sopenharmony_ci		seghdr.sync2 = le32_to_cpu(shptr[1]);
15762306a36Sopenharmony_ci		seghdr.addr = le32_to_cpu(shptr[2]);
15862306a36Sopenharmony_ci		seghdr.size = le32_to_cpu(shptr[3]);
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci		offset += sizeof(seghdr);
16162306a36Sopenharmony_ci		if (seghdr.sync1 != APU_ROM_SYNC1 ||
16262306a36Sopenharmony_ci		    seghdr.sync2 != APU_ROM_SYNC2) {
16362306a36Sopenharmony_ci			offset += seghdr.size;
16462306a36Sopenharmony_ci			continue;
16562306a36Sopenharmony_ci		}
16662306a36Sopenharmony_ci		CX18_DEBUG_INFO("load segment %x-%x\n", seghdr.addr,
16762306a36Sopenharmony_ci				seghdr.addr + seghdr.size - 1);
16862306a36Sopenharmony_ci		if (*entry_addr == 0)
16962306a36Sopenharmony_ci			*entry_addr = seghdr.addr;
17062306a36Sopenharmony_ci		if (offset + seghdr.size > sz)
17162306a36Sopenharmony_ci			break;
17262306a36Sopenharmony_ci		for (i = 0; i < seghdr.size; i += 4096) {
17362306a36Sopenharmony_ci			cx18_setup_page(cx, seghdr.addr + i);
17462306a36Sopenharmony_ci			for (j = i; j < seghdr.size && j < i + 4096; j += 4) {
17562306a36Sopenharmony_ci				/* no need for endianness conversion on the ppc */
17662306a36Sopenharmony_ci				cx18_raw_writel(cx, src[(offset + j) / 4],
17762306a36Sopenharmony_ci						dst + seghdr.addr + j);
17862306a36Sopenharmony_ci				if (cx18_raw_readl(cx, dst + seghdr.addr + j)
17962306a36Sopenharmony_ci				    != src[(offset + j) / 4]) {
18062306a36Sopenharmony_ci					CX18_ERR("Mismatch at offset %x\n",
18162306a36Sopenharmony_ci						 offset + j);
18262306a36Sopenharmony_ci					release_firmware(fw);
18362306a36Sopenharmony_ci					cx18_setup_page(cx, 0);
18462306a36Sopenharmony_ci					return -EIO;
18562306a36Sopenharmony_ci				}
18662306a36Sopenharmony_ci			}
18762306a36Sopenharmony_ci		}
18862306a36Sopenharmony_ci		offset += seghdr.size;
18962306a36Sopenharmony_ci	}
19062306a36Sopenharmony_ci	if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags))
19162306a36Sopenharmony_ci		CX18_INFO("loaded %s firmware V%08x (%zu bytes)\n",
19262306a36Sopenharmony_ci				fn, apu_version, fw->size);
19362306a36Sopenharmony_ci	size = fw->size;
19462306a36Sopenharmony_ci	release_firmware(fw);
19562306a36Sopenharmony_ci	cx18_setup_page(cx, 0);
19662306a36Sopenharmony_ci	return size;
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_civoid cx18_halt_firmware(struct cx18 *cx)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	CX18_DEBUG_INFO("Preparing for firmware halt.\n");
20262306a36Sopenharmony_ci	cx18_write_reg_expect(cx, 0x000F000F, CX18_PROC_SOFT_RESET,
20362306a36Sopenharmony_ci				  0x0000000F, 0x000F000F);
20462306a36Sopenharmony_ci	cx18_write_reg_expect(cx, 0x00020002, CX18_ADEC_CONTROL,
20562306a36Sopenharmony_ci				  0x00000002, 0x00020002);
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_civoid cx18_init_power(struct cx18 *cx, int lowpwr)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	/* power-down Spare and AOM PLLs */
21162306a36Sopenharmony_ci	/* power-up fast, slow and mpeg PLLs */
21262306a36Sopenharmony_ci	cx18_write_reg(cx, 0x00000008, CX18_PLL_POWER_DOWN);
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	/* ADEC out of sleep */
21562306a36Sopenharmony_ci	cx18_write_reg_expect(cx, 0x00020000, CX18_ADEC_CONTROL,
21662306a36Sopenharmony_ci				  0x00000000, 0x00020002);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	/*
21962306a36Sopenharmony_ci	 * The PLL parameters are based on the external crystal frequency that
22062306a36Sopenharmony_ci	 * would ideally be:
22162306a36Sopenharmony_ci	 *
22262306a36Sopenharmony_ci	 * NTSC Color subcarrier freq * 8 =
22362306a36Sopenharmony_ci	 *	4.5 MHz/286 * 455/2 * 8 = 28.63636363... MHz
22462306a36Sopenharmony_ci	 *
22562306a36Sopenharmony_ci	 * The accidents of history and rationale that explain from where this
22662306a36Sopenharmony_ci	 * combination of magic numbers originate can be found in:
22762306a36Sopenharmony_ci	 *
22862306a36Sopenharmony_ci	 * [1] Abrahams, I. C., "Choice of Chrominance Subcarrier Frequency in
22962306a36Sopenharmony_ci	 * the NTSC Standards", Proceedings of the I-R-E, January 1954, pp 79-80
23062306a36Sopenharmony_ci	 *
23162306a36Sopenharmony_ci	 * [2] Abrahams, I. C., "The 'Frequency Interleaving' Principle in the
23262306a36Sopenharmony_ci	 * NTSC Standards", Proceedings of the I-R-E, January 1954, pp 81-83
23362306a36Sopenharmony_ci	 *
23462306a36Sopenharmony_ci	 * As Mike Bradley has rightly pointed out, it's not the exact crystal
23562306a36Sopenharmony_ci	 * frequency that matters, only that all parts of the driver and
23662306a36Sopenharmony_ci	 * firmware are using the same value (close to the ideal value).
23762306a36Sopenharmony_ci	 *
23862306a36Sopenharmony_ci	 * Since I have a strong suspicion that, if the firmware ever assumes a
23962306a36Sopenharmony_ci	 * crystal value at all, it will assume 28.636360 MHz, the crystal
24062306a36Sopenharmony_ci	 * freq used in calculations in this driver will be:
24162306a36Sopenharmony_ci	 *
24262306a36Sopenharmony_ci	 *	xtal_freq = 28.636360 MHz
24362306a36Sopenharmony_ci	 *
24462306a36Sopenharmony_ci	 * an error of less than 0.13 ppm which is way, way better than any off
24562306a36Sopenharmony_ci	 * the shelf crystal will have for accuracy anyway.
24662306a36Sopenharmony_ci	 *
24762306a36Sopenharmony_ci	 * Below I aim to run the PLLs' VCOs near 400 MHz to minimize errors.
24862306a36Sopenharmony_ci	 *
24962306a36Sopenharmony_ci	 * Many thanks to Jeff Campbell and Mike Bradley for their extensive
25062306a36Sopenharmony_ci	 * investigation, experimentation, testing, and suggested solutions of
25162306a36Sopenharmony_ci	 * audio/video sync problems with SVideo and CVBS captures.
25262306a36Sopenharmony_ci	 */
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	/* the fast clock is at 200/245 MHz */
25562306a36Sopenharmony_ci	/* 1 * xtal_freq * 0x0d.f7df9b8 / 2 = 200 MHz: 400 MHz pre post-divide*/
25662306a36Sopenharmony_ci	/* 1 * xtal_freq * 0x11.1c71eb8 / 2 = 245 MHz: 490 MHz pre post-divide*/
25762306a36Sopenharmony_ci	cx18_write_reg(cx, lowpwr ? 0xD : 0x11, CX18_FAST_CLOCK_PLL_INT);
25862306a36Sopenharmony_ci	cx18_write_reg(cx, lowpwr ? 0x1EFBF37 : 0x038E3D7,
25962306a36Sopenharmony_ci						CX18_FAST_CLOCK_PLL_FRAC);
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	cx18_write_reg(cx, 2, CX18_FAST_CLOCK_PLL_POST);
26262306a36Sopenharmony_ci	cx18_write_reg(cx, 1, CX18_FAST_CLOCK_PLL_PRESCALE);
26362306a36Sopenharmony_ci	cx18_write_reg(cx, 4, CX18_FAST_CLOCK_PLL_ADJUST_BANDWIDTH);
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	/* set slow clock to 125/120 MHz */
26662306a36Sopenharmony_ci	/* xtal_freq * 0x0d.1861a20 / 3 = 125 MHz: 375 MHz before post-divide */
26762306a36Sopenharmony_ci	/* xtal_freq * 0x0c.92493f8 / 3 = 120 MHz: 360 MHz before post-divide */
26862306a36Sopenharmony_ci	cx18_write_reg(cx, lowpwr ? 0xD : 0xC, CX18_SLOW_CLOCK_PLL_INT);
26962306a36Sopenharmony_ci	cx18_write_reg(cx, lowpwr ? 0x30C344 : 0x124927F,
27062306a36Sopenharmony_ci						CX18_SLOW_CLOCK_PLL_FRAC);
27162306a36Sopenharmony_ci	cx18_write_reg(cx, 3, CX18_SLOW_CLOCK_PLL_POST);
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	/* mpeg clock pll 54MHz */
27462306a36Sopenharmony_ci	/* xtal_freq * 0xf.15f17f0 / 8 = 54 MHz: 432 MHz before post-divide */
27562306a36Sopenharmony_ci	cx18_write_reg(cx, 0xF, CX18_MPEG_CLOCK_PLL_INT);
27662306a36Sopenharmony_ci	cx18_write_reg(cx, 0x2BE2FE, CX18_MPEG_CLOCK_PLL_FRAC);
27762306a36Sopenharmony_ci	cx18_write_reg(cx, 8, CX18_MPEG_CLOCK_PLL_POST);
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	/* Defaults */
28062306a36Sopenharmony_ci	/* APU = SC or SC/2 = 125/62.5 */
28162306a36Sopenharmony_ci	/* EPU = SC = 125 */
28262306a36Sopenharmony_ci	/* DDR = FC = 180 */
28362306a36Sopenharmony_ci	/* ENC = SC = 125 */
28462306a36Sopenharmony_ci	/* AI1 = SC = 125 */
28562306a36Sopenharmony_ci	/* VIM2 = disabled */
28662306a36Sopenharmony_ci	/* PCI = FC/2 = 90 */
28762306a36Sopenharmony_ci	/* AI2 = disabled */
28862306a36Sopenharmony_ci	/* DEMUX = disabled */
28962306a36Sopenharmony_ci	/* AO = SC/2 = 62.5 */
29062306a36Sopenharmony_ci	/* SER = 54MHz */
29162306a36Sopenharmony_ci	/* VFC = disabled */
29262306a36Sopenharmony_ci	/* USB = disabled */
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	if (lowpwr) {
29562306a36Sopenharmony_ci		cx18_write_reg_expect(cx, 0xFFFF0020, CX18_CLOCK_SELECT1,
29662306a36Sopenharmony_ci					  0x00000020, 0xFFFFFFFF);
29762306a36Sopenharmony_ci		cx18_write_reg_expect(cx, 0xFFFF0004, CX18_CLOCK_SELECT2,
29862306a36Sopenharmony_ci					  0x00000004, 0xFFFFFFFF);
29962306a36Sopenharmony_ci	} else {
30062306a36Sopenharmony_ci		/* This doesn't explicitly set every clock select */
30162306a36Sopenharmony_ci		cx18_write_reg_expect(cx, 0x00060004, CX18_CLOCK_SELECT1,
30262306a36Sopenharmony_ci					  0x00000004, 0x00060006);
30362306a36Sopenharmony_ci		cx18_write_reg_expect(cx, 0x00060006, CX18_CLOCK_SELECT2,
30462306a36Sopenharmony_ci					  0x00000006, 0x00060006);
30562306a36Sopenharmony_ci	}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	cx18_write_reg_expect(cx, 0xFFFF0002, CX18_HALF_CLOCK_SELECT1,
30862306a36Sopenharmony_ci				  0x00000002, 0xFFFFFFFF);
30962306a36Sopenharmony_ci	cx18_write_reg_expect(cx, 0xFFFF0104, CX18_HALF_CLOCK_SELECT2,
31062306a36Sopenharmony_ci				  0x00000104, 0xFFFFFFFF);
31162306a36Sopenharmony_ci	cx18_write_reg_expect(cx, 0xFFFF9026, CX18_CLOCK_ENABLE1,
31262306a36Sopenharmony_ci				  0x00009026, 0xFFFFFFFF);
31362306a36Sopenharmony_ci	cx18_write_reg_expect(cx, 0xFFFF3105, CX18_CLOCK_ENABLE2,
31462306a36Sopenharmony_ci				  0x00003105, 0xFFFFFFFF);
31562306a36Sopenharmony_ci}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_civoid cx18_init_memory(struct cx18 *cx)
31862306a36Sopenharmony_ci{
31962306a36Sopenharmony_ci	cx18_msleep_timeout(10, 0);
32062306a36Sopenharmony_ci	cx18_write_reg_expect(cx, 0x00010000, CX18_DDR_SOFT_RESET,
32162306a36Sopenharmony_ci				  0x00000000, 0x00010001);
32262306a36Sopenharmony_ci	cx18_msleep_timeout(10, 0);
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	cx18_write_reg(cx, cx->card->ddr.chip_config, CX18_DDR_CHIP_CONFIG);
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	cx18_msleep_timeout(10, 0);
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	cx18_write_reg(cx, cx->card->ddr.refresh, CX18_DDR_REFRESH);
32962306a36Sopenharmony_ci	cx18_write_reg(cx, cx->card->ddr.timing1, CX18_DDR_TIMING1);
33062306a36Sopenharmony_ci	cx18_write_reg(cx, cx->card->ddr.timing2, CX18_DDR_TIMING2);
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	cx18_msleep_timeout(10, 0);
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	/* Initialize DQS pad time */
33562306a36Sopenharmony_ci	cx18_write_reg(cx, cx->card->ddr.tune_lane, CX18_DDR_TUNE_LANE);
33662306a36Sopenharmony_ci	cx18_write_reg(cx, cx->card->ddr.initial_emrs, CX18_DDR_INITIAL_EMRS);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	cx18_msleep_timeout(10, 0);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	cx18_write_reg_expect(cx, 0x00020000, CX18_DDR_SOFT_RESET,
34162306a36Sopenharmony_ci				  0x00000000, 0x00020002);
34262306a36Sopenharmony_ci	cx18_msleep_timeout(10, 0);
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	/* use power-down mode when idle */
34562306a36Sopenharmony_ci	cx18_write_reg(cx, 0x00000010, CX18_DDR_POWER_REG);
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	cx18_write_reg_expect(cx, 0x00010001, CX18_REG_BUS_TIMEOUT_EN,
34862306a36Sopenharmony_ci				  0x00000001, 0x00010001);
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	cx18_write_reg(cx, 0x48, CX18_DDR_MB_PER_ROW_7);
35162306a36Sopenharmony_ci	cx18_write_reg(cx, 0xE0000, CX18_DDR_BASE_63_ADDR);
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT02);  /* AO */
35462306a36Sopenharmony_ci	cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT09);  /* AI2 */
35562306a36Sopenharmony_ci	cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT05);  /* VIM1 */
35662306a36Sopenharmony_ci	cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT06);  /* AI1 */
35762306a36Sopenharmony_ci	cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT07);  /* 3D comb */
35862306a36Sopenharmony_ci	cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT10);  /* ME */
35962306a36Sopenharmony_ci	cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT12);  /* ENC */
36062306a36Sopenharmony_ci	cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT13);  /* PK */
36162306a36Sopenharmony_ci	cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT11);  /* RC */
36262306a36Sopenharmony_ci	cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT14);  /* AVO */
36362306a36Sopenharmony_ci}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci#define CX18_CPU_FIRMWARE "v4l-cx23418-cpu.fw"
36662306a36Sopenharmony_ci#define CX18_APU_FIRMWARE "v4l-cx23418-apu.fw"
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ciint cx18_firmware_init(struct cx18 *cx)
36962306a36Sopenharmony_ci{
37062306a36Sopenharmony_ci	u32 fw_entry_addr;
37162306a36Sopenharmony_ci	int sz, retries;
37262306a36Sopenharmony_ci	u32 api_args[MAX_MB_ARGUMENTS];
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	/* Allow chip to control CLKRUN */
37562306a36Sopenharmony_ci	cx18_write_reg(cx, 0x5, CX18_DSP0_INTERRUPT_MASK);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	/* Stop the firmware */
37862306a36Sopenharmony_ci	cx18_write_reg_expect(cx, 0x000F000F, CX18_PROC_SOFT_RESET,
37962306a36Sopenharmony_ci				  0x0000000F, 0x000F000F);
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	cx18_msleep_timeout(1, 0);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	/* If the CPU is still running */
38462306a36Sopenharmony_ci	if ((cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 8) == 0) {
38562306a36Sopenharmony_ci		CX18_ERR("%s: couldn't stop CPU to load firmware\n", __func__);
38662306a36Sopenharmony_ci		return -EIO;
38762306a36Sopenharmony_ci	}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	cx18_sw1_irq_enable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU);
39062306a36Sopenharmony_ci	cx18_sw2_irq_enable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK);
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	sz = load_cpu_fw_direct(CX18_CPU_FIRMWARE, cx->enc_mem, cx);
39362306a36Sopenharmony_ci	if (sz <= 0)
39462306a36Sopenharmony_ci		return sz;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	/* The SCB & IPC area *must* be correct before starting the firmwares */
39762306a36Sopenharmony_ci	cx18_init_scb(cx);
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	fw_entry_addr = 0;
40062306a36Sopenharmony_ci	sz = load_apu_fw_direct(CX18_APU_FIRMWARE, cx->enc_mem, cx,
40162306a36Sopenharmony_ci				&fw_entry_addr);
40262306a36Sopenharmony_ci	if (sz <= 0)
40362306a36Sopenharmony_ci		return sz;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	/* Start the CPU. The CPU will take care of the APU for us. */
40662306a36Sopenharmony_ci	cx18_write_reg_expect(cx, 0x00080000, CX18_PROC_SOFT_RESET,
40762306a36Sopenharmony_ci				  0x00000000, 0x00080008);
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	/* Wait up to 500 ms for the APU to come out of reset */
41062306a36Sopenharmony_ci	for (retries = 0;
41162306a36Sopenharmony_ci	     retries < 50 && (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 1) == 1;
41262306a36Sopenharmony_ci	     retries++)
41362306a36Sopenharmony_ci		cx18_msleep_timeout(10, 0);
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	cx18_msleep_timeout(200, 0);
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	if (retries == 50 &&
41862306a36Sopenharmony_ci	    (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 1) == 1) {
41962306a36Sopenharmony_ci		CX18_ERR("Could not start the CPU\n");
42062306a36Sopenharmony_ci		return -EIO;
42162306a36Sopenharmony_ci	}
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	/*
42462306a36Sopenharmony_ci	 * The CPU had once before set up to receive an interrupt for it's
42562306a36Sopenharmony_ci	 * outgoing IRQ_CPU_TO_EPU_ACK to us.  If it ever does this, we get an
42662306a36Sopenharmony_ci	 * interrupt when it sends us an ack, but by the time we process it,
42762306a36Sopenharmony_ci	 * that flag in the SW2 status register has been cleared by the CPU
42862306a36Sopenharmony_ci	 * firmware.  We'll prevent that not so useful condition from happening
42962306a36Sopenharmony_ci	 * by clearing the CPU's interrupt enables for Ack IRQ's we want to
43062306a36Sopenharmony_ci	 * process.
43162306a36Sopenharmony_ci	 */
43262306a36Sopenharmony_ci	cx18_sw2_irq_disable_cpu(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK);
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	/* Try a benign command to see if the CPU is alive and well */
43562306a36Sopenharmony_ci	sz = cx18_vapi_result(cx, api_args, CX18_CPU_DEBUG_PEEK32, 1, 0);
43662306a36Sopenharmony_ci	if (sz < 0)
43762306a36Sopenharmony_ci		return sz;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	/* initialize GPIO */
44062306a36Sopenharmony_ci	cx18_write_reg_expect(cx, 0x14001400, 0xc78110, 0x00001400, 0x14001400);
44162306a36Sopenharmony_ci	return 0;
44262306a36Sopenharmony_ci}
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ciMODULE_FIRMWARE(CX18_CPU_FIRMWARE);
44562306a36Sopenharmony_ciMODULE_FIRMWARE(CX18_APU_FIRMWARE);
446