// Copyright 2018 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "aml-pwm.h" #include "aml-pwm-regs.h" #include #include #include #include #include #include namespace thermal { namespace { // MMIO index. constexpr uint32_t kPwmMmio = 3; // Input clock frequency constexpr uint32_t kXtalFreq = 24000000; } // namespace zx_status_t AmlPwm::Init(zx_device_t* parent) { zx_status_t status = device_get_protocol(parent, ZX_PROTOCOL_PDEV, &pdev_); if (status != ZX_OK) { return status; } // Map amlogic pwm registers mmio_buffer_t mmio; status = pdev_map_mmio_buffer2(&pdev_, kPwmMmio, ZX_CACHE_POLICY_UNCACHED_DEVICE, &mmio); if (status != ZX_OK) { zxlogf(ERROR, "aml-pwm: could not map periph mmio: %d\n", status); return status; } pwm_mmio_ = ddk::MmioBuffer(mmio); switch (hwpwm_) { case 0: pwm_duty_cycle_offset_ = S905D2_AO_PWM_PWM_A; enable_bit_ = A_ENABLE; clk_enable_bit_ = CLK_A_ENABLE; break; case 1: pwm_duty_cycle_offset_ = S905D2_AO_PWM_PWM_B; enable_bit_ = B_ENABLE; clk_enable_bit_ = CLK_B_ENABLE; break; default: return ZX_ERR_INVALID_ARGS; } return ZX_OK; } zx_status_t AmlPwm::Configure(uint32_t duty_cycle) { uint16_t low_count; uint16_t high_count; const uint64_t fin_ns = NSEC_PER_SEC / kXtalFreq; if (duty_cycle > 100) { return ZX_ERR_INVALID_ARGS; } // If the current duty cycle is the same as requested, no need to do anything. if (duty_cycle == duty_cycle_) { return ZX_OK; } // Calculate the high and low count first based on the duty cycle requested. const uint32_t duty = (duty_cycle * period_) / 100; const uint16_t count = static_cast(period_ / fin_ns); if (duty == period_) { high_count = count; low_count = 0; } else if (duty == 0) { high_count = 0; low_count = count; } else { const uint16_t duty_count = static_cast(duty / fin_ns); high_count = duty_count; low_count = static_cast(count - duty_count); } fbl::AutoLock lock(&pwm_lock_); const uint32_t value = (high_count << PWM_HIGH_SHIFT) | low_count; pwm_mmio_->Write32(value, pwm_duty_cycle_offset_); pwm_mmio_->SetBits32(enable_bit_ | clk_enable_bit_, S905D2_AO_PWM_MISC_REG_AB); // Update the new duty_cycle information duty_cycle_ = duty_cycle; return ZX_OK; } } // namespace thermal