162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright 2011 Advanced Micro Devices, Inc.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
562306a36Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
662306a36Sopenharmony_ci * to deal in the Software without restriction, including without limitation
762306a36Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
862306a36Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
962306a36Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * The above copyright notice and this permission notice shall be included in
1262306a36Sopenharmony_ci * all copies or substantial portions of the Software.
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1562306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1662306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
1762306a36Sopenharmony_ci * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
1862306a36Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
1962306a36Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
2062306a36Sopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE.
2162306a36Sopenharmony_ci *
2262306a36Sopenharmony_ci * Authors: Alex Deucher
2362306a36Sopenharmony_ci */
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include <linux/firmware.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#include "radeon.h"
2862306a36Sopenharmony_ci#include "rv770d.h"
2962306a36Sopenharmony_ci#include "rv770_dpm.h"
3062306a36Sopenharmony_ci#include "rv770_smc.h"
3162306a36Sopenharmony_ci#include "atom.h"
3262306a36Sopenharmony_ci#include "radeon_ucode.h"
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#define FIRST_SMC_INT_VECT_REG 0xFFD8
3562306a36Sopenharmony_ci#define FIRST_INT_VECT_S19     0xFFC0
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic const u8 rv770_smc_int_vectors[] = {
3862306a36Sopenharmony_ci	0x08, 0x10, 0x08, 0x10,
3962306a36Sopenharmony_ci	0x08, 0x10, 0x08, 0x10,
4062306a36Sopenharmony_ci	0x08, 0x10, 0x08, 0x10,
4162306a36Sopenharmony_ci	0x08, 0x10, 0x08, 0x10,
4262306a36Sopenharmony_ci	0x08, 0x10, 0x08, 0x10,
4362306a36Sopenharmony_ci	0x08, 0x10, 0x08, 0x10,
4462306a36Sopenharmony_ci	0x08, 0x10, 0x08, 0x10,
4562306a36Sopenharmony_ci	0x08, 0x10, 0x08, 0x10,
4662306a36Sopenharmony_ci	0x08, 0x10, 0x08, 0x10,
4762306a36Sopenharmony_ci	0x08, 0x10, 0x08, 0x10,
4862306a36Sopenharmony_ci	0x08, 0x10, 0x08, 0x10,
4962306a36Sopenharmony_ci	0x08, 0x10, 0x08, 0x10,
5062306a36Sopenharmony_ci	0x08, 0x10, 0x0C, 0xD7,
5162306a36Sopenharmony_ci	0x08, 0x2B, 0x08, 0x10,
5262306a36Sopenharmony_ci	0x03, 0x51, 0x03, 0x51,
5362306a36Sopenharmony_ci	0x03, 0x51, 0x03, 0x51
5462306a36Sopenharmony_ci};
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic const u8 rv730_smc_int_vectors[] = {
5762306a36Sopenharmony_ci	0x08, 0x15, 0x08, 0x15,
5862306a36Sopenharmony_ci	0x08, 0x15, 0x08, 0x15,
5962306a36Sopenharmony_ci	0x08, 0x15, 0x08, 0x15,
6062306a36Sopenharmony_ci	0x08, 0x15, 0x08, 0x15,
6162306a36Sopenharmony_ci	0x08, 0x15, 0x08, 0x15,
6262306a36Sopenharmony_ci	0x08, 0x15, 0x08, 0x15,
6362306a36Sopenharmony_ci	0x08, 0x15, 0x08, 0x15,
6462306a36Sopenharmony_ci	0x08, 0x15, 0x08, 0x15,
6562306a36Sopenharmony_ci	0x08, 0x15, 0x08, 0x15,
6662306a36Sopenharmony_ci	0x08, 0x15, 0x08, 0x15,
6762306a36Sopenharmony_ci	0x08, 0x15, 0x08, 0x15,
6862306a36Sopenharmony_ci	0x08, 0x15, 0x08, 0x15,
6962306a36Sopenharmony_ci	0x08, 0x15, 0x0C, 0xBB,
7062306a36Sopenharmony_ci	0x08, 0x30, 0x08, 0x15,
7162306a36Sopenharmony_ci	0x03, 0x56, 0x03, 0x56,
7262306a36Sopenharmony_ci	0x03, 0x56, 0x03, 0x56
7362306a36Sopenharmony_ci};
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic const u8 rv710_smc_int_vectors[] = {
7662306a36Sopenharmony_ci	0x08, 0x04, 0x08, 0x04,
7762306a36Sopenharmony_ci	0x08, 0x04, 0x08, 0x04,
7862306a36Sopenharmony_ci	0x08, 0x04, 0x08, 0x04,
7962306a36Sopenharmony_ci	0x08, 0x04, 0x08, 0x04,
8062306a36Sopenharmony_ci	0x08, 0x04, 0x08, 0x04,
8162306a36Sopenharmony_ci	0x08, 0x04, 0x08, 0x04,
8262306a36Sopenharmony_ci	0x08, 0x04, 0x08, 0x04,
8362306a36Sopenharmony_ci	0x08, 0x04, 0x08, 0x04,
8462306a36Sopenharmony_ci	0x08, 0x04, 0x08, 0x04,
8562306a36Sopenharmony_ci	0x08, 0x04, 0x08, 0x04,
8662306a36Sopenharmony_ci	0x08, 0x04, 0x08, 0x04,
8762306a36Sopenharmony_ci	0x08, 0x04, 0x08, 0x04,
8862306a36Sopenharmony_ci	0x08, 0x04, 0x0C, 0xCB,
8962306a36Sopenharmony_ci	0x08, 0x1F, 0x08, 0x04,
9062306a36Sopenharmony_ci	0x03, 0x51, 0x03, 0x51,
9162306a36Sopenharmony_ci	0x03, 0x51, 0x03, 0x51
9262306a36Sopenharmony_ci};
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic const u8 rv740_smc_int_vectors[] = {
9562306a36Sopenharmony_ci	0x08, 0x10, 0x08, 0x10,
9662306a36Sopenharmony_ci	0x08, 0x10, 0x08, 0x10,
9762306a36Sopenharmony_ci	0x08, 0x10, 0x08, 0x10,
9862306a36Sopenharmony_ci	0x08, 0x10, 0x08, 0x10,
9962306a36Sopenharmony_ci	0x08, 0x10, 0x08, 0x10,
10062306a36Sopenharmony_ci	0x08, 0x10, 0x08, 0x10,
10162306a36Sopenharmony_ci	0x08, 0x10, 0x08, 0x10,
10262306a36Sopenharmony_ci	0x08, 0x10, 0x08, 0x10,
10362306a36Sopenharmony_ci	0x08, 0x10, 0x08, 0x10,
10462306a36Sopenharmony_ci	0x08, 0x10, 0x08, 0x10,
10562306a36Sopenharmony_ci	0x08, 0x10, 0x08, 0x10,
10662306a36Sopenharmony_ci	0x08, 0x10, 0x08, 0x10,
10762306a36Sopenharmony_ci	0x08, 0x10, 0x0C, 0xD7,
10862306a36Sopenharmony_ci	0x08, 0x2B, 0x08, 0x10,
10962306a36Sopenharmony_ci	0x03, 0x51, 0x03, 0x51,
11062306a36Sopenharmony_ci	0x03, 0x51, 0x03, 0x51
11162306a36Sopenharmony_ci};
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic const u8 cedar_smc_int_vectors[] = {
11462306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
11562306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
11662306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
11762306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
11862306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
11962306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
12062306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
12162306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
12262306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
12362306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
12462306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
12562306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
12662306a36Sopenharmony_ci	0x0B, 0x05, 0x11, 0x8B,
12762306a36Sopenharmony_ci	0x0B, 0x20, 0x0B, 0x05,
12862306a36Sopenharmony_ci	0x04, 0xF6, 0x04, 0xF6,
12962306a36Sopenharmony_ci	0x04, 0xF6, 0x04, 0xF6
13062306a36Sopenharmony_ci};
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistatic const u8 redwood_smc_int_vectors[] = {
13362306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
13462306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
13562306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
13662306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
13762306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
13862306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
13962306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
14062306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
14162306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
14262306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
14362306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
14462306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
14562306a36Sopenharmony_ci	0x0B, 0x05, 0x11, 0x8B,
14662306a36Sopenharmony_ci	0x0B, 0x20, 0x0B, 0x05,
14762306a36Sopenharmony_ci	0x04, 0xF6, 0x04, 0xF6,
14862306a36Sopenharmony_ci	0x04, 0xF6, 0x04, 0xF6
14962306a36Sopenharmony_ci};
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cistatic const u8 juniper_smc_int_vectors[] = {
15262306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
15362306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
15462306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
15562306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
15662306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
15762306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
15862306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
15962306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
16062306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
16162306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
16262306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
16362306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
16462306a36Sopenharmony_ci	0x0B, 0x05, 0x11, 0x8B,
16562306a36Sopenharmony_ci	0x0B, 0x20, 0x0B, 0x05,
16662306a36Sopenharmony_ci	0x04, 0xF6, 0x04, 0xF6,
16762306a36Sopenharmony_ci	0x04, 0xF6, 0x04, 0xF6
16862306a36Sopenharmony_ci};
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_cistatic const u8 cypress_smc_int_vectors[] = {
17162306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
17262306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
17362306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
17462306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
17562306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
17662306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
17762306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
17862306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
17962306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
18062306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
18162306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
18262306a36Sopenharmony_ci	0x0B, 0x05, 0x0B, 0x05,
18362306a36Sopenharmony_ci	0x0B, 0x05, 0x11, 0x8B,
18462306a36Sopenharmony_ci	0x0B, 0x20, 0x0B, 0x05,
18562306a36Sopenharmony_ci	0x04, 0xF6, 0x04, 0xF6,
18662306a36Sopenharmony_ci	0x04, 0xF6, 0x04, 0xF6
18762306a36Sopenharmony_ci};
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_cistatic const u8 barts_smc_int_vectors[] = {
19062306a36Sopenharmony_ci	0x0C, 0x14, 0x0C, 0x14,
19162306a36Sopenharmony_ci	0x0C, 0x14, 0x0C, 0x14,
19262306a36Sopenharmony_ci	0x0C, 0x14, 0x0C, 0x14,
19362306a36Sopenharmony_ci	0x0C, 0x14, 0x0C, 0x14,
19462306a36Sopenharmony_ci	0x0C, 0x14, 0x0C, 0x14,
19562306a36Sopenharmony_ci	0x0C, 0x14, 0x0C, 0x14,
19662306a36Sopenharmony_ci	0x0C, 0x14, 0x0C, 0x14,
19762306a36Sopenharmony_ci	0x0C, 0x14, 0x0C, 0x14,
19862306a36Sopenharmony_ci	0x0C, 0x14, 0x0C, 0x14,
19962306a36Sopenharmony_ci	0x0C, 0x14, 0x0C, 0x14,
20062306a36Sopenharmony_ci	0x0C, 0x14, 0x0C, 0x14,
20162306a36Sopenharmony_ci	0x0C, 0x14, 0x0C, 0x14,
20262306a36Sopenharmony_ci	0x0C, 0x14, 0x12, 0xAA,
20362306a36Sopenharmony_ci	0x0C, 0x2F, 0x15, 0xF6,
20462306a36Sopenharmony_ci	0x15, 0xF6, 0x05, 0x0A,
20562306a36Sopenharmony_ci	0x05, 0x0A, 0x05, 0x0A
20662306a36Sopenharmony_ci};
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_cistatic const u8 turks_smc_int_vectors[] = {
20962306a36Sopenharmony_ci	0x0C, 0x14, 0x0C, 0x14,
21062306a36Sopenharmony_ci	0x0C, 0x14, 0x0C, 0x14,
21162306a36Sopenharmony_ci	0x0C, 0x14, 0x0C, 0x14,
21262306a36Sopenharmony_ci	0x0C, 0x14, 0x0C, 0x14,
21362306a36Sopenharmony_ci	0x0C, 0x14, 0x0C, 0x14,
21462306a36Sopenharmony_ci	0x0C, 0x14, 0x0C, 0x14,
21562306a36Sopenharmony_ci	0x0C, 0x14, 0x0C, 0x14,
21662306a36Sopenharmony_ci	0x0C, 0x14, 0x0C, 0x14,
21762306a36Sopenharmony_ci	0x0C, 0x14, 0x0C, 0x14,
21862306a36Sopenharmony_ci	0x0C, 0x14, 0x0C, 0x14,
21962306a36Sopenharmony_ci	0x0C, 0x14, 0x0C, 0x14,
22062306a36Sopenharmony_ci	0x0C, 0x14, 0x0C, 0x14,
22162306a36Sopenharmony_ci	0x0C, 0x14, 0x12, 0xAA,
22262306a36Sopenharmony_ci	0x0C, 0x2F, 0x15, 0xF6,
22362306a36Sopenharmony_ci	0x15, 0xF6, 0x05, 0x0A,
22462306a36Sopenharmony_ci	0x05, 0x0A, 0x05, 0x0A
22562306a36Sopenharmony_ci};
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic const u8 caicos_smc_int_vectors[] = {
22862306a36Sopenharmony_ci	0x0C, 0x14, 0x0C, 0x14,
22962306a36Sopenharmony_ci	0x0C, 0x14, 0x0C, 0x14,
23062306a36Sopenharmony_ci	0x0C, 0x14, 0x0C, 0x14,
23162306a36Sopenharmony_ci	0x0C, 0x14, 0x0C, 0x14,
23262306a36Sopenharmony_ci	0x0C, 0x14, 0x0C, 0x14,
23362306a36Sopenharmony_ci	0x0C, 0x14, 0x0C, 0x14,
23462306a36Sopenharmony_ci	0x0C, 0x14, 0x0C, 0x14,
23562306a36Sopenharmony_ci	0x0C, 0x14, 0x0C, 0x14,
23662306a36Sopenharmony_ci	0x0C, 0x14, 0x0C, 0x14,
23762306a36Sopenharmony_ci	0x0C, 0x14, 0x0C, 0x14,
23862306a36Sopenharmony_ci	0x0C, 0x14, 0x0C, 0x14,
23962306a36Sopenharmony_ci	0x0C, 0x14, 0x0C, 0x14,
24062306a36Sopenharmony_ci	0x0C, 0x14, 0x12, 0xAA,
24162306a36Sopenharmony_ci	0x0C, 0x2F, 0x15, 0xF6,
24262306a36Sopenharmony_ci	0x15, 0xF6, 0x05, 0x0A,
24362306a36Sopenharmony_ci	0x05, 0x0A, 0x05, 0x0A
24462306a36Sopenharmony_ci};
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_cistatic const u8 cayman_smc_int_vectors[] = {
24762306a36Sopenharmony_ci	0x12, 0x05, 0x12, 0x05,
24862306a36Sopenharmony_ci	0x12, 0x05, 0x12, 0x05,
24962306a36Sopenharmony_ci	0x12, 0x05, 0x12, 0x05,
25062306a36Sopenharmony_ci	0x12, 0x05, 0x12, 0x05,
25162306a36Sopenharmony_ci	0x12, 0x05, 0x12, 0x05,
25262306a36Sopenharmony_ci	0x12, 0x05, 0x12, 0x05,
25362306a36Sopenharmony_ci	0x12, 0x05, 0x12, 0x05,
25462306a36Sopenharmony_ci	0x12, 0x05, 0x12, 0x05,
25562306a36Sopenharmony_ci	0x12, 0x05, 0x12, 0x05,
25662306a36Sopenharmony_ci	0x12, 0x05, 0x12, 0x05,
25762306a36Sopenharmony_ci	0x12, 0x05, 0x12, 0x05,
25862306a36Sopenharmony_ci	0x12, 0x05, 0x12, 0x05,
25962306a36Sopenharmony_ci	0x12, 0x05, 0x18, 0xEA,
26062306a36Sopenharmony_ci	0x12, 0x20, 0x1C, 0x34,
26162306a36Sopenharmony_ci	0x1C, 0x34, 0x08, 0x72,
26262306a36Sopenharmony_ci	0x08, 0x72, 0x08, 0x72
26362306a36Sopenharmony_ci};
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_cistatic int rv770_set_smc_sram_address(struct radeon_device *rdev,
26662306a36Sopenharmony_ci				      u16 smc_address, u16 limit)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	u32 addr;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	if (smc_address & 3)
27162306a36Sopenharmony_ci		return -EINVAL;
27262306a36Sopenharmony_ci	if ((smc_address + 3) > limit)
27362306a36Sopenharmony_ci		return -EINVAL;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	addr = smc_address;
27662306a36Sopenharmony_ci	addr |= SMC_SRAM_AUTO_INC_DIS;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	WREG32(SMC_SRAM_ADDR, addr);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	return 0;
28162306a36Sopenharmony_ci}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ciint rv770_copy_bytes_to_smc(struct radeon_device *rdev,
28462306a36Sopenharmony_ci			    u16 smc_start_address, const u8 *src,
28562306a36Sopenharmony_ci			    u16 byte_count, u16 limit)
28662306a36Sopenharmony_ci{
28762306a36Sopenharmony_ci	unsigned long flags;
28862306a36Sopenharmony_ci	u32 data, original_data, extra_shift;
28962306a36Sopenharmony_ci	u16 addr;
29062306a36Sopenharmony_ci	int ret = 0;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	if (smc_start_address & 3)
29362306a36Sopenharmony_ci		return -EINVAL;
29462306a36Sopenharmony_ci	if ((smc_start_address + byte_count) > limit)
29562306a36Sopenharmony_ci		return -EINVAL;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	addr = smc_start_address;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	spin_lock_irqsave(&rdev->smc_idx_lock, flags);
30062306a36Sopenharmony_ci	while (byte_count >= 4) {
30162306a36Sopenharmony_ci		/* SMC address space is BE */
30262306a36Sopenharmony_ci		data = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci		ret = rv770_set_smc_sram_address(rdev, addr, limit);
30562306a36Sopenharmony_ci		if (ret)
30662306a36Sopenharmony_ci			goto done;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci		WREG32(SMC_SRAM_DATA, data);
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci		src += 4;
31162306a36Sopenharmony_ci		byte_count -= 4;
31262306a36Sopenharmony_ci		addr += 4;
31362306a36Sopenharmony_ci	}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	/* RMW for final bytes */
31662306a36Sopenharmony_ci	if (byte_count > 0) {
31762306a36Sopenharmony_ci		data = 0;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci		ret = rv770_set_smc_sram_address(rdev, addr, limit);
32062306a36Sopenharmony_ci		if (ret)
32162306a36Sopenharmony_ci			goto done;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci		original_data = RREG32(SMC_SRAM_DATA);
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci		extra_shift = 8 * (4 - byte_count);
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci		while (byte_count > 0) {
32862306a36Sopenharmony_ci			/* SMC address space is BE */
32962306a36Sopenharmony_ci			data = (data << 8) + *src++;
33062306a36Sopenharmony_ci			byte_count--;
33162306a36Sopenharmony_ci		}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci		data <<= extra_shift;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci		data |= (original_data & ~((~0UL) << extra_shift));
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci		ret = rv770_set_smc_sram_address(rdev, addr, limit);
33862306a36Sopenharmony_ci		if (ret)
33962306a36Sopenharmony_ci			goto done;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci		WREG32(SMC_SRAM_DATA, data);
34262306a36Sopenharmony_ci	}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_cidone:
34562306a36Sopenharmony_ci	spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	return ret;
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_cistatic int rv770_program_interrupt_vectors(struct radeon_device *rdev,
35162306a36Sopenharmony_ci					   u32 smc_first_vector, const u8 *src,
35262306a36Sopenharmony_ci					   u32 byte_count)
35362306a36Sopenharmony_ci{
35462306a36Sopenharmony_ci	u32 tmp, i;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	if (byte_count % 4)
35762306a36Sopenharmony_ci		return -EINVAL;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	if (smc_first_vector < FIRST_SMC_INT_VECT_REG) {
36062306a36Sopenharmony_ci		tmp = FIRST_SMC_INT_VECT_REG - smc_first_vector;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci		if (tmp > byte_count)
36362306a36Sopenharmony_ci			return 0;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci		byte_count -= tmp;
36662306a36Sopenharmony_ci		src += tmp;
36762306a36Sopenharmony_ci		smc_first_vector = FIRST_SMC_INT_VECT_REG;
36862306a36Sopenharmony_ci	}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	for (i = 0; i < byte_count; i += 4) {
37162306a36Sopenharmony_ci		/* SMC address space is BE */
37262306a36Sopenharmony_ci		tmp = (src[i] << 24) | (src[i + 1] << 16) | (src[i + 2] << 8) | src[i + 3];
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci		WREG32(SMC_ISR_FFD8_FFDB + i, tmp);
37562306a36Sopenharmony_ci	}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	return 0;
37862306a36Sopenharmony_ci}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_civoid rv770_start_smc(struct radeon_device *rdev)
38162306a36Sopenharmony_ci{
38262306a36Sopenharmony_ci	WREG32_P(SMC_IO, SMC_RST_N, ~SMC_RST_N);
38362306a36Sopenharmony_ci}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_civoid rv770_reset_smc(struct radeon_device *rdev)
38662306a36Sopenharmony_ci{
38762306a36Sopenharmony_ci	WREG32_P(SMC_IO, 0, ~SMC_RST_N);
38862306a36Sopenharmony_ci}
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_civoid rv770_stop_smc_clock(struct radeon_device *rdev)
39162306a36Sopenharmony_ci{
39262306a36Sopenharmony_ci	WREG32_P(SMC_IO, 0, ~SMC_CLK_EN);
39362306a36Sopenharmony_ci}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_civoid rv770_start_smc_clock(struct radeon_device *rdev)
39662306a36Sopenharmony_ci{
39762306a36Sopenharmony_ci	WREG32_P(SMC_IO, SMC_CLK_EN, ~SMC_CLK_EN);
39862306a36Sopenharmony_ci}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_cibool rv770_is_smc_running(struct radeon_device *rdev)
40162306a36Sopenharmony_ci{
40262306a36Sopenharmony_ci	u32 tmp;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	tmp = RREG32(SMC_IO);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	if ((tmp & SMC_RST_N) && (tmp & SMC_CLK_EN))
40762306a36Sopenharmony_ci		return true;
40862306a36Sopenharmony_ci	else
40962306a36Sopenharmony_ci		return false;
41062306a36Sopenharmony_ci}
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ciPPSMC_Result rv770_send_msg_to_smc(struct radeon_device *rdev, PPSMC_Msg msg)
41362306a36Sopenharmony_ci{
41462306a36Sopenharmony_ci	u32 tmp;
41562306a36Sopenharmony_ci	int i;
41662306a36Sopenharmony_ci	PPSMC_Result result;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	if (!rv770_is_smc_running(rdev))
41962306a36Sopenharmony_ci		return PPSMC_Result_Failed;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	WREG32_P(SMC_MSG, HOST_SMC_MSG(msg), ~HOST_SMC_MSG_MASK);
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	for (i = 0; i < rdev->usec_timeout; i++) {
42462306a36Sopenharmony_ci		tmp = RREG32(SMC_MSG) & HOST_SMC_RESP_MASK;
42562306a36Sopenharmony_ci		tmp >>= HOST_SMC_RESP_SHIFT;
42662306a36Sopenharmony_ci		if (tmp != 0)
42762306a36Sopenharmony_ci			break;
42862306a36Sopenharmony_ci		udelay(1);
42962306a36Sopenharmony_ci	}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	tmp = RREG32(SMC_MSG) & HOST_SMC_RESP_MASK;
43262306a36Sopenharmony_ci	tmp >>= HOST_SMC_RESP_SHIFT;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	result = (PPSMC_Result)tmp;
43562306a36Sopenharmony_ci	return result;
43662306a36Sopenharmony_ci}
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ciPPSMC_Result rv770_wait_for_smc_inactive(struct radeon_device *rdev)
43962306a36Sopenharmony_ci{
44062306a36Sopenharmony_ci	int i;
44162306a36Sopenharmony_ci	PPSMC_Result result = PPSMC_Result_OK;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	if (!rv770_is_smc_running(rdev))
44462306a36Sopenharmony_ci		return result;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	for (i = 0; i < rdev->usec_timeout; i++) {
44762306a36Sopenharmony_ci		if (RREG32(SMC_IO) & SMC_STOP_MODE)
44862306a36Sopenharmony_ci			break;
44962306a36Sopenharmony_ci		udelay(1);
45062306a36Sopenharmony_ci	}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	return result;
45362306a36Sopenharmony_ci}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_cistatic void rv770_clear_smc_sram(struct radeon_device *rdev, u16 limit)
45662306a36Sopenharmony_ci{
45762306a36Sopenharmony_ci	unsigned long flags;
45862306a36Sopenharmony_ci	u16 i;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	spin_lock_irqsave(&rdev->smc_idx_lock, flags);
46162306a36Sopenharmony_ci	for (i = 0;  i < limit; i += 4) {
46262306a36Sopenharmony_ci		rv770_set_smc_sram_address(rdev, i, limit);
46362306a36Sopenharmony_ci		WREG32(SMC_SRAM_DATA, 0);
46462306a36Sopenharmony_ci	}
46562306a36Sopenharmony_ci	spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
46662306a36Sopenharmony_ci}
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ciint rv770_load_smc_ucode(struct radeon_device *rdev,
46962306a36Sopenharmony_ci			 u16 limit)
47062306a36Sopenharmony_ci{
47162306a36Sopenharmony_ci	int ret;
47262306a36Sopenharmony_ci	const u8 *int_vect;
47362306a36Sopenharmony_ci	u16 int_vect_start_address;
47462306a36Sopenharmony_ci	u16 int_vect_size;
47562306a36Sopenharmony_ci	const u8 *ucode_data;
47662306a36Sopenharmony_ci	u16 ucode_start_address;
47762306a36Sopenharmony_ci	u16 ucode_size;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	if (!rdev->smc_fw)
48062306a36Sopenharmony_ci		return -EINVAL;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	rv770_clear_smc_sram(rdev, limit);
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	switch (rdev->family) {
48562306a36Sopenharmony_ci	case CHIP_RV770:
48662306a36Sopenharmony_ci		ucode_start_address = RV770_SMC_UCODE_START;
48762306a36Sopenharmony_ci		ucode_size = RV770_SMC_UCODE_SIZE;
48862306a36Sopenharmony_ci		int_vect = (const u8 *)&rv770_smc_int_vectors;
48962306a36Sopenharmony_ci		int_vect_start_address = RV770_SMC_INT_VECTOR_START;
49062306a36Sopenharmony_ci		int_vect_size = RV770_SMC_INT_VECTOR_SIZE;
49162306a36Sopenharmony_ci		break;
49262306a36Sopenharmony_ci	case CHIP_RV730:
49362306a36Sopenharmony_ci		ucode_start_address = RV730_SMC_UCODE_START;
49462306a36Sopenharmony_ci		ucode_size = RV730_SMC_UCODE_SIZE;
49562306a36Sopenharmony_ci		int_vect = (const u8 *)&rv730_smc_int_vectors;
49662306a36Sopenharmony_ci		int_vect_start_address = RV730_SMC_INT_VECTOR_START;
49762306a36Sopenharmony_ci		int_vect_size = RV730_SMC_INT_VECTOR_SIZE;
49862306a36Sopenharmony_ci		break;
49962306a36Sopenharmony_ci	case CHIP_RV710:
50062306a36Sopenharmony_ci		ucode_start_address = RV710_SMC_UCODE_START;
50162306a36Sopenharmony_ci		ucode_size = RV710_SMC_UCODE_SIZE;
50262306a36Sopenharmony_ci		int_vect = (const u8 *)&rv710_smc_int_vectors;
50362306a36Sopenharmony_ci		int_vect_start_address = RV710_SMC_INT_VECTOR_START;
50462306a36Sopenharmony_ci		int_vect_size = RV710_SMC_INT_VECTOR_SIZE;
50562306a36Sopenharmony_ci		break;
50662306a36Sopenharmony_ci	case CHIP_RV740:
50762306a36Sopenharmony_ci		ucode_start_address = RV740_SMC_UCODE_START;
50862306a36Sopenharmony_ci		ucode_size = RV740_SMC_UCODE_SIZE;
50962306a36Sopenharmony_ci		int_vect = (const u8 *)&rv740_smc_int_vectors;
51062306a36Sopenharmony_ci		int_vect_start_address = RV740_SMC_INT_VECTOR_START;
51162306a36Sopenharmony_ci		int_vect_size = RV740_SMC_INT_VECTOR_SIZE;
51262306a36Sopenharmony_ci		break;
51362306a36Sopenharmony_ci	case CHIP_CEDAR:
51462306a36Sopenharmony_ci		ucode_start_address = CEDAR_SMC_UCODE_START;
51562306a36Sopenharmony_ci		ucode_size = CEDAR_SMC_UCODE_SIZE;
51662306a36Sopenharmony_ci		int_vect = (const u8 *)&cedar_smc_int_vectors;
51762306a36Sopenharmony_ci		int_vect_start_address = CEDAR_SMC_INT_VECTOR_START;
51862306a36Sopenharmony_ci		int_vect_size = CEDAR_SMC_INT_VECTOR_SIZE;
51962306a36Sopenharmony_ci		break;
52062306a36Sopenharmony_ci	case CHIP_REDWOOD:
52162306a36Sopenharmony_ci		ucode_start_address = REDWOOD_SMC_UCODE_START;
52262306a36Sopenharmony_ci		ucode_size = REDWOOD_SMC_UCODE_SIZE;
52362306a36Sopenharmony_ci		int_vect = (const u8 *)&redwood_smc_int_vectors;
52462306a36Sopenharmony_ci		int_vect_start_address = REDWOOD_SMC_INT_VECTOR_START;
52562306a36Sopenharmony_ci		int_vect_size = REDWOOD_SMC_INT_VECTOR_SIZE;
52662306a36Sopenharmony_ci		break;
52762306a36Sopenharmony_ci	case CHIP_JUNIPER:
52862306a36Sopenharmony_ci		ucode_start_address = JUNIPER_SMC_UCODE_START;
52962306a36Sopenharmony_ci		ucode_size = JUNIPER_SMC_UCODE_SIZE;
53062306a36Sopenharmony_ci		int_vect = (const u8 *)&juniper_smc_int_vectors;
53162306a36Sopenharmony_ci		int_vect_start_address = JUNIPER_SMC_INT_VECTOR_START;
53262306a36Sopenharmony_ci		int_vect_size = JUNIPER_SMC_INT_VECTOR_SIZE;
53362306a36Sopenharmony_ci		break;
53462306a36Sopenharmony_ci	case CHIP_CYPRESS:
53562306a36Sopenharmony_ci	case CHIP_HEMLOCK:
53662306a36Sopenharmony_ci		ucode_start_address = CYPRESS_SMC_UCODE_START;
53762306a36Sopenharmony_ci		ucode_size = CYPRESS_SMC_UCODE_SIZE;
53862306a36Sopenharmony_ci		int_vect = (const u8 *)&cypress_smc_int_vectors;
53962306a36Sopenharmony_ci		int_vect_start_address = CYPRESS_SMC_INT_VECTOR_START;
54062306a36Sopenharmony_ci		int_vect_size = CYPRESS_SMC_INT_VECTOR_SIZE;
54162306a36Sopenharmony_ci		break;
54262306a36Sopenharmony_ci	case CHIP_BARTS:
54362306a36Sopenharmony_ci		ucode_start_address = BARTS_SMC_UCODE_START;
54462306a36Sopenharmony_ci		ucode_size = BARTS_SMC_UCODE_SIZE;
54562306a36Sopenharmony_ci		int_vect = (const u8 *)&barts_smc_int_vectors;
54662306a36Sopenharmony_ci		int_vect_start_address = BARTS_SMC_INT_VECTOR_START;
54762306a36Sopenharmony_ci		int_vect_size = BARTS_SMC_INT_VECTOR_SIZE;
54862306a36Sopenharmony_ci		break;
54962306a36Sopenharmony_ci	case CHIP_TURKS:
55062306a36Sopenharmony_ci		ucode_start_address = TURKS_SMC_UCODE_START;
55162306a36Sopenharmony_ci		ucode_size = TURKS_SMC_UCODE_SIZE;
55262306a36Sopenharmony_ci		int_vect = (const u8 *)&turks_smc_int_vectors;
55362306a36Sopenharmony_ci		int_vect_start_address = TURKS_SMC_INT_VECTOR_START;
55462306a36Sopenharmony_ci		int_vect_size = TURKS_SMC_INT_VECTOR_SIZE;
55562306a36Sopenharmony_ci		break;
55662306a36Sopenharmony_ci	case CHIP_CAICOS:
55762306a36Sopenharmony_ci		ucode_start_address = CAICOS_SMC_UCODE_START;
55862306a36Sopenharmony_ci		ucode_size = CAICOS_SMC_UCODE_SIZE;
55962306a36Sopenharmony_ci		int_vect = (const u8 *)&caicos_smc_int_vectors;
56062306a36Sopenharmony_ci		int_vect_start_address = CAICOS_SMC_INT_VECTOR_START;
56162306a36Sopenharmony_ci		int_vect_size = CAICOS_SMC_INT_VECTOR_SIZE;
56262306a36Sopenharmony_ci		break;
56362306a36Sopenharmony_ci	case CHIP_CAYMAN:
56462306a36Sopenharmony_ci		ucode_start_address = CAYMAN_SMC_UCODE_START;
56562306a36Sopenharmony_ci		ucode_size = CAYMAN_SMC_UCODE_SIZE;
56662306a36Sopenharmony_ci		int_vect = (const u8 *)&cayman_smc_int_vectors;
56762306a36Sopenharmony_ci		int_vect_start_address = CAYMAN_SMC_INT_VECTOR_START;
56862306a36Sopenharmony_ci		int_vect_size = CAYMAN_SMC_INT_VECTOR_SIZE;
56962306a36Sopenharmony_ci		break;
57062306a36Sopenharmony_ci	default:
57162306a36Sopenharmony_ci		DRM_ERROR("unknown asic in smc ucode loader\n");
57262306a36Sopenharmony_ci		BUG();
57362306a36Sopenharmony_ci	}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	/* load the ucode */
57662306a36Sopenharmony_ci	ucode_data = (const u8 *)rdev->smc_fw->data;
57762306a36Sopenharmony_ci	ret = rv770_copy_bytes_to_smc(rdev, ucode_start_address,
57862306a36Sopenharmony_ci				      ucode_data, ucode_size, limit);
57962306a36Sopenharmony_ci	if (ret)
58062306a36Sopenharmony_ci		return ret;
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	/* set up the int vectors */
58362306a36Sopenharmony_ci	ret = rv770_program_interrupt_vectors(rdev, int_vect_start_address,
58462306a36Sopenharmony_ci					      int_vect, int_vect_size);
58562306a36Sopenharmony_ci	if (ret)
58662306a36Sopenharmony_ci		return ret;
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	return 0;
58962306a36Sopenharmony_ci}
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ciint rv770_read_smc_sram_dword(struct radeon_device *rdev,
59262306a36Sopenharmony_ci			      u16 smc_address, u32 *value, u16 limit)
59362306a36Sopenharmony_ci{
59462306a36Sopenharmony_ci	unsigned long flags;
59562306a36Sopenharmony_ci	int ret;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	spin_lock_irqsave(&rdev->smc_idx_lock, flags);
59862306a36Sopenharmony_ci	ret = rv770_set_smc_sram_address(rdev, smc_address, limit);
59962306a36Sopenharmony_ci	if (ret == 0)
60062306a36Sopenharmony_ci		*value = RREG32(SMC_SRAM_DATA);
60162306a36Sopenharmony_ci	spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	return ret;
60462306a36Sopenharmony_ci}
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ciint rv770_write_smc_sram_dword(struct radeon_device *rdev,
60762306a36Sopenharmony_ci			       u16 smc_address, u32 value, u16 limit)
60862306a36Sopenharmony_ci{
60962306a36Sopenharmony_ci	unsigned long flags;
61062306a36Sopenharmony_ci	int ret;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	spin_lock_irqsave(&rdev->smc_idx_lock, flags);
61362306a36Sopenharmony_ci	ret = rv770_set_smc_sram_address(rdev, smc_address, limit);
61462306a36Sopenharmony_ci	if (ret == 0)
61562306a36Sopenharmony_ci		WREG32(SMC_SRAM_DATA, value);
61662306a36Sopenharmony_ci	spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	return ret;
61962306a36Sopenharmony_ci}
620