Skip to content
2 changes: 2 additions & 0 deletions Documentation/devicetree/bindings/power/apple,pmc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ properties:
- apple,t8132-pmc
- apple,t8140-pmc
- apple,t8142-pmc
- apple,t8152-pmc

reg:
minItems: 2
Expand Down Expand Up @@ -230,6 +231,7 @@ patternProperties:
- apple,t8132-clpc
- apple,t8140-clpc
- apple,t8142-clpc
- apple,t8152-clpc

reg:
maxItems: 1
Expand Down
14 changes: 9 additions & 5 deletions Documentation/devicetree/bindings/spi/apple,qspi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,15 @@ allOf:

properties:
compatible:
enum:
- apple,t8122-qspi
- apple,t8132-qspi
- apple,t6041-qspi
- apple,t8140-qspi
oneOf:
- enum:
- apple,t8122-qspi
- apple,t8132-qspi
- apple,t6041-qspi
- apple,t8140-qspi
- items:
- const: apple,t8152-qspi
- const: apple,t8140-qspi

reg:
maxItems: 1
Expand Down
27 changes: 27 additions & 0 deletions arch/arm64/boot/dts/apple/t8152-j873-pmc.dtsi
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// SPDX-License-Identifier: GPL-2.0+ OR MIT

/ {
soc {
/*
* Perf-state floors of voter agents 0-3 (PMP, CLPC, GFX, AP) and,
* at 0x1c6000, the honoured states and per-rail voter enables
* (rails DCS, SOC, DISP) in the PMC window, pmgr reg 46.
*/
pmc: power-management@302800000 {
compatible = "apple,t8152-pmc";
reg = <0x3 0x02800000 0x0 0x10000>,
<0x3 0x029c6000 0x0 0x3000>;
reg-names = "voters", "rails";
#address-cells = <1>;
#size-cells = <0>;
#cooling-cells = <2>;
/* PMP, CLPC and AP agents */
apple,static-voters = <0 1 3>;

clpc: performance-controller@1 {
compatible = "apple,t8152-clpc";
reg = <1>;
};
};
};
};
85 changes: 85 additions & 0 deletions arch/arm64/boot/dts/apple/t8152-j873-qspi.dtsi
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// SPDX-License-Identifier: GPL-2.0+ OR MIT

/*
* The boot NOR behind the QSPIMC controller (ADT /arm-io/qspi/spinor: firmware,
* anvram, syscfg), for asahi-nvram and asahi-bless. The controller's power
* states (PMGR devices SPI_P and QSPI) are off once iBoot is done with it;
* reading it without them takes an SError.
*/
/ {
aliases {
nvram = &nvram;
};

/* QSPIMC input clock, as on T8140 (ADT clock-ids 412) */
clk_200m: clock-200m {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <200000000>;
clock-output-names = "clk_200m";
};

soc {
pmgr: power-management@300700000 {
compatible = "apple,t8152-pmgr", "apple,t8103-pmgr",
"syscon", "simple-mfd";
#address-cells = <1>;
#size-cells = <1>;
reg = <0x3 0x700000 0x0 0x14000>;
};

qspi: spi@331118000 {
compatible = "apple,t8152-qspi", "apple,t8140-qspi";
/* One aperture; the command block starts at offset 0x800. */
reg = <0x3 0x31118000 0x0 0x4800>;
interrupt-parent = <&aic>;
interrupts = <AIC_IRQ 1411 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clk_200m>;
power-domains = <&ps_qspi>;
#address-cells = <1>;
#size-cells = <0>;

spinor: flash@0 {
compatible = "jedec,spi-nor";
reg = <0>;
spi-max-frequency = <10000000>;

partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;

/*
* 64 KiB NVRAM v3 banks ("3VVN") from 0x24000
* to 0x1f4000, as on T8140 J700.
*/
nvram: partition@24000 {
label = "nvram";
reg = <0x24000 0x1d0000>;
};
};
};
};
};
};

&pmgr {
ps_spi_p: power-controller@d40 {
compatible = "apple,t8152-pmgr-pwrstate",
"apple,t8103-pmgr-pwrstate";
reg = <0xd40 4>;
#power-domain-cells = <0>;
#reset-cells = <0>;
label = "spi_p";
};

ps_qspi: power-controller@1140 {
compatible = "apple,t8152-pmgr-pwrstate",
"apple,t8103-pmgr-pwrstate";
reg = <0x1140 4>;
#power-domain-cells = <0>;
#reset-cells = <0>;
label = "qspi";
power-domains = <&ps_spi_p>;
};
};
3 changes: 3 additions & 0 deletions arch/arm64/boot/dts/apple/t8152-j873g.dts
Original file line number Diff line number Diff line change
Expand Up @@ -283,3 +283,6 @@

