GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)

This commit is contained in:
lai
2026-09-06 03:52:57 +08:00
commit b1928b41c0
21813 changed files with 4413081 additions and 0 deletions
@@ -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(&regs->id);
sysid[1] = readl(&regs->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(&regs_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(&regs_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(&regs->stat, 0);
iim_setbits32(&regs->err, 0);
}
static void finish_access(struct fsl_iim *regs, u32 *stat, u32 *err)
{
*stat = iim_read32(&regs->stat);
*err = iim_read32(&regs->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(&regs, bank, word, val, __func__);
if (ret)
return ret;
*val = iim_read32(&regs->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(&regs->ua, bank << 3 | word >> 5);
iim_write32(&regs->la, (word << 3 | bit) & 0xff);
if (fctl == FCTL_PRG)
iim_write32(&regs->prg_p, 0xaa);
iim_setbits32(&regs->fctl, fctl);
while (iim_read32(&regs->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(&regs, 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(&regs->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(&regs->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(&regs, 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(&regs, bank, word, val, __func__);
if (ret)
return ret;
clear_status(regs);
iim_write32(&regs->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(&regs->ctrl) & BM_CTRL_BUSY)
udelay(delay_us);
}
static void clear_error(struct ocotp_regs *regs)
{
writel(BM_CTRL_ERROR, &regs->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(&regs->ctrl) & BM_CTRL_ERROR);
clear_error(regs);
#ifdef CONFIG_MX7ULP
/* Need to power down the OTP memory */
writel(1, &regs->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(&regs, bank, word, val, __func__);
if (ret)
return ret;
phy_bank = fuse_bank_physical(bank);
phy_word = fuse_word_physical(bank, word);
*val = readl(&regs->bank[phy_bank].fuse_regs[phy_word << 2]);
#ifdef CONFIG_MX7ULP
if (readl(&regs->out_status) & BM_OUT_STATUS_DED) {
writel(BM_OUT_STATUS_DED, &regs->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(&regs->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(&regs->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(&regs->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(&regs, bank, word, val, __func__);
if (ret)
return ret;
setup_direct_access(regs, bank, word, false);
writel(BM_READ_CTRL_READ_FUSE, &regs->read_ctrl);
wait_busy(regs, 1);
#ifdef CONFIG_MX7
*val = readl((&regs->read_fuse_data0) + (word << 2));
#else
*val = readl(&regs->read_fuse_data);
#endif
#ifdef CONFIG_MX7ULP
if (readl(&regs->out_status) & BM_OUT_STATUS_DED) {
writel(BM_OUT_STATUS_DED, &regs->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(&regs, bank, word, __func__);
if (ret)
return ret;
setup_direct_access(regs, bank, word, true);
#ifdef CONFIG_MX7
switch (word) {
case 0:
writel(0, &regs->data1);
writel(0, &regs->data2);
writel(0, &regs->data3);
writel(val, &regs->data0);
break;
case 1:
writel(val, &regs->data1);
writel(0, &regs->data2);
writel(0, &regs->data3);
writel(0, &regs->data0);
break;
case 2:
writel(0, &regs->data1);
writel(val, &regs->data2);
writel(0, &regs->data3);
writel(0, &regs->data0);
break;
case 3:
writel(0, &regs->data1);
writel(0, &regs->data2);
writel(val, &regs->data3);
writel(0, &regs->data0);
break;
}
wait_busy(regs, BV_TIMING_PROG_US);
#else
writel(val, &regs->data);
wait_busy(regs, BV_TIMING_STROBE_PROG_US);
#endif
udelay(WRITE_POSTAMBLE_US);
#ifdef CONFIG_MX7ULP
if (readl(&regs->out_status) & (BM_OUT_STATUS_PROGFAIL | BM_OUT_STATUS_LOCKED)) {
writel((BM_OUT_STATUS_PROGFAIL | BM_OUT_STATUS_LOCKED), &regs->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(&regs, bank, word, __func__);
if (ret)
return ret;
phy_bank = fuse_bank_physical(bank);
phy_word = fuse_word_physical(bank, word);
writel(val, &regs->bank[phy_bank].fuse_regs[phy_word << 2]);
#ifdef CONFIG_MX7ULP
if (readl(&regs->out_status) & (BM_OUT_STATUS_PROGFAIL | BM_OUT_STATUS_LOCKED)) {
writel((BM_OUT_STATUS_PROGFAIL | BM_OUT_STATUS_LOCKED), &regs->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);
}