Import U-Boot v2026.07 (unmodified upstream source, base for rtwo patches)

This commit is contained in:
2026-09-16 23:15:56 -03:00
commit 391981275c
38022 changed files with 8370285 additions and 0 deletions
+174
View File
@@ -0,0 +1,174 @@
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/bios_emulator/Kconfig"
source "drivers/bus/Kconfig"
source "drivers/block/Kconfig"
source "drivers/bootcount/Kconfig"
source "drivers/button/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/ddr/fsl/Kconfig"
source "drivers/dfu/Kconfig"
source "drivers/dma/Kconfig"
source "drivers/extcon/Kconfig"
source "drivers/fastboot/Kconfig"
source "drivers/firmware/Kconfig"
source "drivers/fuzz/Kconfig"
source "drivers/fpga/Kconfig"
source "drivers/fwu-mdata/Kconfig"
source "drivers/gpio/Kconfig"
source "drivers/hwspinlock/Kconfig"
source "drivers/i2c/Kconfig"
source "drivers/i3c/Kconfig"
source "drivers/input/Kconfig"
source "drivers/interconnect/Kconfig"
source "drivers/iommu/Kconfig"
source "drivers/led/Kconfig"
source "drivers/mailbox/Kconfig"
source "drivers/memory/Kconfig"
source "drivers/mfd/Kconfig"
source "drivers/misc/Kconfig"
source "drivers/mmc/Kconfig"
source "drivers/mtd/Kconfig"
source "drivers/mux/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/phy/socionext/Kconfig"
source "drivers/pinctrl/Kconfig"
source "drivers/power/Kconfig"
source "drivers/pwm/Kconfig"
source "drivers/qe/Kconfig"
source "drivers/ram/Kconfig"
source "drivers/reboot-mode/Kconfig"
source "drivers/remoteproc/Kconfig"
source "drivers/reset/Kconfig"
source "drivers/rng/Kconfig"
source "drivers/rtc/Kconfig"
source "drivers/scsi/Kconfig"
source "drivers/serial/Kconfig"
source "drivers/sm/Kconfig"
source "drivers/smem/Kconfig"
source "drivers/sound/Kconfig"
source "drivers/soc/Kconfig"
source "drivers/spi/Kconfig"
source "drivers/spmi/Kconfig"
source "drivers/sysinfo/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"
source "drivers/xen/Kconfig"
config PHYS_TO_BUS
bool "Custom physical to bus address mapping"
depends on !COMPILE_TEST
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
+137
View File
@@ -0,0 +1,137 @@
# SPDX-License-Identifier: GPL-2.0+
obj-$(CONFIG_$(PHASE_)ADC) += adc/
obj-$(CONFIG_$(PHASE_)BIOSEMU) += bios_emulator/
obj-$(CONFIG_$(PHASE_)BLK) += block/
obj-$(CONFIG_$(PHASE_)BOOTCOUNT_LIMIT) += bootcount/
obj-$(CONFIG_$(PHASE_)BUTTON) += button/
obj-$(CONFIG_$(PHASE_)CACHE) += cache/
obj-$(CONFIG_$(PHASE_)CLK) += clk/
obj-$(CONFIG_$(PHASE_)DM) += core/
obj-$(CONFIG_$(PHASE_)DMA) += dma/
obj-$(CONFIG_$(PHASE_)DMA_LEGACY) += dma/
obj-$(CONFIG_$(PHASE_)DFU) += dfu/
obj-$(CONFIG_$(PHASE_)EXTCON) += extcon/
obj-$(CONFIG_$(PHASE_)GPIO) += gpio/
obj-$(CONFIG_$(PHASE_)DRIVERS_MISC) += misc/
obj-$(CONFIG_$(PHASE_)SYSRESET) += sysreset/
obj-$(CONFIG_$(PHASE_)FIRMWARE) +=firmware/
obj-$(CONFIG_$(PHASE_)I2C) += i2c/
obj-$(CONFIG_$(PHASE_)I3C) += i3c/
obj-$(CONFIG_$(PHASE_)INPUT) += input/
obj-$(CONFIG_$(PHASE_)INTERCONNECT) += interconnect/
obj-$(CONFIG_$(PHASE_)LED) += led/
obj-$(CONFIG_$(PHASE_)MMC) += mmc/
obj-y += mtd/
obj-$(CONFIG_MULTIPLEXER) += mux/
obj-$(CONFIG_$(PHASE_)ETH) += net/
obj-$(CONFIG_$(PHASE_)PCH) += pch/
obj-$(CONFIG_$(PHASE_)PCI) += pci/
obj-$(CONFIG_$(PHASE_)PCI_ENDPOINT) += pci_endpoint/
obj-$(CONFIG_$(PHASE_)PHY) += phy/
obj-$(CONFIG_$(PHASE_)PINCTRL) += pinctrl/
obj-$(CONFIG_$(PHASE_)POWER) += power/
obj-$(CONFIG_$(PHASE_)RAM) += ram/
obj-$(CONFIG_$(PHASE_)RTC) += rtc/
obj-$(CONFIG_$(PHASE_)SERIAL) += serial/
obj-$(CONFIG_$(PHASE_)SPI) += spi/
obj-$(CONFIG_$(PHASE_)TIMER) += timer/
obj-$(CONFIG_$(PHASE_)VIRTIO) += virtio/
obj-$(CONFIG_$(PHASE_)DM_MAILBOX) += mailbox/
obj-$(CONFIG_$(PHASE_)REMOTEPROC) += remoteproc/
obj-$(CONFIG_$(PHASE_)SYSINFO) += sysinfo/
obj-$(CONFIG_$(PHASE_)SM) += sm/
obj-$(CONFIG_$(PHASE_)TPM) += tpm/
obj-$(CONFIG_$(PHASE_)NVME) += nvme/
obj-$(CONFIG_XEN) += xen/
obj-$(CONFIG_$(PHASE_)FPGA) += fpga/
obj-$(CONFIG_$(PHASE_)VIDEO) += video/
obj-y += bus/
ifndef CONFIG_TPL_BUILD
ifndef CONFIG_VPL_BUILD
ifdef CONFIG_XPL_BUILD
obj-$(CONFIG_SPL_CPU) += cpu/
obj-$(CONFIG_SPL_CRYPTO) += crypto/
obj-$(CONFIG_SPL_MPC8XXX_INIT_DDR) += ddr/fsl/
obj-$(CONFIG_ARMADA_38X) += ddr/marvell/a38x/
obj-$(CONFIG_ARMADA_XP) += ddr/marvell/axp/
obj-$(CONFIG_$(PHASE_)ALTERA_SDRAM) += ddr/altera/
obj-$(CONFIG_ARCH_IMX8M) += ddr/imx/imx8m/
obj-$(CONFIG_IMX8ULP_DRAM) += ddr/imx/imx8ulp/
obj-$(CONFIG_ARCH_IMX9) += ddr/imx/imx9/
obj-$(CONFIG_DRAM_SUN20I_D1) += ram/
obj-$(CONFIG_SPL_DM_RESET) += reset/
obj-$(CONFIG_SPL_MUSB_NEW) += 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) += watchdog/
obj-$(CONFIG_SPL_USB_HOST) += usb/host/
obj-$(CONFIG_SPL_SATA) += ata/ scsi/
obj-$(CONFIG_SPL_LEGACY_BLOCK) += block/
obj-$(CONFIG_SPL_THERMAL) += thermal/
obj-$(CONFIG_SPL_UFS_SUPPORT) += scsi/ ufs/
endif
endif
endif
ifdef CONFIG_TPL_BUILD
obj-$(CONFIG_TPL_MPC8XXX_INIT_DDR) += ddr/fsl/
endif
ifeq ($(CONFIG_XPL_BUILD)$(CONFIG_TPL_BUILD),)
obj-y += ata/
obj-$(CONFIG_DM_DEMO) += demo/
obj-y += block/
obj-y += cache/
obj-$(CONFIG_CPU) += cpu/
obj-y += crypto/
obj-$(CONFIG_FASTBOOT) += fastboot/
obj-$(CONFIG_FWU_MDATA) += fwu-mdata/
obj-y += misc/
obj-$(CONFIG_MMC) += mmc/
obj-$(CONFIG_MULTIPLEXER) += mux/
obj-$(CONFIG_NVME) += nvme/
obj-$(CONFIG_PCI_ENDPOINT) += pci_endpoint/
obj-y += dfu/
obj-$(CONFIG_PCH) += pch/
obj-$(CONFIG_DM_REBOOT_MODE) += reboot-mode/
obj-y += rtc/
obj-y += scsi/
obj-y += sound/
obj-y += spmi/
obj-y += watchdog/
obj-$(CONFIG_QE) += qe/
obj-$(CONFIG_U_QE) += qe/
obj-y += mailbox/
obj-y += memory/
obj-y += mfd/
obj-y += mtd/
obj-y += pwm/
obj-y += reset/
obj-y += input/
obj-y += iommu/
# SOC specific infrastructure drivers.
obj-y += smem/
obj-y += thermal/
obj-$(CONFIG_TEE) += tee/
obj-$(CONFIG_ARM_FFA_TRANSPORT) += firmware/arm-ffa/
obj-y += axi/
obj-y += ufs/
obj-$(CONFIG_W1) += w1/
obj-$(CONFIG_W1_EEPROM) += w1-eeprom/
obj-$(CONFIG_MACH_PIC32) += ddr/microchip/
obj-$(CONFIG_FUZZ) += fuzz/
obj-$(CONFIG_DM_HWSPINLOCK) += hwspinlock/
obj-$(CONFIG_$(PHASE_)DM_RNG) += rng/
endif
obj-y += soc/
+83
View File
@@ -0,0 +1,83 @@
config ADC
bool "Enable ADC drivers using Driver Model"
depends on DM
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 SPL_ADC
bool "Enable ADC drivers using Driver Model in SPL"
depends on SPL_DM
config ADC_EXYNOS
bool "Enable Exynos 54xx ADC driver"
depends on ADC && ARCH_EXYNOS5
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"
depends on ADC
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"
depends on ADC
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"
depends on ADC && ARCH_ROCKCHIP
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)
config ADC_IMX93
bool "Enable NXP IMX93 ADC driver"
depends on ADC
help
This enables basic driver for NXP IMX93 ADC.
It provides:
- 4 analog input channels
- 12-bit resolution
+13
View File
@@ -0,0 +1,13 @@
# SPDX-License-Identifier: GPL-2.0+
#
# Copyright (C) 2015 Samsung Electronics
# Przemyslaw Marczak <p.marczak@samsung.com>
#
obj-$(CONFIG_$(PHASE_)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
obj-$(CONFIG_ADC_IMX93) += imx93-adc.o
+441
View File
@@ -0,0 +1,441 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2015 Samsung Electronics
* Przemyslaw Marczak <p.marczak@samsung.com>
*/
#define LOG_CATEGORY UCLASS_ADC
#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 <linux/delay.h>
#include <linux/printk.h>
#include <power/regulator.h>
#define ADC_UCLASS_PLATDATA_SIZE sizeof(struct adc_uclass_plat)
#define CHECK_NUMBER true
#define CHECK_MASK (!CHECK_NUMBER)
/* TODO: add support for timer uclass (for early calls) */
#ifdef CONFIG_SANDBOX
#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_plat *uc_pdata = dev_get_uclass_plat(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_plat *uc_pdata = dev_get_uclass_plat(dev);
int ret;
ret = regulator_set_enable_if_allowed(uc_pdata->vdd_supply, true);
if (ret && ret != -ENOSYS) {
pr_err("%s: can't enable vdd-supply!", dev->name);
return ret;
}
ret = regulator_set_enable_if_allowed(uc_pdata->vss_supply, true);
if (ret && ret != -ENOSYS) {
pr_err("%s: can't enable vss-supply!", dev->name);
return ret;
}
return 0;
}
int adc_data_mask(struct udevice *dev, unsigned int *data_mask)
{
struct adc_uclass_plat *uc_pdata = dev_get_uclass_plat(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_plat *uc_pdata = dev_get_uclass_plat(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_plat *uc_pdata = dev_get_uclass_plat(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_plat *uc_pdata = dev_get_uclass_plat(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_plat_update(struct udevice *dev)
{
struct adc_uclass_plat *uc_pdata = dev_get_uclass_plat(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_plat_update(struct udevice *dev)
{
struct adc_uclass_plat *uc_pdata = dev_get_uclass_plat(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_plat *uc_pdata = dev_get_uclass_plat(dev);
int ret, value_sign = uc_pdata->vdd_polarity_negative ? -1 : 1;
/* Update the regulator Value. */
ret = adc_vdd_plat_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_plat *uc_pdata = dev_get_uclass_plat(dev);
int ret, value_sign = uc_pdata->vss_polarity_negative ? -1 : 1;
/* Update the regulator Value. */
ret = adc_vss_plat_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_plat_set(struct udevice *dev)
{
struct adc_uclass_plat *uc_pdata = dev_get_uclass_plat(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_plat_update(dev);
if (ret != -ENOSYS && 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_plat_set(struct udevice *dev)
{
struct adc_uclass_plat *uc_pdata = dev_get_uclass_plat(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_plat_update(dev);
if (ret != -ENOSYS && 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 plat: polarity, uV, regulator (phandle). */
ret = adc_vdd_plat_set(dev);
if (ret)
pr_err("%s: Can't update Vdd. Error: %d", dev->name, ret);
/* Set ADC VSS plat: polarity, uV, regulator (phandle). */
ret = adc_vss_plat_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_plat_auto = ADC_UCLASS_PLATDATA_SIZE,
};
+144
View File
@@ -0,0 +1,144 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2015 Samsung Electronics
* Przemyslaw Marczak <p.marczak@samsung.com>
*/
#include <errno.h>
#include <dm.h>
#include <adc.h>
#include <asm/arch/adc.h>
#include <linux/printk.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(&regs->status)) != FLAG_CONV_END)
return -EBUSY;
*data = readl(&regs->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(&regs->con2);
cfg &= ~ADC_V2_CON2_CHAN_SEL_MASK;
cfg |= ADC_V2_CON2_CHAN_SEL(channel);
writel(cfg, &regs->con2);
/* Start conversion */
cfg = readl(&regs->con1);
writel(cfg | ADC_V2_CON1_STC_EN, &regs->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(&regs->con1);
cfg &= ~ADC_V2_CON1_STC_EN;
writel(cfg, &regs->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(&regs->version) != ADC_V2_VERSION) {
pr_err("This driver supports only ADC v2!");
return -ENXIO;
}
/* ADC Reset */
writel(ADC_V2_CON1_SOFT_RESET, &regs->con1);
/* Disable INT - will read status only */
writel(0x0, &regs->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, &regs->con2);
priv->active_channel = -1;
return 0;
}
int exynos_adc_of_to_plat(struct udevice *dev)
{
struct adc_uclass_plat *uc_pdata = dev_get_uclass_plat(dev);
struct exynos_adc_priv *priv = dev_get_priv(dev);
priv->regs = dev_read_addr_ptr(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,
.of_to_plat = exynos_adc_of_to_plat,
.priv_auto = sizeof(struct exynos_adc_priv),
};
+289
View File
@@ -0,0 +1,289 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2023 ASEM Srl
* Author: Luca Ellero <l.ellero@asem.it>
*
* Originally based on NXP linux-imx kernel v5.15 drivers/iio/adc/imx93_adc.c
*/
#include <errno.h>
#include <dm.h>
#include <linux/bitfield.h>
#include <linux/iopoll.h>
#include <clk.h>
#include <adc.h>
#define IMX93_ADC_MCR 0x00
#define IMX93_ADC_MSR 0x04
#define IMX93_ADC_ISR 0x10
#define IMX93_ADC_IMR 0x20
#define IMX93_ADC_CIMR0 0x24
#define IMX93_ADC_CTR0 0x94
#define IMX93_ADC_NCMR0 0xA4
#define IMX93_ADC_PCDR0 0x100
#define IMX93_ADC_PCDR1 0x104
#define IMX93_ADC_PCDR2 0x108
#define IMX93_ADC_PCDR3 0x10c
#define IMX93_ADC_PCDR4 0x110
#define IMX93_ADC_PCDR5 0x114
#define IMX93_ADC_PCDR6 0x118
#define IMX93_ADC_PCDR7 0x11c
#define IMX93_ADC_CALSTAT 0x39C
#define IMX93_ADC_MCR_MODE_MASK BIT(29)
#define IMX93_ADC_MCR_NSTART_MASK BIT(24)
#define IMX93_ADC_MCR_CALSTART_MASK BIT(14)
#define IMX93_ADC_MCR_ADCLKSE_MASK BIT(8)
#define IMX93_ADC_MCR_PWDN_MASK BIT(0)
#define IMX93_ADC_MSR_CALFAIL_MASK BIT(30)
#define IMX93_ADC_MSR_CALBUSY_MASK BIT(29)
#define IMX93_ADC_MSR_ADCSTATUS_MASK GENMASK(2, 0)
#define IMX93_ADC_ISR_EOC_MASK BIT(1)
#define IMX93_ADC_IMR_EOC_MASK BIT(1)
#define IMX93_ADC_IMR_ECH_MASK BIT(0)
#define IMX93_ADC_PCDR_CDATA_MASK GENMASK(11, 0)
#define IDLE 0
#define POWER_DOWN 1
#define WAIT_STATE 2
#define BUSY_IN_CALIBRATION 3
#define SAMPLE 4
#define CONVERSION 6
#define IMX93_ADC_MAX_CHANNEL 3
#define IMX93_ADC_DAT_MASK 0xfff
#define IMX93_ADC_TIMEOUT 100000
struct imx93_adc_priv {
int active_channel;
void __iomem *regs;
struct clk ipg_clk;
};
static void imx93_adc_power_down(struct imx93_adc_priv *adc)
{
u32 mcr, msr;
int ret;
mcr = readl(adc->regs + IMX93_ADC_MCR);
mcr |= FIELD_PREP(IMX93_ADC_MCR_PWDN_MASK, 1);
writel(mcr, adc->regs + IMX93_ADC_MCR);
ret = readl_poll_timeout(adc->regs + IMX93_ADC_MSR, msr,
((msr & IMX93_ADC_MSR_ADCSTATUS_MASK) == POWER_DOWN), 50);
if (ret == -ETIMEDOUT)
pr_warn("ADC not in power down mode, current MSR: %x\n", msr);
}
static void imx93_adc_power_up(struct imx93_adc_priv *adc)
{
u32 mcr;
/* bring ADC out of power down state, in idle state */
mcr = readl(adc->regs + IMX93_ADC_MCR);
mcr &= ~FIELD_PREP(IMX93_ADC_MCR_PWDN_MASK, 1);
writel(mcr, adc->regs + IMX93_ADC_MCR);
}
static void imx93_adc_config_ad_clk(struct imx93_adc_priv *adc)
{
u32 mcr;
/* put adc in power down mode */
imx93_adc_power_down(adc);
/* config the AD_CLK equal to bus clock */
mcr = readl(adc->regs + IMX93_ADC_MCR);
mcr |= FIELD_PREP(IMX93_ADC_MCR_ADCLKSE_MASK, 1);
writel(mcr, adc->regs + IMX93_ADC_MCR);
/* bring ADC out of power down state, in idle state */
imx93_adc_power_up(adc);
}
static int imx93_adc_calibration(struct imx93_adc_priv *adc)
{
u32 mcr, msr;
int ret;
/* make sure ADC is in power down mode */
imx93_adc_power_down(adc);
/* config SAR controller operating clock */
mcr = readl(adc->regs + IMX93_ADC_MCR);
mcr &= ~FIELD_PREP(IMX93_ADC_MCR_ADCLKSE_MASK, 1);
writel(mcr, adc->regs + IMX93_ADC_MCR);
/* bring ADC out of power down state */
imx93_adc_power_up(adc);
/*
* we use the default TSAMP/NRSMPL/AVGEN in MCR,
* can add the setting of these bit if need
*/
/* run calibration */
mcr = readl(adc->regs + IMX93_ADC_MCR);
mcr |= FIELD_PREP(IMX93_ADC_MCR_CALSTART_MASK, 1);
writel(mcr, adc->regs + IMX93_ADC_MCR);
/* wait calibration to be finished */
ret = readl_poll_timeout(adc->regs + IMX93_ADC_MSR, msr,
!(msr & IMX93_ADC_MSR_CALBUSY_MASK), 2000000);
if (ret == -ETIMEDOUT) {
pr_warn("ADC calibration timeout\n");
return ret;
}
/* check whether calbration is successful or not */
msr = readl(adc->regs + IMX93_ADC_MSR);
if (msr & IMX93_ADC_MSR_CALFAIL_MASK) {
pr_warn("ADC calibration failed!\n");
return -EAGAIN;
}
return 0;
}
static int imx93_adc_channel_data(struct udevice *dev, int channel,
unsigned int *data)
{
struct imx93_adc_priv *adc = dev_get_priv(dev);
u32 isr, pcda;
int ret;
if (channel != adc->active_channel) {
pr_err("Requested channel is not active!\n");
return -EINVAL;
}
ret = readl_poll_timeout(adc->regs + IMX93_ADC_ISR, isr,
(isr & IMX93_ADC_ISR_EOC_MASK), IMX93_ADC_TIMEOUT);
/* clear interrupts */
writel(isr, adc->regs + IMX93_ADC_ISR);
if (ret == -ETIMEDOUT) {
pr_warn("ADC conversion timeout!\n");
return ret;
}
pcda = readl(adc->regs + IMX93_ADC_PCDR0 + channel * 4);
*data = FIELD_GET(IMX93_ADC_PCDR_CDATA_MASK, pcda);
return 0;
}
static int imx93_adc_start_channel(struct udevice *dev, int channel)
{
struct imx93_adc_priv *adc = dev_get_priv(dev);
u32 imr, mcr;
/* config channel mask register */
writel(1 << channel, adc->regs + IMX93_ADC_NCMR0);
/* config interrupt mask */
imr = FIELD_PREP(IMX93_ADC_IMR_EOC_MASK, 1);
writel(imr, adc->regs + IMX93_ADC_IMR);
writel(1 << channel, adc->regs + IMX93_ADC_CIMR0);
/* config one-shot mode */
mcr = readl(adc->regs + IMX93_ADC_MCR);
mcr &= ~FIELD_PREP(IMX93_ADC_MCR_MODE_MASK, 1);
writel(mcr, adc->regs + IMX93_ADC_MCR);
/* start normal conversion */
mcr = readl(adc->regs + IMX93_ADC_MCR);
mcr |= FIELD_PREP(IMX93_ADC_MCR_NSTART_MASK, 1);
writel(mcr, adc->regs + IMX93_ADC_MCR);
adc->active_channel = channel;
return 0;
}
static int imx93_adc_stop(struct udevice *dev)
{
struct imx93_adc_priv *adc = dev_get_priv(dev);
imx93_adc_power_down(adc);
adc->active_channel = -1;
return 0;
}
static int imx93_adc_probe(struct udevice *dev)
{
struct imx93_adc_priv *adc = dev_get_priv(dev);
int ret;
ret = imx93_adc_calibration(adc);
if (ret < 0)
return ret;
imx93_adc_config_ad_clk(adc);
adc->active_channel = -1;
return 0;
}
static int imx93_adc_of_to_plat(struct udevice *dev)
{
struct adc_uclass_plat *uc_pdata = dev_get_uclass_plat(dev);
struct imx93_adc_priv *adc = dev_get_priv(dev);
int ret;
adc->regs = dev_read_addr_ptr(dev);
if (adc->regs == (struct imx93_adc *)FDT_ADDR_T_NONE) {
pr_err("Dev: %s - can't get address!", dev->name);
return -ENODATA;
}
ret = clk_get_by_name(dev, "ipg", &adc->ipg_clk);
if (ret < 0) {
pr_err("Can't get ADC ipg clk: %d\n", ret);
return ret;
}
ret = clk_enable(&adc->ipg_clk);
if(ret) {
pr_err("Can't enable ADC ipg clk: %d\n", ret);
return ret;
}
uc_pdata->data_mask = IMX93_ADC_DAT_MASK;
uc_pdata->data_format = ADC_DATA_FORMAT_BIN;
uc_pdata->data_timeout_us = IMX93_ADC_TIMEOUT;
/* Mask available channel bits: [0:3] */
uc_pdata->channel_mask = (2 << IMX93_ADC_MAX_CHANNEL) - 1;
return 0;
}
static const struct adc_ops imx93_adc_ops = {
.start_channel = imx93_adc_start_channel,
.channel_data = imx93_adc_channel_data,
.stop = imx93_adc_stop,
};
static const struct udevice_id imx93_adc_ids[] = {
{ .compatible = "nxp,imx93-adc" },
{ }
};
U_BOOT_DRIVER(imx93_adc) = {
.name = "imx93-adc",
.id = UCLASS_ADC,
.of_match = imx93_adc_ids,
.ops = &imx93_adc_ops,
.probe = imx93_adc_probe,
.of_to_plat = imx93_adc_of_to_plat,
.priv_auto = sizeof(struct imx93_adc_priv),
};
+753
View File
@@ -0,0 +1,753 @@
// 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 <adc.h>
#include <clk.h>
#include <dm.h>
#include <regmap.h>
#include <errno.h>
#include <asm/io.h>
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/math64.h>
#include <linux/bitfield.h>
#include <linux/printk.h>
#include <power/regulator.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, &regval);
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) {
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, &regval);
} while (FIELD_GET(MESON_SAR_ADC_REG0_BUSY_MASK, regval) && --timeout);
if (!timeout)
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, &regval);
fifo_chan = FIELD_GET(MESON_SAR_ADC_FIFO_RD_CHAN_ID_MASK, regval);
if (fifo_chan != channel) {
printf("ADC FIFO entry belongs to channel %u instead of %u\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, &regval);
if (regval & MESON_SAR_ADC_REG3_BL30_INITIALIZED) {
regmap_read(priv->regmap, MESON_SAR_ADC_REG3, &regval);
if (regval & MESON_SAR_ADC_REG3_ADC_EN)
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 adc_uclass_plat *uc_pdata = dev_get_uclass_plat(dev);
struct meson_saradc_priv *priv = dev_get_priv(dev);
struct udevice *vref;
int vref_uv;
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;
ret = device_get_supply_regulator(dev, "vref-supply", &vref);
if (ret) {
printf("can't get vref-supply: %d\n", ret);
return ret;
}
vref_uv = regulator_get_value(vref);
if (vref_uv < 0) {
printf("can't get vref-supply value: %d\n", vref_uv);
return vref_uv;
}
/* VDD supplied by common vref pin */
uc_pdata->vdd_supply = vref;
uc_pdata->vdd_microvolts = vref_uv;
uc_pdata->vss_microvolts = 0;
return 0;
}
int meson_saradc_of_to_plat(struct udevice *dev)
{
struct adc_uclass_plat *uc_pdata = dev_get_uclass_plat(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 },
{ .compatible = "amlogic,meson-g12a-saradc",
.data = (ulong)&gxl_saradc_data },
{ .compatible = "amlogic,meson-axg-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,
.of_to_plat = meson_saradc_of_to_plat,
.priv_auto = sizeof(struct meson_saradc_priv),
};
+380
View File
@@ -0,0 +1,380 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2017, Fuzhou Rockchip Electronics Co., Ltd
*
* Rockchip SARADC driver for U-Boot
*/
#include <adc.h>
#include <clk.h>
#include <dm.h>
#include <errno.h>
#include <reset.h>
#include <asm/arch-rockchip/hardware.h>
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/printk.h>
#include <power/regulator.h>
#define usleep_range(a, b) udelay((b))
#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_v1 {
unsigned int data;
unsigned int stas;
unsigned int ctrl;
unsigned int dly_pu_soc;
};
struct rockchip_saradc_regs_v2 {
unsigned int conv_con;
#define SARADC2_SINGLE_MODE BIT(5)
#define SARADC2_START BIT(4)
#define SARADC2_CONV_CHANNELS GENMASK(3, 0)
unsigned int t_pd_soc;
unsigned int t_as_soc;
unsigned int t_das_soc;
unsigned int t_sel_soc;
unsigned int high_comp[16];
unsigned int low_comp[16];
unsigned int debounce;
unsigned int ht_int_en;
unsigned int lt_int_en;
unsigned int reserved[24];
unsigned int mt_int_en;
unsigned int end_int_en;
#define SARADC2_EN_END_INT BIT(0)
unsigned int st_con;
unsigned int status;
unsigned int end_int_st;
unsigned int ht_int_st;
unsigned int lt_int_st;
unsigned int mt_int_st;
unsigned int data[16];
unsigned int auto_ch_en;
};
union rockchip_saradc_regs {
struct rockchip_saradc_regs_v1 *v1;
struct rockchip_saradc_regs_v2 *v2;
};
struct rockchip_saradc_data {
int num_bits;
int num_channels;
unsigned long clk_rate;
int (*channel_data)(struct udevice *dev, int channel, unsigned int *data);
int (*start_channel)(struct udevice *dev, int channel);
int (*stop)(struct udevice *dev);
};
struct rockchip_saradc_priv {
union rockchip_saradc_regs regs;
int active_channel;
const struct rockchip_saradc_data *data;
struct reset_ctl *reset;
};
int rockchip_saradc_channel_data_v1(struct udevice *dev, int channel,
unsigned int *data)
{
struct rockchip_saradc_priv *priv = dev_get_priv(dev);
if ((readl(&priv->regs.v1->ctrl) & SARADC_CTRL_IRQ_STATUS) !=
SARADC_CTRL_IRQ_STATUS)
return -EBUSY;
/* Read value */
*data = readl(&priv->regs.v1->data);
/* Power down adc */
writel(0, &priv->regs.v1->ctrl);
return 0;
}
int rockchip_saradc_channel_data_v2(struct udevice *dev, int channel,
unsigned int *data)
{
struct rockchip_saradc_priv *priv = dev_get_priv(dev);
if (!(readl(&priv->regs.v2->end_int_st) & SARADC2_EN_END_INT))
return -EBUSY;
/* Read value */
*data = readl(&priv->regs.v2->data[channel]);
/* Acknowledge the interrupt */
writel(SARADC2_EN_END_INT, &priv->regs.v2->end_int_st);
return 0;
}
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_plat *uc_pdata = dev_get_uclass_plat(dev);
int ret;
if (channel != priv->active_channel) {
pr_err("Requested channel is not active!");
return -EINVAL;
}
ret = priv->data->channel_data(dev, channel, data);
if (ret) {
if (ret != -EBUSY)
pr_err("Error reading channel data, %d!", ret);
return ret;
}
*data &= uc_pdata->data_mask;
return 0;
}
int rockchip_saradc_start_channel_v1(struct udevice *dev, int channel)
{
struct rockchip_saradc_priv *priv = dev_get_priv(dev);
/* 8 clock periods as delay between power up and start cmd */
writel(8, &priv->regs.v1->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.v1->ctrl);
return 0;
}
static void rockchip_saradc_reset_controller(struct reset_ctl *reset)
{
reset_assert(reset);
usleep_range(10, 20);
reset_deassert(reset);
}
int rockchip_saradc_start_channel_v2(struct udevice *dev, int channel)
{
struct rockchip_saradc_priv *priv = dev_get_priv(dev);
/*
* Downstream says
* """If read other chn at anytime, then chn1 will error, assert
* controller as a workaround."""
*/
if (priv->reset)
rockchip_saradc_reset_controller(priv->reset);
writel(0xc, &priv->regs.v2->t_das_soc);
writel(0x20, &priv->regs.v2->t_pd_soc);
/* Acknowledge any previous interrupt */
writel(SARADC2_EN_END_INT, &priv->regs.v2->end_int_st);
rk_clrsetreg(&priv->regs.v2->conv_con,
SARADC2_CONV_CHANNELS | SARADC2_START | SARADC2_SINGLE_MODE,
FIELD_PREP(SARADC2_CONV_CHANNELS, channel) |
FIELD_PREP(SARADC2_START, 1) |
FIELD_PREP(SARADC2_SINGLE_MODE, 1));
return 0;
}
int rockchip_saradc_start_channel(struct udevice *dev, int channel)
{
struct rockchip_saradc_priv *priv = dev_get_priv(dev);
int ret;
if (channel < 0 || channel >= priv->data->num_channels) {
pr_err("Requested channel is invalid!");
return -EINVAL;
}
ret = priv->data->start_channel(dev, channel);
if (ret) {
pr_err("Error starting channel, %d!", ret);
return ret;
}
priv->active_channel = channel;
return 0;
}
int rockchip_saradc_stop_v1(struct udevice *dev)
{
struct rockchip_saradc_priv *priv = dev_get_priv(dev);
/* Power down adc */
writel(0, &priv->regs.v1->ctrl);
return 0;
}
int rockchip_saradc_stop(struct udevice *dev)
{
struct rockchip_saradc_priv *priv = dev_get_priv(dev);
if (priv->data->stop) {
int ret = priv->data->stop(dev);
if (ret) {
pr_err("Error stopping channel, %d!", ret);
return ret;
}
}
priv->active_channel = -1;
return 0;
}
int rockchip_saradc_probe(struct udevice *dev)
{
struct adc_uclass_plat *uc_pdata = dev_get_uclass_plat(dev);
struct rockchip_saradc_priv *priv = dev_get_priv(dev);
struct udevice *vref = NULL;
struct clk clk;
int vref_uv;
int ret;
priv->reset = devm_reset_control_get_optional(dev, "saradc-apb");
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;
ret = device_get_supply_regulator(dev, "vref-supply", &vref);
if (ret && uc_pdata->vdd_microvolts <= 0) {
printf("can't get vref-supply: %d\n", ret);
return ret;
}
if (priv->reset)
rockchip_saradc_reset_controller(priv->reset);
if (vref)
vref_uv = regulator_get_value(vref);
else
vref_uv = uc_pdata->vdd_microvolts;
if (vref_uv < 0) {
printf("can't get vref-supply value: %d\n", vref_uv);
return vref_uv;
}
/* VDD supplied by common vref pin */
uc_pdata->vdd_supply = vref;
uc_pdata->vdd_microvolts = vref_uv;
uc_pdata->vss_microvolts = 0;
return 0;
}
int rockchip_saradc_of_to_plat(struct udevice *dev)
{
struct adc_uclass_plat *uc_pdata = dev_get_uclass_plat(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.v1 = dev_read_addr_ptr(dev);
if (!priv->regs.v1) {
pr_err("Dev: %s - can't get address!", dev->name);
return -EINVAL;
}
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,
.channel_data = rockchip_saradc_channel_data_v1,
.start_channel = rockchip_saradc_start_channel_v1,
.stop = rockchip_saradc_stop_v1,
};
static const struct rockchip_saradc_data rk3066_tsadc_data = {
.num_bits = 12,
.num_channels = 2,
.clk_rate = 50000,
.channel_data = rockchip_saradc_channel_data_v1,
.start_channel = rockchip_saradc_start_channel_v1,
.stop = rockchip_saradc_stop_v1,
};
static const struct rockchip_saradc_data rk3399_saradc_data = {
.num_bits = 10,
.num_channels = 6,
.clk_rate = 1000000,
.channel_data = rockchip_saradc_channel_data_v1,
.start_channel = rockchip_saradc_start_channel_v1,
.stop = rockchip_saradc_stop_v1,
};
static const struct rockchip_saradc_data rk3528_saradc_data = {
.num_bits = 10,
.num_channels = 4,
.clk_rate = 1000000,
.channel_data = rockchip_saradc_channel_data_v2,
.start_channel = rockchip_saradc_start_channel_v2,
};
static const struct rockchip_saradc_data rk3588_saradc_data = {
.num_bits = 12,
.num_channels = 8,
.clk_rate = 1000000,
.channel_data = rockchip_saradc_channel_data_v2,
.start_channel = rockchip_saradc_start_channel_v2,
};
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 },
{ .compatible = "rockchip,rk3528-saradc",
.data = (ulong)&rk3528_saradc_data },
{ .compatible = "rockchip,rk3588-saradc",
.data = (ulong)&rk3588_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,
.of_to_plat = rockchip_saradc_of_to_plat,
.priv_auto = sizeof(struct rockchip_saradc_priv),
};
+173
View File
@@ -0,0 +1,173 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2015 Samsung Electronics
* Przemyslaw Marczak <p.marczak@samsung.com>
*/
#include <errno.h>
#include <dm.h>
#include <adc.h>
#include <sandbox-adc.h>
#include <linux/printk.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_of_to_plat(struct udevice *dev)
{
struct adc_uclass_plat *uc_pdata = dev_get_uclass_plat(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,
.of_to_plat = sandbox_adc_of_to_plat,
.priv_auto = sizeof(struct sandbox_adc_priv),
};
+213
View File
@@ -0,0 +1,213 @@
// 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 <dm.h>
#include <asm/io.h>
#include <dm/device_compat.h>
#include <linux/bitops.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" },
{ .compatible = "st,stm32mp13-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 = sizeof(struct stm32_adc_common),
};
+51
View File
@@ -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 <clk.h>
struct udevice;
/**
* 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
+482
View File
@@ -0,0 +1,482 @@
// 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 <adc.h>
#include <dm.h>
#include <asm/io.h>
#include <dm/device_compat.h>
#include <linux/bitops.h>
#include <linux/delay.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_ADCAL BIT(31)
#define STM32H7_ADCALDIF BIT(30)
#define STM32H7_DEEPPWD BIT(29)
#define STM32H7_ADVREGEN BIT(28)
#define STM32H7_ADCALLIN BIT(16)
#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
/* STM32H7_ADC_DIFSEL - bit fields */
#define STM32H7_DIFSEL_SHIFT 0
#define STM32H7_DIFSEL_MASK GENMASK(19, 0)
/* BOOST bit must be set on STM32H7 when ADC clock is above 20MHz */
#define STM32H7_BOOST_CLKRATE 20000000UL
/* STM32MP13 - Registers for each ADC instance */
#define STM32MP13_ADC_DIFSEL 0xB0
/* STM32MP13_ADC_CFGR specific bit fields */
#define STM32MP13_DMAEN BIT(0)
#define STM32MP13_DMACFG BIT(1)
/* STM32MP13_ADC_DIFSEL - bit fields */
#define STM32MP13_DIFSEL_SHIFT 0
#define STM32MP13_DIFSEL_MASK GENMASK(18, 0)
#define STM32_ADC_CH_MAX 20 /* max number of channels */
#define STM32_ADC_TIMEOUT_US 100000
struct stm32_adc {
void __iomem *regs;
int active_channel;
const struct stm32_adc_cfg *cfg;
};
struct stm32_adc_regs {
int reg;
int mask;
int shift;
};
struct stm32_adc_regspec {
const struct stm32_adc_regs difsel;
};
struct stm32_adc_cfg {
const struct stm32_adc_regspec *regs;
unsigned int max_channels;
unsigned int num_bits;
bool has_vregready;
bool has_boostmode;
bool has_linearcal;
bool has_presel;
};
static const struct stm32_adc_regspec stm32h7_adc_regspec = {
.difsel = { STM32H7_ADC_DIFSEL, STM32H7_DIFSEL_MASK },
};
static const struct stm32_adc_regspec stm32mp13_adc_regspec = {
.difsel = { STM32MP13_ADC_DIFSEL, STM32MP13_DIFSEL_MASK },
};
static void stm32_adc_enter_pwr_down(struct udevice *dev)
{
struct stm32_adc *adc = dev_get_priv(dev);
if (adc->cfg->has_boostmode)
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);
}
static int stm32_adc_exit_pwr_down(struct udevice *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;
/* return immediately if ADC is not in deep power down mode */
if (!(readl(adc->regs + STM32H7_ADC_CR) & STM32H7_DEEPPWD))
return 0;
/* 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 (adc->cfg->has_boostmode && 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);
return 0;
}
ret = readl_poll_timeout(adc->regs + STM32H7_ADC_ISR, val,
val & STM32MP1_VREGREADY,
STM32_ADC_TIMEOUT_US);
if (ret < 0) {
stm32_adc_enter_pwr_down(dev);
dev_err(dev, "Failed to enable vreg: %d\n", ret);
}
return ret;
}
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);
stm32_adc_enter_pwr_down(dev);
adc->active_channel = -1;
return 0;
}
static int stm32_adc_start_channel(struct udevice *dev, int channel)
{
struct adc_uclass_plat *uc_pdata = dev_get_uclass_plat(dev);
struct stm32_adc *adc = dev_get_priv(dev);
int ret;
u32 val;
ret = stm32_adc_exit_pwr_down(dev);
if (ret < 0)
return ret;
/* Only use single ended channels */
clrbits_le32(adc->regs + adc->cfg->regs->difsel.reg, adc->cfg->regs->difsel.mask);
/* 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 */
if (adc->cfg->has_presel)
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)
* STM32H7_DMNGT is equivalent to STM32MP13_DMAEN & STM32MP13_DMACFG
*/
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;
}
/**
* Fixed timeout value for ADC calibration.
* worst cases:
* - low clock frequency (0.12 MHz min)
* - maximum prescalers
* Calibration requires:
* - 16384 ADC clock cycle for the linear calibration
* - 20 ADC clock cycle for the offset calibration
*
* Set to 100ms for now
*/
#define STM32H7_ADC_CALIB_TIMEOUT_US 100000
static int stm32_adc_selfcalib(struct udevice *dev)
{
struct stm32_adc *adc = dev_get_priv(dev);
int ret;
u32 val, mask;
/*
* Select calibration mode:
* - Offset calibration for single ended inputs
* - No linearity calibration. Done in next step.
*/
clrbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_ADCALDIF | STM32H7_ADCALLIN);
/* Start calibration, then wait for completion */
setbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_ADCAL);
ret = readl_poll_sleep_timeout(adc->regs + STM32H7_ADC_CR, val,
!(val & STM32H7_ADCAL), 100,
STM32H7_ADC_CALIB_TIMEOUT_US);
if (ret) {
dev_err(dev, "calibration failed\n");
goto out;
}
/*
* Select calibration mode, then start calibration:
* - Offset calibration for differential input
* - Linearity calibration (needs to be done only once for single/diff)
* will run simultaneously with offset calibration.
*/
mask = STM32H7_ADCALDIF;
if (adc->cfg->has_linearcal)
mask |= STM32H7_ADCALLIN;
setbits_le32(adc->regs + STM32H7_ADC_CR, mask);
/* Start calibration, then wait for completion */
setbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_ADCAL);
ret = readl_poll_sleep_timeout(adc->regs + STM32H7_ADC_CR, val,
!(val & STM32H7_ADCAL), 100,
STM32H7_ADC_CALIB_TIMEOUT_US);
if (ret)
dev_err(dev, "calibration failed\n");
out:
clrbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_ADCALDIF | STM32H7_ADCALLIN);
return ret;
}
static int stm32_adc_get_legacy_chan_count(struct udevice *dev)
{
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;
}
return (ret / sizeof(u32));
}
static int stm32_adc_legacy_chan_init(struct udevice *dev, unsigned int num_channels)
{
struct adc_uclass_plat *uc_pdata = dev_get_uclass_plat(dev);
struct stm32_adc *adc = dev_get_priv(dev);
u32 chans[STM32_ADC_CH_MAX];
int i, ret;
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];
}
return ret;
}
static int stm32_adc_generic_chan_init(struct udevice *dev, unsigned int num_channels)
{
struct adc_uclass_plat *uc_pdata = dev_get_uclass_plat(dev);
struct stm32_adc *adc = dev_get_priv(dev);
ofnode child;
int val, ret;
ofnode_for_each_subnode(child, dev_ofnode(dev)) {
ret = ofnode_read_u32(child, "reg", &val);
if (ret) {
dev_err(dev, "Missing channel index %d\n", ret);
return ret;
}
if (val >= adc->cfg->max_channels) {
dev_err(dev, "Invalid channel %d\n", val);
return -EINVAL;
}
uc_pdata->channel_mask |= 1 << val;
}
return 0;
}
static int stm32_adc_chan_of_init(struct udevice *dev)
{
struct adc_uclass_plat *uc_pdata = dev_get_uclass_plat(dev);
struct stm32_adc *adc = dev_get_priv(dev);
unsigned int num_channels;
int ret;
bool legacy = false;
num_channels = dev_get_child_count(dev);
/* If no channels have been found, fallback to channels legacy properties. */
if (!num_channels) {
legacy = true;
ret = stm32_adc_get_legacy_chan_count(dev);
if (!ret) {
dev_err(dev, "No channel found\n");
return -ENODATA;
} else if (ret < 0) {
return ret;
}
num_channels = ret;
}
if (num_channels > adc->cfg->max_channels) {
dev_err(dev, "too many st,adc-channels: %d\n", num_channels);
return -EINVAL;
}
if (legacy)
ret = stm32_adc_legacy_chan_init(dev, num_channels);
else
ret = stm32_adc_generic_chan_init(dev, num_channels);
if (ret < 0)
return ret;
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_plat *uc_pdata = dev_get_uclass_plat(dev);
struct stm32_adc_common *common = dev_get_priv(dev_get_parent(dev));
struct stm32_adc *adc = dev_get_priv(dev);
int offset, ret;
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;
ret = stm32_adc_chan_of_init(dev);
if (ret < 0)
return ret;
ret = stm32_adc_exit_pwr_down(dev);
if (ret < 0)
return ret;
ret = stm32_adc_selfcalib(dev);
if (ret)
stm32_adc_enter_pwr_down(dev);
return ret;
}
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 = {
.regs = &stm32h7_adc_regspec,
.num_bits = 16,
.max_channels = STM32_ADC_CH_MAX,
.has_boostmode = true,
.has_linearcal = true,
.has_presel = true,
};
static const struct stm32_adc_cfg stm32mp1_adc_cfg = {
.regs = &stm32h7_adc_regspec,
.num_bits = 16,
.max_channels = STM32_ADC_CH_MAX,
.has_vregready = true,
.has_boostmode = true,
.has_linearcal = true,
.has_presel = true,
};
static const struct stm32_adc_cfg stm32mp13_adc_cfg = {
.regs = &stm32mp13_adc_regspec,
.num_bits = 12,
.max_channels = STM32_ADC_CH_MAX - 1,
};
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 },
{ .compatible = "st,stm32mp13-adc",
.data = (ulong)&stm32mp13_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 = sizeof(struct stm32_adc),
};
+162
View File
@@ -0,0 +1,162 @@
config AHCI
bool "Support SATA controllers with driver model"
depends on DM
select BLK
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 BLK
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 PCI
depends on SCSI
depends on SCSI_AHCI
help
Enables support for the PCI-based AHCI controller.
if AHCI
config SPL_AHCI_PCI
bool "Support for PCI-based AHCI controller for SPL"
depends on SPL
depends on SPL_PCI
depends on SPL_SATA && SCSI
config DWC_AHCI
bool "Enable Synopsys DWC AHCI driver support"
select SCSI_AHCI
select PHY
depends on SCSI
help
Enable this driver to support Sata devices through
Synopsys DWC AHCI module.
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
default y
help
Enable this option unless you need your private ahci implementation
config MTK_AHCI
bool "Enable Mediatek AHCI driver support"
help
Enable this driver to support Sata devices through
Mediatek AHCI controller (e.g. MT7622).
config AHCI_GENERIC
bool "Generic AHCI SATA support"
depends on OF_CONTROL
select SCSI_AHCI
select SCSI
help
This option enables support for generic onboard AHCI SATA controller
that do not need platform specific quirks, like emulated devices,
Marvell EBU SoC's onboard AHCI SATA controllers or Cavium's Octeon
7130 AHCI controllers.
If unsure, say N.
config SUNXI_AHCI
bool "Enable Allwinner SATA driver support"
default y if ARCH_SUNXI
help
Enable this driver to support the SATA controllers found in the
Allwinner A10, A20 and R40 SoCs.
endif # AHCI
if SATA
config SATA_CEVA
bool "Ceva Sata controller"
depends on AHCI
depends on SCSI
help
This option enables Ceva Sata controller hard IP available on Xilinx
ZynqMP. Support up to 2 external devices. Compliant with SATA 3.1 and
AHCI 1.3 specifications with hot-plug detect feature.
config FSL_SATA
bool "Enable Freescale SATA controller driver support"
depends on PPC
select AHCI
select LIBATA
imply LBA48
help
Enable this driver to support the SATA controller found in
some Freescale PowerPC SoCs.
config FSL_SATA_V2
bool "Enable support for V2 of the Freescale SATA controller"
depends on FSL_SATA
help
Enable support for V2 of this controller, rather than V1.
config SATA_MV
bool "Enable Marvell SATA controller driver support"
depends on ARCH_KIRKWOOD || ARCH_MVEBU
select AHCI
select LIBATA
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"
depends on PCI
select AHCI
select LIBATA
help
Enable this driver to support the SIL3131, SIL3132 and SIL3124
SATA controllers.
config SYS_SATA_MAX_DEVICE
int "Maximum number of SATA devices"
depends on !AHCI || FSL_SATA || SATA_MV || API
help
Sets the maximum number of SATA devices which can be supported
by U-Boot.
This is only partially converted to driver model. See sata_bread()
for example, which shows where the conversion needs to be completed.
endif # SATA
endmenu
+19
View File
@@ -0,0 +1,19 @@
# 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_AHCI) += ahci-uclass.o
obj-$(CONFIG_$(PHASE_)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_SATA) += sata.o sata_bootdev.o
obj-$(CONFIG_SATA_CEVA) += sata_ceva.o
obj-$(CONFIG_SATA_MV) += sata_mv.o
obj-$(CONFIG_SATA_SIL) += sata_sil.o
obj-$(CONFIG_AHCI_GENERIC) += ahci_generic.o
obj-$(CONFIG_SUNXI_AHCI) += ahci_sunxi.o
obj-$(CONFIG_MTK_AHCI) += mtk_ahci.o
+45
View File
@@ -0,0 +1,45 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2017, Bin Meng <bmeng.cn@gmail.com>
*/
#include <ahci.h>
#include <scsi.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,
.ops = &scsi_ops,
.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(PCI_VENDOR_ID_ASMEDIA, 0x0611) },
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x6121) },
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x6145) },
{},
};
U_BOOT_PCI_DEVICE(ahci_pci, ahci_pci_supported);
+16
View File
@@ -0,0 +1,16 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2015 Google, Inc
* Written by Simon Glass <sjg@chromium.org>
*/
#define LOG_CATEGORY UCLASS_AHCI
#include <ahci.h>
#include <dm.h>
UCLASS_DRIVER(ahci) = {
.id = UCLASS_AHCI,
.name = "ahci",
.per_device_auto = sizeof(struct ahci_uc_priv),
};
+1017
View File
File diff suppressed because it is too large Load Diff
+61
View File
@@ -0,0 +1,61 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2016 Stefan Roese <sr@denx.de>
*/
#include <ahci.h>
#include <dm.h>
#include <log.h>
/*
* Dummy implementation that can be overwritten by a board
* specific function
*/
__weak int board_ahci_enable(void)
{
return 0;
}
static int generic_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 generic_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)dev_remap_addr(dev));
return 0;
}
static const struct udevice_id generic_ahci_ids[] = {
{ .compatible = "marvell,armada-380-ahci" },
{ .compatible = "marvell,armada-3700-ahci" },
{ .compatible = "marvell,armada-8k-ahci" },
{ .compatible = "cavium,octeon-7130-ahci" },
{ .compatible = "generic-ahci" },
{ }
};
U_BOOT_DRIVER(ahci_generic_drv) = {
.name = "ahci_generic",
.id = UCLASS_AHCI,
.of_match = generic_ahci_ids,
.bind = generic_ahci_bind,
.probe = generic_ahci_probe,
};
+135
View File
@@ -0,0 +1,135 @@
#include <ahci.h>
#include <dm.h>
#include <log.h>
#include <scsi.h>
#include <errno.h>
#include <asm/io.h>
#include <asm/gpio.h>
#include <linux/delay.h>
#include <power/regulator.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)
{
struct udevice *reg_dev;
ulong base;
u8 *reg;
int ret;
base = dev_read_addr(dev);
if (base == FDT_ADDR_T_NONE) {
debug("%s: Failed to find address\n", __func__);
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 = device_get_supply_regulator(dev, "target-supply", &reg_dev);
if (ret == 0) {
regulator_set_enable(reg_dev, true);
mdelay(500);
}
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,
};
+103
View File
@@ -0,0 +1,103 @@
// 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 <dm.h>
#include <ahci.h>
#include <scsi.h>
#include <sata.h>
#ifdef CONFIG_ARCH_OMAP2PLUS
#include <asm/arch/sata.h>
#endif
#include <asm/io.h>
#include <generic-phy.h>
#include <linux/printk.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_of_to_plat(struct udevice *dev)
{
struct dwc_ahci_priv *priv = dev_get_priv(dev);
fdt_addr_t addr;
priv->base = map_physmem(dev_read_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_debug("unable to initialize the sata phy\n");
return ret;
}
ret = generic_phy_power_on(&phy);
if (ret) {
pr_debug("unable to power on the sata phy\n");
return ret;
}
#ifdef CONFIG_ARCH_OMAP2PLUS
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);
}
#endif
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,
.of_to_plat = dwc_ahci_of_to_plat,
.ops = &scsi_ops,
.probe = dwc_ahci_probe,
.priv_auto = sizeof(struct dwc_ahci_priv),
};
File diff suppressed because it is too large Load Diff
+317
View File
@@ -0,0 +1,317 @@
/* 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__
/* 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__ */
+981
View File
@@ -0,0 +1,981 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2008,2010 Freescale Semiconductor, Inc.
* Copyright 2019 NXP
* Author: Dave Liu <daveliu@freescale.com>
*/
#include <ahci.h>
#include <blk.h>
#include <command.h>
#include <console.h>
#include <cpu_func.h>
#include <dm.h>
#include <dm/device-internal.h>
#include <log.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 <linux/delay.h>
#include "fsl_sata.h"
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;
}
static int init_sata(struct fsl_ata_priv *priv, int dev)
{
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:", dev);
/* Set the controller register base address to device struct */
reg = (fsl_sata_reg_t *)(priv->base + priv->offset * dev);
sata->dma_flag = priv->flag;
priv->fsl_sata = sata;
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(&reg->hcontrol);
val32 &= ~HCONTROL_ONOFF;
val32 |= HCONTROL_FORCE_OFFLINE;
out_le32(&reg->hcontrol, val32);
/* Wait the controller offline */
ata_wait_register(&reg->hstatus, HSTATUS_ONOFF, 0, 1000);
/* Set the command header base address to CHBA register to tell DMA */
out_le32(&reg->chba, (u32)cmd_hdr & ~0x3);
/* Snoop for the command header */
val32 = in_le32(&reg->hcontrol);
val32 |= HCONTROL_HDR_SNOOP;
out_le32(&reg->hcontrol, val32);
/* Disable all of interrupts */
val32 = in_le32(&reg->hcontrol);
val32 &= ~HCONTROL_INT_EN_ALL;
out_le32(&reg->hcontrol, val32);
/* Clear all of interrupts */
val32 = in_le32(&reg->hstatus);
out_le32(&reg->hstatus, val32);
/* Set the ICC, no interrupt coalescing */
out_le32(&reg->icc, 0x01000000);
/* No PM attatched, the SATA device direct connect */
out_le32(&reg->cqpmp, 0);
/* Clear SError register */
val32 = in_le32(&reg->serror);
out_le32(&reg->serror, val32);
/* Clear CER register */
val32 = in_le32(&reg->cer);
out_le32(&reg->cer, val32);
/* Clear DER register */
val32 = in_le32(&reg->der);
out_le32(&reg->der, val32);
/* No device detection or initialization action requested */
out_le32(&reg->scontrol, 0x00000300);
/* Configure the transport layer, default value */
out_le32(&reg->transcfg, 0x08000016);
/* Configure the link layer, default value */
out_le32(&reg->linkcfg, 0x0000ff34);
/* Bring the controller online */
val32 = in_le32(&reg->hcontrol);
val32 |= HCONTROL_ONOFF;
out_le32(&reg->hcontrol, val32);
mdelay(100);
/* print sata device name */
printf("%s ", sata->name);
/* Wait PHY RDY signal changed for 500ms */
ata_wait_register(&reg->hstatus, HSTATUS_PHY_RDY,
HSTATUS_PHY_RDY, 500);
/* Check PHYRDY */
val32 = in_le32(&reg->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(&reg->hstatus, HSTATUS_SIGNATURE,
HSTATUS_SIGNATURE, 10000);
if (val32 & HSTATUS_SIGNATURE) {
sig = in_le32(&reg->sig);
debug("Signature updated, the sig =%08x\n\r", sig);
sata->ata_device_type = ata_dev_classify(sig);
}
/* Check the speed */
val32 = in_le32(&reg->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(&reg->cqr));
printf("CAR: %08x\n\r", in_le32(&reg->car));
printf("CCR: %08x\n\r", in_le32(&reg->ccr));
printf("CER: %08x\n\r", in_le32(&reg->cer));
printf("CQR: %08x\n\r", in_le32(&reg->cqr));
printf("DER: %08x\n\r", in_le32(&reg->der));
printf("CHBA: %08x\n\r", in_le32(&reg->chba));
printf("HStatus: %08x\n\r", in_le32(&reg->hstatus));
printf("HControl: %08x\n\r", in_le32(&reg->hcontrol));
printf("CQPMP: %08x\n\r", in_le32(&reg->cqpmp));
printf("SIG: %08x\n\r", in_le32(&reg->sig));
printf("ICC: %08x\n\r", in_le32(&reg->icc));
printf("SStatus: %08x\n\r", in_le32(&reg->sstatus));
printf("SError: %08x\n\r", in_le32(&reg->serror));
printf("SControl: %08x\n\r", in_le32(&reg->scontrol));
printf("SNotification: %08x\n\r", in_le32(&reg->snotification));
printf("TransCfg: %08x\n\r", in_le32(&reg->transcfg));
printf("TransStatus: %08x\n\r", in_le32(&reg->transstatus));
printf("LinkCfg: %08x\n\r", in_le32(&reg->linkcfg));
printf("LinkCfg1: %08x\n\r", in_le32(&reg->linkcfg1));
printf("LinkCfg2: %08x\n\r", in_le32(&reg->linkcfg2));
printf("LinkStatus: %08x\n\r", in_le32(&reg->linkstatus));
printf("LinkStatus1: %08x\n\r", in_le32(&reg->linkstatus1));
printf("PhyCtrlCfg: %08x\n\r", in_le32(&reg->phyctrlcfg));
printf("SYSPR: %08x\n\r", in_be32(&reg->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(&reg->cqpmp, val32);
/* Wait no active */
if (ata_wait_register(&reg->car, (1 << tag), 0, 10000))
printf("Wait no active time out\n\r");
/* Issue command */
if (!(in_le32(&reg->cqr) & (1 << tag))) {
val32 = 1 << tag;
out_le32(&reg->cqr, val32);
}
/* Wait command completed for 10s */
if (ata_wait_register(&reg->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(&reg->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(&reg->der);
out_le32(&reg->cer, val32);
out_le32(&reg->der, der);
}
/* Clear complete flags */
val32 = in_le32(&reg->ccr);
out_le32(&reg->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
*/
static ulong sata_read(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt,
void *buffer)
{
struct fsl_ata_priv *priv = dev_get_plat(dev);
fsl_sata_t *sata = priv->fsl_sata;
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;
}
static ulong sata_write(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt,
const void *buffer)
{
struct fsl_ata_priv *priv = dev_get_plat(dev);
fsl_sata_t *sata = priv->fsl_sata;
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);
}
static int scan_sata(struct udevice *dev)
{
struct blk_desc *desc = dev_get_uclass_plat(dev);
struct fsl_ata_priv *priv = dev_get_plat(dev);
fsl_sata_t *sata = priv->fsl_sata;
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
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
/* 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;
}
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,
.plat_auto = sizeof(struct fsl_ata_priv),
};
static int fsl_ata_of_to_plat(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_unbind_device(struct udevice *dev)
{
int ret;
ret = device_remove(dev, DM_REMOVE_NORMAL);
if (ret)
return ret;
ret = device_unbind(dev);
if (ret)
return ret;
return 0;
}
static int fsl_ata_probe(struct udevice *dev)
{
struct fsl_ata_priv *blk_priv, *priv;
struct udevice *blk;
int failed_number;
char sata_name[10];
int nr_ports;
int ret;
int i;
failed_number = 0;
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,
UCLASS_AHCI, -1, DEFAULT_BLKSZ,
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__);
ret = fsl_unbind_device(blk);
if (ret)
return ret;
failed_number++;
continue;
}
blk_priv = dev_get_plat(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__);
ret = fsl_unbind_device(blk);
if (ret)
return ret;
failed_number++;
continue;
}
ret = device_probe(dev);
if (ret < 0) {
debug("Probing %s failed (%d)\n", dev->name, ret);
ret = fsl_unbind_device(blk);
if (ret)
return ret;
failed_number++;
continue;
}
}
if (failed_number == nr_ports)
return -ENODEV;
else
return 0;
}
static int fsl_ata_remove(struct udevice *dev)
{
fsl_sata_t *sata;
struct fsl_ata_priv *priv;
priv = dev_get_priv(dev);
sata = priv->fsl_sata;
free(sata->cmd_hdr_tbl_offset);
free(sata->cmd_desc_offset);
free(sata);
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,
.of_to_plat = fsl_ata_of_to_plat,
.probe = fsl_ata_probe,
.remove = fsl_ata_remove,
.priv_auto = sizeof(struct fsl_ata_priv),
};
+330
View File
@@ -0,0 +1,330 @@
/* 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
struct fsl_ata_priv {
u32 base;
u32 flag;
u32 number;
u32 offset;
fsl_sata_t *fsl_sata;
};
#endif /* __FSL_SATA_H__ */
+145
View File
@@ -0,0 +1,145 @@
// 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 <compiler.h>
#include <libata.h>
#include <stdio.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]);
}
+130
View File
@@ -0,0 +1,130 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* MTK SATA platform driver
*
* Copyright (C) 2020 MediaTek Inc.
*
* Author: Ryder Lee <ryder.lee@mediatek.com>
* Author: Frank Wunderlich <frank-w@public-files.de>
*/
#include <ahci.h>
#include <asm/global_data.h>
#include <asm/io.h>
#include <dm.h>
#include <dm/of_access.h>
#include <generic-phy.h>
#include <linux/err.h>
#include <regmap.h>
#include <reset.h>
#include <sata.h>
#include <scsi.h>
#include <syscon.h>
#include <dm/device_compat.h>
#define SYS_CFG 0x14
#define SYS_CFG_SATA_MSK GENMASK(31, 30)
#define SYS_CFG_SATA_EN BIT(31)
struct mtk_ahci_priv {
void *base;
struct ahci_uc_priv ahci_priv;
struct regmap *mode;
struct reset_ctl_bulk rst_bulk;
};
static int mtk_ahci_bind(struct udevice *dev)
{
struct udevice *scsi_dev;
return ahci_bind_scsi(dev, &scsi_dev);
}
static int mtk_ahci_of_to_plat(struct udevice *dev)
{
struct mtk_ahci_priv *priv = dev_get_priv(dev);
priv->base = devfdt_remap_addr_index(dev, 0);
return 0;
}
static int mtk_ahci_parse_property(struct ahci_uc_priv *hpriv,
struct udevice *dev)
{
struct mtk_ahci_priv *plat = dev_get_priv(dev);
const void *fdt = gd->fdt_blob;
/* enable SATA function if needed */
if (fdt_get_property(fdt, dev_of_offset(dev),
"mediatek,phy-mode", NULL)) {
plat->mode = syscon_regmap_lookup_by_phandle(dev,
"mediatek,phy-mode");
if (IS_ERR(plat->mode)) {
dev_err(dev, "missing phy-mode phandle\n");
return PTR_ERR(plat->mode);
}
regmap_update_bits(plat->mode, SYS_CFG,
SYS_CFG_SATA_MSK, SYS_CFG_SATA_EN);
}
ofnode_read_u32(dev_ofnode(dev), "ports-implemented",
&hpriv->port_map);
return 0;
}
static int mtk_ahci_probe(struct udevice *dev)
{
struct mtk_ahci_priv *priv = dev_get_priv(dev);
int ret;
struct phy phy;
ret = mtk_ahci_parse_property(&priv->ahci_priv, dev);
if (ret)
return ret;
ret = reset_get_bulk(dev, &priv->rst_bulk);
if (!ret) {
reset_assert_bulk(&priv->rst_bulk);
reset_deassert_bulk(&priv->rst_bulk);
} else {
dev_err(dev, "Failed to get reset: %d\n", ret);
}
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;
}
return ahci_probe_scsi(dev, (ulong)priv->base);
}
static const struct udevice_id mtk_ahci_ids[] = {
{ .compatible = "mediatek,mtk-ahci" },
{ }
};
U_BOOT_DRIVER(mtk_ahci) = {
.name = "mtk_ahci",
.id = UCLASS_AHCI,
.of_match = mtk_ahci_ids,
.bind = mtk_ahci_bind,
.of_to_plat = mtk_ahci_of_to_plat,
.ops = &scsi_ops,
.probe = mtk_ahci_probe,
.priv_auto = sizeof(struct mtk_ahci_priv),
};
+111
View File
@@ -0,0 +1,111 @@
// 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>
*/
#define LOG_CATEGORY UCLASS_AHCI
#include <ahci.h>
#include <blk.h>
#include <dm.h>
#include <log.h>
#include <part.h>
#include <sata.h>
#include <dm/device-internal.h>
#include <dm/uclass-internal.h>
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);
}
int sata_rescan(bool verbose)
{
struct uclass *uc;
struct udevice *dev; /* SATA controller */
int ret;
if (verbose)
printf("scanning bus for devices...\n");
ret = uclass_get(UCLASS_AHCI, &uc);
if (ret)
return ret;
/* Remove all children of SATA devices (blk and bootdev) */
uclass_foreach_dev(dev, uc) {
log_debug("unbind %s\n", dev->name);
ret = device_chld_remove(dev, NULL, DM_REMOVE_NORMAL);
if (!ret)
ret = device_chld_unbind(dev, NULL);
if (ret && verbose) {
log_err("Unbinding from %s failed (%dE)\n",
dev->name, ret);
}
}
if (verbose)
printf("Rescanning SATA bus for devices...\n");
uclass_foreach_dev_probe(UCLASS_AHCI, dev) {
ret = sata_scan(dev);
if (ret && verbose)
log_err("Scanning %s failed (%dE)\n", dev->name, ret);
}
return 0;
}
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;
}
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,
};
+61
View File
@@ -0,0 +1,61 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Bootdev for sata
*
* Copyright 2023 Tony Dinh <mibodhi@gmail.com>
*/
#include <ahci.h>
#include <bootdev.h>
#include <dm.h>
#include <init.h>
#include <sata.h>
static int sata_bootdev_bind(struct udevice *dev)
{
struct bootdev_uc_plat *ucp = dev_get_uclass_plat(dev);
ucp->prio = BOOTDEVP_4_SCAN_FAST;
return 0;
}
static int sata_bootdev_hunt(struct bootdev_hunter *info, bool show)
{
int ret;
if (IS_ENABLED(CONFIG_PCI)) {
ret = pci_init();
if (ret)
return ret;
}
ret = sata_rescan(true);
if (ret)
return ret;
return 0;
}
struct bootdev_ops sata_bootdev_ops = {
};
static const struct udevice_id sata_bootdev_ids[] = {
{ .compatible = "u-boot,bootdev-sata" },
{ }
};
U_BOOT_DRIVER(sata_bootdev) = {
.name = "sata_bootdev",
.id = UCLASS_BOOTDEV,
.ops = &sata_bootdev_ops,
.bind = sata_bootdev_bind,
.of_match = sata_bootdev_ids,
};
BOOTDEV_HUNTER(sata_bootdev_hunter) = {
.prio = BOOTDEVP_4_SCAN_FAST,
.uclass = UCLASS_AHCI,
.hunt = sata_bootdev_hunt,
.drv = DM_DRIVER_REF(sata_bootdev),
};
+282
View File
@@ -0,0 +1,282 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2015 - 2016 Xilinx, Inc.
* Michal Simek <michal.simek@amd.com>
*/
#include <dm.h>
#include <ahci.h>
#include <generic-phy.h>
#include <log.h>
#include <reset.h>
#include <scsi.h>
#include <asm/io.h>
#include <dm/device_compat.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);
struct phy phy;
int ret;
struct reset_ctl_bulk resets;
ret = generic_phy_get_by_index(dev, 0, &phy);
if (!ret) {
dev_dbg(dev, "Perform PHY initialization\n");
ret = generic_phy_init(&phy);
if (ret)
return ret;
} else if (ret != -ENOENT) {
dev_dbg(dev, "could not get phy (err %d)\n", ret);
return ret;
}
/* reset is optional */
ret = reset_get_bulk(dev, &resets);
if (ret && ret != -ENOTSUPP && ret != -ENOENT) {
dev_dbg(dev, "Getting reset fails (err %d)\n", ret);
return ret;
}
/* Just trigger reset when reset is specified */
if (!ret) {
dev_dbg(dev, "Perform IP reset\n");
ret = reset_deassert_bulk(&resets);
if (ret) {
dev_dbg(dev, "Reset fails (err %d)\n", ret);
reset_release_bulk(&resets);
return ret;
}
}
if (generic_phy_valid(&phy)) {
dev_dbg(dev, "Perform PHY power on\n");
ret = generic_phy_power_on(&phy);
if (ret) {
dev_dbg(dev, "PHY power on failed (err %d)\n", ret);
return ret;
}
}
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_of_to_plat(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, "sata-ecc", &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 = sizeof(struct ceva_sata_priv),
.probe = sata_ceva_probe,
.of_to_plat = sata_ceva_of_to_plat,
};
File diff suppressed because it is too large Load Diff
+821
View File
@@ -0,0 +1,821 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2011 Freescale Semiconductor, Inc.
* Copyright 2019 NXP
* Author: Tang Yuantian <b29983@freescale.com>
*/
#include <blk.h>
#include <cpu_func.h>
#include <dm.h>
#include <log.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>
#include <dm/device-internal.h>
#include <linux/delay.h>
#include "sata_sil.h"
#define virt_to_bus(devno, v) dm_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
*/
static ulong sata_read(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt,
void *buffer)
{
struct sil_sata_priv *priv = dev_get_plat(dev);
int port_number = priv->port_num;
struct sil_sata *sata = priv->sil_sata_desc[port_number];
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
*/
ulong sata_write(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt,
const void *buffer)
{
struct sil_sata_priv *priv = dev_get_plat(dev);
int port_number = priv->port_num;
struct sil_sata *sata = priv->sil_sata_desc[port_number];
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;
}
static int sil_init_sata(struct udevice *uc_dev, int dev)
{
struct sil_sata_priv *priv = dev_get_plat(uc_dev);
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 */
priv->sil_sata_desc[dev] = sata;
priv->port_num = dev;
sata->devno = uc_dev->parent;
sata->id = dev;
sata->port = port;
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;
}
static int scan_sata(struct udevice *blk_dev, int dev)
{
struct blk_desc *desc = dev_get_uclass_plat(blk_dev);
struct sil_sata_priv *priv = dev_get_plat(blk_dev);
struct sil_sata *sata = priv->sil_sata_desc[dev];
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));
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
#ifdef DEBUG
ata_dump_id(id);
#endif
free((void *)id);
return 0;
}
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,
.plat_auto = sizeof(struct sil_sata_priv),
};
static int sil_unbind_device(struct udevice *dev)
{
int ret;
ret = device_remove(dev, DM_REMOVE_NORMAL);
if (ret)
return ret;
ret = device_unbind(dev);
if (ret)
return ret;
return 0;
}
static int sil_pci_probe(struct udevice *dev)
{
struct udevice *blk;
int failed_number;
char sata_name[10];
pci_dev_t devno;
u16 word;
int ret;
int i;
failed_number = 0;
/* 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, 0, 0, PCI_REGION_TYPE,
PCI_REGION_MEM);
sata_info.iobase[1] = (ulong)dm_pci_map_bar(dev,
PCI_BASE_ADDRESS_2, 0, 0, PCI_REGION_TYPE,
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,
UCLASS_AHCI, -1, DEFAULT_BLKSZ,
0, &blk);
if (ret) {
debug("Can't create device\n");
return ret;
}
ret = sil_init_sata(blk, i);
if (ret) {
ret = sil_unbind_device(blk);
if (ret)
return ret;
failed_number++;
continue;
}
ret = scan_sata(blk, i);
if (ret) {
ret = sil_unbind_device(blk);
if (ret)
return ret;
failed_number++;
continue;
}
ret = device_probe(dev);
if (ret < 0) {
debug("Probing %s failed (%d)\n", dev->name, ret);
ret = sil_unbind_device(blk);
device_unbind(dev);
if (ret)
return ret;
failed_number++;
continue;
}
}
if (failed_number == sata_info.maxport)
return -ENODEV;
else
return 0;
}
static int sil_pci_remove(struct udevice *dev)
{
int i;
struct sil_sata *sata;
struct sil_sata_priv *priv;
priv = dev_get_priv(dev);
for (i = sata_info.portbase; i < sata_info.maxport; i++) {
sata = priv->sil_sata_desc[i];
if (sata)
free(sata);
}
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,
.remove = sil_pci_remove,
.priv_auto = sizeof(struct sil_sata_priv),
};
U_BOOT_PCI_DEVICE(sil_ahci_pci, supported);
+221
View File
@@ -0,0 +1,221 @@
/* 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;
struct udevice *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,
};
#define ATA_MAX_PORTS 32
struct sil_sata_priv {
int port_num;
struct sil_sata *sil_sata_desc[ATA_MAX_PORTS];
};
#endif
+32
View File
@@ -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
+11
View File
@@ -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
+87
View File
@@ -0,0 +1,87 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2018
* Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
*/
#define LOG_CATEGORY UCLASS_AXI_EMUL
#include <axi.h>
#include <dm.h>
#include <log.h>
#include <dm/device-internal.h>
#include <asm/axi.h>
int axi_sandbox_get_emul(struct udevice *bus, ulong address,
const 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",
};
+39
View File
@@ -0,0 +1,39 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2017
* Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
*/
#define LOG_CATEGORY UCLASS_AXI
#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,
};
+76
View File
@@ -0,0 +1,76 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2018
* Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
*/
#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,
};
+295
View File
@@ -0,0 +1,295 @@
// 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 <axi.h>
#include <dm.h>
#include <log.h>
#include <regmap.h>
#include <linux/bitops.h>
#include <linux/delay.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 = sizeof(struct ihs_axi_priv),
.probe = ihs_axi_probe,
};
+124
View File
@@ -0,0 +1,124 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2018
* Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
*/
#include <axi.h>
#include <dm.h>
#include <log.h>
#include <malloc.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 = sizeof(struct sandbox_store_priv),
.probe = sandbox_store_probe,
.remove = sandbox_store_remove,
};
+10
View File
@@ -0,0 +1,10 @@
config BIOSEMU
bool
select X86EMU_RAW_IO
config SPL_BIOSEMU
bool
select X86EMU_RAW_IO
config X86EMU_RAW_IO
bool
+14
View File
@@ -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__)
+549
View File
@@ -0,0 +1,549 @@
/****************************************************************************
*
* 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 <compiler.h>
#include <bios_emul.h>
#include <errno.h>
#include <log.h>
#include <malloc.h>
#include <vesa.h>
#include <linux/delay.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 vesa_state *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 vesa_bios_ext_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));
memcpy(info->signature, "VBE2", 4);
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 vesa_state));
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 vesa_state *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 vesa_state));
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.
****************************************************************************/
static void PCI_doBIOSPOST(struct udevice *pcidev, BE_VGAInfo *vga_info,
int vesa_mode, struct vesa_state *mode_info)
{
RMREGS regs;
RMSREGS sregs;
pci_dev_t bdf;
/* 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(&regs, 0, sizeof(regs));
memset(&sregs, 0, sizeof(sregs));
bdf = dm_pci_get_bdf(pcidev);
regs.x.ax = (int)PCI_BUS(bdf) << 8 |
(int)PCI_DEV(bdf) << 3 | (int)PCI_FUNC(bdf);
/*Setup the X86 emulator for the VGA BIOS*/
BE_setVGA(vga_info);
/*Execute the BIOS POST code*/
BE_callRealMode(0xC000, 0x0003, &regs, &sregs);
/*Cleanup and exit*/
BE_getVGA(vga_info);
#ifdef CONFIG_FRAMEBUFFER_SET_VESA_MODE
/* Useful for debugging */
if (0)
atibios_debug_mode(vga_info, &regs, vesa_mode, mode_info);
if (vesa_mode != -1)
atibios_set_vesa_mode(&regs, 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.
****************************************************************************/
static u32 PCI_findBIOSAddr(struct udevice *pcidev, int *bar)
{
u32 base, size;
for (*bar = 0x10; *bar <= 0x14; (*bar) += 4) {
dm_pci_read_config32(pcidev, *bar, &base);
if (!(base & 0x1)) {
dm_pci_write_config32(pcidev, *bar, 0xFFFFFFFF);
dm_pci_read_config32(pcidev, *bar, &size);
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.
****************************************************************************/
static void PCI_fixupIObase(struct udevice *pcidev, int reg, u32 *base)
{
if ((*base & 0x1) && (*base > 0xFFFE)) {
*base &= 0xFFFF;
dm_pci_write_config32(pcidev, reg, *base);
}
}
/****************************************************************************
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.
****************************************************************************/
void *PCI_mapBIOSImage(struct udevice *pcidev)
{
u32 BIOSImageBus;
int BIOSImageBAR;
u8 *BIOSImage;
/*Save PCI BAR registers that might get changed*/
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);
/*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;
}
BIOSImage = dm_pci_bus_to_virt(pcidev, BIOSImageBus, 0, PCI_REGION_TYPE,
PCI_REGION_MEM, 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);
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
****************************************************************************/
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);
}
/****************************************************************************
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.
****************************************************************************/
static int PCI_postController(struct udevice *pcidev, uchar *bios_rom,
int bios_len, BE_VGAInfo *vga_info,
int vesa_mode, struct vesa_state *mode_info)
{
u32 bios_image_len;
uchar *mapped_bios;
uchar *copy_of_bios;
pci_dev_t bdf;
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*/
bdf = dm_pci_get_bdf(pcidev);
vga_info->function = PCI_FUNC(bdf);
vga_info->device = PCI_DEV(bdf);
vga_info->bus = PCI_BUS(bdf);
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;
}
int biosemu_setup(struct udevice *pcidev, BE_VGAInfo **vga_infop)
{
BE_VGAInfo *VGAInfo;
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));
/*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);
}
int biosemu_run(struct udevice *pcidev, uchar *bios_rom, int bios_len,
BE_VGAInfo *vga_info, int clean_up, int vesa_mode,
struct vesa_state *mode_info)
{
/*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.
****************************************************************************/
int BootVideoCardBIOS(struct udevice *pcidev, BE_VGAInfo **pVGAInfo,
int clean_up)
{
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;
}
+754
View File
@@ -0,0 +1,754 @@
/****************************************************************************
*
* 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 <asm/io.h>
#include <pci.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)) {
pci_dev_t bdf = PCI_BDF(bus, device, function);
switch (func) {
case REG_READ_BYTE:
pci_read_config_byte(bdf, regOffset, &val8);
return val8;
case REG_READ_WORD:
pci_read_config_word(bdf, regOffset, &val16);
return val16;
case REG_READ_DWORD:
pci_read_config_dword(bdf, regOffset, &val32);
return val32;
case REG_WRITE_BYTE:
pci_write_config_byte(bdf, regOffset, value);
return 0;
case REG_WRITE_WORD:
pci_write_config_word(bdf, regOffset, value);
return 0;
case REG_WRITE_DWORD:
pci_write_config_dword(bdf, 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");
}
}
+323
View File
@@ -0,0 +1,323 @@
/****************************************************************************
*
* 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 <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__
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);
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__
dm_pci_read_config8(_BE_env.vgaInfo.pcidev, M.x86.R_DI,
&M.x86.R_CL);
#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__
dm_pci_read_config16(_BE_env.vgaInfo.pcidev, M.x86.R_DI,
&M.x86.R_CX);
#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__
dm_pci_read_config32(_BE_env.vgaInfo.pcidev,
M.x86.R_DI, &M.x86.R_ECX);
#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__
dm_pci_write_config8(_BE_env.vgaInfo.pcidev,
M.x86.R_DI, M.x86.R_CL);
#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__
dm_pci_write_config32(_BE_env.vgaInfo.pcidev,
M.x86.R_DI, M.x86.R_CX);
#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__
dm_pci_write_config32(_BE_env.vgaInfo.pcidev,
M.x86.R_DI, M.x86.R_ECX);
#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);
}
+371
View File
@@ -0,0 +1,371 @@
/****************************************************************************
*
* 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 "biosemui.h"
BE_sysEnv _BE_env = {{0}};
static X86EMU_memFuncs _BE_mem __section(GOT2_TYPE) = {
BE_rdb,
BE_rdw,
BE_rdl,
BE_wrb,
BE_wrw,
BE_wrl,
};
static X86EMU_pioFuncs _BE_pio __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;
}
+210
View File
@@ -0,0 +1,210 @@
/****************************************************************************
*
* 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;
#if defined(X86EMU_RAW_IO)
#define PM_inpb(port) inb(port)
#define PM_inpw(port) inw(port)
#define PM_inpd(port) inl(port)
#define PM_outpb(port, val) outb(val, port)
#define PM_outpw(port, val) outw(val, port)
#define PM_outpd(port, val) outl(val, port)
#else
/*
* Until the emulator code is fixed, at least print warnings.
*/
static inline u8 PM_inpb(u16 port)
{
printf("x86 port 0x%x read attempt, returning 0\n", port);
return 0;
}
static inline u16 PM_inpw(u16 port)
{
printf("x86 port 0x%x read attempt, returning 0\n", port);
return 0;
}
static inline u32 PM_inpd(u16 port)
{
printf("x86 port 0x%x read attempt, returning 0\n", port);
return 0;
}
static inline void PM_outpb(u16 port, u8 val)
{
printf("x86 port 0x%x write attempt, ignoring\n", port);
}
static inline void PM_outpw(u16 port, u16 val)
{
printf("x86 port 0x%x write attempt, ignoring\n", port);
}
static inline void PM_outpd(u16 port, u32 val)
{
printf("x86 port 0x%x write attempt, ignoring\n", port);
}
#endif
#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 */
+343
View File
@@ -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 */
+201
View File
@@ -0,0 +1,201 @@
/****************************************************************************
*
* 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 <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) || defined(CONFIG_RISCV)
#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" */
#include <linux/printk.h>
#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 */
+341
View File
@@ -0,0 +1,341 @@
/****************************************************************************
*
* 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 ----------------------*/
#include <linux/printk.h>
#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 */
+463
View File
@@ -0,0 +1,463 @@
/****************************************************************************
*
* 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 <string.h>
#include <vsprintf.h>
#include <linux/ctype.h>
#include <linux/printk.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
+329
View File
@@ -0,0 +1,329 @@
/****************************************************************************
*
* 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 <linux/printk.h>
#include "x86emu/x86emui.h"
/*------------------------- Global Variables ------------------------------*/
/* Note: bios.c defines this if the emulator is not enabled */
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;
}
+274
View File
@@ -0,0 +1,274 @@
config BLK
bool
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 SPL_LEGACY_BLOCK
bool # "Enable Legacy Block Device"
depends on SPL
default y if SPL_MMC || SPL_USB_STORAGE || SCSI || NVME || IDE
default y if SPL_AHCI_PCI
help
Some devices require block support whether or not DM is enabled. This
is only supported in SPL. With this, the blk uclass is not used, but
instead a legacy implementation of block devices is used, with all
devices consisting of 'struct blk_desc' records.
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
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 VPL_BLK
bool "Support block devices in VPL"
depends on VPL_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 BLKMAP
bool "Composable virtual block devices (blkmap)"
depends on BLK
help
Create virtual block devices that are backed by various sources,
e.g. RAM, or parts of an existing block device. Though much more
rudimentary, it borrows a lot of ideas from Linux's device mapper
subsystem.
Example use-cases:
- Treat a region of RAM as a block device, i.e. a RAM disk. This let's
you extract files from filesystem images stored in RAM (perhaps as a
result of a TFTP transfer).
- Create a virtual partition on an existing device. This let's you
access filesystems that aren't stored at an exact partition
boundary. A common example is a filesystem image embedded in an FIT
image.
config SPL_BLOCK_CACHE
bool "Use block device cache in SPL"
depends on SPL_BLK
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
help
This option enables the disk-block cache in TPL
config EFI_MEDIA
bool "Support EFI media drivers"
default y if EFI_CLIENT || SANDBOX
select BLK
help
Enable this to support media devices on top of UEFI. This enables
just the uclass so you also need a specific driver to make this do
anything.
For sandbox there is a test driver.
config SPL_BLK_FS
bool
depends on SPL_BLK
select SPL_FS_LOADER
help
Use generic support to load images from fat/ext filesystems on
different types of block devices such as NVMe.
if EFI_MEDIA
config EFI_MEDIA_SANDBOX
bool "Sandbox EFI media driver"
depends on SANDBOX
default y
help
Enables a simple sandbox media driver, used for testing just the
EFI_MEDIA uclass. It does not do anything useful, since sandbox does
not actually support running on top of UEFI.
config EFI_MEDIA_BLK
bool "EFI media block driver"
depends on EFI_APP
default y
help
Enables a block driver for providing access to UEFI devices. This
allows use of block devices detected by the underlying UEFI
implementation. With this it is possible to use filesystems on these
devices, for example.
endif # EFI_MEDIA
config IDE
bool "Support IDE controllers"
depends on !COMPILE_TEST
select BLK
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.
if IDE
config SYS_IDE_MAXBUS
hex "Maximum number of IDE buses"
default 2
help
This is the number of IDE buses provided by the board. Each one
can have one or two devices. One is designated the master and the
other one the slave. It is not required to have one or both on any
controller.
config SYS_IDE_MAXDEVICE
hex "Maximum number of IDE devices"
default 2
help
This is the number of IDE devices which can be connected to the
board. Normally this is 2 * CONFIG_SYS_IDE_MAXBUS since up to two
devices can be connected to each bus. The number of devices actually
connected is determined by probing.
config SYS_ATA_BASE_ADDR
hex "Base address of IDE controller"
default 0x0
help
This is the address of the IDE controller, from which other addresses
are calculated. Each bus is at a fixed offset from this address,
so it assumed that they are in the same area of the I/O space or
memory.
config SYS_ATA_STRIDE
hex "IDE port stride"
default 0x1
help
This is the distance between each IDE register, in bytes. For an
8-bit controller this is typically 1, meaning that the registers
appear at consecutive bytes. If the value 2 two, that might indicate
a 16-bit register space.
config SYS_ATA_DATA_OFFSET
hex "Offset of the data register"
default 0x0
help
This is the offset of the controller's data register from the base
address of the controller. This is typically 0, but may be something
else if there are some other registers at the start of the
controller space.
config SYS_ATA_REG_OFFSET
hex "Offset of the register space"
default 0x0
help
This is the offset of the controller's 'register' space from the base
address of the controller. The data register (which is typically at
offset 0) has its own CONFIG, to deal with controllers where it is
somewhere else. Register 1 will be at this offset + 1, register 2 at
CONFIG_SYS_ATA_REG_OFFSET + 2, etc.
config SYS_ATA_ALT_OFFSET
hex "Offset of the alternative registers"
default 0x0
help
This is the offset of the controller's 'alternative' space from the
base address of the controller. This allows these registers to be
located separately from the data and register space.
config SYS_ATA_IDE0_OFFSET
hex "Offset of bus 0"
default 0x1f0
help
This is the start offset of bus 0 from the start of the
controller registers. All the other registers are calculated from
this address. using the above options. For x86 hardware this is often
0x1f0.
config SYS_ATA_IDE1_OFFSET
hex "Offset of bus 1"
default 0x170
help
This is the start offset of bus 1 from the start of the
controller registers. All the other registers are calculated from
this address. using the above options. For x86 hardware this is often
0x170.
config ATAPI
bool "Enable ATAPI support"
help
This enabled Advanced Technology Attachment Packet Interface (ATAPI),
a protocol that allows a greater variety of devices to be connected
to the IDE port than with plain ATA. It allows SCSI commands to be
sent across the bus, e.g. to support optical drives.
config IDE_RESET
bool "Support board-specific reset"
help
If this is defined, IDE Reset will be performed by calling the
function:
ide_set_reset(int reset)
where reset is 1 to assert reset and 0 to de-assert it. This function
must be defined in a board-specific file.
endif # IDE
config LBA48
bool "Enable LBA support for disks larger than 137GB"
help
Set this to enable support for disks larger than 137GB.
Also look at CONFIG_SYS_64BIT_LBA. Without both of these, LBA48
support uses 32bit variables and will 'only' support disks up to
2.1TB.
config SYS_64BIT_LBA
bool "Enable 64bit number of blocks on a block device"
help
Make the block subsystem use 64bit sector addresses, rather than the
default of 32bit.
config RKMTD
bool "Rockchip rkmtd virtual block device"
select DEVRES
select RANDOM_UUID
help
Enable "rkmtd" class and driver to create a virtual block device
to transfer Rockchip boot block data to and from NAND with block
orientate tools like "ums" and "rockusb".
+24
View File
@@ -0,0 +1,24 @@
# SPDX-License-Identifier: GPL-2.0+
#
# (C) Copyright 2000-2007
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
obj-$(CONFIG_$(PHASE_)BLK) += blk-uclass.o
ifndef CONFIG_$(PHASE_)BLK
obj-$(CONFIG_SPL_LEGACY_BLOCK) += blk_legacy.o
endif
ifndef CONFIG_XPL_BUILD
obj-$(CONFIG_IDE) += ide.o
obj-$(CONFIG_RKMTD) += rkmtd.o
endif
obj-$(CONFIG_SANDBOX) += sandbox.o host-uclass.o host_dev.o
obj-$(CONFIG_$(PHASE_)BLOCK_CACHE) += blkcache.o
obj-$(CONFIG_$(PHASE_)BLKMAP) += blkmap.o
obj-$(CONFIG_$(PHASE_)BLKMAP) += blkmap_helper.o
obj-$(CONFIG_EFI_MEDIA) += efi-media-uclass.o
obj-$(CONFIG_EFI_MEDIA_SANDBOX) += sb_efi_media.o
obj-$(CONFIG_EFI_MEDIA_BLK) += efi_blk.o
+849
View File
@@ -0,0 +1,849 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2016 Google, Inc
* Written by Simon Glass <sjg@chromium.org>
*/
#define LOG_CATEGORY UCLASS_BLK
#include <blk.h>
#include <dm.h>
#include <log.h>
#include <malloc.h>
#include <part.h>
#include <dm/device-internal.h>
#include <dm/lists.h>
#include <dm/uclass-internal.h>
#include <linux/err.h>
#define blk_get_ops(dev) ((struct blk_ops *)(dev)->driver->ops)
static struct {
enum uclass_id id;
const char *name;
} uclass_idname_str[] = {
{ UCLASS_IDE, "ide" },
{ UCLASS_SCSI, "scsi" },
{ UCLASS_USB, "usb" },
{ UCLASS_MMC, "mmc" },
{ UCLASS_AHCI, "sata" },
{ UCLASS_HOST, "host" },
{ UCLASS_NVME, "nvme" },
{ UCLASS_NVMXIP, "nvmxip" },
{ UCLASS_EFI_MEDIA, "efi" },
{ UCLASS_EFI_LOADER, "efiloader" },
{ UCLASS_VIRTIO, "virtio" },
{ UCLASS_PVBLOCK, "pvblock" },
{ UCLASS_BLKMAP, "blkmap" },
{ UCLASS_RKMTD, "rkmtd" },
{ UCLASS_MTD, "mtd" },
{ UCLASS_MTD, "ubi" },
};
static enum uclass_id uclass_name_to_iftype(const char *uclass_idname)
{
int i;
for (i = 0; i < ARRAY_SIZE(uclass_idname_str); i++) {
if (!strcmp(uclass_idname, uclass_idname_str[i].name))
return uclass_idname_str[i].id;
}
return UCLASS_INVALID;
}
static enum uclass_id conv_uclass_id(enum uclass_id uclass_id)
{
/*
* This strange adjustment is used because we use UCLASS_MASS_STORAGE
* for USB storage devices, so need to return this as the uclass to
* use for USB. In fact USB_UCLASS is for USB controllers, not
* peripherals.
*
* The name of the UCLASS_MASS_STORAGE uclass driver is
* "usb_mass_storage", but we want to use "usb" in things like the
* 'part list' command and when showing interfaces.
*
* So for now we have this one-way conversion.
*
* The fix for this is possibly to:
* - rename UCLASS_MASS_STORAGE name to "usb"
* - rename UCLASS_USB name to "usb_ctlr"
* - use UCLASS_MASS_STORAGE instead of UCLASS_USB in if_typename_str
*/
if (uclass_id == UCLASS_USB)
return UCLASS_MASS_STORAGE;
return uclass_id;
}
const char *blk_get_uclass_name(enum uclass_id uclass_id)
{
int i;
for (i = 0; i < ARRAY_SIZE(uclass_idname_str); i++) {
if (uclass_idname_str[i].id == uclass_id)
return uclass_idname_str[i].name;
}
return "(none)";
}
struct blk_desc *blk_get_devnum_by_uclass_id(enum uclass_id uclass_id, int devnum)
{
struct blk_desc *desc;
struct udevice *dev;
int ret;
ret = blk_get_device(uclass_id, devnum, &dev);
if (ret)
return NULL;
desc = dev_get_uclass_plat(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_uclass_idname(const char *uclass_idname, int devnum)
{
enum uclass_id uclass_id;
enum uclass_id type;
struct udevice *dev;
struct uclass *uc;
int ret;
type = uclass_name_to_iftype(uclass_idname);
if (type == UCLASS_INVALID) {
debug("%s: Unknown interface type '%s'\n", __func__,
uclass_idname);
return NULL;
}
uclass_id = conv_uclass_id(type);
if (uclass_id == UCLASS_INVALID) {
debug("%s: Unknown uclass for interface type'\n",
blk_get_uclass_name(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_plat(dev);
debug("%s: uclass_id=%d, devnum=%d: %s, %d, %d\n", __func__,
type, devnum, dev->name, desc->uclass_id, 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_plat(child_dev);
}
debug("%s: No block device found\n", __func__);
return NULL;
}
int blk_get_desc(enum uclass_id uclass_id, 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_plat(dev);
debug("%s: uclass_id=%d, devnum=%d: %s, %d, %d\n", __func__,
uclass_id, devnum, dev->name, desc->uclass_id, desc->devnum);
if (desc->uclass_id == uclass_id) {
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 uclass_id uclass_id, int devnum, int hwpart)
{
struct udevice *dev;
int ret;
ret = blk_get_device(uclass_id, devnum, &dev);
if (ret)
return ret;
return blk_select_hwpart(dev, hwpart);
}
int blk_list_part(enum uclass_id uclass_id)
{
struct blk_desc *desc;
int devnum, ok;
int ret;
for (ok = 0, devnum = 0;; ++devnum) {
ret = blk_get_desc(uclass_id, 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 uclass_id uclass_id, int devnum)
{
struct blk_desc *desc;
int ret;
ret = blk_get_desc(uclass_id, devnum, &desc);
if (ret)
return ret;
if (desc->type == DEV_TYPE_UNKNOWN)
return -ENOENT;
part_print(desc);
return 0;
}
void blk_list_devices(enum uclass_id uclass_id)
{
struct blk_desc *desc;
int ret;
int i;
for (i = 0;; ++i) {
ret = blk_get_desc(uclass_id, 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 uclass_id uclass_id, int devnum)
{
struct blk_desc *desc;
int ret;
ret = blk_get_desc(uclass_id, devnum, &desc);
if (ret)
return ret;
printf("\nIDE device %d: ", devnum);
dev_print(desc);
return 0;
}
int blk_show_device(enum uclass_id uclass_id, int devnum)
{
struct blk_desc *desc;
int ret;
printf("\nDevice %d: ", devnum);
ret = blk_get_desc(uclass_id, 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;
}
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);
}
static int _blk_next_device(int uclass_id, struct udevice **devp)
{
struct blk_desc *desc;
int ret = 0;
for (; *devp; uclass_find_next_device(devp)) {
desc = dev_get_uclass_plat(*devp);
if (desc->uclass_id == uclass_id) {
ret = device_probe(*devp);
if (!ret)
return 0;
}
}
if (ret)
return ret;
return -ENODEV;
}
int blk_first_device(int uclass_id, struct udevice **devp)
{
uclass_find_first_device(UCLASS_BLK, devp);
return _blk_next_device(uclass_id, devp);
}
int blk_next_device(struct udevice **devp)
{
struct blk_desc *desc;
int uclass_id;
desc = dev_get_uclass_plat(*devp);
uclass_id = desc->uclass_id;
uclass_find_next_device(devp);
return _blk_next_device(uclass_id, devp);
}
int blk_find_device(int uclass_id, 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_plat(dev);
debug("%s: uclass_id=%d, devnum=%d: %s, %d, %d\n", __func__,
uclass_id, devnum, dev->name, desc->uclass_id, desc->devnum);
if (desc->uclass_id == uclass_id && desc->devnum == devnum) {
*devp = dev;
return 0;
}
}
return -ENODEV;
}
int blk_get_device(int uclass_id, int devnum, struct udevice **devp)
{
int ret;
ret = blk_find_device(uclass_id, devnum, devp);
if (ret)
return ret;
return device_probe(*devp);
}
struct blk_bounce_buffer {
struct udevice *dev;
struct bounce_buffer state;
};
static int blk_buffer_aligned(struct bounce_buffer *state)
{
#if IS_ENABLED(CONFIG_BOUNCE_BUFFER)
struct blk_bounce_buffer *bbstate =
container_of(state, struct blk_bounce_buffer, state);
struct udevice *dev = bbstate->dev;
const struct blk_ops *ops = blk_get_ops(dev);
if (ops->buffer_aligned)
return ops->buffer_aligned(dev, state);
#endif /* CONFIG_BOUNCE_BUFFER */
return 1; /* Default, any buffer is OK */
}
long blk_read(struct udevice *dev, lbaint_t start, lbaint_t blkcnt, void *buf)
{
struct blk_desc *desc = dev_get_uclass_plat(dev);
const struct blk_ops *ops = blk_get_ops(dev);
ulong blks_read;
if (!ops->read)
return -ENOSYS;
if (blkcache_read(desc->uclass_id, desc->devnum,
start, blkcnt, desc->blksz, buf))
return blkcnt;
if (IS_ENABLED(CONFIG_BOUNCE_BUFFER) && desc->bb) {
struct blk_bounce_buffer bbstate = { .dev = dev };
int ret;
ret = bounce_buffer_start_extalign(&bbstate.state, buf,
blkcnt * desc->blksz,
GEN_BB_WRITE, desc->blksz,
blk_buffer_aligned);
if (ret)
return ret;
blks_read = ops->read(dev, start, blkcnt, bbstate.state.bounce_buffer);
bounce_buffer_stop(&bbstate.state);
} else {
blks_read = ops->read(dev, start, blkcnt, buf);
}
if (blks_read == blkcnt)
blkcache_fill(desc->uclass_id, desc->devnum, start, blkcnt,
desc->blksz, buf);
return blks_read;
}
long blk_write(struct udevice *dev, lbaint_t start, lbaint_t blkcnt,
const void *buf)
{
struct blk_desc *desc = dev_get_uclass_plat(dev);
const struct blk_ops *ops = blk_get_ops(dev);
long blks_written;
if (!ops->write)
return -ENOSYS;
blkcache_invalidate(desc->uclass_id, desc->devnum);
if (IS_ENABLED(CONFIG_BOUNCE_BUFFER) && desc->bb) {
struct blk_bounce_buffer bbstate = { .dev = dev };
int ret;
ret = bounce_buffer_start_extalign(&bbstate.state, (void *)buf,
blkcnt * desc->blksz,
GEN_BB_READ, desc->blksz,
blk_buffer_aligned);
if (ret)
return ret;
blks_written = ops->write(dev, start, blkcnt,
bbstate.state.bounce_buffer);
bounce_buffer_stop(&bbstate.state);
} else {
blks_written = ops->write(dev, start, blkcnt, buf);
}
return blks_written;
}
long blk_erase(struct udevice *dev, lbaint_t start, lbaint_t blkcnt)
{
struct blk_desc *desc = dev_get_uclass_plat(dev);
const struct blk_ops *ops = blk_get_ops(dev);
if (!ops->erase)
return -ENOSYS;
blkcache_invalidate(desc->uclass_id, desc->devnum);
return ops->erase(dev, start, blkcnt);
}
ulong blk_dread(struct blk_desc *desc, lbaint_t start, lbaint_t blkcnt,
void *buffer)
{
return blk_read(desc->bdev, start, blkcnt, buffer);
}
ulong blk_dwrite(struct blk_desc *desc, lbaint_t start, lbaint_t blkcnt,
const void *buffer)
{
return blk_write(desc->bdev, start, blkcnt, buffer);
}
ulong blk_derase(struct blk_desc *desc, lbaint_t start, lbaint_t blkcnt)
{
return blk_erase(desc->bdev, start, blkcnt);
}
int blk_find_from_parent(struct udevice *parent, struct udevice **devp)
{
struct udevice *dev;
if (device_find_first_child_by_uclass(parent, UCLASS_BLK, &dev)) {
debug("%s: No block device found for parent '%s'\n", __func__,
parent->name);
return -ENODEV;
}
*devp = dev;
return 0;
}
int blk_get_from_parent(struct udevice *parent, struct udevice **devp)
{
struct udevice *dev;
int ret;
ret = blk_find_from_parent(parent, &dev);
if (ret)
return ret;
ret = device_probe(dev);
if (ret)
return ret;
*devp = dev;
return 0;
}
const char *blk_get_devtype(struct udevice *dev)
{
struct udevice *parent = dev_get_parent(dev);
return uclass_get_name(device_get_uclass_id(parent));
};
int blk_find_max_devnum(enum uclass_id uclass_id)
{
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_plat(dev);
if (desc->uclass_id == uclass_id && desc->devnum > max_devnum)
max_devnum = desc->devnum;
}
return max_devnum;
}
int blk_next_free_devnum(enum uclass_id uclass_id)
{
int ret;
ret = blk_find_max_devnum(uclass_id);
if (ret == -ENODEV)
return 0;
if (ret < 0)
return ret;
return ret + 1;
}
static int blk_flags_check(struct udevice *dev, enum blk_flag_t req_flags)
{
const struct blk_desc *desc = dev_get_uclass_plat(dev);
enum blk_flag_t flags;
flags = desc->removable ? BLKF_REMOVABLE : BLKF_FIXED;
return flags & req_flags ? 0 : 1;
}
int blk_first_device_err(enum blk_flag_t flags, struct udevice **devp)
{
for (uclass_first_device(UCLASS_BLK, devp);
*devp;
uclass_next_device(devp)) {
if (!blk_flags_check(*devp, flags))
return 0;
}
return -ENODEV;
}
int blk_next_device_err(enum blk_flag_t flags, struct udevice **devp)
{
for (uclass_next_device(devp);
*devp;
uclass_next_device(devp)) {
if (!blk_flags_check(*devp, flags))
return 0;
}
return -ENODEV;
}
int blk_count_devices(enum blk_flag_t flag)
{
struct udevice *dev;
int count = 0;
blk_foreach_probe(flag, dev)
count++;
return count;
}
static int blk_claim_devnum(enum uclass_id uclass_id, 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_plat(dev);
if (desc->uclass_id == uclass_id && desc->devnum == devnum) {
int next = blk_next_free_devnum(uclass_id);
if (next < 0)
return next;
desc->devnum = next;
return 0;
}
}
return -ENOENT;
}
/**
* blk_create_device() - Create a new block device
*
* @parent: Parent of the new device
* @drv_name: Driver name to use for the block device
* @name: Name for the device
* @uclass_id: Interface type (enum uclass_id_t)
* @devnum: Device number, specific to the interface type, or -1 to
* allocate the next available number
* @blksz: Block size of the device in bytes (typically 512)
* @lba: Total number of blocks of the device
* @devp: the new device (which has not been probed)
*/
static int blk_create_device(struct udevice *parent, const char *drv_name,
const char *name, int uclass_id, 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(uclass_id);
} else {
ret = blk_claim_devnum(uclass_id, 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_plat(dev);
desc->uclass_id = uclass_id;
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 uclass_id, 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, uclass_id, devnum,
blksz, lba, devp);
if (ret) {
free(str);
return ret;
}
device_set_name_alloced(*devp);
return 0;
}
int blk_probe_or_unbind(struct udevice *dev)
{
int ret;
ret = device_probe(dev);
if (ret) {
log_debug("probing %s failed\n", dev->name);
device_unbind(dev);
}
return ret;
}
int blk_unbind_all(int uclass_id)
{
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_plat(dev);
if (desc->uclass_id == uclass_id) {
ret = device_remove(dev, DM_REMOVE_NORMAL);
if (ret)
return ret;
ret = device_unbind(dev);
if (ret)
return ret;
}
}
return 0;
}
static int part_create_block_devices(struct udevice *blk_dev)
{
int part, count;
struct blk_desc *desc = dev_get_uclass_plat(blk_dev);
struct disk_partition info;
struct disk_part *part_data;
char devname[32];
struct udevice *dev;
int ret;
if (!CONFIG_IS_ENABLED(PARTITIONS) || !blk_enabled())
return 0;
if (device_get_uclass_id(blk_dev) != UCLASS_BLK)
return 0;
/* Add devices for each partition */
for (count = 0, part = 1; part <= MAX_SEARCH_PARTITIONS; part++) {
if (part_get_info(desc, part, &info))
continue;
snprintf(devname, sizeof(devname), "%s:%d", blk_dev->name,
part);
ret = device_bind_driver(blk_dev, "blk_partition",
strdup(devname), &dev);
if (ret)
return ret;
part_data = dev_get_uclass_plat(dev);
part_data->partnum = part;
part_data->gpt_part_info = info;
count++;
ret = device_probe(dev);
if (ret) {
debug("Can't probe\n");
count--;
device_unbind(dev);
continue;
}
}
debug("%s: %d partitions found in %s\n", __func__, count,
blk_dev->name);
return 0;
}
static int blk_post_probe(struct udevice *dev)
{
if (CONFIG_IS_ENABLED(PARTITIONS) && blk_enabled()) {
struct blk_desc *desc = dev_get_uclass_plat(dev);
part_init(desc);
if (desc->part_type != PART_TYPE_UNKNOWN &&
part_create_block_devices(dev))
debug("*** creating partitions failed\n");
}
return 0;
}
UCLASS_DRIVER(blk) = {
.id = UCLASS_BLK,
.name = "blk",
.post_probe = blk_post_probe,
.per_device_plat_auto = sizeof(struct blk_desc),
};
+263
View File
@@ -0,0 +1,263 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2016 Google, Inc
* Written by Simon Glass <sjg@chromium.org>
*/
#include <blk.h>
#include <part.h>
#include <linux/err.h>
struct blk_driver *blk_driver_lookup_type(int uclass_id)
{
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 (uclass_id == entry->uclass_id)
return entry;
}
/* Not found */
return NULL;
}
static struct blk_driver *blk_driver_lookup_typename(const char *uclass_idname)
{
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(uclass_idname, entry->uclass_idname))
return entry;
}
/* Not found */
return NULL;
}
const char *blk_get_uclass_name(enum uclass_id uclass_id)
{
struct blk_driver *drv = blk_driver_lookup_type(uclass_id);
return drv ? drv->uclass_idname : 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);
}
int blk_list_part(enum uclass_id uclass_id)
{
struct blk_driver *drv;
struct blk_desc *desc;
int devnum, ok;
bool first = true;
drv = blk_driver_lookup_type(uclass_id);
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 uclass_id uclass_id, int devnum)
{
struct blk_driver *drv = blk_driver_lookup_type(uclass_id);
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 uclass_id uclass_id)
{
struct blk_driver *drv = blk_driver_lookup_type(uclass_id);
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 uclass_id uclass_id, int devnum)
{
struct blk_driver *drv = blk_driver_lookup_type(uclass_id);
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->uclass_idname, devnum);
dev_print(desc);
return 0;
}
int blk_show_device(enum uclass_id uclass_id, int devnum)
{
struct blk_driver *drv = blk_driver_lookup_type(uclass_id);
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;
}
struct blk_desc *blk_get_devnum_by_uclass_id(enum uclass_id uclass_id, int devnum)
{
struct blk_driver *drv = blk_driver_lookup_type(uclass_id);
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->uclass_id);
if (!drv)
return -ENOSYS;
if (drv->select_hwpart)
return drv->select_hwpart(desc, hwpart);
return 0;
}
struct blk_desc *blk_get_devnum_by_uclass_idname(const char *uclass_idname, int devnum)
{
struct blk_driver *drv = blk_driver_lookup_typename(uclass_idname);
struct blk_desc *desc;
if (!drv)
return NULL;
if (get_desc(drv, devnum, &desc))
return NULL;
return desc;
}
ulong blk_read_devnum(enum uclass_id uclass_id, int devnum, lbaint_t start,
lbaint_t blkcnt, void *buffer)
{
struct blk_driver *drv = blk_driver_lookup_type(uclass_id);
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 uclass_id uclass_id, int devnum, lbaint_t start,
lbaint_t blkcnt, const void *buffer)
{
struct blk_driver *drv = blk_driver_lookup_type(uclass_id);
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 uclass_id uclass_id, int devnum, int hwpart)
{
struct blk_driver *drv = blk_driver_lookup_type(uclass_id);
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);
}
+170
View File
@@ -0,0 +1,170 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) Nelson Integration, LLC 2016
* Author: Eric Nelson<eric@nelint.com>
*
*/
#include <blk.h>
#include <log.h>
#include <malloc.h>
#include <part.h>
#include <asm/global_data.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 (iftype == -1 ||
(node->iftype == iftype && node->devnum == devnum)) {
list_del(entry);
free(node->cache);
free(node);
--_stats.entries;
}
}
}
void blkcache_configure(unsigned blocks, unsigned entries)
{
/* invalidate cache if there is a change */
if ((blocks != _stats.max_blocks_per_entry) ||
(entries != _stats.max_entries))
blkcache_invalidate(-1, 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;
}
void blkcache_free(void)
{
blkcache_invalidate(-1, 0);
}
+577
View File
@@ -0,0 +1,577 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2023 Addiva Elektronik
* Author: Tobias Waldekranz <tobias@waldekranz.com>
*/
#include <blk.h>
#include <blkmap.h>
#include <dm.h>
#include <malloc.h>
#include <mapmem.h>
#include <part.h>
#include <dm/device-internal.h>
#include <dm/lists.h>
#include <dm/root.h>
struct blkmap;
/**
* define BLKMAP_SLICE_LINEAR - Linear mapping to another block device
*
* This blkmap slice type is used for mapping to other existing block
* devices.
*/
#define BLKMAP_SLICE_LINEAR BIT(0)
/**
* define BLKMAP_SLICE_MEM - Linear mapping to memory based block device
*
* This blkmap slice type is used for mapping to memory based block
* devices, like ramdisks.
*/
#define BLKMAP_SLICE_MEM BIT(1)
/**
* define BLKMAP_SLICE_PRESERVE - Preserved blkmap slice
*
* This blkmap slice is intended to be preserved, and it's
* information passed on to a later stage, like OS.
*/
#define BLKMAP_SLICE_PRESERVE BIT(2)
/**
* struct blkmap_slice - Region mapped to a blkmap
*
* Common data for a region mapped to a blkmap, specialized by each
* map type.
*
* @node: List node used to associate this slice with a blkmap
* @blknr: Start block number of the mapping
* @blkcnt: Number of blocks covered by this mapping
* @attr: Attributes of blkmap slice
*/
struct blkmap_slice {
struct list_head node;
lbaint_t blknr;
lbaint_t blkcnt;
uint attr;
/**
* @read: - Read from slice
*
* @read.bm: Blkmap to which this slice belongs
* @read.bms: This slice
* @read.blknr: Start block number to read from
* @read.blkcnt: Number of blocks to read
* @read.buffer: Buffer to store read data to
*/
ulong (*read)(struct blkmap *bm, struct blkmap_slice *bms,
lbaint_t blknr, lbaint_t blkcnt, void *buffer);
/**
* @write: - Write to slice
*
* @write.bm: Blkmap to which this slice belongs
* @write.bms: This slice
* @write.blknr: Start block number to write to
* @write.blkcnt: Number of blocks to write
* @write.buffer: Data to be written
*/
ulong (*write)(struct blkmap *bm, struct blkmap_slice *bms,
lbaint_t blknr, lbaint_t blkcnt, const void *buffer);
/**
* @destroy: - Tear down slice
*
* @read.bm: Blkmap to which this slice belongs
* @read.bms: This slice
*/
void (*destroy)(struct blkmap *bm, struct blkmap_slice *bms);
};
static bool blkmap_slice_contains(struct blkmap_slice *bms, lbaint_t blknr)
{
return (blknr >= bms->blknr) && (blknr < (bms->blknr + bms->blkcnt));
}
static bool blkmap_slice_available(struct blkmap *bm, struct blkmap_slice *new)
{
struct blkmap_slice *bms;
lbaint_t first, last;
first = new->blknr;
last = new->blknr + new->blkcnt - 1;
list_for_each_entry(bms, &bm->slices, node) {
if (blkmap_slice_contains(bms, first) ||
blkmap_slice_contains(bms, last) ||
blkmap_slice_contains(new, bms->blknr) ||
blkmap_slice_contains(new, bms->blknr + bms->blkcnt - 1))
return false;
}
return true;
}
static int blkmap_slice_add(struct blkmap *bm, struct blkmap_slice *new)
{
struct blk_desc *bd = dev_get_uclass_plat(bm->blk);
struct list_head *insert = &bm->slices;
struct blkmap_slice *bms;
if (!blkmap_slice_available(bm, new))
return -EBUSY;
list_for_each_entry(bms, &bm->slices, node) {
if (bms->blknr < new->blknr)
continue;
insert = &bms->node;
break;
}
list_add_tail(&new->node, insert);
/* Disk might have grown, update the size */
bms = list_last_entry(&bm->slices, struct blkmap_slice, node);
bd->lba = bms->blknr + bms->blkcnt;
return 0;
}
/**
* struct blkmap_linear - Linear mapping to other block device
*
* @slice: Common map data
* @blk: Target block device of this mapping
* @blknr: Start block number of the target device
*/
struct blkmap_linear {
struct blkmap_slice slice;
struct udevice *blk;
lbaint_t blknr;
};
static ulong blkmap_linear_read(struct blkmap *bm, struct blkmap_slice *bms,
lbaint_t blknr, lbaint_t blkcnt, void *buffer)
{
struct blkmap_linear *bml = container_of(bms, struct blkmap_linear, slice);
return blk_read(bml->blk, bml->blknr + blknr, blkcnt, buffer);
}
static ulong blkmap_linear_write(struct blkmap *bm, struct blkmap_slice *bms,
lbaint_t blknr, lbaint_t blkcnt,
const void *buffer)
{
struct blkmap_linear *bml = container_of(bms, struct blkmap_linear, slice);
return blk_write(bml->blk, bml->blknr + blknr, blkcnt, buffer);
}
int blkmap_map_linear(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt,
struct udevice *lblk, lbaint_t lblknr)
{
struct blkmap *bm = dev_get_plat(dev);
struct blkmap_linear *linear;
struct blk_desc *bd, *lbd;
int err;
bd = dev_get_uclass_plat(bm->blk);
lbd = dev_get_uclass_plat(lblk);
if (lbd->blksz != bd->blksz) {
/* update to match the mapped device */
bd->blksz = lbd->blksz;
bd->log2blksz = LOG2(bd->blksz);
}
linear = malloc(sizeof(*linear));
if (!linear)
return -ENOMEM;
*linear = (struct blkmap_linear) {
.slice = {
.blknr = blknr,
.blkcnt = blkcnt,
.attr = BLKMAP_SLICE_LINEAR,
.read = blkmap_linear_read,
.write = blkmap_linear_write,
},
.blk = lblk,
.blknr = lblknr,
};
err = blkmap_slice_add(bm, &linear->slice);
if (err)
free(linear);
return err;
}
/**
* struct blkmap_mem - Memory mapping
*
* @slice: Common map data
* @addr: Target memory region of this mapping
* @remapped: True if @addr is backed by a physical to virtual memory
* mapping that must be torn down at the end of this mapping's
* lifetime.
*/
struct blkmap_mem {
struct blkmap_slice slice;
void *addr;
bool remapped;
};
static ulong blkmap_mem_read(struct blkmap *bm, struct blkmap_slice *bms,
lbaint_t blknr, lbaint_t blkcnt, void *buffer)
{
struct blkmap_mem *bmm = container_of(bms, struct blkmap_mem, slice);
struct blk_desc *bd = dev_get_uclass_plat(bm->blk);
char *src;
src = bmm->addr + (blknr << bd->log2blksz);
memcpy(buffer, src, blkcnt << bd->log2blksz);
return blkcnt;
}
static ulong blkmap_mem_write(struct blkmap *bm, struct blkmap_slice *bms,
lbaint_t blknr, lbaint_t blkcnt,
const void *buffer)
{
struct blkmap_mem *bmm = container_of(bms, struct blkmap_mem, slice);
struct blk_desc *bd = dev_get_uclass_plat(bm->blk);
char *dst;
dst = bmm->addr + (blknr << bd->log2blksz);
memcpy(dst, buffer, blkcnt << bd->log2blksz);
return blkcnt;
}
static void blkmap_mem_destroy(struct blkmap *bm, struct blkmap_slice *bms)
{
struct blkmap_mem *bmm = container_of(bms, struct blkmap_mem, slice);
if (bmm->remapped)
unmap_sysmem(bmm->addr);
}
int __blkmap_map_mem(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt,
void *addr, bool remapped, bool preserve)
{
struct blkmap *bm = dev_get_plat(dev);
struct blkmap_mem *bmm;
int err;
bmm = malloc(sizeof(*bmm));
if (!bmm)
return -ENOMEM;
*bmm = (struct blkmap_mem) {
.slice = {
.blknr = blknr,
.blkcnt = blkcnt,
.attr = BLKMAP_SLICE_MEM,
.read = blkmap_mem_read,
.write = blkmap_mem_write,
.destroy = blkmap_mem_destroy,
},
.addr = addr,
.remapped = remapped,
};
if (preserve)
bmm->slice.attr |= BLKMAP_SLICE_PRESERVE;
err = blkmap_slice_add(bm, &bmm->slice);
if (err)
free(bmm);
return err;
}
int blkmap_map_mem(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt,
void *addr)
{
return __blkmap_map_mem(dev, blknr, blkcnt, addr, false, false);
}
int blkmap_map_pmem(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt,
phys_addr_t paddr, bool preserve)
{
struct blkmap *bm = dev_get_plat(dev);
struct blk_desc *bd = dev_get_uclass_plat(bm->blk);
void *addr;
int err;
addr = map_sysmem(paddr, blkcnt << bd->log2blksz);
if (!addr)
return -ENOMEM;
err = __blkmap_map_mem(dev, blknr, blkcnt, addr, true, preserve);
if (err)
unmap_sysmem(addr);
return err;
}
static ulong blkmap_blk_read_slice(struct blkmap *bm, struct blkmap_slice *bms,
lbaint_t blknr, lbaint_t blkcnt,
void *buffer)
{
lbaint_t nr, cnt;
nr = blknr - bms->blknr;
cnt = (blkcnt < bms->blkcnt) ? blkcnt : bms->blkcnt;
return bms->read(bm, bms, nr, cnt, buffer);
}
static ulong blkmap_blk_read(struct udevice *dev, lbaint_t blknr,
lbaint_t blkcnt, void *buffer)
{
struct blk_desc *bd = dev_get_uclass_plat(dev);
struct blkmap *bm = dev_get_plat(dev->parent);
struct blkmap_slice *bms;
lbaint_t cnt, total = 0;
list_for_each_entry(bms, &bm->slices, node) {
if (!blkmap_slice_contains(bms, blknr))
continue;
cnt = blkmap_blk_read_slice(bm, bms, blknr, blkcnt, buffer);
blknr += cnt;
blkcnt -= cnt;
buffer += cnt << bd->log2blksz;
total += cnt;
}
return total;
}
static ulong blkmap_blk_write_slice(struct blkmap *bm, struct blkmap_slice *bms,
lbaint_t blknr, lbaint_t blkcnt,
const void *buffer)
{
lbaint_t nr, cnt;
nr = blknr - bms->blknr;
cnt = (blkcnt < bms->blkcnt) ? blkcnt : bms->blkcnt;
return bms->write(bm, bms, nr, cnt, buffer);
}
static ulong blkmap_blk_write(struct udevice *dev, lbaint_t blknr,
lbaint_t blkcnt, const void *buffer)
{
struct blk_desc *bd = dev_get_uclass_plat(dev);
struct blkmap *bm = dev_get_plat(dev->parent);
struct blkmap_slice *bms;
lbaint_t cnt, total = 0;
list_for_each_entry(bms, &bm->slices, node) {
if (!blkmap_slice_contains(bms, blknr))
continue;
cnt = blkmap_blk_write_slice(bm, bms, blknr, blkcnt, buffer);
blknr += cnt;
blkcnt -= cnt;
buffer += cnt << bd->log2blksz;
total += cnt;
}
return total;
}
static const struct blk_ops blkmap_blk_ops = {
.read = blkmap_blk_read,
.write = blkmap_blk_write,
};
U_BOOT_DRIVER(blkmap_blk) = {
.name = "blkmap_blk",
.id = UCLASS_BLK,
.ops = &blkmap_blk_ops,
};
static int blkmap_dev_bind(struct udevice *dev)
{
struct blkmap *bm = dev_get_plat(dev);
struct blk_desc *bd;
int err;
err = blk_create_devicef(dev, "blkmap_blk", "blk", UCLASS_BLKMAP,
dev_seq(dev), DEFAULT_BLKSZ, 0, &bm->blk);
if (err)
return log_msg_ret("blk", err);
INIT_LIST_HEAD(&bm->slices);
bd = dev_get_uclass_plat(bm->blk);
snprintf(bd->vendor, BLK_VEN_SIZE, "U-Boot");
snprintf(bd->product, BLK_PRD_SIZE, "blkmap");
snprintf(bd->revision, BLK_REV_SIZE, "1.0");
/* EFI core isn't keen on zero-sized disks, so we lie. This is
* updated with the correct size once the user adds a
* mapping.
*/
bd->lba = 1;
return 0;
}
static int blkmap_dev_unbind(struct udevice *dev)
{
struct blkmap *bm = dev_get_plat(dev);
struct blkmap_slice *bms, *tmp;
int err;
list_for_each_entry_safe(bms, tmp, &bm->slices, node) {
list_del(&bms->node);
free(bms);
}
err = device_remove(bm->blk, DM_REMOVE_NORMAL);
if (err)
return err;
return device_unbind(bm->blk);
}
U_BOOT_DRIVER(blkmap_root) = {
.name = "blkmap_dev",
.id = UCLASS_BLKMAP,
.bind = blkmap_dev_bind,
.unbind = blkmap_dev_unbind,
.plat_auto = sizeof(struct blkmap),
};
struct udevice *blkmap_from_label(const char *label)
{
struct udevice *dev;
struct uclass *uc;
struct blkmap *bm;
uclass_id_foreach_dev(UCLASS_BLKMAP, dev, uc) {
bm = dev_get_plat(dev);
if (bm->label && !strcmp(label, bm->label))
return dev;
}
return NULL;
}
int blkmap_create(const char *label, struct udevice **devp)
{
char *hname, *hlabel;
struct udevice *dev;
struct blkmap *bm;
size_t namelen;
int err;
dev = blkmap_from_label(label);
if (dev) {
err = -EBUSY;
goto err;
}
hlabel = strdup(label);
if (!hlabel) {
err = -ENOMEM;
goto err;
}
namelen = strlen("blkmap-") + strlen(label) + 1;
hname = malloc(namelen);
if (!hname) {
err = -ENOMEM;
goto err_free_hlabel;
}
strlcpy(hname, "blkmap-", namelen);
strlcat(hname, label, namelen);
err = device_bind_driver(dm_root(), "blkmap_dev", hname, &dev);
if (err)
goto err_free_hname;
device_set_name_alloced(dev);
bm = dev_get_plat(dev);
bm->label = hlabel;
if (devp)
*devp = dev;
return 0;
err_free_hname:
free(hname);
err_free_hlabel:
free(hlabel);
err:
return err;
}
static bool blkmap_mem_preserve_slice(struct blkmap_slice *bms)
{
return (bms->attr & (BLKMAP_SLICE_MEM | BLKMAP_SLICE_PRESERVE)) ==
(BLKMAP_SLICE_MEM | BLKMAP_SLICE_PRESERVE);
}
int blkmap_get_preserved_pmem_slices(int (*cb)(void *ctx, u64 addr,
u64 size), void *ctx)
{
int ret;
u64 addr, size;
struct udevice *dev;
struct uclass *uc;
struct blkmap *bm;
struct blkmap_mem *bmm;
struct blkmap_slice *bms;
struct blk_desc *bd;
if (!cb) {
log_debug("%s: No callback passed to the function\n", __func__);
return 0;
}
uclass_id_foreach_dev(UCLASS_BLKMAP, dev, uc) {
bm = dev_get_plat(dev);
bd = dev_get_uclass_plat(bm->blk);
list_for_each_entry(bms, &bm->slices, node) {
if (!blkmap_mem_preserve_slice(bms))
continue;
bmm = container_of(bms, struct blkmap_mem, slice);
addr = (u64)(uintptr_t)bmm->addr;
size = (u64)bms->blkcnt << bd->log2blksz;
ret = cb(ctx, addr, size);
if (ret)
return ret;
}
}
return 0;
}
int blkmap_destroy(struct udevice *dev)
{
int err;
err = device_remove(dev, DM_REMOVE_NORMAL);
if (err)
return err;
return device_unbind(dev);
}
UCLASS_DRIVER(blkmap) = {
.id = UCLASS_BLKMAP,
.name = "blkmap",
};
+53
View File
@@ -0,0 +1,53 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* blkmap helper function
*
* Copyright (c) 2023, Linaro Limited
*/
#include <blk.h>
#include <blkmap.h>
#include <dm/device.h>
#include <dm/device-internal.h>
int blkmap_create_ramdisk(const char *label, ulong image_addr, ulong image_size,
struct udevice **devp)
{
int ret;
lbaint_t blknum;
struct blkmap *bm;
struct blk_desc *desc;
struct udevice *bm_dev;
ret = blkmap_create(label, &bm_dev);
if (ret) {
log_err("failed to create blkmap\n");
return ret;
}
bm = dev_get_plat(bm_dev);
desc = dev_get_uclass_plat(bm->blk);
blknum = image_size >> desc->log2blksz;
ret = blkmap_map_pmem(bm_dev, 0, blknum, image_addr, true);
if (ret) {
log_err("Unable to map %#llx at block %d : %d\n",
(unsigned long long)image_addr, 0, ret);
goto err;
}
log_info("Block %d+0x" LBAF " mapped to %#llx\n", 0, blknum,
(unsigned long long)image_addr);
ret = device_probe(bm->blk);
if (ret)
goto err;
if (devp)
*devp = bm_dev;
return 0;
err:
blkmap_destroy(bm_dev);
return ret;
}
+14
View File
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Uclass for EFI media devices
*
* Copyright 2021 Google LLC
*/
#include <dm.h>
UCLASS_DRIVER(efi_media) = {
.id = UCLASS_EFI_MEDIA,
.name = "efi_media",
.flags = DM_UC_FLAG_SEQ_ALIAS,
};
+114
View File
@@ -0,0 +1,114 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Block driver for EFI devices
* This supports a media driver of UCLASS_EFI with a child UCLASS_BLK
* It allows block-level access to EFI devices made available via EFI boot
* services
*
* Copyright 2021 Google LLC
*/
#include <blk.h>
#include <dm.h>
#include <efi.h>
#include <efi_api.h>
struct efi_block_plat {
struct efi_block_io *blkio;
};
/**
* Read from block device
*
* @dev: device
* @blknr: first block to be read
* @blkcnt: number of blocks to read
* @buffer: output buffer
* Return: number of blocks transferred
*/
static ulong efi_bl_read(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt,
void *buffer)
{
struct efi_block_plat *plat = dev_get_plat(dev);
struct efi_block_io *io = plat->blkio;
efi_status_t ret;
log_debug("read buf=%p, block=%lx, count=%lx: ", buffer, (ulong)blknr,
(ulong)blkcnt);
ret = io->read_blocks(io, io->media->media_id, blknr,
blkcnt * io->media->block_size, buffer);
log_debug("ret=%lx (dec %ld)\n", ret & ~EFI_ERROR_MASK,
ret & ~EFI_ERROR_MASK);
if (ret)
return 0;
return blkcnt;
}
/**
* Write to block device
*
* @dev: device
* @blknr: first block to write
* @blkcnt: number of blocks to write
* @buffer: input buffer
* Return: number of blocks transferred
*/
static ulong efi_bl_write(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt,
const void *buffer)
{
struct efi_block_plat *plat = dev_get_plat(dev);
struct efi_block_io *io = plat->blkio;
efi_status_t ret;
log_debug("write buf=%p, block=%lx, count=%lx: ", buffer, (ulong)blknr,
(ulong)blkcnt);
ret = io->write_blocks(io, io->media->media_id, blknr,
blkcnt * io->media->block_size, (void *)buffer);
log_debug("ret=%lx (dec %ld)\n", ret & ~EFI_ERROR_MASK,
ret & ~EFI_ERROR_MASK);
if (ret)
return 0;
return blkcnt;
}
/* Block device driver operators */
static const struct blk_ops efi_blk_ops = {
.read = efi_bl_read,
.write = efi_bl_write,
};
U_BOOT_DRIVER(efi_media_block) = {
.name = "efi_block",
.id = UCLASS_BLK,
.ops = &efi_blk_ops,
.plat_auto = sizeof(struct efi_block_plat),
};
static int efi_media_bind(struct udevice *dev)
{
struct efi_media_plat *plat = dev_get_plat(dev);
struct efi_block_plat *blk_plat;
struct udevice *blk;
int ret;
ret = blk_create_devicef(dev, "efi_block", "blk", UCLASS_EFI_MEDIA,
dev_seq(dev), plat->blkio->media->block_size,
plat->blkio->media->last_block, &blk);
if (ret) {
debug("Cannot create block device\n");
return ret;
}
blk_plat = dev_get_plat(blk);
blk_plat->blkio = plat->blkio;
return 0;
}
U_BOOT_DRIVER(efi_media) = {
.name = "efi_media",
.id = UCLASS_EFI_MEDIA,
.bind = efi_media_bind,
.plat_auto = sizeof(struct efi_media_plat),
};
+184
View File
@@ -0,0 +1,184 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Uclass for sandbox host interface, used to access files on the host which
* contain partitions and filesystem
*
* Copyright 2022 Google LLC
* Written by Simon Glass <sjg@chromium.org>
*/
#define LOG_CATEGORY UCLASS_HOST
#include <blk.h>
#include <dm.h>
#include <malloc.h>
#include <part.h>
#include <sandbox_host.h>
#include <dm/device-internal.h>
#include <dm/lists.h>
#include <dm/uclass-internal.h>
DECLARE_GLOBAL_DATA_PTR;
/**
* struct host_priv - information kept by the host uclass
*
* @cur_dev: Currently selected host device, or NULL if none
*/
struct host_priv {
struct udevice *cur_dev;
};
int host_create_device(const char *label, bool removable, unsigned long blksz,
struct udevice **devp)
{
char dev_name[30], *str, *label_new;
struct host_sb_plat *plat;
struct udevice *dev, *blk;
int ret;
/* unbind any existing device with this label */
dev = host_find_by_label(label);
if (dev) {
ret = host_detach_file(dev);
if (ret)
return log_msg_ret("det", ret);
ret = device_unbind(dev);
if (ret)
return log_msg_ret("unb", ret);
}
snprintf(dev_name, sizeof(dev_name), "host-%s", label);
str = strdup(dev_name);
if (!str)
return -ENOMEM;
label_new = strdup(label);
if (!label_new) {
ret = -ENOMEM;
goto no_label;
}
ret = device_bind_driver(gd->dm_root, "host_sb_drv", str, &dev);
if (ret)
goto no_dev;
device_set_name_alloced(dev);
if (!blk_find_from_parent(dev, &blk)) {
struct blk_desc *desc = dev_get_uclass_plat(blk);
desc->removable = removable;
/* update blk device's block size with the provided one */
if (blksz != desc->blksz) {
desc->blksz = blksz;
desc->log2blksz = LOG2(desc->blksz);
}
}
plat = dev_get_plat(dev);
plat->label = label_new;
*devp = dev;
return 0;
no_dev:
free(label_new);
no_label:
free(str);
return ret;
}
int host_attach_file(struct udevice *dev, const char *filename)
{
struct host_ops *ops = host_get_ops(dev);
if (!ops->attach_file)
return -ENOSYS;
return ops->attach_file(dev, filename);
}
int host_create_attach_file(const char *label, const char *filename,
bool removable, unsigned long blksz,
struct udevice **devp)
{
struct udevice *dev;
int ret;
ret = host_create_device(label, removable, blksz, &dev);
if (ret)
return log_msg_ret("cre", ret);
ret = host_attach_file(dev, filename);
if (ret) {
device_unbind(dev);
return log_msg_ret("att", ret);
}
*devp = dev;
return 0;
}
int host_detach_file(struct udevice *dev)
{
struct host_ops *ops = host_get_ops(dev);
if (!ops->detach_file)
return -ENOSYS;
if (dev == host_get_cur_dev())
host_set_cur_dev(NULL);
return ops->detach_file(dev);
}
struct udevice *host_find_by_label(const char *label)
{
struct udevice *dev;
struct uclass *uc;
uclass_id_foreach_dev(UCLASS_HOST, dev, uc) {
struct host_sb_plat *plat = dev_get_plat(dev);
if (plat->label && !strcmp(label, plat->label))
return dev;
}
return NULL;
}
struct udevice *host_get_cur_dev(void)
{
struct uclass *uc = uclass_find(UCLASS_HOST);
if (uc) {
struct host_priv *priv = uclass_get_priv(uc);
return priv->cur_dev;
}
return NULL;
}
void host_set_cur_dev(struct udevice *dev)
{
struct uclass *uc = uclass_find(UCLASS_HOST);
if (uc) {
struct host_priv *priv = uclass_get_priv(uc);
priv->cur_dev = dev;
}
}
UCLASS_DRIVER(host) = {
.id = UCLASS_HOST,
.name = "host",
#if CONFIG_IS_ENABLED(OF_REAL)
.post_bind = dm_scan_fdt_dev,
#endif
.priv_auto = sizeof(struct host_priv),
};
+148
View File
@@ -0,0 +1,148 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Driver for sandbox host interface, used to access files on the host which
* contain partitions and filesystem
*
* Copyright 2022 Google LLC
* Written by Simon Glass <sjg@chromium.org>
*/
#define LOG_CATEGORY UCLASS_HOST
#include <blk.h>
#include <bootdev.h>
#include <dm.h>
#include <log.h>
#include <malloc.h>
#include <os.h>
#include <sandbox_host.h>
#include <dm/device-internal.h>
static int host_sb_attach_file(struct udevice *dev, const char *filename)
{
struct host_sb_plat *plat = dev_get_plat(dev);
struct blk_desc *desc;
struct udevice *blk;
int ret, fd;
off_t size;
char *fname;
if (!filename)
return -EINVAL;
if (plat->fd)
return log_msg_ret("fd", -EEXIST);
/* Sanity check that host_sb_bind() has been used */
ret = blk_find_from_parent(dev, &blk);
if (ret)
return ret;
fd = os_open(filename, OS_O_RDWR);
if (fd == -1) {
printf("Failed to access host backing file '%s', trying read-only\n",
filename);
fd = os_open(filename, OS_O_RDONLY);
if (fd == -1) {
printf("- still failed\n");
return log_msg_ret("open", -ENOENT);
}
}
fname = strdup(filename);
if (!fname) {
ret = -ENOMEM;
goto err_fname;
}
size = os_filesize(fd);
desc = dev_get_uclass_plat(blk);
if (size % desc->blksz) {
printf("The size of host backing file '%s' is not multiple of "
"the device block size\n", filename);
ret = -EINVAL;
goto err_fname;
}
desc->lba = size / desc->blksz;
/* write this in last, when nothing can go wrong */
plat = dev_get_plat(dev);
plat->fd = fd;
plat->filename = fname;
return 0;
err_fname:
os_close(fd);
return ret;
}
static int host_sb_detach_file(struct udevice *dev)
{
struct host_sb_plat *plat = dev_get_plat(dev);
int ret;
if (!plat->fd)
return log_msg_ret("fd", -ENOENT);
ret = device_remove(dev, DM_REMOVE_NORMAL);
if (ret)
return log_msg_ret("rem", ret);
/* Unbind all children */
ret = device_chld_unbind(dev, NULL);
if (ret)
return log_msg_ret("unb", ret);
os_close(plat->fd);
plat->fd = 0;
free(plat->filename);
free(plat->label);
return 0;
}
static int host_sb_bind(struct udevice *dev)
{
struct udevice *blk, *bdev;
struct blk_desc *desc;
int ret;
ret = blk_create_devicef(dev, "sandbox_host_blk", "blk", UCLASS_HOST,
dev_seq(dev), DEFAULT_BLKSZ, 0, &blk);
if (ret)
return log_msg_ret("blk", ret);
desc = dev_get_uclass_plat(blk);
snprintf(desc->vendor, BLK_VEN_SIZE, "U-Boot");
snprintf(desc->product, BLK_PRD_SIZE, "hostfile");
snprintf(desc->revision, BLK_REV_SIZE, "1.0");
if (CONFIG_IS_ENABLED(BOOTSTD)) {
ret = bootdev_bind(dev, "host_bootdev", "bootdev", &bdev);
if (ret)
return log_msg_ret("bd", ret);
}
return 0;
}
static struct host_ops host_sb_ops = {
.attach_file = host_sb_attach_file,
.detach_file = host_sb_detach_file,
};
static const struct udevice_id host_ids[] = {
{ .compatible = "sandbox,host" },
{ }
};
U_BOOT_DRIVER(host_sb_drv) = {
.name = "host_sb_drv",
.id = UCLASS_HOST,
.of_match = host_ids,
.ops = &host_sb_ops,
.bind = host_sb_bind,
.plat_auto = sizeof(struct host_sb_plat),
};
+1084
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+64
View File
@@ -0,0 +1,64 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2013 Henrik Nordstrom <henrik@henriknordstrom.net>
*/
#include <blk.h>
#include <dm.h>
#include <fdtdec.h>
#include <part.h>
#include <os.h>
#include <malloc.h>
#include <sandbox_host.h>
#include <dm/device_compat.h>
#include <dm/device-internal.h>
#include <linux/errno.h>
static unsigned long host_block_read(struct udevice *dev,
lbaint_t start, lbaint_t blkcnt,
void *buffer)
{
struct blk_desc *desc = dev_get_uclass_plat(dev);
struct udevice *host_dev = dev_get_parent(dev);
struct host_sb_plat *plat = dev_get_plat(host_dev);
if (os_lseek(plat->fd, start * desc->blksz, OS_SEEK_SET) < 0) {
printf("ERROR: Invalid block " LBAF "\n", start);
return -1;
}
ssize_t len = os_read(plat->fd, buffer, blkcnt * desc->blksz);
if (len >= 0)
return len / desc->blksz;
return -EIO;
}
static unsigned long host_block_write(struct udevice *dev,
lbaint_t start, lbaint_t blkcnt,
const void *buffer)
{
struct blk_desc *desc = dev_get_uclass_plat(dev);
struct udevice *host_dev = dev_get_parent(dev);
struct host_sb_plat *plat = dev_get_plat(host_dev);
if (os_lseek(plat->fd, start * desc->blksz, OS_SEEK_SET) < 0) {
printf("ERROR: Invalid block " LBAF "\n", start);
return -1;
}
ssize_t len = os_write(plat->fd, buffer, blkcnt * desc->blksz);
if (len >= 0)
return len / desc->blksz;
return -EIO;
}
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,
};
+19
View File
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* EFI_MEDIA driver for sandbox
*
* Copyright 2021 Google LLC
*/
#include <dm.h>
static const struct udevice_id sandbox_efi_media_ids[] = {
{ .compatible = "sandbox,efi-media" },
{ }
};
U_BOOT_DRIVER(sandbox_efi_media) = {
.name = "sandbox_efi_media",
.id = UCLASS_EFI_MEDIA,
.of_match = sandbox_efi_media_ids,
};
+252
View File
@@ -0,0 +1,252 @@
#
# 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: https://docs.u-boot-project.org/en/latest/api/bootcount.html
if BOOTCOUNT_LIMIT
choice
prompt "Boot count device"
default BOOTCOUNT_AM33XX if AM33XX || SOC_DA8XX
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_FS
bool "Boot counter on a filesystem"
help
Add support for maintaining boot count in a file on a filesystem.
This requires that you have enabled write support for the filesystem
that will be used by the partition that you configure this feature
for.
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_AM33XX_NVMEM
bool "Boot counter in AM33XX RTC IP block with upgrade_available flag"
depends on AM33XX
select SPL_AM33XX_ENABLE_RTC32K_OSC if AM33XX
help
Add support for maintaining bootcount,upgrade_available,
version and BOOTMAGIC in a AM33xx RTC IP block
scratch register2.
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.
If there is upgrade in software then "upgrade_available" is 1,
"bootcount" is incremented otherwise "upgrade_available" and
"bootcount" is always 0. So the Userspace Application must set
the "upgrade_available" and "bootcount" variable to 0, if a boot
was successfully.
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" variables to 0, if the system booted successfully.
config BOOTCOUNT_RAM
bool "Boot counter in RAM"
help
Store the bootcount in DRAM protected against bit errors
due to short power loss or holding a system in RESET.
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.
config DM_BOOTCOUNT_I2C
bool "Driver Model boot counter on I2C device"
depends on DM_I2C
help
Enable support for the bootcounter on a generic i2c device, like a RTC
or PMIC. The bootcounter is configured in the device tree using the
"u-boot,bootcount-i2c" compatible string. It requires a phandle
'i2cbcdev' for the i2c device and an 'offset' property used within the
device.
config DM_BOOTCOUNT_I2C_EEPROM
bool "Support i2c eeprom devices as a backing store for bootcount"
depends on I2C_EEPROM
help
Enabled reading/writing the bootcount in a DM i2c eeprom device.
The wrapper device is to be specified with the compatible string
'u-boot,bootcount-i2c-eeprom' and the 'i2c-eeprom'-property (a phandle
pointing to the underlying i2c eeprom device) and an optional 'offset'
property are supported.
config DM_BOOTCOUNT_PMIC_PFUZE100
bool "Enable Bootcount driver for PMIC PFUZE100"
depends on DM_PMIC_PFUZE100
help
Enable support for the bootcounter using PMIC PFUZE100 registers.
This works only, if the PMIC is not connected to a battery.
config DM_BOOTCOUNT_SPI_FLASH
bool "Support SPI flash devices as a backing store for bootcount"
depends on DM_SPI_FLASH
help
Enabled reading/writing the bootcount in a DM SPI flash device.
The wrapper device is to be specified with the compatible string
'u-boot,bootcount-spi-flash' and the 'spi-flash'-property (a phandle
pointing to the underlying SPI flash device) and an optional 'offset'
property are supported.
config BOOTCOUNT_MEM
bool "Support memory based bootcounter"
help
Enabling Memory based bootcount, typically in a SoC register which
is not cleared on softreset.
compatible = "u-boot,bootcount";
config DM_BOOTCOUNT_SYSCON
bool "Support SYSCON devices as a backing store for bootcount"
select REGMAP
select SYSCON
help
Enable reading/writing the bootcount value in a DM SYSCON device.
The driver supports a fixed 32 bits size register using the native
endianness. However, this can be controlled from the SYSCON DT node
configuration.
Accessing the backend is done using the regmap interface.
config DM_BOOTCOUNT_ZYNQMP
bool "Support ZynqMP PMUFW as a backing store for bootcount"
depends on ARCH_ZYNQMP
help
Enable support for the bootcount API by utilising the Persistent
Global General Storage Register 2 of the PMU.
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_ALTBOOTCMD
string "Alternative boot command when BOOTLIMIT is reached"
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_FS_INTERFACE
string "Interface on which to find boot counter filesystem"
default "mmc"
depends on BOOTCOUNT_FS
help
Set the interface to use when locating the filesystem to use for the
boot counter.
config SYS_BOOTCOUNT_FS_DEVPART
string "Partition of the boot counter filesystem"
default "0:1"
depends on BOOTCOUNT_FS
help
Set the partition to use when locating the filesystem to use for the
boot counter.
config SYS_BOOTCOUNT_FS_NAME
string "Path and filename of the FS filesystem based boot counter"
default "/boot/failures"
depends on BOOTCOUNT_FS
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 || BOOTCOUNT_AM33XX_NVMEM
default 0xE0115FF8 if ARCH_LS1043A || ARCH_LS1021A
depends on BOOTCOUNT_AM33XX || BOOTCOUNT_GENERIC || BOOTCOUNT_FS || \
BOOTCOUNT_AM33XX_NVMEM
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 if BOOTCOUNT_GENERIC || BOOTCOUNT_FS || \
BOOTCOUNT_AM33XX || BOOTCOUNT_ENV || \
BOOTCOUNT_RAM || BOOTCOUNT_AT91 || DM_BOOTCOUNT
default 0xB0 if BOOTCOUNT_AM33XX_NVMEM
depends on BOOTCOUNT_GENERIC || BOOTCOUNT_FS || \
BOOTCOUNT_AM33XX || BOOTCOUNT_ENV || \
BOOTCOUNT_RAM || BOOTCOUNT_AT91 || DM_BOOTCOUNT || \
BOOTCOUNT_AM33XX_NVMEM
help
Set the magic value used for the boot counter.
choice
prompt "Endianness of bootcount accessors"
default SYS_BOOTCOUNT_LE
config SYS_BOOTCOUNT_LE
bool "Little endian accessors"
config SYS_BOOTCOUNT_BE
bool "Big endian accessors"
endchoice
endif
+19
View File
@@ -0,0 +1,19 @@
# SPDX-License-Identifier: GPL-2.0+
obj-$(CONFIG_BOOTCOUNT_GENERIC) += bootcount.o
obj-$(CONFIG_BOOTCOUNT_MEM) += 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_FS) += bootcount_fs.o
obj-$(CONFIG_BOOTCOUNT_AM33XX_NVMEM) += bootcount_nvmem.o
obj-$(CONFIG_DM_BOOTCOUNT) += bootcount-uclass.o
obj-$(CONFIG_DM_BOOTCOUNT_PMIC_PFUZE100) += pmic_pfuze100.o
obj-$(CONFIG_DM_BOOTCOUNT_RTC) += rtc.o
obj-$(CONFIG_DM_BOOTCOUNT_I2C_EEPROM) += i2c-eeprom.o
obj-$(CONFIG_DM_BOOTCOUNT_I2C) += bootcount_dm_i2c.o
obj-$(CONFIG_DM_BOOTCOUNT_SPI_FLASH) += spi-flash.o
obj-$(CONFIG_DM_BOOTCOUNT_SYSCON) += bootcount_syscon.o
obj-$(CONFIG_DM_BOOTCOUNT_ZYNQMP) += bootcount_zynqmp.o
+96
View File
@@ -0,0 +1,96 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2018 Theobroma Systems Design und Consulting GmbH
*/
#define LOG_CATEGORY UCLASS_BOOTCOUNT
#include <dm.h>
#include <errno.h>
#include <bootcount.h>
#include <log.h>
#include <linux/printk.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,
};
+146
View File
@@ -0,0 +1,146 @@
// 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 <asm/cache.h>
#include <linux/compiler.h>
#if !defined(CONFIG_DM_BOOTCOUNT)
/* 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 & BOOTCOUNT_MAGIC_MASK)
| (a & BOOTCOUNT_COUNT_MASK));
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 & BOOTCOUNT_MAGIC_MASK) != (CONFIG_SYS_BOOTCOUNT_MAGIC & BOOTCOUNT_MAGIC_MASK))
return 0;
else
return (tmp & BOOTCOUNT_COUNT_MASK);
#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) */
}
#else
#include <dm.h>
/*
* struct bootcount_mem_priv - private bootcount mem driver data
*
* @base: base address used for bootcounter
* @singleword: if true use only one 32 bit word for bootcounter
*/
struct bootcount_mem_priv {
phys_addr_t base;
bool singleword;
};
static int bootcount_mem_get(struct udevice *dev, u32 *a)
{
struct bootcount_mem_priv *priv = dev_get_priv(dev);
void *reg = (void *)priv->base;
u32 magic = CONFIG_SYS_BOOTCOUNT_MAGIC;
if (priv->singleword) {
u32 tmp = raw_bootcount_load(reg);
if ((tmp & BOOTCOUNT_MAGIC_MASK) != (magic & BOOTCOUNT_MAGIC_MASK))
return -ENODEV;
*a = (tmp & BOOTCOUNT_COUNT_MASK);
} else {
if (raw_bootcount_load(reg + 4) != magic)
return -ENODEV;
*a = raw_bootcount_load(reg);
}
return 0;
};
static int bootcount_mem_set(struct udevice *dev, const u32 a)
{
struct bootcount_mem_priv *priv = dev_get_priv(dev);
void *reg = (void *)priv->base;
u32 magic = CONFIG_SYS_BOOTCOUNT_MAGIC;
uintptr_t flush_start = rounddown(priv->base,
CONFIG_SYS_CACHELINE_SIZE);
uintptr_t flush_end;
if (priv->singleword) {
raw_bootcount_store(reg, (magic & BOOTCOUNT_MAGIC_MASK)
| (a & BOOTCOUNT_COUNT_MASK));
flush_end = roundup(priv->base + 4,
CONFIG_SYS_CACHELINE_SIZE);
} else {
raw_bootcount_store(reg, a);
raw_bootcount_store(reg + 4, magic);
flush_end = roundup(priv->base + 8,
CONFIG_SYS_CACHELINE_SIZE);
}
flush_dcache_range(flush_start, flush_end);
return 0;
};
static const struct bootcount_ops bootcount_mem_ops = {
.get = bootcount_mem_get,
.set = bootcount_mem_set,
};
static int bootcount_mem_probe(struct udevice *dev)
{
struct bootcount_mem_priv *priv = dev_get_priv(dev);
priv->base = (phys_addr_t)dev_read_addr(dev);
if (dev_read_bool(dev, "single-word"))
priv->singleword = true;
return 0;
}
static const struct udevice_id bootcount_mem_ids[] = {
{ .compatible = "u-boot,bootcount" },
{ }
};
U_BOOT_DRIVER(bootcount_mem) = {
.name = "bootcount-mem",
.id = UCLASS_BOOTCOUNT,
.priv_auto = sizeof(struct bootcount_mem_priv),
.probe = bootcount_mem_probe,
.of_match = bootcount_mem_ids,
.ops = &bootcount_mem_ops,
};
#endif
+30
View File
@@ -0,0 +1,30 @@
// SPDX-License-Identifier: GPL-2.0+
#include <asm/io.h>
#include <asm/arch/hardware.h>
#include <asm/arch/at91_gpbr.h>
#include <bootcount.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 & BOOTCOUNT_MAGIC_MASK) | (a & BOOTCOUNT_COUNT_MASK),
&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 & BOOTCOUNT_MAGIC_MASK) != (CONFIG_SYS_BOOTCOUNT_MAGIC & BOOTCOUNT_MAGIC_MASK))
return 0;
else
return val & BOOTCOUNT_COUNT_MASK;
}
+41
View File
@@ -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, &reg->kick0r);
writel(RTC_KICK1R_WE, &reg->kick1r);
raw_bootcount_store(&reg->scratch2,
(CONFIG_SYS_BOOTCOUNT_MAGIC & BOOTCOUNT_MAGIC_MASK) | (a & BOOTCOUNT_COUNT_MASK));
}
ulong bootcount_load(void)
{
unsigned long val;
struct davinci_rtc *reg =
(struct davinci_rtc *)CONFIG_SYS_BOOTCOUNT_ADDR;
val = raw_bootcount_load(&reg->scratch2);
if ((val & BOOTCOUNT_MAGIC_MASK) != (CONFIG_SYS_BOOTCOUNT_MAGIC & BOOTCOUNT_MAGIC_MASK))
return 0;
else
return val & BOOTCOUNT_COUNT_MASK;
}
+136
View File
@@ -0,0 +1,136 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2023
* Philip Richard Oberfichtner <pro@denx.de>
*
* Based on previous work from Heiko Schocher (legacy bootcount_i2c.c driver)
*/
#include <bootcount.h>
#include <dm.h>
#include <i2c.h>
#define BC_MAGIC 0x55
struct bootcount_i2c_priv {
struct udevice *bcdev;
unsigned int offset;
unsigned int size;
};
static int bootcount_i2c_set(struct udevice *dev, const u32 val)
{
int ret;
struct bootcount_i2c_priv *priv = dev_get_priv(dev);
if (priv->size == 4) {
u32 bc = (CONFIG_SYS_BOOTCOUNT_MAGIC & BOOTCOUNT_MAGIC_MASK)
| (val & BOOTCOUNT_COUNT_MASK);
ret = dm_i2c_write(priv->bcdev, priv->offset, (uint8_t *)&bc, sizeof(bc));
if (ret < 0)
goto err_exit;
} else {
ret = dm_i2c_reg_write(priv->bcdev, priv->offset, BC_MAGIC);
if (ret < 0)
goto err_exit;
ret = dm_i2c_reg_write(priv->bcdev, priv->offset + 1, val & 0xff);
if (ret < 0)
goto err_exit;
}
return 0;
err_exit:
log_debug("%s: Error writing to I2C device (%d)\n", __func__, ret);
return ret;
}
static int bootcount_i2c_get(struct udevice *dev, u32 *val)
{
int ret;
struct bootcount_i2c_priv *priv = dev_get_priv(dev);
if (priv->size == 4) {
u32 bc;
ret = dm_i2c_read(priv->bcdev, priv->offset, (uint8_t *)&bc, sizeof(bc));
if (ret < 0)
goto err_exit;
if ((bc & BOOTCOUNT_MAGIC_MASK) !=
(CONFIG_SYS_BOOTCOUNT_MAGIC & BOOTCOUNT_MAGIC_MASK)) {
log_debug("%s: Invalid Magic, reset bootcounter.\n", __func__);
*val = 0;
return bootcount_i2c_set(dev, 0);
}
*val = (bc & BOOTCOUNT_COUNT_MASK);
} else {
ret = dm_i2c_reg_read(priv->bcdev, priv->offset);
if (ret < 0)
goto err_exit;
if ((ret & 0xff) != BC_MAGIC) {
log_debug("%s: Invalid Magic, reset bootcounter.\n", __func__);
*val = 0;
return bootcount_i2c_set(dev, 0);
}
ret = dm_i2c_reg_read(priv->bcdev, priv->offset + 1);
if (ret < 0)
goto err_exit;
*val = ret;
}
return 0;
err_exit:
log_debug("%s: Error reading from I2C device (%d)\n", __func__, ret);
return ret;
}
static int bootcount_i2c_probe(struct udevice *dev)
{
struct bootcount_i2c_priv *priv = dev_get_priv(dev);
int ret;
ret = dev_read_u32(dev, "offset", &priv->offset);
if (ret)
goto exit;
priv->size = dev_read_u32_default(dev, "size", 2);
if (priv->size != 2 && priv->size != 4) {
ret = -EINVAL;
goto exit;
}
ret = i2c_get_chip_by_phandle(dev, "i2cbcdev", &priv->bcdev);
exit:
if (ret)
log_debug("%s failed, ret = %d\n", __func__, ret);
return ret;
}
static const struct bootcount_ops bootcount_i2c_ops = {
.get = bootcount_i2c_get,
.set = bootcount_i2c_set,
};
static const struct udevice_id bootcount_i2c_ids[] = {
{ .compatible = "u-boot,bootcount-i2c" },
{ }
};
U_BOOT_DRIVER(bootcount_i2c) = {
.name = "bootcount-i2c",
.id = UCLASS_BOOTCOUNT,
.priv_auto = sizeof(struct bootcount_i2c_priv),
.probe = bootcount_i2c_probe,
.of_match = bootcount_i2c_ids,
.ops = &bootcount_i2c_ops,
};
+28
View File
@@ -0,0 +1,28 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2013
* Heiko Schocher, DENX Software Engineering, hs@denx.de.
*/
#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;
}
+83
View File
@@ -0,0 +1,83 @@
// 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 0xbd
#define BC_VERSION 1
typedef struct {
u8 magic;
u8 version;
u8 bootcount;
u8 upgrade_available;
} bootcount_ext_t;
static u8 upgrade_available = 1;
void bootcount_store(ulong a)
{
bootcount_ext_t *buf;
loff_t len;
int ret;
if (fs_set_blk_dev(CONFIG_SYS_BOOTCOUNT_FS_INTERFACE,
CONFIG_SYS_BOOTCOUNT_FS_DEVPART, FS_TYPE_ANY)) {
puts("Error selecting device\n");
return;
}
/* Only update bootcount during upgrade process */
if (!upgrade_available)
return;
buf = map_sysmem(CONFIG_SYS_BOOTCOUNT_ADDR, sizeof(bootcount_ext_t));
buf->magic = BC_MAGIC;
buf->version = BC_VERSION;
buf->bootcount = (a & 0xff);
buf->upgrade_available = upgrade_available;
unmap_sysmem(buf);
ret = fs_write(CONFIG_SYS_BOOTCOUNT_FS_NAME,
CONFIG_SYS_BOOTCOUNT_ADDR, 0, sizeof(bootcount_ext_t),
&len);
if (ret != 0)
puts("Error storing bootcount\n");
}
ulong bootcount_load(void)
{
bootcount_ext_t *buf;
loff_t len_read;
int ret;
if (fs_set_blk_dev(CONFIG_SYS_BOOTCOUNT_FS_INTERFACE,
CONFIG_SYS_BOOTCOUNT_FS_DEVPART, FS_TYPE_ANY)) {
puts("Error selecting device\n");
return 0;
}
ret = fs_read(CONFIG_SYS_BOOTCOUNT_FS_NAME, CONFIG_SYS_BOOTCOUNT_ADDR,
0, sizeof(bootcount_ext_t), &len_read);
if (ret != 0 || len_read != sizeof(bootcount_ext_t)) {
puts("Error loading bootcount\n");
return 0;
}
buf = map_sysmem(CONFIG_SYS_BOOTCOUNT_ADDR, sizeof(bootcount_ext_t));
if (buf->magic == BC_MAGIC && buf->version == BC_VERSION) {
upgrade_available = buf->upgrade_available;
if (upgrade_available)
ret = buf->bootcount;
} else {
puts("Incorrect bootcount file\n");
}
unmap_sysmem(buf);
return ret;
}
+57
View File
@@ -0,0 +1,57 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2011
* Heiko Schocher, DENX Software Engineering, hs@denx.de.
* (C) Copyright 2018 Robert Bosch Power Tools GmbH.
*
* 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>
#define BC_VERSION 2
void bootcount_store(ulong bootcount)
{
u8 upgrade_available = 0;
ulong val = 0;
struct davinci_rtc *reg =
(struct davinci_rtc *)CONFIG_SYS_BOOTCOUNT_ADDR;
val = raw_bootcount_load(&reg->scratch2);
upgrade_available = (val >> 8) & 0x000000ff;
/* Only update bootcount during upgrade process */
if (!upgrade_available)
bootcount = 0;
val = (bootcount & 0x000000ff) |
(upgrade_available << 8) |
(BC_VERSION << 16) |
(CONFIG_SYS_BOOTCOUNT_MAGIC << 24);
/*
* write RTC kick registers to enable write
* for RTC Scratch registers. Scratch register 2 is
* used for bootcount value.
*/
writel(RTC_KICK0R_WE, &reg->kick0r);
writel(RTC_KICK1R_WE, &reg->kick1r);
raw_bootcount_store(&reg->scratch2, val);
}
ulong bootcount_load(void)
{
unsigned long val = 0;
struct davinci_rtc *reg =
(struct davinci_rtc *)CONFIG_SYS_BOOTCOUNT_ADDR;
val = raw_bootcount_load(&reg->scratch2);
if ((val >> 24) != CONFIG_SYS_BOOTCOUNT_MAGIC)
return 0;
else
return val & 0x000000ff;
}
+65
View File
@@ -0,0 +1,65 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2010
* Heiko Schocher, DENX Software Engineering, hs@denx.de.
*/
#include <cpu_func.h>
#include <asm/cache.h>
#include <asm/global_data.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;
}
+158
View File
@@ -0,0 +1,158 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) Vaisala Oyj. All rights reserved.
*/
#include <bootcount.h>
#include <dm.h>
#include <dm/device_compat.h>
#include <linux/ioport.h>
#include <regmap.h>
#include <syscon.h>
#define BYTES_TO_BITS(bytes) ((bytes) << 3)
#define GEN_REG_MASK(val_size, val_addr) \
(GENMASK(BYTES_TO_BITS(val_size) - 1, 0) \
<< (!!((val_addr) == 0x02) * BYTES_TO_BITS(2)))
#define GET_DEFAULT_VALUE(val_size) \
(CONFIG_SYS_BOOTCOUNT_MAGIC >> \
(BYTES_TO_BITS((sizeof(u32) - (val_size)))))
/**
* struct bootcount_syscon_priv - driver's private data
*
* @regmap: syscon regmap
* @reg_addr: register address used to store the bootcount value
* @size: size of the bootcount value (2 or 4 bytes)
* @magic: magic used to validate/save the bootcount value
* @magic_mask: magic value bitmask
* @reg_mask: mask used to identify the location of the bootcount value
* in the register when 2 bytes length is used
* @shift: value used to extract the botcount value from the register
*/
struct bootcount_syscon_priv {
struct regmap *regmap;
fdt_addr_t reg_addr;
fdt_size_t size;
u32 magic;
u32 magic_mask;
u32 reg_mask;
int shift;
};
static int bootcount_syscon_set(struct udevice *dev, const u32 val)
{
struct bootcount_syscon_priv *priv = dev_get_priv(dev);
u32 regval;
if ((val & priv->magic_mask) != 0)
return -EINVAL;
regval = (priv->magic & priv->magic_mask) | (val & ~priv->magic_mask);
if (priv->size == 2) {
regval &= 0xffff;
regval |= (regval & 0xffff) << BYTES_TO_BITS(priv->size);
}
debug("%s: Prepare to write reg value: 0x%08x with register mask: 0x%08x\n",
__func__, regval, priv->reg_mask);
return regmap_update_bits(priv->regmap, priv->reg_addr, priv->reg_mask,
regval);
}
static int bootcount_syscon_get(struct udevice *dev, u32 *val)
{
struct bootcount_syscon_priv *priv = dev_get_priv(dev);
u32 regval;
int ret;
ret = regmap_read(priv->regmap, priv->reg_addr, &regval);
if (ret)
return ret;
regval &= priv->reg_mask;
regval >>= priv->shift;
if ((regval & priv->magic_mask) == (priv->magic & priv->magic_mask)) {
*val = regval & ~priv->magic_mask;
} else {
dev_err(dev, "%s: Invalid bootcount magic\n", __func__);
return -EINVAL;
}
debug("%s: Read bootcount value: 0x%08x from regval: 0x%08x\n",
__func__, *val, regval);
return 0;
}
static int bootcount_syscon_of_to_plat(struct udevice *dev)
{
struct bootcount_syscon_priv *priv = dev_get_priv(dev);
fdt_addr_t bootcount_offset;
fdt_size_t reg_size;
priv->regmap = syscon_regmap_lookup_by_phandle(dev, "syscon");
if (IS_ERR(priv->regmap)) {
dev_err(dev, "%s: Unable to find regmap (%ld)\n", __func__,
PTR_ERR(priv->regmap));
return PTR_ERR(priv->regmap);
}
priv->reg_addr = dev_read_addr_size_name(dev, "syscon_reg", &reg_size);
if (priv->reg_addr == FDT_ADDR_T_NONE) {
dev_err(dev, "%s: syscon_reg address not found\n", __func__);
return -EINVAL;
}
if (reg_size != 4) {
dev_err(dev, "%s: Unsupported register size: %pa\n", __func__,
&reg_size);
return -EINVAL;
}
bootcount_offset = dev_read_addr_size_name(dev, "offset", &priv->size);
if (bootcount_offset == FDT_ADDR_T_NONE) {
dev_err(dev, "%s: offset configuration not found\n", __func__);
return -EINVAL;
}
if (bootcount_offset + priv->size > reg_size) {
dev_err(dev,
"%s: Bootcount value doesn't fit in the reserved space\n",
__func__);
return -EINVAL;
}
if (priv->size != 2 && priv->size != 4) {
dev_err(dev,
"%s: Driver supports only 2 and 4 bytes bootcount size\n",
__func__);
return -EINVAL;
}
priv->magic = GET_DEFAULT_VALUE(priv->size);
priv->magic_mask = GENMASK(BYTES_TO_BITS(priv->size) - 1,
BYTES_TO_BITS(priv->size >> 1));
priv->shift = !!(bootcount_offset == 0x02) * BYTES_TO_BITS(priv->size);
priv->reg_mask = GEN_REG_MASK(priv->size, bootcount_offset);
return 0;
}
static const struct bootcount_ops bootcount_syscon_ops = {
.get = bootcount_syscon_get,
.set = bootcount_syscon_set,
};
static const struct udevice_id bootcount_syscon_ids[] = {
{ .compatible = "u-boot,bootcount-syscon" },
{}
};
U_BOOT_DRIVER(bootcount_syscon) = {
.name = "bootcount-syscon",
.id = UCLASS_BOOTCOUNT,
.of_to_plat = bootcount_syscon_of_to_plat,
.priv_auto = sizeof(struct bootcount_syscon_priv),
.of_match = bootcount_syscon_ids,
.ops = &bootcount_syscon_ops,
};
+47
View File
@@ -0,0 +1,47 @@
// SPDX-License-Identifier: GPL-2.0+
// SPDX-FileCopyrightText: 2024 CERN (home.cern)
#include <bootcount.h>
#include <dm.h>
#include <stdio.h>
#include <zynqmp_firmware.h>
#include <asm/arch/hardware.h>
#include <dm/platdata.h>
static int bootcount_zynqmp_set(struct udevice *dev, const u32 val)
{
int ret;
ret = zynqmp_mmio_write((ulong)&pmu_base->pers_gen_storage2, 0xFF, val);
if (ret)
pr_info("%s write fail\n", __func__);
return ret;
}
static int bootcount_zynqmp_get(struct udevice *dev, u32 *val)
{
int ret;
*val = 0;
ret = zynqmp_mmio_read((ulong)&pmu_base->pers_gen_storage2, val);
if (ret)
pr_info("%s read fail\n", __func__);
return ret;
}
U_BOOT_DRVINFO(bootcount_zynqmp) = {
.name = "bootcount_zynqmp",
};
static const struct bootcount_ops bootcount_zynqmp_ops = {
.get = bootcount_zynqmp_get,
.set = bootcount_zynqmp_set,
};
U_BOOT_DRIVER(bootcount_zynqmp) = {
.name = "bootcount_zynqmp",
.id = UCLASS_BOOTCOUNT,
.ops = &bootcount_zynqmp_ops,
};
+95
View File
@@ -0,0 +1,95 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2019 Collabora
* (C) Copyright 2019 GE
*/
#include <bootcount.h>
#include <dm.h>
#include <i2c_eeprom.h>
#include <log.h>
static const u8 bootcount_magic = 0xbc;
struct bootcount_i2c_eeprom_priv {
struct udevice *i2c_eeprom;
u32 offset;
};
static int bootcount_i2c_eeprom_set(struct udevice *dev, const u32 a)
{
struct bootcount_i2c_eeprom_priv *priv = dev_get_priv(dev);
const u16 val = bootcount_magic << 8 | (a & 0xff);
if (i2c_eeprom_write(priv->i2c_eeprom, priv->offset,
(uint8_t *)&val, 2) < 0) {
debug("%s: write failed\n", __func__);
return -EIO;
}
return 0;
}
static int bootcount_i2c_eeprom_get(struct udevice *dev, u32 *a)
{
struct bootcount_i2c_eeprom_priv *priv = dev_get_priv(dev);
u16 val;
if (i2c_eeprom_read(priv->i2c_eeprom, priv->offset,
(uint8_t *)&val, 2) < 0) {
debug("%s: read 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_i2c_eeprom_probe(struct udevice *dev)
{
struct ofnode_phandle_args phandle_args;
struct bootcount_i2c_eeprom_priv *priv = dev_get_priv(dev);
struct udevice *i2c_eeprom;
if (dev_read_phandle_with_args(dev, "i2c-eeprom", NULL, 0, 0,
&phandle_args)) {
debug("%s: i2c-eeprom backing device not specified\n",
dev->name);
return -ENOENT;
}
if (uclass_get_device_by_ofnode(UCLASS_I2C_EEPROM, phandle_args.node,
&i2c_eeprom)) {
debug("%s: could not get backing device\n", dev->name);
return -ENODEV;
}
priv->i2c_eeprom = i2c_eeprom;
priv->offset = dev_read_u32_default(dev, "offset", 0);
return 0;
}
static const struct bootcount_ops bootcount_i2c_eeprom_ops = {
.get = bootcount_i2c_eeprom_get,
.set = bootcount_i2c_eeprom_set,
};
static const struct udevice_id bootcount_i2c_eeprom_ids[] = {
{ .compatible = "u-boot,bootcount-i2c-eeprom" },
{ }
};
U_BOOT_DRIVER(bootcount_i2c_eeprom) = {
.name = "bootcount-i2c-eeprom",
.id = UCLASS_BOOTCOUNT,
.priv_auto = sizeof(struct bootcount_i2c_eeprom_priv),
.probe = bootcount_i2c_eeprom_probe,
.of_match = bootcount_i2c_eeprom_ids,
.ops = &bootcount_i2c_eeprom_ops,
};
+158
View File
@@ -0,0 +1,158 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2018-2022 Denx Software Engineering GmbH
* Heiko Schocher <hs@denx.de>
* Philip Oberfichtner <pro@denx.de>
*
* A bootcount driver using the registers MEMA - MEMD on the PFUZE100.
* This works only, if the PMIC is not connected to a battery.
*/
#include <bootcount.h>
#include <dm.h>
#include <power/pmic.h>
#include <power/pfuze100_pmic.h>
#define PFUZE_BC_MAGIC 0xdead
struct bootcount_pmic_priv {
struct udevice *pmic;
};
static int pfuze100_get_magic(struct udevice *dev, u32 *magic)
{
int ret;
ret = pmic_reg_read(dev, PFUZE100_MEMA);
if (ret < 0)
return ret;
*magic = ret;
ret = pmic_reg_read(dev, PFUZE100_MEMB);
if (ret < 0)
return ret;
*magic += ret << 8;
return 0;
}
static int pfuze100_set_magic(struct udevice *dev)
{
int ret;
ret = pmic_reg_write(dev, PFUZE100_MEMA, PFUZE_BC_MAGIC & 0xff);
if (ret)
return ret;
ret = pmic_reg_write(dev, PFUZE100_MEMB, (PFUZE_BC_MAGIC >> 8) & 0xff);
return ret;
}
static int pfuze100_get_value(struct udevice *dev, u32 *a)
{
int ret;
ret = pmic_reg_read(dev, PFUZE100_MEMC);
if (ret < 0)
return ret;
*a = ret;
ret = pmic_reg_read(dev, PFUZE100_MEMD);
if (ret < 0)
return ret;
*a += ret << 8;
return 0;
}
static int pfuze100_set_value(struct udevice *dev, u32 val)
{
int ret;
ret = pmic_reg_write(dev, PFUZE100_MEMC, val & 0xff);
if (ret)
return ret;
ret = pmic_reg_write(dev, PFUZE100_MEMD, (val >> 8) & 0xff);
return ret;
}
static int bootcount_pmic_set(struct udevice *dev, const u32 a)
{
struct bootcount_pmic_priv *priv = dev_get_priv(dev);
if (pfuze100_set_magic(priv->pmic)) {
debug("%s: writing magic failed\n", __func__);
return -EIO;
}
if (pfuze100_set_value(priv->pmic, a)) {
debug("%s: writing value failed\n", __func__);
return -EIO;
}
return 0;
}
static int bootcount_pmic_get(struct udevice *dev, u32 *a)
{
struct bootcount_pmic_priv *priv = dev_get_priv(dev);
u32 magic;
if (pfuze100_get_magic(priv->pmic, &magic)) {
debug("%s: reading magic failed\n", __func__);
return -EIO;
}
if (magic != PFUZE_BC_MAGIC) {
*a = 0;
return 0;
}
if (pfuze100_get_value(priv->pmic, a)) {
debug("%s: reading value failed\n", __func__);
return -EIO;
}
return 0;
}
static int bootcount_pmic_probe(struct udevice *dev)
{
struct ofnode_phandle_args phandle_args;
struct bootcount_pmic_priv *priv = dev_get_priv(dev);
struct udevice *pmic;
if (dev_read_phandle_with_args(dev, "pmic", NULL, 0, 0, &phandle_args)) {
debug("%s: pmic backing device not specified\n", dev->name);
return -ENOENT;
}
if (uclass_get_device_by_ofnode(UCLASS_PMIC, phandle_args.node, &pmic)) {
debug("%s: could not get backing device\n", dev->name);
return -ENODEV;
}
priv->pmic = pmic;
return 0;
}
static const struct bootcount_ops bootcount_pmic_ops = {
.get = bootcount_pmic_get,
.set = bootcount_pmic_set,
};
static const struct udevice_id bootcount_pmic_ids[] = {
{ .compatible = "u-boot,bootcount-pmic" },
{ }
};
U_BOOT_DRIVER(bootcount_pmic) = {
.name = "bootcount-pmic",
.id = UCLASS_BOOTCOUNT,
.priv_auto = sizeof(struct bootcount_pmic_priv),
.probe = bootcount_pmic_probe,
.of_match = bootcount_pmic_ids,
.ops = &bootcount_pmic_ops,
};
+89
View File
@@ -0,0 +1,89 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2018 Theobroma Systems Design und Consulting GmbH
*/
#include <bootcount.h>
#include <dm.h>
#include <log.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 = sizeof(struct bootcount_rtc_priv),
.probe = bootcount_rtc_probe,
.of_match = bootcount_rtc_ids,
.ops = &bootcount_rtc_ops,
};
+124
View File
@@ -0,0 +1,124 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2019 Collabora
* (C) Copyright 2019 GE
*/
#include <bootcount.h>
#include <dm.h>
#include <spi_flash.h>
static const u8 bootcount_magic = 0xbc;
struct bootcount_spi_flash_priv {
struct udevice *spi_flash;
u32 offset;
};
static int bootcount_spi_flash_update(struct udevice *dev, u32 offset, u32 len, const void *buf)
{
struct spi_flash *flash = dev_get_uclass_priv(dev);
u32 sector_size = flash->sector_size;
u32 sector_offset = offset % sector_size;
u32 sector = offset - sector_offset;
int err = 0;
/* code only supports updating a single sector */
if (sector_offset + len > sector_size)
return -ENOSYS;
u8 *buffer = malloc(sector_size);
if (!buffer)
return -ENOMEM;
err = spi_flash_read_dm(dev, sector, sector_size, buffer);
if (err < 0)
goto out;
memcpy(buffer + sector_offset, buf, len);
err = spi_flash_erase_dm(dev, sector, sector_size);
if (err < 0)
goto out;
err = spi_flash_write_dm(dev, sector, sector_size, buffer);
if (err < 0)
goto out;
out:
free(buffer);
return err;
}
static int bootcount_spi_flash_set(struct udevice *dev, const u32 a)
{
struct bootcount_spi_flash_priv *priv = dev_get_priv(dev);
const u16 val = bootcount_magic << 8 | (a & 0xff);
if (bootcount_spi_flash_update(priv->spi_flash, priv->offset, 2, &val) < 0) {
debug("%s: write failed\n", __func__);
return -EIO;
}
return 0;
}
static int bootcount_spi_flash_get(struct udevice *dev, u32 *a)
{
struct bootcount_spi_flash_priv *priv = dev_get_priv(dev);
u16 val;
if (spi_flash_read_dm(priv->spi_flash, priv->offset, 2, &val) < 0) {
debug("%s: read 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_spi_flash_probe(struct udevice *dev)
{
struct ofnode_phandle_args phandle_args;
struct bootcount_spi_flash_priv *priv = dev_get_priv(dev);
struct udevice *spi_flash;
if (dev_read_phandle_with_args(dev, "spi-flash", NULL, 0, 0, &phandle_args)) {
debug("%s: spi-flash backing device not specified\n", dev->name);
return -ENOENT;
}
if (uclass_get_device_by_ofnode(UCLASS_SPI_FLASH, phandle_args.node, &spi_flash)) {
debug("%s: could not get backing device\n", dev->name);
return -ENODEV;
}
priv->spi_flash = spi_flash;
priv->offset = dev_read_u32_default(dev, "offset", 0);
return 0;
}
static const struct bootcount_ops bootcount_spi_flash_ops = {
.get = bootcount_spi_flash_get,
.set = bootcount_spi_flash_set,
};
static const struct udevice_id bootcount_spi_flash_ids[] = {
{ .compatible = "u-boot,bootcount-spi-flash" },
{ }
};
U_BOOT_DRIVER(bootcount_spi_flash) = {
.name = "bootcount-spi-flash",
.id = UCLASS_BOOTCOUNT,
.priv_auto = sizeof(struct bootcount_spi_flash_priv),
.probe = bootcount_spi_flash_probe,
.of_match = bootcount_spi_flash_ids,
.ops = &bootcount_spi_flash_ops,
};
+37
View File
@@ -0,0 +1,37 @@
# SPDX-License-Identifier: GPL-2.0
#
# Bus Devices
#
menu "Bus devices"
config TI_PWMSS
bool
default y if ARCH_OMAP2PLUS && PWM_TI_EHRPWM
help
PWM Subsystem driver support for AM33xx SOC.
config TI_SYSC
bool "TI sysc interconnect target module driver"
depends on DM && ARCH_OMAP2PLUS
select CLK
help
Generic driver for Texas Instruments interconnect target module
found on many TI SoCs.
config SPL_TI_SYSC
bool "Support TI sysc interconnect in SPL"
depends on SPL_DM && TI_SYSC
help
Generic driver for Texas Instruments interconnect target module
found on many TI SoCs.
config UNIPHIER_SYSTEM_BUS
bool "UniPhier System Bus driver"
depends on ARCH_UNIPHIER
default y
help
Support for UniPhier System Bus, a simple external bus. This is
needed to use on-board devices connected to UniPhier SoCs.
endmenu
+11
View File
@@ -0,0 +1,11 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for the bus drivers.
#
ifeq ($(CONFIG_XPL_BUILD)$(CONFIG_TPL_BUILD),)
obj-$(CONFIG_TI_PWMSS) += ti-pwmss.o
obj-$(CONFIG_UNIPHIER_SYSTEM_BUS) += uniphier-system-bus.o
endif
obj-$(CONFIG_$(PHASE_)TI_SYSC) += ti-sysc.o
+20
View File
@@ -0,0 +1,20 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Pulse-Width Modulation Subsystem (pwmss)
*
* Copyright (C) 2020 Dario Binacchi <dariobin@libero.it>
*/
#include <dm.h>
static const struct udevice_id ti_pwmss_ids[] = {
{.compatible = "ti,am33xx-pwmss"},
{}
};
U_BOOT_DRIVER(ti_pwmss) = {
.name = "ti_pwmss",
.id = UCLASS_SIMPLE_BUS,
.of_match = ti_pwmss_ids,
.bind = dm_scan_fdt_dev,
};
+159
View File
@@ -0,0 +1,159 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Texas Instruments sysc interconnect target driver
*
* Copyright (C) 2020 Dario Binacchi <dariobin@libero.it>
*/
#include <clk.h>
#include <dm.h>
#include <dm/device_compat.h>
enum ti_sysc_clocks {
TI_SYSC_FCK,
TI_SYSC_ICK,
TI_SYSC_MAX_CLOCKS,
};
static const char *const clock_names[] = {"fck", "ick"};
struct ti_sysc_priv {
int clocks_count;
struct clk clocks[TI_SYSC_MAX_CLOCKS];
};
static const struct udevice_id ti_sysc_ids[] = {
{.compatible = "ti,sysc-omap2"},
{.compatible = "ti,sysc-omap4"},
{.compatible = "ti,sysc-omap4-simple"},
{.compatible = "ti,sysc-omap3430-sr"},
{.compatible = "ti,sysc-omap3630-sr"},
{.compatible = "ti,sysc-omap4-sr"},
{.compatible = "ti,sysc-omap3-sham"},
{.compatible = "ti,sysc-omap-aes"},
{.compatible = "ti,sysc-mcasp"},
{.compatible = "ti,sysc-usb-host-fs"},
{}
};
static int ti_sysc_get_one_clock(struct udevice *dev, enum ti_sysc_clocks index)
{
struct ti_sysc_priv *priv = dev_get_priv(dev);
const char *name;
int err;
switch (index) {
case TI_SYSC_FCK:
break;
case TI_SYSC_ICK:
break;
default:
return -EINVAL;
}
name = clock_names[index];
err = clk_get_by_name(dev, name, &priv->clocks[index]);
if (err) {
if (err == -ENODATA)
return 0;
dev_err(dev, "failed to get %s clock\n", name);
return err;
}
return 0;
}
static int ti_sysc_put_clocks(struct udevice *dev)
{
struct ti_sysc_priv *priv = dev_get_priv(dev);
int err;
err = clk_release_all(priv->clocks, priv->clocks_count);
if (err)
dev_err(dev, "failed to release all clocks\n");
return err;
}
static int ti_sysc_get_clocks(struct udevice *dev)
{
struct ti_sysc_priv *priv = dev_get_priv(dev);
int i, err;
for (i = 0; i < TI_SYSC_MAX_CLOCKS; i++) {
err = ti_sysc_get_one_clock(dev, i);
if (!err)
priv->clocks_count++;
else if (err != -ENOENT)
return err;
}
return 0;
}
static int ti_sysc_child_post_remove(struct udevice *dev)
{
struct ti_sysc_priv *priv = dev_get_priv(dev->parent);
int i, err;
for (i = 0; i < priv->clocks_count; i++) {
err = clk_disable(&priv->clocks[i]);
if (err) {
dev_err(dev->parent, "failed to disable %s clock\n",
clock_names[i]);
return err;
}
}
return 0;
}
static int ti_sysc_child_pre_probe(struct udevice *dev)
{
struct ti_sysc_priv *priv = dev_get_priv(dev->parent);
int i, err;
for (i = 0; i < priv->clocks_count; i++) {
err = clk_enable(&priv->clocks[i]);
if (err) {
dev_err(dev->parent, "failed to enable %s clock\n",
clock_names[i]);
return err;
}
}
return 0;
}
static int ti_sysc_remove(struct udevice *dev)
{
return ti_sysc_put_clocks(dev);
}
static int ti_sysc_probe(struct udevice *dev)
{
int err;
err = ti_sysc_get_clocks(dev);
if (err)
goto clocks_err;
return 0;
clocks_err:
ti_sysc_put_clocks(dev);
return err;
}
U_BOOT_DRIVER(ti_sysc) = {
.name = "ti_sysc",
.id = UCLASS_SIMPLE_BUS,
.of_match = ti_sysc_ids,
.probe = ti_sysc_probe,
.remove = ti_sysc_remove,
.child_pre_probe = ti_sysc_child_pre_probe,
.child_post_remove = ti_sysc_child_post_remove,
.priv_auto = sizeof(struct ti_sysc_priv)
};
+100
View File
@@ -0,0 +1,100 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <linux/bitops.h>
#include <linux/errno.h>
#include <linux/io.h>
#include <linux/sizes.h>
#include <linux/types.h>
#include <dm.h>
/* System Bus Controller registers */
#define UNIPHIER_SBC_BASE 0x100 /* base address of bank0 space */
#define UNIPHIER_SBC_BASE_BE BIT(0) /* bank_enable */
#define UNIPHIER_SBC_CTRL0 0x200 /* timing parameter 0 of bank0 */
#define UNIPHIER_SBC_CTRL1 0x204 /* timing parameter 1 of bank0 */
#define UNIPHIER_SBC_CTRL2 0x208 /* timing parameter 2 of bank0 */
#define UNIPHIER_SBC_CTRL3 0x20c /* timing parameter 3 of bank0 */
#define UNIPHIER_SBC_CTRL4 0x300 /* timing parameter 4 of bank0 */
#define UNIPHIER_SBC_STRIDE 0x10 /* register stride to next bank */
#if 1
/* slower but LED works */
#define SBCTRL0_VALUE 0x55450000
#define SBCTRL1_VALUE 0x07168d00
#define SBCTRL2_VALUE 0x34000009
#define SBCTRL4_VALUE 0x02110110
#else
/* faster but LED does not work */
#define SBCTRL0_VALUE 0x55450000
#define SBCTRL1_VALUE 0x06057700
/* NOR flash needs more wait counts than SRAM */
#define SBCTRL2_VALUE 0x34000009
#define SBCTRL4_VALUE 0x02110210
#endif
void uniphier_system_bus_set_reg(void __iomem *membase)
{
void __iomem *bank0_base = membase;
void __iomem *bank1_base = membase + UNIPHIER_SBC_STRIDE;
/*
* Only CS1 is connected to support card.
* BKSZ[1:0] should be set to "01".
*/
writel(SBCTRL0_VALUE, bank1_base + UNIPHIER_SBC_CTRL0);
writel(SBCTRL1_VALUE, bank1_base + UNIPHIER_SBC_CTRL1);
writel(SBCTRL2_VALUE, bank1_base + UNIPHIER_SBC_CTRL2);
writel(SBCTRL4_VALUE, bank1_base + UNIPHIER_SBC_CTRL4);
if (readl(bank1_base + UNIPHIER_SBC_BASE) & UNIPHIER_SBC_BASE_BE) {
/*
* Boot Swap On: boot from external NOR/SRAM
* 0x42000000-0x43ffffff is a mirror of 0x40000000-0x41ffffff.
*
* 0x40000000-0x41efffff, 0x42000000-0x43efffff: memory bank
* 0x41f00000-0x41ffffff, 0x43f00000-0x43ffffff: peripherals
*/
writel(0x0000bc01, bank0_base + UNIPHIER_SBC_BASE);
} else {
/*
* Boot Swap Off: boot from mask ROM
* 0x40000000-0x41ffffff: mask ROM
* 0x42000000-0x43efffff: memory bank (31MB)
* 0x43f00000-0x43ffffff: peripherals (1MB)
*/
writel(0x0000be01, bank0_base + UNIPHIER_SBC_BASE); /* dummy */
writel(0x0200be01, bank0_base + UNIPHIER_SBC_BASE);
}
}
static int uniphier_system_bus_probe(struct udevice *dev)
{
fdt_addr_t base;
void __iomem *membase;
base = dev_read_addr(dev);
if (base == FDT_ADDR_T_NONE)
return -EINVAL;
membase = devm_ioremap(dev, base, SZ_1K);
if (!membase)
return -ENOMEM;
uniphier_system_bus_set_reg(membase);
return 0;
}
static const struct udevice_id uniphier_system_bus_match[] = {
{ .compatible = "socionext,uniphier-system-bus" },
{ /* sentinel */ }
};
U_BOOT_DRIVER(uniphier_system_bus_driver) = {
.name = "uniphier-system-bus",
.id = UCLASS_SIMPLE_BUS,
.of_match = uniphier_system_bus_match,
.probe = uniphier_system_bus_probe,
};
+51
View File
@@ -0,0 +1,51 @@
menu "Button Support"
config BUTTON
bool "Enable button support"
depends on DM
help
Many boards have buttons which can be used to change behaviour (reset, ...).
U-Boot provides a uclass API to implement this feature. Button drivers
can provide access to board-specific buttons. Use of the device tree
for configuration is encouraged.
config BUTTON_REMAP_PHONE_KEYS
bool "Remap phone keys for navigation"
depends on BUTTON
help
Enable remapping of phone keys to navigation keys. This is useful for
devices with phone keys that are not used in U-Boot. The phone keys
are remapped to the following navigation keys:
- Volume up: Up
- Volume down: Down
- Power: Enter
config BUTTON_ADC
bool "Button adc"
depends on BUTTON
depends on ADC
help
Enable support for buttons which are connected to Analog to Digital
Converter device. The ADC driver must use driver model. Buttons are
configured using the device tree.
config BUTTON_GPIO
bool "Button gpio"
depends on BUTTON
depends on DM_GPIO
help
Enable support for buttons which are connected to GPIO lines. These
GPIOs may be on the SoC or some other device which provides GPIOs.
The GPIO driver must used driver model. Buttons are configured using
the device tree.
config BUTTON_QCOM_PMIC
bool "Qualcomm power button"
depends on BUTTON
depends on PMIC_QCOM
help
Enable support for the power and "resin" (usually volume down) buttons
on Qualcomm SoCs. These will be configured as the Enter and Down keys
respectively, allowing navigation of bootmenu with buttons on device.
endmenu
+8
View File
@@ -0,0 +1,8 @@
# SPDX-License-Identifier: GPL-2.0+
#
# Copyright (C) 2020 Philippe Reynes <philippe.reynes@softathome.com>
obj-$(CONFIG_BUTTON) += button-uclass.o
obj-$(CONFIG_BUTTON_ADC) += button-adc.o
obj-$(CONFIG_BUTTON_GPIO) += button-gpio.o
obj-$(CONFIG_BUTTON_QCOM_PMIC) += button-qcom-pmic.o

Some files were not shown because too many files have changed in this diff Show More