blob: bc64e77930b7dda0cbbf51cab821a075b4395ae7 [file] [log] [blame]
// 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.
#ifndef ZIRCON_SYSTEM_DEV_NAND_AML_RAWNAND_AML_RAWNAND_H_
#define ZIRCON_SYSTEM_DEV_NAND_AML_RAWNAND_AML_RAWNAND_H_
#include <lib/device-protocol/pdev.h>
#include <lib/device-protocol/platform-device.h>
#include <lib/mmio/mmio.h>
#include <lib/sync/completion.h>
#include <lib/zx/time.h>
#include <string.h>
#include <unistd.h>
#include <zircon/threads.h>
#include <zircon/types.h>
#include <memory>
#include <utility>
#include <ddk/binding.h>
#include <ddk/debug.h>
#include <ddk/io-buffer.h>
#include <ddk/mmio-buffer.h>
#include <ddk/platform-defs.h>
#include <ddk/protocol/platform/device.h>
#include <ddktl/device.h>
#include <ddktl/protocol/rawnand.h>
#include <fbl/bitfield.h>
#include <hw/reg.h>
#include <soc/aml-common/aml-rawnand.h>
#include "onfi.h"
namespace amlrawnand {
struct AmlController {
int ecc_strength;
int user_mode;
int rand_mode;
int options;
int bch_mode;
};
// In the case where user_mode == 2 (2 OOB bytes per ECC page),
// the controller adds one of these structs *per* ECC page in
// the info_buf.
struct AmlInfoFormat {
uint16_t info_bytes;
uint8_t zero_bits; /* bit0~5 is valid */
union ecc_sta {
uint8_t raw_value;
fbl::BitFieldMember<uint8_t, 0, 6> eccerr_cnt;
fbl::BitFieldMember<uint8_t, 7, 1> completed;
} ecc;
uint32_t reserved;
// BitFieldMember is not trivially copyable so neither are we, have to copy
// each member over manually (copy assignment is needed by tests).
AmlInfoFormat& operator=(const AmlInfoFormat& other) {
info_bytes = other.info_bytes;
zero_bits = other.zero_bits;
ecc.raw_value = other.ecc.raw_value;
reserved = other.reserved;
return *this;
}
};
// gcc doesn't let us use __PACKED with fbl::BitFieldMember<>, but it shouldn't
// make a difference practically in how the AmlInfoFormat struct is laid out
// and this assertion will double-check that we don't need it.
static_assert(sizeof(AmlInfoFormat) == 8, "sizeof(AmlInfoFormat) must be exactly 8 bytes");
// This should always be the case, but we also need an array of AmlInfoFormats
// to have no padding between the items.
static_assert(sizeof(AmlInfoFormat[2]) == 16, "AmlInfoFormat has unexpected padding");
class AmlRawNand;
using DeviceType = ddk::Device<AmlRawNand, ddk::UnbindableNew>;
class AmlRawNand : public DeviceType, public ddk::RawNandProtocol<AmlRawNand, ddk::base_protocol> {
public:
explicit AmlRawNand(zx_device_t* parent, ddk::MmioBuffer mmio_nandreg,
ddk::MmioBuffer mmio_clockreg, zx::bti bti, zx::interrupt irq,
std::unique_ptr<Onfi> onfi)
: DeviceType(parent),
onfi_(std::move(onfi)),
mmio_nandreg_(std::move(mmio_nandreg)),
mmio_clockreg_(std::move(mmio_clockreg)),
bti_(std::move(bti)),
irq_(std::move(irq)) {}
static zx_status_t Create(void* ctx, zx_device_t* parent);
virtual ~AmlRawNand() = default;
void DdkRelease();
void DdkUnbindNew(ddk::UnbindTxn txn);
zx_status_t Bind();
zx_status_t Init();
zx_status_t RawNandReadPageHwecc(uint32_t nand_page, void* data, size_t data_size,
size_t* data_actual, void* oob, size_t oob_size,
size_t* oob_actual, uint32_t* ecc_correct);
zx_status_t RawNandWritePageHwecc(const void* data, size_t data_size, const void* oob,
size_t oob_size, uint32_t nand_page);
zx_status_t RawNandEraseBlock(uint32_t nand_page);
zx_status_t RawNandGetNandInfo(fuchsia_hardware_nand_Info* nand_info);
protected:
// These functions require complicated hardware interaction so need to be
// overridden or called differently in tests.
// Waits until a read completes.
virtual zx_status_t AmlQueueRB();
// Waits until DMA has transfered data into or out of the NAND buffers.
virtual zx_status_t AmlWaitDmaFinish();
// Reads a single status byte from a NAND register. Used during initialization
// to query the chip information and settings.
virtual uint8_t AmlReadByte();
// Normally called when the driver is unregistered but is not automatically
// called on destruction, so needs to be called manually by tests before
// destroying this object.
void CleanUpIrq();
// Tests can fake page read/writes by copying bytes to/from these buffers.
const ddk::IoBuffer& data_buffer() { return data_buffer_; }
const ddk::IoBuffer& info_buffer() { return info_buffer_; }
private:
std::unique_ptr<Onfi> onfi_;
void *info_buf_, *data_buf_;
zx_paddr_t info_buf_paddr_, data_buf_paddr_;
ddk::MmioBuffer mmio_nandreg_;
ddk::MmioBuffer mmio_clockreg_;
zx::bti bti_;
zx::interrupt irq_;
thrd_t irq_thread_;
ddk::IoBuffer data_buffer_;
ddk::IoBuffer info_buffer_;
sync_completion_t req_completion_;
AmlController controller_params_;
uint32_t chip_select_ = 0; // Default to 0.
int chip_delay_ = 100; // Conservative default before we query chip to find better value.
uint32_t writesize_; /* NAND pagesize - bytes */
uint32_t erasesize_; /* size of erase block - bytes */
uint32_t erasesize_pages_;
uint32_t oobsize_; /* oob bytes per NAND page - bytes */
uint32_t bus_width_; /* 16bit or 8bit ? */
uint64_t chipsize_; /* MiB */
uint32_t page_shift_; /* NAND page shift */
struct {
uint64_t ecc_corrected;
uint64_t failed;
} stats;
polling_timings_t polling_timings_ = {};
void AmlCmdCtrl(int32_t cmd, uint32_t ctrl);
void NandctrlSetCfg(uint32_t val);
void NandctrlSetTimingAsync(int bus_tim, int bus_cyc);
void NandctrlSendCmd(uint32_t cmd);
void AmlCmdIdle(uint32_t time);
zx_status_t AmlWaitCmdFinish(zx::duration timeout, zx::duration first_interval,
zx::duration polling_interval);
void AmlCmdSeed(uint32_t seed);
void AmlCmdN2M(uint32_t ecc_pages, uint32_t ecc_pagesize);
void AmlCmdM2N(uint32_t ecc_pages, uint32_t ecc_pagesize);
void AmlCmdM2NPage0();
void AmlCmdN2MPage0();
// Returns the AmlInfoFormat struct corresponding to the i'th
// ECC page. THIS ASSUMES user_mode == 2 (2 OOB bytes per ECC page).
void* AmlInfoPtr(int i);
zx_status_t AmlGetOOBByte(uint8_t* oob_buf, size_t* oob_actual);
zx_status_t AmlSetOOBByte(const uint8_t* oob_buf, size_t oob_size, uint32_t ecc_pages);
// Returns the maximum bitflips corrected on this NAND page
// (the maximum bitflips across all of the ECC pages in this page).
zx_status_t AmlGetECCCorrections(int ecc_pages, uint32_t nand_page, uint32_t* ecc_corrected);
zx_status_t AmlCheckECCPages(int ecc_pages);
void AmlSetClockRate(uint32_t clk_freq);
void AmlClockInit();
void AmlAdjustTimings(uint32_t tRC_min, uint32_t tREA_max, uint32_t RHOH_min);
zx_status_t AmlGetFlashType();
int IrqThread();
void AmlSetEncryption();
zx_status_t AmlReadPage0(void* data, size_t data_size, void* oob, size_t oob_size,
uint32_t nand_page, uint32_t* ecc_correct, int retries);
// Reads one of the page0 pages, and use the result to init
// ECC algorithm and rand-mode.
zx_status_t AmlNandInitFromPage0();
zx_status_t AmlRawNandAllocBufs();
zx_status_t AmlNandInit();
};
} // namespace amlrawnand
#endif // ZIRCON_SYSTEM_DEV_NAND_AML_RAWNAND_AML_RAWNAND_H_