#include "t8152-j873-gpu.dtsi"
#include "t8152-j873-n1.dtsi"

#include "t8152-j873-pmc.dtsi"
#include "t8152-j873-qspi.dtsi"
7 changes: 7 additions & 0 deletions drivers/gpu/drm/asahi/g18/fw/graphics_payload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,13 @@ const WITNESS_RANGES: [(u64, usize, u32); 25] = [
(0x100_002d_8000, 0xc0000, 4),
];

/// Every colour-a page (address, captured usage) in file order, for the
/// stage-10 layout that is mapped without a capture file.
pub(crate) fn color_a_pages() -> impl Iterator<Item = (u64, u32)> {
RANGES.iter().flat_map(|&(base, length, usage)|
(0..length).step_by(PAGE_BYTES).map(move |offset| (base + offset as u64, usage)))
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum Capture { ColorA = 1, ColorB = 2, GearsG1 = 3, FragmentWitness = 4 }

Expand Down
3 changes: 2 additions & 1 deletion drivers/gpu/drm/asahi/g18/initdata/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,8 @@ impl G18FirmwareMemory {
let pool = self.uma_pool.prepare_binding()?;
self.uma_pool.load_hardware_descriptor(dev, &mut self.uma, pool, j873_sma_device_words(2, 6)?)?;
job.parameter_buffer().load_row(&mut self.pbdesc)?;
job.encode_h1(dev)?;
// Without a capture (stage 10 only) the first application frame encodes.
if !job.layout_only_payload() { job.encode_h1(dev)?; }
Ok(pool)
}

Expand Down
3 changes: 2 additions & 1 deletion drivers/gpu/drm/asahi/g18/runtime/graphics_job.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ impl G18GraphicsJob {
}

pub(crate) fn vm(&self) -> &mmu::Vm { self.program.vm() }
pub(crate) fn layout_only_payload(&self) -> bool { self.program.layout_only_payload() }
pub(crate) fn context_id(&self) -> u8 { self.program.context_id() }
pub(crate) fn scheduler_record(&self) -> Result<u64> { Ok(self.scheduler.binding(0)?.record) }
pub(crate) fn queues(&mut self) -> &mut G18GraphicsQueues { &mut self.queues }
Expand Down Expand Up @@ -288,7 +289,7 @@ impl G18GraphicsJob {
/// Caller has allocated/retained PB and pool owners and filled all native
/// fields. In particular a default resource word is not an admitted policy.
#[inline(never)]
pub(crate) fn encode(&mut self, dev: &AsahiDrmDevice, inputs: &cmd::G18RenderCommandInputs) -> Result {
pub(crate) fn encode(&mut self, _dev: &AsahiDrmDevice, inputs: &cmd::G18RenderCommandInputs) -> Result {
if self.encoded.is_some() || self.controls.ta.resource_word == 0
|| self.controls.fragment.resource_word == 0
|| inputs.context_id != u32::from(self.context_id())
Expand Down
4 changes: 2 additions & 2 deletions drivers/gpu/drm/asahi/g18/runtime/graphics_job_application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,10 @@ impl ApplicationJob {
self.guard=Some(guard);self.generation=frame.generation;self.retired=false;
self.client_frames=if new_client {1} else {self.client_frames.saturating_add(1)};
if remap {
dev_info!(dev.as_ref(),"G18_APP_VM frame={} vm={} context={} target={}x{} aliases={}\n",
crate::g18_kick_info!(dev.as_ref(),"G18_APP_VM frame={} vm={} context={} target={}x{} aliases={}\n",
frame.generation,vm.id(),self.job.context_id(),render.target.width,render.target.height,self.user_aliases.len());
for (target,name) in self.job.targets.iter().zip(assembly::TARGET_NAMES) {
dev_info!(dev.as_ref(),"G18_APP_TARGET name={} va={:#x} bytes={:#x} context={}\n",
crate::g18_kick_info!(dev.as_ref(),"G18_APP_TARGET name={} va={:#x} bytes={:#x} context={}\n",
name,target.iova(),target.logical_size,self.job.context_id());
}
}
Expand Down
74 changes: 53 additions & 21 deletions drivers/gpu/drm/asahi/g18/runtime/graphics_memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,49 +30,81 @@ impl G18GraphicsMemory {
_ => return Err(EINVAL),
};
if !uat.is_t8152_geometry() || mmu::UAT_PGSZ != payload::PAGE_BYTES { return Err(ENODEV); }
let firmware = Firmware::request(name, dev.as_ref().as_ref())?;
let firmware = match Firmware::request(name, dev.as_ref().as_ref()) {
Ok(firmware) => firmware,
// Stage 10 executes no capture bytes: every application command
// is rebuilt from Mesa's parameters. It needs only the colour-a
// page layout, which is mapped here with zeroed contents.
Err(e) if e == ENOENT && profile == 1
&& crate::g18::params::g18_initdata_test.value() == 10 =>
return Self::layout_only(dev, uat),
Err(e) => return Err(e),
};
let input = payload::GraphicsPayload::parse(firmware.data()).map_err(|_| EINVAL)?;
if input.capture() as u32 != profile { return Err(EINVAL); }
let (context, aliases, pages) = Self::map_pages(dev, uat,
(0..input.page_count()).map(|index| input.page(index).map(|page| (page.address, Some(page.bytes)))))?;
let mut command = KVec::new();
command.extend_from_slice(input.command(), GFP_KERNEL)?;
let owner = Self { context, aliases, vdm_alias: None, ppp_aliased: false, pages, capture: input.capture(), command,
body_digest: input.body_digest(), command_digest: payload::fnv64(input.command()) };
fence(Ordering::SeqCst);
dev_info!(dev.as_ref(), "G18_RENDER_PAYLOAD profile={} pages={} low_aliases={} body_fnv={:#018x} command_fnv={:#018x} context={} roots={:x?}; unpublished\n",
profile, owner.pages.len(), owner.aliases.len(), owner.body_digest, owner.command_digest,
owner.context_id(), owner.roots());
Ok(owner)
}

/// The colour-a VM layout without its capture: zeroed pages, no command.
fn layout_only(dev: &AsahiDrmDevice, uat: &mmu::Uat) -> Result<Self> {
let (context, aliases, pages) = Self::map_pages(dev, uat,
payload::color_a_pages().map(|(address, _)| Some((address, None))))?;
let owner = Self { context, aliases, vdm_alias: None, ppp_aliased: false, pages,
capture: payload::Capture::ColorA, command: KVec::new(), body_digest: 0, command_digest: 0 };
fence(Ordering::SeqCst);
dev_info!(dev.as_ref(), "G18_RENDER_PAYLOAD layout-only colour-a pages={} low_aliases={} context={} roots={:x?}; no capture, stage 10 encodes from Mesa only\n",
owner.pages.len(), owner.aliases.len(), owner.context_id(), owner.roots());
Ok(owner)
}

/// True when no capture was loaded: there is no H1 command to encode.
pub(crate) fn layout_only_payload(&self) -> bool { self.command.is_empty() }

fn map_pages<'a>(dev: &AsahiDrmDevice, uat: &mmu::Uat,
sources: impl Iterator<Item = Option<(u64, Option<&'a [u8]>)>>)
-> Result<(mmu::G18ExecutionContext, KVec<mmu::KernelMapping>, KVec<MappedObject>)> {
let vm = uat.new_t8152_compute_fixture_vm()?;
let context = uat.prepare_t8152_execution_context(&vm)?;
let mut pages = KVec::new();
let mut aliases = KVec::new();
for index in 0..input.page_count() {
let source = input.page(index).ok_or(EINVAL)?;
for source in sources {
let (address, bytes) = source.ok_or(EINVAL)?;
// Captured usage flags are not PTE permissions. These private
// fixture mappings have an explicit diagnostic RW policy.
let mut page = MappedObject::new_wc_at(dev, &vm, source.address,
let mut page = MappedObject::new_wc_at(dev, &vm, address,
payload::PAGE_BYTES, mmu::PROT_GPU_SHARED_RW)?;
page.with_bytes_mut(|out| {
out.copy_from_slice(source.bytes);
if (payload::OUTPUT..payload::OUTPUT + payload::OUTPUT_BYTES).contains(&source.address) {
match bytes { Some(bytes) => out.copy_from_slice(bytes), None => out.fill(0) }
if (payload::OUTPUT..payload::OUTPUT + payload::OUTPUT_BYTES).contains(&address) {
out.fill(0xa5);
}
Ok(())
})?;
if source.address < 0x78000 {
aliases.push(page.map_alias_at(&vm, va::COMPACT_BIAS + source.address,
if address < 0x78000 {
aliases.push(page.map_alias_at(&vm, va::COMPACT_BIAS + address,
mmu::PROT_GPU_SHARED_RW)?, GFP_KERNEL)?;
for delta in [0, va::PAGE - 1] {
if vm.translate_iova(source.address + delta)?
!= vm.translate_iova(va::COMPACT_BIAS + source.address + delta)? {
if vm.translate_iova(address + delta)?
!= vm.translate_iova(va::COMPACT_BIAS + address + delta)? {
return Err(EFAULT);
}
}
}
vm.translate_iova(source.address)?;
vm.translate_iova(source.address + va::PAGE - 1)?;
vm.translate_iova(address)?;
vm.translate_iova(address + va::PAGE - 1)?;
pages.push(page, GFP_KERNEL)?;
}
let mut command = KVec::new();
command.extend_from_slice(input.command(), GFP_KERNEL)?;
let owner = Self { context, aliases, vdm_alias: None, ppp_aliased: false, pages, capture: input.capture(), command,
body_digest: input.body_digest(), command_digest: payload::fnv64(input.command()) };
fence(Ordering::SeqCst);
dev_info!(dev.as_ref(), "G18_RENDER_PAYLOAD profile={} pages={} low_aliases={} body_fnv={:#018x} command_fnv={:#018x} context={} roots={:x?}; unpublished\n",
profile, owner.pages.len(), owner.aliases.len(), owner.body_digest, owner.command_digest,
owner.context_id(), owner.roots());
Ok(owner)
Ok((context, aliases, pages))
}

pub(crate) fn vm(&self) -> &mmu::Vm { self.context.vm() }
Expand Down
13 changes: 13 additions & 0 deletions drivers/pmdomain/apple/pmc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1373,10 +1373,23 @@ static const struct apple_pmc_hw apple_pmc_hw_t6040 = {
.ap_agent = 3,
};

/* T8152 uses the T8142 voter interface with its rails at 0x3029c6000. */
static const struct apple_pmc_hw apple_pmc_hw_t8152 = {
.agent_stride = 0x4000,
.agents = 4,
.rails = 3,
.agent_state = 0x0,
.rail_enable = 0x2000,
.dcs_rail = 0,
.dcs_max = 7,
.ap_agent = 3,
};

static const struct of_device_id apple_pmc_of_match[] = {
{ .compatible = "apple,t8132-pmc", .data = &apple_pmc_hw_t8132 },
{ .compatible = "apple,t8140-pmc", .data = &apple_pmc_hw_t8140 },
{ .compatible = "apple,t8142-pmc", .data = &apple_pmc_hw_t8142 },
{ .compatible = "apple,t8152-pmc", .data = &apple_pmc_hw_t8152 },
{ .compatible = "apple,t6041-pmc", .data = &apple_pmc_hw_t6040 },
{}
};
Expand Down
10 changes: 10 additions & 0 deletions drivers/soc/apple/clpc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1141,10 +1141,20 @@ static struct kunit_suite apple_clpc_suite = {
kunit_test_suite(apple_clpc_suite);
#endif

/* T8152: DCS states 0-7 and fabric states 0-4. */
static const struct apple_clpc_hw apple_clpc_hw_t8152 = {
.rail = { [APPLE_CLPC_DCS] = 0, [APPLE_CLPC_SOC] = 1 },
.map = {
[APPLE_CLPC_DCS] = { 2, 4, 6, 7 },
[APPLE_CLPC_SOC] = { 1, 2, 3, 4 },
},
};

static const struct of_device_id apple_clpc_of_match[] = {
{ .compatible = "apple,t8132-clpc", .data = &apple_clpc_hw_t8132 },
{ .compatible = "apple,t8140-clpc", .data = &apple_clpc_hw_t8140 },
{ .compatible = "apple,t8142-clpc", .data = &apple_clpc_hw_t8142 },
{ .compatible = "apple,t8152-clpc", .data = &apple_clpc_hw_t8152 },
{ .compatible = "apple,t6041-clpc", .data = &apple_clpc_hw_t6040 },
{}
};
Expand Down