GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)
This commit is contained in:
@@ -0,0 +1,442 @@
|
||||
#
|
||||
# Multifunction miscellaneous devices
|
||||
#
|
||||
|
||||
menu "Multifunction device drivers"
|
||||
|
||||
config MISC
|
||||
bool "Enable Driver Model for Misc drivers"
|
||||
depends on DM
|
||||
help
|
||||
Enable driver model for miscellaneous devices. This class is
|
||||
used only for those do not fit other more general classes. A
|
||||
set of generic read, write and ioctl methods may be used to
|
||||
access the device.
|
||||
|
||||
config SPL_MISC
|
||||
bool "Enable Driver Model for Misc drivers in SPL"
|
||||
depends on SPL_DM
|
||||
help
|
||||
Enable driver model for miscellaneous devices. This class is
|
||||
used only for those do not fit other more general classes. A
|
||||
set of generic read, write and ioctl methods may be used to
|
||||
access the device.
|
||||
|
||||
config TPL_MISC
|
||||
bool "Enable Driver Model for Misc drivers in TPL"
|
||||
depends on TPL_DM
|
||||
help
|
||||
Enable driver model for miscellaneous devices. This class is
|
||||
used only for those do not fit other more general classes. A
|
||||
set of generic read, write and ioctl methods may be used to
|
||||
access the device.
|
||||
|
||||
config ALTERA_SYSID
|
||||
bool "Altera Sysid support"
|
||||
depends on MISC
|
||||
help
|
||||
Select this to enable a sysid for Altera devices. Please find
|
||||
details on the "Embedded Peripherals IP User Guide" of Altera.
|
||||
|
||||
config ATSHA204A
|
||||
bool "Support for Atmel ATSHA204A module"
|
||||
depends on MISC
|
||||
help
|
||||
Enable support for I2C connected Atmel's ATSHA204A
|
||||
CryptoAuthentication module found for example on the Turris Omnia
|
||||
board.
|
||||
|
||||
config ROCKCHIP_EFUSE
|
||||
bool "Rockchip e-fuse support"
|
||||
depends on MISC
|
||||
help
|
||||
Enable (read-only) access for the e-fuse block found in Rockchip
|
||||
SoCs: accesses can either be made using byte addressing and a length
|
||||
or through child-nodes that are generated based on the e-fuse map
|
||||
retrieved from the DTS.
|
||||
|
||||
This driver currently supports the RK3399 only, but can easily be
|
||||
extended (by porting the read function from the Linux kernel sources)
|
||||
to support other recent Rockchip devices.
|
||||
|
||||
config ROCKCHIP_OTP
|
||||
bool "Rockchip OTP Support"
|
||||
depends on MISC
|
||||
help
|
||||
Enable (read-only) access for the one-time-programmable memory block
|
||||
found in Rockchip SoCs: accesses can either be made using byte
|
||||
addressing and a length or through child-nodes that are generated
|
||||
based on the e-fuse map retrieved from the DTS.
|
||||
|
||||
config VEXPRESS_CONFIG
|
||||
bool "Enable support for Arm Versatile Express config bus"
|
||||
depends on MISC
|
||||
help
|
||||
If you say Y here, you will get support for accessing the
|
||||
configuration bus on the Arm Versatile Express boards via
|
||||
a sysreg driver.
|
||||
|
||||
config CMD_CROS_EC
|
||||
bool "Enable crosec command"
|
||||
depends on CROS_EC
|
||||
help
|
||||
Enable command-line access to the Chrome OS EC (Embedded
|
||||
Controller). This provides the 'crosec' command which has
|
||||
a number of sub-commands for performing EC tasks such as
|
||||
updating its flash, accessing a small saved context area
|
||||
and talking to the I2C bus behind the EC (if there is one).
|
||||
|
||||
config CROS_EC
|
||||
bool "Enable Chrome OS EC"
|
||||
help
|
||||
Enable access to the Chrome OS EC. This is a separate
|
||||
microcontroller typically available on a SPI bus on Chromebooks. It
|
||||
provides access to the keyboard, some internal storage and may
|
||||
control access to the battery and main PMIC depending on the
|
||||
device. You can use the 'crosec' command to access it.
|
||||
|
||||
config SPL_CROS_EC
|
||||
bool "Enable Chrome OS EC in SPL"
|
||||
depends on SPL
|
||||
help
|
||||
Enable access to the Chrome OS EC in SPL. This is a separate
|
||||
microcontroller typically available on a SPI bus on Chromebooks. It
|
||||
provides access to the keyboard, some internal storage and may
|
||||
control access to the battery and main PMIC depending on the
|
||||
device. You can use the 'crosec' command to access it.
|
||||
|
||||
config TPL_CROS_EC
|
||||
bool "Enable Chrome OS EC in TPL"
|
||||
depends on TPL
|
||||
help
|
||||
Enable access to the Chrome OS EC in TPL. This is a separate
|
||||
microcontroller typically available on a SPI bus on Chromebooks. It
|
||||
provides access to the keyboard, some internal storage and may
|
||||
control access to the battery and main PMIC depending on the
|
||||
device. You can use the 'crosec' command to access it.
|
||||
|
||||
config CROS_EC_I2C
|
||||
bool "Enable Chrome OS EC I2C driver"
|
||||
depends on CROS_EC
|
||||
help
|
||||
Enable I2C access to the Chrome OS EC. This is used on older
|
||||
ARM Chromebooks such as snow and spring before the standard bus
|
||||
changed to SPI. The EC will accept commands across the I2C using
|
||||
a special message protocol, and provide responses.
|
||||
|
||||
config CROS_EC_LPC
|
||||
bool "Enable Chrome OS EC LPC driver"
|
||||
depends on CROS_EC
|
||||
help
|
||||
Enable I2C access to the Chrome OS EC. This is used on x86
|
||||
Chromebooks such as link and falco. The keyboard is provided
|
||||
through a legacy port interface, so on x86 machines the main
|
||||
function of the EC is power and thermal management.
|
||||
|
||||
config SPL_CROS_EC_LPC
|
||||
bool "Enable Chrome OS EC LPC driver in SPL"
|
||||
depends on CROS_EC
|
||||
help
|
||||
Enable I2C access to the Chrome OS EC. This is used on x86
|
||||
Chromebooks such as link and falco. The keyboard is provided
|
||||
through a legacy port interface, so on x86 machines the main
|
||||
function of the EC is power and thermal management.
|
||||
|
||||
config TPL_CROS_EC_LPC
|
||||
bool "Enable Chrome OS EC LPC driver in TPL"
|
||||
depends on CROS_EC
|
||||
help
|
||||
Enable I2C access to the Chrome OS EC. This is used on x86
|
||||
Chromebooks such as link and falco. The keyboard is provided
|
||||
through a legacy port interface, so on x86 machines the main
|
||||
function of the EC is power and thermal management.
|
||||
|
||||
config CROS_EC_SANDBOX
|
||||
bool "Enable Chrome OS EC sandbox driver"
|
||||
depends on CROS_EC && SANDBOX
|
||||
help
|
||||
Enable a sandbox emulation of the Chrome OS EC. This supports
|
||||
keyboard (use the -l flag to enable the LCD), verified boot context,
|
||||
EC flash read/write/erase support and a few other things. It is
|
||||
enough to perform a Chrome OS verified boot on sandbox.
|
||||
|
||||
config SPL_CROS_EC_SANDBOX
|
||||
bool "Enable Chrome OS EC sandbox driver in SPL"
|
||||
depends on SPL_CROS_EC && SANDBOX
|
||||
help
|
||||
Enable a sandbox emulation of the Chrome OS EC in SPL. This supports
|
||||
keyboard (use the -l flag to enable the LCD), verified boot context,
|
||||
EC flash read/write/erase support and a few other things. It is
|
||||
enough to perform a Chrome OS verified boot on sandbox.
|
||||
|
||||
config TPL_CROS_EC_SANDBOX
|
||||
bool "Enable Chrome OS EC sandbox driver in TPL"
|
||||
depends on TPL_CROS_EC && SANDBOX
|
||||
help
|
||||
Enable a sandbox emulation of the Chrome OS EC in TPL. This supports
|
||||
keyboard (use the -l flag to enable the LCD), verified boot context,
|
||||
EC flash read/write/erase support and a few other things. It is
|
||||
enough to perform a Chrome OS verified boot on sandbox.
|
||||
|
||||
config CROS_EC_SPI
|
||||
bool "Enable Chrome OS EC SPI driver"
|
||||
depends on CROS_EC
|
||||
help
|
||||
Enable SPI access to the Chrome OS EC. This is used on newer
|
||||
ARM Chromebooks such as pit, pi and nyan-big. The SPI interface
|
||||
provides a faster and more robust interface than I2C but the bugs
|
||||
are less interesting.
|
||||
|
||||
config DS4510
|
||||
bool "Enable support for DS4510 CPU supervisor"
|
||||
help
|
||||
Enable support for the Maxim DS4510 CPU supervisor. It has an
|
||||
integrated 64-byte EEPROM, four programmable non-volatile I/O pins
|
||||
and a configurable timer for the supervisor function. The device is
|
||||
connected over I2C.
|
||||
|
||||
config FSL_SEC_MON
|
||||
bool "Enable FSL SEC_MON Driver"
|
||||
help
|
||||
Freescale Security Monitor block is responsible for monitoring
|
||||
system states.
|
||||
Security Monitor can be transitioned on any security failures,
|
||||
like software violations or hardware security violations.
|
||||
|
||||
config JZ4780_EFUSE
|
||||
bool "Ingenic JZ4780 eFUSE support"
|
||||
depends on ARCH_JZ47XX
|
||||
help
|
||||
This selects support for the eFUSE on Ingenic JZ4780 SoCs.
|
||||
|
||||
config MXC_OCOTP
|
||||
bool "Enable MXC OCOTP Driver"
|
||||
depends on ARCH_IMX8M || ARCH_MX6 || ARCH_MX7 || ARCH_MX7ULP || ARCH_VF610
|
||||
default y
|
||||
help
|
||||
If you say Y here, you will get support for the One Time
|
||||
Programmable memory pages that are stored on the some
|
||||
Freescale i.MX processors.
|
||||
|
||||
config NUVOTON_NCT6102D
|
||||
bool "Enable Nuvoton NCT6102D Super I/O driver"
|
||||
help
|
||||
If you say Y here, you will get support for the Nuvoton
|
||||
NCT6102D Super I/O driver. This can be used to enable or
|
||||
disable the legacy UART, the watchdog or other devices
|
||||
in the Nuvoton Super IO chips on X86 platforms.
|
||||
|
||||
config PWRSEQ
|
||||
bool "Enable power-sequencing drivers"
|
||||
depends on DM
|
||||
help
|
||||
Power-sequencing drivers provide support for controlling power for
|
||||
devices. They are typically referenced by a phandle from another
|
||||
device. When the device is started up, its power sequence can be
|
||||
initiated.
|
||||
|
||||
config SPL_PWRSEQ
|
||||
bool "Enable power-sequencing drivers for SPL"
|
||||
depends on PWRSEQ
|
||||
help
|
||||
Power-sequencing drivers provide support for controlling power for
|
||||
devices. They are typically referenced by a phandle from another
|
||||
device. When the device is started up, its power sequence can be
|
||||
initiated.
|
||||
|
||||
config PCA9551_LED
|
||||
bool "Enable PCA9551 LED driver"
|
||||
help
|
||||
Enable driver for PCA9551 LED controller. This controller
|
||||
is connected via I2C. So I2C needs to be enabled.
|
||||
|
||||
config PCA9551_I2C_ADDR
|
||||
hex "I2C address of PCA9551 LED controller"
|
||||
depends on PCA9551_LED
|
||||
default 0x60
|
||||
help
|
||||
The I2C address of the PCA9551 LED controller.
|
||||
|
||||
config STM32MP_FUSE
|
||||
bool "Enable STM32MP fuse wrapper providing the fuse API"
|
||||
depends on ARCH_STM32MP && MISC
|
||||
default y if CMD_FUSE
|
||||
help
|
||||
If you say Y here, you will get support for the fuse API (OTP)
|
||||
for STM32MP architecture.
|
||||
This API is needed for CMD_FUSE.
|
||||
|
||||
config STM32_RCC
|
||||
bool "Enable RCC driver for the STM32 SoC's family"
|
||||
depends on (STM32 || ARCH_STM32MP) && MISC
|
||||
help
|
||||
Enable the STM32 RCC driver. The RCC block (Reset and Clock Control
|
||||
block) is responsible of the management of the clock and reset
|
||||
generation.
|
||||
This driver is similar to an MFD driver in the Linux kernel.
|
||||
|
||||
config TEGRA_CAR
|
||||
bool "Enable support for the Tegra CAR driver"
|
||||
depends on TEGRA_NO_BPMP
|
||||
help
|
||||
The Tegra CAR (Clock and Reset Controller) is a HW module that
|
||||
controls almost all clocks and resets in a Tegra SoC.
|
||||
|
||||
config TEGRA186_BPMP
|
||||
bool "Enable support for the Tegra186 BPMP driver"
|
||||
depends on TEGRA186
|
||||
help
|
||||
The Tegra BPMP (Boot and Power Management Processor) is a separate
|
||||
auxiliary CPU embedded into Tegra to perform power management work,
|
||||
and controls related features such as clocks, resets, power domains,
|
||||
PMIC I2C bus, etc. This driver provides the core low-level
|
||||
communication path by which feature-specific drivers (such as clock)
|
||||
can make requests to the BPMP. This driver is similar to an MFD
|
||||
driver in the Linux kernel.
|
||||
|
||||
config TWL4030_LED
|
||||
bool "Enable TWL4030 LED controller"
|
||||
help
|
||||
Enable this to add support for the TWL4030 LED controller.
|
||||
|
||||
config WINBOND_W83627
|
||||
bool "Enable Winbond Super I/O driver"
|
||||
help
|
||||
If you say Y here, you will get support for the Winbond
|
||||
W83627 Super I/O driver. This can be used to enable the
|
||||
legacy UART or other devices in the Winbond Super IO chips
|
||||
on X86 platforms.
|
||||
|
||||
config QFW
|
||||
bool
|
||||
help
|
||||
Hidden option to enable QEMU fw_cfg interface. This will be selected by
|
||||
either CONFIG_CMD_QFW or CONFIG_GENERATE_ACPI_TABLE.
|
||||
|
||||
config I2C_EEPROM
|
||||
bool "Enable driver for generic I2C-attached EEPROMs"
|
||||
depends on MISC
|
||||
help
|
||||
Enable a generic driver for EEPROMs attached via I2C.
|
||||
|
||||
|
||||
config SPL_I2C_EEPROM
|
||||
bool "Enable driver for generic I2C-attached EEPROMs for SPL"
|
||||
depends on MISC && SPL && SPL_DM
|
||||
help
|
||||
This option is an SPL-variant of the I2C_EEPROM option.
|
||||
See the help of I2C_EEPROM for details.
|
||||
|
||||
config ZYNQ_GEM_I2C_MAC_OFFSET
|
||||
hex "Set the I2C MAC offset"
|
||||
default 0x0
|
||||
depends on DM_I2C
|
||||
help
|
||||
Set the MAC offset for i2C.
|
||||
|
||||
if I2C_EEPROM
|
||||
|
||||
config SYS_I2C_EEPROM_ADDR
|
||||
hex "Chip address of the EEPROM device"
|
||||
default 0
|
||||
|
||||
config SYS_I2C_EEPROM_BUS
|
||||
int "I2C bus of the EEPROM device."
|
||||
default 0
|
||||
|
||||
config SYS_EEPROM_SIZE
|
||||
int "Size in bytes of the EEPROM device"
|
||||
default 256
|
||||
|
||||
config SYS_EEPROM_PAGE_WRITE_BITS
|
||||
int "Number of bits used to address bytes in a single page"
|
||||
default 0
|
||||
help
|
||||
The EEPROM page size is 2^SYS_EEPROM_PAGE_WRITE_BITS.
|
||||
A 64 byte page, for example would require six bits.
|
||||
|
||||
config SYS_EEPROM_PAGE_WRITE_DELAY_MS
|
||||
int "Number of milliseconds to delay between page writes"
|
||||
default 0
|
||||
|
||||
config SYS_I2C_EEPROM_ADDR_LEN
|
||||
int "Length in bytes of the EEPROM memory array address"
|
||||
default 1
|
||||
help
|
||||
Note: This is NOT the chip address length!
|
||||
|
||||
config SYS_I2C_EEPROM_ADDR_OVERFLOW
|
||||
hex "EEPROM Address Overflow"
|
||||
default 0
|
||||
help
|
||||
EEPROM chips that implement "address overflow" are ones
|
||||
like Catalyst 24WC04/08/16 which has 9/10/11 bits of
|
||||
address and the extra bits end up in the "chip address" bit
|
||||
slots. This makes a 24WC08 (1Kbyte) chip look like four 256
|
||||
byte chips.
|
||||
|
||||
endif
|
||||
|
||||
config GDSYS_RXAUI_CTRL
|
||||
bool "Enable gdsys RXAUI control driver"
|
||||
depends on MISC
|
||||
help
|
||||
Support gdsys FPGA's RXAUI control.
|
||||
|
||||
config GDSYS_IOEP
|
||||
bool "Enable gdsys IOEP driver"
|
||||
depends on MISC
|
||||
help
|
||||
Support gdsys FPGA's IO endpoint driver.
|
||||
|
||||
config MPC83XX_SERDES
|
||||
bool "Enable MPC83xx serdes driver"
|
||||
depends on MISC
|
||||
help
|
||||
Support for serdes found on MPC83xx SoCs.
|
||||
|
||||
config FS_LOADER
|
||||
bool "Enable loader driver for file system"
|
||||
help
|
||||
This is file system generic loader which can be used to load
|
||||
the file image from the storage into target such as memory.
|
||||
|
||||
The consumer driver would then use this loader to program whatever,
|
||||
ie. the FPGA device.
|
||||
|
||||
config GDSYS_SOC
|
||||
bool "Enable gdsys SOC driver"
|
||||
depends on MISC
|
||||
help
|
||||
Support for gdsys IHS SOC, a simple bus associated with each gdsys
|
||||
IHS (Integrated Hardware Systems) FPGA, which holds all devices whose
|
||||
register maps are contained within the FPGA's register map.
|
||||
|
||||
config IHS_FPGA
|
||||
bool "Enable IHS FPGA driver"
|
||||
depends on MISC
|
||||
help
|
||||
Support IHS (Integrated Hardware Systems) FPGA, the main FPGAs on
|
||||
gdsys devices, which supply the majority of the functionality offered
|
||||
by the devices. This driver supports both CON and CPU variants of the
|
||||
devices, depending on the device tree entry.
|
||||
|
||||
config MICROCHIP_FLEXCOM
|
||||
bool "Enable Microchip Flexcom driver"
|
||||
depends on MISC
|
||||
help
|
||||
The Atmel Flexcom is just a wrapper which embeds a SPI controller,
|
||||
an I2C controller and an USART.
|
||||
Only one function can be used at a time and is chosen at boot time
|
||||
according to the device tree.
|
||||
|
||||
config K3_AVS0
|
||||
depends on ARCH_K3 && SPL_DM_REGULATOR
|
||||
bool "AVS class 0 support for K3 devices"
|
||||
help
|
||||
K3 devices have the optimized voltage values for the main voltage
|
||||
domains stored in efuse within the VTM IP. This driver reads the
|
||||
optimized voltage from the efuse, so that it can be programmed
|
||||
to the PMIC on board.
|
||||
|
||||
endmenu
|
||||
@@ -0,0 +1,70 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
# (C) Copyright 2000-2007
|
||||
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
|
||||
|
||||
obj-$(CONFIG_MISC) += misc-uclass.o
|
||||
|
||||
obj-$(CONFIG_$(SPL_TPL_)CROS_EC) += cros_ec.o
|
||||
obj-$(CONFIG_$(SPL_TPL_)CROS_EC_SANDBOX) += cros_ec_sandbox.o
|
||||
obj-$(CONFIG_$(SPL_TPL_)CROS_EC_LPC) += cros_ec_lpc.o
|
||||
|
||||
ifndef CONFIG_SPL_BUILD
|
||||
obj-$(CONFIG_CROS_EC_I2C) += cros_ec_i2c.o
|
||||
obj-$(CONFIG_CROS_EC_SPI) += cros_ec_spi.o
|
||||
obj-$(CONFIG_SANDBOX) += swap_case.o
|
||||
endif
|
||||
|
||||
ifdef CONFIG_DM_I2C
|
||||
ifndef CONFIG_SPL_BUILD
|
||||
obj-$(CONFIG_SANDBOX) += i2c_eeprom_emul.o
|
||||
endif
|
||||
endif
|
||||
ifdef CONFIG_SPL_OF_PLATDATA
|
||||
ifdef CONFIG_SPL_BUILD
|
||||
obj-$(CONFIG_SANDBOX) += spltest_sandbox.o
|
||||
endif
|
||||
endif
|
||||
obj-$(CONFIG_ALI152X) += ali512x.o
|
||||
obj-$(CONFIG_ALTERA_SYSID) += altera_sysid.o
|
||||
obj-$(CONFIG_ATSHA204A) += atsha204a-i2c.o
|
||||
obj-$(CONFIG_CBMEM_CONSOLE) += cbmem_console.o
|
||||
obj-$(CONFIG_DS4510) += ds4510.o
|
||||
obj-$(CONFIG_FSL_DEVICE_DISABLE) += fsl_devdis.o
|
||||
obj-$(CONFIG_FSL_IFC) += fsl_ifc.o
|
||||
obj-$(CONFIG_FSL_IIM) += fsl_iim.o
|
||||
obj-$(CONFIG_FSL_MC9SDZ60) += mc9sdz60.o
|
||||
obj-$(CONFIG_FSL_SEC_MON) += fsl_sec_mon.o
|
||||
obj-$(CONFIG_FS_LOADER) += fs_loader.o
|
||||
obj-$(CONFIG_GDSYS_IOEP) += gdsys_ioep.o
|
||||
obj-$(CONFIG_GDSYS_RXAUI_CTRL) += gdsys_rxaui_ctrl.o
|
||||
obj-$(CONFIG_GDSYS_SOC) += gdsys_soc.o
|
||||
obj-$(CONFIG_$(SPL_)I2C_EEPROM) += i2c_eeprom.o
|
||||
obj-$(CONFIG_IHS_FPGA) += ihs_fpga.o
|
||||
obj-$(CONFIG_IMX8) += imx8/
|
||||
obj-$(CONFIG_LED_STATUS) += status_led.o
|
||||
obj-$(CONFIG_LED_STATUS_GPIO) += gpio_led.o
|
||||
obj-$(CONFIG_MPC83XX_SERDES) += mpc83xx_serdes.o
|
||||
obj-$(CONFIG_MXC_OCOTP) += mxc_ocotp.o
|
||||
obj-$(CONFIG_MXS_OCOTP) += mxs_ocotp.o
|
||||
obj-$(CONFIG_NS87308) += ns87308.o
|
||||
obj-$(CONFIG_NUVOTON_NCT6102D) += nuvoton_nct6102d.o
|
||||
obj-$(CONFIG_PCA9551_LED) += pca9551_led.o
|
||||
obj-$(CONFIG_$(SPL_)PWRSEQ) += pwrseq-uclass.o
|
||||
obj-$(CONFIG_QFW) += qfw.o
|
||||
obj-$(CONFIG_ROCKCHIP_EFUSE) += rockchip-efuse.o
|
||||
obj-$(CONFIG_ROCKCHIP_OTP) += rockchip-otp.o
|
||||
obj-$(CONFIG_SANDBOX) += syscon_sandbox.o misc_sandbox.o
|
||||
obj-$(CONFIG_SMSC_LPC47M) += smsc_lpc47m.o
|
||||
obj-$(CONFIG_SMSC_SIO1007) += smsc_sio1007.o
|
||||
obj-$(CONFIG_STM32MP_FUSE) += stm32mp_fuse.o
|
||||
obj-$(CONFIG_STM32_RCC) += stm32_rcc.o
|
||||
obj-$(CONFIG_SYS_DPAA_QBMAN) += fsl_portals.o
|
||||
obj-$(CONFIG_TEGRA186_BPMP) += tegra186_bpmp.o
|
||||
obj-$(CONFIG_TEGRA_CAR) += tegra_car.o
|
||||
obj-$(CONFIG_TWL4030_LED) += twl4030_led.o
|
||||
obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress_config.o
|
||||
obj-$(CONFIG_WINBOND_W83627) += winbond_w83627.o
|
||||
obj-$(CONFIG_JZ4780_EFUSE) += jz4780_efuse.o
|
||||
obj-$(CONFIG_MICROCHIP_FLEXCOM) += microchip_flexcom.o
|
||||
obj-$(CONFIG_K3_AVS0) += k3_avs.o
|
||||
@@ -0,0 +1,401 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2002
|
||||
* Daniel Engström, Omicron Ceti AB <daniel@omicron.se>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Based on sc520cdp.c from rolo 1.6:
|
||||
*----------------------------------------------------------------------
|
||||
* (C) Copyright 2000
|
||||
* Sysgo Real-Time Solutions GmbH
|
||||
* Klein-Winternheim, Germany
|
||||
*----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <ali512x.h>
|
||||
|
||||
|
||||
/* ALI M5123 Logical device numbers:
|
||||
* 0 FDC
|
||||
* 1 unused?
|
||||
* 2 unused?
|
||||
* 3 lpt
|
||||
* 4 UART1
|
||||
* 5 UART2
|
||||
* 6 RTC
|
||||
* 7 mouse/kbd
|
||||
* 8 CIO
|
||||
*/
|
||||
|
||||
/*
|
||||
************************************************************
|
||||
* Some access primitives for the ALi chip: *
|
||||
************************************************************
|
||||
*/
|
||||
|
||||
static void ali_write(u8 index, u8 value)
|
||||
{
|
||||
/* write an arbirary register */
|
||||
outb(index, ALI_INDEX);
|
||||
outb(value, ALI_DATA);
|
||||
}
|
||||
|
||||
#if 0
|
||||
static int ali_read(u8 index)
|
||||
{
|
||||
outb(index, ALI_INDEX);
|
||||
return inb(ALI_DATA);
|
||||
}
|
||||
#endif
|
||||
|
||||
#define ALI_OPEN() \
|
||||
outb(0x51, ALI_INDEX); \
|
||||
outb(0x23, ALI_INDEX)
|
||||
|
||||
|
||||
#define ALI_CLOSE() \
|
||||
outb(0xbb, ALI_INDEX)
|
||||
|
||||
/* Select a logical device */
|
||||
#define ALI_SELDEV(dev) \
|
||||
ali_write(0x07, dev)
|
||||
|
||||
|
||||
void ali512x_init(void)
|
||||
{
|
||||
ALI_OPEN();
|
||||
|
||||
ali_write(0x02, 0x01); /* soft reset */
|
||||
ali_write(0x03, 0x03); /* disable access to CIOs */
|
||||
ali_write(0x22, 0x00); /* disable direct powerdown */
|
||||
ali_write(0x23, 0x00); /* disable auto powerdown */
|
||||
ali_write(0x24, 0x00); /* IR 8 is active hi, pin26 is PDIR */
|
||||
|
||||
ALI_CLOSE();
|
||||
}
|
||||
|
||||
void ali512x_set_fdc(int enabled, u16 io, u8 irq, u8 dma_channel)
|
||||
{
|
||||
ALI_OPEN();
|
||||
ALI_SELDEV(0);
|
||||
|
||||
ali_write(0x30, enabled?1:0);
|
||||
if (enabled) {
|
||||
ali_write(0x60, io >> 8);
|
||||
ali_write(0x61, io & 0xff);
|
||||
ali_write(0x70, irq);
|
||||
ali_write(0x74, dma_channel);
|
||||
|
||||
/* AT mode, no drive swap */
|
||||
ali_write(0xf0, 0x08);
|
||||
ali_write(0xf1, 0x00);
|
||||
ali_write(0xf2, 0xff);
|
||||
ali_write(0xf4, 0x00);
|
||||
}
|
||||
ALI_CLOSE();
|
||||
}
|
||||
|
||||
|
||||
void ali512x_set_pp(int enabled, u16 io, u8 irq, u8 dma_channel)
|
||||
{
|
||||
ALI_OPEN();
|
||||
ALI_SELDEV(3);
|
||||
|
||||
ali_write(0x30, enabled?1:0);
|
||||
if (enabled) {
|
||||
ali_write(0x60, io >> 8);
|
||||
ali_write(0x61, io & 0xff);
|
||||
ali_write(0x70, irq);
|
||||
ali_write(0x74, dma_channel);
|
||||
|
||||
/* mode: EPP 1.9, ECP FIFO threshold = 7, IRQ active low */
|
||||
ali_write(0xf0, 0xbc);
|
||||
/* 12 MHz, Burst DMA in ECP */
|
||||
ali_write(0xf1, 0x05);
|
||||
}
|
||||
ALI_CLOSE();
|
||||
|
||||
}
|
||||
|
||||
void ali512x_set_uart(int enabled, int index, u16 io, u8 irq)
|
||||
{
|
||||
ALI_OPEN();
|
||||
ALI_SELDEV(index?5:4);
|
||||
|
||||
ali_write(0x30, enabled?1:0);
|
||||
if (enabled) {
|
||||
ali_write(0x60, io >> 8);
|
||||
ali_write(0x61, io & 0xff);
|
||||
ali_write(0x70, irq);
|
||||
|
||||
ali_write(0xf0, 0x00);
|
||||
ali_write(0xf1, 0x00);
|
||||
|
||||
/* huh? write 0xf2 twice - a typo in rolo
|
||||
* or some secret ali errata? Who knows?
|
||||
*/
|
||||
if (index) {
|
||||
ali_write(0xf2, 0x00);
|
||||
}
|
||||
ali_write(0xf2, 0x0c);
|
||||
}
|
||||
ALI_CLOSE();
|
||||
|
||||
}
|
||||
|
||||
void ali512x_set_uart2_irda(int enabled)
|
||||
{
|
||||
ALI_OPEN();
|
||||
ALI_SELDEV(5);
|
||||
|
||||
ali_write(0xf1, enabled?0x48:0x00); /* fullduplex IrDa */
|
||||
ALI_CLOSE();
|
||||
|
||||
}
|
||||
|
||||
void ali512x_set_rtc(int enabled, u16 io, u8 irq)
|
||||
{
|
||||
ALI_OPEN();
|
||||
ALI_SELDEV(6);
|
||||
|
||||
ali_write(0x30, enabled?1:0);
|
||||
if (enabled) {
|
||||
ali_write(0x60, io >> 8);
|
||||
ali_write(0x61, io & 0xff);
|
||||
ali_write(0x70, irq);
|
||||
|
||||
ali_write(0xf0, 0x00);
|
||||
}
|
||||
ALI_CLOSE();
|
||||
}
|
||||
|
||||
void ali512x_set_kbc(int enabled, u8 kbc_irq, u8 mouse_irq)
|
||||
{
|
||||
ALI_OPEN();
|
||||
ALI_SELDEV(7);
|
||||
|
||||
ali_write(0x30, enabled?1:0);
|
||||
if (enabled) {
|
||||
ali_write(0x70, kbc_irq);
|
||||
ali_write(0x72, mouse_irq);
|
||||
|
||||
ali_write(0xf0, 0x00);
|
||||
}
|
||||
ALI_CLOSE();
|
||||
}
|
||||
|
||||
|
||||
/* Common I/O
|
||||
*
|
||||
* (This descripotsion is base on several incompete sources
|
||||
* since I have not been able to obtain any datasheet for the device
|
||||
* there may be some mis-understandings burried in here.
|
||||
* -- Daniel daniel@omicron.se)
|
||||
*
|
||||
* There are 22 CIO pins numbered
|
||||
* 10-17
|
||||
* 20-25
|
||||
* 30-37
|
||||
*
|
||||
* 20-24 are dedicated CIO pins, the other 17 are muliplexed with
|
||||
* other functions.
|
||||
*
|
||||
* Secondary
|
||||
* CIO Pin Function Decription
|
||||
* =======================================================
|
||||
* CIO10 IRQIN1 Interrupt input 1?
|
||||
* CIO11 IRQIN2 Interrupt input 2?
|
||||
* CIO12 IRRX IrDa Receive
|
||||
* CIO13 IRTX IrDa Transmit
|
||||
* CIO14 P21 KBC P21 fucntion
|
||||
* CIO15 P20 KBC P21 fucntion
|
||||
* CIO16 I2C_CLK I2C Clock
|
||||
* CIO17 I2C_DAT I2C Data
|
||||
*
|
||||
* CIO20 -
|
||||
* CIO21 -
|
||||
* CIO22 -
|
||||
* CIO23 -
|
||||
* CIO24 -
|
||||
* CIO25 LOCK Keylock
|
||||
*
|
||||
* CIO30 KBC_CLK Keybaord Clock
|
||||
* CIO31 CS0J General Chip Select decoder CS0J
|
||||
* CIO32 CS1J General Chip Select decoder CS1J
|
||||
* CIO33 ALT_KCLK Alternative Keyboard Clock
|
||||
* CIO34 ALT_KDAT Alternative Keyboard Data
|
||||
* CIO35 ALT_MCLK Alternative Mouse Clock
|
||||
* CIO36 ALT_MDAT Alternative Mouse Data
|
||||
* CIO37 ALT_KBC Alternative KBC select
|
||||
*
|
||||
* The CIO use an indirect address scheme.
|
||||
*
|
||||
* Reigster 3 in the SIO is used to select the index and data
|
||||
* port addresses where the CIO I/O registers show up.
|
||||
* The function selection registers are accessible under
|
||||
* function SIO 8.
|
||||
*
|
||||
* SIO reigster 3 (CIO Address Selection) bit definitions:
|
||||
* bit 7 CIO index and data registers enabled
|
||||
* bit 1-0 CIO indirect registers port address select
|
||||
* 0 index = 0xE0 data = 0xE1
|
||||
* 1 index = 0xE2 data = 0xE3
|
||||
* 2 index = 0xE4 data = 0xE5
|
||||
* 3 index = 0xEA data = 0xEB
|
||||
*
|
||||
* There are three CIO I/O register accessed via CIO index port and CIO data port
|
||||
* 0x01 CIO 10-17 data
|
||||
* 0x02 CIO 20-25 data (bits 7-6 unused)
|
||||
* 0x03 CIO 30-37 data
|
||||
*
|
||||
*
|
||||
* The pin function is accessed through normal
|
||||
* SIO registers, each register have the same format:
|
||||
*
|
||||
* Bit Function Value
|
||||
* 0 Input/output 1=input
|
||||
* 1 Polarity of signal 1=inverted
|
||||
* 2 Unused ??
|
||||
* 3 Function (normal or special) 1=special
|
||||
* 7-4 Unused
|
||||
*
|
||||
* SIO REG
|
||||
* 0xe0 CIO 10 Config
|
||||
* 0xe1 CIO 11 Config
|
||||
* 0xe2 CIO 12 Config
|
||||
* 0xe3 CIO 13 Config
|
||||
* 0xe4 CIO 14 Config
|
||||
* 0xe5 CIO 15 Config
|
||||
* 0xe6 CIO 16 Config
|
||||
* 0xe7 CIO 16 Config
|
||||
*
|
||||
* 0xe8 CIO 20 Config
|
||||
* 0xe9 CIO 21 Config
|
||||
* 0xea CIO 22 Config
|
||||
* 0xeb CIO 23 Config
|
||||
* 0xec CIO 24 Config
|
||||
* 0xed CIO 25 Config
|
||||
*
|
||||
* 0xf5 CIO 30 Config
|
||||
* 0xf6 CIO 31 Config
|
||||
* 0xf7 CIO 32 Config
|
||||
* 0xf8 CIO 33 Config
|
||||
* 0xf9 CIO 34 Config
|
||||
* 0xfa CIO 35 Config
|
||||
* 0xfb CIO 36 Config
|
||||
* 0xfc CIO 37 Config
|
||||
*
|
||||
*/
|
||||
|
||||
#define ALI_CIO_PORT_SEL 0x83
|
||||
#define ALI_CIO_INDEX 0xea
|
||||
#define ALI_CIO_DATA 0xeb
|
||||
|
||||
void ali512x_set_cio(int enabled)
|
||||
{
|
||||
int i;
|
||||
|
||||
ALI_OPEN();
|
||||
|
||||
if (enabled) {
|
||||
ali_write(0x3, ALI_CIO_PORT_SEL); /* Enable CIO data register */
|
||||
} else {
|
||||
ali_write(0x3, ALI_CIO_PORT_SEL & ~0x80);
|
||||
}
|
||||
|
||||
ALI_SELDEV(8);
|
||||
|
||||
ali_write(0x30, enabled?1:0);
|
||||
|
||||
/* set all pins to input to start with */
|
||||
for (i=0xe0;i<0xee;i++) {
|
||||
ali_write(i, 1);
|
||||
}
|
||||
|
||||
for (i=0xf5;i<0xfe;i++) {
|
||||
ali_write(i, 1);
|
||||
}
|
||||
|
||||
ALI_CLOSE();
|
||||
}
|
||||
|
||||
|
||||
void ali512x_cio_function(int pin, int special, int inv, int input)
|
||||
{
|
||||
u8 data;
|
||||
u8 addr;
|
||||
|
||||
/* valid pins are 10-17, 20-25 and 30-37 */
|
||||
if (pin >= 10 && pin <= 17) {
|
||||
addr = 0xe0+(pin&7);
|
||||
} else if (pin >= 20 && pin <= 25) {
|
||||
addr = 0xe8+(pin&7);
|
||||
} else if (pin >= 30 && pin <= 37) {
|
||||
addr = 0xf5+(pin&7);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
ALI_OPEN();
|
||||
|
||||
ALI_SELDEV(8);
|
||||
|
||||
|
||||
data=0xf4;
|
||||
if (special) {
|
||||
data |= 0x08;
|
||||
} else {
|
||||
if (inv) {
|
||||
data |= 0x02;
|
||||
}
|
||||
if (input) {
|
||||
data |= 0x01;
|
||||
}
|
||||
}
|
||||
|
||||
ali_write(addr, data);
|
||||
|
||||
ALI_CLOSE();
|
||||
}
|
||||
|
||||
void ali512x_cio_out(int pin, int value)
|
||||
{
|
||||
u8 reg;
|
||||
u8 data;
|
||||
u8 bit;
|
||||
|
||||
reg = pin/10;
|
||||
bit = 1 << (pin%10);
|
||||
|
||||
|
||||
outb(reg, ALI_CIO_INDEX); /* select I/O register */
|
||||
data = inb(ALI_CIO_DATA);
|
||||
if (value) {
|
||||
data |= bit;
|
||||
} else {
|
||||
data &= ~bit;
|
||||
}
|
||||
outb(data, ALI_CIO_DATA);
|
||||
}
|
||||
|
||||
int ali512x_cio_in(int pin)
|
||||
{
|
||||
u8 reg;
|
||||
u8 data;
|
||||
u8 bit;
|
||||
|
||||
/* valid pins are 10-17, 20-25 and 30-37 */
|
||||
reg = pin/10;
|
||||
bit = 1 << (pin%10);
|
||||
|
||||
|
||||
outb(reg, ALI_CIO_INDEX); /* select I/O register */
|
||||
data = inb(ALI_CIO_DATA);
|
||||
|
||||
return data & bit;
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2004, Psyent Corporation <www.psyent.com>
|
||||
* Scott McNutt <smcnutt@psyent.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <command.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <misc.h>
|
||||
#include <linux/time.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
struct altera_sysid_regs {
|
||||
u32 id; /* The system build id */
|
||||
u32 timestamp; /* Timestamp */
|
||||
};
|
||||
|
||||
struct altera_sysid_platdata {
|
||||
struct altera_sysid_regs *regs;
|
||||
};
|
||||
|
||||
void display_sysid(void)
|
||||
{
|
||||
struct udevice *dev;
|
||||
u32 sysid[2];
|
||||
struct tm t;
|
||||
char asc[32];
|
||||
time_t stamp;
|
||||
int ret;
|
||||
|
||||
/* the first misc device will be used */
|
||||
ret = uclass_first_device_err(UCLASS_MISC, &dev);
|
||||
if (ret)
|
||||
return;
|
||||
ret = misc_read(dev, 0, &sysid, sizeof(sysid));
|
||||
if (ret < 0)
|
||||
return;
|
||||
|
||||
stamp = sysid[1];
|
||||
localtime_r(&stamp, &t);
|
||||
asctime_r(&t, asc);
|
||||
printf("SYSID: %08x, %s", sysid[0], asc);
|
||||
}
|
||||
|
||||
int do_sysid(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
|
||||
{
|
||||
display_sysid();
|
||||
return 0;
|
||||
}
|
||||
|
||||
U_BOOT_CMD(
|
||||
sysid, 1, 1, do_sysid,
|
||||
"display Nios-II system id",
|
||||
""
|
||||
);
|
||||
|
||||
static int altera_sysid_read(struct udevice *dev,
|
||||
int offset, void *buf, int size)
|
||||
{
|
||||
struct altera_sysid_platdata *plat = dev->platdata;
|
||||
struct altera_sysid_regs *const regs = plat->regs;
|
||||
u32 *sysid = buf;
|
||||
|
||||
sysid[0] = readl(®s->id);
|
||||
sysid[1] = readl(®s->timestamp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int altera_sysid_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct altera_sysid_platdata *plat = dev_get_platdata(dev);
|
||||
|
||||
plat->regs = map_physmem(devfdt_get_addr(dev),
|
||||
sizeof(struct altera_sysid_regs),
|
||||
MAP_NOCACHE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct misc_ops altera_sysid_ops = {
|
||||
.read = altera_sysid_read,
|
||||
};
|
||||
|
||||
static const struct udevice_id altera_sysid_ids[] = {
|
||||
{ .compatible = "altr,sysid-1.0" },
|
||||
{}
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(altera_sysid) = {
|
||||
.name = "altera_sysid",
|
||||
.id = UCLASS_MISC,
|
||||
.of_match = altera_sysid_ids,
|
||||
.ofdata_to_platdata = altera_sysid_ofdata_to_platdata,
|
||||
.platdata_auto_alloc_size = sizeof(struct altera_sysid_platdata),
|
||||
.ops = &altera_sysid_ops,
|
||||
};
|
||||
@@ -0,0 +1,409 @@
|
||||
/*
|
||||
* I2C Driver for Atmel ATSHA204 over I2C
|
||||
*
|
||||
* Copyright (C) 2014 Josh Datko, Cryptotronix, jbd@cryptotronix.com
|
||||
* 2016 Tomas Hlavacek, CZ.NIC, tmshlvck@gmail.com
|
||||
* 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <i2c.h>
|
||||
#include <errno.h>
|
||||
#include <atsha204a-i2c.h>
|
||||
#include <u-boot/crc.h>
|
||||
|
||||
#define ATSHA204A_TWLO 60
|
||||
#define ATSHA204A_TRANSACTION_TIMEOUT 100000
|
||||
#define ATSHA204A_TRANSACTION_RETRY 5
|
||||
#define ATSHA204A_EXECTIME 5000
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
/*
|
||||
* The ATSHA204A uses an (to me) unknown CRC-16 algorithm.
|
||||
* The Reveng CRC-16 catalogue does not contain it.
|
||||
*
|
||||
* Because in Atmel's documentation only a primitive implementation
|
||||
* can be found, I have implemented this one with lookup table.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is the code that computes the table below:
|
||||
*
|
||||
* int i, j;
|
||||
* for (i = 0; i < 256; ++i) {
|
||||
* u8 c = 0;
|
||||
* for (j = 0; j < 8; ++j) {
|
||||
* c = (c << 1) | ((i >> j) & 1);
|
||||
* }
|
||||
* bitreverse_table[i] = c;
|
||||
* }
|
||||
*/
|
||||
|
||||
static u8 const bitreverse_table[256] = {
|
||||
0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
|
||||
0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
|
||||
0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
|
||||
0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
|
||||
0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
|
||||
0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
|
||||
0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
|
||||
0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
|
||||
0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
|
||||
0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
|
||||
0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
|
||||
0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
|
||||
0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
|
||||
0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
|
||||
0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
|
||||
0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
|
||||
0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
|
||||
0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
|
||||
0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
|
||||
0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
|
||||
0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
|
||||
0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
|
||||
0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
|
||||
0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
|
||||
0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
|
||||
0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
|
||||
0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
|
||||
0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
|
||||
0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
|
||||
0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
|
||||
0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
|
||||
0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
|
||||
};
|
||||
|
||||
/*
|
||||
* This is the code that computes the table below:
|
||||
*
|
||||
* int i, j;
|
||||
* for (i = 0; i < 256; ++i) {
|
||||
* u16 c = i << 8;
|
||||
* for (j = 0; j < 8; ++j) {
|
||||
* int b = c >> 15;
|
||||
* c <<= 1;
|
||||
* if (b)
|
||||
* c ^= 0x8005;
|
||||
* }
|
||||
* crc16_table[i] = c;
|
||||
* }
|
||||
*/
|
||||
static u16 const crc16_table[256] = {
|
||||
0x0000, 0x8005, 0x800f, 0x000a, 0x801b, 0x001e, 0x0014, 0x8011,
|
||||
0x8033, 0x0036, 0x003c, 0x8039, 0x0028, 0x802d, 0x8027, 0x0022,
|
||||
0x8063, 0x0066, 0x006c, 0x8069, 0x0078, 0x807d, 0x8077, 0x0072,
|
||||
0x0050, 0x8055, 0x805f, 0x005a, 0x804b, 0x004e, 0x0044, 0x8041,
|
||||
0x80c3, 0x00c6, 0x00cc, 0x80c9, 0x00d8, 0x80dd, 0x80d7, 0x00d2,
|
||||
0x00f0, 0x80f5, 0x80ff, 0x00fa, 0x80eb, 0x00ee, 0x00e4, 0x80e1,
|
||||
0x00a0, 0x80a5, 0x80af, 0x00aa, 0x80bb, 0x00be, 0x00b4, 0x80b1,
|
||||
0x8093, 0x0096, 0x009c, 0x8099, 0x0088, 0x808d, 0x8087, 0x0082,
|
||||
0x8183, 0x0186, 0x018c, 0x8189, 0x0198, 0x819d, 0x8197, 0x0192,
|
||||
0x01b0, 0x81b5, 0x81bf, 0x01ba, 0x81ab, 0x01ae, 0x01a4, 0x81a1,
|
||||
0x01e0, 0x81e5, 0x81ef, 0x01ea, 0x81fb, 0x01fe, 0x01f4, 0x81f1,
|
||||
0x81d3, 0x01d6, 0x01dc, 0x81d9, 0x01c8, 0x81cd, 0x81c7, 0x01c2,
|
||||
0x0140, 0x8145, 0x814f, 0x014a, 0x815b, 0x015e, 0x0154, 0x8151,
|
||||
0x8173, 0x0176, 0x017c, 0x8179, 0x0168, 0x816d, 0x8167, 0x0162,
|
||||
0x8123, 0x0126, 0x012c, 0x8129, 0x0138, 0x813d, 0x8137, 0x0132,
|
||||
0x0110, 0x8115, 0x811f, 0x011a, 0x810b, 0x010e, 0x0104, 0x8101,
|
||||
0x8303, 0x0306, 0x030c, 0x8309, 0x0318, 0x831d, 0x8317, 0x0312,
|
||||
0x0330, 0x8335, 0x833f, 0x033a, 0x832b, 0x032e, 0x0324, 0x8321,
|
||||
0x0360, 0x8365, 0x836f, 0x036a, 0x837b, 0x037e, 0x0374, 0x8371,
|
||||
0x8353, 0x0356, 0x035c, 0x8359, 0x0348, 0x834d, 0x8347, 0x0342,
|
||||
0x03c0, 0x83c5, 0x83cf, 0x03ca, 0x83db, 0x03de, 0x03d4, 0x83d1,
|
||||
0x83f3, 0x03f6, 0x03fc, 0x83f9, 0x03e8, 0x83ed, 0x83e7, 0x03e2,
|
||||
0x83a3, 0x03a6, 0x03ac, 0x83a9, 0x03b8, 0x83bd, 0x83b7, 0x03b2,
|
||||
0x0390, 0x8395, 0x839f, 0x039a, 0x838b, 0x038e, 0x0384, 0x8381,
|
||||
0x0280, 0x8285, 0x828f, 0x028a, 0x829b, 0x029e, 0x0294, 0x8291,
|
||||
0x82b3, 0x02b6, 0x02bc, 0x82b9, 0x02a8, 0x82ad, 0x82a7, 0x02a2,
|
||||
0x82e3, 0x02e6, 0x02ec, 0x82e9, 0x02f8, 0x82fd, 0x82f7, 0x02f2,
|
||||
0x02d0, 0x82d5, 0x82df, 0x02da, 0x82cb, 0x02ce, 0x02c4, 0x82c1,
|
||||
0x8243, 0x0246, 0x024c, 0x8249, 0x0258, 0x825d, 0x8257, 0x0252,
|
||||
0x0270, 0x8275, 0x827f, 0x027a, 0x826b, 0x026e, 0x0264, 0x8261,
|
||||
0x0220, 0x8225, 0x822f, 0x022a, 0x823b, 0x023e, 0x0234, 0x8231,
|
||||
0x8213, 0x0216, 0x021c, 0x8219, 0x0208, 0x820d, 0x8207, 0x0202,
|
||||
};
|
||||
|
||||
static inline u16 crc16_byte(u16 crc, const u8 data)
|
||||
{
|
||||
u16 t = crc16_table[((crc >> 8) ^ bitreverse_table[data]) & 0xff];
|
||||
return ((crc << 8) ^ t);
|
||||
}
|
||||
|
||||
static u16 atsha204a_crc16(const u8 *buffer, size_t len)
|
||||
{
|
||||
u16 crc = 0;
|
||||
|
||||
while (len--)
|
||||
crc = crc16_byte(crc, *buffer++);
|
||||
|
||||
return cpu_to_le16(crc);
|
||||
}
|
||||
|
||||
static int atsha204a_send(struct udevice *dev, const u8 *buf, u8 len)
|
||||
{
|
||||
fdt_addr_t *priv = dev_get_priv(dev);
|
||||
struct i2c_msg msg;
|
||||
|
||||
msg.addr = *priv;
|
||||
msg.flags = I2C_M_STOP;
|
||||
msg.len = len;
|
||||
msg.buf = (u8 *) buf;
|
||||
|
||||
return dm_i2c_xfer(dev, &msg, 1);
|
||||
}
|
||||
|
||||
static int atsha204a_recv(struct udevice *dev, u8 *buf, u8 len)
|
||||
{
|
||||
fdt_addr_t *priv = dev_get_priv(dev);
|
||||
struct i2c_msg msg;
|
||||
|
||||
msg.addr = *priv;
|
||||
msg.flags = I2C_M_RD | I2C_M_STOP;
|
||||
msg.len = len;
|
||||
msg.buf = (u8 *) buf;
|
||||
|
||||
return dm_i2c_xfer(dev, &msg, 1);
|
||||
}
|
||||
|
||||
static int atsha204a_recv_resp(struct udevice *dev,
|
||||
struct atsha204a_resp *resp)
|
||||
{
|
||||
int res;
|
||||
u16 resp_crc, computed_crc;
|
||||
u8 *p = (u8 *) resp;
|
||||
|
||||
res = atsha204a_recv(dev, p, 4);
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
if (resp->length > 4) {
|
||||
if (resp->length > sizeof(*resp))
|
||||
return -EMSGSIZE;
|
||||
|
||||
res = atsha204a_recv(dev, p + 4, resp->length - 4);
|
||||
if (res)
|
||||
return res;
|
||||
}
|
||||
|
||||
resp_crc = (u16) p[resp->length - 2]
|
||||
| (((u16) p[resp->length - 1]) << 8);
|
||||
computed_crc = atsha204a_crc16(p, resp->length - 2);
|
||||
|
||||
if (resp_crc != computed_crc) {
|
||||
debug("Invalid checksum in ATSHA204A response\n");
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int atsha204a_wakeup(struct udevice *dev)
|
||||
{
|
||||
u8 req[4];
|
||||
struct atsha204a_resp resp;
|
||||
int try, res;
|
||||
|
||||
debug("Waking up ATSHA204A\n");
|
||||
|
||||
for (try = 1; try <= 10; ++try) {
|
||||
debug("Try %i... ", try);
|
||||
|
||||
memset(req, 0, 4);
|
||||
res = atsha204a_send(dev, req, 4);
|
||||
if (res) {
|
||||
debug("failed on I2C send, trying again\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
udelay(ATSHA204A_TWLO);
|
||||
|
||||
res = atsha204a_recv_resp(dev, &resp);
|
||||
if (res) {
|
||||
debug("failed on receiving response, ending\n");
|
||||
return res;
|
||||
}
|
||||
|
||||
if (resp.code != ATSHA204A_STATUS_AFTER_WAKE) {
|
||||
debug ("failed (responce code = %02x), ending\n",
|
||||
resp.code);
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
debug("success\n");
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int atsha204a_idle(struct udevice *dev)
|
||||
{
|
||||
int res;
|
||||
u8 req = ATSHA204A_FUNC_IDLE;
|
||||
|
||||
res = atsha204a_send(dev, &req, 1);
|
||||
if (res)
|
||||
debug("Failed putting ATSHA204A idle\n");
|
||||
return res;
|
||||
}
|
||||
|
||||
int atsha204a_sleep(struct udevice *dev)
|
||||
{
|
||||
int res;
|
||||
u8 req = ATSHA204A_FUNC_IDLE;
|
||||
|
||||
res = atsha204a_send(dev, &req, 1);
|
||||
if (res)
|
||||
debug("Failed putting ATSHA204A to sleep\n");
|
||||
return res;
|
||||
}
|
||||
|
||||
static int atsha204a_transaction(struct udevice *dev, struct atsha204a_req *req,
|
||||
struct atsha204a_resp *resp)
|
||||
{
|
||||
int res, timeout = ATSHA204A_TRANSACTION_TIMEOUT;
|
||||
|
||||
res = atsha204a_send(dev, (u8 *) req, req->length + 1);
|
||||
if (res) {
|
||||
debug("ATSHA204A transaction send failed\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
do {
|
||||
res = atsha204a_recv_resp(dev, resp);
|
||||
if (!res || res == -EMSGSIZE || res == -EBADMSG)
|
||||
break;
|
||||
|
||||
debug("ATSHA204A transaction polling for response "
|
||||
"(timeout = %d)\n", timeout);
|
||||
|
||||
udelay(ATSHA204A_EXECTIME);
|
||||
timeout -= ATSHA204A_EXECTIME;
|
||||
} while (timeout > 0);
|
||||
|
||||
if (timeout <= 0) {
|
||||
debug("ATSHA204A transaction timed out\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static void atsha204a_req_crc32(struct atsha204a_req *req)
|
||||
{
|
||||
u8 *p = (u8 *) req;
|
||||
u16 computed_crc;
|
||||
u16 *crc_ptr = (u16 *) &p[req->length - 1];
|
||||
|
||||
/* The buffer to crc16 starts at byte 1, not 0 */
|
||||
computed_crc = atsha204a_crc16(p + 1, req->length - 2);
|
||||
|
||||
*crc_ptr = cpu_to_le16(computed_crc);
|
||||
}
|
||||
|
||||
int atsha204a_read(struct udevice *dev, enum atsha204a_zone zone, bool read32,
|
||||
u16 addr, u8 *buffer)
|
||||
{
|
||||
int res, retry = ATSHA204A_TRANSACTION_RETRY;
|
||||
struct atsha204a_req req;
|
||||
struct atsha204a_resp resp;
|
||||
|
||||
req.function = ATSHA204A_FUNC_COMMAND;
|
||||
req.length = 7;
|
||||
req.command = ATSHA204A_CMD_READ;
|
||||
|
||||
req.param1 = (u8) zone;
|
||||
if (read32)
|
||||
req.param1 |= 0x80;
|
||||
|
||||
req.param2 = cpu_to_le16(addr);
|
||||
|
||||
atsha204a_req_crc32(&req);
|
||||
|
||||
do {
|
||||
res = atsha204a_transaction(dev, &req, &resp);
|
||||
if (!res)
|
||||
break;
|
||||
|
||||
debug("ATSHA204A read retry (%d)\n", retry);
|
||||
retry--;
|
||||
atsha204a_wakeup(dev);
|
||||
} while (retry >= 0);
|
||||
|
||||
if (res) {
|
||||
debug("ATSHA204A read failed\n");
|
||||
return res;
|
||||
}
|
||||
|
||||
if (resp.length != (read32 ? 32 : 4) + 3) {
|
||||
debug("ATSHA204A read bad response length (%d)\n",
|
||||
resp.length);
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
memcpy(buffer, ((u8 *) &resp) + 1, read32 ? 32 : 4);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int atsha204a_get_random(struct udevice *dev, u8 *buffer, size_t max)
|
||||
{
|
||||
int res;
|
||||
struct atsha204a_req req;
|
||||
struct atsha204a_resp resp;
|
||||
|
||||
req.function = ATSHA204A_FUNC_COMMAND;
|
||||
req.length = 7;
|
||||
req.command = ATSHA204A_CMD_RANDOM;
|
||||
|
||||
req.param1 = 1;
|
||||
req.param2 = 0;
|
||||
|
||||
/* We do not have to compute the checksum dynamically */
|
||||
req.data[0] = 0x27;
|
||||
req.data[1] = 0x47;
|
||||
|
||||
res = atsha204a_transaction(dev, &req, &resp);
|
||||
if (res) {
|
||||
debug("ATSHA204A random transaction failed\n");
|
||||
return res;
|
||||
}
|
||||
|
||||
memcpy(buffer, ((u8 *) &resp) + 1, max >= 32 ? 32 : max);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int atsha204a_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
fdt_addr_t *priv = dev_get_priv(dev);
|
||||
fdt_addr_t addr;
|
||||
|
||||
addr = fdtdec_get_addr(gd->fdt_blob, dev_of_offset(dev), "reg");
|
||||
if (addr == FDT_ADDR_T_NONE) {
|
||||
debug("Can't get ATSHA204A I2C base address\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
*priv = addr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id atsha204a_ids[] = {
|
||||
{ .compatible = "atmel,atsha204a" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(atsha204) = {
|
||||
.name = "atsha204",
|
||||
.id = UCLASS_MISC,
|
||||
.of_match = atsha204a_ids,
|
||||
.ofdata_to_platdata = atsha204a_ofdata_to_platdata,
|
||||
.priv_auto_alloc_size = sizeof(fdt_addr_t),
|
||||
};
|
||||
@@ -0,0 +1,55 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2011 The ChromiumOS Authors. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <console.h>
|
||||
#ifndef CONFIG_SYS_COREBOOT
|
||||
#error This driver requires coreboot
|
||||
#endif
|
||||
|
||||
#include <asm/arch/sysinfo.h>
|
||||
|
||||
struct cbmem_console {
|
||||
u32 buffer_size;
|
||||
u32 buffer_cursor;
|
||||
u8 buffer_body[0];
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
static struct cbmem_console *cbmem_console_p;
|
||||
|
||||
void cbmemc_putc(struct stdio_dev *dev, char data)
|
||||
{
|
||||
int cursor;
|
||||
|
||||
cursor = cbmem_console_p->buffer_cursor++;
|
||||
if (cursor < cbmem_console_p->buffer_size)
|
||||
cbmem_console_p->buffer_body[cursor] = data;
|
||||
}
|
||||
|
||||
void cbmemc_puts(struct stdio_dev *dev, const char *str)
|
||||
{
|
||||
char c;
|
||||
|
||||
while ((c = *str++) != 0)
|
||||
cbmemc_putc(dev, c);
|
||||
}
|
||||
|
||||
int cbmemc_init(void)
|
||||
{
|
||||
int rc;
|
||||
struct stdio_dev cons_dev;
|
||||
cbmem_console_p = lib_sysinfo.cbmem_cons;
|
||||
|
||||
memset(&cons_dev, 0, sizeof(cons_dev));
|
||||
|
||||
strcpy(cons_dev.name, "cbmem");
|
||||
cons_dev.flags = DEV_FLAGS_OUTPUT; /* Output only */
|
||||
cons_dev.putc = cbmemc_putc;
|
||||
cons_dev.puts = cbmemc_puts;
|
||||
|
||||
rc = stdio_register(&cons_dev);
|
||||
|
||||
return (rc == 0) ? 1 : rc;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,239 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Chromium OS cros_ec driver - I2C interface
|
||||
*
|
||||
* Copyright (c) 2012 The Chromium OS Authors.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The Matrix Keyboard Protocol driver handles talking to the keyboard
|
||||
* controller chip. Mostly this is for keyboard functions, but some other
|
||||
* things have slipped in, so we provide generic services to talk to the
|
||||
* KBC.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <i2c.h>
|
||||
#include <cros_ec.h>
|
||||
|
||||
#ifdef DEBUG_TRACE
|
||||
#define debug_trace(fmt, b...) debug(fmt, #b)
|
||||
#else
|
||||
#define debug_trace(fmt, b...)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Request format for protocol v3
|
||||
* byte 0 0xda (EC_COMMAND_PROTOCOL_3)
|
||||
* byte 1-8 struct ec_host_request
|
||||
* byte 10- response data
|
||||
*/
|
||||
struct ec_host_request_i2c {
|
||||
/* Always 0xda to backward compatible with v2 struct */
|
||||
uint8_t command_protocol;
|
||||
struct ec_host_request ec_request;
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Response format for protocol v3
|
||||
* byte 0 result code
|
||||
* byte 1 packet_length
|
||||
* byte 2-9 struct ec_host_response
|
||||
* byte 10- response data
|
||||
*/
|
||||
struct ec_host_response_i2c {
|
||||
uint8_t result;
|
||||
uint8_t packet_length;
|
||||
struct ec_host_response ec_response;
|
||||
} __packed;
|
||||
|
||||
static int cros_ec_i2c_packet(struct udevice *udev, int out_bytes, int in_bytes)
|
||||
{
|
||||
struct cros_ec_dev *dev = dev_get_uclass_priv(udev);
|
||||
struct dm_i2c_chip *chip = dev_get_parent_platdata(udev);
|
||||
struct ec_host_request_i2c *ec_request_i2c =
|
||||
(struct ec_host_request_i2c *)dev->dout;
|
||||
struct ec_host_response_i2c *ec_response_i2c =
|
||||
(struct ec_host_response_i2c *)dev->din;
|
||||
struct i2c_msg i2c_msg[2];
|
||||
int ret;
|
||||
|
||||
i2c_msg[0].addr = chip->chip_addr;
|
||||
i2c_msg[0].flags = 0;
|
||||
i2c_msg[1].addr = chip->chip_addr;
|
||||
i2c_msg[1].flags = I2C_M_RD;
|
||||
|
||||
/* one extra byte, to indicate v3 */
|
||||
i2c_msg[0].len = out_bytes + 1;
|
||||
i2c_msg[0].buf = dev->dout;
|
||||
|
||||
/* stitch on EC_COMMAND_PROTOCOL_3 */
|
||||
memmove(&ec_request_i2c->ec_request, dev->dout, out_bytes);
|
||||
ec_request_i2c->command_protocol = EC_COMMAND_PROTOCOL_3;
|
||||
|
||||
/* two extra bytes for v3 */
|
||||
i2c_msg[1].len = in_bytes + 2;
|
||||
i2c_msg[1].buf = dev->din;
|
||||
|
||||
ret = dm_i2c_xfer(udev, &i2c_msg[0], 2);
|
||||
if (ret) {
|
||||
printf("%s: Could not execute transfer: %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* When we send a v3 request to v2 ec, ec won't recognize the 0xda
|
||||
* (EC_COMMAND_PROTOCOL_3) and will return with status
|
||||
* EC_RES_INVALID_COMMAND with zero data length
|
||||
*
|
||||
* In case of invalid command for v3 protocol the data length
|
||||
* will be at least sizeof(struct ec_host_response)
|
||||
*/
|
||||
if (ec_response_i2c->result == EC_RES_INVALID_COMMAND &&
|
||||
ec_response_i2c->packet_length == 0)
|
||||
return -EPROTONOSUPPORT;
|
||||
|
||||
if (ec_response_i2c->packet_length < sizeof(struct ec_host_response)) {
|
||||
printf("%s: response of %u bytes too short; not a full hdr\n",
|
||||
__func__, ec_response_i2c->packet_length);
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
|
||||
/* drop result and packet_len */
|
||||
memmove(dev->din, &ec_response_i2c->ec_response, in_bytes);
|
||||
|
||||
return in_bytes;
|
||||
}
|
||||
|
||||
static int cros_ec_i2c_command(struct udevice *udev, uint8_t cmd,
|
||||
int cmd_version, const uint8_t *dout,
|
||||
int dout_len, uint8_t **dinp, int din_len)
|
||||
{
|
||||
struct cros_ec_dev *dev = dev_get_uclass_priv(udev);
|
||||
struct dm_i2c_chip *chip = dev_get_parent_platdata(udev);
|
||||
struct i2c_msg i2c_msg[2];
|
||||
/* version8, cmd8, arglen8, out8[dout_len], csum8 */
|
||||
int out_bytes = dout_len + 4;
|
||||
/* response8, arglen8, in8[din_len], checksum8 */
|
||||
int in_bytes = din_len + 3;
|
||||
uint8_t *ptr;
|
||||
/* Receive input data, so that args will be dword aligned */
|
||||
uint8_t *in_ptr;
|
||||
int len, csum, ret;
|
||||
|
||||
/*
|
||||
* Sanity-check I/O sizes given transaction overhead in internal
|
||||
* buffers.
|
||||
*/
|
||||
if (out_bytes > sizeof(dev->dout)) {
|
||||
debug("%s: Cannot send %d bytes\n", __func__, dout_len);
|
||||
return -1;
|
||||
}
|
||||
if (in_bytes > sizeof(dev->din)) {
|
||||
debug("%s: Cannot receive %d bytes\n", __func__, din_len);
|
||||
return -1;
|
||||
}
|
||||
assert(dout_len >= 0);
|
||||
assert(dinp);
|
||||
|
||||
i2c_msg[0].addr = chip->chip_addr;
|
||||
i2c_msg[0].len = out_bytes;
|
||||
i2c_msg[0].buf = dev->dout;
|
||||
i2c_msg[0].flags = 0;
|
||||
|
||||
/*
|
||||
* Copy command and data into output buffer so we can do a single I2C
|
||||
* burst transaction.
|
||||
*/
|
||||
ptr = dev->dout;
|
||||
|
||||
/*
|
||||
* in_ptr starts of pointing to a dword-aligned input data buffer.
|
||||
* We decrement it back by the number of header bytes we expect to
|
||||
* receive, so that the first parameter of the resulting input data
|
||||
* will be dword aligned.
|
||||
*/
|
||||
in_ptr = dev->din + sizeof(int64_t);
|
||||
|
||||
if (dev->protocol_version != 2) {
|
||||
/* Something we don't support */
|
||||
debug("%s: Protocol version %d unsupported\n",
|
||||
__func__, dev->protocol_version);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*ptr++ = EC_CMD_VERSION0 + cmd_version;
|
||||
*ptr++ = cmd;
|
||||
*ptr++ = dout_len;
|
||||
in_ptr -= 2; /* Expect status, length bytes */
|
||||
|
||||
memcpy(ptr, dout, dout_len);
|
||||
ptr += dout_len;
|
||||
|
||||
*ptr++ = (uint8_t)
|
||||
cros_ec_calc_checksum(dev->dout, dout_len + 3);
|
||||
|
||||
i2c_msg[1].addr = chip->chip_addr;
|
||||
i2c_msg[1].len = in_bytes;
|
||||
i2c_msg[1].buf = in_ptr;
|
||||
i2c_msg[1].flags = I2C_M_RD;
|
||||
|
||||
/* Send output data */
|
||||
cros_ec_dump_data("out", -1, dev->dout, out_bytes);
|
||||
|
||||
ret = dm_i2c_xfer(udev, &i2c_msg[0], 2);
|
||||
if (ret) {
|
||||
debug("%s: Could not execute transfer to %s\n", __func__,
|
||||
udev->name);
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
if (*in_ptr != EC_RES_SUCCESS) {
|
||||
debug("%s: Received bad result code %d\n", __func__, *in_ptr);
|
||||
return -(int)*in_ptr;
|
||||
}
|
||||
|
||||
len = in_ptr[1];
|
||||
if (len + 3 > sizeof(dev->din)) {
|
||||
debug("%s: Received length %#02x too large\n",
|
||||
__func__, len);
|
||||
return -1;
|
||||
}
|
||||
csum = cros_ec_calc_checksum(in_ptr, 2 + len);
|
||||
if (csum != in_ptr[2 + len]) {
|
||||
debug("%s: Invalid checksum rx %#02x, calced %#02x\n",
|
||||
__func__, in_ptr[2 + din_len], csum);
|
||||
return -1;
|
||||
}
|
||||
din_len = min(din_len, len);
|
||||
cros_ec_dump_data("in", -1, in_ptr, din_len + 3);
|
||||
|
||||
/* Return pointer to dword-aligned input data, if any */
|
||||
*dinp = dev->din + sizeof(int64_t);
|
||||
|
||||
return din_len;
|
||||
}
|
||||
|
||||
static int cros_ec_probe(struct udevice *dev)
|
||||
{
|
||||
return cros_ec_register(dev);
|
||||
}
|
||||
|
||||
static struct dm_cros_ec_ops cros_ec_ops = {
|
||||
.command = cros_ec_i2c_command,
|
||||
.packet = cros_ec_i2c_packet,
|
||||
};
|
||||
|
||||
static const struct udevice_id cros_ec_ids[] = {
|
||||
{ .compatible = "google,cros-ec-i2c" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(cros_ec_i2c) = {
|
||||
.name = "cros_ec_i2c",
|
||||
.id = UCLASS_CROS_EC,
|
||||
.of_match = cros_ec_ids,
|
||||
.probe = cros_ec_probe,
|
||||
.ops = &cros_ec_ops,
|
||||
};
|
||||
@@ -0,0 +1,251 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Chromium OS cros_ec driver - LPC interface
|
||||
*
|
||||
* Copyright (c) 2012 The Chromium OS Authors.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The Matrix Keyboard Protocol driver handles talking to the keyboard
|
||||
* controller chip. Mostly this is for keyboard functions, but some other
|
||||
* things have slipped in, so we provide generic services to talk to the
|
||||
* KBC.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <command.h>
|
||||
#include <cros_ec.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#ifdef DEBUG_TRACE
|
||||
#define debug_trace(fmt, b...) debug(fmt, ##b)
|
||||
#else
|
||||
#define debug_trace(fmt, b...)
|
||||
#endif
|
||||
|
||||
static int wait_for_sync(struct cros_ec_dev *dev)
|
||||
{
|
||||
unsigned long start;
|
||||
|
||||
start = get_timer(0);
|
||||
while (inb(EC_LPC_ADDR_HOST_CMD) & EC_LPC_STATUS_BUSY_MASK) {
|
||||
if (get_timer(start) > 1000) {
|
||||
debug("%s: Timeout waiting for CROS_EC sync\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cros_ec_lpc_packet(struct udevice *udev, int out_bytes, int in_bytes)
|
||||
{
|
||||
struct cros_ec_dev *dev = dev_get_uclass_priv(udev);
|
||||
uint8_t *d;
|
||||
int i;
|
||||
|
||||
if (out_bytes > EC_LPC_HOST_PACKET_SIZE)
|
||||
return log_msg_ret("Cannot send that many bytes\n", -E2BIG);
|
||||
|
||||
if (in_bytes > EC_LPC_HOST_PACKET_SIZE)
|
||||
return log_msg_ret("Cannot receive that many bytes\n", -E2BIG);
|
||||
|
||||
if (wait_for_sync(dev))
|
||||
return log_msg_ret("Timeout waiting ready\n", -ETIMEDOUT);
|
||||
|
||||
/* Write data */
|
||||
for (i = 0, d = (uint8_t *)dev->dout; i < out_bytes; i++, d++)
|
||||
outb(*d, EC_LPC_ADDR_HOST_PACKET + i);
|
||||
|
||||
/* Start the command */
|
||||
outb(EC_COMMAND_PROTOCOL_3, EC_LPC_ADDR_HOST_CMD);
|
||||
|
||||
if (wait_for_sync(dev))
|
||||
return log_msg_ret("Timeout waiting ready\n", -ETIMEDOUT);
|
||||
|
||||
/* Read back args */
|
||||
for (i = 0, d = dev->din; i < in_bytes; i++, d++)
|
||||
*d = inb(EC_LPC_ADDR_HOST_PACKET + i);
|
||||
|
||||
return in_bytes;
|
||||
}
|
||||
|
||||
int cros_ec_lpc_command(struct udevice *udev, uint8_t cmd, int cmd_version,
|
||||
const uint8_t *dout, int dout_len,
|
||||
uint8_t **dinp, int din_len)
|
||||
{
|
||||
struct cros_ec_dev *dev = dev_get_uclass_priv(udev);
|
||||
const int cmd_addr = EC_LPC_ADDR_HOST_CMD;
|
||||
const int data_addr = EC_LPC_ADDR_HOST_DATA;
|
||||
const int args_addr = EC_LPC_ADDR_HOST_ARGS;
|
||||
const int param_addr = EC_LPC_ADDR_HOST_PARAM;
|
||||
|
||||
struct ec_lpc_host_args args;
|
||||
uint8_t *d;
|
||||
int csum;
|
||||
int i;
|
||||
|
||||
if (dout_len > EC_PROTO2_MAX_PARAM_SIZE) {
|
||||
debug("%s: Cannot send %d bytes\n", __func__, dout_len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Fill in args */
|
||||
args.flags = EC_HOST_ARGS_FLAG_FROM_HOST;
|
||||
args.command_version = cmd_version;
|
||||
args.data_size = dout_len;
|
||||
|
||||
/* Calculate checksum */
|
||||
csum = cmd + args.flags + args.command_version + args.data_size;
|
||||
for (i = 0, d = (uint8_t *)dout; i < dout_len; i++, d++)
|
||||
csum += *d;
|
||||
|
||||
args.checksum = (uint8_t)csum;
|
||||
|
||||
if (wait_for_sync(dev)) {
|
||||
debug("%s: Timeout waiting ready\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Write args */
|
||||
for (i = 0, d = (uint8_t *)&args; i < sizeof(args); i++, d++)
|
||||
outb(*d, args_addr + i);
|
||||
|
||||
/* Write data, if any */
|
||||
debug_trace("cmd: %02x, ver: %02x", cmd, cmd_version);
|
||||
for (i = 0, d = (uint8_t *)dout; i < dout_len; i++, d++) {
|
||||
outb(*d, param_addr + i);
|
||||
debug_trace("%02x ", *d);
|
||||
}
|
||||
|
||||
outb(cmd, cmd_addr);
|
||||
debug_trace("\n");
|
||||
|
||||
if (wait_for_sync(dev)) {
|
||||
debug("%s: Timeout waiting for response\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check result */
|
||||
i = inb(data_addr);
|
||||
if (i) {
|
||||
debug("%s: CROS_EC result code %d\n", __func__, i);
|
||||
return -i;
|
||||
}
|
||||
|
||||
/* Read back args */
|
||||
for (i = 0, d = (uint8_t *)&args; i < sizeof(args); i++, d++)
|
||||
*d = inb(args_addr + i);
|
||||
|
||||
/*
|
||||
* If EC didn't modify args flags, then somehow we sent a new-style
|
||||
* command to an old EC, which means it would have read its params
|
||||
* from the wrong place.
|
||||
*/
|
||||
if (!(args.flags & EC_HOST_ARGS_FLAG_TO_HOST)) {
|
||||
debug("%s: CROS_EC protocol mismatch\n", __func__);
|
||||
return -EC_RES_INVALID_RESPONSE;
|
||||
}
|
||||
|
||||
if (args.data_size > din_len) {
|
||||
debug("%s: CROS_EC returned too much data %d > %d\n",
|
||||
__func__, args.data_size, din_len);
|
||||
return -EC_RES_INVALID_RESPONSE;
|
||||
}
|
||||
|
||||
/* Read data, if any */
|
||||
for (i = 0, d = (uint8_t *)dev->din; i < args.data_size; i++, d++) {
|
||||
*d = inb(param_addr + i);
|
||||
debug_trace("%02x ", *d);
|
||||
}
|
||||
debug_trace("\n");
|
||||
|
||||
/* Verify checksum */
|
||||
csum = cmd + args.flags + args.command_version + args.data_size;
|
||||
for (i = 0, d = (uint8_t *)dev->din; i < args.data_size; i++, d++)
|
||||
csum += *d;
|
||||
|
||||
if (args.checksum != (uint8_t)csum) {
|
||||
debug("%s: CROS_EC response has invalid checksum\n", __func__);
|
||||
return -EC_RES_INVALID_CHECKSUM;
|
||||
}
|
||||
*dinp = dev->din;
|
||||
|
||||
/* Return actual amount of data received */
|
||||
return args.data_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize LPC protocol.
|
||||
*
|
||||
* @param dev CROS_EC device
|
||||
* @param blob Device tree blob
|
||||
* @return 0 if ok, -1 on error
|
||||
*/
|
||||
int cros_ec_lpc_init(struct cros_ec_dev *dev, const void *blob)
|
||||
{
|
||||
int byte, i;
|
||||
|
||||
/* See if we can find an EC at the other end */
|
||||
byte = 0xff;
|
||||
byte &= inb(EC_LPC_ADDR_HOST_CMD);
|
||||
byte &= inb(EC_LPC_ADDR_HOST_DATA);
|
||||
for (i = 0; i < EC_PROTO2_MAX_PARAM_SIZE && (byte == 0xff); i++)
|
||||
byte &= inb(EC_LPC_ADDR_HOST_PARAM + i);
|
||||
if (byte == 0xff) {
|
||||
debug("%s: CROS_EC device not found on LPC bus\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Test if LPC command args are supported.
|
||||
*
|
||||
* The cheapest way to do this is by looking for the memory-mapped
|
||||
* flag. This is faster than sending a new-style 'hello' command and
|
||||
* seeing whether the EC sets the EC_HOST_ARGS_FLAG_FROM_HOST flag
|
||||
* in args when it responds.
|
||||
*/
|
||||
static int cros_ec_lpc_check_version(struct udevice *dev)
|
||||
{
|
||||
if (inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID) == 'E' &&
|
||||
inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID + 1)
|
||||
== 'C' &&
|
||||
(inb(EC_LPC_ADDR_MEMMAP +
|
||||
EC_MEMMAP_HOST_CMD_FLAGS) &
|
||||
EC_HOST_CMD_FLAG_LPC_ARGS_SUPPORTED)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
printf("%s: ERROR: old EC interface not supported\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int cros_ec_probe(struct udevice *dev)
|
||||
{
|
||||
return cros_ec_register(dev);
|
||||
}
|
||||
|
||||
static struct dm_cros_ec_ops cros_ec_ops = {
|
||||
.packet = cros_ec_lpc_packet,
|
||||
.command = cros_ec_lpc_command,
|
||||
.check_version = cros_ec_lpc_check_version,
|
||||
};
|
||||
|
||||
static const struct udevice_id cros_ec_ids[] = {
|
||||
{ .compatible = "google,cros-ec-lpc" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(cros_ec_lpc) = {
|
||||
.name = "cros_ec_lpc",
|
||||
.id = UCLASS_CROS_EC,
|
||||
.of_match = cros_ec_ids,
|
||||
.probe = cros_ec_probe,
|
||||
.ops = &cros_ec_ops,
|
||||
};
|
||||
@@ -0,0 +1,581 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Chromium OS cros_ec driver - sandbox emulation
|
||||
*
|
||||
* Copyright (c) 2013 The Chromium OS Authors.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <cros_ec.h>
|
||||
#include <dm.h>
|
||||
#include <ec_commands.h>
|
||||
#include <errno.h>
|
||||
#include <hash.h>
|
||||
#include <malloc.h>
|
||||
#include <os.h>
|
||||
#include <u-boot/sha256.h>
|
||||
#include <spi.h>
|
||||
#include <asm/state.h>
|
||||
#include <asm/sdl.h>
|
||||
#include <linux/input.h>
|
||||
|
||||
/*
|
||||
* Ultimately it shold be possible to connect an Chrome OS EC emulation
|
||||
* to U-Boot and remove all of this code. But this provides a test
|
||||
* environment for bringing up chromeos_sandbox and demonstrating its
|
||||
* utility.
|
||||
*
|
||||
* This emulation includes the following:
|
||||
*
|
||||
* 1. Emulation of the keyboard, by converting keypresses received from SDL
|
||||
* into key scan data, passed back from the EC as key scan messages. The
|
||||
* key layout is read from the device tree.
|
||||
*
|
||||
* 2. Emulation of vboot context - so this can be read/written as required.
|
||||
*
|
||||
* 3. Save/restore of EC state, so that the vboot context, flash memory
|
||||
* contents and current image can be preserved across boots. This is important
|
||||
* since the EC is supposed to continue running even if the AP resets.
|
||||
*
|
||||
* 4. Some event support, in particular allowing Escape to be pressed on boot
|
||||
* to enter recovery mode. The EC passes this to U-Boot through the normal
|
||||
* event message.
|
||||
*
|
||||
* 5. Flash read/write/erase support, so that software sync works. The
|
||||
* protect messages are supported but no protection is implemented.
|
||||
*
|
||||
* 6. Hashing of the EC image, again to support software sync.
|
||||
*
|
||||
* Other features can be added, although a better path is probably to link
|
||||
* the EC image in with U-Boot (Vic has demonstrated a prototype for this).
|
||||
*/
|
||||
|
||||
#define KEYBOARD_ROWS 8
|
||||
#define KEYBOARD_COLS 13
|
||||
|
||||
/* A single entry of the key matrix */
|
||||
struct ec_keymatrix_entry {
|
||||
int row; /* key matrix row */
|
||||
int col; /* key matrix column */
|
||||
int keycode; /* corresponding linux key code */
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ec_state - Information about the EC state
|
||||
*
|
||||
* @vbnv_context: Vboot context data stored by EC
|
||||
* @ec_config: FDT config information about the EC (e.g. flashmap)
|
||||
* @flash_data: Contents of flash memory
|
||||
* @flash_data_len: Size of flash memory
|
||||
* @current_image: Current image the EC is running
|
||||
* @matrix_count: Number of keys to decode in matrix
|
||||
* @matrix: Information about keyboard matrix
|
||||
* @keyscan: Current keyscan information (bit set for each row/column pressed)
|
||||
* @recovery_req: Keyboard recovery requested
|
||||
*/
|
||||
struct ec_state {
|
||||
u8 vbnv_context[EC_VBNV_BLOCK_SIZE_V2];
|
||||
struct fdt_cros_ec ec_config;
|
||||
uint8_t *flash_data;
|
||||
int flash_data_len;
|
||||
enum ec_current_image current_image;
|
||||
int matrix_count;
|
||||
struct ec_keymatrix_entry *matrix; /* the key matrix info */
|
||||
uint8_t keyscan[KEYBOARD_COLS];
|
||||
bool recovery_req;
|
||||
} s_state, *g_state;
|
||||
|
||||
/**
|
||||
* cros_ec_read_state() - read the sandbox EC state from the state file
|
||||
*
|
||||
* If data is available, then blob and node will provide access to it. If
|
||||
* not this function sets up an empty EC.
|
||||
*
|
||||
* @param blob: Pointer to device tree blob, or NULL if no data to read
|
||||
* @param node: Node offset to read from
|
||||
*/
|
||||
static int cros_ec_read_state(const void *blob, int node)
|
||||
{
|
||||
struct ec_state *ec = &s_state;
|
||||
const char *prop;
|
||||
int len;
|
||||
|
||||
/* Set everything to defaults */
|
||||
ec->current_image = EC_IMAGE_RO;
|
||||
if (!blob)
|
||||
return 0;
|
||||
|
||||
/* Read the data if available */
|
||||
ec->current_image = fdtdec_get_int(blob, node, "current-image",
|
||||
EC_IMAGE_RO);
|
||||
prop = fdt_getprop(blob, node, "vbnv-context", &len);
|
||||
if (prop && len == sizeof(ec->vbnv_context))
|
||||
memcpy(ec->vbnv_context, prop, len);
|
||||
|
||||
prop = fdt_getprop(blob, node, "flash-data", &len);
|
||||
if (prop) {
|
||||
ec->flash_data_len = len;
|
||||
ec->flash_data = os_malloc(len);
|
||||
if (!ec->flash_data)
|
||||
return -ENOMEM;
|
||||
memcpy(ec->flash_data, prop, len);
|
||||
debug("%s: Loaded EC flash data size %#x\n", __func__, len);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cros_ec_write_state() - Write out our state to the state file
|
||||
*
|
||||
* The caller will ensure that there is a node ready for the state. The node
|
||||
* may already contain the old state, in which case it is overridden.
|
||||
*
|
||||
* @param blob: Device tree blob holding state
|
||||
* @param node: Node to write our state into
|
||||
*/
|
||||
static int cros_ec_write_state(void *blob, int node)
|
||||
{
|
||||
struct ec_state *ec = g_state;
|
||||
|
||||
/* We are guaranteed enough space to write basic properties */
|
||||
fdt_setprop_u32(blob, node, "current-image", ec->current_image);
|
||||
fdt_setprop(blob, node, "vbnv-context", ec->vbnv_context,
|
||||
sizeof(ec->vbnv_context));
|
||||
return state_setprop(node, "flash-data", ec->flash_data,
|
||||
ec->ec_config.flash.length);
|
||||
}
|
||||
|
||||
SANDBOX_STATE_IO(cros_ec, "google,cros-ec", cros_ec_read_state,
|
||||
cros_ec_write_state);
|
||||
|
||||
/**
|
||||
* Return the number of bytes used in the specified image.
|
||||
*
|
||||
* This is the actual size of code+data in the image, as opposed to the
|
||||
* amount of space reserved in flash for that image. This code is similar to
|
||||
* that used by the real EC code base.
|
||||
*
|
||||
* @param ec Current emulated EC state
|
||||
* @param entry Flash map entry containing the image to check
|
||||
* @return actual image size in bytes, 0 if the image contains no content or
|
||||
* error.
|
||||
*/
|
||||
static int get_image_used(struct ec_state *ec, struct fmap_entry *entry)
|
||||
{
|
||||
int size;
|
||||
|
||||
/*
|
||||
* Scan backwards looking for 0xea byte, which is by definition the
|
||||
* last byte of the image. See ec.lds.S for how this is inserted at
|
||||
* the end of the image.
|
||||
*/
|
||||
for (size = entry->length - 1;
|
||||
size > 0 && ec->flash_data[entry->offset + size] != 0xea;
|
||||
size--)
|
||||
;
|
||||
|
||||
return size ? size + 1 : 0; /* 0xea byte IS part of the image */
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the key matrix from the device tree
|
||||
*
|
||||
* Keymap entries in the fdt take the form of 0xRRCCKKKK where
|
||||
* RR=Row CC=Column KKKK=Key Code
|
||||
*
|
||||
* @param ec Current emulated EC state
|
||||
* @param node Keyboard node of device tree containing keyscan information
|
||||
* @return 0 if ok, -1 on error
|
||||
*/
|
||||
static int keyscan_read_fdt_matrix(struct ec_state *ec, ofnode node)
|
||||
{
|
||||
const u32 *cell;
|
||||
int upto;
|
||||
int len;
|
||||
|
||||
cell = ofnode_get_property(node, "linux,keymap", &len);
|
||||
ec->matrix_count = len / 4;
|
||||
ec->matrix = calloc(ec->matrix_count, sizeof(*ec->matrix));
|
||||
if (!ec->matrix) {
|
||||
debug("%s: Out of memory for key matrix\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Now read the data */
|
||||
for (upto = 0; upto < ec->matrix_count; upto++) {
|
||||
struct ec_keymatrix_entry *matrix = &ec->matrix[upto];
|
||||
u32 word;
|
||||
|
||||
word = fdt32_to_cpu(*cell++);
|
||||
matrix->row = word >> 24;
|
||||
matrix->col = (word >> 16) & 0xff;
|
||||
matrix->keycode = word & 0xffff;
|
||||
|
||||
/* Hard-code some sanity limits for now */
|
||||
if (matrix->row >= KEYBOARD_ROWS ||
|
||||
matrix->col >= KEYBOARD_COLS) {
|
||||
debug("%s: Matrix pos out of range (%d,%d)\n",
|
||||
__func__, matrix->row, matrix->col);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (upto != ec->matrix_count) {
|
||||
debug("%s: Read mismatch from key matrix\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the next keyscan message contents
|
||||
*
|
||||
* @param ec Current emulated EC state
|
||||
* @param scan Place to put keyscan bytes for the keyscan message (must hold
|
||||
* enough space for a full keyscan)
|
||||
* @return number of bytes of valid scan data
|
||||
*/
|
||||
static int cros_ec_keyscan(struct ec_state *ec, uint8_t *scan)
|
||||
{
|
||||
const struct ec_keymatrix_entry *matrix;
|
||||
int bytes = KEYBOARD_COLS;
|
||||
int key[8]; /* allow up to 8 keys to be pressed at once */
|
||||
int count;
|
||||
int i;
|
||||
|
||||
memset(ec->keyscan, '\0', bytes);
|
||||
count = sandbox_sdl_scan_keys(key, ARRAY_SIZE(key));
|
||||
|
||||
/* Look up keycode in matrix */
|
||||
for (i = 0, matrix = ec->matrix; i < ec->matrix_count; i++, matrix++) {
|
||||
bool found;
|
||||
int j;
|
||||
|
||||
for (found = false, j = 0; j < count; j++) {
|
||||
if (matrix->keycode == key[j])
|
||||
found = true;
|
||||
}
|
||||
|
||||
if (found) {
|
||||
debug("%d: %d,%d\n", matrix->keycode, matrix->row,
|
||||
matrix->col);
|
||||
ec->keyscan[matrix->col] |= 1 << matrix->row;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(scan, ec->keyscan, bytes);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process an emulated EC command
|
||||
*
|
||||
* @param ec Current emulated EC state
|
||||
* @param req_hdr Pointer to request header
|
||||
* @param req_data Pointer to body of request
|
||||
* @param resp_hdr Pointer to place to put response header
|
||||
* @param resp_data Pointer to place to put response data, if any
|
||||
* @return length of response data, or 0 for no response data, or -1 on error
|
||||
*/
|
||||
static int process_cmd(struct ec_state *ec,
|
||||
struct ec_host_request *req_hdr, const void *req_data,
|
||||
struct ec_host_response *resp_hdr, void *resp_data)
|
||||
{
|
||||
int len;
|
||||
|
||||
/* TODO(sjg@chromium.org): Check checksums */
|
||||
debug("EC command %#0x\n", req_hdr->command);
|
||||
|
||||
switch (req_hdr->command) {
|
||||
case EC_CMD_HELLO: {
|
||||
const struct ec_params_hello *req = req_data;
|
||||
struct ec_response_hello *resp = resp_data;
|
||||
|
||||
resp->out_data = req->in_data + 0x01020304;
|
||||
len = sizeof(*resp);
|
||||
break;
|
||||
}
|
||||
case EC_CMD_GET_VERSION: {
|
||||
struct ec_response_get_version *resp = resp_data;
|
||||
|
||||
strcpy(resp->version_string_ro, "sandbox_ro");
|
||||
strcpy(resp->version_string_rw, "sandbox_rw");
|
||||
resp->current_image = ec->current_image;
|
||||
debug("Current image %d\n", resp->current_image);
|
||||
len = sizeof(*resp);
|
||||
break;
|
||||
}
|
||||
case EC_CMD_VBNV_CONTEXT: {
|
||||
const struct ec_params_vbnvcontext *req = req_data;
|
||||
struct ec_response_vbnvcontext *resp = resp_data;
|
||||
|
||||
switch (req->op) {
|
||||
case EC_VBNV_CONTEXT_OP_READ:
|
||||
/* TODO(sjg@chromium.org): Support full-size context */
|
||||
memcpy(resp->block, ec->vbnv_context,
|
||||
EC_VBNV_BLOCK_SIZE);
|
||||
len = 16;
|
||||
break;
|
||||
case EC_VBNV_CONTEXT_OP_WRITE:
|
||||
/* TODO(sjg@chromium.org): Support full-size context */
|
||||
memcpy(ec->vbnv_context, req->block,
|
||||
EC_VBNV_BLOCK_SIZE);
|
||||
len = 0;
|
||||
break;
|
||||
default:
|
||||
printf(" ** Unknown vbnv_context command %#02x\n",
|
||||
req->op);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EC_CMD_REBOOT_EC: {
|
||||
const struct ec_params_reboot_ec *req = req_data;
|
||||
|
||||
printf("Request reboot type %d\n", req->cmd);
|
||||
switch (req->cmd) {
|
||||
case EC_REBOOT_DISABLE_JUMP:
|
||||
len = 0;
|
||||
break;
|
||||
case EC_REBOOT_JUMP_RW:
|
||||
ec->current_image = EC_IMAGE_RW;
|
||||
len = 0;
|
||||
break;
|
||||
default:
|
||||
puts(" ** Unknown type");
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EC_CMD_HOST_EVENT_GET_B: {
|
||||
struct ec_response_host_event_mask *resp = resp_data;
|
||||
|
||||
resp->mask = 0;
|
||||
if (ec->recovery_req) {
|
||||
resp->mask |= EC_HOST_EVENT_MASK(
|
||||
EC_HOST_EVENT_KEYBOARD_RECOVERY);
|
||||
}
|
||||
|
||||
len = sizeof(*resp);
|
||||
break;
|
||||
}
|
||||
case EC_CMD_VBOOT_HASH: {
|
||||
const struct ec_params_vboot_hash *req = req_data;
|
||||
struct ec_response_vboot_hash *resp = resp_data;
|
||||
struct fmap_entry *entry;
|
||||
int ret, size;
|
||||
|
||||
entry = &ec->ec_config.region[EC_FLASH_REGION_ACTIVE];
|
||||
|
||||
switch (req->cmd) {
|
||||
case EC_VBOOT_HASH_RECALC:
|
||||
case EC_VBOOT_HASH_GET:
|
||||
size = SHA256_SUM_LEN;
|
||||
len = get_image_used(ec, entry);
|
||||
ret = hash_block("sha256",
|
||||
ec->flash_data + entry->offset,
|
||||
len, resp->hash_digest, &size);
|
||||
if (ret) {
|
||||
printf(" ** hash_block() failed\n");
|
||||
return -1;
|
||||
}
|
||||
resp->status = EC_VBOOT_HASH_STATUS_DONE;
|
||||
resp->hash_type = EC_VBOOT_HASH_TYPE_SHA256;
|
||||
resp->digest_size = size;
|
||||
resp->reserved0 = 0;
|
||||
resp->offset = entry->offset;
|
||||
resp->size = len;
|
||||
len = sizeof(*resp);
|
||||
break;
|
||||
default:
|
||||
printf(" ** EC_CMD_VBOOT_HASH: Unknown command %d\n",
|
||||
req->cmd);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EC_CMD_FLASH_PROTECT: {
|
||||
const struct ec_params_flash_protect *req = req_data;
|
||||
struct ec_response_flash_protect *resp = resp_data;
|
||||
uint32_t expect = EC_FLASH_PROTECT_ALL_NOW |
|
||||
EC_FLASH_PROTECT_ALL_AT_BOOT;
|
||||
|
||||
printf("mask=%#x, flags=%#x\n", req->mask, req->flags);
|
||||
if (req->flags == expect || req->flags == 0) {
|
||||
resp->flags = req->flags ? EC_FLASH_PROTECT_ALL_NOW :
|
||||
0;
|
||||
resp->valid_flags = EC_FLASH_PROTECT_ALL_NOW;
|
||||
resp->writable_flags = 0;
|
||||
len = sizeof(*resp);
|
||||
} else {
|
||||
puts(" ** unexpected flash protect request\n");
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EC_CMD_FLASH_REGION_INFO: {
|
||||
const struct ec_params_flash_region_info *req = req_data;
|
||||
struct ec_response_flash_region_info *resp = resp_data;
|
||||
struct fmap_entry *entry;
|
||||
|
||||
switch (req->region) {
|
||||
case EC_FLASH_REGION_RO:
|
||||
case EC_FLASH_REGION_ACTIVE:
|
||||
case EC_FLASH_REGION_WP_RO:
|
||||
entry = &ec->ec_config.region[req->region];
|
||||
resp->offset = entry->offset;
|
||||
resp->size = entry->length;
|
||||
len = sizeof(*resp);
|
||||
printf("EC flash region %d: offset=%#x, size=%#x\n",
|
||||
req->region, resp->offset, resp->size);
|
||||
break;
|
||||
default:
|
||||
printf("** Unknown flash region %d\n", req->region);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EC_CMD_FLASH_ERASE: {
|
||||
const struct ec_params_flash_erase *req = req_data;
|
||||
|
||||
memset(ec->flash_data + req->offset,
|
||||
ec->ec_config.flash_erase_value,
|
||||
req->size);
|
||||
len = 0;
|
||||
break;
|
||||
}
|
||||
case EC_CMD_FLASH_WRITE: {
|
||||
const struct ec_params_flash_write *req = req_data;
|
||||
|
||||
memcpy(ec->flash_data + req->offset, req + 1, req->size);
|
||||
len = 0;
|
||||
break;
|
||||
}
|
||||
case EC_CMD_MKBP_STATE:
|
||||
len = cros_ec_keyscan(ec, resp_data);
|
||||
break;
|
||||
case EC_CMD_ENTERING_MODE:
|
||||
len = 0;
|
||||
break;
|
||||
default:
|
||||
printf(" ** Unknown EC command %#02x\n", req_hdr->command);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int cros_ec_sandbox_packet(struct udevice *udev, int out_bytes, int in_bytes)
|
||||
{
|
||||
struct cros_ec_dev *dev = dev_get_uclass_priv(udev);
|
||||
struct ec_state *ec = dev_get_priv(dev->dev);
|
||||
struct ec_host_request *req_hdr = (struct ec_host_request *)dev->dout;
|
||||
const void *req_data = req_hdr + 1;
|
||||
struct ec_host_response *resp_hdr = (struct ec_host_response *)dev->din;
|
||||
void *resp_data = resp_hdr + 1;
|
||||
int len;
|
||||
|
||||
len = process_cmd(ec, req_hdr, req_data, resp_hdr, resp_data);
|
||||
if (len < 0)
|
||||
return len;
|
||||
|
||||
resp_hdr->struct_version = 3;
|
||||
resp_hdr->result = EC_RES_SUCCESS;
|
||||
resp_hdr->data_len = len;
|
||||
resp_hdr->reserved = 0;
|
||||
len += sizeof(*resp_hdr);
|
||||
resp_hdr->checksum = 0;
|
||||
resp_hdr->checksum = (uint8_t)
|
||||
-cros_ec_calc_checksum((const uint8_t *)resp_hdr, len);
|
||||
|
||||
return in_bytes;
|
||||
}
|
||||
|
||||
void cros_ec_check_keyboard(struct udevice *dev)
|
||||
{
|
||||
struct ec_state *ec = dev_get_priv(dev);
|
||||
ulong start;
|
||||
|
||||
printf("Press keys for EC to detect on reset (ESC=recovery)...");
|
||||
start = get_timer(0);
|
||||
while (get_timer(start) < 1000)
|
||||
;
|
||||
putc('\n');
|
||||
if (!sandbox_sdl_key_pressed(KEY_ESC)) {
|
||||
ec->recovery_req = true;
|
||||
printf(" - EC requests recovery\n");
|
||||
}
|
||||
}
|
||||
|
||||
int cros_ec_probe(struct udevice *dev)
|
||||
{
|
||||
struct ec_state *ec = dev->priv;
|
||||
struct cros_ec_dev *cdev = dev->uclass_priv;
|
||||
struct udevice *keyb_dev;
|
||||
ofnode node;
|
||||
int err;
|
||||
|
||||
memcpy(ec, &s_state, sizeof(*ec));
|
||||
err = cros_ec_decode_ec_flash(dev, &ec->ec_config);
|
||||
if (err) {
|
||||
debug("%s: Cannot device EC flash\n", __func__);
|
||||
return err;
|
||||
}
|
||||
|
||||
node = ofnode_null();
|
||||
for (device_find_first_child(dev, &keyb_dev);
|
||||
keyb_dev;
|
||||
device_find_next_child(&keyb_dev)) {
|
||||
if (device_get_uclass_id(keyb_dev) == UCLASS_KEYBOARD) {
|
||||
node = dev_ofnode(keyb_dev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ofnode_valid(node)) {
|
||||
debug("%s: No cros_ec keyboard found\n", __func__);
|
||||
} else if (keyscan_read_fdt_matrix(ec, node)) {
|
||||
debug("%s: Could not read key matrix\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* If we loaded EC data, check that the length matches */
|
||||
if (ec->flash_data &&
|
||||
ec->flash_data_len != ec->ec_config.flash.length) {
|
||||
printf("EC data length is %x, expected %x, discarding data\n",
|
||||
ec->flash_data_len, ec->ec_config.flash.length);
|
||||
os_free(ec->flash_data);
|
||||
ec->flash_data = NULL;
|
||||
}
|
||||
|
||||
/* Otherwise allocate the memory */
|
||||
if (!ec->flash_data) {
|
||||
ec->flash_data_len = ec->ec_config.flash.length;
|
||||
ec->flash_data = os_malloc(ec->flash_data_len);
|
||||
if (!ec->flash_data)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
cdev->dev = dev;
|
||||
g_state = ec;
|
||||
return cros_ec_register(dev);
|
||||
}
|
||||
|
||||
struct dm_cros_ec_ops cros_ec_ops = {
|
||||
.packet = cros_ec_sandbox_packet,
|
||||
};
|
||||
|
||||
static const struct udevice_id cros_ec_ids[] = {
|
||||
{ .compatible = "google,cros-ec-sandbox" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(cros_ec_sandbox) = {
|
||||
.name = "cros_ec_sandbox",
|
||||
.id = UCLASS_CROS_EC,
|
||||
.of_match = cros_ec_ids,
|
||||
.probe = cros_ec_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct ec_state),
|
||||
.ops = &cros_ec_ops,
|
||||
};
|
||||
@@ -0,0 +1,192 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Chromium OS cros_ec driver - SPI interface
|
||||
*
|
||||
* Copyright (c) 2012 The Chromium OS Authors.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The Matrix Keyboard Protocol driver handles talking to the keyboard
|
||||
* controller chip. Mostly this is for keyboard functions, but some other
|
||||
* things have slipped in, so we provide generic services to talk to the
|
||||
* KBC.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <cros_ec.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <spi.h>
|
||||
|
||||
int cros_ec_spi_packet(struct udevice *udev, int out_bytes, int in_bytes)
|
||||
{
|
||||
struct cros_ec_dev *dev = dev_get_uclass_priv(udev);
|
||||
struct spi_slave *slave = dev_get_parent_priv(dev->dev);
|
||||
ulong start;
|
||||
uint8_t byte;
|
||||
int rv;
|
||||
|
||||
/* Do the transfer */
|
||||
if (spi_claim_bus(slave)) {
|
||||
debug("%s: Cannot claim SPI bus\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rv = spi_xfer(slave, out_bytes * 8, dev->dout, NULL, SPI_XFER_BEGIN);
|
||||
if (rv)
|
||||
goto done;
|
||||
start = get_timer(0);
|
||||
while (1) {
|
||||
rv = spi_xfer(slave, 8, NULL, &byte, 0);
|
||||
if (byte == SPI_PREAMBLE_END_BYTE)
|
||||
break;
|
||||
if (rv)
|
||||
goto done;
|
||||
if (get_timer(start) > 100) {
|
||||
rv = -ETIMEDOUT;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
rv = spi_xfer(slave, in_bytes * 8, NULL, dev->din, 0);
|
||||
done:
|
||||
spi_xfer(slave, 0, NULL, NULL, SPI_XFER_END);
|
||||
spi_release_bus(slave);
|
||||
|
||||
if (rv) {
|
||||
debug("%s: Cannot complete SPI transfer\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return in_bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a command to a LPC CROS_EC device and return the reply.
|
||||
*
|
||||
* The device's internal input/output buffers are used.
|
||||
*
|
||||
* @param dev CROS_EC device
|
||||
* @param cmd Command to send (EC_CMD_...)
|
||||
* @param cmd_version Version of command to send (EC_VER_...)
|
||||
* @param dout Output data (may be NULL If dout_len=0)
|
||||
* @param dout_len Size of output data in bytes
|
||||
* @param dinp Returns pointer to response data. This will be
|
||||
* untouched unless we return a value > 0.
|
||||
* @param din_len Maximum size of response in bytes
|
||||
* @return number of bytes in response, or -1 on error
|
||||
*/
|
||||
int cros_ec_spi_command(struct udevice *udev, uint8_t cmd, int cmd_version,
|
||||
const uint8_t *dout, int dout_len,
|
||||
uint8_t **dinp, int din_len)
|
||||
{
|
||||
struct cros_ec_dev *dev = dev_get_uclass_priv(udev);
|
||||
struct spi_slave *slave = dev_get_parent_priv(dev->dev);
|
||||
int in_bytes = din_len + 4; /* status, length, checksum, trailer */
|
||||
uint8_t *out;
|
||||
uint8_t *p;
|
||||
int csum, len;
|
||||
int rv;
|
||||
|
||||
if (dev->protocol_version != 2) {
|
||||
debug("%s: Unsupported EC protcol version %d\n",
|
||||
__func__, dev->protocol_version);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sanity-check input size to make sure it plus transaction overhead
|
||||
* fits in the internal device buffer.
|
||||
*/
|
||||
if (in_bytes > sizeof(dev->din)) {
|
||||
debug("%s: Cannot receive %d bytes\n", __func__, din_len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* We represent message length as a byte */
|
||||
if (dout_len > 0xff) {
|
||||
debug("%s: Cannot send %d bytes\n", __func__, dout_len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear input buffer so we don't get false hits for MSG_HEADER
|
||||
*/
|
||||
memset(dev->din, '\0', in_bytes);
|
||||
|
||||
if (spi_claim_bus(slave)) {
|
||||
debug("%s: Cannot claim SPI bus\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
out = dev->dout;
|
||||
out[0] = EC_CMD_VERSION0 + cmd_version;
|
||||
out[1] = cmd;
|
||||
out[2] = (uint8_t)dout_len;
|
||||
memcpy(out + 3, dout, dout_len);
|
||||
csum = cros_ec_calc_checksum(out, 3)
|
||||
+ cros_ec_calc_checksum(dout, dout_len);
|
||||
out[3 + dout_len] = (uint8_t)csum;
|
||||
|
||||
/*
|
||||
* Send output data and receive input data starting such that the
|
||||
* message body will be dword aligned.
|
||||
*/
|
||||
p = dev->din + sizeof(int64_t) - 2;
|
||||
len = dout_len + 4;
|
||||
cros_ec_dump_data("out", cmd, out, len);
|
||||
rv = spi_xfer(slave, max(len, in_bytes) * 8, out, p,
|
||||
SPI_XFER_BEGIN | SPI_XFER_END);
|
||||
|
||||
spi_release_bus(slave);
|
||||
|
||||
if (rv) {
|
||||
debug("%s: Cannot complete SPI transfer\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
len = min((int)p[1], din_len);
|
||||
cros_ec_dump_data("in", -1, p, len + 3);
|
||||
|
||||
/* Response code is first byte of message */
|
||||
if (p[0] != EC_RES_SUCCESS) {
|
||||
printf("%s: Returned status %d\n", __func__, p[0]);
|
||||
return -(int)(p[0]);
|
||||
}
|
||||
|
||||
/* Check checksum */
|
||||
csum = cros_ec_calc_checksum(p, len + 2);
|
||||
if (csum != p[len + 2]) {
|
||||
debug("%s: Invalid checksum rx %#02x, calced %#02x\n", __func__,
|
||||
p[2 + len], csum);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Anything else is the response data */
|
||||
*dinp = p + 2;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int cros_ec_probe(struct udevice *dev)
|
||||
{
|
||||
return cros_ec_register(dev);
|
||||
}
|
||||
|
||||
static struct dm_cros_ec_ops cros_ec_ops = {
|
||||
.packet = cros_ec_spi_packet,
|
||||
.command = cros_ec_spi_command,
|
||||
};
|
||||
|
||||
static const struct udevice_id cros_ec_ids[] = {
|
||||
{ .compatible = "google,cros-ec-spi" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(cros_ec_spi) = {
|
||||
.name = "cros_ec_spi",
|
||||
.id = UCLASS_CROS_EC,
|
||||
.of_match = cros_ec_ids,
|
||||
.probe = cros_ec_probe,
|
||||
.ops = &cros_ec_ops,
|
||||
};
|
||||
@@ -0,0 +1,379 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright 2008 Extreme Engineering Solutions, Inc.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Driver for DS4510, a CPU supervisor with integrated EEPROM, SRAM,
|
||||
* and 4 programmable non-volatile GPIO pins.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <i2c.h>
|
||||
#include <command.h>
|
||||
#include "ds4510.h"
|
||||
|
||||
enum {
|
||||
DS4510_CMD_INFO,
|
||||
DS4510_CMD_DEVICE,
|
||||
DS4510_CMD_NV,
|
||||
DS4510_CMD_RSTDELAY,
|
||||
DS4510_CMD_OUTPUT,
|
||||
DS4510_CMD_INPUT,
|
||||
DS4510_CMD_PULLUP,
|
||||
DS4510_CMD_EEPROM,
|
||||
DS4510_CMD_SEEPROM,
|
||||
DS4510_CMD_SRAM,
|
||||
};
|
||||
|
||||
/*
|
||||
* Write to DS4510, taking page boundaries into account
|
||||
*/
|
||||
static int ds4510_mem_write(uint8_t chip, int offset, uint8_t *buf, int count)
|
||||
{
|
||||
int wrlen;
|
||||
int i = 0;
|
||||
|
||||
do {
|
||||
wrlen = DS4510_EEPROM_PAGE_SIZE -
|
||||
DS4510_EEPROM_PAGE_OFFSET(offset);
|
||||
if (count < wrlen)
|
||||
wrlen = count;
|
||||
if (i2c_write(chip, offset, 1, &buf[i], wrlen))
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* This delay isn't needed for SRAM writes but shouldn't delay
|
||||
* things too much, so do it unconditionally for simplicity
|
||||
*/
|
||||
udelay(DS4510_EEPROM_PAGE_WRITE_DELAY_MS * 1000);
|
||||
count -= wrlen;
|
||||
offset += wrlen;
|
||||
i += wrlen;
|
||||
} while (count > 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* General read from DS4510
|
||||
*/
|
||||
static int ds4510_mem_read(uint8_t chip, int offset, uint8_t *buf, int count)
|
||||
{
|
||||
return i2c_read(chip, offset, 1, buf, count);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write SEE bit in config register.
|
||||
* nv = 0 - Writes to SEEPROM registers behave like EEPROM
|
||||
* nv = 1 - Writes to SEEPROM registers behave like SRAM
|
||||
*/
|
||||
static int ds4510_see_write(uint8_t chip, uint8_t nv)
|
||||
{
|
||||
uint8_t data;
|
||||
|
||||
if (i2c_read(chip, DS4510_CFG, 1, &data, 1))
|
||||
return -1;
|
||||
|
||||
if (nv) /* Treat SEEPROM bits as EEPROM */
|
||||
data &= ~DS4510_CFG_SEE;
|
||||
else /* Treat SEEPROM bits as SRAM */
|
||||
data |= DS4510_CFG_SEE;
|
||||
|
||||
return ds4510_mem_write(chip, DS4510_CFG, &data, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write de-assertion of reset signal delay
|
||||
*/
|
||||
static int ds4510_rstdelay_write(uint8_t chip, uint8_t delay)
|
||||
{
|
||||
uint8_t data;
|
||||
|
||||
if (i2c_read(chip, DS4510_RSTDELAY, 1, &data, 1))
|
||||
return -1;
|
||||
|
||||
data &= ~DS4510_RSTDELAY_MASK;
|
||||
data |= delay & DS4510_RSTDELAY_MASK;
|
||||
|
||||
return ds4510_mem_write(chip, DS4510_RSTDELAY, &data, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write pullup characteristics of IO pins
|
||||
*/
|
||||
static int ds4510_pullup_write(uint8_t chip, uint8_t val)
|
||||
{
|
||||
val &= DS4510_IO_MASK;
|
||||
|
||||
return ds4510_mem_write(chip, DS4510_PULLUP, (uint8_t *)&val, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read pullup characteristics of IO pins
|
||||
*/
|
||||
static int ds4510_pullup_read(uint8_t chip)
|
||||
{
|
||||
uint8_t val;
|
||||
|
||||
if (i2c_read(chip, DS4510_PULLUP, 1, &val, 1))
|
||||
return -1;
|
||||
|
||||
return val & DS4510_IO_MASK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write drive level of IO pins
|
||||
*/
|
||||
static int ds4510_gpio_write(uint8_t chip, uint8_t val)
|
||||
{
|
||||
uint8_t data;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < DS4510_NUM_IO; i++) {
|
||||
if (i2c_read(chip, DS4510_IO0 - i, 1, &data, 1))
|
||||
return -1;
|
||||
|
||||
if (val & (0x1 << i))
|
||||
data |= 0x1;
|
||||
else
|
||||
data &= ~0x1;
|
||||
|
||||
if (ds4510_mem_write(chip, DS4510_IO0 - i, &data, 1))
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read drive level of IO pins
|
||||
*/
|
||||
static int ds4510_gpio_read(uint8_t chip)
|
||||
{
|
||||
uint8_t data;
|
||||
int val = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < DS4510_NUM_IO; i++) {
|
||||
if (i2c_read(chip, DS4510_IO0 - i, 1, &data, 1))
|
||||
return -1;
|
||||
|
||||
if (data & 1)
|
||||
val |= (1 << i);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read physical level of IO pins
|
||||
*/
|
||||
static int ds4510_gpio_read_val(uint8_t chip)
|
||||
{
|
||||
uint8_t val;
|
||||
|
||||
if (i2c_read(chip, DS4510_IO_STATUS, 1, &val, 1))
|
||||
return -1;
|
||||
|
||||
return val & DS4510_IO_MASK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Display DS4510 information
|
||||
*/
|
||||
static int ds4510_info(uint8_t chip)
|
||||
{
|
||||
int i;
|
||||
int tmp;
|
||||
uint8_t data;
|
||||
|
||||
printf("DS4510 @ 0x%x:\n\n", chip);
|
||||
|
||||
if (i2c_read(chip, DS4510_RSTDELAY, 1, &data, 1))
|
||||
return -1;
|
||||
printf("rstdelay = 0x%x\n\n", data & DS4510_RSTDELAY_MASK);
|
||||
|
||||
if (i2c_read(chip, DS4510_CFG, 1, &data, 1))
|
||||
return -1;
|
||||
printf("config = 0x%x\n", data);
|
||||
printf(" /ready = %d\n", data & DS4510_CFG_READY ? 1 : 0);
|
||||
printf(" trip pt = %d\n", data & DS4510_CFG_TRIP_POINT ? 1 : 0);
|
||||
printf(" rst sts = %d\n", data & DS4510_CFG_RESET ? 1 : 0);
|
||||
printf(" /see = %d\n", data & DS4510_CFG_SEE ? 1 : 0);
|
||||
printf(" swrst = %d\n\n", data & DS4510_CFG_SWRST ? 1 : 0);
|
||||
|
||||
printf("gpio pins: 3210\n");
|
||||
printf("---------------\n");
|
||||
printf("pullup ");
|
||||
|
||||
tmp = ds4510_pullup_read(chip);
|
||||
if (tmp == -1)
|
||||
return tmp;
|
||||
for (i = DS4510_NUM_IO - 1; i >= 0; i--)
|
||||
printf("%d", (tmp & (1 << i)) ? 1 : 0);
|
||||
printf("\n");
|
||||
|
||||
printf("driven ");
|
||||
tmp = ds4510_gpio_read(chip);
|
||||
if (tmp == -1)
|
||||
return -1;
|
||||
for (i = DS4510_NUM_IO - 1; i >= 0; i--)
|
||||
printf("%d", (tmp & (1 << i)) ? 1 : 0);
|
||||
printf("\n");
|
||||
|
||||
printf("read ");
|
||||
tmp = ds4510_gpio_read_val(chip);
|
||||
if (tmp == -1)
|
||||
return -1;
|
||||
for (i = DS4510_NUM_IO - 1; i >= 0; i--)
|
||||
printf("%d", (tmp & (1 << i)) ? 1 : 0);
|
||||
printf("\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
cmd_tbl_t cmd_ds4510[] = {
|
||||
U_BOOT_CMD_MKENT(device, 3, 0, (void *)DS4510_CMD_DEVICE, "", ""),
|
||||
U_BOOT_CMD_MKENT(nv, 3, 0, (void *)DS4510_CMD_NV, "", ""),
|
||||
U_BOOT_CMD_MKENT(output, 4, 0, (void *)DS4510_CMD_OUTPUT, "", ""),
|
||||
U_BOOT_CMD_MKENT(input, 3, 0, (void *)DS4510_CMD_INPUT, "", ""),
|
||||
U_BOOT_CMD_MKENT(pullup, 4, 0, (void *)DS4510_CMD_PULLUP, "", ""),
|
||||
U_BOOT_CMD_MKENT(info, 2, 0, (void *)DS4510_CMD_INFO, "", ""),
|
||||
U_BOOT_CMD_MKENT(rstdelay, 3, 0, (void *)DS4510_CMD_RSTDELAY, "", ""),
|
||||
U_BOOT_CMD_MKENT(eeprom, 6, 0, (void *)DS4510_CMD_EEPROM, "", ""),
|
||||
U_BOOT_CMD_MKENT(seeprom, 6, 0, (void *)DS4510_CMD_SEEPROM, "", ""),
|
||||
U_BOOT_CMD_MKENT(sram, 6, 0, (void *)DS4510_CMD_SRAM, "", ""),
|
||||
};
|
||||
|
||||
int do_ds4510(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
|
||||
{
|
||||
static uint8_t chip = 0x51;
|
||||
cmd_tbl_t *c;
|
||||
ulong ul_arg2 = 0;
|
||||
ulong ul_arg3 = 0;
|
||||
int tmp;
|
||||
ulong addr;
|
||||
ulong off;
|
||||
ulong cnt;
|
||||
int end;
|
||||
int (*rw_func)(uint8_t, int, uint8_t *, int);
|
||||
|
||||
c = find_cmd_tbl(argv[1], cmd_ds4510, ARRAY_SIZE(cmd_ds4510));
|
||||
|
||||
/* All commands but "device" require 'maxargs' arguments */
|
||||
if (!c || !((argc == (c->maxargs)) ||
|
||||
(((int)c->cmd == DS4510_CMD_DEVICE) &&
|
||||
(argc == (c->maxargs - 1))))) {
|
||||
return cmd_usage(cmdtp);
|
||||
}
|
||||
|
||||
/* arg2 used as chip addr and pin number */
|
||||
if (argc > 2)
|
||||
ul_arg2 = simple_strtoul(argv[2], NULL, 16);
|
||||
|
||||
/* arg3 used as output/pullup value */
|
||||
if (argc > 3)
|
||||
ul_arg3 = simple_strtoul(argv[3], NULL, 16);
|
||||
|
||||
switch ((int)c->cmd) {
|
||||
case DS4510_CMD_DEVICE:
|
||||
if (argc == 3)
|
||||
chip = ul_arg2;
|
||||
printf("Current device address: 0x%x\n", chip);
|
||||
return 0;
|
||||
case DS4510_CMD_NV:
|
||||
return ds4510_see_write(chip, ul_arg2);
|
||||
case DS4510_CMD_OUTPUT:
|
||||
tmp = ds4510_gpio_read(chip);
|
||||
if (tmp == -1)
|
||||
return -1;
|
||||
if (ul_arg3)
|
||||
tmp |= (1 << ul_arg2);
|
||||
else
|
||||
tmp &= ~(1 << ul_arg2);
|
||||
return ds4510_gpio_write(chip, tmp);
|
||||
case DS4510_CMD_INPUT:
|
||||
tmp = ds4510_gpio_read_val(chip);
|
||||
if (tmp == -1)
|
||||
return -1;
|
||||
return (tmp & (1 << ul_arg2)) != 0;
|
||||
case DS4510_CMD_PULLUP:
|
||||
tmp = ds4510_pullup_read(chip);
|
||||
if (tmp == -1)
|
||||
return -1;
|
||||
if (ul_arg3)
|
||||
tmp |= (1 << ul_arg2);
|
||||
else
|
||||
tmp &= ~(1 << ul_arg2);
|
||||
return ds4510_pullup_write(chip, tmp);
|
||||
case DS4510_CMD_INFO:
|
||||
return ds4510_info(chip);
|
||||
case DS4510_CMD_RSTDELAY:
|
||||
return ds4510_rstdelay_write(chip, ul_arg2);
|
||||
case DS4510_CMD_EEPROM:
|
||||
end = DS4510_EEPROM + DS4510_EEPROM_SIZE;
|
||||
off = DS4510_EEPROM;
|
||||
break;
|
||||
case DS4510_CMD_SEEPROM:
|
||||
end = DS4510_SEEPROM + DS4510_SEEPROM_SIZE;
|
||||
off = DS4510_SEEPROM;
|
||||
break;
|
||||
case DS4510_CMD_SRAM:
|
||||
end = DS4510_SRAM + DS4510_SRAM_SIZE;
|
||||
off = DS4510_SRAM;
|
||||
break;
|
||||
default:
|
||||
/* We should never get here... */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Only eeprom, seeprom, and sram commands should make it here */
|
||||
if (strcmp(argv[2], "read") == 0)
|
||||
rw_func = ds4510_mem_read;
|
||||
else if (strcmp(argv[2], "write") == 0)
|
||||
rw_func = ds4510_mem_write;
|
||||
else
|
||||
return cmd_usage(cmdtp);
|
||||
|
||||
addr = simple_strtoul(argv[3], NULL, 16);
|
||||
off += simple_strtoul(argv[4], NULL, 16);
|
||||
cnt = simple_strtoul(argv[5], NULL, 16);
|
||||
|
||||
if ((off + cnt) > end) {
|
||||
printf("ERROR: invalid len\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return rw_func(chip, off, (uint8_t *)addr, cnt);
|
||||
}
|
||||
|
||||
U_BOOT_CMD(
|
||||
ds4510, 6, 1, do_ds4510,
|
||||
"ds4510 eeprom/seeprom/sram/gpio access",
|
||||
"device [dev]\n"
|
||||
" - show or set current device address\n"
|
||||
"ds4510 info\n"
|
||||
" - display ds4510 info\n"
|
||||
"ds4510 output pin 0|1\n"
|
||||
" - set pin low or high-Z\n"
|
||||
"ds4510 input pin\n"
|
||||
" - read value of pin\n"
|
||||
"ds4510 pullup pin 0|1\n"
|
||||
" - disable/enable pullup on specified pin\n"
|
||||
"ds4510 nv 0|1\n"
|
||||
" - make gpio and seeprom writes volatile/non-volatile"
|
||||
"\n"
|
||||
"ds4510 rstdelay 0-3\n"
|
||||
" - set reset output delay"
|
||||
"\n"
|
||||
"ds4510 eeprom read addr off cnt\n"
|
||||
"ds4510 eeprom write addr off cnt\n"
|
||||
" - read/write 'cnt' bytes at EEPROM offset 'off'\n"
|
||||
"ds4510 seeprom read addr off cnt\n"
|
||||
"ds4510 seeprom write addr off cnt\n"
|
||||
" - read/write 'cnt' bytes at SRAM-shadowed EEPROM offset 'off'\n"
|
||||
"ds4510 sram read addr off cnt\n"
|
||||
"ds4510 sram write addr off cnt\n"
|
||||
" - read/write 'cnt' bytes at SRAM offset 'off'"
|
||||
);
|
||||
@@ -0,0 +1,52 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright 2008 Extreme Engineering Solutions, Inc.
|
||||
*/
|
||||
|
||||
#ifndef __DS4510_H_
|
||||
#define __DS4510_H_
|
||||
|
||||
/* General defines */
|
||||
#define DS4510_NUM_IO 0x04
|
||||
#define DS4510_IO_MASK ((1 << DS4510_NUM_IO) - 1)
|
||||
#define DS4510_EEPROM_PAGE_WRITE_DELAY_MS 20
|
||||
|
||||
/* EEPROM from 0x00 - 0x39 */
|
||||
#define DS4510_EEPROM 0x00
|
||||
#define DS4510_EEPROM_SIZE 0x40
|
||||
#define DS4510_EEPROM_PAGE_SIZE 0x08
|
||||
#define DS4510_EEPROM_PAGE_OFFSET(x) ((x) & (DS4510_EEPROM_PAGE_SIZE - 1))
|
||||
|
||||
/* SEEPROM from 0xf0 - 0xf7 */
|
||||
#define DS4510_SEEPROM 0xf0
|
||||
#define DS4510_SEEPROM_SIZE 0x08
|
||||
|
||||
/* Registers overlapping SEEPROM from 0xf0 - 0xf7 */
|
||||
#define DS4510_PULLUP 0xF0
|
||||
#define DS4510_PULLUP_DIS 0x00
|
||||
#define DS4510_PULLUP_EN 0x01
|
||||
#define DS4510_RSTDELAY 0xF1
|
||||
#define DS4510_RSTDELAY_MASK 0x03
|
||||
#define DS4510_RSTDELAY_125 0x00
|
||||
#define DS4510_RSTDELAY_250 0x01
|
||||
#define DS4510_RSTDELAY_500 0x02
|
||||
#define DS4510_RSTDELAY_1000 0x03
|
||||
#define DS4510_IO3 0xF4
|
||||
#define DS4510_IO2 0xF5
|
||||
#define DS4510_IO1 0xF6
|
||||
#define DS4510_IO0 0xF7
|
||||
|
||||
/* Status configuration registers from 0xf8 - 0xf9*/
|
||||
#define DS4510_IO_STATUS 0xF8
|
||||
#define DS4510_CFG 0xF9
|
||||
#define DS4510_CFG_READY 0x80
|
||||
#define DS4510_CFG_TRIP_POINT 0x40
|
||||
#define DS4510_CFG_RESET 0x20
|
||||
#define DS4510_CFG_SEE 0x10
|
||||
#define DS4510_CFG_SWRST 0x08
|
||||
|
||||
/* SRAM from 0xfa - 0xff */
|
||||
#define DS4510_SRAM 0xfa
|
||||
#define DS4510_SRAM_SIZE 0x06
|
||||
|
||||
#endif /* __DS4510_H_ */
|
||||
@@ -0,0 +1,294 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2018-2019 Intel Corporation <www.intel.com>
|
||||
*
|
||||
*/
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <env.h>
|
||||
#include <errno.h>
|
||||
#include <blk.h>
|
||||
#include <fs.h>
|
||||
#include <fs_loader.h>
|
||||
#include <linux/string.h>
|
||||
#include <mapmem.h>
|
||||
#include <malloc.h>
|
||||
#include <spl.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
/**
|
||||
* struct firmware - A place for storing firmware and its attribute data.
|
||||
*
|
||||
* This holds information about a firmware and its content.
|
||||
*
|
||||
* @size: Size of a file
|
||||
* @data: Buffer for file
|
||||
* @priv: Firmware loader private fields
|
||||
* @name: Filename
|
||||
* @offset: Offset of reading a file
|
||||
*/
|
||||
struct firmware {
|
||||
size_t size;
|
||||
const u8 *data;
|
||||
const char *name;
|
||||
u32 offset;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_CMD_UBIFS
|
||||
static int mount_ubifs(char *mtdpart, char *ubivol)
|
||||
{
|
||||
int ret = ubi_part(mtdpart, NULL);
|
||||
|
||||
if (ret) {
|
||||
debug("Cannot find mtd partition %s\n", mtdpart);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return cmd_ubifs_mount(ubivol);
|
||||
}
|
||||
|
||||
static int umount_ubifs(void)
|
||||
{
|
||||
return cmd_ubifs_umount();
|
||||
}
|
||||
#else
|
||||
static int mount_ubifs(char *mtdpart, char *ubivol)
|
||||
{
|
||||
debug("Error: Cannot load image: no UBIFS support\n");
|
||||
return -ENOSYS;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int select_fs_dev(struct device_platdata *plat)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (plat->phandlepart.phandle) {
|
||||
ofnode node;
|
||||
|
||||
node = ofnode_get_by_phandle(plat->phandlepart.phandle);
|
||||
|
||||
struct udevice *dev;
|
||||
|
||||
ret = device_get_global_by_ofnode(node, &dev);
|
||||
if (!ret) {
|
||||
struct blk_desc *desc = blk_get_by_device(dev);
|
||||
if (desc) {
|
||||
ret = fs_set_blk_dev_with_part(desc,
|
||||
plat->phandlepart.partition);
|
||||
} else {
|
||||
debug("%s: No device found\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
} else if (plat->mtdpart && plat->ubivol) {
|
||||
ret = mount_ubifs(plat->mtdpart, plat->ubivol);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = fs_set_blk_dev("ubi", NULL, FS_TYPE_UBIFS);
|
||||
} else {
|
||||
debug("Error: unsupported storage device.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
debug("Error: could not access storage.\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* _request_firmware_prepare - Prepare firmware struct.
|
||||
*
|
||||
* @dev: An instance of a driver.
|
||||
* @name: Name of firmware file.
|
||||
* @dbuf: Address of buffer to load firmware into.
|
||||
* @size: Size of buffer.
|
||||
* @offset: Offset of a file for start reading into buffer.
|
||||
*
|
||||
* Return: Negative value if fail, 0 for successful.
|
||||
*/
|
||||
static int _request_firmware_prepare(struct udevice *dev,
|
||||
const char *name, void *dbuf,
|
||||
size_t size, u32 offset)
|
||||
{
|
||||
if (!name || name[0] == '\0')
|
||||
return -EINVAL;
|
||||
|
||||
struct firmware *firmwarep = dev_get_priv(dev);
|
||||
|
||||
if (!firmwarep)
|
||||
return -ENOMEM;
|
||||
|
||||
firmwarep->name = name;
|
||||
firmwarep->offset = offset;
|
||||
firmwarep->data = dbuf;
|
||||
firmwarep->size = size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* fw_get_filesystem_firmware - load firmware into an allocated buffer.
|
||||
* @dev: An instance of a driver.
|
||||
*
|
||||
* Return: Size of total read, negative value when error.
|
||||
*/
|
||||
static int fw_get_filesystem_firmware(struct udevice *dev)
|
||||
{
|
||||
loff_t actread;
|
||||
char *storage_interface, *dev_part, *ubi_mtdpart, *ubi_volume;
|
||||
int ret;
|
||||
|
||||
storage_interface = env_get("storage_interface");
|
||||
dev_part = env_get("fw_dev_part");
|
||||
ubi_mtdpart = env_get("fw_ubi_mtdpart");
|
||||
ubi_volume = env_get("fw_ubi_volume");
|
||||
|
||||
if (storage_interface && dev_part) {
|
||||
ret = fs_set_blk_dev(storage_interface, dev_part, FS_TYPE_ANY);
|
||||
} else if (storage_interface && ubi_mtdpart && ubi_volume) {
|
||||
ret = mount_ubifs(ubi_mtdpart, ubi_volume);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!strcmp("ubi", storage_interface))
|
||||
ret = fs_set_blk_dev(storage_interface, NULL,
|
||||
FS_TYPE_UBIFS);
|
||||
else
|
||||
ret = -ENODEV;
|
||||
} else {
|
||||
ret = select_fs_dev(dev->platdata);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
struct firmware *firmwarep = dev_get_priv(dev);
|
||||
|
||||
if (!firmwarep)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = fs_read(firmwarep->name, (ulong)map_to_sysmem(firmwarep->data),
|
||||
firmwarep->offset, firmwarep->size, &actread);
|
||||
|
||||
if (ret) {
|
||||
debug("Error: %d Failed to read %s from flash %lld != %zu.\n",
|
||||
ret, firmwarep->name, actread, firmwarep->size);
|
||||
} else {
|
||||
ret = actread;
|
||||
}
|
||||
|
||||
out:
|
||||
#ifdef CONFIG_CMD_UBIFS
|
||||
umount_ubifs();
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* request_firmware_into_buf - Load firmware into a previously allocated buffer.
|
||||
* @dev: An instance of a driver.
|
||||
* @name: Name of firmware file.
|
||||
* @buf: Address of buffer to load firmware into.
|
||||
* @size: Size of buffer.
|
||||
* @offset: Offset of a file for start reading into buffer.
|
||||
*
|
||||
* The firmware is loaded directly into the buffer pointed to by @buf.
|
||||
*
|
||||
* Return: Size of total read, negative value when error.
|
||||
*/
|
||||
int request_firmware_into_buf(struct udevice *dev,
|
||||
const char *name,
|
||||
void *buf, size_t size, u32 offset)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!dev)
|
||||
return -EINVAL;
|
||||
|
||||
ret = _request_firmware_prepare(dev, name, buf, size, offset);
|
||||
if (ret < 0) /* error */
|
||||
return ret;
|
||||
|
||||
ret = fw_get_filesystem_firmware(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fs_loader_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
u32 phandlepart[2];
|
||||
|
||||
ofnode fs_loader_node = dev_ofnode(dev);
|
||||
|
||||
if (ofnode_valid(fs_loader_node)) {
|
||||
struct device_platdata *plat;
|
||||
|
||||
plat = dev->platdata;
|
||||
if (!ofnode_read_u32_array(fs_loader_node,
|
||||
"phandlepart",
|
||||
phandlepart, 2)) {
|
||||
plat->phandlepart.phandle = phandlepart[0];
|
||||
plat->phandlepart.partition = phandlepart[1];
|
||||
}
|
||||
|
||||
plat->mtdpart = (char *)ofnode_read_string(
|
||||
fs_loader_node, "mtdpart");
|
||||
|
||||
plat->ubivol = (char *)ofnode_read_string(
|
||||
fs_loader_node, "ubivol");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fs_loader_probe(struct udevice *dev)
|
||||
{
|
||||
#if CONFIG_IS_ENABLED(DM) && CONFIG_IS_ENABLED(BLK)
|
||||
int ret;
|
||||
struct device_platdata *plat = dev->platdata;
|
||||
|
||||
if (plat->phandlepart.phandle) {
|
||||
ofnode node = ofnode_get_by_phandle(plat->phandlepart.phandle);
|
||||
struct udevice *parent_dev = NULL;
|
||||
|
||||
ret = device_get_global_by_ofnode(node, &parent_dev);
|
||||
if (!ret) {
|
||||
struct udevice *dev;
|
||||
|
||||
ret = blk_get_from_parent(parent_dev, &dev);
|
||||
if (ret) {
|
||||
debug("fs_loader: No block device: %d\n",
|
||||
ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
static const struct udevice_id fs_loader_ids[] = {
|
||||
{ .compatible = "u-boot,fs-loader"},
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(fs_loader) = {
|
||||
.name = "fs-loader",
|
||||
.id = UCLASS_FS_FIRMWARE_LOADER,
|
||||
.of_match = fs_loader_ids,
|
||||
.probe = fs_loader_probe,
|
||||
.ofdata_to_platdata = fs_loader_ofdata_to_platdata,
|
||||
.platdata_auto_alloc_size = sizeof(struct device_platdata),
|
||||
.priv_auto_alloc_size = sizeof(struct firmware),
|
||||
};
|
||||
|
||||
UCLASS_DRIVER(fs_loader) = {
|
||||
.id = UCLASS_FS_FIRMWARE_LOADER,
|
||||
.name = "fs-loader",
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2015 Freescale Semiconductor, Inc.
|
||||
* Author: Zhuoyu Zhang <Zhuoyu.Zhang@freescale.com>
|
||||
*/
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch-ls102xa/immap_ls102xa.h>
|
||||
#include <asm/arch-ls102xa/config.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <hwconfig.h>
|
||||
#include <fsl_devdis.h>
|
||||
|
||||
void device_disable(const struct devdis_table *tbl, uint32_t num)
|
||||
{
|
||||
int i;
|
||||
struct ccsr_gur __iomem *gur = (void *)CONFIG_SYS_FSL_GUTS_ADDR;
|
||||
|
||||
/*
|
||||
* Extract hwconfig from environment and disable unused device.
|
||||
*/
|
||||
for (i = 0; i < num; i++) {
|
||||
if (hwconfig_sub("devdis", tbl[i].name))
|
||||
setbits_be32(&gur->devdisr + tbl[i].offset,
|
||||
tbl[i].mask);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,580 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2010-2011 Freescale Semiconductor, Inc.
|
||||
* Author: Dipen Dudhat <dipen.dudhat@freescale.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <fsl_ifc.h>
|
||||
|
||||
#ifdef CONFIG_TFABOOT
|
||||
struct ifc_regs ifc_cfg_default_boot[CONFIG_SYS_FSL_IFC_BANK_COUNT] = {
|
||||
{
|
||||
"cs0",
|
||||
#if defined(CONFIG_SYS_CSPR0) && defined(CONFIG_SYS_CSOR0)
|
||||
CONFIG_SYS_CSPR0,
|
||||
#ifdef CONFIG_SYS_CSPR0_EXT
|
||||
CONFIG_SYS_CSPR0_EXT,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_AMASK0
|
||||
CONFIG_SYS_AMASK0,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
CONFIG_SYS_CSOR0,
|
||||
{
|
||||
CONFIG_SYS_CS0_FTIM0,
|
||||
CONFIG_SYS_CS0_FTIM1,
|
||||
CONFIG_SYS_CS0_FTIM2,
|
||||
CONFIG_SYS_CS0_FTIM3,
|
||||
},
|
||||
#ifdef CONFIG_SYS_CSOR0_EXT
|
||||
CONFIG_SYS_CSOR0_EXT,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_CSPR0_FINAL
|
||||
CONFIG_SYS_CSPR0_FINAL,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_AMASK0_FINAL
|
||||
CONFIG_SYS_AMASK0_FINAL,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
#endif
|
||||
},
|
||||
|
||||
#if CONFIG_SYS_FSL_IFC_BANK_COUNT >= 2
|
||||
{
|
||||
"cs1",
|
||||
#if defined(CONFIG_SYS_CSPR1) && defined(CONFIG_SYS_CSOR1)
|
||||
CONFIG_SYS_CSPR1,
|
||||
#ifdef CONFIG_SYS_CSPR1_EXT
|
||||
CONFIG_SYS_CSPR1_EXT,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_AMASK1
|
||||
CONFIG_SYS_AMASK1,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
CONFIG_SYS_CSOR1,
|
||||
{
|
||||
CONFIG_SYS_CS1_FTIM0,
|
||||
CONFIG_SYS_CS1_FTIM1,
|
||||
CONFIG_SYS_CS1_FTIM2,
|
||||
CONFIG_SYS_CS1_FTIM3,
|
||||
},
|
||||
#ifdef CONFIG_SYS_CSOR1_EXT
|
||||
CONFIG_SYS_CSOR1_EXT,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_CSPR1_FINAL
|
||||
CONFIG_SYS_CSPR1_FINAL,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_AMASK1_FINAL
|
||||
CONFIG_SYS_AMASK1_FINAL,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
#endif
|
||||
},
|
||||
#endif
|
||||
|
||||
#if CONFIG_SYS_FSL_IFC_BANK_COUNT >= 3
|
||||
{
|
||||
"cs2",
|
||||
#if defined(CONFIG_SYS_CSPR2) && defined(CONFIG_SYS_CSOR2)
|
||||
CONFIG_SYS_CSPR2,
|
||||
#ifdef CONFIG_SYS_CSPR2_EXT
|
||||
CONFIG_SYS_CSPR2_EXT,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_AMASK2
|
||||
CONFIG_SYS_AMASK2,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
CONFIG_SYS_CSOR2,
|
||||
{
|
||||
CONFIG_SYS_CS2_FTIM0,
|
||||
CONFIG_SYS_CS2_FTIM1,
|
||||
CONFIG_SYS_CS2_FTIM2,
|
||||
CONFIG_SYS_CS2_FTIM3,
|
||||
},
|
||||
#ifdef CONFIG_SYS_CSOR2_EXT
|
||||
CONFIG_SYS_CSOR2_EXT,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_CSPR2_FINAL
|
||||
CONFIG_SYS_CSPR2_FINAL,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_AMASK2_FINAL
|
||||
CONFIG_SYS_AMASK2_FINAL,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
#endif
|
||||
},
|
||||
#endif
|
||||
|
||||
#if CONFIG_SYS_FSL_IFC_BANK_COUNT >= 4
|
||||
{
|
||||
"cs3",
|
||||
#if defined(CONFIG_SYS_CSPR3) && defined(CONFIG_SYS_CSOR3)
|
||||
CONFIG_SYS_CSPR3,
|
||||
#ifdef CONFIG_SYS_CSPR3_EXT
|
||||
CONFIG_SYS_CSPR3_EXT,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_AMASK3
|
||||
CONFIG_SYS_AMASK3,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
CONFIG_SYS_CSOR3,
|
||||
{
|
||||
CONFIG_SYS_CS3_FTIM0,
|
||||
CONFIG_SYS_CS3_FTIM1,
|
||||
CONFIG_SYS_CS3_FTIM2,
|
||||
CONFIG_SYS_CS3_FTIM3,
|
||||
},
|
||||
#ifdef CONFIG_SYS_CSOR3_EXT
|
||||
CONFIG_SYS_CSOR3_EXT,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_CSPR3_FINAL
|
||||
CONFIG_SYS_CSPR3_FINAL,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_AMASK3_FINAL
|
||||
CONFIG_SYS_AMASK3_FINAL,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
#endif
|
||||
},
|
||||
#endif
|
||||
|
||||
#if CONFIG_SYS_FSL_IFC_BANK_COUNT >= 5
|
||||
{
|
||||
"cs4",
|
||||
#if defined(CONFIG_SYS_CSPR4) && defined(CONFIG_SYS_CSOR4)
|
||||
CONFIG_SYS_CSPR4,
|
||||
#ifdef CONFIG_SYS_CSPR4_EXT
|
||||
CONFIG_SYS_CSPR4_EXT,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_AMASK4
|
||||
CONFIG_SYS_AMASK4,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
CONFIG_SYS_CSOR4,
|
||||
{
|
||||
CONFIG_SYS_CS4_FTIM0,
|
||||
CONFIG_SYS_CS4_FTIM1,
|
||||
CONFIG_SYS_CS4_FTIM2,
|
||||
CONFIG_SYS_CS4_FTIM3,
|
||||
},
|
||||
#ifdef CONFIG_SYS_CSOR4_EXT
|
||||
CONFIG_SYS_CSOR4_EXT,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_CSPR4_FINAL
|
||||
CONFIG_SYS_CSPR4_FINAL,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_AMASK4_FINAL
|
||||
CONFIG_SYS_AMASK4_FINAL,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
#endif
|
||||
},
|
||||
#endif
|
||||
|
||||
#if CONFIG_SYS_FSL_IFC_BANK_COUNT >= 6
|
||||
{
|
||||
"cs5",
|
||||
#if defined(CONFIG_SYS_CSPR5) && defined(CONFIG_SYS_CSOR5)
|
||||
CONFIG_SYS_CSPR5,
|
||||
#ifdef CONFIG_SYS_CSPR5_EXT
|
||||
CONFIG_SYS_CSPR5_EXT,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_AMASK5
|
||||
CONFIG_SYS_AMASK5,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
CONFIG_SYS_CSOR5,
|
||||
{
|
||||
CONFIG_SYS_CS5_FTIM0,
|
||||
CONFIG_SYS_CS5_FTIM1,
|
||||
CONFIG_SYS_CS5_FTIM2,
|
||||
CONFIG_SYS_CS5_FTIM3,
|
||||
},
|
||||
#ifdef CONFIG_SYS_CSOR5_EXT
|
||||
CONFIG_SYS_CSOR5_EXT,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_CSPR5_FINAL
|
||||
CONFIG_SYS_CSPR5_FINAL,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_AMASK5_FINAL
|
||||
CONFIG_SYS_AMASK5_FINAL,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
#endif
|
||||
},
|
||||
#endif
|
||||
|
||||
#if CONFIG_SYS_FSL_IFC_BANK_COUNT >= 7
|
||||
{
|
||||
"cs6",
|
||||
#if defined(CONFIG_SYS_CSPR6) && defined(CONFIG_SYS_CSOR6)
|
||||
CONFIG_SYS_CSPR6,
|
||||
#ifdef CONFIG_SYS_CSPR6_EXT
|
||||
CONFIG_SYS_CSPR6_EXT,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_AMASK6
|
||||
CONFIG_SYS_AMASK6,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
CONFIG_SYS_CSOR6,
|
||||
{
|
||||
CONFIG_SYS_CS6_FTIM0,
|
||||
CONFIG_SYS_CS6_FTIM1,
|
||||
CONFIG_SYS_CS6_FTIM2,
|
||||
CONFIG_SYS_CS6_FTIM3,
|
||||
},
|
||||
#ifdef CONFIG_SYS_CSOR6_EXT
|
||||
CONFIG_SYS_CSOR6_EXT,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_CSPR6_FINAL
|
||||
CONFIG_SYS_CSPR6_FINAL,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_AMASK6_FINAL
|
||||
CONFIG_SYS_AMASK6_FINAL,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
#endif
|
||||
},
|
||||
#endif
|
||||
|
||||
#if CONFIG_SYS_FSL_IFC_BANK_COUNT >= 8
|
||||
{
|
||||
"cs7",
|
||||
#if defined(CONFIG_SYS_CSPR7) && defined(CONFIG_SYS_CSOR7)
|
||||
CONFIG_SYS_CSPR7,
|
||||
#ifdef CONFIG_SYS_CSPR7_EXT
|
||||
CONFIG_SYS_CSPR7_EXT,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_AMASK7
|
||||
CONFIG_SYS_AMASK7,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
CONFIG_SYS_CSOR7,
|
||||
#ifdef CONFIG_SYS_CSOR7_EXT
|
||||
CONFIG_SYS_CSOR7_EXT,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
{
|
||||
CONFIG_SYS_CS7_FTIM0,
|
||||
CONFIG_SYS_CS7_FTIM1,
|
||||
CONFIG_SYS_CS7_FTIM2,
|
||||
CONFIG_SYS_CS7_FTIM3,
|
||||
},
|
||||
#ifdef CONFIG_SYS_CSPR7_FINAL
|
||||
CONFIG_SYS_CSPR7_FINAL,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_AMASK7_FINAL
|
||||
CONFIG_SYS_AMASK7_FINAL,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
#endif
|
||||
},
|
||||
#endif
|
||||
};
|
||||
|
||||
__weak void ifc_cfg_boot_info(struct ifc_regs_info *regs_info)
|
||||
{
|
||||
regs_info->regs = ifc_cfg_default_boot;
|
||||
regs_info->cs_size = CONFIG_SYS_FSL_IFC_BANK_COUNT;
|
||||
}
|
||||
#endif
|
||||
|
||||
void print_ifc_regs(void)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
printf("IFC Controller Registers\n");
|
||||
for (i = 0; i < CONFIG_SYS_FSL_IFC_BANK_COUNT; i++) {
|
||||
printf("CSPR%d:0x%08X\tAMASK%d:0x%08X\tCSOR%d:0x%08X\n",
|
||||
i, get_ifc_cspr(i), i, get_ifc_amask(i),
|
||||
i, get_ifc_csor(i));
|
||||
for (j = 0; j < 4; j++)
|
||||
printf("IFC_FTIM%d:0x%08X\n", j, get_ifc_ftim(i, j));
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_TFABOOT
|
||||
void init_early_memctl_regs(void)
|
||||
{
|
||||
int i, j;
|
||||
struct ifc_regs *regs;
|
||||
struct ifc_regs_info regs_info = {0};
|
||||
|
||||
ifc_cfg_boot_info(®s_info);
|
||||
regs = regs_info.regs;
|
||||
|
||||
for (i = 0 ; i < regs_info.cs_size; i++) {
|
||||
if (regs[i].pr && (regs[i].pr & CSPR_V)) {
|
||||
/* skip setting cspr/csor_ext in below condition */
|
||||
if (!(CONFIG_IS_ENABLED(A003399_NOR_WORKAROUND) &&
|
||||
i == 0 &&
|
||||
((regs[0].pr & CSPR_MSEL) == CSPR_MSEL_NOR))) {
|
||||
if (regs[i].pr_ext)
|
||||
set_ifc_cspr_ext(i, regs[i].pr_ext);
|
||||
if (regs[i].or_ext)
|
||||
set_ifc_csor_ext(i, regs[i].or_ext);
|
||||
}
|
||||
|
||||
for (j = 0; j < ARRAY_SIZE(regs->ftim); j++)
|
||||
set_ifc_ftim(i, j, regs[i].ftim[j]);
|
||||
|
||||
set_ifc_csor(i, regs[i].or);
|
||||
set_ifc_amask(i, regs[i].amask);
|
||||
set_ifc_cspr(i, regs[i].pr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void init_final_memctl_regs(void)
|
||||
{
|
||||
int i;
|
||||
struct ifc_regs *regs;
|
||||
struct ifc_regs_info regs_info;
|
||||
|
||||
ifc_cfg_boot_info(®s_info);
|
||||
regs = regs_info.regs;
|
||||
|
||||
for (i = 0 ; i < regs_info.cs_size && i < ARRAY_SIZE(regs->ftim); i++) {
|
||||
if (!(regs[i].pr_final & CSPR_V))
|
||||
continue;
|
||||
if (regs[i].pr_final)
|
||||
set_ifc_cspr(i, regs[i].pr_final);
|
||||
if (regs[i].amask_final)
|
||||
set_ifc_amask(i, (i == 1) ? regs[i].amask_final :
|
||||
regs[i].amask);
|
||||
}
|
||||
}
|
||||
#else
|
||||
void init_early_memctl_regs(void)
|
||||
{
|
||||
#if defined(CONFIG_SYS_CSPR0) && defined(CONFIG_SYS_CSOR0)
|
||||
set_ifc_ftim(IFC_CS0, IFC_FTIM0, CONFIG_SYS_CS0_FTIM0);
|
||||
set_ifc_ftim(IFC_CS0, IFC_FTIM1, CONFIG_SYS_CS0_FTIM1);
|
||||
set_ifc_ftim(IFC_CS0, IFC_FTIM2, CONFIG_SYS_CS0_FTIM2);
|
||||
set_ifc_ftim(IFC_CS0, IFC_FTIM3, CONFIG_SYS_CS0_FTIM3);
|
||||
|
||||
#ifndef CONFIG_A003399_NOR_WORKAROUND
|
||||
#ifdef CONFIG_SYS_CSPR0_EXT
|
||||
set_ifc_cspr_ext(IFC_CS0, CONFIG_SYS_CSPR0_EXT);
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_CSOR0_EXT
|
||||
set_ifc_csor_ext(IFC_CS0, CONFIG_SYS_CSOR0_EXT);
|
||||
#endif
|
||||
set_ifc_cspr(IFC_CS0, CONFIG_SYS_CSPR0);
|
||||
set_ifc_amask(IFC_CS0, CONFIG_SYS_AMASK0);
|
||||
set_ifc_csor(IFC_CS0, CONFIG_SYS_CSOR0);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SYS_CSPR1_EXT
|
||||
set_ifc_cspr_ext(IFC_CS1, CONFIG_SYS_CSPR1_EXT);
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_CSOR1_EXT
|
||||
set_ifc_csor_ext(IFC_CS1, CONFIG_SYS_CSOR1_EXT);
|
||||
#endif
|
||||
#if defined(CONFIG_SYS_CSPR1) && defined(CONFIG_SYS_CSOR1)
|
||||
set_ifc_ftim(IFC_CS1, IFC_FTIM0, CONFIG_SYS_CS1_FTIM0);
|
||||
set_ifc_ftim(IFC_CS1, IFC_FTIM1, CONFIG_SYS_CS1_FTIM1);
|
||||
set_ifc_ftim(IFC_CS1, IFC_FTIM2, CONFIG_SYS_CS1_FTIM2);
|
||||
set_ifc_ftim(IFC_CS1, IFC_FTIM3, CONFIG_SYS_CS1_FTIM3);
|
||||
|
||||
set_ifc_csor(IFC_CS1, CONFIG_SYS_CSOR1);
|
||||
set_ifc_amask(IFC_CS1, CONFIG_SYS_AMASK1);
|
||||
set_ifc_cspr(IFC_CS1, CONFIG_SYS_CSPR1);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SYS_CSPR2_EXT
|
||||
set_ifc_cspr_ext(IFC_CS2, CONFIG_SYS_CSPR2_EXT);
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_CSOR2_EXT
|
||||
set_ifc_csor_ext(IFC_CS2, CONFIG_SYS_CSOR2_EXT);
|
||||
#endif
|
||||
#if defined(CONFIG_SYS_CSPR2) && defined(CONFIG_SYS_CSOR2)
|
||||
set_ifc_ftim(IFC_CS2, IFC_FTIM0, CONFIG_SYS_CS2_FTIM0);
|
||||
set_ifc_ftim(IFC_CS2, IFC_FTIM1, CONFIG_SYS_CS2_FTIM1);
|
||||
set_ifc_ftim(IFC_CS2, IFC_FTIM2, CONFIG_SYS_CS2_FTIM2);
|
||||
set_ifc_ftim(IFC_CS2, IFC_FTIM3, CONFIG_SYS_CS2_FTIM3);
|
||||
|
||||
set_ifc_csor(IFC_CS2, CONFIG_SYS_CSOR2);
|
||||
set_ifc_amask(IFC_CS2, CONFIG_SYS_AMASK2);
|
||||
set_ifc_cspr(IFC_CS2, CONFIG_SYS_CSPR2);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SYS_CSPR3_EXT
|
||||
set_ifc_cspr_ext(IFC_CS3, CONFIG_SYS_CSPR3_EXT);
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_CSOR3_EXT
|
||||
set_ifc_csor_ext(IFC_CS3, CONFIG_SYS_CSOR3_EXT);
|
||||
#endif
|
||||
#if defined(CONFIG_SYS_CSPR3) && defined(CONFIG_SYS_CSOR3)
|
||||
set_ifc_ftim(IFC_CS3, IFC_FTIM0, CONFIG_SYS_CS3_FTIM0);
|
||||
set_ifc_ftim(IFC_CS3, IFC_FTIM1, CONFIG_SYS_CS3_FTIM1);
|
||||
set_ifc_ftim(IFC_CS3, IFC_FTIM2, CONFIG_SYS_CS3_FTIM2);
|
||||
set_ifc_ftim(IFC_CS3, IFC_FTIM3, CONFIG_SYS_CS3_FTIM3);
|
||||
|
||||
set_ifc_cspr(IFC_CS3, CONFIG_SYS_CSPR3);
|
||||
set_ifc_amask(IFC_CS3, CONFIG_SYS_AMASK3);
|
||||
set_ifc_csor(IFC_CS3, CONFIG_SYS_CSOR3);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SYS_CSPR4_EXT
|
||||
set_ifc_cspr_ext(IFC_CS4, CONFIG_SYS_CSPR4_EXT);
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_CSOR4_EXT
|
||||
set_ifc_csor_ext(IFC_CS4, CONFIG_SYS_CSOR4_EXT);
|
||||
#endif
|
||||
#if defined(CONFIG_SYS_CSPR4) && defined(CONFIG_SYS_CSOR4)
|
||||
set_ifc_ftim(IFC_CS4, IFC_FTIM0, CONFIG_SYS_CS4_FTIM0);
|
||||
set_ifc_ftim(IFC_CS4, IFC_FTIM1, CONFIG_SYS_CS4_FTIM1);
|
||||
set_ifc_ftim(IFC_CS4, IFC_FTIM2, CONFIG_SYS_CS4_FTIM2);
|
||||
set_ifc_ftim(IFC_CS4, IFC_FTIM3, CONFIG_SYS_CS4_FTIM3);
|
||||
|
||||
set_ifc_cspr(IFC_CS4, CONFIG_SYS_CSPR4);
|
||||
set_ifc_amask(IFC_CS4, CONFIG_SYS_AMASK4);
|
||||
set_ifc_csor(IFC_CS4, CONFIG_SYS_CSOR4);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SYS_CSPR5_EXT
|
||||
set_ifc_cspr_ext(IFC_CS5, CONFIG_SYS_CSPR5_EXT);
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_CSOR5_EXT
|
||||
set_ifc_csor_ext(IFC_CS5, CONFIG_SYS_CSOR5_EXT);
|
||||
#endif
|
||||
#if defined(CONFIG_SYS_CSPR5) && defined(CONFIG_SYS_CSOR5)
|
||||
set_ifc_ftim(IFC_CS5, IFC_FTIM0, CONFIG_SYS_CS5_FTIM0);
|
||||
set_ifc_ftim(IFC_CS5, IFC_FTIM1, CONFIG_SYS_CS5_FTIM1);
|
||||
set_ifc_ftim(IFC_CS5, IFC_FTIM2, CONFIG_SYS_CS5_FTIM2);
|
||||
set_ifc_ftim(IFC_CS5, IFC_FTIM3, CONFIG_SYS_CS5_FTIM3);
|
||||
|
||||
set_ifc_cspr(IFC_CS5, CONFIG_SYS_CSPR5);
|
||||
set_ifc_amask(IFC_CS5, CONFIG_SYS_AMASK5);
|
||||
set_ifc_csor(IFC_CS5, CONFIG_SYS_CSOR5);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SYS_CSPR6_EXT
|
||||
set_ifc_cspr_ext(IFC_CS6, CONFIG_SYS_CSPR6_EXT);
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_CSOR6_EXT
|
||||
set_ifc_csor_ext(IFC_CS6, CONFIG_SYS_CSOR6_EXT);
|
||||
#endif
|
||||
#if defined(CONFIG_SYS_CSPR6) && defined(CONFIG_SYS_CSOR6)
|
||||
set_ifc_ftim(IFC_CS6, IFC_FTIM0, CONFIG_SYS_CS6_FTIM0);
|
||||
set_ifc_ftim(IFC_CS6, IFC_FTIM1, CONFIG_SYS_CS6_FTIM1);
|
||||
set_ifc_ftim(IFC_CS6, IFC_FTIM2, CONFIG_SYS_CS6_FTIM2);
|
||||
set_ifc_ftim(IFC_CS6, IFC_FTIM3, CONFIG_SYS_CS6_FTIM3);
|
||||
|
||||
set_ifc_cspr(IFC_CS6, CONFIG_SYS_CSPR6);
|
||||
set_ifc_amask(IFC_CS6, CONFIG_SYS_AMASK6);
|
||||
set_ifc_csor(IFC_CS6, CONFIG_SYS_CSOR6);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SYS_CSPR7_EXT
|
||||
set_ifc_cspr_ext(IFC_CS7, CONFIG_SYS_CSPR7_EXT);
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_CSOR7_EXT
|
||||
set_ifc_csor_ext(IFC_CS7, CONFIG_SYS_CSOR7_EXT);
|
||||
#endif
|
||||
#if defined(CONFIG_SYS_CSPR7) && defined(CONFIG_SYS_CSOR7)
|
||||
set_ifc_ftim(IFC_CS7, IFC_FTIM0, CONFIG_SYS_CS7_FTIM0);
|
||||
set_ifc_ftim(IFC_CS7, IFC_FTIM1, CONFIG_SYS_CS7_FTIM1);
|
||||
set_ifc_ftim(IFC_CS7, IFC_FTIM2, CONFIG_SYS_CS7_FTIM2);
|
||||
set_ifc_ftim(IFC_CS7, IFC_FTIM3, CONFIG_SYS_CS7_FTIM3);
|
||||
|
||||
set_ifc_cspr(IFC_CS7, CONFIG_SYS_CSPR7);
|
||||
set_ifc_amask(IFC_CS7, CONFIG_SYS_AMASK7);
|
||||
set_ifc_csor(IFC_CS7, CONFIG_SYS_CSOR7);
|
||||
#endif
|
||||
}
|
||||
|
||||
void init_final_memctl_regs(void)
|
||||
{
|
||||
#ifdef CONFIG_SYS_CSPR0_FINAL
|
||||
set_ifc_cspr(IFC_CS0, CONFIG_SYS_CSPR0_FINAL);
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_AMASK0_FINAL
|
||||
set_ifc_amask(IFC_CS0, CONFIG_SYS_AMASK0);
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_CSPR1_FINAL
|
||||
set_ifc_cspr(IFC_CS1, CONFIG_SYS_CSPR1_FINAL);
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_AMASK1_FINAL
|
||||
set_ifc_amask(IFC_CS1, CONFIG_SYS_AMASK1_FINAL);
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_CSPR2_FINAL
|
||||
set_ifc_cspr(IFC_CS2, CONFIG_SYS_CSPR2_FINAL);
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_AMASK2_FINAL
|
||||
set_ifc_amask(IFC_CS2, CONFIG_SYS_AMASK2);
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_CSPR3_FINAL
|
||||
set_ifc_cspr(IFC_CS3, CONFIG_SYS_CSPR3_FINAL);
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_AMASK3_FINAL
|
||||
set_ifc_amask(IFC_CS3, CONFIG_SYS_AMASK3);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,278 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2009-2013 ADVANSEE
|
||||
* Benoît Thébaudeau <benoit.thebaudeau@advansee.com>
|
||||
*
|
||||
* Based on the mpc512x iim code:
|
||||
* Copyright 2008 Silicon Turnkey Express, Inc.
|
||||
* Martha Marx <mmarx@silicontkx.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <fuse.h>
|
||||
#include <linux/errno.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/imx-regs.h>
|
||||
#if defined(CONFIG_MX51) || defined(CONFIG_MX53)
|
||||
#include <asm/arch/clock.h>
|
||||
#endif
|
||||
|
||||
/* FSL IIM-specific constants */
|
||||
#define STAT_BUSY 0x80
|
||||
#define STAT_PRGD 0x02
|
||||
#define STAT_SNSD 0x01
|
||||
|
||||
#define STATM_PRGD_M 0x02
|
||||
#define STATM_SNSD_M 0x01
|
||||
|
||||
#define ERR_PRGE 0x80
|
||||
#define ERR_WPE 0x40
|
||||
#define ERR_OPE 0x20
|
||||
#define ERR_RPE 0x10
|
||||
#define ERR_WLRE 0x08
|
||||
#define ERR_SNSE 0x04
|
||||
#define ERR_PARITYE 0x02
|
||||
|
||||
#define EMASK_PRGE_M 0x80
|
||||
#define EMASK_WPE_M 0x40
|
||||
#define EMASK_OPE_M 0x20
|
||||
#define EMASK_RPE_M 0x10
|
||||
#define EMASK_WLRE_M 0x08
|
||||
#define EMASK_SNSE_M 0x04
|
||||
#define EMASK_PARITYE_M 0x02
|
||||
|
||||
#define FCTL_DPC 0x80
|
||||
#define FCTL_PRG_LENGTH_MASK 0x70
|
||||
#define FCTL_ESNS_N 0x08
|
||||
#define FCTL_ESNS_0 0x04
|
||||
#define FCTL_ESNS_1 0x02
|
||||
#define FCTL_PRG 0x01
|
||||
|
||||
#define UA_A_BANK_MASK 0x38
|
||||
#define UA_A_ROWH_MASK 0x07
|
||||
|
||||
#define LA_A_ROWL_MASK 0xf8
|
||||
#define LA_A_BIT_MASK 0x07
|
||||
|
||||
#define PREV_PROD_REV_MASK 0xf8
|
||||
#define PREV_PROD_VT_MASK 0x07
|
||||
|
||||
/* Select the correct accessors depending on endianness */
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
#define iim_read32 in_le32
|
||||
#define iim_write32 out_le32
|
||||
#define iim_clrsetbits32 clrsetbits_le32
|
||||
#define iim_clrbits32 clrbits_le32
|
||||
#define iim_setbits32 setbits_le32
|
||||
#elif __BYTE_ORDER == __BIG_ENDIAN
|
||||
#define iim_read32 in_be32
|
||||
#define iim_write32 out_be32
|
||||
#define iim_clrsetbits32 clrsetbits_be32
|
||||
#define iim_clrbits32 clrbits_be32
|
||||
#define iim_setbits32 setbits_be32
|
||||
#else
|
||||
#error Endianess is not defined: please fix to continue
|
||||
#endif
|
||||
|
||||
/* IIM control registers */
|
||||
struct fsl_iim {
|
||||
u32 stat;
|
||||
u32 statm;
|
||||
u32 err;
|
||||
u32 emask;
|
||||
u32 fctl;
|
||||
u32 ua;
|
||||
u32 la;
|
||||
u32 sdat;
|
||||
u32 prev;
|
||||
u32 srev;
|
||||
u32 prg_p;
|
||||
u32 scs[0x1f5];
|
||||
struct {
|
||||
u32 word[0x100];
|
||||
} bank[8];
|
||||
};
|
||||
|
||||
#if !defined(CONFIG_MX51) && !defined(CONFIG_MX53)
|
||||
#define enable_efuse_prog_supply(enable)
|
||||
#endif
|
||||
|
||||
static int prepare_access(struct fsl_iim **regs, u32 bank, u32 word, int assert,
|
||||
const char *caller)
|
||||
{
|
||||
*regs = (struct fsl_iim *)IIM_BASE_ADDR;
|
||||
|
||||
if (bank >= ARRAY_SIZE((*regs)->bank) ||
|
||||
word >= ARRAY_SIZE((*regs)->bank[0].word) ||
|
||||
!assert) {
|
||||
printf("fsl_iim %s(): Invalid argument\n", caller);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void clear_status(struct fsl_iim *regs)
|
||||
{
|
||||
iim_setbits32(®s->stat, 0);
|
||||
iim_setbits32(®s->err, 0);
|
||||
}
|
||||
|
||||
static void finish_access(struct fsl_iim *regs, u32 *stat, u32 *err)
|
||||
{
|
||||
*stat = iim_read32(®s->stat);
|
||||
*err = iim_read32(®s->err);
|
||||
clear_status(regs);
|
||||
}
|
||||
|
||||
static int prepare_read(struct fsl_iim **regs, u32 bank, u32 word, u32 *val,
|
||||
const char *caller)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = prepare_access(regs, bank, word, val != NULL, caller);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
clear_status(*regs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fuse_read(u32 bank, u32 word, u32 *val)
|
||||
{
|
||||
struct fsl_iim *regs;
|
||||
u32 stat, err;
|
||||
int ret;
|
||||
|
||||
ret = prepare_read(®s, bank, word, val, __func__);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = iim_read32(®s->bank[bank].word[word]);
|
||||
finish_access(regs, &stat, &err);
|
||||
|
||||
if (err & ERR_RPE) {
|
||||
puts("fsl_iim fuse_read(): Read protect error\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void direct_access(struct fsl_iim *regs, u32 bank, u32 word, u32 bit,
|
||||
u32 fctl, u32 *stat, u32 *err)
|
||||
{
|
||||
iim_write32(®s->ua, bank << 3 | word >> 5);
|
||||
iim_write32(®s->la, (word << 3 | bit) & 0xff);
|
||||
if (fctl == FCTL_PRG)
|
||||
iim_write32(®s->prg_p, 0xaa);
|
||||
iim_setbits32(®s->fctl, fctl);
|
||||
while (iim_read32(®s->stat) & STAT_BUSY)
|
||||
udelay(20);
|
||||
finish_access(regs, stat, err);
|
||||
}
|
||||
|
||||
int fuse_sense(u32 bank, u32 word, u32 *val)
|
||||
{
|
||||
struct fsl_iim *regs;
|
||||
u32 stat, err;
|
||||
int ret;
|
||||
|
||||
ret = prepare_read(®s, bank, word, val, __func__);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
direct_access(regs, bank, word, 0, FCTL_ESNS_N, &stat, &err);
|
||||
|
||||
if (err & ERR_SNSE) {
|
||||
puts("fsl_iim fuse_sense(): Explicit sense cycle error\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (!(stat & STAT_SNSD)) {
|
||||
puts("fsl_iim fuse_sense(): Explicit sense cycle did not complete\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
*val = iim_read32(®s->sdat);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int prog_bit(struct fsl_iim *regs, u32 bank, u32 word, u32 bit)
|
||||
{
|
||||
u32 stat, err;
|
||||
|
||||
clear_status(regs);
|
||||
direct_access(regs, bank, word, bit, FCTL_PRG, &stat, &err);
|
||||
iim_write32(®s->prg_p, 0x00);
|
||||
|
||||
if (err & ERR_PRGE) {
|
||||
puts("fsl_iim fuse_prog(): Program error\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (err & ERR_WPE) {
|
||||
puts("fsl_iim fuse_prog(): Write protect error\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (!(stat & STAT_PRGD)) {
|
||||
puts("fsl_iim fuse_prog(): Program did not complete\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int prepare_write(struct fsl_iim **regs, u32 bank, u32 word, u32 val,
|
||||
const char *caller)
|
||||
{
|
||||
return prepare_access(regs, bank, word, !(val & ~0xff), caller);
|
||||
}
|
||||
|
||||
int fuse_prog(u32 bank, u32 word, u32 val)
|
||||
{
|
||||
struct fsl_iim *regs;
|
||||
u32 bit;
|
||||
int ret;
|
||||
|
||||
ret = prepare_write(®s, bank, word, val, __func__);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
enable_efuse_prog_supply(1);
|
||||
for (bit = 0; val; bit++, val >>= 1)
|
||||
if (val & 0x01) {
|
||||
ret = prog_bit(regs, bank, word, bit);
|
||||
if (ret) {
|
||||
enable_efuse_prog_supply(0);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
enable_efuse_prog_supply(0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fuse_override(u32 bank, u32 word, u32 val)
|
||||
{
|
||||
struct fsl_iim *regs;
|
||||
u32 stat, err;
|
||||
int ret;
|
||||
|
||||
ret = prepare_write(®s, bank, word, val, __func__);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
clear_status(regs);
|
||||
iim_write32(®s->bank[bank].word[word], val);
|
||||
finish_access(regs, &stat, &err);
|
||||
|
||||
if (err & ERR_OPE) {
|
||||
puts("fsl_iim fuse_override(): Override protect error\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,338 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2008-2011 Freescale Semiconductor, Inc.
|
||||
* Copyright 2017 NXP
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <linux/libfdt.h>
|
||||
#include <fdt_support.h>
|
||||
|
||||
#include <asm/processor.h>
|
||||
#include <asm/io.h>
|
||||
#ifdef CONFIG_PPC
|
||||
#include <asm/fsl_portals.h>
|
||||
#include <asm/fsl_liodn.h>
|
||||
#else
|
||||
#include <asm/arch-fsl-layerscape/fsl_portals.h>
|
||||
#include <asm/arch-fsl-layerscape/fsl_icid.h>
|
||||
#endif
|
||||
#include <fsl_qbman.h>
|
||||
|
||||
#define MAX_BPORTALS (CONFIG_SYS_BMAN_CINH_SIZE / CONFIG_SYS_BMAN_SP_CINH_SIZE)
|
||||
#define MAX_QPORTALS (CONFIG_SYS_QMAN_CINH_SIZE / CONFIG_SYS_QMAN_SP_CINH_SIZE)
|
||||
void setup_qbman_portals(void)
|
||||
{
|
||||
void __iomem *bpaddr = (void *)CONFIG_SYS_BMAN_CINH_BASE +
|
||||
CONFIG_SYS_BMAN_SWP_ISDR_REG;
|
||||
void __iomem *qpaddr = (void *)CONFIG_SYS_QMAN_CINH_BASE +
|
||||
CONFIG_SYS_QMAN_SWP_ISDR_REG;
|
||||
struct ccsr_qman *qman = (void *)CONFIG_SYS_FSL_QMAN_ADDR;
|
||||
|
||||
/* Set the Qman initiator BAR to match the LAW (for DQRR stashing) */
|
||||
#ifdef CONFIG_PHYS_64BIT
|
||||
out_be32(&qman->qcsp_bare, (u32)(CONFIG_SYS_QMAN_MEM_PHYS >> 32));
|
||||
#endif
|
||||
out_be32(&qman->qcsp_bar, (u32)CONFIG_SYS_QMAN_MEM_PHYS);
|
||||
#ifdef CONFIG_FSL_CORENET
|
||||
int i;
|
||||
|
||||
for (i = 0; i < CONFIG_SYS_QMAN_NUM_PORTALS; i++) {
|
||||
u8 sdest = qp_info[i].sdest;
|
||||
u16 fliodn = qp_info[i].fliodn;
|
||||
u16 dliodn = qp_info[i].dliodn;
|
||||
u16 liodn_off = qp_info[i].liodn_offset;
|
||||
|
||||
out_be32(&qman->qcsp[i].qcsp_lio_cfg, (liodn_off << 16) |
|
||||
dliodn);
|
||||
/* set frame liodn */
|
||||
out_be32(&qman->qcsp[i].qcsp_io_cfg, (sdest << 16) | fliodn);
|
||||
}
|
||||
#else
|
||||
#if defined(CONFIG_ARCH_LS1043A) || defined(CONFIG_ARCH_LS1046A)
|
||||
int i;
|
||||
|
||||
for (i = 0; i < CONFIG_SYS_QMAN_NUM_PORTALS; i++) {
|
||||
u8 sdest = qp_info[i].sdest;
|
||||
u16 ficid = qp_info[i].ficid;
|
||||
u16 dicid = qp_info[i].dicid;
|
||||
u16 icid = qp_info[i].icid;
|
||||
|
||||
out_be32(&qman->qcsp[i].qcsp_lio_cfg, (icid << 16) |
|
||||
dicid);
|
||||
/* set frame icid */
|
||||
out_be32(&qman->qcsp[i].qcsp_io_cfg, (sdest << 16) | ficid);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Change default state of BMan ISDR portals to all 1s */
|
||||
inhibit_portals(bpaddr, CONFIG_SYS_BMAN_NUM_PORTALS, MAX_BPORTALS,
|
||||
CONFIG_SYS_BMAN_SP_CINH_SIZE);
|
||||
inhibit_portals(qpaddr, CONFIG_SYS_QMAN_NUM_PORTALS, MAX_QPORTALS,
|
||||
CONFIG_SYS_QMAN_SP_CINH_SIZE);
|
||||
}
|
||||
|
||||
void inhibit_portals(void __iomem *addr, int max_portals,
|
||||
int arch_max_portals, int portal_cinh_size)
|
||||
{
|
||||
u32 val;
|
||||
int i;
|
||||
|
||||
/* arch_max_portals is the maximum based on memory size. This includes
|
||||
* the reserved memory in the SoC. max_portals the number of physical
|
||||
* portals in the SoC
|
||||
*/
|
||||
if (max_portals > arch_max_portals) {
|
||||
printf("ERROR: portal config error\n");
|
||||
max_portals = arch_max_portals;
|
||||
}
|
||||
|
||||
for (i = 0; i < max_portals; i++) {
|
||||
out_be32(addr, -1);
|
||||
val = in_be32(addr);
|
||||
if (!val) {
|
||||
printf("ERROR: Stopped after %d portals\n", i);
|
||||
return;
|
||||
}
|
||||
addr += portal_cinh_size;
|
||||
}
|
||||
debug("Cleared %d portals\n", i);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PPC
|
||||
static int fdt_qportal(void *blob, int off, int id, char *name,
|
||||
enum fsl_dpaa_dev dev, int create)
|
||||
{
|
||||
int childoff, dev_off, ret = 0;
|
||||
u32 dev_handle;
|
||||
#ifdef CONFIG_FSL_CORENET
|
||||
int num;
|
||||
u32 liodns[2];
|
||||
#endif
|
||||
|
||||
childoff = fdt_subnode_offset(blob, off, name);
|
||||
if (create) {
|
||||
char handle[64], *p;
|
||||
|
||||
strncpy(handle, name, sizeof(handle));
|
||||
p = strchr(handle, '@');
|
||||
if (!strncmp(name, "fman", 4)) {
|
||||
*p = *(p + 1);
|
||||
p++;
|
||||
}
|
||||
*p = '\0';
|
||||
|
||||
dev_off = fdt_path_offset(blob, handle);
|
||||
/* skip this node if alias is not found */
|
||||
if (dev_off == -FDT_ERR_BADPATH)
|
||||
return 0;
|
||||
if (dev_off < 0)
|
||||
return dev_off;
|
||||
|
||||
if (childoff <= 0)
|
||||
childoff = fdt_add_subnode(blob, off, name);
|
||||
|
||||
/* need to update the dev_off after adding a subnode */
|
||||
dev_off = fdt_path_offset(blob, handle);
|
||||
if (dev_off < 0)
|
||||
return dev_off;
|
||||
|
||||
if (childoff > 0) {
|
||||
dev_handle = fdt_get_phandle(blob, dev_off);
|
||||
if (dev_handle <= 0) {
|
||||
dev_handle = fdt_alloc_phandle(blob);
|
||||
ret = fdt_set_phandle(blob, dev_off,
|
||||
dev_handle);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = fdt_setprop(blob, childoff, "dev-handle",
|
||||
&dev_handle, sizeof(dev_handle));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
#ifdef CONFIG_FSL_CORENET
|
||||
num = get_dpaa_liodn(dev, &liodns[0], id);
|
||||
ret = fdt_setprop(blob, childoff, "fsl,liodn",
|
||||
&liodns[0], sizeof(u32) * num);
|
||||
if (!strncmp(name, "pme", 3)) {
|
||||
u32 pme_rev1, pme_rev2;
|
||||
ccsr_pme_t *pme_regs =
|
||||
(void *)CONFIG_SYS_FSL_CORENET_PME_ADDR;
|
||||
|
||||
pme_rev1 = in_be32(&pme_regs->pm_ip_rev_1);
|
||||
pme_rev2 = in_be32(&pme_regs->pm_ip_rev_2);
|
||||
ret = fdt_setprop(blob, childoff,
|
||||
"fsl,pme-rev1", &pme_rev1,
|
||||
sizeof(u32));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = fdt_setprop(blob, childoff,
|
||||
"fsl,pme-rev2", &pme_rev2,
|
||||
sizeof(u32));
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
return childoff;
|
||||
}
|
||||
} else {
|
||||
if (childoff > 0)
|
||||
ret = fdt_del_node(blob, childoff);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_PPC */
|
||||
|
||||
void fdt_fixup_qportals(void *blob)
|
||||
{
|
||||
int off, err;
|
||||
unsigned int maj, min;
|
||||
unsigned int ip_cfg;
|
||||
struct ccsr_qman *qman = (void *)CONFIG_SYS_FSL_QMAN_ADDR;
|
||||
u32 rev_1 = in_be32(&qman->ip_rev_1);
|
||||
u32 rev_2 = in_be32(&qman->ip_rev_2);
|
||||
char compat[64];
|
||||
int compat_len;
|
||||
|
||||
#if defined(CONFIG_ARCH_LS1043A) || defined(CONFIG_ARCH_LS1046A)
|
||||
int smmu_ph = fdt_get_smmu_phandle(blob);
|
||||
#endif
|
||||
|
||||
maj = (rev_1 >> 8) & 0xff;
|
||||
min = rev_1 & 0xff;
|
||||
ip_cfg = rev_2 & 0xff;
|
||||
|
||||
compat_len = sprintf(compat, "fsl,qman-portal-%u.%u.%u",
|
||||
maj, min, ip_cfg) + 1;
|
||||
compat_len += sprintf(compat + compat_len, "fsl,qman-portal") + 1;
|
||||
|
||||
off = fdt_node_offset_by_compatible(blob, -1, "fsl,qman-portal");
|
||||
while (off != -FDT_ERR_NOTFOUND) {
|
||||
#if defined(CONFIG_PPC) || defined(CONFIG_ARCH_LS1043A) || \
|
||||
defined(CONFIG_ARCH_LS1046A)
|
||||
#ifdef CONFIG_FSL_CORENET
|
||||
u32 liodns[2];
|
||||
#endif
|
||||
const int *ci = fdt_getprop(blob, off, "cell-index", &err);
|
||||
int i;
|
||||
|
||||
if (!ci)
|
||||
goto err;
|
||||
|
||||
i = fdt32_to_cpu(*ci);
|
||||
#if defined(CONFIG_SYS_DPAA_FMAN) && defined(CONFIG_PPC)
|
||||
int j;
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_PPC || CONFIG_ARCH_LS1043A || CONFIG_ARCH_LS1046A */
|
||||
err = fdt_setprop(blob, off, "compatible", compat, compat_len);
|
||||
if (err < 0)
|
||||
goto err;
|
||||
#ifdef CONFIG_PPC
|
||||
#ifdef CONFIG_FSL_CORENET
|
||||
liodns[0] = qp_info[i].dliodn;
|
||||
liodns[1] = qp_info[i].fliodn;
|
||||
err = fdt_setprop(blob, off, "fsl,liodn",
|
||||
&liodns, sizeof(u32) * 2);
|
||||
if (err < 0)
|
||||
goto err;
|
||||
#endif
|
||||
|
||||
i++;
|
||||
|
||||
err = fdt_qportal(blob, off, i, "crypto@0", FSL_HW_PORTAL_SEC,
|
||||
IS_E_PROCESSOR(get_svr()));
|
||||
if (err < 0)
|
||||
goto err;
|
||||
|
||||
#ifdef CONFIG_FSL_CORENET
|
||||
#ifdef CONFIG_SYS_DPAA_PME
|
||||
err = fdt_qportal(blob, off, i, "pme@0", FSL_HW_PORTAL_PME, 1);
|
||||
if (err < 0)
|
||||
goto err;
|
||||
#else
|
||||
fdt_qportal(blob, off, i, "pme@0", FSL_HW_PORTAL_PME, 0);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SYS_DPAA_FMAN
|
||||
for (j = 0; j < CONFIG_SYS_NUM_FMAN; j++) {
|
||||
char name[] = "fman@0";
|
||||
|
||||
name[sizeof(name) - 2] = '0' + j;
|
||||
err = fdt_qportal(blob, off, i, name,
|
||||
FSL_HW_PORTAL_FMAN1 + j, 1);
|
||||
if (err < 0)
|
||||
goto err;
|
||||
}
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_DPAA_RMAN
|
||||
err = fdt_qportal(blob, off, i, "rman@0",
|
||||
FSL_HW_PORTAL_RMAN, 1);
|
||||
if (err < 0)
|
||||
goto err;
|
||||
#endif
|
||||
#else
|
||||
#if defined(CONFIG_ARCH_LS1043A) || defined(CONFIG_ARCH_LS1046A)
|
||||
if (smmu_ph >= 0) {
|
||||
u32 icids[3];
|
||||
|
||||
icids[0] = qp_info[i].icid;
|
||||
icids[1] = qp_info[i].dicid;
|
||||
icids[2] = qp_info[i].ficid;
|
||||
|
||||
fdt_set_iommu_prop(blob, off, smmu_ph, icids, 3);
|
||||
}
|
||||
#endif
|
||||
#endif /* CONFIG_PPC */
|
||||
|
||||
err:
|
||||
if (err < 0) {
|
||||
printf("ERROR: unable to create props for %s: %s\n",
|
||||
fdt_get_name(blob, off, NULL),
|
||||
fdt_strerror(err));
|
||||
return;
|
||||
}
|
||||
|
||||
off = fdt_node_offset_by_compatible(blob, off,
|
||||
"fsl,qman-portal");
|
||||
}
|
||||
}
|
||||
|
||||
void fdt_fixup_bportals(void *blob)
|
||||
{
|
||||
int off, err;
|
||||
unsigned int maj, min;
|
||||
unsigned int ip_cfg;
|
||||
struct ccsr_bman *bman = (void *)CONFIG_SYS_FSL_BMAN_ADDR;
|
||||
u32 rev_1 = in_be32(&bman->ip_rev_1);
|
||||
u32 rev_2 = in_be32(&bman->ip_rev_2);
|
||||
char compat[64];
|
||||
int compat_len;
|
||||
|
||||
maj = (rev_1 >> 8) & 0xff;
|
||||
min = rev_1 & 0xff;
|
||||
|
||||
ip_cfg = rev_2 & 0xff;
|
||||
|
||||
compat_len = sprintf(compat, "fsl,bman-portal-%u.%u.%u",
|
||||
maj, min, ip_cfg) + 1;
|
||||
compat_len += sprintf(compat + compat_len, "fsl,bman-portal") + 1;
|
||||
|
||||
off = fdt_node_offset_by_compatible(blob, -1, "fsl,bman-portal");
|
||||
while (off != -FDT_ERR_NOTFOUND) {
|
||||
err = fdt_setprop(blob, off, "compatible", compat, compat_len);
|
||||
if (err < 0) {
|
||||
printf("ERROR: unable to create props for %s: %s\n",
|
||||
fdt_get_name(blob, off, NULL),
|
||||
fdt_strerror(err));
|
||||
return;
|
||||
}
|
||||
|
||||
off = fdt_node_offset_by_compatible(blob, off,
|
||||
"fsl,bman-portal");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2015 Freescale Semiconductor, Inc.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <fsl_sec_mon.h>
|
||||
|
||||
static u32 get_sec_mon_state(void)
|
||||
{
|
||||
struct ccsr_sec_mon_regs *sec_mon_regs = (void *)
|
||||
(CONFIG_SYS_SEC_MON_ADDR);
|
||||
return sec_mon_in32(&sec_mon_regs->hp_stat) & HPSR_SSM_ST_MASK;
|
||||
}
|
||||
|
||||
static int set_sec_mon_state_non_sec(void)
|
||||
{
|
||||
u32 sts;
|
||||
int timeout = 10;
|
||||
struct ccsr_sec_mon_regs *sec_mon_regs = (void *)
|
||||
(CONFIG_SYS_SEC_MON_ADDR);
|
||||
|
||||
sts = get_sec_mon_state();
|
||||
|
||||
switch (sts) {
|
||||
/*
|
||||
* If initial state is check or Non-Secure, then set the Software
|
||||
* Security Violation Bit and transition to Non-Secure State.
|
||||
*/
|
||||
case HPSR_SSM_ST_CHECK:
|
||||
printf("SEC_MON state transitioning to Non Secure.\n");
|
||||
sec_mon_setbits32(&sec_mon_regs->hp_com, HPCOMR_SW_SV);
|
||||
|
||||
/* polling loop till SEC_MON is in Non Secure state */
|
||||
while (timeout) {
|
||||
sts = get_sec_mon_state();
|
||||
|
||||
if ((sts & HPSR_SSM_ST_MASK) ==
|
||||
HPSR_SSM_ST_NON_SECURE)
|
||||
break;
|
||||
|
||||
udelay(10);
|
||||
timeout--;
|
||||
}
|
||||
|
||||
if (timeout == 0) {
|
||||
printf("SEC_MON state transition timeout.\n");
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
|
||||
/*
|
||||
* If initial state is Trusted, Secure or Soft-Fail, then first set
|
||||
* the Software Security Violation Bit and transition to Soft-Fail
|
||||
* State.
|
||||
*/
|
||||
case HPSR_SSM_ST_TRUST:
|
||||
case HPSR_SSM_ST_SECURE:
|
||||
case HPSR_SSM_ST_SOFT_FAIL:
|
||||
printf("SEC_MON state transitioning to Soft Fail.\n");
|
||||
sec_mon_setbits32(&sec_mon_regs->hp_com, HPCOMR_SW_SV);
|
||||
|
||||
/* polling loop till SEC_MON is in Soft-Fail state */
|
||||
while (timeout) {
|
||||
sts = get_sec_mon_state();
|
||||
|
||||
if ((sts & HPSR_SSM_ST_MASK) ==
|
||||
HPSR_SSM_ST_SOFT_FAIL)
|
||||
break;
|
||||
|
||||
udelay(10);
|
||||
timeout--;
|
||||
}
|
||||
|
||||
if (timeout == 0) {
|
||||
printf("SEC_MON state transition timeout.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
timeout = 10;
|
||||
|
||||
/*
|
||||
* If SSM Soft Fail to Non-Secure State Transition
|
||||
* disable is not set, then set SSM_ST bit and
|
||||
* transition to Non-Secure State.
|
||||
*/
|
||||
if ((sec_mon_in32(&sec_mon_regs->hp_com) &
|
||||
HPCOMR_SSM_SFNS_DIS) == 0) {
|
||||
printf("SEC_MON state transitioning to Non Secure.\n");
|
||||
sec_mon_setbits32(&sec_mon_regs->hp_com, HPCOMR_SSM_ST);
|
||||
|
||||
/* polling loop till SEC_MON is in Non Secure*/
|
||||
while (timeout) {
|
||||
sts = get_sec_mon_state();
|
||||
|
||||
if ((sts & HPSR_SSM_ST_MASK) ==
|
||||
HPSR_SSM_ST_NON_SECURE)
|
||||
break;
|
||||
|
||||
udelay(10);
|
||||
timeout--;
|
||||
}
|
||||
|
||||
if (timeout == 0) {
|
||||
printf("SEC_MON state transition timeout.\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
printf("SEC_MON already in Non Secure state.\n");
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_sec_mon_state_soft_fail(void)
|
||||
{
|
||||
u32 sts;
|
||||
int timeout = 10;
|
||||
struct ccsr_sec_mon_regs *sec_mon_regs = (void *)
|
||||
(CONFIG_SYS_SEC_MON_ADDR);
|
||||
|
||||
printf("SEC_MON state transitioning to Soft Fail.\n");
|
||||
sec_mon_setbits32(&sec_mon_regs->hp_com, HPCOMR_SW_FSV);
|
||||
|
||||
/* polling loop till SEC_MON is in Soft-Fail state */
|
||||
while (timeout) {
|
||||
sts = get_sec_mon_state();
|
||||
|
||||
if ((sts & HPSR_SSM_ST_MASK) ==
|
||||
HPSR_SSM_ST_SOFT_FAIL)
|
||||
break;
|
||||
|
||||
udelay(10);
|
||||
timeout--;
|
||||
}
|
||||
|
||||
if (timeout == 0) {
|
||||
printf("SEC_MON state transition timeout.\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int set_sec_mon_state(u32 state)
|
||||
{
|
||||
int ret = -1;
|
||||
|
||||
switch (state) {
|
||||
case HPSR_SSM_ST_NON_SECURE:
|
||||
ret = set_sec_mon_state_non_sec();
|
||||
break;
|
||||
case HPSR_SSM_ST_SOFT_FAIL:
|
||||
ret = set_sec_mon_state_soft_fail();
|
||||
break;
|
||||
default:
|
||||
printf("SEC_MON state transition not supported.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2017
|
||||
* Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
|
||||
*
|
||||
* based on the cmd_ioloop driver/command, which is
|
||||
*
|
||||
* (C) Copyright 2014
|
||||
* Dirk Eibach, Guntermann & Drunck GmbH, dirk.eibach@gdsys.cc
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <misc.h>
|
||||
#include <regmap.h>
|
||||
|
||||
#include "gdsys_ioep.h"
|
||||
|
||||
/**
|
||||
* struct gdsys_ioep_priv - Private data structure for IOEP devices
|
||||
* @map: Register map to be used for the device
|
||||
* @state: Flag to keep the current status of the RX control (enabled/disabled)
|
||||
*/
|
||||
struct gdsys_ioep_priv {
|
||||
struct regmap *map;
|
||||
bool state;
|
||||
};
|
||||
|
||||
/**
|
||||
* enum last_spec - Convenience enum for read data sanity check
|
||||
* @READ_DATA_IS_LAST: The data to be read should be the final data of the
|
||||
* current packet
|
||||
* @READ_DATA_IS_NOT_LAST: The data to be read should not be the final data of
|
||||
* the current packet
|
||||
*/
|
||||
enum last_spec {
|
||||
READ_DATA_IS_LAST,
|
||||
READ_DATA_IS_NOT_LAST,
|
||||
};
|
||||
|
||||
static int gdsys_ioep_set_receive(struct udevice *dev, bool val)
|
||||
{
|
||||
struct gdsys_ioep_priv *priv = dev_get_priv(dev);
|
||||
u16 state;
|
||||
|
||||
priv->state = !priv->state;
|
||||
|
||||
if (val)
|
||||
state = CTRL_PROC_RECEIVE_ENABLE;
|
||||
else
|
||||
state = ~CTRL_PROC_RECEIVE_ENABLE;
|
||||
|
||||
gdsys_ioep_set(priv->map, tx_control, state);
|
||||
|
||||
if (val) {
|
||||
/* Set device address to dummy 1 */
|
||||
gdsys_ioep_set(priv->map, device_address, 1);
|
||||
}
|
||||
|
||||
return !priv->state;
|
||||
}
|
||||
|
||||
static int gdsys_ioep_send(struct udevice *dev, int offset,
|
||||
const void *buf, int size)
|
||||
{
|
||||
struct gdsys_ioep_priv *priv = dev_get_priv(dev);
|
||||
int k;
|
||||
u16 *p = (u16 *)buf;
|
||||
|
||||
for (k = 0; k < size; ++k)
|
||||
gdsys_ioep_set(priv->map, transmit_data, *(p++));
|
||||
|
||||
gdsys_ioep_set(priv->map, tx_control, CTRL_PROC_RECEIVE_ENABLE |
|
||||
CTRL_FLUSH_TRANSMIT_BUFFER);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* receive_byte_buffer() - Read data from a IOEP device
|
||||
* @dev: The IOEP device to read data from
|
||||
* @len: The length of the data to read
|
||||
* @buffer: The buffer to read the data into
|
||||
* @last_spec: Flag to indicate if the data to be read in this call should be
|
||||
* the final data of the current packet (i.e. it should be empty
|
||||
* after this read)
|
||||
*
|
||||
* Return: 0 if OK, -ve on error
|
||||
*/
|
||||
static int receive_byte_buffer(struct udevice *dev, uint len,
|
||||
u16 *buffer, enum last_spec last_spec)
|
||||
{
|
||||
struct gdsys_ioep_priv *priv = dev_get_priv(dev);
|
||||
int k;
|
||||
int ret = -EIO;
|
||||
|
||||
for (k = 0; k < len; ++k) {
|
||||
u16 rx_tx_status;
|
||||
|
||||
gdsys_ioep_get(priv->map, receive_data, buffer++);
|
||||
|
||||
gdsys_ioep_get(priv->map, rx_tx_status, &rx_tx_status);
|
||||
/*
|
||||
* Sanity check: If the data read should have been the last,
|
||||
* but wasn't, something is wrong
|
||||
*/
|
||||
if (k == (len - 1) && (last_spec == READ_DATA_IS_NOT_LAST ||
|
||||
rx_tx_status & STATE_RX_DATA_LAST))
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
debug("%s: Error while receiving bufer (err = %d)\n",
|
||||
dev->name, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int gdsys_ioep_receive(struct udevice *dev, int offset, void *buf,
|
||||
int size)
|
||||
{
|
||||
int ret;
|
||||
struct io_generic_packet header;
|
||||
u16 *p = (u16 *)buf;
|
||||
const int header_words = sizeof(struct io_generic_packet) / sizeof(u16);
|
||||
uint len;
|
||||
|
||||
/* Read the packet header */
|
||||
ret = receive_byte_buffer(dev, header_words, p, READ_DATA_IS_NOT_LAST);
|
||||
if (ret) {
|
||||
debug("%s: Failed to read header data (err = %d)\n",
|
||||
dev->name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
memcpy(&header, p, header_words * sizeof(u16));
|
||||
p += header_words;
|
||||
|
||||
/* Get payload data length */
|
||||
len = (header.packet_length + 1) / sizeof(u16);
|
||||
|
||||
/* Read the packet payload */
|
||||
ret = receive_byte_buffer(dev, len, p, READ_DATA_IS_LAST);
|
||||
if (ret) {
|
||||
debug("%s: Failed to read payload data (err = %d)\n",
|
||||
dev->name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gdsys_ioep_get_and_reset_status(struct udevice *dev, int msgid,
|
||||
void *tx_msg, int tx_size,
|
||||
void *rx_msg, int rx_size)
|
||||
{
|
||||
struct gdsys_ioep_priv *priv = dev_get_priv(dev);
|
||||
const u16 mask = STATE_RX_DIST_ERR | STATE_RX_LENGTH_ERR |
|
||||
STATE_RX_FRAME_CTR_ERR | STATE_RX_FCS_ERR |
|
||||
STATE_RX_PACKET_DROPPED | STATE_TX_ERR;
|
||||
u16 *status = rx_msg;
|
||||
|
||||
gdsys_ioep_get(priv->map, rx_tx_status, status);
|
||||
|
||||
gdsys_ioep_set(priv->map, rx_tx_status, *status);
|
||||
|
||||
return (*status & mask) ? 1 : 0;
|
||||
}
|
||||
|
||||
static const struct misc_ops gdsys_ioep_ops = {
|
||||
.set_enabled = gdsys_ioep_set_receive,
|
||||
.write = gdsys_ioep_send,
|
||||
.read = gdsys_ioep_receive,
|
||||
.call = gdsys_ioep_get_and_reset_status,
|
||||
};
|
||||
|
||||
static int gdsys_ioep_probe(struct udevice *dev)
|
||||
{
|
||||
struct gdsys_ioep_priv *priv = dev_get_priv(dev);
|
||||
int ret;
|
||||
|
||||
ret = regmap_init_mem(dev_ofnode(dev), &priv->map);
|
||||
if (ret) {
|
||||
debug("%s: Could not initialize regmap (err = %d)",
|
||||
dev->name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
priv->state = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id gdsys_ioep_ids[] = {
|
||||
{ .compatible = "gdsys,io-endpoint" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(gdsys_ioep) = {
|
||||
.name = "gdsys_ioep",
|
||||
.id = UCLASS_MISC,
|
||||
.ops = &gdsys_ioep_ops,
|
||||
.flags = DM_UC_FLAG_SEQ_ALIAS,
|
||||
.of_match = gdsys_ioep_ids,
|
||||
.probe = gdsys_ioep_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct gdsys_ioep_priv),
|
||||
};
|
||||
@@ -0,0 +1,137 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* (C) Copyright 2018
|
||||
* Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
|
||||
*/
|
||||
|
||||
#ifndef __GDSYS_IOEP_H_
|
||||
#define __GDSYS_IOEP_H_
|
||||
|
||||
/**
|
||||
* struct io_generic_packet - header structure for GDSYS IOEP packets
|
||||
* @target_address: Target protocol address of the packet.
|
||||
* @source_address: Source protocol address of the packet.
|
||||
* @packet_type: Packet type.
|
||||
* @bc: Block counter (filled in by FPGA).
|
||||
* @packet_length: Length of the packet's payload bytes.
|
||||
*/
|
||||
struct io_generic_packet {
|
||||
u16 target_address;
|
||||
u16 source_address;
|
||||
u8 packet_type;
|
||||
u8 bc;
|
||||
u16 packet_length;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/**
|
||||
* struct gdsys_ioep_regs - Registers of a IOEP device
|
||||
* @transmit_data: Register that receives data to be sent
|
||||
* @tx_control: TX control register
|
||||
* @receive_data: Register filled with the received data
|
||||
* @rx_tx_status: RX/TX status register
|
||||
* @device_address: Register for setting/reading the device's address
|
||||
* @target_address: Register for setting/reading the remote endpoint's address
|
||||
* @int_enable: Interrupt/Interrupt enable register
|
||||
*/
|
||||
struct gdsys_ioep_regs {
|
||||
u16 transmit_data;
|
||||
u16 tx_control;
|
||||
u16 receive_data;
|
||||
u16 rx_tx_status;
|
||||
u16 device_address;
|
||||
u16 target_address;
|
||||
u16 int_enable;
|
||||
};
|
||||
|
||||
/**
|
||||
* gdsys_ioep_set() - Convenience macro to write registers of a IOEP device
|
||||
* @map: Register map to write the value in
|
||||
* @member: Name of the member in the gdsys_ioep_regs structure to write
|
||||
* @val: Value to write to the register
|
||||
*/
|
||||
#define gdsys_ioep_set(map, member, val) \
|
||||
regmap_set(map, struct gdsys_ioep_regs, member, val)
|
||||
|
||||
/**
|
||||
* gdsys_ioep_get() - Convenience macro to read registers of a IOEP device
|
||||
* @map: Register map to read the value from
|
||||
* @member: Name of the member in the gdsys_ioep_regs structure to read
|
||||
* @valp: Pointer to buffer to read the register value into
|
||||
*/
|
||||
#define gdsys_ioep_get(map, member, valp) \
|
||||
regmap_get(map, struct gdsys_ioep_regs, member, valp)
|
||||
|
||||
/**
|
||||
* enum rx_tx_status_values - Enum to describe the fields of the rx_tx_status
|
||||
* register
|
||||
* @STATE_TX_PACKET_BUILDING: The device is currently building a packet
|
||||
* (and accepting data for it)
|
||||
* @STATE_TX_TRANSMITTING: A packet is currenly being transmitted
|
||||
* @STATE_TX_BUFFER_FULL: The TX buffer is full
|
||||
* @STATE_TX_ERR: A TX error occurred
|
||||
* @STATE_RECEIVE_TIMEOUT: A receive timeout occurred
|
||||
* @STATE_PROC_RX_STORE_TIMEOUT: A RX store timeout for a processor packet
|
||||
* occurred
|
||||
* @STATE_PROC_RX_RECEIVE_TIMEOUT: A RX receive timeout for a processor packet
|
||||
* occurred
|
||||
* @STATE_RX_DIST_ERR: A error occurred in the distribution block
|
||||
* @STATE_RX_LENGTH_ERR: A length invalid error occurred
|
||||
* @STATE_RX_FRAME_CTR_ERR: A frame count error occurred (two
|
||||
* non-increasing frame count numbers
|
||||
* encountered)
|
||||
* @STATE_RX_FCS_ERR: A CRC error occurred
|
||||
* @STATE_RX_PACKET_DROPPED: A RX packet has been dropped
|
||||
* @STATE_RX_DATA_LAST: The data to be read is the final data of the
|
||||
* current packet
|
||||
* @STATE_RX_DATA_FIRST: The data to be read is the first data of the
|
||||
* current packet
|
||||
* @STATE_RX_DATA_AVAILABLE: RX data is available to be read
|
||||
*/
|
||||
enum rx_tx_status_values {
|
||||
STATE_TX_PACKET_BUILDING = BIT(0),
|
||||
STATE_TX_TRANSMITTING = BIT(1),
|
||||
STATE_TX_BUFFER_FULL = BIT(2),
|
||||
STATE_TX_ERR = BIT(3),
|
||||
STATE_RECEIVE_TIMEOUT = BIT(4),
|
||||
STATE_PROC_RX_STORE_TIMEOUT = BIT(5),
|
||||
STATE_PROC_RX_RECEIVE_TIMEOUT = BIT(6),
|
||||
STATE_RX_DIST_ERR = BIT(7),
|
||||
STATE_RX_LENGTH_ERR = BIT(8),
|
||||
STATE_RX_FRAME_CTR_ERR = BIT(9),
|
||||
STATE_RX_FCS_ERR = BIT(10),
|
||||
STATE_RX_PACKET_DROPPED = BIT(11),
|
||||
STATE_RX_DATA_LAST = BIT(12),
|
||||
STATE_RX_DATA_FIRST = BIT(13),
|
||||
STATE_RX_DATA_AVAILABLE = BIT(15),
|
||||
};
|
||||
|
||||
/**
|
||||
* enum tx_control_values - Enum to describe the fields of the tx_control
|
||||
* register
|
||||
* @CTRL_PROC_RECEIVE_ENABLE: Enable packet reception for the processor
|
||||
* @CTRL_FLUSH_TRANSMIT_BUFFER: Flush the transmit buffer (and send packet data)
|
||||
*/
|
||||
enum tx_control_values {
|
||||
CTRL_PROC_RECEIVE_ENABLE = BIT(12),
|
||||
CTRL_FLUSH_TRANSMIT_BUFFER = BIT(15),
|
||||
};
|
||||
|
||||
/**
|
||||
* enum int_enable_values - Enum to describe the fields of the int_enable
|
||||
* register
|
||||
* @IRQ_CPU_TRANSMITBUFFER_FREE_STATUS: The transmit buffer is free (packet
|
||||
* data can be transmitted to the
|
||||
* device)
|
||||
* @IRQ_CPU_PACKET_TRANSMITTED_EVENT: A packet has been transmitted
|
||||
* @IRQ_NEW_CPU_PACKET_RECEIVED_EVENT: A new packet has been received
|
||||
* @IRQ_CPU_RECEIVE_DATA_AVAILABLE_STATUS: RX packet data are available to be
|
||||
* read
|
||||
*/
|
||||
enum int_enable_values {
|
||||
IRQ_CPU_TRANSMITBUFFER_FREE_STATUS = BIT(5),
|
||||
IRQ_CPU_PACKET_TRANSMITTED_EVENT = BIT(6),
|
||||
IRQ_NEW_CPU_PACKET_RECEIVED_EVENT = BIT(7),
|
||||
IRQ_CPU_RECEIVE_DATA_AVAILABLE_STATUS = BIT(8),
|
||||
};
|
||||
|
||||
#endif /* __GDSYS_IOEP_H_ */
|
||||
@@ -0,0 +1,81 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2015
|
||||
* Dirk Eibach, Guntermann & Drunck GmbH, eibach@gdsys.de
|
||||
*
|
||||
* (C) Copyright 2017
|
||||
* Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <regmap.h>
|
||||
#include <misc.h>
|
||||
|
||||
struct gdsys_rxaui_ctrl_regs {
|
||||
u16 gen_cnt;
|
||||
u16 err_cnt;
|
||||
u16 succ_cnt;
|
||||
u16 status;
|
||||
u16 ctrl_0;
|
||||
u16 ctrl_1;
|
||||
};
|
||||
|
||||
#define rxaui_ctrl_set(map, member, val) \
|
||||
regmap_set(map, struct gdsys_rxaui_ctrl_regs, member, val)
|
||||
|
||||
#define rxaui_ctrl_get(map, member, valp) \
|
||||
regmap_get(map, struct gdsys_rxaui_ctrl_regs, member, valp)
|
||||
|
||||
struct gdsys_rxaui_ctrl_priv {
|
||||
struct regmap *map;
|
||||
bool state;
|
||||
};
|
||||
|
||||
int gdsys_rxaui_set_polarity_inversion(struct udevice *dev, bool val)
|
||||
{
|
||||
struct gdsys_rxaui_ctrl_priv *priv = dev_get_priv(dev);
|
||||
u16 state;
|
||||
|
||||
priv->state = !priv->state;
|
||||
|
||||
rxaui_ctrl_get(priv->map, ctrl_1, &state);
|
||||
|
||||
if (val)
|
||||
state |= ~0x7800;
|
||||
else
|
||||
state &= ~0x7800;
|
||||
|
||||
rxaui_ctrl_set(priv->map, ctrl_1, state);
|
||||
|
||||
return !priv->state;
|
||||
}
|
||||
|
||||
static const struct misc_ops gdsys_rxaui_ctrl_ops = {
|
||||
.set_enabled = gdsys_rxaui_set_polarity_inversion,
|
||||
};
|
||||
|
||||
int gdsys_rxaui_ctrl_probe(struct udevice *dev)
|
||||
{
|
||||
struct gdsys_rxaui_ctrl_priv *priv = dev_get_priv(dev);
|
||||
|
||||
regmap_init_mem(dev_ofnode(dev), &priv->map);
|
||||
|
||||
priv->state = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id gdsys_rxaui_ctrl_ids[] = {
|
||||
{ .compatible = "gdsys,rxaui_ctrl" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(gdsys_rxaui_ctrl) = {
|
||||
.name = "gdsys_rxaui_ctrl",
|
||||
.id = UCLASS_MISC,
|
||||
.ops = &gdsys_rxaui_ctrl_ops,
|
||||
.of_match = gdsys_rxaui_ctrl_ids,
|
||||
.probe = gdsys_rxaui_ctrl_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct gdsys_rxaui_ctrl_priv),
|
||||
};
|
||||
@@ -0,0 +1,74 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2017
|
||||
* Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <dm/lists.h>
|
||||
|
||||
#include "gdsys_soc.h"
|
||||
|
||||
/**
|
||||
* struct gdsys_soc_priv - Private data for gdsys soc bus
|
||||
* @fpga: The gdsys IHS FPGA this bus is associated with
|
||||
*/
|
||||
struct gdsys_soc_priv {
|
||||
struct udevice *fpga;
|
||||
};
|
||||
|
||||
static const struct udevice_id gdsys_soc_ids[] = {
|
||||
{ .compatible = "gdsys,soc" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
int gdsys_soc_get_fpga(struct udevice *child, struct udevice **fpga)
|
||||
{
|
||||
struct gdsys_soc_priv *bus_priv;
|
||||
|
||||
if (!child->parent) {
|
||||
debug("%s: Invalid parent\n", child->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!device_is_compatible(child->parent, "gdsys,soc")) {
|
||||
debug("%s: Not child of a gdsys soc\n", child->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
bus_priv = dev_get_priv(child->parent);
|
||||
|
||||
*fpga = bus_priv->fpga;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gdsys_soc_probe(struct udevice *dev)
|
||||
{
|
||||
struct gdsys_soc_priv *priv = dev_get_priv(dev);
|
||||
struct udevice *fpga;
|
||||
int res = uclass_get_device_by_phandle(UCLASS_MISC, dev, "fpga",
|
||||
&fpga);
|
||||
if (res == -ENOENT) {
|
||||
debug("%s: Could not find 'fpga' phandle\n", dev->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (res == -ENODEV) {
|
||||
debug("%s: Could not get FPGA device\n", dev->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
priv->fpga = fpga;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
U_BOOT_DRIVER(gdsys_soc_bus) = {
|
||||
.name = "gdsys_soc_bus",
|
||||
.id = UCLASS_SIMPLE_BUS,
|
||||
.of_match = gdsys_soc_ids,
|
||||
.probe = gdsys_soc_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct gdsys_soc_priv),
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* (C) Copyright 2017
|
||||
* Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
|
||||
*/
|
||||
|
||||
#ifndef _GDSYS_SOC_H_
|
||||
#define _GDSYS_SOC_H_
|
||||
|
||||
/**
|
||||
* gdsys_soc_get_fpga() - Retrieve pointer to parent bus' FPGA device
|
||||
* @child: The child device on the FPGA bus needing access to the FPGA.
|
||||
* @fpga: Pointer to the retrieved FPGA device.
|
||||
*
|
||||
* To access their register maps, devices on gdsys soc buses usually have
|
||||
* facilitate the accessor function of the IHS FPGA their parent bus is
|
||||
* attached to. To access the FPGA device from within the bus' children, this
|
||||
* function returns a pointer to it.
|
||||
*
|
||||
* Return: 0 on success, -ve on failure
|
||||
*/
|
||||
int gdsys_soc_get_fpga(struct udevice *child, struct udevice **fpga);
|
||||
#endif /* _GDSYS_SOC_H_ */
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Status LED driver based on GPIO access conventions of Linux
|
||||
*
|
||||
* Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw>
|
||||
* Licensed under the GPL-2 or later.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <status_led.h>
|
||||
#include <asm/gpio.h>
|
||||
|
||||
#ifndef CONFIG_GPIO_LED_INVERTED_TABLE
|
||||
#define CONFIG_GPIO_LED_INVERTED_TABLE {}
|
||||
#endif
|
||||
|
||||
static led_id_t gpio_led_inv[] = CONFIG_GPIO_LED_INVERTED_TABLE;
|
||||
|
||||
static int gpio_led_gpio_value(led_id_t mask, int state)
|
||||
{
|
||||
int i, gpio_value = (state == CONFIG_LED_STATUS_ON);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(gpio_led_inv); i++) {
|
||||
if (gpio_led_inv[i] == mask)
|
||||
gpio_value = !gpio_value;
|
||||
}
|
||||
|
||||
return gpio_value;
|
||||
}
|
||||
|
||||
void __led_init(led_id_t mask, int state)
|
||||
{
|
||||
int gpio_value;
|
||||
|
||||
if (gpio_request(mask, "gpio_led") != 0) {
|
||||
printf("%s: failed requesting GPIO%lu!\n", __func__, mask);
|
||||
return;
|
||||
}
|
||||
|
||||
gpio_value = gpio_led_gpio_value(mask, state);
|
||||
gpio_direction_output(mask, gpio_value);
|
||||
}
|
||||
|
||||
void __led_set(led_id_t mask, int state)
|
||||
{
|
||||
int gpio_value = gpio_led_gpio_value(mask, state);
|
||||
|
||||
gpio_set_value(mask, gpio_value);
|
||||
}
|
||||
|
||||
void __led_toggle(led_id_t mask)
|
||||
{
|
||||
gpio_set_value(mask, !gpio_get_value(mask));
|
||||
}
|
||||
|
||||
#ifdef CONFIG_GPIO_LED_STUBS
|
||||
|
||||
/* 'generic' override of colored LED stubs, to use GPIO functions instead */
|
||||
|
||||
#ifdef CONFIG_LED_STATUS_RED
|
||||
void red_led_on(void)
|
||||
{
|
||||
__led_set(CONFIG_LED_STATUS_RED, CONFIG_LED_STATUS_ON);
|
||||
}
|
||||
|
||||
void red_led_off(void)
|
||||
{
|
||||
__led_set(CONFIG_LED_STATUS_RED, CONFIG_LED_STATUS_OFF);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LED_STATUS_GREEN
|
||||
void green_led_on(void)
|
||||
{
|
||||
__led_set(CONFIG_LED_STATUS_GREEN, CONFIG_LED_STATUS_ON);
|
||||
}
|
||||
|
||||
void green_led_off(void)
|
||||
{
|
||||
__led_set(CONFIG_LED_STATUS_GREEN, CONFIG_LED_STATUS_OFF);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LED_STATUS_YELLOW
|
||||
void yellow_led_on(void)
|
||||
{
|
||||
__led_set(CONFIG_LED_STATUS_YELLOW, CONFIG_LED_STATUS_ON);
|
||||
}
|
||||
|
||||
void yellow_led_off(void)
|
||||
{
|
||||
__led_set(CONFIG_LED_STATUS_YELLOW, CONFIG_LED_STATUS_OFF);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LED_STATUS_BLUE
|
||||
void blue_led_on(void)
|
||||
{
|
||||
__led_set(CONFIG_LED_STATUS_BLUE, CONFIG_LED_STATUS_ON);
|
||||
}
|
||||
|
||||
void blue_led_off(void)
|
||||
{
|
||||
__led_set(CONFIG_LED_STATUS_BLUE, CONFIG_LED_STATUS_OFF);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_GPIO_LED_STUBS */
|
||||
@@ -0,0 +1,130 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (c) 2014 Google, Inc
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <eeprom.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <dm.h>
|
||||
#include <i2c.h>
|
||||
#include <i2c_eeprom.h>
|
||||
|
||||
int i2c_eeprom_read(struct udevice *dev, int offset, uint8_t *buf, int size)
|
||||
{
|
||||
const struct i2c_eeprom_ops *ops = device_get_ops(dev);
|
||||
|
||||
if (!ops->read)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->read(dev, offset, buf, size);
|
||||
}
|
||||
|
||||
int i2c_eeprom_write(struct udevice *dev, int offset, uint8_t *buf, int size)
|
||||
{
|
||||
const struct i2c_eeprom_ops *ops = device_get_ops(dev);
|
||||
|
||||
if (!ops->write)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->write(dev, offset, buf, size);
|
||||
}
|
||||
|
||||
static int i2c_eeprom_std_read(struct udevice *dev, int offset, uint8_t *buf,
|
||||
int size)
|
||||
{
|
||||
return dm_i2c_read(dev, offset, buf, size);
|
||||
}
|
||||
|
||||
static int i2c_eeprom_std_write(struct udevice *dev, int offset,
|
||||
const uint8_t *buf, int size)
|
||||
{
|
||||
struct i2c_eeprom *priv = dev_get_priv(dev);
|
||||
int ret;
|
||||
|
||||
while (size > 0) {
|
||||
int write_size = min_t(int, size, priv->pagesize);
|
||||
|
||||
ret = dm_i2c_write(dev, offset, buf, write_size);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
offset += write_size;
|
||||
buf += write_size;
|
||||
size -= write_size;
|
||||
|
||||
udelay(10000);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_eeprom_ops i2c_eeprom_std_ops = {
|
||||
.read = i2c_eeprom_std_read,
|
||||
.write = i2c_eeprom_std_write,
|
||||
};
|
||||
|
||||
static int i2c_eeprom_std_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct i2c_eeprom *priv = dev_get_priv(dev);
|
||||
u64 data = dev_get_driver_data(dev);
|
||||
u32 pagesize;
|
||||
|
||||
if (dev_read_u32(dev, "pagesize", &pagesize) == 0) {
|
||||
priv->pagesize = pagesize;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 6 bit -> page size of up to 2^63 (should be sufficient) */
|
||||
priv->pagewidth = data & 0x3F;
|
||||
priv->pagesize = (1 << priv->pagewidth);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_eeprom_std_probe(struct udevice *dev)
|
||||
{
|
||||
u8 test_byte;
|
||||
int ret;
|
||||
|
||||
/* Verify that the chip is functional */
|
||||
ret = i2c_eeprom_read(dev, 0, &test_byte, 1);
|
||||
if (ret)
|
||||
return -ENODEV;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id i2c_eeprom_std_ids[] = {
|
||||
{ .compatible = "i2c-eeprom", .data = 0 },
|
||||
{ .compatible = "microchip,24aa02e48", .data = 3 },
|
||||
{ .compatible = "atmel,24c01a", .data = 3 },
|
||||
{ .compatible = "atmel,24c02", .data = 3 },
|
||||
{ .compatible = "atmel,24c04", .data = 4 },
|
||||
{ .compatible = "atmel,24c08", .data = 4 },
|
||||
{ .compatible = "atmel,24c08a", .data = 4 },
|
||||
{ .compatible = "atmel,24c16a", .data = 4 },
|
||||
{ .compatible = "atmel,24mac402", .data = 4 },
|
||||
{ .compatible = "atmel,24c32", .data = 5 },
|
||||
{ .compatible = "atmel,24c64", .data = 5 },
|
||||
{ .compatible = "atmel,24c128", .data = 6 },
|
||||
{ .compatible = "atmel,24c256", .data = 6 },
|
||||
{ .compatible = "atmel,24c512", .data = 6 },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(i2c_eeprom_std) = {
|
||||
.name = "i2c_eeprom",
|
||||
.id = UCLASS_I2C_EEPROM,
|
||||
.of_match = i2c_eeprom_std_ids,
|
||||
.probe = i2c_eeprom_std_probe,
|
||||
.ofdata_to_platdata = i2c_eeprom_std_ofdata_to_platdata,
|
||||
.priv_auto_alloc_size = sizeof(struct i2c_eeprom),
|
||||
.ops = &i2c_eeprom_std_ops,
|
||||
};
|
||||
|
||||
UCLASS_DRIVER(i2c_eeprom) = {
|
||||
.id = UCLASS_I2C_EEPROM,
|
||||
.name = "i2c_eeprom",
|
||||
};
|
||||
@@ -0,0 +1,163 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Simulate an I2C eeprom
|
||||
*
|
||||
* Copyright (c) 2014 Google, Inc
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <i2c.h>
|
||||
#include <malloc.h>
|
||||
#include <asm/test.h>
|
||||
|
||||
#ifdef DEBUG
|
||||
#define debug_buffer print_buffer
|
||||
#else
|
||||
#define debug_buffer(x, ...)
|
||||
#endif
|
||||
|
||||
struct sandbox_i2c_flash_plat_data {
|
||||
enum sandbox_i2c_eeprom_test_mode test_mode;
|
||||
const char *filename;
|
||||
int offset_len; /* Length of an offset in bytes */
|
||||
int size; /* Size of data buffer */
|
||||
};
|
||||
|
||||
struct sandbox_i2c_flash {
|
||||
uint8_t *data;
|
||||
};
|
||||
|
||||
void sandbox_i2c_eeprom_set_test_mode(struct udevice *dev,
|
||||
enum sandbox_i2c_eeprom_test_mode mode)
|
||||
{
|
||||
struct sandbox_i2c_flash_plat_data *plat = dev_get_platdata(dev);
|
||||
|
||||
plat->test_mode = mode;
|
||||
}
|
||||
|
||||
void sandbox_i2c_eeprom_set_offset_len(struct udevice *dev, int offset_len)
|
||||
{
|
||||
struct sandbox_i2c_flash_plat_data *plat = dev_get_platdata(dev);
|
||||
|
||||
plat->offset_len = offset_len;
|
||||
}
|
||||
|
||||
static int sandbox_i2c_eeprom_xfer(struct udevice *emul, struct i2c_msg *msg,
|
||||
int nmsgs)
|
||||
{
|
||||
struct sandbox_i2c_flash *priv = dev_get_priv(emul);
|
||||
uint offset = 0;
|
||||
|
||||
debug("\n%s\n", __func__);
|
||||
debug_buffer(0, priv->data, 1, 16, 0);
|
||||
for (; nmsgs > 0; nmsgs--, msg++) {
|
||||
struct sandbox_i2c_flash_plat_data *plat =
|
||||
dev_get_platdata(emul);
|
||||
int len;
|
||||
u8 *ptr;
|
||||
|
||||
if (!plat->size)
|
||||
return -ENODEV;
|
||||
if (msg->addr + msg->len > plat->size) {
|
||||
debug("%s: Address %x, len %x is outside range 0..%x\n",
|
||||
__func__, msg->addr, msg->len, plat->size);
|
||||
return -EINVAL;
|
||||
}
|
||||
len = msg->len;
|
||||
debug(" %s: msg->len=%d",
|
||||
msg->flags & I2C_M_RD ? "read" : "write",
|
||||
msg->len);
|
||||
if (msg->flags & I2C_M_RD) {
|
||||
if (plat->test_mode == SIE_TEST_MODE_SINGLE_BYTE)
|
||||
len = 1;
|
||||
debug(", offset %x, len %x: ", offset, len);
|
||||
memcpy(msg->buf, priv->data + offset, len);
|
||||
memset(msg->buf + len, '\xff', msg->len - len);
|
||||
debug_buffer(0, msg->buf, 1, msg->len, 0);
|
||||
} else if (len >= plat->offset_len) {
|
||||
int i;
|
||||
|
||||
ptr = msg->buf;
|
||||
for (i = 0; i < plat->offset_len; i++, len--)
|
||||
offset = (offset << 8) | *ptr++;
|
||||
debug(", set offset %x: ", offset);
|
||||
debug_buffer(0, msg->buf, 1, msg->len, 0);
|
||||
if (plat->test_mode == SIE_TEST_MODE_SINGLE_BYTE)
|
||||
len = min(len, 1);
|
||||
|
||||
/* For testing, map offsets into our limited buffer */
|
||||
for (i = 24; i > 0; i -= 8) {
|
||||
if (offset > (1 << i)) {
|
||||
offset = (offset >> i) |
|
||||
(offset & ((1 << i) - 1));
|
||||
offset += i;
|
||||
}
|
||||
}
|
||||
memcpy(priv->data + offset, ptr, len);
|
||||
}
|
||||
}
|
||||
debug_buffer(0, priv->data, 1, 16, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct dm_i2c_ops sandbox_i2c_emul_ops = {
|
||||
.xfer = sandbox_i2c_eeprom_xfer,
|
||||
};
|
||||
|
||||
static int sandbox_i2c_eeprom_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct sandbox_i2c_flash_plat_data *plat = dev_get_platdata(dev);
|
||||
|
||||
plat->size = dev_read_u32_default(dev, "sandbox,size", 32);
|
||||
plat->filename = dev_read_string(dev, "sandbox,filename");
|
||||
if (!plat->filename) {
|
||||
debug("%s: No filename for device '%s'\n", __func__,
|
||||
dev->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
plat->test_mode = SIE_TEST_MODE_NONE;
|
||||
plat->offset_len = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sandbox_i2c_eeprom_probe(struct udevice *dev)
|
||||
{
|
||||
struct sandbox_i2c_flash_plat_data *plat = dev_get_platdata(dev);
|
||||
struct sandbox_i2c_flash *priv = dev_get_priv(dev);
|
||||
|
||||
priv->data = calloc(1, plat->size);
|
||||
if (!priv->data)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sandbox_i2c_eeprom_remove(struct udevice *dev)
|
||||
{
|
||||
struct sandbox_i2c_flash *priv = dev_get_priv(dev);
|
||||
|
||||
free(priv->data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id sandbox_i2c_ids[] = {
|
||||
{ .compatible = "sandbox,i2c-eeprom" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(sandbox_i2c_emul) = {
|
||||
.name = "sandbox_i2c_eeprom_emul",
|
||||
.id = UCLASS_I2C_EMUL,
|
||||
.of_match = sandbox_i2c_ids,
|
||||
.ofdata_to_platdata = sandbox_i2c_eeprom_ofdata_to_platdata,
|
||||
.probe = sandbox_i2c_eeprom_probe,
|
||||
.remove = sandbox_i2c_eeprom_remove,
|
||||
.priv_auto_alloc_size = sizeof(struct sandbox_i2c_flash),
|
||||
.platdata_auto_alloc_size = sizeof(struct sandbox_i2c_flash_plat_data),
|
||||
.ops = &sandbox_i2c_emul_ops,
|
||||
};
|
||||
@@ -0,0 +1,867 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2017
|
||||
* Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
|
||||
*
|
||||
* based on the ioep-fpga driver, which is
|
||||
*
|
||||
* (C) Copyright 2014
|
||||
* Dirk Eibach, Guntermann & Drunck GmbH, eibach@gdsys.de
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <regmap.h>
|
||||
#include <asm/gpio.h>
|
||||
|
||||
#include "ihs_fpga.h"
|
||||
|
||||
/**
|
||||
* struct ihs_fpga_priv - Private data structure for IHS FPGA driver
|
||||
* @map: Register map for the FPGA's own register space
|
||||
* @reset_gpio: GPIO to start FPGA reconfiguration
|
||||
* @done_gpio: GPOI to read the 'ready' status of the FPGA
|
||||
*/
|
||||
struct ihs_fpga_priv {
|
||||
struct regmap *map;
|
||||
struct gpio_desc reset_gpio;
|
||||
struct gpio_desc done_gpio;
|
||||
};
|
||||
|
||||
/* Test pattern for reflection test */
|
||||
const u16 REFLECTION_TESTPATTERN = 0xdead;
|
||||
/* Delay (in ms) for each round in the reflection test */
|
||||
const uint REFLECTION_TEST_DELAY = 100;
|
||||
/* Maximum number of rounds in the reflection test */
|
||||
const uint REFLECTION_TEST_ROUNDS = 5;
|
||||
/* Delay (in ms) for each round waiting for the FPGA's done GPIO */
|
||||
const uint FPGA_DONE_WAIT_DELAY = 100;
|
||||
/* Maximum number of rounds for waiting for the FPGA's done GPIO */
|
||||
const uint FPGA_DONE_WAIT_ROUND = 5;
|
||||
|
||||
/**
|
||||
* enum pcb_video_type - Video type of the PCB
|
||||
* @PCB_DVI_SL: Video type is DVI single-link
|
||||
* @PCB_DP_165MPIX: Video type is DisplayPort (165Mpix)
|
||||
* @PCB_DP_300MPIX: Video type is DisplayPort (300Mpix)
|
||||
* @PCB_HDMI: Video type is HDMI
|
||||
* @PCB_DP_1_2: Video type is DisplayPort 1.2
|
||||
* @PCB_HDMI_2_0: Video type is HDMI 2.0
|
||||
*/
|
||||
enum pcb_video_type {
|
||||
PCB_DVI_SL,
|
||||
PCB_DP_165MPIX,
|
||||
PCB_DP_300MPIX,
|
||||
PCB_HDMI,
|
||||
PCB_DP_1_2,
|
||||
PCB_HDMI_2_0,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum pcb_transmission_type - Transmission type of the PCB
|
||||
* @PCB_CAT_1G: Transmission type is 1G Ethernet
|
||||
* @PCB_FIBER_3G: Transmission type is 3G Fiber
|
||||
* @PCB_CAT_10G: Transmission type is 10G Ethernet
|
||||
* @PCB_FIBER_10G: Transmission type is 10G Fiber
|
||||
*/
|
||||
enum pcb_transmission_type {
|
||||
PCB_CAT_1G,
|
||||
PCB_FIBER_3G,
|
||||
PCB_CAT_10G,
|
||||
PCB_FIBER_10G,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum carrier_speed - Speed of the FPGA's carrier
|
||||
* @CARRIER_SPEED_1G: The carrier speed is 1G
|
||||
* @CARRIER_SPEED_2_5G: The carrier speed is 2.5G
|
||||
* @CARRIER_SPEED_3G: The carrier speed is 3G
|
||||
* @CARRIER_SPEED_10G: The carrier speed is 10G
|
||||
*/
|
||||
enum carrier_speed {
|
||||
CARRIER_SPEED_1G,
|
||||
CARRIER_SPEED_3G,
|
||||
CARRIER_SPEED_2_5G = CARRIER_SPEED_3G,
|
||||
CARRIER_SPEED_10G,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum ram_config - FPGA's RAM configuration
|
||||
* @RAM_DDR2_32BIT_295MBPS: DDR2 32 bit at 295Mb/s
|
||||
* @RAM_DDR3_32BIT_590MBPS: DDR3 32 bit at 590Mb/s
|
||||
* @RAM_DDR3_48BIT_590MBPS: DDR3 48 bit at 590Mb/s
|
||||
* @RAM_DDR3_64BIT_1800MBPS: DDR3 64 bit at 1800Mb/s
|
||||
* @RAM_DDR3_48BIT_1800MBPS: DDR3 48 bit at 1800Mb/s
|
||||
*/
|
||||
enum ram_config {
|
||||
RAM_DDR2_32BIT_295MBPS,
|
||||
RAM_DDR3_32BIT_590MBPS,
|
||||
RAM_DDR3_48BIT_590MBPS,
|
||||
RAM_DDR3_64BIT_1800MBPS,
|
||||
RAM_DDR3_48BIT_1800MBPS,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum sysclock - Speed of the FPGA's system clock
|
||||
* @SYSCLK_147456: System clock is 147.456 MHz
|
||||
*/
|
||||
enum sysclock {
|
||||
SYSCLK_147456,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct fpga_versions - Data read from the versions register
|
||||
* @video_channel: Is the FPGA for a video channel (true) or main
|
||||
* channel (false) device?
|
||||
* @con_side: Is the FPGA for a CON (true) or a CPU (false) device?
|
||||
* @pcb_video_type: Defines for whch video type the FPGA is configured
|
||||
* @pcb_transmission_type: Defines for which transmission type the FPGA is
|
||||
* configured
|
||||
* @hw_version: Hardware version of the FPGA
|
||||
*/
|
||||
struct fpga_versions {
|
||||
bool video_channel;
|
||||
bool con_side;
|
||||
enum pcb_video_type pcb_video_type;
|
||||
enum pcb_transmission_type pcb_transmission_type;
|
||||
unsigned int hw_version;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct fpga_features - Data read from the features register
|
||||
* @video_channels: Number of video channels supported
|
||||
* @carriers: Number of carrier channels supported
|
||||
* @carrier_speed: Speed of carriers
|
||||
* @ram_config: RAM configuration of FPGA
|
||||
* @sysclock: System clock speed of FPGA
|
||||
* @pcm_tx: Support for PCM transmission
|
||||
* @pcm_rx: Support for PCM reception
|
||||
* @spdif_tx: Support for SPDIF audio transmission
|
||||
* @spdif_rx: Support for SPDIF audio reception
|
||||
* @usb2: Support for transparent USB2.0
|
||||
* @rs232: Support for bidirectional RS232
|
||||
* @compression_type1: Support for compression type 1
|
||||
* @compression_type2: Support for compression type 2
|
||||
* @compression_type3: Support for compression type 3
|
||||
* @interlace: Support for interlace image formats
|
||||
* @osd: Support for a OSD
|
||||
* @compression_pipes: Number of compression pipes supported
|
||||
*/
|
||||
struct fpga_features {
|
||||
u8 video_channels;
|
||||
u8 carriers;
|
||||
enum carrier_speed carrier_speed;
|
||||
enum ram_config ram_config;
|
||||
enum sysclock sysclock;
|
||||
bool pcm_tx;
|
||||
bool pcm_rx;
|
||||
bool spdif_tx;
|
||||
bool spdif_rx;
|
||||
bool usb2;
|
||||
bool rs232;
|
||||
bool compression_type1;
|
||||
bool compression_type2;
|
||||
bool compression_type3;
|
||||
bool interlace;
|
||||
bool osd;
|
||||
bool compression_pipes;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SYS_FPGA_FLAVOR_GAZERBEAM
|
||||
|
||||
/**
|
||||
* get_versions() - Fill structure with info from version register.
|
||||
* @dev: FPGA device to be queried for information
|
||||
* @versions: Pointer to the structure to fill with information from the
|
||||
* versions register
|
||||
* Return: 0
|
||||
*/
|
||||
static int get_versions(struct udevice *dev, struct fpga_versions *versions)
|
||||
{
|
||||
struct ihs_fpga_priv *priv = dev_get_priv(dev);
|
||||
enum {
|
||||
VERSIONS_FPGA_VIDEO_CHANNEL = BIT(12),
|
||||
VERSIONS_FPGA_CON_SIDE = BIT(13),
|
||||
VERSIONS_FPGA_SC = BIT(14),
|
||||
VERSIONS_PCB_CON = BIT(9),
|
||||
VERSIONS_PCB_SC = BIT(8),
|
||||
VERSIONS_PCB_VIDEO_MASK = 0x3 << 6,
|
||||
VERSIONS_PCB_VIDEO_DP_1_2 = 0x0 << 6,
|
||||
VERSIONS_PCB_VIDEO_HDMI_2_0 = 0x1 << 6,
|
||||
VERSIONS_PCB_TRANSMISSION_MASK = 0x3 << 4,
|
||||
VERSIONS_PCB_TRANSMISSION_FIBER_10G = 0x0 << 4,
|
||||
VERSIONS_PCB_TRANSMISSION_CAT_10G = 0x1 << 4,
|
||||
VERSIONS_PCB_TRANSMISSION_FIBER_3G = 0x2 << 4,
|
||||
VERSIONS_PCB_TRANSMISSION_CAT_1G = 0x3 << 4,
|
||||
VERSIONS_HW_VER_MASK = 0xf << 0,
|
||||
};
|
||||
u16 raw_versions;
|
||||
|
||||
memset(versions, 0, sizeof(struct fpga_versions));
|
||||
|
||||
ihs_fpga_get(priv->map, versions, &raw_versions);
|
||||
|
||||
versions->video_channel = raw_versions & VERSIONS_FPGA_VIDEO_CHANNEL;
|
||||
versions->con_side = raw_versions & VERSIONS_FPGA_CON_SIDE;
|
||||
|
||||
switch (raw_versions & VERSIONS_PCB_VIDEO_MASK) {
|
||||
case VERSIONS_PCB_VIDEO_DP_1_2:
|
||||
versions->pcb_video_type = PCB_DP_1_2;
|
||||
break;
|
||||
|
||||
case VERSIONS_PCB_VIDEO_HDMI_2_0:
|
||||
versions->pcb_video_type = PCB_HDMI_2_0;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (raw_versions & VERSIONS_PCB_TRANSMISSION_MASK) {
|
||||
case VERSIONS_PCB_TRANSMISSION_FIBER_10G:
|
||||
versions->pcb_transmission_type = PCB_FIBER_10G;
|
||||
break;
|
||||
|
||||
case VERSIONS_PCB_TRANSMISSION_CAT_10G:
|
||||
versions->pcb_transmission_type = PCB_CAT_10G;
|
||||
break;
|
||||
|
||||
case VERSIONS_PCB_TRANSMISSION_FIBER_3G:
|
||||
versions->pcb_transmission_type = PCB_FIBER_3G;
|
||||
break;
|
||||
|
||||
case VERSIONS_PCB_TRANSMISSION_CAT_1G:
|
||||
versions->pcb_transmission_type = PCB_CAT_1G;
|
||||
break;
|
||||
}
|
||||
|
||||
versions->hw_version = raw_versions & VERSIONS_HW_VER_MASK;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* get_features() - Fill structure with info from features register.
|
||||
* @dev: FPGA device to be queried for information
|
||||
* @features: Pointer to the structure to fill with information from the
|
||||
* features register
|
||||
* Return: 0
|
||||
*/
|
||||
static int get_features(struct udevice *dev, struct fpga_features *features)
|
||||
{
|
||||
struct ihs_fpga_priv *priv = dev_get_priv(dev);
|
||||
enum {
|
||||
FEATURE_SPDIF_RX = BIT(15),
|
||||
FEATURE_SPDIF_TX = BIT(14),
|
||||
FEATURE_PCM_RX = BIT(13),
|
||||
FEATURE_PCM_TX = BIT(12),
|
||||
FEATURE_RAM_MASK = GENMASK(11, 8),
|
||||
FEATURE_RAM_DDR2_32BIT_295MBPS = 0x0 << 8,
|
||||
FEATURE_RAM_DDR3_32BIT_590MBPS = 0x1 << 8,
|
||||
FEATURE_RAM_DDR3_48BIT_590MBPS = 0x2 << 8,
|
||||
FEATURE_RAM_DDR3_64BIT_1800MBPS = 0x3 << 8,
|
||||
FEATURE_RAM_DDR3_48BIT_1800MBPS = 0x4 << 8,
|
||||
FEATURE_CARRIER_SPEED_MASK = GENMASK(7, 6),
|
||||
FEATURE_CARRIER_SPEED_1G = 0x0 << 6,
|
||||
FEATURE_CARRIER_SPEED_2_5G = 0x1 << 6,
|
||||
FEATURE_CARRIER_SPEED_10G = 0x2 << 6,
|
||||
FEATURE_CARRIERS_MASK = GENMASK(5, 4),
|
||||
FEATURE_CARRIERS_0 = 0x0 << 4,
|
||||
FEATURE_CARRIERS_1 = 0x1 << 4,
|
||||
FEATURE_CARRIERS_2 = 0x2 << 4,
|
||||
FEATURE_CARRIERS_4 = 0x3 << 4,
|
||||
FEATURE_USB2 = BIT(3),
|
||||
FEATURE_VIDEOCHANNELS_MASK = GENMASK(2, 0),
|
||||
FEATURE_VIDEOCHANNELS_0 = 0x0 << 0,
|
||||
FEATURE_VIDEOCHANNELS_1 = 0x1 << 0,
|
||||
FEATURE_VIDEOCHANNELS_1_1 = 0x2 << 0,
|
||||
FEATURE_VIDEOCHANNELS_2 = 0x3 << 0,
|
||||
};
|
||||
|
||||
enum {
|
||||
EXT_FEATURE_OSD = BIT(15),
|
||||
EXT_FEATURE_ETHERNET = BIT(9),
|
||||
EXT_FEATURE_INTERLACE = BIT(8),
|
||||
EXT_FEATURE_RS232 = BIT(7),
|
||||
EXT_FEATURE_COMPRESSION_PERF_MASK = GENMASK(6, 4),
|
||||
EXT_FEATURE_COMPRESSION_PERF_1X = 0x0 << 4,
|
||||
EXT_FEATURE_COMPRESSION_PERF_2X = 0x1 << 4,
|
||||
EXT_FEATURE_COMPRESSION_PERF_4X = 0x2 << 4,
|
||||
EXT_FEATURE_COMPRESSION_TYPE1 = BIT(0),
|
||||
EXT_FEATURE_COMPRESSION_TYPE2 = BIT(1),
|
||||
EXT_FEATURE_COMPRESSION_TYPE3 = BIT(2),
|
||||
};
|
||||
|
||||
u16 raw_features;
|
||||
u16 raw_extended_features;
|
||||
|
||||
memset(features, 0, sizeof(struct fpga_features));
|
||||
|
||||
ihs_fpga_get(priv->map, features, &raw_features);
|
||||
ihs_fpga_get(priv->map, extended_features, &raw_extended_features);
|
||||
|
||||
switch (raw_features & FEATURE_VIDEOCHANNELS_MASK) {
|
||||
case FEATURE_VIDEOCHANNELS_0:
|
||||
features->video_channels = 0;
|
||||
break;
|
||||
|
||||
case FEATURE_VIDEOCHANNELS_1:
|
||||
features->video_channels = 1;
|
||||
break;
|
||||
|
||||
case FEATURE_VIDEOCHANNELS_1_1:
|
||||
case FEATURE_VIDEOCHANNELS_2:
|
||||
features->video_channels = 2;
|
||||
break;
|
||||
};
|
||||
|
||||
switch (raw_features & FEATURE_CARRIERS_MASK) {
|
||||
case FEATURE_CARRIERS_0:
|
||||
features->carriers = 0;
|
||||
break;
|
||||
|
||||
case FEATURE_CARRIERS_1:
|
||||
features->carriers = 1;
|
||||
break;
|
||||
|
||||
case FEATURE_CARRIERS_2:
|
||||
features->carriers = 2;
|
||||
break;
|
||||
|
||||
case FEATURE_CARRIERS_4:
|
||||
features->carriers = 4;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (raw_features & FEATURE_CARRIER_SPEED_MASK) {
|
||||
case FEATURE_CARRIER_SPEED_1G:
|
||||
features->carrier_speed = CARRIER_SPEED_1G;
|
||||
break;
|
||||
case FEATURE_CARRIER_SPEED_2_5G:
|
||||
features->carrier_speed = CARRIER_SPEED_2_5G;
|
||||
break;
|
||||
case FEATURE_CARRIER_SPEED_10G:
|
||||
features->carrier_speed = CARRIER_SPEED_10G;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (raw_features & FEATURE_RAM_MASK) {
|
||||
case FEATURE_RAM_DDR2_32BIT_295MBPS:
|
||||
features->ram_config = RAM_DDR2_32BIT_295MBPS;
|
||||
break;
|
||||
|
||||
case FEATURE_RAM_DDR3_32BIT_590MBPS:
|
||||
features->ram_config = RAM_DDR3_32BIT_590MBPS;
|
||||
break;
|
||||
|
||||
case FEATURE_RAM_DDR3_48BIT_590MBPS:
|
||||
features->ram_config = RAM_DDR3_48BIT_590MBPS;
|
||||
break;
|
||||
|
||||
case FEATURE_RAM_DDR3_64BIT_1800MBPS:
|
||||
features->ram_config = RAM_DDR3_64BIT_1800MBPS;
|
||||
break;
|
||||
|
||||
case FEATURE_RAM_DDR3_48BIT_1800MBPS:
|
||||
features->ram_config = RAM_DDR3_48BIT_1800MBPS;
|
||||
break;
|
||||
}
|
||||
|
||||
features->pcm_tx = raw_features & FEATURE_PCM_TX;
|
||||
features->pcm_rx = raw_features & FEATURE_PCM_RX;
|
||||
features->spdif_tx = raw_features & FEATURE_SPDIF_TX;
|
||||
features->spdif_rx = raw_features & FEATURE_SPDIF_RX;
|
||||
features->usb2 = raw_features & FEATURE_USB2;
|
||||
features->rs232 = raw_extended_features & EXT_FEATURE_RS232;
|
||||
features->compression_type1 = raw_extended_features &
|
||||
EXT_FEATURE_COMPRESSION_TYPE1;
|
||||
features->compression_type2 = raw_extended_features &
|
||||
EXT_FEATURE_COMPRESSION_TYPE2;
|
||||
features->compression_type3 = raw_extended_features &
|
||||
EXT_FEATURE_COMPRESSION_TYPE3;
|
||||
features->interlace = raw_extended_features & EXT_FEATURE_INTERLACE;
|
||||
features->osd = raw_extended_features & EXT_FEATURE_OSD;
|
||||
features->compression_pipes = raw_extended_features &
|
||||
EXT_FEATURE_COMPRESSION_PERF_MASK;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/**
|
||||
* get_versions() - Fill structure with info from version register.
|
||||
* @fpga: Identifier of the FPGA device to be queried for information
|
||||
* @versions: Pointer to the structure to fill with information from the
|
||||
* versions register
|
||||
*
|
||||
* This is the legacy version and should be considered deprecated for new
|
||||
* devices.
|
||||
*
|
||||
* Return: 0
|
||||
*/
|
||||
static int get_versions(unsigned int fpga, struct fpga_versions *versions)
|
||||
{
|
||||
enum {
|
||||
/* HW version encoding is a mess, leave it for the moment */
|
||||
VERSIONS_HW_VER_MASK = 0xf << 0,
|
||||
VERSIONS_PIX_CLOCK_GEN_IDT8N3QV01 = BIT(4),
|
||||
VERSIONS_SFP = BIT(5),
|
||||
VERSIONS_VIDEO_MASK = 0x7 << 6,
|
||||
VERSIONS_VIDEO_DVI = 0x0 << 6,
|
||||
VERSIONS_VIDEO_DP_165 = 0x1 << 6,
|
||||
VERSIONS_VIDEO_DP_300 = 0x2 << 6,
|
||||
VERSIONS_VIDEO_HDMI = 0x3 << 6,
|
||||
VERSIONS_UT_MASK = 0xf << 12,
|
||||
VERSIONS_UT_MAIN_SERVER = 0x0 << 12,
|
||||
VERSIONS_UT_MAIN_USER = 0x1 << 12,
|
||||
VERSIONS_UT_VIDEO_SERVER = 0x2 << 12,
|
||||
VERSIONS_UT_VIDEO_USER = 0x3 << 12,
|
||||
};
|
||||
u16 raw_versions;
|
||||
|
||||
memset(versions, 0, sizeof(struct fpga_versions));
|
||||
|
||||
FPGA_GET_REG(fpga, versions, &raw_versions);
|
||||
|
||||
switch (raw_versions & VERSIONS_UT_MASK) {
|
||||
case VERSIONS_UT_MAIN_SERVER:
|
||||
versions->video_channel = false;
|
||||
versions->con_side = false;
|
||||
break;
|
||||
|
||||
case VERSIONS_UT_MAIN_USER:
|
||||
versions->video_channel = false;
|
||||
versions->con_side = true;
|
||||
break;
|
||||
|
||||
case VERSIONS_UT_VIDEO_SERVER:
|
||||
versions->video_channel = true;
|
||||
versions->con_side = false;
|
||||
break;
|
||||
|
||||
case VERSIONS_UT_VIDEO_USER:
|
||||
versions->video_channel = true;
|
||||
versions->con_side = true;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (raw_versions & VERSIONS_VIDEO_MASK) {
|
||||
case VERSIONS_VIDEO_DVI:
|
||||
versions->pcb_video_type = PCB_DVI_SL;
|
||||
break;
|
||||
|
||||
case VERSIONS_VIDEO_DP_165:
|
||||
versions->pcb_video_type = PCB_DP_165MPIX;
|
||||
break;
|
||||
|
||||
case VERSIONS_VIDEO_DP_300:
|
||||
versions->pcb_video_type = PCB_DP_300MPIX;
|
||||
break;
|
||||
|
||||
case VERSIONS_VIDEO_HDMI:
|
||||
versions->pcb_video_type = PCB_HDMI;
|
||||
break;
|
||||
}
|
||||
|
||||
versions->hw_version = raw_versions & VERSIONS_HW_VER_MASK;
|
||||
|
||||
if (raw_versions & VERSIONS_SFP)
|
||||
versions->pcb_transmission_type = PCB_FIBER_3G;
|
||||
else
|
||||
versions->pcb_transmission_type = PCB_CAT_1G;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* get_features() - Fill structure with info from features register.
|
||||
* @fpga: Identifier of the FPGA device to be queried for information
|
||||
* @features: Pointer to the structure to fill with information from the
|
||||
* features register
|
||||
*
|
||||
* This is the legacy version and should be considered deprecated for new
|
||||
* devices.
|
||||
*
|
||||
* Return: 0
|
||||
*/
|
||||
static int get_features(unsigned int fpga, struct fpga_features *features)
|
||||
{
|
||||
enum {
|
||||
FEATURE_CARRIER_SPEED_2_5 = BIT(4),
|
||||
FEATURE_RAM_MASK = 0x7 << 5,
|
||||
FEATURE_RAM_DDR2_32BIT = 0x0 << 5,
|
||||
FEATURE_RAM_DDR3_32BIT = 0x1 << 5,
|
||||
FEATURE_RAM_DDR3_48BIT = 0x2 << 5,
|
||||
FEATURE_PCM_AUDIO_TX = BIT(9),
|
||||
FEATURE_PCM_AUDIO_RX = BIT(10),
|
||||
FEATURE_OSD = BIT(11),
|
||||
FEATURE_USB20 = BIT(12),
|
||||
FEATURE_COMPRESSION_MASK = 7 << 13,
|
||||
FEATURE_COMPRESSION_TYPE1 = 0x1 << 13,
|
||||
FEATURE_COMPRESSION_TYPE1_TYPE2 = 0x3 << 13,
|
||||
FEATURE_COMPRESSION_TYPE1_TYPE2_TYPE3 = 0x7 << 13,
|
||||
};
|
||||
|
||||
enum {
|
||||
EXTENDED_FEATURE_SPDIF_AUDIO_TX = BIT(0),
|
||||
EXTENDED_FEATURE_SPDIF_AUDIO_RX = BIT(1),
|
||||
EXTENDED_FEATURE_RS232 = BIT(2),
|
||||
EXTENDED_FEATURE_COMPRESSION_PIPES = BIT(3),
|
||||
EXTENDED_FEATURE_INTERLACE = BIT(4),
|
||||
};
|
||||
|
||||
u16 raw_features;
|
||||
u16 raw_extended_features;
|
||||
|
||||
memset(features, 0, sizeof(struct fpga_features));
|
||||
|
||||
FPGA_GET_REG(fpga, fpga_features, &raw_features);
|
||||
FPGA_GET_REG(fpga, fpga_ext_features, &raw_extended_features);
|
||||
|
||||
features->video_channels = raw_features & 0x3;
|
||||
features->carriers = (raw_features >> 2) & 0x3;
|
||||
|
||||
features->carrier_speed = (raw_features & FEATURE_CARRIER_SPEED_2_5)
|
||||
? CARRIER_SPEED_2_5G : CARRIER_SPEED_1G;
|
||||
|
||||
switch (raw_features & FEATURE_RAM_MASK) {
|
||||
case FEATURE_RAM_DDR2_32BIT:
|
||||
features->ram_config = RAM_DDR2_32BIT_295MBPS;
|
||||
break;
|
||||
|
||||
case FEATURE_RAM_DDR3_32BIT:
|
||||
features->ram_config = RAM_DDR3_32BIT_590MBPS;
|
||||
break;
|
||||
|
||||
case FEATURE_RAM_DDR3_48BIT:
|
||||
features->ram_config = RAM_DDR3_48BIT_590MBPS;
|
||||
break;
|
||||
}
|
||||
|
||||
features->pcm_tx = raw_features & FEATURE_PCM_AUDIO_TX;
|
||||
features->pcm_rx = raw_features & FEATURE_PCM_AUDIO_RX;
|
||||
features->spdif_tx = raw_extended_features &
|
||||
EXTENDED_FEATURE_SPDIF_AUDIO_TX;
|
||||
features->spdif_rx = raw_extended_features &
|
||||
EXTENDED_FEATURE_SPDIF_AUDIO_RX;
|
||||
|
||||
features->usb2 = raw_features & FEATURE_USB20;
|
||||
features->rs232 = raw_extended_features & EXTENDED_FEATURE_RS232;
|
||||
|
||||
features->compression_type1 = false;
|
||||
features->compression_type2 = false;
|
||||
features->compression_type3 = false;
|
||||
switch (raw_features & FEATURE_COMPRESSION_MASK) {
|
||||
case FEATURE_COMPRESSION_TYPE1_TYPE2_TYPE3:
|
||||
features->compression_type3 = true;
|
||||
/* fall-through */
|
||||
case FEATURE_COMPRESSION_TYPE1_TYPE2:
|
||||
features->compression_type2 = true;
|
||||
/* fall-through */
|
||||
case FEATURE_COMPRESSION_TYPE1:
|
||||
features->compression_type1 = true;
|
||||
break;
|
||||
}
|
||||
|
||||
features->interlace = raw_extended_features &
|
||||
EXTENDED_FEATURE_INTERLACE;
|
||||
features->osd = raw_features & FEATURE_OSD;
|
||||
features->compression_pipes = raw_extended_features &
|
||||
EXTENDED_FEATURE_COMPRESSION_PIPES;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* fpga_print_info() - Print information about FPGA device
|
||||
* @dev: FPGA device to print information about
|
||||
*/
|
||||
static void fpga_print_info(struct udevice *dev)
|
||||
{
|
||||
struct ihs_fpga_priv *priv = dev_get_priv(dev);
|
||||
u16 fpga_version;
|
||||
struct fpga_versions versions;
|
||||
struct fpga_features features;
|
||||
|
||||
ihs_fpga_get(priv->map, fpga_version, &fpga_version);
|
||||
get_versions(dev, &versions);
|
||||
get_features(dev, &features);
|
||||
|
||||
if (versions.video_channel)
|
||||
printf("Videochannel");
|
||||
else
|
||||
printf("Mainchannel");
|
||||
|
||||
if (versions.con_side)
|
||||
printf(" User");
|
||||
else
|
||||
printf(" Server");
|
||||
|
||||
switch (versions.pcb_transmission_type) {
|
||||
case PCB_CAT_1G:
|
||||
case PCB_CAT_10G:
|
||||
printf(" CAT");
|
||||
break;
|
||||
case PCB_FIBER_3G:
|
||||
case PCB_FIBER_10G:
|
||||
printf(" Fiber");
|
||||
break;
|
||||
};
|
||||
|
||||
switch (versions.pcb_video_type) {
|
||||
case PCB_DVI_SL:
|
||||
printf(" DVI,");
|
||||
break;
|
||||
case PCB_DP_165MPIX:
|
||||
printf(" DP 165MPix/s,");
|
||||
break;
|
||||
case PCB_DP_300MPIX:
|
||||
printf(" DP 300MPix/s,");
|
||||
break;
|
||||
case PCB_HDMI:
|
||||
printf(" HDMI,");
|
||||
break;
|
||||
case PCB_DP_1_2:
|
||||
printf(" DP 1.2,");
|
||||
break;
|
||||
case PCB_HDMI_2_0:
|
||||
printf(" HDMI 2.0,");
|
||||
break;
|
||||
}
|
||||
|
||||
printf(" FPGA V %d.%02d\n features: ",
|
||||
fpga_version / 100, fpga_version % 100);
|
||||
|
||||
if (!features.compression_type1 &&
|
||||
!features.compression_type2 &&
|
||||
!features.compression_type3)
|
||||
printf("no compression, ");
|
||||
|
||||
if (features.compression_type1)
|
||||
printf("type1, ");
|
||||
|
||||
if (features.compression_type2)
|
||||
printf("type2, ");
|
||||
|
||||
if (features.compression_type3)
|
||||
printf("type3, ");
|
||||
|
||||
printf("%sosd", features.osd ? "" : "no ");
|
||||
|
||||
if (features.pcm_rx && features.pcm_tx)
|
||||
printf(", pcm rx+tx");
|
||||
else if (features.pcm_rx)
|
||||
printf(", pcm rx");
|
||||
else if (features.pcm_tx)
|
||||
printf(", pcm tx");
|
||||
|
||||
if (features.spdif_rx && features.spdif_tx)
|
||||
printf(", spdif rx+tx");
|
||||
else if (features.spdif_rx)
|
||||
printf(", spdif rx");
|
||||
else if (features.spdif_tx)
|
||||
printf(", spdif tx");
|
||||
|
||||
puts(",\n ");
|
||||
|
||||
switch (features.sysclock) {
|
||||
case SYSCLK_147456:
|
||||
printf("clock 147.456 MHz");
|
||||
break;
|
||||
}
|
||||
|
||||
switch (features.ram_config) {
|
||||
case RAM_DDR2_32BIT_295MBPS:
|
||||
printf(", RAM 32 bit DDR2");
|
||||
break;
|
||||
case RAM_DDR3_32BIT_590MBPS:
|
||||
printf(", RAM 32 bit DDR3");
|
||||
break;
|
||||
case RAM_DDR3_48BIT_590MBPS:
|
||||
case RAM_DDR3_48BIT_1800MBPS:
|
||||
printf(", RAM 48 bit DDR3");
|
||||
break;
|
||||
case RAM_DDR3_64BIT_1800MBPS:
|
||||
printf(", RAM 64 bit DDR3");
|
||||
break;
|
||||
}
|
||||
|
||||
printf(", %d carrier(s)", features.carriers);
|
||||
|
||||
switch (features.carrier_speed) {
|
||||
case CARRIER_SPEED_1G:
|
||||
printf(", 1Gbit/s");
|
||||
break;
|
||||
case CARRIER_SPEED_3G:
|
||||
printf(", 3Gbit/s");
|
||||
break;
|
||||
case CARRIER_SPEED_10G:
|
||||
printf(", 10Gbit/s");
|
||||
break;
|
||||
}
|
||||
|
||||
printf(", %d video channel(s)\n", features.video_channels);
|
||||
}
|
||||
|
||||
/**
|
||||
* do_reflection_test() - Run reflection test on a FPGA device
|
||||
* @dev: FPGA device to run reflection test on
|
||||
*
|
||||
* Return: 0 if reflection test succeeded, -ve on error
|
||||
*/
|
||||
static int do_reflection_test(struct udevice *dev)
|
||||
{
|
||||
struct ihs_fpga_priv *priv = dev_get_priv(dev);
|
||||
int ctr = 0;
|
||||
|
||||
while (1) {
|
||||
u16 val;
|
||||
|
||||
ihs_fpga_set(priv->map, reflection_low, REFLECTION_TESTPATTERN);
|
||||
|
||||
ihs_fpga_get(priv->map, reflection_low, &val);
|
||||
if (val == (~REFLECTION_TESTPATTERN & 0xffff))
|
||||
return -EIO;
|
||||
|
||||
mdelay(REFLECTION_TEST_DELAY);
|
||||
if (ctr++ > REFLECTION_TEST_ROUNDS)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wait_for_fpga_done() - Wait until 'done'-flag is set for FPGA device
|
||||
* @dev: FPGA device whose done flag to wait for
|
||||
*
|
||||
* This function waits until it detects that the done-GPIO's value was changed
|
||||
* to 1 by the FPGA, which indicates that the device is configured and ready to
|
||||
* use.
|
||||
*
|
||||
* Return: 0 if done flag was detected, -ve on error
|
||||
*/
|
||||
static int wait_for_fpga_done(struct udevice *dev)
|
||||
{
|
||||
struct ihs_fpga_priv *priv = dev_get_priv(dev);
|
||||
int ctr = 0;
|
||||
int done_val;
|
||||
|
||||
while (1) {
|
||||
done_val = dm_gpio_get_value(&priv->done_gpio);
|
||||
if (done_val < 0) {
|
||||
debug("%s: Error while reading done-GPIO (err = %d)\n",
|
||||
dev->name, done_val);
|
||||
return done_val;
|
||||
}
|
||||
|
||||
if (done_val)
|
||||
return 0;
|
||||
|
||||
mdelay(FPGA_DONE_WAIT_DELAY);
|
||||
if (ctr++ > FPGA_DONE_WAIT_ROUND) {
|
||||
debug("%s: FPGA init failed (done not detected)\n",
|
||||
dev->name);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int ihs_fpga_probe(struct udevice *dev)
|
||||
{
|
||||
struct ihs_fpga_priv *priv = dev_get_priv(dev);
|
||||
int ret;
|
||||
|
||||
/* TODO(mario.six@gdsys.cc): Case of FPGA attached to MCLink bus */
|
||||
|
||||
ret = regmap_init_mem(dev_ofnode(dev), &priv->map);
|
||||
if (ret) {
|
||||
debug("%s: Could not initialize regmap (err = %d)",
|
||||
dev->name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = gpio_request_by_name(dev, "reset-gpios", 0, &priv->reset_gpio,
|
||||
GPIOD_IS_OUT);
|
||||
if (ret) {
|
||||
debug("%s: Could not get reset-GPIO (err = %d)\n",
|
||||
dev->name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!priv->reset_gpio.dev) {
|
||||
debug("%s: Could not get reset-GPIO\n", dev->name);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
ret = gpio_request_by_name(dev, "done-gpios", 0, &priv->done_gpio,
|
||||
GPIOD_IS_IN);
|
||||
if (ret) {
|
||||
debug("%s: Could not get done-GPIO (err = %d)\n",
|
||||
dev->name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!priv->done_gpio.dev) {
|
||||
debug("%s: Could not get done-GPIO\n", dev->name);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
ret = dm_gpio_set_value(&priv->reset_gpio, 1);
|
||||
if (ret) {
|
||||
debug("%s: Error while setting reset-GPIO (err = %d)\n",
|
||||
dev->name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* If FPGA already runs, don't initialize again */
|
||||
if (do_reflection_test(dev))
|
||||
goto reflection_ok;
|
||||
|
||||
ret = dm_gpio_set_value(&priv->reset_gpio, 0);
|
||||
if (ret) {
|
||||
debug("%s: Error while setting reset-GPIO (err = %d)\n",
|
||||
dev->name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = wait_for_fpga_done(dev);
|
||||
if (ret) {
|
||||
debug("%s: Error while waiting for FPGA done (err = %d)\n",
|
||||
dev->name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
udelay(10);
|
||||
|
||||
ret = dm_gpio_set_value(&priv->reset_gpio, 1);
|
||||
if (ret) {
|
||||
debug("%s: Error while setting reset-GPIO (err = %d)\n",
|
||||
dev->name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!do_reflection_test(dev)) {
|
||||
debug("%s: Reflection test FAILED\n", dev->name);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
reflection_ok:
|
||||
printf("%s: Reflection test passed.\n", dev->name);
|
||||
|
||||
fpga_print_info(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id ihs_fpga_ids[] = {
|
||||
{ .compatible = "gdsys,iocon_fpga" },
|
||||
{ .compatible = "gdsys,iocpu_fpga" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(ihs_fpga_bus) = {
|
||||
.name = "ihs_fpga_bus",
|
||||
.id = UCLASS_MISC,
|
||||
.of_match = ihs_fpga_ids,
|
||||
.probe = ihs_fpga_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct ihs_fpga_priv),
|
||||
};
|
||||
@@ -0,0 +1,49 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* (C) Copyright 2018
|
||||
* Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
|
||||
*/
|
||||
|
||||
/**
|
||||
* struct ihs_fpga_regs - IHS FPGA register map structure
|
||||
* @reflection_low: Lower reflection register
|
||||
* @versions: PCB versions register
|
||||
* @fpga_version: FPGA versions register
|
||||
* @features: FPGA features register
|
||||
* @extended_features: FPGA extended features register
|
||||
* @top_interrupt: Top interrupt register
|
||||
* @top_interrupt_enable: Top interrupt enable register
|
||||
* @status: FPGA status register
|
||||
* @control: FPGA control register
|
||||
* @extended_control: FPGA extended control register
|
||||
*/
|
||||
struct ihs_fpga_regs {
|
||||
u16 reflection_low;
|
||||
u16 versions;
|
||||
u16 fpga_version;
|
||||
u16 features;
|
||||
u16 extended_features;
|
||||
u16 top_interrupt;
|
||||
u16 top_interrupt_enable;
|
||||
u16 status;
|
||||
u16 control;
|
||||
u16 extended_control;
|
||||
};
|
||||
|
||||
/**
|
||||
* ihs_fpga_set() - Convenience macro to set values in FPGA register map
|
||||
* @map: Register map to set a value in
|
||||
* @member: Name of member (described by ihs_fpga_regs) to set
|
||||
* @val: Value to set the member to
|
||||
*/
|
||||
#define ihs_fpga_set(map, member, val) \
|
||||
regmap_set(map, struct ihs_fpga_regs, member, val)
|
||||
|
||||
/**
|
||||
* ihs_fpga_get() - Convenience macro to get values from FPGA register map
|
||||
* @map: Register map to read value from
|
||||
* @member: Name of member (described by ihs_fpga_regs) to get
|
||||
* @valp: Pointe to variable to receive the value read
|
||||
*/
|
||||
#define ihs_fpga_get(map, member, valp) \
|
||||
regmap_get(map, struct ihs_fpga_regs, member, valp)
|
||||
@@ -0,0 +1,4 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
obj-y += scu_api.o scu.o
|
||||
obj-$(CONFIG_CMD_FUSE) += fuse.o
|
||||
@@ -0,0 +1,84 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright 2019 NXP
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <console.h>
|
||||
#include <errno.h>
|
||||
#include <fuse.h>
|
||||
#include <asm/arch/sci/sci.h>
|
||||
#include <asm/arch/sys_proto.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
#define FSL_ECC_WORD_START_1 0x10
|
||||
#define FSL_ECC_WORD_END_1 0x10F
|
||||
|
||||
#define FSL_ECC_WORD_START_2 0x220
|
||||
#define FSL_ECC_WORD_END_2 0x31F
|
||||
|
||||
#define FSL_QXP_FUSE_GAP_START 0x110
|
||||
#define FSL_QXP_FUSE_GAP_END 0x21F
|
||||
|
||||
#define FSL_SIP_OTP_READ 0xc200000A
|
||||
#define FSL_SIP_OTP_WRITE 0xc200000B
|
||||
|
||||
int fuse_read(u32 bank, u32 word, u32 *val)
|
||||
{
|
||||
return fuse_sense(bank, word, val);
|
||||
}
|
||||
|
||||
int fuse_sense(u32 bank, u32 word, u32 *val)
|
||||
{
|
||||
unsigned long ret = 0, value = 0;
|
||||
|
||||
if (bank != 0) {
|
||||
printf("Invalid bank argument, ONLY bank 0 is supported\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = call_imx_sip_ret2(FSL_SIP_OTP_READ, (unsigned long)word, &value,
|
||||
0, 0);
|
||||
*val = (u32)value;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int fuse_prog(u32 bank, u32 word, u32 val)
|
||||
{
|
||||
if (bank != 0) {
|
||||
printf("Invalid bank argument, ONLY bank 0 is supported\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_IMX8QXP)) {
|
||||
if (word >= FSL_QXP_FUSE_GAP_START &&
|
||||
word <= FSL_QXP_FUSE_GAP_END) {
|
||||
printf("Invalid word argument for this SoC\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if ((word >= FSL_ECC_WORD_START_1 && word <= FSL_ECC_WORD_END_1) ||
|
||||
(word >= FSL_ECC_WORD_START_2 && word <= FSL_ECC_WORD_END_2)) {
|
||||
puts("Warning: Words in this index range have ECC protection\n"
|
||||
"and can only be programmed once per word. Individual bit\n"
|
||||
"operations will be rejected after the first one.\n"
|
||||
"\n\n Really program this word? <y/N>\n");
|
||||
|
||||
if (!confirm_yesno()) {
|
||||
puts("Word programming aborted\n");
|
||||
return -EPERM;
|
||||
}
|
||||
}
|
||||
|
||||
return call_imx_sip(FSL_SIP_OTP_WRITE, (unsigned long)word,
|
||||
(unsigned long)val, 0, 0);
|
||||
}
|
||||
|
||||
int fuse_override(u32 bank, u32 word, u32 val)
|
||||
{
|
||||
printf("Override fuse to i.MX8 in u-boot is forbidden\n");
|
||||
return -EPERM;
|
||||
}
|
||||
@@ -0,0 +1,248 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright 2018 NXP
|
||||
*
|
||||
* Peng Fan <peng.fan@nxp.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <dm.h>
|
||||
#include <dm/lists.h>
|
||||
#include <dm/root.h>
|
||||
#include <dm/device-internal.h>
|
||||
#include <asm/arch/sci/sci.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <misc.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
struct mu_type {
|
||||
u32 tr[4];
|
||||
u32 rr[4];
|
||||
u32 sr;
|
||||
u32 cr;
|
||||
};
|
||||
|
||||
struct imx8_scu {
|
||||
struct mu_type *base;
|
||||
};
|
||||
|
||||
#define MU_CR_GIE_MASK 0xF0000000u
|
||||
#define MU_CR_RIE_MASK 0xF000000u
|
||||
#define MU_CR_GIR_MASK 0xF0000u
|
||||
#define MU_CR_TIE_MASK 0xF00000u
|
||||
#define MU_CR_F_MASK 0x7u
|
||||
#define MU_SR_TE0_MASK BIT(23)
|
||||
#define MU_SR_RF0_MASK BIT(27)
|
||||
#define MU_TR_COUNT 4
|
||||
#define MU_RR_COUNT 4
|
||||
|
||||
static inline void mu_hal_init(struct mu_type *base)
|
||||
{
|
||||
/* Clear GIEn, RIEn, TIEn, GIRn and ABFn. */
|
||||
clrbits_le32(&base->cr, MU_CR_GIE_MASK | MU_CR_RIE_MASK |
|
||||
MU_CR_TIE_MASK | MU_CR_GIR_MASK | MU_CR_F_MASK);
|
||||
}
|
||||
|
||||
static int mu_hal_sendmsg(struct mu_type *base, u32 reg_index, u32 msg)
|
||||
{
|
||||
u32 mask = MU_SR_TE0_MASK >> reg_index;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
assert(reg_index < MU_TR_COUNT);
|
||||
|
||||
/* Wait TX register to be empty. */
|
||||
ret = readl_poll_timeout(&base->sr, val, val & mask, 10000);
|
||||
if (ret < 0) {
|
||||
printf("%s timeout\n", __func__);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
writel(msg, &base->tr[reg_index]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mu_hal_receivemsg(struct mu_type *base, u32 reg_index, u32 *msg)
|
||||
{
|
||||
u32 mask = MU_SR_RF0_MASK >> reg_index;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
assert(reg_index < MU_TR_COUNT);
|
||||
|
||||
/* Wait RX register to be full. */
|
||||
ret = readl_poll_timeout(&base->sr, val, val & mask, 10000);
|
||||
if (ret < 0) {
|
||||
printf("%s timeout\n", __func__);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
*msg = readl(&base->rr[reg_index]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sc_ipc_read(struct mu_type *base, void *data)
|
||||
{
|
||||
struct sc_rpc_msg_s *msg = (struct sc_rpc_msg_s *)data;
|
||||
int ret;
|
||||
u8 count = 0;
|
||||
|
||||
if (!msg)
|
||||
return -EINVAL;
|
||||
|
||||
/* Read first word */
|
||||
ret = mu_hal_receivemsg(base, 0, (u32 *)msg);
|
||||
if (ret)
|
||||
return ret;
|
||||
count++;
|
||||
|
||||
/* Check size */
|
||||
if (msg->size > SC_RPC_MAX_MSG) {
|
||||
*((u32 *)msg) = 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Read remaining words */
|
||||
while (count < msg->size) {
|
||||
ret = mu_hal_receivemsg(base, count % MU_RR_COUNT,
|
||||
&msg->DATA.u32[count - 1]);
|
||||
if (ret)
|
||||
return ret;
|
||||
count++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sc_ipc_write(struct mu_type *base, void *data)
|
||||
{
|
||||
struct sc_rpc_msg_s *msg = (struct sc_rpc_msg_s *)data;
|
||||
int ret;
|
||||
u8 count = 0;
|
||||
|
||||
if (!msg)
|
||||
return -EINVAL;
|
||||
|
||||
/* Check size */
|
||||
if (msg->size > SC_RPC_MAX_MSG)
|
||||
return -EINVAL;
|
||||
|
||||
/* Write first word */
|
||||
ret = mu_hal_sendmsg(base, 0, *((u32 *)msg));
|
||||
if (ret)
|
||||
return ret;
|
||||
count++;
|
||||
|
||||
/* Write remaining words */
|
||||
while (count < msg->size) {
|
||||
ret = mu_hal_sendmsg(base, count % MU_TR_COUNT,
|
||||
msg->DATA.u32[count - 1]);
|
||||
if (ret)
|
||||
return ret;
|
||||
count++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note the function prototype use msgid as the 2nd parameter, here
|
||||
* we take it as no_resp.
|
||||
*/
|
||||
static int imx8_scu_call(struct udevice *dev, int no_resp, void *tx_msg,
|
||||
int tx_size, void *rx_msg, int rx_size)
|
||||
{
|
||||
struct imx8_scu *plat = dev_get_platdata(dev);
|
||||
sc_err_t result;
|
||||
int ret;
|
||||
|
||||
/* Expect tx_msg, rx_msg are the same value */
|
||||
if (rx_msg && tx_msg != rx_msg)
|
||||
printf("tx_msg %p, rx_msg %p\n", tx_msg, rx_msg);
|
||||
|
||||
ret = sc_ipc_write(plat->base, tx_msg);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (!no_resp) {
|
||||
ret = sc_ipc_read(plat->base, rx_msg);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
result = RPC_R8((struct sc_rpc_msg_s *)tx_msg);
|
||||
|
||||
return sc_err_to_linux(result);
|
||||
}
|
||||
|
||||
static int imx8_scu_probe(struct udevice *dev)
|
||||
{
|
||||
struct imx8_scu *plat = dev_get_platdata(dev);
|
||||
fdt_addr_t addr;
|
||||
|
||||
debug("%s(dev=%p) (plat=%p)\n", __func__, dev, plat);
|
||||
|
||||
addr = devfdt_get_addr(dev);
|
||||
if (addr == FDT_ADDR_T_NONE)
|
||||
return -EINVAL;
|
||||
|
||||
#ifdef CONFIG_SPL_BUILD
|
||||
plat->base = (struct mu_type *)CONFIG_MU_BASE_SPL;
|
||||
#else
|
||||
plat->base = (struct mu_type *)addr;
|
||||
#endif
|
||||
|
||||
/* U-Boot not enable interrupts, so need to enable RX interrupts */
|
||||
mu_hal_init(plat->base);
|
||||
|
||||
gd->arch.scu_dev = dev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx8_scu_remove(struct udevice *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx8_scu_bind(struct udevice *dev)
|
||||
{
|
||||
int ret;
|
||||
struct udevice *child;
|
||||
ofnode node;
|
||||
|
||||
debug("%s(dev=%p)\n", __func__, dev);
|
||||
ofnode_for_each_subnode(node, dev_ofnode(dev)) {
|
||||
ret = lists_bind_fdt(dev, node, &child, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
debug("bind child dev %s\n", child->name);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct misc_ops imx8_scu_ops = {
|
||||
.call = imx8_scu_call,
|
||||
};
|
||||
|
||||
static const struct udevice_id imx8_scu_ids[] = {
|
||||
{ .compatible = "fsl,imx8qxp-mu" },
|
||||
{ .compatible = "fsl,imx8-mu" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(imx8_scu) = {
|
||||
.name = "imx8_scu",
|
||||
.id = UCLASS_MISC,
|
||||
.of_match = imx8_scu_ids,
|
||||
.probe = imx8_scu_probe,
|
||||
.bind = imx8_scu_bind,
|
||||
.remove = imx8_scu_remove,
|
||||
.ops = &imx8_scu_ops,
|
||||
.platdata_auto_alloc_size = sizeof(struct imx8_scu),
|
||||
.flags = DM_FLAG_PRE_RELOC,
|
||||
};
|
||||
@@ -0,0 +1,948 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright 2018 NXP
|
||||
*
|
||||
* Peng Fan <peng.fan@nxp.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <dm.h>
|
||||
#include <asm/arch/sci/sci.h>
|
||||
#include <misc.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
#define B2U8(X) (((X) != SC_FALSE) ? (u8)(0x01U) : (u8)(0x00U))
|
||||
|
||||
/* CLK and PM */
|
||||
int sc_pm_set_clock_rate(sc_ipc_t ipc, sc_rsrc_t resource, sc_pm_clk_t clk,
|
||||
sc_pm_clock_rate_t *rate)
|
||||
{
|
||||
struct udevice *dev = gd->arch.scu_dev;
|
||||
int size = sizeof(struct sc_rpc_msg_s);
|
||||
struct sc_rpc_msg_s msg;
|
||||
int ret;
|
||||
|
||||
RPC_VER(&msg) = SC_RPC_VERSION;
|
||||
RPC_SVC(&msg) = (u8)SC_RPC_SVC_PM;
|
||||
RPC_FUNC(&msg) = (u8)PM_FUNC_SET_CLOCK_RATE;
|
||||
RPC_U32(&msg, 0U) = *(u32 *)rate;
|
||||
RPC_U16(&msg, 4U) = (u16)resource;
|
||||
RPC_U8(&msg, 6U) = (u8)clk;
|
||||
RPC_SIZE(&msg) = 3U;
|
||||
|
||||
ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size);
|
||||
if (ret)
|
||||
printf("%s: rate:%u resource:%u: clk:%u res:%d\n",
|
||||
__func__, *rate, resource, clk, RPC_R8(&msg));
|
||||
|
||||
*rate = RPC_U32(&msg, 0U);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sc_pm_get_clock_rate(sc_ipc_t ipc, sc_rsrc_t resource, sc_pm_clk_t clk,
|
||||
sc_pm_clock_rate_t *rate)
|
||||
{
|
||||
struct udevice *dev = gd->arch.scu_dev;
|
||||
int size = sizeof(struct sc_rpc_msg_s);
|
||||
struct sc_rpc_msg_s msg;
|
||||
int ret;
|
||||
|
||||
RPC_VER(&msg) = SC_RPC_VERSION;
|
||||
RPC_SVC(&msg) = (u8)SC_RPC_SVC_PM;
|
||||
RPC_FUNC(&msg) = (u8)PM_FUNC_GET_CLOCK_RATE;
|
||||
RPC_U16(&msg, 0U) = (u16)resource;
|
||||
RPC_U8(&msg, 2U) = (u8)clk;
|
||||
RPC_SIZE(&msg) = 2U;
|
||||
|
||||
ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size);
|
||||
if (ret) {
|
||||
printf("%s: resource:%d clk:%d: res:%d\n",
|
||||
__func__, resource, clk, RPC_R8(&msg));
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (rate)
|
||||
*rate = RPC_U32(&msg, 0U);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sc_pm_clock_enable(sc_ipc_t ipc, sc_rsrc_t resource, sc_pm_clk_t clk,
|
||||
sc_bool_t enable, sc_bool_t autog)
|
||||
{
|
||||
struct udevice *dev = gd->arch.scu_dev;
|
||||
int size = sizeof(struct sc_rpc_msg_s);
|
||||
struct sc_rpc_msg_s msg;
|
||||
int ret;
|
||||
|
||||
RPC_VER(&msg) = SC_RPC_VERSION;
|
||||
RPC_SVC(&msg) = (u8)SC_RPC_SVC_PM;
|
||||
RPC_FUNC(&msg) = (u8)PM_FUNC_CLOCK_ENABLE;
|
||||
RPC_U16(&msg, 0U) = (u16)resource;
|
||||
RPC_U8(&msg, 2U) = (u8)clk;
|
||||
RPC_U8(&msg, 3U) = (u8)enable;
|
||||
RPC_U8(&msg, 4U) = (u8)autog;
|
||||
RPC_SIZE(&msg) = 3U;
|
||||
|
||||
ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size);
|
||||
if (ret)
|
||||
printf("%s: resource:%d clk:%d: enable:%d autog: %d, res:%d\n",
|
||||
__func__, resource, clk, enable, autog, RPC_R8(&msg));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sc_pm_set_clock_parent(sc_ipc_t ipc, sc_rsrc_t resource,
|
||||
sc_pm_clk_t clk, sc_pm_clk_parent_t parent)
|
||||
{
|
||||
struct udevice *dev = gd->arch.scu_dev;
|
||||
int size = sizeof(struct sc_rpc_msg_s);
|
||||
struct sc_rpc_msg_s msg;
|
||||
int ret;
|
||||
|
||||
RPC_VER(&msg) = SC_RPC_VERSION;
|
||||
RPC_SVC(&msg) = (u8)SC_RPC_SVC_PM;
|
||||
RPC_FUNC(&msg) = (u8)PM_FUNC_SET_CLOCK_PARENT;
|
||||
RPC_U16(&msg, 0U) = (u16)resource;
|
||||
RPC_U8(&msg, 2U) = (u8)clk;
|
||||
RPC_U8(&msg, 3U) = (u8)parent;
|
||||
RPC_SIZE(&msg) = 2U;
|
||||
|
||||
ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size);
|
||||
if (ret)
|
||||
printf("%s: resource:%d clk:%d: parent clk: %d, res:%d\n",
|
||||
__func__, resource, clk, parent, RPC_R8(&msg));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sc_pm_set_resource_power_mode(sc_ipc_t ipc, sc_rsrc_t resource,
|
||||
sc_pm_power_mode_t mode)
|
||||
{
|
||||
struct udevice *dev = gd->arch.scu_dev;
|
||||
int size = sizeof(struct sc_rpc_msg_s);
|
||||
struct sc_rpc_msg_s msg;
|
||||
int ret;
|
||||
|
||||
if (!dev)
|
||||
hang();
|
||||
|
||||
RPC_VER(&msg) = SC_RPC_VERSION;
|
||||
RPC_SVC(&msg) = (u8)SC_RPC_SVC_PM;
|
||||
RPC_FUNC(&msg) = (u8)PM_FUNC_SET_RESOURCE_POWER_MODE;
|
||||
RPC_U16(&msg, 0U) = (u16)resource;
|
||||
RPC_U8(&msg, 2U) = (u8)mode;
|
||||
RPC_SIZE(&msg) = 2U;
|
||||
|
||||
ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size);
|
||||
if (ret)
|
||||
printf("%s: resource:%d mode:%d: res:%d\n",
|
||||
__func__, resource, mode, RPC_R8(&msg));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
sc_bool_t sc_pm_is_partition_started(sc_ipc_t ipc, sc_rm_pt_t pt)
|
||||
{
|
||||
struct udevice *dev = gd->arch.scu_dev;
|
||||
int size = sizeof(struct sc_rpc_msg_s);
|
||||
struct sc_rpc_msg_s msg;
|
||||
int ret;
|
||||
u8 result;
|
||||
|
||||
RPC_VER(&msg) = SC_RPC_VERSION;
|
||||
RPC_SVC(&msg) = (u8)(SC_RPC_SVC_PM);
|
||||
RPC_FUNC(&msg) = (u8)(PM_FUNC_IS_PARTITION_STARTED);
|
||||
RPC_U8(&msg, 0U) = (u8)(pt);
|
||||
RPC_SIZE(&msg) = 2U;
|
||||
|
||||
ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size);
|
||||
|
||||
result = RPC_R8(&msg);
|
||||
if (result != 0 && result != 1) {
|
||||
printf("%s: partition:%d res:%d\n",
|
||||
__func__, pt, RPC_R8(&msg));
|
||||
if (ret)
|
||||
printf("%s: partition:%d res:%d\n", __func__, pt,
|
||||
RPC_R8(&msg));
|
||||
}
|
||||
return !!result;
|
||||
}
|
||||
|
||||
/* PAD */
|
||||
int sc_pad_set(sc_ipc_t ipc, sc_pad_t pad, u32 val)
|
||||
{
|
||||
struct udevice *dev = gd->arch.scu_dev;
|
||||
int size = sizeof(struct sc_rpc_msg_s);
|
||||
struct sc_rpc_msg_s msg;
|
||||
int ret;
|
||||
|
||||
if (!dev)
|
||||
hang();
|
||||
|
||||
RPC_VER(&msg) = SC_RPC_VERSION;
|
||||
RPC_SVC(&msg) = (u8)SC_RPC_SVC_PAD;
|
||||
RPC_FUNC(&msg) = (u8)PAD_FUNC_SET;
|
||||
RPC_U32(&msg, 0U) = (u32)val;
|
||||
RPC_U16(&msg, 4U) = (u16)pad;
|
||||
RPC_SIZE(&msg) = 3U;
|
||||
|
||||
ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size);
|
||||
if (ret)
|
||||
printf("%s: val:%d pad:%d: res:%d\n",
|
||||
__func__, val, pad, RPC_R8(&msg));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* MISC */
|
||||
int sc_misc_set_control(sc_ipc_t ipc, sc_rsrc_t resource,
|
||||
sc_ctrl_t ctrl, u32 val)
|
||||
{
|
||||
struct udevice *dev = gd->arch.scu_dev;
|
||||
int size = sizeof(struct sc_rpc_msg_s);
|
||||
struct sc_rpc_msg_s msg;
|
||||
int ret;
|
||||
|
||||
if (!dev)
|
||||
hang();
|
||||
|
||||
RPC_VER(&msg) = SC_RPC_VERSION;
|
||||
RPC_SVC(&msg) = (u8)SC_RPC_SVC_MISC;
|
||||
RPC_FUNC(&msg) = (u8)MISC_FUNC_SET_CONTROL;
|
||||
RPC_U32(&msg, 0U) = (u32)ctrl;
|
||||
RPC_U32(&msg, 4U) = (u32)val;
|
||||
RPC_U16(&msg, 8U) = (u16)resource;
|
||||
RPC_SIZE(&msg) = 4U;
|
||||
|
||||
ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size);
|
||||
if (ret)
|
||||
printf("%s: ctrl:%d resource:%d: res:%d\n",
|
||||
__func__, ctrl, resource, RPC_R8(&msg));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sc_misc_get_control(sc_ipc_t ipc, sc_rsrc_t resource, sc_ctrl_t ctrl,
|
||||
u32 *val)
|
||||
{
|
||||
struct udevice *dev = gd->arch.scu_dev;
|
||||
int size = sizeof(struct sc_rpc_msg_s);
|
||||
struct sc_rpc_msg_s msg;
|
||||
int ret;
|
||||
|
||||
if (!dev)
|
||||
hang();
|
||||
|
||||
RPC_VER(&msg) = SC_RPC_VERSION;
|
||||
RPC_SVC(&msg) = (u8)SC_RPC_SVC_MISC;
|
||||
RPC_FUNC(&msg) = (u8)MISC_FUNC_GET_CONTROL;
|
||||
RPC_U32(&msg, 0U) = (u32)ctrl;
|
||||
RPC_U16(&msg, 4U) = (u16)resource;
|
||||
RPC_SIZE(&msg) = 3U;
|
||||
|
||||
ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size);
|
||||
if (ret)
|
||||
printf("%s: ctrl:%d resource:%d: res:%d\n",
|
||||
__func__, ctrl, resource, RPC_R8(&msg));
|
||||
|
||||
if (val)
|
||||
*val = RPC_U32(&msg, 0U);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sc_rm_set_master_sid(sc_ipc_t ipc, sc_rsrc_t resource, sc_rm_sid_t sid)
|
||||
{
|
||||
struct udevice *dev = gd->arch.scu_dev;
|
||||
struct sc_rpc_msg_s msg;
|
||||
int size = sizeof(struct sc_rpc_msg_s);
|
||||
int ret;
|
||||
|
||||
RPC_VER(&msg) = SC_RPC_VERSION;
|
||||
RPC_SVC(&msg) = (u8)SC_RPC_SVC_RM;
|
||||
RPC_FUNC(&msg) = (u8)RM_FUNC_SET_MASTER_SID;
|
||||
RPC_U16(&msg, 0U) = (u16)resource;
|
||||
RPC_U16(&msg, 2U) = (u16)sid;
|
||||
RPC_SIZE(&msg) = 2U;
|
||||
|
||||
ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size);
|
||||
if (ret)
|
||||
printf("%s: resource:%d sid:%d: res:%d\n",
|
||||
__func__, resource, sid, RPC_R8(&msg));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void sc_misc_get_boot_dev(sc_ipc_t ipc, sc_rsrc_t *boot_dev)
|
||||
{
|
||||
struct udevice *dev = gd->arch.scu_dev;
|
||||
int size = sizeof(struct sc_rpc_msg_s);
|
||||
struct sc_rpc_msg_s msg;
|
||||
int ret;
|
||||
|
||||
if (!dev)
|
||||
hang();
|
||||
|
||||
RPC_VER(&msg) = SC_RPC_VERSION;
|
||||
RPC_SVC(&msg) = (u8)SC_RPC_SVC_MISC;
|
||||
RPC_FUNC(&msg) = (u8)MISC_FUNC_GET_BOOT_DEV;
|
||||
RPC_SIZE(&msg) = 1U;
|
||||
|
||||
ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size);
|
||||
if (ret)
|
||||
printf("%s: res:%d\n", __func__, RPC_R8(&msg));
|
||||
|
||||
if (boot_dev)
|
||||
*boot_dev = RPC_U16(&msg, 0U);
|
||||
}
|
||||
|
||||
void sc_misc_boot_status(sc_ipc_t ipc, sc_misc_boot_status_t status)
|
||||
{
|
||||
struct udevice *dev = gd->arch.scu_dev;
|
||||
int size = sizeof(struct sc_rpc_msg_s);
|
||||
struct sc_rpc_msg_s msg;
|
||||
int ret;
|
||||
|
||||
if (!dev)
|
||||
hang();
|
||||
|
||||
RPC_VER(&msg) = SC_RPC_VERSION;
|
||||
RPC_SVC(&msg) = (u8)SC_RPC_SVC_MISC;
|
||||
RPC_FUNC(&msg) = (u8)MISC_FUNC_BOOT_STATUS;
|
||||
RPC_U8(&msg, 0U) = (u8)status;
|
||||
RPC_SIZE(&msg) = 2U;
|
||||
|
||||
ret = misc_call(dev, SC_TRUE, &msg, size, &msg, size);
|
||||
if (ret)
|
||||
printf("%s: status:%d res:%d\n",
|
||||
__func__, status, RPC_R8(&msg));
|
||||
}
|
||||
|
||||
void sc_misc_build_info(sc_ipc_t ipc, u32 *build, u32 *commit)
|
||||
{
|
||||
struct udevice *dev = gd->arch.scu_dev;
|
||||
int size = sizeof(struct sc_rpc_msg_s);
|
||||
struct sc_rpc_msg_s msg;
|
||||
int ret;
|
||||
|
||||
if (!dev)
|
||||
hang();
|
||||
|
||||
RPC_VER(&msg) = SC_RPC_VERSION;
|
||||
RPC_SVC(&msg) = SC_RPC_SVC_MISC;
|
||||
RPC_FUNC(&msg) = MISC_FUNC_BUILD_INFO;
|
||||
RPC_SIZE(&msg) = 1;
|
||||
|
||||
ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size);
|
||||
if (ret < 0) {
|
||||
printf("%s: err: %d\n", __func__, ret);
|
||||
return;
|
||||
}
|
||||
|
||||
if (build)
|
||||
*build = RPC_U32(&msg, 0);
|
||||
if (commit)
|
||||
*commit = RPC_U32(&msg, 4);
|
||||
}
|
||||
|
||||
int sc_misc_otp_fuse_read(sc_ipc_t ipc, u32 word, u32 *val)
|
||||
{
|
||||
struct udevice *dev = gd->arch.scu_dev;
|
||||
int size = sizeof(struct sc_rpc_msg_s);
|
||||
struct sc_rpc_msg_s msg;
|
||||
int ret;
|
||||
|
||||
if (!dev)
|
||||
hang();
|
||||
|
||||
RPC_VER(&msg) = SC_RPC_VERSION;
|
||||
RPC_SVC(&msg) = SC_RPC_SVC_MISC;
|
||||
RPC_FUNC(&msg) = MISC_FUNC_OTP_FUSE_READ;
|
||||
RPC_U32(&msg, 0) = word;
|
||||
RPC_SIZE(&msg) = 2;
|
||||
|
||||
ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (val)
|
||||
*val = RPC_U32(&msg, 0U);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sc_misc_get_temp(sc_ipc_t ipc, sc_rsrc_t resource, sc_misc_temp_t temp,
|
||||
s16 *celsius, s8 *tenths)
|
||||
{
|
||||
struct udevice *dev = gd->arch.scu_dev;
|
||||
int size = sizeof(struct sc_rpc_msg_s);
|
||||
struct sc_rpc_msg_s msg;
|
||||
int ret;
|
||||
|
||||
RPC_VER(&msg) = SC_RPC_VERSION;
|
||||
RPC_SVC(&msg) = (u8)SC_RPC_SVC_MISC;
|
||||
RPC_FUNC(&msg) = (u8)MISC_FUNC_GET_TEMP;
|
||||
RPC_U16(&msg, 0U) = (u16)resource;
|
||||
RPC_U8(&msg, 2U) = (u8)temp;
|
||||
RPC_SIZE(&msg) = 2U;
|
||||
|
||||
ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (celsius)
|
||||
*celsius = RPC_I16(&msg, 0U);
|
||||
|
||||
if (tenths)
|
||||
*tenths = RPC_I8(&msg, 2U);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* RM */
|
||||
sc_bool_t sc_rm_is_memreg_owned(sc_ipc_t ipc, sc_rm_mr_t mr)
|
||||
{
|
||||
struct udevice *dev = gd->arch.scu_dev;
|
||||
int size = sizeof(struct sc_rpc_msg_s);
|
||||
struct sc_rpc_msg_s msg;
|
||||
int ret;
|
||||
sc_err_t result;
|
||||
|
||||
if (!dev)
|
||||
hang();
|
||||
|
||||
RPC_VER(&msg) = SC_RPC_VERSION;
|
||||
RPC_SVC(&msg) = (u8)SC_RPC_SVC_RM;
|
||||
RPC_FUNC(&msg) = (u8)RM_FUNC_IS_MEMREG_OWNED;
|
||||
RPC_U8(&msg, 0U) = (u8)mr;
|
||||
RPC_SIZE(&msg) = 2U;
|
||||
|
||||
ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size);
|
||||
result = RPC_R8(&msg);
|
||||
|
||||
if (result != 0 && result != 1) {
|
||||
printf("%s: mr:%d res:%d\n", __func__, mr, RPC_R8(&msg));
|
||||
if (ret)
|
||||
printf("%s: mr:%d res:%d\n", __func__, mr,
|
||||
RPC_R8(&msg));
|
||||
}
|
||||
|
||||
return (sc_bool_t)result;
|
||||
}
|
||||
|
||||
int sc_rm_find_memreg(sc_ipc_t ipc, sc_rm_mr_t *mr, sc_faddr_t addr_start,
|
||||
sc_faddr_t addr_end)
|
||||
{
|
||||
struct udevice *dev = gd->arch.scu_dev;
|
||||
int size = sizeof(struct sc_rpc_msg_s);
|
||||
struct sc_rpc_msg_s msg;
|
||||
int ret;
|
||||
|
||||
if (!dev)
|
||||
hang();
|
||||
|
||||
RPC_VER(&msg) = SC_RPC_VERSION;
|
||||
RPC_SVC(&msg) = (u8)(SC_RPC_SVC_RM);
|
||||
RPC_FUNC(&msg) = (u8)(RM_FUNC_FIND_MEMREG);
|
||||
RPC_U32(&msg, 0U) = (u32)(addr_start >> 32ULL);
|
||||
RPC_U32(&msg, 4U) = (u32)(addr_start);
|
||||
RPC_U32(&msg, 8U) = (u32)(addr_end >> 32ULL);
|
||||
RPC_U32(&msg, 12U) = (u32)(addr_end);
|
||||
RPC_SIZE(&msg) = 5U;
|
||||
|
||||
ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size);
|
||||
if (ret)
|
||||
printf("%s: start:0x%llx, end:0x%llx res:%d\n", __func__, addr_start, addr_end, RPC_R8(&msg));
|
||||
|
||||
if (mr)
|
||||
*mr = RPC_U8(&msg, 0U);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sc_rm_set_memreg_permissions(sc_ipc_t ipc, sc_rm_mr_t mr,
|
||||
sc_rm_pt_t pt, sc_rm_perm_t perm)
|
||||
{
|
||||
struct udevice *dev = gd->arch.scu_dev;
|
||||
int size = sizeof(struct sc_rpc_msg_s);
|
||||
struct sc_rpc_msg_s msg;
|
||||
int ret;
|
||||
|
||||
if (!dev)
|
||||
hang();
|
||||
|
||||
RPC_VER(&msg) = SC_RPC_VERSION;
|
||||
RPC_SVC(&msg) = (u8)(SC_RPC_SVC_RM);
|
||||
RPC_FUNC(&msg) = (u8)(RM_FUNC_SET_MEMREG_PERMISSIONS);
|
||||
RPC_U8(&msg, 0U) = (u8)(mr);
|
||||
RPC_U8(&msg, 1U) = (u8)(pt);
|
||||
RPC_U8(&msg, 2U) = (u8)(perm);
|
||||
RPC_SIZE(&msg) = 2U;
|
||||
|
||||
ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size);
|
||||
if (ret) {
|
||||
printf("%s: mr:%u, pt:%u, perm:%u, res:%d\n", __func__,
|
||||
mr, pt, perm, RPC_R8(&msg));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sc_rm_get_memreg_info(sc_ipc_t ipc, sc_rm_mr_t mr, sc_faddr_t *addr_start,
|
||||
sc_faddr_t *addr_end)
|
||||
{
|
||||
struct udevice *dev = gd->arch.scu_dev;
|
||||
int size = sizeof(struct sc_rpc_msg_s);
|
||||
struct sc_rpc_msg_s msg;
|
||||
int ret;
|
||||
|
||||
if (!dev)
|
||||
hang();
|
||||
|
||||
RPC_VER(&msg) = SC_RPC_VERSION;
|
||||
RPC_SVC(&msg) = (u8)SC_RPC_SVC_RM;
|
||||
RPC_FUNC(&msg) = (u8)RM_FUNC_GET_MEMREG_INFO;
|
||||
RPC_U8(&msg, 0U) = (u8)mr;
|
||||
RPC_SIZE(&msg) = 2U;
|
||||
|
||||
ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size);
|
||||
if (ret)
|
||||
printf("%s: mr:%d res:%d\n", __func__, mr, RPC_R8(&msg));
|
||||
|
||||
if (addr_start)
|
||||
*addr_start = ((u64)RPC_U32(&msg, 0U) << 32U) |
|
||||
RPC_U32(&msg, 4U);
|
||||
|
||||
if (addr_end)
|
||||
*addr_end = ((u64)RPC_U32(&msg, 8U) << 32U) |
|
||||
RPC_U32(&msg, 12U);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
sc_bool_t sc_rm_is_resource_owned(sc_ipc_t ipc, sc_rsrc_t resource)
|
||||
{
|
||||
struct udevice *dev = gd->arch.scu_dev;
|
||||
int size = sizeof(struct sc_rpc_msg_s);
|
||||
struct sc_rpc_msg_s msg;
|
||||
int ret;
|
||||
u8 result;
|
||||
|
||||
if (!dev)
|
||||
hang();
|
||||
|
||||
RPC_VER(&msg) = SC_RPC_VERSION;
|
||||
RPC_SVC(&msg) = (u8)SC_RPC_SVC_RM;
|
||||
RPC_FUNC(&msg) = (u8)RM_FUNC_IS_RESOURCE_OWNED;
|
||||
RPC_U16(&msg, 0U) = (u16)resource;
|
||||
RPC_SIZE(&msg) = 2U;
|
||||
|
||||
ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size);
|
||||
result = RPC_R8(&msg);
|
||||
if (result != 0 && result != 1) {
|
||||
printf("%s: resource:%d res:%d\n",
|
||||
__func__, resource, RPC_R8(&msg));
|
||||
if (ret)
|
||||
printf("%s: res:%d res:%d\n", __func__, resource,
|
||||
RPC_R8(&msg));
|
||||
}
|
||||
|
||||
return !!result;
|
||||
}
|
||||
|
||||
int sc_rm_partition_alloc(sc_ipc_t ipc, sc_rm_pt_t *pt, sc_bool_t secure,
|
||||
sc_bool_t isolated, sc_bool_t restricted,
|
||||
sc_bool_t grant, sc_bool_t coherent)
|
||||
{
|
||||
struct udevice *dev = gd->arch.scu_dev;
|
||||
struct sc_rpc_msg_s msg;
|
||||
int size = sizeof(struct sc_rpc_msg_s);
|
||||
int ret;
|
||||
|
||||
RPC_VER(&msg) = SC_RPC_VERSION;
|
||||
RPC_SVC(&msg) = (u8)SC_RPC_SVC_RM;
|
||||
RPC_FUNC(&msg) = (u8)RM_FUNC_PARTITION_ALLOC;
|
||||
RPC_U8(&msg, 0U) = B2U8(secure);
|
||||
RPC_U8(&msg, 1U) = B2U8(isolated);
|
||||
RPC_U8(&msg, 2U) = B2U8(restricted);
|
||||
RPC_U8(&msg, 3U) = B2U8(grant);
|
||||
RPC_U8(&msg, 4U) = B2U8(coherent);
|
||||
RPC_SIZE(&msg) = 3U;
|
||||
|
||||
ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size);
|
||||
if (ret) {
|
||||
printf("%s: secure:%u isolated:%u restricted:%u grant:%u coherent:%u res:%d\n",
|
||||
__func__, secure, isolated, restricted, grant, coherent,
|
||||
RPC_R8(&msg));
|
||||
}
|
||||
|
||||
if (pt)
|
||||
*pt = RPC_U8(&msg, 0U);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sc_rm_partition_free(sc_ipc_t ipc, sc_rm_pt_t pt)
|
||||
{
|
||||
struct udevice *dev = gd->arch.scu_dev;
|
||||
struct sc_rpc_msg_s msg;
|
||||
int size = sizeof(struct sc_rpc_msg_s);
|
||||
int ret;
|
||||
|
||||
RPC_VER(&msg) = SC_RPC_VERSION;
|
||||
RPC_SVC(&msg) = (u8)SC_RPC_SVC_RM;
|
||||
RPC_FUNC(&msg) = (u8)RM_FUNC_PARTITION_FREE;
|
||||
RPC_U8(&msg, 0U) = (u8)pt;
|
||||
RPC_SIZE(&msg) = 2U;
|
||||
|
||||
ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size);
|
||||
if (ret) {
|
||||
printf("%s: pt:%u res:%d\n",
|
||||
__func__, pt, RPC_R8(&msg));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sc_rm_get_partition(sc_ipc_t ipc, sc_rm_pt_t *pt)
|
||||
{
|
||||
struct udevice *dev = gd->arch.scu_dev;
|
||||
struct sc_rpc_msg_s msg;
|
||||
int size = sizeof(struct sc_rpc_msg_s);
|
||||
int ret;
|
||||
|
||||
RPC_VER(&msg) = SC_RPC_VERSION;
|
||||
RPC_SVC(&msg) = (u8)SC_RPC_SVC_RM;
|
||||
RPC_FUNC(&msg) = (u8)RM_FUNC_GET_PARTITION;
|
||||
RPC_SIZE(&msg) = 1U;
|
||||
|
||||
ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size);
|
||||
if (ret)
|
||||
printf("%s: res:%d\n", __func__, RPC_R8(&msg));
|
||||
|
||||
if (pt)
|
||||
*pt = RPC_U8(&msg, 0U);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sc_rm_set_parent(sc_ipc_t ipc, sc_rm_pt_t pt, sc_rm_pt_t pt_parent)
|
||||
{
|
||||
struct udevice *dev = gd->arch.scu_dev;
|
||||
struct sc_rpc_msg_s msg;
|
||||
int size = sizeof(struct sc_rpc_msg_s);
|
||||
int ret;
|
||||
|
||||
RPC_VER(&msg) = SC_RPC_VERSION;
|
||||
RPC_SVC(&msg) = (u8)SC_RPC_SVC_RM;
|
||||
RPC_FUNC(&msg) = (u8)RM_FUNC_SET_PARENT;
|
||||
RPC_U8(&msg, 0U) = (u8)pt;
|
||||
RPC_U8(&msg, 1U) = (u8)pt_parent;
|
||||
RPC_SIZE(&msg) = 2U;
|
||||
|
||||
ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size);
|
||||
if (ret) {
|
||||
printf("%s: pt:%u, pt_parent:%u, res:%d\n",
|
||||
__func__, pt, pt_parent, RPC_R8(&msg));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sc_rm_assign_resource(sc_ipc_t ipc, sc_rm_pt_t pt, sc_rsrc_t resource)
|
||||
{
|
||||
struct udevice *dev = gd->arch.scu_dev;
|
||||
struct sc_rpc_msg_s msg;
|
||||
int size = sizeof(struct sc_rpc_msg_s);
|
||||
int ret;
|
||||
|
||||
RPC_VER(&msg) = SC_RPC_VERSION;
|
||||
RPC_SVC(&msg) = (u8)SC_RPC_SVC_RM;
|
||||
RPC_FUNC(&msg) = (u8)RM_FUNC_ASSIGN_RESOURCE;
|
||||
RPC_U16(&msg, 0U) = (u16)resource;
|
||||
RPC_U8(&msg, 2U) = (u8)pt;
|
||||
RPC_SIZE(&msg) = 2U;
|
||||
|
||||
ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size);
|
||||
if (ret) {
|
||||
printf("%s: pt:%u, resource:%u, res:%d\n",
|
||||
__func__, pt, resource, RPC_R8(&msg));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sc_rm_assign_pad(sc_ipc_t ipc, sc_rm_pt_t pt, sc_pad_t pad)
|
||||
{
|
||||
struct udevice *dev = gd->arch.scu_dev;
|
||||
struct sc_rpc_msg_s msg;
|
||||
int size = sizeof(struct sc_rpc_msg_s);
|
||||
int ret;
|
||||
|
||||
RPC_VER(&msg) = SC_RPC_VERSION;
|
||||
RPC_SVC(&msg) = (u8)SC_RPC_SVC_RM;
|
||||
RPC_FUNC(&msg) = (u8)RM_FUNC_ASSIGN_PAD;
|
||||
RPC_U16(&msg, 0U) = (u16)pad;
|
||||
RPC_U8(&msg, 2U) = (u8)pt;
|
||||
RPC_SIZE(&msg) = 2U;
|
||||
|
||||
ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size);
|
||||
if (ret) {
|
||||
printf("%s: pt:%u, pad:%u, res:%d\n",
|
||||
__func__, pt, pad, RPC_R8(&msg));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
sc_bool_t sc_rm_is_pad_owned(sc_ipc_t ipc, sc_pad_t pad)
|
||||
{
|
||||
struct udevice *dev = gd->arch.scu_dev;
|
||||
struct sc_rpc_msg_s msg;
|
||||
int size = sizeof(struct sc_rpc_msg_s);
|
||||
int ret;
|
||||
u8 result;
|
||||
|
||||
RPC_VER(&msg) = SC_RPC_VERSION;
|
||||
RPC_SVC(&msg) = (u8)SC_RPC_SVC_RM;
|
||||
RPC_FUNC(&msg) = (u8)RM_FUNC_IS_PAD_OWNED;
|
||||
RPC_U8(&msg, 0U) = (u8)pad;
|
||||
RPC_SIZE(&msg) = 2U;
|
||||
|
||||
ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size);
|
||||
result = RPC_R8(&msg);
|
||||
if (result != 0 && result != 1) {
|
||||
printf("%s: pad:%d res:%d\n", __func__, pad, RPC_R8(&msg));
|
||||
if (ret) {
|
||||
printf("%s: pad:%d res:%d\n", __func__,
|
||||
pad, RPC_R8(&msg));
|
||||
}
|
||||
}
|
||||
|
||||
return !!result;
|
||||
}
|
||||
|
||||
int sc_rm_get_resource_owner(sc_ipc_t ipc, sc_rsrc_t resource,
|
||||
sc_rm_pt_t *pt)
|
||||
{
|
||||
struct udevice *dev = gd->arch.scu_dev;
|
||||
struct sc_rpc_msg_s msg;
|
||||
int size = sizeof(struct sc_rpc_msg_s);
|
||||
int ret;
|
||||
|
||||
RPC_VER(&msg) = SC_RPC_VERSION;
|
||||
RPC_SVC(&msg) = (u8)SC_RPC_SVC_RM;
|
||||
RPC_FUNC(&msg) = (u8)RM_FUNC_GET_RESOURCE_OWNER;
|
||||
RPC_U16(&msg, 0U) = (u16)resource;
|
||||
RPC_SIZE(&msg) = 2U;
|
||||
|
||||
ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size);
|
||||
if (pt)
|
||||
*pt = RPC_U8(&msg, 0U);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sc_pm_cpu_start(sc_ipc_t ipc, sc_rsrc_t resource, sc_bool_t enable,
|
||||
sc_faddr_t address)
|
||||
{
|
||||
struct udevice *dev = gd->arch.scu_dev;
|
||||
struct sc_rpc_msg_s msg;
|
||||
int size = sizeof(struct sc_rpc_msg_s);
|
||||
int ret;
|
||||
|
||||
RPC_VER(&msg) = SC_RPC_VERSION;
|
||||
RPC_SVC(&msg) = (u8)SC_RPC_SVC_PM;
|
||||
RPC_FUNC(&msg) = (u8)PM_FUNC_CPU_START;
|
||||
RPC_U32(&msg, 0U) = (u32)(address >> 32ULL);
|
||||
RPC_U32(&msg, 4U) = (u32)address;
|
||||
RPC_U16(&msg, 8U) = (u16)resource;
|
||||
RPC_U8(&msg, 10U) = B2U8(enable);
|
||||
RPC_SIZE(&msg) = 4U;
|
||||
|
||||
ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size);
|
||||
if (ret) {
|
||||
printf("%s: resource:%d address:0x%llx: res:%d\n",
|
||||
__func__, resource, address, RPC_R8(&msg));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sc_pm_get_resource_power_mode(sc_ipc_t ipc, sc_rsrc_t resource,
|
||||
sc_pm_power_mode_t *mode)
|
||||
{
|
||||
struct udevice *dev = gd->arch.scu_dev;
|
||||
struct sc_rpc_msg_s msg;
|
||||
int size = sizeof(struct sc_rpc_msg_s);
|
||||
int ret;
|
||||
|
||||
RPC_VER(&msg) = SC_RPC_VERSION;
|
||||
RPC_SVC(&msg) = (u8)SC_RPC_SVC_PM;
|
||||
RPC_FUNC(&msg) = (u8)PM_FUNC_GET_RESOURCE_POWER_MODE;
|
||||
RPC_U16(&msg, 0U) = (u16)resource;
|
||||
RPC_SIZE(&msg) = 2U;
|
||||
|
||||
ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size);
|
||||
if (ret) {
|
||||
printf("%s: resource:%d: res:%d\n",
|
||||
__func__, resource, RPC_R8(&msg));
|
||||
}
|
||||
|
||||
if (mode)
|
||||
*mode = RPC_U8(&msg, 0U);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sc_seco_authenticate(sc_ipc_t ipc, sc_seco_auth_cmd_t cmd,
|
||||
sc_faddr_t addr)
|
||||
{
|
||||
struct udevice *dev = gd->arch.scu_dev;
|
||||
struct sc_rpc_msg_s msg;
|
||||
int size = sizeof(struct sc_rpc_msg_s);
|
||||
int ret;
|
||||
|
||||
RPC_VER(&msg) = SC_RPC_VERSION;
|
||||
RPC_SVC(&msg) = (u8)SC_RPC_SVC_SECO;
|
||||
RPC_FUNC(&msg) = (u8)SECO_FUNC_AUTHENTICATE;
|
||||
RPC_U32(&msg, 0U) = (u32)(addr >> 32ULL);
|
||||
RPC_U32(&msg, 4U) = (u32)addr;
|
||||
RPC_U8(&msg, 8U) = (u8)cmd;
|
||||
RPC_SIZE(&msg) = 4U;
|
||||
|
||||
ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size);
|
||||
if (ret)
|
||||
printf("%s: res:%d\n", __func__, RPC_R8(&msg));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sc_seco_forward_lifecycle(sc_ipc_t ipc, u32 change)
|
||||
{
|
||||
struct udevice *dev = gd->arch.scu_dev;
|
||||
struct sc_rpc_msg_s msg;
|
||||
int size = sizeof(struct sc_rpc_msg_s);
|
||||
int ret;
|
||||
|
||||
RPC_VER(&msg) = SC_RPC_VERSION;
|
||||
RPC_SVC(&msg) = (u8)SC_RPC_SVC_SECO;
|
||||
RPC_FUNC(&msg) = (u8)SECO_FUNC_FORWARD_LIFECYCLE;
|
||||
RPC_U32(&msg, 0U) = (u32)change;
|
||||
RPC_SIZE(&msg) = 2U;
|
||||
|
||||
ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size);
|
||||
if (ret) {
|
||||
printf("%s: change:%u, res:%d\n", __func__,
|
||||
change, RPC_R8(&msg));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sc_seco_chip_info(sc_ipc_t ipc, u16 *lc, u16 *monotonic, u32 *uid_l,
|
||||
u32 *uid_h)
|
||||
{
|
||||
struct udevice *dev = gd->arch.scu_dev;
|
||||
struct sc_rpc_msg_s msg;
|
||||
int size = sizeof(struct sc_rpc_msg_s);
|
||||
int ret;
|
||||
|
||||
RPC_VER(&msg) = SC_RPC_VERSION;
|
||||
RPC_SVC(&msg) = (u8)SC_RPC_SVC_SECO;
|
||||
RPC_FUNC(&msg) = (u8)SECO_FUNC_CHIP_INFO;
|
||||
RPC_SIZE(&msg) = 1U;
|
||||
|
||||
ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size);
|
||||
if (ret)
|
||||
printf("%s: res:%d\n", __func__, RPC_R8(&msg));
|
||||
|
||||
if (uid_l)
|
||||
*uid_l = RPC_U32(&msg, 0U);
|
||||
|
||||
if (uid_h)
|
||||
*uid_h = RPC_U32(&msg, 4U);
|
||||
|
||||
if (lc)
|
||||
*lc = RPC_U16(&msg, 8U);
|
||||
|
||||
if (monotonic)
|
||||
*monotonic = RPC_U16(&msg, 10U);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void sc_seco_build_info(sc_ipc_t ipc, u32 *version, u32 *commit)
|
||||
{
|
||||
struct udevice *dev = gd->arch.scu_dev;
|
||||
struct sc_rpc_msg_s msg;
|
||||
int size = sizeof(struct sc_rpc_msg_s);
|
||||
|
||||
RPC_VER(&msg) = SC_RPC_VERSION;
|
||||
RPC_SVC(&msg) = (u8)(SC_RPC_SVC_SECO);
|
||||
RPC_FUNC(&msg) = (u8)(SECO_FUNC_BUILD_INFO);
|
||||
RPC_SIZE(&msg) = 1U;
|
||||
|
||||
misc_call(dev, SC_FALSE, &msg, size, &msg, size);
|
||||
|
||||
if (version)
|
||||
*version = RPC_U32(&msg, 0U);
|
||||
|
||||
if (commit)
|
||||
*commit = RPC_U32(&msg, 4U);
|
||||
}
|
||||
|
||||
int sc_seco_get_event(sc_ipc_t ipc, u8 idx, u32 *event)
|
||||
{
|
||||
struct udevice *dev = gd->arch.scu_dev;
|
||||
struct sc_rpc_msg_s msg;
|
||||
int size = sizeof(struct sc_rpc_msg_s);
|
||||
int ret;
|
||||
|
||||
RPC_VER(&msg) = SC_RPC_VERSION;
|
||||
RPC_SVC(&msg) = (u8)SC_RPC_SVC_SECO;
|
||||
RPC_FUNC(&msg) = (u8)SECO_FUNC_GET_EVENT;
|
||||
RPC_U8(&msg, 0U) = (u8)idx;
|
||||
RPC_SIZE(&msg) = 2U;
|
||||
|
||||
ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size);
|
||||
if (ret)
|
||||
printf("%s: idx: %u, res:%d\n", __func__, idx, RPC_R8(&msg));
|
||||
|
||||
if (event)
|
||||
*event = RPC_U32(&msg, 0U);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sc_seco_gen_key_blob(sc_ipc_t ipc, u32 id, sc_faddr_t load_addr,
|
||||
sc_faddr_t export_addr, u16 max_size)
|
||||
{
|
||||
struct udevice *dev = gd->arch.scu_dev;
|
||||
struct sc_rpc_msg_s msg;
|
||||
int size = sizeof(struct sc_rpc_msg_s);
|
||||
int ret;
|
||||
|
||||
RPC_VER(&msg) = SC_RPC_VERSION;
|
||||
RPC_SVC(&msg) = (u8)SC_RPC_SVC_SECO;
|
||||
RPC_FUNC(&msg) = (u8)SECO_FUNC_GEN_KEY_BLOB;
|
||||
RPC_U32(&msg, 0U) = (u32)(load_addr >> 32ULL);
|
||||
RPC_U32(&msg, 4U) = (u32)load_addr;
|
||||
RPC_U32(&msg, 8U) = (u32)(export_addr >> 32ULL);
|
||||
RPC_U32(&msg, 12U) = (u32)export_addr;
|
||||
RPC_U32(&msg, 16U) = (u32)id;
|
||||
RPC_U16(&msg, 20U) = (u16)max_size;
|
||||
RPC_SIZE(&msg) = 7U;
|
||||
|
||||
ret = misc_call(dev, SC_FALSE, &msg, size, &msg, size);
|
||||
if (ret) {
|
||||
printf("%s: id: %u, load_addr 0x%llx, export_addr 0x%llx, res:%d\n",
|
||||
__func__, id, load_addr, export_addr, RPC_R8(&msg));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* JZ4780 EFUSE driver
|
||||
*
|
||||
* Copyright (c) 2014 Imagination Technologies
|
||||
* Author: Alex Smith <alex.smith@imgtec.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <errno.h>
|
||||
#include <mach/jz4780.h>
|
||||
#include <wait_bit.h>
|
||||
|
||||
#define EFUSE_EFUCTRL 0xd0
|
||||
#define EFUSE_EFUCFG 0xd4
|
||||
#define EFUSE_EFUSTATE 0xd8
|
||||
#define EFUSE_EFUDATA(n) (0xdc + ((n) * 4))
|
||||
|
||||
#define EFUSE_EFUCTRL_RD_EN BIT(0)
|
||||
#define EFUSE_EFUCTRL_LEN_BIT 16
|
||||
#define EFUSE_EFUCTRL_LEN_MASK 0x1f
|
||||
#define EFUSE_EFUCTRL_ADDR_BIT 21
|
||||
#define EFUSE_EFUCTRL_ADDR_MASK 0x1ff
|
||||
#define EFUSE_EFUCTRL_CS BIT(30)
|
||||
|
||||
#define EFUSE_EFUCFG_RD_STROBE_BIT 16
|
||||
#define EFUSE_EFUCFG_RD_STROBE_MASK 0xf
|
||||
#define EFUSE_EFUCFG_RD_ADJ_BIT 20
|
||||
#define EFUSE_EFUCFG_RD_ADJ_MASK 0xf
|
||||
|
||||
#define EFUSE_EFUSTATE_RD_DONE BIT(0)
|
||||
|
||||
static void jz4780_efuse_read_chunk(size_t addr, size_t count, u8 *buf)
|
||||
{
|
||||
void __iomem *regs = (void __iomem *)NEMC_BASE;
|
||||
size_t i;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
val = EFUSE_EFUCTRL_RD_EN |
|
||||
((count - 1) << EFUSE_EFUCTRL_LEN_BIT) |
|
||||
(addr << EFUSE_EFUCTRL_ADDR_BIT) |
|
||||
((addr > 0x200) ? EFUSE_EFUCTRL_CS : 0);
|
||||
writel(val, regs + EFUSE_EFUCTRL);
|
||||
|
||||
ret = wait_for_bit_le32(regs + EFUSE_EFUSTATE,
|
||||
EFUSE_EFUSTATE_RD_DONE, true, 10000, false);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
if ((count % 4) == 0) {
|
||||
for (i = 0; i < count / 4; i++) {
|
||||
val = readl(regs + EFUSE_EFUDATA(i));
|
||||
put_unaligned(val, (u32 *)(buf + (i * 4)));
|
||||
}
|
||||
} else {
|
||||
val = readl(regs + EFUSE_EFUDATA(0));
|
||||
if (count > 2)
|
||||
buf[2] = (val >> 16) & 0xff;
|
||||
if (count > 1)
|
||||
buf[1] = (val >> 8) & 0xff;
|
||||
buf[0] = val & 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int jz4780_efuse_chunk_size(size_t count)
|
||||
{
|
||||
if (count >= 32)
|
||||
return 32;
|
||||
else if ((count / 4) > 0)
|
||||
return (count / 4) * 4;
|
||||
else
|
||||
return count % 4;
|
||||
}
|
||||
|
||||
void jz4780_efuse_read(size_t addr, size_t count, u8 *buf)
|
||||
{
|
||||
size_t chunk;
|
||||
|
||||
while (count > 0) {
|
||||
chunk = jz4780_efuse_chunk_size(count);
|
||||
jz4780_efuse_read_chunk(addr, chunk, buf);
|
||||
addr += chunk;
|
||||
buf += chunk;
|
||||
count -= chunk;
|
||||
}
|
||||
}
|
||||
|
||||
void jz4780_efuse_init(u32 ahb2_rate)
|
||||
{
|
||||
void __iomem *regs = (void __iomem *)NEMC_BASE;
|
||||
u32 rd_adj, rd_strobe, tmp;
|
||||
|
||||
rd_adj = (((6500 * (ahb2_rate / 1000000)) / 1000000) + 0xf) / 2;
|
||||
tmp = (((35000 * (ahb2_rate / 1000000)) / 1000000) - 4) - rd_adj;
|
||||
rd_strobe = ((tmp + 0xf) / 2 < 7) ? 7 : (tmp + 0xf) / 2;
|
||||
|
||||
tmp = (rd_adj << EFUSE_EFUCFG_RD_ADJ_BIT) |
|
||||
(rd_strobe << EFUSE_EFUCFG_RD_STROBE_BIT);
|
||||
writel(tmp, regs + EFUSE_EFUCFG);
|
||||
}
|
||||
@@ -0,0 +1,388 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Texas Instruments' K3 Clas 0 Adaptive Voltage Scaling driver
|
||||
*
|
||||
* Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
|
||||
* Tero Kristo <t-kristo@ti.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <asm/io.h>
|
||||
#include <i2c.h>
|
||||
#include <k3-avs.h>
|
||||
#include <power/regulator.h>
|
||||
|
||||
#define AM6_VTM_DEVINFO(i) (priv->base + 0x100 + 0x20 * (i))
|
||||
#define AM6_VTM_OPPVID_VD(i) (priv->base + 0x104 + 0x20 * (i))
|
||||
|
||||
#define AM6_VTM_AVS0_SUPPORTED BIT(12)
|
||||
|
||||
#define AM6_VTM_OPP_SHIFT(opp) (8 * (opp))
|
||||
#define AM6_VTM_OPP_MASK 0xff
|
||||
|
||||
#define VD_FLAG_INIT_DONE BIT(0)
|
||||
|
||||
struct k3_avs_privdata {
|
||||
void *base;
|
||||
struct vd_config *vd_config;
|
||||
};
|
||||
|
||||
struct opp {
|
||||
u32 freq;
|
||||
u32 volt;
|
||||
};
|
||||
|
||||
struct vd_data {
|
||||
int id;
|
||||
u8 opp;
|
||||
u8 flags;
|
||||
int dev_id;
|
||||
int clk_id;
|
||||
struct opp opps[NUM_OPPS];
|
||||
struct udevice *supply;
|
||||
};
|
||||
|
||||
struct vd_config {
|
||||
struct vd_data *vds;
|
||||
u32 (*efuse_xlate)(struct k3_avs_privdata *priv, int idx, int opp);
|
||||
};
|
||||
|
||||
static struct k3_avs_privdata *k3_avs_priv;
|
||||
|
||||
/**
|
||||
* am6_efuse_voltage: read efuse voltage from VTM
|
||||
* @priv: driver private data
|
||||
* @idx: VD to read efuse for
|
||||
* @opp: opp id to read
|
||||
*
|
||||
* Reads efuse value for the specified OPP, and converts the register
|
||||
* value to a voltage. Returns the voltage in uV, or 0 if nominal voltage
|
||||
* should be used.
|
||||
*
|
||||
* Efuse val to volt conversion logic:
|
||||
*
|
||||
* val > 171 volt increments in 20mV steps with base 171 => 1.66V
|
||||
* val between 115 to 11 increments in 10mV steps with base 115 => 1.1V
|
||||
* val between 15 to 115 increments in 5mV steps with base 15 => .6V
|
||||
* val between 1 to 15 increments in 20mv steps with base 0 => .3V
|
||||
* val 0 is invalid
|
||||
*/
|
||||
static u32 am6_efuse_xlate(struct k3_avs_privdata *priv, int idx, int opp)
|
||||
{
|
||||
u32 val = readl(AM6_VTM_OPPVID_VD(idx));
|
||||
|
||||
val >>= AM6_VTM_OPP_SHIFT(opp);
|
||||
val &= AM6_VTM_OPP_MASK;
|
||||
|
||||
if (!val)
|
||||
return 0;
|
||||
|
||||
if (val > 171)
|
||||
return 1660000 + 20000 * (val - 171);
|
||||
|
||||
if (val > 115)
|
||||
return 1100000 + 10000 * (val - 115);
|
||||
|
||||
if (val > 15)
|
||||
return 600000 + 5000 * (val - 15);
|
||||
|
||||
return 300000 + 20000 * val;
|
||||
}
|
||||
|
||||
static int k3_avs_program_voltage(struct k3_avs_privdata *priv,
|
||||
struct vd_data *vd,
|
||||
int opp_id)
|
||||
{
|
||||
u32 volt = vd->opps[opp_id].volt;
|
||||
struct vd_data *vd2;
|
||||
|
||||
if (!vd->supply)
|
||||
return -ENODEV;
|
||||
|
||||
vd->opp = opp_id;
|
||||
vd->flags |= VD_FLAG_INIT_DONE;
|
||||
|
||||
/* Take care of ganged rails and pick the Max amongst them*/
|
||||
for (vd2 = priv->vd_config->vds; vd2->id >= 0; vd2++) {
|
||||
if (vd == vd2)
|
||||
continue;
|
||||
|
||||
if (vd2->supply != vd->supply)
|
||||
continue;
|
||||
|
||||
if (vd2->opps[vd2->opp].volt > volt)
|
||||
volt = vd2->opps[vd2->opp].volt;
|
||||
|
||||
vd2->flags |= VD_FLAG_INIT_DONE;
|
||||
}
|
||||
|
||||
return regulator_set_value(vd->supply, volt);
|
||||
}
|
||||
|
||||
static struct vd_data *get_vd(struct k3_avs_privdata *priv, int idx)
|
||||
{
|
||||
struct vd_data *vd;
|
||||
|
||||
for (vd = priv->vd_config->vds; vd->id >= 0 && vd->id != idx; vd++)
|
||||
;
|
||||
|
||||
if (vd->id < 0)
|
||||
return NULL;
|
||||
|
||||
return vd;
|
||||
}
|
||||
|
||||
/**
|
||||
* k3_avs_set_opp: Sets the voltage for an arbitrary VD rail
|
||||
* @dev: AVS device
|
||||
* @vdd_id: voltage domain ID
|
||||
* @opp_id: OPP ID
|
||||
*
|
||||
* Programs the desired OPP value for the defined voltage rail. This
|
||||
* should be called from board files if reconfiguration is desired.
|
||||
* Returns 0 on success, negative error value on failure.
|
||||
*/
|
||||
int k3_avs_set_opp(struct udevice *dev, int vdd_id, int opp_id)
|
||||
{
|
||||
struct k3_avs_privdata *priv = dev_get_priv(dev);
|
||||
struct vd_data *vd;
|
||||
|
||||
vd = get_vd(priv, vdd_id);
|
||||
if (!vd)
|
||||
return -EINVAL;
|
||||
|
||||
return k3_avs_program_voltage(priv, vd, opp_id);
|
||||
}
|
||||
|
||||
static int match_opp(struct vd_data *vd, u32 freq)
|
||||
{
|
||||
struct opp *opp;
|
||||
int opp_id;
|
||||
|
||||
for (opp_id = 0; opp_id < NUM_OPPS; opp_id++) {
|
||||
opp = &vd->opps[opp_id];
|
||||
if (opp->freq == freq)
|
||||
return opp_id;
|
||||
}
|
||||
|
||||
printf("No matching OPP found for freq %d.\n", freq);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* k3_avs_notify_freq: Notify clock rate change towards AVS subsystem
|
||||
* @dev_id: Device ID for the clock to be changed
|
||||
* @clk_id: Clock ID for the clock to be changed
|
||||
* @freq: New frequency for clock
|
||||
*
|
||||
* Checks if the provided clock is the MPU clock or not, if not, return
|
||||
* immediately. If MPU clock is provided, maps the provided MPU frequency
|
||||
* towards an MPU OPP, and programs the voltage to the regulator. Return 0
|
||||
* on success, negative error value on failure.
|
||||
*/
|
||||
int k3_avs_notify_freq(int dev_id, int clk_id, u32 freq)
|
||||
{
|
||||
int opp_id;
|
||||
struct k3_avs_privdata *priv = k3_avs_priv;
|
||||
struct vd_data *vd;
|
||||
|
||||
for (vd = priv->vd_config->vds; vd->id >= 0; vd++) {
|
||||
if (vd->dev_id != dev_id || vd->clk_id != clk_id)
|
||||
continue;
|
||||
|
||||
opp_id = match_opp(vd, freq);
|
||||
if (opp_id < 0)
|
||||
return opp_id;
|
||||
|
||||
vd->opp = opp_id;
|
||||
return k3_avs_program_voltage(priv, vd, opp_id);
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int k3_avs_configure(struct udevice *dev, struct k3_avs_privdata *priv)
|
||||
{
|
||||
struct vd_config *conf;
|
||||
int ret;
|
||||
char pname[20];
|
||||
struct vd_data *vd;
|
||||
|
||||
conf = (void *)dev_get_driver_data(dev);
|
||||
|
||||
priv->vd_config = conf;
|
||||
|
||||
for (vd = conf->vds; vd->id >= 0; vd++) {
|
||||
sprintf(pname, "vdd-supply-%d", vd->id);
|
||||
ret = device_get_supply_regulator(dev, pname, &vd->supply);
|
||||
if (ret)
|
||||
dev_warn(dev, "supply not found for VD%d.\n", vd->id);
|
||||
|
||||
sprintf(pname, "ti,default-opp-%d", vd->id);
|
||||
ret = dev_read_u32_default(dev, pname, -1);
|
||||
if (ret != -1)
|
||||
vd->opp = ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* k3_avs_probe: parses VD info from VTM, and re-configures the OPP data
|
||||
*
|
||||
* Parses all VDs on a device calculating the AVS class-0 voltages for them,
|
||||
* and updates the vd_data based on this. The vd_data itself shall be used
|
||||
* to program the required OPPs later on. Returns 0 on success, negative
|
||||
* error value on failure.
|
||||
*/
|
||||
static int k3_avs_probe(struct udevice *dev)
|
||||
{
|
||||
int opp_id;
|
||||
u32 volt;
|
||||
struct opp *opp;
|
||||
struct k3_avs_privdata *priv;
|
||||
struct vd_data *vd;
|
||||
int ret;
|
||||
|
||||
priv = dev_get_priv(dev);
|
||||
|
||||
k3_avs_priv = priv;
|
||||
|
||||
ret = k3_avs_configure(dev, priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
priv->base = dev_read_addr_ptr(dev);
|
||||
if (!priv->base)
|
||||
return -ENODEV;
|
||||
|
||||
for (vd = priv->vd_config->vds; vd->id >= 0; vd++) {
|
||||
if (!(readl(AM6_VTM_DEVINFO(vd->id)) &
|
||||
AM6_VTM_AVS0_SUPPORTED)) {
|
||||
dev_warn(dev, "AVS-class 0 not supported for VD%d\n",
|
||||
vd->id);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (opp_id = 0; opp_id < NUM_OPPS; opp_id++) {
|
||||
opp = &vd->opps[opp_id];
|
||||
|
||||
if (!opp->freq)
|
||||
continue;
|
||||
|
||||
volt = priv->vd_config->efuse_xlate(priv, vd->id,
|
||||
opp_id);
|
||||
if (volt)
|
||||
opp->volt = volt;
|
||||
}
|
||||
}
|
||||
|
||||
for (vd = priv->vd_config->vds; vd->id >= 0; vd++) {
|
||||
if (vd->flags & VD_FLAG_INIT_DONE)
|
||||
continue;
|
||||
|
||||
k3_avs_program_voltage(priv, vd, vd->opp);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct vd_data am654_vd_data[] = {
|
||||
{
|
||||
.id = AM6_VDD_CORE,
|
||||
.dev_id = 82, /* AM6_DEV_CBASS0 */
|
||||
.clk_id = 0, /* main sysclk0 */
|
||||
.opp = AM6_OPP_NOM,
|
||||
.opps = {
|
||||
[AM6_OPP_NOM] = {
|
||||
.volt = 1000000,
|
||||
.freq = 250000000, /* CBASS0 */
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
.id = AM6_VDD_MPU0,
|
||||
.dev_id = 202, /* AM6_DEV_COMPUTE_CLUSTER_A53_0 */
|
||||
.clk_id = 0, /* ARM clock */
|
||||
.opp = AM6_OPP_NOM,
|
||||
.opps = {
|
||||
[AM6_OPP_NOM] = {
|
||||
.volt = 1000000,
|
||||
.freq = 800000000,
|
||||
},
|
||||
[AM6_OPP_OD] = {
|
||||
.volt = 1100000,
|
||||
.freq = 1000000000,
|
||||
},
|
||||
[AM6_OPP_TURBO] = {
|
||||
.volt = 1220000,
|
||||
.freq = 1100000000,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
.id = AM6_VDD_MPU1,
|
||||
.opp = AM6_OPP_NOM,
|
||||
.dev_id = 204, /* AM6_DEV_COMPUTE_CLUSTER_A53_2 */
|
||||
.clk_id = 0, /* ARM clock */
|
||||
.opps = {
|
||||
[AM6_OPP_NOM] = {
|
||||
.volt = 1000000,
|
||||
.freq = 800000000,
|
||||
},
|
||||
[AM6_OPP_OD] = {
|
||||
.volt = 1100000,
|
||||
.freq = 1000000000,
|
||||
},
|
||||
[AM6_OPP_TURBO] = {
|
||||
.volt = 1220000,
|
||||
.freq = 1100000000,
|
||||
},
|
||||
},
|
||||
},
|
||||
{ .id = -1 },
|
||||
};
|
||||
|
||||
static struct vd_data j721e_vd_data[] = {
|
||||
{
|
||||
.id = J721E_VDD_MPU,
|
||||
.opp = AM6_OPP_NOM,
|
||||
.dev_id = 202, /* J721E_DEV_A72SS0_CORE0 */
|
||||
.clk_id = 2, /* ARM clock */
|
||||
.opps = {
|
||||
[AM6_OPP_NOM] = {
|
||||
.volt = 880000, /* TBD in DM */
|
||||
.freq = 2000000000,
|
||||
},
|
||||
},
|
||||
},
|
||||
{ .id = -1 },
|
||||
};
|
||||
|
||||
static struct vd_config j721e_vd_config = {
|
||||
.efuse_xlate = am6_efuse_xlate,
|
||||
.vds = j721e_vd_data,
|
||||
};
|
||||
|
||||
static struct vd_config am654_vd_config = {
|
||||
.efuse_xlate = am6_efuse_xlate,
|
||||
.vds = am654_vd_data,
|
||||
};
|
||||
|
||||
static const struct udevice_id k3_avs_ids[] = {
|
||||
{ .compatible = "ti,am654-avs", .data = (ulong)&am654_vd_config },
|
||||
{ .compatible = "ti,j721e-avs", .data = (ulong)&j721e_vd_config },
|
||||
{}
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(k3_avs) = {
|
||||
.name = "k3_avs",
|
||||
.of_match = k3_avs_ids,
|
||||
.id = UCLASS_MISC,
|
||||
.probe = k3_avs_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct k3_avs_privdata),
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2010 Stefano Babic <sbabic@denx.de>
|
||||
*/
|
||||
|
||||
|
||||
#include <config.h>
|
||||
#include <common.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/types.h>
|
||||
#include <i2c.h>
|
||||
#include <mc9sdz60.h>
|
||||
|
||||
#ifndef CONFIG_SYS_FSL_MC9SDZ60_I2C_ADDR
|
||||
#error "You have to configure I2C address for MC9SDZ60"
|
||||
#endif
|
||||
|
||||
|
||||
u8 mc9sdz60_reg_read(enum mc9sdz60_reg reg)
|
||||
{
|
||||
u8 val;
|
||||
|
||||
if (i2c_read(CONFIG_SYS_FSL_MC9SDZ60_I2C_ADDR, reg, 1, &val, 1)) {
|
||||
puts("Error reading MC9SDZ60 register\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
void mc9sdz60_reg_write(enum mc9sdz60_reg reg, u8 val)
|
||||
{
|
||||
i2c_write(CONFIG_SYS_FSL_MC9SDZ60_I2C_ADDR, reg, 1, &val, 1);
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2019, Microchip Technology, Inc.
|
||||
* Author: Eugen Hristev <eugen.hristev@microchip.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <misc.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
struct microchip_flexcom_regs {
|
||||
u32 cr;
|
||||
};
|
||||
|
||||
struct microchip_flexcom_platdata {
|
||||
struct microchip_flexcom_regs *regs;
|
||||
u32 flexcom_mode;
|
||||
};
|
||||
|
||||
static int microchip_flexcom_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct microchip_flexcom_platdata *plat = dev_get_platdata(dev);
|
||||
int ret;
|
||||
|
||||
plat->regs = map_physmem(devfdt_get_addr(dev),
|
||||
sizeof(struct microchip_flexcom_regs),
|
||||
MAP_NOCACHE);
|
||||
|
||||
ret = dev_read_u32(dev, "atmel,flexcom-mode", &plat->flexcom_mode);
|
||||
|
||||
if (IS_ERR_VALUE(ret)) {
|
||||
debug("Missing atmel,flexcom-mode property\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* The mode must have only 2 bits. If any other bits are set,
|
||||
* the value is not supported.
|
||||
*/
|
||||
if (plat->flexcom_mode & 0xfffffffc) {
|
||||
debug("Wrong atmel,flexcom-mode property\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
writel(plat->flexcom_mode, &plat->regs->cr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id microchip_flexcom_ids[] = {
|
||||
{ .compatible = "atmel,sama5d2-flexcom" },
|
||||
{ .compatible = "microchip,flexcom" },
|
||||
{}
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(microchip_flexcom) = {
|
||||
.name = "microchip_flexcom",
|
||||
.id = UCLASS_MISC,
|
||||
.of_match = microchip_flexcom_ids,
|
||||
.ofdata_to_platdata = microchip_flexcom_ofdata_to_platdata,
|
||||
.platdata_auto_alloc_size = sizeof(struct microchip_flexcom_platdata),
|
||||
};
|
||||
@@ -0,0 +1,74 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <misc.h>
|
||||
|
||||
/*
|
||||
* Implement a miscellaneous uclass for those do not fit other more
|
||||
* general classes. A set of generic read, write and ioctl methods may
|
||||
* be used to access the device.
|
||||
*/
|
||||
|
||||
int misc_read(struct udevice *dev, int offset, void *buf, int size)
|
||||
{
|
||||
const struct misc_ops *ops = device_get_ops(dev);
|
||||
|
||||
if (!ops->read)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->read(dev, offset, buf, size);
|
||||
}
|
||||
|
||||
int misc_write(struct udevice *dev, int offset, void *buf, int size)
|
||||
{
|
||||
const struct misc_ops *ops = device_get_ops(dev);
|
||||
|
||||
if (!ops->write)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->write(dev, offset, buf, size);
|
||||
}
|
||||
|
||||
int misc_ioctl(struct udevice *dev, unsigned long request, void *buf)
|
||||
{
|
||||
const struct misc_ops *ops = device_get_ops(dev);
|
||||
|
||||
if (!ops->ioctl)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->ioctl(dev, request, buf);
|
||||
}
|
||||
|
||||
int misc_call(struct udevice *dev, int msgid, void *tx_msg, int tx_size,
|
||||
void *rx_msg, int rx_size)
|
||||
{
|
||||
const struct misc_ops *ops = device_get_ops(dev);
|
||||
|
||||
if (!ops->call)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->call(dev, msgid, tx_msg, tx_size, rx_msg, rx_size);
|
||||
}
|
||||
|
||||
int misc_set_enabled(struct udevice *dev, bool val)
|
||||
{
|
||||
const struct misc_ops *ops = device_get_ops(dev);
|
||||
|
||||
if (!ops->set_enabled)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->set_enabled(dev, val);
|
||||
}
|
||||
|
||||
UCLASS_DRIVER(misc) = {
|
||||
.id = UCLASS_MISC,
|
||||
.name = "misc",
|
||||
#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)
|
||||
.post_bind = dm_scan_fdt_dev,
|
||||
#endif
|
||||
};
|
||||
@@ -0,0 +1,133 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2018
|
||||
* Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <misc.h>
|
||||
|
||||
struct misc_sandbox_priv {
|
||||
u8 mem[128];
|
||||
ulong last_ioctl;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
int misc_sandbox_read(struct udevice *dev, int offset, void *buf, int size)
|
||||
{
|
||||
struct misc_sandbox_priv *priv = dev_get_priv(dev);
|
||||
|
||||
memcpy(buf, priv->mem + offset, size);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
int misc_sandbox_write(struct udevice *dev, int offset, const void *buf,
|
||||
int size)
|
||||
{
|
||||
struct misc_sandbox_priv *priv = dev_get_priv(dev);
|
||||
|
||||
memcpy(priv->mem + offset, buf, size);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
int misc_sandbox_ioctl(struct udevice *dev, unsigned long request, void *buf)
|
||||
{
|
||||
struct misc_sandbox_priv *priv = dev_get_priv(dev);
|
||||
|
||||
priv->last_ioctl = request;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int misc_sandbox_call(struct udevice *dev, int msgid, void *tx_msg,
|
||||
int tx_size, void *rx_msg, int rx_size)
|
||||
{
|
||||
struct misc_sandbox_priv *priv = dev_get_priv(dev);
|
||||
|
||||
if (msgid == 0) {
|
||||
int num = *(int *)tx_msg;
|
||||
|
||||
switch (num) {
|
||||
case 0:
|
||||
strncpy(rx_msg, "Zero", rx_size);
|
||||
break;
|
||||
case 1:
|
||||
strncpy(rx_msg, "One", rx_size);
|
||||
break;
|
||||
case 2:
|
||||
strncpy(rx_msg, "Two", rx_size);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (msgid == 1) {
|
||||
int num = *(int *)tx_msg;
|
||||
|
||||
switch (num) {
|
||||
case 0:
|
||||
strncpy(rx_msg, "Forty", rx_size);
|
||||
break;
|
||||
case 1:
|
||||
strncpy(rx_msg, "Forty-one", rx_size);
|
||||
break;
|
||||
case 2:
|
||||
strncpy(rx_msg, "Forty-two", rx_size);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (msgid == 2)
|
||||
memcpy(rx_msg, &priv->last_ioctl, sizeof(priv->last_ioctl));
|
||||
|
||||
if (msgid == 3)
|
||||
memcpy(rx_msg, &priv->enabled, sizeof(priv->enabled));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int misc_sandbox_set_enabled(struct udevice *dev, bool val)
|
||||
{
|
||||
struct misc_sandbox_priv *priv = dev_get_priv(dev);
|
||||
|
||||
priv->enabled = !priv->enabled;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct misc_ops misc_sandbox_ops = {
|
||||
.read = misc_sandbox_read,
|
||||
.write = misc_sandbox_write,
|
||||
.ioctl = misc_sandbox_ioctl,
|
||||
.call = misc_sandbox_call,
|
||||
.set_enabled = misc_sandbox_set_enabled,
|
||||
};
|
||||
|
||||
int misc_sandbox_probe(struct udevice *dev)
|
||||
{
|
||||
struct misc_sandbox_priv *priv = dev_get_priv(dev);
|
||||
|
||||
priv->enabled = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id misc_sandbox_ids[] = {
|
||||
{ .compatible = "sandbox,misc_sandbox" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(misc_sandbox) = {
|
||||
.name = "misc_sandbox",
|
||||
.id = UCLASS_MISC,
|
||||
.ops = &misc_sandbox_ops,
|
||||
.of_match = misc_sandbox_ids,
|
||||
.probe = misc_sandbox_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct misc_sandbox_priv),
|
||||
};
|
||||
@@ -0,0 +1,185 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2018
|
||||
* Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
|
||||
*
|
||||
* base on the MPC83xx serdes initialization, which is
|
||||
*
|
||||
* Copyright 2007,2011 Freescale Semiconductor, Inc.
|
||||
* Copyright (C) 2008 MontaVista Software, Inc.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <mapmem.h>
|
||||
#include <misc.h>
|
||||
|
||||
#include "mpc83xx_serdes.h"
|
||||
|
||||
/**
|
||||
* struct mpc83xx_serdes_priv - Private structure for MPC83xx serdes
|
||||
* @regs: The device's register map
|
||||
* @rfcks: Variable to keep the serdes reference clock selection set during
|
||||
* initialization in (is or'd to every value written to SRDSCR4)
|
||||
*/
|
||||
struct mpc83xx_serdes_priv {
|
||||
struct mpc83xx_serdes_regs *regs;
|
||||
u32 rfcks;
|
||||
};
|
||||
|
||||
/**
|
||||
* setup_sata() - Configure the SerDes device to SATA mode
|
||||
* @dev: The device to configure
|
||||
*/
|
||||
static void setup_sata(struct udevice *dev)
|
||||
{
|
||||
struct mpc83xx_serdes_priv *priv = dev_get_priv(dev);
|
||||
|
||||
/* Set and clear reset bits */
|
||||
setbits_be32(&priv->regs->srdsrstctl, SRDSRSTCTL_SATA_RESET);
|
||||
udelay(1000);
|
||||
clrbits_be32(&priv->regs->srdsrstctl, SRDSRSTCTL_SATA_RESET);
|
||||
|
||||
/* Configure SRDSCR0 */
|
||||
clrsetbits_be32(&priv->regs->srdscr0,
|
||||
SRDSCR0_TXEQA_MASK | SRDSCR0_TXEQE_MASK,
|
||||
SRDSCR0_TXEQA_SATA | SRDSCR0_TXEQE_SATA);
|
||||
|
||||
/* Configure SRDSCR1 */
|
||||
clrbits_be32(&priv->regs->srdscr1, SRDSCR1_PLLBW);
|
||||
|
||||
/* Configure SRDSCR2 */
|
||||
clrsetbits_be32(&priv->regs->srdscr2,
|
||||
SRDSCR2_SEIC_MASK,
|
||||
SRDSCR2_SEIC_SATA);
|
||||
|
||||
/* Configure SRDSCR3 */
|
||||
out_be32(&priv->regs->srdscr3,
|
||||
SRDSCR3_KFR_SATA | SRDSCR3_KPH_SATA |
|
||||
SRDSCR3_SDFM_SATA_PEX | SRDSCR3_SDTXL_SATA);
|
||||
|
||||
/* Configure SRDSCR4 */
|
||||
out_be32(&priv->regs->srdscr4, priv->rfcks | SRDSCR4_PROT_SATA);
|
||||
}
|
||||
|
||||
/**
|
||||
* setup_pex() - Configure the SerDes device to PCI Express mode
|
||||
* @dev: The device to configure
|
||||
* @type: The PCI Express type to configure for (x1 or x2)
|
||||
*/
|
||||
static void setup_pex(struct udevice *dev, enum pex_type type)
|
||||
{
|
||||
struct mpc83xx_serdes_priv *priv = dev_get_priv(dev);
|
||||
|
||||
/* Configure SRDSCR1 */
|
||||
setbits_be32(&priv->regs->srdscr1, SRDSCR1_PLLBW);
|
||||
|
||||
/* Configure SRDSCR2 */
|
||||
clrsetbits_be32(&priv->regs->srdscr2,
|
||||
SRDSCR2_SEIC_MASK,
|
||||
SRDSCR2_SEIC_PEX);
|
||||
|
||||
/* Configure SRDSCR3 */
|
||||
out_be32(&priv->regs->srdscr3, SRDSCR3_SDFM_SATA_PEX);
|
||||
|
||||
/* Configure SRDSCR4 */
|
||||
if (type == PEX_X2)
|
||||
out_be32(&priv->regs->srdscr4,
|
||||
priv->rfcks | SRDSCR4_PROT_PEX | SRDSCR4_PLANE_X2);
|
||||
else
|
||||
out_be32(&priv->regs->srdscr4,
|
||||
priv->rfcks | SRDSCR4_PROT_PEX);
|
||||
}
|
||||
|
||||
/**
|
||||
* setup_sgmii() - Configure the SerDes device to SGMII mode
|
||||
* @dev: The device to configure
|
||||
*/
|
||||
static void setup_sgmii(struct udevice *dev)
|
||||
{
|
||||
struct mpc83xx_serdes_priv *priv = dev_get_priv(dev);
|
||||
|
||||
/* Configure SRDSCR1 */
|
||||
clrbits_be32(&priv->regs->srdscr1, SRDSCR1_PLLBW);
|
||||
|
||||
/* Configure SRDSCR2 */
|
||||
clrsetbits_be32(&priv->regs->srdscr2,
|
||||
SRDSCR2_SEIC_MASK,
|
||||
SRDSCR2_SEIC_SGMII);
|
||||
|
||||
/* Configure SRDSCR3 */
|
||||
out_be32(&priv->regs->srdscr3, 0);
|
||||
|
||||
/* Configure SRDSCR4 */
|
||||
out_be32(&priv->regs->srdscr4, priv->rfcks | SRDSCR4_PROT_SGMII);
|
||||
}
|
||||
|
||||
static int mpc83xx_serdes_probe(struct udevice *dev)
|
||||
{
|
||||
struct mpc83xx_serdes_priv *priv = dev_get_priv(dev);
|
||||
bool vdd;
|
||||
const char *proto;
|
||||
|
||||
priv->regs = map_sysmem(dev_read_addr(dev),
|
||||
sizeof(struct mpc83xx_serdes_regs));
|
||||
|
||||
switch (dev_read_u32_default(dev, "serdes-clk", -1)) {
|
||||
case 100:
|
||||
priv->rfcks = SRDSCR4_RFCKS_100;
|
||||
break;
|
||||
case 125:
|
||||
priv->rfcks = SRDSCR4_RFCKS_125;
|
||||
break;
|
||||
case 150:
|
||||
priv->rfcks = SRDSCR4_RFCKS_150;
|
||||
break;
|
||||
default:
|
||||
debug("%s: Could not read serdes clock value\n", dev->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
vdd = dev_read_bool(dev, "vdd");
|
||||
|
||||
/* 1.0V corevdd */
|
||||
if (vdd) {
|
||||
/* DPPE/DPPA = 0 */
|
||||
clrbits_be32(&priv->regs->srdscr0, SRDSCR0_DPP_1V2);
|
||||
|
||||
/* VDD = 0 */
|
||||
clrbits_be32(&priv->regs->srdscr0, SRDSCR2_VDD_1V2);
|
||||
}
|
||||
|
||||
proto = dev_read_string(dev, "proto");
|
||||
|
||||
/* protocol specific configuration */
|
||||
if (!strcmp(proto, "sata")) {
|
||||
setup_sata(dev);
|
||||
} else if (!strcmp(proto, "pex")) {
|
||||
setup_pex(dev, PEX_X1);
|
||||
} else if (!strcmp(proto, "pex-x2")) {
|
||||
setup_pex(dev, PEX_X2);
|
||||
} else if (!strcmp(proto, "sgmii")) {
|
||||
setup_sgmii(dev);
|
||||
} else {
|
||||
debug("%s: Invalid protocol value %s\n", dev->name, proto);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Do a software reset */
|
||||
setbits_be32(&priv->regs->srdsrstctl, SRDSRSTCTL_RST);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id mpc83xx_serdes_ids[] = {
|
||||
{ .compatible = "fsl,mpc83xx-serdes" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(mpc83xx_serdes) = {
|
||||
.name = "mpc83xx_serdes",
|
||||
.id = UCLASS_MISC,
|
||||
.of_match = mpc83xx_serdes_ids,
|
||||
.probe = mpc83xx_serdes_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct mpc83xx_serdes_priv),
|
||||
};
|
||||
@@ -0,0 +1,232 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* (C) Copyright 2018
|
||||
* Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
|
||||
*/
|
||||
|
||||
/**
|
||||
* enum srdscr0_mask - Bit masks for SRDSCR0 (SerDes Control Register 0)
|
||||
* @SRDSCR0_DPPA: Bitmask for the DPPA (diff pk-pk swing for lane A)
|
||||
* field of the SRCSCR0
|
||||
* @SRDSCR0_DPPE: Bitmask for the DPPE (diff pk-pk swing for lane E)
|
||||
* field of the SRCSCR0
|
||||
* @SRDSCR0_DPP_1V2: Combined bitmask to set diff pk-pk swing for both lanes
|
||||
* @SRDSCR0_TXEQA_MASK: Bitmask for the TXEQA (transmit equalization for
|
||||
* lane A) field of the SRCSCR0
|
||||
* @SRDSCR0_TXEQA_SATA: Bitmask to set the TXEQA to the value used for SATA
|
||||
* @SRDSCR0_TXEQE_MASK: Bitmask for the TXEQE (transmit equalization for
|
||||
* lane E) field of the SRCSCR0
|
||||
* @SRDSCR0_TXEQE_SATA: Bitmask to set the TXEQE to the value used for SATA
|
||||
*/
|
||||
enum srdscr0_mask {
|
||||
SRDSCR0_DPPA = BIT(31 - 16),
|
||||
SRDSCR0_DPPE = BIT(31 - 20),
|
||||
SRDSCR0_DPP_1V2 = SRDSCR0_DPPE | SRDSCR0_DPPA,
|
||||
|
||||
SRDSCR0_TXEQA_MASK = 0x00007000,
|
||||
SRDSCR0_TXEQA_SATA = 0x00001000,
|
||||
SRDSCR0_TXEQE_MASK = 0x00000700,
|
||||
SRDSCR0_TXEQE_SATA = 0x00000100,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum srdscr1_mask - Bit masks for SRDSCR1 (SerDes Control Register 1)
|
||||
* @SRDSCR1_PLLBW: Bitmask for the PLLBW (PLL bandwidth) field of SRDSCR1
|
||||
*/
|
||||
enum srdscr1_mask {
|
||||
SRDSCR1_PLLBW = BIT(31 - 25),
|
||||
};
|
||||
|
||||
/**
|
||||
* enum srdscr2_mask - Bit masks for SRDSCR2 (SerDes Control Register 2)
|
||||
* @SRDSCR2_VDD_1V2: Bit mask to to set the VDD field of the SCRSCR2
|
||||
* @SRDSCR2_SEICA_MASK: Bitmask for the SEICA (Receiver electrical idle
|
||||
* detection control for lane A) field of the SRCSCR2
|
||||
* @SRDSCR2_SEICE_MASK: Bitmask for the SEICE (Receiver electrical idle
|
||||
* detection control for lane E) field of the SRCSCR2
|
||||
* @SRDSCR2_SEIC_MASK: Combined bitmask to set the receiver electrical idle
|
||||
* detection control for both lanes
|
||||
* @SRDSCR2_SEICA_SATA: Bitmask to set the SEICA field to the value used for
|
||||
* SATA
|
||||
* @SRDSCR2_SEICE_SATA: Bitmask to set the SEICE field to the value used for
|
||||
* SATA
|
||||
* @SRDSCR2_SEIC_SATA: Combined bitmask to set the value of both SEIC fields
|
||||
* to the value used for SATA
|
||||
* @SRDSCR2_SEICA_PEX: Bitmask to set the SEICA field to the value used for
|
||||
* PCI Express
|
||||
* @SRDSCR2_SEICE_PEX: Bitmask to set the SEICE field to the value used for
|
||||
* PCI Express
|
||||
* @SRDSCR2_SEIC_PEX: Combined bitmask to set the value of both SEIC fields
|
||||
* to the value used for PCI Express
|
||||
* @SRDSCR2_SEICA_SGMII: Bitmask to set the SEICA field to the value used for
|
||||
* SGMII
|
||||
* @SRDSCR2_SEICE_SGMII: Bitmask to set the SEICE field to the value used for
|
||||
* SGMII
|
||||
* @SRDSCR2_SEIC_SGMII: Combined bitmask to set the value of both SEIC fields
|
||||
* to the value used for SGMII
|
||||
*/
|
||||
enum srdscr2_mask {
|
||||
SRDSCR2_VDD_1V2 = 0x00800000,
|
||||
|
||||
SRDSCR2_SEICA_MASK = 0x00001c00,
|
||||
SRDSCR2_SEICE_MASK = 0x0000001c,
|
||||
SRDSCR2_SEIC_MASK = SRDSCR2_SEICA_MASK | SRDSCR2_SEICE_MASK,
|
||||
|
||||
SRDSCR2_SEICA_SATA = 0x00001400,
|
||||
SRDSCR2_SEICE_SATA = 0x00000014,
|
||||
SRDSCR2_SEIC_SATA = SRDSCR2_SEICA_SATA | SRDSCR2_SEICE_SATA,
|
||||
|
||||
SRDSCR2_SEICA_PEX = 0x00001000,
|
||||
SRDSCR2_SEICE_PEX = 0x00000010,
|
||||
SRDSCR2_SEIC_PEX = SRDSCR2_SEICA_PEX | SRDSCR2_SEICE_PEX,
|
||||
|
||||
SRDSCR2_SEICA_SGMII = 0x00000100,
|
||||
SRDSCR2_SEICE_SGMII = 0x00000001,
|
||||
SRDSCR2_SEIC_SGMII = SRDSCR2_SEICA_SGMII | SRDSCR2_SEICE_SGMII,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum srdscr3_mask - Bit masks for SRDSCR3 (SerDes Control Register 3)
|
||||
* @SRDSCR3_KFRA_SATA: Bitmask to set the KFRA field of SRDSCR3 to the
|
||||
* value used by SATA
|
||||
* @SRDSCR3_KFRE_SATA: Bitmask to set the KFRE field of SRDSCR3 to the
|
||||
* value used by SATA
|
||||
* @SRDSCR3_KFR_SATA: Combined bitmask to set both KFR fields to the
|
||||
* value used by SATA
|
||||
* @SRDSCR3_KPHA_SATA: Bitmask to set the KPHA field of SRDSCR3 to the
|
||||
* value used by SATA
|
||||
* @SRDSCR3_KPHE_SATA: Bitmask to set the KPHE field of SRDSCR3 to the
|
||||
* value used by SATA
|
||||
* @SRDSCR3_KPH_SATA: Combined bitmask to set both KPH fields to the
|
||||
* value used by SATA
|
||||
* @SRDSCR3_SDFMA_SATA_PEX: Bitmask to set the SDFMA field of SRDSCR3 to the
|
||||
* value used by SATA and PCI Express
|
||||
* @SRDSCR3_SDFME_SATA_PEX: Bitmask to set the SDFME field of SRDSCR3 to the
|
||||
* value used by SATA and PCI Express
|
||||
* @SRDSCR3_SDFM_SATA_PEX: Combined bitmask to set both SDFM fields to the
|
||||
* value used by SATA and PCI Express
|
||||
* @SRDSCR3_SDTXLA_SATA: Bitmask to set the SDTXLA field of SRDSCR3 to the
|
||||
* value used by SATA
|
||||
* @SRDSCR3_SDTXLE_SATA: Bitmask to set the SDTXLE field of SRDSCR3 to the
|
||||
* value used by SATA
|
||||
* @SRDSCR3_SDTXL_SATA: Combined bitmask to set both SDTXL fields to the
|
||||
* value used by SATA
|
||||
*
|
||||
* KFRA = 'Kfr' gain selection in the CDR for lane A
|
||||
* KFRE = 'Kfr' gain selection in the CDR for lane E
|
||||
* SDFMA = Bandwidth of digital filter for lane A
|
||||
* SDFME = Bandwidth of digital filter for lane E
|
||||
* SDTXLA = Lane A transmitter amplitude levels
|
||||
* SDTXLE = Lane E transmitter amplitude levels
|
||||
*/
|
||||
enum srdscr3_mask {
|
||||
SRDSCR3_KFRA_SATA = 0x10000000,
|
||||
SRDSCR3_KFRE_SATA = 0x00100000,
|
||||
SRDSCR3_KFR_SATA = SRDSCR3_KFRA_SATA | SRDSCR3_KFRE_SATA,
|
||||
|
||||
SRDSCR3_KPHA_SATA = 0x04000000,
|
||||
SRDSCR3_KPHE_SATA = 0x00040000,
|
||||
SRDSCR3_KPH_SATA = SRDSCR3_KPHA_SATA | SRDSCR3_KPHE_SATA,
|
||||
|
||||
SRDSCR3_SDFMA_SATA_PEX = 0x01000000,
|
||||
SRDSCR3_SDFME_SATA_PEX = 0x00010000,
|
||||
SRDSCR3_SDFM_SATA_PEX = SRDSCR3_SDFMA_SATA_PEX | SRDSCR3_SDFME_SATA_PEX,
|
||||
|
||||
SRDSCR3_SDTXLA_SATA = 0x00000500,
|
||||
SRDSCR3_SDTXLE_SATA = 0x00000005,
|
||||
SRDSCR3_SDTXL_SATA = SRDSCR3_SDTXLA_SATA | SRDSCR3_SDTXLE_SATA,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum srdscr4_mask - Bit masks for SRDSCR4 (SerDes Control Register 4)
|
||||
* @SRDSCR4_PROTA_SATA: Bitmask to set the PROTA field of SRDSCR4 to the
|
||||
* value used by SATA
|
||||
* @SRDSCR4_PROTE_SATA: Bitmask to set the PROTE field of SRDSCR4 to the
|
||||
* value used by SATA
|
||||
* @SRDSCR4_PROT_SATA: Combined bitmask to set both PROT fields to the
|
||||
* value used by SATA
|
||||
* @SRDSCR4_PROTA_PEX: Bitmask to set the PROTA field of SRDSCR4 to the
|
||||
* value used by PCI Express
|
||||
* @SRDSCR4_PROTE_PEX: Bitmask to set the PROTE field of SRDSCR4 to the
|
||||
* value used by PCI Express
|
||||
* @SRDSCR4_PROT_PEX: Combined bitmask to set both PROT fields to the
|
||||
* value used by PCI Express
|
||||
* @SRDSCR4_PROTA_SGMII: Bitmask to set the PROTA field of SRDSCR4 to the
|
||||
* value used by SGMII
|
||||
* @SRDSCR4_PROTE_SGMII: Bitmask to set the PROTE field of SRDSCR4 to the
|
||||
* value used by SGMII
|
||||
* @SRDSCR4_PROT_SGMII: Combined bitmask to set both PROT fields to the
|
||||
* value used by SGMII
|
||||
* @SRDSCR4_PLANE_X2: Bitmask to set the PLANE field of SRDSCR4
|
||||
* @SRDSCR4_RFCKS_100: Bitmask to set the RFCKS field of SRDSCR4 to the
|
||||
* value 100Mhz
|
||||
* @SRDSCR4_RFCKS_125: Bitmask to set the RFCKS field of SRDSCR4 to the
|
||||
* value 125Mhz
|
||||
* @SRDSCR4_RFCKS_150: Bitmask to set the RFCKS field of SRDSCR4 to the
|
||||
* value 150Mhz
|
||||
*
|
||||
* PROTA = Lane A protocol select
|
||||
* PROTE = Lane E protocol select
|
||||
* PLAME = Number of PCI Express lanes
|
||||
*/
|
||||
enum srdscr4_mask {
|
||||
SRDSCR4_PROTA_SATA = 0x00000800,
|
||||
SRDSCR4_PROTE_SATA = 0x00000008,
|
||||
SRDSCR4_PROT_SATA = SRDSCR4_PROTA_SATA | SRDSCR4_PROTE_SATA,
|
||||
|
||||
SRDSCR4_PROTA_PEX = 0x00000100,
|
||||
SRDSCR4_PROTE_PEX = 0x00000001,
|
||||
SRDSCR4_PROT_PEX = SRDSCR4_PROTA_PEX | SRDSCR4_PROTE_PEX,
|
||||
|
||||
SRDSCR4_PROTA_SGMII = 0x00000500,
|
||||
SRDSCR4_PROTE_SGMII = 0x00000005,
|
||||
SRDSCR4_PROT_SGMII = SRDSCR4_PROTA_SGMII | SRDSCR4_PROTE_SGMII,
|
||||
|
||||
SRDSCR4_PLANE_X2 = 0x01000000,
|
||||
|
||||
SRDSCR4_RFCKS_100 = (0 << 28),
|
||||
SRDSCR4_RFCKS_125 = (1 << 28),
|
||||
SRDSCR4_RFCKS_150 = (3 << 28),
|
||||
};
|
||||
|
||||
/**
|
||||
* enum srdsrstctl_mask - Bit masks for SRDSRSTCTL (SerDes Reset Control Register)
|
||||
* @SRDSRSTCTL_RST: Bitmask for the RST (Software reset) field of the
|
||||
* SRDSRSTCTL
|
||||
* @SRDSRSTCTL_SATA_RESET: Bitmask for the SATA_RESET (SATA reset) field of the
|
||||
* SRDSRSTCTL
|
||||
*/
|
||||
enum srdsrstctl_mask {
|
||||
SRDSRSTCTL_RST = 0x80000000,
|
||||
SRDSRSTCTL_SATA_RESET = 0xf,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mpc83xx_serdes_regs - Register map of the SerDes controller
|
||||
* @srdscr0: SerDes Control Register 0
|
||||
* @srdscr1: SerDes Control Register 1
|
||||
* @srdscr2: SerDes Control Register 2
|
||||
* @srdscr3: SerDes Control Register 3
|
||||
* @srdscr4: SerDes Control Register 4
|
||||
* @fill0: Reserved space in the register map
|
||||
* @srdsrstctl: SerDes Reset Control Register
|
||||
*/
|
||||
struct mpc83xx_serdes_regs {
|
||||
u32 srdscr0;
|
||||
u32 srdscr1;
|
||||
u32 srdscr2;
|
||||
u32 srdscr3;
|
||||
u32 srdscr4;
|
||||
u8 fill0[12];
|
||||
u32 srdsrstctl;
|
||||
};
|
||||
|
||||
/**
|
||||
* enum pex_type - Types of PCI Express
|
||||
* @PEX_X1: PCI Express in x1 mode
|
||||
* @PEX_X2: PCI Express in x2 mode
|
||||
*/
|
||||
enum pex_type {
|
||||
PEX_X1,
|
||||
PEX_X2,
|
||||
};
|
||||
@@ -0,0 +1,460 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2013 ADVANSEE
|
||||
* Benoît Thébaudeau <benoit.thebaudeau@advansee.com>
|
||||
*
|
||||
* Based on Dirk Behme's
|
||||
* https://github.com/dirkbehme/u-boot-imx6/blob/28b17e9/drivers/misc/imx_otp.c,
|
||||
* which is based on Freescale's
|
||||
* http://git.freescale.com/git/cgit.cgi/imx/uboot-imx.git/tree/drivers/misc/imx_otp.c?h=imx_v2009.08_1.1.0&id=9aa74e6,
|
||||
* which is:
|
||||
* Copyright (C) 2011 Freescale Semiconductor, Inc.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <fuse.h>
|
||||
#include <linux/errno.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/clock.h>
|
||||
#include <asm/arch/imx-regs.h>
|
||||
#include <asm/mach-imx/sys_proto.h>
|
||||
|
||||
#define BO_CTRL_WR_UNLOCK 16
|
||||
#define BM_CTRL_WR_UNLOCK 0xffff0000
|
||||
#define BV_CTRL_WR_UNLOCK_KEY 0x3e77
|
||||
#define BM_CTRL_ERROR 0x00000200
|
||||
#define BM_CTRL_BUSY 0x00000100
|
||||
#define BO_CTRL_ADDR 0
|
||||
#ifdef CONFIG_MX7
|
||||
#define BM_CTRL_ADDR 0x0000000f
|
||||
#define BM_CTRL_RELOAD 0x00000400
|
||||
#elif defined(CONFIG_MX7ULP)
|
||||
#define BM_CTRL_ADDR 0x000000FF
|
||||
#define BM_CTRL_RELOAD 0x00000400
|
||||
#define BM_OUT_STATUS_DED 0x00000400
|
||||
#define BM_OUT_STATUS_LOCKED 0x00000800
|
||||
#define BM_OUT_STATUS_PROGFAIL 0x00001000
|
||||
#elif defined(CONFIG_IMX8M)
|
||||
#define BM_CTRL_ADDR 0x000000ff
|
||||
#else
|
||||
#define BM_CTRL_ADDR 0x0000007f
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MX7
|
||||
#define BO_TIMING_FSOURCE 12
|
||||
#define BM_TIMING_FSOURCE 0x0007f000
|
||||
#define BV_TIMING_FSOURCE_NS 1001
|
||||
#define BO_TIMING_PROG 0
|
||||
#define BM_TIMING_PROG 0x00000fff
|
||||
#define BV_TIMING_PROG_US 10
|
||||
#else
|
||||
#define BO_TIMING_STROBE_READ 16
|
||||
#define BM_TIMING_STROBE_READ 0x003f0000
|
||||
#define BV_TIMING_STROBE_READ_NS 37
|
||||
#define BO_TIMING_RELAX 12
|
||||
#define BM_TIMING_RELAX 0x0000f000
|
||||
#define BV_TIMING_RELAX_NS 17
|
||||
#define BO_TIMING_STROBE_PROG 0
|
||||
#define BM_TIMING_STROBE_PROG 0x00000fff
|
||||
#define BV_TIMING_STROBE_PROG_US 10
|
||||
#endif
|
||||
|
||||
#define BM_READ_CTRL_READ_FUSE 0x00000001
|
||||
|
||||
#define BF(value, field) (((value) << BO_##field) & BM_##field)
|
||||
|
||||
#define WRITE_POSTAMBLE_US 2
|
||||
|
||||
#if defined(CONFIG_MX6) || defined(CONFIG_VF610)
|
||||
#define FUSE_BANK_SIZE 0x80
|
||||
#ifdef CONFIG_MX6SL
|
||||
#define FUSE_BANKS 8
|
||||
#elif defined(CONFIG_MX6ULL) || defined(CONFIG_MX6SLL)
|
||||
#define FUSE_BANKS 9
|
||||
#else
|
||||
#define FUSE_BANKS 16
|
||||
#endif
|
||||
#elif defined CONFIG_MX7
|
||||
#define FUSE_BANK_SIZE 0x40
|
||||
#define FUSE_BANKS 16
|
||||
#elif defined(CONFIG_MX7ULP)
|
||||
#define FUSE_BANK_SIZE 0x80
|
||||
#define FUSE_BANKS 31
|
||||
#elif defined(CONFIG_IMX8M)
|
||||
#define FUSE_BANK_SIZE 0x40
|
||||
#define FUSE_BANKS 64
|
||||
#else
|
||||
#error "Unsupported architecture\n"
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_MX6)
|
||||
|
||||
/*
|
||||
* There is a hole in shadow registers address map of size 0x100
|
||||
* between bank 5 and bank 6 on iMX6QP, iMX6DQ, iMX6SDL, iMX6SX,
|
||||
* iMX6UL, i.MX6ULL and i.MX6SLL.
|
||||
* Bank 5 ends at 0x6F0 and Bank 6 starts at 0x800. When reading the fuses,
|
||||
* we should account for this hole in address space.
|
||||
*
|
||||
* Similar hole exists between bank 14 and bank 15 of size
|
||||
* 0x80 on iMX6QP, iMX6DQ, iMX6SDL and iMX6SX.
|
||||
* Note: iMX6SL has only 0-7 banks and there is no hole.
|
||||
* Note: iMX6UL doesn't have this one.
|
||||
*
|
||||
* This function is to covert user input to physical bank index.
|
||||
* Only needed when read fuse, because we use register offset, so
|
||||
* need to calculate real register offset.
|
||||
* When write, no need to consider hole, always use the bank/word
|
||||
* index from fuse map.
|
||||
*/
|
||||
u32 fuse_bank_physical(int index)
|
||||
{
|
||||
u32 phy_index;
|
||||
|
||||
if (is_mx6sl() || is_mx7ulp()) {
|
||||
phy_index = index;
|
||||
} else if (is_mx6ul() || is_mx6ull() || is_mx6sll()) {
|
||||
if ((is_mx6ull() || is_mx6sll()) && index == 8)
|
||||
index = 7;
|
||||
|
||||
if (index >= 6)
|
||||
phy_index = fuse_bank_physical(5) + (index - 6) + 3;
|
||||
else
|
||||
phy_index = index;
|
||||
} else {
|
||||
if (index >= 15)
|
||||
phy_index = fuse_bank_physical(14) + (index - 15) + 2;
|
||||
else if (index >= 6)
|
||||
phy_index = fuse_bank_physical(5) + (index - 6) + 3;
|
||||
else
|
||||
phy_index = index;
|
||||
}
|
||||
return phy_index;
|
||||
}
|
||||
|
||||
u32 fuse_word_physical(u32 bank, u32 word_index)
|
||||
{
|
||||
if (is_mx6ull() || is_mx6sll()) {
|
||||
if (bank == 8)
|
||||
word_index = word_index + 4;
|
||||
}
|
||||
|
||||
return word_index;
|
||||
}
|
||||
#else
|
||||
u32 fuse_bank_physical(int index)
|
||||
{
|
||||
return index;
|
||||
}
|
||||
|
||||
u32 fuse_word_physical(u32 bank, u32 word_index)
|
||||
{
|
||||
return word_index;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void wait_busy(struct ocotp_regs *regs, unsigned int delay_us)
|
||||
{
|
||||
while (readl(®s->ctrl) & BM_CTRL_BUSY)
|
||||
udelay(delay_us);
|
||||
}
|
||||
|
||||
static void clear_error(struct ocotp_regs *regs)
|
||||
{
|
||||
writel(BM_CTRL_ERROR, ®s->ctrl_clr);
|
||||
}
|
||||
|
||||
static int prepare_access(struct ocotp_regs **regs, u32 bank, u32 word,
|
||||
int assert, const char *caller)
|
||||
{
|
||||
*regs = (struct ocotp_regs *)OCOTP_BASE_ADDR;
|
||||
|
||||
if (bank >= FUSE_BANKS ||
|
||||
word >= ARRAY_SIZE((*regs)->bank[0].fuse_regs) >> 2 ||
|
||||
!assert) {
|
||||
printf("mxc_ocotp %s(): Invalid argument\n", caller);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (is_mx6ull() || is_mx6sll()) {
|
||||
if ((bank == 7 || bank == 8) &&
|
||||
word >= ARRAY_SIZE((*regs)->bank[0].fuse_regs) >> 3) {
|
||||
printf("mxc_ocotp %s(): Invalid argument\n", caller);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
enable_ocotp_clk(1);
|
||||
|
||||
wait_busy(*regs, 1);
|
||||
clear_error(*regs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int finish_access(struct ocotp_regs *regs, const char *caller)
|
||||
{
|
||||
u32 err;
|
||||
|
||||
err = !!(readl(®s->ctrl) & BM_CTRL_ERROR);
|
||||
clear_error(regs);
|
||||
|
||||
#ifdef CONFIG_MX7ULP
|
||||
/* Need to power down the OTP memory */
|
||||
writel(1, ®s->pdn);
|
||||
#endif
|
||||
if (err) {
|
||||
printf("mxc_ocotp %s(): Access protect error\n", caller);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int prepare_read(struct ocotp_regs **regs, u32 bank, u32 word, u32 *val,
|
||||
const char *caller)
|
||||
{
|
||||
return prepare_access(regs, bank, word, val != NULL, caller);
|
||||
}
|
||||
|
||||
int fuse_read(u32 bank, u32 word, u32 *val)
|
||||
{
|
||||
struct ocotp_regs *regs;
|
||||
int ret;
|
||||
u32 phy_bank;
|
||||
u32 phy_word;
|
||||
|
||||
ret = prepare_read(®s, bank, word, val, __func__);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
phy_bank = fuse_bank_physical(bank);
|
||||
phy_word = fuse_word_physical(bank, word);
|
||||
|
||||
*val = readl(®s->bank[phy_bank].fuse_regs[phy_word << 2]);
|
||||
|
||||
#ifdef CONFIG_MX7ULP
|
||||
if (readl(®s->out_status) & BM_OUT_STATUS_DED) {
|
||||
writel(BM_OUT_STATUS_DED, ®s->out_status_clr);
|
||||
printf("mxc_ocotp %s(): fuse read wrong\n", __func__);
|
||||
return -EIO;
|
||||
}
|
||||
#endif
|
||||
return finish_access(regs, __func__);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MX7
|
||||
static void set_timing(struct ocotp_regs *regs)
|
||||
{
|
||||
u32 ipg_clk;
|
||||
u32 fsource, prog;
|
||||
u32 timing;
|
||||
|
||||
ipg_clk = mxc_get_clock(MXC_IPG_CLK);
|
||||
|
||||
fsource = DIV_ROUND_UP((ipg_clk / 1000) * BV_TIMING_FSOURCE_NS,
|
||||
+ 1000000) + 1;
|
||||
prog = DIV_ROUND_CLOSEST(ipg_clk * BV_TIMING_PROG_US, 1000000) + 1;
|
||||
|
||||
timing = BF(fsource, TIMING_FSOURCE) | BF(prog, TIMING_PROG);
|
||||
|
||||
clrsetbits_le32(®s->timing, BM_TIMING_FSOURCE | BM_TIMING_PROG,
|
||||
timing);
|
||||
}
|
||||
#elif defined(CONFIG_MX7ULP)
|
||||
static void set_timing(struct ocotp_regs *regs)
|
||||
{
|
||||
/* No timing set for MX7ULP */
|
||||
}
|
||||
|
||||
#else
|
||||
static void set_timing(struct ocotp_regs *regs)
|
||||
{
|
||||
u32 ipg_clk;
|
||||
u32 relax, strobe_read, strobe_prog;
|
||||
u32 timing;
|
||||
|
||||
ipg_clk = mxc_get_clock(MXC_IPG_CLK);
|
||||
|
||||
relax = DIV_ROUND_UP(ipg_clk * BV_TIMING_RELAX_NS, 1000000000) - 1;
|
||||
strobe_read = DIV_ROUND_UP(ipg_clk * BV_TIMING_STROBE_READ_NS,
|
||||
1000000000) + 2 * (relax + 1) - 1;
|
||||
strobe_prog = DIV_ROUND_CLOSEST(ipg_clk * BV_TIMING_STROBE_PROG_US,
|
||||
1000000) + 2 * (relax + 1) - 1;
|
||||
|
||||
timing = BF(strobe_read, TIMING_STROBE_READ) |
|
||||
BF(relax, TIMING_RELAX) |
|
||||
BF(strobe_prog, TIMING_STROBE_PROG);
|
||||
|
||||
clrsetbits_le32(®s->timing, BM_TIMING_STROBE_READ | BM_TIMING_RELAX |
|
||||
BM_TIMING_STROBE_PROG, timing);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void setup_direct_access(struct ocotp_regs *regs, u32 bank, u32 word,
|
||||
int write)
|
||||
{
|
||||
u32 wr_unlock = write ? BV_CTRL_WR_UNLOCK_KEY : 0;
|
||||
#ifdef CONFIG_MX7
|
||||
u32 addr = bank;
|
||||
#elif defined CONFIG_IMX8M
|
||||
u32 addr = bank << 2 | word;
|
||||
#else
|
||||
u32 addr;
|
||||
/* Bank 7 and Bank 8 only supports 4 words each for i.MX6ULL */
|
||||
if ((is_mx6ull() || is_mx6sll()) && (bank > 7)) {
|
||||
bank = bank - 1;
|
||||
word += 4;
|
||||
}
|
||||
addr = bank << 3 | word;
|
||||
#endif
|
||||
|
||||
set_timing(regs);
|
||||
clrsetbits_le32(®s->ctrl, BM_CTRL_WR_UNLOCK | BM_CTRL_ADDR,
|
||||
BF(wr_unlock, CTRL_WR_UNLOCK) |
|
||||
BF(addr, CTRL_ADDR));
|
||||
}
|
||||
|
||||
int fuse_sense(u32 bank, u32 word, u32 *val)
|
||||
{
|
||||
struct ocotp_regs *regs;
|
||||
int ret;
|
||||
|
||||
if (is_imx8mq() && is_soc_rev(CHIP_REV_2_1)) {
|
||||
printf("mxc_ocotp %s(): fuse sense is disabled\n", __func__);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
ret = prepare_read(®s, bank, word, val, __func__);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
setup_direct_access(regs, bank, word, false);
|
||||
writel(BM_READ_CTRL_READ_FUSE, ®s->read_ctrl);
|
||||
wait_busy(regs, 1);
|
||||
#ifdef CONFIG_MX7
|
||||
*val = readl((®s->read_fuse_data0) + (word << 2));
|
||||
#else
|
||||
*val = readl(®s->read_fuse_data);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MX7ULP
|
||||
if (readl(®s->out_status) & BM_OUT_STATUS_DED) {
|
||||
writel(BM_OUT_STATUS_DED, ®s->out_status_clr);
|
||||
printf("mxc_ocotp %s(): fuse read wrong\n", __func__);
|
||||
return -EIO;
|
||||
}
|
||||
#endif
|
||||
|
||||
return finish_access(regs, __func__);
|
||||
}
|
||||
|
||||
static int prepare_write(struct ocotp_regs **regs, u32 bank, u32 word,
|
||||
const char *caller)
|
||||
{
|
||||
#ifdef CONFIG_MX7ULP
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
/* Only bank 0 and 1 are redundancy mode, others are ECC mode */
|
||||
if (bank != 0 && bank != 1) {
|
||||
if ((soc_rev() < CHIP_REV_2_0) ||
|
||||
((soc_rev() >= CHIP_REV_2_0) &&
|
||||
bank != 9 && bank != 10 && bank != 28)) {
|
||||
ret = fuse_sense(bank, word, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (val != 0) {
|
||||
printf("mxc_ocotp: The word has been programmed, no more write\n");
|
||||
return -EPERM;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return prepare_access(regs, bank, word, true, caller);
|
||||
}
|
||||
|
||||
int fuse_prog(u32 bank, u32 word, u32 val)
|
||||
{
|
||||
struct ocotp_regs *regs;
|
||||
int ret;
|
||||
|
||||
ret = prepare_write(®s, bank, word, __func__);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
setup_direct_access(regs, bank, word, true);
|
||||
#ifdef CONFIG_MX7
|
||||
switch (word) {
|
||||
case 0:
|
||||
writel(0, ®s->data1);
|
||||
writel(0, ®s->data2);
|
||||
writel(0, ®s->data3);
|
||||
writel(val, ®s->data0);
|
||||
break;
|
||||
case 1:
|
||||
writel(val, ®s->data1);
|
||||
writel(0, ®s->data2);
|
||||
writel(0, ®s->data3);
|
||||
writel(0, ®s->data0);
|
||||
break;
|
||||
case 2:
|
||||
writel(0, ®s->data1);
|
||||
writel(val, ®s->data2);
|
||||
writel(0, ®s->data3);
|
||||
writel(0, ®s->data0);
|
||||
break;
|
||||
case 3:
|
||||
writel(0, ®s->data1);
|
||||
writel(0, ®s->data2);
|
||||
writel(val, ®s->data3);
|
||||
writel(0, ®s->data0);
|
||||
break;
|
||||
}
|
||||
wait_busy(regs, BV_TIMING_PROG_US);
|
||||
#else
|
||||
writel(val, ®s->data);
|
||||
wait_busy(regs, BV_TIMING_STROBE_PROG_US);
|
||||
#endif
|
||||
udelay(WRITE_POSTAMBLE_US);
|
||||
|
||||
#ifdef CONFIG_MX7ULP
|
||||
if (readl(®s->out_status) & (BM_OUT_STATUS_PROGFAIL | BM_OUT_STATUS_LOCKED)) {
|
||||
writel((BM_OUT_STATUS_PROGFAIL | BM_OUT_STATUS_LOCKED), ®s->out_status_clr);
|
||||
printf("mxc_ocotp %s(): fuse write is failed\n", __func__);
|
||||
return -EIO;
|
||||
}
|
||||
#endif
|
||||
|
||||
return finish_access(regs, __func__);
|
||||
}
|
||||
|
||||
int fuse_override(u32 bank, u32 word, u32 val)
|
||||
{
|
||||
struct ocotp_regs *regs;
|
||||
int ret;
|
||||
u32 phy_bank;
|
||||
u32 phy_word;
|
||||
|
||||
ret = prepare_write(®s, bank, word, __func__);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
phy_bank = fuse_bank_physical(bank);
|
||||
phy_word = fuse_word_physical(bank, word);
|
||||
|
||||
writel(val, ®s->bank[phy_bank].fuse_regs[phy_word << 2]);
|
||||
|
||||
#ifdef CONFIG_MX7ULP
|
||||
if (readl(®s->out_status) & (BM_OUT_STATUS_PROGFAIL | BM_OUT_STATUS_LOCKED)) {
|
||||
writel((BM_OUT_STATUS_PROGFAIL | BM_OUT_STATUS_LOCKED), ®s->out_status_clr);
|
||||
printf("mxc_ocotp %s(): fuse write is failed\n", __func__);
|
||||
return -EIO;
|
||||
}
|
||||
#endif
|
||||
|
||||
return finish_access(regs, __func__);
|
||||
}
|
||||
@@ -0,0 +1,317 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Freescale i.MX28 OCOTP Driver
|
||||
*
|
||||
* Copyright (C) 2014 Marek Vasut <marex@denx.de>
|
||||
*
|
||||
* Note: The i.MX23/i.MX28 OCOTP block is a predecessor to the OCOTP block
|
||||
* used in i.MX6 . While these blocks are very similar at the first
|
||||
* glance, by digging deeper, one will notice differences (like the
|
||||
* tight dependence on MXS power block, some completely new registers
|
||||
* etc.) which would make common driver an ifdef nightmare :-(
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <fuse.h>
|
||||
#include <linux/errno.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/clock.h>
|
||||
#include <asm/arch/imx-regs.h>
|
||||
#include <asm/arch/sys_proto.h>
|
||||
|
||||
#define MXS_OCOTP_TIMEOUT 100000
|
||||
|
||||
static struct mxs_ocotp_regs *ocotp_regs =
|
||||
(struct mxs_ocotp_regs *)MXS_OCOTP_BASE;
|
||||
static struct mxs_power_regs *power_regs =
|
||||
(struct mxs_power_regs *)MXS_POWER_BASE;
|
||||
static struct mxs_clkctrl_regs *clkctrl_regs =
|
||||
(struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
|
||||
|
||||
static int mxs_ocotp_wait_busy_clear(void)
|
||||
{
|
||||
uint32_t reg;
|
||||
int timeout = MXS_OCOTP_TIMEOUT;
|
||||
|
||||
while (--timeout) {
|
||||
reg = readl(&ocotp_regs->hw_ocotp_ctrl);
|
||||
if (!(reg & OCOTP_CTRL_BUSY))
|
||||
break;
|
||||
udelay(10);
|
||||
}
|
||||
|
||||
if (!timeout)
|
||||
return -EINVAL;
|
||||
|
||||
/* Wait a little as per FSL datasheet's 'write postamble' section. */
|
||||
udelay(10);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mxs_ocotp_clear_error(void)
|
||||
{
|
||||
writel(OCOTP_CTRL_ERROR, &ocotp_regs->hw_ocotp_ctrl_clr);
|
||||
}
|
||||
|
||||
static int mxs_ocotp_read_bank_open(bool open)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (open) {
|
||||
writel(OCOTP_CTRL_RD_BANK_OPEN,
|
||||
&ocotp_regs->hw_ocotp_ctrl_set);
|
||||
|
||||
/*
|
||||
* Wait before polling the BUSY bit, since the BUSY bit might
|
||||
* be asserted only after a few HCLK cycles and if we were to
|
||||
* poll immediatelly, we could miss the busy bit.
|
||||
*/
|
||||
udelay(10);
|
||||
ret = mxs_ocotp_wait_busy_clear();
|
||||
} else {
|
||||
writel(OCOTP_CTRL_RD_BANK_OPEN,
|
||||
&ocotp_regs->hw_ocotp_ctrl_clr);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void mxs_ocotp_scale_vddio(bool enter, uint32_t *val)
|
||||
{
|
||||
uint32_t scale_val;
|
||||
|
||||
if (enter) {
|
||||
/*
|
||||
* Enter the fuse programming VDDIO voltage setup. We start
|
||||
* scaling the voltage from it's current value down to 2.8V
|
||||
* which is the one and only correct voltage for programming
|
||||
* the OCOTP fuses (according to datasheet).
|
||||
*/
|
||||
scale_val = readl(&power_regs->hw_power_vddioctrl);
|
||||
scale_val &= POWER_VDDIOCTRL_TRG_MASK;
|
||||
|
||||
/* Return the original voltage. */
|
||||
*val = scale_val;
|
||||
|
||||
/*
|
||||
* Start scaling VDDIO down to 0x2, which is 2.8V . Actually,
|
||||
* the value 0x0 should be 2.8V, but that's not the case on
|
||||
* most designs due to load etc., so we play safe. Undervolt
|
||||
* can actually cause incorrect programming of the fuses and
|
||||
* or reboots of the board.
|
||||
*/
|
||||
while (scale_val > 2) {
|
||||
clrsetbits_le32(&power_regs->hw_power_vddioctrl,
|
||||
POWER_VDDIOCTRL_TRG_MASK, --scale_val);
|
||||
udelay(500);
|
||||
}
|
||||
} else {
|
||||
/* Start scaling VDDIO up to original value . */
|
||||
for (scale_val = 2; scale_val <= *val; scale_val++) {
|
||||
clrsetbits_le32(&power_regs->hw_power_vddioctrl,
|
||||
POWER_VDDIOCTRL_TRG_MASK, scale_val);
|
||||
udelay(500);
|
||||
}
|
||||
}
|
||||
|
||||
mdelay(10);
|
||||
}
|
||||
|
||||
static int mxs_ocotp_wait_hclk_ready(void)
|
||||
{
|
||||
uint32_t reg, timeout = MXS_OCOTP_TIMEOUT;
|
||||
|
||||
while (--timeout) {
|
||||
reg = readl(&clkctrl_regs->hw_clkctrl_hbus);
|
||||
if (!(reg & CLKCTRL_HBUS_ASM_BUSY))
|
||||
break;
|
||||
}
|
||||
|
||||
if (!timeout)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxs_ocotp_scale_hclk(bool enter, uint32_t *val)
|
||||
{
|
||||
uint32_t scale_val;
|
||||
int ret;
|
||||
|
||||
ret = mxs_ocotp_wait_hclk_ready();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Set CPU bypass */
|
||||
writel(CLKCTRL_CLKSEQ_BYPASS_CPU,
|
||||
&clkctrl_regs->hw_clkctrl_clkseq_set);
|
||||
|
||||
if (enter) {
|
||||
/* Return the original HCLK clock speed. */
|
||||
*val = readl(&clkctrl_regs->hw_clkctrl_hbus);
|
||||
*val &= CLKCTRL_HBUS_DIV_MASK;
|
||||
*val >>= CLKCTRL_HBUS_DIV_OFFSET;
|
||||
|
||||
/* Scale the HCLK to 454/19 = 23.9 MHz . */
|
||||
scale_val = (~19) << CLKCTRL_HBUS_DIV_OFFSET;
|
||||
scale_val &= CLKCTRL_HBUS_DIV_MASK;
|
||||
} else {
|
||||
/* Scale the HCLK back to original frequency. */
|
||||
scale_val = (~(*val)) << CLKCTRL_HBUS_DIV_OFFSET;
|
||||
scale_val &= CLKCTRL_HBUS_DIV_MASK;
|
||||
}
|
||||
|
||||
writel(CLKCTRL_HBUS_DIV_MASK,
|
||||
&clkctrl_regs->hw_clkctrl_hbus_set);
|
||||
writel(scale_val,
|
||||
&clkctrl_regs->hw_clkctrl_hbus_clr);
|
||||
|
||||
mdelay(10);
|
||||
|
||||
ret = mxs_ocotp_wait_hclk_ready();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Disable CPU bypass */
|
||||
writel(CLKCTRL_CLKSEQ_BYPASS_CPU,
|
||||
&clkctrl_regs->hw_clkctrl_clkseq_clr);
|
||||
|
||||
mdelay(10);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxs_ocotp_write_fuse(uint32_t addr, uint32_t mask)
|
||||
{
|
||||
uint32_t hclk_val, vddio_val;
|
||||
int ret;
|
||||
|
||||
mxs_ocotp_clear_error();
|
||||
|
||||
/* Make sure the banks are closed for reading. */
|
||||
ret = mxs_ocotp_read_bank_open(0);
|
||||
if (ret) {
|
||||
puts("Failed closing banks for reading!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = mxs_ocotp_scale_hclk(1, &hclk_val);
|
||||
if (ret) {
|
||||
puts("Failed scaling down the HCLK!\n");
|
||||
return ret;
|
||||
}
|
||||
mxs_ocotp_scale_vddio(1, &vddio_val);
|
||||
|
||||
ret = mxs_ocotp_wait_busy_clear();
|
||||
if (ret) {
|
||||
puts("Failed waiting for ready state!\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Program the fuse address */
|
||||
writel(addr | OCOTP_CTRL_WR_UNLOCK_KEY, &ocotp_regs->hw_ocotp_ctrl);
|
||||
|
||||
/* Program the data. */
|
||||
writel(mask, &ocotp_regs->hw_ocotp_data);
|
||||
|
||||
udelay(10);
|
||||
|
||||
ret = mxs_ocotp_wait_busy_clear();
|
||||
if (ret) {
|
||||
puts("Failed waiting for ready state!\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Check for errors */
|
||||
if (readl(&ocotp_regs->hw_ocotp_ctrl) & OCOTP_CTRL_ERROR) {
|
||||
puts("Failed writing fuses!\n");
|
||||
ret = -EPERM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
fail:
|
||||
mxs_ocotp_scale_vddio(0, &vddio_val);
|
||||
if (mxs_ocotp_scale_hclk(0, &hclk_val))
|
||||
puts("Failed scaling up the HCLK!\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mxs_ocotp_read_fuse(uint32_t reg, uint32_t *val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Register offset from CUST0 */
|
||||
reg = ((uint32_t)&ocotp_regs->hw_ocotp_cust0) + (reg << 4);
|
||||
|
||||
ret = mxs_ocotp_wait_busy_clear();
|
||||
if (ret) {
|
||||
puts("Failed waiting for ready state!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
mxs_ocotp_clear_error();
|
||||
|
||||
ret = mxs_ocotp_read_bank_open(1);
|
||||
if (ret) {
|
||||
puts("Failed opening banks for reading!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
*val = readl(reg);
|
||||
|
||||
ret = mxs_ocotp_read_bank_open(0);
|
||||
if (ret) {
|
||||
puts("Failed closing banks for reading!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mxs_ocotp_valid(u32 bank, u32 word)
|
||||
{
|
||||
if (bank > 4)
|
||||
return -EINVAL;
|
||||
if (word > 7)
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The 'fuse' command API
|
||||
*/
|
||||
int fuse_read(u32 bank, u32 word, u32 *val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = mxs_ocotp_valid(bank, word);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return mxs_ocotp_read_fuse((bank << 3) | word, val);
|
||||
}
|
||||
|
||||
int fuse_prog(u32 bank, u32 word, u32 val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = mxs_ocotp_valid(bank, word);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return mxs_ocotp_write_fuse((bank << 3) | word, val);
|
||||
}
|
||||
|
||||
int fuse_sense(u32 bank, u32 word, u32 *val)
|
||||
{
|
||||
/* We do not support sensing :-( */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int fuse_override(u32 bank, u32 word, u32 val)
|
||||
{
|
||||
/* We do not support overriding :-( */
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2000
|
||||
* Rob Taylor, Flying Pig Systems. robt@flyingpig.com.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <ns87308.h>
|
||||
|
||||
void initialise_ns87308 (void)
|
||||
{
|
||||
#ifdef CONFIG_SYS_NS87308_PS2MOD
|
||||
unsigned char data;
|
||||
|
||||
/*
|
||||
* Switch floppy drive to PS/2 mode.
|
||||
*/
|
||||
read_pnp_config(SUPOERIO_CONF1, &data);
|
||||
data &= 0xFB;
|
||||
write_pnp_config(SUPOERIO_CONF1, data);
|
||||
#endif
|
||||
|
||||
#if (CONFIG_SYS_NS87308_DEVS & CONFIG_SYS_NS87308_KBC1)
|
||||
PNP_SET_DEVICE_BASE(LDEV_KBC1, CONFIG_SYS_NS87308_KBC1_BASE);
|
||||
write_pnp_config(LUN_CONFIG_REG, 0);
|
||||
write_pnp_config(CBASE_HIGH, 0x00);
|
||||
write_pnp_config(CBASE_LOW, 0x64);
|
||||
#endif
|
||||
|
||||
#if (CONFIG_SYS_NS87308_DEVS & CONFIG_SYS_NS87308_MOUSE)
|
||||
PNP_ACTIVATE_DEVICE(LDEV_MOUSE);
|
||||
#endif
|
||||
|
||||
#if (CONFIG_SYS_NS87308_DEVS & CONFIG_SYS_NS87308_RTC_APC)
|
||||
PNP_SET_DEVICE_BASE(LDEV_RTC_APC, CONFIG_SYS_NS87308_RTC_BASE);
|
||||
#endif
|
||||
|
||||
#if (CONFIG_SYS_NS87308_DEVS & CONFIG_SYS_NS87308_FDC)
|
||||
PNP_SET_DEVICE_BASE(LDEV_FDC, CONFIG_SYS_NS87308_FDC_BASE);
|
||||
write_pnp_config(LUN_CONFIG_REG, 0x40);
|
||||
#endif
|
||||
|
||||
#if (CONFIG_SYS_NS87308_DEVS & CONFIG_SYS_NS87308_RARP)
|
||||
PNP_SET_DEVICE_BASE(LDEV_PARP, CONFIG_SYS_NS87308_LPT_BASE);
|
||||
#endif
|
||||
|
||||
#if (CONFIG_SYS_NS87308_DEVS & CONFIG_SYS_NS87308_UART1)
|
||||
PNP_SET_DEVICE_BASE(LDEV_UART1, CONFIG_SYS_NS87308_UART1_BASE);
|
||||
#endif
|
||||
|
||||
#if (CONFIG_SYS_NS87308_DEVS & CONFIG_SYS_NS87308_UART2)
|
||||
PNP_SET_DEVICE_BASE(LDEV_UART2, CONFIG_SYS_NS87308_UART2_BASE);
|
||||
#endif
|
||||
|
||||
#if (CONFIG_SYS_NS87308_DEVS & CONFIG_SYS_NS87308_GPIO)
|
||||
PNP_SET_DEVICE_BASE(LDEV_GPIO, CONFIG_SYS_NS87308_GPIO_BASE);
|
||||
#endif
|
||||
|
||||
#if (CONFIG_SYS_NS87308_DEVS & CONFIG_SYS_NS87308_POWRMAN)
|
||||
#ifndef CONFIG_SYS_NS87308_PWMAN_BASE
|
||||
PNP_ACTIVATE_DEVICE(LDEV_POWRMAN);
|
||||
#else
|
||||
PNP_SET_DEVICE_BASE(LDEV_POWRMAN, CONFIG_SYS_NS87308_PWMAN_BASE);
|
||||
|
||||
/*
|
||||
* Enable all units
|
||||
*/
|
||||
write_pm_reg(CONFIG_SYS_NS87308_PWMAN_BASE, PWM_FER1, 0x7d);
|
||||
write_pm_reg(CONFIG_SYS_NS87308_PWMAN_BASE, PWM_FER2, 0x87);
|
||||
|
||||
#ifdef CONFIG_SYS_NS87308_PMC1
|
||||
write_pm_reg(CONFIG_SYS_NS87308_PWMAN_BASE, PWM_PMC1, CONFIG_SYS_NS87308_PMC1);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SYS_NS87308_PMC2
|
||||
write_pm_reg(CONFIG_SYS_NS87308_PWMAN_BASE, PWM_PMC2, CONFIG_SYS_NS87308_PMC2);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SYS_NS87308_PMC3
|
||||
write_pm_reg(CONFIG_SYS_NS87308_PWMAN_BASE, PWM_PMC3, CONFIG_SYS_NS87308_PMC3);
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SYS_NS87308_CS0_BASE
|
||||
PNP_PGCS_CSLINE_BASE(0, CONFIG_SYS_NS87308_CS0_BASE);
|
||||
PNP_PGCS_CSLINE_CONF(0, CONFIG_SYS_NS87308_CS0_CONF);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SYS_NS87308_CS1_BASE
|
||||
PNP_PGCS_CSLINE_BASE(1, CONFIG_SYS_NS87308_CS1_BASE);
|
||||
PNP_PGCS_CSLINE_CONF(1, CONFIG_SYS_NS87308_CS1_CONF);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SYS_NS87308_CS2_BASE
|
||||
PNP_PGCS_CSLINE_BASE(2, CONFIG_SYS_NS87308_CS2_BASE);
|
||||
PNP_PGCS_CSLINE_CONF(2, CONFIG_SYS_NS87308_CS2_CONF);
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2016 Stefan Roese <sr@denx.de>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <nuvoton_nct6102d.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/pnp_def.h>
|
||||
|
||||
static void superio_outb(int reg, int val)
|
||||
{
|
||||
outb(reg, NCT_EFER);
|
||||
outb(val, NCT_EFDR);
|
||||
}
|
||||
|
||||
static inline int superio_inb(int reg)
|
||||
{
|
||||
outb(reg, NCT_EFER);
|
||||
return inb(NCT_EFDR);
|
||||
}
|
||||
|
||||
static int superio_enter(void)
|
||||
{
|
||||
outb(NCT_ENTRY_KEY, NCT_EFER); /* Enter extended function mode */
|
||||
outb(NCT_ENTRY_KEY, NCT_EFER); /* Again according to manual */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void superio_select(int ld)
|
||||
{
|
||||
superio_outb(NCT_LD_SELECT_REG, ld);
|
||||
}
|
||||
|
||||
static void superio_exit(void)
|
||||
{
|
||||
outb(NCT_EXIT_KEY, NCT_EFER); /* Leave extended function mode */
|
||||
}
|
||||
|
||||
/*
|
||||
* The Nuvoton NCT6102D starts per default after reset with both,
|
||||
* the internal watchdog and the internal legacy UART enabled. This
|
||||
* code provides a function to disable the watchdog.
|
||||
*/
|
||||
int nct6102d_wdt_disable(void)
|
||||
{
|
||||
superio_enter();
|
||||
/* Select logical device for WDT */
|
||||
superio_select(NCT6102D_LD_WDT);
|
||||
superio_outb(NCT6102D_WDT_TIMEOUT, 0x00);
|
||||
superio_exit();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2015 Stefan Roese <sr@denx.de>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <errno.h>
|
||||
#include <i2c.h>
|
||||
#include <status_led.h>
|
||||
|
||||
#ifndef CONFIG_PCA9551_I2C_ADDR
|
||||
#error "CONFIG_PCA9551_I2C_ADDR not defined!"
|
||||
#endif
|
||||
|
||||
#define PCA9551_REG_INPUT 0x00 /* Input register (read only) */
|
||||
#define PCA9551_REG_PSC0 0x01 /* Frequency prescaler 0 */
|
||||
#define PCA9551_REG_PWM0 0x02 /* PWM0 */
|
||||
#define PCA9551_REG_PSC1 0x03 /* Frequency prescaler 1 */
|
||||
#define PCA9551_REG_PWM1 0x04 /* PWM1 */
|
||||
#define PCA9551_REG_LS0 0x05 /* LED0 to LED3 selector */
|
||||
#define PCA9551_REG_LS1 0x06 /* LED4 to LED7 selector */
|
||||
|
||||
#define PCA9551_CTRL_AI (1 << 4) /* Auto-increment flag */
|
||||
|
||||
#define PCA9551_LED_STATE_ON 0x00
|
||||
#define PCA9551_LED_STATE_OFF 0x01
|
||||
#define PCA9551_LED_STATE_BLINK0 0x02
|
||||
#define PCA9551_LED_STATE_BLINK1 0x03
|
||||
|
||||
struct pca9551_blink_rate {
|
||||
u8 psc; /* Frequency preescaler, see PCA9551_7.pdf p. 6 */
|
||||
u8 pwm; /* Pulse width modulation, see PCA9551_7.pdf p. 6 */
|
||||
};
|
||||
|
||||
static int freq_last = -1;
|
||||
static int mask_last = -1;
|
||||
static int idx_last = -1;
|
||||
static int mode_last;
|
||||
|
||||
static int pca9551_led_get_state(int led, int *state)
|
||||
{
|
||||
unsigned int reg;
|
||||
u8 shift, buf;
|
||||
int ret;
|
||||
|
||||
if (led < 0 || led > 7) {
|
||||
return -EINVAL;
|
||||
} else if (led < 4) {
|
||||
reg = PCA9551_REG_LS0;
|
||||
shift = led << 1;
|
||||
} else {
|
||||
reg = PCA9551_REG_LS1;
|
||||
shift = (led - 4) << 1;
|
||||
}
|
||||
|
||||
ret = i2c_read(CONFIG_PCA9551_I2C_ADDR, reg, 1, &buf, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*state = (buf >> shift) & 0x03;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pca9551_led_set_state(int led, int state)
|
||||
{
|
||||
unsigned int reg;
|
||||
u8 shift, buf, mask;
|
||||
int ret;
|
||||
|
||||
if (led < 0 || led > 7) {
|
||||
return -EINVAL;
|
||||
} else if (led < 4) {
|
||||
reg = PCA9551_REG_LS0;
|
||||
shift = led << 1;
|
||||
} else {
|
||||
reg = PCA9551_REG_LS1;
|
||||
shift = (led - 4) << 1;
|
||||
}
|
||||
mask = 0x03 << shift;
|
||||
|
||||
ret = i2c_read(CONFIG_PCA9551_I2C_ADDR, reg, 1, &buf, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
buf = (buf & ~mask) | ((state & 0x03) << shift);
|
||||
|
||||
ret = i2c_write(CONFIG_PCA9551_I2C_ADDR, reg, 1, &buf, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pca9551_led_set_blink_rate(int idx, struct pca9551_blink_rate rate)
|
||||
{
|
||||
unsigned int reg;
|
||||
int ret;
|
||||
|
||||
switch (idx) {
|
||||
case 0:
|
||||
reg = PCA9551_REG_PSC0;
|
||||
break;
|
||||
case 1:
|
||||
reg = PCA9551_REG_PSC1;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
reg |= PCA9551_CTRL_AI;
|
||||
|
||||
ret = i2c_write(CONFIG_PCA9551_I2C_ADDR, reg, 1, (u8 *)&rate, 2);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Functions referenced by cmd_led.c or status_led.c
|
||||
*/
|
||||
void __led_init(led_id_t id, int state)
|
||||
{
|
||||
}
|
||||
|
||||
void __led_set(led_id_t mask, int state)
|
||||
{
|
||||
if (state == CONFIG_LED_STATUS_OFF)
|
||||
pca9551_led_set_state(mask, PCA9551_LED_STATE_OFF);
|
||||
else
|
||||
pca9551_led_set_state(mask, PCA9551_LED_STATE_ON);
|
||||
}
|
||||
|
||||
void __led_toggle(led_id_t mask)
|
||||
{
|
||||
int state = 0;
|
||||
|
||||
pca9551_led_get_state(mask, &state);
|
||||
pca9551_led_set_state(mask, !state);
|
||||
}
|
||||
|
||||
void __led_blink(led_id_t mask, int freq)
|
||||
{
|
||||
struct pca9551_blink_rate rate;
|
||||
int mode;
|
||||
int idx;
|
||||
|
||||
if ((freq == freq_last) || (mask == mask_last)) {
|
||||
idx = idx_last;
|
||||
mode = mode_last;
|
||||
} else {
|
||||
/* Toggle blink index */
|
||||
if (idx_last == 0) {
|
||||
idx = 1;
|
||||
mode = PCA9551_LED_STATE_BLINK1;
|
||||
} else {
|
||||
idx = 0;
|
||||
mode = PCA9551_LED_STATE_BLINK0;
|
||||
}
|
||||
|
||||
idx_last = idx;
|
||||
mode_last = mode;
|
||||
}
|
||||
freq_last = freq;
|
||||
mask_last = mask;
|
||||
|
||||
rate.psc = ((freq * 38) / 1000) - 1;
|
||||
rate.pwm = 128; /* 50% duty cycle */
|
||||
|
||||
pca9551_led_set_blink_rate(idx, rate);
|
||||
pca9551_led_set_state(mask, mode);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (c) 2015 Google, Inc
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <pwrseq.h>
|
||||
|
||||
int pwrseq_set_power(struct udevice *dev, bool enable)
|
||||
{
|
||||
struct pwrseq_ops *ops = pwrseq_get_ops(dev);
|
||||
|
||||
if (!ops->set_power)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->set_power(dev, enable);
|
||||
}
|
||||
|
||||
UCLASS_DRIVER(pwrseq) = {
|
||||
.id = UCLASS_PWRSEQ,
|
||||
.name = "pwrseq",
|
||||
};
|
||||
@@ -0,0 +1,393 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2015 Miao Yan <yanmiaobest@gmail.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <command.h>
|
||||
#include <errno.h>
|
||||
#include <malloc.h>
|
||||
#include <qfw.h>
|
||||
#include <asm/io.h>
|
||||
#ifdef CONFIG_GENERATE_ACPI_TABLE
|
||||
#include <asm/tables.h>
|
||||
#endif
|
||||
#include <linux/list.h>
|
||||
|
||||
static bool fwcfg_present;
|
||||
static bool fwcfg_dma_present;
|
||||
static struct fw_cfg_arch_ops *fwcfg_arch_ops;
|
||||
|
||||
static LIST_HEAD(fw_list);
|
||||
|
||||
#ifdef CONFIG_GENERATE_ACPI_TABLE
|
||||
/*
|
||||
* This function allocates memory for ACPI tables
|
||||
*
|
||||
* @entry : BIOS linker command entry which tells where to allocate memory
|
||||
* (either high memory or low memory)
|
||||
* @addr : The address that should be used for low memory allcation. If the
|
||||
* memory allocation request is 'ZONE_HIGH' then this parameter will
|
||||
* be ignored.
|
||||
* @return: 0 on success, or negative value on failure
|
||||
*/
|
||||
static int bios_linker_allocate(struct bios_linker_entry *entry, ulong *addr)
|
||||
{
|
||||
uint32_t size, align;
|
||||
struct fw_file *file;
|
||||
unsigned long aligned_addr;
|
||||
|
||||
align = le32_to_cpu(entry->alloc.align);
|
||||
/* align must be power of 2 */
|
||||
if (align & (align - 1)) {
|
||||
printf("error: wrong alignment %u\n", align);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
file = qemu_fwcfg_find_file(entry->alloc.file);
|
||||
if (!file) {
|
||||
printf("error: can't find file %s\n", entry->alloc.file);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
size = be32_to_cpu(file->cfg.size);
|
||||
|
||||
/*
|
||||
* ZONE_HIGH means we need to allocate from high memory, since
|
||||
* malloc space is already at the end of RAM, so we directly use it.
|
||||
* If allocation zone is ZONE_FSEG, then we use the 'addr' passed
|
||||
* in which is low memory
|
||||
*/
|
||||
if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_HIGH) {
|
||||
aligned_addr = (unsigned long)memalign(align, size);
|
||||
if (!aligned_addr) {
|
||||
printf("error: allocating resource\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
} else if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG) {
|
||||
aligned_addr = ALIGN(*addr, align);
|
||||
} else {
|
||||
printf("error: invalid allocation zone\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
debug("bios_linker_allocate: allocate file %s, size %u, zone %d, align %u, addr 0x%lx\n",
|
||||
file->cfg.name, size, entry->alloc.zone, align, aligned_addr);
|
||||
|
||||
qemu_fwcfg_read_entry(be16_to_cpu(file->cfg.select),
|
||||
size, (void *)aligned_addr);
|
||||
file->addr = aligned_addr;
|
||||
|
||||
/* adjust address for low memory allocation */
|
||||
if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG)
|
||||
*addr = (aligned_addr + size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function patches ACPI tables previously loaded
|
||||
* by bios_linker_allocate()
|
||||
*
|
||||
* @entry : BIOS linker command entry which tells how to patch
|
||||
* ACPI tables
|
||||
* @return: 0 on success, or negative value on failure
|
||||
*/
|
||||
static int bios_linker_add_pointer(struct bios_linker_entry *entry)
|
||||
{
|
||||
struct fw_file *dest, *src;
|
||||
uint32_t offset = le32_to_cpu(entry->pointer.offset);
|
||||
uint64_t pointer = 0;
|
||||
|
||||
dest = qemu_fwcfg_find_file(entry->pointer.dest_file);
|
||||
if (!dest || !dest->addr)
|
||||
return -ENOENT;
|
||||
src = qemu_fwcfg_find_file(entry->pointer.src_file);
|
||||
if (!src || !src->addr)
|
||||
return -ENOENT;
|
||||
|
||||
debug("bios_linker_add_pointer: dest->addr 0x%lx, src->addr 0x%lx, offset 0x%x size %u, 0x%llx\n",
|
||||
dest->addr, src->addr, offset, entry->pointer.size, pointer);
|
||||
|
||||
memcpy(&pointer, (char *)dest->addr + offset, entry->pointer.size);
|
||||
pointer = le64_to_cpu(pointer);
|
||||
pointer += (unsigned long)src->addr;
|
||||
pointer = cpu_to_le64(pointer);
|
||||
memcpy((char *)dest->addr + offset, &pointer, entry->pointer.size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function updates checksum fields of ACPI tables previously loaded
|
||||
* by bios_linker_allocate()
|
||||
*
|
||||
* @entry : BIOS linker command entry which tells where to update ACPI table
|
||||
* checksums
|
||||
* @return: 0 on success, or negative value on failure
|
||||
*/
|
||||
static int bios_linker_add_checksum(struct bios_linker_entry *entry)
|
||||
{
|
||||
struct fw_file *file;
|
||||
uint8_t *data, cksum = 0;
|
||||
uint8_t *cksum_start;
|
||||
|
||||
file = qemu_fwcfg_find_file(entry->cksum.file);
|
||||
if (!file || !file->addr)
|
||||
return -ENOENT;
|
||||
|
||||
data = (uint8_t *)(file->addr + le32_to_cpu(entry->cksum.offset));
|
||||
cksum_start = (uint8_t *)(file->addr + le32_to_cpu(entry->cksum.start));
|
||||
cksum = table_compute_checksum(cksum_start,
|
||||
le32_to_cpu(entry->cksum.length));
|
||||
*data = cksum;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This function loads and patches ACPI tables provided by QEMU */
|
||||
ulong write_acpi_tables(ulong addr)
|
||||
{
|
||||
int i, ret = 0;
|
||||
struct fw_file *file;
|
||||
struct bios_linker_entry *table_loader;
|
||||
struct bios_linker_entry *entry;
|
||||
uint32_t size;
|
||||
|
||||
/* make sure fw_list is loaded */
|
||||
ret = qemu_fwcfg_read_firmware_list();
|
||||
if (ret) {
|
||||
printf("error: can't read firmware file list\n");
|
||||
return addr;
|
||||
}
|
||||
|
||||
file = qemu_fwcfg_find_file("etc/table-loader");
|
||||
if (!file) {
|
||||
printf("error: can't find etc/table-loader\n");
|
||||
return addr;
|
||||
}
|
||||
|
||||
size = be32_to_cpu(file->cfg.size);
|
||||
if ((size % sizeof(*entry)) != 0) {
|
||||
printf("error: table-loader maybe corrupted\n");
|
||||
return addr;
|
||||
}
|
||||
|
||||
table_loader = malloc(size);
|
||||
if (!table_loader) {
|
||||
printf("error: no memory for table-loader\n");
|
||||
return addr;
|
||||
}
|
||||
|
||||
qemu_fwcfg_read_entry(be16_to_cpu(file->cfg.select),
|
||||
size, table_loader);
|
||||
|
||||
for (i = 0; i < (size / sizeof(*entry)); i++) {
|
||||
entry = table_loader + i;
|
||||
switch (le32_to_cpu(entry->command)) {
|
||||
case BIOS_LINKER_LOADER_COMMAND_ALLOCATE:
|
||||
ret = bios_linker_allocate(entry, &addr);
|
||||
if (ret)
|
||||
goto out;
|
||||
break;
|
||||
case BIOS_LINKER_LOADER_COMMAND_ADD_POINTER:
|
||||
ret = bios_linker_add_pointer(entry);
|
||||
if (ret)
|
||||
goto out;
|
||||
break;
|
||||
case BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM:
|
||||
ret = bios_linker_add_checksum(entry);
|
||||
if (ret)
|
||||
goto out;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
if (ret) {
|
||||
struct fw_cfg_file_iter iter;
|
||||
for (file = qemu_fwcfg_file_iter_init(&iter);
|
||||
!qemu_fwcfg_file_iter_end(&iter);
|
||||
file = qemu_fwcfg_file_iter_next(&iter)) {
|
||||
if (file->addr) {
|
||||
free((void *)file->addr);
|
||||
file->addr = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(table_loader);
|
||||
return addr;
|
||||
}
|
||||
|
||||
ulong acpi_get_rsdp_addr(void)
|
||||
{
|
||||
struct fw_file *file;
|
||||
|
||||
file = qemu_fwcfg_find_file("etc/acpi/rsdp");
|
||||
return file->addr;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Read configuration item using fw_cfg PIO interface */
|
||||
static void qemu_fwcfg_read_entry_pio(uint16_t entry,
|
||||
uint32_t size, void *address)
|
||||
{
|
||||
debug("qemu_fwcfg_read_entry_pio: entry 0x%x, size %u address %p\n",
|
||||
entry, size, address);
|
||||
|
||||
return fwcfg_arch_ops->arch_read_pio(entry, size, address);
|
||||
}
|
||||
|
||||
/* Read configuration item using fw_cfg DMA interface */
|
||||
static void qemu_fwcfg_read_entry_dma(uint16_t entry,
|
||||
uint32_t size, void *address)
|
||||
{
|
||||
struct fw_cfg_dma_access dma;
|
||||
|
||||
dma.length = cpu_to_be32(size);
|
||||
dma.address = cpu_to_be64((uintptr_t)address);
|
||||
dma.control = cpu_to_be32(FW_CFG_DMA_READ);
|
||||
|
||||
/*
|
||||
* writting FW_CFG_INVALID will cause read operation to resume at
|
||||
* last offset, otherwise read will start at offset 0
|
||||
*/
|
||||
if (entry != FW_CFG_INVALID)
|
||||
dma.control |= cpu_to_be32(FW_CFG_DMA_SELECT | (entry << 16));
|
||||
|
||||
barrier();
|
||||
|
||||
debug("qemu_fwcfg_read_entry_dma: entry 0x%x, size %u address %p, control 0x%x\n",
|
||||
entry, size, address, be32_to_cpu(dma.control));
|
||||
|
||||
fwcfg_arch_ops->arch_read_dma(&dma);
|
||||
}
|
||||
|
||||
bool qemu_fwcfg_present(void)
|
||||
{
|
||||
return fwcfg_present;
|
||||
}
|
||||
|
||||
bool qemu_fwcfg_dma_present(void)
|
||||
{
|
||||
return fwcfg_dma_present;
|
||||
}
|
||||
|
||||
void qemu_fwcfg_read_entry(uint16_t entry, uint32_t length, void *address)
|
||||
{
|
||||
if (fwcfg_dma_present)
|
||||
qemu_fwcfg_read_entry_dma(entry, length, address);
|
||||
else
|
||||
qemu_fwcfg_read_entry_pio(entry, length, address);
|
||||
}
|
||||
|
||||
int qemu_fwcfg_online_cpus(void)
|
||||
{
|
||||
uint16_t nb_cpus;
|
||||
|
||||
if (!fwcfg_present)
|
||||
return -ENODEV;
|
||||
|
||||
qemu_fwcfg_read_entry(FW_CFG_NB_CPUS, 2, &nb_cpus);
|
||||
|
||||
return le16_to_cpu(nb_cpus);
|
||||
}
|
||||
|
||||
int qemu_fwcfg_read_firmware_list(void)
|
||||
{
|
||||
int i;
|
||||
uint32_t count;
|
||||
struct fw_file *file;
|
||||
struct list_head *entry;
|
||||
|
||||
/* don't read it twice */
|
||||
if (!list_empty(&fw_list))
|
||||
return 0;
|
||||
|
||||
qemu_fwcfg_read_entry(FW_CFG_FILE_DIR, 4, &count);
|
||||
if (!count)
|
||||
return 0;
|
||||
|
||||
count = be32_to_cpu(count);
|
||||
for (i = 0; i < count; i++) {
|
||||
file = malloc(sizeof(*file));
|
||||
if (!file) {
|
||||
printf("error: allocating resource\n");
|
||||
goto err;
|
||||
}
|
||||
qemu_fwcfg_read_entry(FW_CFG_INVALID,
|
||||
sizeof(struct fw_cfg_file), &file->cfg);
|
||||
file->addr = 0;
|
||||
list_add_tail(&file->list, &fw_list);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
list_for_each(entry, &fw_list) {
|
||||
file = list_entry(entry, struct fw_file, list);
|
||||
free(file);
|
||||
}
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
struct fw_file *qemu_fwcfg_find_file(const char *name)
|
||||
{
|
||||
struct list_head *entry;
|
||||
struct fw_file *file;
|
||||
|
||||
list_for_each(entry, &fw_list) {
|
||||
file = list_entry(entry, struct fw_file, list);
|
||||
if (!strcmp(file->cfg.name, name))
|
||||
return file;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct fw_file *qemu_fwcfg_file_iter_init(struct fw_cfg_file_iter *iter)
|
||||
{
|
||||
iter->entry = fw_list.next;
|
||||
return list_entry((struct list_head *)iter->entry,
|
||||
struct fw_file, list);
|
||||
}
|
||||
|
||||
struct fw_file *qemu_fwcfg_file_iter_next(struct fw_cfg_file_iter *iter)
|
||||
{
|
||||
iter->entry = ((struct list_head *)iter->entry)->next;
|
||||
return list_entry((struct list_head *)iter->entry,
|
||||
struct fw_file, list);
|
||||
}
|
||||
|
||||
bool qemu_fwcfg_file_iter_end(struct fw_cfg_file_iter *iter)
|
||||
{
|
||||
return iter->entry == &fw_list;
|
||||
}
|
||||
|
||||
void qemu_fwcfg_init(struct fw_cfg_arch_ops *ops)
|
||||
{
|
||||
uint32_t qemu;
|
||||
uint32_t dma_enabled;
|
||||
|
||||
fwcfg_present = false;
|
||||
fwcfg_dma_present = false;
|
||||
fwcfg_arch_ops = NULL;
|
||||
|
||||
if (!ops || !ops->arch_read_pio || !ops->arch_read_dma)
|
||||
return;
|
||||
fwcfg_arch_ops = ops;
|
||||
|
||||
qemu_fwcfg_read_entry_pio(FW_CFG_SIGNATURE, 4, &qemu);
|
||||
if (be32_to_cpu(qemu) == QEMU_FW_CFG_SIGNATURE)
|
||||
fwcfg_present = true;
|
||||
|
||||
if (fwcfg_present) {
|
||||
qemu_fwcfg_read_entry_pio(FW_CFG_ID, 1, &dma_enabled);
|
||||
if (dma_enabled & FW_CFG_DMA_ENABLED)
|
||||
fwcfg_dma_present = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* eFuse driver for Rockchip devices
|
||||
*
|
||||
* Copyright 2017, Theobroma Systems Design und Consulting GmbH
|
||||
* Written by Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <command.h>
|
||||
#include <display_options.h>
|
||||
#include <dm.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/delay.h>
|
||||
#include <misc.h>
|
||||
|
||||
#define RK3399_A_SHIFT 16
|
||||
#define RK3399_A_MASK 0x3ff
|
||||
#define RK3399_NFUSES 32
|
||||
#define RK3399_BYTES_PER_FUSE 4
|
||||
#define RK3399_STROBSFTSEL BIT(9)
|
||||
#define RK3399_RSB BIT(7)
|
||||
#define RK3399_PD BIT(5)
|
||||
#define RK3399_PGENB BIT(3)
|
||||
#define RK3399_LOAD BIT(2)
|
||||
#define RK3399_STROBE BIT(1)
|
||||
#define RK3399_CSB BIT(0)
|
||||
|
||||
struct rockchip_efuse_regs {
|
||||
u32 ctrl; /* 0x00 efuse control register */
|
||||
u32 dout; /* 0x04 efuse data out register */
|
||||
u32 rf; /* 0x08 efuse redundancy bit used register */
|
||||
u32 _rsvd0;
|
||||
u32 jtag_pass; /* 0x10 JTAG password */
|
||||
u32 strobe_finish_ctrl;
|
||||
/* 0x14 efuse strobe finish control register */
|
||||
};
|
||||
|
||||
struct rockchip_efuse_platdata {
|
||||
void __iomem *base;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
#if defined(DEBUG)
|
||||
static int dump_efuses(cmd_tbl_t *cmdtp, int flag,
|
||||
int argc, char * const argv[])
|
||||
{
|
||||
/*
|
||||
* N.B.: This function is tailored towards the RK3399 and assumes that
|
||||
* there's always 32 fuses x 32 bits (i.e. 128 bytes of data) to
|
||||
* be read.
|
||||
*/
|
||||
|
||||
struct udevice *dev;
|
||||
u8 fuses[128];
|
||||
int ret;
|
||||
|
||||
/* retrieve the device */
|
||||
ret = uclass_get_device_by_driver(UCLASS_MISC,
|
||||
DM_GET_DRIVER(rockchip_efuse), &dev);
|
||||
if (ret) {
|
||||
printf("%s: no misc-device found\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = misc_read(dev, 0, &fuses, sizeof(fuses));
|
||||
if (ret < 0) {
|
||||
printf("%s: misc_read failed\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
printf("efuse-contents:\n");
|
||||
print_buffer(0, fuses, 1, 128, 16);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
U_BOOT_CMD(
|
||||
rk3399_dump_efuses, 1, 1, dump_efuses,
|
||||
"Dump the content of the efuses",
|
||||
""
|
||||
);
|
||||
#endif
|
||||
|
||||
static int rockchip_rk3399_efuse_read(struct udevice *dev, int offset,
|
||||
void *buf, int size)
|
||||
{
|
||||
struct rockchip_efuse_platdata *plat = dev_get_platdata(dev);
|
||||
struct rockchip_efuse_regs *efuse =
|
||||
(struct rockchip_efuse_regs *)plat->base;
|
||||
|
||||
unsigned int addr_start, addr_end, addr_offset;
|
||||
u32 out_value;
|
||||
u8 bytes[RK3399_NFUSES * RK3399_BYTES_PER_FUSE];
|
||||
int i = 0;
|
||||
u32 addr;
|
||||
|
||||
addr_start = offset / RK3399_BYTES_PER_FUSE;
|
||||
addr_offset = offset % RK3399_BYTES_PER_FUSE;
|
||||
addr_end = DIV_ROUND_UP(offset + size, RK3399_BYTES_PER_FUSE);
|
||||
|
||||
/* cap to the size of the efuse block */
|
||||
if (addr_end > RK3399_NFUSES)
|
||||
addr_end = RK3399_NFUSES;
|
||||
|
||||
writel(RK3399_LOAD | RK3399_PGENB | RK3399_STROBSFTSEL | RK3399_RSB,
|
||||
&efuse->ctrl);
|
||||
udelay(1);
|
||||
for (addr = addr_start; addr < addr_end; addr++) {
|
||||
setbits_le32(&efuse->ctrl,
|
||||
RK3399_STROBE | (addr << RK3399_A_SHIFT));
|
||||
udelay(1);
|
||||
out_value = readl(&efuse->dout);
|
||||
clrbits_le32(&efuse->ctrl, RK3399_STROBE);
|
||||
udelay(1);
|
||||
|
||||
memcpy(&bytes[i], &out_value, RK3399_BYTES_PER_FUSE);
|
||||
i += RK3399_BYTES_PER_FUSE;
|
||||
}
|
||||
|
||||
/* Switch to standby mode */
|
||||
writel(RK3399_PD | RK3399_CSB, &efuse->ctrl);
|
||||
|
||||
memcpy(buf, bytes + addr_offset, size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rockchip_efuse_read(struct udevice *dev, int offset,
|
||||
void *buf, int size)
|
||||
{
|
||||
return rockchip_rk3399_efuse_read(dev, offset, buf, size);
|
||||
}
|
||||
|
||||
static const struct misc_ops rockchip_efuse_ops = {
|
||||
.read = rockchip_efuse_read,
|
||||
};
|
||||
|
||||
static int rockchip_efuse_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct rockchip_efuse_platdata *plat = dev_get_platdata(dev);
|
||||
|
||||
plat->base = dev_read_addr_ptr(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id rockchip_efuse_ids[] = {
|
||||
{ .compatible = "rockchip,rk3399-efuse" },
|
||||
{}
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(rockchip_efuse) = {
|
||||
.name = "rockchip_efuse",
|
||||
.id = UCLASS_MISC,
|
||||
.of_match = rockchip_efuse_ids,
|
||||
.ofdata_to_platdata = rockchip_efuse_ofdata_to_platdata,
|
||||
.platdata_auto_alloc_size = sizeof(struct rockchip_efuse_platdata),
|
||||
.ops = &rockchip_efuse_ops,
|
||||
};
|
||||
@@ -0,0 +1,176 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <command.h>
|
||||
#include <dm.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/delay.h>
|
||||
#include <misc.h>
|
||||
|
||||
/* OTP Register Offsets */
|
||||
#define OTPC_SBPI_CTRL 0x0020
|
||||
#define OTPC_SBPI_CMD_VALID_PRE 0x0024
|
||||
#define OTPC_SBPI_CS_VALID_PRE 0x0028
|
||||
#define OTPC_SBPI_STATUS 0x002C
|
||||
#define OTPC_USER_CTRL 0x0100
|
||||
#define OTPC_USER_ADDR 0x0104
|
||||
#define OTPC_USER_ENABLE 0x0108
|
||||
#define OTPC_USER_QP 0x0120
|
||||
#define OTPC_USER_Q 0x0124
|
||||
#define OTPC_INT_STATUS 0x0304
|
||||
#define OTPC_SBPI_CMD0_OFFSET 0x1000
|
||||
#define OTPC_SBPI_CMD1_OFFSET 0x1004
|
||||
|
||||
/* OTP Register bits and masks */
|
||||
#define OTPC_USER_ADDR_MASK GENMASK(31, 16)
|
||||
#define OTPC_USE_USER BIT(0)
|
||||
#define OTPC_USE_USER_MASK GENMASK(16, 16)
|
||||
#define OTPC_USER_FSM_ENABLE BIT(0)
|
||||
#define OTPC_USER_FSM_ENABLE_MASK GENMASK(16, 16)
|
||||
#define OTPC_SBPI_DONE BIT(1)
|
||||
#define OTPC_USER_DONE BIT(2)
|
||||
|
||||
#define SBPI_DAP_ADDR 0x02
|
||||
#define SBPI_DAP_ADDR_SHIFT 8
|
||||
#define SBPI_DAP_ADDR_MASK GENMASK(31, 24)
|
||||
#define SBPI_CMD_VALID_MASK GENMASK(31, 16)
|
||||
#define SBPI_DAP_CMD_WRF 0xC0
|
||||
#define SBPI_DAP_REG_ECC 0x3A
|
||||
#define SBPI_ECC_ENABLE 0x00
|
||||
#define SBPI_ECC_DISABLE 0x09
|
||||
#define SBPI_ENABLE BIT(0)
|
||||
#define SBPI_ENABLE_MASK GENMASK(16, 16)
|
||||
|
||||
#define OTPC_TIMEOUT 10000
|
||||
|
||||
struct rockchip_otp_platdata {
|
||||
void __iomem *base;
|
||||
unsigned long secure_conf_base;
|
||||
unsigned long otp_mask_base;
|
||||
};
|
||||
|
||||
static int rockchip_otp_wait_status(struct rockchip_otp_platdata *otp,
|
||||
u32 flag)
|
||||
{
|
||||
int delay = OTPC_TIMEOUT;
|
||||
|
||||
while (!(readl(otp->base + OTPC_INT_STATUS) & flag)) {
|
||||
udelay(1);
|
||||
delay--;
|
||||
if (delay <= 0) {
|
||||
printf("%s: wait init status timeout\n", __func__);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
|
||||
/* clean int status */
|
||||
writel(flag, otp->base + OTPC_INT_STATUS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rockchip_otp_ecc_enable(struct rockchip_otp_platdata *otp,
|
||||
bool enable)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
writel(SBPI_DAP_ADDR_MASK | (SBPI_DAP_ADDR << SBPI_DAP_ADDR_SHIFT),
|
||||
otp->base + OTPC_SBPI_CTRL);
|
||||
|
||||
writel(SBPI_CMD_VALID_MASK | 0x1, otp->base + OTPC_SBPI_CMD_VALID_PRE);
|
||||
writel(SBPI_DAP_CMD_WRF | SBPI_DAP_REG_ECC,
|
||||
otp->base + OTPC_SBPI_CMD0_OFFSET);
|
||||
|
||||
if (enable)
|
||||
writel(SBPI_ECC_ENABLE, otp->base + OTPC_SBPI_CMD1_OFFSET);
|
||||
else
|
||||
writel(SBPI_ECC_DISABLE, otp->base + OTPC_SBPI_CMD1_OFFSET);
|
||||
|
||||
writel(SBPI_ENABLE_MASK | SBPI_ENABLE, otp->base + OTPC_SBPI_CTRL);
|
||||
|
||||
ret = rockchip_otp_wait_status(otp, OTPC_SBPI_DONE);
|
||||
if (ret < 0)
|
||||
printf("%s timeout during ecc_enable\n", __func__);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rockchip_px30_otp_read(struct udevice *dev, int offset,
|
||||
void *buf, int size)
|
||||
{
|
||||
struct rockchip_otp_platdata *otp = dev_get_platdata(dev);
|
||||
u8 *buffer = buf;
|
||||
int ret = 0;
|
||||
|
||||
ret = rockchip_otp_ecc_enable(otp, false);
|
||||
if (ret < 0) {
|
||||
printf("%s rockchip_otp_ecc_enable err\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
writel(OTPC_USE_USER | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL);
|
||||
udelay(5);
|
||||
while (size--) {
|
||||
writel(offset++ | OTPC_USER_ADDR_MASK,
|
||||
otp->base + OTPC_USER_ADDR);
|
||||
writel(OTPC_USER_FSM_ENABLE | OTPC_USER_FSM_ENABLE_MASK,
|
||||
otp->base + OTPC_USER_ENABLE);
|
||||
|
||||
ret = rockchip_otp_wait_status(otp, OTPC_USER_DONE);
|
||||
if (ret < 0) {
|
||||
printf("%s timeout during read setup\n", __func__);
|
||||
goto read_end;
|
||||
}
|
||||
|
||||
*buffer++ = readb(otp->base + OTPC_USER_Q);
|
||||
}
|
||||
|
||||
read_end:
|
||||
writel(0x0 | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rockchip_otp_read(struct udevice *dev, int offset,
|
||||
void *buf, int size)
|
||||
{
|
||||
return rockchip_px30_otp_read(dev, offset, buf, size);
|
||||
}
|
||||
|
||||
static const struct misc_ops rockchip_otp_ops = {
|
||||
.read = rockchip_otp_read,
|
||||
};
|
||||
|
||||
static int rockchip_otp_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct rockchip_otp_platdata *otp = dev_get_platdata(dev);
|
||||
|
||||
otp->base = dev_read_addr_ptr(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id rockchip_otp_ids[] = {
|
||||
{
|
||||
.compatible = "rockchip,px30-otp",
|
||||
.data = (ulong)&rockchip_px30_otp_read,
|
||||
},
|
||||
{
|
||||
.compatible = "rockchip,rk3308-otp",
|
||||
.data = (ulong)&rockchip_px30_otp_read,
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(rockchip_otp) = {
|
||||
.name = "rockchip_otp",
|
||||
.id = UCLASS_MISC,
|
||||
.of_match = rockchip_otp_ids,
|
||||
.ops = &rockchip_otp_ops,
|
||||
.ofdata_to_platdata = rockchip_otp_ofdata_to_platdata,
|
||||
.platdata_auto_alloc_size = sizeof(struct rockchip_otp_platdata),
|
||||
};
|
||||
@@ -0,0 +1,44 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/pnp_def.h>
|
||||
|
||||
static void pnp_enter_conf_state(u16 dev)
|
||||
{
|
||||
u16 port = dev >> 8;
|
||||
|
||||
outb(0x55, port);
|
||||
}
|
||||
|
||||
static void pnp_exit_conf_state(u16 dev)
|
||||
{
|
||||
u16 port = dev >> 8;
|
||||
|
||||
outb(0xaa, port);
|
||||
}
|
||||
|
||||
void lpc47m_enable_serial(uint dev, uint iobase, uint irq)
|
||||
{
|
||||
pnp_enter_conf_state(dev);
|
||||
pnp_set_logical_device(dev);
|
||||
pnp_set_enable(dev, 0);
|
||||
pnp_set_iobase(dev, PNP_IDX_IO0, iobase);
|
||||
pnp_set_irq(dev, PNP_IDX_IRQ0, irq);
|
||||
pnp_set_enable(dev, 1);
|
||||
pnp_exit_conf_state(dev);
|
||||
}
|
||||
|
||||
void lpc47m_enable_kbc(uint dev, uint irq0, uint irq1)
|
||||
{
|
||||
pnp_enter_conf_state(dev);
|
||||
pnp_set_logical_device(dev);
|
||||
pnp_set_enable(dev, 0);
|
||||
pnp_set_irq(dev, PNP_IDX_IRQ0, irq0);
|
||||
pnp_set_irq(dev, PNP_IDX_IRQ1, irq1);
|
||||
pnp_set_enable(dev, 1);
|
||||
pnp_exit_conf_state(dev);
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2016, Bin Meng <bmeng.cn@gmail.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <errno.h>
|
||||
#include <smsc_sio1007.h>
|
||||
|
||||
static inline u8 sio1007_read(int port, int reg)
|
||||
{
|
||||
outb(reg, port);
|
||||
|
||||
return inb(port + 1);
|
||||
}
|
||||
|
||||
static inline void sio1007_write(int port, int reg, int val)
|
||||
{
|
||||
outb(reg, port);
|
||||
outb(val, port + 1);
|
||||
}
|
||||
|
||||
static inline void sio1007_clrsetbits(int port, int reg, u8 clr, u8 set)
|
||||
{
|
||||
sio1007_write(port, reg, (sio1007_read(port, reg) & ~clr) | set);
|
||||
}
|
||||
|
||||
void sio1007_enable_serial(int port, int num, int iobase, int irq)
|
||||
{
|
||||
if (num < 0 || num > SIO1007_UART_NUM)
|
||||
return;
|
||||
|
||||
/* enter configuration state */
|
||||
outb(0x55, port);
|
||||
|
||||
/* power on serial port and set up its i/o base & irq */
|
||||
if (!num) {
|
||||
sio1007_clrsetbits(port, DEV_POWER_CTRL, 0, UART1_POWER_ON);
|
||||
sio1007_clrsetbits(port, UART1_IOBASE, 0xfe, iobase >> 2);
|
||||
sio1007_clrsetbits(port, UART_IRQ, 0xf0, irq << 4);
|
||||
} else {
|
||||
sio1007_clrsetbits(port, DEV_POWER_CTRL, 0, UART2_POWER_ON);
|
||||
sio1007_clrsetbits(port, UART2_IOBASE, 0xfe, iobase >> 2);
|
||||
sio1007_clrsetbits(port, UART_IRQ, 0x0f, irq);
|
||||
}
|
||||
|
||||
/* exit configuration state */
|
||||
outb(0xaa, port);
|
||||
}
|
||||
|
||||
void sio1007_enable_runtime(int port, int iobase)
|
||||
{
|
||||
/* enter configuration state */
|
||||
outb(0x55, port);
|
||||
|
||||
/* set i/o base for the runtime register block */
|
||||
sio1007_clrsetbits(port, RTR_IOBASE_LOW, 0, iobase >> 4);
|
||||
sio1007_clrsetbits(port, RTR_IOBASE_HIGH, 0, iobase >> 12);
|
||||
/* turn on address decoding for this block */
|
||||
sio1007_clrsetbits(port, DEV_ACTIVATE, 0, RTR_EN);
|
||||
|
||||
/* exit configuration state */
|
||||
outb(0xaa, port);
|
||||
}
|
||||
|
||||
void sio1007_gpio_config(int port, int gpio, int dir, int pol, int type)
|
||||
{
|
||||
int reg = GPIO0_DIR;
|
||||
|
||||
if (gpio < 0 || gpio > SIO1007_GPIO_NUM)
|
||||
return;
|
||||
if (gpio >= GPIO_NUM_PER_GROUP) {
|
||||
reg = GPIO1_DIR;
|
||||
gpio -= GPIO_NUM_PER_GROUP;
|
||||
}
|
||||
|
||||
/* enter configuration state */
|
||||
outb(0x55, port);
|
||||
|
||||
/* set gpio pin direction, polority and type */
|
||||
sio1007_clrsetbits(port, reg, 1 << gpio, dir << gpio);
|
||||
sio1007_clrsetbits(port, reg + 1, 1 << gpio, pol << gpio);
|
||||
sio1007_clrsetbits(port, reg + 2, 1 << gpio, type << gpio);
|
||||
|
||||
/* exit configuration state */
|
||||
outb(0xaa, port);
|
||||
}
|
||||
|
||||
int sio1007_gpio_get_value(int port, int gpio)
|
||||
{
|
||||
int reg = GPIO0_DATA;
|
||||
int val;
|
||||
|
||||
if (gpio < 0 || gpio > SIO1007_GPIO_NUM)
|
||||
return -EINVAL;
|
||||
if (gpio >= GPIO_NUM_PER_GROUP) {
|
||||
reg = GPIO1_DATA;
|
||||
gpio -= GPIO_NUM_PER_GROUP;
|
||||
}
|
||||
|
||||
val = inb(port + reg);
|
||||
if (val & (1 << gpio))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sio1007_gpio_set_value(int port, int gpio, int val)
|
||||
{
|
||||
int reg = GPIO0_DATA;
|
||||
u8 data;
|
||||
|
||||
if (gpio < 0 || gpio > SIO1007_GPIO_NUM)
|
||||
return;
|
||||
if (gpio >= GPIO_NUM_PER_GROUP) {
|
||||
reg = GPIO1_DATA;
|
||||
gpio -= GPIO_NUM_PER_GROUP;
|
||||
}
|
||||
|
||||
data = inb(port + reg);
|
||||
data &= ~(1 << gpio);
|
||||
data |= (val << gpio);
|
||||
outb(data, port + reg);
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (c) 2016 Google, Inc
|
||||
* Written by Simon Glass <sjg@chromium.org>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <dt-structs.h>
|
||||
|
||||
static int sandbox_spl_probe(struct udevice *dev)
|
||||
{
|
||||
struct dtd_sandbox_spl_test *plat = dev_get_platdata(dev);
|
||||
int i;
|
||||
|
||||
printf("of-platdata probe:\n");
|
||||
printf("bool %d\n", plat->boolval);
|
||||
|
||||
printf("byte %02x\n", plat->byteval);
|
||||
printf("bytearray");
|
||||
for (i = 0; i < sizeof(plat->bytearray); i++)
|
||||
printf(" %02x", plat->bytearray[i]);
|
||||
printf("\n");
|
||||
|
||||
printf("int %d\n", plat->intval);
|
||||
printf("intarray");
|
||||
for (i = 0; i < ARRAY_SIZE(plat->intarray); i++)
|
||||
printf(" %d", plat->intarray[i]);
|
||||
printf("\n");
|
||||
|
||||
printf("longbytearray");
|
||||
for (i = 0; i < sizeof(plat->longbytearray); i++)
|
||||
printf(" %02x", plat->longbytearray[i]);
|
||||
printf("\n");
|
||||
|
||||
printf("string %s\n", plat->stringval);
|
||||
printf("stringarray");
|
||||
for (i = 0; i < ARRAY_SIZE(plat->stringarray); i++)
|
||||
printf(" \"%s\"", plat->stringarray[i]);
|
||||
printf("\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
U_BOOT_DRIVER(sandbox_spl_test) = {
|
||||
.name = "sandbox_spl_test",
|
||||
.id = UCLASS_MISC,
|
||||
.flags = DM_FLAG_PRE_RELOC,
|
||||
.probe = sandbox_spl_probe,
|
||||
};
|
||||
@@ -0,0 +1,124 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2000-2003
|
||||
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <status_led.h>
|
||||
|
||||
/*
|
||||
* The purpose of this code is to signal the operational status of a
|
||||
* target which usually boots over the network; while running in
|
||||
* U-Boot, a status LED is blinking. As soon as a valid BOOTP reply
|
||||
* message has been received, the LED is turned off. The Linux
|
||||
* kernel, once it is running, will start blinking the LED again,
|
||||
* with another frequency.
|
||||
*/
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
typedef struct {
|
||||
led_id_t mask;
|
||||
int state;
|
||||
int period;
|
||||
int cnt;
|
||||
} led_dev_t;
|
||||
|
||||
led_dev_t led_dev[] = {
|
||||
{ CONFIG_LED_STATUS_BIT,
|
||||
CONFIG_LED_STATUS_STATE,
|
||||
LED_STATUS_PERIOD,
|
||||
0,
|
||||
},
|
||||
#if defined(CONFIG_LED_STATUS1)
|
||||
{ CONFIG_LED_STATUS_BIT1,
|
||||
CONFIG_LED_STATUS_STATE1,
|
||||
LED_STATUS_PERIOD1,
|
||||
0,
|
||||
},
|
||||
#endif
|
||||
#if defined(CONFIG_LED_STATUS2)
|
||||
{ CONFIG_LED_STATUS_BIT2,
|
||||
CONFIG_LED_STATUS_STATE2,
|
||||
LED_STATUS_PERIOD2,
|
||||
0,
|
||||
},
|
||||
#endif
|
||||
#if defined(CONFIG_LED_STATUS3)
|
||||
{ CONFIG_LED_STATUS_BIT3,
|
||||
CONFIG_LED_STATUS_STATE3,
|
||||
LED_STATUS_PERIOD3,
|
||||
0,
|
||||
},
|
||||
#endif
|
||||
#if defined(CONFIG_LED_STATUS4)
|
||||
{ CONFIG_LED_STATUS_BIT4,
|
||||
CONFIG_LED_STATUS_STATE4,
|
||||
LED_STATUS_PERIOD4,
|
||||
0,
|
||||
},
|
||||
#endif
|
||||
#if defined(CONFIG_LED_STATUS5)
|
||||
{ CONFIG_LED_STATUS_BIT5,
|
||||
CONFIG_LED_STATUS_STATE5,
|
||||
LED_STATUS_PERIOD5,
|
||||
0,
|
||||
},
|
||||
#endif
|
||||
};
|
||||
|
||||
#define MAX_LED_DEV (sizeof(led_dev)/sizeof(led_dev_t))
|
||||
|
||||
static int status_led_init_done = 0;
|
||||
|
||||
void status_led_init(void)
|
||||
{
|
||||
led_dev_t *ld;
|
||||
int i;
|
||||
|
||||
for (i = 0, ld = led_dev; i < MAX_LED_DEV; i++, ld++)
|
||||
__led_init (ld->mask, ld->state);
|
||||
status_led_init_done = 1;
|
||||
}
|
||||
|
||||
void status_led_tick(ulong timestamp)
|
||||
{
|
||||
led_dev_t *ld;
|
||||
int i;
|
||||
|
||||
if (!status_led_init_done)
|
||||
status_led_init();
|
||||
|
||||
for (i = 0, ld = led_dev; i < MAX_LED_DEV; i++, ld++) {
|
||||
|
||||
if (ld->state != CONFIG_LED_STATUS_BLINKING)
|
||||
continue;
|
||||
|
||||
if (++ld->cnt >= ld->period) {
|
||||
__led_toggle (ld->mask);
|
||||
ld->cnt -= ld->period;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void status_led_set(int led, int state)
|
||||
{
|
||||
led_dev_t *ld;
|
||||
|
||||
if (led < 0 || led >= MAX_LED_DEV)
|
||||
return;
|
||||
|
||||
if (!status_led_init_done)
|
||||
status_led_init();
|
||||
|
||||
ld = &led_dev[led];
|
||||
|
||||
ld->state = state;
|
||||
if (state == CONFIG_LED_STATUS_BLINKING) {
|
||||
ld->cnt = 0; /* always start with full period */
|
||||
state = CONFIG_LED_STATUS_ON; /* always start with LED _ON_ */
|
||||
}
|
||||
__led_set (ld->mask, state);
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2017, STMicroelectronics - All Rights Reserved
|
||||
* Author(s): Patrice Chotard, <patrice.chotard@st.com> for STMicroelectronics.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <misc.h>
|
||||
#include <stm32_rcc.h>
|
||||
#include <dm/device-internal.h>
|
||||
#include <dm/lists.h>
|
||||
|
||||
struct stm32_rcc_clk stm32_rcc_clk_f42x = {
|
||||
.drv_name = "stm32fx_rcc_clock",
|
||||
.soc = STM32F42X,
|
||||
};
|
||||
|
||||
struct stm32_rcc_clk stm32_rcc_clk_f469 = {
|
||||
.drv_name = "stm32fx_rcc_clock",
|
||||
.soc = STM32F469,
|
||||
};
|
||||
|
||||
struct stm32_rcc_clk stm32_rcc_clk_f7 = {
|
||||
.drv_name = "stm32fx_rcc_clock",
|
||||
.soc = STM32F7,
|
||||
};
|
||||
|
||||
struct stm32_rcc_clk stm32_rcc_clk_h7 = {
|
||||
.drv_name = "stm32h7_rcc_clock",
|
||||
};
|
||||
|
||||
struct stm32_rcc_clk stm32_rcc_clk_mp1 = {
|
||||
.drv_name = "stm32mp1_clk",
|
||||
.soc = STM32MP1,
|
||||
};
|
||||
|
||||
static int stm32_rcc_bind(struct udevice *dev)
|
||||
{
|
||||
struct udevice *child;
|
||||
struct driver *drv;
|
||||
struct stm32_rcc_clk *rcc_clk =
|
||||
(struct stm32_rcc_clk *)dev_get_driver_data(dev);
|
||||
int ret;
|
||||
|
||||
debug("%s(dev=%p)\n", __func__, dev);
|
||||
drv = lists_driver_lookup_name(rcc_clk->drv_name);
|
||||
if (!drv) {
|
||||
debug("Cannot find driver '%s'\n", rcc_clk->drv_name);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
ret = device_bind_with_driver_data(dev, drv, rcc_clk->drv_name,
|
||||
rcc_clk->soc,
|
||||
dev_ofnode(dev), &child);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
drv = lists_driver_lookup_name("stm32_rcc_reset");
|
||||
if (!drv) {
|
||||
dev_err(dev, "Cannot find driver stm32_rcc_reset'\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
return device_bind_with_driver_data(dev, drv, "stm32_rcc_reset",
|
||||
rcc_clk->soc,
|
||||
dev_ofnode(dev), &child);
|
||||
}
|
||||
|
||||
|
||||
static const struct udevice_id stm32_rcc_ids[] = {
|
||||
{.compatible = "st,stm32f42xx-rcc", .data = (ulong)&stm32_rcc_clk_f42x },
|
||||
{.compatible = "st,stm32f469-rcc", .data = (ulong)&stm32_rcc_clk_f469 },
|
||||
{.compatible = "st,stm32f746-rcc", .data = (ulong)&stm32_rcc_clk_f7 },
|
||||
{.compatible = "st,stm32h743-rcc", .data = (ulong)&stm32_rcc_clk_h7 },
|
||||
{.compatible = "st,stm32mp1-rcc", .data = (ulong)&stm32_rcc_clk_mp1 },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(stm32_rcc) = {
|
||||
.name = "stm32-rcc",
|
||||
.id = UCLASS_NOP,
|
||||
.of_match = stm32_rcc_ids,
|
||||
.bind = stm32_rcc_bind,
|
||||
};
|
||||
@@ -0,0 +1,197 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2018, STMicroelectronics - All Rights Reserved
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <command.h>
|
||||
#include <fuse.h>
|
||||
#include <misc.h>
|
||||
#include <errno.h>
|
||||
#include <dm/device.h>
|
||||
#include <dm/uclass.h>
|
||||
#include <power/stpmic1.h>
|
||||
|
||||
#define STM32MP_OTP_BANK 0
|
||||
#define STM32MP_NVM_BANK 1
|
||||
|
||||
/*
|
||||
* The 'fuse' command API
|
||||
*/
|
||||
int fuse_read(u32 bank, u32 word, u32 *val)
|
||||
{
|
||||
int ret;
|
||||
struct udevice *dev;
|
||||
|
||||
switch (bank) {
|
||||
case STM32MP_OTP_BANK:
|
||||
ret = uclass_get_device_by_driver(UCLASS_MISC,
|
||||
DM_GET_DRIVER(stm32mp_bsec),
|
||||
&dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = misc_read(dev, word * 4 + STM32_BSEC_SHADOW_OFFSET,
|
||||
val, 4);
|
||||
if (ret != 4)
|
||||
ret = -EINVAL;
|
||||
else
|
||||
ret = 0;
|
||||
break;
|
||||
|
||||
#ifdef CONFIG_PMIC_STPMIC1
|
||||
case STM32MP_NVM_BANK:
|
||||
ret = uclass_get_device_by_driver(UCLASS_MISC,
|
||||
DM_GET_DRIVER(stpmic1_nvm),
|
||||
&dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
*val = 0;
|
||||
ret = misc_read(dev, -word, val, 1);
|
||||
if (ret != 1)
|
||||
ret = -EINVAL;
|
||||
else
|
||||
ret = 0;
|
||||
break;
|
||||
#endif /* CONFIG_PMIC_STPMIC1 */
|
||||
|
||||
default:
|
||||
printf("stm32mp %s: wrong value for bank %i\n", __func__, bank);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int fuse_prog(u32 bank, u32 word, u32 val)
|
||||
{
|
||||
struct udevice *dev;
|
||||
int ret;
|
||||
|
||||
switch (bank) {
|
||||
case STM32MP_OTP_BANK:
|
||||
ret = uclass_get_device_by_driver(UCLASS_MISC,
|
||||
DM_GET_DRIVER(stm32mp_bsec),
|
||||
&dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = misc_write(dev, word * 4 + STM32_BSEC_OTP_OFFSET,
|
||||
&val, 4);
|
||||
if (ret != 4)
|
||||
ret = -EINVAL;
|
||||
else
|
||||
ret = 0;
|
||||
break;
|
||||
|
||||
#ifdef CONFIG_PMIC_STPMIC1
|
||||
case STM32MP_NVM_BANK:
|
||||
ret = uclass_get_device_by_driver(UCLASS_MISC,
|
||||
DM_GET_DRIVER(stpmic1_nvm),
|
||||
&dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = misc_write(dev, word, &val, 1);
|
||||
if (ret != 1)
|
||||
ret = -EINVAL;
|
||||
else
|
||||
ret = 0;
|
||||
break;
|
||||
#endif /* CONFIG_PMIC_STPMIC1 */
|
||||
|
||||
default:
|
||||
printf("stm32mp %s: wrong value for bank %i\n", __func__, bank);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int fuse_sense(u32 bank, u32 word, u32 *val)
|
||||
{
|
||||
struct udevice *dev;
|
||||
int ret;
|
||||
|
||||
switch (bank) {
|
||||
case STM32MP_OTP_BANK:
|
||||
ret = uclass_get_device_by_driver(UCLASS_MISC,
|
||||
DM_GET_DRIVER(stm32mp_bsec),
|
||||
&dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = misc_read(dev, word * 4 + STM32_BSEC_OTP_OFFSET, val, 4);
|
||||
if (ret != 4)
|
||||
ret = -EINVAL;
|
||||
else
|
||||
ret = 0;
|
||||
break;
|
||||
|
||||
#ifdef CONFIG_PMIC_STPMIC1
|
||||
case STM32MP_NVM_BANK:
|
||||
ret = uclass_get_device_by_driver(UCLASS_MISC,
|
||||
DM_GET_DRIVER(stpmic1_nvm),
|
||||
&dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
*val = 0;
|
||||
ret = misc_read(dev, word, val, 1);
|
||||
if (ret != 1)
|
||||
ret = -EINVAL;
|
||||
else
|
||||
ret = 0;
|
||||
break;
|
||||
#endif /* CONFIG_PMIC_STPMIC1 */
|
||||
|
||||
default:
|
||||
printf("stm32mp %s: wrong value for bank %i\n", __func__, bank);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int fuse_override(u32 bank, u32 word, u32 val)
|
||||
{
|
||||
struct udevice *dev;
|
||||
int ret;
|
||||
|
||||
switch (bank) {
|
||||
case STM32MP_OTP_BANK:
|
||||
ret = uclass_get_device_by_driver(UCLASS_MISC,
|
||||
DM_GET_DRIVER(stm32mp_bsec),
|
||||
&dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = misc_write(dev, word * 4 + STM32_BSEC_SHADOW_OFFSET,
|
||||
&val, 4);
|
||||
if (ret != 4)
|
||||
ret = -EINVAL;
|
||||
else
|
||||
ret = 0;
|
||||
break;
|
||||
|
||||
#ifdef CONFIG_PMIC_STPMIC1
|
||||
case STM32MP_NVM_BANK:
|
||||
ret = uclass_get_device_by_driver(UCLASS_MISC,
|
||||
DM_GET_DRIVER(stpmic1_nvm),
|
||||
&dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = misc_write(dev, -word, &val, 1);
|
||||
if (ret != 1)
|
||||
ret = -EINVAL;
|
||||
else
|
||||
ret = 0;
|
||||
break;
|
||||
#endif /* CONFIG_PMIC_STPMIC1 */
|
||||
|
||||
default:
|
||||
printf("stm32mp %s: wrong value for bank %i\n",
|
||||
__func__, bank);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,402 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* PCI emulation device which swaps the case of text
|
||||
*
|
||||
* Copyright (c) 2014 Google, Inc
|
||||
* Written by Simon Glass <sjg@chromium.org>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <pci.h>
|
||||
#include <asm/test.h>
|
||||
#include <linux/ctype.h>
|
||||
|
||||
/**
|
||||
* struct swap_case_platdata - platform data for this device
|
||||
*
|
||||
* @command: Current PCI command value
|
||||
* @bar: Current base address values
|
||||
*/
|
||||
struct swap_case_platdata {
|
||||
u16 command;
|
||||
u32 bar[6];
|
||||
};
|
||||
|
||||
enum {
|
||||
MEM_TEXT_SIZE = 0x100,
|
||||
};
|
||||
|
||||
enum swap_case_op {
|
||||
OP_TO_LOWER,
|
||||
OP_TO_UPPER,
|
||||
OP_SWAP,
|
||||
};
|
||||
|
||||
static struct pci_bar {
|
||||
int type;
|
||||
u32 size;
|
||||
} barinfo[] = {
|
||||
{ PCI_BASE_ADDRESS_SPACE_IO, 1 },
|
||||
{ PCI_BASE_ADDRESS_MEM_TYPE_32, MEM_TEXT_SIZE },
|
||||
{ 0, 0 },
|
||||
{ 0, 0 },
|
||||
{ 0, 0 },
|
||||
{ 0, 0 },
|
||||
};
|
||||
|
||||
struct swap_case_priv {
|
||||
enum swap_case_op op;
|
||||
char mem_text[MEM_TEXT_SIZE];
|
||||
};
|
||||
|
||||
static int sandbox_swap_case_use_ea(struct udevice *dev)
|
||||
{
|
||||
return !!ofnode_get_property(dev->node, "use-ea", NULL);
|
||||
}
|
||||
|
||||
/* Please keep these macros in sync with ea_regs below */
|
||||
#define PCI_CAP_ID_EA_SIZE (sizeof(ea_regs) + 4)
|
||||
#define PCI_CAP_ID_EA_ENTRY_CNT 4
|
||||
/* Hardcoded EA structure, excluding 1st DW. */
|
||||
static const u32 ea_regs[] = {
|
||||
/* BEI=0, ES=2, BAR0 32b Base + 32b MaxOffset, I/O space */
|
||||
(2 << 8) | 2,
|
||||
PCI_CAP_EA_BASE_LO0,
|
||||
0,
|
||||
/* BEI=1, ES=2, BAR1 32b Base + 32b MaxOffset */
|
||||
(1 << 4) | 2,
|
||||
PCI_CAP_EA_BASE_LO1,
|
||||
MEM_TEXT_SIZE - 1,
|
||||
/* BEI=2, ES=3, BAR2 64b Base + 32b MaxOffset */
|
||||
(2 << 4) | 3,
|
||||
PCI_CAP_EA_BASE_LO2 | PCI_EA_IS_64,
|
||||
PCI_CAP_EA_SIZE_LO,
|
||||
PCI_CAP_EA_BASE_HI2,
|
||||
/* BEI=4, ES=4, BAR4 64b Base + 64b MaxOffset */
|
||||
(4 << 4) | 4,
|
||||
PCI_CAP_EA_BASE_LO4 | PCI_EA_IS_64,
|
||||
PCI_CAP_EA_SIZE_LO | PCI_EA_IS_64,
|
||||
PCI_CAP_EA_BASE_HI4,
|
||||
PCI_CAP_EA_SIZE_HI,
|
||||
};
|
||||
|
||||
static int sandbox_swap_case_read_ea(struct udevice *emul, uint offset,
|
||||
ulong *valuep, enum pci_size_t size)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
offset = offset - PCI_CAP_ID_EA_OFFSET - 4;
|
||||
reg = ea_regs[offset >> 2];
|
||||
reg >>= (offset % 4) * 8;
|
||||
|
||||
*valuep = reg;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sandbox_swap_case_read_config(struct udevice *emul, uint offset,
|
||||
ulong *valuep, enum pci_size_t size)
|
||||
{
|
||||
struct swap_case_platdata *plat = dev_get_platdata(emul);
|
||||
|
||||
/*
|
||||
* The content of the EA capability structure is handled elsewhere to
|
||||
* keep the switch/case below sane
|
||||
*/
|
||||
if (offset > PCI_CAP_ID_EA_OFFSET + PCI_CAP_LIST_NEXT &&
|
||||
offset < PCI_CAP_ID_EA_OFFSET + PCI_CAP_ID_EA_SIZE)
|
||||
return sandbox_swap_case_read_ea(emul, offset, valuep, size);
|
||||
|
||||
switch (offset) {
|
||||
case PCI_COMMAND:
|
||||
*valuep = plat->command;
|
||||
break;
|
||||
case PCI_HEADER_TYPE:
|
||||
*valuep = 0;
|
||||
break;
|
||||
case PCI_VENDOR_ID:
|
||||
*valuep = SANDBOX_PCI_VENDOR_ID;
|
||||
break;
|
||||
case PCI_DEVICE_ID:
|
||||
*valuep = SANDBOX_PCI_SWAP_CASE_EMUL_ID;
|
||||
break;
|
||||
case PCI_CLASS_DEVICE:
|
||||
if (size == PCI_SIZE_8) {
|
||||
*valuep = SANDBOX_PCI_CLASS_SUB_CODE;
|
||||
} else {
|
||||
*valuep = (SANDBOX_PCI_CLASS_CODE << 8) |
|
||||
SANDBOX_PCI_CLASS_SUB_CODE;
|
||||
}
|
||||
break;
|
||||
case PCI_CLASS_CODE:
|
||||
*valuep = SANDBOX_PCI_CLASS_CODE;
|
||||
break;
|
||||
case PCI_BASE_ADDRESS_0:
|
||||
case PCI_BASE_ADDRESS_1:
|
||||
case PCI_BASE_ADDRESS_2:
|
||||
case PCI_BASE_ADDRESS_3:
|
||||
case PCI_BASE_ADDRESS_4:
|
||||
case PCI_BASE_ADDRESS_5: {
|
||||
int barnum;
|
||||
u32 *bar;
|
||||
|
||||
barnum = pci_offset_to_barnum(offset);
|
||||
bar = &plat->bar[barnum];
|
||||
|
||||
*valuep = sandbox_pci_read_bar(*bar, barinfo[barnum].type,
|
||||
barinfo[barnum].size);
|
||||
break;
|
||||
}
|
||||
case PCI_CAPABILITY_LIST:
|
||||
*valuep = PCI_CAP_ID_PM_OFFSET;
|
||||
break;
|
||||
case PCI_CAP_ID_PM_OFFSET:
|
||||
*valuep = (PCI_CAP_ID_EXP_OFFSET << 8) | PCI_CAP_ID_PM;
|
||||
break;
|
||||
case PCI_CAP_ID_PM_OFFSET + PCI_CAP_LIST_NEXT:
|
||||
*valuep = PCI_CAP_ID_EXP_OFFSET;
|
||||
break;
|
||||
case PCI_CAP_ID_EXP_OFFSET:
|
||||
*valuep = (PCI_CAP_ID_MSIX_OFFSET << 8) | PCI_CAP_ID_EXP;
|
||||
break;
|
||||
case PCI_CAP_ID_EXP_OFFSET + PCI_CAP_LIST_NEXT:
|
||||
*valuep = PCI_CAP_ID_MSIX_OFFSET;
|
||||
break;
|
||||
case PCI_CAP_ID_MSIX_OFFSET:
|
||||
if (sandbox_swap_case_use_ea(emul))
|
||||
*valuep = (PCI_CAP_ID_EA_OFFSET << 8) | PCI_CAP_ID_MSIX;
|
||||
else
|
||||
*valuep = PCI_CAP_ID_MSIX;
|
||||
break;
|
||||
case PCI_CAP_ID_MSIX_OFFSET + PCI_CAP_LIST_NEXT:
|
||||
if (sandbox_swap_case_use_ea(emul))
|
||||
*valuep = PCI_CAP_ID_EA_OFFSET;
|
||||
else
|
||||
*valuep = 0;
|
||||
break;
|
||||
case PCI_CAP_ID_EA_OFFSET:
|
||||
*valuep = (PCI_CAP_ID_EA_ENTRY_CNT << 16) | PCI_CAP_ID_EA;
|
||||
break;
|
||||
case PCI_CAP_ID_EA_OFFSET + PCI_CAP_LIST_NEXT:
|
||||
*valuep = 0;
|
||||
break;
|
||||
case PCI_EXT_CAP_ID_ERR_OFFSET:
|
||||
*valuep = (PCI_EXT_CAP_ID_VC_OFFSET << 20) | PCI_EXT_CAP_ID_ERR;
|
||||
break;
|
||||
case PCI_EXT_CAP_ID_VC_OFFSET:
|
||||
*valuep = (PCI_EXT_CAP_ID_DSN_OFFSET << 20) | PCI_EXT_CAP_ID_VC;
|
||||
break;
|
||||
case PCI_EXT_CAP_ID_DSN_OFFSET:
|
||||
*valuep = PCI_EXT_CAP_ID_DSN;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sandbox_swap_case_write_config(struct udevice *emul, uint offset,
|
||||
ulong value, enum pci_size_t size)
|
||||
{
|
||||
struct swap_case_platdata *plat = dev_get_platdata(emul);
|
||||
|
||||
switch (offset) {
|
||||
case PCI_COMMAND:
|
||||
plat->command = value;
|
||||
break;
|
||||
case PCI_BASE_ADDRESS_0:
|
||||
case PCI_BASE_ADDRESS_1: {
|
||||
int barnum;
|
||||
u32 *bar;
|
||||
|
||||
barnum = pci_offset_to_barnum(offset);
|
||||
bar = &plat->bar[barnum];
|
||||
|
||||
debug("w bar %d=%lx\n", barnum, value);
|
||||
*bar = value;
|
||||
/* space indicator (bit#0) is read-only */
|
||||
*bar |= barinfo[barnum].type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sandbox_swap_case_find_bar(struct udevice *emul, unsigned int addr,
|
||||
int *barnump, unsigned int *offsetp)
|
||||
{
|
||||
struct swap_case_platdata *plat = dev_get_platdata(emul);
|
||||
int barnum;
|
||||
|
||||
for (barnum = 0; barnum < ARRAY_SIZE(barinfo); barnum++) {
|
||||
unsigned int size = barinfo[barnum].size;
|
||||
u32 base = plat->bar[barnum] & ~PCI_BASE_ADDRESS_SPACE;
|
||||
|
||||
if (addr >= base && addr < base + size) {
|
||||
*barnump = barnum;
|
||||
*offsetp = addr - base;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
*barnump = -1;
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static void sandbox_swap_case_do_op(enum swap_case_op op, char *str, int len)
|
||||
{
|
||||
for (; len > 0; len--, str++) {
|
||||
switch (op) {
|
||||
case OP_TO_UPPER:
|
||||
*str = toupper(*str);
|
||||
break;
|
||||
case OP_TO_LOWER:
|
||||
*str = tolower(*str);
|
||||
break;
|
||||
case OP_SWAP:
|
||||
if (isupper(*str))
|
||||
*str = tolower(*str);
|
||||
else
|
||||
*str = toupper(*str);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int sandbox_swap_case_read_io(struct udevice *dev, unsigned int addr,
|
||||
ulong *valuep, enum pci_size_t size)
|
||||
{
|
||||
struct swap_case_priv *priv = dev_get_priv(dev);
|
||||
unsigned int offset;
|
||||
int barnum;
|
||||
int ret;
|
||||
|
||||
ret = sandbox_swap_case_find_bar(dev, addr, &barnum, &offset);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (barnum == 0 && offset == 0)
|
||||
*valuep = (*valuep & ~0xff) | priv->op;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sandbox_swap_case_write_io(struct udevice *dev, unsigned int addr,
|
||||
ulong value, enum pci_size_t size)
|
||||
{
|
||||
struct swap_case_priv *priv = dev_get_priv(dev);
|
||||
unsigned int offset;
|
||||
int barnum;
|
||||
int ret;
|
||||
|
||||
ret = sandbox_swap_case_find_bar(dev, addr, &barnum, &offset);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (barnum == 0 && offset == 0)
|
||||
priv->op = value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pci_ea_bar2_magic = PCI_EA_BAR2_MAGIC;
|
||||
static int pci_ea_bar4_magic = PCI_EA_BAR4_MAGIC;
|
||||
|
||||
static int sandbox_swap_case_map_physmem(struct udevice *dev,
|
||||
phys_addr_t addr, unsigned long *lenp, void **ptrp)
|
||||
{
|
||||
struct swap_case_priv *priv = dev_get_priv(dev);
|
||||
unsigned int offset, avail;
|
||||
int barnum;
|
||||
int ret;
|
||||
|
||||
if (sandbox_swap_case_use_ea(dev)) {
|
||||
/*
|
||||
* only support mapping base address in EA test for now, we
|
||||
* don't handle mapping an offset inside a BAR. Seems good
|
||||
* enough for the current test.
|
||||
*/
|
||||
switch (addr) {
|
||||
case (phys_addr_t)PCI_CAP_EA_BASE_LO0:
|
||||
*ptrp = &priv->op;
|
||||
*lenp = 4;
|
||||
break;
|
||||
case (phys_addr_t)PCI_CAP_EA_BASE_LO1:
|
||||
*ptrp = priv->mem_text;
|
||||
*lenp = barinfo[1].size - 1;
|
||||
break;
|
||||
case (phys_addr_t)((PCI_CAP_EA_BASE_HI2 << 32) |
|
||||
PCI_CAP_EA_BASE_LO2):
|
||||
*ptrp = &pci_ea_bar2_magic;
|
||||
*lenp = PCI_CAP_EA_SIZE_LO;
|
||||
break;
|
||||
case (phys_addr_t)((PCI_CAP_EA_BASE_HI4 << 32) |
|
||||
PCI_CAP_EA_BASE_LO4):
|
||||
*ptrp = &pci_ea_bar4_magic;
|
||||
*lenp = (PCI_CAP_EA_SIZE_HI << 32) |
|
||||
PCI_CAP_EA_SIZE_LO;
|
||||
break;
|
||||
default:
|
||||
return -ENOENT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = sandbox_swap_case_find_bar(dev, addr, &barnum, &offset);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (barnum == 1) {
|
||||
*ptrp = priv->mem_text + offset;
|
||||
avail = barinfo[1].size - offset;
|
||||
if (avail > barinfo[1].size)
|
||||
*lenp = 0;
|
||||
else
|
||||
*lenp = min(*lenp, (ulong)avail);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static int sandbox_swap_case_unmap_physmem(struct udevice *dev,
|
||||
const void *vaddr, unsigned long len)
|
||||
{
|
||||
struct swap_case_priv *priv = dev_get_priv(dev);
|
||||
|
||||
sandbox_swap_case_do_op(priv->op, (void *)vaddr, len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct dm_pci_emul_ops sandbox_swap_case_emul_ops = {
|
||||
.read_config = sandbox_swap_case_read_config,
|
||||
.write_config = sandbox_swap_case_write_config,
|
||||
.read_io = sandbox_swap_case_read_io,
|
||||
.write_io = sandbox_swap_case_write_io,
|
||||
.map_physmem = sandbox_swap_case_map_physmem,
|
||||
.unmap_physmem = sandbox_swap_case_unmap_physmem,
|
||||
};
|
||||
|
||||
static const struct udevice_id sandbox_swap_case_ids[] = {
|
||||
{ .compatible = "sandbox,swap-case" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(sandbox_swap_case_emul) = {
|
||||
.name = "sandbox_swap_case_emul",
|
||||
.id = UCLASS_PCI_EMUL,
|
||||
.of_match = sandbox_swap_case_ids,
|
||||
.ops = &sandbox_swap_case_emul_ops,
|
||||
.priv_auto_alloc_size = sizeof(struct swap_case_priv),
|
||||
.platdata_auto_alloc_size = sizeof(struct swap_case_platdata),
|
||||
};
|
||||
|
||||
static struct pci_device_id sandbox_swap_case_supported[] = {
|
||||
{ PCI_VDEVICE(SANDBOX, SANDBOX_PCI_SWAP_CASE_EMUL_ID),
|
||||
SWAP_CASE_DRV_DATA },
|
||||
{},
|
||||
};
|
||||
|
||||
U_BOOT_PCI_DEVICE(sandbox_swap_case_emul, sandbox_swap_case_supported);
|
||||
@@ -0,0 +1,24 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (c) 2015 Google, Inc
|
||||
* Written by Simon Glass <sjg@chromium.org>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <syscon.h>
|
||||
#include <asm/test.h>
|
||||
#include <dm/lists.h>
|
||||
|
||||
static const struct udevice_id sandbox_syscon_ids[] = {
|
||||
{ .compatible = "sandbox,syscon0", .data = SYSCON0 },
|
||||
{ .compatible = "sandbox,syscon1", .data = SYSCON1 },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(sandbox_syscon) = {
|
||||
.name = "sandbox_syscon",
|
||||
.id = UCLASS_SYSCON,
|
||||
.of_match = sandbox_syscon_ids,
|
||||
};
|
||||
@@ -0,0 +1,257 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2016, NVIDIA CORPORATION.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <time.h>
|
||||
#include <dm/lists.h>
|
||||
#include <dm/root.h>
|
||||
#include <mailbox.h>
|
||||
#include <misc.h>
|
||||
#include <asm/arch-tegra/bpmp_abi.h>
|
||||
#include <asm/arch-tegra/ivc.h>
|
||||
|
||||
#define BPMP_IVC_FRAME_COUNT 1
|
||||
#define BPMP_IVC_FRAME_SIZE 128
|
||||
|
||||
#define BPMP_FLAG_DO_ACK BIT(0)
|
||||
#define BPMP_FLAG_RING_DOORBELL BIT(1)
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
struct tegra186_bpmp {
|
||||
struct mbox_chan mbox;
|
||||
struct tegra_ivc ivc;
|
||||
};
|
||||
|
||||
static int tegra186_bpmp_call(struct udevice *dev, int mrq, void *tx_msg,
|
||||
int tx_size, void *rx_msg, int rx_size)
|
||||
{
|
||||
struct tegra186_bpmp *priv = dev_get_priv(dev);
|
||||
int ret, err;
|
||||
void *ivc_frame;
|
||||
struct mrq_request *req;
|
||||
struct mrq_response *resp;
|
||||
ulong start_time;
|
||||
|
||||
debug("%s(dev=%p, mrq=%u, tx_msg=%p, tx_size=%d, rx_msg=%p, rx_size=%d) (priv=%p)\n",
|
||||
__func__, dev, mrq, tx_msg, tx_size, rx_msg, rx_size, priv);
|
||||
|
||||
if ((tx_size > BPMP_IVC_FRAME_SIZE) || (rx_size > BPMP_IVC_FRAME_SIZE))
|
||||
return -EINVAL;
|
||||
|
||||
ret = tegra_ivc_write_get_next_frame(&priv->ivc, &ivc_frame);
|
||||
if (ret) {
|
||||
pr_err("tegra_ivc_write_get_next_frame() failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
req = ivc_frame;
|
||||
req->mrq = mrq;
|
||||
req->flags = BPMP_FLAG_DO_ACK | BPMP_FLAG_RING_DOORBELL;
|
||||
memcpy(req + 1, tx_msg, tx_size);
|
||||
|
||||
ret = tegra_ivc_write_advance(&priv->ivc);
|
||||
if (ret) {
|
||||
pr_err("tegra_ivc_write_advance() failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
start_time = timer_get_us();
|
||||
for (;;) {
|
||||
ret = tegra_ivc_channel_notified(&priv->ivc);
|
||||
if (ret) {
|
||||
pr_err("tegra_ivc_channel_notified() failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = tegra_ivc_read_get_next_frame(&priv->ivc, &ivc_frame);
|
||||
if (!ret)
|
||||
break;
|
||||
|
||||
/* Timeout 20ms; roughly 10x current max observed duration */
|
||||
if ((timer_get_us() - start_time) > 20 * 1000) {
|
||||
pr_err("tegra_ivc_read_get_next_frame() timed out (%d)\n",
|
||||
ret);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
|
||||
resp = ivc_frame;
|
||||
err = resp->err;
|
||||
if (!err && rx_msg && rx_size)
|
||||
memcpy(rx_msg, resp + 1, rx_size);
|
||||
|
||||
ret = tegra_ivc_read_advance(&priv->ivc);
|
||||
if (ret) {
|
||||
pr_err("tegra_ivc_write_advance() failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
pr_err("BPMP responded with error %d\n", err);
|
||||
/* err isn't a U-Boot error code, so don't that */
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return rx_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* The BPMP exposes multiple different services. We create a sub-device for
|
||||
* each separate type of service, since each device must be of the appropriate
|
||||
* UCLASS.
|
||||
*/
|
||||
static int tegra186_bpmp_bind(struct udevice *dev)
|
||||
{
|
||||
int ret;
|
||||
struct udevice *child;
|
||||
|
||||
debug("%s(dev=%p)\n", __func__, dev);
|
||||
|
||||
ret = device_bind_driver_to_node(dev, "tegra186_clk", "tegra186_clk",
|
||||
dev_ofnode(dev), &child);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = device_bind_driver_to_node(dev, "tegra186_reset",
|
||||
"tegra186_reset", dev_ofnode(dev),
|
||||
&child);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = device_bind_driver_to_node(dev, "tegra186_power_domain",
|
||||
"tegra186_power_domain",
|
||||
dev_ofnode(dev), &child);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = dm_scan_fdt_dev(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ulong tegra186_bpmp_get_shmem(struct udevice *dev, int index)
|
||||
{
|
||||
int ret;
|
||||
struct fdtdec_phandle_args args;
|
||||
fdt_addr_t reg;
|
||||
|
||||
ret = fdtdec_parse_phandle_with_args(gd->fdt_blob, dev_of_offset(dev),
|
||||
"shmem", NULL, 0, index, &args);
|
||||
if (ret < 0) {
|
||||
pr_err("fdtdec_parse_phandle_with_args() failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
reg = fdtdec_get_addr_size_auto_noparent(gd->fdt_blob, args.node,
|
||||
"reg", 0, NULL, true);
|
||||
if (reg == FDT_ADDR_T_NONE) {
|
||||
pr_err("fdtdec_get_addr_size_auto_noparent() failed\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return reg;
|
||||
}
|
||||
|
||||
static void tegra186_bpmp_ivc_notify(struct tegra_ivc *ivc)
|
||||
{
|
||||
struct tegra186_bpmp *priv =
|
||||
container_of(ivc, struct tegra186_bpmp, ivc);
|
||||
int ret;
|
||||
|
||||
ret = mbox_send(&priv->mbox, NULL);
|
||||
if (ret)
|
||||
pr_err("mbox_send() failed: %d\n", ret);
|
||||
}
|
||||
|
||||
static int tegra186_bpmp_probe(struct udevice *dev)
|
||||
{
|
||||
struct tegra186_bpmp *priv = dev_get_priv(dev);
|
||||
int ret;
|
||||
ulong tx_base, rx_base, start_time;
|
||||
|
||||
debug("%s(dev=%p) (priv=%p)\n", __func__, dev, priv);
|
||||
|
||||
ret = mbox_get_by_index(dev, 0, &priv->mbox);
|
||||
if (ret) {
|
||||
pr_err("mbox_get_by_index() failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
tx_base = tegra186_bpmp_get_shmem(dev, 0);
|
||||
if (IS_ERR_VALUE(tx_base)) {
|
||||
pr_err("tegra186_bpmp_get_shmem failed for tx_base\n");
|
||||
return tx_base;
|
||||
}
|
||||
rx_base = tegra186_bpmp_get_shmem(dev, 1);
|
||||
if (IS_ERR_VALUE(rx_base)) {
|
||||
pr_err("tegra186_bpmp_get_shmem failed for rx_base\n");
|
||||
return rx_base;
|
||||
}
|
||||
debug("shmem: rx=%lx, tx=%lx\n", rx_base, tx_base);
|
||||
|
||||
ret = tegra_ivc_init(&priv->ivc, rx_base, tx_base, BPMP_IVC_FRAME_COUNT,
|
||||
BPMP_IVC_FRAME_SIZE, tegra186_bpmp_ivc_notify);
|
||||
if (ret) {
|
||||
pr_err("tegra_ivc_init() failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
tegra_ivc_channel_reset(&priv->ivc);
|
||||
start_time = timer_get_us();
|
||||
for (;;) {
|
||||
ret = tegra_ivc_channel_notified(&priv->ivc);
|
||||
if (!ret)
|
||||
break;
|
||||
|
||||
/* Timeout 100ms */
|
||||
if ((timer_get_us() - start_time) > 100 * 1000) {
|
||||
pr_err("Initial IVC reset timed out (%d)\n", ret);
|
||||
ret = -ETIMEDOUT;
|
||||
goto err_free_mbox;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_mbox:
|
||||
mbox_free(&priv->mbox);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tegra186_bpmp_remove(struct udevice *dev)
|
||||
{
|
||||
struct tegra186_bpmp *priv = dev_get_priv(dev);
|
||||
|
||||
debug("%s(dev=%p) (priv=%p)\n", __func__, dev, priv);
|
||||
|
||||
mbox_free(&priv->mbox);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct misc_ops tegra186_bpmp_ops = {
|
||||
.call = tegra186_bpmp_call,
|
||||
};
|
||||
|
||||
static const struct udevice_id tegra186_bpmp_ids[] = {
|
||||
{ .compatible = "nvidia,tegra186-bpmp" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(tegra186_bpmp) = {
|
||||
.name = "tegra186_bpmp",
|
||||
.id = UCLASS_MISC,
|
||||
.of_match = tegra186_bpmp_ids,
|
||||
.bind = tegra186_bpmp_bind,
|
||||
.probe = tegra186_bpmp_probe,
|
||||
.remove = tegra186_bpmp_remove,
|
||||
.ops = &tegra186_bpmp_ops,
|
||||
.priv_auto_alloc_size = sizeof(struct tegra186_bpmp),
|
||||
};
|
||||
@@ -0,0 +1,67 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2016, NVIDIA CORPORATION.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <dm/lists.h>
|
||||
#include <dm/root.h>
|
||||
|
||||
/**
|
||||
* The CAR exposes multiple different services. We create a sub-device for
|
||||
* each separate type of service, since each device must be of the appropriate
|
||||
* UCLASS.
|
||||
*/
|
||||
static int tegra_car_bpmp_bind(struct udevice *dev)
|
||||
{
|
||||
int ret;
|
||||
struct udevice *child;
|
||||
|
||||
debug("%s(dev=%p)\n", __func__, dev);
|
||||
|
||||
ret = device_bind_driver_to_node(dev, "tegra_car_clk", "tegra_car_clk",
|
||||
dev_ofnode(dev), &child);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = device_bind_driver_to_node(dev, "tegra_car_reset",
|
||||
"tegra_car_reset", dev_ofnode(dev),
|
||||
&child);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_car_bpmp_probe(struct udevice *dev)
|
||||
{
|
||||
debug("%s(dev=%p)\n", __func__, dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_car_bpmp_remove(struct udevice *dev)
|
||||
{
|
||||
debug("%s(dev=%p)\n", __func__, dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id tegra_car_bpmp_ids[] = {
|
||||
{ .compatible = "nvidia,tegra20-car" },
|
||||
{ .compatible = "nvidia,tegra30-car" },
|
||||
{ .compatible = "nvidia,tegra114-car" },
|
||||
{ .compatible = "nvidia,tegra124-car" },
|
||||
{ .compatible = "nvidia,tegra210-car" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(tegra_car_bpmp) = {
|
||||
.name = "tegra_car",
|
||||
.id = UCLASS_MISC,
|
||||
.of_match = tegra_car_bpmp_ids,
|
||||
.bind = tegra_car_bpmp_bind,
|
||||
.probe = tegra_car_bpmp_probe,
|
||||
.remove = tegra_car_bpmp_remove,
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (c) 2009 Wind River Systems, Inc.
|
||||
* Tom Rix <Tom.Rix at windriver.com>
|
||||
*
|
||||
* twl4030_led_init is from cpu/omap3/common.c, power_init_r
|
||||
*
|
||||
* (C) Copyright 2004-2008
|
||||
* Texas Instruments, <www.ti.com>
|
||||
*
|
||||
* Author :
|
||||
* Sunil Kumar <sunilsaini05 at gmail.com>
|
||||
* Shashi Ranjan <shashiranjanmca05 at gmail.com>
|
||||
*
|
||||
* Derived from Beagle Board and 3430 SDP code by
|
||||
* Richard Woodruff <r-woodruff2 at ti.com>
|
||||
* Syed Mohammed Khasim <khasim at ti.com>
|
||||
*/
|
||||
|
||||
#include <twl4030.h>
|
||||
|
||||
void twl4030_led_init(unsigned char ledon_mask)
|
||||
{
|
||||
/* LEDs need to have corresponding PWMs enabled */
|
||||
if (ledon_mask & TWL4030_LED_LEDEN_LEDAON)
|
||||
ledon_mask |= TWL4030_LED_LEDEN_LEDAPWM;
|
||||
if (ledon_mask & TWL4030_LED_LEDEN_LEDBON)
|
||||
ledon_mask |= TWL4030_LED_LEDEN_LEDBPWM;
|
||||
|
||||
twl4030_i2c_write_u8(TWL4030_CHIP_LED, TWL4030_LED_LEDEN,
|
||||
ledon_mask);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2018 Arm Ltd
|
||||
* Author: Liviu Dudau <liviu.dudau@foss.arm.com>
|
||||
*
|
||||
*/
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <dm/read.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/delay.h>
|
||||
#include <misc.h>
|
||||
|
||||
#define SYS_CFGDATA 0xa0
|
||||
|
||||
#define SYS_CFGCTRL 0xa4
|
||||
#define SYS_CFGCTRL_START BIT(31)
|
||||
#define SYS_CFGCTRL_WRITE BIT(30)
|
||||
|
||||
#define SYS_CFGSTAT 0xa8
|
||||
#define SYS_CFGSTAT_ERR BIT(1)
|
||||
#define SYS_CFGSTAT_COMPLETE BIT(0)
|
||||
|
||||
struct vexpress_config_sysreg {
|
||||
phys_addr_t addr;
|
||||
u32 site;
|
||||
};
|
||||
|
||||
static int vexpress_config_exec(struct vexpress_config_sysreg *syscfg,
|
||||
bool write, void *buf, int size)
|
||||
{
|
||||
u32 cmd, status, tries = 100;
|
||||
|
||||
cmd = (*(u32 *)buf) | SYS_CFGCTRL_START | (syscfg->site << 16);
|
||||
|
||||
if (!write) {
|
||||
/* write a canary in the data register for reads */
|
||||
writel(0xdeadbeef, syscfg->addr + SYS_CFGDATA);
|
||||
} else {
|
||||
cmd |= SYS_CFGCTRL_WRITE;
|
||||
writel(((u32 *)buf)[1], syscfg->addr + SYS_CFGDATA);
|
||||
}
|
||||
writel(0, syscfg->addr + SYS_CFGSTAT);
|
||||
writel(cmd, syscfg->addr + SYS_CFGCTRL);
|
||||
|
||||
/* completion of command takes ages, go to sleep (150us) */
|
||||
do {
|
||||
udelay(150);
|
||||
status = readl(syscfg->addr + SYS_CFGSTAT);
|
||||
if (status & SYS_CFGSTAT_ERR)
|
||||
return -EFAULT;
|
||||
} while (--tries && !(status & SYS_CFGSTAT_COMPLETE));
|
||||
|
||||
if (!tries)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
if (!write)
|
||||
(*(u32 *)buf) = readl(syscfg->addr + SYS_CFGDATA);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vexpress_config_read(struct udevice *dev,
|
||||
int offset, void *buf, int size)
|
||||
{
|
||||
struct vexpress_config_sysreg *priv = dev_get_uclass_priv(dev);
|
||||
|
||||
if (size != sizeof(u32))
|
||||
return -EINVAL;
|
||||
|
||||
return vexpress_config_exec(priv, false, buf, size);
|
||||
}
|
||||
|
||||
static int vexpress_config_write(struct udevice *dev,
|
||||
int offset, const void *buf, int size)
|
||||
{
|
||||
struct vexpress_config_sysreg *priv = dev_get_uclass_priv(dev);
|
||||
|
||||
if (size != sizeof(u32) * 2)
|
||||
return -EINVAL;
|
||||
|
||||
return vexpress_config_exec(priv, true, (void *)buf, size);
|
||||
}
|
||||
|
||||
static struct misc_ops vexpress_config_ops = {
|
||||
.read = vexpress_config_read,
|
||||
.write = vexpress_config_write,
|
||||
};
|
||||
|
||||
static int vexpress_config_probe(struct udevice *dev)
|
||||
{
|
||||
struct ofnode_phandle_args args;
|
||||
struct vexpress_config_sysreg *priv;
|
||||
const char *prop;
|
||||
int err, prop_size;
|
||||
|
||||
err = dev_read_phandle_with_args(dev, "arm,vexpress,config-bridge",
|
||||
NULL, 0, 0, &args);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
prop = ofnode_get_property(args.node, "compatible", &prop_size);
|
||||
if (!prop || (strncmp(prop, "arm,vexpress-sysreg", 19) != 0))
|
||||
return -ENOENT;
|
||||
|
||||
priv = calloc(1, sizeof(*priv));
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
dev->uclass_priv = priv;
|
||||
priv->addr = ofnode_get_addr(args.node);
|
||||
|
||||
return dev_read_u32(dev, "arm,vexpress,site", &priv->site);
|
||||
}
|
||||
|
||||
static const struct udevice_id vexpress_config_ids[] = {
|
||||
{ .compatible = "arm,vexpress,config-bus" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(vexpress_config_drv) = {
|
||||
.name = "vexpress_config_bus",
|
||||
.id = UCLASS_MISC,
|
||||
.of_match = vexpress_config_ids,
|
||||
.bind = dm_scan_fdt_dev,
|
||||
.probe = vexpress_config_probe,
|
||||
.ops = &vexpress_config_ops,
|
||||
};
|
||||
@@ -0,0 +1,40 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2016 Stefan Roese <sr@denx.de>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/pnp_def.h>
|
||||
|
||||
#define WINBOND_ENTRY_KEY 0x87
|
||||
#define WINBOND_EXIT_KEY 0xaa
|
||||
|
||||
/* Enable configuration: pass entry key '0x87' into index port dev twice */
|
||||
static void pnp_enter_conf_state(u16 dev)
|
||||
{
|
||||
u16 port = dev >> 8;
|
||||
|
||||
outb(WINBOND_ENTRY_KEY, port);
|
||||
outb(WINBOND_ENTRY_KEY, port);
|
||||
}
|
||||
|
||||
/* Disable configuration: pass exit key '0xAA' into index port dev */
|
||||
static void pnp_exit_conf_state(u16 dev)
|
||||
{
|
||||
u16 port = dev >> 8;
|
||||
|
||||
outb(WINBOND_EXIT_KEY, port);
|
||||
}
|
||||
|
||||
/* Bring up early serial debugging output before the RAM is initialized */
|
||||
void winbond_enable_serial(uint dev, uint iobase, uint irq)
|
||||
{
|
||||
pnp_enter_conf_state(dev);
|
||||
pnp_set_logical_device(dev);
|
||||
pnp_set_enable(dev, 0);
|
||||
pnp_set_iobase(dev, PNP_IDX_IO0, iobase);
|
||||
pnp_set_irq(dev, PNP_IDX_IRQ0, irq);
|
||||
pnp_set_enable(dev, 1);
|
||||
pnp_exit_conf_state(dev);
|
||||
}
|
||||
Reference in New Issue
Block a user