blob: 98c5b3b9a877fc6d46c10983e5fde40b306d0c6a [file] [log] [blame]
// Copyright 2017 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 <ddk/binding.h>
#include <ddk/debug.h>
#include <ddk/device.h>
#include <ddk/driver.h>
#include <ddk/protocol/platform-defs.h>
#include <ddk/protocol/platform-device.h>
#include <ddk/protocol/sdhci.h>
#include <hw/reg.h>
#include <hw/sdhci.h>
#include <stdlib.h>
#define SDHCI_MMIO 0
#define SDHCI_PINMUX 1
#define SDHCI_IRQ 0
typedef struct {
zx_device_t* zxdev;
platform_device_protocol_t pdev;
pdev_vmo_buffer_t mmio;
pdev_vmo_buffer_t pinmux;
volatile sdhci_regs_t* regs;
uint64_t regs_size;
} hisi_sdhci_device_t;
static zx_status_t hisi_sdhci_get_interrupt(void* ctx, zx_handle_t* handle_out) {
hisi_sdhci_device_t* dev = ctx;
return pdev_map_interrupt(&dev->pdev, SDHCI_IRQ, handle_out);
}
static zx_status_t hisi_sdhci_get_mmio(void* ctx, volatile sdhci_regs_t** out) {
hisi_sdhci_device_t* dev = ctx;
*out = (sdhci_regs_t *)dev->mmio.vaddr;
return ZX_OK;
}
static uint32_t hisi_sdhci_get_base_clock(void* ctx) {
return 0;
}
static zx_paddr_t hisi_sdhci_get_dma_offset(void* ctx) {
return 0x200;
}
static uint64_t hisi_sdhci_get_quirks(void* ctx) {
return 0;
}
static void hisi_sdhci_hw_reset(void* ctx) {
}
static sdhci_protocol_ops_t hisi_sdhci_sdhci_proto = {
.get_interrupt = hisi_sdhci_get_interrupt,
.get_mmio = hisi_sdhci_get_mmio,
.get_base_clock = hisi_sdhci_get_base_clock,
.get_dma_offset = hisi_sdhci_get_dma_offset,
.get_quirks = hisi_sdhci_get_quirks,
.hw_reset = hisi_sdhci_hw_reset,
};
static void hisi_sdhci_unbind(void* ctx) {
hisi_sdhci_device_t* dev = ctx;
device_remove(dev->zxdev);
}
static void hisi_sdhci_release(void* ctx) {
hisi_sdhci_device_t* dev = ctx;
pdev_vmo_buffer_release(&dev->mmio);
pdev_vmo_buffer_release(&dev->pinmux);
free(dev);
}
static zx_protocol_device_t hisi_sdhci_device_proto = {
.version = DEVICE_OPS_VERSION,
.unbind = hisi_sdhci_unbind,
.release = hisi_sdhci_release,
};
static zx_status_t hisi_sdhci_bind(void* ctx, zx_device_t* parent) {
zxlogf(INFO, "hisi_sdhci_bind\n");
hisi_sdhci_device_t* dev = calloc(1, sizeof(hisi_sdhci_device_t));
if (!dev) {
return ZX_ERR_NO_MEMORY;
}
zx_status_t status = device_get_protocol(parent, ZX_PROTOCOL_PLATFORM_DEV, &dev->pdev);
if (status != ZX_OK) {
goto fail;
}
status = pdev_map_mmio_buffer(&dev->pdev, SDHCI_MMIO, ZX_CACHE_POLICY_UNCACHED_DEVICE,
&dev->mmio);
if (status != ZX_OK) {
goto fail;
}
status = pdev_map_mmio_buffer(&dev->pdev, SDHCI_PINMUX, ZX_CACHE_POLICY_UNCACHED_DEVICE,
&dev->pinmux);
if (status != ZX_OK) {
goto fail;
}
// Configure pins for the SD controller
volatile uint32_t* pinmux = dev->pinmux.vaddr;
writel(1, pinmux + 0); // SD_CLK
writel(1, pinmux + 1); // SD_CMD
writel(1, pinmux + 2); // SD_DATA0
writel(1, pinmux + 3); // SD_DATA1
writel(1, pinmux + 4); // SD_DATA2
writel(1, pinmux + 5); // SD_DATA3
#define SDMMC_CDTHRCTL 0x100
#define SDMMC_SET_THLD(v, x) (((v) & 0xFFF) << 16 | (x))
#define SDMMC_CARD_RD_THR_EN (1 << 0)
#define SDCARD_RD_THRESHOLD (512)
#define SDMMC_VERID 0x06c
volatile uint32_t* cdthrctl = dev->mmio.vaddr + SDMMC_CDTHRCTL;
volatile uint32_t* verid = dev->mmio.vaddr + SDMMC_VERID;
printf("try to read version\n");
printf("SDMMC_VERID %04x\n", readl(verid));
writel(SDMMC_SET_THLD(SDCARD_RD_THRESHOLD, SDMMC_CARD_RD_THR_EN), cdthrctl);
device_add_args_t args = {
.version = DEVICE_ADD_ARGS_VERSION,
.name = "hi3660-sdhci",
.ctx = dev,
.ops = &hisi_sdhci_device_proto,
.proto_id = ZX_PROTOCOL_SDHCI,
.proto_ops = &hisi_sdhci_sdhci_proto,
};
status = device_add(parent, &args, &dev->zxdev);
if (status != ZX_OK) {
goto fail;
}
return ZX_OK;
fail:
zxlogf(ERROR, "hisi_sdhci_bind failed: %d\n", status);
hisi_sdhci_release(dev);
return status;
}
static zx_driver_ops_t hisi_sdhci_driver_ops = {
.version = DRIVER_OPS_VERSION,
.bind = hisi_sdhci_bind,
};
// clang-format off
ZIRCON_DRIVER_BEGIN(hisi_sdhci, hisi_sdhci_driver_ops, "zircon", "0.1", 3)
BI_ABORT_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_HISILICON),
BI_ABORT_IF(NE, BIND_PLATFORM_DEV_PID, PDEV_PID_HI3660),
BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_DID, PDEV_DID_HI3660_SDHCI),
ZIRCON_DRIVER_END(hi3660_sdhci)