GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
menu "Device Drivers"
|
||||
|
||||
source "drivers/core/Kconfig"
|
||||
|
||||
# types of drivers sorted in alphabetical order
|
||||
|
||||
source "drivers/adc/Kconfig"
|
||||
|
||||
source "drivers/ata/Kconfig"
|
||||
|
||||
source "drivers/axi/Kconfig"
|
||||
|
||||
source "drivers/block/Kconfig"
|
||||
|
||||
source "drivers/bootcount/Kconfig"
|
||||
|
||||
source "drivers/cache/Kconfig"
|
||||
|
||||
source "drivers/clk/Kconfig"
|
||||
|
||||
source "drivers/cpu/Kconfig"
|
||||
|
||||
source "drivers/crypto/Kconfig"
|
||||
|
||||
source "drivers/ddr/Kconfig"
|
||||
|
||||
source "drivers/demo/Kconfig"
|
||||
|
||||
source "drivers/board/Kconfig"
|
||||
|
||||
source "drivers/ddr/fsl/Kconfig"
|
||||
|
||||
source "drivers/dfu/Kconfig"
|
||||
|
||||
source "drivers/dma/Kconfig"
|
||||
|
||||
source "drivers/fastboot/Kconfig"
|
||||
|
||||
source "drivers/firmware/Kconfig"
|
||||
|
||||
source "drivers/fpga/Kconfig"
|
||||
|
||||
source "drivers/gpio/Kconfig"
|
||||
|
||||
source "drivers/hwspinlock/Kconfig"
|
||||
|
||||
source "drivers/i2c/Kconfig"
|
||||
|
||||
source "drivers/input/Kconfig"
|
||||
|
||||
source "drivers/led/Kconfig"
|
||||
|
||||
source "drivers/mailbox/Kconfig"
|
||||
|
||||
source "drivers/memory/Kconfig"
|
||||
|
||||
source "drivers/misc/Kconfig"
|
||||
|
||||
source "drivers/mmc/Kconfig"
|
||||
|
||||
source "drivers/mtd/Kconfig"
|
||||
|
||||
source "drivers/net/Kconfig"
|
||||
|
||||
source "drivers/nvme/Kconfig"
|
||||
|
||||
source "drivers/pci/Kconfig"
|
||||
|
||||
source "drivers/pci_endpoint/Kconfig"
|
||||
|
||||
source "drivers/pch/Kconfig"
|
||||
|
||||
source "drivers/phy/Kconfig"
|
||||
|
||||
source "drivers/phy/allwinner/Kconfig"
|
||||
|
||||
source "drivers/phy/marvell/Kconfig"
|
||||
|
||||
source "drivers/pinctrl/Kconfig"
|
||||
|
||||
source "drivers/power/Kconfig"
|
||||
|
||||
source "drivers/pwm/Kconfig"
|
||||
|
||||
source "drivers/qe/Kconfig"
|
||||
|
||||
source "drivers/ram/Kconfig"
|
||||
|
||||
source "drivers/remoteproc/Kconfig"
|
||||
|
||||
source "drivers/reset/Kconfig"
|
||||
|
||||
source "drivers/rtc/Kconfig"
|
||||
|
||||
source "drivers/scsi/Kconfig"
|
||||
|
||||
source "drivers/serial/Kconfig"
|
||||
|
||||
source "drivers/smem/Kconfig"
|
||||
|
||||
source "drivers/sound/Kconfig"
|
||||
|
||||
source "drivers/soc/Kconfig"
|
||||
|
||||
source "drivers/spi/Kconfig"
|
||||
|
||||
source "drivers/spmi/Kconfig"
|
||||
|
||||
source "drivers/sysreset/Kconfig"
|
||||
|
||||
source "drivers/tee/Kconfig"
|
||||
|
||||
source "drivers/thermal/Kconfig"
|
||||
|
||||
source "drivers/timer/Kconfig"
|
||||
|
||||
source "drivers/tpm/Kconfig"
|
||||
|
||||
source "drivers/usb/Kconfig"
|
||||
|
||||
source "drivers/ufs/Kconfig"
|
||||
|
||||
source "drivers/video/Kconfig"
|
||||
|
||||
source "drivers/virtio/Kconfig"
|
||||
|
||||
source "drivers/w1/Kconfig"
|
||||
|
||||
source "drivers/w1-eeprom/Kconfig"
|
||||
|
||||
source "drivers/watchdog/Kconfig"
|
||||
|
||||
config PHYS_TO_BUS
|
||||
bool "Custom physical to bus address mapping"
|
||||
help
|
||||
Some SoCs use a different address map for CPU physical addresses and
|
||||
peripheral DMA master accesses. If yours does, select this option in
|
||||
your platform's Kconfig, and implement the appropriate mapping
|
||||
functions in your platform's support code.
|
||||
|
||||
endmenu
|
||||
@@ -0,0 +1,118 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
obj-$(CONFIG_$(SPL_TPL_)CLK) += clk/
|
||||
obj-$(CONFIG_$(SPL_TPL_)DM) += core/
|
||||
obj-$(CONFIG_$(SPL_TPL_)DFU) += dfu/
|
||||
obj-$(CONFIG_$(SPL_TPL_)GPIO_SUPPORT) += gpio/
|
||||
obj-$(CONFIG_$(SPL_TPL_)DRIVERS_MISC_SUPPORT) += misc/
|
||||
obj-$(CONFIG_$(SPL_TPL_)SYSRESET) += sysreset/
|
||||
obj-$(CONFIG_$(SPL_TPL_)FIRMWARE) +=firmware/
|
||||
obj-$(CONFIG_$(SPL_TPL_)I2C_SUPPORT) += i2c/
|
||||
obj-$(CONFIG_$(SPL_TPL_)INPUT) += input/
|
||||
obj-$(CONFIG_$(SPL_TPL_)LED) += led/
|
||||
obj-$(CONFIG_$(SPL_TPL_)MMC_SUPPORT) += mmc/
|
||||
obj-y += mtd/
|
||||
obj-$(CONFIG_$(SPL_TPL_)PCH_SUPPORT) += pch/
|
||||
obj-$(CONFIG_$(SPL_TPL_)PCI) += pci/
|
||||
obj-$(CONFIG_$(SPL_TPL_)PHY) += phy/
|
||||
obj-$(CONFIG_$(SPL_TPL_)PINCTRL) += pinctrl/
|
||||
obj-$(CONFIG_$(SPL_TPL_)RAM) += ram/
|
||||
obj-$(CONFIG_$(SPL_TPL_)RTC_SUPPORT) += rtc/
|
||||
obj-$(CONFIG_$(SPL_TPL_)SERIAL_SUPPORT) += serial/
|
||||
obj-$(CONFIG_$(SPL_TPL_)SPI_SUPPORT) += spi/
|
||||
obj-$(CONFIG_$(SPL_TPL_)TIMER) += timer/
|
||||
obj-$(CONFIG_$(SPL_TPL_)VIRTIO) += virtio/
|
||||
obj-$(CONFIG_$(SPL_)DM_MAILBOX) += mailbox/
|
||||
obj-$(CONFIG_$(SPL_)REMOTEPROC) += remoteproc/
|
||||
obj-$(CONFIG_$(SPL_TPL_)TPM) += tpm/
|
||||
|
||||
ifndef CONFIG_TPL_BUILD
|
||||
ifdef CONFIG_SPL_BUILD
|
||||
|
||||
obj-$(CONFIG_SPL_BOOTCOUNT_LIMIT) += bootcount/
|
||||
obj-$(CONFIG_SPL_CACHE_SUPPORT) += cache/
|
||||
obj-$(CONFIG_SPL_CPU_SUPPORT) += cpu/
|
||||
obj-$(CONFIG_SPL_CRYPTO_SUPPORT) += crypto/
|
||||
obj-$(CONFIG_SPL_MPC8XXX_INIT_DDR_SUPPORT) += ddr/fsl/
|
||||
obj-$(CONFIG_ARMADA_38X) += ddr/marvell/a38x/
|
||||
obj-$(CONFIG_ARMADA_XP) += ddr/marvell/axp/
|
||||
obj-$(CONFIG_$(SPL_)ALTERA_SDRAM) += ddr/altera/
|
||||
obj-$(CONFIG_ARCH_IMX8M) += ddr/imx/imx8m/
|
||||
obj-$(CONFIG_SPL_POWER_SUPPORT) += power/ power/pmic/
|
||||
obj-$(CONFIG_SPL_POWER_SUPPORT) += power/regulator/
|
||||
obj-$(CONFIG_SPL_POWER_DOMAIN) += power/domain/
|
||||
obj-$(CONFIG_SPL_DM_RESET) += reset/
|
||||
obj-$(CONFIG_SPL_DMA_SUPPORT) += dma/
|
||||
obj-$(CONFIG_SPL_ETH_SUPPORT) += net/
|
||||
obj-$(CONFIG_SPL_ETH_SUPPORT) += net/phy/
|
||||
obj-$(CONFIG_SPL_USB_ETHER) += net/phy/
|
||||
obj-$(CONFIG_SPL_MUSB_NEW_SUPPORT) += usb/musb-new/
|
||||
obj-$(CONFIG_SPL_USB_GADGET) += usb/gadget/
|
||||
obj-$(CONFIG_SPL_USB_GADGET) += usb/common/
|
||||
obj-$(CONFIG_SPL_USB_GADGET) += usb/gadget/udc/
|
||||
obj-$(CONFIG_SPL_WATCHDOG_SUPPORT) += watchdog/
|
||||
obj-$(CONFIG_SPL_USB_HOST_SUPPORT) += usb/host/
|
||||
obj-$(CONFIG_OMAP_USB_PHY) += usb/phy/
|
||||
obj-$(CONFIG_SPL_SATA_SUPPORT) += ata/ scsi/
|
||||
obj-$(CONFIG_HAVE_BLOCK_DEVICE) += block/
|
||||
obj-$(CONFIG_SPL_FPGA_SUPPORT) += fpga/
|
||||
obj-$(CONFIG_SPL_THERMAL) += thermal/
|
||||
|
||||
endif
|
||||
endif
|
||||
|
||||
ifdef CONFIG_TPL_BUILD
|
||||
|
||||
obj-$(CONFIG_TPL_MPC8XXX_INIT_DDR_SUPPORT) += ddr/fsl/
|
||||
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_SPL_BUILD)$(CONFIG_TPL_BUILD),)
|
||||
|
||||
obj-y += adc/
|
||||
obj-y += ata/
|
||||
obj-$(CONFIG_DM_DEMO) += demo/
|
||||
obj-$(CONFIG_BIOSEMU) += bios_emulator/
|
||||
obj-y += block/
|
||||
obj-y += board/
|
||||
obj-$(CONFIG_BOOTCOUNT_LIMIT) += bootcount/
|
||||
obj-y += cache/
|
||||
obj-$(CONFIG_CPU) += cpu/
|
||||
obj-y += crypto/
|
||||
obj-$(CONFIG_FASTBOOT) += fastboot/
|
||||
obj-$(CONFIG_FPGA) += fpga/
|
||||
obj-y += misc/
|
||||
obj-$(CONFIG_MMC) += mmc/
|
||||
obj-$(CONFIG_NVME) += nvme/
|
||||
obj-$(CONFIG_PCI_ENDPOINT) += pci_endpoint/
|
||||
obj-y += dfu/
|
||||
obj-$(CONFIG_PCH) += pch/
|
||||
obj-y += phy/allwinner/
|
||||
obj-y += phy/marvell/
|
||||
obj-y += rtc/
|
||||
obj-y += scsi/
|
||||
obj-y += sound/
|
||||
obj-y += spmi/
|
||||
obj-y += video/
|
||||
obj-y += watchdog/
|
||||
obj-$(CONFIG_QE) += qe/
|
||||
obj-$(CONFIG_U_QE) += qe/
|
||||
obj-y += mailbox/
|
||||
obj-y += memory/
|
||||
obj-y += mtd/
|
||||
obj-y += pwm/
|
||||
obj-y += reset/
|
||||
obj-y += input/
|
||||
# SOC specific infrastructure drivers.
|
||||
obj-y += smem/
|
||||
obj-y += soc/
|
||||
obj-y += thermal/
|
||||
obj-$(CONFIG_TEE) += tee/
|
||||
obj-y += axi/
|
||||
obj-y += ufs/
|
||||
obj-$(CONFIG_W1) += w1/
|
||||
obj-$(CONFIG_W1_EEPROM) += w1-eeprom/
|
||||
|
||||
obj-$(CONFIG_MACH_PIC32) += ddr/microchip/
|
||||
obj-$(CONFIG_DM_HWSPINLOCK) += hwspinlock/
|
||||
endif
|
||||
@@ -0,0 +1,65 @@
|
||||
config ADC
|
||||
bool "Enable ADC drivers using Driver Model"
|
||||
help
|
||||
This enables ADC API for drivers, which allows driving ADC features
|
||||
by single and multi-channel methods for:
|
||||
- start/stop/get data for conversion of a single-channel selected by
|
||||
a number or multi-channels selected by a bitmask
|
||||
- get data mask (ADC resolution)
|
||||
ADC reference Voltage supply options:
|
||||
- methods for get Vdd/Vss reference Voltage values with polarity
|
||||
- support supply's phandle with auto-enable
|
||||
- supply polarity setting in fdt
|
||||
|
||||
config ADC_EXYNOS
|
||||
bool "Enable Exynos 54xx ADC driver"
|
||||
help
|
||||
This enables basic driver for Exynos ADC compatible with Exynos54xx.
|
||||
It provides:
|
||||
- 10 analog input channels
|
||||
- 12-bit resolution
|
||||
- 600 KSPS of sample rate
|
||||
|
||||
config ADC_SANDBOX
|
||||
bool "Enable Sandbox ADC test driver"
|
||||
help
|
||||
This enables driver for Sandbox ADC device emulation.
|
||||
It provides:
|
||||
- 4 analog input channels
|
||||
- 16-bit resolution
|
||||
- single and multi-channel conversion mode
|
||||
|
||||
config SARADC_MESON
|
||||
bool "Enable Amlogic Meson SARADC driver"
|
||||
imply REGMAP
|
||||
help
|
||||
This enables driver for Amlogic Meson SARADC.
|
||||
It provides:
|
||||
- 8 analog input channels
|
||||
- 1O or 12 bits resolution
|
||||
- Up to 1MSPS of sample rate
|
||||
|
||||
config SARADC_ROCKCHIP
|
||||
bool "Enable Rockchip SARADC driver"
|
||||
help
|
||||
This enables driver for Rockchip SARADC.
|
||||
It provides:
|
||||
- 2~6 analog input channels
|
||||
- 1O or 12 bits resolution
|
||||
- Up to 1MSPS of sample rate
|
||||
|
||||
config STM32_ADC
|
||||
bool "Enable STMicroelectronics STM32 ADC driver"
|
||||
depends on ADC && (STM32H7 || ARCH_STM32MP)
|
||||
help
|
||||
This enables driver for STMicroelectronics STM32 analog-to-digital
|
||||
converter (ADC).
|
||||
A STM32 ADC block can be composed of several individual ADCs.
|
||||
Each has its own private registers, but shares some resources:
|
||||
- clock selection and prescaler
|
||||
- voltage reference
|
||||
- common registers area.
|
||||
STM32 ADC driver is composed of:
|
||||
- core driver to deal with common resources
|
||||
- child driver to deal with individual ADC resources (declare ADC
|
||||
device and associated channels, start/stop conversions)
|
||||
@@ -0,0 +1,12 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
# Copyright (C) 2015 Samsung Electronics
|
||||
# Przemyslaw Marczak <p.marczak@samsung.com>
|
||||
#
|
||||
|
||||
obj-$(CONFIG_ADC) += adc-uclass.o
|
||||
obj-$(CONFIG_ADC_EXYNOS) += exynos-adc.o
|
||||
obj-$(CONFIG_ADC_SANDBOX) += sandbox.o
|
||||
obj-$(CONFIG_SARADC_ROCKCHIP) += rockchip-saradc.o
|
||||
obj-$(CONFIG_SARADC_MESON) += meson-saradc.o
|
||||
obj-$(CONFIG_STM32_ADC) += stm32-adc.o stm32-adc-core.o
|
||||
@@ -0,0 +1,440 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2015 Samsung Electronics
|
||||
* Przemyslaw Marczak <p.marczak@samsung.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <errno.h>
|
||||
#include <div64.h>
|
||||
#include <dm.h>
|
||||
#include <dm/lists.h>
|
||||
#include <dm/device-internal.h>
|
||||
#include <dm/uclass-internal.h>
|
||||
#include <adc.h>
|
||||
#include <power/regulator.h>
|
||||
|
||||
#define ADC_UCLASS_PLATDATA_SIZE sizeof(struct adc_uclass_platdata)
|
||||
#define CHECK_NUMBER true
|
||||
#define CHECK_MASK (!CHECK_NUMBER)
|
||||
|
||||
/* TODO: add support for timer uclass (for early calls) */
|
||||
#ifdef CONFIG_SANDBOX_ARCH
|
||||
#define sdelay(x) udelay(x)
|
||||
#else
|
||||
extern void sdelay(unsigned long loops);
|
||||
#endif
|
||||
|
||||
static int check_channel(struct udevice *dev, int value, bool number_or_mask,
|
||||
const char *caller_function)
|
||||
{
|
||||
struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev);
|
||||
unsigned mask = number_or_mask ? (1 << value) : value;
|
||||
|
||||
/* For the real ADC hardware, some ADC channels can be inactive.
|
||||
* For example if device has 4 analog channels, and only channels
|
||||
* 1-st and 3-rd are valid, then channel mask is: 0b1010, so request
|
||||
* with mask 0b1110 should return an error.
|
||||
*/
|
||||
if ((uc_pdata->channel_mask >= mask) && (uc_pdata->channel_mask & mask))
|
||||
return 0;
|
||||
|
||||
printf("Error in %s/%s().\nWrong channel selection for device: %s\n",
|
||||
__FILE__, caller_function, dev->name);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int adc_supply_enable(struct udevice *dev)
|
||||
{
|
||||
struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev);
|
||||
const char *supply_type;
|
||||
int ret = 0;
|
||||
|
||||
if (uc_pdata->vdd_supply) {
|
||||
supply_type = "vdd";
|
||||
ret = regulator_set_enable(uc_pdata->vdd_supply, true);
|
||||
}
|
||||
|
||||
if (!ret && uc_pdata->vss_supply) {
|
||||
supply_type = "vss";
|
||||
ret = regulator_set_enable(uc_pdata->vss_supply, true);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
pr_err("%s: can't enable %s-supply!", dev->name, supply_type);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int adc_data_mask(struct udevice *dev, unsigned int *data_mask)
|
||||
{
|
||||
struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev);
|
||||
|
||||
if (!uc_pdata)
|
||||
return -ENOSYS;
|
||||
|
||||
*data_mask = uc_pdata->data_mask;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int adc_channel_mask(struct udevice *dev, unsigned int *channel_mask)
|
||||
{
|
||||
struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev);
|
||||
|
||||
if (!uc_pdata)
|
||||
return -ENOSYS;
|
||||
|
||||
*channel_mask = uc_pdata->channel_mask;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int adc_stop(struct udevice *dev)
|
||||
{
|
||||
const struct adc_ops *ops = dev_get_driver_ops(dev);
|
||||
|
||||
if (!ops->stop)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->stop(dev);
|
||||
}
|
||||
|
||||
int adc_start_channel(struct udevice *dev, int channel)
|
||||
{
|
||||
const struct adc_ops *ops = dev_get_driver_ops(dev);
|
||||
int ret;
|
||||
|
||||
if (!ops->start_channel)
|
||||
return -ENOSYS;
|
||||
|
||||
ret = check_channel(dev, channel, CHECK_NUMBER, __func__);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adc_supply_enable(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return ops->start_channel(dev, channel);
|
||||
}
|
||||
|
||||
int adc_start_channels(struct udevice *dev, unsigned int channel_mask)
|
||||
{
|
||||
const struct adc_ops *ops = dev_get_driver_ops(dev);
|
||||
int ret;
|
||||
|
||||
if (!ops->start_channels)
|
||||
return -ENOSYS;
|
||||
|
||||
ret = check_channel(dev, channel_mask, CHECK_MASK, __func__);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adc_supply_enable(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return ops->start_channels(dev, channel_mask);
|
||||
}
|
||||
|
||||
int adc_channel_data(struct udevice *dev, int channel, unsigned int *data)
|
||||
{
|
||||
struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev);
|
||||
const struct adc_ops *ops = dev_get_driver_ops(dev);
|
||||
unsigned int timeout_us = uc_pdata->data_timeout_us;
|
||||
int ret;
|
||||
|
||||
if (!ops->channel_data)
|
||||
return -ENOSYS;
|
||||
|
||||
ret = check_channel(dev, channel, CHECK_NUMBER, __func__);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
do {
|
||||
ret = ops->channel_data(dev, channel, data);
|
||||
if (!ret || ret != -EBUSY)
|
||||
break;
|
||||
|
||||
/* TODO: use timer uclass (for early calls). */
|
||||
sdelay(5);
|
||||
} while (timeout_us--);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int adc_channels_data(struct udevice *dev, unsigned int channel_mask,
|
||||
struct adc_channel *channels)
|
||||
{
|
||||
struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev);
|
||||
unsigned int timeout_us = uc_pdata->multidata_timeout_us;
|
||||
const struct adc_ops *ops = dev_get_driver_ops(dev);
|
||||
int ret;
|
||||
|
||||
if (!ops->channels_data)
|
||||
return -ENOSYS;
|
||||
|
||||
ret = check_channel(dev, channel_mask, CHECK_MASK, __func__);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
do {
|
||||
ret = ops->channels_data(dev, channel_mask, channels);
|
||||
if (!ret || ret != -EBUSY)
|
||||
break;
|
||||
|
||||
/* TODO: use timer uclass (for early calls). */
|
||||
sdelay(5);
|
||||
} while (timeout_us--);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int adc_channel_single_shot(const char *name, int channel, unsigned int *data)
|
||||
{
|
||||
struct udevice *dev;
|
||||
int ret;
|
||||
|
||||
ret = uclass_get_device_by_name(UCLASS_ADC, name, &dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adc_start_channel(dev, channel);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adc_channel_data(dev, channel, data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _adc_channels_single_shot(struct udevice *dev,
|
||||
unsigned int channel_mask,
|
||||
struct adc_channel *channels)
|
||||
{
|
||||
unsigned int data;
|
||||
int channel, ret;
|
||||
|
||||
for (channel = 0; channel <= ADC_MAX_CHANNEL; channel++) {
|
||||
/* Check channel bit. */
|
||||
if (!((channel_mask >> channel) & 0x1))
|
||||
continue;
|
||||
|
||||
ret = adc_start_channel(dev, channel);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adc_channel_data(dev, channel, &data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
channels->id = channel;
|
||||
channels->data = data;
|
||||
channels++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int adc_channels_single_shot(const char *name, unsigned int channel_mask,
|
||||
struct adc_channel *channels)
|
||||
{
|
||||
struct udevice *dev;
|
||||
int ret;
|
||||
|
||||
ret = uclass_get_device_by_name(UCLASS_ADC, name, &dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adc_start_channels(dev, channel_mask);
|
||||
if (ret)
|
||||
goto try_manual;
|
||||
|
||||
ret = adc_channels_data(dev, channel_mask, channels);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
|
||||
try_manual:
|
||||
if (ret != -ENOSYS)
|
||||
return ret;
|
||||
|
||||
return _adc_channels_single_shot(dev, channel_mask, channels);
|
||||
}
|
||||
|
||||
static int adc_vdd_platdata_update(struct udevice *dev)
|
||||
{
|
||||
struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev);
|
||||
int ret;
|
||||
|
||||
/* Warning!
|
||||
* This function can't return supply device before its bind.
|
||||
* Please pay attention to proper fdt scan sequence. If ADC device
|
||||
* will bind before its supply regulator device, then the below 'get'
|
||||
* will return an error.
|
||||
*/
|
||||
if (!uc_pdata->vdd_supply)
|
||||
return 0;
|
||||
|
||||
ret = regulator_get_value(uc_pdata->vdd_supply);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
uc_pdata->vdd_microvolts = ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adc_vss_platdata_update(struct udevice *dev)
|
||||
{
|
||||
struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev);
|
||||
int ret;
|
||||
|
||||
if (!uc_pdata->vss_supply)
|
||||
return 0;
|
||||
|
||||
ret = regulator_get_value(uc_pdata->vss_supply);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
uc_pdata->vss_microvolts = ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int adc_vdd_value(struct udevice *dev, int *uV)
|
||||
{
|
||||
struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev);
|
||||
int ret, value_sign = uc_pdata->vdd_polarity_negative ? -1 : 1;
|
||||
|
||||
/* Update the regulator Value. */
|
||||
ret = adc_vdd_platdata_update(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (uc_pdata->vdd_microvolts == -ENODATA)
|
||||
return -ENODATA;
|
||||
|
||||
*uV = uc_pdata->vdd_microvolts * value_sign;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int adc_vss_value(struct udevice *dev, int *uV)
|
||||
{
|
||||
struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev);
|
||||
int ret, value_sign = uc_pdata->vss_polarity_negative ? -1 : 1;
|
||||
|
||||
/* Update the regulator Value. */
|
||||
ret = adc_vss_platdata_update(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (uc_pdata->vss_microvolts == -ENODATA)
|
||||
return -ENODATA;
|
||||
|
||||
*uV = uc_pdata->vss_microvolts * value_sign;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int adc_raw_to_uV(struct udevice *dev, unsigned int raw, int *uV)
|
||||
{
|
||||
unsigned int data_mask;
|
||||
int ret, val, vref;
|
||||
u64 raw64 = raw;
|
||||
|
||||
ret = adc_vdd_value(dev, &vref);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!adc_vss_value(dev, &val))
|
||||
vref -= val;
|
||||
|
||||
ret = adc_data_mask(dev, &data_mask);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
raw64 *= vref;
|
||||
do_div(raw64, data_mask);
|
||||
*uV = raw64;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adc_vdd_platdata_set(struct udevice *dev)
|
||||
{
|
||||
struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev);
|
||||
int ret;
|
||||
char *prop;
|
||||
|
||||
prop = "vdd-polarity-negative";
|
||||
uc_pdata->vdd_polarity_negative = dev_read_bool(dev, prop);
|
||||
|
||||
/* Optionally get regulators */
|
||||
ret = device_get_supply_regulator(dev, "vdd-supply",
|
||||
&uc_pdata->vdd_supply);
|
||||
if (!ret)
|
||||
return adc_vdd_platdata_update(dev);
|
||||
|
||||
if (ret != -ENOENT)
|
||||
return ret;
|
||||
|
||||
/* No vdd-supply phandle. */
|
||||
prop = "vdd-microvolts";
|
||||
uc_pdata->vdd_microvolts = dev_read_u32_default(dev, prop, -ENODATA);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adc_vss_platdata_set(struct udevice *dev)
|
||||
{
|
||||
struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev);
|
||||
int ret;
|
||||
char *prop;
|
||||
|
||||
prop = "vss-polarity-negative";
|
||||
uc_pdata->vss_polarity_negative = dev_read_bool(dev, prop);
|
||||
|
||||
ret = device_get_supply_regulator(dev, "vss-supply",
|
||||
&uc_pdata->vss_supply);
|
||||
if (!ret)
|
||||
return adc_vss_platdata_update(dev);
|
||||
|
||||
if (ret != -ENOENT)
|
||||
return ret;
|
||||
|
||||
/* No vss-supply phandle. */
|
||||
prop = "vss-microvolts";
|
||||
uc_pdata->vss_microvolts = dev_read_u32_default(dev, prop, -ENODATA);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adc_pre_probe(struct udevice *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Set ADC VDD platdata: polarity, uV, regulator (phandle). */
|
||||
ret = adc_vdd_platdata_set(dev);
|
||||
if (ret)
|
||||
pr_err("%s: Can't update Vdd. Error: %d", dev->name, ret);
|
||||
|
||||
/* Set ADC VSS platdata: polarity, uV, regulator (phandle). */
|
||||
ret = adc_vss_platdata_set(dev);
|
||||
if (ret)
|
||||
pr_err("%s: Can't update Vss. Error: %d", dev->name, ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
UCLASS_DRIVER(adc) = {
|
||||
.id = UCLASS_ADC,
|
||||
.name = "adc",
|
||||
.pre_probe = adc_pre_probe,
|
||||
.per_device_platdata_auto_alloc_size = ADC_UCLASS_PLATDATA_SIZE,
|
||||
};
|
||||
@@ -0,0 +1,144 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2015 Samsung Electronics
|
||||
* Przemyslaw Marczak <p.marczak@samsung.com>
|
||||
*/
|
||||
#include <common.h>
|
||||
#include <errno.h>
|
||||
#include <dm.h>
|
||||
#include <adc.h>
|
||||
#include <asm/arch/adc.h>
|
||||
|
||||
struct exynos_adc_priv {
|
||||
int active_channel;
|
||||
struct exynos_adc_v2 *regs;
|
||||
};
|
||||
|
||||
int exynos_adc_channel_data(struct udevice *dev, int channel,
|
||||
unsigned int *data)
|
||||
{
|
||||
struct exynos_adc_priv *priv = dev_get_priv(dev);
|
||||
struct exynos_adc_v2 *regs = priv->regs;
|
||||
|
||||
if (channel != priv->active_channel) {
|
||||
pr_err("Requested channel is not active!");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ADC_V2_GET_STATUS_FLAG(readl(®s->status)) != FLAG_CONV_END)
|
||||
return -EBUSY;
|
||||
|
||||
*data = readl(®s->dat) & ADC_V2_DAT_MASK;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int exynos_adc_start_channel(struct udevice *dev, int channel)
|
||||
{
|
||||
struct exynos_adc_priv *priv = dev_get_priv(dev);
|
||||
struct exynos_adc_v2 *regs = priv->regs;
|
||||
unsigned int cfg;
|
||||
|
||||
/* Choose channel */
|
||||
cfg = readl(®s->con2);
|
||||
cfg &= ~ADC_V2_CON2_CHAN_SEL_MASK;
|
||||
cfg |= ADC_V2_CON2_CHAN_SEL(channel);
|
||||
writel(cfg, ®s->con2);
|
||||
|
||||
/* Start conversion */
|
||||
cfg = readl(®s->con1);
|
||||
writel(cfg | ADC_V2_CON1_STC_EN, ®s->con1);
|
||||
|
||||
priv->active_channel = channel;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int exynos_adc_stop(struct udevice *dev)
|
||||
{
|
||||
struct exynos_adc_priv *priv = dev_get_priv(dev);
|
||||
struct exynos_adc_v2 *regs = priv->regs;
|
||||
unsigned int cfg;
|
||||
|
||||
/* Stop conversion */
|
||||
cfg = readl(®s->con1);
|
||||
cfg &= ~ADC_V2_CON1_STC_EN;
|
||||
|
||||
writel(cfg, ®s->con1);
|
||||
|
||||
priv->active_channel = -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int exynos_adc_probe(struct udevice *dev)
|
||||
{
|
||||
struct exynos_adc_priv *priv = dev_get_priv(dev);
|
||||
struct exynos_adc_v2 *regs = priv->regs;
|
||||
unsigned int cfg;
|
||||
|
||||
/* Check HW version */
|
||||
if (readl(®s->version) != ADC_V2_VERSION) {
|
||||
pr_err("This driver supports only ADC v2!");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
/* ADC Reset */
|
||||
writel(ADC_V2_CON1_SOFT_RESET, ®s->con1);
|
||||
|
||||
/* Disable INT - will read status only */
|
||||
writel(0x0, ®s->int_en);
|
||||
|
||||
/* CON2 - set conversion parameters */
|
||||
cfg = ADC_V2_CON2_C_TIME(3); /* Conversion times: (1 << 3) = 8 */
|
||||
cfg |= ADC_V2_CON2_OSEL(OSEL_BINARY);
|
||||
cfg |= ADC_V2_CON2_ESEL(ESEL_ADC_EVAL_TIME_20CLK);
|
||||
cfg |= ADC_V2_CON2_HIGHF(HIGHF_CONV_RATE_600KSPS);
|
||||
writel(cfg, ®s->con2);
|
||||
|
||||
priv->active_channel = -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int exynos_adc_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev);
|
||||
struct exynos_adc_priv *priv = dev_get_priv(dev);
|
||||
|
||||
priv->regs = (struct exynos_adc_v2 *)devfdt_get_addr(dev);
|
||||
if (priv->regs == (struct exynos_adc_v2 *)FDT_ADDR_T_NONE) {
|
||||
pr_err("Dev: %s - can't get address!", dev->name);
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
uc_pdata->data_mask = ADC_V2_DAT_MASK;
|
||||
uc_pdata->data_format = ADC_DATA_FORMAT_BIN;
|
||||
uc_pdata->data_timeout_us = ADC_V2_CONV_TIMEOUT_US;
|
||||
|
||||
/* Mask available channel bits: [0:9] */
|
||||
uc_pdata->channel_mask = (2 << ADC_V2_MAX_CHANNEL) - 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct adc_ops exynos_adc_ops = {
|
||||
.start_channel = exynos_adc_start_channel,
|
||||
.channel_data = exynos_adc_channel_data,
|
||||
.stop = exynos_adc_stop,
|
||||
};
|
||||
|
||||
static const struct udevice_id exynos_adc_ids[] = {
|
||||
{ .compatible = "samsung,exynos-adc-v2" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(exynos_adc) = {
|
||||
.name = "exynos-adc",
|
||||
.id = UCLASS_ADC,
|
||||
.of_match = exynos_adc_ids,
|
||||
.ops = &exynos_adc_ops,
|
||||
.probe = exynos_adc_probe,
|
||||
.ofdata_to_platdata = exynos_adc_ofdata_to_platdata,
|
||||
.priv_auto_alloc_size = sizeof(struct exynos_adc_priv),
|
||||
};
|
||||
@@ -0,0 +1,723 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2017 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
|
||||
* Copyright (C) 2018 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
*
|
||||
* Amlogic Meson Successive Approximation Register (SAR) A/D Converter
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <adc.h>
|
||||
#include <clk.h>
|
||||
#include <dm.h>
|
||||
#include <regmap.h>
|
||||
#include <errno.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/bitfield.h>
|
||||
|
||||
#define MESON_SAR_ADC_REG0 0x00
|
||||
#define MESON_SAR_ADC_REG0_PANEL_DETECT BIT(31)
|
||||
#define MESON_SAR_ADC_REG0_BUSY_MASK GENMASK(30, 28)
|
||||
#define MESON_SAR_ADC_REG0_DELTA_BUSY BIT(30)
|
||||
#define MESON_SAR_ADC_REG0_AVG_BUSY BIT(29)
|
||||
#define MESON_SAR_ADC_REG0_SAMPLE_BUSY BIT(28)
|
||||
#define MESON_SAR_ADC_REG0_FIFO_FULL BIT(27)
|
||||
#define MESON_SAR_ADC_REG0_FIFO_EMPTY BIT(26)
|
||||
#define MESON_SAR_ADC_REG0_FIFO_COUNT_MASK GENMASK(25, 21)
|
||||
#define MESON_SAR_ADC_REG0_ADC_BIAS_CTRL_MASK GENMASK(20, 19)
|
||||
#define MESON_SAR_ADC_REG0_CURR_CHAN_ID_MASK GENMASK(18, 16)
|
||||
#define MESON_SAR_ADC_REG0_ADC_TEMP_SEN_SEL BIT(15)
|
||||
#define MESON_SAR_ADC_REG0_SAMPLING_STOP BIT(14)
|
||||
#define MESON_SAR_ADC_REG0_CHAN_DELTA_EN_MASK GENMASK(13, 12)
|
||||
#define MESON_SAR_ADC_REG0_DETECT_IRQ_POL BIT(10)
|
||||
#define MESON_SAR_ADC_REG0_DETECT_IRQ_EN BIT(9)
|
||||
#define MESON_SAR_ADC_REG0_FIFO_CNT_IRQ_MASK GENMASK(8, 4)
|
||||
#define MESON_SAR_ADC_REG0_FIFO_IRQ_EN BIT(3)
|
||||
#define MESON_SAR_ADC_REG0_SAMPLING_START BIT(2)
|
||||
#define MESON_SAR_ADC_REG0_CONTINUOUS_EN BIT(1)
|
||||
#define MESON_SAR_ADC_REG0_SAMPLE_ENGINE_ENABLE BIT(0)
|
||||
|
||||
#define MESON_SAR_ADC_CHAN_LIST 0x04
|
||||
#define MESON_SAR_ADC_CHAN_LIST_MAX_INDEX_MASK GENMASK(26, 24)
|
||||
#define MESON_SAR_ADC_CHAN_LIST_ENTRY_MASK(_chan) \
|
||||
(GENMASK(2, 0) << ((_chan) * 3))
|
||||
|
||||
#define MESON_SAR_ADC_AVG_CNTL 0x08
|
||||
#define MESON_SAR_ADC_AVG_CNTL_AVG_MODE_SHIFT(_chan) \
|
||||
(16 + ((_chan) * 2))
|
||||
#define MESON_SAR_ADC_AVG_CNTL_AVG_MODE_MASK(_chan) \
|
||||
(GENMASK(17, 16) << ((_chan) * 2))
|
||||
#define MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_SHIFT(_chan) \
|
||||
(0 + ((_chan) * 2))
|
||||
#define MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_MASK(_chan) \
|
||||
(GENMASK(1, 0) << ((_chan) * 2))
|
||||
|
||||
#define MESON_SAR_ADC_REG3 0x0c
|
||||
#define MESON_SAR_ADC_REG3_CNTL_USE_SC_DLY BIT(31)
|
||||
#define MESON_SAR_ADC_REG3_CLK_EN BIT(30)
|
||||
#define MESON_SAR_ADC_REG3_BL30_INITIALIZED BIT(28)
|
||||
#define MESON_SAR_ADC_REG3_CTRL_CONT_RING_COUNTER_EN BIT(27)
|
||||
#define MESON_SAR_ADC_REG3_CTRL_SAMPLING_CLOCK_PHASE BIT(26)
|
||||
#define MESON_SAR_ADC_REG3_CTRL_CHAN7_MUX_SEL_MASK GENMASK(25, 23)
|
||||
#define MESON_SAR_ADC_REG3_DETECT_EN BIT(22)
|
||||
#define MESON_SAR_ADC_REG3_ADC_EN BIT(21)
|
||||
#define MESON_SAR_ADC_REG3_PANEL_DETECT_COUNT_MASK GENMASK(20, 18)
|
||||
#define MESON_SAR_ADC_REG3_PANEL_DETECT_FILTER_TB_MASK GENMASK(17, 16)
|
||||
#define MESON_SAR_ADC_REG3_ADC_CLK_DIV_SHIFT 10
|
||||
#define MESON_SAR_ADC_REG3_ADC_CLK_DIV_WIDTH 5
|
||||
#define MESON_SAR_ADC_REG3_BLOCK_DLY_SEL_MASK GENMASK(9, 8)
|
||||
#define MESON_SAR_ADC_REG3_BLOCK_DLY_MASK GENMASK(7, 0)
|
||||
|
||||
#define MESON_SAR_ADC_DELAY 0x10
|
||||
#define MESON_SAR_ADC_DELAY_INPUT_DLY_SEL_MASK GENMASK(25, 24)
|
||||
#define MESON_SAR_ADC_DELAY_BL30_BUSY BIT(15)
|
||||
#define MESON_SAR_ADC_DELAY_KERNEL_BUSY BIT(14)
|
||||
#define MESON_SAR_ADC_DELAY_INPUT_DLY_CNT_MASK GENMASK(23, 16)
|
||||
#define MESON_SAR_ADC_DELAY_SAMPLE_DLY_SEL_MASK GENMASK(9, 8)
|
||||
#define MESON_SAR_ADC_DELAY_SAMPLE_DLY_CNT_MASK GENMASK(7, 0)
|
||||
|
||||
#define MESON_SAR_ADC_LAST_RD 0x14
|
||||
#define MESON_SAR_ADC_LAST_RD_LAST_CHANNEL1_MASK GENMASK(23, 16)
|
||||
#define MESON_SAR_ADC_LAST_RD_LAST_CHANNEL0_MASK GENMASK(9, 0)
|
||||
|
||||
#define MESON_SAR_ADC_FIFO_RD 0x18
|
||||
#define MESON_SAR_ADC_FIFO_RD_CHAN_ID_MASK GENMASK(14, 12)
|
||||
#define MESON_SAR_ADC_FIFO_RD_SAMPLE_VALUE_MASK GENMASK(11, 0)
|
||||
|
||||
#define MESON_SAR_ADC_AUX_SW 0x1c
|
||||
#define MESON_SAR_ADC_AUX_SW_MUX_SEL_CHAN_SHIFT(_chan) \
|
||||
(8 + (((_chan) - 2) * 3))
|
||||
#define MESON_SAR_ADC_AUX_SW_VREF_P_MUX BIT(6)
|
||||
#define MESON_SAR_ADC_AUX_SW_VREF_N_MUX BIT(5)
|
||||
#define MESON_SAR_ADC_AUX_SW_MODE_SEL BIT(4)
|
||||
#define MESON_SAR_ADC_AUX_SW_YP_DRIVE_SW BIT(3)
|
||||
#define MESON_SAR_ADC_AUX_SW_XP_DRIVE_SW BIT(2)
|
||||
#define MESON_SAR_ADC_AUX_SW_YM_DRIVE_SW BIT(1)
|
||||
#define MESON_SAR_ADC_AUX_SW_XM_DRIVE_SW BIT(0)
|
||||
|
||||
#define MESON_SAR_ADC_CHAN_10_SW 0x20
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN1_MUX_SEL_MASK GENMASK(25, 23)
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN1_VREF_P_MUX BIT(22)
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN1_VREF_N_MUX BIT(21)
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN1_MODE_SEL BIT(20)
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN1_YP_DRIVE_SW BIT(19)
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN1_XP_DRIVE_SW BIT(18)
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN1_YM_DRIVE_SW BIT(17)
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN1_XM_DRIVE_SW BIT(16)
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN0_MUX_SEL_MASK GENMASK(9, 7)
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN0_VREF_P_MUX BIT(6)
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN0_VREF_N_MUX BIT(5)
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN0_MODE_SEL BIT(4)
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN0_YP_DRIVE_SW BIT(3)
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN0_XP_DRIVE_SW BIT(2)
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN0_YM_DRIVE_SW BIT(1)
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN0_XM_DRIVE_SW BIT(0)
|
||||
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW 0x24
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_SW_EN BIT(26)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_MUX_MASK GENMASK(25, 23)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_VREF_P_MUX BIT(22)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_VREF_N_MUX BIT(21)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_MODE_SEL BIT(20)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_YP_DRIVE_SW BIT(19)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_XP_DRIVE_SW BIT(18)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_YM_DRIVE_SW BIT(17)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_XM_DRIVE_SW BIT(16)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MUX_SEL_MASK GENMASK(9, 7)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_VREF_P_MUX BIT(6)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_VREF_N_MUX BIT(5)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MODE_SEL BIT(4)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_YP_DRIVE_SW BIT(3)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_XP_DRIVE_SW BIT(2)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_YM_DRIVE_SW BIT(1)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_XM_DRIVE_SW BIT(0)
|
||||
|
||||
#define MESON_SAR_ADC_DELTA_10 0x28
|
||||
#define MESON_SAR_ADC_DELTA_10_TEMP_SEL BIT(27)
|
||||
#define MESON_SAR_ADC_DELTA_10_TS_REVE1 BIT(26)
|
||||
#define MESON_SAR_ADC_DELTA_10_CHAN1_DELTA_VALUE_MASK GENMASK(25, 16)
|
||||
#define MESON_SAR_ADC_DELTA_10_TS_REVE0 BIT(15)
|
||||
#define MESON_SAR_ADC_DELTA_10_TS_C_SHIFT 11
|
||||
#define MESON_SAR_ADC_DELTA_10_TS_C_MASK GENMASK(14, 11)
|
||||
#define MESON_SAR_ADC_DELTA_10_TS_VBG_EN BIT(10)
|
||||
#define MESON_SAR_ADC_DELTA_10_CHAN0_DELTA_VALUE_MASK GENMASK(9, 0)
|
||||
|
||||
/*
|
||||
* NOTE: registers from here are undocumented (the vendor Linux kernel driver
|
||||
* and u-boot source served as reference). These only seem to be relevant on
|
||||
* GXBB and newer.
|
||||
*/
|
||||
#define MESON_SAR_ADC_REG11 0x2c
|
||||
#define MESON_SAR_ADC_REG11_BANDGAP_EN BIT(13)
|
||||
|
||||
#define MESON_SAR_ADC_REG13 0x34
|
||||
#define MESON_SAR_ADC_REG13_12BIT_CALIBRATION_MASK GENMASK(13, 8)
|
||||
|
||||
#define MESON_SAR_ADC_MAX_FIFO_SIZE 32
|
||||
#define MESON_SAR_ADC_TIMEOUT 100 /* ms */
|
||||
|
||||
#define NUM_CHANNELS 8
|
||||
|
||||
#define MILLION 1000000
|
||||
|
||||
struct meson_saradc_data {
|
||||
int num_bits;
|
||||
};
|
||||
|
||||
struct meson_saradc_priv {
|
||||
const struct meson_saradc_data *data;
|
||||
struct regmap *regmap;
|
||||
struct clk core_clk;
|
||||
struct clk adc_clk;
|
||||
bool initialized;
|
||||
int active_channel;
|
||||
int calibbias;
|
||||
int calibscale;
|
||||
};
|
||||
|
||||
static unsigned int
|
||||
meson_saradc_get_fifo_count(struct meson_saradc_priv *priv)
|
||||
{
|
||||
u32 regval;
|
||||
|
||||
regmap_read(priv->regmap, MESON_SAR_ADC_REG0, ®val);
|
||||
|
||||
return FIELD_GET(MESON_SAR_ADC_REG0_FIFO_COUNT_MASK, regval);
|
||||
}
|
||||
|
||||
static int meson_saradc_lock(struct meson_saradc_priv *priv)
|
||||
{
|
||||
uint val, timeout = 10000;
|
||||
|
||||
/* prevent BL30 from using the SAR ADC while we are using it */
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY,
|
||||
MESON_SAR_ADC_DELAY_KERNEL_BUSY,
|
||||
MESON_SAR_ADC_DELAY_KERNEL_BUSY);
|
||||
|
||||
/*
|
||||
* wait until BL30 releases it's lock (so we can use the SAR ADC)
|
||||
*/
|
||||
do {
|
||||
udelay(1);
|
||||
regmap_read(priv->regmap, MESON_SAR_ADC_DELAY, &val);
|
||||
} while (val & MESON_SAR_ADC_DELAY_BL30_BUSY && timeout--);
|
||||
|
||||
if (timeout < 0) {
|
||||
printf("Timeout while waiting for BL30 unlock\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void meson_saradc_unlock(struct meson_saradc_priv *priv)
|
||||
{
|
||||
/* allow BL30 to use the SAR ADC again */
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY,
|
||||
MESON_SAR_ADC_DELAY_KERNEL_BUSY, 0);
|
||||
}
|
||||
|
||||
static void meson_saradc_clear_fifo(struct meson_saradc_priv *priv)
|
||||
{
|
||||
unsigned int count, tmp;
|
||||
|
||||
for (count = 0; count < MESON_SAR_ADC_MAX_FIFO_SIZE; count++) {
|
||||
if (!meson_saradc_get_fifo_count(priv))
|
||||
break;
|
||||
|
||||
regmap_read(priv->regmap, MESON_SAR_ADC_FIFO_RD, &tmp);
|
||||
}
|
||||
}
|
||||
|
||||
static int meson_saradc_calib_val(struct meson_saradc_priv *priv, int val)
|
||||
{
|
||||
int tmp;
|
||||
|
||||
/* use val_calib = scale * val_raw + offset calibration function */
|
||||
tmp = div_s64((s64)val * priv->calibscale, MILLION) + priv->calibbias;
|
||||
|
||||
return clamp(tmp, 0, (1 << priv->data->num_bits) - 1);
|
||||
}
|
||||
|
||||
static int meson_saradc_wait_busy_clear(struct meson_saradc_priv *priv)
|
||||
{
|
||||
uint regval, timeout = 10000;
|
||||
|
||||
/*
|
||||
* NOTE: we need a small delay before reading the status, otherwise
|
||||
* the sample engine may not have started internally (which would
|
||||
* seem to us that sampling is already finished).
|
||||
*/
|
||||
do {
|
||||
udelay(1);
|
||||
regmap_read(priv->regmap, MESON_SAR_ADC_REG0, ®val);
|
||||
} while (FIELD_GET(MESON_SAR_ADC_REG0_BUSY_MASK, regval) && timeout--);
|
||||
|
||||
if (timeout < 0)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int meson_saradc_read_raw_sample(struct meson_saradc_priv *priv,
|
||||
unsigned int channel, uint *val)
|
||||
{
|
||||
uint regval, fifo_chan, fifo_val, count;
|
||||
int ret;
|
||||
|
||||
ret = meson_saradc_wait_busy_clear(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
count = meson_saradc_get_fifo_count(priv);
|
||||
if (count != 1) {
|
||||
printf("ADC FIFO has %d element(s) instead of one\n", count);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
regmap_read(priv->regmap, MESON_SAR_ADC_FIFO_RD, ®val);
|
||||
fifo_chan = FIELD_GET(MESON_SAR_ADC_FIFO_RD_CHAN_ID_MASK, regval);
|
||||
if (fifo_chan != channel) {
|
||||
printf("ADC FIFO entry belongs to channel %d instead of %d\n",
|
||||
fifo_chan, channel);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fifo_val = FIELD_GET(MESON_SAR_ADC_FIFO_RD_SAMPLE_VALUE_MASK, regval);
|
||||
fifo_val &= GENMASK(priv->data->num_bits - 1, 0);
|
||||
*val = meson_saradc_calib_val(priv, fifo_val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void meson_saradc_start_sample_engine(struct meson_saradc_priv *priv)
|
||||
{
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0,
|
||||
MESON_SAR_ADC_REG0_FIFO_IRQ_EN,
|
||||
MESON_SAR_ADC_REG0_FIFO_IRQ_EN);
|
||||
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0,
|
||||
MESON_SAR_ADC_REG0_SAMPLE_ENGINE_ENABLE,
|
||||
MESON_SAR_ADC_REG0_SAMPLE_ENGINE_ENABLE);
|
||||
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0,
|
||||
MESON_SAR_ADC_REG0_SAMPLING_START,
|
||||
MESON_SAR_ADC_REG0_SAMPLING_START);
|
||||
}
|
||||
|
||||
static void meson_saradc_stop_sample_engine(struct meson_saradc_priv *priv)
|
||||
{
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0,
|
||||
MESON_SAR_ADC_REG0_FIFO_IRQ_EN, 0);
|
||||
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0,
|
||||
MESON_SAR_ADC_REG0_SAMPLING_STOP,
|
||||
MESON_SAR_ADC_REG0_SAMPLING_STOP);
|
||||
|
||||
/* wait until all modules are stopped */
|
||||
meson_saradc_wait_busy_clear(priv);
|
||||
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0,
|
||||
MESON_SAR_ADC_REG0_SAMPLE_ENGINE_ENABLE, 0);
|
||||
}
|
||||
|
||||
enum meson_saradc_avg_mode {
|
||||
NO_AVERAGING = 0x0,
|
||||
MEAN_AVERAGING = 0x1,
|
||||
MEDIAN_AVERAGING = 0x2,
|
||||
};
|
||||
|
||||
enum meson_saradc_num_samples {
|
||||
ONE_SAMPLE = 0x0,
|
||||
TWO_SAMPLES = 0x1,
|
||||
FOUR_SAMPLES = 0x2,
|
||||
EIGHT_SAMPLES = 0x3,
|
||||
};
|
||||
|
||||
static void meson_saradc_set_averaging(struct meson_saradc_priv *priv,
|
||||
unsigned int channel,
|
||||
enum meson_saradc_avg_mode mode,
|
||||
enum meson_saradc_num_samples samples)
|
||||
{
|
||||
int val;
|
||||
|
||||
val = samples << MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_SHIFT(channel);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_AVG_CNTL,
|
||||
MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_MASK(channel),
|
||||
val);
|
||||
|
||||
val = mode << MESON_SAR_ADC_AVG_CNTL_AVG_MODE_SHIFT(channel);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_AVG_CNTL,
|
||||
MESON_SAR_ADC_AVG_CNTL_AVG_MODE_MASK(channel), val);
|
||||
}
|
||||
|
||||
static void meson_saradc_enable_channel(struct meson_saradc_priv *priv,
|
||||
unsigned int channel)
|
||||
{
|
||||
uint regval;
|
||||
|
||||
/*
|
||||
* the SAR ADC engine allows sampling multiple channels at the same
|
||||
* time. to keep it simple we're only working with one *internal*
|
||||
* channel, which starts counting at index 0 (which means: count = 1).
|
||||
*/
|
||||
regval = FIELD_PREP(MESON_SAR_ADC_CHAN_LIST_MAX_INDEX_MASK, 0);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_LIST,
|
||||
MESON_SAR_ADC_CHAN_LIST_MAX_INDEX_MASK, regval);
|
||||
|
||||
/* map channel index 0 to the channel which we want to read */
|
||||
regval = FIELD_PREP(MESON_SAR_ADC_CHAN_LIST_ENTRY_MASK(0), channel);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_LIST,
|
||||
MESON_SAR_ADC_CHAN_LIST_ENTRY_MASK(0), regval);
|
||||
|
||||
regval = FIELD_PREP(MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_MUX_MASK,
|
||||
channel);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DETECT_IDLE_SW,
|
||||
MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_MUX_MASK,
|
||||
regval);
|
||||
|
||||
regval = FIELD_PREP(MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MUX_SEL_MASK,
|
||||
channel);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DETECT_IDLE_SW,
|
||||
MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MUX_SEL_MASK,
|
||||
regval);
|
||||
|
||||
if (channel == 6)
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10,
|
||||
MESON_SAR_ADC_DELTA_10_TEMP_SEL, 0);
|
||||
}
|
||||
|
||||
static int meson_saradc_get_sample(struct meson_saradc_priv *priv,
|
||||
int chan, uint *val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = meson_saradc_lock(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* clear the FIFO to make sure we're not reading old values */
|
||||
meson_saradc_clear_fifo(priv);
|
||||
|
||||
meson_saradc_set_averaging(priv, chan, MEAN_AVERAGING, EIGHT_SAMPLES);
|
||||
|
||||
meson_saradc_enable_channel(priv, chan);
|
||||
|
||||
meson_saradc_start_sample_engine(priv);
|
||||
ret = meson_saradc_read_raw_sample(priv, chan, val);
|
||||
meson_saradc_stop_sample_engine(priv);
|
||||
|
||||
meson_saradc_unlock(priv);
|
||||
|
||||
if (ret) {
|
||||
printf("failed to read sample for channel %d: %d\n",
|
||||
chan, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int meson_saradc_channel_data(struct udevice *dev, int channel,
|
||||
unsigned int *data)
|
||||
{
|
||||
struct meson_saradc_priv *priv = dev_get_priv(dev);
|
||||
|
||||
if (channel != priv->active_channel) {
|
||||
pr_err("Requested channel is not active!");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return meson_saradc_get_sample(priv, channel, data);
|
||||
}
|
||||
|
||||
enum meson_saradc_chan7_mux_sel {
|
||||
CHAN7_MUX_VSS = 0x0,
|
||||
CHAN7_MUX_VDD_DIV4 = 0x1,
|
||||
CHAN7_MUX_VDD_DIV2 = 0x2,
|
||||
CHAN7_MUX_VDD_MUL3_DIV4 = 0x3,
|
||||
CHAN7_MUX_VDD = 0x4,
|
||||
CHAN7_MUX_CH7_INPUT = 0x7,
|
||||
};
|
||||
|
||||
static void meson_saradc_set_chan7_mux(struct meson_saradc_priv *priv,
|
||||
enum meson_saradc_chan7_mux_sel sel)
|
||||
{
|
||||
u32 regval;
|
||||
|
||||
regval = FIELD_PREP(MESON_SAR_ADC_REG3_CTRL_CHAN7_MUX_SEL_MASK, sel);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3,
|
||||
MESON_SAR_ADC_REG3_CTRL_CHAN7_MUX_SEL_MASK, regval);
|
||||
|
||||
udelay(20);
|
||||
}
|
||||
|
||||
static int meson_saradc_calib(struct meson_saradc_priv *priv)
|
||||
{
|
||||
uint nominal0, nominal1, value0, value1;
|
||||
int ret;
|
||||
|
||||
/* use points 25% and 75% for calibration */
|
||||
nominal0 = (1 << priv->data->num_bits) / 4;
|
||||
nominal1 = (1 << priv->data->num_bits) * 3 / 4;
|
||||
|
||||
meson_saradc_set_chan7_mux(priv, CHAN7_MUX_VDD_DIV4);
|
||||
udelay(20);
|
||||
ret = meson_saradc_get_sample(priv, 7, &value0);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
meson_saradc_set_chan7_mux(priv, CHAN7_MUX_VDD_MUL3_DIV4);
|
||||
udelay(20);
|
||||
ret = meson_saradc_get_sample(priv, 7, &value1);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
if (value1 <= value0) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
priv->calibscale = div_s64((nominal1 - nominal0) * (s64)MILLION,
|
||||
value1 - value0);
|
||||
priv->calibbias = nominal0 - div_s64((s64)value0 * priv->calibscale,
|
||||
MILLION);
|
||||
ret = 0;
|
||||
out:
|
||||
meson_saradc_set_chan7_mux(priv, CHAN7_MUX_CH7_INPUT);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int meson_saradc_init(struct meson_saradc_priv *priv)
|
||||
{
|
||||
uint regval;
|
||||
int ret, i;
|
||||
|
||||
priv->calibscale = MILLION;
|
||||
|
||||
/*
|
||||
* make sure we start at CH7 input since the other muxes are only used
|
||||
* for internal calibration.
|
||||
*/
|
||||
meson_saradc_set_chan7_mux(priv, CHAN7_MUX_CH7_INPUT);
|
||||
|
||||
/*
|
||||
* leave sampling delay and the input clocks as configured by
|
||||
* BL30 to make sure BL30 gets the values it expects when
|
||||
* reading the temperature sensor.
|
||||
*/
|
||||
regmap_read(priv->regmap, MESON_SAR_ADC_REG3, ®val);
|
||||
if (regval & MESON_SAR_ADC_REG3_BL30_INITIALIZED)
|
||||
return 0;
|
||||
|
||||
meson_saradc_stop_sample_engine(priv);
|
||||
|
||||
/* update the channel 6 MUX to select the temperature sensor */
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0,
|
||||
MESON_SAR_ADC_REG0_ADC_TEMP_SEN_SEL,
|
||||
MESON_SAR_ADC_REG0_ADC_TEMP_SEN_SEL);
|
||||
|
||||
/* disable all channels by default */
|
||||
regmap_write(priv->regmap, MESON_SAR_ADC_CHAN_LIST, 0x0);
|
||||
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3,
|
||||
MESON_SAR_ADC_REG3_CTRL_SAMPLING_CLOCK_PHASE, 0);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3,
|
||||
MESON_SAR_ADC_REG3_CNTL_USE_SC_DLY,
|
||||
MESON_SAR_ADC_REG3_CNTL_USE_SC_DLY);
|
||||
|
||||
/* delay between two samples = (10+1) * 1uS */
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY,
|
||||
MESON_SAR_ADC_DELAY_INPUT_DLY_CNT_MASK,
|
||||
FIELD_PREP(MESON_SAR_ADC_DELAY_SAMPLE_DLY_CNT_MASK,
|
||||
10));
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY,
|
||||
MESON_SAR_ADC_DELAY_SAMPLE_DLY_SEL_MASK,
|
||||
FIELD_PREP(MESON_SAR_ADC_DELAY_SAMPLE_DLY_SEL_MASK,
|
||||
0));
|
||||
|
||||
/* delay between two samples = (10+1) * 1uS */
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY,
|
||||
MESON_SAR_ADC_DELAY_INPUT_DLY_CNT_MASK,
|
||||
FIELD_PREP(MESON_SAR_ADC_DELAY_INPUT_DLY_CNT_MASK,
|
||||
10));
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY,
|
||||
MESON_SAR_ADC_DELAY_INPUT_DLY_SEL_MASK,
|
||||
FIELD_PREP(MESON_SAR_ADC_DELAY_INPUT_DLY_SEL_MASK,
|
||||
1));
|
||||
|
||||
/*
|
||||
* set up the input channel muxes in MESON_SAR_ADC_CHAN_10_SW
|
||||
* (0 = SAR_ADC_CH0, 1 = SAR_ADC_CH1)
|
||||
*/
|
||||
regval = FIELD_PREP(MESON_SAR_ADC_CHAN_10_SW_CHAN0_MUX_SEL_MASK, 0);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_10_SW,
|
||||
MESON_SAR_ADC_CHAN_10_SW_CHAN0_MUX_SEL_MASK,
|
||||
regval);
|
||||
regval = FIELD_PREP(MESON_SAR_ADC_CHAN_10_SW_CHAN1_MUX_SEL_MASK, 1);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_10_SW,
|
||||
MESON_SAR_ADC_CHAN_10_SW_CHAN1_MUX_SEL_MASK,
|
||||
regval);
|
||||
|
||||
/*
|
||||
* set up the input channel muxes in MESON_SAR_ADC_AUX_SW
|
||||
* (2 = SAR_ADC_CH2, 3 = SAR_ADC_CH3, ...) and enable
|
||||
* MESON_SAR_ADC_AUX_SW_YP_DRIVE_SW and
|
||||
* MESON_SAR_ADC_AUX_SW_XP_DRIVE_SW like the vendor driver.
|
||||
*/
|
||||
regval = 0;
|
||||
for (i = 2; i <= 7; i++)
|
||||
regval |= i << MESON_SAR_ADC_AUX_SW_MUX_SEL_CHAN_SHIFT(i);
|
||||
regval |= MESON_SAR_ADC_AUX_SW_YP_DRIVE_SW;
|
||||
regval |= MESON_SAR_ADC_AUX_SW_XP_DRIVE_SW;
|
||||
regmap_write(priv->regmap, MESON_SAR_ADC_AUX_SW, regval);
|
||||
|
||||
ret = meson_saradc_lock(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
#if CONFIG_IS_ENABLED(CLK)
|
||||
ret = clk_enable(&priv->core_clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
#endif
|
||||
|
||||
regval = FIELD_PREP(MESON_SAR_ADC_REG0_FIFO_CNT_IRQ_MASK, 1);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0,
|
||||
MESON_SAR_ADC_REG0_FIFO_CNT_IRQ_MASK, regval);
|
||||
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11,
|
||||
MESON_SAR_ADC_REG11_BANDGAP_EN,
|
||||
MESON_SAR_ADC_REG11_BANDGAP_EN);
|
||||
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3,
|
||||
MESON_SAR_ADC_REG3_ADC_EN,
|
||||
MESON_SAR_ADC_REG3_ADC_EN);
|
||||
|
||||
udelay(5);
|
||||
|
||||
#if CONFIG_IS_ENABLED(CLK)
|
||||
ret = clk_enable(&priv->adc_clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
#endif
|
||||
|
||||
meson_saradc_unlock(priv);
|
||||
|
||||
ret = meson_saradc_calib(priv);
|
||||
if (ret) {
|
||||
printf("calibration failed\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int meson_saradc_start_channel(struct udevice *dev, int channel)
|
||||
{
|
||||
struct meson_saradc_priv *priv = dev_get_priv(dev);
|
||||
|
||||
if (channel < 0 || channel >= NUM_CHANNELS) {
|
||||
printf("Requested channel is invalid!");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!priv->initialized) {
|
||||
int ret;
|
||||
|
||||
ret = meson_saradc_init(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
priv->initialized = true;
|
||||
}
|
||||
|
||||
priv->active_channel = channel;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int meson_saradc_stop(struct udevice *dev)
|
||||
{
|
||||
struct meson_saradc_priv *priv = dev_get_priv(dev);
|
||||
|
||||
priv->active_channel = -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int meson_saradc_probe(struct udevice *dev)
|
||||
{
|
||||
struct meson_saradc_priv *priv = dev_get_priv(dev);
|
||||
int ret;
|
||||
|
||||
ret = regmap_init_mem(dev_ofnode(dev), &priv->regmap);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
#if CONFIG_IS_ENABLED(CLK)
|
||||
ret = clk_get_by_name(dev, "core", &priv->core_clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_get_by_name(dev, "adc_clk", &priv->adc_clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
#endif
|
||||
|
||||
priv->active_channel = -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int meson_saradc_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev);
|
||||
struct meson_saradc_priv *priv = dev_get_priv(dev);
|
||||
|
||||
priv->data = (struct meson_saradc_data *)dev_get_driver_data(dev);
|
||||
|
||||
uc_pdata->data_mask = GENMASK(priv->data->num_bits - 1, 0);
|
||||
uc_pdata->data_format = ADC_DATA_FORMAT_BIN;
|
||||
uc_pdata->data_timeout_us = MESON_SAR_ADC_TIMEOUT * 1000;
|
||||
uc_pdata->channel_mask = GENMASK(NUM_CHANNELS - 1, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct adc_ops meson_saradc_ops = {
|
||||
.start_channel = meson_saradc_start_channel,
|
||||
.channel_data = meson_saradc_channel_data,
|
||||
.stop = meson_saradc_stop,
|
||||
};
|
||||
|
||||
static const struct meson_saradc_data gxbb_saradc_data = {
|
||||
.num_bits = 10,
|
||||
};
|
||||
|
||||
static const struct meson_saradc_data gxl_saradc_data = {
|
||||
.num_bits = 12,
|
||||
};
|
||||
|
||||
static const struct udevice_id meson_saradc_ids[] = {
|
||||
{ .compatible = "amlogic,meson-gxbb-saradc",
|
||||
.data = (ulong)&gxbb_saradc_data },
|
||||
{ .compatible = "amlogic,meson-gxl-saradc",
|
||||
.data = (ulong)&gxl_saradc_data },
|
||||
{ .compatible = "amlogic,meson-gxm-saradc",
|
||||
.data = (ulong)&gxl_saradc_data },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(meson_saradc) = {
|
||||
.name = "meson_saradc",
|
||||
.id = UCLASS_ADC,
|
||||
.of_match = meson_saradc_ids,
|
||||
.ops = &meson_saradc_ops,
|
||||
.probe = meson_saradc_probe,
|
||||
.ofdata_to_platdata = meson_saradc_ofdata_to_platdata,
|
||||
.priv_auto_alloc_size = sizeof(struct meson_saradc_priv),
|
||||
};
|
||||
@@ -0,0 +1,182 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2017, Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* Rockchip SARADC driver for U-Boot
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <adc.h>
|
||||
#include <clk.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#define SARADC_CTRL_CHN_MASK GENMASK(2, 0)
|
||||
#define SARADC_CTRL_POWER_CTRL BIT(3)
|
||||
#define SARADC_CTRL_IRQ_ENABLE BIT(5)
|
||||
#define SARADC_CTRL_IRQ_STATUS BIT(6)
|
||||
|
||||
#define SARADC_TIMEOUT (100 * 1000)
|
||||
|
||||
struct rockchip_saradc_regs {
|
||||
unsigned int data;
|
||||
unsigned int stas;
|
||||
unsigned int ctrl;
|
||||
unsigned int dly_pu_soc;
|
||||
};
|
||||
|
||||
struct rockchip_saradc_data {
|
||||
int num_bits;
|
||||
int num_channels;
|
||||
unsigned long clk_rate;
|
||||
};
|
||||
|
||||
struct rockchip_saradc_priv {
|
||||
struct rockchip_saradc_regs *regs;
|
||||
int active_channel;
|
||||
const struct rockchip_saradc_data *data;
|
||||
};
|
||||
|
||||
int rockchip_saradc_channel_data(struct udevice *dev, int channel,
|
||||
unsigned int *data)
|
||||
{
|
||||
struct rockchip_saradc_priv *priv = dev_get_priv(dev);
|
||||
struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev);
|
||||
|
||||
if (channel != priv->active_channel) {
|
||||
pr_err("Requested channel is not active!");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((readl(&priv->regs->ctrl) & SARADC_CTRL_IRQ_STATUS) !=
|
||||
SARADC_CTRL_IRQ_STATUS)
|
||||
return -EBUSY;
|
||||
|
||||
/* Read value */
|
||||
*data = readl(&priv->regs->data);
|
||||
*data &= uc_pdata->data_mask;
|
||||
|
||||
/* Power down adc */
|
||||
writel(0, &priv->regs->ctrl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rockchip_saradc_start_channel(struct udevice *dev, int channel)
|
||||
{
|
||||
struct rockchip_saradc_priv *priv = dev_get_priv(dev);
|
||||
|
||||
if (channel < 0 || channel >= priv->data->num_channels) {
|
||||
pr_err("Requested channel is invalid!");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* 8 clock periods as delay between power up and start cmd */
|
||||
writel(8, &priv->regs->dly_pu_soc);
|
||||
|
||||
/* Select the channel to be used and trigger conversion */
|
||||
writel(SARADC_CTRL_POWER_CTRL | (channel & SARADC_CTRL_CHN_MASK) |
|
||||
SARADC_CTRL_IRQ_ENABLE, &priv->regs->ctrl);
|
||||
|
||||
priv->active_channel = channel;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rockchip_saradc_stop(struct udevice *dev)
|
||||
{
|
||||
struct rockchip_saradc_priv *priv = dev_get_priv(dev);
|
||||
|
||||
/* Power down adc */
|
||||
writel(0, &priv->regs->ctrl);
|
||||
|
||||
priv->active_channel = -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rockchip_saradc_probe(struct udevice *dev)
|
||||
{
|
||||
struct rockchip_saradc_priv *priv = dev_get_priv(dev);
|
||||
struct clk clk;
|
||||
int ret;
|
||||
|
||||
ret = clk_get_by_index(dev, 0, &clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_set_rate(&clk, priv->data->clk_rate);
|
||||
if (IS_ERR_VALUE(ret))
|
||||
return ret;
|
||||
|
||||
priv->active_channel = -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rockchip_saradc_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev);
|
||||
struct rockchip_saradc_priv *priv = dev_get_priv(dev);
|
||||
struct rockchip_saradc_data *data;
|
||||
|
||||
data = (struct rockchip_saradc_data *)dev_get_driver_data(dev);
|
||||
priv->regs = (struct rockchip_saradc_regs *)dev_read_addr(dev);
|
||||
if (priv->regs == (struct rockchip_saradc_regs *)FDT_ADDR_T_NONE) {
|
||||
pr_err("Dev: %s - can't get address!", dev->name);
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
priv->data = data;
|
||||
uc_pdata->data_mask = (1 << priv->data->num_bits) - 1;;
|
||||
uc_pdata->data_format = ADC_DATA_FORMAT_BIN;
|
||||
uc_pdata->data_timeout_us = SARADC_TIMEOUT / 5;
|
||||
uc_pdata->channel_mask = (1 << priv->data->num_channels) - 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct adc_ops rockchip_saradc_ops = {
|
||||
.start_channel = rockchip_saradc_start_channel,
|
||||
.channel_data = rockchip_saradc_channel_data,
|
||||
.stop = rockchip_saradc_stop,
|
||||
};
|
||||
|
||||
static const struct rockchip_saradc_data saradc_data = {
|
||||
.num_bits = 10,
|
||||
.num_channels = 3,
|
||||
.clk_rate = 1000000,
|
||||
};
|
||||
|
||||
static const struct rockchip_saradc_data rk3066_tsadc_data = {
|
||||
.num_bits = 12,
|
||||
.num_channels = 2,
|
||||
.clk_rate = 50000,
|
||||
};
|
||||
|
||||
static const struct rockchip_saradc_data rk3399_saradc_data = {
|
||||
.num_bits = 10,
|
||||
.num_channels = 6,
|
||||
.clk_rate = 1000000,
|
||||
};
|
||||
|
||||
static const struct udevice_id rockchip_saradc_ids[] = {
|
||||
{ .compatible = "rockchip,saradc",
|
||||
.data = (ulong)&saradc_data },
|
||||
{ .compatible = "rockchip,rk3066-tsadc",
|
||||
.data = (ulong)&rk3066_tsadc_data },
|
||||
{ .compatible = "rockchip,rk3399-saradc",
|
||||
.data = (ulong)&rk3399_saradc_data },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(rockchip_saradc) = {
|
||||
.name = "rockchip_saradc",
|
||||
.id = UCLASS_ADC,
|
||||
.of_match = rockchip_saradc_ids,
|
||||
.ops = &rockchip_saradc_ops,
|
||||
.probe = rockchip_saradc_probe,
|
||||
.ofdata_to_platdata = rockchip_saradc_ofdata_to_platdata,
|
||||
.priv_auto_alloc_size = sizeof(struct rockchip_saradc_priv),
|
||||
};
|
||||
@@ -0,0 +1,173 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2015 Samsung Electronics
|
||||
* Przemyslaw Marczak <p.marczak@samsung.com>
|
||||
*/
|
||||
#include <common.h>
|
||||
#include <errno.h>
|
||||
#include <dm.h>
|
||||
#include <adc.h>
|
||||
#include <sandbox-adc.h>
|
||||
|
||||
/**
|
||||
* struct sandbox_adc_priv - sandbox ADC device's operation status and data
|
||||
*
|
||||
* @conversion_status - conversion status: ACTIVE (started) / INACTIVE (stopped)
|
||||
* @conversion_mode - conversion mode: single or multi-channel
|
||||
* @active_channel - active channel number, valid for single channel mode
|
||||
* data[] - channels data
|
||||
*/
|
||||
struct sandbox_adc_priv {
|
||||
int conversion_status;
|
||||
int conversion_mode;
|
||||
int active_channel_mask;
|
||||
unsigned int data[4];
|
||||
};
|
||||
|
||||
int sandbox_adc_start_channel(struct udevice *dev, int channel)
|
||||
{
|
||||
struct sandbox_adc_priv *priv = dev_get_priv(dev);
|
||||
|
||||
/* Set single-channel mode */
|
||||
priv->conversion_mode = SANDBOX_ADC_MODE_SINGLE_CHANNEL;
|
||||
/* Select channel */
|
||||
priv->active_channel_mask = 1 << channel;
|
||||
/* Start conversion */
|
||||
priv->conversion_status = SANDBOX_ADC_ACTIVE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sandbox_adc_start_channels(struct udevice *dev, unsigned int channel_mask)
|
||||
{
|
||||
struct sandbox_adc_priv *priv = dev_get_priv(dev);
|
||||
|
||||
/* Set single-channel mode */
|
||||
priv->conversion_mode = SANDBOX_ADC_MODE_MULTI_CHANNEL;
|
||||
/* Select channel */
|
||||
priv->active_channel_mask = channel_mask;
|
||||
/* Start conversion */
|
||||
priv->conversion_status = SANDBOX_ADC_ACTIVE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sandbox_adc_channel_data(struct udevice *dev, int channel,
|
||||
unsigned int *data)
|
||||
{
|
||||
struct sandbox_adc_priv *priv = dev_get_priv(dev);
|
||||
|
||||
/* For single-channel conversion mode, check if channel was selected */
|
||||
if ((priv->conversion_mode == SANDBOX_ADC_MODE_SINGLE_CHANNEL) &&
|
||||
!(priv->active_channel_mask & (1 << channel))) {
|
||||
pr_err("Request for an inactive channel!");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* The conversion must be started before reading the data */
|
||||
if (priv->conversion_status == SANDBOX_ADC_INACTIVE)
|
||||
return -EIO;
|
||||
|
||||
*data = priv->data[channel];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sandbox_adc_channels_data(struct udevice *dev, unsigned int channel_mask,
|
||||
struct adc_channel *channels)
|
||||
{
|
||||
struct sandbox_adc_priv *priv = dev_get_priv(dev);
|
||||
int i;
|
||||
|
||||
/* Return error for single-channel conversion mode */
|
||||
if (priv->conversion_mode == SANDBOX_ADC_MODE_SINGLE_CHANNEL) {
|
||||
pr_err("ADC in single-channel mode!");
|
||||
return -EPERM;
|
||||
}
|
||||
/* Check channel selection */
|
||||
if (!(priv->active_channel_mask & channel_mask)) {
|
||||
pr_err("Request for an inactive channel!");
|
||||
return -EINVAL;
|
||||
}
|
||||
/* The conversion must be started before reading the data */
|
||||
if (priv->conversion_status == SANDBOX_ADC_INACTIVE)
|
||||
return -EIO;
|
||||
|
||||
for (i = 0; i < SANDBOX_ADC_CHANNELS; i++) {
|
||||
if (!((channel_mask >> i) & 0x1))
|
||||
continue;
|
||||
|
||||
channels->data = priv->data[i];
|
||||
channels->id = i;
|
||||
channels++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sandbox_adc_stop(struct udevice *dev)
|
||||
{
|
||||
struct sandbox_adc_priv *priv = dev_get_priv(dev);
|
||||
|
||||
/* Start conversion */
|
||||
priv->conversion_status = SANDBOX_ADC_INACTIVE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sandbox_adc_probe(struct udevice *dev)
|
||||
{
|
||||
struct sandbox_adc_priv *priv = dev_get_priv(dev);
|
||||
|
||||
/* Stop conversion */
|
||||
priv->conversion_status = SANDBOX_ADC_INACTIVE;
|
||||
/* Set single-channel mode */
|
||||
priv->conversion_mode = SANDBOX_ADC_MODE_SINGLE_CHANNEL;
|
||||
/* Deselect all channels */
|
||||
priv->active_channel_mask = 0;
|
||||
|
||||
/* Set sandbox test data */
|
||||
priv->data[0] = SANDBOX_ADC_CHANNEL0_DATA;
|
||||
priv->data[1] = SANDBOX_ADC_CHANNEL1_DATA;
|
||||
priv->data[2] = SANDBOX_ADC_CHANNEL2_DATA;
|
||||
priv->data[3] = SANDBOX_ADC_CHANNEL3_DATA;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sandbox_adc_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev);
|
||||
|
||||
uc_pdata->data_mask = SANDBOX_ADC_DATA_MASK;
|
||||
uc_pdata->data_format = ADC_DATA_FORMAT_BIN;
|
||||
uc_pdata->data_timeout_us = 0;
|
||||
|
||||
/* Mask available channel bits: [0:3] */
|
||||
uc_pdata->channel_mask = (1 << SANDBOX_ADC_CHANNELS) - 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct adc_ops sandbox_adc_ops = {
|
||||
.start_channel = sandbox_adc_start_channel,
|
||||
.start_channels = sandbox_adc_start_channels,
|
||||
.channel_data = sandbox_adc_channel_data,
|
||||
.channels_data = sandbox_adc_channels_data,
|
||||
.stop = sandbox_adc_stop,
|
||||
};
|
||||
|
||||
static const struct udevice_id sandbox_adc_ids[] = {
|
||||
{ .compatible = "sandbox,adc" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(sandbox_adc) = {
|
||||
.name = "sandbox-adc",
|
||||
.id = UCLASS_ADC,
|
||||
.of_match = sandbox_adc_ids,
|
||||
.ops = &sandbox_adc_ops,
|
||||
.probe = sandbox_adc_probe,
|
||||
.ofdata_to_platdata = sandbox_adc_ofdata_to_platdata,
|
||||
.priv_auto_alloc_size = sizeof(struct sandbox_adc_priv),
|
||||
};
|
||||
@@ -0,0 +1,210 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2018, STMicroelectronics - All Rights Reserved
|
||||
* Author: Fabrice Gasnier <fabrice.gasnier@st.com>
|
||||
*
|
||||
* Originally based on the Linux kernel v4.18 drivers/iio/adc/stm32-adc-core.c.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <power/regulator.h>
|
||||
#include "stm32-adc-core.h"
|
||||
|
||||
/* STM32H7 - common registers for all ADC instances */
|
||||
#define STM32H7_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x08)
|
||||
|
||||
/* STM32H7_ADC_CCR - bit fields */
|
||||
#define STM32H7_PRESC_SHIFT 18
|
||||
#define STM32H7_PRESC_MASK GENMASK(21, 18)
|
||||
#define STM32H7_CKMODE_SHIFT 16
|
||||
#define STM32H7_CKMODE_MASK GENMASK(17, 16)
|
||||
|
||||
/* STM32 H7 maximum analog clock rate (from datasheet) */
|
||||
#define STM32H7_ADC_MAX_CLK_RATE 36000000
|
||||
|
||||
/**
|
||||
* struct stm32h7_adc_ck_spec - specification for stm32h7 adc clock
|
||||
* @ckmode: ADC clock mode, Async or sync with prescaler.
|
||||
* @presc: prescaler bitfield for async clock mode
|
||||
* @div: prescaler division ratio
|
||||
*/
|
||||
struct stm32h7_adc_ck_spec {
|
||||
u32 ckmode;
|
||||
u32 presc;
|
||||
int div;
|
||||
};
|
||||
|
||||
static const struct stm32h7_adc_ck_spec stm32h7_adc_ckmodes_spec[] = {
|
||||
/* 00: CK_ADC[1..3]: Asynchronous clock modes */
|
||||
{ 0, 0, 1 },
|
||||
{ 0, 1, 2 },
|
||||
{ 0, 2, 4 },
|
||||
{ 0, 3, 6 },
|
||||
{ 0, 4, 8 },
|
||||
{ 0, 5, 10 },
|
||||
{ 0, 6, 12 },
|
||||
{ 0, 7, 16 },
|
||||
{ 0, 8, 32 },
|
||||
{ 0, 9, 64 },
|
||||
{ 0, 10, 128 },
|
||||
{ 0, 11, 256 },
|
||||
/* HCLK used: Synchronous clock modes (1, 2 or 4 prescaler) */
|
||||
{ 1, 0, 1 },
|
||||
{ 2, 0, 2 },
|
||||
{ 3, 0, 4 },
|
||||
};
|
||||
|
||||
static int stm32h7_adc_clk_sel(struct udevice *dev,
|
||||
struct stm32_adc_common *common)
|
||||
{
|
||||
u32 ckmode, presc;
|
||||
unsigned long rate;
|
||||
unsigned int i;
|
||||
int div;
|
||||
|
||||
/* stm32h7 bus clock is common for all ADC instances (mandatory) */
|
||||
if (!clk_valid(&common->bclk)) {
|
||||
dev_err(dev, "No bclk clock found\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
/*
|
||||
* stm32h7 can use either 'bus' or 'adc' clock for analog circuitry.
|
||||
* So, choice is to have bus clock mandatory and adc clock optional.
|
||||
* If optional 'adc' clock has been found, then try to use it first.
|
||||
*/
|
||||
if (clk_valid(&common->aclk)) {
|
||||
/*
|
||||
* Asynchronous clock modes (e.g. ckmode == 0)
|
||||
* From spec: PLL output musn't exceed max rate
|
||||
*/
|
||||
rate = clk_get_rate(&common->aclk);
|
||||
if (!rate) {
|
||||
dev_err(dev, "Invalid aclk rate: 0\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(stm32h7_adc_ckmodes_spec); i++) {
|
||||
ckmode = stm32h7_adc_ckmodes_spec[i].ckmode;
|
||||
presc = stm32h7_adc_ckmodes_spec[i].presc;
|
||||
div = stm32h7_adc_ckmodes_spec[i].div;
|
||||
|
||||
if (ckmode)
|
||||
continue;
|
||||
|
||||
if ((rate / div) <= STM32H7_ADC_MAX_CLK_RATE)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* Synchronous clock modes (e.g. ckmode is 1, 2 or 3) */
|
||||
rate = clk_get_rate(&common->bclk);
|
||||
if (!rate) {
|
||||
dev_err(dev, "Invalid bus clock rate: 0\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(stm32h7_adc_ckmodes_spec); i++) {
|
||||
ckmode = stm32h7_adc_ckmodes_spec[i].ckmode;
|
||||
presc = stm32h7_adc_ckmodes_spec[i].presc;
|
||||
div = stm32h7_adc_ckmodes_spec[i].div;
|
||||
|
||||
if (!ckmode)
|
||||
continue;
|
||||
|
||||
if ((rate / div) <= STM32H7_ADC_MAX_CLK_RATE)
|
||||
goto out;
|
||||
}
|
||||
|
||||
dev_err(dev, "clk selection failed\n");
|
||||
return -EINVAL;
|
||||
|
||||
out:
|
||||
/* rate used later by each ADC instance to control BOOST mode */
|
||||
common->rate = rate / div;
|
||||
|
||||
/* Set common clock mode and prescaler */
|
||||
clrsetbits_le32(common->base + STM32H7_ADC_CCR,
|
||||
STM32H7_CKMODE_MASK | STM32H7_PRESC_MASK,
|
||||
ckmode << STM32H7_CKMODE_SHIFT |
|
||||
presc << STM32H7_PRESC_SHIFT);
|
||||
|
||||
dev_dbg(dev, "Using %s clock/%d source at %ld kHz\n",
|
||||
ckmode ? "bus" : "adc", div, common->rate / 1000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_adc_core_probe(struct udevice *dev)
|
||||
{
|
||||
struct stm32_adc_common *common = dev_get_priv(dev);
|
||||
int ret;
|
||||
|
||||
common->base = dev_read_addr_ptr(dev);
|
||||
if (!common->base) {
|
||||
dev_err(dev, "can't get address\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
ret = device_get_supply_regulator(dev, "vref-supply", &common->vref);
|
||||
if (ret) {
|
||||
dev_err(dev, "can't get vref-supply: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regulator_get_value(common->vref);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "can't get vref-supply value: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
common->vref_uv = ret;
|
||||
|
||||
ret = clk_get_by_name(dev, "adc", &common->aclk);
|
||||
if (!ret) {
|
||||
ret = clk_enable(&common->aclk);
|
||||
if (ret) {
|
||||
dev_err(dev, "Can't enable aclk: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = clk_get_by_name(dev, "bus", &common->bclk);
|
||||
if (!ret) {
|
||||
ret = clk_enable(&common->bclk);
|
||||
if (ret) {
|
||||
dev_err(dev, "Can't enable bclk: %d\n", ret);
|
||||
goto err_aclk_disable;
|
||||
}
|
||||
}
|
||||
|
||||
ret = stm32h7_adc_clk_sel(dev, common);
|
||||
if (ret)
|
||||
goto err_bclk_disable;
|
||||
|
||||
return ret;
|
||||
|
||||
err_bclk_disable:
|
||||
if (clk_valid(&common->bclk))
|
||||
clk_disable(&common->bclk);
|
||||
|
||||
err_aclk_disable:
|
||||
if (clk_valid(&common->aclk))
|
||||
clk_disable(&common->aclk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct udevice_id stm32_adc_core_ids[] = {
|
||||
{ .compatible = "st,stm32h7-adc-core" },
|
||||
{ .compatible = "st,stm32mp1-adc-core" },
|
||||
{}
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(stm32_adc_core) = {
|
||||
.name = "stm32-adc-core",
|
||||
.id = UCLASS_SIMPLE_BUS,
|
||||
.of_match = stm32_adc_core_ids,
|
||||
.probe = stm32_adc_core_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct stm32_adc_common),
|
||||
};
|
||||
@@ -0,0 +1,51 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (C) 2018, STMicroelectronics - All Rights Reserved
|
||||
* Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
|
||||
*
|
||||
* Originally based on the Linux kernel v4.18 drivers/iio/adc/stm32-adc-core.h.
|
||||
*/
|
||||
|
||||
#ifndef __STM32_ADC_H
|
||||
#define __STM32_ADC_H
|
||||
|
||||
/*
|
||||
* STM32 - ADC global register map
|
||||
* ________________________________________________________
|
||||
* | Offset | Register |
|
||||
* --------------------------------------------------------
|
||||
* | 0x000 | Master ADC1 |
|
||||
* --------------------------------------------------------
|
||||
* | 0x100 | Slave ADC2 |
|
||||
* --------------------------------------------------------
|
||||
* | 0x200 | Slave ADC3 |
|
||||
* --------------------------------------------------------
|
||||
* | 0x300 | Master & Slave common regs |
|
||||
* --------------------------------------------------------
|
||||
*/
|
||||
#define STM32_ADC_MAX_ADCS 3
|
||||
#define STM32_ADCX_COMN_OFFSET 0x300
|
||||
|
||||
#include <common.h>
|
||||
#include <clk.h>
|
||||
#include <dm.h>
|
||||
|
||||
/**
|
||||
* struct stm32_adc_common - stm32 ADC driver common data (for all instances)
|
||||
* @base: control registers base cpu addr
|
||||
* @rate: clock rate used for analog circuitry
|
||||
* @aclk: clock for the analog circuitry
|
||||
* @bclk: bus clock common for all ADCs
|
||||
* @vref: regulator reference
|
||||
* @vref_uv: reference supply voltage (uV)
|
||||
*/
|
||||
struct stm32_adc_common {
|
||||
void __iomem *base;
|
||||
unsigned long rate;
|
||||
struct clk aclk;
|
||||
struct clk bclk;
|
||||
struct udevice *vref;
|
||||
int vref_uv;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,258 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2018, STMicroelectronics - All Rights Reserved
|
||||
* Author: Fabrice Gasnier <fabrice.gasnier@st.com>
|
||||
*
|
||||
* Originally based on the Linux kernel v4.18 drivers/iio/adc/stm32-adc.c.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <adc.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include "stm32-adc-core.h"
|
||||
|
||||
/* STM32H7 - Registers for each ADC instance */
|
||||
#define STM32H7_ADC_ISR 0x00
|
||||
#define STM32H7_ADC_CR 0x08
|
||||
#define STM32H7_ADC_CFGR 0x0C
|
||||
#define STM32H7_ADC_SMPR1 0x14
|
||||
#define STM32H7_ADC_SMPR2 0x18
|
||||
#define STM32H7_ADC_PCSEL 0x1C
|
||||
#define STM32H7_ADC_SQR1 0x30
|
||||
#define STM32H7_ADC_DR 0x40
|
||||
#define STM32H7_ADC_DIFSEL 0xC0
|
||||
|
||||
/* STM32H7_ADC_ISR - bit fields */
|
||||
#define STM32MP1_VREGREADY BIT(12)
|
||||
#define STM32H7_EOC BIT(2)
|
||||
#define STM32H7_ADRDY BIT(0)
|
||||
|
||||
/* STM32H7_ADC_CR - bit fields */
|
||||
#define STM32H7_DEEPPWD BIT(29)
|
||||
#define STM32H7_ADVREGEN BIT(28)
|
||||
#define STM32H7_BOOST BIT(8)
|
||||
#define STM32H7_ADSTART BIT(2)
|
||||
#define STM32H7_ADDIS BIT(1)
|
||||
#define STM32H7_ADEN BIT(0)
|
||||
|
||||
/* STM32H7_ADC_CFGR bit fields */
|
||||
#define STM32H7_EXTEN GENMASK(11, 10)
|
||||
#define STM32H7_DMNGT GENMASK(1, 0)
|
||||
|
||||
/* STM32H7_ADC_SQR1 - bit fields */
|
||||
#define STM32H7_SQ1_SHIFT 6
|
||||
|
||||
/* BOOST bit must be set on STM32H7 when ADC clock is above 20MHz */
|
||||
#define STM32H7_BOOST_CLKRATE 20000000UL
|
||||
|
||||
#define STM32_ADC_CH_MAX 20 /* max number of channels */
|
||||
#define STM32_ADC_TIMEOUT_US 100000
|
||||
|
||||
struct stm32_adc_cfg {
|
||||
unsigned int max_channels;
|
||||
unsigned int num_bits;
|
||||
bool has_vregready;
|
||||
};
|
||||
|
||||
struct stm32_adc {
|
||||
void __iomem *regs;
|
||||
int active_channel;
|
||||
const struct stm32_adc_cfg *cfg;
|
||||
};
|
||||
|
||||
static int stm32_adc_stop(struct udevice *dev)
|
||||
{
|
||||
struct stm32_adc *adc = dev_get_priv(dev);
|
||||
|
||||
setbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_ADDIS);
|
||||
clrbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_BOOST);
|
||||
/* Setting DEEPPWD disables ADC vreg and clears ADVREGEN */
|
||||
setbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_DEEPPWD);
|
||||
adc->active_channel = -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_adc_start_channel(struct udevice *dev, int channel)
|
||||
{
|
||||
struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev);
|
||||
struct stm32_adc_common *common = dev_get_priv(dev_get_parent(dev));
|
||||
struct stm32_adc *adc = dev_get_priv(dev);
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
/* Exit deep power down, then enable ADC voltage regulator */
|
||||
clrbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_DEEPPWD);
|
||||
setbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_ADVREGEN);
|
||||
if (common->rate > STM32H7_BOOST_CLKRATE)
|
||||
setbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_BOOST);
|
||||
|
||||
/* Wait for startup time */
|
||||
if (!adc->cfg->has_vregready) {
|
||||
udelay(20);
|
||||
} else {
|
||||
ret = readl_poll_timeout(adc->regs + STM32H7_ADC_ISR, val,
|
||||
val & STM32MP1_VREGREADY,
|
||||
STM32_ADC_TIMEOUT_US);
|
||||
if (ret < 0) {
|
||||
stm32_adc_stop(dev);
|
||||
dev_err(dev, "Failed to enable vreg: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* Only use single ended channels */
|
||||
writel(0, adc->regs + STM32H7_ADC_DIFSEL);
|
||||
|
||||
/* Enable ADC, Poll for ADRDY to be set (after adc startup time) */
|
||||
setbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_ADEN);
|
||||
ret = readl_poll_timeout(adc->regs + STM32H7_ADC_ISR, val,
|
||||
val & STM32H7_ADRDY, STM32_ADC_TIMEOUT_US);
|
||||
if (ret < 0) {
|
||||
stm32_adc_stop(dev);
|
||||
dev_err(dev, "Failed to enable ADC: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Preselect channels */
|
||||
writel(uc_pdata->channel_mask, adc->regs + STM32H7_ADC_PCSEL);
|
||||
|
||||
/* Set sampling time to max value by default */
|
||||
writel(0xffffffff, adc->regs + STM32H7_ADC_SMPR1);
|
||||
writel(0xffffffff, adc->regs + STM32H7_ADC_SMPR2);
|
||||
|
||||
/* Program regular sequence: chan in SQ1 & len = 0 for one channel */
|
||||
writel(channel << STM32H7_SQ1_SHIFT, adc->regs + STM32H7_ADC_SQR1);
|
||||
|
||||
/* Trigger detection disabled (conversion can be launched in SW) */
|
||||
clrbits_le32(adc->regs + STM32H7_ADC_CFGR, STM32H7_EXTEN |
|
||||
STM32H7_DMNGT);
|
||||
adc->active_channel = channel;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_adc_channel_data(struct udevice *dev, int channel,
|
||||
unsigned int *data)
|
||||
{
|
||||
struct stm32_adc *adc = dev_get_priv(dev);
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
if (channel != adc->active_channel) {
|
||||
dev_err(dev, "Requested channel is not active!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
setbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_ADSTART);
|
||||
ret = readl_poll_timeout(adc->regs + STM32H7_ADC_ISR, val,
|
||||
val & STM32H7_EOC, STM32_ADC_TIMEOUT_US);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "conversion timed out: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*data = readl(adc->regs + STM32H7_ADC_DR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_adc_chan_of_init(struct udevice *dev)
|
||||
{
|
||||
struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev);
|
||||
struct stm32_adc *adc = dev_get_priv(dev);
|
||||
u32 chans[STM32_ADC_CH_MAX];
|
||||
unsigned int i, num_channels;
|
||||
int ret;
|
||||
|
||||
/* Retrieve single ended channels listed in device tree */
|
||||
ret = dev_read_size(dev, "st,adc-channels");
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "can't get st,adc-channels: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
num_channels = ret / sizeof(u32);
|
||||
|
||||
if (num_channels > adc->cfg->max_channels) {
|
||||
dev_err(dev, "too many st,adc-channels: %d\n", num_channels);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = dev_read_u32_array(dev, "st,adc-channels", chans, num_channels);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "can't read st,adc-channels: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_channels; i++) {
|
||||
if (chans[i] >= adc->cfg->max_channels) {
|
||||
dev_err(dev, "bad channel %u\n", chans[i]);
|
||||
return -EINVAL;
|
||||
}
|
||||
uc_pdata->channel_mask |= 1 << chans[i];
|
||||
}
|
||||
|
||||
uc_pdata->data_mask = (1 << adc->cfg->num_bits) - 1;
|
||||
uc_pdata->data_format = ADC_DATA_FORMAT_BIN;
|
||||
uc_pdata->data_timeout_us = 100000;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_adc_probe(struct udevice *dev)
|
||||
{
|
||||
struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev);
|
||||
struct stm32_adc_common *common = dev_get_priv(dev_get_parent(dev));
|
||||
struct stm32_adc *adc = dev_get_priv(dev);
|
||||
int offset;
|
||||
|
||||
offset = dev_read_u32_default(dev, "reg", -ENODATA);
|
||||
if (offset < 0) {
|
||||
dev_err(dev, "Can't read reg property\n");
|
||||
return offset;
|
||||
}
|
||||
adc->regs = common->base + offset;
|
||||
adc->cfg = (const struct stm32_adc_cfg *)dev_get_driver_data(dev);
|
||||
|
||||
/* VDD supplied by common vref pin */
|
||||
uc_pdata->vdd_supply = common->vref;
|
||||
uc_pdata->vdd_microvolts = common->vref_uv;
|
||||
uc_pdata->vss_microvolts = 0;
|
||||
|
||||
return stm32_adc_chan_of_init(dev);
|
||||
}
|
||||
|
||||
static const struct adc_ops stm32_adc_ops = {
|
||||
.start_channel = stm32_adc_start_channel,
|
||||
.channel_data = stm32_adc_channel_data,
|
||||
.stop = stm32_adc_stop,
|
||||
};
|
||||
|
||||
static const struct stm32_adc_cfg stm32h7_adc_cfg = {
|
||||
.num_bits = 16,
|
||||
.max_channels = STM32_ADC_CH_MAX,
|
||||
};
|
||||
|
||||
static const struct stm32_adc_cfg stm32mp1_adc_cfg = {
|
||||
.num_bits = 16,
|
||||
.max_channels = STM32_ADC_CH_MAX,
|
||||
.has_vregready = true,
|
||||
};
|
||||
|
||||
static const struct udevice_id stm32_adc_ids[] = {
|
||||
{ .compatible = "st,stm32h7-adc",
|
||||
.data = (ulong)&stm32h7_adc_cfg },
|
||||
{ .compatible = "st,stm32mp1-adc",
|
||||
.data = (ulong)&stm32mp1_adc_cfg },
|
||||
{}
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(stm32_adc) = {
|
||||
.name = "stm32-adc",
|
||||
.id = UCLASS_ADC,
|
||||
.of_match = stm32_adc_ids,
|
||||
.probe = stm32_adc_probe,
|
||||
.ops = &stm32_adc_ops,
|
||||
.priv_auto_alloc_size = sizeof(struct stm32_adc),
|
||||
};
|
||||
@@ -0,0 +1,143 @@
|
||||
config AHCI
|
||||
bool "Support SATA controllers with driver model"
|
||||
depends on DM
|
||||
help
|
||||
This enables a uclass for disk controllers in U-Boot. Various driver
|
||||
types can use this, such as AHCI/SATA. It does not provide any standard
|
||||
operations at present. The block device interface has not been converted
|
||||
to driver model.
|
||||
|
||||
config SATA
|
||||
bool "Support SATA controllers"
|
||||
select HAVE_BLOCK_DEVICE
|
||||
help
|
||||
This enables support for SATA (Serial Advanced Technology
|
||||
Attachment), a serial bus standard for connecting to hard drives and
|
||||
other storage devices.
|
||||
|
||||
SATA replaces PATA (originally just ATA), which stands for Parallel AT
|
||||
Attachment, where AT refers to an IBM AT (Advanced Technology)
|
||||
computer released in 1984.
|
||||
|
||||
See also CMD_SATA which provides command-line support.
|
||||
|
||||
config LIBATA
|
||||
bool
|
||||
help
|
||||
Select this to build and link the libata helper functions.
|
||||
|
||||
config SCSI_AHCI
|
||||
bool "Enable SCSI interface to SATA devices"
|
||||
select LIBATA
|
||||
help
|
||||
Enable this to allow interfacing SATA devices via the SCSI layer.
|
||||
|
||||
menu "SATA/SCSI device support"
|
||||
|
||||
config AHCI_PCI
|
||||
bool "Support for PCI-based AHCI controller"
|
||||
depends on DM_SCSI
|
||||
help
|
||||
Enables support for the PCI-based AHCI controller.
|
||||
|
||||
config SATA_CEVA
|
||||
bool "Ceva Sata controller"
|
||||
depends on AHCI
|
||||
depends on DM_SCSI
|
||||
help
|
||||
This option enables Ceva Sata controller hard IP available on Xilinx
|
||||
ZynqMP. Support up to 2 external devices. Complient with SATA 3.1 and
|
||||
AHCI 1.3 specifications with hot-plug detect feature.
|
||||
|
||||
|
||||
config DWC_AHCI
|
||||
bool "Enable Synopsys DWC AHCI driver support"
|
||||
select SCSI_AHCI
|
||||
select PHY
|
||||
depends on DM_SCSI
|
||||
help
|
||||
Enable this driver to support Sata devices through
|
||||
Synopsys DWC AHCI module.
|
||||
|
||||
config FSL_AHCI
|
||||
bool "Enable Freescale AHCI driver support"
|
||||
select SCSI_AHCI
|
||||
depends on AHCI
|
||||
depends on DM_SCSI
|
||||
help
|
||||
Enable this driver to support Sata devices found in
|
||||
some Freescale PowerPC SoCs.
|
||||
|
||||
|
||||
config DWC_AHSATA
|
||||
bool "Enable DWC AHSATA driver support"
|
||||
select LIBATA
|
||||
help
|
||||
Enable this driver to support the DWC AHSATA SATA controller found
|
||||
in i.MX5 and i.MX6 SoCs.
|
||||
|
||||
config DWC_AHSATA_AHCI
|
||||
bool "Enable DWC AHSATA AHCI driver support"
|
||||
depends on DWC_AHSATA
|
||||
depends on AHCI
|
||||
default y
|
||||
help
|
||||
Enable this option unless you need your private ahci implementation
|
||||
|
||||
config FSL_SATA
|
||||
bool "Enable Freescale SATA controller driver support"
|
||||
select LIBATA
|
||||
select AHCI if BLK
|
||||
help
|
||||
Enable this driver to support the SATA controller found in
|
||||
some Freescale PowerPC SoCs.
|
||||
|
||||
config MVSATA_IDE
|
||||
bool "Enable Marvell SATA controller driver support via IDE interface"
|
||||
help
|
||||
Enable this driver to support the SATA controller found in
|
||||
some Marvell SoCs, running in IDE compatibility mode using PIO.
|
||||
|
||||
config SATA_MV
|
||||
bool "Enable Marvell SATA controller driver support"
|
||||
select AHCI
|
||||
select LIBATA
|
||||
depends on BLK
|
||||
help
|
||||
Enable this driver to support the SATA controller found in
|
||||
some Marvell SoCs.
|
||||
|
||||
config SATA_SIL
|
||||
bool "Enable Silicon Image SIL3131 / SIL3132 / SIL3124 SATA driver support"
|
||||
select LIBATA
|
||||
select AHCI if BLK
|
||||
help
|
||||
Enable this driver to support the SIL3131, SIL3132 and SIL3124
|
||||
SATA controllers.
|
||||
|
||||
config SATA_SIL3114
|
||||
bool "Enable Silicon Image SIL3114 SATA driver support"
|
||||
select LIBATA
|
||||
help
|
||||
Enable this driver to support the SIL3114 SATA controllers.
|
||||
|
||||
config SUNXI_AHCI
|
||||
bool "Enable Allwinner SATA driver support"
|
||||
depends on AHCI
|
||||
default y if ARCH_SUNXI
|
||||
help
|
||||
Enable this driver to support the SATA controllers found in the
|
||||
Allwinner A10, A20 and R40 SoCs.
|
||||
|
||||
config AHCI_MVEBU
|
||||
bool "Marvell EBU AHCI SATA support"
|
||||
depends on ARCH_MVEBU
|
||||
depends on AHCI
|
||||
select SCSI_AHCI
|
||||
select DM_SCSI
|
||||
help
|
||||
This option enables support for the Marvell EBU SoC's
|
||||
onboard AHCI SATA.
|
||||
|
||||
If unsure, say N.
|
||||
endmenu
|
||||
@@ -0,0 +1,22 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
# (C) Copyright 2000-2007
|
||||
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
|
||||
|
||||
obj-$(CONFIG_DWC_AHCI) += dwc_ahci.o
|
||||
obj-$(CONFIG_FSL_AHCI) += fsl_ahci.o
|
||||
obj-$(CONFIG_AHCI) += ahci-uclass.o
|
||||
obj-$(CONFIG_AHCI_PCI) += ahci-pci.o
|
||||
obj-$(CONFIG_SCSI_AHCI) += ahci.o
|
||||
obj-$(CONFIG_DWC_AHSATA) += dwc_ahsata.o
|
||||
obj-$(CONFIG_FSL_SATA) += fsl_sata.o
|
||||
obj-$(CONFIG_LIBATA) += libata.o
|
||||
obj-$(CONFIG_MVSATA_IDE) += mvsata_ide.o
|
||||
obj-$(CONFIG_SATA) += sata.o
|
||||
obj-$(CONFIG_SATA_CEVA) += sata_ceva.o
|
||||
obj-$(CONFIG_SATA_MV) += sata_mv.o
|
||||
obj-$(CONFIG_SATA_SIL3114) += sata_sil3114.o
|
||||
obj-$(CONFIG_SATA_SIL) += sata_sil.o
|
||||
obj-$(CONFIG_SANDBOX) += sata_sandbox.o
|
||||
obj-$(CONFIG_AHCI_MVEBU) += ahci_mvebu.o
|
||||
obj-$(CONFIG_SUNXI_AHCI) += ahci_sunxi.o
|
||||
@@ -0,0 +1,42 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2017, Bin Meng <bmeng.cn@gmail.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <ahci.h>
|
||||
#include <dm.h>
|
||||
#include <pci.h>
|
||||
|
||||
static int ahci_pci_bind(struct udevice *dev)
|
||||
{
|
||||
struct udevice *scsi_dev;
|
||||
|
||||
return ahci_bind_scsi(dev, &scsi_dev);
|
||||
}
|
||||
|
||||
static int ahci_pci_probe(struct udevice *dev)
|
||||
{
|
||||
return ahci_probe_scsi_pci(dev);
|
||||
}
|
||||
|
||||
static const struct udevice_id ahci_pci_ids[] = {
|
||||
{ .compatible = "ahci-pci" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(ahci_pci) = {
|
||||
.name = "ahci_pci",
|
||||
.id = UCLASS_AHCI,
|
||||
.of_match = ahci_pci_ids,
|
||||
.bind = ahci_pci_bind,
|
||||
.probe = ahci_pci_probe,
|
||||
};
|
||||
|
||||
static struct pci_device_id ahci_pci_supported[] = {
|
||||
{ PCI_DEVICE_CLASS(PCI_CLASS_STORAGE_SATA_AHCI, ~0) },
|
||||
{ PCI_DEVICE(0x1b21, 0x0611) },
|
||||
{},
|
||||
};
|
||||
|
||||
U_BOOT_PCI_DEVICE(ahci_pci, ahci_pci_supported);
|
||||
@@ -0,0 +1,15 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (c) 2015 Google, Inc
|
||||
* Written by Simon Glass <sjg@chromium.org>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <ahci.h>
|
||||
#include <dm.h>
|
||||
|
||||
UCLASS_DRIVER(ahci) = {
|
||||
.id = UCLASS_AHCI,
|
||||
.name = "ahci",
|
||||
.per_device_auto_alloc_size = sizeof(struct ahci_uc_priv),
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,59 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2016 Stefan Roese <sr@denx.de>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <ahci.h>
|
||||
#include <dm.h>
|
||||
|
||||
/*
|
||||
* Dummy implementation that can be overwritten by a board
|
||||
* specific function
|
||||
*/
|
||||
__weak int board_ahci_enable(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mvebu_ahci_bind(struct udevice *dev)
|
||||
{
|
||||
struct udevice *scsi_dev;
|
||||
int ret;
|
||||
|
||||
ret = ahci_bind_scsi(dev, &scsi_dev);
|
||||
if (ret) {
|
||||
debug("%s: Failed to bind (err=%d\n)", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mvebu_ahci_probe(struct udevice *dev)
|
||||
{
|
||||
/*
|
||||
* Board specific SATA / AHCI enable code, e.g. enable the
|
||||
* AHCI power or deassert reset
|
||||
*/
|
||||
board_ahci_enable();
|
||||
|
||||
ahci_probe_scsi(dev, (ulong)devfdt_get_addr_ptr(dev));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id mvebu_ahci_ids[] = {
|
||||
{ .compatible = "marvell,armada-380-ahci" },
|
||||
{ .compatible = "marvell,armada-3700-ahci" },
|
||||
{ .compatible = "marvell,armada-8k-ahci" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(ahci_mvebu_drv) = {
|
||||
.name = "ahci_mvebu",
|
||||
.id = UCLASS_AHCI,
|
||||
.of_match = mvebu_ahci_ids,
|
||||
.bind = mvebu_ahci_bind,
|
||||
.probe = mvebu_ahci_probe,
|
||||
};
|
||||
@@ -0,0 +1,125 @@
|
||||
#include <common.h>
|
||||
#include <ahci.h>
|
||||
#include <dm.h>
|
||||
#include <scsi.h>
|
||||
#include <errno.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/gpio.h>
|
||||
|
||||
#define AHCI_PHYCS0R 0x00c0
|
||||
#define AHCI_PHYCS1R 0x00c4
|
||||
#define AHCI_PHYCS2R 0x00c8
|
||||
#define AHCI_RWCR 0x00fc
|
||||
|
||||
/* This magic PHY initialisation was taken from the Allwinner releases
|
||||
* and Linux driver, but is completely undocumented.
|
||||
*/
|
||||
static int sunxi_ahci_phy_init(u8 *reg_base)
|
||||
{
|
||||
u32 reg_val;
|
||||
int timeout;
|
||||
|
||||
writel(0, reg_base + AHCI_RWCR);
|
||||
mdelay(5);
|
||||
|
||||
setbits_le32(reg_base + AHCI_PHYCS1R, 0x1 << 19);
|
||||
clrsetbits_le32(reg_base + AHCI_PHYCS0R,
|
||||
(0x7 << 24),
|
||||
(0x5 << 24) | (0x1 << 23) | (0x1 << 18));
|
||||
clrsetbits_le32(reg_base + AHCI_PHYCS1R,
|
||||
(0x3 << 16) | (0x1f << 8) | (0x3 << 6),
|
||||
(0x2 << 16) | (0x6 << 8) | (0x2 << 6));
|
||||
setbits_le32(reg_base + AHCI_PHYCS1R, (0x1 << 28) | (0x1 << 15));
|
||||
clrbits_le32(reg_base + AHCI_PHYCS1R, (0x1 << 19));
|
||||
clrsetbits_le32(reg_base + AHCI_PHYCS0R, (0x7 << 20), (0x3 << 20));
|
||||
clrsetbits_le32(reg_base + AHCI_PHYCS2R, (0x1f << 5), (0x19 << 5));
|
||||
mdelay(5);
|
||||
|
||||
setbits_le32(reg_base + AHCI_PHYCS0R, (0x1 << 19));
|
||||
|
||||
timeout = 250; /* Power up takes approx 50 us */
|
||||
for (;;) {
|
||||
reg_val = readl(reg_base + AHCI_PHYCS0R) & (0x7 << 28);
|
||||
if (reg_val == (0x2 << 28))
|
||||
break;
|
||||
if (--timeout == 0) {
|
||||
printf("AHCI PHY power up failed.\n");
|
||||
return -EIO;
|
||||
}
|
||||
udelay(1);
|
||||
};
|
||||
|
||||
setbits_le32(reg_base + AHCI_PHYCS2R, (0x1 << 24));
|
||||
|
||||
timeout = 100; /* Calibration takes approx 10 us */
|
||||
for (;;) {
|
||||
reg_val = readl(reg_base + AHCI_PHYCS2R) & (0x1 << 24);
|
||||
if (reg_val == 0x0)
|
||||
break;
|
||||
if (--timeout == 0) {
|
||||
printf("AHCI PHY calibration failed.\n");
|
||||
return -EIO;
|
||||
}
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
mdelay(15);
|
||||
|
||||
writel(0x7, reg_base + AHCI_RWCR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sunxi_sata_probe(struct udevice *dev)
|
||||
{
|
||||
ulong base;
|
||||
u8 *reg;
|
||||
int ret;
|
||||
|
||||
base = dev_read_addr(dev);
|
||||
if (base == FDT_ADDR_T_NONE) {
|
||||
debug("%s: Failed to find address (err=%d\n)", __func__, ret);
|
||||
return -EINVAL;
|
||||
}
|
||||
reg = (u8 *)base;
|
||||
ret = sunxi_ahci_phy_init(reg);
|
||||
if (ret) {
|
||||
debug("%s: Failed to init phy (err=%d\n)", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
ret = ahci_probe_scsi(dev, base);
|
||||
if (ret) {
|
||||
debug("%s: Failed to probe (err=%d\n)", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sunxi_sata_bind(struct udevice *dev)
|
||||
{
|
||||
struct udevice *scsi_dev;
|
||||
int ret;
|
||||
|
||||
ret = ahci_bind_scsi(dev, &scsi_dev);
|
||||
if (ret) {
|
||||
debug("%s: Failed to bind (err=%d\n)", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id sunxi_ahci_ids[] = {
|
||||
{ .compatible = "allwinner,sun4i-a10-ahci" },
|
||||
{ .compatible = "allwinner,sun8i-r40-ahci" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(ahci_sunxi_drv) = {
|
||||
.name = "ahci_sunxi",
|
||||
.id = UCLASS_AHCI,
|
||||
.of_match = sunxi_ahci_ids,
|
||||
.bind = sunxi_sata_bind,
|
||||
.probe = sunxi_sata_probe,
|
||||
};
|
||||
@@ -0,0 +1,99 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* DWC SATA platform driver
|
||||
*
|
||||
* (C) Copyright 2016
|
||||
* Texas Instruments Incorporated, <www.ti.com>
|
||||
*
|
||||
* Author: Mugunthan V N <mugunthanvnm@ti.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <ahci.h>
|
||||
#include <scsi.h>
|
||||
#include <sata.h>
|
||||
#include <asm/arch/sata.h>
|
||||
#include <asm/io.h>
|
||||
#include <generic-phy.h>
|
||||
|
||||
struct dwc_ahci_priv {
|
||||
void *base;
|
||||
void *wrapper_base;
|
||||
};
|
||||
|
||||
static int dwc_ahci_bind(struct udevice *dev)
|
||||
{
|
||||
struct udevice *scsi_dev;
|
||||
|
||||
return ahci_bind_scsi(dev, &scsi_dev);
|
||||
}
|
||||
|
||||
static int dwc_ahci_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct dwc_ahci_priv *priv = dev_get_priv(dev);
|
||||
fdt_addr_t addr;
|
||||
|
||||
priv->base = map_physmem(devfdt_get_addr(dev), sizeof(void *),
|
||||
MAP_NOCACHE);
|
||||
|
||||
addr = devfdt_get_addr_index(dev, 1);
|
||||
if (addr != FDT_ADDR_T_NONE) {
|
||||
priv->wrapper_base = map_physmem(addr, sizeof(void *),
|
||||
MAP_NOCACHE);
|
||||
} else {
|
||||
priv->wrapper_base = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc_ahci_probe(struct udevice *dev)
|
||||
{
|
||||
struct dwc_ahci_priv *priv = dev_get_priv(dev);
|
||||
int ret;
|
||||
struct phy phy;
|
||||
|
||||
ret = generic_phy_get_by_name(dev, "sata-phy", &phy);
|
||||
if (ret) {
|
||||
pr_err("can't get the phy from DT\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = generic_phy_init(&phy);
|
||||
if (ret) {
|
||||
pr_err("unable to initialize the sata phy\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = generic_phy_power_on(&phy);
|
||||
if (ret) {
|
||||
pr_err("unable to power on the sata phy\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (priv->wrapper_base) {
|
||||
u32 val = TI_SATA_IDLE_NO | TI_SATA_STANDBY_NO;
|
||||
|
||||
/* Enable SATA module, No Idle, No Standby */
|
||||
writel(val, priv->wrapper_base + TI_SATA_SYSCONFIG);
|
||||
}
|
||||
|
||||
return ahci_probe_scsi(dev, (ulong)priv->base);
|
||||
}
|
||||
|
||||
static const struct udevice_id dwc_ahci_ids[] = {
|
||||
{ .compatible = "snps,dwc-ahci" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(dwc_ahci) = {
|
||||
.name = "dwc_ahci",
|
||||
.id = UCLASS_AHCI,
|
||||
.of_match = dwc_ahci_ids,
|
||||
.bind = dwc_ahci_bind,
|
||||
.ofdata_to_platdata = dwc_ahci_ofdata_to_platdata,
|
||||
.ops = &scsi_ops,
|
||||
.probe = dwc_ahci_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct dwc_ahci_priv),
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,319 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright (C) 2010 Freescale Semiconductor, Inc.
|
||||
* Terry Lv <r65388@freescale.com>
|
||||
*/
|
||||
|
||||
#ifndef __DWC_AHSATA_PRIV_H__
|
||||
#define __DWC_AHSATA_PRIV_H__
|
||||
|
||||
#define DWC_AHSATA_MAX_CMD_SLOTS 32
|
||||
|
||||
/* Max host controller numbers */
|
||||
#define SATA_HC_MAX_NUM 4
|
||||
/* Max command queue depth per host controller */
|
||||
#define DWC_AHSATA_HC_MAX_CMD 32
|
||||
/* Max port number per host controller */
|
||||
#define SATA_HC_MAX_PORT 16
|
||||
|
||||
/* Generic Host Register */
|
||||
|
||||
/* HBA Capabilities Register */
|
||||
#define SATA_HOST_CAP_S64A 0x80000000
|
||||
#define SATA_HOST_CAP_SNCQ 0x40000000
|
||||
#define SATA_HOST_CAP_SSNTF 0x20000000
|
||||
#define SATA_HOST_CAP_SMPS 0x10000000
|
||||
#define SATA_HOST_CAP_SSS 0x08000000
|
||||
#define SATA_HOST_CAP_SALP 0x04000000
|
||||
#define SATA_HOST_CAP_SAL 0x02000000
|
||||
#define SATA_HOST_CAP_SCLO 0x01000000
|
||||
#define SATA_HOST_CAP_ISS_MASK 0x00f00000
|
||||
#define SATA_HOST_CAP_ISS_OFFSET 20
|
||||
#define SATA_HOST_CAP_SNZO 0x00080000
|
||||
#define SATA_HOST_CAP_SAM 0x00040000
|
||||
#define SATA_HOST_CAP_SPM 0x00020000
|
||||
#define SATA_HOST_CAP_PMD 0x00008000
|
||||
#define SATA_HOST_CAP_SSC 0x00004000
|
||||
#define SATA_HOST_CAP_PSC 0x00002000
|
||||
#define SATA_HOST_CAP_NCS 0x00001f00
|
||||
#define SATA_HOST_CAP_CCCS 0x00000080
|
||||
#define SATA_HOST_CAP_EMS 0x00000040
|
||||
#define SATA_HOST_CAP_SXS 0x00000020
|
||||
#define SATA_HOST_CAP_NP_MASK 0x0000001f
|
||||
|
||||
/* Global HBA Control Register */
|
||||
#define SATA_HOST_GHC_AE 0x80000000
|
||||
#define SATA_HOST_GHC_IE 0x00000002
|
||||
#define SATA_HOST_GHC_HR 0x00000001
|
||||
|
||||
/* Interrupt Status Register */
|
||||
|
||||
/* Ports Implemented Register */
|
||||
|
||||
/* AHCI Version Register */
|
||||
#define SATA_HOST_VS_MJR_MASK 0xffff0000
|
||||
#define SATA_HOST_VS_MJR_OFFSET 16
|
||||
#define SATA_HOST_VS_MJR_MNR 0x0000ffff
|
||||
|
||||
/* Command Completion Coalescing Control */
|
||||
#define SATA_HOST_CCC_CTL_TV_MASK 0xffff0000
|
||||
#define SATA_HOST_CCC_CTL_TV_OFFSET 16
|
||||
#define SATA_HOST_CCC_CTL_CC_MASK 0x0000ff00
|
||||
#define SATA_HOST_CCC_CTL_CC_OFFSET 8
|
||||
#define SATA_HOST_CCC_CTL_INT_MASK 0x000000f8
|
||||
#define SATA_HOST_CCC_CTL_INT_OFFSET 3
|
||||
#define SATA_HOST_CCC_CTL_EN 0x00000001
|
||||
|
||||
/* Command Completion Coalescing Ports */
|
||||
|
||||
/* HBA Capabilities Extended Register */
|
||||
#define SATA_HOST_CAP2_APST 0x00000004
|
||||
|
||||
/* BIST Activate FIS Register */
|
||||
#define SATA_HOST_BISTAFR_NCP_MASK 0x0000ff00
|
||||
#define SATA_HOST_BISTAFR_NCP_OFFSET 8
|
||||
#define SATA_HOST_BISTAFR_PD_MASK 0x000000ff
|
||||
#define SATA_HOST_BISTAFR_PD_OFFSET 0
|
||||
|
||||
/* BIST Control Register */
|
||||
#define SATA_HOST_BISTCR_FERLB 0x00100000
|
||||
#define SATA_HOST_BISTCR_TXO 0x00040000
|
||||
#define SATA_HOST_BISTCR_CNTCLR 0x00020000
|
||||
#define SATA_HOST_BISTCR_NEALB 0x00010000
|
||||
#define SATA_HOST_BISTCR_LLC_MASK 0x00000700
|
||||
#define SATA_HOST_BISTCR_LLC_OFFSET 8
|
||||
#define SATA_HOST_BISTCR_ERREN 0x00000040
|
||||
#define SATA_HOST_BISTCR_FLIP 0x00000020
|
||||
#define SATA_HOST_BISTCR_PV 0x00000010
|
||||
#define SATA_HOST_BISTCR_PATTERN_MASK 0x0000000f
|
||||
#define SATA_HOST_BISTCR_PATTERN_OFFSET 0
|
||||
|
||||
/* BIST FIS Count Register */
|
||||
|
||||
/* BIST Status Register */
|
||||
#define SATA_HOST_BISTSR_FRAMERR_MASK 0x0000ffff
|
||||
#define SATA_HOST_BISTSR_FRAMERR_OFFSET 0
|
||||
#define SATA_HOST_BISTSR_BRSTERR_MASK 0x00ff0000
|
||||
#define SATA_HOST_BISTSR_BRSTERR_OFFSET 16
|
||||
|
||||
/* BIST DWORD Error Count Register */
|
||||
|
||||
/* OOB Register*/
|
||||
#define SATA_HOST_OOBR_WE 0x80000000
|
||||
#define SATA_HOST_OOBR_cwMin_MASK 0x7f000000
|
||||
#define SATA_HOST_OOBR_cwMAX_MASK 0x00ff0000
|
||||
#define SATA_HOST_OOBR_ciMin_MASK 0x0000ff00
|
||||
#define SATA_HOST_OOBR_ciMax_MASK 0x000000ff
|
||||
|
||||
/* Timer 1-ms Register */
|
||||
|
||||
/* Global Parameter 1 Register */
|
||||
#define SATA_HOST_GPARAM1R_ALIGN_M 0x80000000
|
||||
#define SATA_HOST_GPARAM1R_RX_BUFFER 0x40000000
|
||||
#define SATA_HOST_GPARAM1R_PHY_DATA_MASK 0x30000000
|
||||
#define SATA_HOST_GPARAM1R_PHY_RST 0x08000000
|
||||
#define SATA_HOST_GPARAM1R_PHY_CTRL_MASK 0x07e00000
|
||||
#define SATA_HOST_GPARAM1R_PHY_STAT_MASK 0x001f8000
|
||||
#define SATA_HOST_GPARAM1R_LATCH_M 0x00004000
|
||||
#define SATA_HOST_GPARAM1R_BIST_M 0x00002000
|
||||
#define SATA_HOST_GPARAM1R_PHY_TYPE 0x00001000
|
||||
#define SATA_HOST_GPARAM1R_RETURN_ERR 0x00000400
|
||||
#define SATA_HOST_GPARAM1R_AHB_ENDIAN_MASK 0x00000300
|
||||
#define SATA_HOST_GPARAM1R_S_HADDR 0X00000080
|
||||
#define SATA_HOST_GPARAM1R_M_HADDR 0X00000040
|
||||
|
||||
/* Global Parameter 2 Register */
|
||||
#define SATA_HOST_GPARAM2R_DEV_CP 0x00004000
|
||||
#define SATA_HOST_GPARAM2R_DEV_MP 0x00002000
|
||||
#define SATA_HOST_GPARAM2R_DEV_ENCODE_M 0x00001000
|
||||
#define SATA_HOST_GPARAM2R_RXOOB_CLK_M 0x00000800
|
||||
#define SATA_HOST_GPARAM2R_RXOOB_M 0x00000400
|
||||
#define SATA_HOST_GPARAM2R_TX_OOB_M 0x00000200
|
||||
#define SATA_HOST_GPARAM2R_RXOOB_CLK_MASK 0x000001ff
|
||||
|
||||
/* Port Parameter Register */
|
||||
#define SATA_HOST_PPARAMR_TX_MEM_M 0x00000200
|
||||
#define SATA_HOST_PPARAMR_TX_MEM_S 0x00000100
|
||||
#define SATA_HOST_PPARAMR_RX_MEM_M 0x00000080
|
||||
#define SATA_HOST_PPARAMR_RX_MEM_S 0x00000040
|
||||
#define SATA_HOST_PPARAMR_TXFIFO_DEPTH_MASK 0x00000038
|
||||
#define SATA_HOST_PPARAMR_RXFIFO_DEPTH_MASK 0x00000007
|
||||
|
||||
/* Test Register */
|
||||
#define SATA_HOST_TESTR_PSEL_MASK 0x00070000
|
||||
#define SATA_HOST_TESTR_TEST_IF 0x00000001
|
||||
|
||||
/* Port Register Descriptions */
|
||||
/* Port# Command List Base Address Register */
|
||||
#define SATA_PORT_CLB_CLB_MASK 0xfffffc00
|
||||
|
||||
/* Port# Command List Base Address Upper 32-Bits Register */
|
||||
|
||||
/* Port# FIS Base Address Register */
|
||||
#define SATA_PORT_FB_FB_MASK 0xfffffff0
|
||||
|
||||
/* Port# FIS Base Address Upper 32-Bits Register */
|
||||
|
||||
/* Port# Interrupt Status Register */
|
||||
#define SATA_PORT_IS_CPDS 0x80000000
|
||||
#define SATA_PORT_IS_TFES 0x40000000
|
||||
#define SATA_PORT_IS_HBFS 0x20000000
|
||||
#define SATA_PORT_IS_HBDS 0x10000000
|
||||
#define SATA_PORT_IS_IFS 0x08000000
|
||||
#define SATA_PORT_IS_INFS 0x04000000
|
||||
#define SATA_PORT_IS_OFS 0x01000000
|
||||
#define SATA_PORT_IS_IPMS 0x00800000
|
||||
#define SATA_PORT_IS_PRCS 0x00400000
|
||||
#define SATA_PORT_IS_DMPS 0x00000080
|
||||
#define SATA_PORT_IS_PCS 0x00000040
|
||||
#define SATA_PORT_IS_DPS 0x00000020
|
||||
#define SATA_PORT_IS_UFS 0x00000010
|
||||
#define SATA_PORT_IS_SDBS 0x00000008
|
||||
#define SATA_PORT_IS_DSS 0x00000004
|
||||
#define SATA_PORT_IS_PSS 0x00000002
|
||||
#define SATA_PORT_IS_DHRS 0x00000001
|
||||
|
||||
/* Port# Interrupt Enable Register */
|
||||
#define SATA_PORT_IE_CPDE 0x80000000
|
||||
#define SATA_PORT_IE_TFEE 0x40000000
|
||||
#define SATA_PORT_IE_HBFE 0x20000000
|
||||
#define SATA_PORT_IE_HBDE 0x10000000
|
||||
#define SATA_PORT_IE_IFE 0x08000000
|
||||
#define SATA_PORT_IE_INFE 0x04000000
|
||||
#define SATA_PORT_IE_OFE 0x01000000
|
||||
#define SATA_PORT_IE_IPME 0x00800000
|
||||
#define SATA_PORT_IE_PRCE 0x00400000
|
||||
#define SATA_PORT_IE_DMPE 0x00000080
|
||||
#define SATA_PORT_IE_PCE 0x00000040
|
||||
#define SATA_PORT_IE_DPE 0x00000020
|
||||
#define SATA_PORT_IE_UFE 0x00000010
|
||||
#define SATA_PORT_IE_SDBE 0x00000008
|
||||
#define SATA_PORT_IE_DSE 0x00000004
|
||||
#define SATA_PORT_IE_PSE 0x00000002
|
||||
#define SATA_PORT_IE_DHRE 0x00000001
|
||||
|
||||
/* Port# Command Register */
|
||||
#define SATA_PORT_CMD_ICC_MASK 0xf0000000
|
||||
#define SATA_PORT_CMD_ASP 0x08000000
|
||||
#define SATA_PORT_CMD_ALPE 0x04000000
|
||||
#define SATA_PORT_CMD_DLAE 0x02000000
|
||||
#define SATA_PORT_CMD_ATAPI 0x01000000
|
||||
#define SATA_PORT_CMD_APSTE 0x00800000
|
||||
#define SATA_PORT_CMD_ESP 0x00200000
|
||||
#define SATA_PORT_CMD_CPD 0x00100000
|
||||
#define SATA_PORT_CMD_MPSP 0x00080000
|
||||
#define SATA_PORT_CMD_HPCP 0x00040000
|
||||
#define SATA_PORT_CMD_PMA 0x00020000
|
||||
#define SATA_PORT_CMD_CPS 0x00010000
|
||||
#define SATA_PORT_CMD_CR 0x00008000
|
||||
#define SATA_PORT_CMD_FR 0x00004000
|
||||
#define SATA_PORT_CMD_MPSS 0x00002000
|
||||
#define SATA_PORT_CMD_CCS_MASK 0x00001f00
|
||||
#define SATA_PORT_CMD_FRE 0x00000010
|
||||
#define SATA_PORT_CMD_CLO 0x00000008
|
||||
#define SATA_PORT_CMD_POD 0x00000004
|
||||
#define SATA_PORT_CMD_SUD 0x00000002
|
||||
#define SATA_PORT_CMD_ST 0x00000001
|
||||
|
||||
/* Port# Task File Data Register */
|
||||
#define SATA_PORT_TFD_ERR_MASK 0x0000ff00
|
||||
#define SATA_PORT_TFD_STS_MASK 0x000000ff
|
||||
#define SATA_PORT_TFD_STS_ERR 0x00000001
|
||||
#define SATA_PORT_TFD_STS_DRQ 0x00000008
|
||||
#define SATA_PORT_TFD_STS_BSY 0x00000080
|
||||
|
||||
/* Port# Signature Register */
|
||||
|
||||
/* Port# Serial ATA Status {SStatus} Register */
|
||||
#define SATA_PORT_SSTS_IPM_MASK 0x00000f00
|
||||
#define SATA_PORT_SSTS_SPD_MASK 0x000000f0
|
||||
#define SATA_PORT_SSTS_DET_MASK 0x0000000f
|
||||
|
||||
/* Port# Serial ATA Control {SControl} Register */
|
||||
#define SATA_PORT_SCTL_IPM_MASK 0x00000f00
|
||||
#define SATA_PORT_SCTL_SPD_MASK 0x000000f0
|
||||
#define SATA_PORT_SCTL_DET_MASK 0x0000000f
|
||||
|
||||
/* Port# Serial ATA Error {SError} Register */
|
||||
#define SATA_PORT_SERR_DIAG_X 0x04000000
|
||||
#define SATA_PORT_SERR_DIAG_F 0x02000000
|
||||
#define SATA_PORT_SERR_DIAG_T 0x01000000
|
||||
#define SATA_PORT_SERR_DIAG_S 0x00800000
|
||||
#define SATA_PORT_SERR_DIAG_H 0x00400000
|
||||
#define SATA_PORT_SERR_DIAG_C 0x00200000
|
||||
#define SATA_PORT_SERR_DIAG_D 0x00100000
|
||||
#define SATA_PORT_SERR_DIAG_B 0x00080000
|
||||
#define SATA_PORT_SERR_DIAG_W 0x00040000
|
||||
#define SATA_PORT_SERR_DIAG_I 0x00020000
|
||||
#define SATA_PORT_SERR_DIAG_N 0x00010000
|
||||
#define SATA_PORT_SERR_ERR_E 0x00000800
|
||||
#define SATA_PORT_SERR_ERR_P 0x00000400
|
||||
#define SATA_PORT_SERR_ERR_C 0x00000200
|
||||
#define SATA_PORT_SERR_ERR_T 0x00000100
|
||||
#define SATA_PORT_SERR_ERR_M 0x00000002
|
||||
#define SATA_PORT_SERR_ERR_I 0x00000001
|
||||
|
||||
/* Port# Serial ATA Active {SActive} Register */
|
||||
|
||||
/* Port# Command Issue Register */
|
||||
|
||||
/* Port# Serial ATA Notification Register */
|
||||
|
||||
/* Port# DMA Control Register */
|
||||
#define SATA_PORT_DMACR_RXABL_MASK 0x0000f000
|
||||
#define SATA_PORT_DMACR_TXABL_MASK 0x00000f00
|
||||
#define SATA_PORT_DMACR_RXTS_MASK 0x000000f0
|
||||
#define SATA_PORT_DMACR_TXTS_MASK 0x0000000f
|
||||
|
||||
/* Port# PHY Control Register */
|
||||
|
||||
/* Port# PHY Status Register */
|
||||
|
||||
#define SATA_HC_CMD_HDR_ENTRY_SIZE sizeof(struct cmd_hdr_entry)
|
||||
|
||||
/* DW0
|
||||
*/
|
||||
#define CMD_HDR_DI_CFL_MASK 0x0000001f
|
||||
#define CMD_HDR_DI_CFL_OFFSET 0
|
||||
#define CMD_HDR_DI_A 0x00000020
|
||||
#define CMD_HDR_DI_W 0x00000040
|
||||
#define CMD_HDR_DI_P 0x00000080
|
||||
#define CMD_HDR_DI_R 0x00000100
|
||||
#define CMD_HDR_DI_B 0x00000200
|
||||
#define CMD_HDR_DI_C 0x00000400
|
||||
#define CMD_HDR_DI_PMP_MASK 0x0000f000
|
||||
#define CMD_HDR_DI_PMP_OFFSET 12
|
||||
#define CMD_HDR_DI_PRDTL 0xffff0000
|
||||
#define CMD_HDR_DI_PRDTL_OFFSET 16
|
||||
|
||||
/* prde_fis_len
|
||||
*/
|
||||
#define CMD_HDR_PRD_ENTRY_SHIFT 16
|
||||
#define CMD_HDR_PRD_ENTRY_MASK 0x003f0000
|
||||
#define CMD_HDR_FIS_LEN_SHIFT 2
|
||||
|
||||
/* attribute
|
||||
*/
|
||||
#define CMD_HDR_ATTR_RES 0x00000800 /* Reserved bit, should be 1 */
|
||||
#define CMD_HDR_ATTR_VBIST 0x00000400 /* Vendor BIST */
|
||||
/* Snoop enable for all descriptor */
|
||||
#define CMD_HDR_ATTR_SNOOP 0x00000200
|
||||
#define CMD_HDR_ATTR_FPDMA 0x00000100 /* FPDMA queued command */
|
||||
#define CMD_HDR_ATTR_RESET 0x00000080 /* Reset - a SRST or device reset */
|
||||
/* BIST - require the host to enter BIST mode */
|
||||
#define CMD_HDR_ATTR_BIST 0x00000040
|
||||
#define CMD_HDR_ATTR_ATAPI 0x00000020 /* ATAPI command */
|
||||
#define CMD_HDR_ATTR_TAG 0x0000001f /* TAG mask */
|
||||
|
||||
#define FLAGS_DMA 0x00000000
|
||||
#define FLAGS_FPDMA 0x00000001
|
||||
|
||||
#define SATA_FLAG_Q_DEP_MASK 0x0000000f
|
||||
#define SATA_FLAG_WCACHE 0x00000100
|
||||
#define SATA_FLAG_FLUSH 0x00000200
|
||||
#define SATA_FLAG_FLUSH_EXT 0x00000400
|
||||
|
||||
#define READ_CMD 0
|
||||
#define WRITE_CMD 1
|
||||
|
||||
#endif /* __DWC_AHSATA_H__ */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,987 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2008,2010 Freescale Semiconductor, Inc.
|
||||
* Copyright 2019 NXP
|
||||
* Author: Dave Liu <daveliu@freescale.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <command.h>
|
||||
#include <console.h>
|
||||
#include <cpu_func.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/fsl_serdes.h>
|
||||
#include <malloc.h>
|
||||
#include <libata.h>
|
||||
#include <fis.h>
|
||||
#include <sata.h>
|
||||
#include "fsl_sata.h"
|
||||
|
||||
#if CONFIG_IS_ENABLED(BLK)
|
||||
#include <dm.h>
|
||||
#include <ahci.h>
|
||||
#include <blk.h>
|
||||
#else
|
||||
#ifndef CONFIG_SYS_SATA1_FLAGS
|
||||
#define CONFIG_SYS_SATA1_FLAGS FLAGS_DMA
|
||||
#endif
|
||||
#ifndef CONFIG_SYS_SATA2_FLAGS
|
||||
#define CONFIG_SYS_SATA2_FLAGS FLAGS_DMA
|
||||
#endif
|
||||
|
||||
static struct fsl_sata_info fsl_sata_info[] = {
|
||||
#ifdef CONFIG_SATA1
|
||||
{CONFIG_SYS_SATA1, CONFIG_SYS_SATA1_FLAGS},
|
||||
#else
|
||||
{0, 0},
|
||||
#endif
|
||||
#ifdef CONFIG_SATA2
|
||||
{CONFIG_SYS_SATA2, CONFIG_SYS_SATA2_FLAGS},
|
||||
#else
|
||||
{0, 0},
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
|
||||
static inline void sdelay(unsigned long sec)
|
||||
{
|
||||
unsigned long i;
|
||||
for (i = 0; i < sec; i++)
|
||||
mdelay(1000);
|
||||
}
|
||||
|
||||
static void fsl_sata_dump_sfis(struct sata_fis_d2h *s)
|
||||
{
|
||||
printf("Status FIS dump:\n\r");
|
||||
printf("fis_type: %02x\n\r", s->fis_type);
|
||||
printf("pm_port_i: %02x\n\r", s->pm_port_i);
|
||||
printf("status: %02x\n\r", s->status);
|
||||
printf("error: %02x\n\r", s->error);
|
||||
printf("lba_low: %02x\n\r", s->lba_low);
|
||||
printf("lba_mid: %02x\n\r", s->lba_mid);
|
||||
printf("lba_high: %02x\n\r", s->lba_high);
|
||||
printf("device: %02x\n\r", s->device);
|
||||
printf("lba_low_exp: %02x\n\r", s->lba_low_exp);
|
||||
printf("lba_mid_exp: %02x\n\r", s->lba_mid_exp);
|
||||
printf("lba_high_exp: %02x\n\r", s->lba_high_exp);
|
||||
printf("res1: %02x\n\r", s->res1);
|
||||
printf("sector_count: %02x\n\r", s->sector_count);
|
||||
printf("sector_count_exp: %02x\n\r", s->sector_count_exp);
|
||||
}
|
||||
|
||||
static int ata_wait_register(unsigned __iomem *addr, u32 mask,
|
||||
u32 val, u32 timeout_msec)
|
||||
{
|
||||
int i;
|
||||
u32 temp;
|
||||
|
||||
for (i = 0; (((temp = in_le32(addr)) & mask) != val)
|
||||
&& i < timeout_msec; i++)
|
||||
mdelay(1);
|
||||
return (i < timeout_msec) ? 0 : -1;
|
||||
}
|
||||
|
||||
#if !CONFIG_IS_ENABLED(BLK)
|
||||
int init_sata(int dev)
|
||||
#else
|
||||
static int init_sata(struct fsl_ata_priv *priv, int dev)
|
||||
#endif
|
||||
{
|
||||
u32 length, align;
|
||||
cmd_hdr_tbl_t *cmd_hdr;
|
||||
u32 cda;
|
||||
u32 val32;
|
||||
fsl_sata_reg_t __iomem *reg;
|
||||
u32 sig;
|
||||
int i;
|
||||
fsl_sata_t *sata;
|
||||
|
||||
if (dev < 0 || dev > (CONFIG_SYS_SATA_MAX_DEVICE - 1)) {
|
||||
printf("the sata index %d is out of ranges\n\r", dev);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MPC85xx
|
||||
if ((dev == 0) && (!is_serdes_configured(SATA1))) {
|
||||
printf("SATA%d [dev = %d] is not enabled\n", dev+1, dev);
|
||||
return -1;
|
||||
}
|
||||
if ((dev == 1) && (!is_serdes_configured(SATA2))) {
|
||||
printf("SATA%d [dev = %d] is not enabled\n", dev+1, dev);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Allocate SATA device driver struct */
|
||||
sata = (fsl_sata_t *)malloc(sizeof(fsl_sata_t));
|
||||
if (!sata) {
|
||||
printf("alloc the sata device struct failed\n\r");
|
||||
return -1;
|
||||
}
|
||||
/* Zero all of the device driver struct */
|
||||
memset((void *)sata, 0, sizeof(fsl_sata_t));
|
||||
|
||||
snprintf(sata->name, 12, "SATA%d:\n", dev);
|
||||
|
||||
/* Set the controller register base address to device struct */
|
||||
#if !CONFIG_IS_ENABLED(BLK)
|
||||
sata_dev_desc[dev].priv = (void *)sata;
|
||||
reg = (fsl_sata_reg_t *)(fsl_sata_info[dev].sata_reg_base);
|
||||
sata->dma_flag = fsl_sata_info[dev].flags;
|
||||
#else
|
||||
reg = (fsl_sata_reg_t *)(priv->base + priv->offset * dev);
|
||||
sata->dma_flag = priv->flag;
|
||||
priv->fsl_sata = sata;
|
||||
#endif
|
||||
sata->reg_base = reg;
|
||||
|
||||
/* Allocate the command header table, 4 bytes aligned */
|
||||
length = sizeof(struct cmd_hdr_tbl);
|
||||
align = SATA_HC_CMD_HDR_TBL_ALIGN;
|
||||
sata->cmd_hdr_tbl_offset = (void *)malloc(length + align);
|
||||
if (!sata->cmd_hdr_tbl_offset) {
|
||||
printf("alloc the command header failed\n\r");
|
||||
return -1;
|
||||
}
|
||||
|
||||
cmd_hdr = (cmd_hdr_tbl_t *)(((u32)sata->cmd_hdr_tbl_offset + align)
|
||||
& ~(align - 1));
|
||||
sata->cmd_hdr = cmd_hdr;
|
||||
|
||||
/* Zero all of the command header table */
|
||||
memset((void *)sata->cmd_hdr_tbl_offset, 0, length + align);
|
||||
|
||||
/* Allocate command descriptor for all command */
|
||||
length = sizeof(struct cmd_desc) * SATA_HC_MAX_CMD;
|
||||
align = SATA_HC_CMD_DESC_ALIGN;
|
||||
sata->cmd_desc_offset = (void *)malloc(length + align);
|
||||
if (!sata->cmd_desc_offset) {
|
||||
printf("alloc the command descriptor failed\n\r");
|
||||
return -1;
|
||||
}
|
||||
sata->cmd_desc = (cmd_desc_t *)(((u32)sata->cmd_desc_offset + align)
|
||||
& ~(align - 1));
|
||||
/* Zero all of command descriptor */
|
||||
memset((void *)sata->cmd_desc_offset, 0, length + align);
|
||||
|
||||
/* Link the command descriptor to command header */
|
||||
for (i = 0; i < SATA_HC_MAX_CMD; i++) {
|
||||
cda = ((u32)sata->cmd_desc + SATA_HC_CMD_DESC_SIZE * i)
|
||||
& ~(CMD_HDR_CDA_ALIGN - 1);
|
||||
cmd_hdr->cmd_slot[i].cda = cpu_to_le32(cda);
|
||||
}
|
||||
|
||||
/* To have safe state, force the controller offline */
|
||||
val32 = in_le32(®->hcontrol);
|
||||
val32 &= ~HCONTROL_ONOFF;
|
||||
val32 |= HCONTROL_FORCE_OFFLINE;
|
||||
out_le32(®->hcontrol, val32);
|
||||
|
||||
/* Wait the controller offline */
|
||||
ata_wait_register(®->hstatus, HSTATUS_ONOFF, 0, 1000);
|
||||
|
||||
/* Set the command header base address to CHBA register to tell DMA */
|
||||
out_le32(®->chba, (u32)cmd_hdr & ~0x3);
|
||||
|
||||
/* Snoop for the command header */
|
||||
val32 = in_le32(®->hcontrol);
|
||||
val32 |= HCONTROL_HDR_SNOOP;
|
||||
out_le32(®->hcontrol, val32);
|
||||
|
||||
/* Disable all of interrupts */
|
||||
val32 = in_le32(®->hcontrol);
|
||||
val32 &= ~HCONTROL_INT_EN_ALL;
|
||||
out_le32(®->hcontrol, val32);
|
||||
|
||||
/* Clear all of interrupts */
|
||||
val32 = in_le32(®->hstatus);
|
||||
out_le32(®->hstatus, val32);
|
||||
|
||||
/* Set the ICC, no interrupt coalescing */
|
||||
out_le32(®->icc, 0x01000000);
|
||||
|
||||
/* No PM attatched, the SATA device direct connect */
|
||||
out_le32(®->cqpmp, 0);
|
||||
|
||||
/* Clear SError register */
|
||||
val32 = in_le32(®->serror);
|
||||
out_le32(®->serror, val32);
|
||||
|
||||
/* Clear CER register */
|
||||
val32 = in_le32(®->cer);
|
||||
out_le32(®->cer, val32);
|
||||
|
||||
/* Clear DER register */
|
||||
val32 = in_le32(®->der);
|
||||
out_le32(®->der, val32);
|
||||
|
||||
/* No device detection or initialization action requested */
|
||||
out_le32(®->scontrol, 0x00000300);
|
||||
|
||||
/* Configure the transport layer, default value */
|
||||
out_le32(®->transcfg, 0x08000016);
|
||||
|
||||
/* Configure the link layer, default value */
|
||||
out_le32(®->linkcfg, 0x0000ff34);
|
||||
|
||||
/* Bring the controller online */
|
||||
val32 = in_le32(®->hcontrol);
|
||||
val32 |= HCONTROL_ONOFF;
|
||||
out_le32(®->hcontrol, val32);
|
||||
|
||||
mdelay(100);
|
||||
|
||||
/* print sata device name */
|
||||
if (!dev)
|
||||
printf("%s ", sata->name);
|
||||
else
|
||||
printf(" %s ", sata->name);
|
||||
|
||||
/* Wait PHY RDY signal changed for 500ms */
|
||||
ata_wait_register(®->hstatus, HSTATUS_PHY_RDY,
|
||||
HSTATUS_PHY_RDY, 500);
|
||||
|
||||
/* Check PHYRDY */
|
||||
val32 = in_le32(®->hstatus);
|
||||
if (val32 & HSTATUS_PHY_RDY) {
|
||||
sata->link = 1;
|
||||
} else {
|
||||
sata->link = 0;
|
||||
printf("(No RDY)\n\r");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Wait for signature updated, which is 1st D2H */
|
||||
ata_wait_register(®->hstatus, HSTATUS_SIGNATURE,
|
||||
HSTATUS_SIGNATURE, 10000);
|
||||
|
||||
if (val32 & HSTATUS_SIGNATURE) {
|
||||
sig = in_le32(®->sig);
|
||||
debug("Signature updated, the sig =%08x\n\r", sig);
|
||||
sata->ata_device_type = ata_dev_classify(sig);
|
||||
}
|
||||
|
||||
/* Check the speed */
|
||||
val32 = in_le32(®->sstatus);
|
||||
if ((val32 & SSTATUS_SPD_MASK) == SSTATUS_SPD_GEN1)
|
||||
printf("(1.5 Gbps)\n\r");
|
||||
else if ((val32 & SSTATUS_SPD_MASK) == SSTATUS_SPD_GEN2)
|
||||
printf("(3 Gbps)\n\r");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int reset_sata(int dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fsl_sata_dump_regs(fsl_sata_reg_t __iomem *reg)
|
||||
{
|
||||
printf("\n\rSATA: %08x\n\r", (u32)reg);
|
||||
printf("CQR: %08x\n\r", in_le32(®->cqr));
|
||||
printf("CAR: %08x\n\r", in_le32(®->car));
|
||||
printf("CCR: %08x\n\r", in_le32(®->ccr));
|
||||
printf("CER: %08x\n\r", in_le32(®->cer));
|
||||
printf("CQR: %08x\n\r", in_le32(®->cqr));
|
||||
printf("DER: %08x\n\r", in_le32(®->der));
|
||||
printf("CHBA: %08x\n\r", in_le32(®->chba));
|
||||
printf("HStatus: %08x\n\r", in_le32(®->hstatus));
|
||||
printf("HControl: %08x\n\r", in_le32(®->hcontrol));
|
||||
printf("CQPMP: %08x\n\r", in_le32(®->cqpmp));
|
||||
printf("SIG: %08x\n\r", in_le32(®->sig));
|
||||
printf("ICC: %08x\n\r", in_le32(®->icc));
|
||||
printf("SStatus: %08x\n\r", in_le32(®->sstatus));
|
||||
printf("SError: %08x\n\r", in_le32(®->serror));
|
||||
printf("SControl: %08x\n\r", in_le32(®->scontrol));
|
||||
printf("SNotification: %08x\n\r", in_le32(®->snotification));
|
||||
printf("TransCfg: %08x\n\r", in_le32(®->transcfg));
|
||||
printf("TransStatus: %08x\n\r", in_le32(®->transstatus));
|
||||
printf("LinkCfg: %08x\n\r", in_le32(®->linkcfg));
|
||||
printf("LinkCfg1: %08x\n\r", in_le32(®->linkcfg1));
|
||||
printf("LinkCfg2: %08x\n\r", in_le32(®->linkcfg2));
|
||||
printf("LinkStatus: %08x\n\r", in_le32(®->linkstatus));
|
||||
printf("LinkStatus1: %08x\n\r", in_le32(®->linkstatus1));
|
||||
printf("PhyCtrlCfg: %08x\n\r", in_le32(®->phyctrlcfg));
|
||||
printf("SYSPR: %08x\n\r", in_be32(®->syspr));
|
||||
}
|
||||
|
||||
static int fsl_ata_exec_ata_cmd(struct fsl_sata *sata, struct sata_fis_h2d *cfis,
|
||||
int is_ncq, int tag, u8 *buffer, u32 len)
|
||||
{
|
||||
cmd_hdr_entry_t *cmd_hdr;
|
||||
cmd_desc_t *cmd_desc;
|
||||
sata_fis_h2d_t *h2d;
|
||||
prd_entry_t *prde;
|
||||
u32 ext_c_ddc;
|
||||
u32 prde_count;
|
||||
u32 val32;
|
||||
u32 ttl;
|
||||
fsl_sata_reg_t __iomem *reg = sata->reg_base;
|
||||
int i;
|
||||
|
||||
/* Check xfer length */
|
||||
if (len > SATA_HC_MAX_XFER_LEN) {
|
||||
printf("max transfer length is 64MB\n\r");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Setup the command descriptor */
|
||||
cmd_desc = sata->cmd_desc + tag;
|
||||
|
||||
/* Get the pointer cfis of command descriptor */
|
||||
h2d = (sata_fis_h2d_t *)cmd_desc->cfis;
|
||||
|
||||
/* Zero the cfis of command descriptor */
|
||||
memset((void *)h2d, 0, SATA_HC_CMD_DESC_CFIS_SIZE);
|
||||
|
||||
/* Copy the cfis from user to command descriptor */
|
||||
h2d->fis_type = cfis->fis_type;
|
||||
h2d->pm_port_c = cfis->pm_port_c;
|
||||
h2d->command = cfis->command;
|
||||
|
||||
h2d->features = cfis->features;
|
||||
h2d->features_exp = cfis->features_exp;
|
||||
|
||||
h2d->lba_low = cfis->lba_low;
|
||||
h2d->lba_mid = cfis->lba_mid;
|
||||
h2d->lba_high = cfis->lba_high;
|
||||
h2d->lba_low_exp = cfis->lba_low_exp;
|
||||
h2d->lba_mid_exp = cfis->lba_mid_exp;
|
||||
h2d->lba_high_exp = cfis->lba_high_exp;
|
||||
|
||||
if (!is_ncq) {
|
||||
h2d->sector_count = cfis->sector_count;
|
||||
h2d->sector_count_exp = cfis->sector_count_exp;
|
||||
} else { /* NCQ */
|
||||
h2d->sector_count = (u8)(tag << 3);
|
||||
}
|
||||
|
||||
h2d->device = cfis->device;
|
||||
h2d->control = cfis->control;
|
||||
|
||||
/* Setup the PRD table */
|
||||
prde = (prd_entry_t *)cmd_desc->prdt;
|
||||
memset((void *)prde, 0, sizeof(struct prdt));
|
||||
|
||||
prde_count = 0;
|
||||
ttl = len;
|
||||
for (i = 0; i < SATA_HC_MAX_PRD_DIRECT; i++) {
|
||||
if (!len)
|
||||
break;
|
||||
prde->dba = cpu_to_le32((u32)buffer & ~0x3);
|
||||
debug("dba = %08x\n\r", (u32)buffer);
|
||||
|
||||
if (len < PRD_ENTRY_MAX_XFER_SZ) {
|
||||
ext_c_ddc = PRD_ENTRY_DATA_SNOOP | len;
|
||||
debug("ext_c_ddc1 = %08x, len = %08x\n\r", ext_c_ddc, len);
|
||||
prde->ext_c_ddc = cpu_to_le32(ext_c_ddc);
|
||||
prde_count++;
|
||||
prde++;
|
||||
break;
|
||||
} else {
|
||||
ext_c_ddc = PRD_ENTRY_DATA_SNOOP; /* 4M bytes */
|
||||
debug("ext_c_ddc2 = %08x, len = %08x\n\r", ext_c_ddc, len);
|
||||
prde->ext_c_ddc = cpu_to_le32(ext_c_ddc);
|
||||
buffer += PRD_ENTRY_MAX_XFER_SZ;
|
||||
len -= PRD_ENTRY_MAX_XFER_SZ;
|
||||
prde_count++;
|
||||
prde++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Setup the command slot of cmd hdr */
|
||||
cmd_hdr = (cmd_hdr_entry_t *)&sata->cmd_hdr->cmd_slot[tag];
|
||||
|
||||
cmd_hdr->cda = cpu_to_le32((u32)cmd_desc & ~0x3);
|
||||
|
||||
val32 = prde_count << CMD_HDR_PRD_ENTRY_SHIFT;
|
||||
val32 |= sizeof(sata_fis_h2d_t);
|
||||
cmd_hdr->prde_fis_len = cpu_to_le32(val32);
|
||||
|
||||
cmd_hdr->ttl = cpu_to_le32(ttl);
|
||||
|
||||
if (!is_ncq) {
|
||||
val32 = CMD_HDR_ATTR_RES | CMD_HDR_ATTR_SNOOP;
|
||||
} else {
|
||||
val32 = CMD_HDR_ATTR_RES | CMD_HDR_ATTR_SNOOP | CMD_HDR_ATTR_FPDMA;
|
||||
}
|
||||
|
||||
tag &= CMD_HDR_ATTR_TAG;
|
||||
val32 |= tag;
|
||||
|
||||
debug("attribute = %08x\n\r", val32);
|
||||
cmd_hdr->attribute = cpu_to_le32(val32);
|
||||
|
||||
/* Make sure cmd desc and cmd slot valid before command issue */
|
||||
sync();
|
||||
|
||||
/* PMP*/
|
||||
val32 = (u32)(h2d->pm_port_c & 0x0f);
|
||||
out_le32(®->cqpmp, val32);
|
||||
|
||||
/* Wait no active */
|
||||
if (ata_wait_register(®->car, (1 << tag), 0, 10000))
|
||||
printf("Wait no active time out\n\r");
|
||||
|
||||
/* Issue command */
|
||||
if (!(in_le32(®->cqr) & (1 << tag))) {
|
||||
val32 = 1 << tag;
|
||||
out_le32(®->cqr, val32);
|
||||
}
|
||||
|
||||
/* Wait command completed for 10s */
|
||||
if (ata_wait_register(®->ccr, (1 << tag), (1 << tag), 10000)) {
|
||||
if (!is_ncq)
|
||||
printf("Non-NCQ command time out\n\r");
|
||||
else
|
||||
printf("NCQ command time out\n\r");
|
||||
}
|
||||
|
||||
val32 = in_le32(®->cer);
|
||||
|
||||
if (val32) {
|
||||
u32 der;
|
||||
fsl_sata_dump_sfis((struct sata_fis_d2h *)cmd_desc->sfis);
|
||||
printf("CE at device\n\r");
|
||||
fsl_sata_dump_regs(reg);
|
||||
der = in_le32(®->der);
|
||||
out_le32(®->cer, val32);
|
||||
out_le32(®->der, der);
|
||||
}
|
||||
|
||||
/* Clear complete flags */
|
||||
val32 = in_le32(®->ccr);
|
||||
out_le32(®->ccr, val32);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int fsl_ata_exec_reset_cmd(struct fsl_sata *sata, struct sata_fis_h2d *cfis,
|
||||
int tag, u8 *buffer, u32 len)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_sata_exec_cmd(struct fsl_sata *sata, struct sata_fis_h2d *cfis,
|
||||
enum cmd_type command_type, int tag, u8 *buffer, u32 len)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (tag > SATA_HC_MAX_CMD || tag < 0) {
|
||||
printf("tag is out of range, tag=%d\n\r", tag);
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (command_type) {
|
||||
case CMD_ATA:
|
||||
rc = fsl_ata_exec_ata_cmd(sata, cfis, 0, tag, buffer, len);
|
||||
return rc;
|
||||
case CMD_RESET:
|
||||
rc = fsl_ata_exec_reset_cmd(sata, cfis, tag, buffer, len);
|
||||
return rc;
|
||||
case CMD_NCQ:
|
||||
rc = fsl_ata_exec_ata_cmd(sata, cfis, 1, tag, buffer, len);
|
||||
return rc;
|
||||
case CMD_ATAPI:
|
||||
case CMD_VENDOR_BIST:
|
||||
case CMD_BIST:
|
||||
printf("not support now\n\r");
|
||||
return -1;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void fsl_sata_xfer_mode(fsl_sata_t *sata, u16 *id)
|
||||
{
|
||||
sata->pio = id[ATA_ID_PIO_MODES];
|
||||
sata->mwdma = id[ATA_ID_MWDMA_MODES];
|
||||
sata->udma = id[ATA_ID_UDMA_MODES];
|
||||
debug("pio %04x, mwdma %04x, udma %04x\n\r", sata->pio, sata->mwdma, sata->udma);
|
||||
}
|
||||
|
||||
static void fsl_sata_set_features(fsl_sata_t *sata)
|
||||
{
|
||||
struct sata_fis_h2d h2d, *cfis = &h2d;
|
||||
u8 udma_cap;
|
||||
|
||||
memset(cfis, 0, sizeof(struct sata_fis_h2d));
|
||||
|
||||
cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
|
||||
cfis->pm_port_c = 0x80; /* is command */
|
||||
cfis->command = ATA_CMD_SET_FEATURES;
|
||||
cfis->features = SETFEATURES_XFER;
|
||||
|
||||
/* First check the device capablity */
|
||||
udma_cap = (u8)(sata->udma & 0xff);
|
||||
debug("udma_cap %02x\n\r", udma_cap);
|
||||
|
||||
if (udma_cap == ATA_UDMA6)
|
||||
cfis->sector_count = XFER_UDMA_6;
|
||||
if (udma_cap == ATA_UDMA5)
|
||||
cfis->sector_count = XFER_UDMA_5;
|
||||
if (udma_cap == ATA_UDMA4)
|
||||
cfis->sector_count = XFER_UDMA_4;
|
||||
if (udma_cap == ATA_UDMA3)
|
||||
cfis->sector_count = XFER_UDMA_3;
|
||||
|
||||
fsl_sata_exec_cmd(sata, cfis, CMD_ATA, 0, NULL, 0);
|
||||
}
|
||||
|
||||
static u32 fsl_sata_rw_cmd(fsl_sata_t *sata, u32 start, u32 blkcnt, u8 *buffer,
|
||||
int is_write)
|
||||
{
|
||||
struct sata_fis_h2d h2d, *cfis = &h2d;
|
||||
u32 block;
|
||||
|
||||
block = start;
|
||||
|
||||
memset(cfis, 0, sizeof(struct sata_fis_h2d));
|
||||
|
||||
cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
|
||||
cfis->pm_port_c = 0x80; /* is command */
|
||||
cfis->command = (is_write) ? ATA_CMD_WRITE : ATA_CMD_READ;
|
||||
cfis->device = ATA_LBA;
|
||||
|
||||
cfis->device |= (block >> 24) & 0xf;
|
||||
cfis->lba_high = (block >> 16) & 0xff;
|
||||
cfis->lba_mid = (block >> 8) & 0xff;
|
||||
cfis->lba_low = block & 0xff;
|
||||
cfis->sector_count = (u8)(blkcnt & 0xff);
|
||||
|
||||
fsl_sata_exec_cmd(sata, cfis, CMD_ATA, 0, buffer, ATA_SECT_SIZE * blkcnt);
|
||||
return blkcnt;
|
||||
}
|
||||
|
||||
static void fsl_sata_flush_cache(fsl_sata_t *sata)
|
||||
{
|
||||
struct sata_fis_h2d h2d, *cfis = &h2d;
|
||||
|
||||
memset(cfis, 0, sizeof(struct sata_fis_h2d));
|
||||
|
||||
cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
|
||||
cfis->pm_port_c = 0x80; /* is command */
|
||||
cfis->command = ATA_CMD_FLUSH;
|
||||
|
||||
fsl_sata_exec_cmd(sata, cfis, CMD_ATA, 0, NULL, 0);
|
||||
}
|
||||
|
||||
static u32 fsl_sata_rw_cmd_ext(fsl_sata_t *sata, u32 start, u32 blkcnt,
|
||||
u8 *buffer, int is_write)
|
||||
{
|
||||
struct sata_fis_h2d h2d, *cfis = &h2d;
|
||||
u64 block;
|
||||
|
||||
block = (u64)start;
|
||||
|
||||
memset(cfis, 0, sizeof(struct sata_fis_h2d));
|
||||
|
||||
cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
|
||||
cfis->pm_port_c = 0x80; /* is command */
|
||||
|
||||
cfis->command = (is_write) ? ATA_CMD_WRITE_EXT
|
||||
: ATA_CMD_READ_EXT;
|
||||
|
||||
cfis->lba_high_exp = (block >> 40) & 0xff;
|
||||
cfis->lba_mid_exp = (block >> 32) & 0xff;
|
||||
cfis->lba_low_exp = (block >> 24) & 0xff;
|
||||
cfis->lba_high = (block >> 16) & 0xff;
|
||||
cfis->lba_mid = (block >> 8) & 0xff;
|
||||
cfis->lba_low = block & 0xff;
|
||||
cfis->device = ATA_LBA;
|
||||
cfis->sector_count_exp = (blkcnt >> 8) & 0xff;
|
||||
cfis->sector_count = blkcnt & 0xff;
|
||||
|
||||
fsl_sata_exec_cmd(sata, cfis, CMD_ATA, 0, buffer, ATA_SECT_SIZE * blkcnt);
|
||||
return blkcnt;
|
||||
}
|
||||
|
||||
static u32 fsl_sata_rw_ncq_cmd(fsl_sata_t *sata, u32 start, u32 blkcnt,
|
||||
u8 *buffer, int is_write)
|
||||
{
|
||||
struct sata_fis_h2d h2d, *cfis = &h2d;
|
||||
int ncq_channel;
|
||||
u64 block;
|
||||
|
||||
if (sata->lba48 != 1) {
|
||||
printf("execute FPDMA command on non-LBA48 hard disk\n\r");
|
||||
return -1;
|
||||
}
|
||||
|
||||
block = (u64)start;
|
||||
|
||||
memset(cfis, 0, sizeof(struct sata_fis_h2d));
|
||||
|
||||
cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
|
||||
cfis->pm_port_c = 0x80; /* is command */
|
||||
|
||||
cfis->command = (is_write) ? ATA_CMD_FPDMA_WRITE
|
||||
: ATA_CMD_FPDMA_READ;
|
||||
|
||||
cfis->lba_high_exp = (block >> 40) & 0xff;
|
||||
cfis->lba_mid_exp = (block >> 32) & 0xff;
|
||||
cfis->lba_low_exp = (block >> 24) & 0xff;
|
||||
cfis->lba_high = (block >> 16) & 0xff;
|
||||
cfis->lba_mid = (block >> 8) & 0xff;
|
||||
cfis->lba_low = block & 0xff;
|
||||
|
||||
cfis->device = ATA_LBA;
|
||||
cfis->features_exp = (blkcnt >> 8) & 0xff;
|
||||
cfis->features = blkcnt & 0xff;
|
||||
|
||||
if (sata->queue_depth >= SATA_HC_MAX_CMD)
|
||||
ncq_channel = SATA_HC_MAX_CMD - 1;
|
||||
else
|
||||
ncq_channel = sata->queue_depth - 1;
|
||||
|
||||
/* Use the latest queue */
|
||||
fsl_sata_exec_cmd(sata, cfis, CMD_NCQ, ncq_channel, buffer, ATA_SECT_SIZE * blkcnt);
|
||||
return blkcnt;
|
||||
}
|
||||
|
||||
static void fsl_sata_flush_cache_ext(fsl_sata_t *sata)
|
||||
{
|
||||
struct sata_fis_h2d h2d, *cfis = &h2d;
|
||||
|
||||
memset(cfis, 0, sizeof(struct sata_fis_h2d));
|
||||
|
||||
cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
|
||||
cfis->pm_port_c = 0x80; /* is command */
|
||||
cfis->command = ATA_CMD_FLUSH_EXT;
|
||||
|
||||
fsl_sata_exec_cmd(sata, cfis, CMD_ATA, 0, NULL, 0);
|
||||
}
|
||||
|
||||
static void fsl_sata_init_wcache(fsl_sata_t *sata, u16 *id)
|
||||
{
|
||||
if (ata_id_has_wcache(id) && ata_id_wcache_enabled(id))
|
||||
sata->wcache = 1;
|
||||
if (ata_id_has_flush(id))
|
||||
sata->flush = 1;
|
||||
if (ata_id_has_flush_ext(id))
|
||||
sata->flush_ext = 1;
|
||||
}
|
||||
|
||||
static u32 ata_low_level_rw_lba48(fsl_sata_t *sata, u32 blknr, lbaint_t blkcnt,
|
||||
const void *buffer, int is_write)
|
||||
{
|
||||
u32 start, blks;
|
||||
u8 *addr;
|
||||
int max_blks;
|
||||
|
||||
start = blknr;
|
||||
blks = blkcnt;
|
||||
addr = (u8 *)buffer;
|
||||
|
||||
max_blks = ATA_MAX_SECTORS_LBA48;
|
||||
do {
|
||||
if (blks > max_blks) {
|
||||
if (sata->dma_flag != FLAGS_FPDMA)
|
||||
fsl_sata_rw_cmd_ext(sata, start, max_blks, addr,
|
||||
is_write);
|
||||
else
|
||||
fsl_sata_rw_ncq_cmd(sata, start, max_blks, addr,
|
||||
is_write);
|
||||
start += max_blks;
|
||||
blks -= max_blks;
|
||||
addr += ATA_SECT_SIZE * max_blks;
|
||||
} else {
|
||||
if (sata->dma_flag != FLAGS_FPDMA)
|
||||
fsl_sata_rw_cmd_ext(sata, start, blks, addr,
|
||||
is_write);
|
||||
else
|
||||
fsl_sata_rw_ncq_cmd(sata, start, blks, addr,
|
||||
is_write);
|
||||
start += blks;
|
||||
blks = 0;
|
||||
addr += ATA_SECT_SIZE * blks;
|
||||
}
|
||||
} while (blks != 0);
|
||||
|
||||
return blkcnt;
|
||||
}
|
||||
|
||||
static u32 ata_low_level_rw_lba28(fsl_sata_t *sata, u32 blknr, u32 blkcnt,
|
||||
const void *buffer, int is_write)
|
||||
{
|
||||
u32 start, blks;
|
||||
u8 *addr;
|
||||
int max_blks;
|
||||
|
||||
start = blknr;
|
||||
blks = blkcnt;
|
||||
addr = (u8 *)buffer;
|
||||
|
||||
max_blks = ATA_MAX_SECTORS;
|
||||
do {
|
||||
if (blks > max_blks) {
|
||||
fsl_sata_rw_cmd(sata, start, max_blks, addr, is_write);
|
||||
start += max_blks;
|
||||
blks -= max_blks;
|
||||
addr += ATA_SECT_SIZE * max_blks;
|
||||
} else {
|
||||
fsl_sata_rw_cmd(sata, start, blks, addr, is_write);
|
||||
start += blks;
|
||||
blks = 0;
|
||||
addr += ATA_SECT_SIZE * blks;
|
||||
}
|
||||
} while (blks != 0);
|
||||
|
||||
return blkcnt;
|
||||
}
|
||||
|
||||
/*
|
||||
* SATA interface between low level driver and command layer
|
||||
*/
|
||||
#if !CONFIG_IS_ENABLED(BLK)
|
||||
ulong sata_read(int dev, ulong blknr, lbaint_t blkcnt, void *buffer)
|
||||
{
|
||||
fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv;
|
||||
#else
|
||||
static ulong sata_read(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt,
|
||||
void *buffer)
|
||||
{
|
||||
struct fsl_ata_priv *priv = dev_get_platdata(dev);
|
||||
fsl_sata_t *sata = priv->fsl_sata;
|
||||
#endif
|
||||
u32 rc;
|
||||
|
||||
if (sata->lba48)
|
||||
rc = ata_low_level_rw_lba48(sata, blknr, blkcnt, buffer,
|
||||
READ_CMD);
|
||||
else
|
||||
rc = ata_low_level_rw_lba28(sata, blknr, blkcnt, buffer,
|
||||
READ_CMD);
|
||||
return rc;
|
||||
}
|
||||
|
||||
#if !CONFIG_IS_ENABLED(BLK)
|
||||
ulong sata_write(int dev, ulong blknr, lbaint_t blkcnt, const void *buffer)
|
||||
{
|
||||
fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv;
|
||||
#else
|
||||
static ulong sata_write(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt,
|
||||
const void *buffer)
|
||||
{
|
||||
struct fsl_ata_priv *priv = dev_get_platdata(dev);
|
||||
fsl_sata_t *sata = priv->fsl_sata;
|
||||
#endif
|
||||
u32 rc;
|
||||
|
||||
if (sata->lba48) {
|
||||
rc = ata_low_level_rw_lba48(sata, blknr, blkcnt, buffer,
|
||||
WRITE_CMD);
|
||||
if (sata->wcache && sata->flush_ext)
|
||||
fsl_sata_flush_cache_ext(sata);
|
||||
} else {
|
||||
rc = ata_low_level_rw_lba28(sata, blknr, blkcnt, buffer,
|
||||
WRITE_CMD);
|
||||
if (sata->wcache && sata->flush)
|
||||
fsl_sata_flush_cache(sata);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void fsl_sata_identify(fsl_sata_t *sata, u16 *id)
|
||||
{
|
||||
struct sata_fis_h2d h2d, *cfis = &h2d;
|
||||
|
||||
memset(cfis, 0, sizeof(struct sata_fis_h2d));
|
||||
|
||||
cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
|
||||
cfis->pm_port_c = 0x80; /* is command */
|
||||
cfis->command = ATA_CMD_ID_ATA;
|
||||
|
||||
fsl_sata_exec_cmd(sata, cfis, CMD_ATA, 0, (u8 *)id, ATA_ID_WORDS * 2);
|
||||
ata_swap_buf_le16(id, ATA_ID_WORDS);
|
||||
}
|
||||
|
||||
#if !CONFIG_IS_ENABLED(BLK)
|
||||
int scan_sata(int dev)
|
||||
{
|
||||
fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv;
|
||||
#else
|
||||
static int scan_sata(struct udevice *dev)
|
||||
{
|
||||
struct blk_desc *desc = dev_get_uclass_platdata(dev);
|
||||
struct fsl_ata_priv *priv = dev_get_platdata(dev);
|
||||
fsl_sata_t *sata = priv->fsl_sata;
|
||||
#endif
|
||||
|
||||
unsigned char serial[ATA_ID_SERNO_LEN + 1];
|
||||
unsigned char firmware[ATA_ID_FW_REV_LEN + 1];
|
||||
unsigned char product[ATA_ID_PROD_LEN + 1];
|
||||
u16 *id;
|
||||
u64 n_sectors;
|
||||
|
||||
/* if no detected link */
|
||||
if (!sata->link)
|
||||
return -1;
|
||||
|
||||
id = (u16 *)malloc(ATA_ID_WORDS * 2);
|
||||
if (!id) {
|
||||
printf("id malloc failed\n\r");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Identify device to get information */
|
||||
fsl_sata_identify(sata, id);
|
||||
|
||||
/* Serial number */
|
||||
ata_id_c_string(id, serial, ATA_ID_SERNO, sizeof(serial));
|
||||
|
||||
/* Firmware version */
|
||||
ata_id_c_string(id, firmware, ATA_ID_FW_REV, sizeof(firmware));
|
||||
|
||||
/* Product model */
|
||||
ata_id_c_string(id, product, ATA_ID_PROD, sizeof(product));
|
||||
|
||||
/* Totoal sectors */
|
||||
n_sectors = ata_id_n_sectors(id);
|
||||
|
||||
#ifdef CONFIG_LBA48
|
||||
/* Check if support LBA48 */
|
||||
if (ata_id_has_lba48(id)) {
|
||||
sata->lba48 = 1;
|
||||
debug("Device support LBA48\n\r");
|
||||
} else
|
||||
debug("Device supports LBA28\n\r");
|
||||
#endif
|
||||
|
||||
#if !CONFIG_IS_ENABLED(BLK)
|
||||
memcpy(sata_dev_desc[dev].product, serial, sizeof(serial));
|
||||
memcpy(sata_dev_desc[dev].revision, firmware, sizeof(firmware));
|
||||
memcpy(sata_dev_desc[dev].vendor, product, sizeof(product));
|
||||
sata_dev_desc[dev].lba = (u32)n_sectors;
|
||||
#ifdef CONFIG_LBA48
|
||||
sata_dev_desc[dev].lba48 = sata->lba48;
|
||||
#endif
|
||||
#else
|
||||
memcpy(desc->product, serial, sizeof(serial));
|
||||
memcpy(desc->revision, firmware, sizeof(firmware));
|
||||
memcpy(desc->vendor, product, sizeof(product));
|
||||
desc->lba = n_sectors;
|
||||
#ifdef CONFIG_LBA48
|
||||
desc->lba48 = sata->lba48;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Get the NCQ queue depth from device */
|
||||
sata->queue_depth = ata_id_queue_depth(id);
|
||||
|
||||
/* Get the xfer mode from device */
|
||||
fsl_sata_xfer_mode(sata, id);
|
||||
|
||||
/* Get the write cache status from device */
|
||||
fsl_sata_init_wcache(sata, id);
|
||||
|
||||
/* Set the xfer mode to highest speed */
|
||||
fsl_sata_set_features(sata);
|
||||
|
||||
#ifdef DEBUG
|
||||
ata_dump_id(id);
|
||||
#endif
|
||||
free((void *)id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if CONFIG_IS_ENABLED(BLK)
|
||||
static const struct blk_ops sata_fsl_blk_ops = {
|
||||
.read = sata_read,
|
||||
.write = sata_write,
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(sata_fsl_driver) = {
|
||||
.name = "sata_fsl_blk",
|
||||
.id = UCLASS_BLK,
|
||||
.ops = &sata_fsl_blk_ops,
|
||||
.platdata_auto_alloc_size = sizeof(struct fsl_ata_priv),
|
||||
};
|
||||
|
||||
static int fsl_ata_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct fsl_ata_priv *priv = dev_get_priv(dev);
|
||||
|
||||
priv->number = dev_read_u32_default(dev, "sata-number", -1);
|
||||
priv->flag = dev_read_u32_default(dev, "sata-fpdma", -1);
|
||||
priv->offset = dev_read_u32_default(dev, "sata-offset", -1);
|
||||
|
||||
priv->base = dev_read_addr(dev);
|
||||
if (priv->base == FDT_ADDR_T_NONE)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_ata_probe(struct udevice *dev)
|
||||
{
|
||||
struct fsl_ata_priv *blk_priv, *priv;
|
||||
struct udevice *blk;
|
||||
char sata_name[10];
|
||||
int nr_ports;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
priv = dev_get_priv(dev);
|
||||
nr_ports = priv->number;
|
||||
nr_ports = min(nr_ports, CONFIG_SYS_SATA_MAX_DEVICE);
|
||||
|
||||
for (i = 0; i < nr_ports; i++) {
|
||||
snprintf(sata_name, sizeof(sata_name), "fsl_sata%d", i);
|
||||
ret = blk_create_devicef(dev, "sata_fsl_blk", sata_name,
|
||||
IF_TYPE_SATA, -1, 512, 0, &blk);
|
||||
if (ret) {
|
||||
debug("Can't create device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Init SATA port */
|
||||
ret = init_sata(priv, i);
|
||||
if (ret) {
|
||||
debug("%s: Failed to init sata\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
blk_priv = dev_get_platdata(blk);
|
||||
blk_priv->fsl_sata = priv->fsl_sata;
|
||||
/* Scan SATA port */
|
||||
ret = scan_sata(blk);
|
||||
if (ret) {
|
||||
debug("%s: Failed to scan bus\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sata_fsl_scan(struct udevice *dev)
|
||||
{
|
||||
/* Nothing to do here */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ahci_ops sata_fsl_ahci_ops = {
|
||||
.scan = sata_fsl_scan,
|
||||
};
|
||||
|
||||
static const struct udevice_id fsl_ata_ids[] = {
|
||||
{ .compatible = "fsl,pq-sata-v2" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(fsl_ahci) = {
|
||||
.name = "fsl_ahci",
|
||||
.id = UCLASS_AHCI,
|
||||
.of_match = fsl_ata_ids,
|
||||
.ops = &sata_fsl_ahci_ops,
|
||||
.ofdata_to_platdata = fsl_ata_ofdata_to_platdata,
|
||||
.probe = fsl_ata_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct fsl_ata_priv),
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,332 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright (C) 2007-2008 Freescale Semiconductor, Inc.
|
||||
* Copyright 2019 NXP
|
||||
* Author: Dave Liu <daveliu@freescale.com>
|
||||
*/
|
||||
|
||||
#ifndef __FSL_SATA_H__
|
||||
#define __FSL_SATA_H__
|
||||
|
||||
#define SATA_HC_MAX_NUM 4 /* Max host controller numbers */
|
||||
#define SATA_HC_MAX_CMD 16 /* Max command queue depth per host controller */
|
||||
#define SATA_HC_MAX_PORT 16 /* Max port number per host controller */
|
||||
|
||||
/*
|
||||
* SATA Host Controller Registers
|
||||
*/
|
||||
typedef struct fsl_sata_reg {
|
||||
/* SATA command registers */
|
||||
u32 cqr; /* Command queue register */
|
||||
u8 res1[0x4];
|
||||
u32 car; /* Command active register */
|
||||
u8 res2[0x4];
|
||||
u32 ccr; /* Command completed register */
|
||||
u8 res3[0x4];
|
||||
u32 cer; /* Command error register */
|
||||
u8 res4[0x4];
|
||||
u32 der; /* Device error register */
|
||||
u32 chba; /* Command header base address */
|
||||
u32 hstatus; /* Host status register */
|
||||
u32 hcontrol; /* Host control register */
|
||||
u32 cqpmp; /* Port number queue register */
|
||||
u32 sig; /* Signature register */
|
||||
u32 icc; /* Interrupt coalescing control register */
|
||||
u8 res5[0xc4];
|
||||
|
||||
/* SATA supperset registers */
|
||||
u32 sstatus; /* SATA interface status register */
|
||||
u32 serror; /* SATA interface error register */
|
||||
u32 scontrol; /* SATA interface control register */
|
||||
u32 snotification; /* SATA interface notification register */
|
||||
u8 res6[0x30];
|
||||
|
||||
/* SATA control status registers */
|
||||
u32 transcfg; /* Transport layer configuration */
|
||||
u32 transstatus; /* Transport layer status */
|
||||
u32 linkcfg; /* Link layer configuration */
|
||||
u32 linkcfg1; /* Link layer configuration1 */
|
||||
u32 linkcfg2; /* Link layer configuration2 */
|
||||
u32 linkstatus; /* Link layer status */
|
||||
u32 linkstatus1; /* Link layer status1 */
|
||||
u32 phyctrlcfg; /* PHY control configuration */
|
||||
u8 res7[0x2b0];
|
||||
|
||||
/* SATA system control registers */
|
||||
u32 syspr; /* System priority register - big endian */
|
||||
u8 res8[0xbec];
|
||||
} __attribute__ ((packed)) fsl_sata_reg_t;
|
||||
|
||||
/* HStatus register
|
||||
*/
|
||||
#define HSTATUS_ONOFF 0x80000000 /* Online/offline status */
|
||||
#define HSTATUS_FORCE_OFFLINE 0x40000000 /* In process going offline */
|
||||
#define HSTATUS_BIST_ERR 0x20000000
|
||||
|
||||
/* Fatal error */
|
||||
#define HSTATUS_MASTER_ERR 0x00004000
|
||||
#define HSTATUS_DATA_UNDERRUN 0x00002000
|
||||
#define HSTATUS_DATA_OVERRUN 0x00001000
|
||||
#define HSTATUS_CRC_ERR_TX 0x00000800
|
||||
#define HSTATUS_CRC_ERR_RX 0x00000400
|
||||
#define HSTATUS_FIFO_OVERFLOW_TX 0x00000200
|
||||
#define HSTATUS_FIFO_OVERFLOW_RX 0x00000100
|
||||
#define HSTATUS_FATAL_ERR_ALL (HSTATUS_MASTER_ERR | \
|
||||
HSTATUS_DATA_UNDERRUN | \
|
||||
HSTATUS_DATA_OVERRUN | \
|
||||
HSTATUS_CRC_ERR_TX | \
|
||||
HSTATUS_CRC_ERR_RX | \
|
||||
HSTATUS_FIFO_OVERFLOW_TX | \
|
||||
HSTATUS_FIFO_OVERFLOW_RX)
|
||||
/* Interrupt status */
|
||||
#define HSTATUS_FATAL_ERR 0x00000020
|
||||
#define HSTATUS_PHY_RDY 0x00000010
|
||||
#define HSTATUS_SIGNATURE 0x00000008
|
||||
#define HSTATUS_SNOTIFY 0x00000004
|
||||
#define HSTATUS_DEVICE_ERR 0x00000002
|
||||
#define HSTATUS_CMD_COMPLETE 0x00000001
|
||||
|
||||
/* HControl register
|
||||
*/
|
||||
#define HCONTROL_ONOFF 0x80000000 /* Online or offline request */
|
||||
#define HCONTROL_FORCE_OFFLINE 0x40000000 /* Force offline request */
|
||||
#define HCONTROL_ENTERPRISE_EN 0x10000000 /* Enterprise mode enabled */
|
||||
#define HCONTROL_HDR_SNOOP 0x00000400 /* Command header snoop */
|
||||
#define HCONTROL_PMP_ATTACHED 0x00000200 /* Port multiplier attached */
|
||||
|
||||
/* Interrupt enable */
|
||||
#define HCONTROL_FATAL_ERR 0x00000020
|
||||
#define HCONTROL_PHY_RDY 0x00000010
|
||||
#define HCONTROL_SIGNATURE 0x00000008
|
||||
#define HCONTROL_SNOTIFY 0x00000004
|
||||
#define HCONTROL_DEVICE_ERR 0x00000002
|
||||
#define HCONTROL_CMD_COMPLETE 0x00000001
|
||||
|
||||
#define HCONTROL_INT_EN_ALL (HCONTROL_FATAL_ERR | \
|
||||
HCONTROL_PHY_RDY | \
|
||||
HCONTROL_SIGNATURE | \
|
||||
HCONTROL_SNOTIFY | \
|
||||
HCONTROL_DEVICE_ERR | \
|
||||
HCONTROL_CMD_COMPLETE)
|
||||
|
||||
/* SStatus register
|
||||
*/
|
||||
#define SSTATUS_IPM_MASK 0x00000780
|
||||
#define SSTATUS_IPM_NOPRESENT 0x00000000
|
||||
#define SSTATUS_IPM_ACTIVE 0x00000080
|
||||
#define SSTATUS_IPM_PATIAL 0x00000100
|
||||
#define SSTATUS_IPM_SLUMBER 0x00000300
|
||||
|
||||
#define SSTATUS_SPD_MASK 0x000000f0
|
||||
#define SSTATUS_SPD_GEN1 0x00000010
|
||||
#define SSTATUS_SPD_GEN2 0x00000020
|
||||
|
||||
#define SSTATUS_DET_MASK 0x0000000f
|
||||
#define SSTATUS_DET_NODEVICE 0x00000000
|
||||
#define SSTATUS_DET_DISCONNECT 0x00000001
|
||||
#define SSTATUS_DET_CONNECT 0x00000003
|
||||
#define SSTATUS_DET_PHY_OFFLINE 0x00000004
|
||||
|
||||
/* SControl register
|
||||
*/
|
||||
#define SCONTROL_SPM_MASK 0x0000f000
|
||||
#define SCONTROL_SPM_GO_PARTIAL 0x00001000
|
||||
#define SCONTROL_SPM_GO_SLUMBER 0x00002000
|
||||
#define SCONTROL_SPM_GO_ACTIVE 0x00004000
|
||||
|
||||
#define SCONTROL_IPM_MASK 0x00000f00
|
||||
#define SCONTROL_IPM_NO_RESTRICT 0x00000000
|
||||
#define SCONTROL_IPM_PARTIAL 0x00000100
|
||||
#define SCONTROL_IPM_SLUMBER 0x00000200
|
||||
#define SCONTROL_IPM_PART_SLUM 0x00000300
|
||||
|
||||
#define SCONTROL_SPD_MASK 0x000000f0
|
||||
#define SCONTROL_SPD_NO_RESTRICT 0x00000000
|
||||
#define SCONTROL_SPD_GEN1 0x00000010
|
||||
#define SCONTROL_SPD_GEN2 0x00000020
|
||||
|
||||
#define SCONTROL_DET_MASK 0x0000000f
|
||||
#define SCONTROL_DET_HRESET 0x00000001
|
||||
#define SCONTROL_DET_DISABLE 0x00000004
|
||||
|
||||
/* TransCfg register
|
||||
*/
|
||||
#define TRANSCFG_DFIS_SIZE_SHIFT 16
|
||||
#define TRANSCFG_RX_WATER_MARK_MASK 0x0000001f
|
||||
|
||||
/* PhyCtrlCfg register
|
||||
*/
|
||||
#define PHYCTRLCFG_FPRFTI_MASK 0x00000018
|
||||
#define PHYCTRLCFG_LOOPBACK_MASK 0x0000000e
|
||||
|
||||
/*
|
||||
* Command Header Entry
|
||||
*/
|
||||
typedef struct cmd_hdr_entry {
|
||||
__le32 cda; /* Command Descriptor Address,
|
||||
4 bytes aligned */
|
||||
__le32 prde_fis_len; /* Number of PRD entries and FIS length */
|
||||
__le32 ttl; /* Total transfer length */
|
||||
__le32 attribute; /* the attribute of command */
|
||||
} __attribute__ ((packed)) cmd_hdr_entry_t;
|
||||
|
||||
#define SATA_HC_CMD_HDR_ENTRY_SIZE sizeof(struct cmd_hdr_entry)
|
||||
|
||||
/* cda
|
||||
*/
|
||||
#define CMD_HDR_CDA_ALIGN 4
|
||||
|
||||
/* prde_fis_len
|
||||
*/
|
||||
#define CMD_HDR_PRD_ENTRY_SHIFT 16
|
||||
#define CMD_HDR_PRD_ENTRY_MASK 0x003f0000
|
||||
#define CMD_HDR_FIS_LEN_SHIFT 2
|
||||
|
||||
/* attribute
|
||||
*/
|
||||
#define CMD_HDR_ATTR_RES 0x00000800 /* Reserved bit, should be 1 */
|
||||
#define CMD_HDR_ATTR_VBIST 0x00000400 /* Vendor BIST */
|
||||
#define CMD_HDR_ATTR_SNOOP 0x00000200 /* Snoop enable for all descriptor */
|
||||
#define CMD_HDR_ATTR_FPDMA 0x00000100 /* FPDMA queued command */
|
||||
#define CMD_HDR_ATTR_RESET 0x00000080 /* Reset - a SRST or device reset */
|
||||
#define CMD_HDR_ATTR_BIST 0x00000040 /* BIST - require the host to enter BIST mode */
|
||||
#define CMD_HDR_ATTR_ATAPI 0x00000020 /* ATAPI command */
|
||||
#define CMD_HDR_ATTR_TAG 0x0000001f /* TAG mask */
|
||||
|
||||
/* command type
|
||||
*/
|
||||
enum cmd_type {
|
||||
CMD_VENDOR_BIST,
|
||||
CMD_BIST,
|
||||
CMD_RESET, /* SRST or device reset */
|
||||
CMD_ATAPI,
|
||||
CMD_NCQ,
|
||||
CMD_ATA, /* None of all above */
|
||||
};
|
||||
|
||||
/*
|
||||
* Command Header Table
|
||||
*/
|
||||
typedef struct cmd_hdr_tbl {
|
||||
cmd_hdr_entry_t cmd_slot[SATA_HC_MAX_CMD];
|
||||
} __attribute__ ((packed)) cmd_hdr_tbl_t;
|
||||
|
||||
#define SATA_HC_CMD_HDR_TBL_SIZE sizeof(struct cmd_hdr_tbl)
|
||||
#define SATA_HC_CMD_HDR_TBL_ALIGN 4
|
||||
|
||||
/*
|
||||
* PRD entry - Physical Region Descriptor entry
|
||||
*/
|
||||
typedef struct prd_entry {
|
||||
__le32 dba; /* Data base address, 4 bytes aligned */
|
||||
u32 res1;
|
||||
u32 res2;
|
||||
__le32 ext_c_ddc; /* Indirect PRD flags, snoop and data word count */
|
||||
} __attribute__ ((packed)) prd_entry_t;
|
||||
|
||||
#define SATA_HC_CMD_DESC_PRD_SIZE sizeof(struct prd_entry)
|
||||
|
||||
/* dba
|
||||
*/
|
||||
#define PRD_ENTRY_DBA_ALIGN 4
|
||||
|
||||
/* ext_c_ddc
|
||||
*/
|
||||
#define PRD_ENTRY_EXT 0x80000000 /* extension flag */
|
||||
#ifdef CONFIG_FSL_SATA_V2
|
||||
#define PRD_ENTRY_DATA_SNOOP 0x10000000 /* Data snoop enable */
|
||||
#else
|
||||
#define PRD_ENTRY_DATA_SNOOP 0x00400000 /* Data snoop enable */
|
||||
#endif
|
||||
#define PRD_ENTRY_LEN_MASK 0x003fffff /* Data word count */
|
||||
|
||||
#define PRD_ENTRY_MAX_XFER_SZ (PRD_ENTRY_LEN_MASK + 1)
|
||||
|
||||
/*
|
||||
* This SATA host controller supports a max of 16 direct PRD entries, but if use
|
||||
* chained indirect PRD entries, then the contollers supports upto a max of 63
|
||||
* entries including direct and indirect PRD entries.
|
||||
* The PRDT is an array of 63 PRD entries contigiously, but the PRD entries#15
|
||||
* will be setup as an indirect descriptor, pointing to it's next (contigious)
|
||||
* PRD entries#16.
|
||||
*/
|
||||
#define SATA_HC_MAX_PRD 63 /* Max PRD entry numbers per command */
|
||||
#define SATA_HC_MAX_PRD_DIRECT 16 /* Direct PRDT entries */
|
||||
#define SATA_HC_MAX_PRD_USABLE (SATA_HC_MAX_PRD - 1)
|
||||
#define SATA_HC_MAX_XFER_LEN 0x4000000
|
||||
|
||||
/*
|
||||
* PRDT - Physical Region Descriptor Table
|
||||
*/
|
||||
typedef struct prdt {
|
||||
prd_entry_t prdt[SATA_HC_MAX_PRD];
|
||||
} __attribute__ ((packed)) prdt_t;
|
||||
|
||||
/*
|
||||
* Command Descriptor
|
||||
*/
|
||||
#define SATA_HC_CMD_DESC_CFIS_SIZE 32 /* bytes */
|
||||
#define SATA_HC_CMD_DESC_SFIS_SIZE 32 /* bytes */
|
||||
#define SATA_HC_CMD_DESC_ACMD_SIZE 16 /* bytes */
|
||||
#define SATA_HC_CMD_DESC_RES 16 /* bytes */
|
||||
|
||||
typedef struct cmd_desc {
|
||||
u8 cfis[SATA_HC_CMD_DESC_CFIS_SIZE];
|
||||
u8 sfis[SATA_HC_CMD_DESC_SFIS_SIZE];
|
||||
u8 acmd[SATA_HC_CMD_DESC_ACMD_SIZE];
|
||||
u8 res[SATA_HC_CMD_DESC_RES];
|
||||
prd_entry_t prdt[SATA_HC_MAX_PRD];
|
||||
} __attribute__ ((packed)) cmd_desc_t;
|
||||
|
||||
#define SATA_HC_CMD_DESC_SIZE sizeof(struct cmd_desc)
|
||||
#define SATA_HC_CMD_DESC_ALIGN 4
|
||||
|
||||
/*
|
||||
* SATA device driver info
|
||||
*/
|
||||
typedef struct fsl_sata_info {
|
||||
u32 sata_reg_base;
|
||||
u32 flags;
|
||||
} fsl_sata_info_t;
|
||||
|
||||
#define FLAGS_DMA 0x00000000
|
||||
#define FLAGS_FPDMA 0x00000001
|
||||
|
||||
/*
|
||||
* SATA device driver struct
|
||||
*/
|
||||
typedef struct fsl_sata {
|
||||
char name[12];
|
||||
fsl_sata_reg_t *reg_base; /* the base address of controller register */
|
||||
void *cmd_hdr_tbl_offset; /* alloc address of command header table */
|
||||
cmd_hdr_tbl_t *cmd_hdr; /* aligned address of command header table */
|
||||
void *cmd_desc_offset; /* alloc address of command descriptor */
|
||||
cmd_desc_t *cmd_desc; /* aligned address of command descriptor */
|
||||
int link; /* PHY link status */
|
||||
/* device attribute */
|
||||
int ata_device_type; /* device type */
|
||||
int lba48;
|
||||
int queue_depth; /* Max NCQ queue depth */
|
||||
u16 pio;
|
||||
u16 mwdma;
|
||||
u16 udma;
|
||||
int wcache;
|
||||
int flush;
|
||||
int flush_ext;
|
||||
u32 dma_flag;
|
||||
} fsl_sata_t;
|
||||
|
||||
#define READ_CMD 0
|
||||
#define WRITE_CMD 1
|
||||
|
||||
#if CONFIG_IS_ENABLED(BLK)
|
||||
struct fsl_ata_priv {
|
||||
u32 base;
|
||||
u32 flag;
|
||||
u32 number;
|
||||
u32 offset;
|
||||
fsl_sata_t *fsl_sata;
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* __FSL_SATA_H__ */
|
||||
@@ -0,0 +1,143 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2008 Freescale Semiconductor, Inc.
|
||||
* Dave Liu <daveliu@freescale.com>
|
||||
* port from the libata of linux kernel
|
||||
*/
|
||||
|
||||
#include <libata.h>
|
||||
|
||||
u64 ata_id_n_sectors(u16 *id)
|
||||
{
|
||||
if (ata_id_has_lba(id)) {
|
||||
if (ata_id_has_lba48(id))
|
||||
return ata_id_u64(id, ATA_ID_LBA48_SECTORS);
|
||||
else
|
||||
return ata_id_u32(id, ATA_ID_LBA_SECTORS);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
u32 ata_dev_classify(u32 sig)
|
||||
{
|
||||
u8 lbam, lbah;
|
||||
|
||||
lbam = (sig >> 16) & 0xff;
|
||||
lbah = (sig >> 24) & 0xff;
|
||||
|
||||
if (((lbam == 0) && (lbah == 0)) ||
|
||||
((lbam == 0x3c) && (lbah == 0xc3)))
|
||||
return ATA_DEV_ATA;
|
||||
|
||||
if ((lbam == 0x14) && (lbah == 0xeb))
|
||||
return ATA_DEV_ATAPI;
|
||||
|
||||
if ((lbam == 0x69) && (lbah == 0x96))
|
||||
return ATA_DEV_PMP;
|
||||
|
||||
return ATA_DEV_UNKNOWN;
|
||||
}
|
||||
|
||||
static void ata_id_string(const u16 *id, unsigned char *s,
|
||||
unsigned int ofs, unsigned int len)
|
||||
{
|
||||
unsigned int c;
|
||||
|
||||
while (len > 0) {
|
||||
c = id[ofs] >> 8;
|
||||
*s = c;
|
||||
s++;
|
||||
|
||||
c = id[ofs] & 0xff;
|
||||
*s = c;
|
||||
s++;
|
||||
|
||||
ofs++;
|
||||
len -= 2;
|
||||
}
|
||||
}
|
||||
|
||||
void ata_id_c_string(const u16 *id, unsigned char *s,
|
||||
unsigned int ofs, unsigned int len)
|
||||
{
|
||||
unsigned char *p;
|
||||
|
||||
ata_id_string(id, s, ofs, len - 1);
|
||||
|
||||
p = s + strnlen((char *)s, len - 1);
|
||||
while (p > s && p[-1] == ' ')
|
||||
p--;
|
||||
*p = '\0';
|
||||
}
|
||||
|
||||
void ata_dump_id(u16 *id)
|
||||
{
|
||||
unsigned char serial[ATA_ID_SERNO_LEN + 1];
|
||||
unsigned char firmware[ATA_ID_FW_REV_LEN + 1];
|
||||
unsigned char product[ATA_ID_PROD_LEN + 1];
|
||||
u64 n_sectors;
|
||||
|
||||
/* Serial number */
|
||||
ata_id_c_string(id, serial, ATA_ID_SERNO, sizeof(serial));
|
||||
printf("S/N: %s\n\r", serial);
|
||||
|
||||
/* Firmware version */
|
||||
ata_id_c_string(id, firmware, ATA_ID_FW_REV, sizeof(firmware));
|
||||
printf("Firmware version: %s\n\r", firmware);
|
||||
|
||||
/* Product model */
|
||||
ata_id_c_string(id, product, ATA_ID_PROD, sizeof(product));
|
||||
printf("Product model number: %s\n\r", product);
|
||||
|
||||
/* Total sectors of device */
|
||||
n_sectors = ata_id_n_sectors(id);
|
||||
printf("Capablity: %lld sectors\n\r", n_sectors);
|
||||
|
||||
printf ("id[49]: capabilities = 0x%04x\n"
|
||||
"id[53]: field valid = 0x%04x\n"
|
||||
"id[63]: mwdma = 0x%04x\n"
|
||||
"id[64]: pio = 0x%04x\n"
|
||||
"id[75]: queue depth = 0x%04x\n",
|
||||
id[49],
|
||||
id[53],
|
||||
id[63],
|
||||
id[64],
|
||||
id[75]);
|
||||
|
||||
printf ("id[76]: sata capablity = 0x%04x\n"
|
||||
"id[78]: sata features supported = 0x%04x\n"
|
||||
"id[79]: sata features enable = 0x%04x\n",
|
||||
id[76],
|
||||
id[78],
|
||||
id[79]);
|
||||
|
||||
printf ("id[80]: major version = 0x%04x\n"
|
||||
"id[81]: minor version = 0x%04x\n"
|
||||
"id[82]: command set supported 1 = 0x%04x\n"
|
||||
"id[83]: command set supported 2 = 0x%04x\n"
|
||||
"id[84]: command set extension = 0x%04x\n",
|
||||
id[80],
|
||||
id[81],
|
||||
id[82],
|
||||
id[83],
|
||||
id[84]);
|
||||
printf ("id[85]: command set enable 1 = 0x%04x\n"
|
||||
"id[86]: command set enable 2 = 0x%04x\n"
|
||||
"id[87]: command set default = 0x%04x\n"
|
||||
"id[88]: udma = 0x%04x\n"
|
||||
"id[93]: hardware reset result = 0x%04x\n",
|
||||
id[85],
|
||||
id[86],
|
||||
id[87],
|
||||
id[88],
|
||||
id[93]);
|
||||
}
|
||||
|
||||
void ata_swap_buf_le16(u16 *buf, unsigned int buf_words)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < buf_words; i++)
|
||||
buf[i] = le16_to_cpu(buf[i]);
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2010 Albert ARIBAUD <albert.u.boot@aribaud.net>
|
||||
*
|
||||
* Written-by: Albert ARIBAUD <albert.u.boot@aribaud.net>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#if defined(CONFIG_ORION5X)
|
||||
#include <asm/arch/orion5x.h>
|
||||
#elif defined(CONFIG_KIRKWOOD)
|
||||
#include <asm/arch/soc.h>
|
||||
#elif defined(CONFIG_ARCH_MVEBU)
|
||||
#include <linux/mbus.h>
|
||||
#endif
|
||||
|
||||
/* SATA port registers */
|
||||
struct mvsata_port_registers {
|
||||
u32 reserved0[10];
|
||||
u32 edma_cmd;
|
||||
u32 reserved1[181];
|
||||
/* offset 0x300 : ATA Interface registers */
|
||||
u32 sstatus;
|
||||
u32 serror;
|
||||
u32 scontrol;
|
||||
u32 ltmode;
|
||||
u32 phymode3;
|
||||
u32 phymode4;
|
||||
u32 reserved2[5];
|
||||
u32 phymode1;
|
||||
u32 phymode2;
|
||||
u32 bist_cr;
|
||||
u32 bist_dw1;
|
||||
u32 bist_dw2;
|
||||
u32 serrorintrmask;
|
||||
};
|
||||
|
||||
/*
|
||||
* Sanity checks:
|
||||
* - to compile at all, we need CONFIG_SYS_ATA_BASE_ADDR.
|
||||
* - for ide_preinit to make sense, we need at least one of
|
||||
* CONFIG_SYS_ATA_IDE0_OFFSET or CONFIG_SYS_ATA_IDE1_OFFSET;
|
||||
* - for ide_preinit to be called, we need CONFIG_IDE_PREINIT.
|
||||
* Fail with an explanation message if these conditions are not met.
|
||||
* This is particularly important for CONFIG_IDE_PREINIT, because
|
||||
* its lack would not cause a build error.
|
||||
*/
|
||||
|
||||
#if !defined(CONFIG_SYS_ATA_BASE_ADDR)
|
||||
#error CONFIG_SYS_ATA_BASE_ADDR must be defined
|
||||
#elif !defined(CONFIG_SYS_ATA_IDE0_OFFSET) \
|
||||
&& !defined(CONFIG_SYS_ATA_IDE1_OFFSET)
|
||||
#error CONFIG_SYS_ATA_IDE0_OFFSET or CONFIG_SYS_ATA_IDE1_OFFSET \
|
||||
must be defined
|
||||
#elif !defined(CONFIG_IDE_PREINIT)
|
||||
#error CONFIG_IDE_PREINIT must be defined
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Masks and values for SControl DETection and Interface Power Management,
|
||||
* and for SStatus DETection.
|
||||
*/
|
||||
|
||||
#define MVSATA_EDMA_CMD_ATA_RST 0x00000004
|
||||
#define MVSATA_SCONTROL_DET_MASK 0x0000000F
|
||||
#define MVSATA_SCONTROL_DET_NONE 0x00000000
|
||||
#define MVSATA_SCONTROL_DET_INIT 0x00000001
|
||||
#define MVSATA_SCONTROL_IPM_MASK 0x00000F00
|
||||
#define MVSATA_SCONTROL_IPM_NO_LP_ALLOWED 0x00000300
|
||||
#define MVSATA_SCONTROL_MASK \
|
||||
(MVSATA_SCONTROL_DET_MASK|MVSATA_SCONTROL_IPM_MASK)
|
||||
#define MVSATA_PORT_INIT \
|
||||
(MVSATA_SCONTROL_DET_INIT|MVSATA_SCONTROL_IPM_NO_LP_ALLOWED)
|
||||
#define MVSATA_PORT_USE \
|
||||
(MVSATA_SCONTROL_DET_NONE|MVSATA_SCONTROL_IPM_NO_LP_ALLOWED)
|
||||
#define MVSATA_SSTATUS_DET_MASK 0x0000000F
|
||||
#define MVSATA_SSTATUS_DET_DEVCOMM 0x00000003
|
||||
|
||||
/*
|
||||
* Status codes to return to client callers. Currently, callers ignore
|
||||
* exact value and only care for zero or nonzero, so no need to make this
|
||||
* public, it is only #define'd for clarity.
|
||||
* If/when standard negative codes are implemented in U-Boot, then these
|
||||
* #defines should be moved to, or replaced by ones from, the common list
|
||||
* of status codes.
|
||||
*/
|
||||
|
||||
#define MVSATA_STATUS_OK 0
|
||||
#define MVSATA_STATUS_TIMEOUT -1
|
||||
|
||||
/*
|
||||
* Registers for SATA MBUS memory windows
|
||||
*/
|
||||
|
||||
#define MVSATA_WIN_CONTROL(w) (MVEBU_AXP_SATA_BASE + 0x30 + ((w) << 4))
|
||||
#define MVSATA_WIN_BASE(w) (MVEBU_AXP_SATA_BASE + 0x34 + ((w) << 4))
|
||||
|
||||
/*
|
||||
* Initialize SATA memory windows for Armada XP
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_ARCH_MVEBU
|
||||
static void mvsata_ide_conf_mbus_windows(void)
|
||||
{
|
||||
const struct mbus_dram_target_info *dram;
|
||||
int i;
|
||||
|
||||
dram = mvebu_mbus_dram_info();
|
||||
|
||||
/* Disable windows, Set Size/Base to 0 */
|
||||
for (i = 0; i < 4; i++) {
|
||||
writel(0, MVSATA_WIN_CONTROL(i));
|
||||
writel(0, MVSATA_WIN_BASE(i));
|
||||
}
|
||||
|
||||
for (i = 0; i < dram->num_cs; i++) {
|
||||
const struct mbus_dram_window *cs = dram->cs + i;
|
||||
writel(((cs->size - 1) & 0xffff0000) | (cs->mbus_attr << 8) |
|
||||
(dram->mbus_dram_target_id << 4) | 1,
|
||||
MVSATA_WIN_CONTROL(i));
|
||||
writel(cs->base & 0xffff0000, MVSATA_WIN_BASE(i));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Initialize one MVSATAHC port: set SControl's IPM to "always active"
|
||||
* and DET to "reset", then wait for SStatus's DET to become "device and
|
||||
* comm ok" (or time out after 50 us if no device), then set SControl's
|
||||
* DET back to "no action".
|
||||
*/
|
||||
|
||||
static int mvsata_ide_initialize_port(struct mvsata_port_registers *port)
|
||||
{
|
||||
u32 control;
|
||||
u32 status;
|
||||
u32 timeleft = 10000; /* wait at most 10 ms for SATA reset to complete */
|
||||
|
||||
/* Hard reset */
|
||||
writel(MVSATA_EDMA_CMD_ATA_RST, &port->edma_cmd);
|
||||
udelay(25); /* taken from original marvell port */
|
||||
writel(0, &port->edma_cmd);
|
||||
|
||||
/* Set control IPM to 3 (no low power) and DET to 1 (initialize) */
|
||||
control = readl(&port->scontrol);
|
||||
control = (control & ~MVSATA_SCONTROL_MASK) | MVSATA_PORT_INIT;
|
||||
writel(control, &port->scontrol);
|
||||
/* Toggle control DET back to 0 (normal operation) */
|
||||
control = (control & ~MVSATA_SCONTROL_MASK) | MVSATA_PORT_USE;
|
||||
writel(control, &port->scontrol);
|
||||
/* wait for status DET to become 3 (device and communication OK) */
|
||||
while (--timeleft) {
|
||||
status = readl(&port->sstatus) & MVSATA_SSTATUS_DET_MASK;
|
||||
if (status == MVSATA_SSTATUS_DET_DEVCOMM)
|
||||
break;
|
||||
udelay(1);
|
||||
}
|
||||
/* return success or time-out error depending on time left */
|
||||
if (!timeleft)
|
||||
return MVSATA_STATUS_TIMEOUT;
|
||||
return MVSATA_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* ide_preinit() will be called by ide_init in cmd_ide.c and will
|
||||
* reset the MVSTATHC ports needed by the board.
|
||||
*/
|
||||
|
||||
int ide_preinit(void)
|
||||
{
|
||||
int ret = MVSATA_STATUS_TIMEOUT;
|
||||
int status;
|
||||
|
||||
#ifdef CONFIG_ARCH_MVEBU
|
||||
mvsata_ide_conf_mbus_windows();
|
||||
#endif
|
||||
|
||||
/* Enable ATA port 0 (could be SATA port 0 or 1) if declared */
|
||||
#if defined(CONFIG_SYS_ATA_IDE0_OFFSET)
|
||||
status = mvsata_ide_initialize_port(
|
||||
(struct mvsata_port_registers *)
|
||||
(CONFIG_SYS_ATA_BASE_ADDR + CONFIG_SYS_ATA_IDE0_OFFSET));
|
||||
if (status == MVSATA_STATUS_OK)
|
||||
ret = MVSATA_STATUS_OK;
|
||||
#endif
|
||||
/* Enable ATA port 1 (could be SATA port 0 or 1) if declared */
|
||||
#if defined(CONFIG_SYS_ATA_IDE1_OFFSET)
|
||||
status = mvsata_ide_initialize_port(
|
||||
(struct mvsata_port_registers *)
|
||||
(CONFIG_SYS_ATA_BASE_ADDR + CONFIG_SYS_ATA_IDE1_OFFSET));
|
||||
if (status == MVSATA_STATUS_OK)
|
||||
ret = MVSATA_STATUS_OK;
|
||||
#endif
|
||||
/* Return success if at least one port initialization succeeded */
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2000-2005, DENX Software Engineering
|
||||
* Wolfgang Denk <wd@denx.de>
|
||||
* Copyright (C) Procsys. All rights reserved.
|
||||
* Mushtaq Khan <mushtaq_k@procsys.com>
|
||||
* <mushtaqk_921@yahoo.co.in>
|
||||
* Copyright (C) 2008 Freescale Semiconductor, Inc.
|
||||
* Dave Liu <daveliu@freescale.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <ahci.h>
|
||||
#include <dm.h>
|
||||
#include <sata.h>
|
||||
|
||||
#ifndef CONFIG_AHCI
|
||||
struct blk_desc sata_dev_desc[CONFIG_SYS_SATA_MAX_DEVICE];
|
||||
#endif
|
||||
|
||||
int sata_reset(struct udevice *dev)
|
||||
{
|
||||
struct ahci_ops *ops = ahci_get_ops(dev);
|
||||
|
||||
if (!ops->reset)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->reset(dev);
|
||||
}
|
||||
|
||||
int sata_dm_port_status(struct udevice *dev, int port)
|
||||
{
|
||||
struct ahci_ops *ops = ahci_get_ops(dev);
|
||||
|
||||
if (!ops->port_status)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->port_status(dev, port);
|
||||
}
|
||||
|
||||
int sata_scan(struct udevice *dev)
|
||||
{
|
||||
struct ahci_ops *ops = ahci_get_ops(dev);
|
||||
|
||||
if (!ops->scan)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->scan(dev);
|
||||
}
|
||||
|
||||
#ifndef CONFIG_AHCI
|
||||
#ifdef CONFIG_PARTITIONS
|
||||
struct blk_desc *sata_get_dev(int dev)
|
||||
{
|
||||
return (dev < CONFIG_SYS_SATA_MAX_DEVICE) ? &sata_dev_desc[dev] : NULL;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BLK
|
||||
static unsigned long sata_bread(struct udevice *dev, lbaint_t start,
|
||||
lbaint_t blkcnt, void *dst)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static unsigned long sata_bwrite(struct udevice *dev, lbaint_t start,
|
||||
lbaint_t blkcnt, const void *buffer)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
#else
|
||||
static unsigned long sata_bread(struct blk_desc *block_dev, lbaint_t start,
|
||||
lbaint_t blkcnt, void *dst)
|
||||
{
|
||||
return sata_read(block_dev->devnum, start, blkcnt, dst);
|
||||
}
|
||||
|
||||
static unsigned long sata_bwrite(struct blk_desc *block_dev, lbaint_t start,
|
||||
lbaint_t blkcnt, const void *buffer)
|
||||
{
|
||||
return sata_write(block_dev->devnum, start, blkcnt, buffer);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_AHCI
|
||||
int __sata_initialize(void)
|
||||
{
|
||||
int rc, ret = -1;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < CONFIG_SYS_SATA_MAX_DEVICE; i++) {
|
||||
memset(&sata_dev_desc[i], 0, sizeof(struct blk_desc));
|
||||
sata_dev_desc[i].if_type = IF_TYPE_SATA;
|
||||
sata_dev_desc[i].devnum = i;
|
||||
sata_dev_desc[i].part_type = PART_TYPE_UNKNOWN;
|
||||
sata_dev_desc[i].type = DEV_TYPE_HARDDISK;
|
||||
sata_dev_desc[i].lba = 0;
|
||||
sata_dev_desc[i].blksz = 512;
|
||||
sata_dev_desc[i].log2blksz = LOG2(sata_dev_desc[i].blksz);
|
||||
#ifndef CONFIG_BLK
|
||||
sata_dev_desc[i].block_read = sata_bread;
|
||||
sata_dev_desc[i].block_write = sata_bwrite;
|
||||
#endif
|
||||
rc = init_sata(i);
|
||||
if (!rc) {
|
||||
rc = scan_sata(i);
|
||||
if (!rc && sata_dev_desc[i].lba > 0 &&
|
||||
sata_dev_desc[i].blksz > 0) {
|
||||
part_init(&sata_dev_desc[i]);
|
||||
ret = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
int sata_initialize(void) __attribute__((weak, alias("__sata_initialize")));
|
||||
|
||||
__weak int __sata_stop(void)
|
||||
{
|
||||
int i, err = 0;
|
||||
|
||||
for (i = 0; i < CONFIG_SYS_SATA_MAX_DEVICE; i++)
|
||||
err |= reset_sata(i);
|
||||
|
||||
if (err)
|
||||
printf("Could not reset some SATA devices\n");
|
||||
|
||||
return err;
|
||||
}
|
||||
int sata_stop(void) __attribute__((weak, alias("__sata_stop")));
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BLK
|
||||
static const struct blk_ops sata_blk_ops = {
|
||||
.read = sata_bread,
|
||||
.write = sata_bwrite,
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(sata_blk) = {
|
||||
.name = "sata_blk",
|
||||
.id = UCLASS_BLK,
|
||||
.ops = &sata_blk_ops,
|
||||
};
|
||||
#else
|
||||
U_BOOT_LEGACY_BLK(sata) = {
|
||||
.if_typename = "sata",
|
||||
.if_type = IF_TYPE_SATA,
|
||||
.max_devs = CONFIG_SYS_SATA_MAX_DEVICE,
|
||||
.desc = sata_dev_desc,
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,238 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2015 - 2016 Xilinx, Inc.
|
||||
* Michal Simek <michal.simek@xilinx.com>
|
||||
*/
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <ahci.h>
|
||||
#include <scsi.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/ioport.h>
|
||||
|
||||
/* Vendor Specific Register Offsets */
|
||||
#define AHCI_VEND_PCFG 0xA4
|
||||
#define AHCI_VEND_PPCFG 0xA8
|
||||
#define AHCI_VEND_PP2C 0xAC
|
||||
#define AHCI_VEND_PP3C 0xB0
|
||||
#define AHCI_VEND_PP4C 0xB4
|
||||
#define AHCI_VEND_PP5C 0xB8
|
||||
#define AHCI_VEND_AXICC 0xBc
|
||||
#define AHCI_VEND_PAXIC 0xC0
|
||||
#define AHCI_VEND_PTC 0xC8
|
||||
|
||||
/* Vendor Specific Register bit definitions */
|
||||
#define PAXIC_ADBW_BW64 0x1
|
||||
#define PAXIC_MAWIDD (1 << 8)
|
||||
#define PAXIC_MARIDD (1 << 16)
|
||||
#define PAXIC_OTL (0x4 << 20)
|
||||
|
||||
#define PCFG_TPSS_VAL (0x32 << 16)
|
||||
#define PCFG_TPRS_VAL (0x2 << 12)
|
||||
#define PCFG_PAD_VAL 0x2
|
||||
|
||||
#define PPCFG_TTA 0x1FFFE
|
||||
#define PPCFG_PSSO_EN (1 << 28)
|
||||
#define PPCFG_PSS_EN (1 << 29)
|
||||
#define PPCFG_ESDF_EN (1 << 31)
|
||||
|
||||
#define PP2C_CIBGMN 0x0F
|
||||
#define PP2C_CIBGMX (0x25 << 8)
|
||||
#define PP2C_CIBGN (0x18 << 16)
|
||||
#define PP2C_CINMP (0x29 << 24)
|
||||
|
||||
#define PP3C_CWBGMN 0x04
|
||||
#define PP3C_CWBGMX (0x0B << 8)
|
||||
#define PP3C_CWBGN (0x08 << 16)
|
||||
#define PP3C_CWNMP (0x0F << 24)
|
||||
|
||||
#define PP4C_BMX 0x0a
|
||||
#define PP4C_BNM (0x08 << 8)
|
||||
#define PP4C_SFD (0x4a << 16)
|
||||
#define PP4C_PTST (0x06 << 24)
|
||||
|
||||
#define PP5C_RIT 0x60216
|
||||
#define PP5C_RCT (0x7f0 << 20)
|
||||
|
||||
#define PTC_RX_WM_VAL 0x40
|
||||
#define PTC_RSVD (1 << 27)
|
||||
|
||||
#define PORT0_BASE 0x100
|
||||
#define PORT1_BASE 0x180
|
||||
|
||||
/* Port Control Register Bit Definitions */
|
||||
#define PORT_SCTL_SPD_GEN3 (0x3 << 4)
|
||||
#define PORT_SCTL_SPD_GEN2 (0x2 << 4)
|
||||
#define PORT_SCTL_SPD_GEN1 (0x1 << 4)
|
||||
#define PORT_SCTL_IPM (0x3 << 8)
|
||||
|
||||
#define PORT_BASE 0x100
|
||||
#define PORT_OFFSET 0x80
|
||||
#define NR_PORTS 2
|
||||
#define DRV_NAME "ahci-ceva"
|
||||
#define CEVA_FLAG_BROKEN_GEN2 1
|
||||
|
||||
/* flag bit definition */
|
||||
#define FLAG_COHERENT 1
|
||||
|
||||
/* register config value */
|
||||
#define CEVA_PHY1_CFG 0xa003fffe
|
||||
#define CEVA_PHY2_CFG 0x28184d1f
|
||||
#define CEVA_PHY3_CFG 0x0e081509
|
||||
#define CEVA_TRANS_CFG 0x08000029
|
||||
#define CEVA_AXICC_CFG 0x3fffffff
|
||||
|
||||
/* for ls1021a */
|
||||
#define LS1021_AHCI_VEND_AXICC 0xC0
|
||||
#define LS1021_CEVA_PHY2_CFG 0x28183414
|
||||
#define LS1021_CEVA_PHY3_CFG 0x0e080e06
|
||||
#define LS1021_CEVA_PHY4_CFG 0x064a080b
|
||||
#define LS1021_CEVA_PHY5_CFG 0x2aa86470
|
||||
|
||||
/* ecc val pair */
|
||||
#define ECC_DIS_VAL_CH1 0x00020000
|
||||
#define ECC_DIS_VAL_CH2 0x80000000
|
||||
#define ECC_DIS_VAL_CH3 0x40000000
|
||||
|
||||
enum ceva_soc {
|
||||
CEVA_1V84,
|
||||
CEVA_LS1012A,
|
||||
CEVA_LS1021A,
|
||||
CEVA_LS1028A,
|
||||
CEVA_LS1043A,
|
||||
CEVA_LS1046A,
|
||||
CEVA_LS1088A,
|
||||
CEVA_LS2080A,
|
||||
};
|
||||
|
||||
struct ceva_sata_priv {
|
||||
ulong base;
|
||||
ulong ecc_base;
|
||||
enum ceva_soc soc;
|
||||
ulong flag;
|
||||
};
|
||||
|
||||
static int ceva_init_sata(struct ceva_sata_priv *priv)
|
||||
{
|
||||
ulong ecc_addr = priv->ecc_base;
|
||||
ulong base = priv->base;
|
||||
ulong tmp;
|
||||
|
||||
switch (priv->soc) {
|
||||
case CEVA_1V84:
|
||||
tmp = PAXIC_ADBW_BW64 | PAXIC_MAWIDD | PAXIC_MARIDD | PAXIC_OTL;
|
||||
writel(tmp, base + AHCI_VEND_PAXIC);
|
||||
tmp = PCFG_TPSS_VAL | PCFG_TPRS_VAL | PCFG_PAD_VAL;
|
||||
writel(tmp, base + AHCI_VEND_PCFG);
|
||||
tmp = PPCFG_TTA | PPCFG_PSS_EN | PPCFG_ESDF_EN;
|
||||
writel(tmp, base + AHCI_VEND_PPCFG);
|
||||
tmp = PTC_RX_WM_VAL | PTC_RSVD;
|
||||
writel(tmp, base + AHCI_VEND_PTC);
|
||||
break;
|
||||
|
||||
case CEVA_LS1021A:
|
||||
if (!ecc_addr)
|
||||
return -EINVAL;
|
||||
writel(ECC_DIS_VAL_CH1, ecc_addr);
|
||||
writel(CEVA_PHY1_CFG, base + AHCI_VEND_PPCFG);
|
||||
writel(LS1021_CEVA_PHY2_CFG, base + AHCI_VEND_PP2C);
|
||||
writel(LS1021_CEVA_PHY3_CFG, base + AHCI_VEND_PP3C);
|
||||
writel(LS1021_CEVA_PHY4_CFG, base + AHCI_VEND_PP4C);
|
||||
writel(LS1021_CEVA_PHY5_CFG, base + AHCI_VEND_PP5C);
|
||||
writel(CEVA_TRANS_CFG, base + AHCI_VEND_PTC);
|
||||
break;
|
||||
|
||||
case CEVA_LS1012A:
|
||||
case CEVA_LS1043A:
|
||||
case CEVA_LS1046A:
|
||||
if (!ecc_addr)
|
||||
return -EINVAL;
|
||||
writel(ECC_DIS_VAL_CH2, ecc_addr);
|
||||
/* fallthrough */
|
||||
case CEVA_LS2080A:
|
||||
writel(CEVA_PHY1_CFG, base + AHCI_VEND_PPCFG);
|
||||
writel(CEVA_TRANS_CFG, base + AHCI_VEND_PTC);
|
||||
break;
|
||||
|
||||
case CEVA_LS1028A:
|
||||
case CEVA_LS1088A:
|
||||
if (!ecc_addr)
|
||||
return -EINVAL;
|
||||
writel(ECC_DIS_VAL_CH3, ecc_addr);
|
||||
writel(CEVA_PHY1_CFG, base + AHCI_VEND_PPCFG);
|
||||
writel(CEVA_TRANS_CFG, base + AHCI_VEND_PTC);
|
||||
break;
|
||||
}
|
||||
|
||||
if (priv->flag & FLAG_COHERENT)
|
||||
writel(CEVA_AXICC_CFG, base + AHCI_VEND_AXICC);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sata_ceva_bind(struct udevice *dev)
|
||||
{
|
||||
struct udevice *scsi_dev;
|
||||
|
||||
return ahci_bind_scsi(dev, &scsi_dev);
|
||||
}
|
||||
|
||||
static int sata_ceva_probe(struct udevice *dev)
|
||||
{
|
||||
struct ceva_sata_priv *priv = dev_get_priv(dev);
|
||||
|
||||
ceva_init_sata(priv);
|
||||
|
||||
return ahci_probe_scsi(dev, priv->base);
|
||||
}
|
||||
|
||||
static const struct udevice_id sata_ceva_ids[] = {
|
||||
{ .compatible = "ceva,ahci-1v84", .data = CEVA_1V84 },
|
||||
{ .compatible = "fsl,ls1012a-ahci", .data = CEVA_LS1012A },
|
||||
{ .compatible = "fsl,ls1021a-ahci", .data = CEVA_LS1021A },
|
||||
{ .compatible = "fsl,ls1028a-ahci", .data = CEVA_LS1028A },
|
||||
{ .compatible = "fsl,ls1043a-ahci", .data = CEVA_LS1043A },
|
||||
{ .compatible = "fsl,ls1046a-ahci", .data = CEVA_LS1046A },
|
||||
{ .compatible = "fsl,ls1088a-ahci", .data = CEVA_LS1088A },
|
||||
{ .compatible = "fsl,ls2080a-ahci", .data = CEVA_LS2080A },
|
||||
{ }
|
||||
};
|
||||
|
||||
static int sata_ceva_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct ceva_sata_priv *priv = dev_get_priv(dev);
|
||||
struct resource res_regs;
|
||||
int ret;
|
||||
|
||||
if (dev_read_bool(dev, "dma-coherent"))
|
||||
priv->flag |= FLAG_COHERENT;
|
||||
|
||||
priv->base = dev_read_addr(dev);
|
||||
if (priv->base == FDT_ADDR_T_NONE)
|
||||
return -EINVAL;
|
||||
|
||||
ret = dev_read_resource_byname(dev, "ecc-addr", &res_regs);
|
||||
if (ret)
|
||||
priv->ecc_base = 0;
|
||||
else
|
||||
priv->ecc_base = res_regs.start;
|
||||
|
||||
priv->soc = dev_get_driver_data(dev);
|
||||
|
||||
debug("ccsr-sata-base %lx\t ecc-base %lx\n",
|
||||
priv->base,
|
||||
priv->ecc_base);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
U_BOOT_DRIVER(ceva_host_blk) = {
|
||||
.name = "ceva_sata",
|
||||
.id = UCLASS_AHCI,
|
||||
.of_match = sata_ceva_ids,
|
||||
.bind = sata_ceva_bind,
|
||||
.ops = &scsi_ops,
|
||||
.priv_auto_alloc_size = sizeof(struct ceva_sata_priv),
|
||||
.probe = sata_ceva_probe,
|
||||
.ofdata_to_platdata = sata_ceva_ofdata_to_platdata,
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,32 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2015 Google, Inc
|
||||
* Written by Simon Glass <sjg@chromium.org>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
|
||||
int init_sata(int dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int reset_sata(int dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int scan_sata(int dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
ulong sata_read(int dev, ulong blknr, lbaint_t blkcnt, void *buffer)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
ulong sata_write(int dev, ulong blknr, lbaint_t blkcnt, const void *buffer)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,864 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2011 Freescale Semiconductor, Inc.
|
||||
* Copyright 2019 NXP
|
||||
* Author: Tang Yuantian <b29983@freescale.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <cpu_func.h>
|
||||
#include <pci.h>
|
||||
#include <command.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include <malloc.h>
|
||||
#include <asm/io.h>
|
||||
#include <fis.h>
|
||||
#include <sata.h>
|
||||
#include <libata.h>
|
||||
#include <sata.h>
|
||||
|
||||
#if CONFIG_IS_ENABLED(BLK)
|
||||
#include <dm.h>
|
||||
#include <blk.h>
|
||||
#endif
|
||||
|
||||
#include "sata_sil.h"
|
||||
|
||||
#define virt_to_bus(devno, v) pci_virt_to_mem(devno, (void *) (v))
|
||||
|
||||
/* just compatible ahci_ops */
|
||||
struct sil_ops {
|
||||
int *rev0;
|
||||
int *rev1;
|
||||
int (*scan)(struct udevice *dev);
|
||||
};
|
||||
|
||||
static struct sata_info sata_info;
|
||||
|
||||
static struct pci_device_id supported[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_SILICONIMAGE, PCI_DEVICE_ID_SIL3131) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_SILICONIMAGE, PCI_DEVICE_ID_SIL3132) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_SILICONIMAGE, PCI_DEVICE_ID_SIL3124) },
|
||||
{}
|
||||
};
|
||||
|
||||
static void sil_sata_dump_fis(struct sata_fis_d2h *s)
|
||||
{
|
||||
printf("Status FIS dump:\n");
|
||||
printf("fis_type: %02x\n", s->fis_type);
|
||||
printf("pm_port_i: %02x\n", s->pm_port_i);
|
||||
printf("status: %02x\n", s->status);
|
||||
printf("error: %02x\n", s->error);
|
||||
printf("lba_low: %02x\n", s->lba_low);
|
||||
printf("lba_mid: %02x\n", s->lba_mid);
|
||||
printf("lba_high: %02x\n", s->lba_high);
|
||||
printf("device: %02x\n", s->device);
|
||||
printf("lba_low_exp: %02x\n", s->lba_low_exp);
|
||||
printf("lba_mid_exp: %02x\n", s->lba_mid_exp);
|
||||
printf("lba_high_exp: %02x\n", s->lba_high_exp);
|
||||
printf("res1: %02x\n", s->res1);
|
||||
printf("sector_count: %02x\n", s->sector_count);
|
||||
printf("sector_count_exp: %02x\n", s->sector_count_exp);
|
||||
}
|
||||
|
||||
static const char *sata_spd_string(unsigned int speed)
|
||||
{
|
||||
static const char * const spd_str[] = {
|
||||
"1.5 Gbps",
|
||||
"3.0 Gbps",
|
||||
"6.0 Gbps",
|
||||
};
|
||||
|
||||
if ((speed - 1) > 2)
|
||||
return "<unknown>";
|
||||
|
||||
return spd_str[speed - 1];
|
||||
}
|
||||
|
||||
static u32 ata_wait_register(void *reg, u32 mask,
|
||||
u32 val, int timeout_msec)
|
||||
{
|
||||
u32 tmp;
|
||||
|
||||
tmp = readl(reg);
|
||||
while ((tmp & mask) == val && timeout_msec > 0) {
|
||||
mdelay(1);
|
||||
timeout_msec--;
|
||||
tmp = readl(reg);
|
||||
}
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static void sil_config_port(void *port)
|
||||
{
|
||||
/* configure IRQ WoC */
|
||||
writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_CLR);
|
||||
|
||||
/* zero error counters. */
|
||||
writew(0x8000, port + PORT_DECODE_ERR_THRESH);
|
||||
writew(0x8000, port + PORT_CRC_ERR_THRESH);
|
||||
writew(0x8000, port + PORT_HSHK_ERR_THRESH);
|
||||
writew(0x0000, port + PORT_DECODE_ERR_CNT);
|
||||
writew(0x0000, port + PORT_CRC_ERR_CNT);
|
||||
writew(0x0000, port + PORT_HSHK_ERR_CNT);
|
||||
|
||||
/* always use 64bit activation */
|
||||
writel(PORT_CS_32BIT_ACTV, port + PORT_CTRL_CLR);
|
||||
|
||||
/* clear port multiplier enable and resume bits */
|
||||
writel(PORT_CS_PMP_EN | PORT_CS_PMP_RESUME, port + PORT_CTRL_CLR);
|
||||
}
|
||||
|
||||
static int sil_init_port(void *port)
|
||||
{
|
||||
u32 tmp;
|
||||
|
||||
writel(PORT_CS_INIT, port + PORT_CTRL_STAT);
|
||||
ata_wait_register(port + PORT_CTRL_STAT,
|
||||
PORT_CS_INIT, PORT_CS_INIT, 100);
|
||||
tmp = ata_wait_register(port + PORT_CTRL_STAT,
|
||||
PORT_CS_RDY, 0, 100);
|
||||
|
||||
if ((tmp & (PORT_CS_INIT | PORT_CS_RDY)) != PORT_CS_RDY)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sil_read_fis(struct sil_sata *sata, int tag,
|
||||
struct sata_fis_d2h *fis)
|
||||
{
|
||||
void *port = sata->port;
|
||||
struct sil_prb *prb;
|
||||
int i;
|
||||
u32 *src, *dst;
|
||||
|
||||
prb = port + PORT_LRAM + tag * PORT_LRAM_SLOT_SZ;
|
||||
src = (u32 *)&prb->fis;
|
||||
dst = (u32 *)fis;
|
||||
for (i = 0; i < sizeof(struct sata_fis_h2d); i += 4)
|
||||
*dst++ = readl(src++);
|
||||
}
|
||||
|
||||
static int sil_exec_cmd(struct sil_sata *sata, struct sil_cmd_block *pcmd,
|
||||
int tag)
|
||||
{
|
||||
void *port = sata->port;
|
||||
u64 paddr = virt_to_bus(sata->devno, pcmd);
|
||||
u32 irq_mask, irq_stat;
|
||||
int rc;
|
||||
|
||||
writel(PORT_IRQ_COMPLETE | PORT_IRQ_ERROR, port + PORT_IRQ_ENABLE_CLR);
|
||||
|
||||
/* better to add momery barrior here */
|
||||
writel((u32)paddr, port + PORT_CMD_ACTIVATE + tag * 8);
|
||||
writel((u64)paddr >> 32, port + PORT_CMD_ACTIVATE + tag * 8 + 4);
|
||||
|
||||
irq_mask = (PORT_IRQ_COMPLETE | PORT_IRQ_ERROR) << PORT_IRQ_RAW_SHIFT;
|
||||
irq_stat = ata_wait_register(port + PORT_IRQ_STAT, irq_mask,
|
||||
0, 10000);
|
||||
|
||||
/* clear IRQs */
|
||||
writel(irq_mask, port + PORT_IRQ_STAT);
|
||||
irq_stat >>= PORT_IRQ_RAW_SHIFT;
|
||||
|
||||
if (irq_stat & PORT_IRQ_COMPLETE)
|
||||
rc = 0;
|
||||
else {
|
||||
/* force port into known state */
|
||||
sil_init_port(port);
|
||||
if (irq_stat & PORT_IRQ_ERROR)
|
||||
rc = 1; /* error */
|
||||
else
|
||||
rc = 2; /* busy */
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int sil_cmd_set_feature(struct sil_sata *sata)
|
||||
{
|
||||
struct sil_cmd_block cmdb, *pcmd = &cmdb;
|
||||
struct sata_fis_d2h fis;
|
||||
u8 udma_cap;
|
||||
int ret;
|
||||
|
||||
memset((void *)&cmdb, 0, sizeof(struct sil_cmd_block));
|
||||
pcmd->prb.fis.fis_type = SATA_FIS_TYPE_REGISTER_H2D;
|
||||
pcmd->prb.fis.pm_port_c = (1 << 7);
|
||||
pcmd->prb.fis.command = ATA_CMD_SET_FEATURES;
|
||||
pcmd->prb.fis.features = SETFEATURES_XFER;
|
||||
|
||||
/* First check the device capablity */
|
||||
udma_cap = (u8)(sata->udma & 0xff);
|
||||
debug("udma_cap %02x\n", udma_cap);
|
||||
|
||||
if (udma_cap == ATA_UDMA6)
|
||||
pcmd->prb.fis.sector_count = XFER_UDMA_6;
|
||||
if (udma_cap == ATA_UDMA5)
|
||||
pcmd->prb.fis.sector_count = XFER_UDMA_5;
|
||||
if (udma_cap == ATA_UDMA4)
|
||||
pcmd->prb.fis.sector_count = XFER_UDMA_4;
|
||||
if (udma_cap == ATA_UDMA3)
|
||||
pcmd->prb.fis.sector_count = XFER_UDMA_3;
|
||||
|
||||
ret = sil_exec_cmd(sata, pcmd, 0);
|
||||
if (ret) {
|
||||
sil_read_fis(sata, 0, &fis);
|
||||
printf("Err: exe cmd(0x%x).\n",
|
||||
readl(sata->port + PORT_SERROR));
|
||||
sil_sata_dump_fis(&fis);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sil_sata_init_wcache(struct sil_sata *sata, u16 *id)
|
||||
{
|
||||
if (ata_id_has_wcache(id) && ata_id_wcache_enabled(id))
|
||||
sata->wcache = 1;
|
||||
if (ata_id_has_flush(id))
|
||||
sata->flush = 1;
|
||||
if (ata_id_has_flush_ext(id))
|
||||
sata->flush_ext = 1;
|
||||
}
|
||||
|
||||
static void sil_sata_set_feature_by_id(struct sil_sata *sata, u16 *id)
|
||||
{
|
||||
#ifdef CONFIG_LBA48
|
||||
/* Check if support LBA48 */
|
||||
if (ata_id_has_lba48(id)) {
|
||||
sata->lba48 = 1;
|
||||
debug("Device supports LBA48\n");
|
||||
} else {
|
||||
debug("Device supports LBA28\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
sil_sata_init_wcache(sata, id);
|
||||
sil_cmd_set_feature(sata);
|
||||
}
|
||||
|
||||
static int sil_cmd_identify_device(struct sil_sata *sata, u16 *id)
|
||||
{
|
||||
struct sil_cmd_block cmdb, *pcmd = &cmdb;
|
||||
struct sata_fis_d2h fis;
|
||||
int ret;
|
||||
|
||||
memset((void *)&cmdb, 0, sizeof(struct sil_cmd_block));
|
||||
pcmd->prb.ctrl = cpu_to_le16(PRB_CTRL_PROTOCOL);
|
||||
pcmd->prb.prot = cpu_to_le16(PRB_PROT_READ);
|
||||
pcmd->prb.fis.fis_type = SATA_FIS_TYPE_REGISTER_H2D;
|
||||
pcmd->prb.fis.pm_port_c = (1 << 7);
|
||||
pcmd->prb.fis.command = ATA_CMD_ID_ATA;
|
||||
pcmd->sge.addr = cpu_to_le64(virt_to_bus(sata->devno, id));
|
||||
pcmd->sge.cnt = cpu_to_le32(sizeof(id[0]) * ATA_ID_WORDS);
|
||||
pcmd->sge.flags = cpu_to_le32(SGE_TRM);
|
||||
|
||||
ret = sil_exec_cmd(sata, pcmd, 0);
|
||||
if (ret) {
|
||||
sil_read_fis(sata, 0, &fis);
|
||||
printf("Err: id cmd(0x%x).\n", readl(sata->port + PORT_SERROR));
|
||||
sil_sata_dump_fis(&fis);
|
||||
return 1;
|
||||
}
|
||||
ata_swap_buf_le16(id, ATA_ID_WORDS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sil_cmd_soft_reset(struct sil_sata *sata)
|
||||
{
|
||||
struct sil_cmd_block cmdb, *pcmd = &cmdb;
|
||||
struct sata_fis_d2h fis;
|
||||
void *port = sata->port;
|
||||
int ret;
|
||||
|
||||
/* put the port into known state */
|
||||
if (sil_init_port(port)) {
|
||||
printf("SRST: port %d not ready\n", sata->id);
|
||||
return 1;
|
||||
}
|
||||
|
||||
memset((void *)&cmdb, 0, sizeof(struct sil_cmd_block));
|
||||
|
||||
pcmd->prb.ctrl = cpu_to_le16(PRB_CTRL_SRST);
|
||||
pcmd->prb.fis.fis_type = SATA_FIS_TYPE_REGISTER_H2D;
|
||||
pcmd->prb.fis.pm_port_c = 0xf;
|
||||
|
||||
ret = sil_exec_cmd(sata, &cmdb, 0);
|
||||
if (ret) {
|
||||
sil_read_fis(sata, 0, &fis);
|
||||
printf("SRST cmd error.\n");
|
||||
sil_sata_dump_fis(&fis);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ulong sil_sata_rw_cmd(struct sil_sata *sata, ulong start, ulong blkcnt,
|
||||
u8 *buffer, int is_write)
|
||||
{
|
||||
struct sil_cmd_block cmdb, *pcmd = &cmdb;
|
||||
struct sata_fis_d2h fis;
|
||||
u64 block;
|
||||
int ret;
|
||||
|
||||
block = (u64)start;
|
||||
memset(pcmd, 0, sizeof(struct sil_cmd_block));
|
||||
pcmd->prb.ctrl = cpu_to_le16(PRB_CTRL_PROTOCOL);
|
||||
pcmd->prb.fis.fis_type = SATA_FIS_TYPE_REGISTER_H2D;
|
||||
pcmd->prb.fis.pm_port_c = (1 << 7);
|
||||
if (is_write) {
|
||||
pcmd->prb.fis.command = ATA_CMD_WRITE;
|
||||
pcmd->prb.prot = cpu_to_le16(PRB_PROT_WRITE);
|
||||
} else {
|
||||
pcmd->prb.fis.command = ATA_CMD_READ;
|
||||
pcmd->prb.prot = cpu_to_le16(PRB_PROT_READ);
|
||||
}
|
||||
|
||||
pcmd->prb.fis.device = ATA_LBA;
|
||||
pcmd->prb.fis.device |= (block >> 24) & 0xf;
|
||||
pcmd->prb.fis.lba_high = (block >> 16) & 0xff;
|
||||
pcmd->prb.fis.lba_mid = (block >> 8) & 0xff;
|
||||
pcmd->prb.fis.lba_low = block & 0xff;
|
||||
pcmd->prb.fis.sector_count = (u8)blkcnt & 0xff;
|
||||
|
||||
pcmd->sge.addr = cpu_to_le64(virt_to_bus(sata->devno, buffer));
|
||||
pcmd->sge.cnt = cpu_to_le32(blkcnt * ATA_SECT_SIZE);
|
||||
pcmd->sge.flags = cpu_to_le32(SGE_TRM);
|
||||
|
||||
ret = sil_exec_cmd(sata, pcmd, 0);
|
||||
if (ret) {
|
||||
sil_read_fis(sata, 0, &fis);
|
||||
printf("Err: rw cmd(0x%08x).\n",
|
||||
readl(sata->port + PORT_SERROR));
|
||||
sil_sata_dump_fis(&fis);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return blkcnt;
|
||||
}
|
||||
|
||||
static ulong sil_sata_rw_cmd_ext(struct sil_sata *sata, ulong start,
|
||||
ulong blkcnt, u8 *buffer, int is_write)
|
||||
{
|
||||
struct sil_cmd_block cmdb, *pcmd = &cmdb;
|
||||
struct sata_fis_d2h fis;
|
||||
u64 block;
|
||||
int ret;
|
||||
|
||||
block = (u64)start;
|
||||
memset(pcmd, 0, sizeof(struct sil_cmd_block));
|
||||
pcmd->prb.ctrl = cpu_to_le16(PRB_CTRL_PROTOCOL);
|
||||
pcmd->prb.fis.fis_type = SATA_FIS_TYPE_REGISTER_H2D;
|
||||
pcmd->prb.fis.pm_port_c = (1 << 7);
|
||||
if (is_write) {
|
||||
pcmd->prb.fis.command = ATA_CMD_WRITE_EXT;
|
||||
pcmd->prb.prot = cpu_to_le16(PRB_PROT_WRITE);
|
||||
} else {
|
||||
pcmd->prb.fis.command = ATA_CMD_READ_EXT;
|
||||
pcmd->prb.prot = cpu_to_le16(PRB_PROT_READ);
|
||||
}
|
||||
|
||||
pcmd->prb.fis.lba_high_exp = (block >> 40) & 0xff;
|
||||
pcmd->prb.fis.lba_mid_exp = (block >> 32) & 0xff;
|
||||
pcmd->prb.fis.lba_low_exp = (block >> 24) & 0xff;
|
||||
pcmd->prb.fis.lba_high = (block >> 16) & 0xff;
|
||||
pcmd->prb.fis.lba_mid = (block >> 8) & 0xff;
|
||||
pcmd->prb.fis.lba_low = block & 0xff;
|
||||
pcmd->prb.fis.device = ATA_LBA;
|
||||
pcmd->prb.fis.sector_count_exp = (blkcnt >> 8) & 0xff;
|
||||
pcmd->prb.fis.sector_count = blkcnt & 0xff;
|
||||
|
||||
pcmd->sge.addr = cpu_to_le64(virt_to_bus(sata->devno, buffer));
|
||||
pcmd->sge.cnt = cpu_to_le32(blkcnt * ATA_SECT_SIZE);
|
||||
pcmd->sge.flags = cpu_to_le32(SGE_TRM);
|
||||
|
||||
ret = sil_exec_cmd(sata, pcmd, 0);
|
||||
if (ret) {
|
||||
sil_read_fis(sata, 0, &fis);
|
||||
printf("Err: rw ext cmd(0x%08x).\n",
|
||||
readl(sata->port + PORT_SERROR));
|
||||
sil_sata_dump_fis(&fis);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return blkcnt;
|
||||
}
|
||||
|
||||
static ulong sil_sata_rw_lba28(struct sil_sata *sata, ulong blknr,
|
||||
lbaint_t blkcnt, const void *buffer,
|
||||
int is_write)
|
||||
{
|
||||
ulong start, blks, max_blks;
|
||||
u8 *addr;
|
||||
|
||||
start = blknr;
|
||||
blks = blkcnt;
|
||||
addr = (u8 *)buffer;
|
||||
|
||||
max_blks = ATA_MAX_SECTORS;
|
||||
do {
|
||||
if (blks > max_blks) {
|
||||
sil_sata_rw_cmd(sata, start, max_blks, addr, is_write);
|
||||
start += max_blks;
|
||||
blks -= max_blks;
|
||||
addr += ATA_SECT_SIZE * max_blks;
|
||||
} else {
|
||||
sil_sata_rw_cmd(sata, start, blks, addr, is_write);
|
||||
start += blks;
|
||||
blks = 0;
|
||||
addr += ATA_SECT_SIZE * blks;
|
||||
}
|
||||
} while (blks != 0);
|
||||
|
||||
return blkcnt;
|
||||
}
|
||||
|
||||
static ulong sil_sata_rw_lba48(struct sil_sata *sata, ulong blknr,
|
||||
lbaint_t blkcnt, const void *buffer,
|
||||
int is_write)
|
||||
{
|
||||
ulong start, blks, max_blks;
|
||||
u8 *addr;
|
||||
|
||||
start = blknr;
|
||||
blks = blkcnt;
|
||||
addr = (u8 *)buffer;
|
||||
|
||||
max_blks = ATA_MAX_SECTORS_LBA48;
|
||||
do {
|
||||
if (blks > max_blks) {
|
||||
sil_sata_rw_cmd_ext(sata, start, max_blks,
|
||||
addr, is_write);
|
||||
start += max_blks;
|
||||
blks -= max_blks;
|
||||
addr += ATA_SECT_SIZE * max_blks;
|
||||
} else {
|
||||
sil_sata_rw_cmd_ext(sata, start, blks,
|
||||
addr, is_write);
|
||||
start += blks;
|
||||
blks = 0;
|
||||
addr += ATA_SECT_SIZE * blks;
|
||||
}
|
||||
} while (blks != 0);
|
||||
|
||||
return blkcnt;
|
||||
}
|
||||
|
||||
static void sil_sata_cmd_flush_cache(struct sil_sata *sata)
|
||||
{
|
||||
struct sil_cmd_block cmdb, *pcmd = &cmdb;
|
||||
|
||||
memset((void *)pcmd, 0, sizeof(struct sil_cmd_block));
|
||||
pcmd->prb.fis.fis_type = SATA_FIS_TYPE_REGISTER_H2D;
|
||||
pcmd->prb.fis.pm_port_c = (1 << 7);
|
||||
pcmd->prb.fis.command = ATA_CMD_FLUSH;
|
||||
|
||||
sil_exec_cmd(sata, pcmd, 0);
|
||||
}
|
||||
|
||||
static void sil_sata_cmd_flush_cache_ext(struct sil_sata *sata)
|
||||
{
|
||||
struct sil_cmd_block cmdb, *pcmd = &cmdb;
|
||||
|
||||
memset((void *)pcmd, 0, sizeof(struct sil_cmd_block));
|
||||
pcmd->prb.fis.fis_type = SATA_FIS_TYPE_REGISTER_H2D;
|
||||
pcmd->prb.fis.pm_port_c = (1 << 7);
|
||||
pcmd->prb.fis.command = ATA_CMD_FLUSH_EXT;
|
||||
|
||||
sil_exec_cmd(sata, pcmd, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* SATA interface between low level driver and command layer
|
||||
*/
|
||||
#if !CONFIG_IS_ENABLED(BLK)
|
||||
ulong sata_read(int dev, ulong blknr, lbaint_t blkcnt, void *buffer)
|
||||
{
|
||||
struct sil_sata *sata = (struct sil_sata *)sata_dev_desc[dev].priv;
|
||||
#else
|
||||
static ulong sata_read(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt,
|
||||
void *buffer)
|
||||
{
|
||||
struct sil_sata_priv *priv = dev_get_platdata(dev);
|
||||
int port_number = priv->port_num;
|
||||
struct sil_sata *sata = priv->sil_sata_desc[port_number];
|
||||
#endif
|
||||
ulong rc;
|
||||
|
||||
if (sata->lba48)
|
||||
rc = sil_sata_rw_lba48(sata, blknr, blkcnt, buffer, READ_CMD);
|
||||
else
|
||||
rc = sil_sata_rw_lba28(sata, blknr, blkcnt, buffer, READ_CMD);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* SATA interface between low level driver and command layer
|
||||
*/
|
||||
#if !CONFIG_IS_ENABLED(BLK)
|
||||
ulong sata_write(int dev, ulong blknr, lbaint_t blkcnt, const void *buffer)
|
||||
{
|
||||
struct sil_sata *sata = (struct sil_sata *)sata_dev_desc[dev].priv;
|
||||
#else
|
||||
ulong sata_write(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt,
|
||||
const void *buffer)
|
||||
{
|
||||
struct sil_sata_priv *priv = dev_get_platdata(dev);
|
||||
int port_number = priv->port_num;
|
||||
struct sil_sata *sata = priv->sil_sata_desc[port_number];
|
||||
#endif
|
||||
ulong rc;
|
||||
|
||||
if (sata->lba48) {
|
||||
rc = sil_sata_rw_lba48(sata, blknr, blkcnt, buffer, WRITE_CMD);
|
||||
if (sata->wcache && sata->flush_ext)
|
||||
sil_sata_cmd_flush_cache_ext(sata);
|
||||
} else {
|
||||
rc = sil_sata_rw_lba28(sata, blknr, blkcnt, buffer, WRITE_CMD);
|
||||
if (sata->wcache && sata->flush)
|
||||
sil_sata_cmd_flush_cache(sata);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
#if !CONFIG_IS_ENABLED(BLK)
|
||||
static int sil_init_sata(int dev)
|
||||
{
|
||||
#else
|
||||
static int sil_init_sata(struct udevice *uc_dev, int dev)
|
||||
{
|
||||
struct sil_sata_priv *priv = dev_get_platdata(uc_dev);
|
||||
#endif
|
||||
struct sil_sata *sata;
|
||||
void *port;
|
||||
u32 tmp;
|
||||
int cnt;
|
||||
|
||||
printf("SATA#%d:\n", dev);
|
||||
|
||||
port = (void *)sata_info.iobase[1] +
|
||||
PORT_REGS_SIZE * (dev - sata_info.portbase);
|
||||
|
||||
/* Initial PHY setting */
|
||||
writel(0x20c, port + PORT_PHY_CFG);
|
||||
|
||||
/* clear port RST */
|
||||
tmp = readl(port + PORT_CTRL_STAT);
|
||||
if (tmp & PORT_CS_PORT_RST) {
|
||||
writel(PORT_CS_PORT_RST, port + PORT_CTRL_CLR);
|
||||
tmp = ata_wait_register(port + PORT_CTRL_STAT,
|
||||
PORT_CS_PORT_RST, PORT_CS_PORT_RST, 100);
|
||||
if (tmp & PORT_CS_PORT_RST)
|
||||
printf("Err: Failed to clear port RST\n");
|
||||
}
|
||||
|
||||
/* Check if device is present */
|
||||
for (cnt = 0; cnt < 100; cnt++) {
|
||||
tmp = readl(port + PORT_SSTATUS);
|
||||
if ((tmp & 0xF) == 0x3)
|
||||
break;
|
||||
mdelay(1);
|
||||
}
|
||||
|
||||
tmp = readl(port + PORT_SSTATUS);
|
||||
if ((tmp & 0xf) != 0x3) {
|
||||
printf(" (No RDY)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Wait for port ready */
|
||||
tmp = ata_wait_register(port + PORT_CTRL_STAT,
|
||||
PORT_CS_RDY, PORT_CS_RDY, 100);
|
||||
if ((tmp & PORT_CS_RDY) != PORT_CS_RDY) {
|
||||
printf("%d port not ready.\n", dev);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* configure port */
|
||||
sil_config_port(port);
|
||||
|
||||
/* Reset port */
|
||||
writel(PORT_CS_DEV_RST, port + PORT_CTRL_STAT);
|
||||
readl(port + PORT_CTRL_STAT);
|
||||
tmp = ata_wait_register(port + PORT_CTRL_STAT, PORT_CS_DEV_RST,
|
||||
PORT_CS_DEV_RST, 100);
|
||||
if (tmp & PORT_CS_DEV_RST) {
|
||||
printf("%d port reset failed.\n", dev);
|
||||
return 1;
|
||||
}
|
||||
|
||||
sata = (struct sil_sata *)malloc(sizeof(struct sil_sata));
|
||||
if (!sata) {
|
||||
printf("%d no memory.\n", dev);
|
||||
return 1;
|
||||
}
|
||||
memset((void *)sata, 0, sizeof(struct sil_sata));
|
||||
|
||||
/* Save the private struct to block device struct */
|
||||
#if !CONFIG_IS_ENABLED(BLK)
|
||||
sata_dev_desc[dev].priv = (void *)sata;
|
||||
#else
|
||||
priv->sil_sata_desc[dev] = sata;
|
||||
priv->port_num = dev;
|
||||
#endif
|
||||
sata->id = dev;
|
||||
sata->port = port;
|
||||
sata->devno = sata_info.devno;
|
||||
sprintf(sata->name, "SATA#%d", dev);
|
||||
sil_cmd_soft_reset(sata);
|
||||
tmp = readl(port + PORT_SSTATUS);
|
||||
tmp = (tmp >> 4) & 0xf;
|
||||
printf(" (%s)\n", sata_spd_string(tmp));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if !CONFIG_IS_ENABLED(BLK)
|
||||
/*
|
||||
* SATA interface between low level driver and command layer
|
||||
*/
|
||||
int init_sata(int dev)
|
||||
{
|
||||
static int init_done, idx;
|
||||
pci_dev_t devno;
|
||||
u16 word;
|
||||
|
||||
if (init_done == 1 && dev < sata_info.maxport)
|
||||
goto init_start;
|
||||
|
||||
init_done = 1;
|
||||
|
||||
/* Find PCI device(s) */
|
||||
devno = pci_find_devices(supported, idx++);
|
||||
if (devno == -1)
|
||||
return 1;
|
||||
|
||||
pci_read_config_word(devno, PCI_DEVICE_ID, &word);
|
||||
|
||||
/* get the port count */
|
||||
word &= 0xf;
|
||||
|
||||
sata_info.portbase = 0;
|
||||
sata_info.maxport = sata_info.portbase + word;
|
||||
sata_info.devno = devno;
|
||||
|
||||
/* Read out all BARs */
|
||||
sata_info.iobase[0] = (ulong)pci_map_bar(devno,
|
||||
PCI_BASE_ADDRESS_0, PCI_REGION_MEM);
|
||||
sata_info.iobase[1] = (ulong)pci_map_bar(devno,
|
||||
PCI_BASE_ADDRESS_2, PCI_REGION_MEM);
|
||||
|
||||
/* mask out the unused bits */
|
||||
sata_info.iobase[0] &= 0xffffff80;
|
||||
sata_info.iobase[1] &= 0xfffffc00;
|
||||
|
||||
/* Enable Bus Mastering and memory region */
|
||||
pci_write_config_word(devno, PCI_COMMAND,
|
||||
PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
|
||||
|
||||
/* Check if mem accesses and Bus Mastering are enabled. */
|
||||
pci_read_config_word(devno, PCI_COMMAND, &word);
|
||||
if (!(word & PCI_COMMAND_MEMORY) ||
|
||||
(!(word & PCI_COMMAND_MASTER))) {
|
||||
printf("Error: Can not enable MEM access or Bus Mastering.\n");
|
||||
debug("PCI command: %04x\n", word);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* GPIO off */
|
||||
writel(0, (void *)(sata_info.iobase[0] + HOST_FLASH_CMD));
|
||||
/* clear global reset & mask interrupts during initialization */
|
||||
writel(0, (void *)(sata_info.iobase[0] + HOST_CTRL));
|
||||
|
||||
init_start:
|
||||
return sil_init_sata(dev);
|
||||
}
|
||||
|
||||
int reset_sata(int dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* SATA interface between low level driver and command layer
|
||||
*/
|
||||
int scan_sata(int dev)
|
||||
{
|
||||
struct sil_sata *sata = (struct sil_sata *)sata_dev_desc[dev].priv;
|
||||
#else
|
||||
static int scan_sata(struct udevice *blk_dev, int dev)
|
||||
{
|
||||
struct blk_desc *desc = dev_get_uclass_platdata(blk_dev);
|
||||
struct sil_sata_priv *priv = dev_get_platdata(blk_dev);
|
||||
struct sil_sata *sata = priv->sil_sata_desc[dev];
|
||||
#endif
|
||||
unsigned char serial[ATA_ID_SERNO_LEN + 1];
|
||||
unsigned char firmware[ATA_ID_FW_REV_LEN + 1];
|
||||
unsigned char product[ATA_ID_PROD_LEN + 1];
|
||||
u16 *id;
|
||||
|
||||
id = (u16 *)malloc(ATA_ID_WORDS * 2);
|
||||
if (!id) {
|
||||
printf("Id malloc failed\n");
|
||||
return 1;
|
||||
}
|
||||
sil_cmd_identify_device(sata, id);
|
||||
|
||||
sil_sata_set_feature_by_id(sata, id);
|
||||
|
||||
/* Serial number */
|
||||
ata_id_c_string(id, serial, ATA_ID_SERNO, sizeof(serial));
|
||||
|
||||
/* Firmware version */
|
||||
ata_id_c_string(id, firmware, ATA_ID_FW_REV, sizeof(firmware));
|
||||
|
||||
/* Product model */
|
||||
ata_id_c_string(id, product, ATA_ID_PROD, sizeof(product));
|
||||
|
||||
#if !CONFIG_IS_ENABLED(BLK)
|
||||
memcpy(sata_dev_desc[dev].product, serial, sizeof(serial));
|
||||
memcpy(sata_dev_desc[dev].revision, firmware, sizeof(firmware));
|
||||
memcpy(sata_dev_desc[dev].vendor, product, sizeof(product));
|
||||
/* Totoal sectors */
|
||||
sata_dev_desc[dev].lba = ata_id_n_sectors(id);
|
||||
#ifdef CONFIG_LBA48
|
||||
sata_dev_desc[dev].lba48 = sata->lba48;
|
||||
#endif
|
||||
#else
|
||||
memcpy(desc->product, serial, sizeof(serial));
|
||||
memcpy(desc->revision, firmware, sizeof(firmware));
|
||||
memcpy(desc->vendor, product, sizeof(product));
|
||||
desc->lba = ata_id_n_sectors(id);
|
||||
#ifdef CONFIG_LBA48
|
||||
desc->lba48 = sata->lba48;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
ata_dump_id(id);
|
||||
#endif
|
||||
free((void *)id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if CONFIG_IS_ENABLED(BLK)
|
||||
static const struct blk_ops sata_sil_blk_ops = {
|
||||
.read = sata_read,
|
||||
.write = sata_write,
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(sata_sil_driver) = {
|
||||
.name = "sata_sil_blk",
|
||||
.id = UCLASS_BLK,
|
||||
.ops = &sata_sil_blk_ops,
|
||||
.platdata_auto_alloc_size = sizeof(struct sil_sata_priv),
|
||||
};
|
||||
|
||||
static int sil_pci_probe(struct udevice *dev)
|
||||
{
|
||||
struct udevice *blk;
|
||||
char sata_name[10];
|
||||
pci_dev_t devno;
|
||||
u16 word;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
/* Get PCI device number */
|
||||
devno = dm_pci_get_bdf(dev);
|
||||
if (devno == -1)
|
||||
return 1;
|
||||
|
||||
dm_pci_read_config16(dev, PCI_DEVICE_ID, &word);
|
||||
|
||||
/* get the port count */
|
||||
word &= 0xf;
|
||||
|
||||
sata_info.portbase = 0;
|
||||
sata_info.maxport = sata_info.portbase + word;
|
||||
sata_info.devno = devno;
|
||||
|
||||
/* Read out all BARs */
|
||||
sata_info.iobase[0] = (ulong)dm_pci_map_bar(dev,
|
||||
PCI_BASE_ADDRESS_0, PCI_REGION_MEM);
|
||||
sata_info.iobase[1] = (ulong)dm_pci_map_bar(dev,
|
||||
PCI_BASE_ADDRESS_2, PCI_REGION_MEM);
|
||||
|
||||
/* mask out the unused bits */
|
||||
sata_info.iobase[0] &= 0xffffff80;
|
||||
sata_info.iobase[1] &= 0xfffffc00;
|
||||
|
||||
/* Enable Bus Mastering and memory region */
|
||||
dm_pci_write_config16(dev, PCI_COMMAND,
|
||||
PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
|
||||
|
||||
/* Check if mem accesses and Bus Mastering are enabled. */
|
||||
dm_pci_read_config16(dev, PCI_COMMAND, &word);
|
||||
if (!(word & PCI_COMMAND_MEMORY) ||
|
||||
(!(word & PCI_COMMAND_MASTER))) {
|
||||
printf("Error: Can not enable MEM access or Bus Mastering.\n");
|
||||
debug("PCI command: %04x\n", word);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* GPIO off */
|
||||
writel(0, (void *)(sata_info.iobase[0] + HOST_FLASH_CMD));
|
||||
/* clear global reset & mask interrupts during initialization */
|
||||
writel(0, (void *)(sata_info.iobase[0] + HOST_CTRL));
|
||||
|
||||
for (i = sata_info.portbase; i < sata_info.maxport; i++) {
|
||||
snprintf(sata_name, sizeof(sata_name), "sil_sata%d", i);
|
||||
ret = blk_create_devicef(dev, "sata_sil_blk", sata_name,
|
||||
IF_TYPE_SATA, -1, 512, 0, &blk);
|
||||
if (ret) {
|
||||
debug("Can't create device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = sil_init_sata(blk, i);
|
||||
if (ret)
|
||||
return -ENODEV;
|
||||
|
||||
ret = scan_sata(blk, i);
|
||||
if (ret)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sata_sil_scan(struct udevice *dev)
|
||||
{
|
||||
/* Nothing to do here */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct sil_ops sata_sil_ops = {
|
||||
.scan = sata_sil_scan,
|
||||
};
|
||||
|
||||
static const struct udevice_id sil_pci_ids[] = {
|
||||
{ .compatible = "sil-pci-sample" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(sil_ahci_pci) = {
|
||||
.name = "sil_ahci_pci",
|
||||
.id = UCLASS_AHCI,
|
||||
.of_match = sil_pci_ids,
|
||||
.ops = &sata_sil_ops,
|
||||
.probe = sil_pci_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct sil_sata_priv),
|
||||
};
|
||||
|
||||
U_BOOT_PCI_DEVICE(sil_ahci_pci, supported);
|
||||
#endif
|
||||
@@ -0,0 +1,223 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright (C) 2011 Freescale Semiconductor, Inc.
|
||||
* Copyright 2019 NXP
|
||||
* Author: Tang Yuantian <b29983@freescale.com>
|
||||
*/
|
||||
|
||||
#ifndef SATA_SIL3132_H
|
||||
#define SATA_SIL3132_H
|
||||
|
||||
#define READ_CMD 0
|
||||
#define WRITE_CMD 1
|
||||
|
||||
/*
|
||||
* SATA device driver struct for each dev
|
||||
*/
|
||||
struct sil_sata {
|
||||
char name[12];
|
||||
void *port; /* the port base address */
|
||||
int lba48;
|
||||
u16 pio;
|
||||
u16 mwdma;
|
||||
u16 udma;
|
||||
pci_dev_t devno;
|
||||
int wcache;
|
||||
int flush;
|
||||
int flush_ext;
|
||||
int id;
|
||||
};
|
||||
|
||||
/* sata info for each controller */
|
||||
struct sata_info {
|
||||
ulong iobase[3];
|
||||
pci_dev_t devno;
|
||||
int portbase;
|
||||
int maxport;
|
||||
};
|
||||
|
||||
/*
|
||||
* Scatter gather entry (SGE),MUST 8 bytes aligned
|
||||
*/
|
||||
struct sil_sge {
|
||||
__le64 addr;
|
||||
__le32 cnt;
|
||||
__le32 flags;
|
||||
} __attribute__ ((aligned(8), packed));
|
||||
|
||||
/*
|
||||
* Port request block, MUST 8 bytes aligned
|
||||
*/
|
||||
struct sil_prb {
|
||||
__le16 ctrl;
|
||||
__le16 prot;
|
||||
__le32 rx_cnt;
|
||||
struct sata_fis_h2d fis;
|
||||
} __attribute__ ((aligned(8), packed));
|
||||
|
||||
struct sil_cmd_block {
|
||||
struct sil_prb prb;
|
||||
struct sil_sge sge;
|
||||
};
|
||||
|
||||
enum {
|
||||
HOST_SLOT_STAT = 0x00, /* 32 bit slot stat * 4 */
|
||||
HOST_CTRL = 0x40,
|
||||
HOST_IRQ_STAT = 0x44,
|
||||
HOST_PHY_CFG = 0x48,
|
||||
HOST_BIST_CTRL = 0x50,
|
||||
HOST_BIST_PTRN = 0x54,
|
||||
HOST_BIST_STAT = 0x58,
|
||||
HOST_MEM_BIST_STAT = 0x5c,
|
||||
HOST_FLASH_CMD = 0x70,
|
||||
/* 8 bit regs */
|
||||
HOST_FLASH_DATA = 0x74,
|
||||
HOST_TRANSITION_DETECT = 0x75,
|
||||
HOST_GPIO_CTRL = 0x76,
|
||||
HOST_I2C_ADDR = 0x78, /* 32 bit */
|
||||
HOST_I2C_DATA = 0x7c,
|
||||
HOST_I2C_XFER_CNT = 0x7e,
|
||||
HOST_I2C_CTRL = 0x7f,
|
||||
|
||||
/* HOST_SLOT_STAT bits */
|
||||
HOST_SSTAT_ATTN = (1 << 31),
|
||||
|
||||
/* HOST_CTRL bits */
|
||||
HOST_CTRL_M66EN = (1 << 16), /* M66EN PCI bus signal */
|
||||
HOST_CTRL_TRDY = (1 << 17), /* latched PCI TRDY */
|
||||
HOST_CTRL_STOP = (1 << 18), /* latched PCI STOP */
|
||||
HOST_CTRL_DEVSEL = (1 << 19), /* latched PCI DEVSEL */
|
||||
HOST_CTRL_REQ64 = (1 << 20), /* latched PCI REQ64 */
|
||||
HOST_CTRL_GLOBAL_RST = (1 << 31), /* global reset */
|
||||
|
||||
/*
|
||||
* Port registers
|
||||
* (8192 bytes @ +0x0000, +0x2000, +0x4000 and +0x6000 @ BAR2)
|
||||
*/
|
||||
PORT_REGS_SIZE = 0x2000,
|
||||
|
||||
PORT_LRAM = 0x0000, /* 31 LRAM slots and PMP regs */
|
||||
PORT_LRAM_SLOT_SZ = 0x0080, /* 32 bytes PRB + 2 SGE, ACT... */
|
||||
|
||||
PORT_PMP = 0x0f80, /* 8 bytes PMP * 16 (128 bytes) */
|
||||
PORT_PMP_STATUS = 0x0000, /* port device status offset */
|
||||
PORT_PMP_QACTIVE = 0x0004, /* port device QActive offset */
|
||||
PORT_PMP_SIZE = 0x0008, /* 8 bytes per PMP */
|
||||
|
||||
/* 32 bit regs */
|
||||
PORT_CTRL_STAT = 0x1000, /* write: ctrl-set, read: stat */
|
||||
PORT_CTRL_CLR = 0x1004, /* write: ctrl-clear */
|
||||
PORT_IRQ_STAT = 0x1008, /* high: status, low: interrupt */
|
||||
PORT_IRQ_ENABLE_SET = 0x1010, /* write: enable-set */
|
||||
PORT_IRQ_ENABLE_CLR = 0x1014, /* write: enable-clear */
|
||||
PORT_ACTIVATE_UPPER_ADDR = 0x101c,
|
||||
PORT_EXEC_FIFO = 0x1020, /* command execution fifo */
|
||||
PORT_CMD_ERR = 0x1024, /* command error number */
|
||||
PORT_FIS_CFG = 0x1028,
|
||||
PORT_FIFO_THRES = 0x102c,
|
||||
|
||||
/* 16 bit regs */
|
||||
PORT_DECODE_ERR_CNT = 0x1040,
|
||||
PORT_DECODE_ERR_THRESH = 0x1042,
|
||||
PORT_CRC_ERR_CNT = 0x1044,
|
||||
PORT_CRC_ERR_THRESH = 0x1046,
|
||||
PORT_HSHK_ERR_CNT = 0x1048,
|
||||
PORT_HSHK_ERR_THRESH = 0x104a,
|
||||
|
||||
/* 32 bit regs */
|
||||
PORT_PHY_CFG = 0x1050,
|
||||
PORT_SLOT_STAT = 0x1800,
|
||||
PORT_CMD_ACTIVATE = 0x1c00, /* 64 bit cmd activate * 31 */
|
||||
PORT_CONTEXT = 0x1e04,
|
||||
PORT_EXEC_DIAG = 0x1e00, /* 32bit exec diag * 16 */
|
||||
PORT_PSD_DIAG = 0x1e40, /* 32bit psd diag * 16 */
|
||||
PORT_SCONTROL = 0x1f00,
|
||||
PORT_SSTATUS = 0x1f04,
|
||||
PORT_SERROR = 0x1f08,
|
||||
PORT_SACTIVE = 0x1f0c,
|
||||
|
||||
/* PORT_CTRL_STAT bits */
|
||||
PORT_CS_PORT_RST = (1 << 0), /* port reset */
|
||||
PORT_CS_DEV_RST = (1 << 1), /* device reset */
|
||||
PORT_CS_INIT = (1 << 2), /* port initialize */
|
||||
PORT_CS_IRQ_WOC = (1 << 3), /* interrupt write one to clear */
|
||||
PORT_CS_CDB16 = (1 << 5), /* 0=12b cdb, 1=16b cdb */
|
||||
PORT_CS_PMP_RESUME = (1 << 6), /* PMP resume */
|
||||
PORT_CS_32BIT_ACTV = (1 << 10), /* 32-bit activation */
|
||||
PORT_CS_PMP_EN = (1 << 13), /* port multiplier enable */
|
||||
PORT_CS_RDY = (1 << 31), /* port ready to accept commands */
|
||||
|
||||
/* PORT_IRQ_STAT/ENABLE_SET/CLR */
|
||||
/* bits[11:0] are masked */
|
||||
PORT_IRQ_COMPLETE = (1 << 0), /* command(s) completed */
|
||||
PORT_IRQ_ERROR = (1 << 1), /* command execution error */
|
||||
PORT_IRQ_PORTRDY_CHG = (1 << 2), /* port ready change */
|
||||
PORT_IRQ_PWR_CHG = (1 << 3), /* power management change */
|
||||
PORT_IRQ_PHYRDY_CHG = (1 << 4), /* PHY ready change */
|
||||
PORT_IRQ_COMWAKE = (1 << 5), /* COMWAKE received */
|
||||
PORT_IRQ_UNK_FIS = (1 << 6), /* unknown FIS received */
|
||||
PORT_IRQ_DEV_XCHG = (1 << 7), /* device exchanged */
|
||||
PORT_IRQ_8B10B = (1 << 8), /* 8b/10b decode error threshold */
|
||||
PORT_IRQ_CRC = (1 << 9), /* CRC error threshold */
|
||||
PORT_IRQ_HANDSHAKE = (1 << 10), /* handshake error threshold */
|
||||
PORT_IRQ_SDB_NOTIFY = (1 << 11), /* SDB notify received */
|
||||
|
||||
DEF_PORT_IRQ = PORT_IRQ_COMPLETE | PORT_IRQ_ERROR |
|
||||
PORT_IRQ_PHYRDY_CHG | PORT_IRQ_DEV_XCHG |
|
||||
PORT_IRQ_UNK_FIS | PORT_IRQ_SDB_NOTIFY,
|
||||
|
||||
/* bits[27:16] are unmasked (raw) */
|
||||
PORT_IRQ_RAW_SHIFT = 16,
|
||||
PORT_IRQ_MASKED_MASK = 0x7ff,
|
||||
PORT_IRQ_RAW_MASK = (0x7ff << PORT_IRQ_RAW_SHIFT),
|
||||
|
||||
/* ENABLE_SET/CLR specific, intr steering - 2 bit field */
|
||||
PORT_IRQ_STEER_SHIFT = 30,
|
||||
PORT_IRQ_STEER_MASK = (3 << PORT_IRQ_STEER_SHIFT),
|
||||
|
||||
/* PORT_CMD_ERR constants */
|
||||
PORT_CERR_DEV = 1, /* Error bit in D2H Register FIS */
|
||||
PORT_CERR_SDB = 2, /* Error bit in SDB FIS */
|
||||
PORT_CERR_DATA = 3, /* Error in data FIS not detected by dev */
|
||||
PORT_CERR_SEND = 4, /* Initial cmd FIS transmission failure */
|
||||
PORT_CERR_INCONSISTENT = 5, /* Protocol mismatch */
|
||||
PORT_CERR_DIRECTION = 6, /* Data direction mismatch */
|
||||
PORT_CERR_UNDERRUN = 7, /* Ran out of SGEs while writing */
|
||||
PORT_CERR_OVERRUN = 8, /* Ran out of SGEs while reading */
|
||||
|
||||
/* bits of PRB control field */
|
||||
PRB_CTRL_PROTOCOL = (1 << 0), /* override def. ATA protocol */
|
||||
PRB_CTRL_PACKET_READ = (1 << 4), /* PACKET cmd read */
|
||||
PRB_CTRL_PACKET_WRITE = (1 << 5), /* PACKET cmd write */
|
||||
PRB_CTRL_NIEN = (1 << 6), /* Mask completion irq */
|
||||
PRB_CTRL_SRST = (1 << 7), /* Soft reset request (ign BSY?) */
|
||||
|
||||
/* PRB protocol field */
|
||||
PRB_PROT_PACKET = (1 << 0),
|
||||
PRB_PROT_TCQ = (1 << 1),
|
||||
PRB_PROT_NCQ = (1 << 2),
|
||||
PRB_PROT_READ = (1 << 3),
|
||||
PRB_PROT_WRITE = (1 << 4),
|
||||
PRB_PROT_TRANSPARENT = (1 << 5),
|
||||
|
||||
/*
|
||||
* Other constants
|
||||
*/
|
||||
SGE_TRM = (1 << 31), /* Last SGE in chain */
|
||||
SGE_LNK = (1 << 30), /* linked list
|
||||
Points to SGT, not SGE */
|
||||
SGE_DRD = (1 << 29), /* discard data read (/dev/null)
|
||||
data address ignored */
|
||||
|
||||
CMD_ERR = 0x21,
|
||||
};
|
||||
|
||||
#if CONFIG_IS_ENABLED(BLK)
|
||||
#define ATA_MAX_PORTS 32
|
||||
struct sil_sata_priv {
|
||||
int port_num;
|
||||
struct sil_sata *sil_sata_desc[ATA_MAX_PORTS];
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,834 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) Excito Elektronik i Skåne AB, All rights reserved.
|
||||
* Author: Tor Krill <tor@excito.com>
|
||||
*
|
||||
* This is a driver for Silicon Image sil3114 sata chip modelled on
|
||||
* the ata_piix driver
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <pci.h>
|
||||
#include <command.h>
|
||||
#include <config.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include <asm/io.h>
|
||||
#include <ide.h>
|
||||
#include <sata.h>
|
||||
#include <libata.h>
|
||||
#include "sata_sil3114.h"
|
||||
|
||||
/* Convert sectorsize to wordsize */
|
||||
#define ATA_SECTOR_WORDS (ATA_SECT_SIZE/2)
|
||||
|
||||
/* Forwards */
|
||||
u8 sil3114_spin_up (int num);
|
||||
u8 sil3114_spin_down (int num);
|
||||
static int sata_bus_softreset (int num);
|
||||
static void sata_identify (int num, int dev);
|
||||
static u8 check_power_mode (int num);
|
||||
static void sata_port (struct sata_ioports *ioport);
|
||||
static void set_Feature_cmd (int num, int dev);
|
||||
static u8 sata_busy_wait (struct sata_ioports *ioaddr, int bits,
|
||||
unsigned int max, u8 usealtstatus);
|
||||
static u8 sata_chk_status (struct sata_ioports *ioaddr, u8 usealtstatus);
|
||||
static void msleep (int count);
|
||||
|
||||
static u32 iobase[6] = { 0, 0, 0, 0, 0, 0}; /* PCI BAR registers for device */
|
||||
|
||||
static struct sata_port port[CONFIG_SYS_SATA_MAX_DEVICE];
|
||||
|
||||
static void output_data (struct sata_ioports *ioaddr, u16 * sect_buf, int words)
|
||||
{
|
||||
while (words--) {
|
||||
__raw_writew (*sect_buf++, (void *)ioaddr->data_addr);
|
||||
}
|
||||
}
|
||||
|
||||
static int input_data (struct sata_ioports *ioaddr, u16 * sect_buf, int words)
|
||||
{
|
||||
while (words--) {
|
||||
*sect_buf++ = __raw_readw ((void *)ioaddr->data_addr);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sata_bus_softreset (int num)
|
||||
{
|
||||
u8 status = 0;
|
||||
|
||||
port[num].dev_mask = 1;
|
||||
|
||||
port[num].ctl_reg = 0x08; /*Default value of control reg */
|
||||
writeb (port[num].ctl_reg, port[num].ioaddr.ctl_addr);
|
||||
udelay (10);
|
||||
writeb (port[num].ctl_reg | ATA_SRST, port[num].ioaddr.ctl_addr);
|
||||
udelay (10);
|
||||
writeb (port[num].ctl_reg, port[num].ioaddr.ctl_addr);
|
||||
|
||||
/* spec mandates ">= 2ms" before checking status.
|
||||
* We wait 150ms, because that was the magic delay used for
|
||||
* ATAPI devices in Hale Landis's ATADRVR, for the period of time
|
||||
* between when the ATA command register is written, and then
|
||||
* status is checked. Because waiting for "a while" before
|
||||
* checking status is fine, post SRST, we perform this magic
|
||||
* delay here as well.
|
||||
*/
|
||||
msleep (150);
|
||||
status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 300, 0);
|
||||
while ((status & ATA_BUSY)) {
|
||||
msleep (100);
|
||||
status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 3, 0);
|
||||
}
|
||||
|
||||
if (status & ATA_BUSY) {
|
||||
printf ("ata%u is slow to respond,plz be patient\n", num);
|
||||
}
|
||||
|
||||
while ((status & ATA_BUSY)) {
|
||||
msleep (100);
|
||||
status = sata_chk_status (&port[num].ioaddr, 0);
|
||||
}
|
||||
|
||||
if (status & ATA_BUSY) {
|
||||
printf ("ata%u failed to respond : ", num);
|
||||
printf ("bus reset failed\n");
|
||||
port[num].dev_mask = 0;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sata_identify (int num, int dev)
|
||||
{
|
||||
u8 cmd = 0, status = 0, devno = num;
|
||||
u16 iobuf[ATA_SECTOR_WORDS];
|
||||
u64 n_sectors = 0;
|
||||
|
||||
memset (iobuf, 0, sizeof (iobuf));
|
||||
|
||||
if (!(port[num].dev_mask & 0x01)) {
|
||||
printf ("dev%d is not present on port#%d\n", dev, num);
|
||||
return;
|
||||
}
|
||||
|
||||
debug ("port=%d dev=%d\n", num, dev);
|
||||
|
||||
status = 0;
|
||||
cmd = ATA_CMD_ID_ATA; /*Device Identify Command */
|
||||
writeb (cmd, port[num].ioaddr.command_addr);
|
||||
readb (port[num].ioaddr.altstatus_addr);
|
||||
udelay (10);
|
||||
|
||||
status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 1000, 0);
|
||||
if (status & ATA_ERR) {
|
||||
printf ("\ndevice not responding\n");
|
||||
port[num].dev_mask &= ~0x01;
|
||||
return;
|
||||
}
|
||||
|
||||
input_data (&port[num].ioaddr, iobuf, ATA_SECTOR_WORDS);
|
||||
|
||||
ata_swap_buf_le16 (iobuf, ATA_SECTOR_WORDS);
|
||||
|
||||
debug ("Specific config: %x\n", iobuf[2]);
|
||||
|
||||
/* we require LBA and DMA support (bits 8 & 9 of word 49) */
|
||||
if (!ata_id_has_dma (iobuf) || !ata_id_has_lba (iobuf)) {
|
||||
debug ("ata%u: no dma/lba\n", num);
|
||||
}
|
||||
#ifdef DEBUG
|
||||
ata_dump_id (iobuf);
|
||||
#endif
|
||||
n_sectors = ata_id_n_sectors (iobuf);
|
||||
|
||||
if (n_sectors == 0) {
|
||||
port[num].dev_mask &= ~0x01;
|
||||
return;
|
||||
}
|
||||
ata_id_c_string (iobuf, (unsigned char *)sata_dev_desc[devno].revision,
|
||||
ATA_ID_FW_REV, sizeof (sata_dev_desc[devno].revision));
|
||||
ata_id_c_string (iobuf, (unsigned char *)sata_dev_desc[devno].vendor,
|
||||
ATA_ID_PROD, sizeof (sata_dev_desc[devno].vendor));
|
||||
ata_id_c_string (iobuf, (unsigned char *)sata_dev_desc[devno].product,
|
||||
ATA_ID_SERNO, sizeof (sata_dev_desc[devno].product));
|
||||
|
||||
/* TODO - atm we asume harddisk ie not removable */
|
||||
sata_dev_desc[devno].removable = 0;
|
||||
|
||||
sata_dev_desc[devno].lba = (u32) n_sectors;
|
||||
debug("lba=0x%lx\n", sata_dev_desc[devno].lba);
|
||||
|
||||
#ifdef CONFIG_LBA48
|
||||
if (iobuf[83] & (1 << 10)) {
|
||||
sata_dev_desc[devno].lba48 = 1;
|
||||
} else {
|
||||
sata_dev_desc[devno].lba48 = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* assuming HD */
|
||||
sata_dev_desc[devno].type = DEV_TYPE_HARDDISK;
|
||||
sata_dev_desc[devno].blksz = ATA_SECT_SIZE;
|
||||
sata_dev_desc[devno].lun = 0; /* just to fill something in... */
|
||||
}
|
||||
|
||||
static void set_Feature_cmd (int num, int dev)
|
||||
{
|
||||
u8 status = 0;
|
||||
|
||||
if (!(port[num].dev_mask & 0x01)) {
|
||||
debug ("dev%d is not present on port#%d\n", dev, num);
|
||||
return;
|
||||
}
|
||||
|
||||
writeb (SETFEATURES_XFER, port[num].ioaddr.feature_addr);
|
||||
writeb (XFER_PIO_4, port[num].ioaddr.nsect_addr);
|
||||
writeb (0, port[num].ioaddr.lbal_addr);
|
||||
writeb (0, port[num].ioaddr.lbam_addr);
|
||||
writeb (0, port[num].ioaddr.lbah_addr);
|
||||
|
||||
writeb (ATA_DEVICE_OBS, port[num].ioaddr.device_addr);
|
||||
writeb (ATA_CMD_SET_FEATURES, port[num].ioaddr.command_addr);
|
||||
|
||||
udelay (50);
|
||||
msleep (150);
|
||||
|
||||
status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 5000, 0);
|
||||
if ((status & (ATA_BUSY | ATA_ERR))) {
|
||||
printf ("Error : status 0x%02x\n", status);
|
||||
port[num].dev_mask &= ~0x01;
|
||||
}
|
||||
}
|
||||
|
||||
u8 sil3114_spin_down (int num)
|
||||
{
|
||||
u8 status = 0;
|
||||
|
||||
debug ("Spin down disk\n");
|
||||
|
||||
if (!(port[num].dev_mask & 0x01)) {
|
||||
debug ("Device ata%d is not present\n", num);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((status = check_power_mode (num)) == 0x00) {
|
||||
debug ("Already in standby\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (status == 0x01) {
|
||||
printf ("Failed to check power mode on ata%d\n", num);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!((status = sata_chk_status (&port[num].ioaddr, 0)) & ATA_DRDY)) {
|
||||
printf ("Device ata%d not ready\n", num);
|
||||
return 1;
|
||||
}
|
||||
|
||||
writeb (0x00, port[num].ioaddr.feature_addr);
|
||||
|
||||
writeb (0x00, port[num].ioaddr.nsect_addr);
|
||||
writeb (0x00, port[num].ioaddr.lbal_addr);
|
||||
writeb (0x00, port[num].ioaddr.lbam_addr);
|
||||
writeb (0x00, port[num].ioaddr.lbah_addr);
|
||||
|
||||
writeb (ATA_DEVICE_OBS, port[num].ioaddr.device_addr);
|
||||
writeb (ATA_CMD_STANDBY, port[num].ioaddr.command_addr);
|
||||
|
||||
status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 30000, 0);
|
||||
if ((status & (ATA_BUSY | ATA_ERR))) {
|
||||
printf ("Error waiting for disk spin down: status 0x%02x\n",
|
||||
status);
|
||||
port[num].dev_mask &= ~0x01;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
u8 sil3114_spin_up (int num)
|
||||
{
|
||||
u8 status = 0;
|
||||
|
||||
debug ("Spin up disk\n");
|
||||
|
||||
if (!(port[num].dev_mask & 0x01)) {
|
||||
debug ("Device ata%d is not present\n", num);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((status = check_power_mode (num)) != 0x00) {
|
||||
if (status == 0x01) {
|
||||
printf ("Failed to check power mode on ata%d\n", num);
|
||||
return 1;
|
||||
} else {
|
||||
/* should be up and running already */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!((status = sata_chk_status (&port[num].ioaddr, 0)) & ATA_DRDY)) {
|
||||
printf ("Device ata%d not ready\n", num);
|
||||
return 1;
|
||||
}
|
||||
|
||||
debug ("Stautus of device check: %d\n", status);
|
||||
|
||||
writeb (0x00, port[num].ioaddr.feature_addr);
|
||||
|
||||
writeb (0x00, port[num].ioaddr.nsect_addr);
|
||||
writeb (0x00, port[num].ioaddr.lbal_addr);
|
||||
writeb (0x00, port[num].ioaddr.lbam_addr);
|
||||
writeb (0x00, port[num].ioaddr.lbah_addr);
|
||||
|
||||
writeb (ATA_DEVICE_OBS, port[num].ioaddr.device_addr);
|
||||
writeb (ATA_CMD_IDLE, port[num].ioaddr.command_addr);
|
||||
|
||||
status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 30000, 0);
|
||||
if ((status & (ATA_BUSY | ATA_ERR))) {
|
||||
printf ("Error waiting for disk spin up: status 0x%02x\n",
|
||||
status);
|
||||
port[num].dev_mask &= ~0x01;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Wait for disk to enter Active state */
|
||||
do {
|
||||
msleep (10);
|
||||
status = check_power_mode (num);
|
||||
} while ((status == 0x00) || (status == 0x80));
|
||||
|
||||
if (status == 0x01) {
|
||||
printf ("Falied waiting for disk to spin up\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Return value is not the usual here
|
||||
* 0x00 - Device stand by
|
||||
* 0x01 - Operation failed
|
||||
* 0x80 - Device idle
|
||||
* 0xff - Device active
|
||||
*/
|
||||
static u8 check_power_mode (int num)
|
||||
{
|
||||
u8 status = 0;
|
||||
u8 res = 0;
|
||||
if (!(port[num].dev_mask & 0x01)) {
|
||||
debug ("Device ata%d is not present\n", num);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!(sata_chk_status (&port[num].ioaddr, 0) & ATA_DRDY)) {
|
||||
printf ("Device ata%d not ready\n", num);
|
||||
return 1;
|
||||
}
|
||||
|
||||
writeb (0, port[num].ioaddr.feature_addr);
|
||||
writeb (0, port[num].ioaddr.nsect_addr);
|
||||
writeb (0, port[num].ioaddr.lbal_addr);
|
||||
writeb (0, port[num].ioaddr.lbam_addr);
|
||||
writeb (0, port[num].ioaddr.lbah_addr);
|
||||
|
||||
writeb (ATA_DEVICE_OBS, port[num].ioaddr.device_addr);
|
||||
writeb (ATA_CMD_CHK_POWER, port[num].ioaddr.command_addr);
|
||||
|
||||
status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 5000, 0);
|
||||
if ((status & (ATA_BUSY | ATA_ERR))) {
|
||||
printf
|
||||
("Error waiting for check power mode complete : status 0x%02x\n",
|
||||
status);
|
||||
port[num].dev_mask &= ~0x01;
|
||||
return 1;
|
||||
}
|
||||
res = readb (port[num].ioaddr.nsect_addr);
|
||||
debug ("Check powermode: %d\n", res);
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
static void sata_port (struct sata_ioports *ioport)
|
||||
{
|
||||
ioport->data_addr = ioport->cmd_addr + ATA_REG_DATA;
|
||||
ioport->error_addr = ioport->cmd_addr + ATA_REG_ERR;
|
||||
ioport->feature_addr = ioport->cmd_addr + ATA_REG_FEATURE;
|
||||
ioport->nsect_addr = ioport->cmd_addr + ATA_REG_NSECT;
|
||||
ioport->lbal_addr = ioport->cmd_addr + ATA_REG_LBAL;
|
||||
ioport->lbam_addr = ioport->cmd_addr + ATA_REG_LBAM;
|
||||
ioport->lbah_addr = ioport->cmd_addr + ATA_REG_LBAH;
|
||||
ioport->device_addr = ioport->cmd_addr + ATA_REG_DEVICE;
|
||||
ioport->status_addr = ioport->cmd_addr + ATA_REG_STATUS;
|
||||
ioport->command_addr = ioport->cmd_addr + ATA_REG_CMD;
|
||||
}
|
||||
|
||||
static u8 wait_for_irq (int num, unsigned int max)
|
||||
{
|
||||
|
||||
u32 port = iobase[5];
|
||||
switch (num) {
|
||||
case 0:
|
||||
port += VND_TF_CNST_CH0;
|
||||
break;
|
||||
case 1:
|
||||
port += VND_TF_CNST_CH1;
|
||||
break;
|
||||
case 2:
|
||||
port += VND_TF_CNST_CH2;
|
||||
break;
|
||||
case 3:
|
||||
port += VND_TF_CNST_CH3;
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
|
||||
do {
|
||||
if (readl (port) & VND_TF_CNST_INTST) {
|
||||
break;
|
||||
}
|
||||
udelay (1000);
|
||||
max--;
|
||||
} while ((max > 0));
|
||||
|
||||
return (max == 0);
|
||||
}
|
||||
|
||||
static u8 sata_busy_wait (struct sata_ioports *ioaddr, int bits,
|
||||
unsigned int max, u8 usealtstatus)
|
||||
{
|
||||
u8 status;
|
||||
|
||||
do {
|
||||
if (!((status = sata_chk_status (ioaddr, usealtstatus)) & bits)) {
|
||||
break;
|
||||
}
|
||||
udelay (1000);
|
||||
max--;
|
||||
} while ((status & bits) && (max > 0));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static u8 sata_chk_status (struct sata_ioports *ioaddr, u8 usealtstatus)
|
||||
{
|
||||
if (!usealtstatus) {
|
||||
return readb (ioaddr->status_addr);
|
||||
} else {
|
||||
return readb (ioaddr->altstatus_addr);
|
||||
}
|
||||
}
|
||||
|
||||
static void msleep (int count)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
udelay (1000);
|
||||
}
|
||||
|
||||
/* Read up to 255 sectors
|
||||
*
|
||||
* Returns sectors read
|
||||
*/
|
||||
static u8 do_one_read (int device, ulong block, u8 blkcnt, u16 * buff,
|
||||
uchar lba48)
|
||||
{
|
||||
|
||||
u8 sr = 0;
|
||||
u8 status;
|
||||
u64 blknr = (u64) block;
|
||||
|
||||
if (!(sata_chk_status (&port[device].ioaddr, 0) & ATA_DRDY)) {
|
||||
printf ("Device ata%d not ready\n", device);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set up transfer */
|
||||
#ifdef CONFIG_LBA48
|
||||
if (lba48) {
|
||||
/* write high bits */
|
||||
writeb (0, port[device].ioaddr.nsect_addr);
|
||||
writeb ((blknr >> 24) & 0xFF, port[device].ioaddr.lbal_addr);
|
||||
writeb ((blknr >> 32) & 0xFF, port[device].ioaddr.lbam_addr);
|
||||
writeb ((blknr >> 40) & 0xFF, port[device].ioaddr.lbah_addr);
|
||||
}
|
||||
#endif
|
||||
writeb (blkcnt, port[device].ioaddr.nsect_addr);
|
||||
writeb (((blknr) >> 0) & 0xFF, port[device].ioaddr.lbal_addr);
|
||||
writeb ((blknr >> 8) & 0xFF, port[device].ioaddr.lbam_addr);
|
||||
writeb ((blknr >> 16) & 0xFF, port[device].ioaddr.lbah_addr);
|
||||
|
||||
#ifdef CONFIG_LBA48
|
||||
if (lba48) {
|
||||
writeb (ATA_LBA, port[device].ioaddr.device_addr);
|
||||
writeb (ATA_CMD_PIO_READ_EXT, port[device].ioaddr.command_addr);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
writeb (ATA_LBA | ((blknr >> 24) & 0xF),
|
||||
port[device].ioaddr.device_addr);
|
||||
writeb (ATA_CMD_PIO_READ, port[device].ioaddr.command_addr);
|
||||
}
|
||||
|
||||
status = sata_busy_wait (&port[device].ioaddr, ATA_BUSY, 10000, 1);
|
||||
|
||||
if (status & ATA_BUSY) {
|
||||
u8 err = 0;
|
||||
|
||||
printf ("Device %d not responding status %d\n", device, status);
|
||||
err = readb (port[device].ioaddr.error_addr);
|
||||
printf ("Error reg = 0x%x\n", err);
|
||||
|
||||
return (sr);
|
||||
}
|
||||
while (blkcnt--) {
|
||||
|
||||
if (wait_for_irq (device, 500)) {
|
||||
printf ("ata%u irq failed\n", device);
|
||||
return sr;
|
||||
}
|
||||
|
||||
status = sata_chk_status (&port[device].ioaddr, 0);
|
||||
if (status & ATA_ERR) {
|
||||
printf ("ata%u error %d\n", device,
|
||||
readb (port[device].ioaddr.error_addr));
|
||||
return sr;
|
||||
}
|
||||
/* Read one sector */
|
||||
input_data (&port[device].ioaddr, buff, ATA_SECTOR_WORDS);
|
||||
buff += ATA_SECTOR_WORDS;
|
||||
sr++;
|
||||
|
||||
}
|
||||
return sr;
|
||||
}
|
||||
|
||||
ulong sata_read (int device, ulong block, lbaint_t blkcnt, void *buff)
|
||||
{
|
||||
ulong n = 0, sread;
|
||||
u16 *buffer = (u16 *) buff;
|
||||
u8 status = 0;
|
||||
u64 blknr = (u64) block;
|
||||
unsigned char lba48 = 0;
|
||||
|
||||
#ifdef CONFIG_LBA48
|
||||
if (blknr > 0xfffffff) {
|
||||
if (!sata_dev_desc[device].lba48) {
|
||||
printf ("Drive doesn't support 48-bit addressing\n");
|
||||
return 0;
|
||||
}
|
||||
/* more than 28 bits used, use 48bit mode */
|
||||
lba48 = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
while (blkcnt > 0) {
|
||||
|
||||
if (blkcnt > 255) {
|
||||
sread = 255;
|
||||
} else {
|
||||
sread = blkcnt;
|
||||
}
|
||||
|
||||
status = do_one_read (device, blknr, sread, buffer, lba48);
|
||||
if (status != sread) {
|
||||
printf ("Read failed\n");
|
||||
return n;
|
||||
}
|
||||
|
||||
blkcnt -= sread;
|
||||
blknr += sread;
|
||||
n += sread;
|
||||
buffer += sread * ATA_SECTOR_WORDS;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
ulong sata_write (int device, ulong block, lbaint_t blkcnt, const void *buff)
|
||||
{
|
||||
ulong n = 0;
|
||||
u16 *buffer = (u16 *) buff;
|
||||
unsigned char status = 0, num = 0;
|
||||
u64 blknr = (u64) block;
|
||||
#ifdef CONFIG_LBA48
|
||||
unsigned char lba48 = 0;
|
||||
|
||||
if (blknr > 0xfffffff) {
|
||||
if (!sata_dev_desc[device].lba48) {
|
||||
printf ("Drive doesn't support 48-bit addressing\n");
|
||||
return 0;
|
||||
}
|
||||
/* more than 28 bits used, use 48bit mode */
|
||||
lba48 = 1;
|
||||
}
|
||||
#endif
|
||||
/*Port Number */
|
||||
num = device;
|
||||
|
||||
while (blkcnt-- > 0) {
|
||||
status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 500, 0);
|
||||
if (status & ATA_BUSY) {
|
||||
printf ("ata%u failed to respond\n", port[num].port_no);
|
||||
return n;
|
||||
}
|
||||
#ifdef CONFIG_LBA48
|
||||
if (lba48) {
|
||||
/* write high bits */
|
||||
writeb (0, port[num].ioaddr.nsect_addr);
|
||||
writeb ((blknr >> 24) & 0xFF,
|
||||
port[num].ioaddr.lbal_addr);
|
||||
writeb ((blknr >> 32) & 0xFF,
|
||||
port[num].ioaddr.lbam_addr);
|
||||
writeb ((blknr >> 40) & 0xFF,
|
||||
port[num].ioaddr.lbah_addr);
|
||||
}
|
||||
#endif
|
||||
writeb (1, port[num].ioaddr.nsect_addr);
|
||||
writeb ((blknr >> 0) & 0xFF, port[num].ioaddr.lbal_addr);
|
||||
writeb ((blknr >> 8) & 0xFF, port[num].ioaddr.lbam_addr);
|
||||
writeb ((blknr >> 16) & 0xFF, port[num].ioaddr.lbah_addr);
|
||||
#ifdef CONFIG_LBA48
|
||||
if (lba48) {
|
||||
writeb (ATA_LBA, port[num].ioaddr.device_addr);
|
||||
writeb (ATA_CMD_PIO_WRITE_EXT, port[num].ioaddr.command_addr);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
writeb (ATA_LBA | ((blknr >> 24) & 0xF),
|
||||
port[num].ioaddr.device_addr);
|
||||
writeb (ATA_CMD_PIO_WRITE, port[num].ioaddr.command_addr);
|
||||
}
|
||||
|
||||
msleep (50);
|
||||
/*may take up to 4 sec */
|
||||
status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 4000, 0);
|
||||
if ((status & (ATA_DRQ | ATA_BUSY | ATA_ERR)) != ATA_DRQ) {
|
||||
printf ("Error no DRQ dev %d blk %ld: sts 0x%02x\n",
|
||||
device, (ulong) blknr, status);
|
||||
return (n);
|
||||
}
|
||||
|
||||
output_data (&port[num].ioaddr, buffer, ATA_SECTOR_WORDS);
|
||||
readb (port[num].ioaddr.altstatus_addr);
|
||||
udelay (50);
|
||||
|
||||
++n;
|
||||
++blknr;
|
||||
buffer += ATA_SECTOR_WORDS;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/* Driver implementation */
|
||||
static u8 sil_get_device_cache_line (pci_dev_t pdev)
|
||||
{
|
||||
u8 cache_line = 0;
|
||||
pci_read_config_byte (pdev, PCI_CACHE_LINE_SIZE, &cache_line);
|
||||
return cache_line;
|
||||
}
|
||||
|
||||
int init_sata (int dev)
|
||||
{
|
||||
static u8 init_done = 0;
|
||||
static int res = 1;
|
||||
pci_dev_t devno;
|
||||
u8 cls = 0;
|
||||
u16 cmd = 0;
|
||||
u32 sconf = 0;
|
||||
|
||||
if (init_done) {
|
||||
return res;
|
||||
}
|
||||
|
||||
init_done = 1;
|
||||
|
||||
if ((devno = pci_find_device (SIL_VEND_ID, SIL3114_DEVICE_ID, 0)) == -1) {
|
||||
res = 1;
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Read out all BARs, even though we only use MMIO from BAR5 */
|
||||
pci_read_config_dword (devno, PCI_BASE_ADDRESS_0, &iobase[0]);
|
||||
pci_read_config_dword (devno, PCI_BASE_ADDRESS_1, &iobase[1]);
|
||||
pci_read_config_dword (devno, PCI_BASE_ADDRESS_2, &iobase[2]);
|
||||
pci_read_config_dword (devno, PCI_BASE_ADDRESS_3, &iobase[3]);
|
||||
pci_read_config_dword (devno, PCI_BASE_ADDRESS_4, &iobase[4]);
|
||||
pci_read_config_dword (devno, PCI_BASE_ADDRESS_5, &iobase[5]);
|
||||
|
||||
if ((iobase[0] == 0xFFFFFFFF) || (iobase[1] == 0xFFFFFFFF) ||
|
||||
(iobase[2] == 0xFFFFFFFF) || (iobase[3] == 0xFFFFFFFF) ||
|
||||
(iobase[4] == 0xFFFFFFFF) || (iobase[5] == 0xFFFFFFFF)) {
|
||||
printf ("Error no base addr for SATA controller\n");
|
||||
res = 1;
|
||||
return res;
|
||||
}
|
||||
|
||||
/* mask off unused bits */
|
||||
iobase[0] &= 0xfffffffc;
|
||||
iobase[1] &= 0xfffffff8;
|
||||
iobase[2] &= 0xfffffffc;
|
||||
iobase[3] &= 0xfffffff8;
|
||||
iobase[4] &= 0xfffffff0;
|
||||
iobase[5] &= 0xfffffc00;
|
||||
|
||||
/* from sata_sil in Linux kernel */
|
||||
cls = sil_get_device_cache_line (devno);
|
||||
if (cls) {
|
||||
cls >>= 3;
|
||||
cls++; /* cls = (line_size/8)+1 */
|
||||
writel (cls << 8 | cls, iobase[5] + VND_FIFOCFG_CH0);
|
||||
writel (cls << 8 | cls, iobase[5] + VND_FIFOCFG_CH1);
|
||||
writel (cls << 8 | cls, iobase[5] + VND_FIFOCFG_CH2);
|
||||
writel (cls << 8 | cls, iobase[5] + VND_FIFOCFG_CH3);
|
||||
} else {
|
||||
printf ("Cache line not set. Driver may not function\n");
|
||||
}
|
||||
|
||||
/* Enable operation */
|
||||
pci_read_config_word (devno, PCI_COMMAND, &cmd);
|
||||
cmd |= PCI_COMMAND_MASTER | PCI_COMMAND_IO | PCI_COMMAND_MEMORY;
|
||||
pci_write_config_word (devno, PCI_COMMAND, cmd);
|
||||
|
||||
/* Disable interrupt usage */
|
||||
pci_read_config_dword (devno, VND_SYSCONFSTAT, &sconf);
|
||||
sconf |= (VND_SYSCONFSTAT_CHN_0_INTBLOCK | VND_SYSCONFSTAT_CHN_1_INTBLOCK);
|
||||
pci_write_config_dword (devno, VND_SYSCONFSTAT, sconf);
|
||||
|
||||
res = 0;
|
||||
return res;
|
||||
}
|
||||
|
||||
int reset_sata(int dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check if device is connected to port */
|
||||
int sata_bus_probe (int portno)
|
||||
{
|
||||
u32 port = iobase[5];
|
||||
u32 val;
|
||||
switch (portno) {
|
||||
case 0:
|
||||
port += VND_SSTATUS_CH0;
|
||||
break;
|
||||
case 1:
|
||||
port += VND_SSTATUS_CH1;
|
||||
break;
|
||||
case 2:
|
||||
port += VND_SSTATUS_CH2;
|
||||
break;
|
||||
case 3:
|
||||
port += VND_SSTATUS_CH3;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
val = readl (port);
|
||||
if ((val & SATA_DET_PRES) == SATA_DET_PRES) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int sata_phy_reset (int portno)
|
||||
{
|
||||
u32 port = iobase[5];
|
||||
u32 val;
|
||||
switch (portno) {
|
||||
case 0:
|
||||
port += VND_SCONTROL_CH0;
|
||||
break;
|
||||
case 1:
|
||||
port += VND_SCONTROL_CH1;
|
||||
break;
|
||||
case 2:
|
||||
port += VND_SCONTROL_CH2;
|
||||
break;
|
||||
case 3:
|
||||
port += VND_SCONTROL_CH3;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
val = readl (port);
|
||||
writel (val | SATA_SC_DET_RST, port);
|
||||
msleep (150);
|
||||
writel (val & ~SATA_SC_DET_RST, port);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int scan_sata (int dev)
|
||||
{
|
||||
/* A bit brain dead, but the code has a legacy */
|
||||
switch (dev) {
|
||||
case 0:
|
||||
port[0].port_no = 0;
|
||||
port[0].ioaddr.cmd_addr = iobase[5] + VND_TF0_CH0;
|
||||
port[0].ioaddr.altstatus_addr = port[0].ioaddr.ctl_addr =
|
||||
(iobase[5] + VND_TF2_CH0) | ATA_PCI_CTL_OFS;
|
||||
port[0].ioaddr.bmdma_addr = iobase[5] + VND_BMDMA_CH0;
|
||||
break;
|
||||
#if (CONFIG_SYS_SATA_MAX_DEVICE >= 1)
|
||||
case 1:
|
||||
port[1].port_no = 0;
|
||||
port[1].ioaddr.cmd_addr = iobase[5] + VND_TF0_CH1;
|
||||
port[1].ioaddr.altstatus_addr = port[1].ioaddr.ctl_addr =
|
||||
(iobase[5] + VND_TF2_CH1) | ATA_PCI_CTL_OFS;
|
||||
port[1].ioaddr.bmdma_addr = iobase[5] + VND_BMDMA_CH1;
|
||||
break;
|
||||
#elif (CONFIG_SYS_SATA_MAX_DEVICE >= 2)
|
||||
case 2:
|
||||
port[2].port_no = 0;
|
||||
port[2].ioaddr.cmd_addr = iobase[5] + VND_TF0_CH2;
|
||||
port[2].ioaddr.altstatus_addr = port[2].ioaddr.ctl_addr =
|
||||
(iobase[5] + VND_TF2_CH2) | ATA_PCI_CTL_OFS;
|
||||
port[2].ioaddr.bmdma_addr = iobase[5] + VND_BMDMA_CH2;
|
||||
break;
|
||||
#elif (CONFIG_SYS_SATA_MAX_DEVICE >= 3)
|
||||
case 3:
|
||||
port[3].port_no = 0;
|
||||
port[3].ioaddr.cmd_addr = iobase[5] + VND_TF0_CH3;
|
||||
port[3].ioaddr.altstatus_addr = port[3].ioaddr.ctl_addr =
|
||||
(iobase[5] + VND_TF2_CH3) | ATA_PCI_CTL_OFS;
|
||||
port[3].ioaddr.bmdma_addr = iobase[5] + VND_BMDMA_CH3;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
printf ("Tried to scan unknown port: ata%d\n", dev);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Initialize other registers */
|
||||
sata_port (&port[dev].ioaddr);
|
||||
|
||||
/* Check for attached device */
|
||||
if (!sata_bus_probe (dev)) {
|
||||
port[dev].port_state = 0;
|
||||
debug ("SATA#%d port is not present\n", dev);
|
||||
} else {
|
||||
debug ("SATA#%d port is present\n", dev);
|
||||
if (sata_bus_softreset (dev)) {
|
||||
/* soft reset failed, try a hard one */
|
||||
sata_phy_reset (dev);
|
||||
if (sata_bus_softreset (dev)) {
|
||||
port[dev].port_state = 0;
|
||||
} else {
|
||||
port[dev].port_state = 1;
|
||||
}
|
||||
} else {
|
||||
port[dev].port_state = 1;
|
||||
}
|
||||
}
|
||||
if (port[dev].port_state == 1) {
|
||||
/* Probe device and set xfer mode */
|
||||
sata_identify (dev, 0);
|
||||
set_Feature_cmd (dev, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright (C) Excito Elektronik i Skåne AB, All rights reserved.
|
||||
* Author: Tor Krill <tor@excito.com>
|
||||
*/
|
||||
|
||||
#ifndef SATA_SIL3114_H
|
||||
#define SATA_SIL3114_H
|
||||
|
||||
struct sata_ioports {
|
||||
unsigned long cmd_addr;
|
||||
unsigned long data_addr;
|
||||
unsigned long error_addr;
|
||||
unsigned long feature_addr;
|
||||
unsigned long nsect_addr;
|
||||
unsigned long lbal_addr;
|
||||
unsigned long lbam_addr;
|
||||
unsigned long lbah_addr;
|
||||
unsigned long device_addr;
|
||||
unsigned long status_addr;
|
||||
unsigned long command_addr;
|
||||
unsigned long altstatus_addr;
|
||||
unsigned long ctl_addr;
|
||||
unsigned long bmdma_addr;
|
||||
unsigned long scr_addr;
|
||||
};
|
||||
|
||||
struct sata_port {
|
||||
unsigned char port_no; /* primary=0, secondary=1 */
|
||||
struct sata_ioports ioaddr; /* ATA cmd/ctl/dma reg blks */
|
||||
unsigned char ctl_reg;
|
||||
unsigned char last_ctl;
|
||||
unsigned char port_state; /* 1-port is available and */
|
||||
/* 0-port is not available */
|
||||
unsigned char dev_mask;
|
||||
};
|
||||
|
||||
/* Missing ata defines */
|
||||
#define ATA_CMD_STANDBY 0xE2
|
||||
#define ATA_CMD_STANDBYNOW1 0xE0
|
||||
#define ATA_CMD_IDLE 0xE3
|
||||
#define ATA_CMD_IDLEIMMEDIATE 0xE1
|
||||
|
||||
/* Defines for SIL3114 chip */
|
||||
|
||||
/* PCI defines */
|
||||
#define SIL_VEND_ID 0x1095
|
||||
#define SIL3114_DEVICE_ID 0x3114
|
||||
|
||||
/* some vendor specific registers */
|
||||
#define VND_SYSCONFSTAT 0x88 /* System Configuration Status and Command */
|
||||
#define VND_SYSCONFSTAT_CHN_0_INTBLOCK (1<<22)
|
||||
#define VND_SYSCONFSTAT_CHN_1_INTBLOCK (1<<23)
|
||||
#define VND_SYSCONFSTAT_CHN_2_INTBLOCK (1<<24)
|
||||
#define VND_SYSCONFSTAT_CHN_3_INTBLOCK (1<<25)
|
||||
|
||||
/* internal registers mapped by BAR5 */
|
||||
/* SATA Control*/
|
||||
#define VND_SCONTROL_CH0 0x100
|
||||
#define VND_SCONTROL_CH1 0x180
|
||||
#define VND_SCONTROL_CH2 0x300
|
||||
#define VND_SCONTROL_CH3 0x380
|
||||
|
||||
#define SATA_SC_IPM_T2P (1<<16)
|
||||
#define SATA_SC_IPM_T2S (2<<16)
|
||||
#define SATA_SC_SPD_1_5 (1<<4)
|
||||
#define SATA_SC_SPD_3_0 (2<<4)
|
||||
#define SATA_SC_DET_RST (1) /* ATA Reset sequence */
|
||||
#define SATA_SC_DET_PDIS (4) /* PHY Disable */
|
||||
|
||||
/* SATA Status */
|
||||
#define VND_SSTATUS_CH0 0x104
|
||||
#define VND_SSTATUS_CH1 0x184
|
||||
#define VND_SSTATUS_CH2 0x304
|
||||
#define VND_SSTATUS_CH3 0x384
|
||||
|
||||
#define SATA_SS_IPM_ACTIVE (1<<8)
|
||||
#define SATA_SS_IPM_PARTIAL (2<<8)
|
||||
#define SATA_SS_IPM_SLUMBER (6<<8)
|
||||
#define SATA_SS_SPD_1_5 (1<<4)
|
||||
#define SATA_SS_SPD_3_0 (2<<4)
|
||||
#define SATA_DET_P_NOPHY (1) /* Device presence but no PHY connection established */
|
||||
#define SATA_DET_PRES (3) /* Device presence and active PHY */
|
||||
#define SATA_DET_OFFLINE (4) /* Device offline or in loopback mode */
|
||||
|
||||
/* Task file registers in BAR5 mapping */
|
||||
#define VND_TF0_CH0 0x80
|
||||
#define VND_TF0_CH1 0xc0
|
||||
#define VND_TF0_CH2 0x280
|
||||
#define VND_TF0_CH3 0x2c0
|
||||
#define VND_TF1_CH0 0x88
|
||||
#define VND_TF1_CH1 0xc8
|
||||
#define VND_TF1_CH2 0x288
|
||||
#define VND_TF1_CH3 0x2c8
|
||||
#define VND_TF2_CH0 0x88
|
||||
#define VND_TF2_CH1 0xc8
|
||||
#define VND_TF2_CH2 0x288
|
||||
#define VND_TF2_CH3 0x2c8
|
||||
|
||||
#define VND_BMDMA_CH0 0x00
|
||||
#define VND_BMDMA_CH1 0x08
|
||||
#define VND_BMDMA_CH2 0x200
|
||||
#define VND_BMDMA_CH3 0x208
|
||||
#define VND_BMDMA2_CH0 0x10
|
||||
#define VND_BMDMA2_CH1 0x18
|
||||
#define VND_BMDMA2_CH2 0x210
|
||||
#define VND_BMDMA2_CH3 0x218
|
||||
|
||||
/* FIFO control */
|
||||
#define VND_FIFOCFG_CH0 0x40
|
||||
#define VND_FIFOCFG_CH1 0x44
|
||||
#define VND_FIFOCFG_CH2 0x240
|
||||
#define VND_FIFOCFG_CH3 0x244
|
||||
|
||||
/* Task File configuration and status */
|
||||
#define VND_TF_CNST_CH0 0xa0
|
||||
#define VND_TF_CNST_CH1 0xe0
|
||||
#define VND_TF_CNST_CH2 0x2a0
|
||||
#define VND_TF_CNST_CH3 0x2e0
|
||||
|
||||
#define VND_TF_CNST_BFCMD (1<<1)
|
||||
#define VND_TF_CNST_CHNRST (1<<2)
|
||||
#define VND_TF_CNST_VDMA (1<<10)
|
||||
#define VND_TF_CNST_INTST (1<<11)
|
||||
#define VND_TF_CNST_WDTO (1<<12)
|
||||
#define VND_TF_CNST_WDEN (1<<13)
|
||||
#define VND_TF_CNST_WDIEN (1<<14)
|
||||
|
||||
/* for testing */
|
||||
#define VND_SSDR 0x04c /* System Software Data Register */
|
||||
#define VND_FMACS 0x050 /* Flash Memory Address control and status */
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,32 @@
|
||||
menuconfig AXI
|
||||
bool "AXI bus drivers"
|
||||
help
|
||||
Support AXI (Advanced eXtensible Interface) busses, a on-chip
|
||||
interconnect specification for managing functional blocks in SoC
|
||||
designs, which is also often used in designs involving FPGAs (e.g.
|
||||
communication with IP cores in Xilinx FPGAs).
|
||||
|
||||
These types of busses expose a virtual address space that can be
|
||||
accessed using different address widths (8, 16, and 32 are supported
|
||||
for now).
|
||||
|
||||
Other similar bus architectures may be compatible as well.
|
||||
|
||||
if AXI
|
||||
|
||||
config IHS_AXI
|
||||
bool "Enable IHS AXI driver"
|
||||
depends on DM
|
||||
help
|
||||
Support for gdsys Integrated Hardware Systems Advanced eXtensible
|
||||
Interface (IHS AXI) bus on a gdsys IHS FPGA used to communicate with
|
||||
IP cores in the FPGA (e.g. video transmitter cores).
|
||||
|
||||
config AXI_SANDBOX
|
||||
bool "Enable AXI sandbox driver"
|
||||
depends on DM
|
||||
help
|
||||
Support AXI (Advanced eXtensible Interface) emulation for the sandbox
|
||||
environment.
|
||||
|
||||
endif
|
||||
@@ -0,0 +1,11 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
# (C) Copyright 2017
|
||||
# Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
|
||||
#
|
||||
|
||||
obj-$(CONFIG_AXI) += axi-uclass.o
|
||||
obj-$(CONFIG_IHS_AXI) += ihs_axi.o
|
||||
obj-$(CONFIG_SANDBOX) += axi-emul-uclass.o
|
||||
obj-$(CONFIG_SANDBOX) += sandbox_store.o
|
||||
obj-$(CONFIG_AXI_SANDBOX) += axi_sandbox.o
|
||||
@@ -0,0 +1,85 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2018
|
||||
* Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <axi.h>
|
||||
#include <dm.h>
|
||||
#include <dm/device-internal.h>
|
||||
#include <asm/axi.h>
|
||||
|
||||
int axi_sandbox_get_emul(struct udevice *bus, ulong address,
|
||||
enum axi_size_t size, struct udevice **emulp)
|
||||
{
|
||||
struct udevice *dev;
|
||||
u32 reg[2];
|
||||
uint offset;
|
||||
|
||||
switch (size) {
|
||||
case AXI_SIZE_8:
|
||||
offset = 1;
|
||||
break;
|
||||
case AXI_SIZE_16:
|
||||
offset = 2;
|
||||
break;
|
||||
case AXI_SIZE_32:
|
||||
offset = 4;
|
||||
break;
|
||||
default:
|
||||
debug("%s: Unknown AXI transfer size '%d'", bus->name, size);
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note: device_find_* don't activate the devices; they're activated
|
||||
* as-needed below.
|
||||
*/
|
||||
for (device_find_first_child(bus, &dev);
|
||||
dev;
|
||||
device_find_next_child(&dev)) {
|
||||
int ret;
|
||||
|
||||
ret = dev_read_u32_array(dev, "reg", reg, ARRAY_SIZE(reg));
|
||||
if (ret) {
|
||||
debug("%s: Could not read 'reg' property of %s\n",
|
||||
bus->name, dev->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Does the transfer's address fall into this device's address
|
||||
* space?
|
||||
*/
|
||||
if (address >= reg[0] && address <= reg[0] + reg[1] - offset) {
|
||||
/* If yes, activate it... */
|
||||
if (device_probe(dev)) {
|
||||
debug("%s: Could not activate %s\n",
|
||||
bus->name, dev->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* ...and return it */
|
||||
*emulp = dev;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
int axi_get_store(struct udevice *dev, u8 **storep)
|
||||
{
|
||||
struct axi_emul_ops *ops = axi_emul_get_ops(dev);
|
||||
|
||||
if (!ops->get_store)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->get_store(dev, storep);
|
||||
}
|
||||
|
||||
UCLASS_DRIVER(axi_emul) = {
|
||||
.id = UCLASS_AXI_EMUL,
|
||||
.name = "axi_emul",
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2017
|
||||
* Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <axi.h>
|
||||
|
||||
int axi_read(struct udevice *dev, ulong address, void *data,
|
||||
enum axi_size_t size)
|
||||
{
|
||||
struct axi_ops *ops = axi_get_ops(dev);
|
||||
|
||||
if (!ops->read)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->read(dev, address, data, size);
|
||||
}
|
||||
|
||||
int axi_write(struct udevice *dev, ulong address, void *data,
|
||||
enum axi_size_t size)
|
||||
{
|
||||
struct axi_ops *ops = axi_get_ops(dev);
|
||||
|
||||
if (!ops->write)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->write(dev, address, data, size);
|
||||
}
|
||||
|
||||
UCLASS_DRIVER(axi) = {
|
||||
.id = UCLASS_AXI,
|
||||
.name = "axi",
|
||||
.post_bind = dm_scan_fdt_dev,
|
||||
.flags = DM_UC_FLAG_SEQ_ALIAS,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2018
|
||||
* Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <axi.h>
|
||||
#include <dm.h>
|
||||
#include <asm/axi.h>
|
||||
|
||||
/*
|
||||
* This driver implements a AXI bus for the sandbox architecture for testing
|
||||
* purposes.
|
||||
*
|
||||
* The bus forwards every access to it to a special AXI emulation device (which
|
||||
* it gets via the axi_emul_get_ops function) that implements a simple
|
||||
* read/write storage.
|
||||
*
|
||||
* The emulator device must still be contained in the device tree in the usual
|
||||
* way, since configuration data for the storage is read from the DT.
|
||||
*/
|
||||
|
||||
static int axi_sandbox_read(struct udevice *bus, ulong address, void *data,
|
||||
enum axi_size_t size)
|
||||
{
|
||||
struct axi_emul_ops *ops;
|
||||
struct udevice *emul;
|
||||
int ret;
|
||||
|
||||
/* Get emulator device */
|
||||
ret = axi_sandbox_get_emul(bus, address, size, &emul);
|
||||
if (ret)
|
||||
return ret == -ENODEV ? 0 : ret;
|
||||
/* Forward all reads to the AXI emulator */
|
||||
ops = axi_emul_get_ops(emul);
|
||||
if (!ops || !ops->read)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->read(emul, address, data, size);
|
||||
}
|
||||
|
||||
static int axi_sandbox_write(struct udevice *bus, ulong address, void *data,
|
||||
enum axi_size_t size)
|
||||
{
|
||||
struct axi_emul_ops *ops;
|
||||
struct udevice *emul;
|
||||
int ret;
|
||||
|
||||
/* Get emulator device */
|
||||
ret = axi_sandbox_get_emul(bus, address, size, &emul);
|
||||
if (ret)
|
||||
return ret == -ENODEV ? 0 : ret;
|
||||
/* Forward all writes to the AXI emulator */
|
||||
ops = axi_emul_get_ops(emul);
|
||||
if (!ops || !ops->write)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->write(emul, address, data, size);
|
||||
}
|
||||
|
||||
static const struct udevice_id axi_sandbox_ids[] = {
|
||||
{ .compatible = "sandbox,axi" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
static const struct axi_ops axi_sandbox_ops = {
|
||||
.read = axi_sandbox_read,
|
||||
.write = axi_sandbox_write,
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(axi_sandbox_bus) = {
|
||||
.name = "axi_sandbox_bus",
|
||||
.id = UCLASS_AXI,
|
||||
.of_match = axi_sandbox_ids,
|
||||
.ops = &axi_sandbox_ops,
|
||||
};
|
||||
@@ -0,0 +1,293 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2016
|
||||
* Dirk Eibach, Guntermann & Drunck GmbH, dirk.eibach@gdsys.cc
|
||||
*
|
||||
* (C) Copyright 2017, 2018
|
||||
* Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <axi.h>
|
||||
#include <dm.h>
|
||||
#include <regmap.h>
|
||||
|
||||
/**
|
||||
* struct ihs_axi_regs - Structure for the register map of a IHS AXI device
|
||||
* @interrupt_status: Status register to indicate certain events (e.g.
|
||||
* error during transfer, transfer complete, etc.)
|
||||
* @interrupt_enable_control: Register to both control which statuses will be
|
||||
* indicated in the interrupt_status register, and
|
||||
* to change bus settings
|
||||
* @address_lsb: Least significant 16-bit word of the address of a
|
||||
* device to transfer data from/to
|
||||
* @address_msb: Most significant 16-bit word of the address of a
|
||||
* device to transfer data from/to
|
||||
* @write_data_lsb: Least significant 16-bit word of the data to be
|
||||
* written to a device
|
||||
* @write_data_msb: Most significant 16-bit word of the data to be
|
||||
* written to a device
|
||||
* @read_data_lsb: Least significant 16-bit word of the data read
|
||||
* from a device
|
||||
* @read_data_msb: Most significant 16-bit word of the data read
|
||||
* from a device
|
||||
*/
|
||||
struct ihs_axi_regs {
|
||||
u16 interrupt_status;
|
||||
u16 interrupt_enable_control;
|
||||
u16 address_lsb;
|
||||
u16 address_msb;
|
||||
u16 write_data_lsb;
|
||||
u16 write_data_msb;
|
||||
u16 read_data_lsb;
|
||||
u16 read_data_msb;
|
||||
};
|
||||
|
||||
/**
|
||||
* ihs_axi_set() - Convenience macro to set values in register map
|
||||
* @map: The register map to write to
|
||||
* @member: The member of the ihs_axi_regs structure to write
|
||||
* @val: The value to write to the register map
|
||||
*/
|
||||
#define ihs_axi_set(map, member, val) \
|
||||
regmap_set(map, struct ihs_axi_regs, member, val)
|
||||
|
||||
/**
|
||||
* ihs_axi_get() - Convenience macro to read values from register map
|
||||
* @map: The register map to read from
|
||||
* @member: The member of the ihs_axi_regs structure to read
|
||||
* @valp: Pointer to a buffer to receive the value read
|
||||
*/
|
||||
#define ihs_axi_get(map, member, valp) \
|
||||
regmap_get(map, struct ihs_axi_regs, member, valp)
|
||||
|
||||
/**
|
||||
* struct ihs_axi_priv - Private data structure of IHS AXI devices
|
||||
* @map: Register map for the IHS AXI device
|
||||
*/
|
||||
struct ihs_axi_priv {
|
||||
struct regmap *map;
|
||||
};
|
||||
|
||||
/**
|
||||
* enum status_reg - Description of bits in the interrupt_status register
|
||||
* @STATUS_READ_COMPLETE_EVENT: A read transfer was completed
|
||||
* @STATUS_WRITE_COMPLETE_EVENT: A write transfer was completed
|
||||
* @STATUS_TIMEOUT_EVENT: A timeout has occurred during the transfer
|
||||
* @STATUS_ERROR_EVENT: A error has occurred during the transfer
|
||||
* @STATUS_AXI_INT: A AXI interrupt has occurred
|
||||
* @STATUS_READ_DATA_AVAILABLE: Data is available to be read
|
||||
* @STATUS_BUSY: The bus is busy
|
||||
* @STATUS_INIT_DONE: The bus has finished initializing
|
||||
*/
|
||||
enum status_reg {
|
||||
STATUS_READ_COMPLETE_EVENT = BIT(15),
|
||||
STATUS_WRITE_COMPLETE_EVENT = BIT(14),
|
||||
STATUS_TIMEOUT_EVENT = BIT(13),
|
||||
STATUS_ERROR_EVENT = BIT(12),
|
||||
STATUS_AXI_INT = BIT(11),
|
||||
STATUS_READ_DATA_AVAILABLE = BIT(7),
|
||||
STATUS_BUSY = BIT(6),
|
||||
STATUS_INIT_DONE = BIT(5),
|
||||
};
|
||||
|
||||
/**
|
||||
* enum control_reg - Description of bit fields in the interrupt_enable_control
|
||||
* register
|
||||
* @CONTROL_READ_COMPLETE_EVENT_ENABLE: STATUS_READ_COMPLETE_EVENT will be
|
||||
* raised in the interrupt_status register
|
||||
* @CONTROL_WRITE_COMPLETE_EVENT_ENABLE: STATUS_WRITE_COMPLETE_EVENT will be
|
||||
* raised in the interrupt_status register
|
||||
* @CONTROL_TIMEOUT_EVENT_ENABLE: STATUS_TIMEOUT_EVENT will be raised in
|
||||
* the interrupt_status register
|
||||
* @CONTROL_ERROR_EVENT_ENABLE: STATUS_ERROR_EVENT will be raised in
|
||||
* the interrupt_status register
|
||||
* @CONTROL_AXI_INT_ENABLE: STATUS_AXI_INT will be raised in the
|
||||
* interrupt_status register
|
||||
* @CONTROL_CMD_NOP: Configure bus to send a NOP command
|
||||
* for the next transfer
|
||||
* @CONTROL_CMD_WRITE: Configure bus to do a write transfer
|
||||
* @CONTROL_CMD_WRITE_POST_INC: Auto-increment address after write
|
||||
* transfer
|
||||
* @CONTROL_CMD_READ: Configure bus to do a read transfer
|
||||
* @CONTROL_CMD_READ_POST_INC: Auto-increment address after read
|
||||
* transfer
|
||||
*/
|
||||
enum control_reg {
|
||||
CONTROL_READ_COMPLETE_EVENT_ENABLE = BIT(15),
|
||||
CONTROL_WRITE_COMPLETE_EVENT_ENABLE = BIT(14),
|
||||
CONTROL_TIMEOUT_EVENT_ENABLE = BIT(13),
|
||||
CONTROL_ERROR_EVENT_ENABLE = BIT(12),
|
||||
CONTROL_AXI_INT_ENABLE = BIT(11),
|
||||
|
||||
CONTROL_CMD_NOP = 0x0,
|
||||
CONTROL_CMD_WRITE = 0x8,
|
||||
CONTROL_CMD_WRITE_POST_INC = 0x9,
|
||||
CONTROL_CMD_READ = 0xa,
|
||||
CONTROL_CMD_READ_POST_INC = 0xb,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum axi_cmd - Determine if transfer is read or write transfer
|
||||
* @AXI_CMD_READ: The transfer should be a read transfer
|
||||
* @AXI_CMD_WRITE: The transfer should be a write transfer
|
||||
*/
|
||||
enum axi_cmd {
|
||||
AXI_CMD_READ,
|
||||
AXI_CMD_WRITE,
|
||||
};
|
||||
|
||||
/**
|
||||
* ihs_axi_transfer() - Run transfer on the AXI bus
|
||||
* @bus: The AXI bus device on which to run the transfer on
|
||||
* @address: The address to use in the transfer (i.e. which address to
|
||||
* read/write from/to)
|
||||
* @cmd: Should the transfer be a read or write transfer?
|
||||
*
|
||||
* Return: 0 if OK, -ve on error
|
||||
*/
|
||||
static int ihs_axi_transfer(struct udevice *bus, ulong address,
|
||||
enum axi_cmd cmd)
|
||||
{
|
||||
struct ihs_axi_priv *priv = dev_get_priv(bus);
|
||||
/* Try waiting for events up to 10 times */
|
||||
const uint WAIT_TRIES = 10;
|
||||
u16 wait_mask = STATUS_TIMEOUT_EVENT |
|
||||
STATUS_ERROR_EVENT;
|
||||
u16 complete_flag;
|
||||
u16 status;
|
||||
uint k;
|
||||
|
||||
if (cmd == AXI_CMD_READ) {
|
||||
complete_flag = STATUS_READ_COMPLETE_EVENT;
|
||||
cmd = CONTROL_CMD_READ;
|
||||
} else {
|
||||
complete_flag = STATUS_WRITE_COMPLETE_EVENT;
|
||||
cmd = CONTROL_CMD_WRITE;
|
||||
}
|
||||
|
||||
wait_mask |= complete_flag;
|
||||
|
||||
/* Lower 16 bit */
|
||||
ihs_axi_set(priv->map, address_lsb, address & 0xffff);
|
||||
/* Upper 16 bit */
|
||||
ihs_axi_set(priv->map, address_msb, (address >> 16) & 0xffff);
|
||||
|
||||
ihs_axi_set(priv->map, interrupt_status, wait_mask);
|
||||
ihs_axi_set(priv->map, interrupt_enable_control, cmd);
|
||||
|
||||
for (k = WAIT_TRIES; k > 0; --k) {
|
||||
ihs_axi_get(priv->map, interrupt_status, &status);
|
||||
if (status & wait_mask)
|
||||
break;
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* k == 0 -> Tries ran out with no event we were waiting for actually
|
||||
* occurring.
|
||||
*/
|
||||
if (!k)
|
||||
ihs_axi_get(priv->map, interrupt_status, &status);
|
||||
|
||||
if (status & complete_flag)
|
||||
return 0;
|
||||
|
||||
if (status & STATUS_ERROR_EVENT) {
|
||||
debug("%s: Error occurred during transfer\n", bus->name);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
debug("%s: Transfer timed out\n", bus->name);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/*
|
||||
* API
|
||||
*/
|
||||
|
||||
static int ihs_axi_read(struct udevice *dev, ulong address, void *data,
|
||||
enum axi_size_t size)
|
||||
{
|
||||
struct ihs_axi_priv *priv = dev_get_priv(dev);
|
||||
int ret;
|
||||
u16 data_lsb, data_msb;
|
||||
u32 *p = data;
|
||||
|
||||
if (size != AXI_SIZE_32) {
|
||||
debug("%s: transfer size '%d' not supported\n",
|
||||
dev->name, size);
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
ret = ihs_axi_transfer(dev, address, AXI_CMD_READ);
|
||||
if (ret < 0) {
|
||||
debug("%s: Error during AXI transfer (err = %d)\n",
|
||||
dev->name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ihs_axi_get(priv->map, read_data_lsb, &data_lsb);
|
||||
ihs_axi_get(priv->map, read_data_msb, &data_msb);
|
||||
|
||||
/* Assemble data from two 16-bit words */
|
||||
*p = (data_msb << 16) | data_lsb;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ihs_axi_write(struct udevice *dev, ulong address, void *data,
|
||||
enum axi_size_t size)
|
||||
{
|
||||
struct ihs_axi_priv *priv = dev_get_priv(dev);
|
||||
int ret;
|
||||
u32 *p = data;
|
||||
|
||||
if (size != AXI_SIZE_32) {
|
||||
debug("%s: transfer size '%d' not supported\n",
|
||||
dev->name, size);
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
/* Lower 16 bit */
|
||||
ihs_axi_set(priv->map, write_data_lsb, *p & 0xffff);
|
||||
/* Upper 16 bit */
|
||||
ihs_axi_set(priv->map, write_data_msb, (*p >> 16) & 0xffff);
|
||||
|
||||
ret = ihs_axi_transfer(dev, address, AXI_CMD_WRITE);
|
||||
if (ret < 0) {
|
||||
debug("%s: Error during AXI transfer (err = %d)\n",
|
||||
dev->name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id ihs_axi_ids[] = {
|
||||
{ .compatible = "gdsys,ihs_axi" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
static const struct axi_ops ihs_axi_ops = {
|
||||
.read = ihs_axi_read,
|
||||
.write = ihs_axi_write,
|
||||
};
|
||||
|
||||
static int ihs_axi_probe(struct udevice *dev)
|
||||
{
|
||||
struct ihs_axi_priv *priv = dev_get_priv(dev);
|
||||
|
||||
regmap_init_mem(dev_ofnode(dev), &priv->map);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
U_BOOT_DRIVER(ihs_axi_bus) = {
|
||||
.name = "ihs_axi_bus",
|
||||
.id = UCLASS_AXI,
|
||||
.of_match = ihs_axi_ids,
|
||||
.ops = &ihs_axi_ops,
|
||||
.priv_auto_alloc_size = sizeof(struct ihs_axi_priv),
|
||||
.probe = ihs_axi_probe,
|
||||
};
|
||||
@@ -0,0 +1,123 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2018
|
||||
* Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <axi.h>
|
||||
#include <dm.h>
|
||||
|
||||
/**
|
||||
* struct sandbox_store_priv - Private data structure of a AXI store device
|
||||
* @store: The buffer holding the device's internal memory, which is read from
|
||||
* and written to using the driver's methods
|
||||
*/
|
||||
struct sandbox_store_priv {
|
||||
u8 *store;
|
||||
};
|
||||
|
||||
/**
|
||||
* copy_axi_data() - Copy data from source to destination with a given AXI
|
||||
* transfer width
|
||||
* @src: Pointer to the data source from where data will be read
|
||||
* @dst: Pointer to the data destination where data will be written to
|
||||
* @size: Size of the data to be copied given by a axi_size_t enum value
|
||||
*
|
||||
* Return: 0 if OK, -ve on error
|
||||
*/
|
||||
static int copy_axi_data(void *src, void *dst, enum axi_size_t size)
|
||||
{
|
||||
switch (size) {
|
||||
case AXI_SIZE_8:
|
||||
*((u8 *)dst) = *((u8 *)src);
|
||||
return 0;
|
||||
case AXI_SIZE_16:
|
||||
*((u16 *)dst) = be16_to_cpu(*((u16 *)src));
|
||||
return 0;
|
||||
case AXI_SIZE_32:
|
||||
*((u32 *)dst) = be32_to_cpu(*((u32 *)src));
|
||||
return 0;
|
||||
default:
|
||||
debug("%s: Unknown AXI transfer size '%d'\n", __func__, size);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int sandbox_store_read(struct udevice *dev, ulong address, void *data,
|
||||
enum axi_size_t size)
|
||||
{
|
||||
struct sandbox_store_priv *priv = dev_get_priv(dev);
|
||||
|
||||
return copy_axi_data(priv->store + address, data, size);
|
||||
}
|
||||
|
||||
static int sandbox_store_write(struct udevice *dev, ulong address, void *data,
|
||||
enum axi_size_t size)
|
||||
{
|
||||
struct sandbox_store_priv *priv = dev_get_priv(dev);
|
||||
|
||||
return copy_axi_data(data, priv->store + address, size);
|
||||
}
|
||||
|
||||
static int sandbox_store_get_store(struct udevice *dev, u8 **store)
|
||||
{
|
||||
struct sandbox_store_priv *priv = dev_get_priv(dev);
|
||||
|
||||
*store = priv->store;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id sandbox_store_ids[] = {
|
||||
{ .compatible = "sandbox,sandbox_store" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
static const struct axi_emul_ops sandbox_store_ops = {
|
||||
.read = sandbox_store_read,
|
||||
.write = sandbox_store_write,
|
||||
.get_store = sandbox_store_get_store,
|
||||
};
|
||||
|
||||
static int sandbox_store_probe(struct udevice *dev)
|
||||
{
|
||||
struct sandbox_store_priv *priv = dev_get_priv(dev);
|
||||
u32 reg[2];
|
||||
int ret;
|
||||
|
||||
ret = dev_read_u32_array(dev, "reg", reg, ARRAY_SIZE(reg));
|
||||
if (ret) {
|
||||
debug("%s: Could not read 'reg' property\n", dev->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate the device's internal storage that will be read
|
||||
* from/written to
|
||||
*/
|
||||
priv->store = calloc(reg[1], 1);
|
||||
if (!priv->store)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sandbox_store_remove(struct udevice *dev)
|
||||
{
|
||||
struct sandbox_store_priv *priv = dev_get_priv(dev);
|
||||
|
||||
free(priv->store);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
U_BOOT_DRIVER(sandbox_axi_store) = {
|
||||
.name = "sandbox_axi_store",
|
||||
.id = UCLASS_AXI_EMUL,
|
||||
.of_match = sandbox_store_ids,
|
||||
.ops = &sandbox_store_ops,
|
||||
.priv_auto_alloc_size = sizeof(struct sandbox_store_priv),
|
||||
.probe = sandbox_store_probe,
|
||||
.remove = sandbox_store_remove,
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
X86DIR = x86emu
|
||||
|
||||
obj-y = atibios.o biosemu.o besys.o bios.o \
|
||||
$(X86DIR)/decode.o \
|
||||
$(X86DIR)/ops2.o \
|
||||
$(X86DIR)/ops.o \
|
||||
$(X86DIR)/prim_ops.o \
|
||||
$(X86DIR)/sys.o \
|
||||
$(X86DIR)/debug.o
|
||||
|
||||
ccflags-y := -I$(srctree)/$(src) -I$(srctree)/$(src)/include \
|
||||
$(if $(CONFIG_PPC),-D__PPC__ -D__BIG_ENDIAN__)
|
||||
@@ -0,0 +1,645 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* Video BOOT Graphics Card POST Module
|
||||
*
|
||||
* ========================================================================
|
||||
* Copyright (C) 2007 Freescale Semiconductor, Inc.
|
||||
* Jason Jin <Jason.jin@freescale.com>
|
||||
*
|
||||
* Copyright (C) 1991-2004 SciTech Software, Inc. All rights reserved.
|
||||
*
|
||||
* This file may be distributed and/or modified under the terms of the
|
||||
* GNU General Public License version 2.0 as published by the Free
|
||||
* Software Foundation and appearing in the file LICENSE.GPL included
|
||||
* in the packaging of this file.
|
||||
*
|
||||
* Licensees holding a valid Commercial License for this product from
|
||||
* SciTech Software, Inc. may use this file in accordance with the
|
||||
* Commercial License Agreement provided with the Software.
|
||||
*
|
||||
* This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
|
||||
* THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE.
|
||||
*
|
||||
* See http://www.scitechsoft.com/license/ for information about
|
||||
* the licensing options available and how to purchase a Commercial
|
||||
* License Agreement.
|
||||
*
|
||||
* Contact license@scitechsoft.com if any conditions of this licensing
|
||||
* are not clear to you, or you have questions about licensing options.
|
||||
*
|
||||
* ========================================================================
|
||||
*
|
||||
* Language: ANSI C
|
||||
* Environment: Linux Kernel
|
||||
* Developer: Kendall Bennett
|
||||
*
|
||||
* Description: Module to implement booting PCI/AGP controllers on the
|
||||
* bus. We use the x86 real mode emulator to run the BIOS on
|
||||
* graphics controllers to bring the cards up.
|
||||
*
|
||||
* Note that at present this module does *not* support
|
||||
* multiple controllers.
|
||||
*
|
||||
* The orignal name of this file is warmboot.c.
|
||||
* Jason ported this file to u-boot to run the ATI video card
|
||||
* BIOS in u-boot.
|
||||
****************************************************************************/
|
||||
#include <common.h>
|
||||
#include <bios_emul.h>
|
||||
#include <errno.h>
|
||||
#include <malloc.h>
|
||||
#include <vbe.h>
|
||||
#include "biosemui.h"
|
||||
|
||||
/* Length of the BIOS image */
|
||||
#define MAX_BIOSLEN (128 * 1024L)
|
||||
|
||||
/* Place to save PCI BAR's that we change and later restore */
|
||||
static u32 saveROMBaseAddress;
|
||||
static u32 saveBaseAddress10;
|
||||
static u32 saveBaseAddress14;
|
||||
static u32 saveBaseAddress18;
|
||||
static u32 saveBaseAddress20;
|
||||
|
||||
/* Addres im memory of VBE region */
|
||||
const int vbe_offset = 0x2000;
|
||||
|
||||
#ifdef CONFIG_FRAMEBUFFER_SET_VESA_MODE
|
||||
static const void *bios_ptr(const void *buf, BE_VGAInfo *vga_info,
|
||||
u32 x86_dword_ptr)
|
||||
{
|
||||
u32 seg_ofs, flat;
|
||||
|
||||
seg_ofs = le32_to_cpu(x86_dword_ptr);
|
||||
flat = ((seg_ofs & 0xffff0000) >> 12) | (seg_ofs & 0xffff);
|
||||
if (flat >= 0xc0000)
|
||||
return vga_info->BIOSImage + flat - 0xc0000;
|
||||
else
|
||||
return buf + (flat - vbe_offset);
|
||||
}
|
||||
|
||||
static int atibios_debug_mode(BE_VGAInfo *vga_info, RMREGS *regs,
|
||||
int vesa_mode, struct vbe_mode_info *mode_info)
|
||||
{
|
||||
void *buffer = (void *)(M.mem_base + vbe_offset);
|
||||
u16 buffer_seg = (((unsigned long)vbe_offset) >> 4) & 0xff00;
|
||||
u16 buffer_adr = ((unsigned long)vbe_offset) & 0xffff;
|
||||
struct vesa_mode_info *vm;
|
||||
struct vbe_info *info;
|
||||
const u16 *modes_bios, *ptr;
|
||||
u16 *modes;
|
||||
int size;
|
||||
|
||||
debug("VBE: Getting information\n");
|
||||
regs->e.eax = VESA_GET_INFO;
|
||||
regs->e.esi = buffer_seg;
|
||||
regs->e.edi = buffer_adr;
|
||||
info = buffer;
|
||||
memset(info, '\0', sizeof(*info));
|
||||
strcpy(info->signature, "VBE2");
|
||||
BE_int86(0x10, regs, regs);
|
||||
if (regs->e.eax != 0x4f) {
|
||||
debug("VESA_GET_INFO: error %x\n", regs->e.eax);
|
||||
return -ENOSYS;
|
||||
}
|
||||
debug("version %x\n", le16_to_cpu(info->version));
|
||||
debug("oem '%s'\n", (char *)bios_ptr(buffer, vga_info,
|
||||
info->oem_string_ptr));
|
||||
debug("vendor '%s'\n", (char *)bios_ptr(buffer, vga_info,
|
||||
info->vendor_name_ptr));
|
||||
debug("product '%s'\n", (char *)bios_ptr(buffer, vga_info,
|
||||
info->product_name_ptr));
|
||||
debug("rev '%s'\n", (char *)bios_ptr(buffer, vga_info,
|
||||
info->product_rev_ptr));
|
||||
modes_bios = bios_ptr(buffer, vga_info, info->modes_ptr);
|
||||
debug("Modes: ");
|
||||
for (ptr = modes_bios; *ptr != 0xffff; ptr++)
|
||||
debug("%x ", le16_to_cpu(*ptr));
|
||||
debug("\nmemory %dMB\n", le16_to_cpu(info->total_memory) >> 4);
|
||||
size = (ptr - modes_bios) * sizeof(u16) + 2;
|
||||
modes = malloc(size);
|
||||
if (!modes)
|
||||
return -ENOMEM;
|
||||
memcpy(modes, modes_bios, size);
|
||||
|
||||
regs->e.eax = VESA_GET_CUR_MODE;
|
||||
BE_int86(0x10, regs, regs);
|
||||
if (regs->e.eax != 0x4f) {
|
||||
debug("VESA_GET_CUR_MODE: error %x\n", regs->e.eax);
|
||||
return -ENOSYS;
|
||||
}
|
||||
debug("Current mode %x\n", regs->e.ebx);
|
||||
|
||||
for (ptr = modes; *ptr != 0xffff; ptr++) {
|
||||
int mode = le16_to_cpu(*ptr);
|
||||
bool linear_ok;
|
||||
int attr;
|
||||
|
||||
debug("Mode %x: ", mode);
|
||||
memset(buffer, '\0', sizeof(struct vbe_mode_info));
|
||||
regs->e.eax = VESA_GET_MODE_INFO;
|
||||
regs->e.ebx = 0;
|
||||
regs->e.ecx = mode;
|
||||
regs->e.edx = 0;
|
||||
regs->e.esi = buffer_seg;
|
||||
regs->e.edi = buffer_adr;
|
||||
BE_int86(0x10, regs, regs);
|
||||
if (regs->e.eax != 0x4f) {
|
||||
debug("VESA_GET_MODE_INFO: error %x\n", regs->e.eax);
|
||||
continue;
|
||||
}
|
||||
memcpy(mode_info->mode_info_block, buffer,
|
||||
sizeof(struct vesa_mode_info));
|
||||
mode_info->valid = true;
|
||||
vm = &mode_info->vesa;
|
||||
attr = le16_to_cpu(vm->mode_attributes);
|
||||
linear_ok = attr & 0x80;
|
||||
debug("res %d x %d, %d bpp, mm %d, (Linear %s, attr %02x)\n",
|
||||
le16_to_cpu(vm->x_resolution),
|
||||
le16_to_cpu(vm->y_resolution),
|
||||
vm->bits_per_pixel, vm->memory_model,
|
||||
linear_ok ? "OK" : "not available",
|
||||
attr);
|
||||
debug("\tRGB pos=%d,%d,%d, size=%d,%d,%d\n",
|
||||
vm->red_mask_pos, vm->green_mask_pos, vm->blue_mask_pos,
|
||||
vm->red_mask_size, vm->green_mask_size,
|
||||
vm->blue_mask_size);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int atibios_set_vesa_mode(RMREGS *regs, int vesa_mode,
|
||||
struct vbe_mode_info *mode_info)
|
||||
{
|
||||
void *buffer = (void *)(M.mem_base + vbe_offset);
|
||||
u16 buffer_seg = (((unsigned long)vbe_offset) >> 4) & 0xff00;
|
||||
u16 buffer_adr = ((unsigned long)vbe_offset) & 0xffff;
|
||||
struct vesa_mode_info *vm;
|
||||
|
||||
debug("VBE: Setting VESA mode %#04x\n", vesa_mode);
|
||||
regs->e.eax = VESA_SET_MODE;
|
||||
regs->e.ebx = vesa_mode;
|
||||
/* request linear framebuffer mode and don't clear display */
|
||||
regs->e.ebx |= (1 << 14) | (1 << 15);
|
||||
BE_int86(0x10, regs, regs);
|
||||
if (regs->e.eax != 0x4f) {
|
||||
debug("VESA_SET_MODE: error %x\n", regs->e.eax);
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
memset(buffer, '\0', sizeof(struct vbe_mode_info));
|
||||
debug("VBE: Geting info for VESA mode %#04x\n", vesa_mode);
|
||||
regs->e.eax = VESA_GET_MODE_INFO;
|
||||
regs->e.ecx = vesa_mode;
|
||||
regs->e.esi = buffer_seg;
|
||||
regs->e.edi = buffer_adr;
|
||||
BE_int86(0x10, regs, regs);
|
||||
if (regs->e.eax != 0x4f) {
|
||||
debug("VESA_GET_MODE_INFO: error %x\n", regs->e.eax);
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
memcpy(mode_info->mode_info_block, buffer,
|
||||
sizeof(struct vesa_mode_info));
|
||||
mode_info->valid = true;
|
||||
mode_info->video_mode = vesa_mode;
|
||||
vm = &mode_info->vesa;
|
||||
vm->x_resolution = le16_to_cpu(vm->x_resolution);
|
||||
vm->y_resolution = le16_to_cpu(vm->y_resolution);
|
||||
vm->bytes_per_scanline = le16_to_cpu(vm->bytes_per_scanline);
|
||||
vm->phys_base_ptr = le32_to_cpu(vm->phys_base_ptr);
|
||||
vm->mode_attributes = le16_to_cpu(vm->mode_attributes);
|
||||
debug("VBE: Init complete\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_FRAMEBUFFER_SET_VESA_MODE */
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
pcidev - PCI device info for the video card on the bus to boot
|
||||
vga_info - BIOS emulator VGA info structure
|
||||
|
||||
REMARKS:
|
||||
This function executes the BIOS POST code on the controller. We assume that
|
||||
at this stage the controller has its I/O and memory space enabled and
|
||||
that all other controllers are in a disabled state.
|
||||
****************************************************************************/
|
||||
#ifdef CONFIG_DM_PCI
|
||||
static void PCI_doBIOSPOST(struct udevice *pcidev, BE_VGAInfo *vga_info,
|
||||
int vesa_mode, struct vbe_mode_info *mode_info)
|
||||
#else
|
||||
static void PCI_doBIOSPOST(pci_dev_t pcidev, BE_VGAInfo *vga_info,
|
||||
int vesa_mode, struct vbe_mode_info *mode_info)
|
||||
#endif
|
||||
{
|
||||
RMREGS regs;
|
||||
RMSREGS sregs;
|
||||
#ifdef CONFIG_DM_PCI
|
||||
pci_dev_t bdf;
|
||||
#endif
|
||||
|
||||
/* Determine the value to store in AX for BIOS POST. Per the PCI specs,
|
||||
AH must contain the bus and AL must contain the devfn, encoded as
|
||||
(dev << 3) | fn
|
||||
*/
|
||||
memset(®s, 0, sizeof(regs));
|
||||
memset(&sregs, 0, sizeof(sregs));
|
||||
#ifdef CONFIG_DM_PCI
|
||||
bdf = dm_pci_get_bdf(pcidev);
|
||||
regs.x.ax = (int)PCI_BUS(bdf) << 8 |
|
||||
(int)PCI_DEV(bdf) << 3 | (int)PCI_FUNC(bdf);
|
||||
#else
|
||||
regs.x.ax = ((int)PCI_BUS(pcidev) << 8) |
|
||||
((int)PCI_DEV(pcidev) << 3) | (int)PCI_FUNC(pcidev);
|
||||
#endif
|
||||
/*Setup the X86 emulator for the VGA BIOS*/
|
||||
BE_setVGA(vga_info);
|
||||
|
||||
/*Execute the BIOS POST code*/
|
||||
BE_callRealMode(0xC000, 0x0003, ®s, &sregs);
|
||||
|
||||
/*Cleanup and exit*/
|
||||
BE_getVGA(vga_info);
|
||||
|
||||
#ifdef CONFIG_FRAMEBUFFER_SET_VESA_MODE
|
||||
/* Useful for debugging */
|
||||
if (0)
|
||||
atibios_debug_mode(vga_info, ®s, vesa_mode, mode_info);
|
||||
if (vesa_mode != -1)
|
||||
atibios_set_vesa_mode(®s, vesa_mode, mode_info);
|
||||
#endif
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
pcidev - PCI device info for the video card on the bus
|
||||
bar - Place to return the base address register offset to use
|
||||
|
||||
RETURNS:
|
||||
The address to use to map the secondary BIOS (AGP devices)
|
||||
|
||||
REMARKS:
|
||||
Searches all the PCI base address registers for the device looking for a
|
||||
memory mapping that is large enough to hold our ROM BIOS. We usually end up
|
||||
finding the framebuffer mapping (usually BAR 0x10), and we use this mapping
|
||||
to map the BIOS for the device into. We use a mapping that is already
|
||||
assigned to the device to ensure the memory range will be passed through
|
||||
by any PCI->PCI or AGP->PCI bridge that may be present.
|
||||
|
||||
NOTE: Usually this function is only used for AGP devices, but it may be
|
||||
used for PCI devices that have already been POST'ed and the BIOS
|
||||
ROM base address has been zero'ed out.
|
||||
|
||||
NOTE: This function leaves the original memory aperture disabled by leaving
|
||||
it programmed to all 1's. It must be restored to the correct value
|
||||
later.
|
||||
****************************************************************************/
|
||||
#ifdef CONFIG_DM_PCI
|
||||
static u32 PCI_findBIOSAddr(struct udevice *pcidev, int *bar)
|
||||
#else
|
||||
static u32 PCI_findBIOSAddr(pci_dev_t pcidev, int *bar)
|
||||
#endif
|
||||
{
|
||||
u32 base, size;
|
||||
|
||||
for (*bar = 0x10; *bar <= 0x14; (*bar) += 4) {
|
||||
#ifdef CONFIG_DM_PCI
|
||||
dm_pci_read_config32(pcidev, *bar, &base);
|
||||
#else
|
||||
pci_read_config_dword(pcidev, *bar, &base);
|
||||
#endif
|
||||
if (!(base & 0x1)) {
|
||||
#ifdef CONFIG_DM_PCI
|
||||
dm_pci_write_config32(pcidev, *bar, 0xFFFFFFFF);
|
||||
dm_pci_read_config32(pcidev, *bar, &size);
|
||||
#else
|
||||
pci_write_config_dword(pcidev, *bar, 0xFFFFFFFF);
|
||||
pci_read_config_dword(pcidev, *bar, &size);
|
||||
#endif
|
||||
size = ~(size & ~0xFF) + 1;
|
||||
if (size >= MAX_BIOSLEN)
|
||||
return base & ~0xFF;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
REMARKS:
|
||||
Some non-x86 Linux kernels map PCI relocateable I/O to values that
|
||||
are above 64K, which will not work with the BIOS image that requires
|
||||
the offset for the I/O ports to be a maximum of 16-bits. Ideally
|
||||
someone should fix the kernel to map the I/O ports for VGA compatible
|
||||
devices to a different location (or just all I/O ports since it is
|
||||
unlikely you can have enough devices in the machine to use up all
|
||||
64K of the I/O space - a total of more than 256 cards would be
|
||||
necessary).
|
||||
|
||||
Anyway to fix this we change all I/O mapped base registers and
|
||||
chop off the top bits.
|
||||
****************************************************************************/
|
||||
#ifdef CONFIG_DM_PCI
|
||||
static void PCI_fixupIObase(struct udevice *pcidev, int reg, u32 *base)
|
||||
#else
|
||||
static void PCI_fixupIObase(pci_dev_t pcidev, int reg, u32 * base)
|
||||
#endif
|
||||
{
|
||||
if ((*base & 0x1) && (*base > 0xFFFE)) {
|
||||
*base &= 0xFFFF;
|
||||
#ifdef CONFIG_DM_PCI
|
||||
dm_pci_write_config32(pcidev, reg, *base);
|
||||
#else
|
||||
pci_write_config_dword(pcidev, reg, *base);
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
pcidev - PCI device info for the video card on the bus
|
||||
|
||||
RETURNS:
|
||||
Pointers to the mapped BIOS image
|
||||
|
||||
REMARKS:
|
||||
Maps a pointer to the BIOS image on the graphics card on the PCI bus.
|
||||
****************************************************************************/
|
||||
#ifdef CONFIG_DM_PCI
|
||||
void *PCI_mapBIOSImage(struct udevice *pcidev)
|
||||
#else
|
||||
void *PCI_mapBIOSImage(pci_dev_t pcidev)
|
||||
#endif
|
||||
{
|
||||
u32 BIOSImageBus;
|
||||
int BIOSImageBAR;
|
||||
u8 *BIOSImage;
|
||||
|
||||
/*Save PCI BAR registers that might get changed*/
|
||||
#ifdef CONFIG_DM_PCI
|
||||
dm_pci_read_config32(pcidev, PCI_ROM_ADDRESS, &saveROMBaseAddress);
|
||||
dm_pci_read_config32(pcidev, PCI_BASE_ADDRESS_0, &saveBaseAddress10);
|
||||
dm_pci_read_config32(pcidev, PCI_BASE_ADDRESS_1, &saveBaseAddress14);
|
||||
dm_pci_read_config32(pcidev, PCI_BASE_ADDRESS_2, &saveBaseAddress18);
|
||||
dm_pci_read_config32(pcidev, PCI_BASE_ADDRESS_4, &saveBaseAddress20);
|
||||
#else
|
||||
pci_read_config_dword(pcidev, PCI_ROM_ADDRESS, &saveROMBaseAddress);
|
||||
pci_read_config_dword(pcidev, PCI_BASE_ADDRESS_0, &saveBaseAddress10);
|
||||
pci_read_config_dword(pcidev, PCI_BASE_ADDRESS_1, &saveBaseAddress14);
|
||||
pci_read_config_dword(pcidev, PCI_BASE_ADDRESS_2, &saveBaseAddress18);
|
||||
pci_read_config_dword(pcidev, PCI_BASE_ADDRESS_4, &saveBaseAddress20);
|
||||
#endif
|
||||
|
||||
/*Fix up I/O base registers to less than 64K */
|
||||
if(saveBaseAddress14 != 0)
|
||||
PCI_fixupIObase(pcidev, PCI_BASE_ADDRESS_1, &saveBaseAddress14);
|
||||
else
|
||||
PCI_fixupIObase(pcidev, PCI_BASE_ADDRESS_4, &saveBaseAddress20);
|
||||
|
||||
/* Some cards have problems that stop us from being able to read the
|
||||
BIOS image from the ROM BAR. To fix this we have to do some chipset
|
||||
specific programming for different cards to solve this problem.
|
||||
*/
|
||||
|
||||
BIOSImageBus = PCI_findBIOSAddr(pcidev, &BIOSImageBAR);
|
||||
if (BIOSImageBus == 0) {
|
||||
printf("Find bios addr error\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DM_PCI
|
||||
BIOSImage = dm_pci_bus_to_virt(pcidev, BIOSImageBus,
|
||||
PCI_REGION_MEM, 0, MAP_NOCACHE);
|
||||
|
||||
/*Change the PCI BAR registers to map it onto the bus.*/
|
||||
dm_pci_write_config32(pcidev, BIOSImageBAR, 0);
|
||||
dm_pci_write_config32(pcidev, PCI_ROM_ADDRESS, BIOSImageBus | 0x1);
|
||||
#else
|
||||
BIOSImage = pci_bus_to_virt(pcidev, BIOSImageBus,
|
||||
PCI_REGION_MEM, 0, MAP_NOCACHE);
|
||||
|
||||
/*Change the PCI BAR registers to map it onto the bus.*/
|
||||
pci_write_config_dword(pcidev, BIOSImageBAR, 0);
|
||||
pci_write_config_dword(pcidev, PCI_ROM_ADDRESS, BIOSImageBus | 0x1);
|
||||
#endif
|
||||
udelay(1);
|
||||
|
||||
/*Check that the BIOS image is valid. If not fail, or return the
|
||||
compiled in BIOS image if that option was enabled
|
||||
*/
|
||||
if (BIOSImage[0] != 0x55 || BIOSImage[1] != 0xAA || BIOSImage[2] == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return BIOSImage;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
pcidev - PCI device info for the video card on the bus
|
||||
|
||||
REMARKS:
|
||||
Unmaps the BIOS image for the device and restores framebuffer mappings
|
||||
****************************************************************************/
|
||||
#ifdef CONFIG_DM_PCI
|
||||
void PCI_unmapBIOSImage(struct udevice *pcidev, void *BIOSImage)
|
||||
{
|
||||
dm_pci_write_config32(pcidev, PCI_ROM_ADDRESS, saveROMBaseAddress);
|
||||
dm_pci_write_config32(pcidev, PCI_BASE_ADDRESS_0, saveBaseAddress10);
|
||||
dm_pci_write_config32(pcidev, PCI_BASE_ADDRESS_1, saveBaseAddress14);
|
||||
dm_pci_write_config32(pcidev, PCI_BASE_ADDRESS_2, saveBaseAddress18);
|
||||
dm_pci_write_config32(pcidev, PCI_BASE_ADDRESS_4, saveBaseAddress20);
|
||||
}
|
||||
#else
|
||||
void PCI_unmapBIOSImage(pci_dev_t pcidev, void *BIOSImage)
|
||||
{
|
||||
pci_write_config_dword(pcidev, PCI_ROM_ADDRESS, saveROMBaseAddress);
|
||||
pci_write_config_dword(pcidev, PCI_BASE_ADDRESS_0, saveBaseAddress10);
|
||||
pci_write_config_dword(pcidev, PCI_BASE_ADDRESS_1, saveBaseAddress14);
|
||||
pci_write_config_dword(pcidev, PCI_BASE_ADDRESS_2, saveBaseAddress18);
|
||||
pci_write_config_dword(pcidev, PCI_BASE_ADDRESS_4, saveBaseAddress20);
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
pcidev - PCI device info for the video card on the bus to boot
|
||||
VGAInfo - BIOS emulator VGA info structure
|
||||
|
||||
RETURNS:
|
||||
true if successfully initialised, false if not.
|
||||
|
||||
REMARKS:
|
||||
Loads and POST's the display controllers BIOS, directly from the BIOS
|
||||
image we can extract over the PCI bus.
|
||||
****************************************************************************/
|
||||
#ifdef CONFIG_DM_PCI
|
||||
static int PCI_postController(struct udevice *pcidev, uchar *bios_rom,
|
||||
int bios_len, BE_VGAInfo *vga_info,
|
||||
int vesa_mode, struct vbe_mode_info *mode_info)
|
||||
#else
|
||||
static int PCI_postController(pci_dev_t pcidev, uchar *bios_rom, int bios_len,
|
||||
BE_VGAInfo *vga_info, int vesa_mode,
|
||||
struct vbe_mode_info *mode_info)
|
||||
#endif
|
||||
{
|
||||
u32 bios_image_len;
|
||||
uchar *mapped_bios;
|
||||
uchar *copy_of_bios;
|
||||
#ifdef CONFIG_DM_PCI
|
||||
pci_dev_t bdf;
|
||||
#endif
|
||||
|
||||
if (bios_rom) {
|
||||
copy_of_bios = bios_rom;
|
||||
bios_image_len = bios_len;
|
||||
} else {
|
||||
/*
|
||||
* Allocate memory to store copy of BIOS from display
|
||||
* controller
|
||||
*/
|
||||
mapped_bios = PCI_mapBIOSImage(pcidev);
|
||||
if (mapped_bios == NULL) {
|
||||
printf("videoboot: Video ROM failed to map!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bios_image_len = mapped_bios[2] * 512;
|
||||
|
||||
copy_of_bios = malloc(bios_image_len);
|
||||
if (copy_of_bios == NULL) {
|
||||
printf("videoboot: Out of memory!\n");
|
||||
return false;
|
||||
}
|
||||
memcpy(copy_of_bios, mapped_bios, bios_image_len);
|
||||
PCI_unmapBIOSImage(pcidev, mapped_bios);
|
||||
}
|
||||
|
||||
/*Save information in vga_info structure*/
|
||||
#ifdef CONFIG_DM_PCI
|
||||
bdf = dm_pci_get_bdf(pcidev);
|
||||
vga_info->function = PCI_FUNC(bdf);
|
||||
vga_info->device = PCI_DEV(bdf);
|
||||
vga_info->bus = PCI_BUS(bdf);
|
||||
#else
|
||||
vga_info->function = PCI_FUNC(pcidev);
|
||||
vga_info->device = PCI_DEV(pcidev);
|
||||
vga_info->bus = PCI_BUS(pcidev);
|
||||
#endif
|
||||
vga_info->pcidev = pcidev;
|
||||
vga_info->BIOSImage = copy_of_bios;
|
||||
vga_info->BIOSImageLen = bios_image_len;
|
||||
|
||||
/*Now execute the BIOS POST for the device*/
|
||||
if (copy_of_bios[0] != 0x55 || copy_of_bios[1] != 0xAA) {
|
||||
printf("videoboot: Video ROM image is invalid!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
PCI_doBIOSPOST(pcidev, vga_info, vesa_mode, mode_info);
|
||||
|
||||
/*Reset the size of the BIOS image to the final size*/
|
||||
vga_info->BIOSImageLen = copy_of_bios[2] * 512;
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DM_PCI
|
||||
int biosemu_setup(struct udevice *pcidev, BE_VGAInfo **vga_infop)
|
||||
#else
|
||||
int biosemu_setup(pci_dev_t pcidev, BE_VGAInfo **vga_infop)
|
||||
#endif
|
||||
{
|
||||
BE_VGAInfo *VGAInfo;
|
||||
#ifdef CONFIG_DM_PCI
|
||||
pci_dev_t bdf = dm_pci_get_bdf(pcidev);
|
||||
|
||||
printf("videoboot: Booting PCI video card bus %d, function %d, device %d\n",
|
||||
PCI_BUS(bdf), PCI_FUNC(bdf), PCI_DEV(bdf));
|
||||
#else
|
||||
printf("videoboot: Booting PCI video card bus %d, function %d, device %d\n",
|
||||
PCI_BUS(pcidev), PCI_FUNC(pcidev), PCI_DEV(pcidev));
|
||||
#endif
|
||||
/*Initialise the x86 BIOS emulator*/
|
||||
if ((VGAInfo = malloc(sizeof(*VGAInfo))) == NULL) {
|
||||
printf("videoboot: Out of memory!\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
memset(VGAInfo, 0, sizeof(*VGAInfo));
|
||||
BE_init(0, 65536, VGAInfo, 0);
|
||||
*vga_infop = VGAInfo;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void biosemu_set_interrupt_handler(int intnum, int (*int_func)(void))
|
||||
{
|
||||
X86EMU_setupIntrFunc(intnum, (X86EMU_intrFuncs)int_func);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DM_PCI
|
||||
int biosemu_run(struct udevice *pcidev, uchar *bios_rom, int bios_len,
|
||||
BE_VGAInfo *vga_info, int clean_up, int vesa_mode,
|
||||
struct vbe_mode_info *mode_info)
|
||||
#else
|
||||
int biosemu_run(pci_dev_t pcidev, uchar *bios_rom, int bios_len,
|
||||
BE_VGAInfo *vga_info, int clean_up, int vesa_mode,
|
||||
struct vbe_mode_info *mode_info)
|
||||
#endif
|
||||
{
|
||||
/*Post all the display controller BIOS'es*/
|
||||
if (!PCI_postController(pcidev, bios_rom, bios_len, vga_info,
|
||||
vesa_mode, mode_info))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Cleanup and exit the emulator if requested. If the BIOS emulator
|
||||
* is needed after booting the card, we will not call BE_exit and
|
||||
* leave it enabled for further use (ie: VESA driver etc).
|
||||
*/
|
||||
if (clean_up) {
|
||||
BE_exit();
|
||||
if (vga_info->BIOSImage &&
|
||||
(ulong)(vga_info->BIOSImage) != 0xc0000)
|
||||
free(vga_info->BIOSImage);
|
||||
free(vga_info);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
pcidev - PCI device info for the video card on the bus to boot
|
||||
pVGAInfo - Place to return VGA info structure is requested
|
||||
cleanUp - true to clean up on exit, false to leave emulator active
|
||||
|
||||
REMARKS:
|
||||
Boots the PCI/AGP video card on the bus using the Video ROM BIOS image
|
||||
and the X86 BIOS emulator module.
|
||||
****************************************************************************/
|
||||
#ifdef CONFIG_DM_PCI
|
||||
int BootVideoCardBIOS(struct udevice *pcidev, BE_VGAInfo **pVGAInfo,
|
||||
int clean_up)
|
||||
#else
|
||||
int BootVideoCardBIOS(pci_dev_t pcidev, BE_VGAInfo **pVGAInfo, int clean_up)
|
||||
#endif
|
||||
{
|
||||
BE_VGAInfo *VGAInfo;
|
||||
int ret;
|
||||
|
||||
ret = biosemu_setup(pcidev, &VGAInfo);
|
||||
if (ret)
|
||||
return false;
|
||||
ret = biosemu_run(pcidev, NULL, 0, VGAInfo, clean_up, -1, NULL);
|
||||
if (ret)
|
||||
return false;
|
||||
|
||||
/* Return VGA info pointer if the caller requested it*/
|
||||
if (pVGAInfo)
|
||||
*pVGAInfo = VGAInfo;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,760 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* BIOS emulator and interface
|
||||
* to Realmode X86 Emulator Library
|
||||
*
|
||||
* ========================================================================
|
||||
*
|
||||
* Copyright (C) 2007 Freescale Semiconductor, Inc.
|
||||
* Jason Jin<Jason.jin@freescale.com>
|
||||
*
|
||||
* Copyright (C) 1991-2004 SciTech Software, Inc. All rights reserved.
|
||||
*
|
||||
* This file may be distributed and/or modified under the terms of the
|
||||
* GNU General Public License version 2.0 as published by the Free
|
||||
* Software Foundation and appearing in the file LICENSE.GPL included
|
||||
* in the packaging of this file.
|
||||
*
|
||||
* Licensees holding a valid Commercial License for this product from
|
||||
* SciTech Software, Inc. may use this file in accordance with the
|
||||
* Commercial License Agreement provided with the Software.
|
||||
*
|
||||
* This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
|
||||
* THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE.
|
||||
*
|
||||
* See http://www.scitechsoft.com/license/ for information about
|
||||
* the licensing options available and how to purchase a Commercial
|
||||
* License Agreement.
|
||||
*
|
||||
* Contact license@scitechsoft.com if any conditions of this licensing
|
||||
* are not clear to you, or you have questions about licensing options.
|
||||
*
|
||||
* ========================================================================
|
||||
*
|
||||
* Language: ANSI C
|
||||
* Environment: Any
|
||||
* Developer: Kendall Bennett
|
||||
*
|
||||
* Description: This file includes BIOS emulator I/O and memory access
|
||||
* functions.
|
||||
*
|
||||
* Jason ported this file to u-boot to run the ATI video card
|
||||
* BIOS in u-boot. Removed some emulate functions such as the
|
||||
* timer port access. Made all the VGA port except reading 0x3c3
|
||||
* be emulated. Seems like reading 0x3c3 should return the high
|
||||
* 16 bit of the io port.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#define __io
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include "biosemui.h"
|
||||
|
||||
/*------------------------- Global Variables ------------------------------*/
|
||||
|
||||
#ifndef CONFIG_X86EMU_RAW_IO
|
||||
static char *BE_biosDate = "08/14/99";
|
||||
static u8 BE_model = 0xFC;
|
||||
static u8 BE_submodel = 0x00;
|
||||
#endif
|
||||
|
||||
#undef DEBUG_IO_ACCESS
|
||||
|
||||
#ifdef DEBUG_IO_ACCESS
|
||||
#define debug_io(fmt, ...) printf(fmt, ##__VA_ARGS__)
|
||||
#else
|
||||
#define debug_io(x, b...)
|
||||
#endif
|
||||
|
||||
/*----------------------------- Implementation ----------------------------*/
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
addr - Emulator memory address to convert
|
||||
|
||||
RETURNS:
|
||||
Actual memory address to read or write the data
|
||||
|
||||
REMARKS:
|
||||
This function converts an emulator memory address in a 32-bit range to
|
||||
a real memory address that we wish to access. It handles splitting up the
|
||||
memory address space appropriately to access the emulator BIOS image, video
|
||||
memory and system BIOS etc.
|
||||
****************************************************************************/
|
||||
static u8 *BE_memaddr(u32 addr)
|
||||
{
|
||||
if (addr >= 0xC0000 && addr <= _BE_env.biosmem_limit) {
|
||||
return (u8*)(_BE_env.biosmem_base + addr - 0xC0000);
|
||||
} else if (addr > _BE_env.biosmem_limit && addr < 0xD0000) {
|
||||
DB(printf("BE_memaddr: address %#lx may be invalid!\n",
|
||||
(ulong)addr);)
|
||||
return (u8 *)M.mem_base;
|
||||
} else if (addr >= 0xA0000 && addr <= 0xBFFFF) {
|
||||
return (u8*)(_BE_env.busmem_base + addr - 0xA0000);
|
||||
}
|
||||
#ifdef CONFIG_X86EMU_RAW_IO
|
||||
else if (addr >= 0xD0000 && addr <= 0xFFFFF) {
|
||||
/* We map the real System BIOS directly on real PC's */
|
||||
DB(printf("BE_memaddr: System BIOS address %#lx\n",
|
||||
(ulong)addr);)
|
||||
return (u8 *)_BE_env.busmem_base + addr - 0xA0000;
|
||||
}
|
||||
#else
|
||||
else if (addr >= 0xFFFF5 && addr < 0xFFFFE) {
|
||||
/* Return a faked BIOS date string for non-x86 machines */
|
||||
debug_io("BE_memaddr - Returning BIOS date\n");
|
||||
return (u8 *)(BE_biosDate + addr - 0xFFFF5);
|
||||
} else if (addr == 0xFFFFE) {
|
||||
/* Return system model identifier for non-x86 machines */
|
||||
debug_io("BE_memaddr - Returning model\n");
|
||||
return &BE_model;
|
||||
} else if (addr == 0xFFFFF) {
|
||||
/* Return system submodel identifier for non-x86 machines */
|
||||
debug_io("BE_memaddr - Returning submodel\n");
|
||||
return &BE_submodel;
|
||||
}
|
||||
#endif
|
||||
else if (addr > M.mem_size - 1) {
|
||||
HALT_SYS();
|
||||
return (u8 *)M.mem_base;
|
||||
}
|
||||
|
||||
return (u8 *)(M.mem_base + addr);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
addr - Emulator memory address to read
|
||||
|
||||
RETURNS:
|
||||
Byte value read from emulator memory.
|
||||
|
||||
REMARKS:
|
||||
Reads a byte value from the emulator memory. We have three distinct memory
|
||||
regions that are handled differently, which this function handles.
|
||||
****************************************************************************/
|
||||
u8 X86API BE_rdb(u32 addr)
|
||||
{
|
||||
if (_BE_env.emulateVGA && addr >= 0xA0000 && addr <= 0xBFFFF)
|
||||
return 0;
|
||||
else {
|
||||
u8 val = readb_le(BE_memaddr(addr));
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
addr - Emulator memory address to read
|
||||
|
||||
RETURNS:
|
||||
Word value read from emulator memory.
|
||||
|
||||
REMARKS:
|
||||
Reads a word value from the emulator memory. We have three distinct memory
|
||||
regions that are handled differently, which this function handles.
|
||||
****************************************************************************/
|
||||
u16 X86API BE_rdw(u32 addr)
|
||||
{
|
||||
if (_BE_env.emulateVGA && addr >= 0xA0000 && addr <= 0xBFFFF)
|
||||
return 0;
|
||||
else {
|
||||
u8 *base = BE_memaddr(addr);
|
||||
u16 val = readw_le(base);
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
addr - Emulator memory address to read
|
||||
|
||||
RETURNS:
|
||||
Long value read from emulator memory.
|
||||
|
||||
REMARKS:
|
||||
Reads a 32-bit value from the emulator memory. We have three distinct memory
|
||||
regions that are handled differently, which this function handles.
|
||||
****************************************************************************/
|
||||
u32 X86API BE_rdl(u32 addr)
|
||||
{
|
||||
if (_BE_env.emulateVGA && addr >= 0xA0000 && addr <= 0xBFFFF)
|
||||
return 0;
|
||||
else {
|
||||
u8 *base = BE_memaddr(addr);
|
||||
u32 val = readl_le(base);
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
addr - Emulator memory address to read
|
||||
val - Value to store
|
||||
|
||||
REMARKS:
|
||||
Writes a byte value to emulator memory. We have three distinct memory
|
||||
regions that are handled differently, which this function handles.
|
||||
****************************************************************************/
|
||||
void X86API BE_wrb(u32 addr, u8 val)
|
||||
{
|
||||
if (!(_BE_env.emulateVGA && addr >= 0xA0000 && addr <= 0xBFFFF)) {
|
||||
writeb_le(BE_memaddr(addr), val);
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
addr - Emulator memory address to read
|
||||
val - Value to store
|
||||
|
||||
REMARKS:
|
||||
Writes a word value to emulator memory. We have three distinct memory
|
||||
regions that are handled differently, which this function handles.
|
||||
****************************************************************************/
|
||||
void X86API BE_wrw(u32 addr, u16 val)
|
||||
{
|
||||
if (!(_BE_env.emulateVGA && addr >= 0xA0000 && addr <= 0xBFFFF)) {
|
||||
u8 *base = BE_memaddr(addr);
|
||||
writew_le(base, val);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
addr - Emulator memory address to read
|
||||
val - Value to store
|
||||
|
||||
REMARKS:
|
||||
Writes a 32-bit value to emulator memory. We have three distinct memory
|
||||
regions that are handled differently, which this function handles.
|
||||
****************************************************************************/
|
||||
void X86API BE_wrl(u32 addr, u32 val)
|
||||
{
|
||||
if (!(_BE_env.emulateVGA && addr >= 0xA0000 && addr <= 0xBFFFF)) {
|
||||
u8 *base = BE_memaddr(addr);
|
||||
writel_le(base, val);
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined(CONFIG_X86EMU_RAW_IO)
|
||||
|
||||
/* For Non-Intel machines we may need to emulate some I/O port accesses that
|
||||
* the BIOS may try to access, such as the PCI config registers.
|
||||
*/
|
||||
|
||||
#define IS_TIMER_PORT(port) (0x40 <= port && port <= 0x43)
|
||||
#define IS_CMOS_PORT(port) (0x70 <= port && port <= 0x71)
|
||||
/*#define IS_VGA_PORT(port) (_BE_env.emulateVGA && 0x3C0 <= port && port <= 0x3DA)*/
|
||||
#define IS_VGA_PORT(port) (0x3C0 <= port && port <= 0x3DA)
|
||||
#define IS_PCI_PORT(port) (0xCF8 <= port && port <= 0xCFF)
|
||||
#define IS_SPKR_PORT(port) (port == 0x61)
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
port - Port to read from
|
||||
type - Type of access to perform
|
||||
|
||||
REMARKS:
|
||||
Performs an emulated read from the Standard VGA I/O ports. If the target
|
||||
hardware does not support mapping the VGA I/O and memory (such as some
|
||||
PowerPC systems), we emulate the VGA so that the BIOS will still be able to
|
||||
set NonVGA display modes such as on ATI hardware.
|
||||
****************************************************************************/
|
||||
static u8 VGA_inpb (const int port)
|
||||
{
|
||||
u8 val = 0xff;
|
||||
|
||||
debug_io("vga_inb.%04X -> ", (u16) port);
|
||||
switch (port) {
|
||||
case 0x3C0:
|
||||
/* 3C0 has funky characteristics because it can act as either
|
||||
a data register or index register depending on the state
|
||||
of an internal flip flop in the hardware. Hence we have
|
||||
to emulate that functionality in here. */
|
||||
if (_BE_env.flipFlop3C0 == 0) {
|
||||
/* Access 3C0 as index register */
|
||||
val = _BE_env.emu3C0;
|
||||
} else {
|
||||
/* Access 3C0 as data register */
|
||||
if (_BE_env.emu3C0 < ATT_C)
|
||||
val = _BE_env.emu3C1[_BE_env.emu3C0];
|
||||
}
|
||||
_BE_env.flipFlop3C0 ^= 1;
|
||||
break;
|
||||
case 0x3C1:
|
||||
if (_BE_env.emu3C0 < ATT_C)
|
||||
return _BE_env.emu3C1[_BE_env.emu3C0];
|
||||
break;
|
||||
case 0x3CC:
|
||||
return _BE_env.emu3C2;
|
||||
case 0x3C4:
|
||||
return _BE_env.emu3C4;
|
||||
case 0x3C5:
|
||||
if (_BE_env.emu3C4 < ATT_C)
|
||||
return _BE_env.emu3C5[_BE_env.emu3C4];
|
||||
break;
|
||||
case 0x3C6:
|
||||
return _BE_env.emu3C6;
|
||||
case 0x3C7:
|
||||
return _BE_env.emu3C7;
|
||||
case 0x3C8:
|
||||
return _BE_env.emu3C8;
|
||||
case 0x3C9:
|
||||
if (_BE_env.emu3C7 < PAL_C)
|
||||
return _BE_env.emu3C9[_BE_env.emu3C7++];
|
||||
break;
|
||||
case 0x3CE:
|
||||
return _BE_env.emu3CE;
|
||||
case 0x3CF:
|
||||
if (_BE_env.emu3CE < GRA_C)
|
||||
return _BE_env.emu3CF[_BE_env.emu3CE];
|
||||
break;
|
||||
case 0x3D4:
|
||||
if (_BE_env.emu3C2 & 0x1)
|
||||
return _BE_env.emu3D4;
|
||||
break;
|
||||
case 0x3D5:
|
||||
if ((_BE_env.emu3C2 & 0x1) && (_BE_env.emu3D4 < CRT_C))
|
||||
return _BE_env.emu3D5[_BE_env.emu3D4];
|
||||
break;
|
||||
case 0x3DA:
|
||||
_BE_env.flipFlop3C0 = 0;
|
||||
val = _BE_env.emu3DA;
|
||||
_BE_env.emu3DA ^= 0x9;
|
||||
break;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
port - Port to write to
|
||||
type - Type of access to perform
|
||||
|
||||
REMARKS:
|
||||
Performs an emulated write to one of the 8253 timer registers. For now
|
||||
we only emulate timer 0 which is the only timer that the BIOS code appears
|
||||
to use.
|
||||
****************************************************************************/
|
||||
static void VGA_outpb (int port, u8 val)
|
||||
{
|
||||
switch (port) {
|
||||
case 0x3C0:
|
||||
/* 3C0 has funky characteristics because it can act as either
|
||||
a data register or index register depending on the state
|
||||
of an internal flip flop in the hardware. Hence we have
|
||||
to emulate that functionality in here. */
|
||||
if (_BE_env.flipFlop3C0 == 0) {
|
||||
/* Access 3C0 as index register */
|
||||
_BE_env.emu3C0 = val;
|
||||
} else {
|
||||
/* Access 3C0 as data register */
|
||||
if (_BE_env.emu3C0 < ATT_C)
|
||||
_BE_env.emu3C1[_BE_env.emu3C0] = val;
|
||||
}
|
||||
_BE_env.flipFlop3C0 ^= 1;
|
||||
break;
|
||||
case 0x3C2:
|
||||
_BE_env.emu3C2 = val;
|
||||
break;
|
||||
case 0x3C4:
|
||||
_BE_env.emu3C4 = val;
|
||||
break;
|
||||
case 0x3C5:
|
||||
if (_BE_env.emu3C4 < ATT_C)
|
||||
_BE_env.emu3C5[_BE_env.emu3C4] = val;
|
||||
break;
|
||||
case 0x3C6:
|
||||
_BE_env.emu3C6 = val;
|
||||
break;
|
||||
case 0x3C7:
|
||||
_BE_env.emu3C7 = (int) val *3;
|
||||
|
||||
break;
|
||||
case 0x3C8:
|
||||
_BE_env.emu3C8 = (int) val *3;
|
||||
|
||||
break;
|
||||
case 0x3C9:
|
||||
if (_BE_env.emu3C8 < PAL_C)
|
||||
_BE_env.emu3C9[_BE_env.emu3C8++] = val;
|
||||
break;
|
||||
case 0x3CE:
|
||||
_BE_env.emu3CE = val;
|
||||
break;
|
||||
case 0x3CF:
|
||||
if (_BE_env.emu3CE < GRA_C)
|
||||
_BE_env.emu3CF[_BE_env.emu3CE] = val;
|
||||
break;
|
||||
case 0x3D4:
|
||||
if (_BE_env.emu3C2 & 0x1)
|
||||
_BE_env.emu3D4 = val;
|
||||
break;
|
||||
case 0x3D5:
|
||||
if ((_BE_env.emu3C2 & 0x1) && (_BE_env.emu3D4 < CRT_C))
|
||||
_BE_env.emu3D5[_BE_env.emu3D4] = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
regOffset - Offset into register space for non-DWORD accesses
|
||||
value - Value to write to register for PCI_WRITE_* operations
|
||||
func - Function to perform (PCIAccessRegFlags)
|
||||
|
||||
RETURNS:
|
||||
Value read from configuration register for PCI_READ_* operations
|
||||
|
||||
REMARKS:
|
||||
Accesses a PCI configuration space register by decoding the value currently
|
||||
stored in the _BE_env.configAddress variable and passing it through to the
|
||||
portable PCI_accessReg function.
|
||||
****************************************************************************/
|
||||
static u32 BE_accessReg(int regOffset, u32 value, int func)
|
||||
{
|
||||
#ifdef __KERNEL__
|
||||
int function, device, bus;
|
||||
u8 val8;
|
||||
u16 val16;
|
||||
u32 val32;
|
||||
|
||||
|
||||
/* Decode the configuration register values for the register we wish to
|
||||
* access
|
||||
*/
|
||||
regOffset += (_BE_env.configAddress & 0xFF);
|
||||
function = (_BE_env.configAddress >> 8) & 0x7;
|
||||
device = (_BE_env.configAddress >> 11) & 0x1F;
|
||||
bus = (_BE_env.configAddress >> 16) & 0xFF;
|
||||
|
||||
/* Ignore accesses to all devices other than the one we're POSTing */
|
||||
if ((function == _BE_env.vgaInfo.function) &&
|
||||
(device == _BE_env.vgaInfo.device) &&
|
||||
(bus == _BE_env.vgaInfo.bus)) {
|
||||
switch (func) {
|
||||
case REG_READ_BYTE:
|
||||
pci_read_config_byte(_BE_env.vgaInfo.pcidev, regOffset,
|
||||
&val8);
|
||||
return val8;
|
||||
case REG_READ_WORD:
|
||||
pci_read_config_word(_BE_env.vgaInfo.pcidev, regOffset,
|
||||
&val16);
|
||||
return val16;
|
||||
case REG_READ_DWORD:
|
||||
pci_read_config_dword(_BE_env.vgaInfo.pcidev, regOffset,
|
||||
&val32);
|
||||
return val32;
|
||||
case REG_WRITE_BYTE:
|
||||
pci_write_config_byte(_BE_env.vgaInfo.pcidev, regOffset,
|
||||
value);
|
||||
|
||||
return 0;
|
||||
case REG_WRITE_WORD:
|
||||
pci_write_config_word(_BE_env.vgaInfo.pcidev, regOffset,
|
||||
value);
|
||||
|
||||
return 0;
|
||||
case REG_WRITE_DWORD:
|
||||
pci_write_config_dword(_BE_env.vgaInfo.pcidev,
|
||||
regOffset, value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
#else
|
||||
PCIDeviceInfo pciInfo;
|
||||
|
||||
pciInfo.mech1 = 1;
|
||||
pciInfo.slot.i = 0;
|
||||
pciInfo.slot.p.Function = (_BE_env.configAddress >> 8) & 0x7;
|
||||
pciInfo.slot.p.Device = (_BE_env.configAddress >> 11) & 0x1F;
|
||||
pciInfo.slot.p.Bus = (_BE_env.configAddress >> 16) & 0xFF;
|
||||
pciInfo.slot.p.Enable = 1;
|
||||
|
||||
/* Ignore accesses to all devices other than the one we're POSTing */
|
||||
if ((pciInfo.slot.p.Function ==
|
||||
_BE_env.vgaInfo.pciInfo->slot.p.Function)
|
||||
&& (pciInfo.slot.p.Device == _BE_env.vgaInfo.pciInfo->slot.p.Device)
|
||||
&& (pciInfo.slot.p.Bus == _BE_env.vgaInfo.pciInfo->slot.p.Bus))
|
||||
return PCI_accessReg((_BE_env.configAddress & 0xFF) + regOffset,
|
||||
value, func, &pciInfo);
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
port - Port to read from
|
||||
type - Type of access to perform
|
||||
|
||||
REMARKS:
|
||||
Performs an emulated read from one of the PCI configuration space registers.
|
||||
We emulate this using our PCI_accessReg function which will access the PCI
|
||||
configuration space registers in a portable fashion.
|
||||
****************************************************************************/
|
||||
static u32 PCI_inp(int port, int type)
|
||||
{
|
||||
switch (type) {
|
||||
case REG_READ_BYTE:
|
||||
if ((_BE_env.configAddress & 0x80000000) && 0xCFC <= port
|
||||
&& port <= 0xCFF)
|
||||
return BE_accessReg(port - 0xCFC, 0, REG_READ_BYTE);
|
||||
break;
|
||||
case REG_READ_WORD:
|
||||
if ((_BE_env.configAddress & 0x80000000) && 0xCFC <= port
|
||||
&& port <= 0xCFF)
|
||||
return BE_accessReg(port - 0xCFC, 0, REG_READ_WORD);
|
||||
break;
|
||||
case REG_READ_DWORD:
|
||||
if (port == 0xCF8)
|
||||
return _BE_env.configAddress;
|
||||
else if ((_BE_env.configAddress & 0x80000000) && port == 0xCFC)
|
||||
return BE_accessReg(0, 0, REG_READ_DWORD);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
port - Port to write to
|
||||
type - Type of access to perform
|
||||
|
||||
REMARKS:
|
||||
Performs an emulated write to one of the PCI control registers.
|
||||
****************************************************************************/
|
||||
static void PCI_outp(int port, u32 val, int type)
|
||||
{
|
||||
switch (type) {
|
||||
case REG_WRITE_BYTE:
|
||||
if ((_BE_env.configAddress & 0x80000000) && 0xCFC <= port
|
||||
&& port <= 0xCFF)
|
||||
BE_accessReg(port - 0xCFC, val, REG_WRITE_BYTE);
|
||||
break;
|
||||
case REG_WRITE_WORD:
|
||||
if ((_BE_env.configAddress & 0x80000000) && 0xCFC <= port
|
||||
&& port <= 0xCFF)
|
||||
BE_accessReg(port - 0xCFC, val, REG_WRITE_WORD);
|
||||
break;
|
||||
case REG_WRITE_DWORD:
|
||||
if (port == 0xCF8)
|
||||
{
|
||||
_BE_env.configAddress = val & 0x80FFFFFC;
|
||||
}
|
||||
else if ((_BE_env.configAddress & 0x80000000) && port == 0xCFC)
|
||||
BE_accessReg(0, val, REG_WRITE_DWORD);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
port - Port to write to
|
||||
|
||||
RETURNS:
|
||||
Value read from the I/O port
|
||||
|
||||
REMARKS:
|
||||
Performs an emulated 8-bit read from an I/O port. We handle special cases
|
||||
that we need to emulate in here, and fall through to reflecting the write
|
||||
through to the real hardware if we don't need to special case it.
|
||||
****************************************************************************/
|
||||
u8 X86API BE_inb(X86EMU_pioAddr port)
|
||||
{
|
||||
u8 val = 0;
|
||||
|
||||
#if !defined(CONFIG_X86EMU_RAW_IO)
|
||||
if (IS_VGA_PORT(port)){
|
||||
/*seems reading port 0x3c3 return the high 16 bit of io port*/
|
||||
if(port == 0x3c3)
|
||||
val = LOG_inpb(port);
|
||||
else
|
||||
val = VGA_inpb(port);
|
||||
}
|
||||
else if (IS_TIMER_PORT(port))
|
||||
DB(printf("Can not interept TIMER port now!\n");)
|
||||
else if (IS_SPKR_PORT(port))
|
||||
DB(printf("Can not interept SPEAKER port now!\n");)
|
||||
else if (IS_CMOS_PORT(port))
|
||||
DB(printf("Can not interept CMOS port now!\n");)
|
||||
else if (IS_PCI_PORT(port))
|
||||
val = PCI_inp(port, REG_READ_BYTE);
|
||||
else if (port < 0x100) {
|
||||
DB(printf("WARN: INVALID inb.%04X -> %02X\n", (u16) port, val);)
|
||||
val = LOG_inpb(port);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
debug_io("inb.%04X -> ", (u16) port);
|
||||
val = LOG_inpb(port);
|
||||
debug_io("%02X\n", val);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
port - Port to write to
|
||||
|
||||
RETURNS:
|
||||
Value read from the I/O port
|
||||
|
||||
REMARKS:
|
||||
Performs an emulated 16-bit read from an I/O port. We handle special cases
|
||||
that we need to emulate in here, and fall through to reflecting the write
|
||||
through to the real hardware if we don't need to special case it.
|
||||
****************************************************************************/
|
||||
u16 X86API BE_inw(X86EMU_pioAddr port)
|
||||
{
|
||||
u16 val = 0;
|
||||
|
||||
#if !defined(CONFIG_X86EMU_RAW_IO)
|
||||
if (IS_PCI_PORT(port))
|
||||
val = PCI_inp(port, REG_READ_WORD);
|
||||
else if (port < 0x100) {
|
||||
DB(printf("WARN: Maybe INVALID inw.%04X -> %04X\n", (u16) port, val);)
|
||||
val = LOG_inpw(port);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
debug_io("inw.%04X -> ", (u16) port);
|
||||
val = LOG_inpw(port);
|
||||
debug_io("%04X\n", val);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
port - Port to write to
|
||||
|
||||
RETURNS:
|
||||
Value read from the I/O port
|
||||
|
||||
REMARKS:
|
||||
Performs an emulated 32-bit read from an I/O port. We handle special cases
|
||||
that we need to emulate in here, and fall through to reflecting the write
|
||||
through to the real hardware if we don't need to special case it.
|
||||
****************************************************************************/
|
||||
u32 X86API BE_inl(X86EMU_pioAddr port)
|
||||
{
|
||||
u32 val = 0;
|
||||
|
||||
#if !defined(CONFIG_X86EMU_RAW_IO)
|
||||
if (IS_PCI_PORT(port))
|
||||
val = PCI_inp(port, REG_READ_DWORD);
|
||||
else if (port < 0x100) {
|
||||
val = LOG_inpd(port);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
debug_io("inl.%04X -> ", (u16) port);
|
||||
val = LOG_inpd(port);
|
||||
debug_io("%08X\n", val);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
port - Port to write to
|
||||
val - Value to write to port
|
||||
|
||||
REMARKS:
|
||||
Performs an emulated 8-bit write to an I/O port. We handle special cases
|
||||
that we need to emulate in here, and fall through to reflecting the write
|
||||
through to the real hardware if we don't need to special case it.
|
||||
****************************************************************************/
|
||||
void X86API BE_outb(X86EMU_pioAddr port, u8 val)
|
||||
{
|
||||
#if !defined(CONFIG_X86EMU_RAW_IO)
|
||||
if (IS_VGA_PORT(port))
|
||||
VGA_outpb(port, val);
|
||||
else if (IS_TIMER_PORT(port))
|
||||
DB(printf("Can not interept TIMER port now!\n");)
|
||||
else if (IS_SPKR_PORT(port))
|
||||
DB(printf("Can not interept SPEAKER port now!\n");)
|
||||
else if (IS_CMOS_PORT(port))
|
||||
DB(printf("Can not interept CMOS port now!\n");)
|
||||
else if (IS_PCI_PORT(port))
|
||||
PCI_outp(port, val, REG_WRITE_BYTE);
|
||||
else if (port < 0x100) {
|
||||
DB(printf("WARN:Maybe INVALID outb.%04X <- %02X\n", (u16) port, val);)
|
||||
LOG_outpb(port, val);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
debug_io("outb.%04X <- %02X", (u16) port, val);
|
||||
LOG_outpb(port, val);
|
||||
debug_io("\n");
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
port - Port to write to
|
||||
val - Value to write to port
|
||||
|
||||
REMARKS:
|
||||
Performs an emulated 16-bit write to an I/O port. We handle special cases
|
||||
that we need to emulate in here, and fall through to reflecting the write
|
||||
through to the real hardware if we don't need to special case it.
|
||||
****************************************************************************/
|
||||
void X86API BE_outw(X86EMU_pioAddr port, u16 val)
|
||||
{
|
||||
#if !defined(CONFIG_X86EMU_RAW_IO)
|
||||
if (IS_VGA_PORT(port)) {
|
||||
VGA_outpb(port, val);
|
||||
VGA_outpb(port + 1, val >> 8);
|
||||
} else if (IS_PCI_PORT(port)) {
|
||||
PCI_outp(port, val, REG_WRITE_WORD);
|
||||
} else if (port < 0x100) {
|
||||
DB(printf("WARN: MAybe INVALID outw.%04X <- %04X\n", (u16)port,
|
||||
val);)
|
||||
LOG_outpw(port, val);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
debug_io("outw.%04X <- %04X", (u16) port, val);
|
||||
LOG_outpw(port, val);
|
||||
debug_io("\n");
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
port - Port to write to
|
||||
val - Value to write to port
|
||||
|
||||
REMARKS:
|
||||
Performs an emulated 32-bit write to an I/O port. We handle special cases
|
||||
that we need to emulate in here, and fall through to reflecting the write
|
||||
through to the real hardware if we don't need to special case it.
|
||||
****************************************************************************/
|
||||
void X86API BE_outl(X86EMU_pioAddr port, u32 val)
|
||||
{
|
||||
#if !defined(CONFIG_X86EMU_RAW_IO)
|
||||
if (IS_PCI_PORT(port)) {
|
||||
PCI_outp(port, val, REG_WRITE_DWORD);
|
||||
} else if (port < 0x100) {
|
||||
DB(printf("WARN: INVALID outl.%04X <- %08X\n", (u16) port,val);)
|
||||
LOG_outpd(port, val);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
debug_io("outl.%04X <- %08X", (u16) port, val);
|
||||
LOG_outpd(port, val);
|
||||
debug_io("\n");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,363 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* BIOS emulator and interface
|
||||
* to Realmode X86 Emulator Library
|
||||
*
|
||||
* Copyright (C) 2007 Freescale Semiconductor, Inc.
|
||||
* Jason Jin <Jason.jin@freescale.com>
|
||||
*
|
||||
* Copyright (C) 1996-1999 SciTech Software, Inc.
|
||||
*
|
||||
* ========================================================================
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software and
|
||||
* its documentation for any purpose is hereby granted without fee,
|
||||
* provided that the above copyright notice appear in all copies and that
|
||||
* both that copyright notice and this permission notice appear in
|
||||
* supporting documentation, and that the name of the authors not be used
|
||||
* in advertising or publicity pertaining to distribution of the software
|
||||
* without specific, written prior permission. The authors makes no
|
||||
* representations about the suitability of this software for any purpose.
|
||||
* It is provided "as is" without express or implied warranty.
|
||||
*
|
||||
* THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
||||
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
||||
* EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
||||
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
|
||||
* USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* ========================================================================
|
||||
*
|
||||
* Language: ANSI C
|
||||
* Environment: Any
|
||||
* Developer: Kendall Bennett
|
||||
*
|
||||
* Description: Module implementing the BIOS specific functions.
|
||||
*
|
||||
* Jason ported this file to u-boot to run the ATI video card
|
||||
* video BIOS.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#define __io
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include "biosemui.h"
|
||||
|
||||
/*----------------------------- Implementation ----------------------------*/
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
intno - Interrupt number being serviced
|
||||
|
||||
REMARKS:
|
||||
Handler for undefined interrupts.
|
||||
****************************************************************************/
|
||||
static void X86API undefined_intr(int intno)
|
||||
{
|
||||
if (BE_rdw(intno * 4 + 2) == BIOS_SEG) {
|
||||
DB(printf("biosEmu: undefined interrupt %xh called!\n", intno);)
|
||||
} else
|
||||
X86EMU_prepareForInt(intno);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
intno - Interrupt number being serviced
|
||||
|
||||
REMARKS:
|
||||
This function handles the default system BIOS Int 10h (the default is stored
|
||||
in the Int 42h vector by the system BIOS at bootup). We only need to handle
|
||||
a small number of special functions used by the BIOS during POST time.
|
||||
****************************************************************************/
|
||||
static void X86API int42(int intno)
|
||||
{
|
||||
if (M.x86.R_AH == 0x12 && M.x86.R_BL == 0x32) {
|
||||
if (M.x86.R_AL == 0) {
|
||||
/* Enable CPU accesses to video memory */
|
||||
PM_outpb(0x3c2, PM_inpb(0x3cc) | (u8) 0x02);
|
||||
return;
|
||||
} else if (M.x86.R_AL == 1) {
|
||||
/* Disable CPU accesses to video memory */
|
||||
PM_outpb(0x3c2, PM_inpb(0x3cc) & (u8) ~ 0x02);
|
||||
return;
|
||||
}
|
||||
#ifdef CONFIG_X86EMU_DEBUG
|
||||
else {
|
||||
printf("int42: unknown function AH=0x12, BL=0x32, AL=%#02x\n",
|
||||
M.x86.R_AL);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#ifdef CONFIG_X86EMU_DEBUG
|
||||
else {
|
||||
printf("int42: unknown function AH=%#02x, AL=%#02x, BL=%#02x\n",
|
||||
M.x86.R_AH, M.x86.R_AL, M.x86.R_BL);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
intno - Interrupt number being serviced
|
||||
|
||||
REMARKS:
|
||||
This function handles the default system BIOS Int 10h. If the POST code
|
||||
has not yet re-vectored the Int 10h BIOS interrupt vector, we handle this
|
||||
by simply calling the int42 interrupt handler above. Very early in the
|
||||
BIOS POST process, the vector gets replaced and we simply let the real
|
||||
mode interrupt handler process the interrupt.
|
||||
****************************************************************************/
|
||||
static void X86API int10(int intno)
|
||||
{
|
||||
if (BE_rdw(intno * 4 + 2) == BIOS_SEG)
|
||||
int42(intno);
|
||||
else
|
||||
X86EMU_prepareForInt(intno);
|
||||
}
|
||||
|
||||
/* Result codes returned by the PCI BIOS */
|
||||
|
||||
#define SUCCESSFUL 0x00
|
||||
#define FUNC_NOT_SUPPORT 0x81
|
||||
#define BAD_VENDOR_ID 0x83
|
||||
#define DEVICE_NOT_FOUND 0x86
|
||||
#define BAD_REGISTER_NUMBER 0x87
|
||||
#define SET_FAILED 0x88
|
||||
#define BUFFER_TOO_SMALL 0x89
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
intno - Interrupt number being serviced
|
||||
|
||||
REMARKS:
|
||||
This function handles the default Int 1Ah interrupt handler for the real
|
||||
mode code, which provides support for the PCI BIOS functions. Since we only
|
||||
want to allow the real mode BIOS code *only* see the PCI config space for
|
||||
its own device, we only return information for the specific PCI config
|
||||
space that we have passed in to the init function. This solves problems
|
||||
when using the BIOS to warm boot a secondary adapter when there is an
|
||||
identical adapter before it on the bus (some BIOS'es get confused in this
|
||||
case).
|
||||
****************************************************************************/
|
||||
static void X86API int1A(int unused)
|
||||
{
|
||||
u16 pciSlot;
|
||||
|
||||
#ifdef __KERNEL__
|
||||
u8 interface, subclass, baseclass;
|
||||
|
||||
/* Initialise the PCI slot number */
|
||||
pciSlot = ((int)_BE_env.vgaInfo.bus << 8) |
|
||||
((int)_BE_env.vgaInfo.device << 3) | (int)_BE_env.vgaInfo.function;
|
||||
#else
|
||||
/* Fail if no PCI device information has been registered */
|
||||
if (!_BE_env.vgaInfo.pciInfo)
|
||||
return;
|
||||
|
||||
pciSlot = (u16) (_BE_env.vgaInfo.pciInfo->slot.i >> 8);
|
||||
#endif
|
||||
switch (M.x86.R_AX) {
|
||||
case 0xB101: /* PCI bios present? */
|
||||
M.x86.R_AL = 0x00; /* no config space/special cycle generation support */
|
||||
M.x86.R_EDX = 0x20494350; /* " ICP" */
|
||||
M.x86.R_BX = 0x0210; /* Version 2.10 */
|
||||
M.x86.R_CL = 0; /* Max bus number in system */
|
||||
CLEAR_FLAG(F_CF);
|
||||
break;
|
||||
case 0xB102: /* Find PCI device */
|
||||
M.x86.R_AH = DEVICE_NOT_FOUND;
|
||||
#ifdef __KERNEL__
|
||||
if (M.x86.R_DX == _BE_env.vgaInfo.VendorID &&
|
||||
M.x86.R_CX == _BE_env.vgaInfo.DeviceID && M.x86.R_SI == 0) {
|
||||
#else
|
||||
if (M.x86.R_DX == _BE_env.vgaInfo.pciInfo->VendorID &&
|
||||
M.x86.R_CX == _BE_env.vgaInfo.pciInfo->DeviceID &&
|
||||
M.x86.R_SI == 0) {
|
||||
#endif
|
||||
M.x86.R_AH = SUCCESSFUL;
|
||||
M.x86.R_BX = pciSlot;
|
||||
}
|
||||
CONDITIONAL_SET_FLAG((M.x86.R_AH != SUCCESSFUL), F_CF);
|
||||
break;
|
||||
case 0xB103: /* Find PCI class code */
|
||||
M.x86.R_AH = DEVICE_NOT_FOUND;
|
||||
#ifdef __KERNEL__
|
||||
#ifdef CONFIG_DM_PCI
|
||||
dm_pci_read_config8(_BE_env.vgaInfo.pcidev, PCI_CLASS_PROG,
|
||||
&interface);
|
||||
dm_pci_read_config8(_BE_env.vgaInfo.pcidev, PCI_CLASS_DEVICE,
|
||||
&subclass);
|
||||
dm_pci_read_config8(_BE_env.vgaInfo.pcidev,
|
||||
PCI_CLASS_DEVICE + 1, &baseclass);
|
||||
#else
|
||||
pci_read_config_byte(_BE_env.vgaInfo.pcidev, PCI_CLASS_PROG,
|
||||
&interface);
|
||||
pci_read_config_byte(_BE_env.vgaInfo.pcidev, PCI_CLASS_DEVICE,
|
||||
&subclass);
|
||||
pci_read_config_byte(_BE_env.vgaInfo.pcidev,
|
||||
PCI_CLASS_DEVICE + 1, &baseclass);
|
||||
#endif
|
||||
if (M.x86.R_CL == interface && M.x86.R_CH == subclass
|
||||
&& (u8) (M.x86.R_ECX >> 16) == baseclass) {
|
||||
#else
|
||||
if (M.x86.R_CL == _BE_env.vgaInfo.pciInfo->Interface &&
|
||||
M.x86.R_CH == _BE_env.vgaInfo.pciInfo->SubClass &&
|
||||
(u8) (M.x86.R_ECX >> 16) ==
|
||||
_BE_env.vgaInfo.pciInfo->BaseClass) {
|
||||
#endif
|
||||
M.x86.R_AH = SUCCESSFUL;
|
||||
M.x86.R_BX = pciSlot;
|
||||
}
|
||||
CONDITIONAL_SET_FLAG((M.x86.R_AH != SUCCESSFUL), F_CF);
|
||||
break;
|
||||
case 0xB108: /* Read configuration byte */
|
||||
M.x86.R_AH = BAD_REGISTER_NUMBER;
|
||||
if (M.x86.R_BX == pciSlot) {
|
||||
M.x86.R_AH = SUCCESSFUL;
|
||||
#ifdef __KERNEL__
|
||||
# ifdef CONFIG_DM_PCI
|
||||
dm_pci_read_config8(_BE_env.vgaInfo.pcidev, M.x86.R_DI,
|
||||
&M.x86.R_CL);
|
||||
# else
|
||||
pci_read_config_byte(_BE_env.vgaInfo.pcidev, M.x86.R_DI,
|
||||
&M.x86.R_CL);
|
||||
# endif
|
||||
#else
|
||||
M.x86.R_CL =
|
||||
(u8) PCI_accessReg(M.x86.R_DI, 0, PCI_READ_BYTE,
|
||||
_BE_env.vgaInfo.pciInfo);
|
||||
#endif
|
||||
}
|
||||
CONDITIONAL_SET_FLAG((M.x86.R_AH != SUCCESSFUL), F_CF);
|
||||
break;
|
||||
case 0xB109: /* Read configuration word */
|
||||
M.x86.R_AH = BAD_REGISTER_NUMBER;
|
||||
if (M.x86.R_BX == pciSlot) {
|
||||
M.x86.R_AH = SUCCESSFUL;
|
||||
#ifdef __KERNEL__
|
||||
# ifdef CONFIG_DM_PCI
|
||||
dm_pci_read_config16(_BE_env.vgaInfo.pcidev, M.x86.R_DI,
|
||||
&M.x86.R_CX);
|
||||
# else
|
||||
pci_read_config_word(_BE_env.vgaInfo.pcidev, M.x86.R_DI,
|
||||
&M.x86.R_CX);
|
||||
# endif
|
||||
#else
|
||||
M.x86.R_CX =
|
||||
(u16) PCI_accessReg(M.x86.R_DI, 0, PCI_READ_WORD,
|
||||
_BE_env.vgaInfo.pciInfo);
|
||||
#endif
|
||||
}
|
||||
CONDITIONAL_SET_FLAG((M.x86.R_AH != SUCCESSFUL), F_CF);
|
||||
break;
|
||||
case 0xB10A: /* Read configuration dword */
|
||||
M.x86.R_AH = BAD_REGISTER_NUMBER;
|
||||
if (M.x86.R_BX == pciSlot) {
|
||||
M.x86.R_AH = SUCCESSFUL;
|
||||
#ifdef __KERNEL__
|
||||
# ifdef CONFIG_DM_PCI
|
||||
dm_pci_read_config32(_BE_env.vgaInfo.pcidev,
|
||||
M.x86.R_DI, &M.x86.R_ECX);
|
||||
# else
|
||||
pci_read_config_dword(_BE_env.vgaInfo.pcidev,
|
||||
M.x86.R_DI, &M.x86.R_ECX);
|
||||
# endif
|
||||
#else
|
||||
M.x86.R_ECX =
|
||||
(u32) PCI_accessReg(M.x86.R_DI, 0, PCI_READ_DWORD,
|
||||
_BE_env.vgaInfo.pciInfo);
|
||||
#endif
|
||||
}
|
||||
CONDITIONAL_SET_FLAG((M.x86.R_AH != SUCCESSFUL), F_CF);
|
||||
break;
|
||||
case 0xB10B: /* Write configuration byte */
|
||||
M.x86.R_AH = BAD_REGISTER_NUMBER;
|
||||
if (M.x86.R_BX == pciSlot) {
|
||||
M.x86.R_AH = SUCCESSFUL;
|
||||
#ifdef __KERNEL__
|
||||
# ifdef CONFIG_DM_PCI
|
||||
dm_pci_write_config8(_BE_env.vgaInfo.pcidev,
|
||||
M.x86.R_DI, M.x86.R_CL);
|
||||
# else
|
||||
pci_write_config_byte(_BE_env.vgaInfo.pcidev,
|
||||
M.x86.R_DI, M.x86.R_CL);
|
||||
# endif
|
||||
#else
|
||||
PCI_accessReg(M.x86.R_DI, M.x86.R_CL, PCI_WRITE_BYTE,
|
||||
_BE_env.vgaInfo.pciInfo);
|
||||
#endif
|
||||
}
|
||||
CONDITIONAL_SET_FLAG((M.x86.R_AH != SUCCESSFUL), F_CF);
|
||||
break;
|
||||
case 0xB10C: /* Write configuration word */
|
||||
M.x86.R_AH = BAD_REGISTER_NUMBER;
|
||||
if (M.x86.R_BX == pciSlot) {
|
||||
M.x86.R_AH = SUCCESSFUL;
|
||||
#ifdef __KERNEL__
|
||||
# ifdef CONFIG_DM_PCI
|
||||
dm_pci_write_config32(_BE_env.vgaInfo.pcidev,
|
||||
M.x86.R_DI, M.x86.R_CX);
|
||||
# else
|
||||
pci_write_config_word(_BE_env.vgaInfo.pcidev,
|
||||
M.x86.R_DI, M.x86.R_CX);
|
||||
# endif
|
||||
#else
|
||||
PCI_accessReg(M.x86.R_DI, M.x86.R_CX, PCI_WRITE_WORD,
|
||||
_BE_env.vgaInfo.pciInfo);
|
||||
#endif
|
||||
}
|
||||
CONDITIONAL_SET_FLAG((M.x86.R_AH != SUCCESSFUL), F_CF);
|
||||
break;
|
||||
case 0xB10D: /* Write configuration dword */
|
||||
M.x86.R_AH = BAD_REGISTER_NUMBER;
|
||||
if (M.x86.R_BX == pciSlot) {
|
||||
M.x86.R_AH = SUCCESSFUL;
|
||||
#ifdef __KERNEL__
|
||||
# ifdef CONFIG_DM_PCI
|
||||
dm_pci_write_config32(_BE_env.vgaInfo.pcidev,
|
||||
M.x86.R_DI, M.x86.R_ECX);
|
||||
# else
|
||||
pci_write_config_dword(_BE_env.vgaInfo.pcidev,
|
||||
M.x86.R_DI, M.x86.R_ECX);
|
||||
# endif
|
||||
#else
|
||||
PCI_accessReg(M.x86.R_DI, M.x86.R_ECX, PCI_WRITE_DWORD,
|
||||
_BE_env.vgaInfo.pciInfo);
|
||||
#endif
|
||||
}
|
||||
CONDITIONAL_SET_FLAG((M.x86.R_AH != SUCCESSFUL), F_CF);
|
||||
break;
|
||||
default:
|
||||
printf("biosEmu/bios.int1a: unknown function AX=%#04x\n",
|
||||
M.x86.R_AX);
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
REMARKS:
|
||||
This function initialises the BIOS emulation functions for the specific
|
||||
PCI display device. We insulate the real mode BIOS from any other devices
|
||||
on the bus, so that it will work correctly thinking that it is the only
|
||||
device present on the bus (ie: avoiding any adapters present in from of
|
||||
the device we are trying to control).
|
||||
****************************************************************************/
|
||||
#define BE_constLE_32(v) ((((((v)&0xff00)>>8)|(((v)&0xff)<<8))<<16)|(((((v)&0xff000000)>>8)|(((v)&0x00ff0000)<<8))>>16))
|
||||
|
||||
void _BE_bios_init(u32 * intrTab)
|
||||
{
|
||||
int i;
|
||||
X86EMU_intrFuncs bios_intr_tab[256];
|
||||
|
||||
for (i = 0; i < 256; ++i) {
|
||||
intrTab[i] = BE_constLE_32(BIOS_SEG << 16);
|
||||
bios_intr_tab[i] = undefined_intr;
|
||||
}
|
||||
bios_intr_tab[0x10] = int10;
|
||||
bios_intr_tab[0x1A] = int1A;
|
||||
bios_intr_tab[0x42] = int42;
|
||||
bios_intr_tab[0x6D] = int10;
|
||||
X86EMU_setupIntrFuncs(bios_intr_tab);
|
||||
}
|
||||
@@ -0,0 +1,372 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* BIOS emulator and interface
|
||||
* to Realmode X86 Emulator Library
|
||||
*
|
||||
* Copyright (C) 2007 Freescale Semiconductor, Inc.
|
||||
* Jason Jin <Jason.jin@freescale.com>
|
||||
*
|
||||
* Copyright (C) 1996-1999 SciTech Software, Inc.
|
||||
*
|
||||
* ========================================================================
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software and
|
||||
* its documentation for any purpose is hereby granted without fee,
|
||||
* provided that the above copyright notice appear in all copies and that
|
||||
* both that copyright notice and this permission notice appear in
|
||||
* supporting documentation, and that the name of the authors not be used
|
||||
* in advertising or publicity pertaining to distribution of the software
|
||||
* without specific, written prior permission. The authors makes no
|
||||
* representations about the suitability of this software for any purpose.
|
||||
* It is provided "as is" without express or implied warranty.
|
||||
*
|
||||
* THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
||||
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
||||
* EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
||||
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
|
||||
* USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* ========================================================================
|
||||
*
|
||||
* Language: ANSI C
|
||||
* Environment: Any
|
||||
* Developer: Kendall Bennett
|
||||
*
|
||||
* Description: Module implementing the system specific functions. This
|
||||
* module is always compiled and linked in the OS depedent
|
||||
* libraries, and never in a binary portable driver.
|
||||
*
|
||||
* Jason ported this file to u-boot to run the ATI video card BIOS
|
||||
* in u-boot. Made all the video memory be emulated during the
|
||||
* BIOS runing process which may affect the VGA function but the
|
||||
* frambuffer function can work after run the BIOS.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#include <malloc.h>
|
||||
#include <common.h>
|
||||
#include "biosemui.h"
|
||||
|
||||
BE_sysEnv _BE_env = {{0}};
|
||||
static X86EMU_memFuncs _BE_mem __attribute__((section(GOT2_TYPE))) = {
|
||||
BE_rdb,
|
||||
BE_rdw,
|
||||
BE_rdl,
|
||||
BE_wrb,
|
||||
BE_wrw,
|
||||
BE_wrl,
|
||||
};
|
||||
|
||||
static X86EMU_pioFuncs _BE_pio __attribute__((section(GOT2_TYPE))) = {
|
||||
BE_inb,
|
||||
BE_inw,
|
||||
BE_inl,
|
||||
BE_outb,
|
||||
BE_outw,
|
||||
BE_outl,
|
||||
};
|
||||
|
||||
#define OFF(addr) (u16)(((addr) >> 0) & 0xffff)
|
||||
#define SEG(addr) (u16)(((addr) >> 4) & 0xf000)
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
debugFlags - Flags to enable debugging options (debug builds only)
|
||||
memSize - Amount of memory to allocate for real mode machine
|
||||
info - Pointer to default VGA device information
|
||||
|
||||
REMARKS:
|
||||
This functions initialises the BElib, and uses the passed in
|
||||
BIOS image as the BIOS that is used and emulated at 0xC0000.
|
||||
****************************************************************************/
|
||||
int X86API BE_init(u32 debugFlags, int memSize, BE_VGAInfo * info, int shared)
|
||||
{
|
||||
#if !defined(__DRIVER__) && !defined(__KERNEL__)
|
||||
|
||||
PM_init();
|
||||
#endif
|
||||
memset(&M, 0, sizeof(M));
|
||||
if (memSize < 20480){
|
||||
printf("Emulator requires at least 20Kb of memory!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
M.mem_base = malloc(memSize);
|
||||
|
||||
if (M.mem_base == NULL){
|
||||
printf("Biosemu:Out of memory!");
|
||||
return 0;
|
||||
}
|
||||
M.mem_size = memSize;
|
||||
|
||||
_BE_env.emulateVGA = 0;
|
||||
_BE_env.busmem_base = (unsigned long)malloc(128 * 1024);
|
||||
if ((void *)_BE_env.busmem_base == NULL){
|
||||
printf("Biosemu:Out of memory!");
|
||||
return 0;
|
||||
}
|
||||
M.x86.debug = debugFlags;
|
||||
_BE_bios_init((u32*)info->LowMem);
|
||||
X86EMU_setupMemFuncs(&_BE_mem);
|
||||
X86EMU_setupPioFuncs(&_BE_pio);
|
||||
BE_setVGA(info);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
info - Pointer to VGA device information to make current
|
||||
|
||||
REMARKS:
|
||||
This function sets the VGA BIOS functions in the emulator to point to the
|
||||
specific VGA BIOS in use. This includes swapping the BIOS interrupt
|
||||
vectors, BIOS image and BIOS data area to the new BIOS. This allows the
|
||||
real mode BIOS to be swapped without resetting the entire emulator.
|
||||
****************************************************************************/
|
||||
void X86API BE_setVGA(BE_VGAInfo * info)
|
||||
{
|
||||
|
||||
#ifdef __KERNEL__
|
||||
_BE_env.vgaInfo.function = info->function;
|
||||
_BE_env.vgaInfo.device = info->device;
|
||||
_BE_env.vgaInfo.bus = info->bus;
|
||||
_BE_env.vgaInfo.pcidev = info->pcidev;
|
||||
#else
|
||||
_BE_env.vgaInfo.pciInfo = info->pciInfo;
|
||||
#endif
|
||||
_BE_env.vgaInfo.BIOSImage = info->BIOSImage;
|
||||
if (info->BIOSImage) {
|
||||
_BE_env.biosmem_base = (ulong) info->BIOSImage;
|
||||
_BE_env.biosmem_limit = 0xC0000 + info->BIOSImageLen - 1;
|
||||
} else {
|
||||
_BE_env.biosmem_base = _BE_env.busmem_base + 0x20000;
|
||||
_BE_env.biosmem_limit = 0xC7FFF;
|
||||
}
|
||||
if ((info->LowMem[0] == 0) && (info->LowMem[1] == 0) &&
|
||||
(info->LowMem[2] == 0) && (info->LowMem[3] == 0))
|
||||
_BE_bios_init((u32 *) info->LowMem);
|
||||
memcpy((u8 *) M.mem_base, info->LowMem, sizeof(info->LowMem));
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
info - Pointer to VGA device information to retrieve current
|
||||
|
||||
REMARKS:
|
||||
This function returns the VGA BIOS functions currently active in the
|
||||
emulator, so they can be restored at a later date.
|
||||
****************************************************************************/
|
||||
void X86API BE_getVGA(BE_VGAInfo * info)
|
||||
{
|
||||
#ifdef __KERNEL__
|
||||
info->function = _BE_env.vgaInfo.function;
|
||||
info->device = _BE_env.vgaInfo.device;
|
||||
info->bus = _BE_env.vgaInfo.bus;
|
||||
info->pcidev = _BE_env.vgaInfo.pcidev;
|
||||
#else
|
||||
info->pciInfo = _BE_env.vgaInfo.pciInfo;
|
||||
#endif
|
||||
info->BIOSImage = _BE_env.vgaInfo.BIOSImage;
|
||||
memcpy(info->LowMem, (u8 *) M.mem_base, sizeof(info->LowMem));
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
r_seg - Segment for pointer to convert
|
||||
r_off - Offset for pointer to convert
|
||||
|
||||
REMARKS:
|
||||
This function maps a real mode pointer in the emulator memory to a protected
|
||||
mode pointer that can be used to directly access the memory.
|
||||
|
||||
NOTE: The memory is *always* in little endian format, son on non-x86
|
||||
systems you will need to do endian translations to access this
|
||||
memory.
|
||||
****************************************************************************/
|
||||
void *X86API BE_mapRealPointer(uint r_seg, uint r_off)
|
||||
{
|
||||
u32 addr = ((u32) r_seg << 4) + r_off;
|
||||
|
||||
if (addr >= 0xC0000 && addr <= _BE_env.biosmem_limit) {
|
||||
return (void *)(_BE_env.biosmem_base + addr - 0xC0000);
|
||||
} else if (addr >= 0xA0000 && addr <= 0xFFFFF) {
|
||||
return (void *)(_BE_env.busmem_base + addr - 0xA0000);
|
||||
}
|
||||
return (void *)(M.mem_base + addr);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
len - Return the length of the VESA buffer
|
||||
rseg - Place to store VESA buffer segment
|
||||
roff - Place to store VESA buffer offset
|
||||
|
||||
REMARKS:
|
||||
This function returns the address of the VESA transfer buffer in real
|
||||
_BE_piomode emulator memory. The VESA transfer buffer is always 1024 bytes long,
|
||||
and located at 15Kb into the start of the real mode memory (16Kb is where
|
||||
we put the real mode code we execute for issuing interrupts).
|
||||
|
||||
NOTE: The memory is *always* in little endian format, son on non-x86
|
||||
systems you will need to do endian translations to access this
|
||||
memory.
|
||||
****************************************************************************/
|
||||
void *X86API BE_getVESABuf(uint * len, uint * rseg, uint * roff)
|
||||
{
|
||||
*len = 1024;
|
||||
*rseg = SEG(0x03C00);
|
||||
*roff = OFF(0x03C00);
|
||||
return (void *)(M.mem_base + ((u32) * rseg << 4) + *roff);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
REMARKS:
|
||||
Cleans up and exits the emulator.
|
||||
****************************************************************************/
|
||||
void X86API BE_exit(void)
|
||||
{
|
||||
free(M.mem_base);
|
||||
free((void *)_BE_env.busmem_base);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
seg - Segment of code to call
|
||||
off - Offset of code to call
|
||||
regs - Real mode registers to load
|
||||
sregs - Real mode segment registers to load
|
||||
|
||||
REMARKS:
|
||||
This functions calls a real mode far function at the specified address,
|
||||
and loads all the x86 registers from the passed in registers structure.
|
||||
On exit the registers returned from the call are returned in the same
|
||||
structures.
|
||||
****************************************************************************/
|
||||
void X86API BE_callRealMode(uint seg, uint off, RMREGS * regs, RMSREGS * sregs)
|
||||
{
|
||||
M.x86.R_EAX = regs->e.eax;
|
||||
M.x86.R_EBX = regs->e.ebx;
|
||||
M.x86.R_ECX = regs->e.ecx;
|
||||
M.x86.R_EDX = regs->e.edx;
|
||||
M.x86.R_ESI = regs->e.esi;
|
||||
M.x86.R_EDI = regs->e.edi;
|
||||
M.x86.R_DS = sregs->ds;
|
||||
M.x86.R_ES = sregs->es;
|
||||
M.x86.R_FS = sregs->fs;
|
||||
M.x86.R_GS = sregs->gs;
|
||||
|
||||
((u8 *) M.mem_base)[0x4000] = 0x9A;
|
||||
((u8 *) M.mem_base)[0x4001] = (u8) off;
|
||||
((u8 *) M.mem_base)[0x4002] = (u8) (off >> 8);
|
||||
((u8 *) M.mem_base)[0x4003] = (u8) seg;
|
||||
((u8 *) M.mem_base)[0x4004] = (u8) (seg >> 8);
|
||||
((u8 *) M.mem_base)[0x4005] = 0xF1; /* Illegal op-code */
|
||||
M.x86.R_CS = SEG(0x04000);
|
||||
M.x86.R_IP = OFF(0x04000);
|
||||
|
||||
M.x86.R_SS = SEG(M.mem_size - 2);
|
||||
M.x86.R_SP = OFF(M.mem_size - 2) + 2;
|
||||
|
||||
X86EMU_exec();
|
||||
|
||||
regs->e.cflag = M.x86.R_EFLG & F_CF;
|
||||
regs->e.eax = M.x86.R_EAX;
|
||||
regs->e.ebx = M.x86.R_EBX;
|
||||
regs->e.ecx = M.x86.R_ECX;
|
||||
regs->e.edx = M.x86.R_EDX;
|
||||
regs->e.esi = M.x86.R_ESI;
|
||||
regs->e.edi = M.x86.R_EDI;
|
||||
sregs->ds = M.x86.R_DS;
|
||||
sregs->es = M.x86.R_ES;
|
||||
sregs->fs = M.x86.R_FS;
|
||||
sregs->gs = M.x86.R_GS;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
intno - Interrupt number to execute
|
||||
in - Real mode registers to load
|
||||
out - Place to store resulting real mode registers
|
||||
|
||||
REMARKS:
|
||||
This functions calls a real mode interrupt function at the specified address,
|
||||
and loads all the x86 registers from the passed in registers structure.
|
||||
On exit the registers returned from the call are returned in out stucture.
|
||||
****************************************************************************/
|
||||
int X86API BE_int86(int intno, RMREGS * in, RMREGS * out)
|
||||
{
|
||||
M.x86.R_EAX = in->e.eax;
|
||||
M.x86.R_EBX = in->e.ebx;
|
||||
M.x86.R_ECX = in->e.ecx;
|
||||
M.x86.R_EDX = in->e.edx;
|
||||
M.x86.R_ESI = in->e.esi;
|
||||
M.x86.R_EDI = in->e.edi;
|
||||
((u8 *) M.mem_base)[0x4000] = 0xCD;
|
||||
((u8 *) M.mem_base)[0x4001] = (u8) intno;
|
||||
((u8 *) M.mem_base)[0x4002] = 0xF1;
|
||||
M.x86.R_CS = SEG(0x04000);
|
||||
M.x86.R_IP = OFF(0x04000);
|
||||
|
||||
M.x86.R_SS = SEG(M.mem_size - 1);
|
||||
M.x86.R_SP = OFF(M.mem_size - 1) - 1;
|
||||
|
||||
X86EMU_exec();
|
||||
out->e.cflag = M.x86.R_EFLG & F_CF;
|
||||
out->e.eax = M.x86.R_EAX;
|
||||
out->e.ebx = M.x86.R_EBX;
|
||||
out->e.ecx = M.x86.R_ECX;
|
||||
out->e.edx = M.x86.R_EDX;
|
||||
out->e.esi = M.x86.R_ESI;
|
||||
out->e.edi = M.x86.R_EDI;
|
||||
return out->x.ax;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
intno - Interrupt number to execute
|
||||
in - Real mode registers to load
|
||||
out - Place to store resulting real mode registers
|
||||
sregs - Real mode segment registers to load
|
||||
|
||||
REMARKS:
|
||||
This functions calls a real mode interrupt function at the specified address,
|
||||
and loads all the x86 registers from the passed in registers structure.
|
||||
On exit the registers returned from the call are returned in out stucture.
|
||||
****************************************************************************/
|
||||
int X86API BE_int86x(int intno, RMREGS * in, RMREGS * out, RMSREGS * sregs)
|
||||
{
|
||||
M.x86.R_EAX = in->e.eax;
|
||||
M.x86.R_EBX = in->e.ebx;
|
||||
M.x86.R_ECX = in->e.ecx;
|
||||
M.x86.R_EDX = in->e.edx;
|
||||
M.x86.R_ESI = in->e.esi;
|
||||
M.x86.R_EDI = in->e.edi;
|
||||
M.x86.R_DS = sregs->ds;
|
||||
M.x86.R_ES = sregs->es;
|
||||
M.x86.R_FS = sregs->fs;
|
||||
M.x86.R_GS = sregs->gs;
|
||||
((u8 *) M.mem_base)[0x4000] = 0xCD;
|
||||
((u8 *) M.mem_base)[0x4001] = (u8) intno;
|
||||
((u8 *) M.mem_base)[0x4002] = 0xF1;
|
||||
M.x86.R_CS = SEG(0x04000);
|
||||
M.x86.R_IP = OFF(0x04000);
|
||||
|
||||
M.x86.R_SS = SEG(M.mem_size - 1);
|
||||
M.x86.R_SP = OFF(M.mem_size - 1) - 1;
|
||||
|
||||
X86EMU_exec();
|
||||
out->e.cflag = M.x86.R_EFLG & F_CF;
|
||||
out->e.eax = M.x86.R_EAX;
|
||||
out->e.ebx = M.x86.R_EBX;
|
||||
out->e.ecx = M.x86.R_ECX;
|
||||
out->e.edx = M.x86.R_EDX;
|
||||
out->e.esi = M.x86.R_ESI;
|
||||
out->e.edi = M.x86.R_EDI;
|
||||
sregs->ds = M.x86.R_DS;
|
||||
sregs->es = M.x86.R_ES;
|
||||
sregs->fs = M.x86.R_FS;
|
||||
sregs->gs = M.x86.R_GS;
|
||||
return out->x.ax;
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* BIOS emulator and interface
|
||||
* to Realmode X86 Emulator Library
|
||||
*
|
||||
* Copyright (C) 2007 Freescale Semiconductor, Inc.
|
||||
* Jason Jin <Jason.jin@freescale.com>
|
||||
*
|
||||
* Copyright (C) 1996-1999 SciTech Software, Inc.
|
||||
*
|
||||
* ========================================================================
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software and
|
||||
* its documentation for any purpose is hereby granted without fee,
|
||||
* provided that the above copyright notice appear in all copies and that
|
||||
* both that copyright notice and this permission notice appear in
|
||||
* supporting documentation, and that the name of the authors not be used
|
||||
* in advertising or publicity pertaining to distribution of the software
|
||||
* without specific, written prior permission. The authors makes no
|
||||
* representations about the suitability of this software for any purpose.
|
||||
* It is provided "as is" without express or implied warranty.
|
||||
*
|
||||
* THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
||||
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
||||
* EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
||||
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
|
||||
* USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* ========================================================================
|
||||
*
|
||||
* Language: ANSI C
|
||||
* Environment: Any
|
||||
* Developer: Kendall Bennett
|
||||
*
|
||||
* Description: Internal header file for the BIOS emulator library.
|
||||
*
|
||||
* Jason ported this file to u-boot, Added some architecture
|
||||
* related Macro.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef __BIOSEMUI_H
|
||||
#define __BIOSEMUI_H
|
||||
|
||||
#include "biosemu.h"
|
||||
#include <asm/io.h>
|
||||
/*---------------------- Macros and type definitions ----------------------*/
|
||||
|
||||
#ifdef CONFIG_X86EMU_DEBUG
|
||||
#define DB(x) x
|
||||
#else
|
||||
#define DB(x) do{}while(0);
|
||||
#endif
|
||||
|
||||
#define BIOS_SEG 0xfff0
|
||||
extern X86EMU_sysEnv _X86EMU_env;
|
||||
#define M _X86EMU_env
|
||||
|
||||
/* Macros to read and write values to x86 emulator memory. Memory is always
|
||||
* considered to be little endian, so we use macros to do endian swapping
|
||||
* where necessary.
|
||||
*/
|
||||
|
||||
#ifdef __BIG_ENDIAN__
|
||||
#define readb_le(base) *((u8*)(base))
|
||||
#define readw_le(base) ((u16)readb_le(base) | ((u16)readb_le((base) + 1) << 8))
|
||||
#define readl_le(base) ((u32)readb_le((base) + 0) | ((u32)readb_le((base) + 1) << 8) | \
|
||||
((u32)readb_le((base) + 2) << 16) | ((u32)readb_le((base) + 3) << 24))
|
||||
#define writeb_le(base, v) *((u8*)(base)) = (v)
|
||||
#define writew_le(base, v) writeb_le(base + 0, (v >> 0) & 0xff), \
|
||||
writeb_le(base + 1, (v >> 8) & 0xff)
|
||||
#define writel_le(base, v) writeb_le(base + 0, (v >> 0) & 0xff), \
|
||||
writeb_le(base + 1, (v >> 8) & 0xff), \
|
||||
writeb_le(base + 2, (v >> 16) & 0xff), \
|
||||
writeb_le(base + 3, (v >> 24) & 0xff)
|
||||
#else
|
||||
#define readb_le(base) *((u8*)(base))
|
||||
#define readw_le(base) *((u16*)(base))
|
||||
#define readl_le(base) *((u32*)(base))
|
||||
#define writeb_le(base, v) *((u8*)(base)) = (v)
|
||||
#define writew_le(base, v) *((u16*)(base)) = (v)
|
||||
#define writel_le(base, v) *((u32*)(base)) = (v)
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
REMARKS:
|
||||
Function codes passed to the emulated I/O port functions to determine the
|
||||
type of operation to perform.
|
||||
****************************************************************************/
|
||||
typedef enum {
|
||||
REG_READ_BYTE = 0,
|
||||
REG_READ_WORD = 1,
|
||||
REG_READ_DWORD = 2,
|
||||
REG_WRITE_BYTE = 3,
|
||||
REG_WRITE_WORD = 4,
|
||||
REG_WRITE_DWORD = 5
|
||||
} RegisterFlags;
|
||||
|
||||
/****************************************************************************
|
||||
REMARKS:
|
||||
Function codes passed to the emulated I/O port functions to determine the
|
||||
type of operation to perform.
|
||||
****************************************************************************/
|
||||
typedef enum {
|
||||
PORT_BYTE = 1,
|
||||
PORT_WORD = 2,
|
||||
PORT_DWORD = 3,
|
||||
} PortInfoFlags;
|
||||
|
||||
/****************************************************************************
|
||||
REMARKS:
|
||||
Data structure used to describe the details for the BIOS emulator system
|
||||
environment as used by the X86 emulator library.
|
||||
|
||||
HEADER:
|
||||
biosemu.h
|
||||
|
||||
MEMBERS:
|
||||
type - Type of port access (1 = byte, 2 = word, 3 = dword)
|
||||
defVal - Default power on value
|
||||
finalVal - Final value
|
||||
****************************************************************************/
|
||||
typedef struct {
|
||||
u8 type;
|
||||
u32 defVal;
|
||||
u32 finalVal;
|
||||
} BE_portInfo;
|
||||
|
||||
#define PM_inpb(port) inb(port+VIDEO_IO_OFFSET)
|
||||
#define PM_inpw(port) inw(port+VIDEO_IO_OFFSET)
|
||||
#define PM_inpd(port) inl(port+VIDEO_IO_OFFSET)
|
||||
#define PM_outpb(port,val) outb(val,port+VIDEO_IO_OFFSET)
|
||||
#define PM_outpw(port,val) outw(val,port+VIDEO_IO_OFFSET)
|
||||
#define PM_outpd(port,val) outl(val,port+VIDEO_IO_OFFSET)
|
||||
|
||||
#define LOG_inpb(port) PM_inpb(port)
|
||||
#define LOG_inpw(port) PM_inpw(port)
|
||||
#define LOG_inpd(port) PM_inpd(port)
|
||||
#define LOG_outpb(port,val) PM_outpb(port,val)
|
||||
#define LOG_outpw(port,val) PM_outpw(port,val)
|
||||
#define LOG_outpd(port,val) PM_outpd(port,val)
|
||||
|
||||
/*-------------------------- Function Prototypes --------------------------*/
|
||||
|
||||
/* bios.c */
|
||||
|
||||
void _BE_bios_init(u32 * intrTab);
|
||||
void _BE_setup_funcs(void);
|
||||
|
||||
/* besys.c */
|
||||
#define DEBUG_IO() (M.x86.debug & DEBUG_IO_TRACE_F)
|
||||
|
||||
u8 X86API BE_rdb(u32 addr);
|
||||
u16 X86API BE_rdw(u32 addr);
|
||||
u32 X86API BE_rdl(u32 addr);
|
||||
void X86API BE_wrb(u32 addr, u8 val);
|
||||
void X86API BE_wrw(u32 addr, u16 val);
|
||||
void X86API BE_wrl(u32 addr, u32 val);
|
||||
|
||||
u8 X86API BE_inb(X86EMU_pioAddr port);
|
||||
u16 X86API BE_inw(X86EMU_pioAddr port);
|
||||
u32 X86API BE_inl(X86EMU_pioAddr port);
|
||||
void X86API BE_outb(X86EMU_pioAddr port, u8 val);
|
||||
void X86API BE_outw(X86EMU_pioAddr port, u16 val);
|
||||
void X86API BE_outl(X86EMU_pioAddr port, u32 val);
|
||||
#endif
|
||||
/* __BIOSEMUI_H */
|
||||
@@ -0,0 +1,343 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* BIOS emulator and interface
|
||||
* to Realmode X86 Emulator Library
|
||||
*
|
||||
* Copyright (C) 1996-1999 SciTech Software, Inc.
|
||||
*
|
||||
* ========================================================================
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software and
|
||||
* its documentation for any purpose is hereby granted without fee,
|
||||
* provided that the above copyright notice appear in all copies and that
|
||||
* both that copyright notice and this permission notice appear in
|
||||
* supporting documentation, and that the name of the authors not be used
|
||||
* in advertising or publicity pertaining to distribution of the software
|
||||
* without specific, written prior permission. The authors makes no
|
||||
* representations about the suitability of this software for any purpose.
|
||||
* It is provided "as is" without express or implied warranty.
|
||||
*
|
||||
* THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
||||
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
||||
* EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
||||
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
|
||||
* USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* ========================================================================
|
||||
*
|
||||
* Language: ANSI C
|
||||
* Environment: Any
|
||||
* Developer: Kendall Bennett
|
||||
*
|
||||
* Description: Header file for the real mode x86 BIOS emulator, which is
|
||||
* used to warmboot any number of VGA compatible PCI/AGP
|
||||
* controllers under any OS, on any processor family that
|
||||
* supports PCI. We also allow the user application to call
|
||||
* real mode BIOS functions and Int 10h functions (including
|
||||
* the VESA BIOS).
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef __BIOSEMU_H
|
||||
#define __BIOSEMU_H
|
||||
|
||||
#include <bios_emul.h>
|
||||
|
||||
#ifdef __KERNEL__
|
||||
#include "x86emu.h"
|
||||
#else
|
||||
#include "x86emu.h"
|
||||
#include "pmapi.h"
|
||||
#include "pcilib.h"
|
||||
#endif
|
||||
|
||||
/*---------------------- Macros and type definitions ----------------------*/
|
||||
|
||||
#pragma pack(1)
|
||||
|
||||
#define CRT_C 24 /* 24 CRT Controller Registers */
|
||||
#define ATT_C 21 /* 21 Attribute Controller Registers */
|
||||
#define GRA_C 9 /* 9 Graphics Controller Registers */
|
||||
#define SEQ_C 5 /* 5 Sequencer Registers */
|
||||
#define PAL_C 768 /* 768 Palette Registers */
|
||||
|
||||
/****************************************************************************
|
||||
REMARKS:
|
||||
Data structure used to describe the details for the BIOS emulator system
|
||||
environment as used by the X86 emulator library.
|
||||
|
||||
HEADER:
|
||||
biosemu.h
|
||||
|
||||
MEMBERS:
|
||||
vgaInfo - VGA BIOS information structure
|
||||
biosmem_base - Base of the BIOS image
|
||||
biosmem_limit - Limit of the BIOS image
|
||||
busmem_base - Base of the VGA bus memory
|
||||
timer - Timer used to emulate PC timer ports
|
||||
timer0 - Latched value for timer 0
|
||||
timer0Latched - true if timer 0 value was just latched
|
||||
timer2 - Current value for timer 2
|
||||
emulateVGA - true to emulate VGA I/O and memory accesses
|
||||
****************************************************************************/
|
||||
|
||||
typedef struct {
|
||||
BE_VGAInfo vgaInfo;
|
||||
ulong biosmem_base;
|
||||
ulong biosmem_limit;
|
||||
ulong busmem_base;
|
||||
|
||||
u32 timer0;
|
||||
int timer0Latched;
|
||||
u32 timer1;
|
||||
int timer1Latched;
|
||||
u32 timer2;
|
||||
int timer2Latched;
|
||||
|
||||
int emulateVGA;
|
||||
u8 emu61;
|
||||
u8 emu70;
|
||||
int flipFlop3C0;
|
||||
u32 configAddress;
|
||||
u8 emu3C0;
|
||||
u8 emu3C1[ATT_C];
|
||||
u8 emu3C2;
|
||||
u8 emu3C4;
|
||||
u8 emu3C5[SEQ_C];
|
||||
u8 emu3C6;
|
||||
uint emu3C7;
|
||||
uint emu3C8;
|
||||
u8 emu3C9[PAL_C];
|
||||
u8 emu3CE;
|
||||
u8 emu3CF[GRA_C];
|
||||
u8 emu3D4;
|
||||
u8 emu3D5[CRT_C];
|
||||
u8 emu3DA;
|
||||
|
||||
} BE_sysEnv;
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
/* Define some types when compiling for the Linux kernel that normally
|
||||
* come from the SciTech PM library.
|
||||
*/
|
||||
|
||||
/****************************************************************************
|
||||
REMARKS:
|
||||
Structure describing the 32-bit extended x86 CPU registers
|
||||
|
||||
HEADER:
|
||||
pmapi.h
|
||||
|
||||
MEMBERS:
|
||||
eax - Value of the EAX register
|
||||
ebx - Value of the EBX register
|
||||
ecx - Value of the ECX register
|
||||
edx - Value of the EDX register
|
||||
esi - Value of the ESI register
|
||||
edi - Value of the EDI register
|
||||
cflag - Value of the carry flag
|
||||
****************************************************************************/
|
||||
typedef struct {
|
||||
u32 eax;
|
||||
u32 ebx;
|
||||
u32 ecx;
|
||||
u32 edx;
|
||||
u32 esi;
|
||||
u32 edi;
|
||||
u32 cflag;
|
||||
} RMDWORDREGS;
|
||||
|
||||
/****************************************************************************
|
||||
REMARKS:
|
||||
Structure describing the 16-bit x86 CPU registers
|
||||
|
||||
HEADER:
|
||||
pmapi.h
|
||||
|
||||
MEMBERS:
|
||||
ax - Value of the AX register
|
||||
bx - Value of the BX register
|
||||
cx - Value of the CX register
|
||||
dx - Value of the DX register
|
||||
si - Value of the SI register
|
||||
di - Value of the DI register
|
||||
cflag - Value of the carry flag
|
||||
****************************************************************************/
|
||||
#ifdef __BIG_ENDIAN__
|
||||
typedef struct {
|
||||
u16 ax_hi, ax;
|
||||
u16 bx_hi, bx;
|
||||
u16 cx_hi, cx;
|
||||
u16 dx_hi, dx;
|
||||
u16 si_hi, si;
|
||||
u16 di_hi, di;
|
||||
u16 cflag_hi, cflag;
|
||||
} RMWORDREGS;
|
||||
#else
|
||||
typedef struct {
|
||||
u16 ax, ax_hi;
|
||||
u16 bx, bx_hi;
|
||||
u16 cx, cx_hi;
|
||||
u16 dx, dx_hi;
|
||||
u16 si, si_hi;
|
||||
u16 di, di_hi;
|
||||
u16 cflag, cflag_hi;
|
||||
} RMWORDREGS;
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
REMARKS:
|
||||
Structure describing the 8-bit x86 CPU registers
|
||||
|
||||
HEADER:
|
||||
pmapi.h
|
||||
|
||||
MEMBERS:
|
||||
al - Value of the AL register
|
||||
ah - Value of the AH register
|
||||
bl - Value of the BL register
|
||||
bh - Value of the BH register
|
||||
cl - Value of the CL register
|
||||
ch - Value of the CH register
|
||||
dl - Value of the DL register
|
||||
dh - Value of the DH register
|
||||
****************************************************************************/
|
||||
#ifdef __BIG_ENDIAN__
|
||||
typedef struct {
|
||||
u16 ax_hi;
|
||||
u8 ah, al;
|
||||
u16 bx_hi;
|
||||
u8 bh, bl;
|
||||
u16 cx_hi;
|
||||
u8 ch, cl;
|
||||
u16 dx_hi;
|
||||
u8 dh, dl;
|
||||
} RMBYTEREGS;
|
||||
#else
|
||||
typedef struct {
|
||||
u8 al;
|
||||
u8 ah;
|
||||
u16 ax_hi;
|
||||
u8 bl;
|
||||
u8 bh;
|
||||
u16 bx_hi;
|
||||
u8 cl;
|
||||
u8 ch;
|
||||
u16 cx_hi;
|
||||
u8 dl;
|
||||
u8 dh;
|
||||
u16 dx_hi;
|
||||
} RMBYTEREGS;
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
REMARKS:
|
||||
Structure describing all the x86 CPU registers
|
||||
|
||||
HEADER:
|
||||
pmapi.h
|
||||
|
||||
MEMBERS:
|
||||
e - Member to access registers as 32-bit values
|
||||
x - Member to access registers as 16-bit values
|
||||
h - Member to access registers as 8-bit values
|
||||
****************************************************************************/
|
||||
typedef union {
|
||||
RMDWORDREGS e;
|
||||
RMWORDREGS x;
|
||||
RMBYTEREGS h;
|
||||
} RMREGS;
|
||||
|
||||
/****************************************************************************
|
||||
REMARKS:
|
||||
Structure describing all the x86 segment registers
|
||||
|
||||
HEADER:
|
||||
pmapi.h
|
||||
|
||||
MEMBERS:
|
||||
es - ES segment register
|
||||
cs - CS segment register
|
||||
ss - SS segment register
|
||||
ds - DS segment register
|
||||
fs - FS segment register
|
||||
gs - GS segment register
|
||||
****************************************************************************/
|
||||
typedef struct {
|
||||
u16 es;
|
||||
u16 cs;
|
||||
u16 ss;
|
||||
u16 ds;
|
||||
u16 fs;
|
||||
u16 gs;
|
||||
} RMSREGS;
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
#ifndef __KERNEL__
|
||||
|
||||
/****************************************************************************
|
||||
REMARKS:
|
||||
Structure defining all the BIOS Emulator API functions as exported from
|
||||
the Binary Portable DLL.
|
||||
{secret}
|
||||
****************************************************************************/
|
||||
typedef struct {
|
||||
ulong dwSize;
|
||||
ibool(PMAPIP BE_init) (u32 debugFlags, int memSize, BE_VGAInfo * info);
|
||||
void (PMAPIP BE_setVGA) (BE_VGAInfo * info);
|
||||
void (PMAPIP BE_getVGA) (BE_VGAInfo * info);
|
||||
void *(PMAPIP BE_mapRealPointer) (uint r_seg, uint r_off);
|
||||
void *(PMAPIP BE_getVESABuf) (uint * len, uint * rseg, uint * roff);
|
||||
void (PMAPIP BE_callRealMode) (uint seg, uint off, RMREGS * regs,
|
||||
RMSREGS * sregs);
|
||||
int (PMAPIP BE_int86) (int intno, RMREGS * in, RMREGS * out);
|
||||
int (PMAPIP BE_int86x) (int intno, RMREGS * in, RMREGS * out,
|
||||
RMSREGS * sregs);
|
||||
void *reserved1;
|
||||
void (PMAPIP BE_exit) (void);
|
||||
} BE_exports;
|
||||
|
||||
/****************************************************************************
|
||||
REMARKS:
|
||||
Function pointer type for the Binary Portable DLL initialisation entry point.
|
||||
{secret}
|
||||
****************************************************************************/
|
||||
typedef BE_exports *(PMAPIP BE_initLibrary_t) (PM_imports * PMImp);
|
||||
#endif
|
||||
|
||||
#pragma pack()
|
||||
|
||||
/*---------------------------- Global variables ---------------------------*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" { /* Use "C" linkage when in C++ mode */
|
||||
#endif
|
||||
|
||||
/* {secret} Global BIOS emulator system environment */
|
||||
extern BE_sysEnv _BE_env;
|
||||
|
||||
/*-------------------------- Function Prototypes --------------------------*/
|
||||
|
||||
/* BIOS emulator library entry points */
|
||||
int X86API BE_init(u32 debugFlags, int memSize, BE_VGAInfo * info,
|
||||
int shared);
|
||||
void X86API BE_setVGA(BE_VGAInfo * info);
|
||||
void X86API BE_getVGA(BE_VGAInfo * info);
|
||||
void X86API BE_setDebugFlags(u32 debugFlags);
|
||||
void *X86API BE_mapRealPointer(uint r_seg, uint r_off);
|
||||
void *X86API BE_getVESABuf(uint * len, uint * rseg, uint * roff);
|
||||
void X86API BE_callRealMode(uint seg, uint off, RMREGS * regs,
|
||||
RMSREGS * sregs);
|
||||
int X86API BE_int86(int intno, RMREGS * in, RMREGS * out);
|
||||
int X86API BE_int86x(int intno, RMREGS * in, RMREGS * out,
|
||||
RMSREGS * sregs);
|
||||
void X86API BE_exit(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* End of "C" linkage for C++ */
|
||||
#endif
|
||||
#endif /* __BIOSEMU_H */
|
||||
@@ -0,0 +1,202 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* Realmode X86 Emulator Library
|
||||
*
|
||||
* Copyright (C) 1996-1999 SciTech Software, Inc.
|
||||
* Copyright (C) David Mosberger-Tang
|
||||
* Copyright (C) 1999 Egbert Eich
|
||||
*
|
||||
* ========================================================================
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software and
|
||||
* its documentation for any purpose is hereby granted without fee,
|
||||
* provided that the above copyright notice appear in all copies and that
|
||||
* both that copyright notice and this permission notice appear in
|
||||
* supporting documentation, and that the name of the authors not be used
|
||||
* in advertising or publicity pertaining to distribution of the software
|
||||
* without specific, written prior permission. The authors makes no
|
||||
* representations about the suitability of this software for any purpose.
|
||||
* It is provided "as is" without express or implied warranty.
|
||||
*
|
||||
* THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
||||
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
||||
* EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
||||
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
|
||||
* USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* ========================================================================
|
||||
*
|
||||
* Language: ANSI C
|
||||
* Environment: Any
|
||||
* Developer: Kendall Bennett
|
||||
*
|
||||
* Description: Header file for public specific functions.
|
||||
* Any application linking against us should only
|
||||
* include this header
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef __X86EMU_X86EMU_H
|
||||
#define __X86EMU_X86EMU_H
|
||||
|
||||
#include <asm/types.h>
|
||||
#include <common.h>
|
||||
#include <pci.h>
|
||||
#include <asm/io.h>
|
||||
#define X86API
|
||||
#define X86APIP *
|
||||
typedef u16 X86EMU_pioAddr;
|
||||
|
||||
#include "x86emu/regs.h"
|
||||
|
||||
/*---------------------- Macros and type definitions ----------------------*/
|
||||
|
||||
#if defined(CONFIG_ARM)
|
||||
#define GAS_LINE_COMMENT "@"
|
||||
#elif defined(CONFIG_MIPS) || defined(CONFIG_PPC) || defined(CONFIG_X86)
|
||||
#define GAS_LINE_COMMENT "#"
|
||||
#elif defined (CONFIG_SH)
|
||||
#define GAS_LINE_COMMENT "!"
|
||||
#endif
|
||||
|
||||
#define GOT2_TYPE ".got2,\"aw\"\t"GAS_LINE_COMMENT
|
||||
|
||||
#pragma pack(1)
|
||||
|
||||
/****************************************************************************
|
||||
REMARKS:
|
||||
Data structure containing ponters to programmed I/O functions used by the
|
||||
emulator. This is used so that the user program can hook all programmed
|
||||
I/O for the emulator to handled as necessary by the user program. By
|
||||
default the emulator contains simple functions that do not do access the
|
||||
hardware in any way. To allow the emualtor access the hardware, you will
|
||||
need to override the programmed I/O functions using the X86EMU_setupPioFuncs
|
||||
function.
|
||||
|
||||
HEADER:
|
||||
x86emu.h
|
||||
|
||||
MEMBERS:
|
||||
inb - Function to read a byte from an I/O port
|
||||
inw - Function to read a word from an I/O port
|
||||
inl - Function to read a dword from an I/O port
|
||||
outb - Function to write a byte to an I/O port
|
||||
outw - Function to write a word to an I/O port
|
||||
outl - Function to write a dword to an I/O port
|
||||
****************************************************************************/
|
||||
typedef struct {
|
||||
u8(X86APIP inb) (X86EMU_pioAddr addr);
|
||||
u16(X86APIP inw) (X86EMU_pioAddr addr);
|
||||
u32(X86APIP inl) (X86EMU_pioAddr addr);
|
||||
void (X86APIP outb) (X86EMU_pioAddr addr, u8 val);
|
||||
void (X86APIP outw) (X86EMU_pioAddr addr, u16 val);
|
||||
void (X86APIP outl) (X86EMU_pioAddr addr, u32 val);
|
||||
} X86EMU_pioFuncs;
|
||||
|
||||
/****************************************************************************
|
||||
REMARKS:
|
||||
Data structure containing ponters to memory access functions used by the
|
||||
emulator. This is used so that the user program can hook all memory
|
||||
access functions as necessary for the emulator. By default the emulator
|
||||
contains simple functions that only access the internal memory of the
|
||||
emulator. If you need specialised functions to handle access to different
|
||||
types of memory (ie: hardware framebuffer accesses and BIOS memory access
|
||||
etc), you will need to override this using the X86EMU_setupMemFuncs
|
||||
function.
|
||||
|
||||
HEADER:
|
||||
x86emu.h
|
||||
|
||||
MEMBERS:
|
||||
rdb - Function to read a byte from an address
|
||||
rdw - Function to read a word from an address
|
||||
rdl - Function to read a dword from an address
|
||||
wrb - Function to write a byte to an address
|
||||
wrw - Function to write a word to an address
|
||||
wrl - Function to write a dword to an address
|
||||
****************************************************************************/
|
||||
typedef struct {
|
||||
u8(X86APIP rdb) (u32 addr);
|
||||
u16(X86APIP rdw) (u32 addr);
|
||||
u32(X86APIP rdl) (u32 addr);
|
||||
void (X86APIP wrb) (u32 addr, u8 val);
|
||||
void (X86APIP wrw) (u32 addr, u16 val);
|
||||
void (X86APIP wrl) (u32 addr, u32 val);
|
||||
} X86EMU_memFuncs;
|
||||
|
||||
/****************************************************************************
|
||||
Here are the default memory read and write
|
||||
function in case they are needed as fallbacks.
|
||||
***************************************************************************/
|
||||
extern u8 X86API rdb(u32 addr);
|
||||
extern u16 X86API rdw(u32 addr);
|
||||
extern u32 X86API rdl(u32 addr);
|
||||
extern void X86API wrb(u32 addr, u8 val);
|
||||
extern void X86API wrw(u32 addr, u16 val);
|
||||
extern void X86API wrl(u32 addr, u32 val);
|
||||
|
||||
#pragma pack()
|
||||
|
||||
/*--------------------- type definitions -----------------------------------*/
|
||||
|
||||
typedef void (X86APIP X86EMU_intrFuncs) (int num);
|
||||
extern X86EMU_intrFuncs _X86EMU_intrTab[256];
|
||||
|
||||
/*-------------------------- Function Prototypes --------------------------*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" { /* Use "C" linkage when in C++ mode */
|
||||
#endif
|
||||
|
||||
void X86EMU_setupMemFuncs(X86EMU_memFuncs * funcs);
|
||||
void X86EMU_setupPioFuncs(X86EMU_pioFuncs * funcs);
|
||||
void X86EMU_setupIntrFuncs(X86EMU_intrFuncs funcs[]);
|
||||
void X86EMU_setupIntrFunc(int intnum, X86EMU_intrFuncs func);
|
||||
void X86EMU_prepareForInt(int num);
|
||||
|
||||
/* decode.c */
|
||||
|
||||
void X86EMU_exec(void);
|
||||
void X86EMU_halt_sys(void);
|
||||
|
||||
#ifdef CONFIG_X86EMU_DEBUG
|
||||
#define HALT_SYS() \
|
||||
printf("halt_sys: file %s, line %d\n", __FILE__, __LINE__), \
|
||||
X86EMU_halt_sys()
|
||||
#else
|
||||
#define HALT_SYS() X86EMU_halt_sys()
|
||||
#endif
|
||||
|
||||
/* Debug options */
|
||||
|
||||
#define DEBUG_DECODE_F 0x0001 /* print decoded instruction */
|
||||
#define DEBUG_TRACE_F 0x0002 /* dump regs before/after execution */
|
||||
#define DEBUG_STEP_F 0x0004
|
||||
#define DEBUG_DISASSEMBLE_F 0x0008
|
||||
#define DEBUG_BREAK_F 0x0010
|
||||
#define DEBUG_SVC_F 0x0020
|
||||
#define DEBUG_SAVE_CS_IP 0x0040
|
||||
#define DEBUG_FS_F 0x0080
|
||||
#define DEBUG_PROC_F 0x0100
|
||||
#define DEBUG_SYSINT_F 0x0200 /* bios system interrupts. */
|
||||
#define DEBUG_TRACECALL_F 0x0400
|
||||
#define DEBUG_INSTRUMENT_F 0x0800
|
||||
#define DEBUG_MEM_TRACE_F 0x1000
|
||||
#define DEBUG_IO_TRACE_F 0x2000
|
||||
#define DEBUG_TRACECALL_REGS_F 0x4000
|
||||
#define DEBUG_DECODE_NOPRINT_F 0x8000
|
||||
#define DEBUG_EXIT 0x10000
|
||||
#define DEBUG_SYS_F (DEBUG_SVC_F|DEBUG_FS_F|DEBUG_PROC_F)
|
||||
|
||||
void X86EMU_trace_regs(void);
|
||||
void X86EMU_trace_xregs(void);
|
||||
void X86EMU_dump_memory(u16 seg, u16 off, u32 amt);
|
||||
int X86EMU_trace_on(void);
|
||||
int X86EMU_trace_off(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* End of "C" linkage for C++ */
|
||||
#endif
|
||||
#endif /* __X86EMU_X86EMU_H */
|
||||
@@ -0,0 +1,213 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* Realmode X86 Emulator Library
|
||||
*
|
||||
* Copyright (C) 1991-2004 SciTech Software, Inc.
|
||||
* Copyright (C) David Mosberger-Tang
|
||||
* Copyright (C) 1999 Egbert Eich
|
||||
*
|
||||
* ========================================================================
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software and
|
||||
* its documentation for any purpose is hereby granted without fee,
|
||||
* provided that the above copyright notice appear in all copies and that
|
||||
* both that copyright notice and this permission notice appear in
|
||||
* supporting documentation, and that the name of the authors not be used
|
||||
* in advertising or publicity pertaining to distribution of the software
|
||||
* without specific, written prior permission. The authors makes no
|
||||
* representations about the suitability of this software for any purpose.
|
||||
* It is provided "as is" without express or implied warranty.
|
||||
*
|
||||
* THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
||||
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
||||
* EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
||||
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
|
||||
* USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* ========================================================================
|
||||
*
|
||||
* Language: ANSI C
|
||||
* Environment: Any
|
||||
* Developer: Kendall Bennett
|
||||
*
|
||||
* Description: Header file for debug definitions.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef __X86EMU_DEBUG_H
|
||||
#define __X86EMU_DEBUG_H
|
||||
|
||||
/*---------------------- Macros and type definitions ----------------------*/
|
||||
|
||||
/* checks to be enabled for "runtime" */
|
||||
|
||||
#define CHECK_IP_FETCH_F 0x1
|
||||
#define CHECK_SP_ACCESS_F 0x2
|
||||
#define CHECK_MEM_ACCESS_F 0x4 /*using regular linear pointer */
|
||||
#define CHECK_DATA_ACCESS_F 0x8 /*using segment:offset */
|
||||
|
||||
#ifdef CONFIG_X86EMU_DEBUG
|
||||
# define CHECK_IP_FETCH() (M.x86.check & CHECK_IP_FETCH_F)
|
||||
# define CHECK_SP_ACCESS() (M.x86.check & CHECK_SP_ACCESS_F)
|
||||
# define CHECK_MEM_ACCESS() (M.x86.check & CHECK_MEM_ACCESS_F)
|
||||
# define CHECK_DATA_ACCESS() (M.x86.check & CHECK_DATA_ACCESS_F)
|
||||
#else
|
||||
# define CHECK_IP_FETCH()
|
||||
# define CHECK_SP_ACCESS()
|
||||
# define CHECK_MEM_ACCESS()
|
||||
# define CHECK_DATA_ACCESS()
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_X86EMU_DEBUG
|
||||
# define DEBUG_INSTRUMENT() (M.x86.debug & DEBUG_INSTRUMENT_F)
|
||||
# define DEBUG_DECODE() (M.x86.debug & DEBUG_DECODE_F)
|
||||
# define DEBUG_TRACE() (M.x86.debug & DEBUG_TRACE_F)
|
||||
# define DEBUG_STEP() (M.x86.debug & DEBUG_STEP_F)
|
||||
# define DEBUG_DISASSEMBLE() (M.x86.debug & DEBUG_DISASSEMBLE_F)
|
||||
# define DEBUG_BREAK() (M.x86.debug & DEBUG_BREAK_F)
|
||||
# define DEBUG_SVC() (M.x86.debug & DEBUG_SVC_F)
|
||||
# define DEBUG_SAVE_IP_CS() (M.x86.debug & DEBUG_SAVE_CS_IP)
|
||||
|
||||
# define DEBUG_FS() (M.x86.debug & DEBUG_FS_F)
|
||||
# define DEBUG_PROC() (M.x86.debug & DEBUG_PROC_F)
|
||||
# define DEBUG_SYSINT() (M.x86.debug & DEBUG_SYSINT_F)
|
||||
# define DEBUG_TRACECALL() (M.x86.debug & DEBUG_TRACECALL_F)
|
||||
# define DEBUG_TRACECALLREGS() (M.x86.debug & DEBUG_TRACECALL_REGS_F)
|
||||
# define DEBUG_SYS() (M.x86.debug & DEBUG_SYS_F)
|
||||
# define DEBUG_MEM_TRACE() (M.x86.debug & DEBUG_MEM_TRACE_F)
|
||||
# define DEBUG_IO_TRACE() (M.x86.debug & DEBUG_IO_TRACE_F)
|
||||
# define DEBUG_DECODE_NOPRINT() (M.x86.debug & DEBUG_DECODE_NOPRINT_F)
|
||||
#else
|
||||
# define DEBUG_INSTRUMENT() 0
|
||||
# define DEBUG_DECODE() 0
|
||||
# define DEBUG_TRACE() 0
|
||||
# define DEBUG_STEP() 0
|
||||
# define DEBUG_DISASSEMBLE() 0
|
||||
# define DEBUG_BREAK() 0
|
||||
# define DEBUG_SVC() 0
|
||||
# define DEBUG_SAVE_IP_CS() 0
|
||||
# define DEBUG_FS() 0
|
||||
# define DEBUG_PROC() 0
|
||||
# define DEBUG_SYSINT() 0
|
||||
# define DEBUG_TRACECALL() 0
|
||||
# define DEBUG_TRACECALLREGS() 0
|
||||
# define DEBUG_SYS() 0
|
||||
# define DEBUG_MEM_TRACE() 0
|
||||
# define DEBUG_IO_TRACE() 0
|
||||
# define DEBUG_DECODE_NOPRINT() 0
|
||||
#endif
|
||||
|
||||
# define ERR_PRINTF(x) printf(x)
|
||||
# define ERR_PRINTF2(x, y) printf(x, y)
|
||||
|
||||
#ifdef CONFIG_X86EMU_DEBUG
|
||||
|
||||
|
||||
# define DECODE_PRINTF(x) if (DEBUG_DECODE()) \
|
||||
x86emu_decode_printf(x)
|
||||
# define DECODE_PRINTF2(x,y) if (DEBUG_DECODE()) \
|
||||
x86emu_decode_printf2(x,y)
|
||||
|
||||
/*
|
||||
* The following allow us to look at the bytes of an instruction. The
|
||||
* first INCR_INSTRN_LEN, is called everytime bytes are consumed in
|
||||
* the decoding process. The SAVE_IP_CS is called initially when the
|
||||
* major opcode of the instruction is accessed.
|
||||
*/
|
||||
#define INC_DECODED_INST_LEN(x) \
|
||||
if (DEBUG_DECODE()) \
|
||||
x86emu_inc_decoded_inst_len(x)
|
||||
|
||||
#define SAVE_IP_CS(x,y) \
|
||||
if (DEBUG_DECODE() | DEBUG_TRACECALL() | DEBUG_BREAK() \
|
||||
| DEBUG_IO_TRACE() | DEBUG_SAVE_IP_CS()) { \
|
||||
M.x86.saved_cs = x; \
|
||||
M.x86.saved_ip = y; \
|
||||
}
|
||||
#else
|
||||
# define INC_DECODED_INST_LEN(x)
|
||||
# define DECODE_PRINTF(x)
|
||||
# define DECODE_PRINTF2(x,y)
|
||||
# define SAVE_IP_CS(x,y)
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_X86EMU_DEBUG
|
||||
#define TRACE_REGS() \
|
||||
if (DEBUG_DISASSEMBLE()) { \
|
||||
x86emu_just_disassemble(); \
|
||||
goto EndOfTheInstructionProcedure; \
|
||||
} \
|
||||
if (DEBUG_TRACE() || DEBUG_DECODE()) X86EMU_trace_regs()
|
||||
#else
|
||||
# define TRACE_REGS()
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_X86EMU_DEBUG
|
||||
# define SINGLE_STEP() if (DEBUG_STEP()) x86emu_single_step()
|
||||
#else
|
||||
# define SINGLE_STEP()
|
||||
#endif
|
||||
|
||||
#define TRACE_AND_STEP() \
|
||||
TRACE_REGS(); \
|
||||
SINGLE_STEP()
|
||||
|
||||
#ifdef CONFIG_X86EMU_DEBUG
|
||||
# define START_OF_INSTR()
|
||||
# define END_OF_INSTR() EndOfTheInstructionProcedure: x86emu_end_instr();
|
||||
# define END_OF_INSTR_NO_TRACE() x86emu_end_instr();
|
||||
#else
|
||||
# define START_OF_INSTR()
|
||||
# define END_OF_INSTR()
|
||||
# define END_OF_INSTR_NO_TRACE()
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_X86EMU_DEBUG
|
||||
# define CALL_TRACE(u,v,w,x,s) \
|
||||
if (DEBUG_TRACECALLREGS()) \
|
||||
x86emu_dump_regs(); \
|
||||
if (DEBUG_TRACECALL()) \
|
||||
printk("%04x:%04x: CALL %s%04x:%04x\n", u , v, s, w, x);
|
||||
# define RETURN_TRACE(n,u,v) \
|
||||
if (DEBUG_TRACECALLREGS()) \
|
||||
x86emu_dump_regs(); \
|
||||
if (DEBUG_TRACECALL()) \
|
||||
printk("%04x:%04x: %s\n",u,v,n);
|
||||
#else
|
||||
# define CALL_TRACE(u,v,w,x,s)
|
||||
# define RETURN_TRACE(n,u,v)
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_X86EMU_DEBUG
|
||||
#define DB(x) x
|
||||
#else
|
||||
#define DB(x)
|
||||
#endif
|
||||
|
||||
/*-------------------------- Function Prototypes --------------------------*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" { /* Use "C" linkage when in C++ mode */
|
||||
#endif
|
||||
|
||||
extern void x86emu_inc_decoded_inst_len(int x);
|
||||
extern void x86emu_decode_printf(char *x);
|
||||
extern void x86emu_decode_printf2(char *x, int y);
|
||||
extern void x86emu_just_disassemble(void);
|
||||
extern void x86emu_single_step(void);
|
||||
extern void x86emu_end_instr(void);
|
||||
extern void x86emu_dump_regs(void);
|
||||
extern void x86emu_dump_xregs(void);
|
||||
extern void x86emu_print_int_vect(u16 iv);
|
||||
extern void x86emu_instrument_instruction(void);
|
||||
extern void x86emu_check_ip_access(void);
|
||||
extern void x86emu_check_sp_access(void);
|
||||
extern void x86emu_check_mem_access(u32 p);
|
||||
extern void x86emu_check_data_access(uint s, uint o);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* End of "C" linkage for C++ */
|
||||
#endif
|
||||
#endif /* __X86EMU_DEBUG_H */
|
||||
@@ -0,0 +1,88 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* Realmode X86 Emulator Library
|
||||
*
|
||||
* Copyright (C) 1991-2004 SciTech Software, Inc.
|
||||
* Copyright (C) David Mosberger-Tang
|
||||
* Copyright (C) 1999 Egbert Eich
|
||||
*
|
||||
* ========================================================================
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software and
|
||||
* its documentation for any purpose is hereby granted without fee,
|
||||
* provided that the above copyright notice appear in all copies and that
|
||||
* both that copyright notice and this permission notice appear in
|
||||
* supporting documentation, and that the name of the authors not be used
|
||||
* in advertising or publicity pertaining to distribution of the software
|
||||
* without specific, written prior permission. The authors makes no
|
||||
* representations about the suitability of this software for any purpose.
|
||||
* It is provided "as is" without express or implied warranty.
|
||||
*
|
||||
* THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
||||
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
||||
* EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
||||
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
|
||||
* USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* ========================================================================
|
||||
*
|
||||
* Language: ANSI C
|
||||
* Environment: Any
|
||||
* Developer: Kendall Bennett
|
||||
*
|
||||
* Description: Header file for instruction decoding logic.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef __X86EMU_DECODE_H
|
||||
#define __X86EMU_DECODE_H
|
||||
|
||||
/*---------------------- Macros and type definitions ----------------------*/
|
||||
|
||||
/* Instruction Decoding Stuff */
|
||||
|
||||
#define FETCH_DECODE_MODRM(mod,rh,rl) fetch_decode_modrm(&mod,&rh,&rl)
|
||||
#define DECODE_RM_BYTE_REGISTER(r) decode_rm_byte_register(r)
|
||||
#define DECODE_RM_WORD_REGISTER(r) decode_rm_word_register(r)
|
||||
#define DECODE_RM_LONG_REGISTER(r) decode_rm_long_register(r)
|
||||
#define DECODE_CLEAR_SEGOVR() M.x86.mode &= ~SYSMODE_CLRMASK
|
||||
|
||||
/*-------------------------- Function Prototypes --------------------------*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" { /* Use "C" linkage when in C++ mode */
|
||||
#endif
|
||||
|
||||
void x86emu_intr_raise (u8 type);
|
||||
void fetch_decode_modrm (int *mod,int *regh,int *regl);
|
||||
u8 fetch_byte_imm (void);
|
||||
u16 fetch_word_imm (void);
|
||||
u32 fetch_long_imm (void);
|
||||
u8 fetch_data_byte (uint offset);
|
||||
u8 fetch_data_byte_abs (uint segment, uint offset);
|
||||
u16 fetch_data_word (uint offset);
|
||||
u16 fetch_data_word_abs (uint segment, uint offset);
|
||||
u32 fetch_data_long (uint offset);
|
||||
u32 fetch_data_long_abs (uint segment, uint offset);
|
||||
void store_data_byte (uint offset, u8 val);
|
||||
void store_data_byte_abs (uint segment, uint offset, u8 val);
|
||||
void store_data_word (uint offset, u16 val);
|
||||
void store_data_word_abs (uint segment, uint offset, u16 val);
|
||||
void store_data_long (uint offset, u32 val);
|
||||
void store_data_long_abs (uint segment, uint offset, u32 val);
|
||||
u8* decode_rm_byte_register(int reg);
|
||||
u16* decode_rm_word_register(int reg);
|
||||
u32* decode_rm_long_register(int reg);
|
||||
u16* decode_rm_seg_register(int reg);
|
||||
unsigned decode_rm00_address(int rm);
|
||||
unsigned decode_rm01_address(int rm);
|
||||
unsigned decode_rm10_address(int rm);
|
||||
unsigned decode_rmXX_address(int mod, int rm);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* End of "C" linkage for C++ */
|
||||
#endif
|
||||
|
||||
#endif /* __X86EMU_DECODE_H */
|
||||
@@ -0,0 +1,45 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* Realmode X86 Emulator Library
|
||||
*
|
||||
* Copyright (C) 1991-2004 SciTech Software, Inc.
|
||||
* Copyright (C) David Mosberger-Tang
|
||||
* Copyright (C) 1999 Egbert Eich
|
||||
*
|
||||
* ========================================================================
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software and
|
||||
* its documentation for any purpose is hereby granted without fee,
|
||||
* provided that the above copyright notice appear in all copies and that
|
||||
* both that copyright notice and this permission notice appear in
|
||||
* supporting documentation, and that the name of the authors not be used
|
||||
* in advertising or publicity pertaining to distribution of the software
|
||||
* without specific, written prior permission. The authors makes no
|
||||
* representations about the suitability of this software for any purpose.
|
||||
* It is provided "as is" without express or implied warranty.
|
||||
*
|
||||
* THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
||||
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
||||
* EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
||||
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
|
||||
* USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* ========================================================================
|
||||
*
|
||||
* Language: ANSI C
|
||||
* Environment: Any
|
||||
* Developer: Kendall Bennett
|
||||
*
|
||||
* Description: Header file for operand decoding functions.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef __X86EMU_OPS_H
|
||||
#define __X86EMU_OPS_H
|
||||
|
||||
extern void (*x86emu_optab[0x100])(u8 op1);
|
||||
extern void (*x86emu_optab2[0x100])(u8 op2);
|
||||
|
||||
#endif /* __X86EMU_OPS_H */
|
||||
@@ -0,0 +1,141 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* Realmode X86 Emulator Library
|
||||
*
|
||||
* Copyright (C) 1991-2004 SciTech Software, Inc.
|
||||
* Copyright (C) David Mosberger-Tang
|
||||
* Copyright (C) 1999 Egbert Eich
|
||||
*
|
||||
* ========================================================================
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software and
|
||||
* its documentation for any purpose is hereby granted without fee,
|
||||
* provided that the above copyright notice appear in all copies and that
|
||||
* both that copyright notice and this permission notice appear in
|
||||
* supporting documentation, and that the name of the authors not be used
|
||||
* in advertising or publicity pertaining to distribution of the software
|
||||
* without specific, written prior permission. The authors makes no
|
||||
* representations about the suitability of this software for any purpose.
|
||||
* It is provided "as is" without express or implied warranty.
|
||||
*
|
||||
* THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
||||
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
||||
* EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
||||
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
|
||||
* USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* ========================================================================
|
||||
*
|
||||
* Language: ANSI C
|
||||
* Environment: Any
|
||||
* Developer: Kendall Bennett
|
||||
*
|
||||
* Description: Header file for primitive operation functions.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef __X86EMU_PRIM_OPS_H
|
||||
#define __X86EMU_PRIM_OPS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" { /* Use "C" linkage when in C++ mode */
|
||||
#endif
|
||||
|
||||
u16 aaa_word (u16 d);
|
||||
u16 aas_word (u16 d);
|
||||
u16 aad_word (u16 d);
|
||||
u16 aam_word (u8 d);
|
||||
u8 adc_byte (u8 d, u8 s);
|
||||
u16 adc_word (u16 d, u16 s);
|
||||
u32 adc_long (u32 d, u32 s);
|
||||
u8 add_byte (u8 d, u8 s);
|
||||
u16 add_word (u16 d, u16 s);
|
||||
u32 add_long (u32 d, u32 s);
|
||||
u8 and_byte (u8 d, u8 s);
|
||||
u16 and_word (u16 d, u16 s);
|
||||
u32 and_long (u32 d, u32 s);
|
||||
u8 cmp_byte (u8 d, u8 s);
|
||||
u16 cmp_word (u16 d, u16 s);
|
||||
u32 cmp_long (u32 d, u32 s);
|
||||
u8 daa_byte (u8 d);
|
||||
u8 das_byte (u8 d);
|
||||
u8 dec_byte (u8 d);
|
||||
u16 dec_word (u16 d);
|
||||
u32 dec_long (u32 d);
|
||||
u8 inc_byte (u8 d);
|
||||
u16 inc_word (u16 d);
|
||||
u32 inc_long (u32 d);
|
||||
u8 or_byte (u8 d, u8 s);
|
||||
u16 or_word (u16 d, u16 s);
|
||||
u32 or_long (u32 d, u32 s);
|
||||
u8 neg_byte (u8 s);
|
||||
u16 neg_word (u16 s);
|
||||
u32 neg_long (u32 s);
|
||||
u8 not_byte (u8 s);
|
||||
u16 not_word (u16 s);
|
||||
u32 not_long (u32 s);
|
||||
u8 rcl_byte (u8 d, u8 s);
|
||||
u16 rcl_word (u16 d, u8 s);
|
||||
u32 rcl_long (u32 d, u8 s);
|
||||
u8 rcr_byte (u8 d, u8 s);
|
||||
u16 rcr_word (u16 d, u8 s);
|
||||
u32 rcr_long (u32 d, u8 s);
|
||||
u8 rol_byte (u8 d, u8 s);
|
||||
u16 rol_word (u16 d, u8 s);
|
||||
u32 rol_long (u32 d, u8 s);
|
||||
u8 ror_byte (u8 d, u8 s);
|
||||
u16 ror_word (u16 d, u8 s);
|
||||
u32 ror_long (u32 d, u8 s);
|
||||
u8 shl_byte (u8 d, u8 s);
|
||||
u16 shl_word (u16 d, u8 s);
|
||||
u32 shl_long (u32 d, u8 s);
|
||||
u8 shr_byte (u8 d, u8 s);
|
||||
u16 shr_word (u16 d, u8 s);
|
||||
u32 shr_long (u32 d, u8 s);
|
||||
u8 sar_byte (u8 d, u8 s);
|
||||
u16 sar_word (u16 d, u8 s);
|
||||
u32 sar_long (u32 d, u8 s);
|
||||
u16 shld_word (u16 d, u16 fill, u8 s);
|
||||
u32 shld_long (u32 d, u32 fill, u8 s);
|
||||
u16 shrd_word (u16 d, u16 fill, u8 s);
|
||||
u32 shrd_long (u32 d, u32 fill, u8 s);
|
||||
u8 sbb_byte (u8 d, u8 s);
|
||||
u16 sbb_word (u16 d, u16 s);
|
||||
u32 sbb_long (u32 d, u32 s);
|
||||
u8 sub_byte (u8 d, u8 s);
|
||||
u16 sub_word (u16 d, u16 s);
|
||||
u32 sub_long (u32 d, u32 s);
|
||||
void test_byte (u8 d, u8 s);
|
||||
void test_word (u16 d, u16 s);
|
||||
void test_long (u32 d, u32 s);
|
||||
u8 xor_byte (u8 d, u8 s);
|
||||
u16 xor_word (u16 d, u16 s);
|
||||
u32 xor_long (u32 d, u32 s);
|
||||
void imul_byte (u8 s);
|
||||
void imul_word (u16 s);
|
||||
void imul_long (u32 s);
|
||||
void imul_long_direct(u32 *res_lo, u32* res_hi,u32 d, u32 s);
|
||||
void mul_byte (u8 s);
|
||||
void mul_word (u16 s);
|
||||
void mul_long (u32 s);
|
||||
void idiv_byte (u8 s);
|
||||
void idiv_word (u16 s);
|
||||
void idiv_long (u32 s);
|
||||
void div_byte (u8 s);
|
||||
void div_word (u16 s);
|
||||
void div_long (u32 s);
|
||||
void ins (int size);
|
||||
void outs (int size);
|
||||
u16 mem_access_word (int addr);
|
||||
void push_word (u16 w);
|
||||
void push_long (u32 w);
|
||||
u16 pop_word (void);
|
||||
u32 pop_long (void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* End of "C" linkage for C++ */
|
||||
#endif
|
||||
|
||||
#endif /* __X86EMU_PRIM_OPS_H */
|
||||
@@ -0,0 +1,340 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* Realmode X86 Emulator Library
|
||||
*
|
||||
* Copyright (C) 1991-2004 SciTech Software, Inc.
|
||||
* Copyright (C) David Mosberger-Tang
|
||||
* Copyright (C) 1999 Egbert Eich
|
||||
*
|
||||
* ========================================================================
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software and
|
||||
* its documentation for any purpose is hereby granted without fee,
|
||||
* provided that the above copyright notice appear in all copies and that
|
||||
* both that copyright notice and this permission notice appear in
|
||||
* supporting documentation, and that the name of the authors not be used
|
||||
* in advertising or publicity pertaining to distribution of the software
|
||||
* without specific, written prior permission. The authors makes no
|
||||
* representations about the suitability of this software for any purpose.
|
||||
* It is provided "as is" without express or implied warranty.
|
||||
*
|
||||
* THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
||||
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
||||
* EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
||||
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
|
||||
* USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* ========================================================================
|
||||
*
|
||||
* Language: ANSI C
|
||||
* Environment: Any
|
||||
* Developer: Kendall Bennett
|
||||
*
|
||||
* Description: Header file for x86 register definitions.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef __X86EMU_REGS_H
|
||||
#define __X86EMU_REGS_H
|
||||
|
||||
/*---------------------- Macros and type definitions ----------------------*/
|
||||
|
||||
#pragma pack(1)
|
||||
|
||||
/*
|
||||
* General EAX, EBX, ECX, EDX type registers. Note that for
|
||||
* portability, and speed, the issue of byte swapping is not addressed
|
||||
* in the registers. All registers are stored in the default format
|
||||
* available on the host machine. The only critical issue is that the
|
||||
* registers should line up EXACTLY in the same manner as they do in
|
||||
* the 386. That is:
|
||||
*
|
||||
* EAX & 0xff === AL
|
||||
* EAX & 0xffff == AX
|
||||
*
|
||||
* etc. The result is that alot of the calculations can then be
|
||||
* done using the native instruction set fully.
|
||||
*/
|
||||
|
||||
#ifdef __BIG_ENDIAN__
|
||||
|
||||
typedef struct {
|
||||
u32 e_reg;
|
||||
} I32_reg_t;
|
||||
|
||||
typedef struct {
|
||||
u16 filler0, x_reg;
|
||||
} I16_reg_t;
|
||||
|
||||
typedef struct {
|
||||
u8 filler0, filler1, h_reg, l_reg;
|
||||
} I8_reg_t;
|
||||
|
||||
#else /* !__BIG_ENDIAN__ */
|
||||
|
||||
typedef struct {
|
||||
u32 e_reg;
|
||||
} I32_reg_t;
|
||||
|
||||
typedef struct {
|
||||
u16 x_reg;
|
||||
} I16_reg_t;
|
||||
|
||||
typedef struct {
|
||||
u8 l_reg, h_reg;
|
||||
} I8_reg_t;
|
||||
|
||||
#endif /* BIG_ENDIAN */
|
||||
|
||||
typedef union {
|
||||
I32_reg_t I32_reg;
|
||||
I16_reg_t I16_reg;
|
||||
I8_reg_t I8_reg;
|
||||
} i386_general_register;
|
||||
|
||||
struct i386_general_regs {
|
||||
i386_general_register A, B, C, D;
|
||||
};
|
||||
|
||||
typedef struct i386_general_regs Gen_reg_t;
|
||||
|
||||
struct i386_special_regs {
|
||||
i386_general_register SP, BP, SI, DI, IP;
|
||||
u32 FLAGS;
|
||||
};
|
||||
|
||||
/*
|
||||
* Segment registers here represent the 16 bit quantities
|
||||
* CS, DS, ES, SS.
|
||||
*/
|
||||
|
||||
#undef CS
|
||||
#undef DS
|
||||
#undef SS
|
||||
#undef ES
|
||||
#undef FS
|
||||
#undef GS
|
||||
|
||||
struct i386_segment_regs {
|
||||
u16 CS, DS, SS, ES, FS, GS;
|
||||
};
|
||||
|
||||
/* 8 bit registers */
|
||||
#define R_AH gen.A.I8_reg.h_reg
|
||||
#define R_AL gen.A.I8_reg.l_reg
|
||||
#define R_BH gen.B.I8_reg.h_reg
|
||||
#define R_BL gen.B.I8_reg.l_reg
|
||||
#define R_CH gen.C.I8_reg.h_reg
|
||||
#define R_CL gen.C.I8_reg.l_reg
|
||||
#define R_DH gen.D.I8_reg.h_reg
|
||||
#define R_DL gen.D.I8_reg.l_reg
|
||||
|
||||
/* 16 bit registers */
|
||||
#define R_AX gen.A.I16_reg.x_reg
|
||||
#define R_BX gen.B.I16_reg.x_reg
|
||||
#define R_CX gen.C.I16_reg.x_reg
|
||||
#define R_DX gen.D.I16_reg.x_reg
|
||||
|
||||
/* 32 bit extended registers */
|
||||
#define R_EAX gen.A.I32_reg.e_reg
|
||||
#define R_EBX gen.B.I32_reg.e_reg
|
||||
#define R_ECX gen.C.I32_reg.e_reg
|
||||
#define R_EDX gen.D.I32_reg.e_reg
|
||||
|
||||
/* special registers */
|
||||
#define R_SP spc.SP.I16_reg.x_reg
|
||||
#define R_BP spc.BP.I16_reg.x_reg
|
||||
#define R_SI spc.SI.I16_reg.x_reg
|
||||
#define R_DI spc.DI.I16_reg.x_reg
|
||||
#define R_IP spc.IP.I16_reg.x_reg
|
||||
#define R_FLG spc.FLAGS
|
||||
|
||||
/* special registers */
|
||||
#define R_SP spc.SP.I16_reg.x_reg
|
||||
#define R_BP spc.BP.I16_reg.x_reg
|
||||
#define R_SI spc.SI.I16_reg.x_reg
|
||||
#define R_DI spc.DI.I16_reg.x_reg
|
||||
#define R_IP spc.IP.I16_reg.x_reg
|
||||
#define R_FLG spc.FLAGS
|
||||
|
||||
/* special registers */
|
||||
#define R_ESP spc.SP.I32_reg.e_reg
|
||||
#define R_EBP spc.BP.I32_reg.e_reg
|
||||
#define R_ESI spc.SI.I32_reg.e_reg
|
||||
#define R_EDI spc.DI.I32_reg.e_reg
|
||||
#define R_EIP spc.IP.I32_reg.e_reg
|
||||
#define R_EFLG spc.FLAGS
|
||||
|
||||
/* segment registers */
|
||||
#define R_CS seg.CS
|
||||
#define R_DS seg.DS
|
||||
#define R_SS seg.SS
|
||||
#define R_ES seg.ES
|
||||
#define R_FS seg.FS
|
||||
#define R_GS seg.GS
|
||||
|
||||
/* flag conditions */
|
||||
#define FB_CF 0x0001 /* CARRY flag */
|
||||
#define FB_PF 0x0004 /* PARITY flag */
|
||||
#define FB_AF 0x0010 /* AUX flag */
|
||||
#define FB_ZF 0x0040 /* ZERO flag */
|
||||
#define FB_SF 0x0080 /* SIGN flag */
|
||||
#define FB_TF 0x0100 /* TRAP flag */
|
||||
#define FB_IF 0x0200 /* INTERRUPT ENABLE flag */
|
||||
#define FB_DF 0x0400 /* DIR flag */
|
||||
#define FB_OF 0x0800 /* OVERFLOW flag */
|
||||
|
||||
/* 80286 and above always have bit#1 set */
|
||||
#define F_ALWAYS_ON (0x0002) /* flag bits always on */
|
||||
|
||||
/*
|
||||
* Define a mask for only those flag bits we will ever pass back
|
||||
* (via PUSHF)
|
||||
*/
|
||||
#define F_MSK (FB_CF|FB_PF|FB_AF|FB_ZF|FB_SF|FB_TF|FB_IF|FB_DF|FB_OF)
|
||||
|
||||
/* following bits masked in to a 16bit quantity */
|
||||
|
||||
#define F_CF 0x0001 /* CARRY flag */
|
||||
#define F_PF 0x0004 /* PARITY flag */
|
||||
#define F_AF 0x0010 /* AUX flag */
|
||||
#define F_ZF 0x0040 /* ZERO flag */
|
||||
#define F_SF 0x0080 /* SIGN flag */
|
||||
#define F_TF 0x0100 /* TRAP flag */
|
||||
#define F_IF 0x0200 /* INTERRUPT ENABLE flag */
|
||||
#define F_DF 0x0400 /* DIR flag */
|
||||
#define F_OF 0x0800 /* OVERFLOW flag */
|
||||
|
||||
#define TOGGLE_FLAG(flag) (M.x86.R_FLG ^= (flag))
|
||||
#define SET_FLAG(flag) (M.x86.R_FLG |= (flag))
|
||||
#define CLEAR_FLAG(flag) (M.x86.R_FLG &= ~(flag))
|
||||
#define ACCESS_FLAG(flag) (M.x86.R_FLG & (flag))
|
||||
#define CLEARALL_FLAG(m) (M.x86.R_FLG = 0)
|
||||
|
||||
#define CONDITIONAL_SET_FLAG(COND,FLAG) \
|
||||
if (COND) SET_FLAG(FLAG); else CLEAR_FLAG(FLAG)
|
||||
|
||||
#define F_PF_CALC 0x010000 /* PARITY flag has been calced */
|
||||
#define F_ZF_CALC 0x020000 /* ZERO flag has been calced */
|
||||
#define F_SF_CALC 0x040000 /* SIGN flag has been calced */
|
||||
|
||||
#define F_ALL_CALC 0xff0000 /* All have been calced */
|
||||
|
||||
/*
|
||||
* Emulator machine state.
|
||||
* Segment usage control.
|
||||
*/
|
||||
#define SYSMODE_SEG_DS_SS 0x00000001
|
||||
#define SYSMODE_SEGOVR_CS 0x00000002
|
||||
#define SYSMODE_SEGOVR_DS 0x00000004
|
||||
#define SYSMODE_SEGOVR_ES 0x00000008
|
||||
#define SYSMODE_SEGOVR_FS 0x00000010
|
||||
#define SYSMODE_SEGOVR_GS 0x00000020
|
||||
#define SYSMODE_SEGOVR_SS 0x00000040
|
||||
#define SYSMODE_PREFIX_REPE 0x00000080
|
||||
#define SYSMODE_PREFIX_REPNE 0x00000100
|
||||
#define SYSMODE_PREFIX_DATA 0x00000200
|
||||
#define SYSMODE_PREFIX_ADDR 0x00000400
|
||||
#define SYSMODE_INTR_PENDING 0x10000000
|
||||
#define SYSMODE_EXTRN_INTR 0x20000000
|
||||
#define SYSMODE_HALTED 0x40000000
|
||||
|
||||
#define SYSMODE_SEGMASK (SYSMODE_SEG_DS_SS | \
|
||||
SYSMODE_SEGOVR_CS | \
|
||||
SYSMODE_SEGOVR_DS | \
|
||||
SYSMODE_SEGOVR_ES | \
|
||||
SYSMODE_SEGOVR_FS | \
|
||||
SYSMODE_SEGOVR_GS | \
|
||||
SYSMODE_SEGOVR_SS)
|
||||
#define SYSMODE_CLRMASK (SYSMODE_SEG_DS_SS | \
|
||||
SYSMODE_SEGOVR_CS | \
|
||||
SYSMODE_SEGOVR_DS | \
|
||||
SYSMODE_SEGOVR_ES | \
|
||||
SYSMODE_SEGOVR_FS | \
|
||||
SYSMODE_SEGOVR_GS | \
|
||||
SYSMODE_SEGOVR_SS | \
|
||||
SYSMODE_PREFIX_DATA | \
|
||||
SYSMODE_PREFIX_ADDR)
|
||||
|
||||
#define INTR_SYNCH 0x1
|
||||
#define INTR_ASYNCH 0x2
|
||||
#define INTR_HALTED 0x4
|
||||
|
||||
typedef struct {
|
||||
struct i386_general_regs gen;
|
||||
struct i386_special_regs spc;
|
||||
struct i386_segment_regs seg;
|
||||
/*
|
||||
* MODE contains information on:
|
||||
* REPE prefix 2 bits repe,repne
|
||||
* SEGMENT overrides 5 bits normal,DS,SS,CS,ES
|
||||
* Delayed flag set 3 bits (zero, signed, parity)
|
||||
* reserved 6 bits
|
||||
* interrupt # 8 bits instruction raised interrupt
|
||||
* BIOS video segregs 4 bits
|
||||
* Interrupt Pending 1 bits
|
||||
* Extern interrupt 1 bits
|
||||
* Halted 1 bits
|
||||
*/
|
||||
long mode;
|
||||
u8 intno;
|
||||
volatile int intr; /* mask of pending interrupts */
|
||||
int debug;
|
||||
#ifdef CONFIG_X86EMU_DEBUG
|
||||
int check;
|
||||
u16 saved_ip;
|
||||
u16 saved_cs;
|
||||
int enc_pos;
|
||||
int enc_str_pos;
|
||||
char decode_buf[32]; /* encoded byte stream */
|
||||
char decoded_buf[256]; /* disassembled strings */
|
||||
#endif
|
||||
} X86EMU_regs;
|
||||
|
||||
/****************************************************************************
|
||||
REMARKS:
|
||||
Structure maintaining the emulator machine state.
|
||||
|
||||
MEMBERS:
|
||||
x86 - X86 registers
|
||||
mem_base - Base real mode memory for the emulator
|
||||
mem_size - Size of the real mode memory block for the emulator
|
||||
****************************************************************************/
|
||||
#undef x86
|
||||
typedef struct {
|
||||
X86EMU_regs x86;
|
||||
u8 *mem_base;
|
||||
u32 mem_size;
|
||||
void *private;
|
||||
} X86EMU_sysEnv;
|
||||
|
||||
#pragma pack()
|
||||
|
||||
/*----------------------------- Global Variables --------------------------*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" { /* Use "C" linkage when in C++ mode */
|
||||
#endif
|
||||
|
||||
/* Global emulator machine state.
|
||||
*
|
||||
* We keep it global to avoid pointer dereferences in the code for speed.
|
||||
*/
|
||||
|
||||
extern X86EMU_sysEnv _X86EMU_env;
|
||||
#define M _X86EMU_env
|
||||
|
||||
/*-------------------------- Function Prototypes --------------------------*/
|
||||
|
||||
/* Function to log information at runtime */
|
||||
|
||||
#ifndef __KERNEL__
|
||||
void printk(const char *fmt, ...);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* End of "C" linkage for C++ */
|
||||
#endif
|
||||
#endif /* __X86EMU_REGS_H */
|
||||
@@ -0,0 +1,98 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* Realmode X86 Emulator Library
|
||||
*
|
||||
* Copyright (C) 1991-2004 SciTech Software, Inc.
|
||||
* Copyright (C) David Mosberger-Tang
|
||||
* Copyright (C) 1999 Egbert Eich
|
||||
*
|
||||
* ========================================================================
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software and
|
||||
* its documentation for any purpose is hereby granted without fee,
|
||||
* provided that the above copyright notice appear in all copies and that
|
||||
* both that copyright notice and this permission notice appear in
|
||||
* supporting documentation, and that the name of the authors not be used
|
||||
* in advertising or publicity pertaining to distribution of the software
|
||||
* without specific, written prior permission. The authors makes no
|
||||
* representations about the suitability of this software for any purpose.
|
||||
* It is provided "as is" without express or implied warranty.
|
||||
*
|
||||
* THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
||||
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
||||
* EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
||||
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
|
||||
* USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* ========================================================================
|
||||
*
|
||||
* Language: ANSI C
|
||||
* Environment: Any
|
||||
* Developer: Kendall Bennett
|
||||
*
|
||||
* Description: Header file for system specific functions. These functions
|
||||
* are always compiled and linked in the OS depedent libraries,
|
||||
* and never in a binary portable driver.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef __X86EMU_X86EMUI_H
|
||||
#define __X86EMU_X86EMUI_H
|
||||
|
||||
/* If we are compiling in C++ mode, we can compile some functions as
|
||||
* inline to increase performance (however the code size increases quite
|
||||
* dramatically in this case).
|
||||
*/
|
||||
|
||||
#if defined(__cplusplus) && !defined(_NO_INLINE)
|
||||
#define _INLINE inline
|
||||
#else
|
||||
#define _INLINE static
|
||||
#endif
|
||||
|
||||
/* Get rid of unused parameters in C++ compilation mode */
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define X86EMU_UNUSED(v)
|
||||
#else
|
||||
#define X86EMU_UNUSED(v) v
|
||||
#endif
|
||||
|
||||
#include "x86emu.h"
|
||||
#include "x86emu/regs.h"
|
||||
#include "x86emu/debug.h"
|
||||
#include "x86emu/decode.h"
|
||||
#include "x86emu/ops.h"
|
||||
#include "x86emu/prim_ops.h"
|
||||
#ifndef __KERNEL__
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
/*--------------------------- Inline Functions ----------------------------*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" { /* Use "C" linkage when in C++ mode */
|
||||
#endif
|
||||
|
||||
extern u8(X86APIP sys_rdb) (u32 addr);
|
||||
extern u16(X86APIP sys_rdw) (u32 addr);
|
||||
extern u32(X86APIP sys_rdl) (u32 addr);
|
||||
extern void (X86APIP sys_wrb) (u32 addr, u8 val);
|
||||
extern void (X86APIP sys_wrw) (u32 addr, u16 val);
|
||||
extern void (X86APIP sys_wrl) (u32 addr, u32 val);
|
||||
|
||||
extern u8(X86APIP sys_inb) (X86EMU_pioAddr addr);
|
||||
extern u16(X86APIP sys_inw) (X86EMU_pioAddr addr);
|
||||
extern u32(X86APIP sys_inl) (X86EMU_pioAddr addr);
|
||||
extern void (X86APIP sys_outb) (X86EMU_pioAddr addr, u8 val);
|
||||
extern void (X86APIP sys_outw) (X86EMU_pioAddr addr, u16 val);
|
||||
extern void (X86APIP sys_outl) (X86EMU_pioAddr addr, u32 val);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* End of "C" linkage for C++ */
|
||||
#endif
|
||||
#endif /* __X86EMU_X86EMUI_H */
|
||||
@@ -0,0 +1,461 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* Realmode X86 Emulator Library
|
||||
*
|
||||
* Copyright (C) 1991-2004 SciTech Software, Inc.
|
||||
* Copyright (C) David Mosberger-Tang
|
||||
* Copyright (C) 1999 Egbert Eich
|
||||
*
|
||||
* ========================================================================
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software and
|
||||
* its documentation for any purpose is hereby granted without fee,
|
||||
* provided that the above copyright notice appear in all copies and that
|
||||
* both that copyright notice and this permission notice appear in
|
||||
* supporting documentation, and that the name of the authors not be used
|
||||
* in advertising or publicity pertaining to distribution of the software
|
||||
* without specific, written prior permission. The authors makes no
|
||||
* representations about the suitability of this software for any purpose.
|
||||
* It is provided "as is" without express or implied warranty.
|
||||
*
|
||||
* THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
||||
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
||||
* EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
||||
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
|
||||
* USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* ========================================================================
|
||||
*
|
||||
* Language: ANSI C
|
||||
* Environment: Any
|
||||
* Developer: Kendall Bennett
|
||||
*
|
||||
* Description: This file contains the code to handle debugging of the
|
||||
* emulator.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <common.h>
|
||||
#include <linux/ctype.h>
|
||||
#include "x86emu/x86emui.h"
|
||||
|
||||
/*----------------------------- Implementation ----------------------------*/
|
||||
|
||||
#ifdef CONFIG_X86EMU_DEBUG
|
||||
|
||||
static void print_encoded_bytes(u16 s, u16 o);
|
||||
static void print_decoded_instruction(void);
|
||||
static int x86emu_parse_line(char *s, int *ps, int *n);
|
||||
|
||||
/* should look something like debug's output. */
|
||||
void X86EMU_trace_regs(void)
|
||||
{
|
||||
if (DEBUG_TRACE()) {
|
||||
x86emu_dump_regs();
|
||||
}
|
||||
if (DEBUG_DECODE() && !DEBUG_DECODE_NOPRINT()) {
|
||||
printk("%04x:%04x ", M.x86.saved_cs, M.x86.saved_ip);
|
||||
print_encoded_bytes(M.x86.saved_cs, M.x86.saved_ip);
|
||||
print_decoded_instruction();
|
||||
}
|
||||
}
|
||||
|
||||
void X86EMU_trace_xregs(void)
|
||||
{
|
||||
if (DEBUG_TRACE()) {
|
||||
x86emu_dump_xregs();
|
||||
}
|
||||
}
|
||||
|
||||
void x86emu_just_disassemble(void)
|
||||
{
|
||||
/*
|
||||
* This routine called if the flag DEBUG_DISASSEMBLE is set kind
|
||||
* of a hack!
|
||||
*/
|
||||
printk("%04x:%04x ", M.x86.saved_cs, M.x86.saved_ip);
|
||||
print_encoded_bytes(M.x86.saved_cs, M.x86.saved_ip);
|
||||
print_decoded_instruction();
|
||||
}
|
||||
|
||||
static void disassemble_forward(u16 seg, u16 off, int n)
|
||||
{
|
||||
X86EMU_sysEnv tregs;
|
||||
int i;
|
||||
u8 op1;
|
||||
/*
|
||||
* hack, hack, hack. What we do is use the exact machinery set up
|
||||
* for execution, except that now there is an additional state
|
||||
* flag associated with the "execution", and we are using a copy
|
||||
* of the register struct. All the major opcodes, once fully
|
||||
* decoded, have the following two steps: TRACE_REGS(r,m);
|
||||
* SINGLE_STEP(r,m); which disappear if DEBUG is not defined to
|
||||
* the preprocessor. The TRACE_REGS macro expands to:
|
||||
*
|
||||
* if (debug&DEBUG_DISASSEMBLE)
|
||||
* {just_disassemble(); goto EndOfInstruction;}
|
||||
* if (debug&DEBUG_TRACE) trace_regs(r,m);
|
||||
*
|
||||
* ...... and at the last line of the routine.
|
||||
*
|
||||
* EndOfInstruction: end_instr();
|
||||
*
|
||||
* Up to the point where TRACE_REG is expanded, NO modifications
|
||||
* are done to any register EXCEPT the IP register, for fetch and
|
||||
* decoding purposes.
|
||||
*
|
||||
* This was done for an entirely different reason, but makes a
|
||||
* nice way to get the system to help debug codes.
|
||||
*/
|
||||
tregs = M;
|
||||
tregs.x86.R_IP = off;
|
||||
tregs.x86.R_CS = seg;
|
||||
|
||||
/* reset the decoding buffers */
|
||||
tregs.x86.enc_str_pos = 0;
|
||||
tregs.x86.enc_pos = 0;
|
||||
|
||||
/* turn on the "disassemble only, no execute" flag */
|
||||
tregs.x86.debug |= DEBUG_DISASSEMBLE_F;
|
||||
|
||||
/* DUMP NEXT n instructions to screen in straight_line fashion */
|
||||
/*
|
||||
* This looks like the regular instruction fetch stream, except
|
||||
* that when this occurs, each fetched opcode, upon seeing the
|
||||
* DEBUG_DISASSEMBLE flag set, exits immediately after decoding
|
||||
* the instruction. XXX --- CHECK THAT MEM IS NOT AFFECTED!!!
|
||||
* Note the use of a copy of the register structure...
|
||||
*/
|
||||
for (i = 0; i < n; i++) {
|
||||
op1 = (*sys_rdb) (((u32) M.x86.R_CS << 4) + (M.x86.R_IP++));
|
||||
(x86emu_optab[op1]) (op1);
|
||||
}
|
||||
/* end major hack mode. */
|
||||
}
|
||||
|
||||
void x86emu_check_ip_access(void)
|
||||
{
|
||||
/* NULL as of now */
|
||||
}
|
||||
|
||||
void x86emu_check_sp_access(void)
|
||||
{
|
||||
}
|
||||
|
||||
void x86emu_check_mem_access(u32 dummy)
|
||||
{
|
||||
/* check bounds, etc */
|
||||
}
|
||||
|
||||
void x86emu_check_data_access(uint dummy1, uint dummy2)
|
||||
{
|
||||
/* check bounds, etc */
|
||||
}
|
||||
|
||||
void x86emu_inc_decoded_inst_len(int x)
|
||||
{
|
||||
M.x86.enc_pos += x;
|
||||
}
|
||||
|
||||
void x86emu_decode_printf(char *x)
|
||||
{
|
||||
sprintf(M.x86.decoded_buf + M.x86.enc_str_pos, "%s", x);
|
||||
M.x86.enc_str_pos += strlen(x);
|
||||
}
|
||||
|
||||
void x86emu_decode_printf2(char *x, int y)
|
||||
{
|
||||
char temp[100];
|
||||
sprintf(temp, x, y);
|
||||
sprintf(M.x86.decoded_buf + M.x86.enc_str_pos, "%s", temp);
|
||||
M.x86.enc_str_pos += strlen(temp);
|
||||
}
|
||||
|
||||
void x86emu_end_instr(void)
|
||||
{
|
||||
M.x86.enc_str_pos = 0;
|
||||
M.x86.enc_pos = 0;
|
||||
}
|
||||
|
||||
static void print_encoded_bytes(u16 s, u16 o)
|
||||
{
|
||||
int i;
|
||||
char buf1[64];
|
||||
for (i = 0; i < M.x86.enc_pos; i++) {
|
||||
sprintf(buf1 + 2 * i, "%02x", fetch_data_byte_abs(s, o + i));
|
||||
}
|
||||
printk("%-20s", buf1);
|
||||
}
|
||||
|
||||
static void print_decoded_instruction(void)
|
||||
{
|
||||
printk("%s", M.x86.decoded_buf);
|
||||
}
|
||||
|
||||
void x86emu_print_int_vect(u16 iv)
|
||||
{
|
||||
u16 seg, off;
|
||||
|
||||
if (iv > 256)
|
||||
return;
|
||||
seg = fetch_data_word_abs(0, iv * 4);
|
||||
off = fetch_data_word_abs(0, iv * 4 + 2);
|
||||
printk("%04x:%04x ", seg, off);
|
||||
}
|
||||
|
||||
void X86EMU_dump_memory(u16 seg, u16 off, u32 amt)
|
||||
{
|
||||
u32 start = off & 0xfffffff0;
|
||||
u32 end = (off + 16) & 0xfffffff0;
|
||||
u32 i;
|
||||
|
||||
while (end <= off + amt) {
|
||||
printk("%04x:%04x ", seg, start);
|
||||
for (i = start; i < off; i++)
|
||||
printk(" ");
|
||||
for (; i < end; i++)
|
||||
printk("%02x ", fetch_data_byte_abs(seg, i));
|
||||
printk("\n");
|
||||
start = end;
|
||||
end = start + 16;
|
||||
}
|
||||
}
|
||||
|
||||
void x86emu_single_step(void)
|
||||
{
|
||||
char s[1024];
|
||||
int ps[10];
|
||||
int ntok;
|
||||
int cmd;
|
||||
int done;
|
||||
int segment;
|
||||
int offset;
|
||||
static int breakpoint;
|
||||
static int noDecode = 1;
|
||||
|
||||
if (DEBUG_BREAK()) {
|
||||
if (M.x86.saved_ip != breakpoint) {
|
||||
return;
|
||||
} else {
|
||||
M.x86.debug &= ~DEBUG_DECODE_NOPRINT_F;
|
||||
M.x86.debug |= DEBUG_TRACE_F;
|
||||
M.x86.debug &= ~DEBUG_BREAK_F;
|
||||
print_decoded_instruction();
|
||||
X86EMU_trace_regs();
|
||||
}
|
||||
}
|
||||
done = 0;
|
||||
offset = M.x86.saved_ip;
|
||||
while (!done) {
|
||||
printk("-");
|
||||
ps[1] = 0; /* Avoid dodgy compiler warnings */
|
||||
ps[2] = 0;
|
||||
cmd = x86emu_parse_line(s, ps, &ntok);
|
||||
switch (cmd) {
|
||||
case 'u':
|
||||
disassemble_forward(M.x86.saved_cs, (u16) offset, 10);
|
||||
break;
|
||||
case 'd':
|
||||
if (ntok == 2) {
|
||||
segment = M.x86.saved_cs;
|
||||
offset = ps[1];
|
||||
X86EMU_dump_memory(segment, (u16) offset, 16);
|
||||
offset += 16;
|
||||
} else if (ntok == 3) {
|
||||
segment = ps[1];
|
||||
offset = ps[2];
|
||||
X86EMU_dump_memory(segment, (u16) offset, 16);
|
||||
offset += 16;
|
||||
} else {
|
||||
segment = M.x86.saved_cs;
|
||||
X86EMU_dump_memory(segment, (u16) offset, 16);
|
||||
offset += 16;
|
||||
}
|
||||
break;
|
||||
case 'c':
|
||||
M.x86.debug ^= DEBUG_TRACECALL_F;
|
||||
break;
|
||||
case 's':
|
||||
M.x86.debug ^=
|
||||
DEBUG_SVC_F | DEBUG_SYS_F | DEBUG_SYSINT_F;
|
||||
break;
|
||||
case 'r':
|
||||
X86EMU_trace_regs();
|
||||
break;
|
||||
case 'x':
|
||||
X86EMU_trace_xregs();
|
||||
break;
|
||||
case 'g':
|
||||
if (ntok == 2) {
|
||||
breakpoint = ps[1];
|
||||
if (noDecode) {
|
||||
M.x86.debug |= DEBUG_DECODE_NOPRINT_F;
|
||||
} else {
|
||||
M.x86.debug &= ~DEBUG_DECODE_NOPRINT_F;
|
||||
}
|
||||
M.x86.debug &= ~DEBUG_TRACE_F;
|
||||
M.x86.debug |= DEBUG_BREAK_F;
|
||||
done = 1;
|
||||
}
|
||||
break;
|
||||
case 'q':
|
||||
M.x86.debug |= DEBUG_EXIT;
|
||||
return;
|
||||
case 'P':
|
||||
noDecode = (noDecode) ? 0 : 1;
|
||||
printk("Toggled decoding to %s\n",
|
||||
(noDecode) ? "false" : "true");
|
||||
break;
|
||||
case 't':
|
||||
case 0:
|
||||
done = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int X86EMU_trace_on(void)
|
||||
{
|
||||
return M.x86.debug |= DEBUG_STEP_F | DEBUG_DECODE_F | DEBUG_TRACE_F;
|
||||
}
|
||||
|
||||
int X86EMU_trace_off(void)
|
||||
{
|
||||
return M.x86.debug &= ~(DEBUG_STEP_F | DEBUG_DECODE_F | DEBUG_TRACE_F);
|
||||
}
|
||||
|
||||
static int x86emu_parse_line(char *s, int *ps, int *n)
|
||||
{
|
||||
int cmd;
|
||||
|
||||
*n = 0;
|
||||
while (isblank(*s))
|
||||
s++;
|
||||
ps[*n] = *s;
|
||||
switch (*s) {
|
||||
case '\n':
|
||||
*n += 1;
|
||||
return 0;
|
||||
default:
|
||||
cmd = *s;
|
||||
*n += 1;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
while (!isblank(*s) && *s != '\n')
|
||||
s++;
|
||||
|
||||
if (*s == '\n')
|
||||
return cmd;
|
||||
|
||||
while (isblank(*s))
|
||||
s++;
|
||||
|
||||
*n += 1;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* DEBUG */
|
||||
|
||||
void x86emu_dump_regs(void)
|
||||
{
|
||||
printk("\tAX=%04x ", M.x86.R_AX);
|
||||
printk("BX=%04x ", M.x86.R_BX);
|
||||
printk("CX=%04x ", M.x86.R_CX);
|
||||
printk("DX=%04x ", M.x86.R_DX);
|
||||
printk("SP=%04x ", M.x86.R_SP);
|
||||
printk("BP=%04x ", M.x86.R_BP);
|
||||
printk("SI=%04x ", M.x86.R_SI);
|
||||
printk("DI=%04x\n", M.x86.R_DI);
|
||||
printk("\tDS=%04x ", M.x86.R_DS);
|
||||
printk("ES=%04x ", M.x86.R_ES);
|
||||
printk("SS=%04x ", M.x86.R_SS);
|
||||
printk("CS=%04x ", M.x86.R_CS);
|
||||
printk("IP=%04x ", M.x86.R_IP);
|
||||
if (ACCESS_FLAG(F_OF))
|
||||
printk("OV "); /* CHECKED... */
|
||||
else
|
||||
printk("NV ");
|
||||
if (ACCESS_FLAG(F_DF))
|
||||
printk("DN ");
|
||||
else
|
||||
printk("UP ");
|
||||
if (ACCESS_FLAG(F_IF))
|
||||
printk("EI ");
|
||||
else
|
||||
printk("DI ");
|
||||
if (ACCESS_FLAG(F_SF))
|
||||
printk("NG ");
|
||||
else
|
||||
printk("PL ");
|
||||
if (ACCESS_FLAG(F_ZF))
|
||||
printk("ZR ");
|
||||
else
|
||||
printk("NZ ");
|
||||
if (ACCESS_FLAG(F_AF))
|
||||
printk("AC ");
|
||||
else
|
||||
printk("NA ");
|
||||
if (ACCESS_FLAG(F_PF))
|
||||
printk("PE ");
|
||||
else
|
||||
printk("PO ");
|
||||
if (ACCESS_FLAG(F_CF))
|
||||
printk("CY ");
|
||||
else
|
||||
printk("NC ");
|
||||
printk("\n");
|
||||
}
|
||||
|
||||
void x86emu_dump_xregs(void)
|
||||
{
|
||||
printk("\tEAX=%08x ", M.x86.R_EAX);
|
||||
printk("EBX=%08x ", M.x86.R_EBX);
|
||||
printk("ECX=%08x ", M.x86.R_ECX);
|
||||
printk("EDX=%08x \n", M.x86.R_EDX);
|
||||
printk("\tESP=%08x ", M.x86.R_ESP);
|
||||
printk("EBP=%08x ", M.x86.R_EBP);
|
||||
printk("ESI=%08x ", M.x86.R_ESI);
|
||||
printk("EDI=%08x\n", M.x86.R_EDI);
|
||||
printk("\tDS=%04x ", M.x86.R_DS);
|
||||
printk("ES=%04x ", M.x86.R_ES);
|
||||
printk("SS=%04x ", M.x86.R_SS);
|
||||
printk("CS=%04x ", M.x86.R_CS);
|
||||
printk("EIP=%08x\n\t", M.x86.R_EIP);
|
||||
if (ACCESS_FLAG(F_OF))
|
||||
printk("OV "); /* CHECKED... */
|
||||
else
|
||||
printk("NV ");
|
||||
if (ACCESS_FLAG(F_DF))
|
||||
printk("DN ");
|
||||
else
|
||||
printk("UP ");
|
||||
if (ACCESS_FLAG(F_IF))
|
||||
printk("EI ");
|
||||
else
|
||||
printk("DI ");
|
||||
if (ACCESS_FLAG(F_SF))
|
||||
printk("NG ");
|
||||
else
|
||||
printk("PL ");
|
||||
if (ACCESS_FLAG(F_ZF))
|
||||
printk("ZR ");
|
||||
else
|
||||
printk("NZ ");
|
||||
if (ACCESS_FLAG(F_AF))
|
||||
printk("AC ");
|
||||
else
|
||||
printk("NA ");
|
||||
if (ACCESS_FLAG(F_PF))
|
||||
printk("PE ");
|
||||
else
|
||||
printk("PO ");
|
||||
if (ACCESS_FLAG(F_CF))
|
||||
printk("CY ");
|
||||
else
|
||||
printk("NC ");
|
||||
printk("\n");
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,328 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* Realmode X86 Emulator Library
|
||||
*
|
||||
* Copyright (C) 1991-2004 SciTech Software, Inc.
|
||||
* Copyright (C) David Mosberger-Tang
|
||||
* Copyright (C) 1999 Egbert Eich
|
||||
*
|
||||
* ========================================================================
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software and
|
||||
* its documentation for any purpose is hereby granted without fee,
|
||||
* provided that the above copyright notice appear in all copies and that
|
||||
* both that copyright notice and this permission notice appear in
|
||||
* supporting documentation, and that the name of the authors not be used
|
||||
* in advertising or publicity pertaining to distribution of the software
|
||||
* without specific, written prior permission. The authors makes no
|
||||
* representations about the suitability of this software for any purpose.
|
||||
* It is provided "as is" without express or implied warranty.
|
||||
*
|
||||
* THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
||||
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
||||
* EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
||||
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
|
||||
* USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* ========================================================================
|
||||
*
|
||||
* Language: ANSI C
|
||||
* Environment: Any
|
||||
* Developer: Kendall Bennett
|
||||
*
|
||||
* Description: This file includes subroutines which are related to
|
||||
* programmed I/O and memory access. Included in this module
|
||||
* are default functions that do nothing. For real uses these
|
||||
* functions will have to be overridden by the user library.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#include <common.h>
|
||||
#include "x86emu/x86emui.h"
|
||||
|
||||
/*------------------------- Global Variables ------------------------------*/
|
||||
|
||||
X86EMU_sysEnv _X86EMU_env; /* Global emulator machine state */
|
||||
X86EMU_intrFuncs _X86EMU_intrTab[256];
|
||||
|
||||
int debug_intr;
|
||||
|
||||
/*----------------------------- Implementation ----------------------------*/
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
addr - Emulator memory address to read
|
||||
|
||||
RETURNS:
|
||||
Byte value read from emulator memory.
|
||||
|
||||
REMARKS:
|
||||
Reads a byte value from the emulator memory.
|
||||
****************************************************************************/
|
||||
u8 X86API rdb(u32 addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
addr - Emulator memory address to read
|
||||
|
||||
RETURNS:
|
||||
Word value read from emulator memory.
|
||||
|
||||
REMARKS:
|
||||
Reads a word value from the emulator memory.
|
||||
****************************************************************************/
|
||||
u16 X86API rdw(u32 addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
addr - Emulator memory address to read
|
||||
|
||||
RETURNS:
|
||||
Long value read from emulator memory.
|
||||
REMARKS:
|
||||
Reads a long value from the emulator memory.
|
||||
****************************************************************************/
|
||||
u32 X86API rdl(u32 addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
addr - Emulator memory address to read
|
||||
val - Value to store
|
||||
|
||||
REMARKS:
|
||||
Writes a byte value to emulator memory.
|
||||
****************************************************************************/
|
||||
void X86API wrb(u32 addr, u8 val)
|
||||
{
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
addr - Emulator memory address to read
|
||||
val - Value to store
|
||||
|
||||
REMARKS:
|
||||
Writes a word value to emulator memory.
|
||||
****************************************************************************/
|
||||
void X86API wrw(u32 addr, u16 val)
|
||||
{
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
addr - Emulator memory address to read
|
||||
val - Value to store
|
||||
|
||||
REMARKS:
|
||||
Writes a long value to emulator memory.
|
||||
****************************************************************************/
|
||||
void X86API wrl(u32 addr, u32 val)
|
||||
{
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
addr - PIO address to read
|
||||
RETURN:
|
||||
0
|
||||
REMARKS:
|
||||
Default PIO byte read function. Doesn't perform real inb.
|
||||
****************************************************************************/
|
||||
static u8 X86API p_inb(X86EMU_pioAddr addr)
|
||||
{
|
||||
DB(if (DEBUG_IO_TRACE())
|
||||
printk("inb %#04x \n", addr);)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
addr - PIO address to read
|
||||
RETURN:
|
||||
0
|
||||
REMARKS:
|
||||
Default PIO word read function. Doesn't perform real inw.
|
||||
****************************************************************************/
|
||||
static u16 X86API p_inw(X86EMU_pioAddr addr)
|
||||
{
|
||||
DB(if (DEBUG_IO_TRACE())
|
||||
printk("inw %#04x \n", addr);)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
addr - PIO address to read
|
||||
RETURN:
|
||||
0
|
||||
REMARKS:
|
||||
Default PIO long read function. Doesn't perform real inl.
|
||||
****************************************************************************/
|
||||
static u32 X86API p_inl(X86EMU_pioAddr addr)
|
||||
{
|
||||
DB(if (DEBUG_IO_TRACE())
|
||||
printk("inl %#04x \n", addr);)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
addr - PIO address to write
|
||||
val - Value to store
|
||||
REMARKS:
|
||||
Default PIO byte write function. Doesn't perform real outb.
|
||||
****************************************************************************/
|
||||
static void X86API p_outb(X86EMU_pioAddr addr, u8 val)
|
||||
{
|
||||
DB(if (DEBUG_IO_TRACE())
|
||||
printk("outb %#02x -> %#04x \n", val, addr);)
|
||||
return;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
addr - PIO address to write
|
||||
val - Value to store
|
||||
REMARKS:
|
||||
Default PIO word write function. Doesn't perform real outw.
|
||||
****************************************************************************/
|
||||
static void X86API p_outw(X86EMU_pioAddr addr, u16 val)
|
||||
{
|
||||
DB(if (DEBUG_IO_TRACE())
|
||||
printk("outw %#04x -> %#04x \n", val, addr);)
|
||||
return;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
addr - PIO address to write
|
||||
val - Value to store
|
||||
REMARKS:
|
||||
Default PIO ;ong write function. Doesn't perform real outl.
|
||||
****************************************************************************/
|
||||
static void X86API p_outl(X86EMU_pioAddr addr, u32 val)
|
||||
{
|
||||
DB(if (DEBUG_IO_TRACE())
|
||||
printk("outl %#08x -> %#04x \n", val, addr);)
|
||||
return;
|
||||
}
|
||||
|
||||
/*------------------------- Global Variables ------------------------------*/
|
||||
|
||||
u8(X86APIP sys_rdb) (u32 addr) = rdb;
|
||||
u16(X86APIP sys_rdw) (u32 addr) = rdw;
|
||||
u32(X86APIP sys_rdl) (u32 addr) = rdl;
|
||||
void (X86APIP sys_wrb) (u32 addr, u8 val) = wrb;
|
||||
void (X86APIP sys_wrw) (u32 addr, u16 val) = wrw;
|
||||
void (X86APIP sys_wrl) (u32 addr, u32 val) = wrl;
|
||||
u8(X86APIP sys_inb) (X86EMU_pioAddr addr) = p_inb;
|
||||
u16(X86APIP sys_inw) (X86EMU_pioAddr addr) = p_inw;
|
||||
u32(X86APIP sys_inl) (X86EMU_pioAddr addr) = p_inl;
|
||||
void (X86APIP sys_outb) (X86EMU_pioAddr addr, u8 val) = p_outb;
|
||||
void (X86APIP sys_outw) (X86EMU_pioAddr addr, u16 val) = p_outw;
|
||||
void (X86APIP sys_outl) (X86EMU_pioAddr addr, u32 val) = p_outl;
|
||||
|
||||
/*----------------------------- Setup -------------------------------------*/
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
funcs - New memory function pointers to make active
|
||||
|
||||
REMARKS:
|
||||
This function is used to set the pointers to functions which access
|
||||
memory space, allowing the user application to override these functions
|
||||
and hook them out as necessary for their application.
|
||||
****************************************************************************/
|
||||
void X86EMU_setupMemFuncs(X86EMU_memFuncs * funcs)
|
||||
{
|
||||
sys_rdb = funcs->rdb;
|
||||
sys_rdw = funcs->rdw;
|
||||
sys_rdl = funcs->rdl;
|
||||
sys_wrb = funcs->wrb;
|
||||
sys_wrw = funcs->wrw;
|
||||
sys_wrl = funcs->wrl;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
funcs - New programmed I/O function pointers to make active
|
||||
|
||||
REMARKS:
|
||||
This function is used to set the pointers to functions which access
|
||||
I/O space, allowing the user application to override these functions
|
||||
and hook them out as necessary for their application.
|
||||
****************************************************************************/
|
||||
void X86EMU_setupPioFuncs(X86EMU_pioFuncs * funcs)
|
||||
{
|
||||
sys_inb = funcs->inb;
|
||||
sys_inw = funcs->inw;
|
||||
sys_inl = funcs->inl;
|
||||
sys_outb = funcs->outb;
|
||||
sys_outw = funcs->outw;
|
||||
sys_outl = funcs->outl;
|
||||
}
|
||||
|
||||
void X86EMU_setupIntrFunc(int intnum, X86EMU_intrFuncs func)
|
||||
{
|
||||
_X86EMU_intrTab[intnum] = func;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
funcs - New interrupt vector table to make active
|
||||
|
||||
REMARKS:
|
||||
This function is used to set the pointers to functions which handle
|
||||
interrupt processing in the emulator, allowing the user application to
|
||||
hook interrupts as necessary for their application. Any interrupts that
|
||||
are not hooked by the user application, and reflected and handled internally
|
||||
in the emulator via the interrupt vector table. This allows the application
|
||||
to get control when the code being emulated executes specific software
|
||||
interrupts.
|
||||
****************************************************************************/
|
||||
void X86EMU_setupIntrFuncs(X86EMU_intrFuncs funcs[])
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 256; i++)
|
||||
_X86EMU_intrTab[i] = NULL;
|
||||
if (funcs) {
|
||||
for (i = 0; i < 256; i++)
|
||||
_X86EMU_intrTab[i] = funcs[i];
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
PARAMETERS:
|
||||
int - New software interrupt to prepare for
|
||||
|
||||
REMARKS:
|
||||
This function is used to set up the emulator state to exceute a software
|
||||
interrupt. This can be used by the user application code to allow an
|
||||
interrupt to be hooked, examined and then reflected back to the emulator
|
||||
so that the code in the emulator will continue processing the software
|
||||
interrupt as per normal. This essentially allows system code to actively
|
||||
hook and handle certain software interrupts as necessary.
|
||||
****************************************************************************/
|
||||
void X86EMU_prepareForInt(int num)
|
||||
{
|
||||
push_word((u16) M.x86.R_FLG);
|
||||
CLEAR_FLAG(F_IF);
|
||||
CLEAR_FLAG(F_TF);
|
||||
push_word(M.x86.R_CS);
|
||||
M.x86.R_CS = mem_access_word(num * 4 + 2);
|
||||
push_word(M.x86.R_IP);
|
||||
M.x86.R_IP = mem_access_word(num * 4);
|
||||
M.x86.intr = 0;
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
config BLK
|
||||
bool "Support block devices"
|
||||
depends on DM
|
||||
default y if DM_MMC
|
||||
help
|
||||
Enable support for block devices, such as SCSI, MMC and USB
|
||||
flash sticks. These provide a block-level interface which permits
|
||||
reading, writing and (in some cases) erasing blocks. Block
|
||||
devices often have a partition table which allows the device to
|
||||
be partitioned into several areas, called 'partitions' in U-Boot.
|
||||
A filesystem can be placed in each partition.
|
||||
|
||||
config HAVE_BLOCK_DEVICE
|
||||
bool "Enable Legacy Block Device"
|
||||
help
|
||||
Some devices require block support whether or not DM is enabled
|
||||
|
||||
config SPL_BLK
|
||||
bool "Support block devices in SPL"
|
||||
depends on SPL_DM && BLK
|
||||
default y
|
||||
help
|
||||
Enable support for block devices, such as SCSI, MMC and USB
|
||||
flash sticks. These provide a block-level interface which permits
|
||||
reading, writing and (in some cases) erasing blocks. Block
|
||||
devices often have a partition table which allows the device to
|
||||
be partitioned into several areas, called 'partitions' in U-Boot.
|
||||
A filesystem can be placed in each partition.
|
||||
|
||||
config TPL_BLK
|
||||
bool "Support block devices in TPL"
|
||||
depends on TPL_DM && BLK
|
||||
default y
|
||||
help
|
||||
Enable support for block devices, such as SCSI, MMC and USB
|
||||
flash sticks. These provide a block-level interface which permits
|
||||
reading, writing and (in some cases) erasing blocks. Block
|
||||
devices often have a partition table which allows the device to
|
||||
be partitioned into several areas, called 'partitions' in U-Boot.
|
||||
A filesystem can be placed in each partition.
|
||||
|
||||
config BLOCK_CACHE
|
||||
bool "Use block device cache"
|
||||
depends on BLK
|
||||
default y
|
||||
help
|
||||
This option enables a disk-block cache for all block devices.
|
||||
This is most useful when accessing filesystems under U-Boot since
|
||||
it will prevent repeated reads from directory structures and other
|
||||
filesystem data structures.
|
||||
|
||||
config SPL_BLOCK_CACHE
|
||||
bool "Use block device cache in SPL"
|
||||
depends on SPL_BLK
|
||||
default n
|
||||
help
|
||||
This option enables the disk-block cache in SPL
|
||||
|
||||
config TPL_BLOCK_CACHE
|
||||
bool "Use block device cache in TPL"
|
||||
depends on TPL_BLK
|
||||
default n
|
||||
help
|
||||
This option enables the disk-block cache in TPL
|
||||
|
||||
config IDE
|
||||
bool "Support IDE controllers"
|
||||
select HAVE_BLOCK_DEVICE
|
||||
help
|
||||
Enables support for IDE (Integrated Drive Electronics) hard drives.
|
||||
This allows access to raw blocks and filesystems on an IDE drive
|
||||
from U-Boot. See also CMD_IDE which provides an 'ide' command for
|
||||
performing various IDE operations.
|
||||
@@ -0,0 +1,16 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
# (C) Copyright 2000-2007
|
||||
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
|
||||
|
||||
obj-$(CONFIG_$(SPL_)BLK) += blk-uclass.o
|
||||
|
||||
ifndef CONFIG_$(SPL_)BLK
|
||||
obj-$(CONFIG_HAVE_BLOCK_DEVICE) += blk_legacy.o
|
||||
endif
|
||||
|
||||
ifndef CONFIG_SPL_BUILD
|
||||
obj-$(CONFIG_IDE) += ide.o
|
||||
endif
|
||||
obj-$(CONFIG_SANDBOX) += sandbox.o
|
||||
obj-$(CONFIG_$(SPL_TPL_)BLOCK_CACHE) += blkcache.o
|
||||
@@ -0,0 +1,657 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2016 Google, Inc
|
||||
* Written by Simon Glass <sjg@chromium.org>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <blk.h>
|
||||
#include <dm.h>
|
||||
#include <dm/device-internal.h>
|
||||
#include <dm/lists.h>
|
||||
#include <dm/uclass-internal.h>
|
||||
|
||||
static const char *if_typename_str[IF_TYPE_COUNT] = {
|
||||
[IF_TYPE_IDE] = "ide",
|
||||
[IF_TYPE_SCSI] = "scsi",
|
||||
[IF_TYPE_ATAPI] = "atapi",
|
||||
[IF_TYPE_USB] = "usb",
|
||||
[IF_TYPE_DOC] = "doc",
|
||||
[IF_TYPE_MMC] = "mmc",
|
||||
[IF_TYPE_SD] = "sd",
|
||||
[IF_TYPE_SATA] = "sata",
|
||||
[IF_TYPE_HOST] = "host",
|
||||
[IF_TYPE_NVME] = "nvme",
|
||||
[IF_TYPE_EFI] = "efi",
|
||||
[IF_TYPE_VIRTIO] = "virtio",
|
||||
};
|
||||
|
||||
static enum uclass_id if_type_uclass_id[IF_TYPE_COUNT] = {
|
||||
[IF_TYPE_IDE] = UCLASS_IDE,
|
||||
[IF_TYPE_SCSI] = UCLASS_SCSI,
|
||||
[IF_TYPE_ATAPI] = UCLASS_INVALID,
|
||||
[IF_TYPE_USB] = UCLASS_MASS_STORAGE,
|
||||
[IF_TYPE_DOC] = UCLASS_INVALID,
|
||||
[IF_TYPE_MMC] = UCLASS_MMC,
|
||||
[IF_TYPE_SD] = UCLASS_INVALID,
|
||||
[IF_TYPE_SATA] = UCLASS_AHCI,
|
||||
[IF_TYPE_HOST] = UCLASS_ROOT,
|
||||
[IF_TYPE_NVME] = UCLASS_NVME,
|
||||
[IF_TYPE_EFI] = UCLASS_EFI,
|
||||
[IF_TYPE_VIRTIO] = UCLASS_VIRTIO,
|
||||
};
|
||||
|
||||
static enum if_type if_typename_to_iftype(const char *if_typename)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < IF_TYPE_COUNT; i++) {
|
||||
if (if_typename_str[i] &&
|
||||
!strcmp(if_typename, if_typename_str[i]))
|
||||
return i;
|
||||
}
|
||||
|
||||
return IF_TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
static enum uclass_id if_type_to_uclass_id(enum if_type if_type)
|
||||
{
|
||||
return if_type_uclass_id[if_type];
|
||||
}
|
||||
|
||||
const char *blk_get_if_type_name(enum if_type if_type)
|
||||
{
|
||||
return if_typename_str[if_type];
|
||||
}
|
||||
|
||||
struct blk_desc *blk_get_devnum_by_type(enum if_type if_type, int devnum)
|
||||
{
|
||||
struct blk_desc *desc;
|
||||
struct udevice *dev;
|
||||
int ret;
|
||||
|
||||
ret = blk_get_device(if_type, devnum, &dev);
|
||||
if (ret)
|
||||
return NULL;
|
||||
desc = dev_get_uclass_platdata(dev);
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is complicated with driver model. We look up the interface
|
||||
* name in a local table. This gives us an interface type which we can match
|
||||
* against the uclass of the block device's parent.
|
||||
*/
|
||||
struct blk_desc *blk_get_devnum_by_typename(const char *if_typename, int devnum)
|
||||
{
|
||||
enum uclass_id uclass_id;
|
||||
enum if_type if_type;
|
||||
struct udevice *dev;
|
||||
struct uclass *uc;
|
||||
int ret;
|
||||
|
||||
if_type = if_typename_to_iftype(if_typename);
|
||||
if (if_type == IF_TYPE_UNKNOWN) {
|
||||
debug("%s: Unknown interface type '%s'\n", __func__,
|
||||
if_typename);
|
||||
return NULL;
|
||||
}
|
||||
uclass_id = if_type_to_uclass_id(if_type);
|
||||
if (uclass_id == UCLASS_INVALID) {
|
||||
debug("%s: Unknown uclass for interface type'\n",
|
||||
if_typename_str[if_type]);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = uclass_get(UCLASS_BLK, &uc);
|
||||
if (ret)
|
||||
return NULL;
|
||||
uclass_foreach_dev(dev, uc) {
|
||||
struct blk_desc *desc = dev_get_uclass_platdata(dev);
|
||||
|
||||
debug("%s: if_type=%d, devnum=%d: %s, %d, %d\n", __func__,
|
||||
if_type, devnum, dev->name, desc->if_type, desc->devnum);
|
||||
if (desc->devnum != devnum)
|
||||
continue;
|
||||
|
||||
/* Find out the parent device uclass */
|
||||
if (device_get_uclass_id(dev->parent) != uclass_id) {
|
||||
debug("%s: parent uclass %d, this dev %d\n", __func__,
|
||||
device_get_uclass_id(dev->parent), uclass_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (device_probe(dev))
|
||||
return NULL;
|
||||
|
||||
debug("%s: Device desc %p\n", __func__, desc);
|
||||
return desc;
|
||||
}
|
||||
debug("%s: No device found\n", __func__);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* blk_get_by_device() - Get the block device descriptor for the given device
|
||||
* @dev: Instance of a storage device
|
||||
*
|
||||
* Return: With block device descriptor on success , NULL if there is no such
|
||||
* block device.
|
||||
*/
|
||||
struct blk_desc *blk_get_by_device(struct udevice *dev)
|
||||
{
|
||||
struct udevice *child_dev;
|
||||
|
||||
device_foreach_child(child_dev, dev) {
|
||||
if (device_get_uclass_id(child_dev) != UCLASS_BLK)
|
||||
continue;
|
||||
|
||||
return dev_get_uclass_platdata(child_dev);
|
||||
}
|
||||
|
||||
debug("%s: No block device found\n", __func__);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* get_desc() - Get the block device descriptor for the given device number
|
||||
*
|
||||
* @if_type: Interface type
|
||||
* @devnum: Device number (0 = first)
|
||||
* @descp: Returns block device descriptor on success
|
||||
* @return 0 on success, -ENODEV if there is no such device and no device
|
||||
* with a higher device number, -ENOENT if there is no such device but there
|
||||
* is one with a higher number, or other -ve on other error.
|
||||
*/
|
||||
static int get_desc(enum if_type if_type, int devnum, struct blk_desc **descp)
|
||||
{
|
||||
bool found_more = false;
|
||||
struct udevice *dev;
|
||||
struct uclass *uc;
|
||||
int ret;
|
||||
|
||||
*descp = NULL;
|
||||
ret = uclass_get(UCLASS_BLK, &uc);
|
||||
if (ret)
|
||||
return ret;
|
||||
uclass_foreach_dev(dev, uc) {
|
||||
struct blk_desc *desc = dev_get_uclass_platdata(dev);
|
||||
|
||||
debug("%s: if_type=%d, devnum=%d: %s, %d, %d\n", __func__,
|
||||
if_type, devnum, dev->name, desc->if_type, desc->devnum);
|
||||
if (desc->if_type == if_type) {
|
||||
if (desc->devnum == devnum) {
|
||||
ret = device_probe(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*descp = desc;
|
||||
return 0;
|
||||
} else if (desc->devnum > devnum) {
|
||||
found_more = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return found_more ? -ENOENT : -ENODEV;
|
||||
}
|
||||
|
||||
int blk_select_hwpart_devnum(enum if_type if_type, int devnum, int hwpart)
|
||||
{
|
||||
struct udevice *dev;
|
||||
int ret;
|
||||
|
||||
ret = blk_get_device(if_type, devnum, &dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return blk_select_hwpart(dev, hwpart);
|
||||
}
|
||||
|
||||
int blk_list_part(enum if_type if_type)
|
||||
{
|
||||
struct blk_desc *desc;
|
||||
int devnum, ok;
|
||||
int ret;
|
||||
|
||||
for (ok = 0, devnum = 0;; ++devnum) {
|
||||
ret = get_desc(if_type, devnum, &desc);
|
||||
if (ret == -ENODEV)
|
||||
break;
|
||||
else if (ret)
|
||||
continue;
|
||||
if (desc->part_type != PART_TYPE_UNKNOWN) {
|
||||
++ok;
|
||||
if (devnum)
|
||||
putc('\n');
|
||||
part_print(desc);
|
||||
}
|
||||
}
|
||||
if (!ok)
|
||||
return -ENODEV;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int blk_print_part_devnum(enum if_type if_type, int devnum)
|
||||
{
|
||||
struct blk_desc *desc;
|
||||
int ret;
|
||||
|
||||
ret = get_desc(if_type, devnum, &desc);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (desc->type == DEV_TYPE_UNKNOWN)
|
||||
return -ENOENT;
|
||||
part_print(desc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void blk_list_devices(enum if_type if_type)
|
||||
{
|
||||
struct blk_desc *desc;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
for (i = 0;; ++i) {
|
||||
ret = get_desc(if_type, i, &desc);
|
||||
if (ret == -ENODEV)
|
||||
break;
|
||||
else if (ret)
|
||||
continue;
|
||||
if (desc->type == DEV_TYPE_UNKNOWN)
|
||||
continue; /* list only known devices */
|
||||
printf("Device %d: ", i);
|
||||
dev_print(desc);
|
||||
}
|
||||
}
|
||||
|
||||
int blk_print_device_num(enum if_type if_type, int devnum)
|
||||
{
|
||||
struct blk_desc *desc;
|
||||
int ret;
|
||||
|
||||
ret = get_desc(if_type, devnum, &desc);
|
||||
if (ret)
|
||||
return ret;
|
||||
printf("\nIDE device %d: ", devnum);
|
||||
dev_print(desc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int blk_show_device(enum if_type if_type, int devnum)
|
||||
{
|
||||
struct blk_desc *desc;
|
||||
int ret;
|
||||
|
||||
printf("\nDevice %d: ", devnum);
|
||||
ret = get_desc(if_type, devnum, &desc);
|
||||
if (ret == -ENODEV || ret == -ENOENT) {
|
||||
printf("unknown device\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
if (ret)
|
||||
return ret;
|
||||
dev_print(desc);
|
||||
|
||||
if (desc->type == DEV_TYPE_UNKNOWN)
|
||||
return -ENOENT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ulong blk_read_devnum(enum if_type if_type, int devnum, lbaint_t start,
|
||||
lbaint_t blkcnt, void *buffer)
|
||||
{
|
||||
struct blk_desc *desc;
|
||||
ulong n;
|
||||
int ret;
|
||||
|
||||
ret = get_desc(if_type, devnum, &desc);
|
||||
if (ret)
|
||||
return ret;
|
||||
n = blk_dread(desc, start, blkcnt, buffer);
|
||||
if (IS_ERR_VALUE(n))
|
||||
return n;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
ulong blk_write_devnum(enum if_type if_type, int devnum, lbaint_t start,
|
||||
lbaint_t blkcnt, const void *buffer)
|
||||
{
|
||||
struct blk_desc *desc;
|
||||
int ret;
|
||||
|
||||
ret = get_desc(if_type, devnum, &desc);
|
||||
if (ret)
|
||||
return ret;
|
||||
return blk_dwrite(desc, start, blkcnt, buffer);
|
||||
}
|
||||
|
||||
int blk_select_hwpart(struct udevice *dev, int hwpart)
|
||||
{
|
||||
const struct blk_ops *ops = blk_get_ops(dev);
|
||||
|
||||
if (!ops)
|
||||
return -ENOSYS;
|
||||
if (!ops->select_hwpart)
|
||||
return 0;
|
||||
|
||||
return ops->select_hwpart(dev, hwpart);
|
||||
}
|
||||
|
||||
int blk_dselect_hwpart(struct blk_desc *desc, int hwpart)
|
||||
{
|
||||
return blk_select_hwpart(desc->bdev, hwpart);
|
||||
}
|
||||
|
||||
int blk_first_device(int if_type, struct udevice **devp)
|
||||
{
|
||||
struct blk_desc *desc;
|
||||
int ret;
|
||||
|
||||
ret = uclass_find_first_device(UCLASS_BLK, devp);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (!*devp)
|
||||
return -ENODEV;
|
||||
do {
|
||||
desc = dev_get_uclass_platdata(*devp);
|
||||
if (desc->if_type == if_type)
|
||||
return 0;
|
||||
ret = uclass_find_next_device(devp);
|
||||
if (ret)
|
||||
return ret;
|
||||
} while (*devp);
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
int blk_next_device(struct udevice **devp)
|
||||
{
|
||||
struct blk_desc *desc;
|
||||
int ret, if_type;
|
||||
|
||||
desc = dev_get_uclass_platdata(*devp);
|
||||
if_type = desc->if_type;
|
||||
do {
|
||||
ret = uclass_find_next_device(devp);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (!*devp)
|
||||
return -ENODEV;
|
||||
desc = dev_get_uclass_platdata(*devp);
|
||||
if (desc->if_type == if_type)
|
||||
return 0;
|
||||
} while (1);
|
||||
}
|
||||
|
||||
int blk_find_device(int if_type, int devnum, struct udevice **devp)
|
||||
{
|
||||
struct uclass *uc;
|
||||
struct udevice *dev;
|
||||
int ret;
|
||||
|
||||
ret = uclass_get(UCLASS_BLK, &uc);
|
||||
if (ret)
|
||||
return ret;
|
||||
uclass_foreach_dev(dev, uc) {
|
||||
struct blk_desc *desc = dev_get_uclass_platdata(dev);
|
||||
|
||||
debug("%s: if_type=%d, devnum=%d: %s, %d, %d\n", __func__,
|
||||
if_type, devnum, dev->name, desc->if_type, desc->devnum);
|
||||
if (desc->if_type == if_type && desc->devnum == devnum) {
|
||||
*devp = dev;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
int blk_get_device(int if_type, int devnum, struct udevice **devp)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = blk_find_device(if_type, devnum, devp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return device_probe(*devp);
|
||||
}
|
||||
|
||||
unsigned long blk_dread(struct blk_desc *block_dev, lbaint_t start,
|
||||
lbaint_t blkcnt, void *buffer)
|
||||
{
|
||||
struct udevice *dev = block_dev->bdev;
|
||||
const struct blk_ops *ops = blk_get_ops(dev);
|
||||
ulong blks_read;
|
||||
|
||||
if (!ops->read)
|
||||
return -ENOSYS;
|
||||
|
||||
if (blkcache_read(block_dev->if_type, block_dev->devnum,
|
||||
start, blkcnt, block_dev->blksz, buffer))
|
||||
return blkcnt;
|
||||
blks_read = ops->read(dev, start, blkcnt, buffer);
|
||||
if (blks_read == blkcnt)
|
||||
blkcache_fill(block_dev->if_type, block_dev->devnum,
|
||||
start, blkcnt, block_dev->blksz, buffer);
|
||||
|
||||
return blks_read;
|
||||
}
|
||||
|
||||
unsigned long blk_dwrite(struct blk_desc *block_dev, lbaint_t start,
|
||||
lbaint_t blkcnt, const void *buffer)
|
||||
{
|
||||
struct udevice *dev = block_dev->bdev;
|
||||
const struct blk_ops *ops = blk_get_ops(dev);
|
||||
|
||||
if (!ops->write)
|
||||
return -ENOSYS;
|
||||
|
||||
blkcache_invalidate(block_dev->if_type, block_dev->devnum);
|
||||
return ops->write(dev, start, blkcnt, buffer);
|
||||
}
|
||||
|
||||
unsigned long blk_derase(struct blk_desc *block_dev, lbaint_t start,
|
||||
lbaint_t blkcnt)
|
||||
{
|
||||
struct udevice *dev = block_dev->bdev;
|
||||
const struct blk_ops *ops = blk_get_ops(dev);
|
||||
|
||||
if (!ops->erase)
|
||||
return -ENOSYS;
|
||||
|
||||
blkcache_invalidate(block_dev->if_type, block_dev->devnum);
|
||||
return ops->erase(dev, start, blkcnt);
|
||||
}
|
||||
|
||||
int blk_get_from_parent(struct udevice *parent, struct udevice **devp)
|
||||
{
|
||||
struct udevice *dev;
|
||||
enum uclass_id id;
|
||||
int ret;
|
||||
|
||||
device_find_first_child(parent, &dev);
|
||||
if (!dev) {
|
||||
debug("%s: No block device found for parent '%s'\n", __func__,
|
||||
parent->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
id = device_get_uclass_id(dev);
|
||||
if (id != UCLASS_BLK) {
|
||||
debug("%s: Incorrect uclass %s for block device '%s'\n",
|
||||
__func__, uclass_get_name(id), dev->name);
|
||||
return -ENOTBLK;
|
||||
}
|
||||
ret = device_probe(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
*devp = dev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int blk_find_max_devnum(enum if_type if_type)
|
||||
{
|
||||
struct udevice *dev;
|
||||
int max_devnum = -ENODEV;
|
||||
struct uclass *uc;
|
||||
int ret;
|
||||
|
||||
ret = uclass_get(UCLASS_BLK, &uc);
|
||||
if (ret)
|
||||
return ret;
|
||||
uclass_foreach_dev(dev, uc) {
|
||||
struct blk_desc *desc = dev_get_uclass_platdata(dev);
|
||||
|
||||
if (desc->if_type == if_type && desc->devnum > max_devnum)
|
||||
max_devnum = desc->devnum;
|
||||
}
|
||||
|
||||
return max_devnum;
|
||||
}
|
||||
|
||||
int blk_next_free_devnum(enum if_type if_type)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = blk_find_max_devnum(if_type);
|
||||
if (ret == -ENODEV)
|
||||
return 0;
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return ret + 1;
|
||||
}
|
||||
|
||||
static int blk_claim_devnum(enum if_type if_type, int devnum)
|
||||
{
|
||||
struct udevice *dev;
|
||||
struct uclass *uc;
|
||||
int ret;
|
||||
|
||||
ret = uclass_get(UCLASS_BLK, &uc);
|
||||
if (ret)
|
||||
return ret;
|
||||
uclass_foreach_dev(dev, uc) {
|
||||
struct blk_desc *desc = dev_get_uclass_platdata(dev);
|
||||
|
||||
if (desc->if_type == if_type && desc->devnum == devnum) {
|
||||
int next = blk_next_free_devnum(if_type);
|
||||
|
||||
if (next < 0)
|
||||
return next;
|
||||
desc->devnum = next;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
int blk_create_device(struct udevice *parent, const char *drv_name,
|
||||
const char *name, int if_type, int devnum, int blksz,
|
||||
lbaint_t lba, struct udevice **devp)
|
||||
{
|
||||
struct blk_desc *desc;
|
||||
struct udevice *dev;
|
||||
int ret;
|
||||
|
||||
if (devnum == -1) {
|
||||
devnum = blk_next_free_devnum(if_type);
|
||||
} else {
|
||||
ret = blk_claim_devnum(if_type, devnum);
|
||||
if (ret < 0 && ret != -ENOENT)
|
||||
return ret;
|
||||
}
|
||||
if (devnum < 0)
|
||||
return devnum;
|
||||
ret = device_bind_driver(parent, drv_name, name, &dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
desc = dev_get_uclass_platdata(dev);
|
||||
desc->if_type = if_type;
|
||||
desc->blksz = blksz;
|
||||
desc->log2blksz = LOG2(desc->blksz);
|
||||
desc->lba = lba;
|
||||
desc->part_type = PART_TYPE_UNKNOWN;
|
||||
desc->bdev = dev;
|
||||
desc->devnum = devnum;
|
||||
*devp = dev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int blk_create_devicef(struct udevice *parent, const char *drv_name,
|
||||
const char *name, int if_type, int devnum, int blksz,
|
||||
lbaint_t lba, struct udevice **devp)
|
||||
{
|
||||
char dev_name[30], *str;
|
||||
int ret;
|
||||
|
||||
snprintf(dev_name, sizeof(dev_name), "%s.%s", parent->name, name);
|
||||
str = strdup(dev_name);
|
||||
if (!str)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = blk_create_device(parent, drv_name, str, if_type, devnum,
|
||||
blksz, lba, devp);
|
||||
if (ret) {
|
||||
free(str);
|
||||
return ret;
|
||||
}
|
||||
device_set_name_alloced(*devp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int blk_unbind_all(int if_type)
|
||||
{
|
||||
struct uclass *uc;
|
||||
struct udevice *dev, *next;
|
||||
int ret;
|
||||
|
||||
ret = uclass_get(UCLASS_BLK, &uc);
|
||||
if (ret)
|
||||
return ret;
|
||||
uclass_foreach_dev_safe(dev, next, uc) {
|
||||
struct blk_desc *desc = dev_get_uclass_platdata(dev);
|
||||
|
||||
if (desc->if_type == if_type) {
|
||||
ret = device_remove(dev, DM_REMOVE_NORMAL);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = device_unbind(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int blk_post_probe(struct udevice *dev)
|
||||
{
|
||||
#if defined(CONFIG_PARTITIONS) && defined(CONFIG_HAVE_BLOCK_DEVICE)
|
||||
struct blk_desc *desc = dev_get_uclass_platdata(dev);
|
||||
|
||||
part_init(desc);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
UCLASS_DRIVER(blk) = {
|
||||
.id = UCLASS_BLK,
|
||||
.name = "blk",
|
||||
.post_probe = blk_post_probe,
|
||||
.per_device_platdata_auto_alloc_size = sizeof(struct blk_desc),
|
||||
};
|
||||
@@ -0,0 +1,264 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2016 Google, Inc
|
||||
* Written by Simon Glass <sjg@chromium.org>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
struct blk_driver *blk_driver_lookup_type(int if_type)
|
||||
{
|
||||
struct blk_driver *drv = ll_entry_start(struct blk_driver, blk_driver);
|
||||
const int n_ents = ll_entry_count(struct blk_driver, blk_driver);
|
||||
struct blk_driver *entry;
|
||||
|
||||
for (entry = drv; entry != drv + n_ents; entry++) {
|
||||
if (if_type == entry->if_type)
|
||||
return entry;
|
||||
}
|
||||
|
||||
/* Not found */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct blk_driver *blk_driver_lookup_typename(const char *if_typename)
|
||||
{
|
||||
struct blk_driver *drv = ll_entry_start(struct blk_driver, blk_driver);
|
||||
const int n_ents = ll_entry_count(struct blk_driver, blk_driver);
|
||||
struct blk_driver *entry;
|
||||
|
||||
for (entry = drv; entry != drv + n_ents; entry++) {
|
||||
if (!strcmp(if_typename, entry->if_typename))
|
||||
return entry;
|
||||
}
|
||||
|
||||
/* Not found */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *blk_get_if_type_name(enum if_type if_type)
|
||||
{
|
||||
struct blk_driver *drv = blk_driver_lookup_type(if_type);
|
||||
|
||||
return drv ? drv->if_typename : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* get_desc() - Get the block device descriptor for the given device number
|
||||
*
|
||||
* @drv: Legacy block driver
|
||||
* @devnum: Device number (0 = first)
|
||||
* @descp: Returns block device descriptor on success
|
||||
* @return 0 on success, -ENODEV if there is no such device, -ENOSYS if the
|
||||
* driver does not provide a way to find a device, or other -ve on other
|
||||
* error.
|
||||
*/
|
||||
static int get_desc(struct blk_driver *drv, int devnum, struct blk_desc **descp)
|
||||
{
|
||||
if (drv->desc) {
|
||||
if (devnum < 0 || devnum >= drv->max_devs)
|
||||
return -ENODEV;
|
||||
*descp = &drv->desc[devnum];
|
||||
return 0;
|
||||
}
|
||||
if (!drv->get_dev)
|
||||
return -ENOSYS;
|
||||
|
||||
return drv->get_dev(devnum, descp);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HAVE_BLOCK_DEVICE
|
||||
int blk_list_part(enum if_type if_type)
|
||||
{
|
||||
struct blk_driver *drv;
|
||||
struct blk_desc *desc;
|
||||
int devnum, ok;
|
||||
bool first = true;
|
||||
|
||||
drv = blk_driver_lookup_type(if_type);
|
||||
if (!drv)
|
||||
return -ENOSYS;
|
||||
for (ok = 0, devnum = 0; devnum < drv->max_devs; ++devnum) {
|
||||
if (get_desc(drv, devnum, &desc))
|
||||
continue;
|
||||
if (desc->part_type != PART_TYPE_UNKNOWN) {
|
||||
++ok;
|
||||
if (!first)
|
||||
putc('\n');
|
||||
part_print(desc);
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
if (!ok)
|
||||
return -ENODEV;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int blk_print_part_devnum(enum if_type if_type, int devnum)
|
||||
{
|
||||
struct blk_driver *drv = blk_driver_lookup_type(if_type);
|
||||
struct blk_desc *desc;
|
||||
int ret;
|
||||
|
||||
if (!drv)
|
||||
return -ENOSYS;
|
||||
ret = get_desc(drv, devnum, &desc);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (desc->type == DEV_TYPE_UNKNOWN)
|
||||
return -ENOENT;
|
||||
part_print(desc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void blk_list_devices(enum if_type if_type)
|
||||
{
|
||||
struct blk_driver *drv = blk_driver_lookup_type(if_type);
|
||||
struct blk_desc *desc;
|
||||
int i;
|
||||
|
||||
if (!drv)
|
||||
return;
|
||||
for (i = 0; i < drv->max_devs; ++i) {
|
||||
if (get_desc(drv, i, &desc))
|
||||
continue;
|
||||
if (desc->type == DEV_TYPE_UNKNOWN)
|
||||
continue; /* list only known devices */
|
||||
printf("Device %d: ", i);
|
||||
dev_print(desc);
|
||||
}
|
||||
}
|
||||
|
||||
int blk_print_device_num(enum if_type if_type, int devnum)
|
||||
{
|
||||
struct blk_driver *drv = blk_driver_lookup_type(if_type);
|
||||
struct blk_desc *desc;
|
||||
int ret;
|
||||
|
||||
if (!drv)
|
||||
return -ENOSYS;
|
||||
ret = get_desc(drv, devnum, &desc);
|
||||
if (ret)
|
||||
return ret;
|
||||
printf("\n%s device %d: ", drv->if_typename, devnum);
|
||||
dev_print(desc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int blk_show_device(enum if_type if_type, int devnum)
|
||||
{
|
||||
struct blk_driver *drv = blk_driver_lookup_type(if_type);
|
||||
struct blk_desc *desc;
|
||||
int ret;
|
||||
|
||||
if (!drv)
|
||||
return -ENOSYS;
|
||||
printf("\nDevice %d: ", devnum);
|
||||
if (devnum >= drv->max_devs) {
|
||||
puts("unknown device\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
ret = get_desc(drv, devnum, &desc);
|
||||
if (ret)
|
||||
return ret;
|
||||
dev_print(desc);
|
||||
|
||||
if (desc->type == DEV_TYPE_UNKNOWN)
|
||||
return -ENOENT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_HAVE_BLOCK_DEVICE */
|
||||
|
||||
struct blk_desc *blk_get_devnum_by_type(enum if_type if_type, int devnum)
|
||||
{
|
||||
struct blk_driver *drv = blk_driver_lookup_type(if_type);
|
||||
struct blk_desc *desc;
|
||||
|
||||
if (!drv)
|
||||
return NULL;
|
||||
|
||||
if (get_desc(drv, devnum, &desc))
|
||||
return NULL;
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
int blk_dselect_hwpart(struct blk_desc *desc, int hwpart)
|
||||
{
|
||||
struct blk_driver *drv = blk_driver_lookup_type(desc->if_type);
|
||||
|
||||
if (!drv)
|
||||
return -ENOSYS;
|
||||
if (drv->select_hwpart)
|
||||
return drv->select_hwpart(desc, hwpart);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct blk_desc *blk_get_devnum_by_typename(const char *if_typename, int devnum)
|
||||
{
|
||||
struct blk_driver *drv = blk_driver_lookup_typename(if_typename);
|
||||
struct blk_desc *desc;
|
||||
|
||||
if (!drv)
|
||||
return NULL;
|
||||
|
||||
if (get_desc(drv, devnum, &desc))
|
||||
return NULL;
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
ulong blk_read_devnum(enum if_type if_type, int devnum, lbaint_t start,
|
||||
lbaint_t blkcnt, void *buffer)
|
||||
{
|
||||
struct blk_driver *drv = blk_driver_lookup_type(if_type);
|
||||
struct blk_desc *desc;
|
||||
ulong n;
|
||||
int ret;
|
||||
|
||||
if (!drv)
|
||||
return -ENOSYS;
|
||||
ret = get_desc(drv, devnum, &desc);
|
||||
if (ret)
|
||||
return ret;
|
||||
n = desc->block_read(desc, start, blkcnt, buffer);
|
||||
if (IS_ERR_VALUE(n))
|
||||
return n;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
ulong blk_write_devnum(enum if_type if_type, int devnum, lbaint_t start,
|
||||
lbaint_t blkcnt, const void *buffer)
|
||||
{
|
||||
struct blk_driver *drv = blk_driver_lookup_type(if_type);
|
||||
struct blk_desc *desc;
|
||||
int ret;
|
||||
|
||||
if (!drv)
|
||||
return -ENOSYS;
|
||||
ret = get_desc(drv, devnum, &desc);
|
||||
if (ret)
|
||||
return ret;
|
||||
return desc->block_write(desc, start, blkcnt, buffer);
|
||||
}
|
||||
|
||||
int blk_select_hwpart_devnum(enum if_type if_type, int devnum, int hwpart)
|
||||
{
|
||||
struct blk_driver *drv = blk_driver_lookup_type(if_type);
|
||||
struct blk_desc *desc;
|
||||
int ret;
|
||||
|
||||
if (!drv)
|
||||
return -ENOSYS;
|
||||
ret = get_desc(drv, devnum, &desc);
|
||||
if (ret)
|
||||
return ret;
|
||||
return drv->select_hwpart(desc, hwpart);
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) Nelson Integration, LLC 2016
|
||||
* Author: Eric Nelson<eric@nelint.com>
|
||||
*
|
||||
*/
|
||||
#include <config.h>
|
||||
#include <common.h>
|
||||
#include <malloc.h>
|
||||
#include <part.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
struct block_cache_node {
|
||||
struct list_head lh;
|
||||
int iftype;
|
||||
int devnum;
|
||||
lbaint_t start;
|
||||
lbaint_t blkcnt;
|
||||
unsigned long blksz;
|
||||
char *cache;
|
||||
};
|
||||
|
||||
static LIST_HEAD(block_cache);
|
||||
|
||||
static struct block_cache_stats _stats = {
|
||||
.max_blocks_per_entry = 8,
|
||||
.max_entries = 32
|
||||
};
|
||||
|
||||
static struct block_cache_node *cache_find(int iftype, int devnum,
|
||||
lbaint_t start, lbaint_t blkcnt,
|
||||
unsigned long blksz)
|
||||
{
|
||||
struct block_cache_node *node;
|
||||
|
||||
list_for_each_entry(node, &block_cache, lh)
|
||||
if ((node->iftype == iftype) &&
|
||||
(node->devnum == devnum) &&
|
||||
(node->blksz == blksz) &&
|
||||
(node->start <= start) &&
|
||||
(node->start + node->blkcnt >= start + blkcnt)) {
|
||||
if (block_cache.next != &node->lh) {
|
||||
/* maintain MRU ordering */
|
||||
list_del(&node->lh);
|
||||
list_add(&node->lh, &block_cache);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int blkcache_read(int iftype, int devnum,
|
||||
lbaint_t start, lbaint_t blkcnt,
|
||||
unsigned long blksz, void *buffer)
|
||||
{
|
||||
struct block_cache_node *node = cache_find(iftype, devnum, start,
|
||||
blkcnt, blksz);
|
||||
if (node) {
|
||||
const char *src = node->cache + (start - node->start) * blksz;
|
||||
memcpy(buffer, src, blksz * blkcnt);
|
||||
debug("hit: start " LBAF ", count " LBAFU "\n",
|
||||
start, blkcnt);
|
||||
++_stats.hits;
|
||||
return 1;
|
||||
}
|
||||
|
||||
debug("miss: start " LBAF ", count " LBAFU "\n",
|
||||
start, blkcnt);
|
||||
++_stats.misses;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void blkcache_fill(int iftype, int devnum,
|
||||
lbaint_t start, lbaint_t blkcnt,
|
||||
unsigned long blksz, void const *buffer)
|
||||
{
|
||||
lbaint_t bytes;
|
||||
struct block_cache_node *node;
|
||||
|
||||
/* don't cache big stuff */
|
||||
if (blkcnt > _stats.max_blocks_per_entry)
|
||||
return;
|
||||
|
||||
if (_stats.max_entries == 0)
|
||||
return;
|
||||
|
||||
bytes = blksz * blkcnt;
|
||||
if (_stats.max_entries <= _stats.entries) {
|
||||
/* pop LRU */
|
||||
node = (struct block_cache_node *)block_cache.prev;
|
||||
list_del(&node->lh);
|
||||
_stats.entries--;
|
||||
debug("drop: start " LBAF ", count " LBAFU "\n",
|
||||
node->start, node->blkcnt);
|
||||
if (node->blkcnt * node->blksz < bytes) {
|
||||
free(node->cache);
|
||||
node->cache = 0;
|
||||
}
|
||||
} else {
|
||||
node = malloc(sizeof(*node));
|
||||
if (!node)
|
||||
return;
|
||||
node->cache = 0;
|
||||
}
|
||||
|
||||
if (!node->cache) {
|
||||
node->cache = malloc(bytes);
|
||||
if (!node->cache) {
|
||||
free(node);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
debug("fill: start " LBAF ", count " LBAFU "\n",
|
||||
start, blkcnt);
|
||||
|
||||
node->iftype = iftype;
|
||||
node->devnum = devnum;
|
||||
node->start = start;
|
||||
node->blkcnt = blkcnt;
|
||||
node->blksz = blksz;
|
||||
memcpy(node->cache, buffer, bytes);
|
||||
list_add(&node->lh, &block_cache);
|
||||
_stats.entries++;
|
||||
}
|
||||
|
||||
void blkcache_invalidate(int iftype, int devnum)
|
||||
{
|
||||
struct list_head *entry, *n;
|
||||
struct block_cache_node *node;
|
||||
|
||||
list_for_each_safe(entry, n, &block_cache) {
|
||||
node = (struct block_cache_node *)entry;
|
||||
if ((node->iftype == iftype) &&
|
||||
(node->devnum == devnum)) {
|
||||
list_del(entry);
|
||||
free(node->cache);
|
||||
free(node);
|
||||
--_stats.entries;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void blkcache_configure(unsigned blocks, unsigned entries)
|
||||
{
|
||||
struct block_cache_node *node;
|
||||
if ((blocks != _stats.max_blocks_per_entry) ||
|
||||
(entries != _stats.max_entries)) {
|
||||
/* invalidate cache */
|
||||
while (!list_empty(&block_cache)) {
|
||||
node = (struct block_cache_node *)block_cache.next;
|
||||
list_del(&node->lh);
|
||||
free(node->cache);
|
||||
free(node);
|
||||
}
|
||||
_stats.entries = 0;
|
||||
}
|
||||
|
||||
_stats.max_blocks_per_entry = blocks;
|
||||
_stats.max_entries = entries;
|
||||
|
||||
_stats.hits = 0;
|
||||
_stats.misses = 0;
|
||||
}
|
||||
|
||||
void blkcache_stats(struct block_cache_stats *stats)
|
||||
{
|
||||
memcpy(stats, &_stats, sizeof(*stats));
|
||||
_stats.hits = 0;
|
||||
_stats.misses = 0;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,239 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2013 Henrik Nordstrom <henrik@henriknordstrom.net>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <blk.h>
|
||||
#include <dm.h>
|
||||
#include <fdtdec.h>
|
||||
#include <part.h>
|
||||
#include <os.h>
|
||||
#include <malloc.h>
|
||||
#include <sandboxblockdev.h>
|
||||
#include <linux/errno.h>
|
||||
#include <dm/device-internal.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
#ifndef CONFIG_BLK
|
||||
static struct host_block_dev host_devices[CONFIG_HOST_MAX_DEVICES];
|
||||
|
||||
static struct host_block_dev *find_host_device(int dev)
|
||||
{
|
||||
if (dev >= 0 && dev < CONFIG_HOST_MAX_DEVICES)
|
||||
return &host_devices[dev];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BLK
|
||||
static unsigned long host_block_read(struct udevice *dev,
|
||||
unsigned long start, lbaint_t blkcnt,
|
||||
void *buffer)
|
||||
{
|
||||
struct host_block_dev *host_dev = dev_get_platdata(dev);
|
||||
struct blk_desc *block_dev = dev_get_uclass_platdata(dev);
|
||||
|
||||
#else
|
||||
static unsigned long host_block_read(struct blk_desc *block_dev,
|
||||
unsigned long start, lbaint_t blkcnt,
|
||||
void *buffer)
|
||||
{
|
||||
int dev = block_dev->devnum;
|
||||
struct host_block_dev *host_dev = find_host_device(dev);
|
||||
|
||||
if (!host_dev)
|
||||
return -1;
|
||||
#endif
|
||||
|
||||
if (os_lseek(host_dev->fd, start * block_dev->blksz, OS_SEEK_SET) ==
|
||||
-1) {
|
||||
printf("ERROR: Invalid block %lx\n", start);
|
||||
return -1;
|
||||
}
|
||||
ssize_t len = os_read(host_dev->fd, buffer, blkcnt * block_dev->blksz);
|
||||
if (len >= 0)
|
||||
return len / block_dev->blksz;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BLK
|
||||
static unsigned long host_block_write(struct udevice *dev,
|
||||
unsigned long start, lbaint_t blkcnt,
|
||||
const void *buffer)
|
||||
{
|
||||
struct host_block_dev *host_dev = dev_get_platdata(dev);
|
||||
struct blk_desc *block_dev = dev_get_uclass_platdata(dev);
|
||||
#else
|
||||
static unsigned long host_block_write(struct blk_desc *block_dev,
|
||||
unsigned long start, lbaint_t blkcnt,
|
||||
const void *buffer)
|
||||
{
|
||||
int dev = block_dev->devnum;
|
||||
struct host_block_dev *host_dev = find_host_device(dev);
|
||||
#endif
|
||||
|
||||
if (os_lseek(host_dev->fd, start * block_dev->blksz, OS_SEEK_SET) ==
|
||||
-1) {
|
||||
printf("ERROR: Invalid block %lx\n", start);
|
||||
return -1;
|
||||
}
|
||||
ssize_t len = os_write(host_dev->fd, buffer, blkcnt * block_dev->blksz);
|
||||
if (len >= 0)
|
||||
return len / block_dev->blksz;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BLK
|
||||
int host_dev_bind(int devnum, char *filename)
|
||||
{
|
||||
struct host_block_dev *host_dev;
|
||||
struct udevice *dev;
|
||||
char dev_name[20], *str, *fname;
|
||||
int ret, fd;
|
||||
|
||||
/* Remove and unbind the old device, if any */
|
||||
ret = blk_get_device(IF_TYPE_HOST, devnum, &dev);
|
||||
if (ret == 0) {
|
||||
ret = device_remove(dev, DM_REMOVE_NORMAL);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = device_unbind(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else if (ret != -ENODEV) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!filename)
|
||||
return 0;
|
||||
|
||||
snprintf(dev_name, sizeof(dev_name), "host%d", devnum);
|
||||
str = strdup(dev_name);
|
||||
if (!str)
|
||||
return -ENOMEM;
|
||||
fname = strdup(filename);
|
||||
if (!fname) {
|
||||
free(str);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
fd = os_open(filename, OS_O_RDWR);
|
||||
if (fd == -1) {
|
||||
printf("Failed to access host backing file '%s'\n", filename);
|
||||
ret = -ENOENT;
|
||||
goto err;
|
||||
}
|
||||
ret = blk_create_device(gd->dm_root, "sandbox_host_blk", str,
|
||||
IF_TYPE_HOST, devnum, 512,
|
||||
os_lseek(fd, 0, OS_SEEK_END) / 512, &dev);
|
||||
if (ret)
|
||||
goto err_file;
|
||||
|
||||
host_dev = dev_get_platdata(dev);
|
||||
host_dev->fd = fd;
|
||||
host_dev->filename = fname;
|
||||
|
||||
ret = device_probe(dev);
|
||||
if (ret) {
|
||||
device_unbind(dev);
|
||||
goto err_file;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err_file:
|
||||
os_close(fd);
|
||||
err:
|
||||
free(fname);
|
||||
free(str);
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
int host_dev_bind(int dev, char *filename)
|
||||
{
|
||||
struct host_block_dev *host_dev = find_host_device(dev);
|
||||
|
||||
if (!host_dev)
|
||||
return -1;
|
||||
if (host_dev->blk_dev.priv) {
|
||||
os_close(host_dev->fd);
|
||||
host_dev->blk_dev.priv = NULL;
|
||||
}
|
||||
if (host_dev->filename)
|
||||
free(host_dev->filename);
|
||||
if (filename && *filename) {
|
||||
host_dev->filename = strdup(filename);
|
||||
} else {
|
||||
host_dev->filename = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
host_dev->fd = os_open(host_dev->filename, OS_O_RDWR);
|
||||
if (host_dev->fd == -1) {
|
||||
printf("Failed to access host backing file '%s'\n",
|
||||
host_dev->filename);
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct blk_desc *blk_dev = &host_dev->blk_dev;
|
||||
blk_dev->if_type = IF_TYPE_HOST;
|
||||
blk_dev->priv = host_dev;
|
||||
blk_dev->blksz = 512;
|
||||
blk_dev->lba = os_lseek(host_dev->fd, 0, OS_SEEK_END) / blk_dev->blksz;
|
||||
blk_dev->block_read = host_block_read;
|
||||
blk_dev->block_write = host_block_write;
|
||||
blk_dev->devnum = dev;
|
||||
blk_dev->part_type = PART_TYPE_UNKNOWN;
|
||||
part_init(blk_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int host_get_dev_err(int devnum, struct blk_desc **blk_devp)
|
||||
{
|
||||
#ifdef CONFIG_BLK
|
||||
struct udevice *dev;
|
||||
int ret;
|
||||
|
||||
ret = blk_get_device(IF_TYPE_HOST, devnum, &dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
*blk_devp = dev_get_uclass_platdata(dev);
|
||||
#else
|
||||
struct host_block_dev *host_dev = find_host_device(devnum);
|
||||
|
||||
if (!host_dev)
|
||||
return -ENODEV;
|
||||
|
||||
if (!host_dev->blk_dev.priv)
|
||||
return -ENOENT;
|
||||
|
||||
*blk_devp = &host_dev->blk_dev;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BLK
|
||||
static const struct blk_ops sandbox_host_blk_ops = {
|
||||
.read = host_block_read,
|
||||
.write = host_block_write,
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(sandbox_host_blk) = {
|
||||
.name = "sandbox_host_blk",
|
||||
.id = UCLASS_BLK,
|
||||
.ops = &sandbox_host_blk_ops,
|
||||
.platdata_auto_alloc_size = sizeof(struct host_block_dev),
|
||||
};
|
||||
#else
|
||||
U_BOOT_LEGACY_BLK(sandbox_host) = {
|
||||
.if_typename = "host",
|
||||
.if_type = IF_TYPE_HOST,
|
||||
.max_devs = CONFIG_HOST_MAX_DEVICES,
|
||||
.get_dev = host_get_dev_err,
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,22 @@
|
||||
menuconfig BOARD
|
||||
bool "Device Information"
|
||||
help
|
||||
Support methods to query hardware configurations from internal
|
||||
mechanisms (e.g. reading GPIO values, determining the presence of
|
||||
devices on busses, etc.). This enables the usage of U-Boot with
|
||||
modular board architectures.
|
||||
|
||||
if BOARD
|
||||
|
||||
|
||||
config BOARD_GAZERBEAM
|
||||
bool "Enable board driver for the Gazerbeam board"
|
||||
help
|
||||
Support querying device information for the gdsys Gazerbeam board.
|
||||
|
||||
config BOARD_SANDBOX
|
||||
bool "Enable board driver for the Sandbox board"
|
||||
help
|
||||
Support querying device information for the Sandbox boards.
|
||||
|
||||
endif
|
||||
@@ -0,0 +1,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
# (C) Copyright 2017
|
||||
# Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
|
||||
obj-$(CONFIG_BOARD) += board-uclass.o
|
||||
obj-$(CONFIG_BOARD_GAZERBEAM) += gazerbeam.o
|
||||
obj-$(CONFIG_BOARD_SANDBOX) += sandbox.o
|
||||
@@ -0,0 +1,60 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2017
|
||||
* Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <board.h>
|
||||
|
||||
int board_get(struct udevice **devp)
|
||||
{
|
||||
return uclass_first_device_err(UCLASS_BOARD, devp);
|
||||
}
|
||||
|
||||
int board_detect(struct udevice *dev)
|
||||
{
|
||||
struct board_ops *ops = board_get_ops(dev);
|
||||
|
||||
if (!ops->detect)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->detect(dev);
|
||||
}
|
||||
|
||||
int board_get_bool(struct udevice *dev, int id, bool *val)
|
||||
{
|
||||
struct board_ops *ops = board_get_ops(dev);
|
||||
|
||||
if (!ops->get_bool)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->get_bool(dev, id, val);
|
||||
}
|
||||
|
||||
int board_get_int(struct udevice *dev, int id, int *val)
|
||||
{
|
||||
struct board_ops *ops = board_get_ops(dev);
|
||||
|
||||
if (!ops->get_int)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->get_int(dev, id, val);
|
||||
}
|
||||
|
||||
int board_get_str(struct udevice *dev, int id, size_t size, char *val)
|
||||
{
|
||||
struct board_ops *ops = board_get_ops(dev);
|
||||
|
||||
if (!ops->get_str)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->get_str(dev, id, size, val);
|
||||
}
|
||||
|
||||
UCLASS_DRIVER(board) = {
|
||||
.id = UCLASS_BOARD,
|
||||
.name = "board",
|
||||
.post_bind = dm_scan_fdt_dev,
|
||||
};
|
||||
@@ -0,0 +1,264 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2017
|
||||
* Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <board.h>
|
||||
#include <i2c.h>
|
||||
#include <asm/gpio.h>
|
||||
|
||||
#include "gazerbeam.h"
|
||||
|
||||
/* Sequence number of I2C bus that holds the GPIO expanders */
|
||||
static const int I2C_BUS_SEQ_NO = 1;
|
||||
|
||||
/* I2C address of SC/MC2 expander */
|
||||
static const int MC2_EXPANDER_ADDR = 0x20;
|
||||
/* I2C address of MC4 expander */
|
||||
static const int MC4_EXPANDER_ADDR = 0x22;
|
||||
|
||||
/* Number of the GPIO to read the SC data from */
|
||||
static const int SC_GPIO_NO;
|
||||
/* Number of the GPIO to read the CON data from */
|
||||
static const int CON_GPIO_NO = 1;
|
||||
|
||||
/**
|
||||
* struct board_gazerbeam_priv - Private data structure for the gazerbeam board
|
||||
* driver.
|
||||
* @reset_gpios: GPIOs for the board's reset GPIOs.
|
||||
* @var_gpios: GPIOs for the board's hardware variant GPIOs
|
||||
* @ver_gpios: GPIOs for the board's hardware version GPIOs
|
||||
* @variant: Container for the board's hardware variant (CON/CPU)
|
||||
* @multichannel: Container for the board's multichannel variant (MC4/MC2/SC)
|
||||
* @hwversion: Container for the board's hardware version
|
||||
*/
|
||||
struct board_gazerbeam_priv {
|
||||
struct gpio_desc reset_gpios[2];
|
||||
struct gpio_desc var_gpios[2];
|
||||
struct gpio_desc ver_gpios[4];
|
||||
int variant;
|
||||
int multichannel;
|
||||
int hwversion;
|
||||
};
|
||||
|
||||
/**
|
||||
* _read_board_variant_data() - Read variant information from the hardware.
|
||||
* @dev: The board device for which to determine the multichannel and device
|
||||
* type information.
|
||||
*
|
||||
* The data read from the board's hardware (mostly hard-wired GPIOs) is stored
|
||||
* in the private data structure of the driver to be used by other driver
|
||||
* methods.
|
||||
*
|
||||
* Return: 0 if OK, -ve on error.
|
||||
*/
|
||||
static int _read_board_variant_data(struct udevice *dev)
|
||||
{
|
||||
struct board_gazerbeam_priv *priv = dev_get_priv(dev);
|
||||
struct udevice *i2c_bus;
|
||||
struct udevice *dummy;
|
||||
char *listname;
|
||||
int mc4, mc2, sc, mc2_sc, con;
|
||||
int gpio_num;
|
||||
int res;
|
||||
|
||||
res = uclass_get_device_by_seq(UCLASS_I2C, I2C_BUS_SEQ_NO, &i2c_bus);
|
||||
if (res) {
|
||||
debug("%s: Could not get I2C bus %d (err = %d)\n",
|
||||
dev->name, I2C_BUS_SEQ_NO, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
if (!i2c_bus) {
|
||||
debug("%s: Could not get I2C bus %d\n",
|
||||
dev->name, I2C_BUS_SEQ_NO);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
mc2_sc = !dm_i2c_probe(i2c_bus, MC2_EXPANDER_ADDR, 0, &dummy);
|
||||
mc4 = !dm_i2c_probe(i2c_bus, MC4_EXPANDER_ADDR, 0, &dummy);
|
||||
|
||||
if (mc2_sc && mc4) {
|
||||
debug("%s: Board hardware configuration inconsistent.\n",
|
||||
dev->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
listname = mc2_sc ? "var-gpios-mc2" : "var-gpios-mc4";
|
||||
|
||||
gpio_num = gpio_request_list_by_name(dev, listname, priv->var_gpios,
|
||||
ARRAY_SIZE(priv->var_gpios),
|
||||
GPIOD_IS_IN);
|
||||
if (gpio_num < 0) {
|
||||
debug("%s: Requesting gpio list %s failed (err = %d).\n",
|
||||
dev->name, listname, gpio_num);
|
||||
return gpio_num;
|
||||
}
|
||||
|
||||
sc = dm_gpio_get_value(&priv->var_gpios[SC_GPIO_NO]);
|
||||
if (sc < 0) {
|
||||
debug("%s: Error while reading 'sc' GPIO (err = %d)",
|
||||
dev->name, sc);
|
||||
return sc;
|
||||
}
|
||||
|
||||
mc2 = mc2_sc ? (sc ? 0 : 1) : 0;
|
||||
|
||||
if ((sc && mc2) || (sc && mc4) || (!sc && !mc2 && !mc4)) {
|
||||
debug("%s: Board hardware configuration inconsistent.\n",
|
||||
dev->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
con = dm_gpio_get_value(&priv->var_gpios[CON_GPIO_NO]);
|
||||
if (con < 0) {
|
||||
debug("%s: Error while reading 'con' GPIO (err = %d)",
|
||||
dev->name, con);
|
||||
return con;
|
||||
}
|
||||
|
||||
priv->variant = con ? VAR_CON : VAR_CPU;
|
||||
|
||||
priv->multichannel = mc4 ? 4 : (mc2 ? 2 : (sc ? 1 : 0));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* _read_hwversion() - Read the hardware version from the board.
|
||||
* @dev: The board device for which to read the hardware version.
|
||||
*
|
||||
* The hardware version read from the board (from hard-wired GPIOs) is stored
|
||||
* in the private data structure of the driver to be used by other driver
|
||||
* methods.
|
||||
*
|
||||
* Return: 0 if OK, -ve on error.
|
||||
*/
|
||||
static int _read_hwversion(struct udevice *dev)
|
||||
{
|
||||
struct board_gazerbeam_priv *priv = dev_get_priv(dev);
|
||||
int res;
|
||||
|
||||
res = gpio_request_list_by_name(dev, "ver-gpios", priv->ver_gpios,
|
||||
ARRAY_SIZE(priv->ver_gpios),
|
||||
GPIOD_IS_IN);
|
||||
if (res < 0) {
|
||||
debug("%s: Error getting GPIO list 'ver-gpios' (err = %d)\n",
|
||||
dev->name, res);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
res = dm_gpio_get_values_as_int(priv->ver_gpios,
|
||||
ARRAY_SIZE(priv->ver_gpios));
|
||||
if (res < 0) {
|
||||
debug("%s: Error reading HW version from expander (err = %d)\n",
|
||||
dev->name, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
priv->hwversion = res;
|
||||
|
||||
res = gpio_free_list(dev, priv->ver_gpios, ARRAY_SIZE(priv->ver_gpios));
|
||||
if (res < 0) {
|
||||
debug("%s: Error freeing HW version GPIO list (err = %d)\n",
|
||||
dev->name, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int board_gazerbeam_detect(struct udevice *dev)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = _read_board_variant_data(dev);
|
||||
if (res) {
|
||||
debug("%s: Error reading multichannel variant (err = %d)\n",
|
||||
dev->name, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
res = _read_hwversion(dev);
|
||||
if (res) {
|
||||
debug("%s: Error reading hardware version (err = %d)\n",
|
||||
dev->name, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int board_gazerbeam_get_int(struct udevice *dev, int id, int *val)
|
||||
{
|
||||
struct board_gazerbeam_priv *priv = dev_get_priv(dev);
|
||||
|
||||
switch (id) {
|
||||
case BOARD_MULTICHANNEL:
|
||||
*val = priv->multichannel;
|
||||
break;
|
||||
case BOARD_VARIANT:
|
||||
*val = priv->variant;
|
||||
break;
|
||||
case BOARD_HWVERSION:
|
||||
*val = priv->hwversion;
|
||||
break;
|
||||
default:
|
||||
debug("%s: Integer value %d unknown\n", dev->name, id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id board_gazerbeam_ids[] = {
|
||||
{ .compatible = "gdsys,board_gazerbeam" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
static const struct board_ops board_gazerbeam_ops = {
|
||||
.detect = board_gazerbeam_detect,
|
||||
.get_int = board_gazerbeam_get_int,
|
||||
};
|
||||
|
||||
static int board_gazerbeam_probe(struct udevice *dev)
|
||||
{
|
||||
struct board_gazerbeam_priv *priv = dev_get_priv(dev);
|
||||
int gpio_num, i;
|
||||
|
||||
gpio_num = gpio_request_list_by_name(dev, "reset-gpios",
|
||||
priv->reset_gpios,
|
||||
ARRAY_SIZE(priv->reset_gpios),
|
||||
GPIOD_IS_OUT);
|
||||
|
||||
if (gpio_num < 0) {
|
||||
debug("%s: Error getting GPIO list 'reset-gpios' (err = %d)\n",
|
||||
dev->name, gpio_num);
|
||||
return gpio_num;
|
||||
}
|
||||
|
||||
/* Set startup-finished GPIOs */
|
||||
for (i = 0; i < ARRAY_SIZE(priv->reset_gpios); i++) {
|
||||
int res = dm_gpio_set_value(&priv->reset_gpios[i], 0);
|
||||
|
||||
if (res) {
|
||||
debug("%s: Error while setting GPIO %d (err = %d)\n",
|
||||
dev->name, i, res);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
U_BOOT_DRIVER(board_gazerbeam) = {
|
||||
.name = "board_gazerbeam",
|
||||
.id = UCLASS_BOARD,
|
||||
.of_match = board_gazerbeam_ids,
|
||||
.ops = &board_gazerbeam_ops,
|
||||
.priv_auto_alloc_size = sizeof(struct board_gazerbeam_priv),
|
||||
.probe = board_gazerbeam_probe,
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* (C) Copyright 2017
|
||||
* Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
|
||||
*
|
||||
*/
|
||||
|
||||
enum {
|
||||
BOARD_MULTICHANNEL,
|
||||
BOARD_VARIANT,
|
||||
BOARD_HWVERSION,
|
||||
};
|
||||
|
||||
enum {
|
||||
VAR_CON,
|
||||
VAR_CPU,
|
||||
};
|
||||
@@ -0,0 +1,107 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2018
|
||||
* Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <board.h>
|
||||
|
||||
#include "sandbox.h"
|
||||
|
||||
struct board_sandbox_priv {
|
||||
bool called_detect;
|
||||
int test_i1;
|
||||
int test_i2;
|
||||
};
|
||||
|
||||
char vacation_spots[][64] = {"R'lyeh", "Dreamlands", "Plateau of Leng",
|
||||
"Carcosa", "Yuggoth", "The Nameless City"};
|
||||
|
||||
int board_sandbox_detect(struct udevice *dev)
|
||||
{
|
||||
struct board_sandbox_priv *priv = dev_get_priv(dev);
|
||||
|
||||
priv->called_detect = true;
|
||||
priv->test_i2 = 100;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int board_sandbox_get_bool(struct udevice *dev, int id, bool *val)
|
||||
{
|
||||
struct board_sandbox_priv *priv = dev_get_priv(dev);
|
||||
|
||||
switch (id) {
|
||||
case BOOL_CALLED_DETECT:
|
||||
/* Checks if the dectect method has been called */
|
||||
*val = priv->called_detect;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
int board_sandbox_get_int(struct udevice *dev, int id, int *val)
|
||||
{
|
||||
struct board_sandbox_priv *priv = dev_get_priv(dev);
|
||||
|
||||
switch (id) {
|
||||
case INT_TEST1:
|
||||
*val = priv->test_i1;
|
||||
/* Increments with every call */
|
||||
priv->test_i1++;
|
||||
return 0;
|
||||
case INT_TEST2:
|
||||
*val = priv->test_i2;
|
||||
/* Decrements with every call */
|
||||
priv->test_i2--;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
int board_sandbox_get_str(struct udevice *dev, int id, size_t size, char *val)
|
||||
{
|
||||
struct board_sandbox_priv *priv = dev_get_priv(dev);
|
||||
int i1 = priv->test_i1;
|
||||
int i2 = priv->test_i2;
|
||||
int index = (i1 * i2) % ARRAY_SIZE(vacation_spots);
|
||||
|
||||
switch (id) {
|
||||
case STR_VACATIONSPOT:
|
||||
/* Picks a vacation spot depending on i1 and i2 */
|
||||
snprintf(val, size, vacation_spots[index]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static const struct udevice_id board_sandbox_ids[] = {
|
||||
{ .compatible = "sandbox,board_sandbox" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
static const struct board_ops board_sandbox_ops = {
|
||||
.detect = board_sandbox_detect,
|
||||
.get_bool = board_sandbox_get_bool,
|
||||
.get_int = board_sandbox_get_int,
|
||||
.get_str = board_sandbox_get_str,
|
||||
};
|
||||
|
||||
int board_sandbox_probe(struct udevice *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
U_BOOT_DRIVER(board_sandbox) = {
|
||||
.name = "board_sandbox",
|
||||
.id = UCLASS_BOARD,
|
||||
.of_match = board_sandbox_ids,
|
||||
.ops = &board_sandbox_ops,
|
||||
.priv_auto_alloc_size = sizeof(struct board_sandbox_priv),
|
||||
.probe = board_sandbox_probe,
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* (C) Copyright 2018
|
||||
* Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
|
||||
*/
|
||||
|
||||
enum {
|
||||
BOOL_CALLED_DETECT,
|
||||
INT_TEST1,
|
||||
INT_TEST2,
|
||||
STR_VACATIONSPOT,
|
||||
};
|
||||
@@ -0,0 +1,164 @@
|
||||
#
|
||||
# Boot count configuration
|
||||
#
|
||||
|
||||
menuconfig BOOTCOUNT_LIMIT
|
||||
bool "Enable support for checking boot count limit"
|
||||
help
|
||||
Enable checking for exceeding the boot count limit.
|
||||
More information: http://www.denx.de/wiki/DULG/UBootBootCountLimit
|
||||
|
||||
if BOOTCOUNT_LIMIT
|
||||
|
||||
choice
|
||||
prompt "Boot count device"
|
||||
default BOOTCOUNT_AM33XX if AM33XX || SOC_DA8XX
|
||||
default BOOTCOUNT_AT91 if AT91SAM9XE
|
||||
default BOOTCOUNT_GENERIC
|
||||
|
||||
config BOOTCOUNT_GENERIC
|
||||
bool "Generic default boot counter"
|
||||
help
|
||||
Generic bootcount stored at SYS_BOOTCOUNT_ADDR.
|
||||
|
||||
SYS_BOOTCOUNT_ADDR:
|
||||
Set to the address where the bootcount and bootcount magic
|
||||
will be stored.
|
||||
|
||||
config BOOTCOUNT_EXT
|
||||
bool "Boot counter on EXT filesystem"
|
||||
help
|
||||
Add support for maintaining boot count in a file on an EXT
|
||||
filesystem.
|
||||
|
||||
config BOOTCOUNT_AM33XX
|
||||
bool "Boot counter in AM33XX RTC IP block"
|
||||
depends on AM33XX || SOC_DA8XX
|
||||
select SPL_AM33XX_ENABLE_RTC32K_OSC if AM33XX
|
||||
help
|
||||
A bootcount driver for the RTC IP block found on many TI platforms.
|
||||
This requires the RTC clocks, etc, to be enabled prior to use and
|
||||
not all boards with this IP block on it will have the RTC in use.
|
||||
|
||||
config BOOTCOUNT_ENV
|
||||
bool "Boot counter in environment"
|
||||
help
|
||||
If no softreset save registers are found on the hardware
|
||||
"bootcount" is stored in the environment. To prevent a
|
||||
saveenv on all reboots, the environment variable
|
||||
"upgrade_available" is used. If "upgrade_available" is
|
||||
0, "bootcount" is always 0, if "upgrade_available" is
|
||||
1 "bootcount" is incremented in the environment.
|
||||
So the Userspace Application must set the "upgrade_available"
|
||||
and "bootcount" variable to 0, if a boot was successfully.
|
||||
|
||||
config BOOTCOUNT_RAM
|
||||
bool "Boot counter in RAM"
|
||||
help
|
||||
Store the bootcount in DRAM protected against against bit errors
|
||||
due to short power loss or holding a system in RESET.
|
||||
|
||||
config BOOTCOUNT_I2C
|
||||
bool "Boot counter on I2C device"
|
||||
help
|
||||
Enable support for the bootcounter on an i2c (like RTC) device.
|
||||
CONFIG_SYS_I2C_RTC_ADDR = i2c chip address
|
||||
CONFIG_SYS_BOOTCOUNT_ADDR = i2c addr which is used for
|
||||
the bootcounter.
|
||||
|
||||
config BOOTCOUNT_AT91
|
||||
bool "Boot counter for Atmel AT91SAM9XE"
|
||||
depends on AT91SAM9XE
|
||||
|
||||
config DM_BOOTCOUNT
|
||||
bool "Boot counter in a device-model device"
|
||||
help
|
||||
Enables reading/writing the bootcount in a device-model based
|
||||
backing store. If an entry in /chosen/u-boot,bootcount-device
|
||||
exists, this will be the preferred bootcount device; otherwise
|
||||
the first available bootcount device will be used.
|
||||
|
||||
endchoice
|
||||
|
||||
if DM_BOOTCOUNT
|
||||
|
||||
menu "Backing stores for device-model backed bootcount"
|
||||
config DM_BOOTCOUNT_RTC
|
||||
bool "Support RTC devices as a backing store for bootcount"
|
||||
depends on DM_RTC
|
||||
help
|
||||
Enabled reading/writing the bootcount in a DM RTC device.
|
||||
The wrapper device is to be specified with the compatible string
|
||||
'u-boot,bootcount-rtc' and the 'rtc'-property (a phandle pointing
|
||||
to the underlying RTC device) and an optional 'offset' property
|
||||
are supported.
|
||||
|
||||
Accesses to the backing store are performed using the write16
|
||||
and read16 ops of DM RTC devices.
|
||||
|
||||
endmenu
|
||||
|
||||
endif
|
||||
|
||||
config BOOTCOUNT_BOOTLIMIT
|
||||
int "Maximum number of reboot cycles allowed"
|
||||
default 0
|
||||
help
|
||||
Set the Maximum number of reboot cycles allowed without the boot
|
||||
counter being cleared.
|
||||
If set to 0 do not set a boot limit in the environment.
|
||||
|
||||
config BOOTCOUNT_ALEN
|
||||
int "I2C address length"
|
||||
default 1
|
||||
depends on BOOTCOUNT_I2C
|
||||
help
|
||||
Length of the the I2C address at SYS_BOOTCOUNT_ADDR for storing
|
||||
the boot counter.
|
||||
|
||||
config SYS_BOOTCOUNT_SINGLEWORD
|
||||
bool "Use single word to pack boot count and magic value"
|
||||
depends on BOOTCOUNT_GENERIC
|
||||
help
|
||||
This option enables packing boot count magic value and boot count
|
||||
into single word (32 bits).
|
||||
|
||||
config SYS_BOOTCOUNT_EXT_INTERFACE
|
||||
string "Interface on which to find boot counter EXT filesystem"
|
||||
default "mmc"
|
||||
depends on BOOTCOUNT_EXT
|
||||
help
|
||||
Set the interface to use when locating the filesystem to use for the
|
||||
boot counter.
|
||||
|
||||
config SYS_BOOTCOUNT_EXT_DEVPART
|
||||
string "Partition of the boot counter EXT filesystem"
|
||||
default "0:1"
|
||||
depends on BOOTCOUNT_EXT
|
||||
help
|
||||
Set the partition to use when locating the filesystem to use for the
|
||||
boot counter.
|
||||
|
||||
config SYS_BOOTCOUNT_EXT_NAME
|
||||
string "Path and filename of the EXT filesystem based boot counter"
|
||||
default "/boot/failures"
|
||||
depends on BOOTCOUNT_EXT
|
||||
help
|
||||
Set the filename and path of the file used to store the boot counter.
|
||||
|
||||
config SYS_BOOTCOUNT_ADDR
|
||||
hex "RAM address used for reading and writing the boot counter"
|
||||
default 0x44E3E000 if BOOTCOUNT_AM33XX
|
||||
default 0xE0115FF8 if ARCH_LS1043A || ARCH_LS1021A
|
||||
depends on BOOTCOUNT_AM33XX || BOOTCOUNT_GENERIC || BOOTCOUNT_EXT || \
|
||||
BOOTCOUNT_I2C
|
||||
help
|
||||
Set the address used for reading and writing the boot counter.
|
||||
|
||||
config SYS_BOOTCOUNT_MAGIC
|
||||
hex "Magic value for the boot counter"
|
||||
default 0xB001C041
|
||||
help
|
||||
Set the magic value used for the boot counter.
|
||||
|
||||
endif
|
||||
@@ -0,0 +1,12 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
obj-$(CONFIG_BOOTCOUNT_GENERIC) += bootcount.o
|
||||
obj-$(CONFIG_BOOTCOUNT_AT91) += bootcount_at91.o
|
||||
obj-$(CONFIG_BOOTCOUNT_AM33XX) += bootcount_davinci.o
|
||||
obj-$(CONFIG_BOOTCOUNT_RAM) += bootcount_ram.o
|
||||
obj-$(CONFIG_BOOTCOUNT_ENV) += bootcount_env.o
|
||||
obj-$(CONFIG_BOOTCOUNT_I2C) += bootcount_i2c.o
|
||||
obj-$(CONFIG_BOOTCOUNT_EXT) += bootcount_ext.o
|
||||
|
||||
obj-$(CONFIG_DM_BOOTCOUNT) += bootcount-uclass.o
|
||||
obj-$(CONFIG_DM_BOOTCOUNT_RTC) += rtc.o
|
||||
@@ -0,0 +1,93 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2018 Theobroma Systems Design und Consulting GmbH
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <bootcount.h>
|
||||
|
||||
int dm_bootcount_get(struct udevice *dev, u32 *bootcount)
|
||||
{
|
||||
struct bootcount_ops *ops = bootcount_get_ops(dev);
|
||||
|
||||
assert(ops);
|
||||
if (!ops->get)
|
||||
return -ENOSYS;
|
||||
return ops->get(dev, bootcount);
|
||||
}
|
||||
|
||||
int dm_bootcount_set(struct udevice *dev, const u32 bootcount)
|
||||
{
|
||||
struct bootcount_ops *ops = bootcount_get_ops(dev);
|
||||
|
||||
assert(ops);
|
||||
if (!ops->set)
|
||||
return -ENOSYS;
|
||||
return ops->set(dev, bootcount);
|
||||
}
|
||||
|
||||
/* Now implement the generic default functions */
|
||||
void bootcount_store(ulong val)
|
||||
{
|
||||
struct udevice *dev = NULL;
|
||||
ofnode node;
|
||||
const char *propname = "u-boot,bootcount-device";
|
||||
int ret = -ENODEV;
|
||||
|
||||
/*
|
||||
* If there's a preferred bootcount device selected by the user (by
|
||||
* setting '/chosen/u-boot,bootcount-device' in the DTS), try to use
|
||||
* it if available.
|
||||
*/
|
||||
node = ofnode_get_chosen_node(propname);
|
||||
if (ofnode_valid(node))
|
||||
ret = uclass_get_device_by_ofnode(UCLASS_BOOTCOUNT, node, &dev);
|
||||
|
||||
/* If there was no user-selected device, use the first available one */
|
||||
if (ret)
|
||||
ret = uclass_get_device(UCLASS_BOOTCOUNT, 0, &dev);
|
||||
|
||||
if (dev)
|
||||
ret = dm_bootcount_set(dev, val);
|
||||
|
||||
if (ret)
|
||||
pr_debug("%s: failed to store 0x%lx\n", __func__, val);
|
||||
}
|
||||
|
||||
ulong bootcount_load(void)
|
||||
{
|
||||
struct udevice *dev = NULL;
|
||||
ofnode node;
|
||||
const char *propname = "u-boot,bootcount-device";
|
||||
int ret = -ENODEV;
|
||||
u32 val;
|
||||
|
||||
/*
|
||||
* If there's a preferred bootcount device selected by the user (by
|
||||
* setting '/chosen/u-boot,bootcount-device' in the DTS), try to use
|
||||
* it if available.
|
||||
*/
|
||||
node = ofnode_get_chosen_node(propname);
|
||||
if (ofnode_valid(node))
|
||||
ret = uclass_get_device_by_ofnode(UCLASS_BOOTCOUNT, node, &dev);
|
||||
|
||||
/* If there was no user-selected device, use the first available one */
|
||||
if (ret)
|
||||
ret = uclass_get_device(UCLASS_BOOTCOUNT, 0, &dev);
|
||||
|
||||
if (dev)
|
||||
ret = dm_bootcount_get(dev, &val);
|
||||
|
||||
if (ret)
|
||||
pr_debug("%s: failed to load bootcount\n", __func__);
|
||||
|
||||
/* Return the 0, if the call to dm_bootcount_get failed */
|
||||
return ret ? 0 : val;
|
||||
}
|
||||
|
||||
UCLASS_DRIVER(bootcount) = {
|
||||
.name = "bootcount",
|
||||
.id = UCLASS_BOOTCOUNT,
|
||||
};
|
||||
@@ -0,0 +1,51 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2010-2012
|
||||
* Stefan Roese, DENX Software Engineering, sr@denx.de.
|
||||
*/
|
||||
|
||||
#include <bootcount.h>
|
||||
#include <cpu_func.h>
|
||||
#include <linux/compiler.h>
|
||||
|
||||
/* Now implement the generic default functions */
|
||||
__weak void bootcount_store(ulong a)
|
||||
{
|
||||
void *reg = (void *)CONFIG_SYS_BOOTCOUNT_ADDR;
|
||||
uintptr_t flush_start = rounddown(CONFIG_SYS_BOOTCOUNT_ADDR,
|
||||
CONFIG_SYS_CACHELINE_SIZE);
|
||||
uintptr_t flush_end;
|
||||
|
||||
#if defined(CONFIG_SYS_BOOTCOUNT_SINGLEWORD)
|
||||
raw_bootcount_store(reg, (CONFIG_SYS_BOOTCOUNT_MAGIC & 0xffff0000) | a);
|
||||
|
||||
flush_end = roundup(CONFIG_SYS_BOOTCOUNT_ADDR + 4,
|
||||
CONFIG_SYS_CACHELINE_SIZE);
|
||||
#else
|
||||
raw_bootcount_store(reg, a);
|
||||
raw_bootcount_store(reg + 4, CONFIG_SYS_BOOTCOUNT_MAGIC);
|
||||
|
||||
flush_end = roundup(CONFIG_SYS_BOOTCOUNT_ADDR + 8,
|
||||
CONFIG_SYS_CACHELINE_SIZE);
|
||||
#endif /* defined(CONFIG_SYS_BOOTCOUNT_SINGLEWORD */
|
||||
flush_dcache_range(flush_start, flush_end);
|
||||
}
|
||||
|
||||
__weak ulong bootcount_load(void)
|
||||
{
|
||||
void *reg = (void *)CONFIG_SYS_BOOTCOUNT_ADDR;
|
||||
|
||||
#if defined(CONFIG_SYS_BOOTCOUNT_SINGLEWORD)
|
||||
u32 tmp = raw_bootcount_load(reg);
|
||||
|
||||
if ((tmp & 0xffff0000) != (CONFIG_SYS_BOOTCOUNT_MAGIC & 0xffff0000))
|
||||
return 0;
|
||||
else
|
||||
return (tmp & 0x0000ffff);
|
||||
#else
|
||||
if (raw_bootcount_load(reg + 4) != CONFIG_SYS_BOOTCOUNT_MAGIC)
|
||||
return 0;
|
||||
else
|
||||
return raw_bootcount_load(reg);
|
||||
#endif /* defined(CONFIG_SYS_BOOTCOUNT_SINGLEWORD) */
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/hardware.h>
|
||||
#include <asm/arch/at91_gpbr.h>
|
||||
|
||||
/*
|
||||
* We combine the CONFIG_SYS_BOOTCOUNT_MAGIC and bootcount in one 32-bit
|
||||
* register. This is done so we need to use only one of the four GPBR
|
||||
* registers.
|
||||
*/
|
||||
void bootcount_store(ulong a)
|
||||
{
|
||||
at91_gpbr_t *gpbr = (at91_gpbr_t *) ATMEL_BASE_GPBR;
|
||||
|
||||
writel((CONFIG_SYS_BOOTCOUNT_MAGIC & 0xffff0000) | (a & 0x0000ffff),
|
||||
&gpbr->reg[AT91_GPBR_INDEX_BOOTCOUNT]);
|
||||
}
|
||||
|
||||
ulong bootcount_load(void)
|
||||
{
|
||||
at91_gpbr_t *gpbr = (at91_gpbr_t *) ATMEL_BASE_GPBR;
|
||||
|
||||
ulong val = readl(&gpbr->reg[AT91_GPBR_INDEX_BOOTCOUNT]);
|
||||
if ((val & 0xffff0000) != (CONFIG_SYS_BOOTCOUNT_MAGIC & 0xffff0000))
|
||||
return 0;
|
||||
else
|
||||
return val & 0x0000ffff;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2011
|
||||
* Heiko Schocher, DENX Software Engineering, hs@denx.de.
|
||||
*
|
||||
* A bootcount driver for the RTC IP block found on many TI platforms.
|
||||
* This requires the RTC clocks, etc, to be enabled prior to use and
|
||||
* not all boards with this IP block on it will have the RTC in use.
|
||||
*/
|
||||
|
||||
#include <bootcount.h>
|
||||
#include <asm/davinci_rtc.h>
|
||||
|
||||
void bootcount_store(ulong a)
|
||||
{
|
||||
struct davinci_rtc *reg =
|
||||
(struct davinci_rtc *)CONFIG_SYS_BOOTCOUNT_ADDR;
|
||||
|
||||
/*
|
||||
* write RTC kick registers to enable write
|
||||
* for RTC Scratch registers. Scratch register 2 is
|
||||
* used for bootcount value.
|
||||
*/
|
||||
writel(RTC_KICK0R_WE, ®->kick0r);
|
||||
writel(RTC_KICK1R_WE, ®->kick1r);
|
||||
raw_bootcount_store(®->scratch2,
|
||||
(CONFIG_SYS_BOOTCOUNT_MAGIC & 0xffff0000) | (a & 0x0000ffff));
|
||||
}
|
||||
|
||||
ulong bootcount_load(void)
|
||||
{
|
||||
unsigned long val;
|
||||
struct davinci_rtc *reg =
|
||||
(struct davinci_rtc *)CONFIG_SYS_BOOTCOUNT_ADDR;
|
||||
|
||||
val = raw_bootcount_load(®->scratch2);
|
||||
if ((val & 0xffff0000) != (CONFIG_SYS_BOOTCOUNT_MAGIC & 0xffff0000))
|
||||
return 0;
|
||||
else
|
||||
return val & 0x0000ffff;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2013
|
||||
* Heiko Schocher, DENX Software Engineering, hs@denx.de.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <env.h>
|
||||
|
||||
void bootcount_store(ulong a)
|
||||
{
|
||||
int upgrade_available = env_get_ulong("upgrade_available", 10, 0);
|
||||
|
||||
if (upgrade_available) {
|
||||
env_set_ulong("bootcount", a);
|
||||
env_save();
|
||||
}
|
||||
}
|
||||
|
||||
ulong bootcount_load(void)
|
||||
{
|
||||
int upgrade_available = env_get_ulong("upgrade_available", 10, 0);
|
||||
ulong val = 0;
|
||||
|
||||
if (upgrade_available)
|
||||
val = env_get_ulong("bootcount", 10, 0);
|
||||
|
||||
return val;
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (c) 2017 General Electric Company. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <bootcount.h>
|
||||
#include <fs.h>
|
||||
#include <mapmem.h>
|
||||
|
||||
#define BC_MAGIC 0xbc
|
||||
|
||||
void bootcount_store(ulong a)
|
||||
{
|
||||
u8 *buf;
|
||||
loff_t len;
|
||||
int ret;
|
||||
|
||||
if (fs_set_blk_dev(CONFIG_SYS_BOOTCOUNT_EXT_INTERFACE,
|
||||
CONFIG_SYS_BOOTCOUNT_EXT_DEVPART, FS_TYPE_EXT)) {
|
||||
puts("Error selecting device\n");
|
||||
return;
|
||||
}
|
||||
|
||||
buf = map_sysmem(CONFIG_SYS_BOOTCOUNT_ADDR, 2);
|
||||
buf[0] = BC_MAGIC;
|
||||
buf[1] = (a & 0xff);
|
||||
unmap_sysmem(buf);
|
||||
|
||||
ret = fs_write(CONFIG_SYS_BOOTCOUNT_EXT_NAME,
|
||||
CONFIG_SYS_BOOTCOUNT_ADDR, 0, 2, &len);
|
||||
if (ret != 0)
|
||||
puts("Error storing bootcount\n");
|
||||
}
|
||||
|
||||
ulong bootcount_load(void)
|
||||
{
|
||||
u8 *buf;
|
||||
loff_t len_read;
|
||||
int ret;
|
||||
|
||||
if (fs_set_blk_dev(CONFIG_SYS_BOOTCOUNT_EXT_INTERFACE,
|
||||
CONFIG_SYS_BOOTCOUNT_EXT_DEVPART, FS_TYPE_EXT)) {
|
||||
puts("Error selecting device\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = fs_read(CONFIG_SYS_BOOTCOUNT_EXT_NAME, CONFIG_SYS_BOOTCOUNT_ADDR,
|
||||
0, 2, &len_read);
|
||||
if (ret != 0 || len_read != 2) {
|
||||
puts("Error loading bootcount\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
buf = map_sysmem(CONFIG_SYS_BOOTCOUNT_ADDR, 2);
|
||||
if (buf[0] == BC_MAGIC)
|
||||
ret = buf[1];
|
||||
|
||||
unmap_sysmem(buf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2013
|
||||
* Heiko Schocher, DENX Software Engineering, hs@denx.de.
|
||||
*/
|
||||
|
||||
#include <bootcount.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <i2c.h>
|
||||
|
||||
#define BC_MAGIC 0xbc
|
||||
|
||||
void bootcount_store(ulong a)
|
||||
{
|
||||
unsigned char buf[3];
|
||||
int ret;
|
||||
|
||||
buf[0] = BC_MAGIC;
|
||||
buf[1] = (a & 0xff);
|
||||
ret = i2c_write(CONFIG_SYS_I2C_RTC_ADDR, CONFIG_SYS_BOOTCOUNT_ADDR,
|
||||
CONFIG_BOOTCOUNT_ALEN, buf, 2);
|
||||
if (ret != 0)
|
||||
puts("Error writing bootcount\n");
|
||||
}
|
||||
|
||||
ulong bootcount_load(void)
|
||||
{
|
||||
unsigned char buf[3];
|
||||
int ret;
|
||||
|
||||
ret = i2c_read(CONFIG_SYS_I2C_RTC_ADDR, CONFIG_SYS_BOOTCOUNT_ADDR,
|
||||
CONFIG_BOOTCOUNT_ALEN, buf, 2);
|
||||
if (ret != 0) {
|
||||
puts("Error loading bootcount\n");
|
||||
return 0;
|
||||
}
|
||||
if (buf[0] == BC_MAGIC)
|
||||
return buf[1];
|
||||
|
||||
bootcount_store(0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2010
|
||||
* Heiko Schocher, DENX Software Engineering, hs@denx.de.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <cpu_func.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
const ulong patterns[] = { 0x00000000,
|
||||
0xFFFFFFFF,
|
||||
0xFF00FF00,
|
||||
0x0F0F0F0F,
|
||||
0xF0F0F0F0};
|
||||
const ulong NBR_OF_PATTERNS = sizeof(patterns) / sizeof(*patterns);
|
||||
const ulong OFFS_PATTERN = 3;
|
||||
const ulong REPEAT_PATTERN = 1000;
|
||||
|
||||
void bootcount_store(ulong a)
|
||||
{
|
||||
ulong *save_addr;
|
||||
ulong size = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++)
|
||||
size += gd->bd->bi_dram[i].size;
|
||||
save_addr = (ulong *)(size - BOOTCOUNT_ADDR);
|
||||
writel(a, save_addr);
|
||||
writel(CONFIG_SYS_BOOTCOUNT_MAGIC, &save_addr[1]);
|
||||
|
||||
for (i = 0; i < REPEAT_PATTERN; i++)
|
||||
writel(patterns[i % NBR_OF_PATTERNS],
|
||||
&save_addr[i + OFFS_PATTERN]);
|
||||
|
||||
/* Make sure the data is written to RAM */
|
||||
flush_dcache_range((ulong)&save_addr[0],
|
||||
(((ulong)&save_addr[REPEAT_PATTERN + OFFS_PATTERN] &
|
||||
~(ARCH_DMA_MINALIGN - 1)) + ARCH_DMA_MINALIGN));
|
||||
}
|
||||
|
||||
ulong bootcount_load(void)
|
||||
{
|
||||
ulong *save_addr;
|
||||
ulong size = 0;
|
||||
ulong counter = 0;
|
||||
int i, tmp;
|
||||
|
||||
for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++)
|
||||
size += gd->bd->bi_dram[i].size;
|
||||
save_addr = (ulong *)(size - BOOTCOUNT_ADDR);
|
||||
|
||||
counter = readl(&save_addr[0]);
|
||||
|
||||
/* Is the counter reliable, check in the big pattern for bit errors */
|
||||
for (i = 0; (i < REPEAT_PATTERN) && (counter != 0); i++) {
|
||||
tmp = readl(&save_addr[i + OFFS_PATTERN]);
|
||||
if (tmp != patterns[i % NBR_OF_PATTERNS])
|
||||
counter = 0;
|
||||
}
|
||||
return counter;
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2018 Theobroma Systems Design und Consulting GmbH
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <bootcount.h>
|
||||
#include <dm.h>
|
||||
#include <rtc.h>
|
||||
|
||||
static const u8 bootcount_magic = 0xbc;
|
||||
|
||||
struct bootcount_rtc_priv {
|
||||
struct udevice *rtc;
|
||||
u32 offset;
|
||||
};
|
||||
|
||||
static int bootcount_rtc_set(struct udevice *dev, const u32 a)
|
||||
{
|
||||
struct bootcount_rtc_priv *priv = dev_get_priv(dev);
|
||||
const u16 val = bootcount_magic << 8 | (a & 0xff);
|
||||
|
||||
if (rtc_write16(priv->rtc, priv->offset, val) < 0) {
|
||||
debug("%s: rtc_write16 failed\n", __func__);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bootcount_rtc_get(struct udevice *dev, u32 *a)
|
||||
{
|
||||
struct bootcount_rtc_priv *priv = dev_get_priv(dev);
|
||||
u16 val;
|
||||
|
||||
if (rtc_read16(priv->rtc, priv->offset, &val) < 0) {
|
||||
debug("%s: rtc_write16 failed\n", __func__);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (val >> 8 == bootcount_magic) {
|
||||
*a = val & 0xff;
|
||||
return 0;
|
||||
}
|
||||
|
||||
debug("%s: bootcount magic does not match on %04x\n", __func__, val);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int bootcount_rtc_probe(struct udevice *dev)
|
||||
{
|
||||
struct ofnode_phandle_args phandle_args;
|
||||
struct bootcount_rtc_priv *priv = dev_get_priv(dev);
|
||||
struct udevice *rtc;
|
||||
|
||||
if (dev_read_phandle_with_args(dev, "rtc", NULL, 0, 0, &phandle_args)) {
|
||||
debug("%s: rtc backing device not specified\n", dev->name);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (uclass_get_device_by_ofnode(UCLASS_RTC, phandle_args.node, &rtc)) {
|
||||
debug("%s: could not get backing device\n", dev->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
priv->rtc = rtc;
|
||||
priv->offset = dev_read_u32_default(dev, "offset", 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct bootcount_ops bootcount_rtc_ops = {
|
||||
.get = bootcount_rtc_get,
|
||||
.set = bootcount_rtc_set,
|
||||
};
|
||||
|
||||
static const struct udevice_id bootcount_rtc_ids[] = {
|
||||
{ .compatible = "u-boot,bootcount-rtc" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(bootcount_rtc) = {
|
||||
.name = "bootcount-rtc",
|
||||
.id = UCLASS_BOOTCOUNT,
|
||||
.priv_auto_alloc_size = sizeof(struct bootcount_rtc_priv),
|
||||
.probe = bootcount_rtc_probe,
|
||||
.of_match = bootcount_rtc_ids,
|
||||
.ops = &bootcount_rtc_ops,
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
#
|
||||
# Cache controllers
|
||||
#
|
||||
|
||||
menu "Cache Controller drivers"
|
||||
|
||||
config CACHE
|
||||
bool "Enable Driver Model for Cache controllers"
|
||||
depends on DM
|
||||
help
|
||||
Enable driver model for cache controllers that are found on
|
||||
most CPU's. Cache is memory that the CPU can access directly and
|
||||
is usually located on the same chip. This uclass can be used for
|
||||
configuring settings that be found from a device tree file.
|
||||
|
||||
config L2X0_CACHE
|
||||
tristate "PL310 cache driver"
|
||||
select CACHE
|
||||
depends on ARM
|
||||
help
|
||||
This driver is for the PL310 cache controller commonly found on
|
||||
ARMv7(32-bit) devices. The driver configures the cache settings
|
||||
found in the device tree.
|
||||
|
||||
config V5L2_CACHE
|
||||
bool "Andes V5L2 cache driver"
|
||||
select CACHE
|
||||
depends on RISCV_NDS_CACHE
|
||||
help
|
||||
Support Andes V5L2 cache controller in AE350 platform.
|
||||
It will configure tag and data ram timing control from the
|
||||
device tree and enable L2 cache.
|
||||
|
||||
endmenu
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
obj-$(CONFIG_CACHE) += cache-uclass.o
|
||||
obj-$(CONFIG_SANDBOX) += sandbox_cache.o
|
||||
obj-$(CONFIG_L2X0_CACHE) += cache-l2x0.o
|
||||
obj-$(CONFIG_V5L2_CACHE) += cache-v5l2.o
|
||||
@@ -0,0 +1,76 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2019 Intel Corporation <www.intel.com>
|
||||
*/
|
||||
#include <common.h>
|
||||
#include <command.h>
|
||||
#include <dm.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/pl310.h>
|
||||
|
||||
static void l2c310_of_parse_and_init(struct udevice *dev)
|
||||
{
|
||||
u32 tag[3] = { 0, 0, 0 };
|
||||
u32 saved_reg, prefetch;
|
||||
struct pl310_regs *regs = (struct pl310_regs *)dev_read_addr(dev);
|
||||
|
||||
/* Disable the L2 Cache */
|
||||
clrbits_le32(®s->pl310_ctrl, L2X0_CTRL_EN);
|
||||
|
||||
saved_reg = readl(®s->pl310_aux_ctrl);
|
||||
if (!dev_read_u32(dev, "prefetch-data", &prefetch)) {
|
||||
if (prefetch)
|
||||
saved_reg |= L310_AUX_CTRL_DATA_PREFETCH_MASK;
|
||||
else
|
||||
saved_reg &= ~L310_AUX_CTRL_DATA_PREFETCH_MASK;
|
||||
}
|
||||
|
||||
if (!dev_read_u32(dev, "prefetch-instr", &prefetch)) {
|
||||
if (prefetch)
|
||||
saved_reg |= L310_AUX_CTRL_INST_PREFETCH_MASK;
|
||||
else
|
||||
saved_reg &= ~L310_AUX_CTRL_INST_PREFETCH_MASK;
|
||||
}
|
||||
|
||||
saved_reg |= dev_read_bool(dev, "arm,shared-override");
|
||||
writel(saved_reg, ®s->pl310_aux_ctrl);
|
||||
|
||||
saved_reg = readl(®s->pl310_tag_latency_ctrl);
|
||||
if (!dev_read_u32_array(dev, "arm,tag-latency", tag, 3))
|
||||
saved_reg |= L310_LATENCY_CTRL_RD(tag[0] - 1) |
|
||||
L310_LATENCY_CTRL_WR(tag[1] - 1) |
|
||||
L310_LATENCY_CTRL_SETUP(tag[2] - 1);
|
||||
writel(saved_reg, ®s->pl310_tag_latency_ctrl);
|
||||
|
||||
saved_reg = readl(®s->pl310_data_latency_ctrl);
|
||||
if (!dev_read_u32_array(dev, "arm,data-latency", tag, 3))
|
||||
saved_reg |= L310_LATENCY_CTRL_RD(tag[0] - 1) |
|
||||
L310_LATENCY_CTRL_WR(tag[1] - 1) |
|
||||
L310_LATENCY_CTRL_SETUP(tag[2] - 1);
|
||||
writel(saved_reg, ®s->pl310_data_latency_ctrl);
|
||||
|
||||
/* Enable the L2 cache */
|
||||
setbits_le32(®s->pl310_ctrl, L2X0_CTRL_EN);
|
||||
}
|
||||
|
||||
static int l2x0_probe(struct udevice *dev)
|
||||
{
|
||||
l2c310_of_parse_and_init(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const struct udevice_id l2x0_ids[] = {
|
||||
{ .compatible = "arm,pl310-cache" },
|
||||
{}
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(pl310_cache) = {
|
||||
.name = "pl310_cache",
|
||||
.id = UCLASS_CACHE,
|
||||
.of_match = l2x0_ids,
|
||||
.probe = l2x0_probe,
|
||||
.flags = DM_FLAG_PRE_RELOC,
|
||||
};
|
||||
@@ -0,0 +1,44 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2019 Intel Corporation <www.intel.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <cache.h>
|
||||
#include <dm.h>
|
||||
|
||||
int cache_get_info(struct udevice *dev, struct cache_info *info)
|
||||
{
|
||||
struct cache_ops *ops = cache_get_ops(dev);
|
||||
|
||||
if (!ops->get_info)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->get_info(dev, info);
|
||||
}
|
||||
|
||||
int cache_enable(struct udevice *dev)
|
||||
{
|
||||
struct cache_ops *ops = cache_get_ops(dev);
|
||||
|
||||
if (!ops->enable)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->enable(dev);
|
||||
}
|
||||
|
||||
int cache_disable(struct udevice *dev)
|
||||
{
|
||||
struct cache_ops *ops = cache_get_ops(dev);
|
||||
|
||||
if (!ops->disable)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->disable(dev);
|
||||
}
|
||||
|
||||
UCLASS_DRIVER(cache) = {
|
||||
.id = UCLASS_CACHE,
|
||||
.name = "cache",
|
||||
.post_bind = dm_scan_fdt_dev,
|
||||
};
|
||||
@@ -0,0 +1,186 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2019 Andes Technology Corporation
|
||||
* Rick Chen, Andes Technology Corporation <rick@andestech.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <command.h>
|
||||
#include <cache.h>
|
||||
#include <dm.h>
|
||||
#include <asm/io.h>
|
||||
#include <dm/ofnode.h>
|
||||
|
||||
struct l2cache {
|
||||
volatile u64 configure;
|
||||
volatile u64 control;
|
||||
volatile u64 hpm0;
|
||||
volatile u64 hpm1;
|
||||
volatile u64 hpm2;
|
||||
volatile u64 hpm3;
|
||||
volatile u64 error_status;
|
||||
volatile u64 ecc_error;
|
||||
volatile u64 cctl_command0;
|
||||
volatile u64 cctl_access_line0;
|
||||
volatile u64 cctl_command1;
|
||||
volatile u64 cctl_access_line1;
|
||||
volatile u64 cctl_command2;
|
||||
volatile u64 cctl_access_line2;
|
||||
volatile u64 cctl_command3;
|
||||
volatile u64 cctl_access_line4;
|
||||
volatile u64 cctl_status;
|
||||
};
|
||||
|
||||
/* Control Register */
|
||||
#define L2_ENABLE 0x1
|
||||
/* prefetch */
|
||||
#define IPREPETCH_OFF 3
|
||||
#define DPREPETCH_OFF 5
|
||||
#define IPREPETCH_MSK (3 << IPREPETCH_OFF)
|
||||
#define DPREPETCH_MSK (3 << DPREPETCH_OFF)
|
||||
/* tag ram */
|
||||
#define TRAMOCTL_OFF 8
|
||||
#define TRAMICTL_OFF 10
|
||||
#define TRAMOCTL_MSK (3 << TRAMOCTL_OFF)
|
||||
#define TRAMICTL_MSK BIT(TRAMICTL_OFF)
|
||||
/* data ram */
|
||||
#define DRAMOCTL_OFF 11
|
||||
#define DRAMICTL_OFF 13
|
||||
#define DRAMOCTL_MSK (3 << DRAMOCTL_OFF)
|
||||
#define DRAMICTL_MSK BIT(DRAMICTL_OFF)
|
||||
|
||||
/* CCTL Command Register */
|
||||
#define CCTL_CMD_REG(base, hart) ((ulong)(base) + 0x40 + (hart) * 0x10)
|
||||
#define L2_WBINVAL_ALL 0x12
|
||||
|
||||
/* CCTL Status Register */
|
||||
#define CCTL_STATUS_MSK(hart) (0xf << ((hart) * 4))
|
||||
#define CCTL_STATUS_IDLE(hart) (0 << ((hart) * 4))
|
||||
#define CCTL_STATUS_PROCESS(hart) (1 << ((hart) * 4))
|
||||
#define CCTL_STATUS_ILLEGAL(hart) (2 << ((hart) * 4))
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
struct v5l2_plat {
|
||||
struct l2cache *regs;
|
||||
u32 iprefetch;
|
||||
u32 dprefetch;
|
||||
u32 tram_ctl[2];
|
||||
u32 dram_ctl[2];
|
||||
};
|
||||
|
||||
static int v5l2_enable(struct udevice *dev)
|
||||
{
|
||||
struct v5l2_plat *plat = dev_get_platdata(dev);
|
||||
volatile struct l2cache *regs = plat->regs;
|
||||
|
||||
if (regs)
|
||||
setbits_le32(®s->control, L2_ENABLE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int v5l2_disable(struct udevice *dev)
|
||||
{
|
||||
struct v5l2_plat *plat = dev_get_platdata(dev);
|
||||
volatile struct l2cache *regs = plat->regs;
|
||||
u8 hart = gd->arch.boot_hart;
|
||||
void __iomem *cctlcmd = (void __iomem *)CCTL_CMD_REG(regs, hart);
|
||||
|
||||
if ((regs) && (readl(®s->control) & L2_ENABLE)) {
|
||||
writel(L2_WBINVAL_ALL, cctlcmd);
|
||||
|
||||
while ((readl(®s->cctl_status) & CCTL_STATUS_MSK(hart))) {
|
||||
if ((readl(®s->cctl_status) & CCTL_STATUS_ILLEGAL(hart))) {
|
||||
printf("L2 flush illegal! hanging...");
|
||||
hang();
|
||||
}
|
||||
}
|
||||
clrbits_le32(®s->control, L2_ENABLE);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int v5l2_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct v5l2_plat *plat = dev_get_platdata(dev);
|
||||
struct l2cache *regs;
|
||||
|
||||
regs = (struct l2cache *)dev_read_addr(dev);
|
||||
plat->regs = regs;
|
||||
|
||||
plat->iprefetch = -EINVAL;
|
||||
plat->dprefetch = -EINVAL;
|
||||
plat->tram_ctl[0] = -EINVAL;
|
||||
plat->dram_ctl[0] = -EINVAL;
|
||||
|
||||
/* Instruction and data fetch prefetch depth */
|
||||
dev_read_u32(dev, "andes,inst-prefetch", &plat->iprefetch);
|
||||
dev_read_u32(dev, "andes,data-prefetch", &plat->dprefetch);
|
||||
|
||||
/* Set tag RAM and data RAM setup and output cycle */
|
||||
dev_read_u32_array(dev, "andes,tag-ram-ctl", plat->tram_ctl, 2);
|
||||
dev_read_u32_array(dev, "andes,data-ram-ctl", plat->dram_ctl, 2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int v5l2_probe(struct udevice *dev)
|
||||
{
|
||||
struct v5l2_plat *plat = dev_get_platdata(dev);
|
||||
struct l2cache *regs = plat->regs;
|
||||
u32 ctl_val;
|
||||
|
||||
ctl_val = readl(®s->control);
|
||||
|
||||
if (!(ctl_val & L2_ENABLE))
|
||||
ctl_val |= L2_ENABLE;
|
||||
|
||||
if (plat->iprefetch != -EINVAL) {
|
||||
ctl_val &= ~(IPREPETCH_MSK);
|
||||
ctl_val |= (plat->iprefetch << IPREPETCH_OFF);
|
||||
}
|
||||
|
||||
if (plat->dprefetch != -EINVAL) {
|
||||
ctl_val &= ~(DPREPETCH_MSK);
|
||||
ctl_val |= (plat->dprefetch << DPREPETCH_OFF);
|
||||
}
|
||||
|
||||
if (plat->tram_ctl[0] != -EINVAL) {
|
||||
ctl_val &= ~(TRAMOCTL_MSK | TRAMICTL_MSK);
|
||||
ctl_val |= plat->tram_ctl[0] << TRAMOCTL_OFF;
|
||||
ctl_val |= plat->tram_ctl[1] << TRAMICTL_OFF;
|
||||
}
|
||||
|
||||
if (plat->dram_ctl[0] != -EINVAL) {
|
||||
ctl_val &= ~(DRAMOCTL_MSK | DRAMICTL_MSK);
|
||||
ctl_val |= plat->dram_ctl[0] << DRAMOCTL_OFF;
|
||||
ctl_val |= plat->dram_ctl[1] << DRAMICTL_OFF;
|
||||
}
|
||||
|
||||
writel(ctl_val, ®s->control);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id v5l2_cache_ids[] = {
|
||||
{ .compatible = "v5l2cache" },
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct cache_ops v5l2_cache_ops = {
|
||||
.enable = v5l2_enable,
|
||||
.disable = v5l2_disable,
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(v5l2_cache) = {
|
||||
.name = "v5l2_cache",
|
||||
.id = UCLASS_CACHE,
|
||||
.of_match = v5l2_cache_ids,
|
||||
.ofdata_to_platdata = v5l2_ofdata_to_platdata,
|
||||
.probe = v5l2_probe,
|
||||
.platdata_auto_alloc_size = sizeof(struct v5l2_plat),
|
||||
.ops = &v5l2_cache_ops,
|
||||
.flags = DM_FLAG_PRE_RELOC,
|
||||
};
|
||||
@@ -0,0 +1,47 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2019 Intel Corporation <www.intel.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <cache.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
static int sandbox_get_info(struct udevice *dev, struct cache_info *info)
|
||||
{
|
||||
info->base = 0x11223344;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sandbox_enable(struct udevice *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snadbox_disable(struct udevice *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const struct cache_ops sandbox_cache_ops = {
|
||||
.get_info = sandbox_get_info,
|
||||
.enable = sandbox_enable,
|
||||
.disable = snadbox_disable,
|
||||
};
|
||||
|
||||
static const struct udevice_id sandbox_cache_ids[] = {
|
||||
{ .compatible = "sandbox,cache" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(cache_sandbox) = {
|
||||
.name = "cache_sandbox",
|
||||
.id = UCLASS_CACHE,
|
||||
.of_match = sandbox_cache_ids,
|
||||
.ops = &sandbox_cache_ops,
|
||||
};
|
||||
@@ -0,0 +1,179 @@
|
||||
menu "Clock"
|
||||
|
||||
config CLK
|
||||
bool "Enable clock driver support"
|
||||
depends on DM
|
||||
help
|
||||
This allows drivers to be provided for clock generators, including
|
||||
oscillators and PLLs. Devices can use a common clock API to request
|
||||
a particular clock rate and check on available clocks. Clocks can
|
||||
feed into other clocks in a tree structure, with multiplexers to
|
||||
choose the source for each clock.
|
||||
|
||||
config SPL_CLK
|
||||
bool "Enable clock support in SPL"
|
||||
depends on CLK && SPL && SPL_DM
|
||||
help
|
||||
The clock subsystem adds a small amount of overhead to the image.
|
||||
If this is acceptable and you have a need to use clock drivers in
|
||||
SPL, enable this option. It might provide a cleaner interface to
|
||||
setting up clocks within SPL, and allows the same drivers to be
|
||||
used as U-Boot proper.
|
||||
|
||||
config TPL_CLK
|
||||
bool "Enable clock support in TPL"
|
||||
depends on CLK && TPL_DM
|
||||
help
|
||||
The clock subsystem adds a small amount of overhead to the image.
|
||||
If this is acceptable and you have a need to use clock drivers in
|
||||
SPL, enable this option. It might provide a cleaner interface to
|
||||
setting up clocks within TPL, and allows the same drivers to be
|
||||
used as U-Boot proper.
|
||||
|
||||
config CLK_BCM6345
|
||||
bool "Clock controller driver for BCM6345"
|
||||
depends on CLK && ARCH_BMIPS
|
||||
default y
|
||||
help
|
||||
This clock driver adds support for enabling and disabling peripheral
|
||||
clocks on BCM6345 SoCs. HW has no rate changing capabilities.
|
||||
|
||||
config CLK_BOSTON
|
||||
def_bool y if TARGET_BOSTON
|
||||
depends on CLK
|
||||
select REGMAP
|
||||
select SYSCON
|
||||
help
|
||||
Enable this to support the clocks
|
||||
|
||||
config SPL_CLK_CCF
|
||||
bool "SPL Common Clock Framework [CCF] support "
|
||||
depends on SPL
|
||||
help
|
||||
Enable this option if you want to (re-)use the Linux kernel's Common
|
||||
Clock Framework [CCF] code in U-Boot's SPL.
|
||||
|
||||
config SPL_CLK_COMPOSITE_CCF
|
||||
bool "SPL Common Clock Framework [CCF] composite clk support "
|
||||
depends on SPL_CLK_CCF
|
||||
help
|
||||
Enable this option if you want to (re-)use the Linux kernel's Common
|
||||
Clock Framework [CCF] composite code in U-Boot's SPL.
|
||||
|
||||
config CLK_CCF
|
||||
bool "Common Clock Framework [CCF] support "
|
||||
help
|
||||
Enable this option if you want to (re-)use the Linux kernel's Common
|
||||
Clock Framework [CCF] code in U-Boot's clock driver.
|
||||
|
||||
config CLK_COMPOSITE_CCF
|
||||
bool "Common Clock Framework [CCF] composite clk support "
|
||||
depends on CLK_CCF
|
||||
help
|
||||
Enable this option if you want to (re-)use the Linux kernel's Common
|
||||
Clock Framework [CCF] composite code in U-Boot's clock driver.
|
||||
|
||||
config CLK_STM32F
|
||||
bool "Enable clock driver support for STM32F family"
|
||||
depends on CLK && (STM32F7 || STM32F4)
|
||||
default y
|
||||
help
|
||||
This clock driver adds support for RCC clock management
|
||||
for STM32F4 and STM32F7 SoCs.
|
||||
|
||||
config CLK_TI_SCI
|
||||
bool "TI System Control Interface (TI SCI) clock driver"
|
||||
depends on CLK && TI_SCI_PROTOCOL && OF_CONTROL
|
||||
help
|
||||
This enables the clock driver support over TI System Control Interface
|
||||
available on some new TI's SoCs. If you wish to use clock resources
|
||||
managed by the TI System Controller, say Y here. Otherwise, say N.
|
||||
|
||||
config CLK_HSDK
|
||||
bool "Enable cgu clock driver for HSDK"
|
||||
depends on CLK
|
||||
help
|
||||
Enable this to support the cgu clocks on Synopsys ARC HSDK
|
||||
|
||||
config CLK_VERSAL
|
||||
bool "Enable clock driver support for Versal"
|
||||
depends on ARCH_VERSAL
|
||||
select ZYNQMP_FIRMWARE
|
||||
help
|
||||
This clock driver adds support for clock realted settings for
|
||||
Versal platform.
|
||||
|
||||
config CLK_VEXPRESS_OSC
|
||||
bool "Enable driver for Arm Versatile Express OSC clock generators"
|
||||
depends on CLK && VEXPRESS_CONFIG
|
||||
help
|
||||
This clock driver adds support for clock generators present on
|
||||
Arm Versatile Express platforms.
|
||||
|
||||
config CLK_ZYNQ
|
||||
bool "Enable clock driver support for Zynq"
|
||||
depends on CLK && ARCH_ZYNQ
|
||||
default y
|
||||
help
|
||||
This clock driver adds support for clock realted settings for
|
||||
Zynq platform.
|
||||
|
||||
config CLK_ZYNQMP
|
||||
bool "Enable clock driver support for ZynqMP"
|
||||
depends on ARCH_ZYNQMP
|
||||
select ZYNQMP_FIRMWARE
|
||||
help
|
||||
This clock driver adds support for clock realted settings for
|
||||
ZynqMP platform.
|
||||
|
||||
config CLK_STM32MP1
|
||||
bool "Enable RCC clock driver for STM32MP1"
|
||||
depends on ARCH_STM32MP && CLK
|
||||
default y
|
||||
help
|
||||
Enable the STM32 clock (RCC) driver. Enable support for
|
||||
manipulating STM32MP1's on-SoC clocks.
|
||||
|
||||
config CLK_CDCE9XX
|
||||
bool "Enable CDCD9XX clock driver"
|
||||
depends on CLK
|
||||
help
|
||||
Enable the clock synthesizer driver for CDCE913/925/937/949
|
||||
series of chips.
|
||||
|
||||
source "drivers/clk/analogbits/Kconfig"
|
||||
source "drivers/clk/at91/Kconfig"
|
||||
source "drivers/clk/exynos/Kconfig"
|
||||
source "drivers/clk/imx/Kconfig"
|
||||
source "drivers/clk/meson/Kconfig"
|
||||
source "drivers/clk/mvebu/Kconfig"
|
||||
source "drivers/clk/owl/Kconfig"
|
||||
source "drivers/clk/renesas/Kconfig"
|
||||
source "drivers/clk/sunxi/Kconfig"
|
||||
source "drivers/clk/sifive/Kconfig"
|
||||
source "drivers/clk/tegra/Kconfig"
|
||||
source "drivers/clk/uniphier/Kconfig"
|
||||
|
||||
config ICS8N3QV01
|
||||
bool "Enable ICS8N3QV01 VCXO driver"
|
||||
depends on CLK
|
||||
help
|
||||
Support for the ICS8N3QV01 Quad-Frequency VCXO (Voltage-Controlled
|
||||
Crystal Oscillator). The output frequency can be programmed via an
|
||||
I2C interface.
|
||||
|
||||
config CLK_MPC83XX
|
||||
bool "Enable MPC83xx clock driver"
|
||||
depends on CLK
|
||||
help
|
||||
Support for the clock driver of the MPC83xx series of SoCs.
|
||||
|
||||
config SANDBOX_CLK_CCF
|
||||
bool "Sandbox Common Clock Framework [CCF] support "
|
||||
depends on SANDBOX
|
||||
select CLK_CCF
|
||||
help
|
||||
Enable this option if you want to test the Linux kernel's Common
|
||||
Clock Framework [CCF] code in U-Boot's Sandbox clock driver.
|
||||
|
||||
endmenu
|
||||
@@ -0,0 +1,48 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
# Copyright (c) 2015 Google, Inc
|
||||
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_$(SPL_TPL_)CLK) += clk-uclass.o
|
||||
obj-$(CONFIG_$(SPL_TPL_)CLK) += clk_fixed_rate.o
|
||||
obj-$(CONFIG_$(SPL_TPL_)CLK) += clk_fixed_factor.o
|
||||
obj-$(CONFIG_$(SPL_TPL_)CLK_CCF) += clk.o clk-divider.o clk-mux.o clk-gate.o
|
||||
obj-$(CONFIG_$(SPL_TPL_)CLK_CCF) += clk-fixed-factor.o
|
||||
obj-$(CONFIG_$(SPL_TPL_)CLK_COMPOSITE_CCF) += clk-composite.o
|
||||
|
||||
obj-y += analogbits/
|
||||
obj-y += imx/
|
||||
obj-y += tegra/
|
||||
obj-$(CONFIG_ARCH_ASPEED) += aspeed/
|
||||
obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/
|
||||
obj-$(CONFIG_ARCH_MTMIPS) += mtmips/
|
||||
obj-$(CONFIG_ARCH_MESON) += meson/
|
||||
obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
|
||||
obj-$(CONFIG_ARCH_SOCFPGA) += altera/
|
||||
obj-$(CONFIG_CLK_AT91) += at91/
|
||||
obj-$(CONFIG_CLK_MVEBU) += mvebu/
|
||||
obj-$(CONFIG_CLK_BCM6345) += clk_bcm6345.o
|
||||
obj-$(CONFIG_CLK_BOSTON) += clk_boston.o
|
||||
obj-$(CONFIG_CLK_EXYNOS) += exynos/
|
||||
obj-$(CONFIG_CLK_HSDK) += clk-hsdk-cgu.o
|
||||
obj-$(CONFIG_CLK_MPC83XX) += mpc83xx_clk.o
|
||||
obj-$(CONFIG_CLK_OWL) += owl/
|
||||
obj-$(CONFIG_CLK_RENESAS) += renesas/
|
||||
obj-$(CONFIG_CLK_SIFIVE) += sifive/
|
||||
obj-$(CONFIG_ARCH_SUNXI) += sunxi/
|
||||
obj-$(CONFIG_CLK_STM32F) += clk_stm32f.o
|
||||
obj-$(CONFIG_CLK_STM32MP1) += clk_stm32mp1.o
|
||||
obj-$(CONFIG_CLK_UNIPHIER) += uniphier/
|
||||
obj-$(CONFIG_CLK_VEXPRESS_OSC) += clk_vexpress_osc.o
|
||||
obj-$(CONFIG_CLK_ZYNQ) += clk_zynq.o
|
||||
obj-$(CONFIG_CLK_ZYNQMP) += clk_zynqmp.o
|
||||
obj-$(CONFIG_ICS8N3QV01) += ics8n3qv01.o
|
||||
obj-$(CONFIG_MACH_PIC32) += clk_pic32.o
|
||||
obj-$(CONFIG_SANDBOX) += clk_sandbox.o
|
||||
obj-$(CONFIG_SANDBOX) += clk_sandbox_test.o
|
||||
obj-$(CONFIG_SANDBOX_CLK_CCF) += clk_sandbox_ccf.o
|
||||
obj-$(CONFIG_STM32H7) += clk_stm32h7.o
|
||||
obj-$(CONFIG_CLK_TI_SCI) += clk-ti-sci.o
|
||||
obj-$(CONFIG_CLK_VERSAL) += clk_versal.o
|
||||
obj-$(CONFIG_CLK_CDCE9XX) += clk-cdce9xx.o
|
||||
@@ -0,0 +1,6 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
# Copyright (C) 2018 Marek Vasut <marex@denx.de>
|
||||
#
|
||||
|
||||
obj-$(CONFIG_TARGET_SOCFPGA_ARRIA10) += clk-arria10.o
|
||||
@@ -0,0 +1,363 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2018 Marek Vasut <marex@denx.de>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <clk-uclass.h>
|
||||
#include <dm.h>
|
||||
#include <dm/lists.h>
|
||||
#include <dm/util.h>
|
||||
|
||||
#include <asm/arch/clock_manager.h>
|
||||
|
||||
enum socfpga_a10_clk_type {
|
||||
SOCFPGA_A10_CLK_MAIN_PLL,
|
||||
SOCFPGA_A10_CLK_PER_PLL,
|
||||
SOCFPGA_A10_CLK_PERIP_CLK,
|
||||
SOCFPGA_A10_CLK_GATE_CLK,
|
||||
SOCFPGA_A10_CLK_UNKNOWN_CLK,
|
||||
};
|
||||
|
||||
struct socfpga_a10_clk_platdata {
|
||||
enum socfpga_a10_clk_type type;
|
||||
struct clk_bulk clks;
|
||||
u32 regs;
|
||||
/* Fixed divider */
|
||||
u16 fix_div;
|
||||
/* Control register */
|
||||
u16 ctl_reg;
|
||||
/* Divider register */
|
||||
u16 div_reg;
|
||||
u8 div_len;
|
||||
u8 div_off;
|
||||
/* Clock gating register */
|
||||
u16 gate_reg;
|
||||
u8 gate_bit;
|
||||
};
|
||||
|
||||
static int socfpga_a10_clk_get_upstream(struct clk *clk, struct clk **upclk)
|
||||
{
|
||||
struct socfpga_a10_clk_platdata *plat = dev_get_platdata(clk->dev);
|
||||
u32 reg, maxval;
|
||||
|
||||
if (plat->clks.count == 0)
|
||||
return 0;
|
||||
|
||||
if (plat->clks.count == 1) {
|
||||
*upclk = &plat->clks.clks[0];
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!plat->ctl_reg) {
|
||||
dev_err(clk->dev, "Invalid control register\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
reg = readl(plat->regs + plat->ctl_reg);
|
||||
|
||||
/* Assume PLLs are ON for now */
|
||||
if (plat->type == SOCFPGA_A10_CLK_MAIN_PLL) {
|
||||
reg = (reg >> 8) & 0x3;
|
||||
maxval = 2;
|
||||
} else if (plat->type == SOCFPGA_A10_CLK_PER_PLL) {
|
||||
reg = (reg >> 8) & 0x3;
|
||||
maxval = 3;
|
||||
} else {
|
||||
reg = (reg >> 16) & 0x7;
|
||||
maxval = 4;
|
||||
}
|
||||
|
||||
if (reg > maxval) {
|
||||
dev_err(clk->dev, "Invalid clock source\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*upclk = &plat->clks.clks[reg];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int socfpga_a10_clk_endisable(struct clk *clk, bool enable)
|
||||
{
|
||||
struct socfpga_a10_clk_platdata *plat = dev_get_platdata(clk->dev);
|
||||
struct clk *upclk = NULL;
|
||||
int ret;
|
||||
|
||||
if (!enable && plat->gate_reg)
|
||||
clrbits_le32(plat->regs + plat->gate_reg, BIT(plat->gate_bit));
|
||||
|
||||
ret = socfpga_a10_clk_get_upstream(clk, &upclk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (upclk) {
|
||||
if (enable)
|
||||
clk_enable(upclk);
|
||||
else
|
||||
clk_disable(upclk);
|
||||
}
|
||||
|
||||
if (enable && plat->gate_reg)
|
||||
setbits_le32(plat->regs + plat->gate_reg, BIT(plat->gate_bit));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int socfpga_a10_clk_enable(struct clk *clk)
|
||||
{
|
||||
return socfpga_a10_clk_endisable(clk, true);
|
||||
}
|
||||
|
||||
static int socfpga_a10_clk_disable(struct clk *clk)
|
||||
{
|
||||
return socfpga_a10_clk_endisable(clk, false);
|
||||
}
|
||||
|
||||
static ulong socfpga_a10_clk_get_rate(struct clk *clk)
|
||||
{
|
||||
struct socfpga_a10_clk_platdata *plat = dev_get_platdata(clk->dev);
|
||||
struct clk *upclk = NULL;
|
||||
ulong rate = 0, reg, numer, denom;
|
||||
int ret;
|
||||
|
||||
ret = socfpga_a10_clk_get_upstream(clk, &upclk);
|
||||
if (ret || !upclk)
|
||||
return 0;
|
||||
|
||||
rate = clk_get_rate(upclk);
|
||||
|
||||
if (plat->type == SOCFPGA_A10_CLK_MAIN_PLL) {
|
||||
reg = readl(plat->regs + plat->ctl_reg + 4); /* VCO1 */
|
||||
numer = reg & CLKMGR_MAINPLL_VCO1_NUMER_MSK;
|
||||
denom = (reg >> CLKMGR_MAINPLL_VCO1_DENOM_LSB) &
|
||||
CLKMGR_MAINPLL_VCO1_DENOM_MSK;
|
||||
|
||||
rate /= denom + 1;
|
||||
rate *= numer + 1;
|
||||
} else if (plat->type == SOCFPGA_A10_CLK_PER_PLL) {
|
||||
reg = readl(plat->regs + plat->ctl_reg + 4); /* VCO1 */
|
||||
numer = reg & CLKMGR_PERPLL_VCO1_NUMER_MSK;
|
||||
denom = (reg >> CLKMGR_PERPLL_VCO1_DENOM_LSB) &
|
||||
CLKMGR_PERPLL_VCO1_DENOM_MSK;
|
||||
|
||||
rate /= denom + 1;
|
||||
rate *= numer + 1;
|
||||
} else {
|
||||
rate /= plat->fix_div;
|
||||
|
||||
if (plat->fix_div == 1 && plat->ctl_reg) {
|
||||
reg = readl(plat->regs + plat->ctl_reg);
|
||||
reg &= 0x7ff;
|
||||
rate /= reg + 1;
|
||||
}
|
||||
|
||||
if (plat->div_reg) {
|
||||
reg = readl(plat->regs + plat->div_reg);
|
||||
reg >>= plat->div_off;
|
||||
reg &= (1 << plat->div_len) - 1;
|
||||
if (plat->type == SOCFPGA_A10_CLK_PERIP_CLK)
|
||||
rate /= reg + 1;
|
||||
if (plat->type == SOCFPGA_A10_CLK_GATE_CLK)
|
||||
rate /= 1 << reg;
|
||||
}
|
||||
}
|
||||
|
||||
return rate;
|
||||
}
|
||||
|
||||
static struct clk_ops socfpga_a10_clk_ops = {
|
||||
.enable = socfpga_a10_clk_enable,
|
||||
.disable = socfpga_a10_clk_disable,
|
||||
.get_rate = socfpga_a10_clk_get_rate,
|
||||
};
|
||||
|
||||
/*
|
||||
* This workaround tries to fix the massively broken generated "handoff" DT,
|
||||
* which contains duplicate clock nodes without any connection to the clock
|
||||
* manager DT node. Yet, those "handoff" DT nodes contain configuration of
|
||||
* the fixed input clock of the Arria10 which are missing from the base DT
|
||||
* for Arria10.
|
||||
*
|
||||
* This workaround sets up upstream clock for the fixed input clocks of the
|
||||
* A10 described in the base DT such that they map to the fixed clock from
|
||||
* the "handoff" DT. This does not fully match how the clock look on the
|
||||
* A10, but it is the least intrusive way to fix this mess.
|
||||
*/
|
||||
static void socfpga_a10_handoff_workaround(struct udevice *dev)
|
||||
{
|
||||
struct socfpga_a10_clk_platdata *plat = dev_get_platdata(dev);
|
||||
const void *fdt = gd->fdt_blob;
|
||||
struct clk_bulk *bulk = &plat->clks;
|
||||
int i, ret, offset = dev_of_offset(dev);
|
||||
static const char * const socfpga_a10_fixedclk_map[] = {
|
||||
"osc1", "altera_arria10_hps_eosc1",
|
||||
"cb_intosc_ls_clk", "altera_arria10_hps_cb_intosc_ls",
|
||||
"f2s_free_clk", "altera_arria10_hps_f2h_free",
|
||||
};
|
||||
|
||||
if (fdt_node_check_compatible(fdt, offset, "fixed-clock"))
|
||||
return;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(socfpga_a10_fixedclk_map); i += 2)
|
||||
if (!strcmp(dev->name, socfpga_a10_fixedclk_map[i]))
|
||||
break;
|
||||
|
||||
if (i == ARRAY_SIZE(socfpga_a10_fixedclk_map))
|
||||
return;
|
||||
|
||||
ret = uclass_get_device_by_name(UCLASS_CLK,
|
||||
socfpga_a10_fixedclk_map[i + 1], &dev);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
bulk->count = 1;
|
||||
bulk->clks = devm_kcalloc(dev, bulk->count,
|
||||
sizeof(struct clk), GFP_KERNEL);
|
||||
if (!bulk->clks)
|
||||
return;
|
||||
|
||||
ret = clk_request(dev, &bulk->clks[0]);
|
||||
if (ret)
|
||||
free(bulk->clks);
|
||||
}
|
||||
|
||||
static int socfpga_a10_clk_bind(struct udevice *dev)
|
||||
{
|
||||
const void *fdt = gd->fdt_blob;
|
||||
int offset = dev_of_offset(dev);
|
||||
bool pre_reloc_only = !(gd->flags & GD_FLG_RELOC);
|
||||
const char *name;
|
||||
int ret;
|
||||
|
||||
for (offset = fdt_first_subnode(fdt, offset);
|
||||
offset > 0;
|
||||
offset = fdt_next_subnode(fdt, offset)) {
|
||||
name = fdt_get_name(fdt, offset, NULL);
|
||||
if (!name)
|
||||
return -EINVAL;
|
||||
|
||||
if (!strcmp(name, "clocks")) {
|
||||
offset = fdt_first_subnode(fdt, offset);
|
||||
name = fdt_get_name(fdt, offset, NULL);
|
||||
if (!name)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Filter out supported sub-clock */
|
||||
if (fdt_node_check_compatible(fdt, offset,
|
||||
"altr,socfpga-a10-pll-clock") &&
|
||||
fdt_node_check_compatible(fdt, offset,
|
||||
"altr,socfpga-a10-perip-clk") &&
|
||||
fdt_node_check_compatible(fdt, offset,
|
||||
"altr,socfpga-a10-gate-clk") &&
|
||||
fdt_node_check_compatible(fdt, offset, "fixed-clock"))
|
||||
continue;
|
||||
|
||||
if (pre_reloc_only &&
|
||||
!dm_ofnode_pre_reloc(offset_to_ofnode(offset)))
|
||||
continue;
|
||||
|
||||
ret = device_bind_driver_to_node(dev, "clk-a10", name,
|
||||
offset_to_ofnode(offset),
|
||||
NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int socfpga_a10_clk_probe(struct udevice *dev)
|
||||
{
|
||||
struct socfpga_a10_clk_platdata *plat = dev_get_platdata(dev);
|
||||
const void *fdt = gd->fdt_blob;
|
||||
int offset = dev_of_offset(dev);
|
||||
|
||||
clk_get_bulk(dev, &plat->clks);
|
||||
|
||||
socfpga_a10_handoff_workaround(dev);
|
||||
|
||||
if (!fdt_node_check_compatible(fdt, offset,
|
||||
"altr,socfpga-a10-pll-clock")) {
|
||||
/* Main PLL has 3 upstream clock */
|
||||
if (plat->clks.count == 3)
|
||||
plat->type = SOCFPGA_A10_CLK_MAIN_PLL;
|
||||
else
|
||||
plat->type = SOCFPGA_A10_CLK_PER_PLL;
|
||||
} else if (!fdt_node_check_compatible(fdt, offset,
|
||||
"altr,socfpga-a10-perip-clk")) {
|
||||
plat->type = SOCFPGA_A10_CLK_PERIP_CLK;
|
||||
} else if (!fdt_node_check_compatible(fdt, offset,
|
||||
"altr,socfpga-a10-gate-clk")) {
|
||||
plat->type = SOCFPGA_A10_CLK_GATE_CLK;
|
||||
} else {
|
||||
plat->type = SOCFPGA_A10_CLK_UNKNOWN_CLK;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int socfpga_a10_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct socfpga_a10_clk_platdata *plat = dev_get_platdata(dev);
|
||||
struct socfpga_a10_clk_platdata *pplat;
|
||||
struct udevice *pdev;
|
||||
const void *fdt = gd->fdt_blob;
|
||||
unsigned int divreg[3], gatereg[2];
|
||||
int ret, offset = dev_of_offset(dev);
|
||||
u32 regs;
|
||||
|
||||
regs = dev_read_u32_default(dev, "reg", 0x0);
|
||||
|
||||
if (!fdt_node_check_compatible(fdt, offset, "altr,clk-mgr")) {
|
||||
plat->regs = devfdt_get_addr(dev);
|
||||
} else {
|
||||
pdev = dev_get_parent(dev);
|
||||
if (!pdev)
|
||||
return -ENODEV;
|
||||
|
||||
pplat = dev_get_platdata(pdev);
|
||||
if (!pplat)
|
||||
return -EINVAL;
|
||||
|
||||
plat->ctl_reg = regs;
|
||||
plat->regs = pplat->regs;
|
||||
}
|
||||
|
||||
plat->type = SOCFPGA_A10_CLK_UNKNOWN_CLK;
|
||||
|
||||
plat->fix_div = dev_read_u32_default(dev, "fixed-divider", 1);
|
||||
|
||||
ret = dev_read_u32_array(dev, "div-reg", divreg, ARRAY_SIZE(divreg));
|
||||
if (!ret) {
|
||||
plat->div_reg = divreg[0];
|
||||
plat->div_len = divreg[2];
|
||||
plat->div_off = divreg[1];
|
||||
}
|
||||
|
||||
ret = dev_read_u32_array(dev, "clk-gate", gatereg, ARRAY_SIZE(gatereg));
|
||||
if (!ret) {
|
||||
plat->gate_reg = gatereg[0];
|
||||
plat->gate_bit = gatereg[1];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id socfpga_a10_clk_match[] = {
|
||||
{ .compatible = "altr,clk-mgr" },
|
||||
{}
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(socfpga_a10_clk) = {
|
||||
.name = "clk-a10",
|
||||
.id = UCLASS_CLK,
|
||||
.of_match = socfpga_a10_clk_match,
|
||||
.ops = &socfpga_a10_clk_ops,
|
||||
.bind = socfpga_a10_clk_bind,
|
||||
.probe = socfpga_a10_clk_probe,
|
||||
.ofdata_to_platdata = socfpga_a10_ofdata_to_platdata,
|
||||
|
||||
.platdata_auto_alloc_size = sizeof(struct socfpga_a10_clk_platdata),
|
||||
};
|
||||
@@ -0,0 +1,4 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
config CLK_ANALOGBITS_WRPLL_CLN28HPC
|
||||
bool
|
||||
@@ -0,0 +1,3 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
obj-$(CONFIG_CLK_ANALOGBITS_WRPLL_CLN28HPC) += wrpll-cln28hpc.o
|
||||
@@ -0,0 +1,364 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2018-2019 SiFive, Inc.
|
||||
* Wesley Terpstra
|
||||
* Paul Walmsley
|
||||
*
|
||||
* This library supports configuration parsing and reprogramming of
|
||||
* the CLN28HPC variant of the Analog Bits Wide Range PLL. The
|
||||
* intention is for this library to be reusable for any device that
|
||||
* integrates this PLL; thus the register structure and programming
|
||||
* details are expected to be provided by a separate IP block driver.
|
||||
*
|
||||
* The bulk of this code is primarily useful for clock configurations
|
||||
* that must operate at arbitrary rates, as opposed to clock configurations
|
||||
* that are restricted by software or manufacturer guidance to a small,
|
||||
* pre-determined set of performance points.
|
||||
*
|
||||
* References:
|
||||
* - Analog Bits "Wide Range PLL Datasheet", version 2015.10.01
|
||||
* - SiFive FU540-C000 Manual v1p0, Chapter 7 "Clocking and Reset"
|
||||
* https://static.dev.sifive.com/FU540-C000-v1.0.pdf
|
||||
*/
|
||||
|
||||
#include <linux/bug.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/clk/analogbits-wrpll-cln28hpc.h>
|
||||
|
||||
/* MIN_INPUT_FREQ: minimum input clock frequency, in Hz (Fref_min) */
|
||||
#define MIN_INPUT_FREQ 7000000
|
||||
|
||||
/* MAX_INPUT_FREQ: maximum input clock frequency, in Hz (Fref_max) */
|
||||
#define MAX_INPUT_FREQ 600000000
|
||||
|
||||
/* MIN_POST_DIVIDE_REF_FREQ: minimum post-divider reference frequency, in Hz */
|
||||
#define MIN_POST_DIVR_FREQ 7000000
|
||||
|
||||
/* MAX_POST_DIVIDE_REF_FREQ: maximum post-divider reference frequency, in Hz */
|
||||
#define MAX_POST_DIVR_FREQ 200000000
|
||||
|
||||
/* MIN_VCO_FREQ: minimum VCO frequency, in Hz (Fvco_min) */
|
||||
#define MIN_VCO_FREQ 2400000000UL
|
||||
|
||||
/* MAX_VCO_FREQ: maximum VCO frequency, in Hz (Fvco_max) */
|
||||
#define MAX_VCO_FREQ 4800000000ULL
|
||||
|
||||
/* MAX_DIVQ_DIVISOR: maximum output divisor. Selected by DIVQ = 6 */
|
||||
#define MAX_DIVQ_DIVISOR 64
|
||||
|
||||
/* MAX_DIVR_DIVISOR: maximum reference divisor. Selected by DIVR = 63 */
|
||||
#define MAX_DIVR_DIVISOR 64
|
||||
|
||||
/* MAX_LOCK_US: maximum PLL lock time, in microseconds (tLOCK_max) */
|
||||
#define MAX_LOCK_US 70
|
||||
|
||||
/*
|
||||
* ROUND_SHIFT: number of bits to shift to avoid precision loss in the rounding
|
||||
* algorithm
|
||||
*/
|
||||
#define ROUND_SHIFT 20
|
||||
|
||||
/*
|
||||
* Private functions
|
||||
*/
|
||||
|
||||
/**
|
||||
* __wrpll_calc_filter_range() - determine PLL loop filter bandwidth
|
||||
* @post_divr_freq: input clock rate after the R divider
|
||||
*
|
||||
* Select the value to be presented to the PLL RANGE input signals, based
|
||||
* on the input clock frequency after the post-R-divider @post_divr_freq.
|
||||
* This code follows the recommendations in the PLL datasheet for filter
|
||||
* range selection.
|
||||
*
|
||||
* Return: The RANGE value to be presented to the PLL configuration inputs,
|
||||
* or a negative return code upon error.
|
||||
*/
|
||||
static int __wrpll_calc_filter_range(unsigned long post_divr_freq)
|
||||
{
|
||||
if (post_divr_freq < MIN_POST_DIVR_FREQ ||
|
||||
post_divr_freq > MAX_POST_DIVR_FREQ) {
|
||||
WARN(1, "%s: post-divider reference freq out of range: %lu",
|
||||
__func__, post_divr_freq);
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
switch (post_divr_freq) {
|
||||
case 0 ... 10999999:
|
||||
return 1;
|
||||
case 11000000 ... 17999999:
|
||||
return 2;
|
||||
case 18000000 ... 29999999:
|
||||
return 3;
|
||||
case 30000000 ... 49999999:
|
||||
return 4;
|
||||
case 50000000 ... 79999999:
|
||||
return 5;
|
||||
case 80000000 ... 129999999:
|
||||
return 6;
|
||||
}
|
||||
|
||||
return 7;
|
||||
}
|
||||
|
||||
/**
|
||||
* __wrpll_calc_fbdiv() - return feedback fixed divide value
|
||||
* @c: ptr to a struct wrpll_cfg record to read from
|
||||
*
|
||||
* The internal feedback path includes a fixed by-two divider; the
|
||||
* external feedback path does not. Return the appropriate divider
|
||||
* value (2 or 1) depending on whether internal or external feedback
|
||||
* is enabled. This code doesn't test for invalid configurations
|
||||
* (e.g. both or neither of WRPLL_FLAGS_*_FEEDBACK are set); it relies
|
||||
* on the caller to do so.
|
||||
*
|
||||
* Context: Any context. Caller must protect the memory pointed to by
|
||||
* @c from simultaneous modification.
|
||||
*
|
||||
* Return: 2 if internal feedback is enabled or 1 if external feedback
|
||||
* is enabled.
|
||||
*/
|
||||
static u8 __wrpll_calc_fbdiv(const struct wrpll_cfg *c)
|
||||
{
|
||||
return (c->flags & WRPLL_FLAGS_INT_FEEDBACK_MASK) ? 2 : 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* __wrpll_calc_divq() - determine DIVQ based on target PLL output clock rate
|
||||
* @target_rate: target PLL output clock rate
|
||||
* @vco_rate: pointer to a u64 to store the computed VCO rate into
|
||||
*
|
||||
* Determine a reasonable value for the PLL Q post-divider, based on the
|
||||
* target output rate @target_rate for the PLL. Along with returning the
|
||||
* computed Q divider value as the return value, this function stores the
|
||||
* desired target VCO rate into the variable pointed to by @vco_rate.
|
||||
*
|
||||
* Context: Any context. Caller must protect the memory pointed to by
|
||||
* @vco_rate from simultaneous access or modification.
|
||||
*
|
||||
* Return: a positive integer DIVQ value to be programmed into the hardware
|
||||
* upon success, or 0 upon error (since 0 is an invalid DIVQ value)
|
||||
*/
|
||||
static u8 __wrpll_calc_divq(u32 target_rate, u64 *vco_rate)
|
||||
{
|
||||
u64 s;
|
||||
u8 divq = 0;
|
||||
|
||||
if (!vco_rate) {
|
||||
WARN_ON(1);
|
||||
goto wcd_out;
|
||||
}
|
||||
|
||||
s = div_u64(MAX_VCO_FREQ, target_rate);
|
||||
if (s <= 1) {
|
||||
divq = 1;
|
||||
*vco_rate = MAX_VCO_FREQ;
|
||||
} else if (s > MAX_DIVQ_DIVISOR) {
|
||||
divq = ilog2(MAX_DIVQ_DIVISOR);
|
||||
*vco_rate = MIN_VCO_FREQ;
|
||||
} else {
|
||||
divq = ilog2(s);
|
||||
*vco_rate = (u64)target_rate << divq;
|
||||
}
|
||||
|
||||
wcd_out:
|
||||
return divq;
|
||||
}
|
||||
|
||||
/**
|
||||
* __wrpll_update_parent_rate() - update PLL data when parent rate changes
|
||||
* @c: ptr to a struct wrpll_cfg record to write PLL data to
|
||||
* @parent_rate: PLL input refclk rate (pre-R-divider)
|
||||
*
|
||||
* Pre-compute some data used by the PLL configuration algorithm when
|
||||
* the PLL's reference clock rate changes. The intention is to avoid
|
||||
* computation when the parent rate remains constant - expected to be
|
||||
* the common case.
|
||||
*
|
||||
* Returns: 0 upon success or -ERANGE if the reference clock rate is
|
||||
* out of range.
|
||||
*/
|
||||
static int __wrpll_update_parent_rate(struct wrpll_cfg *c,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
u8 max_r_for_parent;
|
||||
|
||||
if (parent_rate > MAX_INPUT_FREQ || parent_rate < MIN_POST_DIVR_FREQ)
|
||||
return -ERANGE;
|
||||
|
||||
c->parent_rate = parent_rate;
|
||||
max_r_for_parent = div_u64(parent_rate, MIN_POST_DIVR_FREQ);
|
||||
c->max_r = min_t(u8, MAX_DIVR_DIVISOR, max_r_for_parent);
|
||||
|
||||
c->init_r = DIV_ROUND_UP_ULL(parent_rate, MAX_POST_DIVR_FREQ);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* wrpll_configure() - compute PLL configuration for a target rate
|
||||
* @c: ptr to a struct wrpll_cfg record to write into
|
||||
* @target_rate: target PLL output clock rate (post-Q-divider)
|
||||
* @parent_rate: PLL input refclk rate (pre-R-divider)
|
||||
*
|
||||
* Compute the appropriate PLL signal configuration values and store
|
||||
* in PLL context @c. PLL reprogramming is not glitchless, so the
|
||||
* caller should switch any downstream logic to a different clock
|
||||
* source or clock-gate it before presenting these values to the PLL
|
||||
* configuration signals.
|
||||
*
|
||||
* The caller must pass this function a pre-initialized struct
|
||||
* wrpll_cfg record: either initialized to zero (with the
|
||||
* exception of the .name and .flags fields) or read from the PLL.
|
||||
*
|
||||
* Context: Any context. Caller must protect the memory pointed to by @c
|
||||
* from simultaneous access or modification.
|
||||
*
|
||||
* Return: 0 upon success; anything else upon failure.
|
||||
*/
|
||||
int wrpll_configure_for_rate(struct wrpll_cfg *c, u32 target_rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
unsigned long ratio;
|
||||
u64 target_vco_rate, delta, best_delta, f_pre_div, vco, vco_pre;
|
||||
u32 best_f, f, post_divr_freq;
|
||||
u8 fbdiv, divq, best_r, r;
|
||||
int range;
|
||||
|
||||
if (c->flags == 0) {
|
||||
WARN(1, "%s called with uninitialized PLL config", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Initialize rounding data if it hasn't been initialized already */
|
||||
if (parent_rate != c->parent_rate) {
|
||||
if (__wrpll_update_parent_rate(c, parent_rate)) {
|
||||
pr_err("%s: PLL input rate is out of range\n",
|
||||
__func__);
|
||||
return -ERANGE;
|
||||
}
|
||||
}
|
||||
|
||||
c->flags &= ~WRPLL_FLAGS_RESET_MASK;
|
||||
|
||||
/* Put the PLL into bypass if the user requests the parent clock rate */
|
||||
if (target_rate == parent_rate) {
|
||||
c->flags |= WRPLL_FLAGS_BYPASS_MASK;
|
||||
return 0;
|
||||
}
|
||||
|
||||
c->flags &= ~WRPLL_FLAGS_BYPASS_MASK;
|
||||
|
||||
/* Calculate the Q shift and target VCO rate */
|
||||
divq = __wrpll_calc_divq(target_rate, &target_vco_rate);
|
||||
if (!divq)
|
||||
return -1;
|
||||
c->divq = divq;
|
||||
|
||||
/* Precalculate the pre-Q divider target ratio */
|
||||
ratio = div64_u64((target_vco_rate << ROUND_SHIFT), parent_rate);
|
||||
|
||||
fbdiv = __wrpll_calc_fbdiv(c);
|
||||
best_r = 0;
|
||||
best_f = 0;
|
||||
best_delta = MAX_VCO_FREQ;
|
||||
|
||||
/*
|
||||
* Consider all values for R which land within
|
||||
* [MIN_POST_DIVR_FREQ, MAX_POST_DIVR_FREQ]; prefer smaller R
|
||||
*/
|
||||
for (r = c->init_r; r <= c->max_r; ++r) {
|
||||
f_pre_div = ratio * r;
|
||||
f = (f_pre_div + (1 << ROUND_SHIFT)) >> ROUND_SHIFT;
|
||||
f >>= (fbdiv - 1);
|
||||
|
||||
post_divr_freq = div_u64(parent_rate, r);
|
||||
vco_pre = fbdiv * post_divr_freq;
|
||||
vco = vco_pre * f;
|
||||
|
||||
/* Ensure rounding didn't take us out of range */
|
||||
if (vco > target_vco_rate) {
|
||||
--f;
|
||||
vco = vco_pre * f;
|
||||
} else if (vco < MIN_VCO_FREQ) {
|
||||
++f;
|
||||
vco = vco_pre * f;
|
||||
}
|
||||
|
||||
delta = abs(target_rate - vco);
|
||||
if (delta < best_delta) {
|
||||
best_delta = delta;
|
||||
best_r = r;
|
||||
best_f = f;
|
||||
}
|
||||
}
|
||||
|
||||
c->divr = best_r - 1;
|
||||
c->divf = best_f - 1;
|
||||
|
||||
post_divr_freq = div_u64(parent_rate, best_r);
|
||||
|
||||
/* Pick the best PLL jitter filter */
|
||||
range = __wrpll_calc_filter_range(post_divr_freq);
|
||||
if (range < 0)
|
||||
return range;
|
||||
c->range = range;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* wrpll_calc_output_rate() - calculate the PLL's target output rate
|
||||
* @c: ptr to a struct wrpll_cfg record to read from
|
||||
* @parent_rate: PLL refclk rate
|
||||
*
|
||||
* Given a pointer to the PLL's current input configuration @c and the
|
||||
* PLL's input reference clock rate @parent_rate (before the R
|
||||
* pre-divider), calculate the PLL's output clock rate (after the Q
|
||||
* post-divider).
|
||||
*
|
||||
* Context: Any context. Caller must protect the memory pointed to by @c
|
||||
* from simultaneous modification.
|
||||
*
|
||||
* Return: the PLL's output clock rate, in Hz. The return value from
|
||||
* this function is intended to be convenient to pass directly
|
||||
* to the Linux clock framework; thus there is no explicit
|
||||
* error return value.
|
||||
*/
|
||||
unsigned long wrpll_calc_output_rate(const struct wrpll_cfg *c,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
u8 fbdiv;
|
||||
u64 n;
|
||||
|
||||
if (c->flags & WRPLL_FLAGS_EXT_FEEDBACK_MASK) {
|
||||
WARN(1, "external feedback mode not yet supported");
|
||||
return ULONG_MAX;
|
||||
}
|
||||
|
||||
fbdiv = __wrpll_calc_fbdiv(c);
|
||||
n = parent_rate * fbdiv * (c->divf + 1);
|
||||
n = div_u64(n, c->divr + 1);
|
||||
n >>= c->divq;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* wrpll_calc_max_lock_us() - return the time for the PLL to lock
|
||||
* @c: ptr to a struct wrpll_cfg record to read from
|
||||
*
|
||||
* Return the minimum amount of time (in microseconds) that the caller
|
||||
* must wait after reprogramming the PLL to ensure that it is locked
|
||||
* to the input frequency and stable. This is likely to depend on the DIVR
|
||||
* value; this is under discussion with the manufacturer.
|
||||
*
|
||||
* Return: the minimum amount of time the caller must wait for the PLL
|
||||
* to lock (in microseconds)
|
||||
*/
|
||||
unsigned int wrpll_calc_max_lock_us(const struct wrpll_cfg *c)
|
||||
{
|
||||
return MAX_LOCK_US;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user